From d53ab3d77a5188652fe28d19d8fd3846b06c04c2 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 20 Sep 2017 10:40:26 +0200 Subject: [PATCH 01/74] add initiatingTransaction event && stamp to associate with executed --- src/universal-dapp.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 8194ca31fe..df04db87b1 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -534,11 +534,13 @@ UniversalDApp.prototype.runTx = function (args, cb) { }, // run transaction function (callback) { + var stamp = Date.now() + self.event.trigger('initiatingTransaction', [stamp, tx]) self.txRunner.rawRun(tx, function (error, result) { if (!args.useCall) { - self.event.trigger('transactionExecuted', [error, args.from, args.to, args.data, false, result]) + self.event.trigger('transactionExecuted', [error, args.from, args.to, args.data, false, result, stamp]) } else { - self.event.trigger('callExecuted', [error, args.from, args.to, args.data, true, result]) + self.event.trigger('callExecuted', [error, args.from, args.to, args.data, true, result, stamp]) } if (error) { if (typeof (error) !== 'string') { From 12dc8b7df022b794d360991b2644b7f74fd2c15e Mon Sep 17 00:00:00 2001 From: serapath Date: Wed, 20 Sep 2017 18:51:50 +0200 Subject: [PATCH 02/74] first concept draft recorder --- src/app.js | 47 ++++++++++++++++++++++++++++++++++ src/recorder.js | 67 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 src/recorder.js diff --git a/src/app.js b/src/app.js index ecce1e1980..7a9594779c 100644 --- a/src/app.js +++ b/src/app.js @@ -6,6 +6,8 @@ var yo = require('yo-yo') var remixLib = require('remix-lib') var EventManager = remixLib.EventManager +var Recorder = require('./recorder') + var UniversalDApp = require('./universal-dapp.js') var Remixd = require('./lib/remixd') var OffsetToLineColumnConverter = require('./lib/offsetToLineColumnConverter') @@ -772,4 +774,49 @@ function run () { self.event.trigger('debuggingRequested', []) transactionDebugger.debug(txHash) } + + /**************************************************************************** + RECORDER + ****************************************************************************/ + var modal = { show: function showModal (args) { console.log('@TODO: open modal') } } + + var recorder = new Recorder({ events: { + txlogger: txLogger.event, + executioncontext: executionContext.event + }}) + + var css = csjs` + .recorder { + background-color: red; + } + .runTxs { + background-color: green; + } + ` + var recordButton = yo`` + var runButton = yo`` + + console.error('@TODO: append record and run buttons') + + recordButton.onclick = () => { + var txJSON = JSON.stringify(recorder.getAll(), null, 2) + copy2clipboard(txJSON) + modal.show(txJSON) + } + runButton.onclick = () => { // on modal OR run tab + var txArray = recorder.getAll() + udapp.runTx(txArray, CALLBACK) // ??? + // OR + txArray.forEach(tx => udap.runTx(tx, CALLBACK)) // ??? + } + + function CALLBACK () { + /* + at each callback call, if the transaction succeed and if this is a creation transaction, we should call + runtab.addInstance( ... ) which basically do: + instanceContainer.appendChild(appAPI.udapp().renderInstance(contract, address, + selectContractNames.value)) + */ + } + function copy2clipboard (json) { console.log('@TODO: copy 2 clipboard') } } diff --git a/src/recorder.js b/src/recorder.js new file mode 100644 index 0000000000..65ea2f5d94 --- /dev/null +++ b/src/recorder.js @@ -0,0 +1,67 @@ +var csjs = require('csjs-inject') +var yo = require('yo-yo') +var remix = require('ethereum-remix') +var EventManager = remix.lib.EventManager + +class Recorder { + constructor (opts = {}) { + var self = this + self._api = opts.api + self.event = new EventManager() + self.data = { journal: [] } + opts.events.txlogger.register('initiatingTransaction', (stamp, tx) => { + var { from, to, value, gas, data /*, gasPrice?, nonce? */ } = tx + from = self.translate(from) // see comments above regarding what `translate(...)` is doing + to = self.translate(to) + var deTx = { from, to, value, gas, data /*, gasPrice?, nonce? */ } + self.append(stamp, deTx) + }) + opts.events.txlogger.register('transactionExecuted', args => { + var [err, from, to, data, isUserCall, result, stamp] = args + console.log('@TODO: should i do something here?') + }) + opts.events.txlogger.register('callExecuted', args => { + var [err, from, to, data, isUserCall, result, stamp] = args + console.log('@TODO: should i do something here?') + }) + opts.events.executioncontext.register('contextChanged', function () { + self.clearAll() + }) + } + translate (prop) { + /* what to apply this to and why and how does it work? + > Basically, transaction from / to tokens will be resolved as follow: + > if (start with 0x) do nothing (we already have a supposed address) + > if not : is resolved to the first account in the list of accounts given from universaldapp. + > is resolved to second first account in the list of accounts given from universaldapp. + > if the account list is not large enough, we take the last one. + + @TODO: what does it mean? (does that talk about the same as the next 4 lines of comments?) + + > Real addresses should be translated into token (apply to: to / from / return value of contract creation) + > e.g: from: 0x123...123 , to: 0x123...145 should be saved as: from:, to: + > e.g: from: 0x123...123, to: null (cause this is a contract creation), + > the return value is the address of the created contract. + */ + return prop + } + append (timestamp, record) { + var self = this + self.data.journal.push({ timestamp, record }) + } + getAllJSON () { + var self = this + var records = [].concat(self.data.journal) + return records.sort((A, B) => { + var stampA = A.timestamp + var stampB = B.timestamp + return stampA - stampB + }) + } + clearAll () { + var self = this + self.data.journal = [] + } +} + +module.exports = Recorder From a57e8c5b50f7fe1f25cabd25936f29201572847e Mon Sep 17 00:00:00 2001 From: serapath Date: Wed, 20 Sep 2017 19:08:20 +0200 Subject: [PATCH 03/74] second concept draft recorder --- src/app.js | 16 +++++++++++----- src/recorder.js | 12 ++++++------ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/app.js b/src/app.js index 7a9594779c..f1e0ccb6fc 100644 --- a/src/app.js +++ b/src/app.js @@ -778,10 +778,8 @@ function run () { /**************************************************************************** RECORDER ****************************************************************************/ - var modal = { show: function showModal (args) { console.log('@TODO: open modal') } } - var recorder = new Recorder({ events: { - txlogger: txLogger.event, + udapp: udapp.event, executioncontext: executionContext.event }}) @@ -801,7 +799,7 @@ function run () { recordButton.onclick = () => { var txJSON = JSON.stringify(recorder.getAll(), null, 2) copy2clipboard(txJSON) - modal.show(txJSON) + modalDialogCustom.alert(txJSON) } runButton.onclick = () => { // on modal OR run tab var txArray = recorder.getAll() @@ -818,5 +816,13 @@ function run () { selectContractNames.value)) */ } - function copy2clipboard (json) { console.log('@TODO: copy 2 clipboard') } + function copy2clipboard (json) { + var textarea = document.createElement('textarea') + textarea.textContent = json + document.body.appendChild(textarea) + textarea.select() + try { document.execCommand('copy') } + catch (e) { } + finally { document.body.removeChild(textarea) } + } } diff --git a/src/recorder.js b/src/recorder.js index 65ea2f5d94..d72824d517 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -10,17 +10,17 @@ class Recorder { self.event = new EventManager() self.data = { journal: [] } opts.events.txlogger.register('initiatingTransaction', (stamp, tx) => { - var { from, to, value, gas, data /*, gasPrice?, nonce? */ } = tx - from = self.translate(from) // see comments above regarding what `translate(...)` is doing + var { from, to, value, gas, data } = tx + from = self.translate(from) to = self.translate(to) - var deTx = { from, to, value, gas, data /*, gasPrice?, nonce? */ } + var deTx = { from, to, value, gas, data } self.append(stamp, deTx) }) - opts.events.txlogger.register('transactionExecuted', args => { + opts.events.udapp.register('transactionExecuted', args => { var [err, from, to, data, isUserCall, result, stamp] = args console.log('@TODO: should i do something here?') }) - opts.events.txlogger.register('callExecuted', args => { + opts.events.udapp.register('callExecuted', args => { var [err, from, to, data, isUserCall, result, stamp] = args console.log('@TODO: should i do something here?') }) @@ -49,7 +49,7 @@ class Recorder { var self = this self.data.journal.push({ timestamp, record }) } - getAllJSON () { + getAll () { var self = this var records = [].concat(self.data.journal) return records.sort((A, B) => { From f105674fd033d627d7a57014bcdf4fc8927056d5 Mon Sep 17 00:00:00 2001 From: serapath Date: Wed, 20 Sep 2017 19:55:59 +0200 Subject: [PATCH 04/74] third concept draft recorder --- src/app.js | 53 --------------------------------- src/app/tabs/run-tab.js | 57 ++++++++++++++++++++++++++++++++++++ src/recorder.js | 65 +++++++++++++++++++++-------------------- 3 files changed, 90 insertions(+), 85 deletions(-) diff --git a/src/app.js b/src/app.js index f1e0ccb6fc..ecce1e1980 100644 --- a/src/app.js +++ b/src/app.js @@ -6,8 +6,6 @@ var yo = require('yo-yo') var remixLib = require('remix-lib') var EventManager = remixLib.EventManager -var Recorder = require('./recorder') - var UniversalDApp = require('./universal-dapp.js') var Remixd = require('./lib/remixd') var OffsetToLineColumnConverter = require('./lib/offsetToLineColumnConverter') @@ -774,55 +772,4 @@ function run () { self.event.trigger('debuggingRequested', []) transactionDebugger.debug(txHash) } - - /**************************************************************************** - RECORDER - ****************************************************************************/ - var recorder = new Recorder({ events: { - udapp: udapp.event, - executioncontext: executionContext.event - }}) - - var css = csjs` - .recorder { - background-color: red; - } - .runTxs { - background-color: green; - } - ` - var recordButton = yo`` - var runButton = yo`` - - console.error('@TODO: append record and run buttons') - - recordButton.onclick = () => { - var txJSON = JSON.stringify(recorder.getAll(), null, 2) - copy2clipboard(txJSON) - modalDialogCustom.alert(txJSON) - } - runButton.onclick = () => { // on modal OR run tab - var txArray = recorder.getAll() - udapp.runTx(txArray, CALLBACK) // ??? - // OR - txArray.forEach(tx => udap.runTx(tx, CALLBACK)) // ??? - } - - function CALLBACK () { - /* - at each callback call, if the transaction succeed and if this is a creation transaction, we should call - runtab.addInstance( ... ) which basically do: - instanceContainer.appendChild(appAPI.udapp().renderInstance(contract, address, - selectContractNames.value)) - */ - } - function copy2clipboard (json) { - var textarea = document.createElement('textarea') - textarea.textContent = json - document.body.appendChild(textarea) - textarea.select() - try { document.execCommand('copy') } - catch (e) { } - finally { document.body.removeChild(textarea) } - } } diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index e1639fbfc1..a79892f3e1 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -8,6 +8,7 @@ var txHelper = require('../execution/txHelper') var modalDialogCustom = require('../ui/modal-dialog-custom') var executionContext = require('../../execution-context') var copyToClipboard = require('../ui/copy-to-clipboard') +var Recorder = require('../../recorder') // -------------- styling ---------------------- var csjs = require('csjs-inject') @@ -240,6 +241,59 @@ function updateAccountBalances (container, appAPI) { }) } +/* ------------------------------------------------ + RECORDER +------------------------------------------------ */ +function makeRecorder (appAPI, appEvents) { + var udapp = appAPI.udapp() + var recorder = new Recorder({ events: { + udapp: appEvents.udapp, + executioncontext: executionContext.event + }}) + var css = csjs` + .container { + margin-top: 10px; + display: flex; + } + .recorder { + ${styles.button} + width: 135px; + } + .runTxs { + ${styles.button} + margin-left: 10px; + width: 135px; + } + ` + var recordButton = yo`` + var runButton = yo`` + var el = yo` +
+ ${recordButton} + ${runButton} +
+ ` + recordButton.onclick = () => { + var txJSON = JSON.stringify(recorder.getAll(), null, 2) + copy(txJSON) + modalDialogCustom.alert(txJSON) + } + runButton.onclick = () => { // on modal OR run tab + var txArray = recorder.getAll() + udapp.runTx(txArray, CALLBACK) // ??? + // OR + // txArray.forEach(tx => udapp.runTx(tx, CALLBACK)) // ??? + } + function CALLBACK () { + /* + at each callback call, if the transaction succeed and if this is a creation transaction, we should call + runtab.addInstance( ... ) which basically do: + instanceContainer.appendChild(appAPI.udapp().renderInstance(contract, address, + selectContractNames.value)) + */ + } + return el +} /* ------------------------------------------------ section CONTRACT DROPDOWN and BUTTONS ------------------------------------------------ */ @@ -429,6 +483,9 @@ function settings (appAPI, appEvents) { +
+ ${makeRecorder(appAPI, appEvents)} +
` diff --git a/src/recorder.js b/src/recorder.js index d72824d517..8897d26fd5 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -1,5 +1,3 @@ -var csjs = require('csjs-inject') -var yo = require('yo-yo') var remix = require('ethereum-remix') var EventManager = remix.lib.EventManager @@ -8,42 +6,45 @@ class Recorder { var self = this self._api = opts.api self.event = new EventManager() - self.data = { journal: [] } - opts.events.txlogger.register('initiatingTransaction', (stamp, tx) => { + self.data = { journal: [], _pending: {} } + opts.events.executioncontext.register('contextChanged', function () { + self.clearAll() + }) + opts.events.udapp.register('initiatingTransaction', (stamp, tx) => { var { from, to, value, gas, data } = tx - from = self.translate(from) - to = self.translate(to) - var deTx = { from, to, value, gas, data } - self.append(stamp, deTx) + var deTx = { from, to, value, gas, data, pending: true } + self.data._pending[stamp] = deTx }) opts.events.udapp.register('transactionExecuted', args => { - var [err, from, to, data, isUserCall, result, stamp] = args - console.log('@TODO: should i do something here?') + var [err, from, to, data, /* isUserCall, result, */ stamp] = args + if (err) console.error(err) + else update(stamp, from, to, data) }) opts.events.udapp.register('callExecuted', args => { - var [err, from, to, data, isUserCall, result, stamp] = args - console.log('@TODO: should i do something here?') + var [err, from, to, data, /* isUserCall, result, */ stamp] = args + if (err) console.error(err) + else update(stamp, from, to, data) }) - opts.events.executioncontext.register('contextChanged', function () { - self.clearAll() - }) - } - translate (prop) { - /* what to apply this to and why and how does it work? - > Basically, transaction from / to tokens will be resolved as follow: - > if (start with 0x) do nothing (we already have a supposed address) - > if not : is resolved to the first account in the list of accounts given from universaldapp. - > is resolved to second first account in the list of accounts given from universaldapp. - > if the account list is not large enough, we take the last one. - - @TODO: what does it mean? (does that talk about the same as the next 4 lines of comments?) - - > Real addresses should be translated into token (apply to: to / from / return value of contract creation) - > e.g: from: 0x123...123 , to: 0x123...145 should be saved as: from:, to: - > e.g: from: 0x123...123, to: null (cause this is a contract creation), - > the return value is the address of the created contract. - */ - return prop + function update (stamp, from, to, data) { + var record = self._pending[stamp] + delete self._pending[stamp] + if (!record) return + // at this point you have a map 0x123789 <=> < contractName - 1> + // if a from` is 0x123789 you ill replace it by < contractName - 1> + // > if (start with 0x) do nothing (we already have a supposed address) + // > if not : is resolved to the first account in the list of accounts given from universaldapp. + // > is resolved to second first account in the list of accounts given from universaldapp. + // > if the account list is not large enough, we take the last one. + // > Real addresses should be translated into token (apply to: to / from / return value of contract creation) + // > e.g: from: 0x123...123 , to: 0x123...145 should be saved as: from:, to: + // > e.g: from: 0x123...123, to: null (cause this is a contract creation), + // > the return value is the address of the created contract. + console.log('@TODO: probably the below translation need to be adapted to the comments above') + record.from = from + record.to = to + record.data = data + self.append(stamp, record) + } } append (timestamp, record) { var self = this From 93839b3ce6057a6b3ade1b142819d88a924a4eac Mon Sep 17 00:00:00 2001 From: serapath Date: Tue, 26 Sep 2017 22:01:05 +0200 Subject: [PATCH 05/74] ADD recorder --- src/recorder.js | 39 ++++++++++++--------------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/src/recorder.js b/src/recorder.js index 8897d26fd5..5506309c89 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -10,41 +10,26 @@ class Recorder { opts.events.executioncontext.register('contextChanged', function () { self.clearAll() }) + var counter = 1 + self._addressCache = {} opts.events.udapp.register('initiatingTransaction', (stamp, tx) => { var { from, to, value, gas, data } = tx - var deTx = { from, to, value, gas, data, pending: true } - self.data._pending[stamp] = deTx + var record = { value, gas, data } + record.from = self._addressCache[from] || (self._addressCache[from] = ``) + if (to === null) self.data._pending[stamp] = record + else record.to = self._addressCache[to] || (self._addressCache[to] = ``) + self.append(stamp, record) }) opts.events.udapp.register('transactionExecuted', args => { - var [err, from, to, data, /* isUserCall, result, */ stamp] = args - if (err) console.error(err) - else update(stamp, from, to, data) - }) - opts.events.udapp.register('callExecuted', args => { - var [err, from, to, data, /* isUserCall, result, */ stamp] = args + var err = args[0] if (err) console.error(err) - else update(stamp, from, to, data) - }) - function update (stamp, from, to, data) { + var stamp = args[6] var record = self._pending[stamp] delete self._pending[stamp] if (!record) return - // at this point you have a map 0x123789 <=> < contractName - 1> - // if a from` is 0x123789 you ill replace it by < contractName - 1> - // > if (start with 0x) do nothing (we already have a supposed address) - // > if not : is resolved to the first account in the list of accounts given from universaldapp. - // > is resolved to second first account in the list of accounts given from universaldapp. - // > if the account list is not large enough, we take the last one. - // > Real addresses should be translated into token (apply to: to / from / return value of contract creation) - // > e.g: from: 0x123...123 , to: 0x123...145 should be saved as: from:, to: - // > e.g: from: 0x123...123, to: null (cause this is a contract creation), - // > the return value is the address of the created contract. - console.log('@TODO: probably the below translation need to be adapted to the comments above') - record.from = from - record.to = to - record.data = data - self.append(stamp, record) - } + var to = args[2] + record.to = self._addressCache[to] || (self._addressCache[to] = ``) + }) } append (timestamp, record) { var self = this From 1be36668df3e833debb826c9518e554317c3b2cd Mon Sep 17 00:00:00 2001 From: serapath Date: Wed, 4 Oct 2017 16:15:37 +0200 Subject: [PATCH 06/74] REFACTOR names & FIX argument bugs --- src/recorder.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/recorder.js b/src/recorder.js index 5506309c89..141ecac45a 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -6,26 +6,27 @@ class Recorder { var self = this self._api = opts.api self.event = new EventManager() - self.data = { journal: [], _pending: {} } + self.data = { journal: [], _pendingCreation: {} } opts.events.executioncontext.register('contextChanged', function () { self.clearAll() }) - var counter = 1 + var counter = 0 self._addressCache = {} - opts.events.udapp.register('initiatingTransaction', (stamp, tx) => { + opts.events.udapp.register('initiatingTransaction', (timestamp, tx) => { var { from, to, value, gas, data } = tx var record = { value, gas, data } record.from = self._addressCache[from] || (self._addressCache[from] = ``) - if (to === null) self.data._pending[stamp] = record + if (to === null) self.data._pendingCreation[timestamp] = record else record.to = self._addressCache[to] || (self._addressCache[to] = ``) - self.append(stamp, record) + self.append(timestamp, record) }) - opts.events.udapp.register('transactionExecuted', args => { + opts.events.udapp.register('transactionExecuted', (...args) => { var err = args[0] if (err) console.error(err) - var stamp = args[6] - var record = self._pending[stamp] - delete self._pending[stamp] + var timestamp = args[6] + // update transaction which was pending with correct `to` address + var record = self._pendingCreation[timestamp] + delete self._pendingCreation[timestamp] if (!record) return var to = args[2] record.to = self._addressCache[to] || (self._addressCache[to] = ``) From 15ba7ad326ae873c50925957e26992a5a1c55f6b Mon Sep 17 00:00:00 2001 From: serapath Date: Wed, 4 Oct 2017 16:19:17 +0200 Subject: [PATCH 07/74] FIX naming --- src/universal-dapp.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index df04db87b1..d97546381a 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -534,13 +534,13 @@ UniversalDApp.prototype.runTx = function (args, cb) { }, // run transaction function (callback) { - var stamp = Date.now() - self.event.trigger('initiatingTransaction', [stamp, tx]) + var timestamp = Date.now() + self.event.trigger('initiatingTransaction', [timestamp, tx]) self.txRunner.rawRun(tx, function (error, result) { if (!args.useCall) { - self.event.trigger('transactionExecuted', [error, args.from, args.to, args.data, false, result, stamp]) + self.event.trigger('transactionExecuted', [error, args.from, args.to, args.data, false, result, timestamp]) } else { - self.event.trigger('callExecuted', [error, args.from, args.to, args.data, true, result, stamp]) + self.event.trigger('callExecuted', [error, args.from, args.to, args.data, true, result, timestamp]) } if (error) { if (typeof (error) !== 'string') { From ccc5c540adaa524767d0bc729eb87710540d09b5 Mon Sep 17 00:00:00 2001 From: serapath Date: Sun, 8 Oct 2017 21:56:06 +0200 Subject: [PATCH 08/74] ADD modal popup to execute transactions --- src/app/tabs/run-tab.js | 30 ++++++++++++++++++++---------- src/app/ui/modal-dialog-custom.js | 9 ++++++--- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index a79892f3e1..6a12ec3e18 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -278,18 +278,28 @@ function makeRecorder (appAPI, appEvents) { copy(txJSON) modalDialogCustom.alert(txJSON) } - runButton.onclick = () => { // on modal OR run tab - var txArray = recorder.getAll() - udapp.runTx(txArray, CALLBACK) // ??? - // OR - // txArray.forEach(tx => udapp.runTx(tx, CALLBACK)) // ??? + runButton.onclick = () => { + var opts = { title: `Enter Transactions`, text: `Paste the array of transaction you want to replay here`, inputValue: '', multiline: true } + modalDialogCustom.prompt(opts, function confirm (json = '[]') { + try { + var txArray = JSON.parse(json) + } catch (e) { + modalDialogCustom.alert('Invalid JSON, please try again') + } + if (txArray.length) { + txArray.forEach(tx => udapp.runTx(tx, CALLBACK)) + } + }, function cancel () { }) } - function CALLBACK () { + function CALLBACK (...args) { + console.log(args) /* - at each callback call, if the transaction succeed and if this is a creation transaction, we should call - runtab.addInstance( ... ) which basically do: - instanceContainer.appendChild(appAPI.udapp().renderInstance(contract, address, - selectContractNames.value)) + at each callback call, if the transaction succeed and if this is a creation transaction, + we should call + + runtab.addInstance( ... ) // which basically do: + + instanceContainer.appendChild(appAPI.udapp().renderInstance(contract, address, selectContractNames.value)) */ } return el diff --git a/src/app/ui/modal-dialog-custom.js b/src/app/ui/modal-dialog-custom.js index 27523552e2..f8fa36ee42 100644 --- a/src/app/ui/modal-dialog-custom.js +++ b/src/app/ui/modal-dialog-custom.js @@ -11,10 +11,13 @@ module.exports = { alert: function (text) { modal('', yo`
${text}
`, null, { label: null }) }, - prompt: function (title, text, inputValue, ok, cancel) { + prompt: function ({ title, text, inputValue, multiline }, ok, cancel) { if (!inputValue) inputValue = '' - modal(title, - yo`
${text}
`, + var input = multiline + ? yo`` + : yo`` + + modal(title, yo`
${text}
${input}
`, { fn: () => { if (typeof ok === 'function') ok(document.getElementById('prompt_text').value) } }, From a92e6106f9b4dab62cddf91ebc02ee273790b010 Mon Sep 17 00:00:00 2001 From: serapath Date: Fri, 13 Oct 2017 03:46:07 +0200 Subject: [PATCH 09/74] FIX replay transactions --- src/universal-dapp.js | 86 ++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 33 deletions(-) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index d97546381a..4929e2f104 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -458,82 +458,103 @@ UniversalDApp.prototype.pendingTransactions = function () { return this.txRunner.pendingTxs } +function execute (pipeline, env, callback) { + function next (err, env) { + if (err) return callback(err) + var step = pipeline.shift() + if (step) step(env, next) + else callback(null, env.result) + } + next(null, env) +} + +UniversalDApp.prototype.replayTx = function (args, cb) { + var self = this + var tx = { to: args.to, data: args.data, useCall: args.useCall } + var pipeline = [runTransaction] + var env = { self, args, tx } + execute(pipeline, env, cb) +} UniversalDApp.prototype.runTx = function (args, cb) { var self = this - var tx = { - to: args.to, - data: args.data, - useCall: args.useCall - } - async.waterfall([ - // query gas limit - function (callback) { + var tx = { to: args.to, data: args.data, useCall: args.useCall } + var pipeline = [queryGasLimit, queryValue, queryAddress, runTransaction] + var env = { self, args, tx } + execute(pipeline, env, cb) +} + + + function queryGasLimit (env, next) { + var { self, args, tx } = env tx.gasLimit = 3000000 if (self.transactionContextAPI.getGasLimit) { self.transactionContextAPI.getGasLimit(function (err, ret) { if (err) { - return callback(err) + return next(err) } tx.gasLimit = ret - callback() + next(null, env) }) } else { - callback() + next(null, env) } - }, - // query value - function (callback) { + } + + function queryGasLimit (env, next) { + var { self, args, tx } = env tx.value = 0 - if (tx.useCall) return callback() + if (tx.useCall) return next(null, env) if (self.transactionContextAPI.getValue) { self.transactionContextAPI.getValue(function (err, ret) { if (err) { - return callback(err) + return next(err) } tx.value = ret - callback() + next(null, env) }) } else { - callback() + next(null, env) } - }, - // query address - function (callback) { + } + + function queryAddress (env, next) { + var { self, args, tx } = env if (self.transactionContextAPI.getAddress) { self.transactionContextAPI.getAddress(function (err, ret) { if (err) { - return callback(err) + return next(err) } tx.from = ret - callback() + next(null, env) }) } else { self.getAccounts(function (err, ret) { if (err) { - return callback(err) + return next(err) } if (ret.length === 0) { - return callback('No accounts available') + return next('No accounts available') } if (executionContext.isVM() && !self.accounts[ret[0]]) { - return callback('Invalid account selected') + return next('Invalid account selected') } tx.from = ret[0] - callback() + next(null, env) }) } - }, - // run transaction - function (callback) { + } + + function runTransaction (env, next) { + var { self, args, tx } = env var timestamp = Date.now() self.event.trigger('initiatingTransaction', [timestamp, tx]) self.txRunner.rawRun(tx, function (error, result) { @@ -553,10 +574,9 @@ UniversalDApp.prototype.runTx = function (args, cb) { } } } - callback(error, result) + env.result = result + next(error, env) }) } - ], cb) -} module.exports = UniversalDApp From d5a835095c342a184495891ea43269f093f64d22 Mon Sep 17 00:00:00 2001 From: serapath Date: Fri, 13 Oct 2017 03:50:05 +0200 Subject: [PATCH 10/74] FIX code formatting --- src/universal-dapp.js | 151 +++++++++++++++++------------------------- 1 file changed, 62 insertions(+), 89 deletions(-) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 4929e2f104..c20e7e0063 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -7,7 +7,6 @@ var BN = ethJSUtil.BN var remixLib = require('remix-lib') var EventManager = remixLib.EventManager var crypto = require('crypto') -var async = require('async') var TxRunner = require('./app/execution/txRunner') var yo = require('yo-yo') var txFormat = require('./app/execution/txFormat') @@ -475,6 +474,7 @@ UniversalDApp.prototype.replayTx = function (args, cb) { var env = { self, args, tx } execute(pipeline, env, cb) } + UniversalDApp.prototype.runTx = function (args, cb) { var self = this var tx = { to: args.to, data: args.data, useCall: args.useCall } @@ -483,100 +483,73 @@ UniversalDApp.prototype.runTx = function (args, cb) { execute(pipeline, env, cb) } +function queryGasLimit (env, next) { + var { self, tx } = env + tx.gasLimit = 3000000 + if (self.transactionContextAPI.getGasLimit) { + self.transactionContextAPI.getGasLimit(function (err, ret) { + if (err) return next(err) + tx.gasLimit = ret + next(null, env) + }) + } else next(null, env) +} - function queryGasLimit (env, next) { - var { self, args, tx } = env - tx.gasLimit = 3000000 - - if (self.transactionContextAPI.getGasLimit) { - self.transactionContextAPI.getGasLimit(function (err, ret) { - if (err) { - return next(err) - } - - tx.gasLimit = ret - next(null, env) - }) - } else { - next(null, env) - } - } - - function queryGasLimit (env, next) { - var { self, args, tx } = env - tx.value = 0 - if (tx.useCall) return next(null, env) - if (self.transactionContextAPI.getValue) { - self.transactionContextAPI.getValue(function (err, ret) { - if (err) { - return next(err) - } +function queryValue (env, next) { + var { self, tx } = env + tx.value = 0 + if (tx.useCall) return next(null, env) + if (self.transactionContextAPI.getValue) { + self.transactionContextAPI.getValue(function (err, ret) { + if (err) return next(err) + tx.value = ret + next(null, env) + }) + } else next(null, env) +} - tx.value = ret - next(null, env) - }) - } else { - next(null, env) +function queryAddress (env, next) { + var { self, tx } = env + if (self.transactionContextAPI.getAddress) { + self.transactionContextAPI.getAddress(function (err, ret) { + if (err) return next(err) + tx.from = ret + next(null, env) + }) + } else { + self.getAccounts(function (err, ret) { + if (err) return next(err) + if (ret.length === 0) return next('No accounts available') + if (executionContext.isVM() && !self.accounts[ret[0]]) { + return next('Invalid account selected') } - } - - function queryAddress (env, next) { - var { self, args, tx } = env - if (self.transactionContextAPI.getAddress) { - self.transactionContextAPI.getAddress(function (err, ret) { - if (err) { - return next(err) - } - - tx.from = ret - - next(null, env) - }) - } else { - self.getAccounts(function (err, ret) { - if (err) { - return next(err) - } - - if (ret.length === 0) { - return next('No accounts available') - } - - if (executionContext.isVM() && !self.accounts[ret[0]]) { - return next('Invalid account selected') - } - - tx.from = ret[0] + tx.from = ret[0] + next(null, env) + }) + } +} - next(null, env) - }) - } +function runTransaction (env, next) { + var { self, args, tx } = env + var timestamp = Date.now() + self.event.trigger('initiatingTransaction', [timestamp, tx]) + self.txRunner.rawRun(tx, function (error, result) { + if (!args.useCall) { + self.event.trigger('transactionExecuted', [error, args.from, args.to, args.data, false, result, timestamp]) + } else { + self.event.trigger('callExecuted', [error, args.from, args.to, args.data, true, result, timestamp]) } - - function runTransaction (env, next) { - var { self, args, tx } = env - var timestamp = Date.now() - self.event.trigger('initiatingTransaction', [timestamp, tx]) - self.txRunner.rawRun(tx, function (error, result) { - if (!args.useCall) { - self.event.trigger('transactionExecuted', [error, args.from, args.to, args.data, false, result, timestamp]) - } else { - self.event.trigger('callExecuted', [error, args.from, args.to, args.data, true, result, timestamp]) + if (error) { + if (typeof (error) !== 'string') { + if (error.message) error = error.message + else { + try { error = 'error: ' + JSON.stringify(error) } catch (e) {} } - if (error) { - if (typeof (error) !== 'string') { - if (error.message) { - error = error.message - } else { - try { - error = 'error: ' + JSON.stringify(error) - } catch (e) {} - } - } - } - env.result = result - next(error, env) - }) + } } + env.result = result + next(error, env) + }) +} module.exports = UniversalDApp From 0e0b445e236bc2cc7808233699bb785587a06b24 Mon Sep 17 00:00:00 2001 From: serapath Date: Fri, 13 Oct 2017 17:40:48 +0200 Subject: [PATCH 11/74] FIX runTx to replayTx & FIX arguments --- src/app/tabs/run-tab.js | 2 +- src/recorder.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 6a12ec3e18..b648323115 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -287,7 +287,7 @@ function makeRecorder (appAPI, appEvents) { modalDialogCustom.alert('Invalid JSON, please try again') } if (txArray.length) { - txArray.forEach(tx => udapp.runTx(tx, CALLBACK)) + txArray.forEach(tx => udapp.replayTx(tx.record, CALLBACK)) } }, function cancel () { }) } diff --git a/src/recorder.js b/src/recorder.js index 141ecac45a..1be3eff71f 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -25,8 +25,8 @@ class Recorder { if (err) console.error(err) var timestamp = args[6] // update transaction which was pending with correct `to` address - var record = self._pendingCreation[timestamp] - delete self._pendingCreation[timestamp] + var record = self.data._pendingCreation[timestamp] + delete self.data._pendingCreation[timestamp] if (!record) return var to = args[2] record.to = self._addressCache[to] || (self._addressCache[to] = ``) From 48f63f3751895e43dd179dcbeb70db806b1ef1ee Mon Sep 17 00:00:00 2001 From: serapath Date: Fri, 13 Oct 2017 20:59:34 +0200 Subject: [PATCH 12/74] UPDATE acount placeholder counter to use address array index --- src/app/tabs/run-tab.js | 13 +++++++++---- src/recorder.js | 17 +++++++++++++---- src/universal-dapp.js | 12 ++++++++---- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index b648323115..dbdda17f90 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -246,10 +246,15 @@ function updateAccountBalances (container, appAPI) { ------------------------------------------------ */ function makeRecorder (appAPI, appEvents) { var udapp = appAPI.udapp() - var recorder = new Recorder({ events: { - udapp: appEvents.udapp, - executioncontext: executionContext.event - }}) + var recorder = new Recorder({ + events: { + udapp: appEvents.udapp, + executioncontext: executionContext.event + }, + api: { + getAccounts (cb) { udapp.getAccounts(cb) } + } + }) var css = csjs` .container { margin-top: 10px; diff --git a/src/recorder.js b/src/recorder.js index 1be3eff71f..13bc87d5d9 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -11,14 +11,23 @@ class Recorder { self.clearAll() }) var counter = 0 + function getIndex (accounts, address) { + var index + accounts.forEach((addr, idx) => { if (address === addr) index = idx }) + if (!index) index = (++counter) + return index + } self._addressCache = {} opts.events.udapp.register('initiatingTransaction', (timestamp, tx) => { var { from, to, value, gas, data } = tx var record = { value, gas, data } - record.from = self._addressCache[from] || (self._addressCache[from] = ``) - if (to === null) self.data._pendingCreation[timestamp] = record - else record.to = self._addressCache[to] || (self._addressCache[to] = ``) - self.append(timestamp, record) + self._api.getAccounts(function (err, accounts = []) { + if (err) console.error(err) + record.from = self._addressCache[from] || (self._addressCache[from] = ``) + if (to === null) self.data._pendingCreation[timestamp] = record + else record.to = self._addressCache[to] || (self._addressCache[to] = ``) + self.append(timestamp, record) + }) }) opts.events.udapp.register('transactionExecuted', (...args) => { var err = args[0] diff --git a/src/universal-dapp.js b/src/universal-dapp.js index c20e7e0063..a00dfd1dc8 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -469,10 +469,14 @@ function execute (pipeline, env, callback) { UniversalDApp.prototype.replayTx = function (args, cb) { var self = this - var tx = { to: args.to, data: args.data, useCall: args.useCall } - var pipeline = [runTransaction] - var env = { self, args, tx } - execute(pipeline, env, cb) + self.getAccounts(function (err, accounts = []) { + if (err) console.error(err) + if (args.to[0] === '<') args.to = accounts[args.to.split('>')[0].slice(11)] + if (args.from && args.from[0] === '<') args.from = accounts[args.from.split('>')[0].slice(11)] + var pipeline = [runTransaction] + var env = { self, args, tx: { to: args.to, from: args.from, data: args.data, useCall: args.useCall } } + execute(pipeline, env, cb) + }) } UniversalDApp.prototype.runTx = function (args, cb) { From 2f354d3a1365262da70f0ae30e98f9d5f0255fae Mon Sep 17 00:00:00 2001 From: serapath Date: Wed, 18 Oct 2017 17:23:09 +0200 Subject: [PATCH 13/74] FIX recorder bugs --- src/app/tabs/run-tab.js | 28 +++++++++++++++++++--------- src/recorder.js | 9 ++++++--- src/universal-dapp.js | 4 ++-- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index dbdda17f90..ddc50177c1 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -296,16 +296,26 @@ function makeRecorder (appAPI, appEvents) { } }, function cancel () { }) } - function CALLBACK (...args) { - console.log(args) - /* - at each callback call, if the transaction succeed and if this is a creation transaction, - we should call + function CALLBACK (err, result) { + if (err) console.error(err) + else { + console.log(result) + // { + // "result": { + // "gasUsed": "5318", + // "vm": { "exception": 1, "selfdestruct": {} }, + // "bloom": { "bitvector": { "type": "Buffer", "data": [0, /* ... */ 0, 0] } }, + // "amountSpent": "5318" + // }, + // "transactionHash": "0x84f68f96944a47b27af4b4ed1986637aa1bc05fd7a6f5cb1d6a53f68058276d8" + // } + /* + at each callback call, if the transaction succeed and if this is a creation transaction, we should call - runtab.addInstance( ... ) // which basically do: - - instanceContainer.appendChild(appAPI.udapp().renderInstance(contract, address, selectContractNames.value)) - */ + runtab.addInstance( ... ) // which basically does: + instanceContainer.appendChild(appAPI.udapp().renderInstance(contract, address, selectContractNames.value)) + */ + } } return el } diff --git a/src/recorder.js b/src/recorder.js index 13bc87d5d9..07e4a24dc4 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -24,8 +24,8 @@ class Recorder { self._api.getAccounts(function (err, accounts = []) { if (err) console.error(err) record.from = self._addressCache[from] || (self._addressCache[from] = ``) - if (to === null) self.data._pendingCreation[timestamp] = record - else record.to = self._addressCache[to] || (self._addressCache[to] = ``) + if (to) record.to = self._addressCache[to] || (self._addressCache[to] = ``) + else self.data._pendingCreation[timestamp] = record self.append(timestamp, record) }) }) @@ -38,7 +38,10 @@ class Recorder { delete self.data._pendingCreation[timestamp] if (!record) return var to = args[2] - record.to = self._addressCache[to] || (self._addressCache[to] = ``) + self._api.getAccounts(function (err, accounts = []) { + if (err) console.error(err) + if (to) record.to = self._addressCache[to] || (self._addressCache[to] = ``) + }) }) } append (timestamp, record) { diff --git a/src/universal-dapp.js b/src/universal-dapp.js index a00dfd1dc8..eb6a9a1860 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -471,9 +471,9 @@ UniversalDApp.prototype.replayTx = function (args, cb) { var self = this self.getAccounts(function (err, accounts = []) { if (err) console.error(err) - if (args.to[0] === '<') args.to = accounts[args.to.split('>')[0].slice(11)] + if (args.to && args.to[0] === '<') args.to = accounts[args.to.split('>')[0].slice(11)] if (args.from && args.from[0] === '<') args.from = accounts[args.from.split('>')[0].slice(11)] - var pipeline = [runTransaction] + var pipeline = [queryGasLimit, runTransaction] var env = { self, args, tx: { to: args.to, from: args.from, data: args.data, useCall: args.useCall } } execute(pipeline, env, cb) }) From a648c6c00421eb8327eacaf23dae89ebc0c00f7a Mon Sep 17 00:00:00 2001 From: serapath Date: Thu, 19 Oct 2017 13:04:08 +0200 Subject: [PATCH 14/74] FIX custom modal multiline prompt --- src/app/tabs/run-tab.js | 2 +- src/app/ui/modal-dialog-custom.js | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index ddc50177c1..905751a24f 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -285,7 +285,7 @@ function makeRecorder (appAPI, appEvents) { } runButton.onclick = () => { var opts = { title: `Enter Transactions`, text: `Paste the array of transaction you want to replay here`, inputValue: '', multiline: true } - modalDialogCustom.prompt(opts, function confirm (json = '[]') { + modalDialogCustom.promptMulti(opts, function confirm (json = '[]') { try { var txArray = JSON.parse(json) } catch (e) { diff --git a/src/app/ui/modal-dialog-custom.js b/src/app/ui/modal-dialog-custom.js index f8fa36ee42..07d47e3f12 100644 --- a/src/app/ui/modal-dialog-custom.js +++ b/src/app/ui/modal-dialog-custom.js @@ -11,12 +11,21 @@ module.exports = { alert: function (text) { modal('', yo`
${text}
`, null, { label: null }) }, - prompt: function ({ title, text, inputValue, multiline }, ok, cancel) { + prompt: function (title, text, inputValue, ok, cancel) { if (!inputValue) inputValue = '' - var input = multiline - ? yo`` - : yo`` - + var input = yo`` + modal(title, yo`
${text}
${input}
`, + { + fn: () => { if (typeof ok === 'function') ok(document.getElementById('prompt_text').value) } + }, + { + fn: () => { if (typeof cancel === 'function') cancel() } + } + ) + }, + promptMulti: function ({ title, text, inputValue }, ok, cancel) { + if (!inputValue) inputValue = '' + var input =yo`` modal(title, yo`
${text}
${input}
`, { fn: () => { if (typeof ok === 'function') ok(document.getElementById('prompt_text').value) } From 30d17b92d71b20ddd4f72ccfa2d9debf0e6f216b Mon Sep 17 00:00:00 2001 From: serapath Date: Sat, 28 Oct 2017 02:26:06 +0200 Subject: [PATCH 15/74] ADD .addInstance after rerunTx --- src/app/tabs/run-tab.js | 57 ++++++++++++++++++------------- src/app/ui/modal-dialog-custom.js | 2 +- src/universal-dapp.js | 2 +- 3 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 905751a24f..3cfaf86edd 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -177,19 +177,42 @@ var pendingTxsText = yo`
` var pendingTxsContainer = yo`
${pendingTxsText}
` function runTab (container, appAPI, appEvents, opts) { + var self = { + _view: {}, + addInstance: addInstance + } + var udapp = appAPI.udapp() var el = yo`
- ${settings(appAPI, appEvents)} - ${contractDropdown(appAPI, appEvents, instanceContainer)} + ${settings(self, appAPI, appEvents)} + ${contractDropdown(self, appAPI, appEvents, instanceContainer)} ${pendingTxsContainer} ${instanceContainer}
` container.appendChild(el) + function addInstance (result) { + // { + // "result": { + // "gasUsed": "5318", + // "vm": { "exception": 1, "selfdestruct": {} }, + // "bloom": { "bitvector": { "type": "Buffer", "data": [0, /* ... */ 0, 0] } }, + // "amountSpent": "5318" + // }, + // "transactionHash": "0x84f68f96944a47b27af4b4ed1986637aa1bc05fd7a6f5cb1d6a53f68058276d8" + // } + console.log(result) + var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`) + var contract = appAPI.getContracts()[contractNames.children[contractNames.selectedIndex].innerHTML] + var address = self._view.atAddressButtonInput.value + var instance = udapp.renderInstance(contract, address, self._view.selectContractNames.value) + instanceContainer.appendChild(instance) + } + // PENDING transactions function updatePendingTxs (container, appAPI) { - var pendingCount = Object.keys(appAPI.udapp().pendingTransactions()).length + var pendingCount = Object.keys(udapp.pendingTransactions()).length pendingTxsText.innerText = pendingCount + ' pending transactions' } @@ -244,7 +267,7 @@ function updateAccountBalances (container, appAPI) { /* ------------------------------------------------ RECORDER ------------------------------------------------ */ -function makeRecorder (appAPI, appEvents) { +function makeRecorder (self, appAPI, appEvents) { var udapp = appAPI.udapp() var recorder = new Recorder({ events: { @@ -292,29 +315,15 @@ function makeRecorder (appAPI, appEvents) { modalDialogCustom.alert('Invalid JSON, please try again') } if (txArray.length) { - txArray.forEach(tx => udapp.replayTx(tx.record, CALLBACK)) + txArray.forEach(tx => udapp.rerunTx(tx.record, CALLBACK)) } }, function cancel () { }) } function CALLBACK (err, result) { if (err) console.error(err) else { - console.log(result) - // { - // "result": { - // "gasUsed": "5318", - // "vm": { "exception": 1, "selfdestruct": {} }, - // "bloom": { "bitvector": { "type": "Buffer", "data": [0, /* ... */ 0, 0] } }, - // "amountSpent": "5318" - // }, - // "transactionHash": "0x84f68f96944a47b27af4b4ed1986637aa1bc05fd7a6f5cb1d6a53f68058276d8" - // } - /* - at each callback call, if the transaction succeed and if this is a creation transaction, we should call - - runtab.addInstance( ... ) // which basically does: - instanceContainer.appendChild(appAPI.udapp().renderInstance(contract, address, selectContractNames.value)) - */ + // at each callback call, if the transaction succeed and if this is a creation transaction, we should call + self.addInstance(result) } } return el @@ -323,7 +332,7 @@ function makeRecorder (appAPI, appEvents) { section CONTRACT DROPDOWN and BUTTONS ------------------------------------------------ */ -function contractDropdown (appAPI, appEvents, instanceContainer) { +function contractDropdown (self, appAPI, appEvents, instanceContainer) { instanceContainer.appendChild(noInstancesText) var compFails = yo`` appEvents.compiler.register('compilationFinished', function (success, data, source) { @@ -338,6 +347,8 @@ function contractDropdown (appAPI, appEvents, instanceContainer) { var atAddressButtonInput = yo`` var createButtonInput = yo`` var selectContractNames = yo`` + self._view.atAddressButtonInput = atAddressButtonInput + self._view.selectContractNames = selectContractNames var el = yo`
@@ -509,7 +520,7 @@ function settings (appAPI, appEvents) {
- ${makeRecorder(appAPI, appEvents)} + ${makeRecorder(self, appAPI, appEvents)}
` diff --git a/src/app/ui/modal-dialog-custom.js b/src/app/ui/modal-dialog-custom.js index 07d47e3f12..6e029696d0 100644 --- a/src/app/ui/modal-dialog-custom.js +++ b/src/app/ui/modal-dialog-custom.js @@ -25,7 +25,7 @@ module.exports = { }, promptMulti: function ({ title, text, inputValue }, ok, cancel) { if (!inputValue) inputValue = '' - var input =yo`` + var input = yo`` modal(title, yo`
${text}
${input}
`, { fn: () => { if (typeof ok === 'function') ok(document.getElementById('prompt_text').value) } diff --git a/src/universal-dapp.js b/src/universal-dapp.js index eb6a9a1860..f69bd63cf8 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -467,7 +467,7 @@ function execute (pipeline, env, callback) { next(null, env) } -UniversalDApp.prototype.replayTx = function (args, cb) { +UniversalDApp.prototype.rerunTx = function (args, cb) { var self = this self.getAccounts(function (err, accounts = []) { if (err) console.error(err) From 04bfe05c8cf7ca0c7c9ee436d2dedea89ef11a27 Mon Sep 17 00:00:00 2001 From: serapath Date: Wed, 22 Nov 2017 05:57:06 +0100 Subject: [PATCH 16/74] switch account resolution logic into recorder + switch transaction copy2clipboard to create scenario.json file --- src/app.js | 1 + src/app/tabs/run-tab.js | 18 +++++++++++++++--- src/recorder.js | 10 ++++++++++ src/universal-dapp.js | 2 -- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/app.js b/src/app.js index ecce1e1980..39c5d74633 100644 --- a/src/app.js +++ b/src/app.js @@ -556,6 +556,7 @@ function run () { udapp: () => { return udapp }, + filesProviders: filesProviders, fileProviderOf: (path) => { return fileManager.fileProviderOf(path) }, diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 3cfaf86edd..374c8dfaa1 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -303,8 +303,14 @@ function makeRecorder (self, appAPI, appEvents) { ` recordButton.onclick = () => { var txJSON = JSON.stringify(recorder.getAll(), null, 2) - copy(txJSON) - modalDialogCustom.alert(txJSON) + modalDialogCustom.prompt(null, 'journal file name', 'scenario.json', input => { + var newName = appAPI.filesProvider['browser'].type + '/' + helper.createNonClashingName(input, appAPI.filesProvider['browser']) + if (!appAPI.filesProvider['browser'].set(newName, txJSON)) { + modalDialogCustom.alert('Failed to create file ' + newName) + } else { + appAPI.switchFile(newName) + } + }) } runButton.onclick = () => { var opts = { title: `Enter Transactions`, text: `Paste the array of transaction you want to replay here`, inputValue: '', multiline: true } @@ -315,7 +321,13 @@ function makeRecorder (self, appAPI, appEvents) { modalDialogCustom.alert('Invalid JSON, please try again') } if (txArray.length) { - txArray.forEach(tx => udapp.rerunTx(tx.record, CALLBACK)) + txArray.forEach(tx => { + udapp.getAccounts((err, accounts = []) => { + if (err) console.error(err) + tx.record = recorder.resolveAddress(tx.record, accounts) + udapp.rerunTx(tx.record, CALLBACK) + }) + }) } }, function cancel () { }) } diff --git a/src/recorder.js b/src/recorder.js index 07e4a24dc4..bea7de57dc 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -44,6 +44,16 @@ class Recorder { }) }) } + resolveAddress (record, accounts) { + if (record.to && record.to[0] === '<') record.to = accounts[record.to.split('>')[0].slice(11)] + if (record.from && record.from[0] === '<') record.from = accounts[record.from.split('>')[0].slice(11)] + // @TODO: change copy/paste to write and read from history file + + // @TODO: writing browser test + + // @TODO: replace addresses with custom ones (maybe address mapping file?) + return record + } append (timestamp, record) { var self = this self.data.journal.push({ timestamp, record }) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index f69bd63cf8..65359d0b7a 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -471,8 +471,6 @@ UniversalDApp.prototype.rerunTx = function (args, cb) { var self = this self.getAccounts(function (err, accounts = []) { if (err) console.error(err) - if (args.to && args.to[0] === '<') args.to = accounts[args.to.split('>')[0].slice(11)] - if (args.from && args.from[0] === '<') args.from = accounts[args.from.split('>')[0].slice(11)] var pipeline = [queryGasLimit, runTransaction] var env = { self, args, tx: { to: args.to, from: args.from, data: args.data, useCall: args.useCall } } execute(pipeline, env, cb) From fca8a8060592f126261680d428e60d484d9f60f4 Mon Sep 17 00:00:00 2001 From: serapath Date: Thu, 23 Nov 2017 04:39:55 +0100 Subject: [PATCH 17/74] ADD replay tx from file --- src/app.js | 3 +++ src/app/tabs/run-tab.js | 56 ++++++++++++++++++++++------------------- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/src/app.js b/src/app.js index 39c5d74633..943025a44e 100644 --- a/src/app.js +++ b/src/app.js @@ -556,6 +556,9 @@ function run () { udapp: () => { return udapp }, + switchFile: function (path) { + fileManager.switchFile(path) + }, filesProviders: filesProviders, fileProviderOf: (path) => { return fileManager.fileProviderOf(path) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 374c8dfaa1..333d1cac9c 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -90,19 +90,15 @@ var css = csjs` flex-direction: row; align-items: baseline; } - .buttons { - display: flex; - cursor: pointer; - justify-content: center; - flex-direction: column; - text-align: center; - font-size: 12px; - } + .button { display: flex; align-items: center; margin-top: 2%; } + .transaction { + ${styles.rightPanel.runTab.button_transaction} + } .atAddress { ${styles.rightPanel.runTab.button_atAddress} } @@ -170,18 +166,25 @@ var css = csjs` module.exports = runTab -var instanceContainer = yo`
` var noInstancesText = yo`
0 contract Instances
` -var pendingTxsText = yo`
` -var pendingTxsContainer = yo`
${pendingTxsText}
` - function runTab (container, appAPI, appEvents, opts) { var self = { _view: {}, addInstance: addInstance } var udapp = appAPI.udapp() + + var instanceContainer = yo`
` + + var pendingTxsText = yo`
` + var pendingTxsContainer = yo` +
+
${makeRecorder(self, appAPI, appEvents)}
+ ${pendingTxsText} +
+ ` + var el = yo`
${settings(self, appAPI, appEvents)} @@ -278,10 +281,12 @@ function makeRecorder (self, appAPI, appEvents) { getAccounts (cb) { udapp.getAccounts(cb) } } }) - var css = csjs` + var css2 = csjs` .container { - margin-top: 10px; + margin: 10px; display: flex; + justify-content: space-evenly; + width: 100%; } .recorder { ${styles.button} @@ -293,19 +298,19 @@ function makeRecorder (self, appAPI, appEvents) { width: 135px; } ` - var recordButton = yo`` - var runButton = yo`` + var recordButton = yo`` + var runButton = yo`` var el = yo` -
+
${recordButton} ${runButton}
` recordButton.onclick = () => { var txJSON = JSON.stringify(recorder.getAll(), null, 2) - modalDialogCustom.prompt(null, 'journal file name', 'scenario.json', input => { - var newName = appAPI.filesProvider['browser'].type + '/' + helper.createNonClashingName(input, appAPI.filesProvider['browser']) - if (!appAPI.filesProvider['browser'].set(newName, txJSON)) { + modalDialogCustom.prompt(null, 'save ran transactions to file (e.g. `scenario.json`)', 'scenario.json', input => { + var newName = appAPI.filesProviders['browser'].type + '/' + helper.createNonClashingName(input, appAPI.filesProviders['browser'], '.json') + if (!appAPI.filesProviders['browser'].set(newName, txJSON)) { modalDialogCustom.alert('Failed to create file ' + newName) } else { appAPI.switchFile(newName) @@ -313,8 +318,10 @@ function makeRecorder (self, appAPI, appEvents) { }) } runButton.onclick = () => { - var opts = { title: `Enter Transactions`, text: `Paste the array of transaction you want to replay here`, inputValue: '', multiline: true } - modalDialogCustom.promptMulti(opts, function confirm (json = '[]') { + modalDialogCustom.prompt(null, 'load from file (e.g. `scenarios/transactions1.json`)', '', filepath => { + var filename = appAPI.filesProviders['browser'].type + '/' + filepath + var json = appAPI.filesProviders['browser'].get(filename) + if (!json) return modalDialogCustom.alert('Could not find file with transactions, please try again') try { var txArray = JSON.parse(json) } catch (e) { @@ -329,7 +336,7 @@ function makeRecorder (self, appAPI, appEvents) { }) }) } - }, function cancel () { }) + }) } function CALLBACK (err, result) { if (err) console.error(err) @@ -531,9 +538,6 @@ function settings (appAPI, appEvents) {
-
- ${makeRecorder(self, appAPI, appEvents)} -
` From adaa257edf59530256e6411f11f2c0649ded2a11 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 23 Nov 2017 17:20:51 +0100 Subject: [PATCH 18/74] use remix-lib --- src/recorder.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/recorder.js b/src/recorder.js index bea7de57dc..ad9bfbe2c1 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -1,5 +1,5 @@ -var remix = require('ethereum-remix') -var EventManager = remix.lib.EventManager +var remixLib = require('remix-lib') +var EventManager = remixLib.EventManager class Recorder { constructor (opts = {}) { From 57ccf689bf77247cf8c0a29881a74efeef1a2e49 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 23 Nov 2017 17:52:37 +0100 Subject: [PATCH 19/74] fix contract retrieval --- src/app/tabs/run-tab.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 333d1cac9c..d5387e8040 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -205,11 +205,10 @@ function runTab (container, appAPI, appEvents, opts) { // }, // "transactionHash": "0x84f68f96944a47b27af4b4ed1986637aa1bc05fd7a6f5cb1d6a53f68058276d8" // } - console.log(result) var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`) - var contract = appAPI.getContracts()[contractNames.children[contractNames.selectedIndex].innerHTML] + var contract = appAPI.getContract(contractNames.children[contractNames.selectedIndex].innerHTML) var address = self._view.atAddressButtonInput.value - var instance = udapp.renderInstance(contract, address, self._view.selectContractNames.value) + var instance = udapp.renderInstance(contract.object, address, self._view.selectContractNames.value) instanceContainer.appendChild(instance) } From 23963cfba12fd6e07fdd66ed4e38c06edfd9f0a0 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 23 Nov 2017 17:54:12 +0100 Subject: [PATCH 20/74] put CALLBACK as param --- src/app/tabs/run-tab.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index d5387e8040..8f399940dd 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -331,19 +331,18 @@ function makeRecorder (self, appAPI, appEvents) { udapp.getAccounts((err, accounts = []) => { if (err) console.error(err) tx.record = recorder.resolveAddress(tx.record, accounts) - udapp.rerunTx(tx.record, CALLBACK) + udapp.rerunTx(tx.record, function (err, result) { + if (err) console.error(err) + else { + // at each callback call, if the transaction succeed and if this is a creation transaction, we should call + self.addInstance(result) + } + }) }) }) } }) } - function CALLBACK (err, result) { - if (err) console.error(err) - else { - // at each callback call, if the transaction succeed and if this is a creation transaction, we should call - self.addInstance(result) - } - } return el } /* ------------------------------------------------ From ee09cb822d522412fbb86851f74f203af5d0ee1d Mon Sep 17 00:00:00 2001 From: serapath Date: Tue, 28 Nov 2017 15:56:08 +0100 Subject: [PATCH 21/74] UPDATE save/run transaction from copy2clipboard to files --- src/app/tabs/run-tab.js | 39 +++++++++----------- src/recorder.js | 79 +++++++++++++++++++++++++++-------------- src/universal-dapp.js | 10 +++--- 3 files changed, 73 insertions(+), 55 deletions(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 8f399940dd..981ba24aa0 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -195,18 +195,8 @@ function runTab (container, appAPI, appEvents, opts) { ` container.appendChild(el) - function addInstance (result) { - // { - // "result": { - // "gasUsed": "5318", - // "vm": { "exception": 1, "selfdestruct": {} }, - // "bloom": { "bitvector": { "type": "Buffer", "data": [0, /* ... */ 0, 0] } }, - // "amountSpent": "5318" - // }, - // "transactionHash": "0x84f68f96944a47b27af4b4ed1986637aa1bc05fd7a6f5cb1d6a53f68058276d8" - // } - var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`) - var contract = appAPI.getContract(contractNames.children[contractNames.selectedIndex].innerHTML) + function addInstance (sourcename) { + var contract = appAPI.getContract(sourcename) var address = self._view.atAddressButtonInput.value var instance = udapp.renderInstance(contract.object, address, self._view.selectContractNames.value) instanceContainer.appendChild(instance) @@ -322,22 +312,27 @@ function makeRecorder (self, appAPI, appEvents) { var json = appAPI.filesProviders['browser'].get(filename) if (!json) return modalDialogCustom.alert('Could not find file with transactions, please try again') try { - var txArray = JSON.parse(json) + var obj = JSON.parse(json) + var txArray = obj.transactions || [] + var addresses = obj.addresses || {} } catch (e) { modalDialogCustom.alert('Invalid JSON, please try again') } if (txArray.length) { txArray.forEach(tx => { - udapp.getAccounts((err, accounts = []) => { + var record = recorder.resolveAddress(tx.record, addresses) + udapp.rerunTx(record, function (err, result) { + // { + // "result": { + // "gasUsed": "5318", + // "vm": { "exception": 1, "selfdestruct": {} }, + // "bloom": { "bitvector": { "type": "Buffer", "data": [0, /* ... */ 0, 0] } }, + // "amountSpent": "5318" + // }, + // "transactionHash": "0x84f68f96944a47b27af4b4ed1986637aa1bc05fd7a6f5cb1d6a53f68058276d8" + // } if (err) console.error(err) - tx.record = recorder.resolveAddress(tx.record, accounts) - udapp.rerunTx(tx.record, function (err, result) { - if (err) console.error(err) - else { - // at each callback call, if the transaction succeed and if this is a creation transaction, we should call - self.addInstance(result) - } - }) + else if (record.src) self.addInstance(record.src) }) }) } diff --git a/src/recorder.js b/src/recorder.js index ad9bfbe2c1..d213a53d34 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -7,25 +7,35 @@ class Recorder { self._api = opts.api self.event = new EventManager() self.data = { journal: [], _pendingCreation: {} } - opts.events.executioncontext.register('contextChanged', function () { - self.clearAll() - }) + opts.events.executioncontext.register('contextChanged', () => self.clearAll()) var counter = 0 - function getIndex (accounts, address) { - var index - accounts.forEach((addr, idx) => { if (address === addr) index = idx }) - if (!index) index = (++counter) - return index - } 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) => { var { from, to, value, gas, data } = tx var record = { value, gas, data } - self._api.getAccounts(function (err, accounts = []) { - if (err) console.error(err) - record.from = self._addressCache[from] || (self._addressCache[from] = ``) - if (to) record.to = self._addressCache[to] || (self._addressCache[to] = ``) - else self.data._pendingCreation[timestamp] = record + getAddresses(addresses => { + if (to) record.to = addresses[to] || (addresses[to] = self._addressCache[to] = `contract{${++counter}}`) + else { + record.src = getCurrentContractName() + self.data._pendingCreation[timestamp] = record + } + record.from = addresses[from] || (addresses[from] = self._addressCache[from] = `account{${++counter}}`) self.append(timestamp, record) }) }) @@ -38,20 +48,32 @@ class Recorder { delete self.data._pendingCreation[timestamp] if (!record) return var to = args[2] - self._api.getAccounts(function (err, accounts = []) { - if (err) console.error(err) - if (to) record.to = self._addressCache[to] || (self._addressCache[to] = ``) + getAddresses(addresses => { + if (to) { + delete record.src + record.to = addresses[to] || (addresses[to] = self._addressCache[to] = `account{${++counter}}`) + } else record.src = getCurrentContractName() }) }) } - resolveAddress (record, accounts) { - if (record.to && record.to[0] === '<') record.to = accounts[record.to.split('>')[0].slice(11)] - if (record.from && record.from[0] === '<') record.from = accounts[record.from.split('>')[0].slice(11)] - // @TODO: change copy/paste to write and read from history file + 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 + // @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 + // @TODO: offer users by default a "load path" prefixed with the currently open file in the editor (show first one that comes) // @TODO: writing browser test - // @TODO: replace addresses with custom ones (maybe address mapping file?) return record } append (timestamp, record) { @@ -61,11 +83,14 @@ class Recorder { getAll () { var self = this var records = [].concat(self.data.journal) - return records.sort((A, B) => { - var stampA = A.timestamp - var stampB = B.timestamp - return stampA - stampB - }) + return { + addresses: self._addressCache, + transactions: records.sort((A, B) => { + var stampA = A.timestamp + var stampB = B.timestamp + return stampA - stampB + }) + } } clearAll () { var self = this diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 65359d0b7a..450d39204e 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -469,12 +469,10 @@ function execute (pipeline, env, callback) { UniversalDApp.prototype.rerunTx = function (args, cb) { var self = this - self.getAccounts(function (err, accounts = []) { - if (err) console.error(err) - var pipeline = [queryGasLimit, runTransaction] - var env = { self, args, tx: { to: args.to, from: args.from, data: args.data, useCall: args.useCall } } - execute(pipeline, env, cb) - }) + var tx = { to: args.to, from: args.from, data: args.data, useCall: args.useCall } + var pipeline = [queryGasLimit, runTransaction] + var env = { self, args, tx } + execute(pipeline, env, cb) } UniversalDApp.prototype.runTx = function (args, cb) { From d9067e54b60eb31e6e3610b040a2bbfd81488177 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 30 Nov 2017 13:26:53 +0100 Subject: [PATCH 22/74] add getAccounts to appapi --- src/app.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/app.js b/src/app.js index 943025a44e..d8af642903 100644 --- a/src/app.js +++ b/src/app.js @@ -535,6 +535,9 @@ function run () { document.querySelector(`.${css.dragbar2}`).style.right = delta + 'px' onResize() }, + getAccounts: (cb) => { + udapp.getAccounts(cb) + }, getSource: (fileName) => { return compiler.getSource(fileName) }, From 0cc179f2ea5ce5a80b7f14423d6af97aefef8716 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 30 Nov 2017 13:27:39 +0100 Subject: [PATCH 23/74] add renderInstanceFromABI --- src/universal-dapp.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 450d39204e..04dba18e95 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -277,7 +277,7 @@ UniversalDApp.prototype.getBalance = function (address, cb) { } UniversalDApp.prototype.renderInstance = function (contract, address, contractName) { - var abi = txHelper.sortAbiFunction(contract.abi) + var abi = txHelper.sortAbiFunction(contract) return this.renderInstanceFromABI(abi, address, contractName) } @@ -309,22 +309,20 @@ UniversalDApp.prototype.renderInstanceFromABI = function (contractABI, address, $(instance).toggleClass(`${css.hidesub}`) } - var abi = txHelper.sortAbiFunction(contractABI) - instance.appendChild(title) // Add the fallback function - var fallback = txHelper.getFallbackInterface(abi) + var fallback = txHelper.getFallbackInterface(contractABI) if (fallback) { instance.appendChild(this.getCallButton({ funABI: fallback, address: address, - contractAbi: abi, + contractAbi: contractABI, contractName: contractName })) } - $.each(abi, (i, funABI) => { + $.each(contractABI, (i, funABI) => { if (funABI.type !== 'function') { return } @@ -332,7 +330,7 @@ UniversalDApp.prototype.renderInstanceFromABI = function (contractABI, address, instance.appendChild(this.getCallButton({ funABI: funABI, address: address, - contractAbi: abi, + contractAbi: contractABI, contractName: contractName })) }) From 9483bf2635205d31196b5de0aaf68d457fd71f1e Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 30 Nov 2017 13:30:18 +0100 Subject: [PATCH 24/74] refactor getSelectedContract & move makeRecorder call --- src/app/tabs/run-tab.js | 113 +++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 59 deletions(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 981ba24aa0..82a197c6a4 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -1,4 +1,5 @@ 'use strict' +var async = require('async') var $ = require('jquery') var yo = require('yo-yo') var helper = require('../../lib/helper.js') @@ -166,45 +167,30 @@ var css = csjs` module.exports = runTab +var instanceContainer = yo`
` var noInstancesText = yo`
0 contract Instances
` -function runTab (container, appAPI, appEvents, opts) { - var self = { - _view: {}, - addInstance: addInstance - } - var udapp = appAPI.udapp() +var pendingTxsText = yo`
` - var instanceContainer = yo`
` - - var pendingTxsText = yo`
` +function runTab (container, appAPI, appEvents, opts) { var pendingTxsContainer = yo` -
-
${makeRecorder(self, appAPI, appEvents)}
- ${pendingTxsText} -
- ` +
+ ${pendingTxsText} +
` var el = yo`
- ${settings(self, appAPI, appEvents)} - ${contractDropdown(self, appAPI, appEvents, instanceContainer)} + ${settings(appAPI, appEvents)} + ${contractDropdown(appAPI, appEvents, instanceContainer)} ${pendingTxsContainer} ${instanceContainer}
` container.appendChild(el) - function addInstance (sourcename) { - var contract = appAPI.getContract(sourcename) - var address = self._view.atAddressButtonInput.value - var instance = udapp.renderInstance(contract.object, address, self._view.selectContractNames.value) - instanceContainer.appendChild(instance) - } - // PENDING transactions function updatePendingTxs (container, appAPI) { - var pendingCount = Object.keys(udapp.pendingTransactions()).length + var pendingCount = Object.keys(appAPI.udapp().pendingTransactions()).length pendingTxsText.innerText = pendingCount + ' pending transactions' } @@ -259,16 +245,14 @@ function updateAccountBalances (container, appAPI) { /* ------------------------------------------------ RECORDER ------------------------------------------------ */ -function makeRecorder (self, appAPI, appEvents) { +function makeRecorder (appAPI, appEvents) { var udapp = appAPI.udapp() var recorder = new Recorder({ events: { udapp: appEvents.udapp, executioncontext: executionContext.event }, - api: { - getAccounts (cb) { udapp.getAccounts(cb) } - } + api: appAPI }) var css2 = csjs` .container { @@ -314,27 +298,30 @@ function makeRecorder (self, appAPI, appEvents) { try { var obj = JSON.parse(json) var txArray = obj.transactions || [] - var addresses = obj.addresses || {} + var accounts = obj.accounts || [] + var options = obj.options + var abis = obj.abis } catch (e) { modalDialogCustom.alert('Invalid JSON, please try again') } if (txArray.length) { - txArray.forEach(tx => { - var record = recorder.resolveAddress(tx.record, addresses) - udapp.rerunTx(record, function (err, result) { - // { - // "result": { - // "gasUsed": "5318", - // "vm": { "exception": 1, "selfdestruct": {} }, - // "bloom": { "bitvector": { "type": "Buffer", "data": [0, /* ... */ 0, 0] } }, - // "amountSpent": "5318" - // }, - // "transactionHash": "0x84f68f96944a47b27af4b4ed1986637aa1bc05fd7a6f5cb1d6a53f68058276d8" - // } - if (err) console.error(err) - else if (record.src) self.addInstance(record.src) + recorder.setListen(false) + async.eachSeries(txArray, function (tx, cb) { + 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) }) } }) } @@ -344,7 +331,7 @@ function makeRecorder (self, appAPI, appEvents) { section CONTRACT DROPDOWN and BUTTONS ------------------------------------------------ */ -function contractDropdown (self, appAPI, appEvents, instanceContainer) { +function contractDropdown (appAPI, appEvents, instanceContainer) { instanceContainer.appendChild(noInstancesText) var compFails = yo`` appEvents.compiler.register('compilationFinished', function (success, data, source) { @@ -359,8 +346,18 @@ function contractDropdown (self, appAPI, appEvents, instanceContainer) { var atAddressButtonInput = yo`` var createButtonInput = yo`` var selectContractNames = yo`` - self._view.atAddressButtonInput = atAddressButtonInput - self._view.selectContractNames = selectContractNames + + function getSelectedContract () { + var contractName = selectContractNames.children[selectContractNames.selectedIndex].innerHTML + if (contractName) { + return { + name: contractName, + contract: appAPI.getContract(contractName) + } + } + return null + } + appAPI.getSelectedContract = getSelectedContract var el = yo`
@@ -375,6 +372,7 @@ function contractDropdown (self, appAPI, appEvents, instanceContainer) { ${atAddressButtonInput}
At Address
+
${makeRecorder(appAPI, appEvents)}
` @@ -382,8 +380,7 @@ function contractDropdown (self, appAPI, appEvents, instanceContainer) { function setInputParamsPlaceHolder () { createButtonInput.value = '' if (appAPI.getContract && selectContractNames.selectedIndex >= 0 && selectContractNames.children.length > 0) { - var contract = appAPI.getContract(selectContractNames.children[selectContractNames.selectedIndex].innerHTML) - var ctrabi = txHelper.getConstructorInterface(contract.object.abi) + var ctrabi = txHelper.getConstructorInterface(getSelectedContract().contract.object.abi) if (ctrabi.inputs.length) { createButtonInput.setAttribute('placeholder', txHelper.inputParametersDeclarationToString(ctrabi.inputs)) createButtonInput.removeAttribute('disabled') @@ -398,20 +395,18 @@ function contractDropdown (self, appAPI, appEvents, instanceContainer) { // ADD BUTTONS AT ADDRESS AND CREATE function createInstance () { - var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`) - var contractName = contractNames.children[contractNames.selectedIndex].innerHTML - var contract = appAPI.getContract(contractName) + var selectedContract = getSelectedContract() - if (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.') return } - var constructor = txHelper.getConstructorInterface(contract.object.abi) + var constructor = txHelper.getConstructorInterface(selectedContract.contract.object.abi) var args = createButtonInput.value - txFormat.buildData(contract.object, appAPI.getContracts(), true, constructor, args, appAPI.udapp(), (error, data) => { + txFormat.buildData(selectedContract.contract.object, appAPI.getContracts(), true, constructor, args, appAPI.udapp(), (error, data) => { if (!error) { - appAPI.logMessage(`creation of ${contractName} pending...`) + appAPI.logMessage(`creation of ${selectedContract.name} pending...`) txExecution.createContract(data, appAPI.udapp(), (error, txResult) => { if (!error) { var isVM = executionContext.isVM() @@ -424,13 +419,13 @@ function contractDropdown (self, appAPI, appEvents, instanceContainer) { } noInstancesText.style.display = 'none' var address = isVM ? txResult.result.createdAddress : txResult.result.contractAddress - instanceContainer.appendChild(appAPI.udapp().renderInstance(contract.object, address, selectContractNames.value)) + instanceContainer.appendChild(appAPI.udapp().renderInstance(selectedContract.contract.object, address, selectContractNames.value)) } else { - appAPI.logMessage(`creation of ${contractName} errored: ` + error) + appAPI.logMessage(`creation of ${selectedContract.name} errored: ` + error) } }) } else { - appAPI.logMessage(`creation of ${contractName} errored: ` + error) + appAPI.logMessage(`creation of ${selectedContract.name} errored: ` + error) } }, (msg) => { appAPI.logMessage(msg) From 5089c6581c5d6f9e8fe1e6717d43f8e32d6fd3e7 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 30 Nov 2017 13:30:43 +0100 Subject: [PATCH 25/74] store ABI & simplify address resolver --- remix | 1 + src/app/tabs/run-tab.js | 37 +++----- src/recorder.js | 195 +++++++++++++++++++++++++++------------- 3 files changed, 146 insertions(+), 87 deletions(-) create mode 160000 remix diff --git a/remix b/remix new file mode 160000 index 0000000000..4fe13da1c8 --- /dev/null +++ b/remix @@ -0,0 +1 @@ +Subproject commit 4fe13da1c8c51dc7bb728df14c30fbb274e0bc80 diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 82a197c6a4..30133f519f 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -1,5 +1,4 @@ 'use strict' -var async = require('async') var $ = require('jquery') var yo = require('yo-yo') var helper = require('../../lib/helper.js') @@ -246,7 +245,6 @@ function updateAccountBalances (container, appAPI) { RECORDER ------------------------------------------------ */ function makeRecorder (appAPI, appEvents) { - var udapp = appAPI.udapp() var recorder = new Recorder({ events: { udapp: appEvents.udapp, @@ -291,10 +289,9 @@ function makeRecorder (appAPI, appEvents) { }) } runButton.onclick = () => { - modalDialogCustom.prompt(null, 'load from file (e.g. `scenarios/transactions1.json`)', '', filepath => { - var filename = appAPI.filesProviders['browser'].type + '/' + filepath - var json = appAPI.filesProviders['browser'].get(filename) - if (!json) return modalDialogCustom.alert('Could not find file with transactions, please try again') + var currentFile = appAPI.config.get('currentFile') + var json = appAPI.filesProviders['browser'].get(currentFile) + if (currentFile.match('.json$')) { try { var obj = JSON.parse(json) var txArray = obj.transactions || [] @@ -302,28 +299,16 @@ function makeRecorder (appAPI, appEvents) { var options = obj.options var abis = obj.abis } catch (e) { - modalDialogCustom.alert('Invalid JSON, please try again') + return modalDialogCustom.alert('Invalid Scenario File, please try again') } if (txArray.length) { - recorder.setListen(false) - async.eachSeries(txArray, function (tx, cb) { - 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) }) + recorder.run(txArray, accounts, options, abis, (abi, address, contractName) => { + instanceContainer.appendChild(appAPI.udapp().renderInstanceFromABI(abi, address, contractName)) + }) } - }) + } else { + modalDialogCustom.alert('Scenario File require JSON type') + } } return el } @@ -395,7 +380,7 @@ function contractDropdown (appAPI, appEvents, instanceContainer) { // ADD BUTTONS AT ADDRESS AND CREATE function createInstance () { - var selectedContract = getSelectedContract() + var selectedContract = getSelectedContract() if (selectedContract.contract.object.evm.bytecode.object.length === 0) { modalDialogCustom.alert('This contract does not implement all functions and thus cannot be created.') diff --git a/src/recorder.js b/src/recorder.js index d213a53d34..7582c60d5e 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -1,100 +1,173 @@ var remixLib = require('remix-lib') 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 { constructor (opts = {}) { var self = this self._api = opts.api self.event = new EventManager() - self.data = { journal: [], _pendingCreation: {} } - opts.events.executioncontext.register('contextChanged', () => self.clearAll()) - var counter = 0 - self._addressCache = {} + self.data = { _listen: true, _replay: false, journal: [], _pendingTxs: {}, _createdContracts: {}, _createdContractsReverse: {}, _usedAccounts: {}, _abis: {} } + opts.events.executioncontext.register('contextChanged', () => { + self.clearAll() + }) - 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) => { - var { from, to, value, gas, data } = tx - var record = { value, gas, data } - getAddresses(addresses => { - if (to) record.to = addresses[to] || (addresses[to] = self._addressCache[to] = `contract{${++counter}}`) - else { - record.src = getCurrentContractName() - self.data._pendingCreation[timestamp] = record + var { from, to, value, data, useCall } = tx + var record = { value, data, useCall } + + this.data._pendingTxs[timestamp] = tx + + // convert to and from to tokens + 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] - if (err) console.error(err) - var timestamp = args[6] - // update transaction which was pending with correct `to` address - var record = self.data._pendingCreation[timestamp] - delete self.data._pendingCreation[timestamp] - if (!record) return - var to = args[2] - getAddresses(addresses => { - if (to) { - delete record.src - record.to = addresses[to] || (addresses[to] = self._addressCache[to] = `account{${++counter}}`) - } else record.src = getCurrentContractName() - }) + + opts.events.udapp.register('transactionExecuted', (error, from, to, data, call, txResult, timestamp) => { + if (error) return console.log(error) + var tx = this.data._pendingTxs[timestamp] + + 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 + this.data._createdContracts[address] = timestamp + this.data._createdContractsReverse[timestamp] = address }) } - 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 - // @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 - // @TODO: offer users by default a "load path" prefixed with the currently open file in the editor (show first one that comes) + /** + * stop/start saving txs. If not listenning, is basically in replay mode + * + * @param {Bool} listen + */ + 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 - return record } + + /** + * save the given @arg record + * + * @param {Number/String} timestamp + * @param {Object} record + * + */ append (timestamp, record) { var self = this self.data.journal.push({ timestamp, record }) } + + /** + * basically return the records + associate values (like abis / accounts) + * + */ getAll () { var self = this var records = [].concat(self.data.journal) return { - addresses: self._addressCache, + accounts: self.data._usedAccounts, + options: { + useAccountList: true + }, transactions: records.sort((A, B) => { var stampA = A.timestamp var stampB = B.timestamp return stampA - stampB - }) + }), + abis: self.data._abis } } + + /** + * delete the seen transactions + * + */ clearAll () { var self = this 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) }) } } From 36108500979c905b3bbead293c365585e556447a Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 30 Nov 2017 20:27:13 +0100 Subject: [PATCH 26/74] fix contractName --- src/recorder.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/recorder.js b/src/recorder.js index 7582c60d5e..2bb39242a1 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -33,6 +33,7 @@ class Recorder { var abi = selectedContract.contract.object.abi var sha3 = util.sha3_256(JSON.stringify(abi)) record.abi = sha3 + record.contractName = selectedContract.name self.data._abis[sha3] = abi } } else { From 04e9b823e434cb97648f019dda7eb7242beadada Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 30 Nov 2017 20:27:29 +0100 Subject: [PATCH 27/74] fix log logging --- src/app/execution/txLogger.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/execution/txLogger.js b/src/app/execution/txLogger.js index fb79f0cde8..a334bc2ffa 100644 --- a/src/app/execution/txLogger.js +++ b/src/app/execution/txLogger.js @@ -269,7 +269,7 @@ function renderUnknownTransaction (self, data) { input: data.tx.input, hash: data.tx.hash, gas: data.tx.gas, - logs: data.logs, + logs: data.tx.logs, transactionCost: data.tx.transactionCost, executionCost: data.tx.executionCost, status: data.tx.status @@ -440,7 +440,7 @@ function createTable (opts) { } var stringified = ' - ' - if (opts.logs.decoded) { + if (opts.logs && opts.logs.decoded) { stringified = typeConversion.stringify(opts.logs.decoded) } var logs = yo` From 384bdf76612ef88db257d39fa0cce5d25286aa46 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 30 Nov 2017 20:52:17 +0100 Subject: [PATCH 28/74] don't load source location is compilation data not available --- src/app/debugger/debugger.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/debugger/debugger.js b/src/app/debugger/debugger.js index 62b17c019b..1f78525ad6 100644 --- a/src/app/debugger/debugger.js +++ b/src/app/debugger/debugger.js @@ -75,7 +75,10 @@ Debugger.prototype.debug = function (txHash) { var self = this this.debugger.web3().eth.getTransaction(txHash, function (error, tx) { if (!error) { - self.debugger.setCompilationResult(self.appAPI.lastCompilationResult().data) + var compilationResult = self.appAPI.lastCompilationResult() + if (compilationResult) { + self.debugger.setCompilationResult(compilationResult.data) + } self.debugger.debug(tx) } }) From 2cd3ebbe9e46087c5bedb64704d3e9f04399c620 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 30 Nov 2017 20:52:49 +0100 Subject: [PATCH 29/74] remove no_instance div when replying --- src/app/tabs/run-tab.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 30133f519f..ac28ff02b6 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -302,6 +302,7 @@ function makeRecorder (appAPI, appEvents) { return modalDialogCustom.alert('Invalid Scenario File, please try again') } if (txArray.length) { + noInstancesText.style.display = 'none' recorder.run(txArray, accounts, options, abis, (abi, address, contractName) => { instanceContainer.appendChild(appAPI.udapp().renderInstanceFromABI(abi, address, contractName)) }) From 0ab241711f3cb9d72c5ed1bd6d9921fbe9423e61 Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 1 Dec 2017 18:36:07 +0100 Subject: [PATCH 30/74] don't save calls --- src/recorder.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/recorder.js b/src/recorder.js index 2bb39242a1..8ab91062ba 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -20,8 +20,9 @@ class Recorder { }) opts.events.udapp.register('initiatingTransaction', (timestamp, tx) => { - var { from, to, value, data, useCall } = tx - var record = { value, data, useCall } + if (tx.useCall) return + var { from, to, value, data } = tx + var record = { value, data } this.data._pendingTxs[timestamp] = tx @@ -51,6 +52,7 @@ class Recorder { opts.events.udapp.register('transactionExecuted', (error, from, to, data, call, txResult, timestamp) => { if (error) return console.log(error) + if (call) return var tx = this.data._pendingTxs[timestamp] var address = executionContext.isVM() ? txResult.result.createdAddress : tx.result.contractAddress From b0086743b0d646551b6dd9011bc21f5d1d7fb896 Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 1 Dec 2017 18:36:28 +0100 Subject: [PATCH 31/74] remove option field --- src/recorder.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/recorder.js b/src/recorder.js index 8ab91062ba..21da6a1967 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -113,9 +113,6 @@ class Recorder { var records = [].concat(self.data.journal) return { accounts: self.data._usedAccounts, - options: { - useAccountList: true - }, transactions: records.sort((A, B) => { var stampA = A.timestamp var stampB = B.timestamp From 7d166a8a8e0585c427a0fec6d865806c8b51b0ca Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 1 Dec 2017 18:37:11 +0100 Subject: [PATCH 32/74] currentPath and add to API --- src/app.js | 3 +++ src/app/files/fileManager.js | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/src/app.js b/src/app.js index d8af642903..e02232ed37 100644 --- a/src/app.js +++ b/src/app.js @@ -569,6 +569,9 @@ function run () { fileProvider: (name) => { return self._api.filesProviders[name] }, + currentPath: function () { + return fileManager.currentPath() + }, getBalance: (address, callback) => { udapp.getBalance(address, (error, balance) => { if (error) { diff --git a/src/app/files/fileManager.js b/src/app/files/fileManager.js index 4a641416f8..e405ad6006 100644 --- a/src/app/files/fileManager.js +++ b/src/app/files/fileManager.js @@ -79,6 +79,13 @@ class FileManager { this.refreshTabs() } + currentPath () { + var currentFile = this.opt.config.get('currentFile') + var reg = /(.*\/).*/ + var path = reg.exec(currentFile) + return path ? path[1] : null + } + fileRemovedEvent (path) { if (path === this.opt.config.get('currentFile')) { this.opt.config.set('currentFile', '') From a3a183df35f7e100487997d67a40cb7e5441583f Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 1 Dec 2017 18:37:33 +0100 Subject: [PATCH 33/74] save on the current file provider and not only on browser/ --- src/app/tabs/run-tab.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index ac28ff02b6..199e32faed 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -279,12 +279,17 @@ function makeRecorder (appAPI, appEvents) { ` recordButton.onclick = () => { var txJSON = JSON.stringify(recorder.getAll(), null, 2) - modalDialogCustom.prompt(null, 'save ran transactions to file (e.g. `scenario.json`)', 'scenario.json', input => { - var newName = appAPI.filesProviders['browser'].type + '/' + helper.createNonClashingName(input, appAPI.filesProviders['browser'], '.json') - if (!appAPI.filesProviders['browser'].set(newName, txJSON)) { - modalDialogCustom.alert('Failed to create file ' + newName) - } else { - appAPI.switchFile(newName) + var path = appAPI.currentPath() + modalDialogCustom.prompt(null, 'save ran transactions to file (e.g. `scenario.json`). The file is goiing to be saved under ' + path, 'scenario.json', input => { + var fileProvider = appAPI.fileProviderOf(path) + if (fileProvider) { + input = helper.createNonClashingName(input, fileProvider, '.json') + var newFile = path + input + if (!fileProvider.set(newFile, txJSON)) { + modalDialogCustom.alert('Failed to create file ' + newFile) + } else { + appAPI.switchFile(newFile) + } } }) } From 1873619115a3ca2e927a1ba2398f13300421aaa9 Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 1 Dec 2017 19:04:10 +0100 Subject: [PATCH 34/74] fix abi sha3 --- src/recorder.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/recorder.js b/src/recorder.js index 21da6a1967..b0d4022070 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -1,6 +1,6 @@ var remixLib = require('remix-lib') var EventManager = remixLib.EventManager -var util = remixLib.util +var ethutil = require('ethereumjs-util') var executionContext = require('./execution-context') var async = require('async') @@ -32,7 +32,7 @@ class Recorder { var selectedContract = self._api.getSelectedContract() if (selectedContract) { var abi = selectedContract.contract.object.abi - var sha3 = util.sha3_256(JSON.stringify(abi)) + var sha3 = ethutil.bufferToHex(ethutil.sha3(abi)) record.abi = sha3 record.contractName = selectedContract.name self.data._abis[sha3] = abi From 9f58381d0eea225c411568c0b65cac6d971fe76f Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 1 Dec 2017 19:04:23 +0100 Subject: [PATCH 35/74] typo --- src/app/tabs/run-tab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 199e32faed..6ce2e8662e 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -280,7 +280,7 @@ function makeRecorder (appAPI, appEvents) { recordButton.onclick = () => { var txJSON = JSON.stringify(recorder.getAll(), null, 2) var path = appAPI.currentPath() - modalDialogCustom.prompt(null, 'save ran transactions to file (e.g. `scenario.json`). The file is goiing to be saved under ' + path, 'scenario.json', input => { + modalDialogCustom.prompt(null, 'save ran transactions to file (e.g. `scenario.json`). The file is going to be saved under ' + path, 'scenario.json', input => { var fileProvider = appAPI.fileProviderOf(path) if (fileProvider) { input = helper.createNonClashingName(input, fileProvider, '.json') From 32ad128aa5a83f6e59db82ce3a1578b24d8173a6 Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 1 Dec 2017 19:04:41 +0100 Subject: [PATCH 36/74] fix scenario save --- src/app/tabs/run-tab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 6ce2e8662e..59758d2e8a 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -283,8 +283,8 @@ function makeRecorder (appAPI, appEvents) { modalDialogCustom.prompt(null, 'save ran transactions to file (e.g. `scenario.json`). The file is going to be saved under ' + path, 'scenario.json', input => { var fileProvider = appAPI.fileProviderOf(path) if (fileProvider) { - input = helper.createNonClashingName(input, fileProvider, '.json') var newFile = path + input + newFile = helper.createNonClashingName(newFile, fileProvider, '.json') if (!fileProvider.set(newFile, txJSON)) { modalDialogCustom.alert('Failed to create file ' + newFile) } else { From 389b7ea50723fd580b9ac1fd8cc13734de5277c4 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 5 Dec 2017 15:42:12 +0100 Subject: [PATCH 37/74] correclt convert address --- src/recorder.js | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/recorder.js b/src/recorder.js index b0d4022070..ad6171d81f 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -53,11 +53,10 @@ class Recorder { opts.events.udapp.register('transactionExecuted', (error, from, to, data, call, txResult, timestamp) => { if (error) return console.log(error) if (call) return - var tx = this.data._pendingTxs[timestamp] - var address = executionContext.isVM() ? txResult.result.createdAddress : tx.result.contractAddress + var address = executionContext.isVM() ? txResult.result.createdAddress : txResult.result.contractAddress if (!address) return // not a contract creation - address = '0x' + address.toString('hex') + address = addressToString(address) // save back created addresses for the convertion from tokens to real adresses this.data._createdContracts[address] = timestamp this.data._createdContractsReverse[timestamp] = address @@ -154,9 +153,9 @@ class Recorder { if (err) { console.error(err) } else { - var address = executionContext.isVM() ? txResult.result.createdAddress : tx.result.contractAddress + var address = executionContext.isVM() ? txResult.result.createdAddress : txResult.result.contractAddress if (!address) return // not a contract creation - address = '0x' + address.toString('hex') + address = addressToString(address) // save back created addresses for the convertion from tokens to real adresses self.data._createdContracts[address] = tx.timestamp self.data._createdContractsReverse[tx.timestamp] = address @@ -171,4 +170,15 @@ class Recorder { } } +function addressToString (address) { + if (!address) return null + if (typeof address !== 'string') { + address = address.toString('hex') + } + if (address.indexOf('0x') === -1) { + address = '0x' + address + } + return address +} + module.exports = Recorder From 923814774bf63b03a5fd82667a287910ddd889b0 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 6 Dec 2017 17:56:32 +0100 Subject: [PATCH 38/74] fix contextview --- src/app/editor/contextView.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/editor/contextView.js b/src/app/editor/contextView.js index 9622a5bc13..b4a3df4485 100644 --- a/src/app/editor/contextView.js +++ b/src/app/editor/contextView.js @@ -109,7 +109,7 @@ class ContextView { if (target) { this._current = target } else { - this._current = last + this._current = null } } } @@ -123,7 +123,7 @@ class ContextView { if (!node) return yo`
` var self = this var references = this._api.contextualListener.referencesOf(node) - var type = node.attributes.type ? node.attributes.type : node.name + var type = (node.attributes && node.attributes.type) ? node.attributes.type : node.name references = `${references ? references.length : '0'} reference(s)` var ref = 0 From 5f0c90fe1896cdd03db185feeab453cce1a40c58 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 6 Dec 2017 17:56:57 +0100 Subject: [PATCH 39/74] don't pull remix dev for CI --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 22dca2fde2..dc03c584f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: node_js node_js: - "7" script: - - npm run lint && npm run test && npm run downloadsolc && npm run make-mock-compiler && npm run pullremix && npm run linkremix && npm run build + - npm run lint && npm run test && npm run downloadsolc && npm run make-mock-compiler && npm run build - ./ci/browser_tests.sh deploy: - provider: script From bd900d6cfaf6b49f41234eb23bd6b0b71b029c80 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 6 Dec 2017 18:02:39 +0100 Subject: [PATCH 40/74] pass decoded value to recorder --- src/app/execution/txFormat.js | 2 +- src/recorder.js | 12 +++++------- src/universal-dapp.js | 13 +++++++------ 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/app/execution/txFormat.js b/src/app/execution/txFormat.js index cd164c3d2b..3ae1c9d432 100644 --- a/src/app/execution/txFormat.js +++ b/src/app/execution/txFormat.js @@ -63,7 +63,7 @@ module.exports = { } else { dataHex = Buffer.concat([helper.encodeFunctionId(funAbi), data]).toString('hex') } - callback(null, dataHex) + callback(null, { dataHex, funAbi, funArgs }) }, atAddress: function () {}, diff --git a/src/recorder.js b/src/recorder.js index ad6171d81f..1300f936d2 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -14,17 +14,15 @@ class Recorder { var self = this self._api = opts.api self.event = new EventManager() - self.data = { _listen: true, _replay: false, journal: [], _pendingTxs: {}, _createdContracts: {}, _createdContractsReverse: {}, _usedAccounts: {}, _abis: {} } + self.data = { _listen: true, _replay: false, journal: [], _createdContracts: {}, _createdContractsReverse: {}, _usedAccounts: {}, _abis: {} } opts.events.executioncontext.register('contextChanged', () => { self.clearAll() }) - opts.events.udapp.register('initiatingTransaction', (timestamp, tx) => { + opts.events.udapp.register('initiatingTransaction', (timestamp, tx, payLoad) => { if (tx.useCall) return - var { from, to, value, data } = tx - var record = { value, data } - - this.data._pendingTxs[timestamp] = tx + var { from, to, value } = tx + var record = { value, parameters: { definition: payLoad.funAbi, values: payLoad.funArgs } } // convert to and from to tokens if (this.data._listen) { @@ -128,7 +126,6 @@ class Recorder { clearAll () { var self = this self.data.journal = [] - self.data._pendingTxs = {} self.data._createdContracts = {} self.data._createdContractsReverse = {} self.data._usedAccounts = {} @@ -149,6 +146,7 @@ class Recorder { 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) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 04dba18e95..6632b162f0 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -475,9 +475,10 @@ UniversalDApp.prototype.rerunTx = function (args, cb) { UniversalDApp.prototype.runTx = function (args, cb) { var self = this - var tx = { to: args.to, data: args.data, useCall: args.useCall } + var tx = { to: args.to, data: args.data.dataHex, useCall: args.useCall } + var payLoad = { funAbi: args.data.funAbi, funArgs: args.data.funArgs } // contains decoded parameters var pipeline = [queryGasLimit, queryValue, queryAddress, runTransaction] - var env = { self, args, tx } + var env = { self, args, tx, payLoad } execute(pipeline, env, cb) } @@ -528,14 +529,14 @@ function queryAddress (env, next) { } function runTransaction (env, next) { - var { self, args, tx } = env + var { self, args, tx, payLoad } = env var timestamp = Date.now() - self.event.trigger('initiatingTransaction', [timestamp, tx]) + self.event.trigger('initiatingTransaction', [timestamp, tx, payLoad]) self.txRunner.rawRun(tx, function (error, result) { if (!args.useCall) { - self.event.trigger('transactionExecuted', [error, args.from, args.to, args.data, false, result, timestamp]) + self.event.trigger('transactionExecuted', [error, args.from, args.to, args.data, false, result, timestamp, payLoad]) } else { - self.event.trigger('callExecuted', [error, args.from, args.to, args.data, true, result, timestamp]) + self.event.trigger('callExecuted', [error, args.from, args.to, args.data, true, result, timestamp, payLoad]) } if (error) { if (typeof (error) !== 'string') { From 2be92f5bb1c46e59e0a5829533b3f02ec47e820f Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 6 Dec 2017 18:53:52 +0100 Subject: [PATCH 41/74] add tiny encode data function --- src/app/execution/txFormat.js | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/app/execution/txFormat.js b/src/app/execution/txFormat.js index 3ae1c9d432..484ded1d0a 100644 --- a/src/app/execution/txFormat.js +++ b/src/app/execution/txFormat.js @@ -8,6 +8,30 @@ var TreeView = require('remix-debugger').ui.TreeView var executionContext = require('../../execution-context') module.exports = { + + /** + * build the transaction data + * + * @param {Object} function abi + * @param {Object} values to encode + * @param {String} contractbyteCode + */ + encodeData: function (funABI, values, contractbyteCode) { + var encoded + var encodedHex + try { + encoded = helper.encodeParams(funABI, values) + encodedHex = encoded.toString('hex') + } catch (e) { + return { error: 'cannot encode arguments' } + } + if (contractbyteCode) { + return { data: contractbyteCode + encodedHex } + } else { + return { data: Buffer.concat([helper.encodeFunctionId(funABI), encoded]).toString('hex') } + } + }, + /** * build the transaction data * @@ -45,7 +69,9 @@ module.exports = { if (data.slice(0, 2) === '0x') { dataHex = data.slice(2) } + var contractBytecode if (isConstructor) { + contractBytecode = contract.evm.bytecode.object var bytecodeToDeploy = contract.evm.bytecode.object if (bytecodeToDeploy.indexOf('_') >= 0) { this.linkBytecode(contract, contracts, udapp, (err, bytecode) => { @@ -63,7 +89,7 @@ module.exports = { } else { dataHex = Buffer.concat([helper.encodeFunctionId(funAbi), data]).toString('hex') } - callback(null, { dataHex, funAbi, funArgs }) + callback(null, { dataHex, funAbi, funArgs, contractBytecode }) }, atAddress: function () {}, From 4b4b06c048c63ca7cd6bc8f5a5fcb5bf1e23f34b Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 6 Dec 2017 18:54:34 +0100 Subject: [PATCH 42/74] remove data from record and display decoed values --- src/recorder.js | 12 ++++++++++-- src/universal-dapp.js | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/recorder.js b/src/recorder.js index 1300f936d2..80f2ab0265 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -2,7 +2,9 @@ var remixLib = require('remix-lib') var EventManager = remixLib.EventManager var ethutil = require('ethereumjs-util') var executionContext = require('./execution-context') +var format = require('./app/execution/txFormat') var async = require('async') +var modal = require('./app/ui/modal-dialog-custom') /** * Record transaction as long as the user create them. @@ -22,10 +24,10 @@ class Recorder { opts.events.udapp.register('initiatingTransaction', (timestamp, tx, payLoad) => { if (tx.useCall) return var { from, to, value } = tx - var record = { value, parameters: { definition: payLoad.funAbi, values: payLoad.funArgs } } // convert to and from to tokens if (this.data._listen) { + var record = { value, parameters: { definitions: payLoad.funAbi, values: payLoad.funArgs } } if (!to) { var selectedContract = self._api.getSelectedContract() if (selectedContract) { @@ -33,6 +35,7 @@ class Recorder { var sha3 = ethutil.bufferToHex(ethutil.sha3(abi)) record.abi = sha3 record.contractName = selectedContract.name + record.bytecode = payLoad.contractBytecode self.data._abis[sha3] = abi } } else { @@ -146,7 +149,12 @@ class Recorder { self.setListen(false) async.eachSeries(records, function (tx, cb) { var record = self.resolveAddress(tx.record, accounts, options) - + var data = format.encodeData(tx.record.parameters.definitions, tx.record.parameters.values, tx.record.bytecode) + if (data.error) { + modal.alert(data.error) + } else { + record.data = data.data + } self._api.udapp().rerunTx(record, function (err, txResult) { if (err) { console.error(err) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 6632b162f0..95e1d5b432 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -476,7 +476,7 @@ UniversalDApp.prototype.rerunTx = function (args, cb) { UniversalDApp.prototype.runTx = function (args, cb) { var self = this var tx = { to: args.to, data: args.data.dataHex, useCall: args.useCall } - var payLoad = { funAbi: args.data.funAbi, funArgs: args.data.funArgs } // contains decoded parameters + var payLoad = { funAbi: args.data.funAbi, funArgs: args.data.funArgs, contractBytecode: args.data.contractBytecode } // contains decoded parameters var pipeline = [queryGasLimit, queryValue, queryAddress, runTransaction] var env = { self, args, tx, payLoad } execute(pipeline, env, cb) From 28150583901330d2cfd5866bbe04073e6a02c89b Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 6 Dec 2017 20:00:21 +0100 Subject: [PATCH 43/74] typo --- src/universal-dapp.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 95e1d5b432..380fead9bf 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -277,7 +277,7 @@ UniversalDApp.prototype.getBalance = function (address, cb) { } UniversalDApp.prototype.renderInstance = function (contract, address, contractName) { - var abi = txHelper.sortAbiFunction(contract) + var abi = txHelper.sortAbiFunction(contract.abi) return this.renderInstanceFromABI(abi, address, contractName) } From fdda4aa845d85b3dc279cd6141f52bf20eb71d4b Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 6 Dec 2017 20:00:34 +0100 Subject: [PATCH 44/74] fix queue txs --- src/app/execution/txRunner.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/execution/txRunner.js b/src/app/execution/txRunner.js index b26bfe263f..d00a790ab9 100644 --- a/src/app/execution/txRunner.js +++ b/src/app/execution/txRunner.js @@ -156,8 +156,8 @@ function run (self, tx, stamp, callback) { self.execute(tx, (error, result) => { delete self.pendingTxs[stamp] callback(error, result) - if (Object.keys(self.pendingTxs).length) { - var next = self.pendingTxs.pop() + if (self.queusTxs.length) { + var next = self.queusTxs.pop() run(self, next.tx, next.stamp, next.callback) } }) From 8eab638b367415a45a4a756424f40507699331e2 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 6 Dec 2017 20:00:54 +0100 Subject: [PATCH 45/74] save ABI ref and not the content --- src/recorder.js | 63 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/src/recorder.js b/src/recorder.js index 80f2ab0265..4ca816640e 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -3,6 +3,7 @@ var EventManager = remixLib.EventManager var ethutil = require('ethereumjs-util') var executionContext = require('./execution-context') var format = require('./app/execution/txFormat') +var txHelper = require('./app/execution/txHelper') var async = require('async') var modal = require('./app/ui/modal-dialog-custom') @@ -16,7 +17,7 @@ class Recorder { var self = this self._api = opts.api self.event = new EventManager() - self.data = { _listen: true, _replay: false, journal: [], _createdContracts: {}, _createdContractsReverse: {}, _usedAccounts: {}, _abis: {} } + self.data = { _listen: true, _replay: false, journal: [], _createdContracts: {}, _createdContractsReverse: {}, _usedAccounts: {}, _abis: {}, _contractABIReferences: {} } opts.events.executioncontext.register('contextChanged', () => { self.clearAll() }) @@ -27,7 +28,7 @@ class Recorder { // convert to and from to tokens if (this.data._listen) { - var record = { value, parameters: { definitions: payLoad.funAbi, values: payLoad.funArgs } } + var record = { value, parameters: payLoad.funArgs } if (!to) { var selectedContract = self._api.getSelectedContract() if (selectedContract) { @@ -37,11 +38,18 @@ class Recorder { record.contractName = selectedContract.name record.bytecode = payLoad.contractBytecode self.data._abis[sha3] = abi + + this.data._contractABIReferences[timestamp] = sha3 } } else { - record.to = `created-contract{${this.data._createdContracts[to]}}` + var creationTimestamp = this.data._createdContracts[to] + record.to = `created-contract{${creationTimestamp}}` + record.abi = this.data._contractABIReferences[creationTimestamp] } + record.name = payLoad.funAbi.name + record.type = payLoad.funAbi.type + self._api.getAccounts((error, accounts) => { if (error) return console.log(error) record.from = `account{${accounts.indexOf(from)}}` @@ -92,6 +100,20 @@ class Recorder { return record } + /** + * resolve ABI reference from the timestamp + * + * @param {Object} record + * + */ + resolveABIReference (record) { + if (record.to) { + var timestamp = /created-contract{(.*)}/g.exec(record.to) + return this.data._contractABIReferences[timestamp[1]] + } + return null + } + /** * save the given @arg record * @@ -133,6 +155,7 @@ class Recorder { self.data._createdContractsReverse = {} self.data._usedAccounts = {} self.data._abis = {} + self.data._contractABIReferences = {} } /** @@ -149,9 +172,27 @@ class Recorder { self.setListen(false) async.eachSeries(records, function (tx, cb) { var record = self.resolveAddress(tx.record, accounts, options) - var data = format.encodeData(tx.record.parameters.definitions, tx.record.parameters.values, tx.record.bytecode) + var abi = abis[tx.record.abi] + if (!abi) { + modal.alert('cannot find ABI for ' + tx.record.abi + '. Execution stopped)') + return + } + var fnABI + if (tx.record.type === 'constructor') { + fnABI = txHelper.getConstructorInterface(abi) + } else { + fnABI = txHelper.getFunction(abi, record.name) + } + if (!fnABI) { + modal.alert('cannot resolve abi of ' + JSON.stringify(record, null, '\t') + '. Execution stopped') + cb('cannot resolve abi') + return + } + var data = format.encodeData(fnABI, tx.record.parameters, tx.record.bytecode) if (data.error) { - modal.alert(data.error) + modal.alert(data.error + '. Record:' + JSON.stringify(record, null, '\t')) + cb(data.error) + return } else { record.data = data.data } @@ -160,13 +201,11 @@ class Recorder { console.error(err) } else { var address = executionContext.isVM() ? txResult.result.createdAddress : txResult.result.contractAddress - if (!address) return // not a contract creation - address = addressToString(address) - // 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) { + if (address) { + address = addressToString(address) + // save back created addresses for the convertion from tokens to real adresses + self.data._createdContracts[address] = tx.timestamp + self.data._createdContractsReverse[tx.timestamp] = address newContractFn(abi, address, record.contractName) } } From 767cdd128d8a87a706ed017cc67d451633a3e4c9 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 7 Dec 2017 14:46:03 +0100 Subject: [PATCH 46/74] don't call all the constant function at load --- src/universal-dapp.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 380fead9bf..3bf6fc3500 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -432,7 +432,6 @@ UniversalDApp.prototype.getCallButton = function (args) { if (lookupOnly) { contractProperty.classList.add(css.constant) button.setAttribute('title', (title + ' - call')) - call(false) } if (args.funABI.inputs && args.funABI.inputs.length > 0) { From 7c7977a4dde0e3cd4ad1ce4c79aa04fda8dafb25 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 7 Dec 2017 14:46:36 +0100 Subject: [PATCH 47/74] add log --- src/recorder.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/recorder.js b/src/recorder.js index 4ca816640e..6070c1b611 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -170,6 +170,7 @@ class Recorder { run (records, accounts, options, abis, newContractFn) { var self = this self.setListen(false) + self._api.logMessage('Running transactions ...') async.eachSeries(records, function (tx, cb) { var record = self.resolveAddress(tx.record, accounts, options) var abi = abis[tx.record.abi] @@ -199,6 +200,7 @@ class Recorder { self._api.udapp().rerunTx(record, function (err, txResult) { if (err) { console.error(err) + self._api.logMessage(err + '. Execution stopped') } else { var address = executionContext.isVM() ? txResult.result.createdAddress : txResult.result.contractAddress if (address) { From 16f121516c737121490eebe816c045e39f130fb9 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 7 Dec 2017 14:47:13 +0100 Subject: [PATCH 48/74] rerun => run --- src/recorder.js | 2 +- src/universal-dapp.js | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/recorder.js b/src/recorder.js index 6070c1b611..2109c1e99f 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -197,7 +197,7 @@ class Recorder { } else { record.data = data.data } - self._api.udapp().rerunTx(record, function (err, txResult) { + self._api.udapp().runTx(record, function (err, txResult) { if (err) { console.error(err) self._api.logMessage(err + '. Execution stopped') diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 3bf6fc3500..6ca990f3ef 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -464,20 +464,19 @@ function execute (pipeline, env, callback) { next(null, env) } -UniversalDApp.prototype.rerunTx = function (args, cb) { - var self = this - var tx = { to: args.to, from: args.from, data: args.data, useCall: args.useCall } - var pipeline = [queryGasLimit, runTransaction] - var env = { self, args, tx } - execute(pipeline, env, cb) -} - UniversalDApp.prototype.runTx = function (args, cb) { var self = this - var tx = { to: args.to, data: args.data.dataHex, useCall: args.useCall } + var tx = { to: args.to, data: args.data.dataHex, useCall: args.useCall, from: args.from, value: args.value } var payLoad = { funAbi: args.data.funAbi, funArgs: args.data.funArgs, contractBytecode: args.data.contractBytecode } // contains decoded parameters - var pipeline = [queryGasLimit, queryValue, queryAddress, runTransaction] - var env = { self, args, tx, payLoad } + var pipeline = [queryGasLimit] + if (!args.value) { + pipeline.push(queryValue) + } + if (!args.from) { + pipeline.push(queryAddress) + } + pipeline.push(runTransaction) + var env = { self, tx, payLoad } execute(pipeline, env, cb) } From 8af71ab32d4be24c7e5596846d35e25d802e6fb4 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 7 Dec 2017 14:47:49 +0100 Subject: [PATCH 49/74] fix data structure --- src/app/execution/txFormat.js | 3 ++- src/recorder.js | 4 ++-- src/universal-dapp.js | 8 ++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/app/execution/txFormat.js b/src/app/execution/txFormat.js index 484ded1d0a..9ff1e3c4d6 100644 --- a/src/app/execution/txFormat.js +++ b/src/app/execution/txFormat.js @@ -143,7 +143,8 @@ module.exports = { }, callbackStep) } else { callbackStep(`creation of library ${libraryName} pending...`) - udapp.runTx({ data: bytecode, useCall: false }, (err, txResult) => { + var data = {dataHex: bytecode} + udapp.runTx({ data: data, useCall: false }, (err, txResult) => { if (err) { return callback(err) } diff --git a/src/recorder.js b/src/recorder.js index 2109c1e99f..c260481222 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -195,7 +195,7 @@ class Recorder { cb(data.error) return } else { - record.data = data.data + record.data = { dataHex: data.data, funArgs: tx.record.parameters, funAbi: fnABI, contractBytecode: tx.record.bytecode } } self._api.udapp().runTx(record, function (err, txResult) { if (err) { @@ -211,7 +211,7 @@ class Recorder { newContractFn(abi, address, record.contractName) } } - cb() + cb(err) }) }, () => { self.setListen(true) }) } diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 6ca990f3ef..c3e8dd2d67 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -527,14 +527,14 @@ function queryAddress (env, next) { } function runTransaction (env, next) { - var { self, args, tx, payLoad } = env + var { self, tx, payLoad } = env var timestamp = Date.now() self.event.trigger('initiatingTransaction', [timestamp, tx, payLoad]) self.txRunner.rawRun(tx, function (error, result) { - if (!args.useCall) { - self.event.trigger('transactionExecuted', [error, args.from, args.to, args.data, false, result, timestamp, payLoad]) + if (!tx.useCall) { + self.event.trigger('transactionExecuted', [error, tx.from, tx.to, tx.data, false, result, timestamp, payLoad]) } else { - self.event.trigger('callExecuted', [error, args.from, args.to, args.data, true, result, timestamp, payLoad]) + self.event.trigger('callExecuted', [error, tx.from, tx.to, tx.data, true, result, timestamp, payLoad]) } if (error) { if (typeof (error) !== 'string') { From d8d7241a6a5a367c0cd4818df361c9fc35d4c39b Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 7 Dec 2017 15:11:02 +0100 Subject: [PATCH 50/74] fix library deploy --- src/app/execution/txFormat.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/execution/txFormat.js b/src/app/execution/txFormat.js index 9ff1e3c4d6..78be718715 100644 --- a/src/app/execution/txFormat.js +++ b/src/app/execution/txFormat.js @@ -79,7 +79,7 @@ module.exports = { callback('Error deploying required libraries: ' + err) } else { bytecodeToDeploy = bytecode + dataHex - return callback(null, bytecodeToDeploy) + return callback(null, { dataHex: bytecodeToDeploy, funAbi, funArgs, contractBytecode }) } }, callbackStep) return @@ -143,7 +143,7 @@ module.exports = { }, callbackStep) } else { callbackStep(`creation of library ${libraryName} pending...`) - var data = {dataHex: bytecode} + var data = {dataHex: bytecode, funAbi: {type: 'constructor'}, funArgs: [], contractBytecode: bytecode} udapp.runTx({ data: data, useCall: false }, (err, txResult) => { if (err) { return callback(err) From 01ae71fe79fee1a71ca506ebb4ad2b4e4dff436c Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 7 Dec 2017 15:36:46 +0100 Subject: [PATCH 51/74] fix library deploy --- src/app/execution/txFormat.js | 15 ++++++++------- src/app/tabs/run-tab.js | 2 +- src/recorder.js | 8 ++++---- src/universal-dapp.js | 4 ++-- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/app/execution/txFormat.js b/src/app/execution/txFormat.js index 78be718715..d54989f186 100644 --- a/src/app/execution/txFormat.js +++ b/src/app/execution/txFormat.js @@ -35,6 +35,7 @@ module.exports = { /** * build the transaction data * + * @param {String} contractName * @param {Object} contract - abi definition of the current contract. * @param {Object} contracts - map of all compiled contracts. * @param {Bool} isConstructor - isConstructor. @@ -44,7 +45,7 @@ module.exports = { * @param {Function} callback - callback * @param {Function} callbackStep - callbackStep */ - buildData: function (contract, contracts, isConstructor, funAbi, params, udapp, callback, callbackStep) { + buildData: function (contractName, contract, contracts, isConstructor, funAbi, params, udapp, callback, callbackStep) { var funArgs = '' try { funArgs = $.parseJSON('[' + params + ']') @@ -79,7 +80,7 @@ module.exports = { callback('Error deploying required libraries: ' + err) } else { bytecodeToDeploy = bytecode + dataHex - return callback(null, { dataHex: bytecodeToDeploy, funAbi, funArgs, contractBytecode }) + return callback(null, {dataHex: bytecodeToDeploy, funAbi, funArgs, contractBytecode, contractName: contractName}) } }, callbackStep) return @@ -89,7 +90,7 @@ module.exports = { } else { dataHex = Buffer.concat([helper.encodeFunctionId(funAbi), data]).toString('hex') } - callback(null, { dataHex, funAbi, funArgs, contractBytecode }) + callback(null, { dataHex, funAbi, funArgs, contractBytecode, contractName: contractName }) }, atAddress: function () {}, @@ -116,7 +117,7 @@ module.exports = { if (!library) { return callback('Library ' + libraryName + ' not found.') } - this.deployLibrary(libraryName, library, contracts, udapp, (err, address) => { + this.deployLibrary(libraryName, libraryShortName, library, contracts, udapp, (err, address) => { if (err) { return callback(err) } @@ -130,7 +131,7 @@ module.exports = { }, callbackStep) }, - deployLibrary: function (libraryName, library, contracts, udapp, callback, callbackStep) { + deployLibrary: function (libraryName, libraryShortName, library, contracts, udapp, callback, callbackStep) { var address = library.address if (address) { return callback(null, address) @@ -139,11 +140,11 @@ module.exports = { if (bytecode.indexOf('_') >= 0) { this.linkBytecode(libraryName, contracts, udapp, (err, bytecode) => { if (err) callback(err) - else this.deployLibrary(libraryName, library, contracts, udapp, callback, callbackStep) + else this.deployLibrary(libraryName, libraryShortName, library, contracts, udapp, callback, callbackStep) }, callbackStep) } else { callbackStep(`creation of library ${libraryName} pending...`) - var data = {dataHex: bytecode, funAbi: {type: 'constructor'}, funArgs: [], contractBytecode: bytecode} + var data = {dataHex: bytecode, funAbi: {type: 'constructor'}, funArgs: [], contractBytecode: bytecode, contractName: libraryShortName} udapp.runTx({ data: data, useCall: false }, (err, txResult) => { if (err) { return callback(err) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 59758d2e8a..4a97abe972 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -395,7 +395,7 @@ function contractDropdown (appAPI, appEvents, instanceContainer) { var constructor = txHelper.getConstructorInterface(selectedContract.contract.object.abi) var args = createButtonInput.value - txFormat.buildData(selectedContract.contract.object, appAPI.getContracts(), true, constructor, args, appAPI.udapp(), (error, data) => { + txFormat.buildData(selectedContract.name, selectedContract.contract.object, appAPI.getContracts(), true, constructor, args, appAPI.udapp(), (error, data) => { if (!error) { appAPI.logMessage(`creation of ${selectedContract.name} pending...`) txExecution.createContract(data, appAPI.udapp(), (error, txResult) => { diff --git a/src/recorder.js b/src/recorder.js index c260481222..436ee7df41 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -30,12 +30,12 @@ class Recorder { if (this.data._listen) { var record = { value, parameters: payLoad.funArgs } if (!to) { - var selectedContract = self._api.getSelectedContract() + var selectedContract = self._api.getContract(payLoad.contractName) if (selectedContract) { - var abi = selectedContract.contract.object.abi + var abi = selectedContract.object.abi var sha3 = ethutil.bufferToHex(ethutil.sha3(abi)) record.abi = sha3 - record.contractName = selectedContract.name + record.contractName = payLoad.contractName record.bytecode = payLoad.contractBytecode self.data._abis[sha3] = abi @@ -195,7 +195,7 @@ class Recorder { cb(data.error) return } else { - record.data = { dataHex: data.data, funArgs: tx.record.parameters, funAbi: fnABI, contractBytecode: tx.record.bytecode } + record.data = { dataHex: data.data, funArgs: tx.record.parameters, funAbi: fnABI, contractBytecode: tx.record.bytecode, contractName: tx.record.contractName } } self._api.udapp().runTx(record, function (err, txResult) { if (err) { diff --git a/src/universal-dapp.js b/src/universal-dapp.js index c3e8dd2d67..de5762a45b 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -381,7 +381,7 @@ UniversalDApp.prototype.getCallButton = function (args) { logMsg = `call to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}` } } - txFormat.buildData(args.contractAbi, self.contracts, false, args.funABI, inputField.value, self, (error, data) => { + txFormat.buildData(args.contractName, args.contractAbi, self.contracts, false, args.funABI, inputField.value, self, (error, data) => { if (!error) { if (isUserAction) { if (!args.funABI.constant) { @@ -467,7 +467,7 @@ function execute (pipeline, env, callback) { UniversalDApp.prototype.runTx = function (args, cb) { var self = this var tx = { to: args.to, data: args.data.dataHex, useCall: args.useCall, from: args.from, value: args.value } - var payLoad = { funAbi: args.data.funAbi, funArgs: args.data.funArgs, contractBytecode: args.data.contractBytecode } // contains decoded parameters + var payLoad = { funAbi: args.data.funAbi, funArgs: args.data.funArgs, contractBytecode: args.data.contractBytecode, contractName: args.data.contractName } // contains decoded parameters var pipeline = [queryGasLimit] if (!args.value) { pipeline.push(queryValue) From 98b1890077757b8f28ecb9cb80f7f9ad51333e6a Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 7 Dec 2017 16:46:37 +0100 Subject: [PATCH 52/74] add link references --- src/app/execution/txFormat.js | 13 ++++++++----- src/app/tabs/run-tab.js | 3 ++- src/recorder.js | 24 ++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/app/execution/txFormat.js b/src/app/execution/txFormat.js index d54989f186..296feefac4 100644 --- a/src/app/execution/txFormat.js +++ b/src/app/execution/txFormat.js @@ -156,18 +156,21 @@ module.exports = { } }, - linkLibraryStandard: function (libraryName, address, contract) { - var bytecode = contract.evm.bytecode.object - for (var file in contract.evm.bytecode.linkReferences) { - for (var libName in contract.evm.bytecode.linkReferences[file]) { + linkLibraryStandardFromlinkReferences: function (libraryName, address, bytecode, linkReferences) { + for (var file in linkReferences) { + for (var libName in linkReferences[file]) { if (libraryName === libName) { - bytecode = this.setLibraryAddress(address, bytecode, contract.evm.bytecode.linkReferences[file][libName]) + bytecode = this.setLibraryAddress(address, bytecode, linkReferences[file][libName]) } } } return bytecode }, + linkLibraryStandard: function (libraryName, address, contract) { + return this.linkLibraryStandardFromlinkReferences(libraryName, address, contract.evm.bytecode.object, contract.evm.bytecode.linkReferences) + }, + setLibraryAddress: function (address, bytecodeToLink, positions) { if (positions) { for (var pos of positions) { diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 4a97abe972..8067730d0e 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -303,12 +303,13 @@ function makeRecorder (appAPI, appEvents) { var accounts = obj.accounts || [] var options = obj.options var abis = obj.abis + var linkReferences = obj.linkReferences || {} } catch (e) { return modalDialogCustom.alert('Invalid Scenario File, please try again') } if (txArray.length) { noInstancesText.style.display = 'none' - recorder.run(txArray, accounts, options, abis, (abi, address, contractName) => { + recorder.run(txArray, accounts, options, abis, linkReferences, (abi, address, contractName) => { instanceContainer.appendChild(appAPI.udapp().renderInstanceFromABI(abi, address, contractName)) }) } diff --git a/src/recorder.js b/src/recorder.js index 436ee7df41..3247021107 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -17,7 +17,7 @@ class Recorder { var self = this self._api = opts.api self.event = new EventManager() - self.data = { _listen: true, _replay: false, journal: [], _createdContracts: {}, _createdContractsReverse: {}, _usedAccounts: {}, _abis: {}, _contractABIReferences: {} } + self.data = { _listen: true, _replay: false, journal: [], _createdContracts: {}, _createdContractsReverse: {}, _usedAccounts: {}, _abis: {}, _contractABIReferences: {}, _linkReferences: {} } opts.events.executioncontext.register('contextChanged', () => { self.clearAll() }) @@ -37,6 +37,14 @@ class Recorder { record.abi = sha3 record.contractName = payLoad.contractName record.bytecode = payLoad.contractBytecode + record.linkReferences = selectedContract.object.evm.bytecode.linkReferences + if (Object.keys(record.linkReferences).length) { + for (var file in record.linkReferences) { + for (var lib in record.linkReferences[file]) { + self.data._linkReferences[lib] = '
' + } + } + } self.data._abis[sha3] = abi this.data._contractABIReferences[timestamp] = sha3 @@ -140,6 +148,7 @@ class Recorder { var stampB = B.timestamp return stampA - stampB }), + linkReferences: self.data._linkReferences, abis: self.data._abis } } @@ -150,12 +159,15 @@ class Recorder { */ clearAll () { var self = this + self.data._listen = true + self.data._replay = false self.data.journal = [] self.data._createdContracts = {} self.data._createdContractsReverse = {} self.data._usedAccounts = {} self.data._abis = {} self.data._contractABIReferences = {} + self.data._linkReferences = {} } /** @@ -167,7 +179,7 @@ class Recorder { * @param {Function} newContractFn * */ - run (records, accounts, options, abis, newContractFn) { + run (records, accounts, options, abis, linkReferences, newContractFn) { var self = this self.setListen(false) self._api.logMessage('Running transactions ...') @@ -178,6 +190,14 @@ class Recorder { modal.alert('cannot find ABI for ' + tx.record.abi + '. Execution stopped)') return } + /* Resolve Library */ + if (record.linkReferences) { + for (var k in linkReferences) { + var link = linkReferences[k] + tx.record.bytecode = format.linkLibraryStandardFromlinkReferences(k, link.replace('0x', ''), tx.record.bytecode, tx.record.linkReferences) + } + } + /* Encode params */ var fnABI if (tx.record.type === 'constructor') { fnABI = txHelper.getConstructorInterface(abi) From c9538c0dfb09e7841c291490ed582c5095dcbfad Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 7 Dec 2017 18:09:56 +0100 Subject: [PATCH 53/74] add test recorder --- src/app/tabs/run-tab.js | 4 +- src/universal-dapp.js | 4 +- test-browser/helpers/contracts.js | 56 +++++++++-- test-browser/tests/compiling.js | 4 +- test-browser/tests/units/testRecorder.js | 118 +++++++++++++++++++++++ 5 files changed, 174 insertions(+), 12 deletions(-) create mode 100644 test-browser/tests/units/testRecorder.js diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 8067730d0e..cc377ee5c6 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -269,8 +269,8 @@ function makeRecorder (appAPI, appEvents) { width: 135px; } ` - var recordButton = yo`` - var runButton = yo`` + var recordButton = yo`` + var runButton = yo`` var el = yo`
${recordButton} diff --git a/src/universal-dapp.js b/src/universal-dapp.js index de5762a45b..f7da8f5174 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -290,10 +290,10 @@ UniversalDApp.prototype.renderInstanceFromABI = function (contractABI, address, function remove () { instance.remove() } - var instance = yo`
` + address = (address.slice(0, 2) === '0x' ? '' : '0x') + address.toString('hex') + var instance = yo`
` var context = executionContext.isVM() ? 'memory' : 'blockchain' - address = (address.slice(0, 2) === '0x' ? '' : '0x') + address.toString('hex') var shortAddress = helper.shortenAddress(address) var title = yo`
${contractName} at ${shortAddress} (${context})
diff --git a/test-browser/helpers/contracts.js b/test-browser/helpers/contracts.js index 45798bb2c4..e7a193c5eb 100644 --- a/test-browser/helpers/contracts.js +++ b/test-browser/helpers/contracts.js @@ -11,7 +11,9 @@ module.exports = { checkDebug, goToVMtraceStep, useFilter, - addInstance + addInstance, + clickFunction, + verifyCallReturnValue } function getCompiledContracts (browser, compiled, callback) { @@ -65,6 +67,46 @@ function testContracts (browser, fileName, contractCode, compiledContractNames, }) } +function clickFunction (fnFullName, expectedInput) { + this.waitForElementPresent('.instance button[title="' + fnFullName + '"]') + .perform(function (client, done) { + client.execute(function () { + document.querySelector('#optionViews').scrollTop = document.querySelector('#optionViews').scrollHeight + }, [], function () { + if (expectedInput) { + client.setValue('#runTabView input[title="' + expectedInput.types + '"]', expectedInput.values, function () {}) + } + done() + }) + }) + .click('.instance button[title="' + fnFullName + '"]') + .pause(500) + return this +} + +function verifyCallReturnValue (browser, address, checks, done) { + browser.execute(function (address, checks) { + var nodes = document.querySelectorAll('#instance' + address + ' div[class^="contractProperty"] div[class^="value"]') + var ret = {sucess: true} + for (var k in checks) { + var text = nodes[k].innerText ? nodes[k].innerText : nodes[k].textContent + text = text.replace('\n', '') + if (checks[k] !== text) { + ret.sucess = false + ret.expected = checks[k] + ret.got = text + return ret + } + } + return ret + }, [address, checks], function (result) { + if (!result.value.sucess) { + browser.assert.fail('verifyCallReturnValue failed', JSON.stringify(result.value), '') + } + done() + }) +} + function testFunction (fnFullName, txHash, log, expectedInput, expectedReturn, expectedEvent) { // this => browser this.waitForElementPresent('.instance button[title="' + fnFullName + '"]') @@ -101,13 +143,13 @@ function testFunction (fnFullName, txHash, log, expectedInput, expectedReturn, e function addInstance (browser, address, done) { browser.setValue('.ataddressinput', address, function () { browser.click('div[class^="atAddress"]') - .perform((client) => { - browser.execute(function () { - document.querySelector('#modal-footer-ok').click() - }, [], function (result) { - done() + .perform((client) => { + browser.execute(function () { + document.querySelector('#modal-footer-ok').click() + }, [], function (result) { + done() + }) }) - }) }) } diff --git a/test-browser/tests/compiling.js b/test-browser/tests/compiling.js index b9704b2c50..1b6df78165 100644 --- a/test-browser/tests/compiling.js +++ b/test-browser/tests/compiling.js @@ -3,6 +3,7 @@ var contractHelper = require('../helpers/contracts') var init = require('../helpers/init') var sauce = require('./sauce') var async = require('async') +var testRecorder = require('./units/testRecorder') module.exports = { before: function (browser, done) { @@ -19,12 +20,13 @@ module.exports = { function runTests (browser) { browser.testFunction = contractHelper.testFunction + browser.clickFunction = contractHelper.clickFunction browser .waitForElementVisible('.newFile', 10000) .click('.compileView') .perform(() => { // the first fn is used to pass browser to the other ones. - async.waterfall([function (callback) { callback(null, browser) }, testSimpleContract, testReturnValues, testInputValues], function () { + async.waterfall([function (callback) { callback(null, browser) }, testSimpleContract, testReturnValues, testInputValues, testRecorder], function () { browser.end() }) }) diff --git a/test-browser/tests/units/testRecorder.js b/test-browser/tests/units/testRecorder.js new file mode 100644 index 0000000000..188687b6b0 --- /dev/null +++ b/test-browser/tests/units/testRecorder.js @@ -0,0 +1,118 @@ +'use strict' +var contractHelper = require('../../helpers/contracts') + +module.exports = function (browser, callback) { + contractHelper.addFile(browser, 'scenario.json', {content: records}, () => { + browser + .click('.runView') + .click('#runTabView .runtransaction') + .clickFunction('getInt - call') + .clickFunction('getAddress - call') + .waitForElementPresent('div[class^="contractProperty"] div[class^="value"]') + .perform(() => { + contractHelper.verifyCallReturnValue(browser, '0xec5bee2dbb67da8757091ad3d9526ba3ed2e2137', ['0: uint256: 1', '0: address: 0xca35b7d915458ef540ade6068dfe2f44e8fa733c'], () => { callback() }) + }) + }) +} + + +var records = `{ + "accounts": { + "account{0}": "0xca35b7d915458ef540ade6068dfe2f44e8fa733c" + }, + "transactions": [ + { + "timestamp": 1512661974836, + "record": { + "value": "0", + "parameters": [ + 23 + ], + "abi": "0xe8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c", + "contractName": "test", + "bytecode": "6060604052341561000f57600080fd5b6040516020806102098339810160405280805190602001909190505080600081905550506101c7806100426000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632f30c6f61461005c57806338cc48311461009e57806362738998146100f3575b600080fd5b341561006757600080fd5b61009c600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061011c565b005b34156100a957600080fd5b6100b1610168565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100fe57600080fd5b610106610192565b6040518082815260200191505060405180910390f35b8160008190555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600080549050905600a165627a7a7230582021de204df5493bae860efacfbdea0118cbb3d73760ad48ee0838d07d37fe3c9e0029", + "linkReferences": {}, + "name": "", + "type": "constructor", + "from": "account{0}" + } + }, + { + "timestamp": 1512661987446, + "record": { + "value": "0", + "parameters": [ + 1, + "0xca35b7d915458ef540ade6068dfe2f44e8fa733c" + ], + "to": "created-contract{1512661974836}", + "abi": "0xe8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c", + "name": "set", + "type": "function", + "from": "account{0}" + } + } + ], + "linkReferences": {}, + "abis": { + "0xe8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c": [ + { + "constant": true, + "inputs": [], + "name": "getInt", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getAddress", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_t", + "type": "uint256" + }, + { + "name": "_add", + "type": "address" + } + ], + "name": "set", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "name": "_r", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + } + ] + } +}` From eeae86a96ea78264ff516d04a104de08e93f4470 Mon Sep 17 00:00:00 2001 From: yann300 Date: Sat, 9 Dec 2017 11:34:56 +0100 Subject: [PATCH 54/74] add clear button --- src/app/tabs/run-tab.js | 25 ++++++++++++++++--------- src/recorder.js | 3 +++ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index cc377ee5c6..5e3743704e 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -9,6 +9,7 @@ var modalDialogCustom = require('../ui/modal-dialog-custom') var executionContext = require('../../execution-context') var copyToClipboard = require('../ui/copy-to-clipboard') var Recorder = require('../../recorder') +var EventManager = require('remix-lib').EventManager // -------------- styling ---------------------- var csjs = require('csjs-inject') @@ -172,6 +173,7 @@ var noInstancesText = yo`
0 contract Instance var pendingTxsText = yo`
` function runTab (container, appAPI, appEvents, opts) { + var events = new EventManager() var pendingTxsContainer = yo`
${pendingTxsText} @@ -180,7 +182,7 @@ function runTab (container, appAPI, appEvents, opts) { var el = yo`
${settings(appAPI, appEvents)} - ${contractDropdown(appAPI, appEvents, instanceContainer)} + ${contractDropdown(events, appAPI, appEvents, instanceContainer)} ${pendingTxsContainer} ${instanceContainer}
@@ -200,11 +202,8 @@ function runTab (container, appAPI, appEvents, opts) { // set the final context. Cause it is possible that this is not the one we've originaly selected selectExEnv.value = executionContext.getProvider() fillAccountsList(appAPI, el) + clearInstance() }) - - instanceContainer.innerHTML = '' // clear the instances list - noInstancesText.style.display = 'block' - instanceContainer.appendChild(noInstancesText) }) selectExEnv.value = executionContext.getProvider() fillAccountsList(appAPI, el) @@ -212,6 +211,13 @@ function runTab (container, appAPI, appEvents, opts) { updateAccountBalances(container, appAPI) updatePendingTxs(container, appAPI) }, 500) + + var clearInstance = function () { + instanceContainer.innerHTML = '' // clear the instances list + noInstancesText.style.display = 'block' + instanceContainer.appendChild(noInstancesText) + events.trigger('clearInstance', []) + } } function fillAccountsList (appAPI, container) { @@ -244,11 +250,12 @@ function updateAccountBalances (container, appAPI) { /* ------------------------------------------------ RECORDER ------------------------------------------------ */ -function makeRecorder (appAPI, appEvents) { +function makeRecorder (events, appAPI, appEvents) { var recorder = new Recorder({ events: { udapp: appEvents.udapp, - executioncontext: executionContext.event + executioncontext: executionContext.event, + runtab: events }, api: appAPI }) @@ -323,7 +330,7 @@ function makeRecorder (appAPI, appEvents) { section CONTRACT DROPDOWN and BUTTONS ------------------------------------------------ */ -function contractDropdown (appAPI, appEvents, instanceContainer) { +function contractDropdown (events, appAPI, appEvents, instanceContainer) { instanceContainer.appendChild(noInstancesText) var compFails = yo`` appEvents.compiler.register('compilationFinished', function (success, data, source) { @@ -364,7 +371,7 @@ function contractDropdown (appAPI, appEvents, instanceContainer) { ${atAddressButtonInput}
At Address
-
${makeRecorder(appAPI, appEvents)}
+
${makeRecorder(events, appAPI, appEvents)}
` diff --git a/src/recorder.js b/src/recorder.js index 3247021107..a8fe1b7aef 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -21,6 +21,9 @@ class Recorder { opts.events.executioncontext.register('contextChanged', () => { self.clearAll() }) + opts.events.runtab.register('clearInstances', () => { + self.clearAll() + }) opts.events.udapp.register('initiatingTransaction', (timestamp, tx, payLoad) => { if (tx.useCall) return From 6a1f4563f64a1cedc9c118a77b08105a10d26703 Mon Sep 17 00:00:00 2001 From: yann300 Date: Sat, 9 Dec 2017 15:37:01 +0100 Subject: [PATCH 55/74] rename token createdcontract=> created --- src/recorder.js | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/recorder.js b/src/recorder.js index a8fe1b7aef..f4b221e159 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -54,7 +54,7 @@ class Recorder { } } else { var creationTimestamp = this.data._createdContracts[to] - record.to = `created-contract{${creationTimestamp}}` + record.to = `created{${creationTimestamp}}` record.abi = this.data._contractABIReferences[creationTimestamp] } @@ -93,6 +93,14 @@ class Recorder { this.data._replay = !listen } + extractTimestamp (value) { + var stamp = /created{(.*)}/g.exec(value) + if (stamp) { + return stamp[1] + } + return null + } + /** * convert back from/to from tokens to real addresses * @@ -103,28 +111,16 @@ class Recorder { */ resolveAddress (record, accounts, options) { if (record.to) { - var timestamp = /created-contract{(.*)}/g.exec(record.to) - record.to = this.data._createdContractsReverse[timestamp[1]] + var stamp = this.extractTimestamp(record.to) + if (stamp) { + record.to = this.data._createdContractsReverse[stamp] + } } record.from = accounts[record.from] // @TODO: writing browser test return record } - /** - * resolve ABI reference from the timestamp - * - * @param {Object} record - * - */ - resolveABIReference (record) { - if (record.to) { - var timestamp = /created-contract{(.*)}/g.exec(record.to) - return this.data._contractABIReferences[timestamp[1]] - } - return null - } - /** * save the given @arg record * From 0eb95eeab78294d9c296cd6f7a4e00fcc6e7ce72 Mon Sep 17 00:00:00 2001 From: yann300 Date: Sat, 9 Dec 2017 15:37:27 +0100 Subject: [PATCH 56/74] put link reference first --- src/recorder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/recorder.js b/src/recorder.js index f4b221e159..5672fd0866 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -142,12 +142,12 @@ class Recorder { var records = [].concat(self.data.journal) return { accounts: self.data._usedAccounts, + linkReferences: self.data._linkReferences, transactions: records.sort((A, B) => { var stampA = A.timestamp var stampB = B.timestamp return stampA - stampB }), - linkReferences: self.data._linkReferences, abis: self.data._abis } } From 727e3f1a0cc014e17dc711c93738758616a0fc54 Mon Sep 17 00:00:00 2001 From: yann300 Date: Sat, 9 Dec 2017 15:38:29 +0100 Subject: [PATCH 57/74] add log --- src/recorder.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/recorder.js b/src/recorder.js index 5672fd0866..d2405b4981 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -181,12 +181,12 @@ class Recorder { run (records, accounts, options, abis, linkReferences, newContractFn) { var self = this self.setListen(false) - self._api.logMessage('Running transactions ...') - async.eachSeries(records, function (tx, cb) { + self._api.logMessage(`Running ${records.length} transaction(s) ...`) + async.eachOfSeries(records, function (tx, index, cb) { var record = self.resolveAddress(tx.record, accounts, options) var abi = abis[tx.record.abi] if (!abi) { - modal.alert('cannot find ABI for ' + tx.record.abi + '. Execution stopped)') + modal.alert('cannot find ABI for ' + tx.record.abi + '. Execution stopped at ' + index) return } /* Resolve Library */ @@ -204,22 +204,24 @@ class Recorder { fnABI = txHelper.getFunction(abi, record.name) } if (!fnABI) { - modal.alert('cannot resolve abi of ' + JSON.stringify(record, null, '\t') + '. Execution stopped') + modal.alert('cannot resolve abi of ' + JSON.stringify(record, null, '\t') + '. Execution stopped at ' + index) cb('cannot resolve abi') return } var data = format.encodeData(fnABI, tx.record.parameters, tx.record.bytecode) if (data.error) { - modal.alert(data.error + '. Record:' + JSON.stringify(record, null, '\t')) + modal.alert(data.error + '. Record:' + JSON.stringify(record, null, '\t') + '. Execution stopped at ' + index) cb(data.error) return } else { + self._api.logMessage(`(${index}) ${JSON.stringify(record, null, '\t')}`) + self._api.logMessage(`(${index}) data: ${data.data}`) record.data = { dataHex: data.data, funArgs: tx.record.parameters, funAbi: fnABI, contractBytecode: tx.record.bytecode, contractName: tx.record.contractName } } self._api.udapp().runTx(record, function (err, txResult) { if (err) { console.error(err) - self._api.logMessage(err + '. Execution stopped') + self._api.logMessage(err + '. Execution failed at ' + index) } else { var address = executionContext.isVM() ? txResult.result.createdAddress : txResult.result.contractAddress if (address) { From 5695a6b563c76d84efdada83fbb51bf0fe5bb250 Mon Sep 17 00:00:00 2001 From: yann300 Date: Sat, 9 Dec 2017 15:38:56 +0100 Subject: [PATCH 58/74] allow using token for library linkage --- src/recorder.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/recorder.js b/src/recorder.js index d2405b4981..52c3df2843 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -190,9 +190,13 @@ class Recorder { return } /* Resolve Library */ - if (record.linkReferences) { + if (record.linkReferences && Object.keys(record.linkReferences).length) { for (var k in linkReferences) { var link = linkReferences[k] + var timestamp = self.extractTimestamp(link) + if (timestamp && self.data._createdContractsReverse[timestamp]) { + link = self.data._createdContractsReverse[timestamp] + } tx.record.bytecode = format.linkLibraryStandardFromlinkReferences(k, link.replace('0x', ''), tx.record.bytecode, tx.record.linkReferences) } } From fdaf388847053d099165b05952cd7c03a4d7b9ea Mon Sep 17 00:00:00 2001 From: yann300 Date: Sat, 9 Dec 2017 15:39:34 +0100 Subject: [PATCH 59/74] clear the cache after having played txs --- src/recorder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/recorder.js b/src/recorder.js index 52c3df2843..1e654fec69 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -238,7 +238,7 @@ class Recorder { } cb(err) }) - }, () => { self.setListen(true) }) + }, () => { self.setListen(true); self.clearAll() }) } } From fbc9a151b2768bc82066d7f96a68d721b5d47b02 Mon Sep 17 00:00:00 2001 From: yann300 Date: Sat, 9 Dec 2017 15:39:56 +0100 Subject: [PATCH 60/74] typo --- src/recorder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/recorder.js b/src/recorder.js index 1e654fec69..0b97d0cd06 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -114,7 +114,7 @@ class Recorder { var stamp = this.extractTimestamp(record.to) if (stamp) { record.to = this.data._createdContractsReverse[stamp] - } + } } record.from = accounts[record.from] // @TODO: writing browser test From cddfa27fa6a35382bdf1f54e7f3ded963ff4e46a Mon Sep 17 00:00:00 2001 From: yann300 Date: Sat, 9 Dec 2017 15:40:08 +0100 Subject: [PATCH 61/74] fix standard --- test-browser/tests/units/testRecorder.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test-browser/tests/units/testRecorder.js b/test-browser/tests/units/testRecorder.js index 188687b6b0..82a52b539b 100644 --- a/test-browser/tests/units/testRecorder.js +++ b/test-browser/tests/units/testRecorder.js @@ -15,7 +15,6 @@ module.exports = function (browser, callback) { }) } - var records = `{ "accounts": { "account{0}": "0xca35b7d915458ef540ade6068dfe2f44e8fa733c" From 0e4808df7e1710758145bc08d055149b22c03e9d Mon Sep 17 00:00:00 2001 From: yann300 Date: Sat, 9 Dec 2017 15:44:12 +0100 Subject: [PATCH 62/74] better testing --- test-browser/helpers/contracts.js | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/test-browser/helpers/contracts.js b/test-browser/helpers/contracts.js index e7a193c5eb..841a96368a 100644 --- a/test-browser/helpers/contracts.js +++ b/test-browser/helpers/contracts.js @@ -85,23 +85,17 @@ function clickFunction (fnFullName, expectedInput) { } function verifyCallReturnValue (browser, address, checks, done) { - browser.execute(function (address, checks) { + browser.execute(function (address) { var nodes = document.querySelectorAll('#instance' + address + ' div[class^="contractProperty"] div[class^="value"]') - var ret = {sucess: true} + var ret = [] for (var k in checks) { var text = nodes[k].innerText ? nodes[k].innerText : nodes[k].textContent - text = text.replace('\n', '') - if (checks[k] !== text) { - ret.sucess = false - ret.expected = checks[k] - ret.got = text - return ret - } + ret.push(text) } return ret - }, [address, checks], function (result) { - if (!result.value.sucess) { - browser.assert.fail('verifyCallReturnValue failed', JSON.stringify(result.value), '') + }, [address], function (result) { + for (var k in checks) { + browser.assert.equal(checks[k], result.value[k]) } done() }) From 4b8a8b2ce22f1e62a522d02f39ba310e2effa7b3 Mon Sep 17 00:00:00 2001 From: yann300 Date: Sat, 9 Dec 2017 15:53:55 +0100 Subject: [PATCH 63/74] test library linkage --- test-browser/tests/units/testRecorder.js | 87 +++++++++++++++++++----- 1 file changed, 71 insertions(+), 16 deletions(-) diff --git a/test-browser/tests/units/testRecorder.js b/test-browser/tests/units/testRecorder.js index 82a52b539b..9c587c6197 100644 --- a/test-browser/tests/units/testRecorder.js +++ b/test-browser/tests/units/testRecorder.js @@ -8,9 +8,10 @@ module.exports = function (browser, callback) { .click('#runTabView .runtransaction') .clickFunction('getInt - call') .clickFunction('getAddress - call') + .clickFunction('getFromLib - call') .waitForElementPresent('div[class^="contractProperty"] div[class^="value"]') .perform(() => { - contractHelper.verifyCallReturnValue(browser, '0xec5bee2dbb67da8757091ad3d9526ba3ed2e2137', ['0: uint256: 1', '0: address: 0xca35b7d915458ef540ade6068dfe2f44e8fa733c'], () => { callback() }) + contractHelper.verifyCallReturnValue(browser, '0xec5bee2dbb67da8757091ad3d9526ba3ed2e2137', ['0: uint256: 1', '1: uint256: 3456', '2: address: 0xca35b7d915458ef540ade6068dfe2f44e8fa733c'], () => { callback() }) }) }) } @@ -19,42 +20,82 @@ var records = `{ "accounts": { "account{0}": "0xca35b7d915458ef540ade6068dfe2f44e8fa733c" }, + "linkReferences": { + "testLib": "created{1512830014773}" + }, "transactions": [ { - "timestamp": 1512661974836, + "timestamp": 1512830014773, "record": { "value": "0", + "parameters": [], + "abi": "0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a", + "contractName": "testLib", + "bytecode": "60606040523415600e57600080fd5b60968061001c6000396000f300606060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680636d4ce63c146044575b600080fd5b604a6060565b6040518082815260200191505060405180910390f35b6000610d809050905600a165627a7a7230582022d123b15248b8176151f8d45c2dc132063bcc9bb8d5cd652aea7efae362c8050029", + "linkReferences": {}, + "type": "constructor", + "from": "account{0}" + } + }, + { + "timestamp": 1512830015080, + "record": { + "value": "100", "parameters": [ - 23 + 11 ], - "abi": "0xe8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c", + "abi": "0xc41589e7559804ea4a2080dad19d876a024ccb05117835447d72ce08c1d020ec", "contractName": "test", - "bytecode": "6060604052341561000f57600080fd5b6040516020806102098339810160405280805190602001909190505080600081905550506101c7806100426000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632f30c6f61461005c57806338cc48311461009e57806362738998146100f3575b600080fd5b341561006757600080fd5b61009c600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061011c565b005b34156100a957600080fd5b6100b1610168565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100fe57600080fd5b610106610192565b6040518082815260200191505060405180910390f35b8160008190555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600080549050905600a165627a7a7230582021de204df5493bae860efacfbdea0118cbb3d73760ad48ee0838d07d37fe3c9e0029", - "linkReferences": {}, + "bytecode": "60606040526040516020806102b183398101604052808051906020019091905050806000819055505061027a806100376000396000f300606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632f30c6f61461006757806338cc48311461009e57806362738998146100f357806387cc10e11461011c575b600080fd5b61009c600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610145565b005b34156100a957600080fd5b6100b1610191565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100fe57600080fd5b6101066101bb565b6040518082815260200191505060405180910390f35b341561012757600080fd5b61012f6101c4565b6040518082815260200191505060405180910390f35b8160008190555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008054905090565b600073__browser/ballot.sol:testLib____________636d4ce63c6000604051602001526040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b151561022e57600080fd5b6102c65a03f4151561023f57600080fd5b505050604051805190509050905600a165627a7a72305820e0b2510bb2890a0334bfe5613d96db3e72442e63b514cdeaee8fc2c6bbd19d3a0029", + "linkReferences": { + "browser/ballot.sol": { + "testLib": [ + { + "length": 20, + "start": 511 + } + ] + } + }, "name": "", "type": "constructor", "from": "account{0}" } }, { - "timestamp": 1512661987446, + "timestamp": 1512830034180, "record": { - "value": "0", + "value": "1000000000000000000", "parameters": [ 1, "0xca35b7d915458ef540ade6068dfe2f44e8fa733c" ], - "to": "created-contract{1512661974836}", - "abi": "0xe8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c", + "to": "created{1512830015080}", + "abi": "0xc41589e7559804ea4a2080dad19d876a024ccb05117835447d72ce08c1d020ec", "name": "set", "type": "function", "from": "account{0}" } } ], - "linkReferences": {}, "abis": { - "0xe8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c": [ + "0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a": [ + { + "constant": true, + "inputs": [], + "name": "get", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ], + "0xc41589e7559804ea4a2080dad19d876a024ccb05117835447d72ce08c1d020ec": [ { "constant": true, "inputs": [], @@ -69,6 +110,20 @@ var records = `{ "stateMutability": "view", "type": "function" }, + { + "constant": true, + "inputs": [], + "name": "getFromLib", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, { "constant": true, "inputs": [], @@ -97,8 +152,8 @@ var records = `{ ], "name": "set", "outputs": [], - "payable": false, - "stateMutability": "nonpayable", + "payable": true, + "stateMutability": "payable", "type": "function" }, { @@ -108,8 +163,8 @@ var records = `{ "type": "uint256" } ], - "payable": false, - "stateMutability": "nonpayable", + "payable": true, + "stateMutability": "payable", "type": "constructor" } ] From 0621c97198620ad22cf885a1243e67664b56dab0 Mon Sep 17 00:00:00 2001 From: yann300 Date: Sat, 9 Dec 2017 15:54:34 +0100 Subject: [PATCH 64/74] remove remix folder --- remix | 1 - 1 file changed, 1 deletion(-) delete mode 160000 remix diff --git a/remix b/remix deleted file mode 160000 index 4fe13da1c8..0000000000 --- a/remix +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4fe13da1c8c51dc7bb728df14c30fbb274e0bc80 From df23a3df4fc952d9fffe23d28e91771f85bc27d4 Mon Sep 17 00:00:00 2001 From: yann300 Date: Sat, 9 Dec 2017 19:29:33 +0100 Subject: [PATCH 65/74] fix tests --- ci/makeMockCompiler.js | 51 +++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/ci/makeMockCompiler.js b/ci/makeMockCompiler.js index 55d7ef6112..b9bafc3fc4 100644 --- a/ci/makeMockCompiler.js +++ b/ci/makeMockCompiler.js @@ -6,41 +6,30 @@ var soljson = require('../soljson') var compiler = solc(soljson) var compilerInput = require('../src/app/compiler/compiler-input') +var compilationResult = {} +gatherCompilationResults('./test-browser/tests/', compilationResult) +gatherCompilationResults('./test-browser/tests/units/', compilationResult) +replaceSolCompiler(compilationResult) -gatherCompilationResults(function (error, data) { - if (error) { - console.log(error) - process.exit(1) - } else { - replaceSolCompiler(data) - } -}) - -function gatherCompilationResults (callback) { - var compilationResult = {} - fs.readdir('./test-browser/tests', 'utf8', function (error, filenames) { - if (error) { - console.log(error) - process.exit(1) - } else { - filenames.map(function (item, i) { - var testDef = require('../test-browser/tests/' + item) - if ('@sources' in testDef) { - var sources = testDef['@sources']() - for (var files in sources) { - compile(sources[files], true, function (result) { - compilationResult[result.key] = result - }) - compile(sources[files], false, function (result) { - compilationResult[result.key] = result - }) - } +function gatherCompilationResults (dir, compilationResult, callback) { + var filenames = fs.readdirSync(dir, 'utf8') + filenames.map(function (item, i) { + if (item.endsWith('.js')) { + var testDef = require('.' + dir + item) + if ('@sources' in testDef) { + var sources = testDef['@sources']() + for (var files in sources) { + compile(sources[files], true, function (result) { + compilationResult[result.key] = result + }) + compile(sources[files], false, function (result) { + compilationResult[result.key] = result + }) } - }) - - callback(null, compilationResult) + } } }) + return compilationResult } function compile (source, optimization, addCompilationResult) { From fb6439c27abdd852d4a91b8fa3bbc2dd9d28dbf1 Mon Sep 17 00:00:00 2001 From: yann300 Date: Sun, 10 Dec 2017 12:00:16 +0100 Subject: [PATCH 66/74] standard --- ci/makeMockCompiler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/makeMockCompiler.js b/ci/makeMockCompiler.js index b9bafc3fc4..042e8f4176 100644 --- a/ci/makeMockCompiler.js +++ b/ci/makeMockCompiler.js @@ -26,7 +26,7 @@ function gatherCompilationResults (dir, compilationResult, callback) { compilationResult[result.key] = result }) } - } + } } }) return compilationResult From 0af5177d1f28003acc0431a034faeae5fa9fe57d Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 11 Dec 2017 11:56:07 +0100 Subject: [PATCH 67/74] add setEditorValue --- test-browser/helpers/contracts.js | 16 ++++++++++++++-- test-browser/tests/ballot.js | 1 + test-browser/tests/compiling.js | 1 + 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/test-browser/helpers/contracts.js b/test-browser/helpers/contracts.js index 841a96368a..3b53223d73 100644 --- a/test-browser/helpers/contracts.js +++ b/test-browser/helpers/contracts.js @@ -13,7 +13,8 @@ module.exports = { useFilter, addInstance, clickFunction, - verifyCallReturnValue + verifyCallReturnValue, + setEditorValue } function getCompiledContracts (browser, compiled, callback) { @@ -134,6 +135,17 @@ function testFunction (fnFullName, txHash, log, expectedInput, expectedReturn, e return this } +function setEditorValue (value) { + this.perform((client, done) => { + this.execute(function (value) { + document.getElementById('input').editor.session.setValue(value) + }, [value], function (result) { + done() + }) + }) + return this +} + function addInstance (browser, address, done) { browser.setValue('.ataddressinput', address, function () { browser.click('div[class^="atAddress"]') @@ -160,7 +172,7 @@ function addFile (browser, name, content, done) { done() }) }) - .setValue('#input textarea', content.content, function () {}) + .setEditorValue(content.content) .pause(1000) .perform(function () { done() diff --git a/test-browser/tests/ballot.js b/test-browser/tests/ballot.js index e6c6be3bb0..0bf04ed247 100644 --- a/test-browser/tests/ballot.js +++ b/test-browser/tests/ballot.js @@ -23,6 +23,7 @@ module.exports = { function runTests (browser, testData) { browser.testFunction = contractHelper.testFunction + browser.setEditorValue = contractHelper.setEditorValue browser .waitForElementVisible('.newFile', 10000) .click('.compileView') diff --git a/test-browser/tests/compiling.js b/test-browser/tests/compiling.js index 1b6df78165..0d13d93a5d 100644 --- a/test-browser/tests/compiling.js +++ b/test-browser/tests/compiling.js @@ -21,6 +21,7 @@ module.exports = { function runTests (browser) { browser.testFunction = contractHelper.testFunction browser.clickFunction = contractHelper.clickFunction + browser.setEditorValue = contractHelper.setEditorValue browser .waitForElementVisible('.newFile', 10000) .click('.compileView') From f4e889a674828c64e2ee46525c69b867add174b6 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 11 Dec 2017 11:56:17 +0100 Subject: [PATCH 68/74] fix tests --- test-browser/tests/ballot.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/test-browser/tests/ballot.js b/test-browser/tests/ballot.js index 0bf04ed247..648e840491 100644 --- a/test-browser/tests/ballot.js +++ b/test-browser/tests/ballot.js @@ -32,10 +32,10 @@ function runTests (browser, testData) { .click('.runView') .setValue('input[placeholder="uint8 _numProposals"]', '1', () => {}) .click('#runTabView div[class^="create"]') - .testFunction('delegate - transact (not payable)', '0xd3cd54e2f76f3993078ecf9e1b54a148def4520afc141a182293b3610bddf10f', - '[vm] from:0xca3...a733c, to:Ballot.delegate(address) 0x692...77b3a, value:0 wei, data:0x5c1...4d2db, 0 logs, hash:0xd3c...df10f', + .testFunction('delegate - transact (not payable)', '0x0571a2439ea58bd349dd130afb8aff62a33af14c06de0dbc3928519bdf13ce2e', + '[vm] from:0xca3...a733c, to:Ballot.delegate(address) 0x692...77b3a, value:0 wei, data:0x5c1...4d2db, 0 logs, hash:0x057...3ce2e', {types: 'address to', values: '"0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"'}, null, null) - .click('span#tx0xd3cd54e2f76f3993078ecf9e1b54a148def4520afc141a182293b3610bddf10f button[class^="debug"]') + .click('span#tx0x0571a2439ea58bd349dd130afb8aff62a33af14c06de0dbc3928519bdf13ce2e button[class^="debug"]') .pause(1000) .click('#jumppreviousbreakpoint') .click('#stepdetail .title') @@ -62,10 +62,9 @@ function runTests (browser, testData) { .perform(function (client, done) { contractHelper.addFile(client, 'ballot.abi', { content: ballotABI }, () => { contractHelper.addInstance(client, '0x692a70d2e424a56d2c6c27aa97d1a86395877b3a', () => { - browser.testFunction('delegate - transact (not payable)', '0x7a9ebc90614274b7eb6b072f9bba7825e588cf88ae00598cfdbc4c215b88433e', - '[vm] from:0xca3...a733c, to:Ballot.delegate(address) 0x692...77b3a, value:0 wei, data:0x5c1...4d2db, 0 logs, hash:0x7a9...8433e', + browser.testFunction('delegate - transact (not payable)', '0xd3cd54e2f76f3993078ecf9e1b54a148def4520afc141a182293b3610bddf10f', + '[vm] from:0xca3...a733c, to:Ballot.delegate(address) 0x692...77b3a, value:0 wei, data:0x5c1...4d2db, 0 logs, hash:0xd3c...df10f', {types: 'address to', values: '"0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"'}, null, null).perform(() => { - done() browser.end() }) }) From 4e081c5edd965ff8f14eeb20cd869e209d64709a Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 11 Dec 2017 14:45:34 +0100 Subject: [PATCH 69/74] fix test: verifyCallReturnValue --- test-browser/helpers/contracts.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-browser/helpers/contracts.js b/test-browser/helpers/contracts.js index 3b53223d73..afb8b1b472 100644 --- a/test-browser/helpers/contracts.js +++ b/test-browser/helpers/contracts.js @@ -89,9 +89,9 @@ function verifyCallReturnValue (browser, address, checks, done) { browser.execute(function (address) { var nodes = document.querySelectorAll('#instance' + address + ' div[class^="contractProperty"] div[class^="value"]') var ret = [] - for (var k in checks) { + for (var k = 0; k < nodes.length; k++) { var text = nodes[k].innerText ? nodes[k].innerText : nodes[k].textContent - ret.push(text) + ret.push(text.replace('\n', '')) } return ret }, [address], function (result) { From 58d4c381d7ff9cea0a63e6653fd91a4c18dec492 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 11 Dec 2017 14:45:55 +0100 Subject: [PATCH 70/74] add callback to testFunction --- test-browser/helpers/contracts.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test-browser/helpers/contracts.js b/test-browser/helpers/contracts.js index afb8b1b472..2e8ef28c40 100644 --- a/test-browser/helpers/contracts.js +++ b/test-browser/helpers/contracts.js @@ -102,7 +102,7 @@ function verifyCallReturnValue (browser, address, checks, done) { }) } -function testFunction (fnFullName, txHash, log, expectedInput, expectedReturn, expectedEvent) { +function testFunction (fnFullName, txHash, log, expectedInput, expectedReturn, expectedEvent, callback) { // this => browser this.waitForElementPresent('.instance button[title="' + fnFullName + '"]') .perform(function (client, done) { @@ -131,6 +131,7 @@ function testFunction (fnFullName, txHash, log, expectedInput, expectedReturn, e client.assert.containsText('#editor-container div[class^="terminal"] span[id="tx' + txHash + '"] table[class^="txTable"] #logs', expectedEvent) } done() + if (callback) callback() }) return this } From 672111635fd587e05a708b05f85a1ee78abb56db Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 11 Dec 2017 14:46:13 +0100 Subject: [PATCH 71/74] fix test: addInstance function --- test-browser/helpers/contracts.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test-browser/helpers/contracts.js b/test-browser/helpers/contracts.js index 2e8ef28c40..b0e6328c04 100644 --- a/test-browser/helpers/contracts.js +++ b/test-browser/helpers/contracts.js @@ -147,15 +147,17 @@ function setEditorValue (value) { return this } -function addInstance (browser, address, done) { +function addInstance (browser, address, callback) { browser.setValue('.ataddressinput', address, function () { browser.click('div[class^="atAddress"]') - .perform((client) => { + .perform((client, done) => { browser.execute(function () { document.querySelector('#modal-footer-ok').click() }, [], function (result) { done() }) + }).perform(() => { + callback() }) }) } From 94efeb580f68b928805a91bc3359e850f611ba5d Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 11 Dec 2017 14:46:27 +0100 Subject: [PATCH 72/74] fix test --- test-browser/tests/units/testRecorder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-browser/tests/units/testRecorder.js b/test-browser/tests/units/testRecorder.js index 9c587c6197..27043ba6d2 100644 --- a/test-browser/tests/units/testRecorder.js +++ b/test-browser/tests/units/testRecorder.js @@ -11,7 +11,7 @@ module.exports = function (browser, callback) { .clickFunction('getFromLib - call') .waitForElementPresent('div[class^="contractProperty"] div[class^="value"]') .perform(() => { - contractHelper.verifyCallReturnValue(browser, '0xec5bee2dbb67da8757091ad3d9526ba3ed2e2137', ['0: uint256: 1', '1: uint256: 3456', '2: address: 0xca35b7d915458ef540ade6068dfe2f44e8fa733c'], () => { callback() }) + contractHelper.verifyCallReturnValue(browser, '0x35ef07393b57464e93deb59175ff72e6499450cf', ['0: uint256: 1', '0: uint256: 3456', '0: address: 0xca35b7d915458ef540ade6068dfe2f44e8fa733c'], () => { callback() }) }) }) } From 3b561e61e814dc4224d176fc02017d59bf706c3a Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 11 Dec 2017 14:47:01 +0100 Subject: [PATCH 73/74] clean test --- test-browser/tests/ballot.js | 40 ++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/test-browser/tests/ballot.js b/test-browser/tests/ballot.js index 648e840491..7dd4fa2e8d 100644 --- a/test-browser/tests/ballot.js +++ b/test-browser/tests/ballot.js @@ -27,14 +27,17 @@ function runTests (browser, testData) { browser .waitForElementVisible('.newFile', 10000) .click('.compileView') - contractHelper.testContracts(browser, 'Untitled.sol', sources[0]['browser/Untitled.sol'], ['Ballot'], function () { - browser - .click('.runView') - .setValue('input[placeholder="uint8 _numProposals"]', '1', () => {}) + .perform((client, done) => { + contractHelper.testContracts(browser, 'Untitled.sol', sources[0]['browser/Untitled.sol'], ['Ballot'], function () { + done() + }) + }).click('.runView') + .setValue('input[placeholder="uint8 _numProposals"]', '1') .click('#runTabView div[class^="create"]') .testFunction('delegate - transact (not payable)', '0x0571a2439ea58bd349dd130afb8aff62a33af14c06de0dbc3928519bdf13ce2e', '[vm] from:0xca3...a733c, to:Ballot.delegate(address) 0x692...77b3a, value:0 wei, data:0x5c1...4d2db, 0 logs, hash:0x057...3ce2e', {types: 'address to', values: '"0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"'}, null, null) + .pause(500) .click('span#tx0x0571a2439ea58bd349dd130afb8aff62a33af14c06de0dbc3928519bdf13ce2e button[class^="debug"]') .pause(1000) .click('#jumppreviousbreakpoint') @@ -42,11 +45,12 @@ function runTests (browser, testData) { .click('#asmcodes .title') .pause(500) .perform(function (client, done) { + console.log('goToVMtraceStep') contractHelper.goToVMtraceStep(browser, 39, () => { done() }) }) - .pause(5000) + .pause(1000) .perform(function (client, done) { contractHelper.checkDebug(browser, 'soliditystate', stateCheck, () => { done() @@ -59,18 +63,24 @@ function runTests (browser, testData) { }) .click('.runView') .click('div[class^="udappClose"]') - .perform(function (client, done) { - contractHelper.addFile(client, 'ballot.abi', { content: ballotABI }, () => { - contractHelper.addInstance(client, '0x692a70d2e424a56d2c6c27aa97d1a86395877b3a', () => { - browser.testFunction('delegate - transact (not payable)', '0xd3cd54e2f76f3993078ecf9e1b54a148def4520afc141a182293b3610bddf10f', - '[vm] from:0xca3...a733c, to:Ballot.delegate(address) 0x692...77b3a, value:0 wei, data:0x5c1...4d2db, 0 logs, hash:0xd3c...df10f', - {types: 'address to', values: '"0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"'}, null, null).perform(() => { - browser.end() - }) - }) + .perform((client, done) => { + console.log('ballot.abi') + contractHelper.addFile(browser, 'ballot.abi', { content: ballotABI }, () => { + done() }) }) - }) + .perform((client, done) => { + console.log('addInstance 0x692a70d2e424a56d2c6c27aa97d1a86395877b3a') + contractHelper.addInstance(browser, '0x692a70d2e424a56d2c6c27aa97d1a86395877b3a', () => { + done() + }) + }) + .perform((client, done) => { + console.log('delegate - transact (not payable)') + browser.testFunction('delegate - transact (not payable)', '0xd3cd54e2f76f3993078ecf9e1b54a148def4520afc141a182293b3610bddf10f', + '[vm] from:0xca3...a733c, to:Ballot.delegate(address) 0x692...77b3a, value:0 wei, data:0x5c1...4d2db, 0 logs, hash:0xd3c...df10f', + {types: 'address to', values: '"0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"'}, null, null, () => { done() }) + }).end() } var localsCheck = { From c797b7e10e5f3202397f5e56388d1818e86f6563 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 11 Dec 2017 15:06:54 +0100 Subject: [PATCH 74/74] fix tests --- test-browser/tests/simpleContract.js | 1 + test-browser/tests/staticanalysis.js | 1 + 2 files changed, 2 insertions(+) diff --git a/test-browser/tests/simpleContract.js b/test-browser/tests/simpleContract.js index d2b3f1de71..ef08304112 100644 --- a/test-browser/tests/simpleContract.js +++ b/test-browser/tests/simpleContract.js @@ -18,6 +18,7 @@ module.exports = { } function runTests (browser) { + browser.setEditorValue = contractHelper.setEditorValue browser .waitForElementVisible('.newFile', 10000) .click('.compileView') diff --git a/test-browser/tests/staticanalysis.js b/test-browser/tests/staticanalysis.js index 158de4b112..26bcb10fe2 100644 --- a/test-browser/tests/staticanalysis.js +++ b/test-browser/tests/staticanalysis.js @@ -33,6 +33,7 @@ module.exports = { } function runTests (browser) { + browser.setEditorValue = contractHelper.setEditorValue browser .waitForElementVisible('.newFile', 10000) .click('.compileView')