diff --git a/assets/css/universal-dapp.css b/assets/css/universal-dapp.css index 4d4f7c289e..79c8dfd8b6 100644 --- a/assets/css/universal-dapp.css +++ b/assets/css/universal-dapp.css @@ -100,6 +100,14 @@ word-wrap: break-word; } +.udapp .output .result .debugTx { + position: absolute; + top: 0.4em; + right: 1.4em; + height: 1.5em; + width: 2.5em; +} + .udapp .output .result:last-child { margin: 0; } .udapp .output:empty { @@ -200,6 +208,10 @@ width: 25%; } +.udapp .contractProperty button.debug { + width: 21px; +} + .udapp .contractProperty .call { background-color: #FF8B8B; border-color: #FF8B8B; diff --git a/package.json b/package.json index a1a9522fd0..ade61f4107 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "webworkify": "^1.2.1", "yo-yo": "^1.2.2", "yo-yoify": "^3.3.0", - "ethereum-remix": "0.0.2-alpha.0.0.4" + "ethereum-remix": "0.0.2-alpha.0.0.7" }, "repository": { "type": "git", diff --git a/src/app.js b/src/app.js index d19d2e8a0c..0909bbdbee 100644 --- a/src/app.js +++ b/src/app.js @@ -15,6 +15,7 @@ var Compiler = require('./app/compiler'); var ExecutionContext = require('./app/execution-context'); var Debugger = require('./app/debugger'); var FormalVerification = require('./app/formalVerification'); +var EthJSVM = require('ethereumjs-vm'); // The event listener needs to be registered as early as possible, because the // parent will send the message upon the "load" event. @@ -431,13 +432,17 @@ var run = function () { $('#output').append($('
').append($('
').text('Loading github.com/' + root + '/' + path + ' ...')));
     return $.getJSON('https://api.github.com/repos/' + root + '/contents/' + path, cb);
   }
-
-  var executionContext = new ExecutionContext();
-  var transactionDebugger = new Debugger(executionContext, '#debugger');
+  var transactionDebugger = new Debugger('#debugger');
+  var vm = new EthJSVM(null, null, { activatePrecompiles: true, enableHomestead: true });
+  vm.stateManager.checkpoint();
+  transactionDebugger.addProvider('VM', vm);
+  transactionDebugger.switchProvider('VM');
+  var executionContext = new ExecutionContext(transactionDebugger);
+  transactionDebugger.addProvider('EXTERNAL', executionContext.web3());
   transactionDebugger.onDebugRequested = function () {
     selectTab($('ul#options li.debugView'));
   };
-  var renderer = new Renderer(editor, executionContext, updateFiles, transactionDebugger);
+  var renderer = new Renderer(editor, executionContext, updateFiles, transactionDebugger, vm);
   var formalVerification = new FormalVerification($('#verificationView'), renderer);
   var compiler = new Compiler(editor, renderer, queryParams, handleGithubCall, $('#output'), getHidingRHP, formalVerification, updateFiles);
   executionContext.setCompiler(compiler);
diff --git a/src/app/debugger.js b/src/app/debugger.js
index 1ad34aa78c..b44432558a 100644
--- a/src/app/debugger.js
+++ b/src/app/debugger.js
@@ -1,20 +1,31 @@
 var remix = require('ethereum-remix');
 
-function Debugger (_executionContext, _id) {
-  this.el = document.querySelector(_id);
-  this.debugger = new remix.Debugger(_executionContext.web3());
+function Debugger (id) {
+  this.el = document.querySelector(id);
+  this.debugger = new remix.ui.Debugger();
   this.el.appendChild(this.debugger.render());
-  this.web3 = _executionContext.web3();
-
-  Debugger.prototype.debug = function (receipt) {
-    if (this.onDebugRequested) this.onDebugRequested();
-    var self = this;
-    this.web3.eth.getTransaction(receipt.transactionHash, function (error, tx) {
-      if (!error) {
-        self.debugger.debug(tx);
-      }
-    });
-  };
 }
 
+Debugger.prototype.debug = function (receipt) {
+  if (this.onDebugRequested) this.onDebugRequested();
+  var self = this;
+  this.debugger.web3().eth.getTransaction(receipt.transactionHash, function (error, tx) {
+    if (!error) {
+      self.debugger.debug(tx);
+    }
+  });
+};
+
+Debugger.prototype.addProvider = function (type, obj) {
+  this.debugger.addProvider(type, obj);
+};
+
+Debugger.prototype.switchProvider = function (type) {
+  this.debugger.switchProvider(type);
+};
+
+Debugger.prototype.web3 = function (type) {
+  return this.debugger.web3();
+};
+
 module.exports = Debugger;
diff --git a/src/app/execution-context.js b/src/app/execution-context.js
index db67897a80..70591307df 100644
--- a/src/app/execution-context.js
+++ b/src/app/execution-context.js
@@ -13,7 +13,8 @@ if (typeof window.web3 !== 'undefined') {
   web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));
 }
 
-function ExecutionContext () {
+function ExecutionContext (_txDebugger) {
+  var txDebugger = _txDebugger;
   var compiler;
   var executionContext = injectedProvider ? 'injected' : 'vm';
 
@@ -59,8 +60,12 @@ function ExecutionContext () {
       executionContext = ev.target.value;
       if (executionContext === 'web3') {
         setProviderFromEndpoint();
+        txDebugger.switchProvider('EXTERNAL');
       } else if (executionContext === 'injected') {
         web3.setProvider(injectedProvider);
+        txDebugger.switchProvider('EXTERNAL');
+      } else if (executionContext === 'vm') {
+        txDebugger.switchProvider('VM');
       }
     }
     compiler.compile();
diff --git a/src/app/renderer.js b/src/app/renderer.js
index 0639bdf991..ab9872cc75 100644
--- a/src/app/renderer.js
+++ b/src/app/renderer.js
@@ -4,7 +4,7 @@ var UniversalDApp = require('../universal-dapp.js');
 
 var utils = require('./utils');
 
-function Renderer (editor, executionContext, updateFiles, transactionDebugger) {
+function Renderer (editor, executionContext, updateFiles, transactionDebugger, vm) {
   var detailsOpen = {};
 
   function renderError (message, container, noAnnotations) {
@@ -61,6 +61,9 @@ function Renderer (editor, executionContext, updateFiles, transactionDebugger) {
       });
     }
 
+    vm.stateManager.revert(function () {
+      vm.stateManager.checkpoint();
+    });
     var dapp = new UniversalDApp(udappContracts, {
       mode: executionContext.isVM() ? 'vm' : 'web3',
       web3: executionContext.web3(),
@@ -86,7 +89,7 @@ function Renderer (editor, executionContext, updateFiles, transactionDebugger) {
         }
         return $contractOutput.append(getDetails(contract, source, contractName));
       }
-    }, transactionDebugger);
+    }, transactionDebugger, vm);
 
     var $contractOutput = dapp.render();
 
diff --git a/src/universal-dapp.js b/src/universal-dapp.js
index 44422bd59f..b69f2f4488 100644
--- a/src/universal-dapp.js
+++ b/src/universal-dapp.js
@@ -1,14 +1,13 @@
 /* global prompt */
 
 var $ = require('jquery');
-var EthJSVM = require('ethereumjs-vm');
 var ethJSUtil = require('ethereumjs-util');
 var EthJSTX = require('ethereumjs-tx');
 var ethJSABI = require('ethereumjs-abi');
 var EthJSBlock = require('ethereumjs-block');
 var BN = ethJSUtil.BN;
 
-function UniversalDApp (contracts, options, transactionDebugger) {
+function UniversalDApp (contracts, options, transactionDebugger, vm) {
   var self = this;
 
   self.options = options || {};
@@ -18,14 +17,13 @@ function UniversalDApp (contracts, options, transactionDebugger) {
 
   self.web3 = options.web3;
   self.transactionDebugger = transactionDebugger;
+
   if (options.mode === 'vm') {
     // FIXME: use `options.vm` or `self.vm` consistently
     options.vm = true;
 
     self.accounts = {};
-
-    self.vm = new EthJSVM(null, null, { activatePrecompiles: true, enableHomestead: true });
-
+    self.vm = vm;
     self.addAccount('3cd7232cd6f3fc66a57a6bedc1a8ed6c228fff0a327e169c2bcc5e869ed49511');
     self.addAccount('2ac6c190b09897cd8987869cc7b918cfea07ee82038d492abce033c75c1b1d0c');
   } else if (options.mode !== 'web3') {
@@ -354,7 +352,17 @@ UniversalDApp.prototype.getCallButton = function (args) {
 
   var getDebugTransaction = function (result) {
     var $debugTx = $('
'); - var $button = $(''); + var $button = $(''); + $button.click(function () { + self.transactionDebugger.debug(result); + }); + $debugTx.append($button); + return $debugTx; + }; + + var getDebugCall = function (result) { + var $debugTx = $('
'); + var $button = $(''); $button.click(function () { self.transactionDebugger.debug(result); }); @@ -514,6 +522,11 @@ UniversalDApp.prototype.getCallButton = function (args) { if (decoded) { $result.append(decoded); } + if (args.abi.constant) { + $result.append(getDebugCall(result)); + } else { + $result.append(getDebugTransaction(result)); + } } else if (args.abi.constant && !isConstructor) { clearOutput($result); $result.append(getReturnOutput(result)).append(getGasUsedOutput({})); @@ -701,7 +714,10 @@ UniversalDApp.prototype.runTx = function (data, args, cb) { transactions: [], uncleHeaders: [] }); - self.vm.runTx({block: block, tx: tx, skipBalance: true, skipNonce: true}, cb); + self.vm.runTx({block: block, tx: tx, skipBalance: true, skipNonce: true}, function (err, result) { + result.transactionHash = self.transactionDebugger.web3().releaseCurrentHash(); // used to keep track of the transaction + cb(err, result); + }); } catch (e) { cb(e, null); }