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;