tx listener

pull/1/head
yann300 7 years ago
parent b924c30f9d
commit 4bf2d990bf
  1. 42
      src/app.js
  2. 226
      src/app/txListener.js

@ -29,6 +29,7 @@ var FilePanel = require('./app/file-panel')
var EditorPanel = require('./app/editor-panel')
var RighthandPanel = require('./app/righthand-panel')
var examples = require('./app/example-contracts')
var Txlistener = require('./app/txListener')
var css = csjs`
html { box-sizing: border-box; }
@ -847,6 +848,47 @@ function run () {
var node = document.getElementById('staticanalysisView')
node.insertBefore(staticanalysis.render(), node.childNodes[0])
// ----------------- Tx listener -----------------
var txlistener = new Txlistener({
appAPI: {
web3: function () { return executionContext.web3() },
isVM: function () { return executionContext.isVM() },
vm: function () { return executionContext.vm() },
contracts: function () {
if (compiler.lastCompilationResult && compiler.lastCompilationResult.data) {
return compiler.lastCompilationResult.data.contracts
}
return null
},
context: function () {
return executionContext.getProvider()
}
},
appEvent: {
executionContext: executionContext.event,
udapp: udapp.event
}
})
txlistener.startListening()
txlistener.event.register('newBlock', (block) => {
for (var t in block.transactions) {
var tx = block.transactions[t]
var resolvedTransaction = txlistener.resolvedTransaction(tx.hash)
var address = null
if (resolvedTransaction) {
address = resolvedTransaction.contractAddress ? resolvedTransaction.contractAddress : tx.to
}
console.log({
tx: tx,
resolvedContract: txlistener.resolvedContract(address),
resolvedTransaction: resolvedTransaction
})
}
})
// ----------------- autoCompile -----------------
var autoCompile = document.querySelector('#autoCompile').checked
if (config.exists('autoCompile')) {

@ -0,0 +1,226 @@
'use strict'
var async = require('async')
var EventManager = require('ethereum-remix').lib.EventManager
var remix = require('ethereum-remix')
var codeUtil = remix.util.code
var Web3VMProvider = remix.web3.web3VMProvider
/**
* poll web3 each 2s if web3
* listen on transaction executed event if VM
* attention: blocks returned by the event `newBlock` have slightly different json properties whether web3 or the VM is used
* trigger 'newBlock'
*
*/
class TxListener {
constructor (opt) {
this.event = new EventManager()
this._appAPI = opt.appAPI
this._web3VMProvider = new Web3VMProvider() // TODO this should maybe be put in app.js
this._web3VMProvider.setVM(opt.appAPI.vm())
this._resolvedTransactions = {}
this._resolvedContracts = {}
this.init()
opt.appEvent.executionContext.register('contextChanged', (context) => {
if (this.loopId) {
this.startListening(context)
}
})
opt.appEvent.udapp.register('transactionExecuted', (to, data, lookupOnly, txResult) => {
if (this.loopId && this._appAPI.isVM()) {
this._web3VMProvider.getTransaction(txResult.transactionHash, (error, tx) => {
if (error) return console.log(error)
this._newBlock({
type: 'VM',
number: -1,
transactions: [tx]
})
})
}
})
}
/**
* reset recorded transactions
*/
init () {
this.blocks = []
this.lastBlock = null
}
/**
* start listening for incoming transactions
*
* @param {String} type - type/name of the provider to add
* @param {Object} obj - provider
*/
startListening () {
this.stopListening()
this.init()
if (this._appAPI.context() === 'vm') {
this.loopId = 'vm-listener'
} else {
this.loopId = setInterval(() => {
this._appAPI.web3().eth.getBlockNumber((error, blockNumber) => {
if (error) return console.log(error)
if (!this.lastBlock || blockNumber > this.lastBlock) {
this.lastBlock = blockNumber
this._appAPI.web3().eth.getBlock(this.lastBlock, true, (error, result) => {
if (!error) {
this._newBlock(Object.assign({type: 'web3'}, result))
}
})
}
})
}, 2)
}
}
currentWeb3 () { // TODO this should maybe be put in app.js
return this._appAPI.isVM() ? this._web3VMProvider : this._appAPI.web3()
}
/**
* stop listening for incoming transactions. do not reset the recorded pool.
*
* @param {String} type - type/name of the provider to add
* @param {Object} obj - provider
*/
stopListening () {
if (this.loopId) {
clearInterval(this.loopId)
}
this.loopId = null
}
/**
* try to resolve the contract name from the given @arg address
*
* @param {String} address - contract address to resolve
* @return {String} - contract name
*/
resolvedContract (address) {
return this._resolvedContracts[address]
}
/**
* try to resolve the transaction from the given @arg txHash
*
* @param {String} txHash - contract address to resolve
* @return {String} - contract name
*/
resolvedTransaction (txHash) {
return this._resolvedTransactions[txHash]
}
_newBlock (block) {
this.blocks.push(block)
this._resolve(block, () => {
this.event.trigger('newBlock', [block])
})
}
_resolve (block, callback) {
async.each(block.transactions, (tx, cb) => {
this._resolveTx(tx, cb)
}, () => {
callback()
})
}
_resolveTx (tx, cb) {
var contracts = this._appAPI.contracts()
if (!contracts) return cb()
var contractName
if (!tx.to) {
// contract creation / resolve using the creation bytes code
// if web3: we have to call getTransactionReceipt to get the created address
// if VM: created address already included
var code = tx.input
contractName = this._tryResolveContract(code, contracts, 'bytecode')
if (contractName) {
this._resolveCreationAddress(tx, (error, address) => {
if (error) return cb(error)
this._resolvedContracts[address] = contractName
this._resolveFunction(contractName, contracts, tx, true)
if (this._resolvedTransactions[tx.hash]) {
this._resolvedTransactions[tx.hash].contractAddress = address
}
return cb()
})
return
}
return cb()
} else {
// first check known contract, resolve against the `runtimeBytecode` if not known
contractName = this._resolvedContracts[tx.to]
if (!contractName) {
this.currentWeb3().eth.getCode(tx.to, (error, code) => {
if (error) return cb(error)
if (code) {
var contractName = this._tryResolveContract(code, contracts, 'runtimeBytecode')
if (contractName) {
this._resolvedContracts[tx.to] = contractName
this._resolveFunction(contractName, contracts, tx, false)
return cb()
}
return cb()
}
return cb()
})
return
}
if (contractName) {
this._resolveFunction(contractName, contracts, tx, false)
return cb()
}
return cb()
}
}
_resolveCreationAddress (tx, cb) {
this.currentWeb3().eth.getTransactionReceipt(tx.hash, (error, receipt) => {
if (!error) {
cb(null, receipt.contractAddress)
} else {
cb(error)
}
})
}
_resolveFunction (contractName, compiledContracts, tx, isCtor) {
if (!isCtor) {
for (var fn in compiledContracts[contractName].functionHashes) {
if (compiledContracts[contractName].functionHashes[fn] === tx.input.replace('0x', '').substring(0, 8)) {
this._resolvedTransactions[tx.hash] = {
to: tx.to,
fn: fn,
params: null
}
}
// fallback function
this._resolvedTransactions[tx.hash] = {
to: tx.to,
fn: '(fallback)',
params: null
}
} else {
this._resolvedTransactions[tx.hash] = {
to: null,
fn: '(constructor)',
params: null
}
}
}
_tryResolveContract (codeToResolve, compiledContracts, type) {
for (var k in compiledContracts) {
if (codeUtil.compareByteCode(codeToResolve, '0x' + compiledContracts[k][type])) {
return k
}
}
return null
}
}
module.exports = TxListener
Loading…
Cancel
Save