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'); class ContractExecutor { constructor() { this.instanceName = ''; this.doSuspend = 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; let confirmed = false; do { await this.sync(); confirmed = await this.confirm(); if (confirmed) { this.onConfirmed(this.syncLogClass + '.confirmed'); } else { ++tryCount; await this.setPrepareBlockNumber('setPrepareBlockNumber:' + this.instanceName, 0); await this.setSuccessBlockNumber('execute:setSuccessBlockNumber:' + this.instanceName, 0); await utils.sleep(utils.randRange(1500, 4000)); } } while (tryCount < 3 && !confirmed && this.getSyncCount() < 3); return confirmed; } async sync() { await bc.mustBeActive(); const found = await this.bcSearch(); if (!found) { const ok = await this.bcSync(); if (!ok) { this.doSuspend(this.syncLogClass, ''); return; } } } 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); log.info( util.format('%s begin search logKey:%s', this.searchLogClass, this.getLogKey() )); events = await this.instance.getPastEvents( this.eventName, { fromBlock: fromBlock, toBlock: fromBlock + 5000 - 1, filter: this.eventFilter }); log.info( util.format('%s end search logKey:%s', this.searchLogClass, this.getLogKey() )); 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.doSuspend(this.searchLogClass, 'blocNumber is empty'); return false; } await this.setSuccessBlockNumber(this.searchLogClass, blockNumber); } return events.length > 0; } async bcSync() { const doSync = async() => { let result = null; await bc.mustBeActive(); let currBlockNumber = bc.getCurrBlockNumber(); try { log.info( util.format('%s begin sync logKey:%s', this.syncLogClass, this.getLogKey() )); let hasErr = false; let holdErr = null; let nowTime = utils.getUtcTime(); //要确保100%能unlock await bc.lock(); try { 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; } } log.info( util.format('%s end sync logKey:%s', this.syncLogClass, this.getLogKey() )); await dblog.addLog( this.syncDbLogClass, 'success', this.getLogKey(), utils.jsonEncode(this.getLogData()), utils.jsonEncode(result), currBlockNumber); await this.onSyncSuccess(this.syncLogClass, result); } catch (err) { await dblog.addLog( this.syncDbLogClass, 'failed', this.getLogKey(), utils.jsonEncode(this.getLogData()), err, currBlockNumber); await this.doSuspend(this.syncLogClass, err); } return result; }; try { const result = await doSync(); return true; } catch (err) { log.warning( util.format('%s end data:%s err:%s', this.syncLogClass, utils.jsonEncode(this.getLogData()), err ) ); } return false; } }; exports.ContractExecutor = ContractExecutor;