const util = require('util'); const utils = require('./utils'); 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.searchLogClass = ''; this.eventName = ''; this.eventFilter = null; this.onSearchSuccess = null; this.searchGetBlockNumber = null; this.syncLogClass = ''; this.isSynced = null; this.methodName = ''; this.methodArgs = []; this.syncDbLogClass = ''; this.syncResetState = null; this.syncGetBlockNumber = null; this.onSyncPrepare = null; this.onSyncSuccess = null; this.onSyncFailed = null; } init() { this.instance = bc[this.instanceName]; this.method = this.instance.methods[this.methodName]; } async execute() { let tryCount = 0; do { await bc.mustBeActive(); await this.sync(); const confirmed = await this.confirm(); if (confirmed) { return; } ++tryCount; await utils.sleep(utils.randRange(1500, 4000)); } while (tryCount < 3); } async sync() { if (!this.isSynced()) { 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.syncGetBlockNumber())) { const found = await this.bcSearch(); return found; break; } await utils.sleep(1000 * 3); } return false; } async bcSearch() { if (this.searchGetBlockNumber() <= 0) { return false; } let events = []; while (true) { try { const fromBlock = Math.max(this.searchGetBlockNumber() - 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.doSuspend(this.searchLogClass, 'blocNumber is empty'); return false; } this.onSearchSuccess(events[0]); } return events.length > 0; } async bcSync() { await bc.mustBeActive(); const currBlockNumber = bc.getCurrBlockNumber(); const doSync = async() => { let result = null; try { await dblog.addLog( this.syncDbLogClass, 'prepare', this.getLogKey(), utils.jsonEncode(this.getLogData())); if (this.onSyncPrepare) { this.onSyncPrepare(); } result = await this.method(this.methodArgs).send({gas: 1000000}); await dblog.addLog( this.syncDbLogClass, 'success', this.getOrderId(), utils.jsonEncode(this.getLogData()), utils.jsonEncode(result)); if (this.onSyncSuccess) { this.onSyncSuccess(result); } } catch (err) { await dblog.addLog( this.syncDbLogClass, 'failed', this.getLogKey(), utils.jsonEncode(this.getLogData()), err); if (this.onSyncFailed) { this.onSyncFailed(err); } 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;