Commit 676068b1 authored by yann300's avatar yann300

store ABI & simplify address resolver

parent 652307e8
remix @ 4fe13da1
Subproject commit 4fe13da1c8c51dc7bb728df14c30fbb274e0bc80
'use strict' 'use strict'
var async = require('async')
var $ = require('jquery') var $ = require('jquery')
var yo = require('yo-yo') var yo = require('yo-yo')
var helper = require('../../lib/helper.js') var helper = require('../../lib/helper.js')
...@@ -246,7 +245,6 @@ function updateAccountBalances (container, appAPI) { ...@@ -246,7 +245,6 @@ function updateAccountBalances (container, appAPI) {
RECORDER RECORDER
------------------------------------------------ */ ------------------------------------------------ */
function makeRecorder (appAPI, appEvents) { function makeRecorder (appAPI, appEvents) {
var udapp = appAPI.udapp()
var recorder = new Recorder({ var recorder = new Recorder({
events: { events: {
udapp: appEvents.udapp, udapp: appEvents.udapp,
...@@ -291,10 +289,9 @@ function makeRecorder (appAPI, appEvents) { ...@@ -291,10 +289,9 @@ function makeRecorder (appAPI, appEvents) {
}) })
} }
runButton.onclick = () => { runButton.onclick = () => {
modalDialogCustom.prompt(null, 'load from file (e.g. `scenarios/transactions1.json`)', '', filepath => { var currentFile = appAPI.config.get('currentFile')
var filename = appAPI.filesProviders['browser'].type + '/' + filepath var json = appAPI.filesProviders['browser'].get(currentFile)
var json = appAPI.filesProviders['browser'].get(filename) if (currentFile.match('.json$')) {
if (!json) return modalDialogCustom.alert('Could not find file with transactions, please try again')
try { try {
var obj = JSON.parse(json) var obj = JSON.parse(json)
var txArray = obj.transactions || [] var txArray = obj.transactions || []
...@@ -302,28 +299,16 @@ function makeRecorder (appAPI, appEvents) { ...@@ -302,28 +299,16 @@ function makeRecorder (appAPI, appEvents) {
var options = obj.options var options = obj.options
var abis = obj.abis var abis = obj.abis
} catch (e) { } catch (e) {
modalDialogCustom.alert('Invalid JSON, please try again') return modalDialogCustom.alert('Invalid Scenario File, please try again')
} }
if (txArray.length) { if (txArray.length) {
recorder.setListen(false) recorder.run(txArray, accounts, options, abis, (abi, address, contractName) => {
async.eachSeries(txArray, function (tx, cb) { instanceContainer.appendChild(appAPI.udapp().renderInstanceFromABI(abi, address, contractName))
var record = recorder.resolveAddress(tx.record, accounts, options) })
udapp.rerunTx(record, function (err, txResult) {
if (err) {
console.error(err)
} else {
var address = executionContext.isVM() ? txResult.result.createdAddress : tx.result.contractAddress
if (!address) return // not a contract creation
var abi = abis[tx.record.abi]
if (abi) {
instanceContainer.appendChild(appAPI.udapp().renderInstanceFromABI(abi, address, record.contractName))
}
}
cb()
})
}, () => { recorder.setListen(true) })
} }
}) } else {
modalDialogCustom.alert('Scenario File require JSON type')
}
} }
return el return el
} }
...@@ -395,7 +380,7 @@ function contractDropdown (appAPI, appEvents, instanceContainer) { ...@@ -395,7 +380,7 @@ function contractDropdown (appAPI, appEvents, instanceContainer) {
// ADD BUTTONS AT ADDRESS AND CREATE // ADD BUTTONS AT ADDRESS AND CREATE
function createInstance () { function createInstance () {
var selectedContract = getSelectedContract() var selectedContract = getSelectedContract()
if (selectedContract.contract.object.evm.bytecode.object.length === 0) { if (selectedContract.contract.object.evm.bytecode.object.length === 0) {
modalDialogCustom.alert('This contract does not implement all functions and thus cannot be created.') modalDialogCustom.alert('This contract does not implement all functions and thus cannot be created.')
......
var remixLib = require('remix-lib') var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager var EventManager = remixLib.EventManager
var util = remixLib.util
var executionContext = require('./execution-context')
var async = require('async')
/**
* Record transaction as long as the user create them.
*
*
*/
class Recorder { class Recorder {
constructor (opts = {}) { constructor (opts = {}) {
var self = this var self = this
self._api = opts.api self._api = opts.api
self.event = new EventManager() self.event = new EventManager()
self.data = { journal: [], _pendingCreation: {} } self.data = { _listen: true, _replay: false, journal: [], _pendingTxs: {}, _createdContracts: {}, _createdContractsReverse: {}, _usedAccounts: {}, _abis: {} }
opts.events.executioncontext.register('contextChanged', () => self.clearAll()) opts.events.executioncontext.register('contextChanged', () => {
var counter = 0 self.clearAll()
self._addressCache = {} })
function getAddresses (cb) {
self._api.getAccounts(function (err, accounts = []) {
if (err) console.error(err)
var addresses = accounts.reduce((addr, account) => {
if (!addr[account]) addr[account] = `account{${++counter}}`
return addr
}, self._addressCache)
cb(addresses)
})
}
function getCurrentContractName () {
var contractNames = document.querySelector(`[class^="contractNames"]`)
var contractName = contractNames.children[contractNames.selectedIndex].innerHTML
return contractName
}
opts.events.udapp.register('initiatingTransaction', (timestamp, tx) => { opts.events.udapp.register('initiatingTransaction', (timestamp, tx) => {
var { from, to, value, gas, data } = tx var { from, to, value, data, useCall } = tx
var record = { value, gas, data } var record = { value, data, useCall }
getAddresses(addresses => {
if (to) record.to = addresses[to] || (addresses[to] = self._addressCache[to] = `contract{${++counter}}`) this.data._pendingTxs[timestamp] = tx
else {
record.src = getCurrentContractName() // convert to and from to tokens
self.data._pendingCreation[timestamp] = record if (this.data._listen) {
if (!to) {
var selectedContract = self._api.getSelectedContract()
if (selectedContract) {
var abi = selectedContract.contract.object.abi
var sha3 = util.sha3_256(JSON.stringify(abi))
record.abi = sha3
self.data._abis[sha3] = abi
}
} else {
record.to = `created-contract{${this.data._createdContracts[to]}}`
} }
record.from = addresses[from] || (addresses[from] = self._addressCache[from] = `account{${++counter}}`)
self.append(timestamp, record) self._api.getAccounts((error, accounts) => {
}) if (error) return console.log(error)
record.from = `account{${accounts.indexOf(from)}}`
self.data._usedAccounts[record.from] = from
self.append(timestamp, record)
})
}
}) })
opts.events.udapp.register('transactionExecuted', (...args) => {
var err = args[0] opts.events.udapp.register('transactionExecuted', (error, from, to, data, call, txResult, timestamp) => {
if (err) console.error(err) if (error) return console.log(error)
var timestamp = args[6] var tx = this.data._pendingTxs[timestamp]
// update transaction which was pending with correct `to` address
var record = self.data._pendingCreation[timestamp] var address = executionContext.isVM() ? txResult.result.createdAddress : tx.result.contractAddress
delete self.data._pendingCreation[timestamp] if (!address) return // not a contract creation
if (!record) return address = '0x' + address.toString('hex')
var to = args[2] // save back created addresses for the convertion from tokens to real adresses
getAddresses(addresses => { this.data._createdContracts[address] = timestamp
if (to) { this.data._createdContractsReverse[timestamp] = address
delete record.src
record.to = addresses[to] || (addresses[to] = self._addressCache[to] = `account{${++counter}}`)
} else record.src = getCurrentContractName()
})
}) })
} }
resolveAddress (record, addresses) {
// var getPseudoAddress = placeholder => placeholder.split(' ')[0]//.split('-')[1].slice(1)
var pseudos = Object.keys(addresses).reduce((pseudos, address) => {
// var p = addresses[address]//getPseudoAddress()//.split('>')[0].split('-')[1].slice(1)
pseudos[addresses[address]] = address
return pseudos
}, {})
if (record.to && record.to[0] !== '0') record.to = pseudos[record.to]
if (record.from && record.from[0] !== '0') record.from = pseudos[record.from]
// @TODO: fix load transactions and execute ! /**
// @TODO: add 'clean' button to clear all recorded transactions * stop/start saving txs. If not listenning, is basically in replay mode
// @TODO: prefix path with `browser/` or `localhost/` if user provides *
// @TODO: offer users by default a "save path" prefixed with the currently open file in the editor * @param {Bool} listen
// @TODO: offer users by default a "load path" prefixed with the currently open file in the editor (show first one that comes) */
setListen (listen) {
this.data._listen = listen
this.data._replay = !listen
}
/**
* convert back from/to from tokens to real addresses
*
* @param {Object} record
* @param {Object} accounts
* @param {Object} options
*
*/
resolveAddress (record, accounts, options) {
if (record.to) {
var timestamp = /created-contract{(.*)}/g.exec(record.to)
record.to = this.data._createdContractsReverse[timestamp[1]]
}
record.from = accounts[record.from]
// @TODO: writing browser test // @TODO: writing browser test
return record return record
} }
/**
* save the given @arg record
*
* @param {Number/String} timestamp
* @param {Object} record
*
*/
append (timestamp, record) { append (timestamp, record) {
var self = this var self = this
self.data.journal.push({ timestamp, record }) self.data.journal.push({ timestamp, record })
} }
/**
* basically return the records + associate values (like abis / accounts)
*
*/
getAll () { getAll () {
var self = this var self = this
var records = [].concat(self.data.journal) var records = [].concat(self.data.journal)
return { return {
addresses: self._addressCache, accounts: self.data._usedAccounts,
options: {
useAccountList: true
},
transactions: records.sort((A, B) => { transactions: records.sort((A, B) => {
var stampA = A.timestamp var stampA = A.timestamp
var stampB = B.timestamp var stampB = B.timestamp
return stampA - stampB return stampA - stampB
}) }),
abis: self.data._abis
} }
} }
/**
* delete the seen transactions
*
*/
clearAll () { clearAll () {
var self = this var self = this
self.data.journal = [] self.data.journal = []
self.data._pendingTxs = {}
self.data._createdContracts = {}
self.data._createdContractsReverse = {}
self.data._usedAccounts = {}
self.data._abis = {}
}
/**
* run the list of records
*
* @param {Object} accounts
* @param {Object} options
* @param {Object} abis
* @param {Function} newContractFn
*
*/
run (records, accounts, options, abis, newContractFn) {
var self = this
self.setListen(false)
async.eachSeries(records, function (tx, cb) {
var record = self.resolveAddress(tx.record, accounts, options)
self._api.udapp().rerunTx(record, function (err, txResult) {
if (err) {
console.error(err)
} else {
var address = executionContext.isVM() ? txResult.result.createdAddress : tx.result.contractAddress
if (!address) return // not a contract creation
address = '0x' + address.toString('hex')
// save back created addresses for the convertion from tokens to real adresses
self.data._createdContracts[address] = tx.timestamp
self.data._createdContractsReverse[tx.timestamp] = address
var abi = abis[tx.record.abi]
if (abi) {
newContractFn(abi, address, record.contractName)
}
}
cb()
})
}, () => { self.setListen(true) })
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment