206 lines
5.3 KiB
JavaScript
206 lines
5.3 KiB
JavaScript
const util = require('util');
|
||
const utils = require('./utils');
|
||
const metamgr = require('./metamgr');
|
||
const dbhelper = require("./dbhelper");
|
||
const log = require("./log");
|
||
const dblog = require("./dblog");
|
||
const bc = require('./blockchain');
|
||
const C = require('./C');
|
||
|
||
/*
|
||
需要重试的情况:
|
||
1、调用合约后当前块的高度>合约高度 + 6并且getPastEvent里查询不到该事件
|
||
2、connection not open on send网络不通(目前未处理)
|
||
3、 Returned error: replacement transaction underpriced(目前排队没这个问题)
|
||
*/
|
||
|
||
class ContractExecutor {
|
||
|
||
constructor() {
|
||
this.instanceName = '';
|
||
this.suspend = null;
|
||
this.getLogKey = null;
|
||
this.getLogData = null;
|
||
this.getPrepareBlockNumber = null;
|
||
this.setPrepareBlockNumber = null;
|
||
this.getSuccessBlockNumber = null;
|
||
this.setSuccessBlockNumber = null;
|
||
this.getSyncCount = null;
|
||
|
||
this.searchLogClass = '';
|
||
this.eventName = '';
|
||
this.eventFilter = null;
|
||
|
||
this.syncLogClass = '';
|
||
this.methodName = '';
|
||
this.methodArgs = [];
|
||
this.syncDbLogClass = '';
|
||
|
||
this.onSyncSuccess = null;
|
||
this.onConfirmed = null;
|
||
}
|
||
|
||
init() {
|
||
this.instance = bc[this.instanceName];
|
||
this.method = this.instance.methods[this.methodName];
|
||
}
|
||
|
||
async execute() {
|
||
let tryCount = 0;
|
||
do {
|
||
await this.sync();
|
||
const confirmed = await this.confirm();
|
||
if (confirmed) {
|
||
this.onConfirmed(this.syncLogClass + '.confirmed');
|
||
return true;
|
||
} else {
|
||
++tryCount;
|
||
await this.setPrepareBlockNumber('execute.setPrepareBlockNumber:' + this.instanceName, 0);
|
||
await this.setSuccessBlockNumber('execute:setSuccessBlockNumber:' + this.instanceName, 0);
|
||
await utils.sleep(utils.randRange(1500, 4000));
|
||
}
|
||
} while (tryCount < 3 && this.getSyncCount() < 3);
|
||
return false;
|
||
}
|
||
|
||
async sync() {
|
||
await bc.mustBeActive();
|
||
const found = await this.bcSearch();
|
||
if (!found) {
|
||
await this.bcSync();
|
||
}
|
||
}
|
||
|
||
async confirm() {
|
||
while (true) {
|
||
if (bc.isComfirmed(this.getSuccessBlockNumber())) {
|
||
const found = await this.bcSearch();
|
||
return found;
|
||
break;
|
||
}
|
||
await utils.sleep(1000 * 3);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
async bcSearch() {
|
||
if (this.getPrepareBlockNumber() <= 0) {
|
||
return false;
|
||
}
|
||
let events = [];
|
||
while (true) {
|
||
try {
|
||
const fromBlock = Math.max(this.getPrepareBlockNumber() - 1000, 0);
|
||
events = await this.instance.getPastEvents(
|
||
this.eventName,
|
||
{
|
||
fromBlock: fromBlock,
|
||
toBlock: fromBlock + 5000 - 1,
|
||
filter: this.eventFilter
|
||
});
|
||
break;
|
||
} catch (err) {
|
||
log.warning(
|
||
util.format('%s getPastEvents data:%s err:%s',
|
||
this.searchLogClass,
|
||
utils.jsonEncode(this.getLogData()),
|
||
err
|
||
));
|
||
}
|
||
await utils.sleep(1000 * 3);
|
||
}
|
||
if (events.length > 0) {
|
||
const blockNumber = events[0]['blockNumber'];
|
||
if (!blockNumber) {
|
||
await this.suspend(this.searchLogClass, 'blocNumber is empty');
|
||
return false;
|
||
}
|
||
await this.setSuccessBlockNumber(this.searchLogClass, blockNumber);
|
||
}
|
||
return events.length > 0;
|
||
}
|
||
|
||
async bcSync() {
|
||
await bc.mustBeActive();
|
||
let currBlockNumber = bc.getCurrBlockNumber();
|
||
try {
|
||
let hasErr = false;
|
||
let holdErr = null;
|
||
let nowTime = utils.getUtcTime();
|
||
//要确保100%能unlock
|
||
let result = null;
|
||
await bc.lock();
|
||
try {
|
||
await bc.mustBeActive();
|
||
currBlockNumber = bc.getCurrBlockNumber();
|
||
nowTime = utils.getUtcTime();
|
||
result = await this.method(...this.methodArgs).send({gas: 1000000});
|
||
} catch (err) {
|
||
hasErr = true;
|
||
holdErr = err;
|
||
} finally {
|
||
await bc.unLock();
|
||
await dblog.addLogEx(
|
||
nowTime,
|
||
this.syncDbLogClass,
|
||
'prepare',
|
||
this.getLogKey(),
|
||
utils.jsonEncode(this.getLogData()),
|
||
currBlockNumber);
|
||
await this.setPrepareBlockNumber(this.syncLogClass, currBlockNumber);
|
||
if (hasErr) {
|
||
throw holdErr;
|
||
}
|
||
}
|
||
await dblog.addLog(
|
||
this.syncDbLogClass,
|
||
'success',
|
||
this.getLogKey(),
|
||
utils.jsonEncode(this.getLogData()),
|
||
utils.jsonEncode(result),
|
||
currBlockNumber);
|
||
const blockNumber = result['blockNumber'];
|
||
if (!blockNumber) {
|
||
await this.suspend(this.syncLogClass, 'blockNumber is empty');
|
||
}
|
||
await this.onSyncSuccess(this.syncLogClass, result);
|
||
} catch (err) {
|
||
await dblog.addLog(
|
||
this.syncDbLogClass,
|
||
'failed',
|
||
this.getLogKey(),
|
||
utils.jsonEncode(this.getLogData()),
|
||
err,
|
||
currBlockNumber);
|
||
await this.suspend(this.syncLogClass, err);
|
||
}
|
||
}
|
||
|
||
};
|
||
|
||
function extractEventTxHash(event)
|
||
{
|
||
let txHash = '';
|
||
try {
|
||
txHash = event['transactionHash'];
|
||
} catch (err) {
|
||
|
||
}
|
||
return txHash;
|
||
}
|
||
|
||
function extractEventBlockNumber(event)
|
||
{
|
||
let blockNumber = '';
|
||
try {
|
||
blockNumber = event['blockNumber'];
|
||
} catch (err) {
|
||
|
||
}
|
||
return blockNumber;
|
||
}
|
||
|
||
exports.ContractExecutor = ContractExecutor;
|
||
exports.extractEventTxHash = extractEventTxHash;
|
||
exports.extractEventBlockNumber = extractEventBlockNumber;
|