diff options
author | Taylor Gerring <taylor.gerring@gmail.com> | 2015-03-11 01:50:13 +0800 |
---|---|---|
committer | Taylor Gerring <taylor.gerring@gmail.com> | 2015-03-11 01:50:13 +0800 |
commit | 617804c32731c5103319e7072557f62a9ce63836 (patch) | |
tree | baca8a8f2e014892983ca45e8ec618ab54121526 | |
parent | 3de51f76eea87f8e8a4b8a7277cd294529e28491 (diff) | |
parent | bbe8b186600992ada6da9e75e9976cd5a9dc0ae3 (diff) | |
download | go-tangerine-617804c32731c5103319e7072557f62a9ce63836.tar go-tangerine-617804c32731c5103319e7072557f62a9ce63836.tar.gz go-tangerine-617804c32731c5103319e7072557f62a9ce63836.tar.bz2 go-tangerine-617804c32731c5103319e7072557f62a9ce63836.tar.lz go-tangerine-617804c32731c5103319e7072557f62a9ce63836.tar.xz go-tangerine-617804c32731c5103319e7072557f62a9ce63836.tar.zst go-tangerine-617804c32731c5103319e7072557f62a9ce63836.zip |
Merge branch 'rpcfrontier' of github.com:ethereum/go-ethereum into rpcfrontier
-rw-r--r-- | cmd/blocktest/main.go | 14 | ||||
-rw-r--r-- | cmd/ethereum/main.go | 43 | ||||
-rw-r--r-- | cmd/mist/assets/examples/coin.html | 2 | ||||
-rw-r--r-- | cmd/mist/assets/ext/ethereum.js/dist/ethereum.js | 2475 | ||||
-rw-r--r-- | cmd/mist/assets/qml/main.qml | 25 | ||||
-rw-r--r-- | cmd/mist/assets/qml/views/chain.qml | 17 | ||||
-rw-r--r-- | cmd/mist/debugger.go | 367 | ||||
-rw-r--r-- | cmd/mist/ui_lib.go | 27 | ||||
-rw-r--r-- | cmd/utils/cmd.go | 50 | ||||
-rw-r--r-- | core/block_processor.go | 2 | ||||
-rw-r--r-- | eth/backend.go | 11 | ||||
-rw-r--r-- | ethutil/natspec/natspec.go | 58 | ||||
-rw-r--r-- | ethutil/natspec/natspec_js.go (renamed from ethutil/natspec/natspec.js) | 91 | ||||
-rw-r--r-- | ethutil/natspec/natspec_test.go | 104 | ||||
-rw-r--r-- | rpc/args.go | 50 | ||||
-rw-r--r-- | tests/vm/gh_test.go | 7 | ||||
-rw-r--r-- | vm/context.go | 2 | ||||
-rw-r--r-- | vm/debugger.go | 10 | ||||
-rw-r--r-- | vm/gas.go | 86 | ||||
-rw-r--r-- | vm/memory.go | 72 | ||||
-rw-r--r-- | vm/stack.go | 170 | ||||
-rw-r--r-- | vm/vm.go | 451 |
22 files changed, 2095 insertions, 2039 deletions
diff --git a/cmd/blocktest/main.go b/cmd/blocktest/main.go index 4a05b8bee..579aa850a 100644 --- a/cmd/blocktest/main.go +++ b/cmd/blocktest/main.go @@ -168,7 +168,7 @@ func main() { logger.Flush() }() - utils.HandleInterrupt() + //utils.HandleInterrupt() utils.InitConfig(VmType, ConfigFile, Datadir, "ethblocktest") @@ -190,12 +190,17 @@ func main() { MinerThreads: MinerThreads, }) + utils.StartEthereumForTest(ethereum) utils.StartRpc(ethereum, RpcListenAddress, RpcPort) - utils.StartEthereum(ethereum) ethereum.ChainManager().ResetWithGenesisBlock(blocks[0]) + // bph := ethereum.ChainManager().GetBlock(blocks[1].Header().ParentHash) + // fmt.Println("bph: ", bph) - // fmt.Println("HURR: ", hex.EncodeToString(ethutil.Encode(blocks[0].RlpData()))) + //fmt.Println("b0: ", hex.EncodeToString(ethutil.Encode(blocks[0].RlpData()))) + //fmt.Println("b0: ", hex.EncodeToString(blocks[0].Hash())) + //fmt.Println("b1: ", hex.EncodeToString(ethutil.Encode(blocks[1].RlpData()))) + //fmt.Println("b1: ", hex.EncodeToString(blocks[1].Hash())) go ethereum.ChainManager().InsertChain(types.Blocks{blocks[1]}) fmt.Println("OK! ") @@ -254,6 +259,9 @@ func loadBlocksFromTestFile(filePath string) (blocks types.Blocks, err error) { } gb := types.NewBlockWithHeader(gbh) + //gb.uncles = *new([]*types.Header) + //gb.transactions = *new(types.Transactions) + gb.Td = new(big.Int) gb.Reward = new(big.Int) testBlock := new(types.Block) diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 8b361f7ae..12e205f37 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -87,6 +87,11 @@ runtime will execute the file and exit. Name: "import", Usage: `import a blockchain file`, }, + { + Action: exportchain, + Name: "export", + Usage: `export blockchain into file`, + }, } app.Author = "" app.Email = "" @@ -171,25 +176,39 @@ func importchain(ctx *cli.Context) { if len(ctx.Args()) != 1 { utils.Fatalf("This command requires an argument.") } - chain, _, _ := utils.GetChain(ctx) + chainmgr, _, _ := utils.GetChain(ctx) start := time.Now() - err := utils.ImportChain(chain, ctx.Args().First()) + err := utils.ImportChain(chainmgr, ctx.Args().First()) if err != nil { utils.Fatalf("Import error: %v\n", err) } - fmt.Printf("Import done in", time.Since(start)) + fmt.Printf("Import done in %v", time.Since(start)) + return +} + +func exportchain(ctx *cli.Context) { + if len(ctx.Args()) != 1 { + utils.Fatalf("This command requires an argument.") + } + chainmgr, _, _ := utils.GetChain(ctx) + start := time.Now() + err := utils.ExportChain(chainmgr, ctx.Args().First()) + if err != nil { + utils.Fatalf("Export error: %v\n", err) + } + fmt.Printf("Export done in %v", time.Since(start)) return } func dump(ctx *cli.Context) { - chain, _, stateDb := utils.GetChain(ctx) + chainmgr, _, stateDb := utils.GetChain(ctx) for _, arg := range ctx.Args() { var block *types.Block if hashish(arg) { - block = chain.GetBlock(ethutil.Hex2Bytes(arg)) + block = chainmgr.GetBlock(ethutil.Hex2Bytes(arg)) } else { num, _ := strconv.Atoi(arg) - block = chain.GetBlockByNumber(uint64(num)) + block = chainmgr.GetBlockByNumber(uint64(num)) } if block == nil { fmt.Println("{}") @@ -209,11 +228,13 @@ func hashish(x string) bool { } func version(c *cli.Context) { - fmt.Printf(`%v %v -PV=%d -GOOS=%s -GO=%s + fmt.Printf(`%v +Version: %v +Protocol Version: %d +Network Id: %d +GO: %s +OS: %s GOPATH=%s GOROOT=%s -`, ClientIdentifier, Version, eth.ProtocolVersion, runtime.GOOS, runtime.Version(), os.Getenv("GOPATH"), runtime.GOROOT()) +`, ClientIdentifier, Version, eth.ProtocolVersion, eth.NetworkId, runtime.Version(), runtime.GOOS, os.Getenv("GOPATH"), runtime.GOROOT()) } diff --git a/cmd/mist/assets/examples/coin.html b/cmd/mist/assets/examples/coin.html index 509a9aeeb..115145c4c 100644 --- a/cmd/mist/assets/examples/coin.html +++ b/cmd/mist/assets/examples/coin.html @@ -35,7 +35,7 @@ var web3 = require('web3'); var eth = web3.eth; - web3.setProvider(new web3.providers.HttpSyncProvider('http://localhost:8545')); + web3.setProvider(new web3.providers.HttpProvider('http://localhost:8545')); var desc = [{ "name": "balance(address)", "type": "function", diff --git a/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js b/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js index 5b7d87270..7b2531677 100644 --- a/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js +++ b/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js @@ -22,34 +22,57 @@ require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof requ * @date 2014 */ -var utils = require('./utils'); +var utils = require('../utils/utils'); +var c = require('../utils/config'); var types = require('./types'); -var c = require('./const'); var f = require('./formatters'); -var displayTypeError = function (type) { - console.error('parser does not support type: ' + type); +/** + * throw incorrect type error + * + * @method throwTypeError + * @param {String} type + * @throws incorrect type error + */ +var throwTypeError = function (type) { + throw new Error('parser does not support type: ' + type); }; -/// This method should be called if we want to check if givent type is an array type -/// @returns true if it is, otherwise false -var arrayType = function (type) { +/** This method should be called if we want to check if givent type is an array type + * + * @method isArrayType + * @param {String} type name + * @returns {Boolean} true if it is, otherwise false + */ +var isArrayType = function (type) { return type.slice(-2) === '[]'; }; +/** + * This method should be called to return dynamic type length in hex + * + * @method dynamicTypeBytes + * @param {String} type + * @param {String|Array} dynamic type + * @return {String} length of dynamic type in hex or empty string if type is not dynamic + */ var dynamicTypeBytes = function (type, value) { // TODO: decide what to do with array of strings - if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length. + if (isArrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length. return f.formatInputInt(value.length); return ""; }; var inputTypes = types.inputTypes(); -/// Formats input params to bytes -/// @param abi contract method inputs -/// @param array of params that will be formatted to bytes -/// @returns bytes representation of input params +/** + * Formats input params to bytes + * + * @method formatInput + * @param {Array} abi inputs of method + * @param {Array} params that will be formatted to bytes + * @returns bytes representation of input params + */ var formatInput = function (inputs, params) { var bytes = ""; var toAppendConstant = ""; @@ -67,12 +90,12 @@ var formatInput = function (inputs, params) { typeMatch = inputTypes[j].type(inputs[i].type, params[i]); } if (!typeMatch) { - displayTypeError(inputs[i].type); + throwTypeError(inputs[i].type); } var formatter = inputTypes[j - 1].format; - if (arrayType(inputs[i].type)) + if (isArrayType(inputs[i].type)) toAppendArrayContent += params[i].reduce(function (acc, curr) { return acc + formatter(curr); }, ""); @@ -87,18 +110,29 @@ var formatInput = function (inputs, params) { return bytes; }; +/** + * This method should be called to predict the length of dynamic type + * + * @method dynamicBytesLength + * @param {String} type + * @returns {Number} length of dynamic type, 0 or multiplication of ETH_PADDING (32) + */ var dynamicBytesLength = function (type) { - if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length. + if (isArrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length. return c.ETH_PADDING * 2; return 0; }; var outputTypes = types.outputTypes(); -/// Formats output bytes back to param list -/// @param contract abi method outputs -/// @param bytes representtion of output -/// @returns array of output params +/** + * Formats output bytes back to param list + * + * @method formatOutput + * @param {Array} abi outputs of method + * @param {String} bytes represention of output + * @returns {Array} output params + */ var formatOutput = function (outs, output) { output = output.slice(2); @@ -120,11 +154,11 @@ var formatOutput = function (outs, output) { } if (!typeMatch) { - displayTypeError(outs[i].type); + throwTypeError(outs[i].type); } var formatter = outputTypes[j - 1].format; - if (arrayType(outs[i].type)) { + if (isArrayType(outs[i].type)) { var size = f.formatOutputUInt(dynamicPart.slice(0, padding)); dynamicPart = dynamicPart.slice(padding); var array = []; @@ -147,9 +181,14 @@ var formatOutput = function (outs, output) { return result; }; -/// @param json abi for contract -/// @returns input parser object for given json abi -/// TODO: refactor creating the parser, do not double logic from contract +/** + * Should be called to create input parser for contract with given abi + * + * @method inputParser + * @param {Array} contract abi + * @returns {Object} input parser object for given json abi + * TODO: refactor creating the parser, do not double logic from contract + */ var inputParser = function (json) { var parser = {}; json.forEach(function (method) { @@ -171,8 +210,13 @@ var inputParser = function (json) { return parser; }; -/// @param json abi for contract -/// @returns output parser for given json abi +/** + * Should be called to create output parser for contract with given abi + * + * @method outputParser + * @param {Array} contract abi + * @returns {Object} output parser for given json abi + */ var outputParser = function (json) { var parser = {}; json.forEach(function (method) { @@ -201,7 +245,304 @@ module.exports = { formatOutput: formatOutput }; -},{"./const":2,"./formatters":8,"./types":15,"./utils":16}],2:[function(require,module,exports){ +},{"../utils/config":4,"../utils/utils":5,"./formatters":2,"./types":3}],2:[function(require,module,exports){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. +*/ +/** @file formatters.js + * @authors: + * Marek Kotewicz <marek@ethdev.com> + * @date 2015 + */ + +if ("build" !== 'build') {/* + var BigNumber = require('bignumber.js'); // jshint ignore:line +*/} + +var utils = require('../utils/utils'); +var c = require('../utils/config'); + +/** + * Should be called to pad string to expected length + * + * @method padLeft + * @param {String} string to be padded + * @param {Number} characters that result string should have + * @param {String} sign, by default 0 + * @returns {String} right aligned string + */ +var padLeft = function (string, chars, sign) { + return new Array(chars - string.length + 1).join(sign ? sign : "0") + string; +}; + +/** + * Formats input value to byte representation of int + * If value is negative, return it's two's complement + * If the value is floating point, round it down + * + * @method formatInputInt + * @param {String|Number|BigNumber} value that needs to be formatted + * @returns {String} right-aligned byte representation of int + */ +var formatInputInt = function (value) { + var padding = c.ETH_PADDING * 2; + BigNumber.config(c.ETH_BIGNUMBER_ROUNDING_MODE); + return padLeft(utils.toTwosComplement(value).round().toString(16), padding); +}; + +/** + * Formats input value to byte representation of string + * + * @method formatInputString + * @param {String} + * @returns {String} left-algined byte representation of string + */ +var formatInputString = function (value) { + return utils.fromAscii(value, c.ETH_PADDING).substr(2); +}; + +/** + * Formats input value to byte representation of bool + * + * @method formatInputBool + * @param {Boolean} + * @returns {String} right-aligned byte representation bool + */ +var formatInputBool = function (value) { + return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0'); +}; + +/** + * Formats input value to byte representation of real + * Values are multiplied by 2^m and encoded as integers + * + * @method formatInputReal + * @param {String|Number|BigNumber} + * @returns {String} byte representation of real + */ +var formatInputReal = function (value) { + return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); +}; + +/** + * Check if input value is negative + * + * @method signedIsNegative + * @param {String} value is hex format + * @returns {Boolean} true if it is negative, otherwise false + */ +var signedIsNegative = function (value) { + return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1'; +}; + +/** + * Formats right-aligned output bytes to int + * + * @method formatOutputInt + * @param {String} bytes + * @returns {BigNumber} right-aligned output bytes formatted to big number + */ +var formatOutputInt = function (value) { + + value = value || "0"; + + // check if it's negative number + // it it is, return two's complement + if (signedIsNegative(value)) { + return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1); + } + return new BigNumber(value, 16); +}; + +/** + * Formats right-aligned output bytes to uint + * + * @method formatOutputUInt + * @param {String} bytes + * @returns {BigNumeber} right-aligned output bytes formatted to uint + */ +var formatOutputUInt = function (value) { + value = value || "0"; + return new BigNumber(value, 16); +}; + +/** + * Formats right-aligned output bytes to real + * + * @method formatOutputReal + * @param {String} + * @returns {BigNumber} input bytes formatted to real + */ +var formatOutputReal = function (value) { + return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128)); +}; + +/** + * Formats right-aligned output bytes to ureal + * + * @method formatOutputUReal + * @param {String} + * @returns {BigNumber} input bytes formatted to ureal + */ +var formatOutputUReal = function (value) { + return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128)); +}; + +/** + * Should be used to format output hash + * + * @method formatOutputHash + * @param {String} + * @returns {String} right-aligned output bytes formatted to hex + */ +var formatOutputHash = function (value) { + return "0x" + value; +}; + +/** + * Should be used to format output bool + * + * @method formatOutputBool + * @param {String} + * @returns {Boolean} right-aligned input bytes formatted to bool + */ +var formatOutputBool = function (value) { + return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false; +}; + +/** + * Should be used to format output string + * + * @method formatOutputString + * @param {Sttring} left-aligned hex representation of string + * @returns {String} ascii string + */ +var formatOutputString = function (value) { + return utils.toAscii(value); +}; + +/** + * Should be used to format output address + * + * @method formatOutputAddress + * @param {String} right-aligned input bytes + * @returns {String} address + */ +var formatOutputAddress = function (value) { + return "0x" + value.slice(value.length - 40, value.length); +}; + +module.exports = { + formatInputInt: formatInputInt, + formatInputString: formatInputString, + formatInputBool: formatInputBool, + formatInputReal: formatInputReal, + formatOutputInt: formatOutputInt, + formatOutputUInt: formatOutputUInt, + formatOutputReal: formatOutputReal, + formatOutputUReal: formatOutputUReal, + formatOutputHash: formatOutputHash, + formatOutputBool: formatOutputBool, + formatOutputString: formatOutputString, + formatOutputAddress: formatOutputAddress +}; + + +},{"../utils/config":4,"../utils/utils":5}],3:[function(require,module,exports){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. +*/ +/** @file types.js + * @authors: + * Marek Kotewicz <marek@ethdev.com> + * @date 2015 + */ + +var f = require('./formatters'); + +/// @param expected type prefix (string) +/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false +var prefixedType = function (prefix) { + return function (type) { + return type.indexOf(prefix) === 0; + }; +}; + +/// @param expected type name (string) +/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false +var namedType = function (name) { + return function (type) { + return name === type; + }; +}; + +/// Setups input formatters for solidity types +/// @returns an array of input formatters +var inputTypes = function () { + + return [ + { type: prefixedType('uint'), format: f.formatInputInt }, + { type: prefixedType('int'), format: f.formatInputInt }, + { type: prefixedType('hash'), format: f.formatInputInt }, + { type: prefixedType('string'), format: f.formatInputString }, + { type: prefixedType('real'), format: f.formatInputReal }, + { type: prefixedType('ureal'), format: f.formatInputReal }, + { type: namedType('address'), format: f.formatInputInt }, + { type: namedType('bool'), format: f.formatInputBool } + ]; +}; + +/// Setups output formaters for solidity types +/// @returns an array of output formatters +var outputTypes = function () { + + return [ + { type: prefixedType('uint'), format: f.formatOutputUInt }, + { type: prefixedType('int'), format: f.formatOutputInt }, + { type: prefixedType('hash'), format: f.formatOutputHash }, + { type: prefixedType('string'), format: f.formatOutputString }, + { type: prefixedType('real'), format: f.formatOutputReal }, + { type: prefixedType('ureal'), format: f.formatOutputUReal }, + { type: namedType('address'), format: f.formatOutputAddress }, + { type: namedType('bool'), format: f.formatOutputBool } + ]; +}; + +module.exports = { + prefixedType: prefixedType, + namedType: namedType, + inputTypes: inputTypes, + outputTypes: outputTypes +}; + + +},{"./formatters":2}],4:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -218,12 +559,25 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. */ -/** @file const.js +/** @file config.js * @authors: * Marek Kotewicz <marek@ethdev.com> * @date 2015 */ +/** + * Utils + * + * @module utils + */ + +/** + * Utility functions + * + * @class [utils] config + * @constructor + */ + /// required to define ETH_BIGNUMBER_ROUNDING_MODE if ("build" !== 'build') {/* var BigNumber = require('bignumber.js'); // jshint ignore:line @@ -256,11 +610,713 @@ module.exports = { ETH_SIGNATURE_LENGTH: 4, ETH_UNITS: ETH_UNITS, ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN }, - ETH_POLLING_TIMEOUT: 1000 + ETH_POLLING_TIMEOUT: 1000, + ETH_DEFAULTBLOCK: 'latest' }; -},{}],3:[function(require,module,exports){ +},{}],5:[function(require,module,exports){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. +*/ +/** @file utils.js + * @authors: + * Marek Kotewicz <marek@ethdev.com> + * @date 2015 + */ + +/** + * Utils + * + * @module utils + */ + +/** + * Utility functions + * + * @class [utils] utils + * @constructor + */ + +if ("build" !== 'build') {/* + var BigNumber = require('bignumber.js'); // jshint ignore:line +*/} + +var unitMap = { + 'wei': '1', + 'kwei': '1000', + 'ada': '1000', + 'mwei': '1000000', + 'babbage': '1000000', + 'gwei': '1000000000', + 'shannon': '1000000000', + 'szabo': '1000000000000', + 'finney': '1000000000000000', + 'ether': '1000000000000000000', + 'kether': '1000000000000000000000', + 'grand': '1000000000000000000000', + 'einstein': '1000000000000000000000', + 'mether': '1000000000000000000000000', + 'gether': '1000000000000000000000000000', + 'tether': '1000000000000000000000000000000' +}; + + +/** Finds first index of array element matching pattern + * + * @method findIndex + * @param {Array} + * @param {Function} pattern + * @returns {Number} index of element + */ +var findIndex = function (array, callback) { + var end = false; + var i = 0; + for (; i < array.length && !end; i++) { + end = callback(array[i]); + } + return end ? i - 1 : -1; +}; + +/** + * Should be called to get sting from it's hex representation + * + * @method toAscii + * @param {String} string in hex + * @returns {String} ascii string representation of hex value + */ +var toAscii = function(hex) { +// Find termination + var str = ""; + var i = 0, l = hex.length; + if (hex.substring(0, 2) === '0x') { + i = 2; + } + for (; i < l; i+=2) { + var code = parseInt(hex.substr(i, 2), 16); + if (code === 0) { + break; + } + + str += String.fromCharCode(code); + } + + return str; +}; + +/** + * Shold be called to get hex representation (prefixed by 0x) of ascii string + * + * @method fromAscii + * @param {String} string + * @returns {String} hex representation of input string + */ +var toHexNative = function(str) { + var hex = ""; + for(var i = 0; i < str.length; i++) { + var n = str.charCodeAt(i).toString(16); + hex += n.length < 2 ? '0' + n : n; + } + + return hex; +}; + +/** + * Shold be called to get hex representation (prefixed by 0x) of ascii string + * + * @method fromAscii + * @param {String} string + * @param {Number} optional padding + * @returns {String} hex representation of input string + */ +var fromAscii = function(str, pad) { + pad = pad === undefined ? 0 : pad; + var hex = toHexNative(str); + while (hex.length < pad*2) + hex += "00"; + return "0x" + hex; +}; + +/** + * Should be called to get display name of contract function + * + * @method extractDisplayName + * @param {String} name of function/event + * @returns {String} display name for function/event eg. multiply(uint256) -> multiply + */ +var extractDisplayName = function (name) { + var length = name.indexOf('('); + return length !== -1 ? name.substr(0, length) : name; +}; + +/// @returns overloaded part of function/event name +var extractTypeName = function (name) { + /// TODO: make it invulnerable + var length = name.indexOf('('); + return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)).replace(' ', '') : ""; +}; + +/** + * Filters all functions from input abi + * + * @method filterFunctions + * @param {Array} abi + * @returns {Array} abi array with filtered objects of type 'function' + */ +var filterFunctions = function (json) { + return json.filter(function (current) { + return current.type === 'function'; + }); +}; + +/** + * Filters all events from input abi + * + * @method filterEvents + * @param {Array} abi + * @returns {Array} abi array with filtered objects of type 'event' + */ +var filterEvents = function (json) { + return json.filter(function (current) { + return current.type === 'event'; + }); +}; + +/** + * Converts value to it's decimal representation in string + * + * @method toDecimal + * @param {String|Number|BigNumber} + * @return {String} + */ +var toDecimal = function (value) { + return toBigNumber(value).toNumber(); +}; + +/** + * Converts value to it's hex representation + * + * @method fromDecimal + * @param {String|Number|BigNumber} + * @return {String} + */ +var fromDecimal = function (value) { + var number = toBigNumber(value); + var result = number.toString(16); + + return (number.lessThan(0)) + ? '-0x' + result.substr(1) + : '0x' + result; +}; + +/** + * Auto converts any given value into it's hex representation. + * + * And even stringifys objects before. + * + * @method toHex + * @param {String|Number|BigNumber|Object} + * @return {String} + */ +var toHex = function (val) { + /*jshint maxcomplexity:5 */ + + if(typeof val === 'boolean') + return val; + + if(isBigNumber(val)) + return fromDecimal(val); + + if(typeof val === 'object') + return fromAscii(JSON.stringify(val)); + + if(isString(val) && val.indexOf('0x') === 0) + return val; + // if its a negative number, pass it through fromDecimal + if(isString(val) && val.indexOf('-0x') === 0) + return fromDecimal(val); + + if(isString(val) && !isFinite(val)) + return fromAscii(val); + + if(isFinite(val)) + return fromDecimal(val); + + return val; +}; + +/** + * Returns value of unit in Wei + * + * @method getValueOfUnit + * @param {String} unit the unit to convert to, default ether + * @returns {BigNumber} value of the unit (in Wei) + * @throws error if the unit is not correct:w + */ +var getValueOfUnit = function (unit) { + unit = unit ? unit.toLowerCase() : 'ether'; + var unitValue = unitMap[unit]; + if (unitValue === undefined) { + throw new Error('This unit doesn\'t exists, please use the one of the following units' + JSON.stringify(unitMap, null, 2)); + } + return new BigNumber(unitValue, 10); +}; + +/** + * Takes a number of wei and converts it to any other ether unit. + * + * Possible units are: + * - kwei/ada + * - mwei/babbage + * - gwei/shannon + * - szabo + * - finney + * - ether + * - kether/grand/einstein + * - mether + * - gether + * - tether + * + * @method fromWei + * @param {Number|String} number can be a number, number string or a HEX of a decimal + * @param {String} unit the unit to convert to, default ether + * @return {String|Object} When given a BigNumber object it returns one as well, otherwise a number +*/ +var fromWei = function(number, unit) { + var returnValue = toBigNumber(number).dividedBy(getValueOfUnit(unit)); + + return (isBigNumber(number)) + ? returnValue : returnValue.toString(10); +}; + +/** + * Takes a number of a unit and converts it to wei. + * + * Possible units are: + * - kwei/ada + * - mwei/babbage + * - gwei/shannon + * - szabo + * - finney + * - ether + * - kether/grand/einstein + * - mether + * - gether + * - tether + * + * @method toWei + * @param {Number|String|BigNumber} number can be a number, number string or a HEX of a decimal + * @param {String} unit the unit to convert from, default ether + * @return {String|Object} When given a BigNumber object it returns one as well, otherwise a number +*/ +var toWei = function(number, unit) { + var returnValue = toBigNumber(number).times(getValueOfUnit(unit)); + + return (isBigNumber(number)) + ? returnValue : returnValue.toString(10); +}; + +/** + * Takes an input and transforms it into an bignumber + * + * @method toBigNumber + * @param {Number|String|BigNumber} a number, string, HEX string or BigNumber + * @return {BigNumber} BigNumber +*/ +var toBigNumber = function(number) { + number = number || 0; + if (isBigNumber(number)) + return number; + + return (isString(number) && (number.indexOf('0x') === 0 || number.indexOf('-0x') === 0)) + ? new BigNumber(number.replace('0x',''), 16) + : new BigNumber(number.toString(10), 10); +}; + +/** + * Takes and input transforms it into bignumber and if it is negative value, into two's complement + * + * @method toTwosComplement + * @param {Number|String|BigNumber} + * @return {BigNumber} + */ +var toTwosComplement = function (number) { + var bigNumber = toBigNumber(number); + if (bigNumber.lessThan(0)) { + return new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(bigNumber).plus(1); + } + return bigNumber; +}; + +/** + * Checks if the given string has proper length + * + * @method isAddress + * @param {String} address the given HEX adress + * @return {Boolean} +*/ +var isAddress = function(address) { + if (!isString(address)) { + return false; + } + + return ((address.indexOf('0x') === 0 && address.length === 42) || + (address.indexOf('0x') === -1 && address.length === 40)); +}; + +/** + * Returns true if object is BigNumber, otherwise false + * + * @method isBigNumber + * @param {Object} + * @return {Boolean} + */ +var isBigNumber = function (object) { + return object instanceof BigNumber || + (object && object.constructor && object.constructor.name === 'BigNumber'); +}; + +/** + * Returns true if object is string, otherwise false + * + * @method isString + * @param {Object} + * @return {Boolean} + */ +var isString = function (object) { + return typeof object === 'string' || + (object && object.constructor && object.constructor.name === 'String'); +}; + +/** + * Returns true if object is function, otherwise false + * + * @method isFunction + * @param {Object} + * @return {Boolean} + */ +var isFunction = function (object) { + return typeof object === 'function'; +}; + +module.exports = { + findIndex: findIndex, + toHex: toHex, + toDecimal: toDecimal, + fromDecimal: fromDecimal, + toAscii: toAscii, + fromAscii: fromAscii, + extractDisplayName: extractDisplayName, + extractTypeName: extractTypeName, + filterFunctions: filterFunctions, + filterEvents: filterEvents, + toWei: toWei, + fromWei: fromWei, + toBigNumber: toBigNumber, + toTwosComplement: toTwosComplement, + isBigNumber: isBigNumber, + isAddress: isAddress, + isFunction: isFunction, + isString: isString +}; + + +},{}],6:[function(require,module,exports){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. +*/ +/** @file web3.js + * @authors: + * Jeffrey Wilcke <jeff@ethdev.com> + * Marek Kotewicz <marek@ethdev.com> + * Marian Oancea <marian@ethdev.com> + * Gav Wood <g@ethdev.com> + * @date 2014 + */ + +var net = require('./web3/net'); +var eth = require('./web3/eth'); +var db = require('./web3/db'); +var shh = require('./web3/shh'); +var watches = require('./web3/watches'); +var filter = require('./web3/filter'); +var utils = require('./utils/utils'); +var formatters = require('./solidity/formatters'); +var requestManager = require('./web3/requestmanager'); +var c = require('./utils/config'); + +/// @returns an array of objects describing web3 api methods +var web3Methods = function () { + return [ + { name: 'sha3', call: 'web3_sha3' } + ]; +}; + +/// creates methods in a given object based on method description on input +/// setups api calls for these methods +var setupMethods = function (obj, methods) { + methods.forEach(function (method) { + // allow for object methods 'myObject.method' + var objectMethods = method.name.split('.'), + callFunction = function () { + /*jshint maxcomplexity:8 */ + + var callback = null, + args = Array.prototype.slice.call(arguments), + call = typeof method.call === 'function' ? method.call(args) : method.call; + + // get the callback if one is available + if(typeof args[args.length-1] === 'function'){ + callback = args[args.length-1]; + Array.prototype.pop.call(args); + } + + // add the defaultBlock if not given + if(method.addDefaultblock) { + if(args.length !== method.addDefaultblock) + Array.prototype.push.call(args, (isFinite(c.ETH_DEFAULTBLOCK) ? utils.fromDecimal(c.ETH_DEFAULTBLOCK) : c.ETH_DEFAULTBLOCK)); + else + args[args.length-1] = isFinite(args[args.length-1]) ? utils.fromDecimal(args[args.length-1]) : args[args.length-1]; + } + + // show deprecated warning + if(method.newMethod) + console.warn('This method is deprecated please use web3.'+ method.newMethod +'() instead.'); + + return web3.manager.send({ + method: call, + params: args, + outputFormatter: method.outputFormatter, + inputFormatter: method.inputFormatter, + addDefaultblock: method.addDefaultblock + }, callback); + }; + + if(objectMethods.length > 1) { + if(!obj[objectMethods[0]]) + obj[objectMethods[0]] = {}; + + obj[objectMethods[0]][objectMethods[1]] = callFunction; + + } else { + + obj[objectMethods[0]] = callFunction; + } + + }); +}; + +/// creates properties in a given object based on properties description on input +/// setups api calls for these properties +var setupProperties = function (obj, properties) { + properties.forEach(function (property) { + var proto = {}; + proto.get = function () { + + // show deprecated warning + if(property.newProperty) + console.warn('This property is deprecated please use web3.'+ property.newProperty +' instead.'); + + + return web3.manager.send({ + method: property.getter, + outputFormatter: property.outputFormatter + }); + }; + + if (property.setter) { + proto.set = function (val) { + + // show deprecated warning + if(property.newProperty) + console.warn('This property is deprecated please use web3.'+ property.newProperty +' instead.'); + + return web3.manager.send({ + method: property.setter, + params: [val], + inputFormatter: property.inputFormatter + }); + }; + } + + proto.enumerable = !property.newProperty; + Object.defineProperty(obj, property.name, proto); + + }); +}; + +/*jshint maxparams:4 */ +var startPolling = function (method, id, callback, uninstall) { + web3.manager.startPolling({ + method: method, + params: [id] + }, id, callback, uninstall); +}; +/*jshint maxparams:3 */ + +var stopPolling = function (id) { + web3.manager.stopPolling(id); +}; + +var ethWatch = { + startPolling: startPolling.bind(null, 'eth_getFilterChanges'), + stopPolling: stopPolling +}; + +var shhWatch = { + startPolling: startPolling.bind(null, 'shh_getFilterChanges'), + stopPolling: stopPolling +}; + +/// setups web3 object, and it's in-browser executed methods +var web3 = { + manager: requestManager(), + providers: {}, + + setProvider: function (provider) { + web3.manager.setProvider(provider); + }, + + /// Should be called to reset state of web3 object + /// Resets everything except manager + reset: function () { + web3.manager.reset(); + }, + + /// @returns hex string of the input + toHex: utils.toHex, + + /// @returns ascii string representation of hex value prefixed with 0x + toAscii: utils.toAscii, + + /// @returns hex representation (prefixed by 0x) of ascii string + fromAscii: utils.fromAscii, + + /// @returns decimal representaton of hex value prefixed by 0x + toDecimal: utils.toDecimal, + + /// @returns hex representation (prefixed by 0x) of decimal value + fromDecimal: utils.fromDecimal, + + /// @returns a BigNumber object + toBigNumber: utils.toBigNumber, + + toWei: utils.toWei, + fromWei: utils.fromWei, + isAddress: utils.isAddress, + + // provide network information + net: { + // peerCount: + }, + + + /// eth object prototype + eth: { + // DEPRECATED + contractFromAbi: function (abi) { + console.warn('Initiating a contract like this is deprecated please use var MyContract = eth.contract(abi); new MyContract(address); instead.'); + + return function(addr) { + // Default to address of Config. TODO: rremove prior to genesis. + addr = addr || '0xc6d9d2cd449a754c494264e1809c50e34d64562b'; + var ret = web3.eth.contract(addr, abi); + ret.address = addr; + return ret; + }; + }, + + /// @param filter may be a string, object or event + /// @param eventParams is optional, this is an object with optional event eventParams params + /// @param options is optional, this is an object with optional event options ('max'...) + /*jshint maxparams:4 */ + filter: function (fil, eventParams, options) { + + // if its event, treat it differently + if (fil._isEvent) + return fil(eventParams, options); + + return filter(fil, ethWatch, formatters.outputLogFormatter); + }, + // DEPRECATED + watch: function (fil, eventParams, options) { + console.warn('eth.watch() is deprecated please use eth.filter() instead.'); + return this.filter(fil, eventParams, options); + } + /*jshint maxparams:3 */ + }, + + /// db object prototype + db: {}, + + /// shh object prototype + shh: { + /// @param filter may be a string, object or event + filter: function (fil) { + return filter(fil, shhWatch, formatters.outputPostFormatter); + }, + // DEPRECATED + watch: function (fil) { + console.warn('shh.watch() is deprecated please use shh.filter() instead.'); + return this.filter(fil); + } + } +}; + + +// ADD defaultblock +Object.defineProperty(web3.eth, 'defaultBlock', { + get: function () { + return c.ETH_DEFAULTBLOCK; + }, + set: function (val) { + c.ETH_DEFAULTBLOCK = val; + return c.ETH_DEFAULTBLOCK; + } +}); + + +/// setups all api methods +setupMethods(web3, web3Methods()); +setupMethods(web3.net, net.methods); +setupProperties(web3.net, net.properties); +setupMethods(web3.eth, eth.methods); +setupProperties(web3.eth, eth.properties); +setupMethods(web3.db, db.methods()); +setupMethods(web3.shh, shh.methods()); +setupMethods(ethWatch, watches.eth()); +setupMethods(shhWatch, watches.shh()); + +module.exports = web3; + + +},{"./solidity/formatters":2,"./utils/config":4,"./utils/utils":5,"./web3/db":8,"./web3/eth":9,"./web3/filter":11,"./web3/net":15,"./web3/requestmanager":17,"./web3/shh":18,"./web3/watches":20}],7:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -283,9 +1339,9 @@ module.exports = { * @date 2014 */ -var web3 = require('./web3'); -var abi = require('./abi'); -var utils = require('./utils'); +var web3 = require('../web3'); +var abi = require('../solidity/abi'); +var utils = require('../utils/utils'); var eventImpl = require('./event'); var signature = require('./signature'); @@ -505,7 +1561,7 @@ function Contract(abi, address) { module.exports = contract; -},{"./abi":1,"./event":6,"./signature":14,"./utils":16,"./web3":18}],4:[function(require,module,exports){ +},{"../solidity/abi":1,"../utils/utils":5,"../web3":6,"./event":10,"./signature":19}],8:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -542,7 +1598,7 @@ module.exports = { methods: methods }; -},{}],5:[function(require,module,exports){ +},{}],9:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -565,81 +1621,124 @@ module.exports = { * @date 2015 */ +/** + * Web3 + * + * @module web3 + */ + +/** + * Eth methods and properties + * + * An example method object can look as follows: + * + * { + * name: 'getBlock', + * call: blockCall, + * outputFormatter: formatters.outputBlockFormatter, + * inputFormatter: [ // can be a formatter funciton or an array of functions. Where each item in the array will be used for one parameter + * utils.toHex, // formats paramter 1 + * function(param){ if(!param) return false; } // formats paramter 2 + * ] + * }, + * + * @class [web3] eth + * @constructor + */ + + var formatters = require('./formatters'); +var utils = require('../utils/utils'); var blockCall = function (args) { - return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber"; + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? "eth_getBlockByHash" : "eth_getBlockByNumber"; }; -var transactionCall = function (args) { - return typeof args[0] === "string" ? 'eth_transactionByHash' : 'eth_transactionByNumber'; +var transactionFromBlockCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getTransactionByBlockHashAndIndex' : 'eth_getTransactionByBlockNumberAndIndex'; }; var uncleCall = function (args) { - return typeof args[0] === "string" ? 'eth_uncleByHash' : 'eth_uncleByNumber'; + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getUncleByBlockHashAndIndex' : 'eth_getUncleByBlockNumberAndIndex'; }; -var transactionCountCall = function (args) { - return typeof args[0] === "string" ? 'eth_transactionCountByHash' : 'eth_transactionCountByNumber'; +var getBlockTransactionCountCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getBlockTransactionCountByHash' : 'eth_getBlockTransactionCountByNumber'; }; var uncleCountCall = function (args) { - return typeof args[0] === "string" ? 'eth_uncleCountByHash' : 'eth_uncleCountByNumber'; + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getUncleCountByBlockHash' : 'eth_getUncleCountByBlockNumber'; }; /// @returns an array of objects describing web3.eth api methods var methods = [ - { name: 'getBalance', call: 'eth_balanceAt', outputFormatter: formatters.convertToBigNumber}, - { name: 'getState', call: 'eth_stateAt' }, - { name: 'getStorage', call: 'eth_storageAt' }, - { name: 'getData', call: 'eth_codeAt' }, - { name: 'getBlock', call: blockCall, outputFormatter: formatters.outputBlockFormatter}, - { name: 'getUncle', call: uncleCall, outputFormatter: formatters.outputBlockFormatter}, - { name: 'getCompilers', call: 'eth_compilers' }, - { name: 'getBlockTransactionCount', call: transactionCountCall }, - { name: 'getBlockUncleCount', call: uncleCountCall }, - { name: 'getTransaction', call: transactionCall, outputFormatter: formatters.outputTransactionFormatter }, - { name: 'getTransactionCount', call: 'eth_countAt'}, - { name: 'sendTransaction', call: 'eth_transact', inputFormatter: formatters.inputTransactionFormatter }, - { name: 'call', call: 'eth_call' }, - { name: 'compile.solidity', call: 'eth_solidity' }, - { name: 'compile.lll', call: 'eth_lll' }, - { name: 'compile.serpent', call: 'eth_serpent' }, + { name: 'getBalance', call: 'eth_getBalance', addDefaultblock: 2, + outputFormatter: formatters.convertToBigNumber}, + { name: 'getStorage', call: 'eth_getStorage', addDefaultblock: 2}, + { name: 'getStorageAt', call: 'eth_getStorageAt', addDefaultblock: 3, + inputFormatter: utils.toHex}, + { name: 'getData', call: 'eth_getData', addDefaultblock: 2}, + { name: 'getBlock', call: blockCall, + outputFormatter: formatters.outputBlockFormatter, + inputFormatter: [utils.toHex, function(param){ return (!param) ? false : true; }]}, + { name: 'getUncle', call: uncleCall, + outputFormatter: formatters.outputBlockFormatter, + inputFormatter: [utils.toHex, function(param){ return (!param) ? false : true; }]}, + { name: 'getCompilers', call: 'eth_getCompilers' }, + { name: 'getBlockTransactionCount', call: getBlockTransactionCountCall, + outputFormatter: utils.toDecimal, + inputFormatter: utils.toHex }, + { name: 'getBlockUncleCount', call: uncleCountCall, + outputFormatter: utils.toDecimal, + inputFormatter: utils.toHex }, + { name: 'getTransaction', call: 'eth_getTransactionByHash', + outputFormatter: formatters.outputTransactionFormatter }, + { name: 'getTransactionFromBlock', call: transactionFromBlockCall, + outputFormatter: formatters.outputTransactionFormatter, + inputFormatter: utils.toHex }, + { name: 'getTransactionCount', call: 'eth_getTransactionCount', addDefaultblock: 2, + outputFormatter: utils.toDecimal}, + { name: 'sendTransaction', call: 'eth_sendTransaction', + inputFormatter: formatters.inputTransactionFormatter }, + { name: 'call', call: 'eth_call', addDefaultblock: 2, + inputFormatter: formatters.inputCallFormatter }, + { name: 'compile.solidity', call: 'eth_compileSolidity' }, + { name: 'compile.lll', call: 'eth_compileLLL' }, + { name: 'compile.serpent', call: 'eth_compileSerpent' }, { name: 'flush', call: 'eth_flush' }, // deprecated methods - { name: 'balanceAt', call: 'eth_balanceAt', newMethod: 'getBalance' }, - { name: 'stateAt', call: 'eth_stateAt', newMethod: 'getState' }, - { name: 'storageAt', call: 'eth_storageAt', newMethod: 'getStorage' }, - { name: 'countAt', call: 'eth_countAt', newMethod: 'getTransactionCount' }, - { name: 'codeAt', call: 'eth_codeAt', newMethod: 'getData' }, - { name: 'transact', call: 'eth_transact', newMethod: 'sendTransaction' }, - { name: 'block', call: blockCall, newMethod: 'getBlock' }, - { name: 'transaction', call: transactionCall, newMethod: 'getTransaction' }, - { name: 'uncle', call: uncleCall, newMethod: 'getUncle' }, - { name: 'compilers', call: 'eth_compilers', newMethod: 'getCompilers' }, - { name: 'solidity', call: 'eth_solidity', newMethod: 'compile.solidity' }, - { name: 'lll', call: 'eth_lll', newMethod: 'compile.lll' }, - { name: 'serpent', call: 'eth_serpent', newMethod: 'compile.serpent' }, - { name: 'transactionCount', call: transactionCountCall, newMethod: 'getBlockTransactionCount' }, - { name: 'uncleCount', call: uncleCountCall, newMethod: 'getBlockUncleCount' }, + { name: 'balanceAt', call: 'eth_balanceAt', newMethod: 'eth.getBalance' }, + { name: 'stateAt', call: 'eth_stateAt', newMethod: 'eth.getStorageAt' }, + { name: 'storageAt', call: 'eth_storageAt', newMethod: 'eth.getStorage' }, + { name: 'countAt', call: 'eth_countAt', newMethod: 'eth.getTransactionCount' }, + { name: 'codeAt', call: 'eth_codeAt', newMethod: 'eth.getData' }, + { name: 'transact', call: 'eth_transact', newMethod: 'eth.sendTransaction' }, + { name: 'block', call: blockCall, newMethod: 'eth.getBlock' }, + { name: 'transaction', call: transactionFromBlockCall, newMethod: 'eth.getTransaction' }, + { name: 'uncle', call: uncleCall, newMethod: 'eth.getUncle' }, + { name: 'compilers', call: 'eth_compilers', newMethod: 'eth.getCompilers' }, + { name: 'solidity', call: 'eth_solidity', newMethod: 'eth.compile.solidity' }, + { name: 'lll', call: 'eth_lll', newMethod: 'eth.compile.lll' }, + { name: 'serpent', call: 'eth_serpent', newMethod: 'eth.compile.serpent' }, + { name: 'transactionCount', call: getBlockTransactionCountCall, newMethod: 'eth.getBlockTransactionCount' }, + { name: 'uncleCount', call: uncleCountCall, newMethod: 'eth.getBlockUncleCount' }, { name: 'logs', call: 'eth_logs' } ]; /// @returns an array of objects describing web3.eth api properties var properties = [ - { name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' }, - { name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' }, - { name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' }, + { name: 'coinbase', getter: 'eth_coinbase'}, + { name: 'mining', getter: 'eth_mining'}, { name: 'gasPrice', getter: 'eth_gasPrice', outputFormatter: formatters.convertToBigNumber}, { name: 'accounts', getter: 'eth_accounts' }, - { name: 'peerCount', getter: 'eth_peerCount' }, - { name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' }, - { name: 'blockNumber', getter: 'eth_number'}, + { name: 'blockNumber', getter: 'eth_blockNumber', outputFormatter: utils.toDecimal}, // deprecated properties - { name: 'number', getter: 'eth_number', newProperty: 'blockNumber'} + { name: 'listening', getter: 'net_listening', setter: 'eth_setListening', newProperty: 'net.listening'}, + { name: 'peerCount', getter: 'net_peerCount', newProperty: 'net.peerCount'}, + { name: 'number', getter: 'eth_number', newProperty: 'eth.blockNumber'} ]; @@ -649,7 +1748,7 @@ module.exports = { }; -},{"./formatters":8}],6:[function(require,module,exports){ +},{"../utils/utils":5,"./formatters":12}],10:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -672,8 +1771,8 @@ module.exports = { * @date 2014 */ -var abi = require('./abi'); -var utils = require('./utils'); +var abi = require('../solidity/abi'); +var utils = require('../utils/utils'); var signature = require('./signature'); /// filter inputs array && returns only indexed (or not) inputs @@ -789,7 +1888,7 @@ module.exports = { }; -},{"./abi":1,"./signature":14,"./utils":16}],7:[function(require,module,exports){ +},{"../solidity/abi":1,"../utils/utils":5,"./signature":19}],11:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -815,6 +1914,8 @@ module.exports = { * @date 2014 */ +var utils = require('../utils/utils'); + /// Should be called to check if filter implementation is valid /// @returns true if it is, otherwise false var implementationIsValid = function (i) { @@ -830,16 +1931,25 @@ var implementationIsValid = function (i) { /// @param should be string or object /// @returns options string or object var getOptions = function (options) { + /*jshint maxcomplexity:5 */ + if (typeof options === 'string') { return options; } options = options || {}; - if (options.topics) { + if (options.topics) console.warn('"topics" is deprecated, is "topic" instead'); + + // make sure topics, get converted to hex + if(options.topic instanceof Array) { + options.topic = options.topic.map(function(topic){ + return utils.toHex(topic); + }); } + // evaluate lazy properties return { to: options.to, @@ -866,6 +1976,8 @@ var filter = function(options, implementation, formatter) { options = getOptions(options); var callbacks = []; var filterId = implementation.newFilter(options); + + // call the callbacks var onMessages = function (messages) { messages.forEach(function (message) { message = formatter ? formatter(message) : message; @@ -927,7 +2039,7 @@ var filter = function(options, implementation, formatter) { module.exports = filter; -},{}],8:[function(require,module,exports){ +},{"../utils/utils":5}],12:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -947,286 +2059,188 @@ module.exports = filter; /** @file formatters.js * @authors: * Marek Kotewicz <marek@ethdev.com> + * Fabian Vogelsteller <fabian@ethdev.com> * @date 2015 */ -if ("build" !== 'build') {/* - var BigNumber = require('bignumber.js'); // jshint ignore:line -*/} - -var utils = require('./utils'); -var c = require('./const'); - -/// @param string string to be padded -/// @param number of characters that result string should have -/// @param sign, by default 0 -/// @returns right aligned string -var padLeft = function (string, chars, sign) { - return new Array(chars - string.length + 1).join(sign ? sign : "0") + string; -}; - -/// Formats input value to byte representation of int -/// If value is negative, return it's two's complement -/// If the value is floating point, round it down -/// @returns right-aligned byte representation of int -var formatInputInt = function (value) { - /*jshint maxcomplexity:7 */ - var padding = c.ETH_PADDING * 2; - if (utils.isBigNumber(value) || typeof value === 'number') { - if (typeof value === 'number') - value = new BigNumber(value); - BigNumber.config(c.ETH_BIGNUMBER_ROUNDING_MODE); - value = value.round(); - - if (value.lessThan(0)) - value = new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(value).plus(1); - value = value.toString(16); - } - else if (typeof value === 'string') { - if (value.indexOf('0x') === 0) { - value = value.substr(2); - } else { - value = formatInputInt(new BigNumber(value)); - } - } - else - value = (+value).toString(16); - return padLeft(value, padding); -}; - -/// Formats input value to byte representation of string -/// @returns left-algined byte representation of string -var formatInputString = function (value) { - return utils.fromAscii(value, c.ETH_PADDING).substr(2); -}; +var utils = require('../utils/utils'); -/// Formats input value to byte representation of bool -/// @returns right-aligned byte representation bool -var formatInputBool = function (value) { - return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0'); -}; - -/// Formats input value to byte representation of real -/// Values are multiplied by 2^m and encoded as integers -/// @returns byte representation of real -var formatInputReal = function (value) { - return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); -}; - - -/// Check if input value is negative -/// @param value is hex format -/// @returns true if it is negative, otherwise false -var signedIsNegative = function (value) { - return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1'; +/** + * Should the input to a big number + * + * @method convertToBigNumber + * @param {String|Number|BigNumber} + * @returns {BigNumber} object + */ +var convertToBigNumber = function (value) { + return utils.toBigNumber(value); }; -/// Formats input right-aligned input bytes to int -/// @returns right-aligned input bytes formatted to int -var formatOutputInt = function (value) { - - value = value || "0"; +/** + * Formats the input of a transaction and converts all values to HEX + * + * @method inputTransactionFormatter + * @param {Object} transaction options + * @returns object +*/ +var inputTransactionFormatter = function (options){ - // check if it's negative number - // it it is, return two's complement - if (signedIsNegative(value)) { - return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1); + // make code -> data + if (options.code) { + options.data = options.code; + delete options.code; } - return new BigNumber(value, 16); -}; + ['gasPrice', 'gas', 'value'].forEach(function(key){ + options[key] = utils.fromDecimal(options[key]); + }); -/// Formats big right-aligned input bytes to uint -/// @returns right-aligned input bytes formatted to uint -var formatOutputUInt = function (value) { - value = value || "0"; - return new BigNumber(value, 16); -}; - -/// @returns input bytes formatted to real -var formatOutputReal = function (value) { - return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128)); -}; - -/// @returns input bytes formatted to ureal -var formatOutputUReal = function (value) { - return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128)); -}; - -/// @returns right-aligned input bytes formatted to hex -var formatOutputHash = function (value) { - return "0x" + value; -}; - -/// @returns right-aligned input bytes formatted to bool -var formatOutputBool = function (value) { - return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false; -}; - -/// @returns left-aligned input bytes formatted to ascii string -var formatOutputString = function (value) { - return utils.toAscii(value); -}; - -/// @returns right-aligned input bytes formatted to address -var formatOutputAddress = function (value) { - return "0x" + value.slice(value.length - 40, value.length); + return options; }; - -/// Formats the input to a big number -/// @returns a BigNumber object -var convertToBigNumber = function (value) { - - // remove the leading 0x - if(typeof value === 'string') - value = value.replace('0x', ''); - - value = value || "0"; - - return new BigNumber(value, 16); +/** + * Formats the output of a transaction to its proper values + * + * @method outputTransactionFormatter + * @param {Object} transaction + * @returns {Object} transaction +*/ +var outputTransactionFormatter = function (tx){ + tx.gas = utils.toDecimal(tx.gas); + tx.gasPrice = utils.toBigNumber(tx.gasPrice); + tx.value = utils.toBigNumber(tx.value); + return tx; }; - /** -Formats the input of a transaction and converts all values to HEX - -@returns object + * Formats the input of a call and converts all values to HEX + * + * @method inputCallFormatter + * @param {Object} transaction options + * @returns object */ -var inputTransactionFormatter = function(options){ +var inputCallFormatter = function (options){ // make code -> data - if(options.code) { + if (options.code) { options.data = options.code; delete options.code; } - // make endowment -> value - if(options.endowment) { - options.value = options.endowment; - delete options.endowment; - } - - - // format the following options - /*jshint maxcomplexity:5 */ - ['gasPrice', 'value'].forEach(function(key){ - - // if hex or string integer - if(typeof options[key] === 'string') { - - // if not hex assume its a number string - if(options[key].indexOf('0x') === -1) - options[key] = utils.fromDecimal(options[key]); - - // if number - } else if(typeof options[key] === 'number') { - options[key] = utils.fromDecimal(options[key]); - - // if bignumber - } else if(options[key] instanceof BigNumber) { - options[key] = '0x'+ options[key].toString(16); - } - }); - - // format gas to number - options.gas = Number(options.gas); - - return options; }; -/** -Formats the output of a transaction to its proper values -@returns object +/** + * Formats the output of a block to its proper values + * + * @method outputBlockFormatter + * @param {Object} block object + * @returns {Object} block object */ -var outputTransactionFormatter = function(tx){ - // transform to number - tx.gas = Number(tx.gas); +var outputBlockFormatter = function(block){ - // gasPrice to bignumber - if(typeof tx.gasPrice === 'string' && tx.gasPrice.indexOf('0x') === 0) - tx.gasPrice = new BigNumber(tx.gasPrice, 16); - else - tx.gasPrice = new BigNumber(tx.gasPrice.toString(10), 10); + // transform to number + block.gasLimit = utils.toDecimal(block.gasLimit); + block.gasUsed = utils.toDecimal(block.gasUsed); + block.size = utils.toDecimal(block.size); + block.timestamp = utils.toDecimal(block.timestamp); + block.number = utils.toDecimal(block.number); + + block.minGasPrice = utils.toBigNumber(block.minGasPrice); + block.difficulty = utils.toBigNumber(block.difficulty); + block.totalDifficulty = utils.toBigNumber(block.totalDifficulty); + + if(block.transactions instanceof Array) { + block.transactions.forEach(function(item){ + if(!utils.isString(item)) + return outputTransactionFormatter(item); + }); + } - // value to bignumber - if(typeof tx.value === 'string' && tx.value.indexOf('0x') === 0) - tx.value = new BigNumber(tx.value, 16); - else - tx.value = new BigNumber(tx.value.toString(10), 10); + return block; +}; - return tx; +/** + * Formats the output of a log + * + * @method outputLogFormatter + * @param {Object} log object + * @returns {Object} log +*/ +var outputLogFormatter = function(log){ + log.number = utils.toDecimal(log.number); + return log; }; /** -Formats the output of a block to its proper values - -@returns object + * Formats the input of a whisper post and converts all values to HEX + * + * @method inputPostFormatter + * @param {Object} transaction object + * @returns {Object} */ -var outputBlockFormatter = function(block){ - /*jshint maxcomplexity:7 */ +var inputPostFormatter = function(post){ - // transform to number - block.gasLimit = Number(block.gasLimit); - block.gasUsed = Number(block.gasUsed); - block.size = Number(block.size); - block.timestamp = Number(block.timestamp); - block.number = Number(block.number); - - // minGasPrice to bignumber - if(block.minGasPrice) { - if(typeof block.minGasPrice === 'string' && block.minGasPrice.indexOf('0x') === 0) - block.minGasPrice = new BigNumber(block.minGasPrice, 16); - else - block.minGasPrice = new BigNumber(block.minGasPrice.toString(10), 10); - } + post.payload = utils.toHex(post.payload); + post.ttl = utils.fromDecimal(post.ttl); + post.workToProve = utils.fromDecimal(post.workToProve); + if(!(post.topic instanceof Array)) + post.topic = [post.topic]; - // difficulty to bignumber - if(block.difficulty) { - if(typeof block.difficulty === 'string' && block.difficulty.indexOf('0x') === 0) - block.difficulty = new BigNumber(block.difficulty, 16); - else - block.difficulty = new BigNumber(block.difficulty.toString(10), 10); - } + // format the following options + post.topic = post.topic.map(function(topic){ + return utils.fromAscii(topic); + }); + + return post; +}; - // difficulty to bignumber - if(block.totalDifficulty) { - if(typeof block.totalDifficulty === 'string' && block.totalDifficulty.indexOf('0x') === 0) - block.totalDifficulty = new BigNumber(block.totalDifficulty, 16); - else - block.totalDifficulty = new BigNumber(block.totalDifficulty.toString(10), 10); +/** + * Formats the output of a received post message + * + * @method outputPostFormatter + * @param {Object} + * @returns {Object} + */ +var outputPostFormatter = function(post){ + + post.expiry = utils.toDecimal(post.expiry); + post.sent = utils.toDecimal(post.sent); + post.ttl = utils.toDecimal(post.ttl); + post.payloadRaw = post.payload; + post.payload = utils.toAscii(post.payload); + + if(post.payload.indexOf('{') === 0 || post.payload.indexOf('[') === 0) { + try { + post.payload = JSON.parse(post.payload); + } catch (e) { } } - return block; -}; + // format the following options + post.topic = post.topic.map(function(topic){ + return utils.toAscii(topic); + }); + return post; +}; module.exports = { - formatInputInt: formatInputInt, - formatInputString: formatInputString, - formatInputBool: formatInputBool, - formatInputReal: formatInputReal, - formatOutputInt: formatOutputInt, - formatOutputUInt: formatOutputUInt, - formatOutputReal: formatOutputReal, - formatOutputUReal: formatOutputUReal, - formatOutputHash: formatOutputHash, - formatOutputBool: formatOutputBool, - formatOutputString: formatOutputString, - formatOutputAddress: formatOutputAddress, convertToBigNumber: convertToBigNumber, inputTransactionFormatter: inputTransactionFormatter, outputTransactionFormatter: outputTransactionFormatter, - outputBlockFormatter: outputBlockFormatter + inputCallFormatter: inputCallFormatter, + outputBlockFormatter: outputBlockFormatter, + outputLogFormatter: outputLogFormatter, + inputPostFormatter: inputPostFormatter, + outputPostFormatter: outputPostFormatter }; -},{"./const":2,"./utils":16}],9:[function(require,module,exports){ +},{"../utils/utils":5}],13:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1243,7 +2257,7 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. */ -/** @file httpsync.js +/** @file httpprovider.js * @authors: * Marek Kotewicz <marek@ethdev.com> * Marian Oancea <marian@ethdev.com> @@ -1254,29 +2268,43 @@ if ("build" !== 'build') {/* var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line */} -var HttpSyncProvider = function (host) { +var HttpProvider = function (host) { this.handlers = []; this.host = host || 'http://localhost:8080'; }; -HttpSyncProvider.prototype.send = function (payload) { - //var data = formatJsonRpcObject(payload); - +HttpProvider.prototype.send = function (payload, callback) { var request = new XMLHttpRequest(); request.open('POST', this.host, false); - request.send(JSON.stringify(payload)); - var result = request.responseText; - // check request.status - if(request.status !== 200) - return; - return JSON.parse(result); + // ASYNC + if(typeof callback === 'function') { + request.onreadystatechange = function() { + if(request.readyState === 4 && request.status === 200) { + callback(JSON.parse(request.responseText)); + } + }; + + request.open('POST', this.host, true); + request.send(JSON.stringify(payload)); + + // SYNC + } else { + request.open('POST', this.host, false); + request.send(JSON.stringify(payload)); + + // check request.status + if(request.status !== 200) + return; + return JSON.parse(request.responseText); + + } }; -module.exports = HttpSyncProvider; +module.exports = HttpProvider; -},{}],10:[function(require,module,exports){ +},{}],14:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1343,7 +2371,50 @@ module.exports = { -},{}],11:[function(require,module,exports){ +},{}],15:[function(require,module,exports){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. +*/ +/** @file eth.js + * @authors: + * Marek Kotewicz <marek@ethdev.com> + * @date 2015 + */ + +var utils = require('../utils/utils'); + +/// @returns an array of objects describing web3.eth api methods +var methods = [ + // { name: 'getBalance', call: 'eth_balanceAt', outputFormatter: formatters.convertToBigNumber}, +]; + +/// @returns an array of objects describing web3.eth api properties +var properties = [ + { name: 'listening', getter: 'net_listening'}, + { name: 'peerCount', getter: 'net_peerCount', outputFormatter: utils.toDecimal }, +]; + + +module.exports = { + methods: methods, + properties: properties +}; + + +},{"../utils/utils":5}],16:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1378,7 +2449,7 @@ QtSyncProvider.prototype.send = function (payload) { module.exports = QtSyncProvider; -},{}],12:[function(require,module,exports){ +},{}],17:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1405,7 +2476,7 @@ module.exports = QtSyncProvider; */ var jsonrpc = require('./jsonrpc'); -var c = require('./const'); +var c = require('../utils/config'); /** * It's responsible for passing messages to providers @@ -1417,16 +2488,29 @@ var requestManager = function() { var timeout = null; var provider; - var send = function (data) { - /*jshint maxcomplexity: 6 */ + var send = function (data, callback) { + /*jshint maxcomplexity: 7 */ - // format the input before sending + // FORMAT BASED ON ONE FORMATTER function if(typeof data.inputFormatter === 'function') { - data.params = Array.prototype.map.call(data.params, function(item){ - return data.inputFormatter(item); + data.params = Array.prototype.map.call(data.params, function(item, index){ + // format everything besides the defaultblock, which is already formated + return (!data.addDefaultblock || index+1 < data.addDefaultblock) + ? data.inputFormatter(item) + : item; + }); + + // FORMAT BASED ON the input FORMATTER ARRAY + } else if(data.inputFormatter instanceof Array) { + data.params = Array.prototype.map.call(data.inputFormatter, function(formatter, index){ + // format everything besides the defaultblock, which is already formated + return (!data.addDefaultblock || index+1 < data.addDefaultblock) + ? formatter(data.params[index]) + : data.params[index]; }); } + var payload = jsonrpc.toPayload(data.method, data.params); if (!provider) { @@ -1434,17 +2518,36 @@ var requestManager = function() { return null; } - var result = provider.send(payload); + // ASYNC (only when callback is given, and it a HttpProvidor) + if(typeof callback === 'function' && provider.host){ + provider.send(payload, function(result){ - if (!jsonrpc.isValidResponse(result)) { - console.log(result); - if(typeof result === 'object' && result.error && result.error.message) - console.error(result.error.message); - return null; + if (!jsonrpc.isValidResponse(result)) { + console.log(result); + if(typeof result === 'object' && result.error && result.error.message) + console.error(result.error.message); + return null; + } + + // format the output + callback((typeof data.outputFormatter === 'function') ? data.outputFormatter(result.result) : result.result); + }); + + // SYNC + } else { + var result = provider.send(payload); + + if (!jsonrpc.isValidResponse(result)) { + console.log(result); + if(typeof result === 'object' && result.error && result.error.message) + console.error(result.error.message); + return null; + } + + // format the output + return (typeof data.outputFormatter === 'function') ? data.outputFormatter(result.result) : result.result; } - // format the output - return (typeof data.outputFormatter === 'function') ? data.outputFormatter(result.result) : result.result; }; var setProvider = function (p) { @@ -1481,11 +2584,13 @@ var requestManager = function() { var poll = function () { polls.forEach(function (data) { - var result = send(data.data); - if (!(result instanceof Array) || result.length === 0) { - return; - } - data.callback(result); + // send async + send(data.data, function(result){ + if (!(result instanceof Array) || result.length === 0) { + return; + } + data.callback(result); + }); }); timeout = setTimeout(poll, c.ETH_POLLING_TIMEOUT); }; @@ -1504,7 +2609,7 @@ var requestManager = function() { module.exports = requestManager; -},{"./const":2,"./jsonrpc":10}],13:[function(require,module,exports){ +},{"../utils/config":4,"./jsonrpc":14}],18:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1527,17 +2632,19 @@ module.exports = requestManager; * @date 2015 */ +var formatters = require('./formatters'); + /// @returns an array of objects describing web3.shh api methods var methods = function () { return [ - { name: 'post', call: 'shh_post' }, + { name: 'post', call: 'shh_post', inputFormatter: formatters.inputPostFormatter }, { name: 'newIdentity', call: 'shh_newIdentity' }, - { name: 'hasIdentity', call: 'shh_haveIdentity' }, + { name: 'hasIdentity', call: 'shh_hasIdentity' }, { name: 'newGroup', call: 'shh_newGroup' }, { name: 'addToGroup', call: 'shh_addToGroup' }, // deprecated - { name: 'haveIdentity', call: 'shh_haveIdentity', newMethod: 'hasIdentity' }, + { name: 'haveIdentity', call: 'shh_haveIdentity', newMethod: 'shh.hasIdentity' }, ]; }; @@ -1546,7 +2653,7 @@ module.exports = { }; -},{}],14:[function(require,module,exports){ +},{"./formatters":12}],19:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1569,8 +2676,8 @@ module.exports = { * @date 2015 */ -var web3 = require('./web3'); -var c = require('./const'); +var web3 = require('../web3'); +var c = require('../utils/config'); /// @param function name for which we want to get signature /// @returns signature of function with given name @@ -1590,402 +2697,7 @@ module.exports = { }; -},{"./const":2,"./web3":18}],15:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. -*/ -/** @file types.js - * @authors: - * Marek Kotewicz <marek@ethdev.com> - * @date 2015 - */ - -var f = require('./formatters'); - -/// @param expected type prefix (string) -/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false -var prefixedType = function (prefix) { - return function (type) { - return type.indexOf(prefix) === 0; - }; -}; - -/// @param expected type name (string) -/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false -var namedType = function (name) { - return function (type) { - return name === type; - }; -}; - -/// Setups input formatters for solidity types -/// @returns an array of input formatters -var inputTypes = function () { - - return [ - { type: prefixedType('uint'), format: f.formatInputInt }, - { type: prefixedType('int'), format: f.formatInputInt }, - { type: prefixedType('hash'), format: f.formatInputInt }, - { type: prefixedType('string'), format: f.formatInputString }, - { type: prefixedType('real'), format: f.formatInputReal }, - { type: prefixedType('ureal'), format: f.formatInputReal }, - { type: namedType('address'), format: f.formatInputInt }, - { type: namedType('bool'), format: f.formatInputBool } - ]; -}; - -/// Setups output formaters for solidity types -/// @returns an array of output formatters -var outputTypes = function () { - - return [ - { type: prefixedType('uint'), format: f.formatOutputUInt }, - { type: prefixedType('int'), format: f.formatOutputInt }, - { type: prefixedType('hash'), format: f.formatOutputHash }, - { type: prefixedType('string'), format: f.formatOutputString }, - { type: prefixedType('real'), format: f.formatOutputReal }, - { type: prefixedType('ureal'), format: f.formatOutputUReal }, - { type: namedType('address'), format: f.formatOutputAddress }, - { type: namedType('bool'), format: f.formatOutputBool } - ]; -}; - -module.exports = { - prefixedType: prefixedType, - namedType: namedType, - inputTypes: inputTypes, - outputTypes: outputTypes -}; - - -},{"./formatters":8}],16:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. -*/ -/** @file utils.js - * @authors: - * Marek Kotewicz <marek@ethdev.com> - * @date 2015 - */ - -var c = require('./const'); - -if ("build" !== 'build') {/* - var BigNumber = require('bignumber.js'); // jshint ignore:line -*/} - -var unitMap = { - 'wei': '1', - 'kwei': '1000', - 'ada': '1000', - 'mwei': '1000000', - 'babbage': '1000000', - 'gwei': '1000000000', - 'shannon': '1000000000', - 'szabo': '1000000000000', - 'finney': '1000000000000000', - 'ether': '1000000000000000000', - 'kether': '1000000000000000000000', - 'grand': '1000000000000000000000', - 'einstein': '1000000000000000000000', - 'mether': '1000000000000000000000000', - 'gether': '1000000000000000000000000000', - 'tether': '1000000000000000000000000000000' -}; - - -/// Finds first index of array element matching pattern -/// @param array -/// @param callback pattern -/// @returns index of element -var findIndex = function (array, callback) { - var end = false; - var i = 0; - for (; i < array.length && !end; i++) { - end = callback(array[i]); - } - return end ? i - 1 : -1; -}; - -/// @returns ascii string representation of hex value prefixed with 0x -var toAscii = function(hex) { -// Find termination - var str = ""; - var i = 0, l = hex.length; - if (hex.substring(0, 2) === '0x') { - i = 2; - } - for (; i < l; i+=2) { - var code = parseInt(hex.substr(i, 2), 16); - if (code === 0) { - break; - } - - str += String.fromCharCode(code); - } - - return str; -}; - -var toHex = function(str) { - var hex = ""; - for(var i = 0; i < str.length; i++) { - var n = str.charCodeAt(i).toString(16); - hex += n.length < 2 ? '0' + n : n; - } - - return hex; -}; - -/// @returns hex representation (prefixed by 0x) of ascii string -var fromAscii = function(str, pad) { - pad = pad === undefined ? 0 : pad; - var hex = toHex(str); - while (hex.length < pad*2) - hex += "00"; - return "0x" + hex; -}; - -/// @returns display name for function/event eg. multiply(uint256) -> multiply -var extractDisplayName = function (name) { - var length = name.indexOf('('); - return length !== -1 ? name.substr(0, length) : name; -}; - -/// @returns overloaded part of function/event name -var extractTypeName = function (name) { - /// TODO: make it invulnerable - var length = name.indexOf('('); - return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)).replace(' ', '') : ""; -}; - -/// Filters all function from input abi -/// @returns abi array with filtered objects of type 'function' -var filterFunctions = function (json) { - return json.filter(function (current) { - return current.type === 'function'; - }); -}; - -/// Filters all events form input abi -/// @returns abi array with filtered objects of type 'event' -var filterEvents = function (json) { - return json.filter(function (current) { - return current.type === 'event'; - }); -}; - -/// used to transform value/string to eth string -/// TODO: use BigNumber.js to parse int -/// TODO: add tests for it! -var toEth = function (str) { - - console.warn('This method is deprecated please use eth.fromWei(BigNumberOrNumber, unit) instead.'); - - /*jshint maxcomplexity:7 */ - var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str.replace(/,/g,'').replace(/ /g,'')) : str; - var unit = 0; - var units = c.ETH_UNITS; - while (val > 3000 && unit < units.length - 1) - { - val /= 1000; - unit++; - } - var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2); - var replaceFunction = function($0, $1, $2) { - return $1 + ',' + $2; - }; - - while (true) { - var o = s; - s = s.replace(/(\d)(\d\d\d[\.\,])/, replaceFunction); - if (o === s) - break; - } - return s + ' ' + units[unit]; -}; - - -var toDecimal = function (val) { - // remove 0x and place 0, if it's required - val = val.length > 2 ? val.substring(2) : "0"; - return (new BigNumber(val, 16).toString(10)); -}; - -var fromDecimal = function (val) { - return "0x" + (new BigNumber(val).toString(16)); -}; - - -/** -Takes a number of wei and converts it to any other ether unit. - -Possible units are: - - - kwei/ada - - mwei/babbage - - gwei/shannon - - szabo - - finney - - ether - - kether/grand/einstein - - mether - - gether - - tether - -@method fromWei -@param {Number|String} number can be a number, number string or a HEX of a decimal -@param {String} unit the unit to convert to -@return {String|Object} When given a BigNumber object it returns one as well, otherwise a number -*/ -var fromWei = function(number, unit) { - /*jshint maxcomplexity: 6 */ - unit = unit.toLowerCase(); - - var isBigNumber = true; - - if(!unitMap[unit]) { - console.warn('This unit doesn\'t exists, please use the one of the following units' , unitMap); - return number; - } - - if(!number) - return number; - - if(typeof number === 'string' && number.indexOf('0x') === 0) { - isBigNumber = false; - number = new BigNumber(number, 16); - } - - if(!(number instanceof BigNumber)) { - isBigNumber = false; - number = new BigNumber(number.toString(10), 10); // toString to prevent errors, the user have to handle giving correct bignums themselves - } - - number = number.dividedBy(new BigNumber(unitMap[unit], 10)); - - return (isBigNumber) ? number : number.toString(10); -}; - -/** -Takes a number of a unit and converts it to wei. - -Possible units are: - - - kwei/ada - - mwei/babbage - - gwei/shannon - - szabo - - finney - - ether - - kether/grand/einstein - - mether - - gether - - tether - -@method toWei -@param {Number|String|BigNumber} number can be a number, number string or a HEX of a decimal -@param {String} unit the unit to convert to -@return {String|Object} When given a BigNumber object it returns one as well, otherwise a number -*/ -var toWei = function(number, unit) { - /*jshint maxcomplexity: 6 */ - unit = unit.toLowerCase(); - - var isBigNumber = true; - - if(!unitMap[unit]) { - console.warn('This unit doesn\'t exists, please use the one of the following units' , unitMap); - return number; - } - - if(!number) - return number; - - if(typeof number === 'string' && number.indexOf('0x') === 0) { - isBigNumber = false; - number = new BigNumber(number, 16); - } - - if(!(number instanceof BigNumber)) { - isBigNumber = false; - number = new BigNumber(number.toString(10), 10);// toString to prevent errors, the user have to handle giving correct bignums themselves - } - - - number = number.times(new BigNumber(unitMap[unit], 10)); - - return (isBigNumber) ? number : number.toString(10); -}; - - -/** -Checks if the given string is a valid ethereum HEX address. - -@method isAddress -@param {String} address the given HEX adress -@return {Boolean} -*/ -var isAddress = function(address) { - if(address.indexOf('0x') === 0 && address.length !== 42) - return false; - if(address.indexOf('0x') === -1 && address.length !== 40) - return false; - - return /^\w+$/.test(address); -}; - -var isBigNumber = function (value) { - return value instanceof BigNumber || - (value && value.constructor && value.constructor.name === 'BigNumber'); -}; - - -module.exports = { - findIndex: findIndex, - toDecimal: toDecimal, - fromDecimal: fromDecimal, - toAscii: toAscii, - fromAscii: fromAscii, - extractDisplayName: extractDisplayName, - extractTypeName: extractTypeName, - filterFunctions: filterFunctions, - filterEvents: filterEvents, - toEth: toEth, - toWei: toWei, - fromWei: fromWei, - isAddress: isAddress, - isBigNumber: isBigNumber -}; - - -},{"./const":2}],17:[function(require,module,exports){ +},{"../utils/config":4,"../web3":6}],20:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -2011,13 +2723,13 @@ module.exports = { /// @returns an array of objects describing web3.eth.filter api methods var eth = function () { var newFilter = function (args) { - return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter'; + return typeof args[0] === 'string' ? 'eth_newBlockFilter' : 'eth_newFilter'; }; return [ { name: 'newFilter', call: newFilter }, { name: 'uninstallFilter', call: 'eth_uninstallFilter' }, - { name: 'getLogs', call: 'eth_filterLogs' } + { name: 'getLogs', call: 'eth_getFilterLogs' } ]; }; @@ -2036,259 +2748,16 @@ module.exports = { }; -},{}],18:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. -*/ -/** @file web3.js - * @authors: - * Jeffrey Wilcke <jeff@ethdev.com> - * Marek Kotewicz <marek@ethdev.com> - * Marian Oancea <marian@ethdev.com> - * Gav Wood <g@ethdev.com> - * @date 2014 - */ - -// if (process.env.NODE_ENV !== 'build') { -// var BigNumber = require('bignumber.js'); -// } - -var eth = require('./eth'); -var db = require('./db'); -var shh = require('./shh'); -var watches = require('./watches'); -var filter = require('./filter'); -var utils = require('./utils'); -var requestManager = require('./requestmanager'); - -/// @returns an array of objects describing web3 api methods -var web3Methods = function () { - return [ - { name: 'sha3', call: 'web3_sha3' } - ]; -}; - -/// creates methods in a given object based on method description on input -/// setups api calls for these methods -var setupMethods = function (obj, methods) { - methods.forEach(function (method) { - // allow for object methods 'myObject.method' - var objectMethods = method.name.split('.'), - callFunction = function () { - var args = Array.prototype.slice.call(arguments); - var call = typeof method.call === 'function' ? method.call(args) : method.call; - - // show deprecated warning - if(method.newMethod) - console.warn('This method is deprecated please use eth.'+ method.newMethod +'() instead.'); - - return web3.manager.send({ - method: call, - params: args, - outputFormatter: method.outputFormatter, - inputFormatter: method.inputFormatter - }); - }; - - if(objectMethods.length > 1) { - if(!obj[objectMethods[0]]) - obj[objectMethods[0]] = {}; - - obj[objectMethods[0]][objectMethods[1]] = callFunction; - - } else { - - obj[objectMethods[0]] = callFunction; - } - - }); -}; - -/// creates properties in a given object based on properties description on input -/// setups api calls for these properties -var setupProperties = function (obj, properties) { - properties.forEach(function (property) { - var proto = {}; - proto.get = function () { - - // show deprecated warning - if(property.newProperty) - console.warn('This property is deprecated please use eth.'+ property.newProperty +' instead.'); - - - return web3.manager.send({ - method: property.getter, - outputFormatter: property.outputFormatter - }); - }; - - if (property.setter) { - proto.set = function (val) { - - // show deprecated warning - if(property.newProperty) - console.warn('This property is deprecated please use eth.'+ property.newProperty +' instead.'); - - return web3.manager.send({ - method: property.setter, - params: [val], - inputFormatter: property.inputFormatter - }); - }; - } - - proto.enumerable = !property.newProperty; - Object.defineProperty(obj, property.name, proto); - - }); -}; - -/*jshint maxparams:4 */ -var startPolling = function (method, id, callback, uninstall) { - web3.manager.startPolling({ - method: method, - params: [id] - }, id, callback, uninstall); -}; -/*jshint maxparams:3 */ - -var stopPolling = function (id) { - web3.manager.stopPolling(id); -}; - -var ethWatch = { - startPolling: startPolling.bind(null, 'eth_changed'), - stopPolling: stopPolling -}; - -var shhWatch = { - startPolling: startPolling.bind(null, 'shh_changed'), - stopPolling: stopPolling -}; - -/// setups web3 object, and it's in-browser executed methods -var web3 = { - manager: requestManager(), - providers: {}, - - setProvider: function (provider) { - web3.manager.setProvider(provider); - }, - - /// Should be called to reset state of web3 object - /// Resets everything except manager - reset: function () { - web3.manager.reset(); - }, - - /// @returns ascii string representation of hex value prefixed with 0x - toAscii: utils.toAscii, - - /// @returns hex representation (prefixed by 0x) of ascii string - fromAscii: utils.fromAscii, - - /// @returns decimal representaton of hex value prefixed by 0x - toDecimal: utils.toDecimal, - - /// @returns hex representation (prefixed by 0x) of decimal value - fromDecimal: utils.fromDecimal, - - /// used to transform value/string to eth string - toEth: utils.toEth, - - toWei: utils.toWei, - fromWei: utils.fromWei, - isAddress: utils.isAddress, - - - /// eth object prototype - eth: { - // DEPRECATED - contractFromAbi: function (abi) { - console.warn('Initiating a contract like this is deprecated please use var MyContract = eth.contract(abi); new MyContract(address); instead.'); - - return function(addr) { - // Default to address of Config. TODO: rremove prior to genesis. - addr = addr || '0xc6d9d2cd449a754c494264e1809c50e34d64562b'; - var ret = web3.eth.contract(addr, abi); - ret.address = addr; - return ret; - }; - }, - - /// @param filter may be a string, object or event - /// @param eventParams is optional, this is an object with optional event eventParams params - /// @param options is optional, this is an object with optional event options ('max'...) - /// TODO: fix it, 4 params? no way - /*jshint maxparams:4 */ - filter: function (fil, eventParams, options, formatter) { - - // if its event, treat it differently - if (fil._isEvent) - return fil(eventParams, options); - - return filter(fil, ethWatch, formatter); - }, - // DEPRECATED - watch: function (fil, eventParams, options, formatter) { - console.warn('eth.watch() is deprecated please use eth.filter() instead.'); - return this.filter(fil, eventParams, options, formatter); - } - /*jshint maxparams:3 */ - }, - - /// db object prototype - db: {}, - - /// shh object prototype - shh: { - /// @param filter may be a string, object or event - filter: function (fil) { - return filter(fil, shhWatch); - }, - // DEPRECATED - watch: function (fil) { - console.warn('shh.watch() is deprecated please use shh.filter() instead.'); - return this.filter(fil); - } - } -}; - -/// setups all api methods -setupMethods(web3, web3Methods()); -setupMethods(web3.eth, eth.methods); -setupProperties(web3.eth, eth.properties); -setupMethods(web3.db, db.methods()); -setupMethods(web3.shh, shh.methods()); -setupMethods(ethWatch, watches.eth()); -setupMethods(shhWatch, watches.shh()); - -module.exports = web3; - - -},{"./db":4,"./eth":5,"./filter":7,"./requestmanager":12,"./shh":13,"./utils":16,"./watches":17}],"web3":[function(require,module,exports){ +},{}],"web3":[function(require,module,exports){ var web3 = require('./lib/web3'); -web3.providers.HttpSyncProvider = require('./lib/httpsync'); -web3.providers.QtSyncProvider = require('./lib/qtsync'); -web3.eth.contract = require('./lib/contract'); -web3.abi = require('./lib/abi'); +web3.providers.HttpProvider = require('./lib/web3/httpprovider'); +web3.providers.QtSyncProvider = require('./lib/web3/qtsync'); +web3.eth.contract = require('./lib/web3/contract'); +web3.abi = require('./lib/solidity/abi'); module.exports = web3; -},{"./lib/abi":1,"./lib/contract":3,"./lib/httpsync":9,"./lib/qtsync":11,"./lib/web3":18}]},{},["web3"]) +},{"./lib/solidity/abi":1,"./lib/web3":6,"./lib/web3/contract":7,"./lib/web3/httpprovider":13,"./lib/web3/qtsync":16}]},{},["web3"]) //# sourceMappingURL=ethereum.js.map diff --git a/cmd/mist/assets/qml/main.qml b/cmd/mist/assets/qml/main.qml index 6824d2ba9..e06ddbd71 100644 --- a/cmd/mist/assets/qml/main.qml +++ b/cmd/mist/assets/qml/main.qml @@ -195,13 +195,6 @@ ApplicationWindow { Menu { title: "Developer" MenuItem { - iconSource: "../icecream.png" - text: "Debugger" - shortcut: "Ctrl+d" - onTriggered: eth.startDebugger() - } - - MenuItem { text: "Import Tx" onTriggered: { txImportDialog.visible = true @@ -756,24 +749,6 @@ ApplicationWindow { } } - Rectangle { - height: 55 - color: "transparent" - visible: true - Text { - text: "DEBUG" - font.family: sourceSansPro.name - font.weight: Font.DemiBold - anchors { - left: parent.left - top: parent.verticalCenter - leftMargin: 16 - } - color: "#AAA0A0" - } - } - - ColumnLayout { id: menuLegacy visible: true diff --git a/cmd/mist/assets/qml/views/chain.qml b/cmd/mist/assets/qml/views/chain.qml index 4d1bc0e03..9892beddf 100644 --- a/cmd/mist/assets/qml/views/chain.qml +++ b/cmd/mist/assets/qml/views/chain.qml @@ -178,7 +178,6 @@ Rectangle { } function showContractData(tx) { - txDetailsDebugButton.tx = tx if(tx.createsContract) { contractData.text = tx.data contractLabel.text = "<h4> Transaction created contract " + tx.address + "</h4>" @@ -202,22 +201,6 @@ Rectangle { id: contractLabel anchors.leftMargin: 10 } - Button { - property var tx - id: txDetailsDebugButton - anchors.right: parent.right - anchors.rightMargin: 10 - anchors.top: parent.top - anchors.topMargin: 10 - text: "Debug contract" - onClicked: { - if(tx && tx.createsContract){ - eth.startDbWithCode(tx.rawData) - }else { - eth.startDbWithContractAndData(tx.address, tx.rawData) - } - } - } TextArea { id: contractData text: "Contract" diff --git a/cmd/mist/debugger.go b/cmd/mist/debugger.go deleted file mode 100644 index c1ab2f3f1..000000000 --- a/cmd/mist/debugger.go +++ /dev/null @@ -1,367 +0,0 @@ -/* - This file is part of go-ethereum - - go-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - go-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @authors - * Jeffrey Wilcke <i@jev.io> - */ -package main - -import ( - "fmt" - "math/big" - "strconv" - "strings" - "unicode" - - "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethutil" - "github.com/ethereum/go-ethereum/state" - "github.com/ethereum/go-ethereum/vm" - "github.com/obscuren/qml" -) - -type DebuggerWindow struct { - win *qml.Window - engine *qml.Engine - lib *UiLib - - vm *vm.Vm - Db *Debugger - - state *state.StateDB -} - -func NewDebuggerWindow(lib *UiLib) *DebuggerWindow { - engine := qml.NewEngine() - component, err := engine.LoadFile(lib.AssetPath("debugger/debugger.qml")) - if err != nil { - fmt.Println(err) - - return nil - } - - win := component.CreateWindow(nil) - - w := &DebuggerWindow{engine: engine, win: win, lib: lib, vm: &vm.Vm{}} - w.Db = NewDebugger(w) - - return w -} - -func (self *DebuggerWindow) Show() { - context := self.engine.Context() - context.SetVar("dbg", self) - - go func() { - self.win.Show() - self.win.Wait() - }() -} - -func (self *DebuggerWindow) SetCode(code string) { - self.win.Set("codeText", code) -} - -func (self *DebuggerWindow) SetData(data string) { - self.win.Set("dataText", data) -} - -func (self *DebuggerWindow) SetAsm(data []byte) { - self.win.Root().Call("clearAsm") - - dis := core.Disassemble(data) - for _, str := range dis { - self.win.Root().Call("setAsm", str) - } -} - -func (self *DebuggerWindow) Compile(code string) { - var err error - script := ethutil.StringToByteFunc(code, func(s string) (ret []byte) { - ret, err = ethutil.Compile(s, true) - return - }) - - if err == nil { - self.SetAsm(script) - } -} - -// Used by QML -func (self *DebuggerWindow) AutoComp(code string) { - if self.Db.done { - self.Compile(code) - } -} - -func (self *DebuggerWindow) ClearLog() { - self.win.Root().Call("clearLog") -} - -func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, dataStr string) { - self.Stop() - - defer func() { - if r := recover(); r != nil { - self.Logf("compile FAULT: %v", r) - } - }() - - data := utils.FormatTransactionData(dataStr) - - var err error - script := ethutil.StringToByteFunc(scriptStr, func(s string) (ret []byte) { - ret, err = ethutil.Compile(s, false) - return - }) - - if err != nil { - self.Logln(err) - - return - } - - var ( - gas = ethutil.Big(gasStr) - gasPrice = ethutil.Big(gasPriceStr) - value = ethutil.Big(valueStr) - // Contract addr as test address - keyPair = self.lib.eth.KeyManager().KeyPair() - ) - - statedb := self.lib.eth.ChainManager().TransState() - account := self.lib.eth.ChainManager().TransState().GetAccount(keyPair.Address()) - contract := statedb.NewStateObject([]byte{0}) - contract.SetCode(script) - contract.SetBalance(value) - - self.SetAsm(script) - - block := self.lib.eth.ChainManager().CurrentBlock() - - msg := types.NewTransactionMessage(nil, value, gas, gasPrice, data) - env := core.NewEnv(statedb, self.lib.eth.ChainManager(), msg, block) - - self.Logf("callsize %d", len(script)) - go func() { - pgas := new(big.Int).Set(gas) - ret, err := env.Call(account, contract.Address(), data, gas, gasPrice, ethutil.Big0) - - rgas := new(big.Int).Sub(pgas, gas) - tot := new(big.Int).Mul(rgas, gasPrice) - self.Logf("gas usage %v total price = %v (%v)", rgas, tot, ethutil.CurrencyToString(tot)) - if err != nil { - self.Logln("exited with errors:", err) - } else { - if len(ret) > 0 { - self.Logf("exited: % x", ret) - } else { - self.Logf("exited: nil") - } - } - - statedb.Reset() - - if !self.Db.interrupt { - self.Db.done = true - } else { - self.Db.interrupt = false - } - }() -} - -func (self *DebuggerWindow) Logf(format string, v ...interface{}) { - self.win.Root().Call("setLog", fmt.Sprintf(format, v...)) -} - -func (self *DebuggerWindow) Logln(v ...interface{}) { - str := fmt.Sprintln(v...) - self.Logf("%s", str[:len(str)-1]) -} - -func (self *DebuggerWindow) Next() { - self.Db.Next() -} - -func (self *DebuggerWindow) Continue() { - self.vm.Stepping = false - self.Next() -} - -func (self *DebuggerWindow) Stop() { - if !self.Db.done { - self.Db.Q <- true - } -} - -func (self *DebuggerWindow) ExecCommand(command string) { - if len(command) > 0 { - cmd := strings.Split(command, " ") - switch cmd[0] { - case "help": - self.Logln("Debugger commands:") - self.Logln("break, bp Set breakpoint on instruction") - self.Logln("clear [log, break, bp] Clears previous set sub-command(s)") - case "break", "bp": - if len(cmd) > 1 { - lineNo, err := strconv.Atoi(cmd[1]) - if err != nil { - self.Logln(err) - break - } - self.Db.breakPoints = append(self.Db.breakPoints, int64(lineNo)) - self.Logf("break point set on instruction %d", lineNo) - } else { - self.Logf("'%s' requires line number", cmd[0]) - } - case "clear": - if len(cmd) > 1 { - switch cmd[1] { - case "break", "bp": - self.Db.breakPoints = nil - - self.Logln("Breakpoints cleared") - case "log": - self.ClearLog() - default: - self.Logf("clear '%s' is not valid", cmd[1]) - } - } else { - self.Logln("'clear' requires sub command") - } - - default: - self.Logf("Unknown command %s", cmd[0]) - } - } -} - -type Debugger struct { - N chan bool - Q chan bool - done, interrupt bool - breakPoints []int64 - main *DebuggerWindow - win *qml.Window -} - -func NewDebugger(main *DebuggerWindow) *Debugger { - db := &Debugger{make(chan bool), make(chan bool), true, false, nil, main, main.win} - - return db -} - -type storeVal struct { - Key, Value string -} - -func (self *Debugger) Step(evm vm.VirtualMachine, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, context *vm.Context) { -} - -func (self *Debugger) BreakHook(pc int, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, stateObject *state.StateObject) bool { - self.main.Logln("break on instr:", pc) - - return self.halting(pc, op, mem, stack, stateObject) -} - -func (self *Debugger) StepHook(pc int, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, stateObject *state.StateObject) bool { - return self.halting(pc, op, mem, stack, stateObject) -} - -func (self *Debugger) SetCode(byteCode []byte) { - self.main.SetAsm(byteCode) -} - -func (self *Debugger) BreakPoints() []int64 { - return self.breakPoints -} - -func (d *Debugger) halting(pc int, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, stateObject *state.StateObject) bool { - d.win.Root().Call("setInstruction", pc) - d.win.Root().Call("clearMem") - d.win.Root().Call("clearStack") - d.win.Root().Call("clearStorage") - - addr := 0 - for i := 0; i+16 <= mem.Len(); i += 16 { - dat := mem.Data()[i : i+16] - var str string - - for _, d := range dat { - if unicode.IsGraphic(rune(d)) { - str += string(d) - } else { - str += "?" - } - } - - d.win.Root().Call("setMem", memAddr{fmt.Sprintf("%03d", addr), fmt.Sprintf("%s % x", str, dat)}) - addr += 16 - } - - for _, val := range stack.Data() { - d.win.Root().Call("setStack", val.String()) - } - - it := stateObject.Trie().Iterator() - for it.Next() { - d.win.Root().Call("setStorage", storeVal{fmt.Sprintf("% x", it.Key), fmt.Sprintf("% x", it.Value)}) - - } - - stackFrameAt := new(big.Int).SetBytes(mem.Get(0, 32)) - psize := mem.Len() - int(new(big.Int).SetBytes(mem.Get(0, 32)).Uint64()) - d.win.Root().ObjectByName("stackFrame").Set("text", fmt.Sprintf(`<b>stack ptr</b>: %v`, stackFrameAt)) - d.win.Root().ObjectByName("stackSize").Set("text", fmt.Sprintf(`<b>stack size</b>: %d`, psize)) - d.win.Root().ObjectByName("memSize").Set("text", fmt.Sprintf(`<b>mem size</b>: %v`, mem.Len())) - -out: - for { - select { - case <-d.N: - break out - case <-d.Q: - d.interrupt = true - d.clearBuffers() - - return false - } - } - - return true -} - -func (d *Debugger) clearBuffers() { -out: - // drain - for { - select { - case <-d.N: - case <-d.Q: - default: - break out - } - } -} - -func (d *Debugger) Next() { - if !d.done { - d.N <- true - } -} diff --git a/cmd/mist/ui_lib.go b/cmd/mist/ui_lib.go index 187d5b2d6..b202432c4 100644 --- a/cmd/mist/ui_lib.go +++ b/cmd/mist/ui_lib.go @@ -47,9 +47,7 @@ type UiLib struct { connected bool assetPath string // The main application window - win *qml.Window - Db *Debugger - DbWindow *DebuggerWindow + win *qml.Window jsEngine *javascript.JSRE @@ -126,29 +124,6 @@ func (ui *UiLib) AssetPath(p string) string { return path.Join(ui.assetPath, p) } -func (self *UiLib) StartDbWithContractAndData(contractHash, data string) { - dbWindow := NewDebuggerWindow(self) - object := self.eth.ChainManager().State().GetStateObject(ethutil.Hex2Bytes(contractHash)) - if len(object.Code()) > 0 { - dbWindow.SetCode(ethutil.Bytes2Hex(object.Code())) - } - dbWindow.SetData(data) - - dbWindow.Show() -} - -func (self *UiLib) StartDbWithCode(code string) { - dbWindow := NewDebuggerWindow(self) - dbWindow.SetCode(code) - dbWindow.Show() -} - -func (self *UiLib) StartDebugger() { - dbWindow := NewDebuggerWindow(self) - - dbWindow.Show() -} - func (self *UiLib) Transact(params map[string]interface{}) (string, error) { object := mapToTxParams(params) diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index a77c6ad4d..a7e609af7 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -35,7 +35,6 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/rlp" rpchttp "github.com/ethereum/go-ethereum/rpc/http" - "github.com/ethereum/go-ethereum/state" "github.com/ethereum/go-ethereum/xeth" ) @@ -134,6 +133,15 @@ func StartEthereum(ethereum *eth.Ethereum) { }) } +func StartEthereumForTest(ethereum *eth.Ethereum) { + clilogger.Infoln("Starting ", ethereum.Name()) + ethereum.StartForTest() + RegisterInterrupt(func(sig os.Signal) { + ethereum.Stop() + logger.Flush() + }) +} + func KeyTasks(keyManager *crypto.KeyManager, KeyRing string, GenAddr bool, SecretFile string, ExportDir string, NonInteractive bool) { var err error switch { @@ -188,27 +196,8 @@ func FormatTransactionData(data string) []byte { return d } -// Replay block -func BlockDo(ethereum *eth.Ethereum, hash []byte) error { - block := ethereum.ChainManager().GetBlock(hash) - if block == nil { - return fmt.Errorf("unknown block %x", hash) - } - - parent := ethereum.ChainManager().GetBlock(block.ParentHash()) - - statedb := state.New(parent.Root(), ethereum.StateDb()) - _, err := ethereum.BlockProcessor().TransitionState(statedb, parent, block, true) - if err != nil { - return err - } - - return nil - -} - -func ImportChain(chain *core.ChainManager, fn string) error { - fmt.Printf("importing chain '%s'\n", fn) +func ImportChain(chainmgr *core.ChainManager, fn string) error { + fmt.Printf("importing blockchain '%s'\n", fn) fh, err := os.OpenFile(fn, os.O_RDONLY, os.ModePerm) if err != nil { return err @@ -220,11 +209,24 @@ func ImportChain(chain *core.ChainManager, fn string) error { return err } - chain.Reset() - if err := chain.InsertChain(blocks); err != nil { + chainmgr.Reset() + if err := chainmgr.InsertChain(blocks); err != nil { return err } fmt.Printf("imported %d blocks\n", len(blocks)) return nil } + +func ExportChain(chainmgr *core.ChainManager, fn string) error { + fmt.Printf("exporting blockchain '%s'\n", fn) + + data := chainmgr.Export() + + if err := ethutil.WriteFile(fn, data); err != nil { + return err + } + fmt.Printf("exported blockchain\n") + + return nil +} diff --git a/core/block_processor.go b/core/block_processor.go index 34c12729c..ea9d06841 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -261,7 +261,7 @@ func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header) error { } if block.Time <= parent.Time { - return ValidationError("Block timestamp not after or equal to prev block (%v - %v)", block.Time, parent.Time) + return ValidationError("Block timestamp equal or less than previous block (%v - %v)", block.Time, parent.Time) } if int64(block.Time) > time.Now().Unix() { diff --git a/eth/backend.go b/eth/backend.go index 584d60c7e..f42ceda69 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -275,6 +275,17 @@ func (s *Ethereum) Start() error { return nil } +func (s *Ethereum) StartForTest() { + jsonlogger.LogJson(&logger.LogStarting{ + ClientString: s.net.Name, + ProtocolVersion: ProtocolVersion, + }) + + // Start services + s.txPool.Start() + s.blockPool.Start() +} + func (self *Ethereum) SuggestPeer(nodeURL string) error { n, err := discover.ParseNode(nodeURL) if err != nil { diff --git a/ethutil/natspec/natspec.go b/ethutil/natspec/natspec.go index 00e6f8720..793bf59ae 100644 --- a/ethutil/natspec/natspec.go +++ b/ethutil/natspec/natspec.go @@ -1,24 +1,21 @@ package natspec import ( + "fmt" "github.com/obscuren/otto" - "io/ioutil" ) type NatSpec struct { jsvm *otto.Otto } -func NewNATSpec(transaction string) (self *NatSpec, err error) { +// TODO: should initialise with abi and userdoc jsons +func New() (self *NatSpec, err error) { self = new(NatSpec) self.jsvm = otto.New() - code, err := ioutil.ReadFile("natspec.js") - if err != nil { - return - } - _, err = self.jsvm.Run(string(code)) + _, err = self.jsvm.Run(natspecJS) if err != nil { return } @@ -27,39 +24,40 @@ func NewNATSpec(transaction string) (self *NatSpec, err error) { return } - self.jsvm.Run("var transaction = " + transaction + ";") - - return -} - -func (self *NatSpec) SetDescription(desc string) (err error) { - - _, err = self.jsvm.Run("var expression = \"" + desc + "\";") return - } -func (self *NatSpec) SetABI(abi string) (err error) { - - _, err = self.jsvm.Run("var abi = " + abi + ";") - return - -} - -func (self *NatSpec) SetMethod(method string) (err error) { +func (self *NatSpec) Notice(transaction, abi, method, expression string) (string, error) { + var err error + if _, err = self.jsvm.Run("var transaction = " + transaction + ";"); err != nil { + return "", fmt.Errorf("natspec.js error setting transaction: %v", err) + } - _, err = self.jsvm.Run("var method = '" + method + "';") - return + if _, err = self.jsvm.Run("var abi = " + abi + ";"); err != nil { + return "", fmt.Errorf("natspec.js error setting abi: %v", err) + } -} + if _, err = self.jsvm.Run("var method = '" + method + "';"); err != nil { + return "", fmt.Errorf("natspec.js error setting method: %v", err) + } -func (self *NatSpec) Parse() string { + if _, err = self.jsvm.Run("var expression = \"" + expression + "\";"); err != nil { + return "", fmt.Errorf("natspec.js error setting expression: %v", err) + } self.jsvm.Run("var call = {method: method,abi: abi,transaction: transaction};") value, err := self.jsvm.Run("natspec.evaluateExpression(expression, call);") if err != nil { - return err.Error() + return "", fmt.Errorf("natspec.js error evaluating expression: %v", err) + } + evalError := "Natspec evaluation failed, wrong input params" + if value.String() == evalError { + return "", fmt.Errorf("natspec.js error evaluating expression: wrong input params in expression '%s'", expression) } - return value.String() + if len(value.String()) == 0 { + return "", fmt.Errorf("natspec.js error evaluating expression") + } + + return value.String(), nil } diff --git a/ethutil/natspec/natspec.js b/ethutil/natspec/natspec_js.go index 419ccd5c9..7acaaee4f 100644 --- a/ethutil/natspec/natspec.js +++ b/ethutil/natspec/natspec_js.go @@ -1,4 +1,6 @@ -require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ +package natspec + +const natspecJS = `require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ },{}],2:[function(require,module,exports){ // shim for using process in browser @@ -291,26 +293,26 @@ if (process.env.NODE_ENV !== 'build') { var BigNumber = require('bignumber.js'); // jshint ignore:line } -var ETH_UNITS = [ - 'wei', - 'Kwei', - 'Mwei', - 'Gwei', - 'szabo', - 'finney', - 'ether', - 'grand', - 'Mether', - 'Gether', - 'Tether', - 'Pether', - 'Eether', - 'Zether', - 'Yether', - 'Nether', - 'Dether', - 'Vether', - 'Uether' +var ETH_UNITS = [ + 'wei', + 'Kwei', + 'Mwei', + 'Gwei', + 'szabo', + 'finney', + 'ether', + 'grand', + 'Mether', + 'Gether', + 'Tether', + 'Pether', + 'Eether', + 'Zether', + 'Yether', + 'Nether', + 'Dether', + 'Vether', + 'Uether' ]; module.exports = { @@ -375,7 +377,7 @@ var formatInputInt = function (value) { BigNumber.config(c.ETH_BIGNUMBER_ROUNDING_MODE); value = value.round(); - if (value.lessThan(0)) + if (value.lessThan(0)) value = new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(value).plus(1); value = value.toString(16); } @@ -404,7 +406,7 @@ var formatInputBool = function (value) { /// Values are multiplied by 2^m and encoded as integers /// @returns byte representation of real var formatInputReal = function (value) { - return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); + return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); }; @@ -436,12 +438,12 @@ var formatOutputUInt = function (value) { /// @returns input bytes formatted to real var formatOutputReal = function (value) { - return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128)); + return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128)); }; /// @returns input bytes formatted to ureal var formatOutputUReal = function (value) { - return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128)); + return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128)); }; /// @returns right-aligned input bytes formatted to hex @@ -524,14 +526,14 @@ var namedType = function (name) { }; /// Setups input formatters for solidity types -/// @returns an array of input formatters +/// @returns an array of input formatters var inputTypes = function () { - + return [ { type: prefixedType('uint'), format: f.formatInputInt }, { type: prefixedType('int'), format: f.formatInputInt }, { type: prefixedType('hash'), format: f.formatInputInt }, - { type: prefixedType('string'), format: f.formatInputString }, + { type: prefixedType('string'), format: f.formatInputString }, { type: prefixedType('real'), format: f.formatInputReal }, { type: prefixedType('ureal'), format: f.formatInputReal }, { type: namedType('address'), format: f.formatInputInt }, @@ -620,7 +622,7 @@ var toAscii = function(hex) { return str; }; - + var toHex = function(str) { var hex = ""; for(var i = 0; i < str.length; i++) { @@ -642,7 +644,7 @@ var fromAscii = function(str, pad) { /// @returns display name for function/event eg. multiply(uint256) -> multiply var extractDisplayName = function (name) { - var length = name.indexOf('('); + var length = name.indexOf('('); return length !== -1 ? name.substr(0, length) : name; }; @@ -657,8 +659,8 @@ var extractTypeName = function (name) { /// @returns abi array with filtered objects of type 'function' var filterFunctions = function (json) { return json.filter(function (current) { - return current.type === 'function'; - }); + return current.type === 'function'; + }); }; /// Filters all events form input abi @@ -3404,7 +3406,7 @@ module.exports = { * @date 2015 */ -var abi = require('./node_modules/ethereum.js/lib/abi.js'); +var abi = require('./node_modules/ethereum.js/lib/abi.js'); /** * This object should be used to evaluate natspec expression @@ -3418,7 +3420,7 @@ var natspec = (function () { context[key] = obj[key]; }); } - + /// generate codes, which will be evaluated var generateCode = function (obj) { return Object.keys(obj).reduce(function (acc, key) { @@ -3440,20 +3442,20 @@ var natspec = (function () { /// @returns hashmap with all contract's method input variables var getMethodInputParams = function (method, transaction) { // do it with output formatter (cause we have to decode) - var params = abi.formatOutput(method.inputs, '0x' + transaction.params[0].data.slice(10)); + var params = abi.formatOutput(method.inputs, '0x' + transaction.params[0].data.slice(10)); return method.inputs.reduce(function (acc, current, index) { acc[current.name] = params[index]; return acc; }, {}); }; - + /// Should be called to evaluate expression var mapExpressionsToEvaluate = function (expression, cb) { var evaluatedExpression = ""; - // match everything in `` quotes - var pattern = /\`(?:\\.|[^`\\])*\`/gim + // match everything in backtick + var pattern = /\` + "`" + `(?:\\.|[^` + "`" + `\\])*\` + "`" + `/gim var match; var lastIndex = 0; while ((match = pattern.exec(expression)) !== null) { @@ -3464,9 +3466,9 @@ var natspec = (function () { evaluatedExpression += evaluatedPart; lastIndex = pattern.lastIndex; } - + evaluatedExpression += expression.slice(lastIndex); - + return evaluatedExpression; }; @@ -3478,11 +3480,11 @@ var natspec = (function () { var evaluateExpression = function (expression, call) { //var self = this; var context = {}; - + if (!!call) { try { var method = getMethodWithName(call.abi, call.method); - var params = getMethodInputParams(method, call.transaction); + var params = getMethodInputParams(method, call.transaction); copyToContext(params, context); } catch (err) { @@ -3498,7 +3500,7 @@ var natspec = (function () { return fn(context).toString(); } catch (err) { - return 'undefined'; + return 'undefined'; } }); @@ -3511,7 +3513,8 @@ var natspec = (function () { })(); -module.exports = natspec; +module.exports = natspec; },{"./node_modules/ethereum.js/lib/abi.js":3}]},{},[]); +` diff --git a/ethutil/natspec/natspec_test.go b/ethutil/natspec/natspec_test.go index 48a9cb25c..498f8d78e 100644 --- a/ethutil/natspec/natspec_test.go +++ b/ethutil/natspec/natspec_test.go @@ -6,41 +6,48 @@ import ( func TestNotice(t *testing.T) { - ns, err := NewNATSpec(` + tx := ` { - "jsonrpc": "2.0", - "method": "eth_call", - "params": [{ - "to": "0x8521742d3f456bd237e312d6e30724960f72517a", - "data": "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a" - }], - "id": 6 - } - `) + "jsonrpc": "2.0", + "method": "eth_call", + "params": [{ + "to": "0x8521742d3f456bd237e312d6e30724960f72517a", + "data": "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a" + }], + "id": 6 + } + ` + abi := ` + [{ + "name": "multiply", + "constant": false, + "type": "function", + "inputs": [{ + "name": "a", + "type": "uint256" + }], + "outputs": [{ + "name": "d", + "type": "uint256" + }] + }] + ` + + desc := "Will multiply `a` by 7 and return `a * 7`." + + method := "multiply" + + ns, err := New() if err != nil { t.Errorf("NewNATSpec error %v", err) } - ns.SetABI(` - [{ - "name": "multiply", - "constant": false, - "type": "function", - "inputs": [{ - "name": "a", - "type": "uint256" - }], - "outputs": [{ - "name": "d", - "type": "uint256" - }] - }] - `) - ns.SetDescription("Will multiply `a` by 7 and return `a * 7`.") - ns.SetMethod("multiply") - - notice := ns.Parse() + notice, err := ns.Notice(tx, abi, method, desc) + + if err != nil { + t.Errorf("expected no error got %v", err) + } expected := "Will multiply 122 by 7 and return 854." if notice != expected { @@ -48,4 +55,43 @@ func TestNotice(t *testing.T) { } else { t.Logf("returned notice \"%v\"", notice) } + + notice, err = ns.Notice(tx, abi, method, "Will multiply 122 by \"7\" and return 854.") + + expected = "natspec.js error setting expression: (anonymous): Line 1:41 Unexpected number" + + if err == nil { + t.Errorf("expected error, got nothing (notice: '%v')", err, notice) + } else { + if err.Error() != expected { + t.Errorf("expected error '%s' got '%v' (notice: '%v')", expected, err, notice) + } + } + + // https://github.com/ethereum/natspec.js/issues/1 + // badDesc := "Will multiply `e` by 7 and return `a * 7`." + // notice, err = ns.Notice(tx, abi, method, badDesc) + + // expected = "natspec.js error evaluating expression: wrong input param in expression ''" + + // if err == nil { + // t.Errorf("expected error, got nothing (notice: '%v')", notice) + // } else { + // if err.Error() != expected { + // t.Errorf("expected error '%s' got '%v' (notice: '%v')", expected, err, notice) + // } + // } + + notice, err = ns.Notice(tx, abi, "missing_method", desc) + + expected = "natspec.js error evaluating expression: wrong input params in expression 'Will multiply `a` by 7 and return `a * 7`.'" + + if err == nil { + t.Errorf("expected error, got nothing (notice: '%v')", notice) + } else { + if err.Error() != expected { + t.Errorf("expected error '%s' got '%v' (notice: '%v')", expected, err, notice) + } + } + } diff --git a/rpc/args.go b/rpc/args.go index 9735feb62..e2e987812 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -8,6 +8,30 @@ import ( "github.com/ethereum/go-ethereum/ethutil" ) +// Unmarshal state is a helper method which has the ability to decode messsages +// that use the `defaultBlock` (https://github.com/ethereum/wiki/wiki/JSON-RPC#the-default-block-parameter) +// For example a `call`: [{to: "0x....", data:"0x..."}, "latest"]. The first argument is the transaction +// message and the second one refers to the block height (or state) to which to apply this `call`. +func unmarshalState(b []byte, iface interface{}, str *string) (err error) { + var data []json.RawMessage + if err = json.Unmarshal(b, &data); err != nil && len(data) == 0 { + return errDecodeArgs + } + + if err = json.Unmarshal(data[0], iface); err != nil { + return errDecodeArgs + } + + // Second argument is optional (transact doesn't require it) + if len(data) > 1 { + if err = json.Unmarshal(data[1], str); err != nil { + return errDecodeArgs + } + } + + return nil +} + type GetBlockByHashArgs struct { BlockHash string Transactions bool @@ -68,10 +92,12 @@ type NewTxArgs struct { Gas *big.Int GasPrice *big.Int Data string + + BlockHeight string } func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { - var obj []struct { + var obj struct { From string `json:"from"` To string `json:"to"` Value string `json:"value"` @@ -79,20 +105,18 @@ func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { GasPrice string `json:"gasPrice"` Data string `json:"data"` } - - if err = json.Unmarshal(b, &obj); err != nil { - return errDecodeArgs + var height string + if err = unmarshalState(b, &obj, &height); err != nil { + return err } - if len(obj) < 1 { - return errArguments - } - args.From = obj[0].From - args.To = obj[0].To - args.Value = ethutil.Big(obj[0].Value) - args.Gas = ethutil.Big(obj[0].Gas) - args.GasPrice = ethutil.Big(obj[0].GasPrice) - args.Data = obj[0].Data + args.From = obj.From + args.To = obj.To + args.Value = ethutil.Big(obj.Value) + args.Gas = ethutil.Big(obj.Gas) + args.GasPrice = ethutil.Big(obj.GasPrice) + args.Data = obj.Data + args.BlockHeight = height return nil } diff --git a/tests/vm/gh_test.go b/tests/vm/gh_test.go index 2db7a0fd0..959f20463 100644 --- a/tests/vm/gh_test.go +++ b/tests/vm/gh_test.go @@ -80,13 +80,6 @@ func RunVmTest(p string, t *testing.T) { helper.CreateFileTests(t, p, &tests) for name, test := range tests { - /* - vm.Debug = true - helper.Logger.SetLogLevel(4) - if name != "refund_CallToSuicideTwice" { - continue - } - */ db, _ := ethdb.NewMemDatabase() statedb := state.New(nil, db) for addr, account := range test.Pre { diff --git a/vm/context.go b/vm/context.go index b48f1a657..9ce07bc4a 100644 --- a/vm/context.go +++ b/vm/context.go @@ -58,7 +58,7 @@ func (c *Context) GetRangeValue(x, size uint64) []byte { x = uint64(math.Min(float64(x), float64(len(c.Code)))) y := uint64(math.Min(float64(x+size), float64(len(c.Code)))) - return ethutil.LeftPadBytes(c.Code[x:y], int(size)) + return ethutil.RightPadBytes(c.Code[x:y], int(size)) } func (c *Context) GetCode(x, size uint64) []byte { diff --git a/vm/debugger.go b/vm/debugger.go deleted file mode 100644 index 9b08634c6..000000000 --- a/vm/debugger.go +++ /dev/null @@ -1,10 +0,0 @@ -package vm - -import "github.com/ethereum/go-ethereum/state" - -type Debugger interface { - BreakHook(step int, op OpCode, mem *Memory, stack *Stack, object *state.StateObject) bool - StepHook(step int, op OpCode, mem *Memory, stack *Stack, object *state.StateObject) bool - BreakPoints() []int64 - SetCode(byteCode []byte) -} diff --git a/vm/gas.go b/vm/gas.go new file mode 100644 index 000000000..a19afeb67 --- /dev/null +++ b/vm/gas.go @@ -0,0 +1,86 @@ +package vm + +import "math/big" + +type req struct { + stack int + gas *big.Int +} + +var _baseCheck = map[OpCode]req{ + // Req stack Gas price + ADD: {2, GasFastestStep}, + LT: {2, GasFastestStep}, + GT: {2, GasFastestStep}, + SLT: {2, GasFastestStep}, + SGT: {2, GasFastestStep}, + EQ: {2, GasFastestStep}, + ISZERO: {1, GasFastestStep}, + SUB: {2, GasFastestStep}, + AND: {2, GasFastestStep}, + OR: {2, GasFastestStep}, + XOR: {2, GasFastestStep}, + NOT: {1, GasFastestStep}, + BYTE: {2, GasFastestStep}, + CALLDATALOAD: {1, GasFastestStep}, + CALLDATACOPY: {3, GasFastestStep}, + MLOAD: {1, GasFastestStep}, + MSTORE: {2, GasFastestStep}, + MSTORE8: {2, GasFastestStep}, + CODECOPY: {3, GasFastestStep}, + MUL: {2, GasFastStep}, + DIV: {2, GasFastStep}, + SDIV: {2, GasFastStep}, + MOD: {2, GasFastStep}, + SMOD: {2, GasFastStep}, + SIGNEXTEND: {2, GasFastStep}, + ADDMOD: {3, GasMidStep}, + MULMOD: {3, GasMidStep}, + JUMP: {1, GasMidStep}, + JUMPI: {2, GasSlowStep}, + EXP: {2, GasSlowStep}, + ADDRESS: {0, GasQuickStep}, + ORIGIN: {0, GasQuickStep}, + CALLER: {0, GasQuickStep}, + CALLVALUE: {0, GasQuickStep}, + CODESIZE: {0, GasQuickStep}, + GASPRICE: {0, GasQuickStep}, + COINBASE: {0, GasQuickStep}, + TIMESTAMP: {0, GasQuickStep}, + NUMBER: {0, GasQuickStep}, + CALLDATASIZE: {0, GasQuickStep}, + DIFFICULTY: {0, GasQuickStep}, + GASLIMIT: {0, GasQuickStep}, + POP: {0, GasQuickStep}, + PC: {0, GasQuickStep}, + MSIZE: {0, GasQuickStep}, + GAS: {0, GasQuickStep}, + BLOCKHASH: {1, GasExtStep}, + BALANCE: {0, GasExtStep}, + EXTCODESIZE: {1, GasExtStep}, + EXTCODECOPY: {4, GasExtStep}, + SLOAD: {1, GasStorageGet}, + SSTORE: {2, Zero}, + SHA3: {1, GasSha3Base}, + CREATE: {3, GasCreate}, + CALL: {7, GasCall}, + CALLCODE: {7, GasCall}, + JUMPDEST: {0, GasJumpDest}, + SUICIDE: {1, Zero}, + RETURN: {2, Zero}, +} + +func baseCheck(op OpCode, stack *stack, gas *big.Int) { + if r, ok := _baseCheck[op]; ok { + stack.require(r.stack) + + gas.Add(gas, r.gas) + } +} + +func toWordSize(size *big.Int) *big.Int { + tmp := new(big.Int) + tmp.Add(size, u256(31)) + tmp.Div(tmp, u256(32)) + return tmp +} diff --git a/vm/memory.go b/vm/memory.go new file mode 100644 index 000000000..2a1e6e1b9 --- /dev/null +++ b/vm/memory.go @@ -0,0 +1,72 @@ +package vm + +import "fmt" + +type Memory struct { + store []byte +} + +func NewMemory() *Memory { + return &Memory{nil} +} + +func (m *Memory) Set(offset, size uint64, value []byte) { + if len(value) > 0 { + totSize := offset + size + lenSize := uint64(len(m.store) - 1) + if totSize > lenSize { + // Calculate the diff between the sizes + diff := totSize - lenSize + if diff > 0 { + // Create a new empty slice and append it + newSlice := make([]byte, diff-1) + // Resize slice + m.store = append(m.store, newSlice...) + } + } + copy(m.store[offset:offset+size], value) + } +} + +func (m *Memory) Resize(size uint64) { + if uint64(m.Len()) < size { + m.store = append(m.store, make([]byte, size-uint64(m.Len()))...) + } +} + +func (self *Memory) Get(offset, size int64) (cpy []byte) { + if size == 0 { + return nil + } + + if len(self.store) > int(offset) { + cpy = make([]byte, size) + copy(cpy, self.store[offset:offset+size]) + + return + } + + return +} + +func (m *Memory) Len() int { + return len(m.store) +} + +func (m *Memory) Data() []byte { + return m.store +} + +func (m *Memory) Print() { + fmt.Printf("### mem %d bytes ###\n", len(m.store)) + if len(m.store) > 0 { + addr := 0 + for i := 0; i+32 <= len(m.store); i += 32 { + fmt.Printf("%03d: % x\n", addr, m.store[i:i+32]) + addr++ + } + } else { + fmt.Println("-- empty --") + } + fmt.Println("####################") +} diff --git a/vm/stack.go b/vm/stack.go index a9aafdb9d..a99cc1805 100644 --- a/vm/stack.go +++ b/vm/stack.go @@ -5,98 +5,53 @@ import ( "math/big" ) -type OpType int - -const ( - tNorm = iota - tData - tExtro - tCrypto -) - -type TxCallback func(opType OpType) bool - -// Simple push/pop stack mechanism -type Stack struct { - data []*big.Int -} - -func NewStack() *Stack { - return &Stack{} -} - -func (st *Stack) Data() []*big.Int { - return st.data -} - -func (st *Stack) Len() int { - return len(st.data) -} - -func (st *Stack) Pop() *big.Int { - str := st.data[len(st.data)-1] - - copy(st.data[:len(st.data)-1], st.data[:len(st.data)-1]) - st.data = st.data[:len(st.data)-1] - - return str +func newStack() *stack { + return &stack{} } -func (st *Stack) Popn() (*big.Int, *big.Int) { - ints := st.data[len(st.data)-2:] - - copy(st.data[:len(st.data)-2], st.data[:len(st.data)-2]) - st.data = st.data[:len(st.data)-2] - - return ints[0], ints[1] +type stack struct { + data []*big.Int + ptr int } -func (st *Stack) Peek() *big.Int { - str := st.data[len(st.data)-1] - - return str +func (st *stack) push(d *big.Int) { + if len(st.data) > st.ptr { + st.data[st.ptr] = d + } else { + st.data = append(st.data, d) + } + st.ptr++ } -func (st *Stack) Peekn() (*big.Int, *big.Int) { - ints := st.data[len(st.data)-2:] - - return ints[0], ints[1] +func (st *stack) pop() (ret *big.Int) { + st.ptr-- + ret = st.data[st.ptr] + return } -func (st *Stack) Swapn(n int) (*big.Int, *big.Int) { - st.data[len(st.data)-n], st.data[len(st.data)-1] = st.data[len(st.data)-1], st.data[len(st.data)-n] - - return st.data[len(st.data)-n], st.data[len(st.data)-1] +func (st *stack) len() int { + return st.ptr } -func (st *Stack) Dupn(n int) *big.Int { - st.Push(st.data[len(st.data)-n]) - - return st.Peek() +func (st *stack) swap(n int) { + st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n] } -func (st *Stack) Push(d *big.Int) { - st.data = append(st.data, new(big.Int).Set(d)) +func (st *stack) dup(n int) { + st.push(st.data[st.len()-n]) } -func (st *Stack) Get(amount *big.Int) []*big.Int { - // offset + size <= len(data) - length := big.NewInt(int64(len(st.data))) - if amount.Cmp(length) <= 0 { - start := new(big.Int).Sub(length, amount) - return st.data[start.Int64():length.Int64()] - } - - return nil +func (st *stack) peek() *big.Int { + return st.data[st.len()-1] } -func (st *Stack) require(n int) { - if st.Len() < n { - panic(fmt.Sprintf("stack underflow (%d <=> %d)", st.Len(), n)) +func (st *stack) require(n int) { + if st.len() < n { + panic(fmt.Sprintf("stack underflow (%d <=> %d)", len(st.data), n)) } } -func (st *Stack) Print() { +func (st *stack) Print() { fmt.Println("### stack ###") if len(st.data) > 0 { for i, val := range st.data { @@ -107,72 +62,3 @@ func (st *Stack) Print() { } fmt.Println("#############") } - -type Memory struct { - store []byte -} - -func NewMemory() *Memory { - return &Memory{nil} -} - -func (m *Memory) Set(offset, size uint64, value []byte) { - if len(value) > 0 { - totSize := offset + size - lenSize := uint64(len(m.store) - 1) - if totSize > lenSize { - // Calculate the diff between the sizes - diff := totSize - lenSize - if diff > 0 { - // Create a new empty slice and append it - newSlice := make([]byte, diff-1) - // Resize slice - m.store = append(m.store, newSlice...) - } - } - copy(m.store[offset:offset+size], value) - } -} - -func (m *Memory) Resize(size uint64) { - if uint64(m.Len()) < size { - m.store = append(m.store, make([]byte, size-uint64(m.Len()))...) - } -} - -func (self *Memory) Get(offset, size int64) (cpy []byte) { - if size == 0 { - return nil - } - - if len(self.store) > int(offset) { - cpy = make([]byte, size) - copy(cpy, self.store[offset:offset+size]) - - return - } - - return -} - -func (m *Memory) Len() int { - return len(m.store) -} - -func (m *Memory) Data() []byte { - return m.store -} - -func (m *Memory) Print() { - fmt.Printf("### mem %d bytes ###\n", len(m.store)) - if len(m.store) > 0 { - addr := 0 - for i := 0; i+32 <= len(m.store); i += 32 { - fmt.Printf("%03d: % x\n", addr, m.store[i:i+32]) - addr++ - } - } else { - fmt.Println("-- empty --") - } - fmt.Println("####################") -} @@ -19,8 +19,6 @@ type Vm struct { // For logging debug bool - Dbg Debugger - BreakPoints []int64 Stepping bool Fn string @@ -66,10 +64,9 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I destinations = analyseJumpDests(context.Code) mem = NewMemory() - stack = NewStack() + stack = newStack() pc uint64 = 0 step = 0 - prevStep = 0 statedb = self.env.State() jump = func(from uint64, to *big.Int) { @@ -93,7 +90,6 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I } for { - prevStep = step // The base for all big integer arithmetic base := new(big.Int) @@ -101,11 +97,7 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I // Get the memory location of pc op = context.GetOp(pc) - self.Printf("(pc) %-3d -o- %-14s (m) %-4d (s) %-4d ", pc, op.String(), mem.Len(), stack.Len()) - if self.Dbg != nil { - //self.Dbg.Step(self, op, mem, stack, context) - } - + self.Printf("(pc) %-3d -o- %-14s (m) %-4d (s) %-4d ", pc, op.String(), mem.Len(), stack.len()) newMemSize, gas := self.calculateGasAndSize(context, caller, op, statedb, mem, stack) self.Printf("(g) %-3v (%v)", gas, context.Gas) @@ -125,40 +117,40 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I switch op { // 0x20 range case ADD: - x, y := stack.Popn() + x, y := stack.pop(), stack.pop() self.Printf(" %v + %v", y, x) - base.Add(y, x) + base.Add(x, y) U256(base) self.Printf(" = %v", base) - // Pop result back on the stack - stack.Push(base) + // pop result back on the stack + stack.push(base) case SUB: - x, y := stack.Popn() + x, y := stack.pop(), stack.pop() self.Printf(" %v - %v", y, x) - base.Sub(y, x) + base.Sub(x, y) U256(base) self.Printf(" = %v", base) - // Pop result back on the stack - stack.Push(base) + // pop result back on the stack + stack.push(base) case MUL: - x, y := stack.Popn() + x, y := stack.pop(), stack.pop() self.Printf(" %v * %v", y, x) - base.Mul(y, x) + base.Mul(x, y) U256(base) self.Printf(" = %v", base) - // Pop result back on the stack - stack.Push(base) + // pop result back on the stack + stack.push(base) case DIV: - x, y := stack.Pop(), stack.Pop() + x, y := stack.pop(), stack.pop() self.Printf(" %v / %v", x, y) if y.Cmp(ethutil.Big0) != 0 { @@ -168,10 +160,10 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I U256(base) self.Printf(" = %v", base) - // Pop result back on the stack - stack.Push(base) + // pop result back on the stack + stack.push(base) case SDIV: - x, y := S256(stack.Pop()), S256(stack.Pop()) + x, y := S256(stack.pop()), S256(stack.pop()) self.Printf(" %v / %v", x, y) @@ -191,9 +183,9 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I } self.Printf(" = %v", base) - stack.Push(base) + stack.push(base) case MOD: - x, y := stack.Pop(), stack.Pop() + x, y := stack.pop(), stack.pop() self.Printf(" %v %% %v", x, y) @@ -206,9 +198,9 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I U256(base) self.Printf(" = %v", base) - stack.Push(base) + stack.push(base) case SMOD: - x, y := S256(stack.Pop()), S256(stack.Pop()) + x, y := S256(stack.pop()), S256(stack.pop()) self.Printf(" %v %% %v", x, y) @@ -228,25 +220,25 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I } self.Printf(" = %v", base) - stack.Push(base) + stack.push(base) case EXP: - x, y := stack.Popn() + x, y := stack.pop(), stack.pop() - self.Printf(" %v ** %v", y, x) + self.Printf(" %v ** %v", x, y) - base.Exp(y, x, Pow256) + base.Exp(x, y, Pow256) U256(base) self.Printf(" = %v", base) - stack.Push(base) + stack.push(base) case SIGNEXTEND: - back := stack.Pop().Uint64() + back := stack.pop().Uint64() if back < 31 { bit := uint(back*8 + 7) - num := stack.Pop() + num := stack.pop() mask := new(big.Int).Lsh(ethutil.Big1, bit) mask.Sub(mask, ethutil.Big1) if ethutil.BitTest(num, int(bit)) { @@ -259,91 +251,91 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I self.Printf(" = %v", num) - stack.Push(num) + stack.push(num) } case NOT: - base.Sub(Pow256, stack.Pop()).Sub(base, ethutil.Big1) + base.Sub(Pow256, stack.pop()).Sub(base, ethutil.Big1) // Not needed base = U256(base) - stack.Push(base) + stack.push(base) case LT: - x, y := stack.Popn() - self.Printf(" %v < %v", y, x) + x, y := stack.pop(), stack.pop() + self.Printf(" %v < %v", x, y) // x < y - if y.Cmp(x) < 0 { - stack.Push(ethutil.BigTrue) + if x.Cmp(y) < 0 { + stack.push(ethutil.BigTrue) } else { - stack.Push(ethutil.BigFalse) + stack.push(ethutil.BigFalse) } case GT: - x, y := stack.Popn() - self.Printf(" %v > %v", y, x) + x, y := stack.pop(), stack.pop() + self.Printf(" %v > %v", x, y) // x > y - if y.Cmp(x) > 0 { - stack.Push(ethutil.BigTrue) + if x.Cmp(y) > 0 { + stack.push(ethutil.BigTrue) } else { - stack.Push(ethutil.BigFalse) + stack.push(ethutil.BigFalse) } case SLT: - y, x := S256(stack.Pop()), S256(stack.Pop()) - self.Printf(" %v < %v", y, x) + x, y := S256(stack.pop()), S256(stack.pop()) + self.Printf(" %v < %v", x, y) // x < y - if y.Cmp(S256(x)) < 0 { - stack.Push(ethutil.BigTrue) + if x.Cmp(S256(y)) < 0 { + stack.push(ethutil.BigTrue) } else { - stack.Push(ethutil.BigFalse) + stack.push(ethutil.BigFalse) } case SGT: - y, x := S256(stack.Pop()), S256(stack.Pop()) - self.Printf(" %v > %v", y, x) + x, y := S256(stack.pop()), S256(stack.pop()) + self.Printf(" %v > %v", x, y) // x > y - if y.Cmp(x) > 0 { - stack.Push(ethutil.BigTrue) + if x.Cmp(y) > 0 { + stack.push(ethutil.BigTrue) } else { - stack.Push(ethutil.BigFalse) + stack.push(ethutil.BigFalse) } case EQ: - x, y := stack.Popn() + x, y := stack.pop(), stack.pop() self.Printf(" %v == %v", y, x) // x == y if x.Cmp(y) == 0 { - stack.Push(ethutil.BigTrue) + stack.push(ethutil.BigTrue) } else { - stack.Push(ethutil.BigFalse) + stack.push(ethutil.BigFalse) } case ISZERO: - x := stack.Pop() + x := stack.pop() if x.Cmp(ethutil.BigFalse) > 0 { - stack.Push(ethutil.BigFalse) + stack.push(ethutil.BigFalse) } else { - stack.Push(ethutil.BigTrue) + stack.push(ethutil.BigTrue) } // 0x10 range case AND: - x, y := stack.Popn() + x, y := stack.pop(), stack.pop() self.Printf(" %v & %v", y, x) - stack.Push(base.And(y, x)) + stack.push(base.And(x, y)) case OR: - x, y := stack.Popn() - self.Printf(" %v | %v", y, x) + x, y := stack.pop(), stack.pop() + self.Printf(" %v | %v", x, y) - stack.Push(base.Or(y, x)) + stack.push(base.Or(x, y)) case XOR: - x, y := stack.Popn() - self.Printf(" %v ^ %v", y, x) + x, y := stack.pop(), stack.pop() + self.Printf(" %v ^ %v", x, y) - stack.Push(base.Xor(y, x)) + stack.push(base.Xor(x, y)) case BYTE: - val, th := stack.Popn() + th, val := stack.pop(), stack.pop() if th.Cmp(big.NewInt(32)) < 0 { byt := big.NewInt(int64(ethutil.LeftPadBytes(val.Bytes(), 32)[th.Int64()])) @@ -355,12 +347,12 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I self.Printf(" => 0x%x", base.Bytes()) - stack.Push(base) + stack.push(base) case ADDMOD: - x := stack.Pop() - y := stack.Pop() - z := stack.Pop() + x := stack.pop() + y := stack.pop() + z := stack.pop() add := new(big.Int).Add(x, y) if len(z.Bytes()) > 0 { // NOT 0x0 @@ -371,12 +363,12 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I self.Printf(" %v + %v %% %v = %v", x, y, z, base) - stack.Push(base) + stack.push(base) case MULMOD: - x := stack.Pop() - y := stack.Pop() - z := stack.Pop() + x := stack.pop() + y := stack.pop() + z := stack.pop() mul := new(big.Int).Mul(x, y) if len(z.Bytes()) > 0 { // NOT 0x0 @@ -387,24 +379,24 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I self.Printf(" %v + %v %% %v = %v", x, y, z, base) - stack.Push(base) + stack.push(base) // 0x20 range case SHA3: - size, offset := stack.Popn() + size, offset := stack.pop(), stack.pop() data := crypto.Sha3(mem.Get(offset.Int64(), size.Int64())) - stack.Push(ethutil.BigD(data)) + stack.push(ethutil.BigD(data)) self.Printf(" => (%v) %x", size, data) // 0x30 range case ADDRESS: - stack.Push(ethutil.BigD(context.Address())) + stack.push(ethutil.BigD(context.Address())) self.Printf(" => %x", context.Address()) case BALANCE: - addr := stack.Pop().Bytes() + addr := stack.pop().Bytes() var balance *big.Int if statedb.GetStateObject(addr) != nil { balance = statedb.GetBalance(addr) @@ -412,27 +404,27 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I balance = base } - stack.Push(balance) + stack.push(balance) self.Printf(" => %v (%x)", balance, addr) case ORIGIN: origin := self.env.Origin() - stack.Push(ethutil.BigD(origin)) + stack.push(ethutil.BigD(origin)) self.Printf(" => %x", origin) case CALLER: caller := context.caller.Address() - stack.Push(ethutil.BigD(caller)) + stack.push(ethutil.BigD(caller)) self.Printf(" => %x", caller) case CALLVALUE: - stack.Push(value) + stack.push(value) self.Printf(" => %v", value) case CALLDATALOAD: var ( - offset = stack.Pop() + offset = stack.pop() data = make([]byte, 32) lenData = big.NewInt(int64(len(callData))) ) @@ -446,18 +438,18 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I self.Printf(" => 0x%x", data) - stack.Push(ethutil.BigD(data)) + stack.push(ethutil.BigD(data)) case CALLDATASIZE: l := int64(len(callData)) - stack.Push(big.NewInt(l)) + stack.push(big.NewInt(l)) self.Printf(" => %d", l) case CALLDATACOPY: var ( size = uint64(len(callData)) - mOff = stack.Pop().Uint64() - cOff = stack.Pop().Uint64() - l = stack.Pop().Uint64() + mOff = stack.pop().Uint64() + cOff = stack.pop().Uint64() + l = stack.pop().Uint64() ) if cOff > size { @@ -475,7 +467,7 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I case CODESIZE, EXTCODESIZE: var code []byte if op == EXTCODESIZE { - addr := stack.Pop().Bytes() + addr := stack.pop().Bytes() code = statedb.GetCode(addr) } else { @@ -483,21 +475,21 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I } l := big.NewInt(int64(len(code))) - stack.Push(l) + stack.push(l) self.Printf(" => %d", l) case CODECOPY, EXTCODECOPY: var code []byte if op == EXTCODECOPY { - code = statedb.GetCode(stack.Pop().Bytes()) + code = statedb.GetCode(stack.pop().Bytes()) } else { code = context.Code } context := NewContext(nil, nil, code, ethutil.Big0, ethutil.Big0) var ( - mOff = stack.Pop().Uint64() - cOff = stack.Pop().Uint64() - l = stack.Pop().Uint64() + mOff = stack.pop().Uint64() + cOff = stack.pop().Uint64() + l = stack.pop().Uint64() ) codeCopy := context.GetCode(cOff, l) @@ -505,80 +497,80 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I self.Printf(" => [%v, %v, %v] %x", mOff, cOff, l, codeCopy) case GASPRICE: - stack.Push(context.Price) + stack.push(context.Price) self.Printf(" => %x", context.Price) // 0x40 range case BLOCKHASH: - num := stack.Pop() + num := stack.pop() n := new(big.Int).Sub(self.env.BlockNumber(), ethutil.Big257) if num.Cmp(n) > 0 && num.Cmp(self.env.BlockNumber()) < 0 { - stack.Push(ethutil.BigD(self.env.GetHash(num.Uint64()))) + stack.push(ethutil.BigD(self.env.GetHash(num.Uint64()))) } else { - stack.Push(ethutil.Big0) + stack.push(ethutil.Big0) } - self.Printf(" => 0x%x", stack.Peek().Bytes()) + self.Printf(" => 0x%x", stack.peek().Bytes()) case COINBASE: coinbase := self.env.Coinbase() - stack.Push(ethutil.BigD(coinbase)) + stack.push(ethutil.BigD(coinbase)) self.Printf(" => 0x%x", coinbase) case TIMESTAMP: time := self.env.Time() - stack.Push(big.NewInt(time)) + stack.push(big.NewInt(time)) self.Printf(" => 0x%x", time) case NUMBER: number := self.env.BlockNumber() - stack.Push(U256(number)) + stack.push(U256(number)) self.Printf(" => 0x%x", number.Bytes()) case DIFFICULTY: difficulty := self.env.Difficulty() - stack.Push(difficulty) + stack.push(difficulty) self.Printf(" => 0x%x", difficulty.Bytes()) case GASLIMIT: self.Printf(" => %v", self.env.GasLimit()) - stack.Push(self.env.GasLimit()) + stack.push(self.env.GasLimit()) // 0x50 range case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: a := uint64(op - PUSH1 + 1) byts := context.GetRangeValue(pc+1, a) - // Push value to stack - stack.Push(ethutil.BigD(byts)) + // push value to stack + stack.push(ethutil.BigD(byts)) pc += a step += int(op) - int(PUSH1) + 1 self.Printf(" => 0x%x", byts) case POP: - stack.Pop() + stack.pop() case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16: n := int(op - DUP1 + 1) - stack.Dupn(n) + stack.dup(n) - self.Printf(" => [%d] 0x%x", n, stack.Peek().Bytes()) + self.Printf(" => [%d] 0x%x", n, stack.peek().Bytes()) case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16: n := int(op - SWAP1 + 2) - x, y := stack.Swapn(n) + stack.swap(n) - self.Printf(" => [%d] %x [0] %x", n, x.Bytes(), y.Bytes()) + self.Printf(" => [%d]", n) case LOG0, LOG1, LOG2, LOG3, LOG4: n := int(op - LOG0) topics := make([][]byte, n) - mSize, mStart := stack.Popn() + mStart, mSize := stack.pop(), stack.pop() for i := 0; i < n; i++ { - topics[i] = ethutil.LeftPadBytes(stack.Pop().Bytes(), 32) + topics[i] = ethutil.LeftPadBytes(stack.pop().Bytes(), 32) } data := mem.Get(mStart.Int64(), mSize.Int64()) @@ -587,41 +579,40 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I self.Printf(" => %v", log) case MLOAD: - offset := stack.Pop() + offset := stack.pop() val := ethutil.BigD(mem.Get(offset.Int64(), 32)) - stack.Push(val) + stack.push(val) self.Printf(" => 0x%x", val.Bytes()) case MSTORE: // Store the value at stack top-1 in to memory at location stack top - // Pop value of the stack - val, mStart := stack.Popn() + // pop value of the stack + mStart, val := stack.pop(), stack.pop() mem.Set(mStart.Uint64(), 32, ethutil.BigToBytes(val, 256)) self.Printf(" => 0x%x", val) case MSTORE8: - off := stack.Pop() - val := stack.Pop() + off, val := stack.pop(), stack.pop() mem.store[off.Int64()] = byte(val.Int64() & 0xff) self.Printf(" => [%v] 0x%x", off, val) case SLOAD: - loc := stack.Pop() + loc := stack.pop() val := ethutil.BigD(statedb.GetState(context.Address(), loc.Bytes())) - stack.Push(val) + stack.push(val) self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes()) case SSTORE: - val, loc := stack.Popn() + loc, val := stack.pop(), stack.pop() statedb.SetState(context.Address(), loc.Bytes(), val) self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes()) case JUMP: - jump(pc, stack.Pop()) + jump(pc, stack.pop()) continue case JUMPI: - cond, pos := stack.Popn() + pos, cond := stack.pop(), stack.pop() if cond.Cmp(ethutil.BigTrue) >= 0 { jump(pc, pos) @@ -633,19 +624,19 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I case JUMPDEST: case PC: - stack.Push(big.NewInt(int64(pc))) + stack.push(big.NewInt(int64(pc))) case MSIZE: - stack.Push(big.NewInt(int64(mem.Len()))) + stack.push(big.NewInt(int64(mem.Len()))) case GAS: - stack.Push(context.Gas) + stack.push(context.Gas) self.Printf(" => %x", context.Gas) // 0x60 range case CREATE: var ( - value = stack.Pop() - size, offset = stack.Popn() + value = stack.pop() + offset, size = stack.pop(), stack.pop() input = mem.Get(offset.Int64(), size.Int64()) gas = new(big.Int).Set(context.Gas) addr []byte @@ -655,7 +646,7 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I context.UseGas(context.Gas) ret, suberr, ref := self.env.Create(context, nil, input, gas, price, value) if suberr != nil { - stack.Push(ethutil.BigFalse) + stack.push(ethutil.BigFalse) self.Printf(" (*) 0x0 %v", suberr) } else { @@ -668,23 +659,19 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I } addr = ref.Address() - stack.Push(ethutil.BigD(addr)) + stack.push(ethutil.BigD(addr)) } - // Debug hook - if self.Dbg != nil { - self.Dbg.SetCode(context.Code) - } case CALL, CALLCODE: - gas := stack.Pop() - // Pop gas and value of the stack. - value, addr := stack.Popn() + gas := stack.pop() + // pop gas and value of the stack. + addr, value := stack.pop(), stack.pop() value = U256(value) - // Pop input size and offset - inSize, inOffset := stack.Popn() - // Pop return size and offset - retSize, retOffset := stack.Popn() + // pop input size and offset + inOffset, inSize := stack.pop(), stack.pop() + // pop return size and offset + retOffset, retSize := stack.pop(), stack.pop() address := ethutil.Address(addr.Bytes()) self.Printf(" => %x", address).Endl() @@ -707,30 +694,24 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I } if err != nil { - stack.Push(ethutil.BigFalse) + stack.push(ethutil.BigFalse) vmlogger.Debugln(err) } else { - stack.Push(ethutil.BigTrue) + stack.push(ethutil.BigTrue) mem.Set(retOffset.Uint64(), retSize.Uint64(), ret) } self.Printf("resume %x (%v)", context.Address(), context.Gas) - - // Debug hook - if self.Dbg != nil { - self.Dbg.SetCode(context.Code) - } - case RETURN: - size, offset := stack.Popn() + offset, size := stack.pop(), stack.pop() ret := mem.Get(offset.Int64(), size.Int64()) self.Printf(" => [%v, %v] (%d) 0x%x", offset, size, len(ret), ret).Endl() return context.Return(ret), nil case SUICIDE: - receiver := statedb.GetOrNewStateObject(stack.Pop().Bytes()) + receiver := statedb.GetOrNewStateObject(stack.pop().Bytes()) balance := statedb.GetBalance(context.Address()) self.Printf(" => (%x) %v", receiver.Address()[:4], balance) @@ -753,117 +734,17 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I pc++ self.Endl() - - if self.Dbg != nil { - for _, instrNo := range self.Dbg.BreakPoints() { - if pc == uint64(instrNo) { - self.Stepping = true - - if !self.Dbg.BreakHook(prevStep, op, mem, stack, statedb.GetStateObject(context.Address())) { - return nil, nil - } - } else if self.Stepping { - if !self.Dbg.StepHook(prevStep, op, mem, stack, statedb.GetStateObject(context.Address())) { - return nil, nil - } - } - } - } - } } -type req struct { - stack int - gas *big.Int -} - -var _baseCheck = map[OpCode]req{ - // Req Stack Gas price - ADD: {2, GasFastestStep}, - LT: {2, GasFastestStep}, - GT: {2, GasFastestStep}, - SLT: {2, GasFastestStep}, - SGT: {2, GasFastestStep}, - EQ: {2, GasFastestStep}, - ISZERO: {1, GasFastestStep}, - SUB: {2, GasFastestStep}, - AND: {2, GasFastestStep}, - OR: {2, GasFastestStep}, - XOR: {2, GasFastestStep}, - NOT: {1, GasFastestStep}, - BYTE: {2, GasFastestStep}, - CALLDATALOAD: {1, GasFastestStep}, - CALLDATACOPY: {3, GasFastestStep}, - MLOAD: {1, GasFastestStep}, - MSTORE: {2, GasFastestStep}, - MSTORE8: {2, GasFastestStep}, - CODECOPY: {3, GasFastestStep}, - MUL: {2, GasFastStep}, - DIV: {2, GasFastStep}, - SDIV: {2, GasFastStep}, - MOD: {2, GasFastStep}, - SMOD: {2, GasFastStep}, - SIGNEXTEND: {2, GasFastStep}, - ADDMOD: {3, GasMidStep}, - MULMOD: {3, GasMidStep}, - JUMP: {1, GasMidStep}, - JUMPI: {2, GasSlowStep}, - EXP: {2, GasSlowStep}, - ADDRESS: {0, GasQuickStep}, - ORIGIN: {0, GasQuickStep}, - CALLER: {0, GasQuickStep}, - CALLVALUE: {0, GasQuickStep}, - CODESIZE: {0, GasQuickStep}, - GASPRICE: {0, GasQuickStep}, - COINBASE: {0, GasQuickStep}, - TIMESTAMP: {0, GasQuickStep}, - NUMBER: {0, GasQuickStep}, - CALLDATASIZE: {0, GasQuickStep}, - DIFFICULTY: {0, GasQuickStep}, - GASLIMIT: {0, GasQuickStep}, - POP: {0, GasQuickStep}, - PC: {0, GasQuickStep}, - MSIZE: {0, GasQuickStep}, - GAS: {0, GasQuickStep}, - BLOCKHASH: {1, GasExtStep}, - BALANCE: {0, GasExtStep}, - EXTCODESIZE: {1, GasExtStep}, - EXTCODECOPY: {4, GasExtStep}, - SLOAD: {1, GasStorageGet}, - SSTORE: {2, Zero}, - SHA3: {1, GasSha3Base}, - CREATE: {3, GasCreate}, - CALL: {7, GasCall}, - CALLCODE: {7, GasCall}, - JUMPDEST: {0, GasJumpDest}, - SUICIDE: {1, Zero}, - RETURN: {2, Zero}, -} - -func baseCheck(op OpCode, stack *Stack, gas *big.Int) { - if r, ok := _baseCheck[op]; ok { - stack.require(r.stack) - - gas.Add(gas, r.gas) - } -} - -func toWordSize(size *big.Int) *big.Int { - tmp := new(big.Int) - tmp.Add(size, u256(31)) - tmp.Div(tmp, u256(32)) - return tmp -} - -func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *Stack) (*big.Int, *big.Int) { +func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int) { var ( gas = new(big.Int) newMemSize *big.Int = new(big.Int) ) baseCheck(op, stack, gas) - // Stack Check, memory resize & gas phase + // stack Check, memory resize & gas phase switch op { case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: gas.Set(GasFastestStep) @@ -879,7 +760,7 @@ func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCo n := int(op - LOG0) stack.require(n + 2) - mSize, mStart := stack.Peekn() + mSize, mStart := stack.data[stack.len()-2], stack.data[stack.len()-1] gas.Add(gas, GasLogBase) gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(n)), GasLogTopic)) @@ -887,12 +768,12 @@ func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCo newMemSize = calcMemSize(mStart, mSize) case EXP: - gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.Len()-2].Bytes()))), GasExpByte)) + gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), GasExpByte)) case SSTORE: stack.require(2) var g *big.Int - y, x := stack.Peekn() + y, x := stack.data[stack.len()-2], stack.data[stack.len()-1] val := statedb.GetState(context.Address(), x.Bytes()) if len(val) == 0 && len(y.Bytes()) > 0 { // 0 => non 0 @@ -911,51 +792,51 @@ func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCo statedb.Refund(self.env.Origin(), RefundSuicide) } case MLOAD: - newMemSize = calcMemSize(stack.Peek(), u256(32)) + newMemSize = calcMemSize(stack.peek(), u256(32)) case MSTORE8: - newMemSize = calcMemSize(stack.Peek(), u256(1)) + newMemSize = calcMemSize(stack.peek(), u256(1)) case MSTORE: - newMemSize = calcMemSize(stack.Peek(), u256(32)) + newMemSize = calcMemSize(stack.peek(), u256(32)) case RETURN: - newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-2]) + newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2]) case SHA3: - newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-2]) + newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2]) - words := toWordSize(stack.data[stack.Len()-2]) + words := toWordSize(stack.data[stack.len()-2]) gas.Add(gas, words.Mul(words, GasSha3Word)) case CALLDATACOPY: - newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-3]) + newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3]) - words := toWordSize(stack.data[stack.Len()-3]) + words := toWordSize(stack.data[stack.len()-3]) gas.Add(gas, words.Mul(words, GasCopyWord)) case CODECOPY: - newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-3]) + newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3]) - words := toWordSize(stack.data[stack.Len()-3]) + words := toWordSize(stack.data[stack.len()-3]) gas.Add(gas, words.Mul(words, GasCopyWord)) case EXTCODECOPY: - newMemSize = calcMemSize(stack.data[stack.Len()-2], stack.data[stack.Len()-4]) + newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4]) - words := toWordSize(stack.data[stack.Len()-4]) + words := toWordSize(stack.data[stack.len()-4]) gas.Add(gas, words.Mul(words, GasCopyWord)) case CREATE: - newMemSize = calcMemSize(stack.data[stack.Len()-2], stack.data[stack.Len()-3]) + newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3]) case CALL, CALLCODE: - gas.Add(gas, stack.data[stack.Len()-1]) + gas.Add(gas, stack.data[stack.len()-1]) if op == CALL { - if self.env.State().GetStateObject(stack.data[stack.Len()-2].Bytes()) == nil { + if self.env.State().GetStateObject(stack.data[stack.len()-2].Bytes()) == nil { gas.Add(gas, GasCallNewAccount) } } - if len(stack.data[stack.Len()-3].Bytes()) > 0 { + if len(stack.data[stack.len()-3].Bytes()) > 0 { gas.Add(gas, GasCallValueTransfer) } - x := calcMemSize(stack.data[stack.Len()-6], stack.data[stack.Len()-7]) - y := calcMemSize(stack.data[stack.Len()-4], stack.data[stack.Len()-5]) + x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7]) + y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5]) newMemSize = ethutil.BigMax(x, y) } |