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.getPrepareBlockNumber = null; this.getSuccessBlockNumber = null; this.getSyncCount = null; this.searchLogClass = ''; this.eventName = ''; this.eventFilter = null; this.syncLogClass = ''; this.methodName = ''; this.methodArgs = []; this.syncDbLogClass = ''; this.onResetState = null; this.onSearchSuccess = null; this.onSyncSuccess = 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) { ++tryCount; await this.onResetState(); 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); 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; } await 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())); result = await this.method(this.methodArgs).send({gas: 1000000}); await dblog.addLog( this.syncDbLogClass, 'success', this.getOrderId(), utils.jsonEncode(this.getLogData()), utils.jsonEncode(result)); await this.onSyncSuccess(result); } catch (err) { await dblog.addLog( this.syncDbLogClass, 'failed', this.getLogKey(), utils.jsonEncode(this.getLogData()), 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;