diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 7b912cf6e7..10d9c8fa4e 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -26,6 +26,7 @@ import ( "os" "runtime" "strconv" + "strings" "time" "github.com/codegangsta/cli" @@ -112,6 +113,7 @@ runtime will execute the file and exit. }, } app.Flags = []cli.Flag{ + utils.UnlockedAccountFlag, utils.BootnodesFlag, utils.DataDirFlag, utils.ListenPortFlag, @@ -191,6 +193,21 @@ Please run 'ethereum account new' to create a new account.`) func startEth(ctx *cli.Context, eth *eth.Ethereum) { utils.StartEthereum(eth) + + // Load startup keys. XXX we are going to need a different format + account := ctx.GlobalString(utils.UnlockedAccountFlag.Name) + if len(account) > 0 { + split := strings.Split(account, ":") + if len(split) != 2 { + utils.Fatalf("Illegal 'unlock' format (address:password)") + } + am := eth.AccountManager() + // Attempt to unlock the account + err := am.Unlock(ethutil.Hex2Bytes(split[0]), split[1]) + if err != nil { + utils.Fatalf("Unlock account failed '%v'", err) + } + } // Start auxiliary services if enabled. if ctx.GlobalBool(utils.RPCEnabledFlag.Name) { utils.StartRPC(eth, ctx) diff --git a/cmd/mist/assets/examples/coin.html b/cmd/mist/assets/examples/coin.html index 509a9aeeb3..96f2299a5f 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", @@ -72,12 +72,13 @@ // deploy if not exist if (address == null) { var code = "0x60056013565b61014f8061003a6000396000f35b620f42406000600033600160a060020a0316815260200190815260200160002081905550560060e060020a600035048063d0679d3414610020578063e3d670d71461003457005b61002e600435602435610049565b60006000f35b61003f600435610129565b8060005260206000f35b806000600033600160a060020a03168152602001908152602001600020541061007157610076565b610125565b806000600033600160a060020a03168152602001908152602001600020908154039081905550806000600084600160a060020a031681526020019081526020016000209081540190819055508033600160a060020a03167fb52dda022b6c1a1f40905a85f257f689aa5d69d850e49cf939d688fbe5af594660006000a38082600160a060020a03167fb52dda022b6c1a1f40905a85f257f689aa5d69d850e49cf939d688fbe5af594660006000a35b5050565b60006000600083600160a060020a0316815260200190815260200160002054905091905056"; - address = web3.eth.transact({data: code}); + address = web3.eth.transact({from: eth.coinbase, data: code}); localStorage.setItem("address", address); } document.querySelector("#contract_addr").innerHTML = address; - var contract = web3.eth.contract(address, desc); + var Contract = web3.eth.contract(desc); + contract = new Contract(address); contract.Changed({from: eth.coinbase}).changed(function() { refresh(); }); @@ -88,7 +89,7 @@ var table = document.querySelector("#table_body"); table.innerHTML = ""; // clear - var storage = eth.storageAt(address); + var storage = eth.getStorage(address); table.innerHTML = ""; for( var item in storage ) { table.innerHTML += ""+item+""+web3.toDecimal(storage[item])+""; diff --git a/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js b/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js index 5b7d872706..c0b37641c7 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,7 @@ 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. @@ -218,12 +262,322 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file const.js +/** @file formatters.js * @authors: * Marek Kotewicz * @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 . +*/ +/** @file types.js + * @authors: + * Marek Kotewicz + * @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. + + 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 . +*/ +/** @file config.js + * @authors: + * Marek Kotewicz + * @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 . +*/ +/** @file utils.js + * @authors: + * Marek Kotewicz + * @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 . +*/ +/** @file web3.js + * @authors: + * Jeffrey Wilcke + * Marek Kotewicz + * Marian Oancea + * Gav Wood + * @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, 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', inputFormatter: utils.toHex }, + { name: 'compile.lll', call: 'eth_compileLLL', inputFormatter: utils.toHex }, + { name: 'compile.serpent', call: 'eth_compileSerpent', inputFormatter: utils.toHex }, { 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,25 +1931,56 @@ 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) { - console.warn('"topics" is deprecated, is "topic" instead'); + if (options.topic) { + console.warn('"topic" is deprecated, is "topics" instead'); + options.topics = options.topic; } + if (options.earliest) { + console.warn('"earliest" is deprecated, is "fromBlock" instead'); + options.fromBlock = options.earliest; + } + + if (options.latest) { + console.warn('"latest" is deprecated, is "toBlock" instead'); + options.toBlock = options.latest; + } + + if (options.skip) { + console.warn('"skip" is deprecated, is "offset" instead'); + options.offset = options.skip; + } + + if (options.max) { + console.warn('"max" is deprecated, is "limit" instead'); + options.limit = options.max; + } + + // make sure topics, get converted to hex + if(options.topics instanceof Array) { + options.topics = options.topics.map(function(topic){ + return utils.toHex(topic); + }); + } + + // evaluate lazy properties return { + fromBlock: utils.toHex(options.fromBlock), + toBlock: utils.toHex(options.toBlock), + limit: utils.toHex(options.limit), + offset: utils.toHex(options.offset), to: options.to, - topic: options.topic, - earliest: options.earliest, - latest: options.latest, - max: options.max, - skip: options.skip, - address: options.address + address: options.address, + topics: options.topics }; }; @@ -866,6 +1998,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 +2061,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 +2081,188 @@ module.exports = filter; /** @file formatters.js * @authors: * Marek Kotewicz + * Fabian Vogelsteller * @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); -}; - -/// 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'; -}; - -/// Formats input right-aligned input bytes to int -/// @returns right-aligned input bytes formatted to int -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 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); -}; - - -/// 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); -}; - +var utils = require('../utils/utils'); /** -Formats the input of a transaction and converts all values to HEX + * Should the input to a big number + * + * @method convertToBigNumber + * @param {String|Number|BigNumber} + * @returns {BigNumber} object + */ +var convertToBigNumber = function (value) { + return utils.toBigNumber(value); +}; -@returns object +/** + * Formats the input of a transaction and converts all values to HEX + * + * @method inputTransactionFormatter + * @param {Object} transaction options + * @returns object */ -var inputTransactionFormatter = function(options){ +var inputTransactionFormatter = 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); - } + ['gasPrice', 'gas', 'value'].forEach(function(key){ + options[key] = utils.fromDecimal(options[key]); }); - // 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 transaction to its proper values + * + * @method outputTransactionFormatter + * @param {Object} transaction + * @returns {Object} transaction */ -var outputTransactionFormatter = function(tx){ - // transform to number - tx.gas = Number(tx.gas); - - // 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); - - // 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); - +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 call and converts all values to HEX + * + * @method inputCallFormatter + * @param {Object} transaction options + * @returns object +*/ +var inputCallFormatter = function (options){ + + // make code -> data + if (options.code) { + options.data = options.code; + delete options.code; + } + + return options; +}; + /** -Formats the output of a block 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 outputBlockFormatter = function(block){ - /*jshint maxcomplexity:7 */ // 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); + 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); - // 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); - } + block.minGasPrice = utils.toBigNumber(block.minGasPrice); + block.difficulty = utils.toBigNumber(block.difficulty); + block.totalDifficulty = utils.toBigNumber(block.totalDifficulty); - - // 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); - } - - - // 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); + if(block.transactions instanceof Array) { + block.transactions.forEach(function(item){ + if(!utils.isString(item)) + return outputTransactionFormatter(item); + }); } return block; }; - -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 +/** + * 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; }; -},{"./const":2,"./utils":16}],9:[function(require,module,exports){ +/** + * Formats the input of a whisper post and converts all values to HEX + * + * @method inputPostFormatter + * @param {Object} transaction object + * @returns {Object} +*/ +var inputPostFormatter = function(post){ + + 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]; + + + // format the following options + post.topic = post.topic.map(function(topic){ + return utils.fromAscii(topic); + }); + + return post; +}; + +/** + * 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) { } + } + + // format the following options + post.topic = post.topic.map(function(topic){ + return utils.toAscii(topic); + }); + + return post; +}; + +module.exports = { + convertToBigNumber: convertToBigNumber, + inputTransactionFormatter: inputTransactionFormatter, + outputTransactionFormatter: outputTransactionFormatter, + inputCallFormatter: inputCallFormatter, + outputBlockFormatter: outputBlockFormatter, + outputLogFormatter: outputLogFormatter, + inputPostFormatter: inputPostFormatter, + outputPostFormatter: outputPostFormatter +}; + + +},{"../utils/utils":5}],13:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1243,7 +2279,7 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file httpsync.js +/** @file httpprovider.js * @authors: * Marek Kotewicz * Marian Oancea @@ -1254,29 +2290,50 @@ if ("build" !== 'build') {/* var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line */} -var HttpSyncProvider = function (host) { +var HttpProvider = function (host) { + this.name = 'HTTP'; 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) { + var result = ''; + try { + result = JSON.parse(request.responseText) + } catch(error) { + result = error; + } + callback(result, request.status); + } + }; + + 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 +2400,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 . +*/ +/** @file eth.js + * @authors: + * Marek Kotewicz + * @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 +2478,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 +2505,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 +2517,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 +2547,43 @@ var requestManager = function() { return null; } - var result = provider.send(payload); + // HTTP ASYNC (only when callback is given, and it a HttpProvidor) + if(typeof callback === 'function' && provider.name === 'HTTP'){ + provider.send(payload, function(result, status){ - 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)) { + if(typeof result === 'object' && result.error && result.error.message) { + console.error(result.error.message); + callback(result.error); + } else { + callback(new Error({ + status: status, + error: result, + message: 'Bad Request' + })); + } + return null; + } + + // format the output + callback(null, (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 +2620,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 +2645,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 +2668,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 +2689,7 @@ module.exports = { }; -},{}],14:[function(require,module,exports){ +},{"./formatters":12}],19:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1569,8 +2712,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 +2733,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 . -*/ -/** @file types.js - * @authors: - * Marek Kotewicz - * @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 . -*/ -/** @file utils.js - * @authors: - * Marek Kotewicz - * @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 +2759,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 +2784,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 . -*/ -/** @file web3.js - * @authors: - * Jeffrey Wilcke - * Marek Kotewicz - * Marian Oancea - * Gav Wood - * @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/utils/flags.go b/cmd/utils/flags.go index e945a32eec..6372aaa423 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -77,6 +77,10 @@ var ( Usage: "Virtual Machine type: 0 is standard VM, 1 is debug VM", } */ + UnlockedAccountFlag = cli.StringFlag{ + Name: "unlock", + Usage: "Unlock a given account untill this programs exits (address:password)", + } VMDebugFlag = cli.BoolFlag{ Name: "vmdebug", Usage: "Virtual Machine debug output", @@ -221,7 +225,8 @@ func GetChain(ctx *cli.Context) (*core.ChainManager, ethutil.Database, ethutil.D func GetAccountManager(ctx *cli.Context) *accounts.Manager { dataDir := ctx.GlobalString(DataDirFlag.Name) ks := crypto.NewKeyStorePassphrase(path.Join(dataDir, "keys")) - return accounts.NewManager(ks) + km := accounts.NewManager(ks) + return km } func StartRPC(eth *eth.Ethereum, ctx *cli.Context) { diff --git a/core/block_processor.go b/core/block_processor.go index ea9d06841a..7ac8a1bd20 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -80,7 +80,7 @@ func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, stated cb := statedb.GetStateObject(coinbase.Address()) st := NewStateTransition(NewEnv(statedb, self.bc, tx, block), tx, cb) _, err := st.TransitionState() - if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err)) { + if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) { return nil, nil, err } @@ -120,17 +120,12 @@ func (self *BlockProcessor) ApplyTransactions(coinbase *state.StateObject, state for _, tx := range txs { receipt, txGas, err := self.ApplyTransaction(coinbase, statedb, block, tx, totalUsedGas, transientProcess) + if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) { + return nil, nil, nil, nil, err + } + if err != nil { - switch { - case IsNonceErr(err): - return nil, nil, nil, nil, err - case state.IsGasLimitErr(err): - return nil, nil, nil, nil, err - default: - statelogger.Infoln(err) - erroneous = append(erroneous, tx) - err = nil - } + statelogger.Infoln("TX err:", err) } receipts = append(receipts, receipt) handled = append(handled, tx) diff --git a/core/chain_manager.go b/core/chain_manager.go index 9dc41f421d..97c61395ea 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -440,12 +440,14 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error { self.setTotalDifficulty(td) self.insert(block) + /* XXX crashes jsonlogger.LogJson(&logger.EthChainNewHead{ BlockHash: ethutil.Bytes2Hex(block.Hash()), BlockNumber: block.Number(), ChainHeadHash: ethutil.Bytes2Hex(cblock.Hash()), BlockPrevHash: ethutil.Bytes2Hex(block.ParentHash()), }) + */ self.setTransState(state.New(block.Root(), self.stateDb)) queue[i] = ChainEvent{block} diff --git a/core/state_transition.go b/core/state_transition.go index 9b67de149f..7659e3d50a 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -184,6 +184,7 @@ func (self *StateTransition) TransitionState() (ret []byte, err error) { } } if err = self.UseGas(big.NewInt(dgas)); err != nil { + println("2") return nil, InvalidTxError(err) } @@ -198,48 +199,18 @@ func (self *StateTransition) TransitionState() (ret []byte, err error) { dataGas.Mul(dataGas, vm.GasCreateByte) if err := self.UseGas(dataGas); err == nil { ref.SetCode(ret) + } else { + statelogger.Infoln("Insufficient gas for creating code. Require", dataGas, "and have", self.gas) } } - - /* - if vmenv, ok := vmenv.(*VMEnv); ok && tryJit { - statelogger.Infof("CREATE: re-running using JIT (PH=%x)\n", stateCopy.Root()[:4]) - // re-run using the JIT (validation for the JIT) - goodState := vmenv.State().Copy() - vmenv.state = stateCopy - vmenv.SetVmType(vm.JitVmTy) - vmenv.Create(sender, contract.Address(), self.msg.Data(), self.gas, self.gasPrice, self.value) - statelogger.Infof("DONE PH=%x STD_H=%x JIT_H=%x\n", stateCopy.Root()[:4], goodState.Root()[:4], vmenv.State().Root()[:4]) - self.state.Set(goodState) - } - */ } else { ret, err = vmenv.Call(self.From(), self.To().Address(), self.msg.Data(), self.gas, self.gasPrice, self.value) - - /* - if vmenv, ok := vmenv.(*VMEnv); ok && tryJit { - statelogger.Infof("CALL: re-running using JIT (PH=%x)\n", stateCopy.Root()[:4]) - // re-run using the JIT (validation for the JIT) - goodState := vmenv.State().Copy() - vmenv.state = stateCopy - vmenv.SetVmType(vm.JitVmTy) - vmenv.Call(self.From(), self.To().Address(), self.msg.Data(), self.gas, self.gasPrice, self.value) - statelogger.Infof("DONE PH=%x STD_H=%x JIT_H=%x\n", stateCopy.Root()[:4], goodState.Root()[:4], vmenv.State().Root()[:4]) - self.state.Set(goodState) - } - */ } if err != nil && IsValueTransferErr(err) { return nil, InvalidTxError(err) } - /* - if err != nil { - self.UseGas(self.gas) - } - */ - return } diff --git a/javascript/types.go b/javascript/types.go index e07267c8f6..5f47c17356 100644 --- a/javascript/types.go +++ b/javascript/types.go @@ -58,7 +58,7 @@ type JSEthereum struct { func (self *JSEthereum) Block(v interface{}) otto.Value { if number, ok := v.(int64); ok { - return self.toVal(&JSBlock{self.XEth.BlockByNumber(int32(number)), self}) + return self.toVal(&JSBlock{self.XEth.BlockByNumber(number), self}) } else if hash, ok := v.(string); ok { return self.toVal(&JSBlock{self.XEth.BlockByHash(hash), self}) } diff --git a/miner/worker.go b/miner/worker.go index 21a0522e8a..61091f3c08 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -209,6 +209,8 @@ gasLimit: err := self.commitTransaction(tx) switch { case core.IsNonceErr(err): + fallthrough + case core.IsInvalidTxErr(err): // Remove invalid transactions remove = append(remove, tx) case state.IsGasLimitErr(err): @@ -222,7 +224,7 @@ gasLimit: } self.eth.TxPool().RemoveSet(remove) - self.current.coinbase.AddBalance(core.BlockReward) + self.current.state.AddBalance(self.coinbase, core.BlockReward) self.current.state.Update(ethutil.Big0) self.push() @@ -258,9 +260,11 @@ func (self *worker) commitUncle(uncle *types.Header) error { } func (self *worker) commitTransaction(tx *types.Transaction) error { + snap := self.current.state.Copy() //fmt.Printf("proc %x %v\n", tx.Hash()[:3], tx.Nonce()) receipt, _, err := self.proc.ApplyTransaction(self.current.coinbase, self.current.state, self.current.block, tx, self.current.totalUsedGas, true) - if err != nil && (core.IsNonceErr(err) || state.IsGasLimitErr(err)) { + if err != nil && (core.IsNonceErr(err) || state.IsGasLimitErr(err) || core.IsInvalidTxErr(err)) { + self.current.state.Set(snap) return err } diff --git a/rpc/api.go b/rpc/api.go index d6854bbab8..8bbe80bd83 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -1,14 +1,8 @@ -/* -For each request type, define the following: - -1. RpcRequest "To" method [message.go], which does basic validation and conversion to "Args" type via json.Decoder() -2. json.Decoder() calls "UnmarshalON" defined on each "Args" struct -3. EthereumApi method, taking the "Args" type and replying with an interface to be marshalled to ON - -*/ package rpc import ( + "encoding/json" + "fmt" "math/big" "path" "strings" @@ -27,8 +21,8 @@ import ( ) var ( - defaultGasPrice = big.NewInt(10000000000000) - defaultGas = big.NewInt(10000) + defaultGasPrice = big.NewInt(150000000000) + defaultGas = big.NewInt(500000) filterTickerTime = 15 * time.Second ) @@ -50,21 +44,18 @@ type EthereumApi struct { register map[string][]*NewTxArgs db ethutil.Database - - defaultBlockAge int64 } func NewEthereumApi(eth *xeth.XEth, dataDir string) *EthereumApi { db, _ := ethdb.NewLDBDatabase(path.Join(dataDir, "dapps")) api := &EthereumApi{ - eth: eth, - mux: eth.Backend().EventMux(), - quit: make(chan struct{}), - filterManager: filter.NewFilterManager(eth.Backend().EventMux()), - logs: make(map[int]*logFilter), - messages: make(map[int]*whisperFilter), - db: db, - defaultBlockAge: -1, + eth: eth, + mux: eth.Backend().EventMux(), + quit: make(chan struct{}), + filterManager: filter.NewFilterManager(eth.Backend().EventMux()), + logs: make(map[int]*logFilter), + messages: make(map[int]*whisperFilter), + db: db, } go api.filterManager.Start() go api.start() @@ -72,36 +63,33 @@ func NewEthereumApi(eth *xeth.XEth, dataDir string) *EthereumApi { return api } -func (self *EthereumApi) setStateByBlockNumber(num int64) { +func (self *EthereumApi) xethWithStateNum(num int64) *xeth.XEth { chain := self.xeth().Backend().ChainManager() var block *types.Block - if self.defaultBlockAge < 0 { + if num < 0 { num = chain.CurrentBlock().Number().Int64() + num + 1 } block = chain.GetBlockByNumber(uint64(num)) + var st *state.StateDB if block != nil { - self.useState(state.New(block.Root(), self.xeth().Backend().StateDb())) + st = state.New(block.Root(), self.xeth().Backend().StateDb()) } else { - self.useState(chain.State()) + st = chain.State() } + return self.xeth().WithState(st) +} + +func (self *EthereumApi) getStateWithNum(num int64) *xeth.State { + return self.xethWithStateNum(num).State() } func (self *EthereumApi) start() { timer := time.NewTicker(filterTickerTime) - events := self.mux.Subscribe(core.ChainEvent{}) - done: for { select { - case ev := <-events.Chan(): - switch ev.(type) { - case core.ChainEvent: - if self.defaultBlockAge < 0 { - self.setStateByBlockNumber(self.defaultBlockAge) - } - } case <-timer.C: self.logMut.Lock() self.messagesMut.Lock() @@ -130,35 +118,35 @@ func (self *EthereumApi) stop() { close(self.quit) } -func (self *EthereumApi) Register(args string, reply *interface{}) error { - self.regmut.Lock() - defer self.regmut.Unlock() +// func (self *EthereumApi) Register(args string, reply *interface{}) error { +// self.regmut.Lock() +// defer self.regmut.Unlock() - if _, ok := self.register[args]; ok { - self.register[args] = nil // register with empty - } - return nil -} +// if _, ok := self.register[args]; ok { +// self.register[args] = nil // register with empty +// } +// return nil +// } -func (self *EthereumApi) Unregister(args string, reply *interface{}) error { - self.regmut.Lock() - defer self.regmut.Unlock() +// func (self *EthereumApi) Unregister(args string, reply *interface{}) error { +// self.regmut.Lock() +// defer self.regmut.Unlock() - delete(self.register, args) +// delete(self.register, args) - return nil -} +// return nil +// } -func (self *EthereumApi) WatchTx(args string, reply *interface{}) error { - self.regmut.Lock() - defer self.regmut.Unlock() +// func (self *EthereumApi) WatchTx(args string, reply *interface{}) error { +// self.regmut.Lock() +// defer self.regmut.Unlock() - txs := self.register[args] - self.register[args] = nil +// txs := self.register[args] +// self.register[args] = nil - *reply = txs - return nil -} +// *reply = txs +// return nil +// } func (self *EthereumApi) NewFilter(args *FilterOptions, reply *interface{}) error { var id int @@ -173,7 +161,7 @@ func (self *EthereumApi) NewFilter(args *FilterOptions, reply *interface{}) erro id = self.filterManager.InstallFilter(filter) self.logs[id] = &logFilter{timeout: time.Now()} - *reply = id + *reply = i2hex(id) return nil } @@ -203,7 +191,7 @@ func (self *EthereumApi) NewFilterString(args string, reply *interface{}) error id = self.filterManager.InstallFilter(filter) self.logs[id] = &logFilter{timeout: time.Now()} - *reply = id + *reply = i2hex(id) return nil } @@ -240,35 +228,49 @@ func (self *EthereumApi) AllLogs(args *FilterOptions, reply *interface{}) error return nil } -func (p *EthereumApi) GetBlock(args *GetBlockArgs, reply *interface{}) error { - // This seems a bit precarious Maybe worth splitting to discrete functions - if len(args.Hash) > 0 { - *reply = p.xeth().BlockByHash(args.Hash) - } else { - *reply = p.xeth().BlockByNumber(args.BlockNumber) - } - return nil -} +func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) (err error) { + // TODO if no_private_key then + //if _, exists := p.register[args.From]; exists { + // p.register[args.From] = append(p.register[args.From], args) + //} else { + /* + account := accounts.Get(fromHex(args.From)) + if account != nil { + if account.Unlocked() { + if !unlockAccount(account) { + return + } + } -func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) error { + result, _ := account.Transact(fromHex(args.To), fromHex(args.Value), fromHex(args.Gas), fromHex(args.GasPrice), fromHex(args.Data)) + if len(result) > 0 { + *reply = toHex(result) + } + } else if _, exists := p.register[args.From]; exists { + p.register[ags.From] = append(p.register[args.From], args) + } + */ // TODO: align default values to have the same type, e.g. not depend on // ethutil.Value conversions later on - if ethutil.Big(args.Gas).Cmp(big.NewInt(0)) == 0 { - args.Gas = defaultGas.String() + if args.Gas.Cmp(big.NewInt(0)) == 0 { + args.Gas = defaultGas } - if ethutil.Big(args.GasPrice).Cmp(big.NewInt(0)) == 0 { - args.GasPrice = defaultGasPrice.String() + if args.GasPrice.Cmp(big.NewInt(0)) == 0 { + args.GasPrice = defaultGasPrice } - result, _ := p.xeth().Transact(args.From, args.To, args.Value, args.Gas, args.GasPrice, args.Data) - *reply = result + *reply, err = p.xeth().Transact(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data) + if err != nil { + fmt.Println("err:", err) + return err + } return nil } func (p *EthereumApi) Call(args *NewTxArgs, reply *interface{}) error { - result, err := p.xeth().Call(args.From, args.To, args.Value, args.Gas, args.GasPrice, args.Data) + result, err := p.xethWithStateNum(args.BlockNumber).Call(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data) if err != nil { return err } @@ -277,23 +279,28 @@ func (p *EthereumApi) Call(args *NewTxArgs, reply *interface{}) error { return nil } -func (p *EthereumApi) PushTx(args *PushTxArgs, reply *interface{}) error { - err := args.requirementsPushTx() - if err != nil { +func (p *EthereumApi) GetBalance(args *GetBalanceArgs, reply *interface{}) error { + if err := args.requirements(); err != nil { return err } - result, _ := p.xeth().PushTx(args.Tx) - *reply = result + state := p.getStateWithNum(args.BlockNumber).SafeGet(args.Address) + *reply = toHex(state.Balance().Bytes()) return nil } -func (p *EthereumApi) GetStateAt(args *GetStateArgs, reply *interface{}) error { - err := args.requirements() - if err != nil { +func (p *EthereumApi) GetStorage(args *GetStorageArgs, reply *interface{}) error { + if err := args.requirements(); err != nil { return err } + *reply = p.getStateWithNum(args.BlockNumber).SafeGet(args.Address).Storage() + return nil +} - state := p.xeth().State().SafeGet(args.Address) +func (p *EthereumApi) GetStorageAt(args *GetStorageAtArgs, reply *interface{}) error { + if err := args.requirements(); err != nil { + return err + } + state := p.getStateWithNum(args.BlockNumber).SafeGet(args.Address) value := state.StorageString(args.Key) var hx string @@ -309,115 +316,31 @@ func (p *EthereumApi) GetStateAt(args *GetStateArgs, reply *interface{}) error { return nil } -func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *interface{}) error { - err := args.requirements() - if err != nil { - return err - } - - *reply = p.xeth().State().SafeGet(args.Address).Storage() - return nil -} - -func (p *EthereumApi) GetPeerCount(reply *interface{}) error { - *reply = p.xeth().PeerCount() - return nil -} - -func (p *EthereumApi) GetIsListening(reply *interface{}) error { - *reply = p.xeth().IsListening() - return nil -} - -func (p *EthereumApi) GetCoinbase(reply *interface{}) error { - *reply = p.xeth().Coinbase() - return nil -} - -func (p *EthereumApi) Accounts(reply *interface{}) error { - *reply = p.xeth().Accounts() - return nil -} - -func (p *EthereumApi) GetIsMining(reply *interface{}) error { - *reply = p.xeth().IsMining() - return nil -} - -func (p *EthereumApi) SetMining(shouldmine bool, reply *interface{}) error { - *reply = p.xeth().SetMining(shouldmine) - return nil -} - -func (p *EthereumApi) GetDefaultBlockAge(reply *interface{}) error { - *reply = p.defaultBlockAge - return nil -} - -func (p *EthereumApi) SetDefaultBlockAge(defaultBlockAge int64, reply *interface{}) error { - p.defaultBlockAge = defaultBlockAge - p.setStateByBlockNumber(p.defaultBlockAge) - - *reply = true - return nil -} - -func (p *EthereumApi) BlockNumber(reply *interface{}) error { - *reply = p.xeth().Backend().ChainManager().CurrentBlock().Number() - return nil -} - func (p *EthereumApi) GetTxCountAt(args *GetTxCountArgs, reply *interface{}) error { err := args.requirements() if err != nil { return err } - *reply = p.xeth().TxCountAt(args.Address) + *reply = p.xethWithStateNum(args.BlockNumber).TxCountAt(args.Address) return nil } -func (p *EthereumApi) GetBalanceAt(args *GetBalanceArgs, reply *interface{}) error { - err := args.requirements() - if err != nil { +func (p *EthereumApi) GetData(args *GetDataArgs, reply *interface{}) error { + if err := args.requirements(); err != nil { return err } - state := p.xeth().State().SafeGet(args.Address) - *reply = toHex(state.Balance().Bytes()) - return nil -} - -func (p *EthereumApi) GetCodeAt(args *GetCodeAtArgs, reply *interface{}) error { - err := args.requirements() - if err != nil { - return err - } - *reply = p.xeth().CodeAt(args.Address) + *reply = p.xethWithStateNum(args.BlockNumber).CodeAt(args.Address) return nil } func (p *EthereumApi) GetCompilers(reply *interface{}) error { - c := []string{"serpent"} + c := []string{""} *reply = c return nil } -func (p *EthereumApi) CompileSerpent(script string, reply *interface{}) error { - res, err := ethutil.Compile(script, false) - if err != nil { - return err - } - *reply = res - return nil -} - -func (p *EthereumApi) Sha3(args *Sha3Args, reply *interface{}) error { - *reply = toHex(crypto.Sha3(fromHex(args.Data))) - return nil -} - func (p *EthereumApi) DbPut(args *DbArgs, reply *interface{}) error { - err := args.requirements() - if err != nil { + if err := args.requirements(); err != nil { return err } @@ -427,8 +350,7 @@ func (p *EthereumApi) DbPut(args *DbArgs, reply *interface{}) error { } func (p *EthereumApi) DbGet(args *DbArgs, reply *interface{}) error { - err := args.requirements() - if err != nil { + if err := args.requirements(); err != nil { return err } @@ -442,14 +364,18 @@ func (p *EthereumApi) NewWhisperIdentity(reply *interface{}) error { return nil } -func (p *EthereumApi) NewWhisperFilter(args *xeth.Options, reply *interface{}) error { +func (p *EthereumApi) NewWhisperFilter(args *WhisperFilterArgs, reply *interface{}) error { var id int - args.Fn = func(msg xeth.WhisperMessage) { + opts := new(xeth.Options) + opts.From = args.From + opts.To = args.To + opts.Topics = args.Topics + opts.Fn = func(msg xeth.WhisperMessage) { p.messagesMut.Lock() defer p.messagesMut.Unlock() p.messages[id].add(msg) // = append(p.messages[id], msg) } - id = p.xeth().Whisper().Watch(args) + id = p.xeth().Whisper().Watch(opts) p.messages[id] = &whisperFilter{timeout: time.Now()} *reply = id return nil @@ -467,7 +393,7 @@ func (self *EthereumApi) MessagesChanged(id int, reply *interface{}) error { } func (p *EthereumApi) WhisperPost(args *WhisperMessageArgs, reply *interface{}) error { - err := p.xeth().Whisper().Post(args.Payload, args.To, args.From, args.Topic, args.Priority, args.Ttl) + err := p.xeth().Whisper().Post(args.Payload, args.To, args.From, args.Topics, args.Priority, args.Ttl) if err != nil { return err } @@ -486,199 +412,358 @@ func (p *EthereumApi) WhisperMessages(id int, reply *interface{}) error { return nil } +func (p *EthereumApi) GetBlockByHash(blockhash string, includetx bool) (*BlockRes, error) { + block := p.xeth().EthBlockByHash(blockhash) + br := NewBlockRes(block) + br.fullTx = includetx + return br, nil +} + +func (p *EthereumApi) GetBlockByNumber(blocknum int64, includetx bool) (*BlockRes, error) { + block := p.xeth().EthBlockByNumber(blocknum) + br := NewBlockRes(block) + br.fullTx = includetx + return br, nil +} + +func (p *EthereumApi) GetBlockTransactionCountByHash(blockhash string) (int64, error) { + block := p.xeth().EthBlockByHash(blockhash) + br := NewBlockRes(block) + return int64(len(br.Transactions)), nil +} + +func (p *EthereumApi) GetBlockTransactionCountByNumber(blocknum int64) (int64, error) { + block := p.xeth().EthBlockByNumber(blocknum) + br := NewBlockRes(block) + return int64(len(br.Transactions)), nil +} + +func (p *EthereumApi) GetBlockUncleCountByHash(blockhash string) (int64, error) { + block := p.xeth().EthBlockByHash(blockhash) + br := NewBlockRes(block) + return int64(len(br.Uncles)), nil +} + +func (p *EthereumApi) GetBlockUncleCountByNumber(blocknum int64) (int64, error) { + block := p.xeth().EthBlockByNumber(blocknum) + br := NewBlockRes(block) + return int64(len(br.Uncles)), nil +} + func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error { // Spec at https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC - rpclogger.DebugDetailf("%T %s", req.Params, req.Params) + rpclogger.Debugf("%s %s", req.Method, req.Params) switch req.Method { + case "web3_sha3": + args := new(Sha3Args) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + *reply = toHex(crypto.Sha3(fromHex(args.Data))) + case "net_listening": + *reply = p.xeth().IsListening() + case "net_peerCount": + *reply = toHex(big.NewInt(int64(p.xeth().PeerCount())).Bytes()) case "eth_coinbase": - return p.GetCoinbase(reply) - case "eth_listening": - return p.GetIsListening(reply) + *reply = p.xeth().Coinbase() case "eth_mining": - return p.GetIsMining(reply) - case "eth_setMining": - args, err := req.ToBoolArgs() - if err != nil { - return err - } - return p.SetMining(args, reply) - case "eth_defaultBlock": - return p.GetDefaultBlockAge(reply) - case "eth_setDefaultBlock": - args, err := req.ToIntArgs() - if err != nil { - return err - } - return p.SetDefaultBlockAge(int64(args), reply) - case "eth_peerCount": - return p.GetPeerCount(reply) - case "eth_number": - return p.BlockNumber(reply) + *reply = p.xeth().IsMining() + case "eth_gasPrice": + *reply = toHex(defaultGasPrice.Bytes()) case "eth_accounts": - return p.Accounts(reply) - case "eth_countAt": - args, err := req.ToGetTxCountArgs() - if err != nil { + *reply = p.xeth().Accounts() + case "eth_blockNumber": + *reply = toHex(p.xeth().Backend().ChainManager().CurrentBlock().Number().Bytes()) + case "eth_getBalance": + args := new(GetBalanceArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } - return p.GetTxCountAt(args, reply) - case "eth_codeAt": - args, err := req.ToGetCodeAtArgs() - if err != nil { + return p.GetBalance(args, reply) + case "eth_getStorage", "eth_storageAt": + args := new(GetStorageArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } - return p.GetCodeAt(args, reply) - case "eth_balanceAt": - args, err := req.ToGetBalanceArgs() - if err != nil { - return err - } - return p.GetBalanceAt(args, reply) - case "eth_stateAt": - args, err := req.ToGetStateArgs() - if err != nil { - return err - } - return p.GetStateAt(args, reply) - case "eth_storageAt": - args, err := req.ToStorageAtArgs() - if err != nil { + return p.GetStorage(args, reply) + case "eth_getStorageAt": + args := new(GetStorageAtArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } return p.GetStorageAt(args, reply) - case "eth_blockByNumber", "eth_blockByHash": - args, err := req.ToGetBlockArgs() + case "eth_getTransactionCount": + args := new(GetTxCountArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + return p.GetTxCountAt(args, reply) + case "eth_getBlockTransactionCountByHash": + args := new(GetBlockByHashArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + v, err := p.GetBlockTransactionCountByHash(args.BlockHash) if err != nil { return err } - return p.GetBlock(args, reply) - case "eth_transact": - args, err := req.ToNewTxArgs() + *reply = toHex(big.NewInt(v).Bytes()) + case "eth_getBlockTransactionCountByNumber": + args := new(GetBlockByNumberArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + v, err := p.GetBlockTransactionCountByNumber(args.BlockNumber) if err != nil { return err } + *reply = toHex(big.NewInt(v).Bytes()) + case "eth_getUncleCountByBlockHash": + args := new(GetBlockByHashArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + v, err := p.GetBlockUncleCountByHash(args.BlockHash) + if err != nil { + return err + } + *reply = toHex(big.NewInt(v).Bytes()) + case "eth_getUncleCountByBlockNumber": + args := new(GetBlockByNumberArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + v, err := p.GetBlockUncleCountByNumber(args.BlockNumber) + if err != nil { + return err + } + *reply = toHex(big.NewInt(v).Bytes()) + case "eth_getData": + args := new(GetDataArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + return p.GetData(args, reply) + case "eth_sendTransaction", "eth_transact": + args := new(NewTxArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } return p.Transact(args, reply) case "eth_call": - args, err := req.ToNewTxArgs() - if err != nil { + args := new(NewTxArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } return p.Call(args, reply) - case "eth_newFilter": - args, err := req.ToFilterArgs() + case "eth_flush": + return errNotImplemented + case "eth_getBlockByHash": + args := new(GetBlockByHashArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + v, err := p.GetBlockByHash(args.BlockHash, args.Transactions) if err != nil { return err } + *reply = v + case "eth_getBlockByNumber": + args := new(GetBlockByNumberArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + v, err := p.GetBlockByNumber(args.BlockNumber, args.Transactions) + if err != nil { + return err + } + *reply = v + case "eth_getTransactionByHash": + return errNotImplemented + case "eth_getTransactionByBlockHashAndIndex": + args := new(HashIndexArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + v, err := p.GetBlockByHash(args.BlockHash, true) + if err != nil { + return err + } + if args.Index > int64(len(v.Transactions)) || args.Index < 0 { + return NewErrorWithMessage(errDecodeArgs, "Transaction index does not exist") + } + *reply = v.Transactions[args.Index] + case "eth_getTransactionByBlockNumberAndIndex": + args := new(BlockNumIndexArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + v, err := p.GetBlockByNumber(args.BlockNumber, true) + if err != nil { + return err + } + if args.Index > int64(len(v.Transactions)) || args.Index < 0 { + return NewErrorWithMessage(errDecodeArgs, "Transaction index does not exist") + } + *reply = v.Transactions[args.Index] + case "eth_getUncleByBlockHashAndIndex": + args := new(HashIndexArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + v, err := p.GetBlockByHash(args.BlockHash, false) + if err != nil { + return err + } + if args.Index > int64(len(v.Uncles)) || args.Index < 0 { + return NewErrorWithMessage(errDecodeArgs, "Uncle index does not exist") + } + + uncle, err := p.GetBlockByHash(toHex(v.Uncles[args.Index]), false) + if err != nil { + return err + } + *reply = uncle + case "eth_getUncleByBlockNumberAndIndex": + args := new(BlockNumIndexArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + v, err := p.GetBlockByNumber(args.BlockNumber, true) + if err != nil { + return err + } + if args.Index > int64(len(v.Uncles)) || args.Index < 0 { + return NewErrorWithMessage(errDecodeArgs, "Uncle index does not exist") + } + + uncle, err := p.GetBlockByHash(toHex(v.Uncles[args.Index]), false) + if err != nil { + return err + } + *reply = uncle + case "eth_getCompilers": + return p.GetCompilers(reply) + case "eth_compileSolidity": + case "eth_compileLLL": + case "eth_compileSerpent": + return errNotImplemented + case "eth_newFilter": + args := new(FilterOptions) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } return p.NewFilter(args, reply) - case "eth_newFilterString": - args, err := req.ToFilterStringArgs() - if err != nil { + case "eth_newBlockFilter": + args := new(FilterStringArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } - return p.NewFilterString(args, reply) + return p.NewFilterString(args.Word, reply) case "eth_uninstallFilter": - args, err := req.ToUninstallFilterArgs() - if err != nil { + args := new(FilterIdArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } - return p.UninstallFilter(args, reply) - case "eth_changed": - args, err := req.ToIdArgs() - if err != nil { + return p.UninstallFilter(args.Id, reply) + case "eth_getFilterChanges": + args := new(FilterIdArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } - return p.FilterChanged(args, reply) - case "eth_filterLogs": - args, err := req.ToIdArgs() - if err != nil { + return p.FilterChanged(args.Id, reply) + case "eth_getFilterLogs": + args := new(FilterIdArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } - return p.Logs(args, reply) - case "eth_logs": - args, err := req.ToFilterArgs() - if err != nil { + return p.Logs(args.Id, reply) + case "eth_getLogs": + args := new(FilterOptions) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } return p.AllLogs(args, reply) - case "eth_gasPrice": - *reply = toHex(defaultGasPrice.Bytes()) - return nil - case "eth_register": - args, err := req.ToRegisterArgs() - if err != nil { - return err - } - return p.Register(args, reply) - case "eth_unregister": - args, err := req.ToRegisterArgs() - if err != nil { - return err - } - return p.Unregister(args, reply) - case "eth_watchTx": - args, err := req.ToWatchTxArgs() - if err != nil { - return err - } - return p.WatchTx(args, reply) - case "eth_compilers": - return p.GetCompilers(reply) - case "eth_serpent": - args, err := req.ToCompileArgs() - if err != nil { - return err - } - return p.CompileSerpent(args, reply) - case "web3_sha3": - args, err := req.ToSha3Args() - if err != nil { - return err - } - return p.Sha3(args, reply) + case "eth_getWork": + case "eth_submitWork": + return errNotImplemented case "db_put": - args, err := req.ToDbPutArgs() - if err != nil { + args := new(DbArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } return p.DbPut(args, reply) case "db_get": - args, err := req.ToDbGetArgs() - if err != nil { + args := new(DbArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } return p.DbGet(args, reply) - case "shh_newIdentity": - return p.NewWhisperIdentity(reply) - case "shh_newFilter": - args, err := req.ToWhisperFilterArgs() - if err != nil { - return err - } - return p.NewWhisperFilter(args, reply) - case "shh_changed": - args, err := req.ToIdArgs() - if err != nil { - return err - } - return p.MessagesChanged(args, reply) case "shh_post": - args, err := req.ToWhisperPostArgs() - if err != nil { + args := new(WhisperMessageArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } return p.WhisperPost(args, reply) - case "shh_haveIdentity": - args, err := req.ToWhisperHasIdentityArgs() - if err != nil { + case "shh_newIdentity": + return p.NewWhisperIdentity(reply) + case "shh_hasIdentity": + args := new(WhisperIdentityArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } - return p.HasWhisperIdentity(args, reply) + return p.HasWhisperIdentity(args.Identity, reply) + case "shh_newGroup": + case "shh_addToGroup": + return errNotImplemented + case "shh_newFilter": + args := new(WhisperFilterArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + return p.NewWhisperFilter(args, reply) + case "shh_uninstallFilter": + return errNotImplemented + case "shh_getFilterChanges": + args := new(FilterIdArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + return p.MessagesChanged(args.Id, reply) case "shh_getMessages": - args, err := req.ToIdArgs() - if err != nil { + args := new(FilterIdArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } - return p.WhisperMessages(args, reply) + return p.WhisperMessages(args.Id, reply) + // case "eth_register": + // args, err := req.ToRegisterArgs() + // if err != nil { + // return err + // } + // return p.Register(args, reply) + // case "eth_unregister": + // args, err := req.ToRegisterArgs() + // if err != nil { + // return err + // } + // return p.Unregister(args, reply) + // case "eth_watchTx": + // args, err := req.ToWatchTxArgs() + // if err != nil { + // return err + // } + // return p.WatchTx(args, reply) default: return NewErrorWithMessage(errNotImplemented, req.Method) } @@ -694,9 +779,38 @@ func (self *EthereumApi) xeth() *xeth.XEth { return self.eth } -func (self *EthereumApi) useState(statedb *state.StateDB) { - self.xethMu.Lock() - defer self.xethMu.Unlock() +func toFilterOptions(options *FilterOptions) core.FilterOptions { + var opts core.FilterOptions - self.eth = self.eth.UseState(statedb) + // Convert optional address slice/string to byte slice + if str, ok := options.Address.(string); ok { + opts.Address = [][]byte{fromHex(str)} + } else if slice, ok := options.Address.([]interface{}); ok { + bslice := make([][]byte, len(slice)) + for i, addr := range slice { + if saddr, ok := addr.(string); ok { + bslice[i] = fromHex(saddr) + } + } + opts.Address = bslice + } + + opts.Earliest = options.Earliest + opts.Latest = options.Latest + + topics := make([][][]byte, len(options.Topics)) + for i, topicDat := range options.Topics { + if slice, ok := topicDat.([]interface{}); ok { + topics[i] = make([][]byte, len(slice)) + for j, topic := range slice { + topics[i][j] = fromHex(topic.(string)) + } + } else if str, ok := topicDat.(string); ok { + topics[i] = make([][]byte, 1) + topics[i][0] = fromHex(str) + } + } + opts.Topics = topics + + return opts } diff --git a/rpc/api_test.go b/rpc/api_test.go index a9fc16cd37..ec03822c5b 100644 --- a/rpc/api_test.go +++ b/rpc/api_test.go @@ -1,11 +1,29 @@ package rpc import ( + "encoding/json" "sync" "testing" "time" ) +func TestWeb3Sha3(t *testing.T) { + jsonstr := `{"jsonrpc":"2.0","method":"web3_sha3","params":["0x68656c6c6f20776f726c64"],"id":64}` + expected := "0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad" + + api := &EthereumApi{} + + var req RpcRequest + json.Unmarshal([]byte(jsonstr), &req) + + var response interface{} + _ = api.GetRequestReply(&req, &response) + + if response.(string) != expected { + t.Errorf("Expected %s got %s", expected, response) + } +} + func TestFilterClose(t *testing.T) { t.Skip() api := &EthereumApi{ diff --git a/rpc/args.go b/rpc/args.go index ec3359a4a4..d4d8070608 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -1,247 +1,331 @@ package rpc -import "encoding/json" +import ( + "bytes" + "encoding/json" + "math/big" -import "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/ethutil" +) -type GetBlockArgs struct { - BlockNumber int32 - Hash string +func blockNumber(raw json.RawMessage, number *int64) (err error) { + var str string + if err = json.Unmarshal(raw, &str); err != nil { + return errDecodeArgs + } + + switch str { + case "latest": + *number = -1 + case "pending": + *number = 0 + default: + *number = ethutil.String2Big(str).Int64() + } + return nil } -func (obj *GetBlockArgs) UnmarshalJSON(b []byte) (err error) { - argint, argstr := int32(0), "" - if err = json.Unmarshal(b, &argint); err == nil { - obj.BlockNumber = argint - return +type GetBlockByHashArgs struct { + BlockHash string + Transactions bool +} + +func (args *GetBlockByHashArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return errDecodeArgs } - if err = json.Unmarshal(b, &argstr); err == nil { - obj.Hash = argstr - return + + if len(obj) < 1 { + return errArguments } - return errDecodeArgs + + argstr, ok := obj[0].(string) + if !ok { + return errDecodeArgs + } + args.BlockHash = argstr + + if len(obj) > 1 { + args.Transactions = obj[1].(bool) + } + + return nil +} + +type GetBlockByNumberArgs struct { + BlockNumber int64 + Transactions bool +} + +func (args *GetBlockByNumberArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return errDecodeArgs + } + + if len(obj) < 1 { + return errArguments + } + + if v, ok := obj[0].(float64); ok { + args.BlockNumber = int64(v) + } else { + args.BlockNumber = ethutil.Big(obj[0].(string)).Int64() + } + + if len(obj) > 1 { + args.Transactions = obj[1].(bool) + } + + return nil } type NewTxArgs struct { - From string `json:"from"` - Pass string `json:"pass"` - To string `json:"to"` - Value string `json:"value"` - Gas string `json:"gas"` - GasPrice string `json:"gasPrice"` - Data string `json:"data"` + From string + To string + Value *big.Int + Gas *big.Int + GasPrice *big.Int + Data string + + BlockNumber int64 } -func (obj *NewTxArgs) UnmarshalJSON(b []byte) (err error) { - // Data can be either specified as "data" or "code" :-/ - var ext struct { - From string - To string - Value string - Gas string - GasPrice string - Data string - Code string +func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { + var obj struct{ From, To, Value, Gas, GasPrice, Data string } + if err = UnmarshalRawMessages(b, &obj, &args.BlockNumber); err != nil { + return err } - if err = json.Unmarshal(b, &ext); err == nil { - if len(ext.Data) == 0 { - ext.Data = ext.Code - } - obj.From = ext.From - obj.To = ext.To - obj.Value = ext.Value - obj.Gas = ext.Gas - obj.GasPrice = ext.GasPrice - obj.Data = ext.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 - return - } - - return errDecodeArgs -} - -type PushTxArgs struct { - Tx string `json:"tx"` -} - -func (obj *PushTxArgs) UnmarshalJSON(b []byte) (err error) { - arg0 := "" - if err = json.Unmarshal(b, &arg0); err == nil { - obj.Tx = arg0 - return - } - return errDecodeArgs -} - -func (a *PushTxArgs) requirementsPushTx() error { - if a.Tx == "" { - return NewErrorWithMessage(errArguments, "PushTx requires a 'tx' as argument") - } return nil } type GetStorageArgs struct { - Address string + Address string + BlockNumber int64 } -func (obj *GetStorageArgs) UnmarshalJSON(b []byte) (err error) { - if err = json.Unmarshal(b, &obj.Address); err != nil { +func (args *GetStorageArgs) UnmarshalJSON(b []byte) (err error) { + if err = UnmarshalRawMessages(b, &args.Address, &args.BlockNumber); err != nil { return errDecodeArgs } - return + + return nil } -func (a *GetStorageArgs) requirements() error { - if len(a.Address) == 0 { - return NewErrorWithMessage(errArguments, "GetStorageAt requires an 'address' value as argument") +func (args *GetStorageArgs) requirements() error { + if len(args.Address) == 0 { + return NewErrorWithMessage(errArguments, "Address cannot be blank") } return nil } -type GetStateArgs struct { - Address string - Key string +type GetStorageAtArgs struct { + Address string + Key string + BlockNumber int64 } -func (obj *GetStateArgs) UnmarshalJSON(b []byte) (err error) { - arg0 := "" - if err = json.Unmarshal(b, &arg0); err == nil { - obj.Address = arg0 - return +func (args *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) { + var obj []string + if err = UnmarshalRawMessages(b, &obj, &args.BlockNumber); err != nil { + return errDecodeArgs } - return errDecodeArgs + if len(obj) < 2 { + return errDecodeArgs + } + + args.Address = obj[0] + args.Key = obj[1] + + return nil } -func (a *GetStateArgs) requirements() error { - if a.Address == "" { - return NewErrorWithMessage(errArguments, "GetStorageAt requires an 'address' value as argument") +func (args *GetStorageAtArgs) requirements() error { + if len(args.Address) == 0 { + return NewErrorWithMessage(errArguments, "Address cannot be blank") } - if a.Key == "" { - return NewErrorWithMessage(errArguments, "GetStorageAt requires an 'key' value as argument") + + if len(args.Key) == 0 { + return NewErrorWithMessage(errArguments, "Key cannot be blank") } return nil } type GetTxCountArgs struct { - Address string `json:"address"` + Address string + BlockNumber int64 } -func (obj *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) { - arg0 := "" - if err = json.Unmarshal(b, &arg0); err == nil { - obj.Address = arg0 - return +func (args *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) { + if err = UnmarshalRawMessages(b, &args.Address, &args.BlockNumber); err != nil { + return errDecodeArgs } - return errDecodeArgs + + return nil } -func (a *GetTxCountArgs) requirements() error { - if a.Address == "" { - return NewErrorWithMessage(errArguments, "GetTxCountAt requires an 'address' value as argument") +func (args *GetTxCountArgs) requirements() error { + if len(args.Address) == 0 { + return NewErrorWithMessage(errArguments, "Address cannot be blank") } return nil } type GetBalanceArgs struct { - Address string + Address string + BlockNumber int64 } -func (obj *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) { - arg0 := "" - if err = json.Unmarshal(b, &arg0); err == nil { - obj.Address = arg0 - return +func (args *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) { + if err = UnmarshalRawMessages(b, &args.Address, &args.BlockNumber); err != nil { + return errDecodeArgs } - return errDecodeArgs + + return nil } -func (a *GetBalanceArgs) requirements() error { - if a.Address == "" { - return NewErrorWithMessage(errArguments, "GetBalanceAt requires an 'address' value as argument") +func (args *GetBalanceArgs) requirements() error { + if len(args.Address) == 0 { + return NewErrorWithMessage(errArguments, "Address cannot be blank") } return nil } -type GetCodeAtArgs struct { - Address string +type GetDataArgs struct { + Address string + BlockNumber int64 } -func (obj *GetCodeAtArgs) UnmarshalJSON(b []byte) (err error) { - arg0 := "" - if err = json.Unmarshal(b, &arg0); err == nil { - obj.Address = arg0 - return +func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) { + if err = UnmarshalRawMessages(b, &args.Address, &args.BlockNumber); err != nil { + return errDecodeArgs } - return errDecodeArgs + + return nil } -func (a *GetCodeAtArgs) requirements() error { - if a.Address == "" { - return NewErrorWithMessage(errArguments, "GetCodeAt requires an 'address' value as argument") +func (args *GetDataArgs) requirements() error { + if len(args.Address) == 0 { + return NewErrorWithMessage(errArguments, "Address cannot be blank") } return nil } +type BlockNumIndexArgs struct { + BlockNumber int64 + Index int64 +} + +type HashIndexArgs struct { + BlockHash string + Index int64 +} + type Sha3Args struct { Data string } -func (obj *Sha3Args) UnmarshalJSON(b []byte) (err error) { - if err = json.Unmarshal(b, &obj.Data); err != nil { - return errDecodeArgs +func (args *Sha3Args) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return NewErrorWithMessage(errDecodeArgs, err.Error()) } - return + + if len(obj) < 1 { + return errArguments + } + args.Data = obj[0].(string) + + return nil } +// type FilterArgs struct { +// FromBlock uint64 +// ToBlock uint64 +// Limit uint64 +// Offset uint64 +// Address string +// Topics []string +// } + +// func (args *FilterArgs) UnmarshalJSON(b []byte) (err error) { +// var obj []struct { +// FromBlock string `json:"fromBlock"` +// ToBlock string `json:"toBlock"` +// Limit string `json:"limit"` +// Offset string `json:"offset"` +// Address string `json:"address"` +// Topics []string `json:"topics"` +// } + +// if err = json.Unmarshal(b, &obj); err != nil { +// return errDecodeArgs +// } + +// if len(obj) < 1 { +// return errArguments +// } +// args.FromBlock = uint64(ethutil.Big(obj[0].FromBlock).Int64()) +// args.ToBlock = uint64(ethutil.Big(obj[0].ToBlock).Int64()) +// args.Limit = uint64(ethutil.Big(obj[0].Limit).Int64()) +// args.Offset = uint64(ethutil.Big(obj[0].Offset).Int64()) +// args.Address = obj[0].Address +// args.Topics = obj[0].Topics + +// return nil +// } + type FilterOptions struct { Earliest int64 Latest int64 Address interface{} - Topic []interface{} + Topics []interface{} Skip int Max int } -func toFilterOptions(options *FilterOptions) core.FilterOptions { - var opts core.FilterOptions - - // Convert optional address slice/string to byte slice - if str, ok := options.Address.(string); ok { - opts.Address = [][]byte{fromHex(str)} - } else if slice, ok := options.Address.([]interface{}); ok { - bslice := make([][]byte, len(slice)) - for i, addr := range slice { - if saddr, ok := addr.(string); ok { - bslice[i] = fromHex(saddr) - } - } - opts.Address = bslice +func (args *FilterOptions) UnmarshalJSON(b []byte) (err error) { + var obj []struct { + FromBlock string `json:"fromBlock"` + ToBlock string `json:"toBlock"` + Limit string `json:"limit"` + Offset string `json:"offset"` + Address string `json:"address"` + Topics []interface{} `json:"topics"` } - opts.Earliest = options.Earliest - opts.Latest = options.Latest - - topics := make([][][]byte, len(options.Topic)) - for i, topicDat := range options.Topic { - if slice, ok := topicDat.([]interface{}); ok { - topics[i] = make([][]byte, len(slice)) - for j, topic := range slice { - topics[i][j] = fromHex(topic.(string)) - } - } else if str, ok := topicDat.(string); ok { - topics[i] = make([][]byte, 1) - topics[i][0] = fromHex(str) - } + if err = json.Unmarshal(b, &obj); err != nil { + return errDecodeArgs } - opts.Topics = topics - return opts + if len(obj) < 1 { + return errArguments + } + args.Earliest = int64(ethutil.Big(obj[0].FromBlock).Int64()) + args.Latest = int64(ethutil.Big(obj[0].ToBlock).Int64()) + args.Max = int(ethutil.Big(obj[0].Limit).Int64()) + args.Skip = int(ethutil.Big(obj[0].Offset).Int64()) + args.Address = obj[0].Address + args.Topics = obj[0].Topics + + return nil } -type FilterChangedArgs struct { - n int -} +// type FilterChangedArgs struct { +// n int +// } type DbArgs struct { Database string @@ -249,12 +333,32 @@ type DbArgs struct { Value string } +func (args *DbArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return errDecodeArgs + } + + if len(obj) < 2 { + return errArguments + } + args.Database = obj[0].(string) + args.Key = obj[1].(string) + + if len(obj) > 2 { + args.Value = obj[2].(string) + } + + return nil +} + func (a *DbArgs) requirements() error { if len(a.Database) == 0 { - return NewErrorWithMessage(errArguments, "DbPutArgs requires an 'Database' value as argument") + return NewErrorWithMessage(errArguments, "Database cannot be blank") } if len(a.Key) == 0 { - return NewErrorWithMessage(errArguments, "DbPutArgs requires an 'Key' value as argument") + return NewErrorWithMessage(errArguments, "Key cannot be blank") } return nil } @@ -263,7 +367,140 @@ type WhisperMessageArgs struct { Payload string To string From string - Topic []string + Topics []string Priority uint32 Ttl uint32 } + +func (args *WhisperMessageArgs) UnmarshalJSON(b []byte) (err error) { + var obj []struct { + Payload string + To string + From string + Topics []string + Priority string + Ttl string + } + + if err = json.Unmarshal(b, &obj); err != nil { + return errDecodeArgs + } + + if len(obj) < 1 { + return errArguments + } + args.Payload = obj[0].Payload + args.To = obj[0].To + args.From = obj[0].From + args.Topics = obj[0].Topics + args.Priority = uint32(ethutil.Big(obj[0].Priority).Int64()) + args.Ttl = uint32(ethutil.Big(obj[0].Ttl).Int64()) + + return nil +} + +type CompileArgs struct { + Source string +} + +func (args *CompileArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return errDecodeArgs + } + + if len(obj) > 0 { + args.Source = obj[0].(string) + } + + return nil +} + +type FilterStringArgs struct { + Word string +} + +func (args *FilterStringArgs) UnmarshalJSON(b []byte) (err error) { + var obj []string + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return errDecodeArgs + } + + if len(obj) < 1 { + return errDecodeArgs + } + + args.Word = obj[0] + + return nil +} + +type FilterIdArgs struct { + Id int +} + +func (args *FilterIdArgs) UnmarshalJSON(b []byte) (err error) { + var obj []string + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return errDecodeArgs + } + + if len(obj) < 1 { + return errDecodeArgs + } + + args.Id = int(ethutil.Big(obj[0]).Int64()) + + return nil +} + +type WhisperIdentityArgs struct { + Identity string +} + +func (args *WhisperIdentityArgs) UnmarshalJSON(b []byte) (err error) { + var obj []string + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return errDecodeArgs + } + + if len(obj) < 1 { + return errDecodeArgs + } + + args.Identity = obj[0] + + return nil +} + +type WhisperFilterArgs struct { + To string `json:"to"` + From string + Topics []string +} + +func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { + var obj []struct { + To string + From string + Topics []string + } + + if err = json.Unmarshal(b, &obj); err != nil { + return errDecodeArgs + } + + if len(obj) < 1 { + return errArguments + } + + args.To = obj[0].To + args.From = obj[0].From + args.Topics = obj[0].Topics + + return nil +} diff --git a/rpc/args_test.go b/rpc/args_test.go new file mode 100644 index 0000000000..0276245001 --- /dev/null +++ b/rpc/args_test.go @@ -0,0 +1,434 @@ +package rpc + +import ( + "bytes" + "encoding/json" + "math/big" + "testing" +) + +func TestSha3(t *testing.T) { + input := `["0x68656c6c6f20776f726c64"]` + expected := "0x68656c6c6f20776f726c64" + + args := new(Sha3Args) + json.Unmarshal([]byte(input), &args) + + if args.Data != expected { + t.Error("got %s expected %s", input, expected) + } +} + +func TestGetBalanceArgs(t *testing.T) { + input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "0x1f"]` + expected := new(GetBalanceArgs) + expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" + expected.BlockNumber = 31 + + args := new(GetBalanceArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if err := args.requirements(); err != nil { + t.Error(err) + } + + if args.Address != expected.Address { + t.Errorf("Address should be %v but is %v", expected.Address, args.Address) + } + + if args.BlockNumber != expected.BlockNumber { + t.Errorf("BlockNumber should be %v but is %v", expected.BlockNumber, args.BlockNumber) + } +} + +func TestGetBlockByHashArgs(t *testing.T) { + input := `["0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", true]` + expected := new(GetBlockByHashArgs) + expected.BlockHash = "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331" + expected.Transactions = true + + args := new(GetBlockByHashArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if args.BlockHash != expected.BlockHash { + t.Errorf("BlockHash should be %v but is %v", expected.BlockHash, args.BlockHash) + } + + if args.Transactions != expected.Transactions { + t.Errorf("Transactions should be %v but is %v", expected.Transactions, args.Transactions) + } +} + +func TestGetBlockByNumberArgs(t *testing.T) { + input := `["0x1b4", false]` + expected := new(GetBlockByNumberArgs) + expected.BlockNumber = 436 + expected.Transactions = false + + args := new(GetBlockByNumberArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if args.BlockNumber != expected.BlockNumber { + t.Errorf("BlockHash should be %v but is %v", expected.BlockNumber, args.BlockNumber) + } + + if args.Transactions != expected.Transactions { + t.Errorf("Transactions should be %v but is %v", expected.Transactions, args.Transactions) + } +} + +func TestNewTxArgs(t *testing.T) { + input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "value": "0x9184e72a000", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}]` + expected := new(NewTxArgs) + expected.From = "0xb60e8dd61c5d32be8058bb8eb970870f07233155" + expected.To = "0xd46e8dd67c5d32be8058bb8eb970870f072445675" + expected.Gas = big.NewInt(30400) + expected.GasPrice = big.NewInt(10000000000000) + expected.Value = big.NewInt(10000000000000) + expected.Data = "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + + args := new(NewTxArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.From != args.From { + t.Errorf("From shoud be %#v but is %#v", expected.From, args.From) + } + + if expected.To != args.To { + t.Errorf("To shoud be %#v but is %#v", expected.To, args.To) + } + + if bytes.Compare(expected.Gas.Bytes(), args.Gas.Bytes()) != 0 { + t.Errorf("Gas shoud be %#v but is %#v", expected.Gas.Bytes(), args.Gas.Bytes()) + } + + if bytes.Compare(expected.GasPrice.Bytes(), args.GasPrice.Bytes()) != 0 { + t.Errorf("GasPrice shoud be %#v but is %#v", expected.GasPrice, args.GasPrice) + } + + if bytes.Compare(expected.Value.Bytes(), args.Value.Bytes()) != 0 { + t.Errorf("Value shoud be %#v but is %#v", expected.Value, args.Value) + } + + if expected.Data != args.Data { + t.Errorf("Data shoud be %#v but is %#v", expected.Data, args.Data) + } +} + +func TestGetStorageArgs(t *testing.T) { + input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "latest"]` + expected := new(GetStorageArgs) + expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" + expected.BlockNumber = -1 + + args := new(GetStorageArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if err := args.requirements(); err != nil { + t.Error(err) + } + + if expected.Address != args.Address { + t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) + } + + if expected.BlockNumber != args.BlockNumber { + t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) + } +} + +func TestGetStorageAtArgs(t *testing.T) { + input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "0x0", "0x2"]` + expected := new(GetStorageAtArgs) + expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" + expected.Key = "0x0" + expected.BlockNumber = 2 + + args := new(GetStorageAtArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if err := args.requirements(); err != nil { + t.Error(err) + } + + if expected.Address != args.Address { + t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) + } + + if expected.Key != args.Key { + t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) + } + + if expected.BlockNumber != args.BlockNumber { + t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) + } +} + +func TestGetTxCountArgs(t *testing.T) { + input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "latest"]` + expected := new(GetTxCountArgs) + expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" + expected.BlockNumber = -1 + + args := new(GetTxCountArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if err := args.requirements(); err != nil { + t.Error(err) + } + + if expected.Address != args.Address { + t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) + } + + if expected.BlockNumber != args.BlockNumber { + t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) + } +} + +func TestGetDataArgs(t *testing.T) { + input := `["0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", "latest"]` + expected := new(GetDataArgs) + expected.Address = "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8" + expected.BlockNumber = -1 + + args := new(GetDataArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if err := args.requirements(); err != nil { + t.Error(err) + } + + if expected.Address != args.Address { + t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) + } + + if expected.BlockNumber != args.BlockNumber { + t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) + } +} + +func TestFilterOptions(t *testing.T) { + input := `[{ + "fromBlock": "0x1", + "toBlock": "0x2", + "limit": "0x3", + "offset": "0x0", + "address": "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", + "topics": ["0x12341234"]}]` + expected := new(FilterOptions) + expected.Earliest = 1 + expected.Latest = 2 + expected.Max = 3 + expected.Skip = 0 + expected.Address = "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8" + // expected.Topics = []string{"0x12341234"} + + args := new(FilterOptions) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Earliest != args.Earliest { + t.Errorf("Earliest shoud be %#v but is %#v", expected.Earliest, args.Earliest) + } + + if expected.Latest != args.Latest { + t.Errorf("Latest shoud be %#v but is %#v", expected.Latest, args.Latest) + } + + if expected.Max != args.Max { + t.Errorf("Max shoud be %#v but is %#v", expected.Max, args.Max) + } + + if expected.Skip != args.Skip { + t.Errorf("Skip shoud be %#v but is %#v", expected.Skip, args.Skip) + } + + if expected.Address != args.Address { + t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) + } + + // if expected.Topics != args.Topics { + // t.Errorf("Topic shoud be %#v but is %#v", expected.Topic, args.Topic) + // } +} + +func TestDbArgs(t *testing.T) { + input := `["0x74657374","0x6b6579","0x6d79537472696e67"]` + expected := new(DbArgs) + expected.Database = "0x74657374" + expected.Key = "0x6b6579" + expected.Value = "0x6d79537472696e67" + + args := new(DbArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if err := args.requirements(); err != nil { + t.Error(err) + } + + if expected.Database != args.Database { + t.Errorf("Database shoud be %#v but is %#v", expected.Database, args.Database) + } + + if expected.Key != args.Key { + t.Errorf("Key shoud be %#v but is %#v", expected.Key, args.Key) + } + + if expected.Value != args.Value { + t.Errorf("Value shoud be %#v but is %#v", expected.Value, args.Value) + } +} + +func TestWhisperMessageArgs(t *testing.T) { + input := `[{"from":"0xc931d93e97ab07fe42d923478ba2465f2", + "topics": ["0x68656c6c6f20776f726c64"], + "payload":"0x68656c6c6f20776f726c64", + "ttl": "0x64", + "priority": "0x64"}]` + expected := new(WhisperMessageArgs) + expected.From = "0xc931d93e97ab07fe42d923478ba2465f2" + expected.To = "" + expected.Payload = "0x68656c6c6f20776f726c64" + expected.Priority = 100 + expected.Ttl = 100 + expected.Topics = []string{"0x68656c6c6f20776f726c64"} + + args := new(WhisperMessageArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.From != args.From { + t.Errorf("From shoud be %#v but is %#v", expected.From, args.From) + } + + if expected.To != args.To { + t.Errorf("To shoud be %#v but is %#v", expected.To, args.To) + } + + if expected.Payload != args.Payload { + t.Errorf("Value shoud be %#v but is %#v", expected.Payload, args.Payload) + } + + if expected.Ttl != args.Ttl { + t.Errorf("Ttl shoud be %#v but is %#v", expected.Ttl, args.Ttl) + } + + if expected.Priority != args.Priority { + t.Errorf("Priority shoud be %#v but is %#v", expected.Priority, args.Priority) + } + + // if expected.Topics != args.Topics { + // t.Errorf("Topic shoud be %#v but is %#v", expected.Topic, args.Topic) + // } +} + +func TestFilterIdArgs(t *testing.T) { + input := `["0x7"]` + expected := new(FilterIdArgs) + expected.Id = 7 + + args := new(FilterIdArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Id != args.Id { + t.Errorf("Id shoud be %#v but is %#v", expected.Id, args.Id) + } +} + +func TestWhsiperFilterArgs(t *testing.T) { + input := `[{"topics": ["0x68656c6c6f20776f726c64"], "to": "0x34ag445g3455b34"}]` + expected := new(WhisperFilterArgs) + expected.From = "" + expected.To = "0x34ag445g3455b34" + expected.Topics = []string{"0x68656c6c6f20776f726c64"} + + args := new(WhisperFilterArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.From != args.From { + t.Errorf("From shoud be %#v but is %#v", expected.From, args.From) + } + + if expected.To != args.To { + t.Errorf("To shoud be %#v but is %#v", expected.To, args.To) + } + + // if expected.Topics != args.Topics { + // t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) + // } +} + +func TestCompileArgs(t *testing.T) { + input := `["contract test { function multiply(uint a) returns(uint d) { return a * 7; } }"]` + expected := new(CompileArgs) + expected.Source = `contract test { function multiply(uint a) returns(uint d) { return a * 7; } }` + + args := new(CompileArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Source != args.Source { + t.Errorf("Source shoud be %#v but is %#v", expected.Source, args.Source) + } +} + +func TestFilterStringArgs(t *testing.T) { + input := `["pending"]` + expected := new(FilterStringArgs) + expected.Word = "pending" + + args := new(FilterStringArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Word != args.Word { + t.Errorf("Word shoud be %#v but is %#v", expected.Word, args.Word) + } +} + +func TestWhisperIdentityArgs(t *testing.T) { + input := `["0xc931d93e97ab07fe42d923478ba2465f283"]` + expected := new(WhisperIdentityArgs) + expected.Identity = "0xc931d93e97ab07fe42d923478ba2465f283" + + args := new(WhisperIdentityArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Identity != args.Identity { + t.Errorf("Identity shoud be %#v but is %#v", expected.Identity, args.Identity) + } +} diff --git a/rpc/messages.go b/rpc/messages.go index b37d8229d6..a3ebbf330c 100644 --- a/rpc/messages.go +++ b/rpc/messages.go @@ -17,12 +17,9 @@ package rpc import ( - "bytes" "encoding/json" "errors" "fmt" - - "github.com/ethereum/go-ethereum/xeth" ) var ( @@ -33,10 +30,10 @@ var ( ) type RpcRequest struct { - ID interface{} `json:"id"` - JsonRpc string `json:"jsonrpc"` - Method string `json:"method"` - Params []json.RawMessage `json:"params"` + ID interface{} `json:"id"` + JsonRpc string `json:"jsonrpc"` + Method string `json:"method"` + Params json.RawMessage `json:"params"` } type RpcSuccessResponse struct { @@ -61,359 +58,30 @@ func NewErrorWithMessage(err error, msg string) error { return fmt.Errorf("%s: %s", err.Error(), msg) } -func (req *RpcRequest) ToSha3Args() (*Sha3Args, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - args := new(Sha3Args) - r := bytes.NewReader(req.Params[0]) - if err := json.NewDecoder(r).Decode(args); err != nil { - return nil, errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToGetBlockArgs() (*GetBlockArgs, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - args := new(GetBlockArgs) - r := bytes.NewReader(req.Params[0]) - err := json.NewDecoder(r).Decode(args) - if err != nil { - return nil, errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToNewTxArgs() (*NewTxArgs, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - args := new(NewTxArgs) - r := bytes.NewReader(req.Params[0]) - err := json.NewDecoder(r).Decode(args) - if err != nil { - return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) - } - - return args, nil -} - -func (req *RpcRequest) ToPushTxArgs() (*PushTxArgs, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - args := new(PushTxArgs) - r := bytes.NewReader(req.Params[0]) - err := json.NewDecoder(r).Decode(args) - if err != nil { - return nil, errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToGetStateArgs() (*GetStateArgs, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - args := new(GetStateArgs) - // TODO need to pass both arguments - r := bytes.NewReader(req.Params[0]) - err := json.NewDecoder(r).Decode(args) - if err != nil { - return nil, errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToStorageAtArgs() (*GetStorageArgs, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - args := new(GetStorageArgs) - r := bytes.NewReader(req.Params[0]) - err := json.NewDecoder(r).Decode(args) - if err != nil { - return nil, errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToGetTxCountArgs() (*GetTxCountArgs, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - args := new(GetTxCountArgs) - r := bytes.NewReader(req.Params[0]) - err := json.NewDecoder(r).Decode(args) - if err != nil { - return nil, errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToGetBalanceArgs() (*GetBalanceArgs, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - args := new(GetBalanceArgs) - r := bytes.NewReader(req.Params[0]) - err := json.NewDecoder(r).Decode(args) - if err != nil { - return nil, errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToGetCodeAtArgs() (*GetCodeAtArgs, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - args := new(GetCodeAtArgs) - r := bytes.NewReader(req.Params[0]) - err := json.NewDecoder(r).Decode(args) - if err != nil { - return nil, errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToBoolArgs() (bool, error) { - if len(req.Params) < 1 { - return false, errArguments - } - - var args bool - err := json.Unmarshal(req.Params[0], &args) - if err != nil { - return false, errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToIntArgs() (int, error) { - if len(req.Params) < 1 { - return 0, errArguments - } - - var args int - if err := json.Unmarshal(req.Params[0], &args); err != nil { - return 0, errArguments - } - - return args, nil -} - -func (req *RpcRequest) ToCompileArgs() (string, error) { - if len(req.Params) < 1 { - return "", errArguments - } - - var args string - err := json.Unmarshal(req.Params[0], &args) - if err != nil { - return "", errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToFilterArgs() (*FilterOptions, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - args := new(FilterOptions) - r := bytes.NewReader(req.Params[0]) - err := json.NewDecoder(r).Decode(args) - if err != nil { - return nil, errDecodeArgs - } - return args, nil -} - -func (req *RpcRequest) ToFilterStringArgs() (string, error) { - if len(req.Params) < 1 { - return "", errArguments - } - - var args string - err := json.Unmarshal(req.Params[0], &args) - if err != nil { - return "", errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToUninstallFilterArgs() (int, error) { - if len(req.Params) < 1 { - return 0, errArguments - } - - var args int - err := json.Unmarshal(req.Params[0], &args) - if err != nil { - return 0, errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToFilterChangedArgs() (int, error) { - if len(req.Params) < 1 { - return 0, errArguments - } - - var id int - r := bytes.NewReader(req.Params[0]) - err := json.NewDecoder(r).Decode(&id) - if err != nil { - return 0, errDecodeArgs - } - return id, nil -} - -func (req *RpcRequest) ToDbPutArgs() (*DbArgs, error) { - if len(req.Params) < 3 { - return nil, errArguments - } - - var args DbArgs - err := json.Unmarshal(req.Params[0], &args.Database) - if err != nil { - return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) - } - err = json.Unmarshal(req.Params[1], &args.Key) - if err != nil { - return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) - } - err = json.Unmarshal(req.Params[2], &args.Value) - if err != nil { - return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) - } - - return &args, nil -} - -func (req *RpcRequest) ToDbGetArgs() (*DbArgs, error) { - if len(req.Params) < 2 { - return nil, errArguments - } - - var args DbArgs - err := json.Unmarshal(req.Params[0], &args.Database) - if err != nil { - return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) - } - - err = json.Unmarshal(req.Params[1], &args.Key) - if err != nil { - return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) - } - - return &args, nil -} - -func (req *RpcRequest) ToWhisperFilterArgs() (*xeth.Options, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - var args xeth.Options - err := json.Unmarshal(req.Params[0], &args) - if err != nil { - return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) - } - - return &args, nil -} - -func (req *RpcRequest) ToIdArgs() (int, error) { - if len(req.Params) < 1 { - return 0, errArguments - } - - var id int - err := json.Unmarshal(req.Params[0], &id) - if err != nil { - return 0, errDecodeArgs - } - - return id, nil -} - -func (req *RpcRequest) ToWhisperPostArgs() (*WhisperMessageArgs, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - var args WhisperMessageArgs - err := json.Unmarshal(req.Params[0], &args) - if err != nil { - return nil, err - } - - return &args, nil -} - -func (req *RpcRequest) ToWhisperHasIdentityArgs() (string, error) { - if len(req.Params) < 1 { - return "", errArguments - } - - var args string - err := json.Unmarshal(req.Params[0], &args) - if err != nil { - return "", err - } - - return args, nil -} - -func (req *RpcRequest) ToRegisterArgs() (string, error) { - if len(req.Params) < 1 { - return "", errArguments - } - - var args string - err := json.Unmarshal(req.Params[0], &args) - if err != nil { - return "", err - } - - return args, nil -} - -func (req *RpcRequest) ToWatchTxArgs() (string, error) { - if len(req.Params) < 1 { - return "", errArguments - } - - var args string - err := json.Unmarshal(req.Params[0], &args) - if err != nil { - return "", err - } - - return args, nil -} +// func (req *RpcRequest) ToRegisterArgs() (string, error) { +// if len(req.Params) < 1 { +// return "", errArguments +// } + +// var args string +// err := json.Unmarshal(req.Params, &args) +// if err != nil { +// return "", err +// } + +// return args, nil +// } + +// func (req *RpcRequest) ToWatchTxArgs() (string, error) { +// if len(req.Params) < 1 { +// return "", errArguments +// } + +// var args string +// err := json.Unmarshal(req.Params, &args) +// if err != nil { +// return "", err +// } + +// return args, nil +// } diff --git a/rpc/responses.go b/rpc/responses.go new file mode 100644 index 0000000000..f41ce7b968 --- /dev/null +++ b/rpc/responses.go @@ -0,0 +1,212 @@ +package rpc + +import ( + "encoding/json" + // "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/core/types" +) + +type BlockRes struct { + fullTx bool + + BlockNumber int64 `json:"number"` + BlockHash []byte `json:"hash"` + ParentHash []byte `json:"parentHash"` + Nonce []byte `json:"nonce"` + Sha3Uncles []byte `json:"sha3Uncles"` + LogsBloom []byte `json:"logsBloom"` + TransactionRoot []byte `json:"transactionRoot"` + StateRoot []byte `json:"stateRoot"` + Miner []byte `json:"miner"` + Difficulty int64 `json:"difficulty"` + TotalDifficulty int64 `json:"totalDifficulty"` + Size int64 `json:"size"` + ExtraData []byte `json:"extraData"` + GasLimit int64 `json:"gasLimit"` + MinGasPrice int64 `json:"minGasPrice"` + GasUsed int64 `json:"gasUsed"` + UnixTimestamp int64 `json:"timestamp"` + Transactions []*TransactionRes `json:"transactions"` + Uncles [][]byte `json:"uncles"` +} + +func (b *BlockRes) MarshalJSON() ([]byte, error) { + var ext struct { + BlockNumber string `json:"number"` + BlockHash string `json:"hash"` + ParentHash string `json:"parentHash"` + Nonce string `json:"nonce"` + Sha3Uncles string `json:"sha3Uncles"` + LogsBloom string `json:"logsBloom"` + TransactionRoot string `json:"transactionRoot"` + StateRoot string `json:"stateRoot"` + Miner string `json:"miner"` + Difficulty string `json:"difficulty"` + TotalDifficulty string `json:"totalDifficulty"` + Size string `json:"size"` + ExtraData string `json:"extraData"` + GasLimit string `json:"gasLimit"` + MinGasPrice string `json:"minGasPrice"` + GasUsed string `json:"gasUsed"` + UnixTimestamp string `json:"timestamp"` + Transactions []interface{} `json:"transactions"` + Uncles []string `json:"uncles"` + } + + // convert strict types to hexified strings + ext.BlockNumber = toHex(big.NewInt(b.BlockNumber).Bytes()) + ext.BlockHash = toHex(b.BlockHash) + ext.ParentHash = toHex(b.ParentHash) + ext.Nonce = toHex(b.Nonce) + ext.Sha3Uncles = toHex(b.Sha3Uncles) + ext.LogsBloom = toHex(b.LogsBloom) + ext.TransactionRoot = toHex(b.TransactionRoot) + ext.StateRoot = toHex(b.StateRoot) + ext.Miner = toHex(b.Miner) + ext.Difficulty = toHex(big.NewInt(b.Difficulty).Bytes()) + ext.TotalDifficulty = toHex(big.NewInt(b.TotalDifficulty).Bytes()) + ext.Size = toHex(big.NewInt(b.Size).Bytes()) + // ext.ExtraData = toHex(b.ExtraData) + ext.GasLimit = toHex(big.NewInt(b.GasLimit).Bytes()) + // ext.MinGasPrice = toHex(big.NewInt(b.MinGasPrice).Bytes()) + ext.GasUsed = toHex(big.NewInt(b.GasUsed).Bytes()) + ext.UnixTimestamp = toHex(big.NewInt(b.UnixTimestamp).Bytes()) + ext.Transactions = make([]interface{}, len(b.Transactions)) + if b.fullTx { + for i, tx := range b.Transactions { + ext.Transactions[i] = tx + } + } else { + for i, tx := range b.Transactions { + ext.Transactions[i] = toHex(tx.Hash) + } + } + ext.Uncles = make([]string, len(b.Uncles)) + for i, v := range b.Uncles { + ext.Uncles[i] = toHex(v) + } + + return json.Marshal(ext) +} + +func NewBlockRes(block *types.Block) *BlockRes { + if block == nil { + return &BlockRes{} + } + + res := new(BlockRes) + res.BlockNumber = block.Number().Int64() + res.BlockHash = block.Hash() + res.ParentHash = block.ParentHash() + res.Nonce = block.Header().Nonce + res.Sha3Uncles = block.Header().UncleHash + res.LogsBloom = block.Bloom() + res.TransactionRoot = block.Header().TxHash + res.StateRoot = block.Root() + res.Miner = block.Header().Coinbase + res.Difficulty = block.Difficulty().Int64() + if block.Td != nil { + res.TotalDifficulty = block.Td.Int64() + } + res.Size = int64(block.Size()) + // res.ExtraData = + res.GasLimit = block.GasLimit().Int64() + // res.MinGasPrice = + res.GasUsed = block.GasUsed().Int64() + res.UnixTimestamp = block.Time() + res.Transactions = make([]*TransactionRes, len(block.Transactions())) + for i, tx := range block.Transactions() { + v := NewTransactionRes(tx) + v.BlockHash = block.Hash() + v.BlockNumber = block.Number().Int64() + v.TxIndex = int64(i) + res.Transactions[i] = v + } + res.Uncles = make([][]byte, len(block.Uncles())) + for i, uncle := range block.Uncles() { + res.Uncles[i] = uncle.Hash() + } + return res +} + +type TransactionRes struct { + Hash []byte `json:"hash"` + Nonce int64 `json:"nonce"` + BlockHash []byte `json:"blockHash,omitempty"` + BlockNumber int64 `json:"blockNumber,omitempty"` + TxIndex int64 `json:"transactionIndex,omitempty"` + From []byte `json:"from"` + To []byte `json:"to"` + Value int64 `json:"value"` + Gas int64 `json:"gas"` + GasPrice int64 `json:"gasPrice"` + Input []byte `json:"input"` +} + +func (t *TransactionRes) MarshalJSON() ([]byte, error) { + var ext struct { + Hash string `json:"hash"` + Nonce string `json:"nonce"` + BlockHash string `json:"blockHash,omitempty"` + BlockNumber string `json:"blockNumber,omitempty"` + TxIndex string `json:"transactionIndex,omitempty"` + From string `json:"from"` + To string `json:"to"` + Value string `json:"value"` + Gas string `json:"gas"` + GasPrice string `json:"gasPrice"` + Input string `json:"input"` + } + + ext.Hash = toHex(t.Hash) + ext.Nonce = toHex(big.NewInt(t.Nonce).Bytes()) + ext.BlockHash = toHex(t.BlockHash) + ext.BlockNumber = toHex(big.NewInt(t.BlockNumber).Bytes()) + ext.TxIndex = toHex(big.NewInt(t.TxIndex).Bytes()) + ext.From = toHex(t.From) + ext.To = toHex(t.To) + ext.Value = toHex(big.NewInt(t.Value).Bytes()) + ext.Gas = toHex(big.NewInt(t.Gas).Bytes()) + ext.GasPrice = toHex(big.NewInt(t.GasPrice).Bytes()) + ext.Input = toHex(t.Input) + + return json.Marshal(ext) +} + +func NewTransactionRes(tx *types.Transaction) *TransactionRes { + var v = new(TransactionRes) + v.Hash = tx.Hash() + v.Nonce = int64(tx.Nonce()) + v.From = tx.From() + v.To = tx.To() + v.Value = tx.Value().Int64() + v.Gas = tx.Gas().Int64() + v.GasPrice = tx.GasPrice().Int64() + v.Input = tx.Data() + return v +} + +type FilterLogRes struct { + Hash string `json:"hash"` + Address string `json:"address"` + Data string `json:"data"` + BlockNumber string `json:"blockNumber"` + TransactionHash string `json:"transactionHash"` + BlockHash string `json:"blockHash"` + TransactionIndex string `json:"transactionIndex"` + LogIndex string `json:"logIndex"` +} + +type FilterWhisperRes struct { + Hash string `json:"hash"` + From string `json:"from"` + To string `json:"to"` + Expiry string `json:"expiry"` + Sent string `json:"sent"` + Ttl string `json:"ttl"` + Topics string `json:"topics"` + Payload string `json:"payload"` + WorkProved string `json:"workProved"` +} diff --git a/rpc/util.go b/rpc/util.go index 3e8ca3fef0..8ff3c6d315 100644 --- a/rpc/util.go +++ b/rpc/util.go @@ -18,8 +18,11 @@ package rpc import ( "encoding/json" + "fmt" "io" + "math/big" "net/http" + "reflect" "time" "github.com/ethereum/go-ethereum/ethutil" @@ -32,9 +35,63 @@ var rpclogger = logger.NewLogger("RPC") type JsonWrapper struct{} +// 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 UnmarshalRawMessages(b []byte, iface interface{}, number *int64) (err error) { + var data []json.RawMessage + if err = json.Unmarshal(b, &data); err != nil && len(data) == 0 { + return errDecodeArgs + } + + // Number index determines the index in the array for a possible block number + numberIndex := 0 + + value := reflect.ValueOf(iface) + rvalue := reflect.Indirect(value) + + switch rvalue.Kind() { + case reflect.Slice: + // This is a bit of a cheat, but `data` is expected to be larger than 2 if iface is a slice + if number != nil { + numberIndex = len(data) - 1 + } else { + numberIndex = len(data) + } + + slice := reflect.MakeSlice(rvalue.Type(), numberIndex, numberIndex) + for i, raw := range data[0:numberIndex] { + v := slice.Index(i).Interface() + if err = json.Unmarshal(raw, &v); err != nil { + fmt.Println(err, v) + return err + } + slice.Index(i).Set(reflect.ValueOf(v)) + } + reflect.Indirect(rvalue).Set(slice) //value.Set(slice) + case reflect.Struct: + fallthrough + default: + if err = json.Unmarshal(data[0], iface); err != nil { + return errDecodeArgs + } + numberIndex = 1 + } + + // <0 index means out of bound for block number + if numberIndex >= 0 && len(data) > numberIndex { + if err = blockNumber(data[numberIndex], number); err != nil { + return errDecodeArgs + } + } + + return nil +} + func (self JsonWrapper) Send(writer io.Writer, v interface{}) (n int, err error) { var payload []byte - payload, err = json.Marshal(v) + payload, err = json.MarshalIndent(v, "", "\t") if err != nil { rpclogger.Fatalln("Error marshalling JSON", err) return 0, err @@ -63,18 +120,31 @@ func (self JsonWrapper) ParseRequestBody(req *http.Request) (RpcRequest, error) } func toHex(b []byte) string { - return "0x" + ethutil.Bytes2Hex(b) + hex := ethutil.Bytes2Hex(b) + // Prefer output of "0x0" instead of "0x" + if len(hex) == 0 { + hex = "0" + } + return "0x" + hex } + func fromHex(s string) []byte { if len(s) > 1 { if s[0:2] == "0x" { s = s[2:] } + if len(s)%2 == 1 { + s = "0" + s + } return ethutil.Hex2Bytes(s) } return nil } +func i2hex(n int) string { + return toHex(big.NewInt(int64(n)).Bytes()) +} + type RpcServer interface { Start() Stop() diff --git a/rpc/util_test.go b/rpc/util_test.go new file mode 100644 index 0000000000..b0a4979b5a --- /dev/null +++ b/rpc/util_test.go @@ -0,0 +1,25 @@ +package rpc + +import ( + "bytes" + "testing" +) + +//fromHex +func TestFromHex(t *testing.T) { + input := "0x01" + expected := []byte{1} + result := fromHex(input) + if bytes.Compare(expected, result) != 0 { + t.Errorf("Expected % x got % x", expected, result) + } +} + +func TestFromHexOddLength(t *testing.T) { + input := "0x1" + expected := []byte{1} + result := fromHex(input) + if bytes.Compare(expected, result) != 0 { + t.Errorf("Expected % x got % x", expected, result) + } +} diff --git a/vm/vm.go b/vm/vm.go index 6f39454721..6e4a54844f 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -37,7 +37,7 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I context := NewContext(caller, me, code, gas, price) - vmlogger.Debugf("(%d) (%x) %x (code=%d) gas: %v (d) %x\n", self.env.Depth(), caller.Address()[:4], context.Address(), len(code), context.Gas, callData) + self.Printf("(%d) (%x) %x (code=%d) gas: %v (d) %x", self.env.Depth(), caller.Address()[:4], context.Address(), len(code), context.Gas, callData).Endl() if self.Recoverable { // Recover from any require exception @@ -696,7 +696,7 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I if err != nil { stack.push(ethutil.BigFalse) - vmlogger.Debugln(err) + self.Printf("%v").Endl() } else { stack.push(ethutil.BigTrue) @@ -726,7 +726,7 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I return context.Return(nil), nil default: - vmlogger.Debugf("(pc) %-3v Invalid opcode %x\n", pc, op) + self.Printf("(pc) %-3v Invalid opcode %x\n", pc, op).Endl() panic(fmt.Errorf("Invalid opcode %x", op)) } @@ -894,7 +894,7 @@ func (self *Vm) Printf(format string, v ...interface{}) VirtualMachine { func (self *Vm) Endl() VirtualMachine { if self.debug { if self.logTy == LogTyPretty { - vmlogger.Debugln(self.logStr) + vmlogger.Infoln(self.logStr) self.logStr = "" } } diff --git a/xeth/xeth.go b/xeth/xeth.go index 60262bf175..d8dd66aeca 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -98,7 +98,7 @@ func New(eth Backend, frontend Frontend) *XEth { } func (self *XEth) Backend() Backend { return self.eth } -func (self *XEth) UseState(statedb *state.StateDB) *XEth { +func (self *XEth) WithState(statedb *state.StateDB) *XEth { xeth := &XEth{ eth: self.eth, blockProcessor: self.blockProcessor, @@ -122,7 +122,14 @@ func (self *XEth) BlockByHash(strHash string) *Block { return NewBlock(block) } -func (self *XEth) BlockByNumber(num int32) *Block { +func (self *XEth) EthBlockByHash(strHash string) *types.Block { + hash := fromHex(strHash) + block := self.chainManager.GetBlock(hash) + + return block +} + +func (self *XEth) BlockByNumber(num int64) *Block { if num == -1 { return NewBlock(self.chainManager.CurrentBlock()) } @@ -130,13 +137,21 @@ func (self *XEth) BlockByNumber(num int32) *Block { return NewBlock(self.chainManager.GetBlockByNumber(uint64(num))) } +func (self *XEth) EthBlockByNumber(num int64) *types.Block { + if num == -1 { + return self.chainManager.CurrentBlock() + } + + return self.chainManager.GetBlockByNumber(uint64(num)) +} + func (self *XEth) Block(v interface{}) *Block { if n, ok := v.(int32); ok { - return self.BlockByNumber(n) + return self.BlockByNumber(int64(n)) } else if str, ok := v.(string); ok { return self.BlockByHash(str) } else if f, ok := v.(float64); ok { // Don't ask ... - return self.BlockByNumber(int32(f)) + return self.BlockByNumber(int64(f)) } return nil @@ -278,14 +293,12 @@ func (self *XEth) PushTx(encodedTx string) (string, error) { return toHex(tx.Hash()), nil } -func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) { - if len(gasStr) == 0 { - gasStr = "100000" - } - if len(gasPriceStr) == 0 { - gasPriceStr = "1" - } +var ( + defaultGasPrice = big.NewInt(10000000000000) + defaultGas = big.NewInt(90000) +) +func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) { statedb := self.State().State() //self.chainManager.TransState() msg := callmsg{ from: statedb.GetOrNewStateObject(fromHex(fromStr)), @@ -295,6 +308,14 @@ func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr st value: ethutil.Big(valueStr), data: fromHex(dataStr), } + if msg.gas.Cmp(big.NewInt(0)) == 0 { + msg.gas = defaultGas + } + + if msg.gasPrice.Cmp(big.NewInt(0)) == 0 { + msg.gasPrice = defaultGasPrice + } + block := self.chainManager.CurrentBlock() vmenv := core.NewEnv(statedb, self.chainManager, msg, block) @@ -327,7 +348,7 @@ func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeSt tx = types.NewTransactionMessage(to, value.BigInt(), gas.BigInt(), price.BigInt(), data) } - state := self.chainManager.TransState() + state := self.chainManager.TxState() nonce := state.GetNonce(from) tx.SetNonce(nonce)