diff options
-rw-r--r-- | cmd/ethtest/main.go | 8 | ||||
-rw-r--r-- | cmd/geth/main.go | 2 | ||||
-rw-r--r-- | jsre/ethereum_js.go | 3031 | ||||
-rw-r--r-- | p2p/discover/table.go | 121 | ||||
-rw-r--r-- | p2p/discover/table_test.go | 6 | ||||
-rw-r--r-- | p2p/discover/udp.go | 78 | ||||
-rw-r--r-- | p2p/discover/udp_test.go | 75 | ||||
-rw-r--r-- | p2p/peer_error.go | 2 | ||||
-rw-r--r-- | p2p/rlpx.go | 16 | ||||
-rw-r--r-- | rlp/decode.go | 29 | ||||
-rw-r--r-- | rlp/decode_test.go | 9 | ||||
-rw-r--r-- | rlp/encode.go | 11 | ||||
-rw-r--r-- | rlp/encode_test.go | 4 |
13 files changed, 2371 insertions, 1021 deletions
diff --git a/cmd/ethtest/main.go b/cmd/ethtest/main.go index 5429cab31..67b965396 100644 --- a/cmd/ethtest/main.go +++ b/cmd/ethtest/main.go @@ -26,6 +26,7 @@ import ( "strings" "github.com/codegangsta/cli" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/tests" ) @@ -62,6 +63,10 @@ var ( Name: "skip", Usage: "Tests names to skip", } + TraceFlag = cli.BoolFlag{ + Name: "trace", + Usage: "Enable VM tracing", + } ) func runTestWithReader(test string, r io.Reader) error { @@ -173,7 +178,6 @@ func runSuite(test, file string) { glog.Fatalln(err) } } - } } } @@ -184,6 +188,7 @@ func setupApp(c *cli.Context) { continueOnError = c.GlobalBool(ContinueOnErrorFlag.Name) useStdIn := c.GlobalBool(ReadStdInFlag.Name) skipTests = strings.Split(c.GlobalString(SkipTestsFlag.Name), " ") + vm.Debug = c.GlobalBool(TraceFlag.Name) if !useStdIn { runSuite(flagTest, flagFile) @@ -211,6 +216,7 @@ func main() { ContinueOnErrorFlag, ReadStdInFlag, SkipTestsFlag, + TraceFlag, } if err := app.Run(os.Args); err != nil { diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 895e55b44..4905d502a 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -51,7 +51,7 @@ import ( ) const ( - ClientIdentifier = "Geth " + ClientIdentifier = "Geth" Version = "1.0.1" VersionMajor = 1 VersionMinor = 0 diff --git a/jsre/ethereum_js.go b/jsre/ethereum_js.go index 012e5af70..f33bb7c25 100644 --- a/jsre/ethereum_js.go +++ b/jsre/ethereum_js.go @@ -18,6 +18,622 @@ package jsre const Web3_JS = ` require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ +module.exports=[ + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + } + ], + "name": "name", + "outputs": [ + { + "name": "o_name", + "type": "bytes32" + } + ], + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_name", + "type": "bytes32" + } + ], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_name", + "type": "bytes32" + } + ], + "name": "content", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_name", + "type": "bytes32" + } + ], + "name": "addr", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_name", + "type": "bytes32" + } + ], + "name": "reserve", + "outputs": [], + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_name", + "type": "bytes32" + } + ], + "name": "subRegistrar", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_name", + "type": "bytes32" + }, + { + "name": "_newOwner", + "type": "address" + } + ], + "name": "transfer", + "outputs": [], + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_name", + "type": "bytes32" + }, + { + "name": "_registrar", + "type": "address" + } + ], + "name": "setSubRegistrar", + "outputs": [], + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "Registrar", + "outputs": [], + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_name", + "type": "bytes32" + }, + { + "name": "_a", + "type": "address" + }, + { + "name": "_primary", + "type": "bool" + } + ], + "name": "setAddress", + "outputs": [], + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_name", + "type": "bytes32" + }, + { + "name": "_content", + "type": "bytes32" + } + ], + "name": "setContent", + "outputs": [], + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_name", + "type": "bytes32" + } + ], + "name": "disown", + "outputs": [], + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_name", + "type": "bytes32" + }, + { + "indexed": false, + "name": "_winner", + "type": "address" + } + ], + "name": "AuctionEnded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_name", + "type": "bytes32" + }, + { + "indexed": false, + "name": "_bidder", + "type": "address" + }, + { + "indexed": false, + "name": "_value", + "type": "uint256" + } + ], + "name": "NewBid", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "name", + "type": "bytes32" + } + ], + "name": "Changed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "name", + "type": "bytes32" + }, + { + "indexed": true, + "name": "addr", + "type": "address" + } + ], + "name": "PrimaryChanged", + "type": "event" + } +] +},{}],2:[function(require,module,exports){ +module.exports=[ + { + "constant": true, + "inputs": [ + { + "name": "_name", + "type": "bytes32" + } + ], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_name", + "type": "bytes32" + }, + { + "name": "_refund", + "type": "address" + } + ], + "name": "disown", + "outputs": [], + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_name", + "type": "bytes32" + } + ], + "name": "addr", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_name", + "type": "bytes32" + } + ], + "name": "reserve", + "outputs": [], + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_name", + "type": "bytes32" + }, + { + "name": "_newOwner", + "type": "address" + } + ], + "name": "transfer", + "outputs": [], + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_name", + "type": "bytes32" + }, + { + "name": "_a", + "type": "address" + } + ], + "name": "setAddr", + "outputs": [], + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "name", + "type": "bytes32" + } + ], + "name": "Changed", + "type": "event" + } +] +},{}],3:[function(require,module,exports){ +module.exports=[ + { + "constant": false, + "inputs": [ + { + "name": "from", + "type": "bytes32" + }, + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [], + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "from", + "type": "bytes32" + }, + { + "name": "to", + "type": "address" + }, + { + "name": "indirectId", + "type": "bytes32" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "icapTransfer", + "outputs": [], + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "bytes32" + } + ], + "name": "deposit", + "outputs": [], + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "AnonymousDeposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "bytes32" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "bytes32" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "bytes32" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "indirectId", + "type": "bytes32" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "IcapTransfer", + "type": "event" + } +] +},{}],4:[function(require,module,exports){ +var f = require('./formatters'); +var SolidityType = require('./type'); + +/** + * SolidityTypeAddress is a prootype that represents address type + * It matches: + * address + * address[] + * address[4] + * address[][] + * address[3][] + * address[][6][], ... + */ +var SolidityTypeAddress = function () { + this._inputFormatter = f.formatInputInt; + this._outputFormatter = f.formatOutputAddress; +}; + +SolidityTypeAddress.prototype = new SolidityType({}); +SolidityTypeAddress.prototype.constructor = SolidityTypeAddress; + +SolidityTypeAddress.prototype.isType = function (name) { + return !!name.match(/address(\[([0-9]*)\])?/); +}; + +SolidityTypeAddress.prototype.staticPartLength = function (name) { + return 32 * this.staticArrayLength(name); +}; + +module.exports = SolidityTypeAddress; + + +},{"./formatters":9,"./type":14}],5:[function(require,module,exports){ +var f = require('./formatters'); +var SolidityType = require('./type'); + +/** + * SolidityTypeBool is a prootype that represents bool type + * It matches: + * bool + * bool[] + * bool[4] + * bool[][] + * bool[3][] + * bool[][6][], ... + */ +var SolidityTypeBool = function () { + this._inputFormatter = f.formatInputBool; + this._outputFormatter = f.formatOutputBool; +}; + +SolidityTypeBool.prototype = new SolidityType({}); +SolidityTypeBool.prototype.constructor = SolidityTypeBool; + +SolidityTypeBool.prototype.isType = function (name) { + return !!name.match(/^bool(\[([0-9]*)\])*$/); +}; + +SolidityTypeBool.prototype.staticPartLength = function (name) { + return 32 * this.staticArrayLength(name); +}; + +module.exports = SolidityTypeBool; + +},{"./formatters":9,"./type":14}],6:[function(require,module,exports){ +var f = require('./formatters'); +var SolidityType = require('./type'); + +/** + * SolidityTypeBytes is a prootype that represents bytes type + * It matches: + * bytes + * bytes[] + * bytes[4] + * bytes[][] + * bytes[3][] + * bytes[][6][], ... + * bytes32 + * bytes64[] + * bytes8[4] + * bytes256[][] + * bytes[3][] + * bytes64[][6][], ... + */ +var SolidityTypeBytes = function () { + this._inputFormatter = f.formatInputBytes; + this._outputFormatter = f.formatOutputBytes; +}; + +SolidityTypeBytes.prototype = new SolidityType({}); +SolidityTypeBytes.prototype.constructor = SolidityTypeBytes; + +SolidityTypeBytes.prototype.isType = function (name) { + return !!name.match(/^bytes([0-9]{1,})(\[([0-9]*)\])*$/); +}; + +SolidityTypeBytes.prototype.staticPartLength = function (name) { + var matches = name.match(/^bytes([0-9]*)/); + var size = parseInt(matches[1]); + return size * this.staticArrayLength(name); +}; + +module.exports = SolidityTypeBytes; + +},{"./formatters":9,"./type":14}],7:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -40,107 +656,17 @@ require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof requ * @date 2015 */ -var BigNumber = require('bignumber.js'); -var utils = require('../utils/utils'); var f = require('./formatters'); -var SolidityParam = require('./param'); - -/** - * Should be used to check if a type is an array type - * - * @method isArrayType - * @param {String} type - * @return {Bool} true is the type is an array, otherwise false - */ -var isArrayType = function (type) { - return type.slice(-2) === '[]'; -}; - -/** - * SolidityType prototype is used to encode/decode solidity params of certain type - */ -var SolidityType = function (config) { - this._name = config.name; - this._match = config.match; - this._mode = config.mode; - this._inputFormatter = config.inputFormatter; - this._outputFormatter = config.outputFormatter; -}; - -/** - * Should be used to determine if this SolidityType do match given type - * - * @method isType - * @param {String} name - * @return {Bool} true if type match this SolidityType, otherwise false - */ -SolidityType.prototype.isType = function (name) { - if (this._match === 'strict') { - return this._name === name || (name.indexOf(this._name) === 0 && name.slice(this._name.length) === '[]'); - } else if (this._match === 'prefix') { - // TODO better type detection! - return name.indexOf(this._name) === 0; - } -}; - -/** - * Should be used to transform plain param to SolidityParam object - * - * @method formatInput - * @param {Object} param - plain object, or an array of objects - * @param {Bool} arrayType - true if a param should be encoded as an array - * @return {SolidityParam} encoded param wrapped in SolidityParam object - */ -SolidityType.prototype.formatInput = function (param, arrayType) { - if (utils.isArray(param) && arrayType) { // TODO: should fail if this two are not the same - var self = this; - return param.map(function (p) { - return self._inputFormatter(p); - }).reduce(function (acc, current) { - return acc.combine(current); - }, f.formatInputInt(param.length)).withOffset(32); - } - return this._inputFormatter(param); -}; -/** - * Should be used to transoform SolidityParam to plain param - * - * @method formatOutput - * @param {SolidityParam} byteArray - * @param {Bool} arrayType - true if a param should be decoded as an array - * @return {Object} plain decoded param - */ -SolidityType.prototype.formatOutput = function (param, arrayType) { - if (arrayType) { - // let's assume, that we solidity will never return long arrays :P - var result = []; - var length = new BigNumber(param.dynamicPart().slice(0, 64), 16); - for (var i = 0; i < length * 64; i += 64) { - result.push(this._outputFormatter(new SolidityParam(param.dynamicPart().substr(i + 64, 64)))); - } - return result; - } - return this._outputFormatter(param); -}; - -/** - * Should be used to slice single param from bytes - * - * @method sliceParam - * @param {String} bytes - * @param {Number} index of param to slice - * @param {String} type - * @returns {SolidityParam} param - */ -SolidityType.prototype.sliceParam = function (bytes, index, type) { - if (this._mode === 'bytes') { - return SolidityParam.decodeBytes(bytes, index); - } else if (isArrayType(type)) { - return SolidityParam.decodeArray(bytes, index); - } - return SolidityParam.decodeParam(bytes, index); -}; +var SolidityTypeAddress = require('./address'); +var SolidityTypeBool = require('./bool'); +var SolidityTypeInt = require('./int'); +var SolidityTypeUInt = require('./uint'); +var SolidityTypeDynamicBytes = require('./dynamicbytes'); +var SolidityTypeString = require('./string'); +var SolidityTypeReal = require('./real'); +var SolidityTypeUReal = require('./ureal'); +var SolidityTypeBytes = require('./bytes'); /** * SolidityCoder prototype should be used to encode/decode solidity params of any type @@ -170,18 +696,6 @@ SolidityCoder.prototype._requireType = function (type) { }; /** - * Should be used to transform plain param of given type to SolidityParam - * - * @method _formatInput - * @param {String} type of param - * @param {Object} plain param - * @return {SolidityParam} - */ -SolidityCoder.prototype._formatInput = function (type, param) { - return this._requireType(type).formatInput(param, isArrayType(type)); -}; - -/** * Should be used to encode plain param * * @method encodeParam @@ -190,7 +704,7 @@ SolidityCoder.prototype._formatInput = function (type, param) { * @return {String} encoded plain param */ SolidityCoder.prototype.encodeParam = function (type, param) { - return this._formatInput(type, param).encode(); + return this.encodeParams([type], [param]); }; /** @@ -202,12 +716,113 @@ SolidityCoder.prototype.encodeParam = function (type, param) { * @return {String} encoded list of params */ SolidityCoder.prototype.encodeParams = function (types, params) { + var solidityTypes = this.getSolidityTypes(types); + + var encodeds = solidityTypes.map(function (solidityType, index) { + return solidityType.encode(params[index], types[index]); + }); + + var dynamicOffset = solidityTypes.reduce(function (acc, solidityType, index) { + return acc + solidityType.staticPartLength(types[index]); + }, 0); + + var result = this.encodeMultiWithOffset(types, solidityTypes, encodeds, dynamicOffset); + + return result; +}; + +SolidityCoder.prototype.encodeMultiWithOffset = function (types, solidityTypes, encodeds, dynamicOffset) { + var result = ""; var self = this; - var solidityParams = types.map(function (type, index) { - return self._formatInput(type, params[index]); + + var isDynamic = function (i) { + return solidityTypes[i].isDynamicArray(types[i]) || solidityTypes[i].isDynamicType(types[i]); + }; + + types.forEach(function (type, i) { + if (isDynamic(i)) { + result += f.formatInputInt(dynamicOffset).encode(); + var e = self.encodeWithOffset(types[i], solidityTypes[i], encodeds[i], dynamicOffset); + dynamicOffset += e.length / 2; + } else { + // don't add length to dynamicOffset. it's already counted + result += self.encodeWithOffset(types[i], solidityTypes[i], encodeds[i], dynamicOffset); + } + + // TODO: figure out nested arrays + }); + + types.forEach(function (type, i) { + if (isDynamic(i)) { + var e = self.encodeWithOffset(types[i], solidityTypes[i], encodeds[i], dynamicOffset); + dynamicOffset += e.length / 2; + result += e; + } }); + return result; +}; + +// TODO: refactor whole encoding! +SolidityCoder.prototype.encodeWithOffset = function (type, solidityType, encoded, offset) { + var self = this; + if (solidityType.isDynamicArray(type)) { + return (function () { + // offset was already set + var nestedName = solidityType.nestedName(type); + var nestedStaticPartLength = solidityType.staticPartLength(nestedName); + var result = encoded[0]; + + (function () { + var previousLength = 2; // in int + if (solidityType.isDynamicArray(nestedName)) { + for (var i = 1; i < encoded.length; i++) { + previousLength += +(encoded[i - 1])[0] || 0; + result += f.formatInputInt(offset + i * nestedStaticPartLength + previousLength * 32).encode(); + } + } + })(); + + // first element is length, skip it + (function () { + for (var i = 0; i < encoded.length - 1; i++) { + var additionalOffset = result / 2; + result += self.encodeWithOffset(nestedName, solidityType, encoded[i + 1], offset + additionalOffset); + } + })(); + + return result; + })(); + + } else if (solidityType.isStaticArray(type)) { + return (function () { + var nestedName = solidityType.nestedName(type); + var nestedStaticPartLength = solidityType.staticPartLength(nestedName); + var result = ""; + + + if (solidityType.isDynamicArray(nestedName)) { + (function () { + var previousLength = 0; // in int + for (var i = 0; i < encoded.length; i++) { + // calculate length of previous item + previousLength += +(encoded[i - 1] || [])[0] || 0; + result += f.formatInputInt(offset + i * nestedStaticPartLength + previousLength * 32).encode(); + } + })(); + } + + (function () { + for (var i = 0; i < encoded.length; i++) { + var additionalOffset = result / 2; + result += self.encodeWithOffset(nestedName, solidityType, encoded[i], offset + additionalOffset); + } + })(); + + return result; + })(); + } - return SolidityParam.encodeList(solidityParams); + return encoded; }; /** @@ -231,84 +846,82 @@ SolidityCoder.prototype.decodeParam = function (type, bytes) { * @return {Array} array of plain params */ SolidityCoder.prototype.decodeParams = function (types, bytes) { + var solidityTypes = this.getSolidityTypes(types); + var offsets = this.getOffsets(types, solidityTypes); + + return solidityTypes.map(function (solidityType, index) { + return solidityType.decode(bytes, offsets[index], types[index], index); + }); +}; + +SolidityCoder.prototype.getOffsets = function (types, solidityTypes) { + var lengths = solidityTypes.map(function (solidityType, index) { + return solidityType.staticPartLength(types[index]); + // get length + }); + + for (var i = 0; i < lengths.length; i++) { + // sum with length of previous element + var previous = (lengths[i - 1] || 0); + lengths[i] += previous; + } + + return lengths.map(function (length, index) { + // remove the current length, so the length is sum of previous elements + return length - solidityTypes[index].staticPartLength(types[index]); + }); +}; + +SolidityCoder.prototype.getSolidityTypes = function (types) { var self = this; - return types.map(function (type, index) { - var solidityType = self._requireType(type); - var p = solidityType.sliceParam(bytes, index, type); - return solidityType.formatOutput(p, isArrayType(type)); + return types.map(function (type) { + return self._requireType(type); }); }; var coder = new SolidityCoder([ - new SolidityType({ - name: 'address', - match: 'strict', - mode: 'value', - inputFormatter: f.formatInputInt, - outputFormatter: f.formatOutputAddress - }), - new SolidityType({ - name: 'bool', - match: 'strict', - mode: 'value', - inputFormatter: f.formatInputBool, - outputFormatter: f.formatOutputBool - }), - new SolidityType({ - name: 'int', - match: 'prefix', - mode: 'value', - inputFormatter: f.formatInputInt, - outputFormatter: f.formatOutputInt, - }), - new SolidityType({ - name: 'uint', - match: 'prefix', - mode: 'value', - inputFormatter: f.formatInputInt, - outputFormatter: f.formatOutputUInt - }), - new SolidityType({ - name: 'bytes', - match: 'strict', - mode: 'bytes', - inputFormatter: f.formatInputDynamicBytes, - outputFormatter: f.formatOutputDynamicBytes - }), - new SolidityType({ - name: 'bytes', - match: 'prefix', - mode: 'value', - inputFormatter: f.formatInputBytes, - outputFormatter: f.formatOutputBytes - }), - new SolidityType({ - name: 'string', - match: 'strict', - mode: 'bytes', - inputFormatter: f.formatInputString, - outputFormatter: f.formatOutputString - }), - new SolidityType({ - name: 'real', - match: 'prefix', - mode: 'value', - inputFormatter: f.formatInputReal, - outputFormatter: f.formatOutputReal - }), - new SolidityType({ - name: 'ureal', - match: 'prefix', - mode: 'value', - inputFormatter: f.formatInputReal, - outputFormatter: f.formatOutputUReal - }) + new SolidityTypeAddress(), + new SolidityTypeBool(), + new SolidityTypeInt(), + new SolidityTypeUInt(), + new SolidityTypeDynamicBytes(), + new SolidityTypeBytes(), + new SolidityTypeString(), + new SolidityTypeReal(), + new SolidityTypeUReal() ]); module.exports = coder; -},{"../utils/utils":7,"./formatters":2,"./param":3,"bignumber.js":"bignumber.js"}],2:[function(require,module,exports){ +},{"./address":4,"./bool":5,"./bytes":6,"./dynamicbytes":8,"./formatters":9,"./int":10,"./real":12,"./string":13,"./uint":15,"./ureal":16}],8:[function(require,module,exports){ +var f = require('./formatters'); +var SolidityType = require('./type'); + +var SolidityTypeDynamicBytes = function () { + this._inputFormatter = f.formatInputDynamicBytes; + this._outputFormatter = f.formatOutputDynamicBytes; +}; + +SolidityTypeDynamicBytes.prototype = new SolidityType({}); +SolidityTypeDynamicBytes.prototype.constructor = SolidityTypeDynamicBytes; + +SolidityTypeDynamicBytes.prototype.isType = function (name) { + return !!name.match(/^bytes(\[([0-9]*)\])*$/); +}; + +SolidityTypeDynamicBytes.prototype.staticPartLength = function (name) { + return 32 * this.staticArrayLength(name); +}; + +SolidityTypeDynamicBytes.prototype.isDynamicType = function () { + return true; +}; + +module.exports = SolidityTypeDynamicBytes; + + +},{"./formatters":9,"./type":14}],9:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -347,9 +960,8 @@ var SolidityParam = require('./param'); * @returns {SolidityParam} */ var formatInputInt = function (value) { - var padding = c.ETH_PADDING * 2; BigNumber.config(c.ETH_BIGNUMBER_ROUNDING_MODE); - var result = utils.padLeft(utils.toTwosComplement(value).round().toString(16), padding); + var result = utils.padLeft(utils.toTwosComplement(value).round().toString(16), 64); return new SolidityParam(result); }; @@ -361,7 +973,9 @@ var formatInputInt = function (value) { * @returns {SolidityParam} */ var formatInputBytes = function (value) { - var result = utils.padRight(utils.toHex(value).substr(2), 64); + var result = utils.toHex(value).substr(2); + var l = Math.floor((result.length + 63) / 64); + result = utils.padRight(result, l * 64); return new SolidityParam(result); }; @@ -376,8 +990,8 @@ var formatInputDynamicBytes = function (value) { var result = utils.toHex(value).substr(2); var length = result.length / 2; var l = Math.floor((result.length + 63) / 64); - var result = utils.padRight(result, l * 64); - return new SolidityParam(formatInputInt(length).value + result, 32); + result = utils.padRight(result, l * 64); + return new SolidityParam(formatInputInt(length).value + result); }; /** @@ -392,7 +1006,7 @@ var formatInputString = function (value) { var length = result.length / 2; var l = Math.floor((result.length + 63) / 64); result = utils.padRight(result, l * 64); - return new SolidityParam(formatInputInt(length).value + result, 32); + return new SolidityParam(formatInputInt(length).value + result); }; /** @@ -559,7 +1173,45 @@ module.exports = { }; -},{"../utils/config":5,"../utils/utils":7,"./param":3,"bignumber.js":"bignumber.js"}],3:[function(require,module,exports){ +},{"../utils/config":18,"../utils/utils":20,"./param":11,"bignumber.js":"bignumber.js"}],10:[function(require,module,exports){ +var f = require('./formatters'); +var SolidityType = require('./type'); + +/** + * SolidityTypeInt is a prootype that represents int type + * It matches: + * int + * int[] + * int[4] + * int[][] + * int[3][] + * int[][6][], ... + * int32 + * int64[] + * int8[4] + * int256[][] + * int[3][] + * int64[][6][], ... + */ +var SolidityTypeInt = function () { + this._inputFormatter = f.formatInputInt; + this._outputFormatter = f.formatOutputInt; +}; + +SolidityTypeInt.prototype = new SolidityType({}); +SolidityTypeInt.prototype.constructor = SolidityTypeInt; + +SolidityTypeInt.prototype.isType = function (name) { + return !!name.match(/^int([0-9]*)?(\[([0-9]*)\])*$/); +}; + +SolidityTypeInt.prototype.staticPartLength = function (name) { + return 32 * this.staticArrayLength(name); +}; + +module.exports = SolidityTypeInt; + +},{"./formatters":9,"./type":14}],11:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -634,7 +1286,7 @@ SolidityParam.prototype.combine = function (param) { * @returns {Boolean} */ SolidityParam.prototype.isDynamic = function () { - return this.value.length > 64 || this.offset !== undefined; + return this.offset !== undefined; }; /** @@ -708,71 +1360,398 @@ SolidityParam.encodeList = function (params) { }, '')); }; + + +module.exports = SolidityParam; + + +},{"../utils/utils":20}],12:[function(require,module,exports){ +var f = require('./formatters'); +var SolidityType = require('./type'); + /** - * This method should be used to decode plain (static) solidity param at given index + * SolidityTypeReal is a prootype that represents real type + * It matches: + * real + * real[] + * real[4] + * real[][] + * real[3][] + * real[][6][], ... + * real32 + * real64[] + * real8[4] + * real256[][] + * real[3][] + * real64[][6][], ... + */ +var SolidityTypeReal = function () { + this._inputFormatter = f.formatInputReal; + this._outputFormatter = f.formatOutputReal; +}; + +SolidityTypeReal.prototype = new SolidityType({}); +SolidityTypeReal.prototype.constructor = SolidityTypeReal; + +SolidityTypeReal.prototype.isType = function (name) { + return !!name.match(/real([0-9]*)?(\[([0-9]*)\])?/); +}; + +SolidityTypeReal.prototype.staticPartLength = function (name) { + return 32 * this.staticArrayLength(name); +}; + +module.exports = SolidityTypeReal; + +},{"./formatters":9,"./type":14}],13:[function(require,module,exports){ +var f = require('./formatters'); +var SolidityType = require('./type'); + +var SolidityTypeString = function () { + this._inputFormatter = f.formatInputString; + this._outputFormatter = f.formatOutputString; +}; + +SolidityTypeString.prototype = new SolidityType({}); +SolidityTypeString.prototype.constructor = SolidityTypeString; + +SolidityTypeString.prototype.isType = function (name) { + return !!name.match(/^string(\[([0-9]*)\])*$/); +}; + +SolidityTypeString.prototype.staticPartLength = function (name) { + return 32 * this.staticArrayLength(name); +}; + +SolidityTypeString.prototype.isDynamicType = function () { + return true; +}; + +module.exports = SolidityTypeString; + + +},{"./formatters":9,"./type":14}],14:[function(require,module,exports){ +var f = require('./formatters'); +var SolidityParam = require('./param'); + +/** + * SolidityType prototype is used to encode/decode solidity params of certain type + */ +var SolidityType = function (config) { + this._inputFormatter = config.inputFormatter; + this._outputFormatter = config.outputFormatter; +}; + +/** + * Should be used to determine if this SolidityType do match given name * - * @method decodeParam - * @param {String} bytes - * @param {Number} index - * @returns {SolidityParam} + * @method isType + * @param {String} name + * @return {Bool} true if type match this SolidityType, otherwise false */ -SolidityParam.decodeParam = function (bytes, index) { - index = index || 0; - return new SolidityParam(bytes.substr(index * 64, 64)); +SolidityType.prototype.isType = function (name) { + throw "this method should be overrwritten for type " + name; }; /** - * This method should be called to get offset value from bytes at given index + * Should be used to determine what is the length of static part in given type * - * @method getOffset - * @param {String} bytes - * @param {Number} index - * @returns {Number} offset as number + * @method staticPartLength + * @param {String} name + * @return {Number} length of static part in bytes */ -var getOffset = function (bytes, index) { - // we can do this cause offset is rather small - return parseInt('0x' + bytes.substr(index * 64, 64)); +SolidityType.prototype.staticPartLength = function (name) { + throw "this method should be overrwritten for type: " + name; }; /** - * This method should be called to decode solidity bytes param at given index + * Should be used to determine if type is dynamic array + * eg: + * "type[]" => true + * "type[4]" => false * - * @method decodeBytes - * @param {String} bytes - * @param {Number} index - * @returns {SolidityParam} + * @method isDynamicArray + * @param {String} name + * @return {Bool} true if the type is dynamic array + */ +SolidityType.prototype.isDynamicArray = function (name) { + var nestedTypes = this.nestedTypes(name); + return !!nestedTypes && !nestedTypes[nestedTypes.length - 1].match(/[0-9]{1,}/g); +}; + +/** + * Should be used to determine if type is static array + * eg: + * "type[]" => false + * "type[4]" => true + * + * @method isStaticArray + * @param {String} name + * @return {Bool} true if the type is static array + */ +SolidityType.prototype.isStaticArray = function (name) { + var nestedTypes = this.nestedTypes(name); + return !!nestedTypes && !!nestedTypes[nestedTypes.length - 1].match(/[0-9]{1,}/g); +}; + +/** + * Should return length of static array + * eg. + * "int[32]" => 32 + * "int256[14]" => 14 + * "int[2][3]" => 3 + * "int" => 1 + * "int[1]" => 1 + * "int[]" => 1 + * + * @method staticArrayLength + * @param {String} name + * @return {Number} static array length + */ +SolidityType.prototype.staticArrayLength = function (name) { + var nestedTypes = this.nestedTypes(name); + if (nestedTypes) { + return parseInt(nestedTypes[nestedTypes.length - 1].match(/[0-9]{1,}/g) || 1); + } + return 1; +}; + +/** + * Should return nested type + * eg. + * "int[32]" => "int" + * "int256[14]" => "int256" + * "int[2][3]" => "int[2]" + * "int" => "int" + * "int[]" => "int" + * + * @method nestedName + * @param {String} name + * @return {String} nested name + */ +SolidityType.prototype.nestedName = function (name) { + // remove last [] in name + var nestedTypes = this.nestedTypes(name); + if (!nestedTypes) { + return name; + } + + return name.substr(0, name.length - nestedTypes[nestedTypes.length - 1].length); +}; + +/** + * Should return true if type has dynamic size by default + * such types are "string", "bytes" + * + * @method isDynamicType + * @param {String} name + * @return {Bool} true if is dynamic, otherwise false + */ +SolidityType.prototype.isDynamicType = function () { + return false; +}; + +/** + * Should return array of nested types + * eg. + * "int[2][3][]" => ["[2]", "[3]", "[]"] + * "int[] => ["[]"] + * "int" => null + * + * @method nestedTypes + * @param {String} name + * @return {Array} array of nested types + */ +SolidityType.prototype.nestedTypes = function (name) { + // return list of strings eg. "[]", "[3]", "[]", "[2]" + return name.match(/(\[[0-9]*\])/g); +}; + +/** + * Should be used to encode the value + * + * @method encode + * @param {Object} value + * @param {String} name + * @return {String} encoded value */ -SolidityParam.decodeBytes = function (bytes, index) { - index = index || 0; +SolidityType.prototype.encode = function (value, name) { + var self = this; + if (this.isDynamicArray(name)) { + + return (function () { + var length = value.length; // in int + var nestedName = self.nestedName(name); + + var result = []; + result.push(f.formatInputInt(length).encode()); + + value.forEach(function (v) { + result.push(self.encode(v, nestedName)); + }); + + return result; + })(); - var offset = getOffset(bytes, index); + } else if (this.isStaticArray(name)) { - var l = parseInt('0x' + bytes.substr(offset * 2, 64)); - l = Math.floor((l + 31) / 32); + return (function () { + var length = self.staticArrayLength(name); // in int + var nestedName = self.nestedName(name); - // (1 + l) * , cause we also parse length - return new SolidityParam(bytes.substr(offset * 2, (1 + l) * 64), 0); + var result = []; + for (var i = 0; i < length; i++) { + result.push(self.encode(value[i], nestedName)); + } + + return result; + })(); + + } + + return this._inputFormatter(value, name).encode(); }; /** - * This method should be used to decode solidity array at given index + * Should be used to decode value from bytes * - * @method decodeArray + * @method decode * @param {String} bytes - * @param {Number} index - * @returns {SolidityParam} + * @param {Number} offset in bytes + * @param {String} name type name + * @returns {Object} decoded value */ -SolidityParam.decodeArray = function (bytes, index) { - index = index || 0; - var offset = getOffset(bytes, index); - var length = parseInt('0x' + bytes.substr(offset * 2, 64)); - return new SolidityParam(bytes.substr(offset * 2, (length + 1) * 64), 0); +SolidityType.prototype.decode = function (bytes, offset, name) { + var self = this; + + if (this.isDynamicArray(name)) { + + return (function () { + var arrayOffset = parseInt('0x' + bytes.substr(offset * 2, 64)); // in bytes + var length = parseInt('0x' + bytes.substr(arrayOffset * 2, 64)); // in int + var arrayStart = arrayOffset + 32; // array starts after length; // in bytes + + var nestedName = self.nestedName(name); + var nestedStaticPartLength = self.staticPartLength(nestedName); // in bytes + var result = []; + + for (var i = 0; i < length * nestedStaticPartLength; i += nestedStaticPartLength) { + result.push(self.decode(bytes, arrayStart + i, nestedName)); + } + + return result; + })(); + + } else if (this.isStaticArray(name)) { + + return (function () { + var length = self.staticArrayLength(name); // in int + var arrayStart = offset; // in bytes + + var nestedName = self.nestedName(name); + var nestedStaticPartLength = self.staticPartLength(nestedName); // in bytes + var result = []; + + for (var i = 0; i < length * nestedStaticPartLength; i += nestedStaticPartLength) { + result.push(self.decode(bytes, arrayStart + i, nestedName)); + } + + return result; + })(); + } else if (this.isDynamicType(name)) { + + return (function () { + var dynamicOffset = parseInt('0x' + bytes.substr(offset * 2, 64)); // in bytes + var length = parseInt('0x' + bytes.substr(dynamicOffset * 2, 64)); // in bytes + var roundedLength = Math.floor((length + 31) / 32); // in int + + return self._outputFormatter(new SolidityParam(bytes.substr(dynamicOffset * 2, ( 1 + roundedLength) * 64), 0)); + })(); + } + + var length = this.staticPartLength(name); + return this._outputFormatter(new SolidityParam(bytes.substr(offset * 2, length * 2))); }; -module.exports = SolidityParam; +module.exports = SolidityType; + +},{"./formatters":9,"./param":11}],15:[function(require,module,exports){ +var f = require('./formatters'); +var SolidityType = require('./type'); + +/** + * SolidityTypeUInt is a prootype that represents uint type + * It matches: + * uint + * uint[] + * uint[4] + * uint[][] + * uint[3][] + * uint[][6][], ... + * uint32 + * uint64[] + * uint8[4] + * uint256[][] + * uint[3][] + * uint64[][6][], ... + */ +var SolidityTypeUInt = function () { + this._inputFormatter = f.formatInputInt; + this._outputFormatter = f.formatOutputInt; +}; + +SolidityTypeUInt.prototype = new SolidityType({}); +SolidityTypeUInt.prototype.constructor = SolidityTypeUInt; + +SolidityTypeUInt.prototype.isType = function (name) { + return !!name.match(/^uint([0-9]*)?(\[([0-9]*)\])*$/); +}; + +SolidityTypeUInt.prototype.staticPartLength = function (name) { + return 32 * this.staticArrayLength(name); +}; + +module.exports = SolidityTypeUInt; + +},{"./formatters":9,"./type":14}],16:[function(require,module,exports){ +var f = require('./formatters'); +var SolidityType = require('./type'); + +/** + * SolidityTypeUReal is a prootype that represents ureal type + * It matches: + * ureal + * ureal[] + * ureal[4] + * ureal[][] + * ureal[3][] + * ureal[][6][], ... + * ureal32 + * ureal64[] + * ureal8[4] + * ureal256[][] + * ureal[3][] + * ureal64[][6][], ... + */ +var SolidityTypeUReal = function () { + this._inputFormatter = f.formatInputReal; + this._outputFormatter = f.formatOutputUReal; +}; + +SolidityTypeUReal.prototype = new SolidityType({}); +SolidityTypeUReal.prototype.constructor = SolidityTypeUReal; + +SolidityTypeUReal.prototype.isType = function (name) { + return !!name.match(/^ureal([0-9]*)?(\[([0-9]*)\])*$/); +}; + +SolidityTypeUReal.prototype.staticPartLength = function (name) { + return 32 * this.staticArrayLength(name); +}; +module.exports = SolidityTypeUReal; -},{"../utils/utils":7}],4:[function(require,module,exports){ +},{"./formatters":9,"./type":14}],17:[function(require,module,exports){ 'use strict'; // go env doesn't have and need XMLHttpRequest @@ -783,7 +1762,7 @@ if (typeof XMLHttpRequest === 'undefined') { } -},{}],5:[function(require,module,exports){ +},{}],18:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -819,6 +1798,7 @@ if (typeof XMLHttpRequest === 'undefined') { * @constructor */ + /// required to define ETH_BIGNUMBER_ROUNDING_MODE var BigNumber = require('bignumber.js'); @@ -863,7 +1843,7 @@ module.exports = { }; -},{"bignumber.js":"bignumber.js"}],6:[function(require,module,exports){ +},{"bignumber.js":"bignumber.js"}],19:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -886,6 +1866,7 @@ module.exports = { * @date 2015 */ + var utils = require('./utils'); var sha3 = require('crypto-js/sha3'); @@ -904,7 +1885,7 @@ module.exports = function (str, isNew) { }; -},{"./utils":7,"crypto-js/sha3":34}],7:[function(require,module,exports){ +},{"./utils":20,"crypto-js/sha3":47}],20:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -940,6 +1921,7 @@ module.exports = function (str, isNew) { * @constructor */ + var BigNumber = require('bignumber.js'); var unitMap = { @@ -1015,7 +1997,7 @@ var toAscii = function(hex) { str += String.fromCharCode(code); } - return decodeURIComponent(escape(str)); + return decodeURIComponent(escape(str)); // jshint ignore:line }; /** @@ -1026,7 +2008,7 @@ var toAscii = function(hex) { * @returns {String} hex representation of input string */ var toHexNative = function(str) { - str = unescape(encodeURIComponent(str)); + str = unescape(encodeURIComponent(str)); // jshint ignore:line var hex = ""; for(var i = 0; i < str.length; i++) { var n = str.charCodeAt(i).toString(16); @@ -1377,18 +2359,6 @@ var isJson = function (str) { } }; -/** - * This method should be called to check if string is valid ethereum IBAN number - * Supports direct and indirect IBANs - * - * @method isIBAN - * @param {String} - * @return {Boolean} - */ -var isIBAN = function (iban) { - return /^XE[0-9]{2}(ETH[0-9A-Z]{13}|[0-9A-Z]{30})$/.test(iban); -}; - module.exports = { padLeft: padLeft, padRight: padRight, @@ -1413,17 +2383,16 @@ module.exports = { isObject: isObject, isBoolean: isBoolean, isArray: isArray, - isJson: isJson, - isIBAN: isIBAN + isJson: isJson }; -},{"bignumber.js":"bignumber.js"}],8:[function(require,module,exports){ +},{"bignumber.js":"bignumber.js"}],21:[function(require,module,exports){ module.exports={ - "version": "0.9.1" + "version": "0.12.1" } -},{}],9:[function(require,module,exports){ +},{}],22:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1451,11 +2420,11 @@ module.exports={ */ var version = require('./version.json'); -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 net = require('./web3/methods/net'); +var eth = require('./web3/methods/eth'); +var db = require('./web3/methods/db'); +var shh = require('./web3/methods/shh'); +var watches = require('./web3/methods/watches'); var Filter = require('./web3/filter'); var utils = require('./utils/utils'); var formatters = require('./web3/formatters'); @@ -1600,7 +2569,7 @@ setupMethods(web3.shh, shh.methods); module.exports = web3; -},{"./utils/config":5,"./utils/sha3":6,"./utils/utils":7,"./version.json":8,"./web3/batch":11,"./web3/db":13,"./web3/eth":15,"./web3/filter":17,"./web3/formatters":18,"./web3/method":24,"./web3/net":26,"./web3/property":27,"./web3/requestmanager":28,"./web3/shh":29,"./web3/watches":31}],10:[function(require,module,exports){ +},{"./utils/config":18,"./utils/sha3":19,"./utils/utils":20,"./version.json":21,"./web3/batch":24,"./web3/filter":28,"./web3/formatters":29,"./web3/method":35,"./web3/methods/db":36,"./web3/methods/eth":37,"./web3/methods/net":38,"./web3/methods/shh":39,"./web3/methods/watches":40,"./web3/property":42,"./web3/requestmanager":43}],23:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1628,7 +2597,7 @@ var SolidityEvent = require('./event'); var formatters = require('./formatters'); var utils = require('../utils/utils'); var Filter = require('./filter'); -var watches = require('./watches'); +var watches = require('./methods/watches'); var AllSolidityEvents = function (json, address) { this._json = json; @@ -1669,6 +2638,13 @@ AllSolidityEvents.prototype.decode = function (data) { }; AllSolidityEvents.prototype.execute = function (options, callback) { + + if (utils.isFunction(arguments[arguments.length - 1])) { + callback = arguments[arguments.length - 1]; + if(arguments.length === 1) + options = null; + } + var o = this.encode(options); var formatter = this.decode.bind(this); return new Filter(o, watches.eth(), formatter, callback); @@ -1682,7 +2658,7 @@ AllSolidityEvents.prototype.attachToContract = function (contract) { module.exports = AllSolidityEvents; -},{"../utils/sha3":6,"../utils/utils":7,"./event":16,"./filter":17,"./formatters":18,"./watches":31}],11:[function(require,module,exports){ +},{"../utils/sha3":19,"../utils/utils":20,"./event":27,"./filter":28,"./formatters":29,"./methods/watches":40}],24:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1750,7 +2726,7 @@ Batch.prototype.execute = function () { module.exports = Batch; -},{"./errors":14,"./jsonrpc":23,"./requestmanager":28}],12:[function(require,module,exports){ +},{"./errors":26,"./jsonrpc":34,"./requestmanager":43}],25:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -2029,65 +3005,7 @@ var Contract = function (abi, address) { module.exports = contract; -},{"../solidity/coder":1,"../utils/utils":7,"../web3":9,"./allevents":10,"./event":16,"./function":19}],13:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. -*/ -/** @file db.js - * @authors: - * Marek Kotewicz <marek@ethdev.com> - * @date 2015 - */ - -var Method = require('./method'); - -var putString = new Method({ - name: 'putString', - call: 'db_putString', - params: 3 -}); - - -var getString = new Method({ - name: 'getString', - call: 'db_getString', - params: 2 -}); - -var putHex = new Method({ - name: 'putHex', - call: 'db_putHex', - params: 3 -}); - -var getHex = new Method({ - name: 'getHex', - call: 'db_getHex', - params: 2 -}); - -var methods = [ - putString, getString, putHex, getHex -]; - -module.exports = { - methods: methods -}; - -},{"./method":24}],14:[function(require,module,exports){ +},{"../solidity/coder":7,"../utils/utils":20,"../web3":22,"./allevents":23,"./event":27,"./function":30}],26:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -2127,300 +3045,7 @@ module.exports = { }; -},{}],15:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @file eth.js - * @author Marek Kotewicz <marek@ethdev.com> - * @author Fabian Vogelsteller <fabian@ethdev.com> - * @date 2015 - */ - -/** - * Web3 - * - * @module web3 - */ - -/** - * Eth methods and properties - * - * An example method object can look as follows: - * - * { - * name: 'getBlock', - * call: blockCall, - * params: 2, - * 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){ return !!param; } // formats paramter 2 - * ] - * }, - * - * @class [web3] eth - * @constructor - */ - -"use strict"; - -var formatters = require('./formatters'); -var utils = require('../utils/utils'); -var Method = require('./method'); -var Property = require('./property'); - -var blockCall = function (args) { - return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? "eth_getBlockByHash" : "eth_getBlockByNumber"; -}; - -var transactionFromBlockCall = function (args) { - return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getTransactionByBlockHashAndIndex' : 'eth_getTransactionByBlockNumberAndIndex'; -}; - -var uncleCall = function (args) { - return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getUncleByBlockHashAndIndex' : 'eth_getUncleByBlockNumberAndIndex'; -}; - -var getBlockTransactionCountCall = function (args) { - return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getBlockTransactionCountByHash' : 'eth_getBlockTransactionCountByNumber'; -}; - -var uncleCountCall = function (args) { - 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 getBalance = new Method({ - name: 'getBalance', - call: 'eth_getBalance', - params: 2, - inputFormatter: [utils.toAddress, formatters.inputDefaultBlockNumberFormatter], - outputFormatter: formatters.outputBigNumberFormatter -}); - -var getStorageAt = new Method({ - name: 'getStorageAt', - call: 'eth_getStorageAt', - params: 3, - inputFormatter: [null, utils.toHex, formatters.inputDefaultBlockNumberFormatter] -}); - -var getCode = new Method({ - name: 'getCode', - call: 'eth_getCode', - params: 2, - inputFormatter: [utils.toAddress, formatters.inputDefaultBlockNumberFormatter] -}); - -var getBlock = new Method({ - name: 'getBlock', - call: blockCall, - params: 2, - inputFormatter: [formatters.inputBlockNumberFormatter, function (val) { return !!val; }], - outputFormatter: formatters.outputBlockFormatter -}); - -var getUncle = new Method({ - name: 'getUncle', - call: uncleCall, - params: 2, - inputFormatter: [formatters.inputBlockNumberFormatter, utils.toHex], - outputFormatter: formatters.outputBlockFormatter, - -}); - -var getCompilers = new Method({ - name: 'getCompilers', - call: 'eth_getCompilers', - params: 0 -}); - -var getBlockTransactionCount = new Method({ - name: 'getBlockTransactionCount', - call: getBlockTransactionCountCall, - params: 1, - inputFormatter: [formatters.inputBlockNumberFormatter], - outputFormatter: utils.toDecimal -}); - -var getBlockUncleCount = new Method({ - name: 'getBlockUncleCount', - call: uncleCountCall, - params: 1, - inputFormatter: [formatters.inputBlockNumberFormatter], - outputFormatter: utils.toDecimal -}); - -var getTransaction = new Method({ - name: 'getTransaction', - call: 'eth_getTransactionByHash', - params: 1, - outputFormatter: formatters.outputTransactionFormatter -}); - -var getTransactionFromBlock = new Method({ - name: 'getTransactionFromBlock', - call: transactionFromBlockCall, - params: 2, - inputFormatter: [formatters.inputBlockNumberFormatter, utils.toHex], - outputFormatter: formatters.outputTransactionFormatter -}); - -var getTransactionReceipt = new Method({ - name: 'getTransactionReceipt', - call: 'eth_getTransactionReceipt', - params: 1, - outputFormatter: formatters.outputTransactionReceiptFormatter -}); - -var getTransactionCount = new Method({ - name: 'getTransactionCount', - call: 'eth_getTransactionCount', - params: 2, - inputFormatter: [null, formatters.inputDefaultBlockNumberFormatter], - outputFormatter: utils.toDecimal -}); - -var sendRawTransaction = new Method({ - name: 'sendRawTransaction', - call: 'eth_sendRawTransaction', - params: 1, - inputFormatter: [null] -}); - -var sendTransaction = new Method({ - name: 'sendTransaction', - call: 'eth_sendTransaction', - params: 1, - inputFormatter: [formatters.inputTransactionFormatter] -}); - -var call = new Method({ - name: 'call', - call: 'eth_call', - params: 2, - inputFormatter: [formatters.inputTransactionFormatter, formatters.inputDefaultBlockNumberFormatter] -}); - -var estimateGas = new Method({ - name: 'estimateGas', - call: 'eth_estimateGas', - params: 1, - inputFormatter: [formatters.inputTransactionFormatter], - outputFormatter: utils.toDecimal -}); - -var compileSolidity = new Method({ - name: 'compile.solidity', - call: 'eth_compileSolidity', - params: 1 -}); - -var compileLLL = new Method({ - name: 'compile.lll', - call: 'eth_compileLLL', - params: 1 -}); - -var compileSerpent = new Method({ - name: 'compile.serpent', - call: 'eth_compileSerpent', - params: 1 -}); - -var submitWork = new Method({ - name: 'submitWork', - call: 'eth_submitWork', - params: 3 -}); - -var getWork = new Method({ - name: 'getWork', - call: 'eth_getWork', - params: 0 -}); - -var methods = [ - getBalance, - getStorageAt, - getCode, - getBlock, - getUncle, - getCompilers, - getBlockTransactionCount, - getBlockUncleCount, - getTransaction, - getTransactionFromBlock, - getTransactionReceipt, - getTransactionCount, - call, - estimateGas, - sendRawTransaction, - sendTransaction, - compileSolidity, - compileLLL, - compileSerpent, - submitWork, - getWork -]; - -/// @returns an array of objects describing web3.eth api properties - - - -var properties = [ - new Property({ - name: 'coinbase', - getter: 'eth_coinbase' - }), - new Property({ - name: 'mining', - getter: 'eth_mining' - }), - new Property({ - name: 'hashrate', - getter: 'eth_hashrate', - outputFormatter: utils.toDecimal - }), - new Property({ - name: 'gasPrice', - getter: 'eth_gasPrice', - outputFormatter: formatters.outputBigNumberFormatter - }), - new Property({ - name: 'accounts', - getter: 'eth_accounts' - }), - new Property({ - name: 'blockNumber', - getter: 'eth_blockNumber', - outputFormatter: utils.toDecimal - }) -]; - -module.exports = { - methods: methods, - properties: properties -}; - - -},{"../utils/utils":7,"./formatters":18,"./method":24,"./property":27}],16:[function(require,module,exports){ +},{}],27:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -2448,7 +3073,7 @@ var coder = require('../solidity/coder'); var formatters = require('./formatters'); var sha3 = require('../utils/sha3'); var Filter = require('./filter'); -var watches = require('./watches'); +var watches = require('./methods/watches'); /** * This prototype should be used to create event filters @@ -2629,7 +3254,7 @@ SolidityEvent.prototype.attachToContract = function (contract) { module.exports = SolidityEvent; -},{"../solidity/coder":1,"../utils/sha3":6,"../utils/utils":7,"./filter":17,"./formatters":18,"./watches":31}],17:[function(require,module,exports){ +},{"../solidity/coder":7,"../utils/sha3":19,"../utils/utils":20,"./filter":28,"./formatters":29,"./methods/watches":40}],28:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -2795,6 +3420,7 @@ var Filter = function (options, methods, formatter, callback) { } }); + return this; }; Filter.prototype.watch = function (callback) { @@ -2840,7 +3466,7 @@ Filter.prototype.get = function (callback) { module.exports = Filter; -},{"../utils/utils":7,"./formatters":18,"./requestmanager":28}],18:[function(require,module,exports){ +},{"../utils/utils":20,"./formatters":29,"./requestmanager":43}],29:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -2866,6 +3492,7 @@ module.exports = Filter; var utils = require('../utils/utils'); var config = require('../utils/config'); +var Iban = require('./iban'); /** * Should the format output to a big number @@ -2901,6 +3528,34 @@ var inputBlockNumberFormatter = function (blockNumber) { /** * Formats the input of a transaction and converts all values to HEX * + * @method inputCallFormatter + * @param {Object} transaction options + * @returns object +*/ +var inputCallFormatter = function (options){ + + options.from = options.from || config.defaultAccount; + + if (options.from) { + options.from = inputAddressFormatter(options.from); + } + + if (options.to) { // it might be contract creation + options.to = inputAddressFormatter(options.to); + } + + ['gasPrice', 'gas', 'value', 'nonce'].filter(function (key) { + return options[key] !== undefined; + }).forEach(function(key){ + options[key] = utils.fromDecimal(options[key]); + }); + + return options; +}; + +/** + * Formats the input of a transaction and converts all values to HEX + * * @method inputTransactionFormatter * @param {Object} transaction options * @returns object @@ -2908,11 +3563,10 @@ var inputBlockNumberFormatter = function (blockNumber) { var inputTransactionFormatter = function (options){ options.from = options.from || config.defaultAccount; + options.from = inputAddressFormatter(options.from); - // make code -> data - if (options.code) { - options.data = options.code; - delete options.code; + if (options.to) { // it might be contract creation + options.to = inputAddressFormatter(options.to); } ['gasPrice', 'gas', 'value', 'nonce'].filter(function (key) { @@ -3073,10 +3727,24 @@ var outputPostFormatter = function(post){ return post; }; +var inputAddressFormatter = function (address) { + var iban = new Iban(address); + if (iban.isValid() && iban.isDirect()) { + return '0x' + iban.address(); + } else if (utils.isStrictAddress(address)) { + return address; + } else if (utils.isAddress(address)) { + return '0x' + address; + } + throw 'invalid address'; +}; + module.exports = { inputDefaultBlockNumberFormatter: inputDefaultBlockNumberFormatter, inputBlockNumberFormatter: inputBlockNumberFormatter, + inputCallFormatter: inputCallFormatter, inputTransactionFormatter: inputTransactionFormatter, + inputAddressFormatter: inputAddressFormatter, inputPostFormatter: inputPostFormatter, outputBigNumberFormatter: outputBigNumberFormatter, outputTransactionFormatter: outputTransactionFormatter, @@ -3087,7 +3755,7 @@ module.exports = { }; -},{"../utils/config":5,"../utils/utils":7}],19:[function(require,module,exports){ +},{"../utils/config":18,"../utils/utils":20,"./iban":32}],30:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -3324,7 +3992,7 @@ SolidityFunction.prototype.attachToContract = function (contract) { module.exports = SolidityFunction; -},{"../solidity/coder":1,"../utils/sha3":6,"../utils/utils":7,"../web3":9,"./formatters":18}],20:[function(require,module,exports){ +},{"../solidity/coder":7,"../utils/sha3":19,"../utils/utils":20,"../web3":22,"./formatters":29}],31:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -3351,51 +4019,61 @@ module.exports = SolidityFunction; "use strict"; -var XMLHttpRequest = (typeof window !== 'undefined' && window.XMLHttpRequest) ? window.XMLHttpRequest : require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line var errors = require('./errors'); +// workaround to use httpprovider in different envs +var XMLHttpRequest; // jshint ignore: line + +// meteor server environment +if (typeof Meteor !== 'undefined' && Meteor.isServer) { // jshint ignore: line + XMLHttpRequest = Npm.require('xmlhttprequest').XMLHttpRequest; // jshint ignore: line + +// browser +} else if (typeof window !== 'undefined' && window.XMLHttpRequest) { + XMLHttpRequest = window.XMLHttpRequest; // jshint ignore: line + +// node +} else { + XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore: line +} + +/** + * HttpProvider should be used to send rpc calls over http + */ var HttpProvider = function (host) { this.host = host || 'http://localhost:8545'; }; -HttpProvider.prototype.isConnected = function() { +/** + * Should be called to prepare new XMLHttpRequest + * + * @method prepareRequest + * @param {Boolean} true if request should be async + * @return {XMLHttpRequest} object + */ +HttpProvider.prototype.prepareRequest = function (async) { var request = new XMLHttpRequest(); - - request.open('POST', this.host, false); - request.setRequestHeader('Content-type','application/json'); - - try { - request.send(JSON.stringify({ - id: 9999999999, - jsonrpc: '2.0', - method: 'net_listening', - params: [] - })); - return true; - } catch(e) { - return false; - } + request.open('POST', this.host, async); + request.setRequestHeader('Content-Type','application/json'); + return request; }; +/** + * Should be called to make sync request + * + * @method send + * @param {Object} payload + * @return {Object} result + */ HttpProvider.prototype.send = function (payload) { - var request = new XMLHttpRequest(); + var request = this.prepareRequest(false); - request.open('POST', this.host, false); - request.setRequestHeader('Content-type','application/json'); - try { request.send(JSON.stringify(payload)); } catch(error) { throw errors.InvalidConnection(this.host); } - - // check request.status - // TODO: throw an error here! it cannot silently fail!!! - //if (request.status !== 200) { - //return; - //} - var result = request.responseText; try { @@ -3407,8 +4085,16 @@ HttpProvider.prototype.send = function (payload) { return result; }; +/** + * Should be used to make async request + * + * @method sendAsync + * @param {Object} payload + * @param {Function} callback triggered on end with (err, result) + */ HttpProvider.prototype.sendAsync = function (payload, callback) { - var request = new XMLHttpRequest(); + var request = this.prepareRequest(true); + request.onreadystatechange = function() { if (request.readyState === 4) { var result = request.responseText; @@ -3423,9 +4109,6 @@ HttpProvider.prototype.sendAsync = function (payload, callback) { callback(error, result); } }; - - request.open('POST', this.host, true); - request.setRequestHeader('Content-type','application/json'); try { request.send(JSON.stringify(payload)); @@ -3434,10 +4117,30 @@ HttpProvider.prototype.sendAsync = function (payload, callback) { } }; +/** + * Synchronously tries to make Http request + * + * @method isConnected + * @return {Boolean} returns true if request haven't failed. Otherwise false + */ +HttpProvider.prototype.isConnected = function() { + try { + this.send({ + id: 9999999999, + jsonrpc: '2.0', + method: 'net_listening', + params: [] + }); + return true; + } catch(e) { + return false; + } +}; + module.exports = HttpProvider; -},{"./errors":14,"xmlhttprequest":4}],21:[function(require,module,exports){ +},{"./errors":26,"xmlhttprequest":17}],32:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -3455,30 +4158,139 @@ module.exports = HttpProvider; along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. */ /** - * @file icap.js + * @file iban.js * @author Marek Kotewicz <marek@ethdev.com> * @date 2015 */ -var utils = require('../utils/utils'); +var BigNumber = require('bignumber.js'); + +var padLeft = function (string, bytes) { + var result = string; + while (result.length < bytes * 2) { + result = '00' + result; + } + return result; +}; /** - * This prototype should be used to extract necessary information from iban address + * Prepare an IBAN for mod 97 computation by moving the first 4 chars to the end and transforming the letters to + * numbers (A = 10, B = 11, ..., Z = 35), as specified in ISO13616. + * + * @method iso13616Prepare + * @param {String} iban the IBAN + * @returns {String} the prepared IBAN + */ +var iso13616Prepare = function (iban) { + var A = 'A'.charCodeAt(0); + var Z = 'Z'.charCodeAt(0); + + iban = iban.toUpperCase(); + iban = iban.substr(4) + iban.substr(0,4); + + return iban.split('').map(function(n){ + var code = n.charCodeAt(0); + if (code >= A && code <= Z){ + // A = 10, B = 11, ... Z = 35 + return code - A + 10; + } else { + return n; + } + }).join(''); +}; + +/** + * Calculates the MOD 97 10 of the passed IBAN as specified in ISO7064. + * + * @method mod9710 + * @param {String} iban + * @returns {Number} + */ +var mod9710 = function (iban) { + var remainder = iban, + block; + + while (remainder.length > 2){ + block = remainder.slice(0, 9); + remainder = parseInt(block, 10) % 97 + remainder.slice(block.length); + } + + return parseInt(remainder, 10) % 97; +}; + +/** + * This prototype should be used to create iban object from iban correct string * * @param {String} iban */ -var ICAP = function (iban) { +var Iban = function (iban) { this._iban = iban; }; /** - * Should be called to check if icap is correct + * This method should be used to create iban object from ethereum address + * + * @method fromAddress + * @param {String} address + * @return {Iban} the IBAN object + */ +Iban.fromAddress = function (address) { + var asBn = new BigNumber(address, 16); + var base36 = asBn.toString(36); + var padded = padLeft(base36, 15); + return Iban.fromBban(padded.toUpperCase()); +}; + +/** + * Convert the passed BBAN to an IBAN for this country specification. + * Please note that <i>"generation of the IBAN shall be the exclusive responsibility of the bank/branch servicing the account"</i>. + * This method implements the preferred algorithm described in http://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_IBAN_check_digits + * + * @method fromBban + * @param {String} bban the BBAN to convert to IBAN + * @returns {Iban} the IBAN object + */ +Iban.fromBban = function (bban) { + var countryCode = 'XE'; + + var remainder = mod9710(iso13616Prepare(countryCode + '00' + bban)); + var checkDigit = ('0' + (98 - remainder)).slice(-2); + + return new Iban(countryCode + checkDigit + bban); +}; + +/** + * Should be used to create IBAN object for given institution and identifier + * + * @method createIndirect + * @param {Object} options, required options are "institution" and "identifier" + * @return {Iban} the IBAN object + */ +Iban.createIndirect = function (options) { + return Iban.fromBban('ETH' + options.institution + options.identifier); +}; + +/** + * Thos method should be used to check if given string is valid iban object + * + * @method isValid + * @param {String} iban string + * @return {Boolean} true if it is valid IBAN + */ +Iban.isValid = function (iban) { + var i = new Iban(iban); + return i.isValid(); +}; + +/** + * Should be called to check if iban is correct * * @method isValid * @returns {Boolean} true if it is, otherwise false */ -ICAP.prototype.isValid = function () { - return utils.isIBAN(this._iban); +Iban.prototype.isValid = function () { + return /^XE[0-9]{2}(ETH[0-9A-Z]{13}|[0-9A-Z]{30})$/.test(this._iban) && + mod9710(iso13616Prepare(this._iban)) === 1; }; /** @@ -3487,8 +4299,8 @@ ICAP.prototype.isValid = function () { * @method isDirect * @returns {Boolean} true if it is, otherwise false */ -ICAP.prototype.isDirect = function () { - return this._iban.length === 34; +Iban.prototype.isDirect = function () { + return this._iban.length === 34 || this._iban.length === 35; }; /** @@ -3497,7 +4309,7 @@ ICAP.prototype.isDirect = function () { * @method isIndirect * @returns {Boolean} true if it is, otherwise false */ -ICAP.prototype.isIndirect = function () { +Iban.prototype.isIndirect = function () { return this._iban.length === 20; }; @@ -3508,7 +4320,7 @@ ICAP.prototype.isIndirect = function () { * @method checksum * @returns {String} checksum */ -ICAP.prototype.checksum = function () { +Iban.prototype.checksum = function () { return this._iban.substr(2, 2); }; @@ -3519,7 +4331,7 @@ ICAP.prototype.checksum = function () { * @method institution * @returns {String} institution identifier */ -ICAP.prototype.institution = function () { +Iban.prototype.institution = function () { return this.isIndirect() ? this._iban.substr(7, 4) : ''; }; @@ -3530,7 +4342,7 @@ ICAP.prototype.institution = function () { * @method client * @returns {String} client identifier */ -ICAP.prototype.client = function () { +Iban.prototype.client = function () { return this.isIndirect() ? this._iban.substr(11) : ''; }; @@ -3540,14 +4352,24 @@ ICAP.prototype.client = function () { * @method address * @returns {String} client direct address */ -ICAP.prototype.address = function () { - return this.isDirect() ? this._iban.substr(4) : ''; +Iban.prototype.address = function () { + if (this.isDirect()) { + var base36 = this._iban.substr(4); + var asBn = new BigNumber(base36, 36); + return padLeft(asBn.toString(16), 20); + } + + return ''; }; -module.exports = ICAP; +Iban.prototype.toString = function () { + return this._iban; +}; + +module.exports = Iban; -},{"../utils/utils":7}],22:[function(require,module,exports){ +},{"bignumber.js":"bignumber.js"}],33:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -3575,16 +4397,23 @@ module.exports = ICAP; var utils = require('../utils/utils'); var errors = require('./errors'); -var errorTimeout = '{"jsonrpc": "2.0", "error": {"code": -32603, "message": "IPC Request timed out for method \'__method__\'"}, "id": "__id__"}'; - +var errorTimeout = function (method, id) { + var err = { + "jsonrpc": "2.0", + "error": { + "code": -32603, + "message": "IPC Request timed out for method \'" + method + "\'" + }, + "id": id + }; + return JSON.stringify(err); +}; var IpcProvider = function (path, net) { var _this = this; this.responseCallbacks = {}; this.path = path; - net = net || require('net'); - this.connection = net.connect({path: this.path}); this.connection.on('error', function(e){ @@ -3701,7 +4530,7 @@ Timeout all requests when the end/error event is fired IpcProvider.prototype._timeout = function() { for(var key in this.responseCallbacks) { if(this.responseCallbacks.hasOwnProperty(key)){ - this.responseCallbacks[key](errorTimeout.replace('__id__', key).replace('__method__', this.responseCallbacks[key].method)); + this.responseCallbacks[key](errorTimeout(this.responseCallbacks[key].method, key)); delete this.responseCallbacks[key]; } } @@ -3760,7 +4589,7 @@ IpcProvider.prototype.sendAsync = function (payload, callback) { module.exports = IpcProvider; -},{"../utils/utils":7,"./errors":14,"net":32}],23:[function(require,module,exports){ +},{"../utils/utils":20,"./errors":26}],34:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -3853,7 +4682,7 @@ Jsonrpc.prototype.toBatchPayload = function (messages) { module.exports = Jsonrpc; -},{}],24:[function(require,module,exports){ +},{}],35:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -4027,7 +4856,7 @@ Method.prototype.send = function () { module.exports = Method; -},{"../utils/utils":7,"./errors":14,"./requestmanager":28}],25:[function(require,module,exports){ +},{"../utils/utils":20,"./errors":26,"./requestmanager":43}],36:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -4044,38 +4873,341 @@ module.exports = Method; You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. */ -/** - * @file namereg.js +/** @file db.js + * @authors: + * Marek Kotewicz <marek@ethdev.com> + * @date 2015 + */ + +var Method = require('../method'); + +var putString = new Method({ + name: 'putString', + call: 'db_putString', + params: 3 +}); + + +var getString = new Method({ + name: 'getString', + call: 'db_getString', + params: 2 +}); + +var putHex = new Method({ + name: 'putHex', + call: 'db_putHex', + params: 3 +}); + +var getHex = new Method({ + name: 'getHex', + call: 'db_getHex', + params: 2 +}); + +var methods = [ + putString, getString, putHex, getHex +]; + +module.exports = { + methods: methods +}; + +},{"../method":35}],37:[function(require,module,exports){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @file eth.js * @author Marek Kotewicz <marek@ethdev.com> + * @author Fabian Vogelsteller <fabian@ethdev.com> * @date 2015 */ -var contract = require('./contract'); +/** + * Web3 + * + * @module web3 + */ + +/** + * Eth methods and properties + * + * An example method object can look as follows: + * + * { + * name: 'getBlock', + * call: blockCall, + * params: 2, + * 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){ return !!param; } // formats paramter 2 + * ] + * }, + * + * @class [web3] eth + * @constructor + */ + +"use strict"; + +var formatters = require('../formatters'); +var utils = require('../../utils/utils'); +var Method = require('../method'); +var Property = require('../property'); + +var blockCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? "eth_getBlockByHash" : "eth_getBlockByNumber"; +}; + +var transactionFromBlockCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getTransactionByBlockHashAndIndex' : 'eth_getTransactionByBlockNumberAndIndex'; +}; + +var uncleCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getUncleByBlockHashAndIndex' : 'eth_getUncleByBlockNumberAndIndex'; +}; + +var getBlockTransactionCountCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getBlockTransactionCountByHash' : 'eth_getBlockTransactionCountByNumber'; +}; + +var uncleCountCall = function (args) { + 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 getBalance = new Method({ + name: 'getBalance', + call: 'eth_getBalance', + params: 2, + inputFormatter: [formatters.inputAddressFormatter, formatters.inputDefaultBlockNumberFormatter], + outputFormatter: formatters.outputBigNumberFormatter +}); + +var getStorageAt = new Method({ + name: 'getStorageAt', + call: 'eth_getStorageAt', + params: 3, + inputFormatter: [null, utils.toHex, formatters.inputDefaultBlockNumberFormatter] +}); + +var getCode = new Method({ + name: 'getCode', + call: 'eth_getCode', + params: 2, + inputFormatter: [formatters.inputAddressFormatter, formatters.inputDefaultBlockNumberFormatter] +}); -var address = '0xc6d9d2cd449a754c494264e1809c50e34d64562b'; - -var abi = [ - {"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"name","outputs":[{"name":"o_name","type":"bytes32"}],"type":"function"}, - {"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"}, - {"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"content","outputs":[{"name":"","type":"bytes32"}],"type":"function"}, - {"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"addr","outputs":[{"name":"","type":"address"}],"type":"function"}, - {"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserve","outputs":[],"type":"function"}, - {"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"subRegistrar","outputs":[{"name":"o_subRegistrar","type":"address"}],"type":"function"}, - {"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_newOwner","type":"address"}],"name":"transfer","outputs":[],"type":"function"}, - {"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_registrar","type":"address"}],"name":"setSubRegistrar","outputs":[],"type":"function"}, - {"constant":false,"inputs":[],"name":"Registrar","outputs":[],"type":"function"}, - {"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_a","type":"address"},{"name":"_primary","type":"bool"}],"name":"setAddress","outputs":[],"type":"function"}, - {"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_content","type":"bytes32"}],"name":"setContent","outputs":[],"type":"function"}, - {"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"disown","outputs":[],"type":"function"}, - {"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"register","outputs":[{"name":"","type":"address"}],"type":"function"}, - {"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"}],"name":"Changed","type":"event"}, - {"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"addr","type":"address"}],"name":"PrimaryChanged","type":"event"} +var getBlock = new Method({ + name: 'getBlock', + call: blockCall, + params: 2, + inputFormatter: [formatters.inputBlockNumberFormatter, function (val) { return !!val; }], + outputFormatter: formatters.outputBlockFormatter +}); + +var getUncle = new Method({ + name: 'getUncle', + call: uncleCall, + params: 2, + inputFormatter: [formatters.inputBlockNumberFormatter, utils.toHex], + outputFormatter: formatters.outputBlockFormatter, + +}); + +var getCompilers = new Method({ + name: 'getCompilers', + call: 'eth_getCompilers', + params: 0 +}); + +var getBlockTransactionCount = new Method({ + name: 'getBlockTransactionCount', + call: getBlockTransactionCountCall, + params: 1, + inputFormatter: [formatters.inputBlockNumberFormatter], + outputFormatter: utils.toDecimal +}); + +var getBlockUncleCount = new Method({ + name: 'getBlockUncleCount', + call: uncleCountCall, + params: 1, + inputFormatter: [formatters.inputBlockNumberFormatter], + outputFormatter: utils.toDecimal +}); + +var getTransaction = new Method({ + name: 'getTransaction', + call: 'eth_getTransactionByHash', + params: 1, + outputFormatter: formatters.outputTransactionFormatter +}); + +var getTransactionFromBlock = new Method({ + name: 'getTransactionFromBlock', + call: transactionFromBlockCall, + params: 2, + inputFormatter: [formatters.inputBlockNumberFormatter, utils.toHex], + outputFormatter: formatters.outputTransactionFormatter +}); + +var getTransactionReceipt = new Method({ + name: 'getTransactionReceipt', + call: 'eth_getTransactionReceipt', + params: 1, + outputFormatter: formatters.outputTransactionReceiptFormatter +}); + +var getTransactionCount = new Method({ + name: 'getTransactionCount', + call: 'eth_getTransactionCount', + params: 2, + inputFormatter: [null, formatters.inputDefaultBlockNumberFormatter], + outputFormatter: utils.toDecimal +}); + +var sendRawTransaction = new Method({ + name: 'sendRawTransaction', + call: 'eth_sendRawTransaction', + params: 1, + inputFormatter: [null] +}); + +var sendTransaction = new Method({ + name: 'sendTransaction', + call: 'eth_sendTransaction', + params: 1, + inputFormatter: [formatters.inputTransactionFormatter] +}); + +var call = new Method({ + name: 'call', + call: 'eth_call', + params: 2, + inputFormatter: [formatters.inputCallFormatter, formatters.inputDefaultBlockNumberFormatter] +}); + +var estimateGas = new Method({ + name: 'estimateGas', + call: 'eth_estimateGas', + params: 1, + inputFormatter: [formatters.inputCallFormatter], + outputFormatter: utils.toDecimal +}); + +var compileSolidity = new Method({ + name: 'compile.solidity', + call: 'eth_compileSolidity', + params: 1 +}); + +var compileLLL = new Method({ + name: 'compile.lll', + call: 'eth_compileLLL', + params: 1 +}); + +var compileSerpent = new Method({ + name: 'compile.serpent', + call: 'eth_compileSerpent', + params: 1 +}); + +var submitWork = new Method({ + name: 'submitWork', + call: 'eth_submitWork', + params: 3 +}); + +var getWork = new Method({ + name: 'getWork', + call: 'eth_getWork', + params: 0 +}); + +var methods = [ + getBalance, + getStorageAt, + getCode, + getBlock, + getUncle, + getCompilers, + getBlockTransactionCount, + getBlockUncleCount, + getTransaction, + getTransactionFromBlock, + getTransactionReceipt, + getTransactionCount, + call, + estimateGas, + sendRawTransaction, + sendTransaction, + compileSolidity, + compileLLL, + compileSerpent, + submitWork, + getWork ]; -module.exports = contract(abi).at(address); +/// @returns an array of objects describing web3.eth api properties + -},{"./contract":12}],26:[function(require,module,exports){ +var properties = [ + new Property({ + name: 'coinbase', + getter: 'eth_coinbase' + }), + new Property({ + name: 'mining', + getter: 'eth_mining' + }), + new Property({ + name: 'hashrate', + getter: 'eth_hashrate', + outputFormatter: utils.toDecimal + }), + new Property({ + name: 'gasPrice', + getter: 'eth_gasPrice', + outputFormatter: formatters.outputBigNumberFormatter + }), + new Property({ + name: 'accounts', + getter: 'eth_accounts' + }), + new Property({ + name: 'blockNumber', + getter: 'eth_blockNumber', + outputFormatter: utils.toDecimal + }) +]; + +module.exports = { + methods: methods, + properties: properties +}; + + +},{"../../utils/utils":20,"../formatters":29,"../method":35,"../property":42}],38:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -4098,8 +5230,8 @@ module.exports = contract(abi).at(address); * @date 2015 */ -var utils = require('../utils/utils'); -var Property = require('./property'); +var utils = require('../../utils/utils'); +var Property = require('../property'); /// @returns an array of objects describing web3.eth api methods var methods = [ @@ -4125,7 +5257,229 @@ module.exports = { }; -},{"../utils/utils":7,"./property":27}],27:[function(require,module,exports){ +},{"../../utils/utils":20,"../property":42}],39:[function(require,module,exports){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. +*/ +/** @file shh.js + * @authors: + * Marek Kotewicz <marek@ethdev.com> + * @date 2015 + */ + +var Method = require('../method'); +var formatters = require('../formatters'); + +var post = new Method({ + name: 'post', + call: 'shh_post', + params: 1, + inputFormatter: [formatters.inputPostFormatter] +}); + +var newIdentity = new Method({ + name: 'newIdentity', + call: 'shh_newIdentity', + params: 0 +}); + +var hasIdentity = new Method({ + name: 'hasIdentity', + call: 'shh_hasIdentity', + params: 1 +}); + +var newGroup = new Method({ + name: 'newGroup', + call: 'shh_newGroup', + params: 0 +}); + +var addToGroup = new Method({ + name: 'addToGroup', + call: 'shh_addToGroup', + params: 0 +}); + +var methods = [ + post, + newIdentity, + hasIdentity, + newGroup, + addToGroup +]; + +module.exports = { + methods: methods +}; + + +},{"../formatters":29,"../method":35}],40:[function(require,module,exports){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. +*/ +/** @file watches.js + * @authors: + * Marek Kotewicz <marek@ethdev.com> + * @date 2015 + */ + +var Method = require('../method'); + +/// @returns an array of objects describing web3.eth.filter api methods +var eth = function () { + var newFilterCall = function (args) { + var type = args[0]; + + switch(type) { + case 'latest': + args.shift(); + this.params = 0; + return 'eth_newBlockFilter'; + case 'pending': + args.shift(); + this.params = 0; + return 'eth_newPendingTransactionFilter'; + default: + return 'eth_newFilter'; + } + }; + + var newFilter = new Method({ + name: 'newFilter', + call: newFilterCall, + params: 1 + }); + + var uninstallFilter = new Method({ + name: 'uninstallFilter', + call: 'eth_uninstallFilter', + params: 1 + }); + + var getLogs = new Method({ + name: 'getLogs', + call: 'eth_getFilterLogs', + params: 1 + }); + + var poll = new Method({ + name: 'poll', + call: 'eth_getFilterChanges', + params: 1 + }); + + return [ + newFilter, + uninstallFilter, + getLogs, + poll + ]; +}; + +/// @returns an array of objects describing web3.shh.watch api methods +var shh = function () { + var newFilter = new Method({ + name: 'newFilter', + call: 'shh_newFilter', + params: 1 + }); + + var uninstallFilter = new Method({ + name: 'uninstallFilter', + call: 'shh_uninstallFilter', + params: 1 + }); + + var getLogs = new Method({ + name: 'getLogs', + call: 'shh_getMessages', + params: 1 + }); + + var poll = new Method({ + name: 'poll', + call: 'shh_getFilterChanges', + params: 1 + }); + + return [ + newFilter, + uninstallFilter, + getLogs, + poll + ]; +}; + +module.exports = { + eth: eth, + shh: shh +}; + + +},{"../method":35}],41:[function(require,module,exports){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @file namereg.js + * @author Marek Kotewicz <marek@ethdev.com> + * @date 2015 + */ + +var contract = require('./contract'); +var globalRegistrarAbi = require('../contracts/GlobalRegistrar.json'); +var icapRegistrarAbi= require('../contracts/ICAPRegistrar.json'); + +var globalNameregAddress = '0xc6d9d2cd449a754c494264e1809c50e34d64562b'; +var ibanNameregAddress = '0xa1a111bc074c9cfa781f0c38e63bd51c91b8af00'; + +module.exports = { + namereg: contract(globalRegistrarAbi).at(globalNameregAddress), + ibanNamereg: contract(icapRegistrarAbi).at(ibanNameregAddress) +}; + + +},{"../contracts/GlobalRegistrar.json":1,"../contracts/ICAPRegistrar.json":2,"./contract":25}],42:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -4277,7 +5631,7 @@ Property.prototype.request = function () { module.exports = Property; -},{"../utils/utils":7,"./requestmanager":28}],28:[function(require,module,exports){ +},{"../utils/utils":20,"./requestmanager":43}],43:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -4427,8 +5781,6 @@ RequestManager.prototype.setProvider = function (p) { } }; -/*jshint maxparams:4 */ - /** * Should be used to start polling * @@ -4441,9 +5793,8 @@ RequestManager.prototype.setProvider = function (p) { * @todo cleanup number of params */ RequestManager.prototype.startPolling = function (data, pollId, callback, uninstall) { - this.polls['poll_'+ pollId] = {data: data, id: pollId, callback: callback, uninstall: uninstall}; + this.polls[pollId] = {data: data, id: pollId, callback: callback, uninstall: uninstall}; }; -/*jshint maxparams:3 */ /** * Should be used to stop polling for filter with given id @@ -4452,7 +5803,7 @@ RequestManager.prototype.startPolling = function (data, pollId, callback, uninst * @param {Number} pollId */ RequestManager.prototype.stopPolling = function (pollId) { - delete this.polls['poll_'+ pollId]; + delete this.polls[pollId]; }; /** @@ -4542,77 +5893,7 @@ RequestManager.prototype.poll = function () { module.exports = RequestManager; -},{"../utils/config":5,"../utils/utils":7,"./errors":14,"./jsonrpc":23}],29:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. -*/ -/** @file shh.js - * @authors: - * Marek Kotewicz <marek@ethdev.com> - * @date 2015 - */ - -var Method = require('./method'); -var formatters = require('./formatters'); - -var post = new Method({ - name: 'post', - call: 'shh_post', - params: 1, - inputFormatter: [formatters.inputPostFormatter] -}); - -var newIdentity = new Method({ - name: 'newIdentity', - call: 'shh_newIdentity', - params: 0 -}); - -var hasIdentity = new Method({ - name: 'hasIdentity', - call: 'shh_hasIdentity', - params: 1 -}); - -var newGroup = new Method({ - name: 'newGroup', - call: 'shh_newGroup', - params: 0 -}); - -var addToGroup = new Method({ - name: 'addToGroup', - call: 'shh_addToGroup', - params: 0 -}); - -var methods = [ - post, - newIdentity, - hasIdentity, - newGroup, - addToGroup -]; - -module.exports = { - methods: methods -}; - - -},{"./formatters":18,"./method":24}],30:[function(require,module,exports){ +},{"../utils/config":18,"../utils/utils":20,"./errors":26,"./jsonrpc":34}],44:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -4636,36 +5917,37 @@ module.exports = { */ var web3 = require('../web3'); -var ICAP = require('./icap'); -var namereg = require('./namereg'); +var Iban = require('./iban'); +var namereg = require('./namereg').ibanNamereg; var contract = require('./contract'); +var exchangeAbi = require('../contracts/SmartExchange.json'); /** - * Should be used to make ICAP transfer + * Should be used to make Iban transfer * * @method transfer - * @param {String} iban number - * @param {String} from (address) + * @param {String} from + * @param {String} to iban * @param {Value} value to be tranfered * @param {Function} callback, callback */ -var transfer = function (from, iban, value, callback) { - var icap = new ICAP(iban); - if (!icap.isValid()) { +var transfer = function (from, to, value, callback) { + var iban = new Iban(to); + if (!iban.isValid()) { throw new Error('invalid iban address'); } - if (icap.isDirect()) { - return transferToAddress(from, icap.address(), value, callback); + if (iban.isDirect()) { + return transferToAddress(from, iban.address(), value, callback); } if (!callback) { - var address = namereg.addr(icap.institution()); - return deposit(from, address, value, icap.client()); + var address = namereg.addr(iban.institution()); + return deposit(from, address, value, iban.client()); } - namereg.addr(icap.insitution(), function (err, address) { - return deposit(from, address, value, icap.client(), callback); + namereg.addr(iban.institution(), function (err, address) { + return deposit(from, address, value, iban.client(), callback); }); }; @@ -4674,14 +5956,14 @@ var transfer = function (from, iban, value, callback) { * Should be used to transfer funds to certain address * * @method transferToAddress - * @param {String} address - * @param {String} from (address) + * @param {String} from + * @param {String} to * @param {Value} value to be tranfered * @param {Function} callback, callback */ -var transferToAddress = function (from, address, value, callback) { +var transferToAddress = function (from, to, value, callback) { return web3.eth.sendTransaction({ - address: address, + address: to, from: from, value: value }, callback); @@ -4691,15 +5973,15 @@ var transferToAddress = function (from, address, value, callback) { * Should be used to deposit funds to generic Exchange contract (must implement deposit(bytes32) method!) * * @method deposit - * @param {String} address - * @param {String} from (address) - * @param {Value} value to be tranfered + * @param {String} from + * @param {String} to + * @param {Value} value to be transfered * @param {String} client unique identifier * @param {Function} callback, callback */ -var deposit = function (from, address, value, client, callback) { - var abi = [{"constant":false,"inputs":[{"name":"name","type":"bytes32"}],"name":"deposit","outputs":[],"type":"function"}]; - return contract(abi).at(address).deposit(client, { +var deposit = function (from, to, value, client, callback) { + var abi = exchangeAbi; + return contract(abi).at(to).deposit(client, { from: from, value: value }, callback); @@ -4708,125 +5990,9 @@ var deposit = function (from, address, value, client, callback) { module.exports = transfer; -},{"../web3":9,"./contract":12,"./icap":21,"./namereg":25}],31:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. -*/ -/** @file watches.js - * @authors: - * Marek Kotewicz <marek@ethdev.com> - * @date 2015 - */ - -var Method = require('./method'); - -/// @returns an array of objects describing web3.eth.filter api methods -var eth = function () { - var newFilterCall = function (args) { - var type = args[0]; - - switch(type) { - case 'latest': - args.shift(); - this.params = 0; - return 'eth_newBlockFilter'; - case 'pending': - args.shift(); - this.params = 0; - return 'eth_newPendingTransactionFilter'; - default: - return 'eth_newFilter'; - } - }; - - var newFilter = new Method({ - name: 'newFilter', - call: newFilterCall, - params: 1 - }); - - var uninstallFilter = new Method({ - name: 'uninstallFilter', - call: 'eth_uninstallFilter', - params: 1 - }); - - var getLogs = new Method({ - name: 'getLogs', - call: 'eth_getFilterLogs', - params: 1 - }); - - var poll = new Method({ - name: 'poll', - call: 'eth_getFilterChanges', - params: 1 - }); - - return [ - newFilter, - uninstallFilter, - getLogs, - poll - ]; -}; - -/// @returns an array of objects describing web3.shh.watch api methods -var shh = function () { - var newFilter = new Method({ - name: 'newFilter', - call: 'shh_newFilter', - params: 1 - }); - - var uninstallFilter = new Method({ - name: 'uninstallFilter', - call: 'shh_uninstallFilter', - params: 1 - }); - - var getLogs = new Method({ - name: 'getLogs', - call: 'shh_getMessages', - params: 1 - }); - - var poll = new Method({ - name: 'poll', - call: 'shh_getFilterChanges', - params: 1 - }); - - return [ - newFilter, - uninstallFilter, - getLogs, - poll - ]; -}; - -module.exports = { - eth: eth, - shh: shh -}; - - -},{"./method":24}],32:[function(require,module,exports){ +},{"../contracts/SmartExchange.json":3,"../web3":22,"./contract":25,"./iban":32,"./namereg":41}],45:[function(require,module,exports){ -},{}],33:[function(require,module,exports){ +},{}],46:[function(require,module,exports){ ;(function (root, factory) { if (typeof exports === "object") { // CommonJS @@ -5569,7 +6735,7 @@ module.exports = { return CryptoJS; })); -},{}],34:[function(require,module,exports){ +},{}],47:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -5893,7 +7059,7 @@ module.exports = { return CryptoJS.SHA3; })); -},{"./core":33,"./x64-core":35}],35:[function(require,module,exports){ +},{"./core":46,"./x64-core":48}],48:[function(require,module,exports){ ;(function (root, factory) { if (typeof exports === "object") { // CommonJS @@ -6198,7 +7364,7 @@ module.exports = { return CryptoJS; })); -},{"./core":33}],"bignumber.js":[function(require,module,exports){ +},{"./core":46}],"bignumber.js":[function(require,module,exports){ 'use strict'; module.exports = BigNumber; // jshint ignore:line @@ -6206,13 +7372,16 @@ module.exports = BigNumber; // jshint ignore:line },{}],"web3":[function(require,module,exports){ var web3 = require('./lib/web3'); +var namereg = require('./lib/web3/namereg'); web3.providers.HttpProvider = require('./lib/web3/httpprovider'); web3.providers.IpcProvider = require('./lib/web3/ipcprovider'); web3.eth.contract = require('./lib/web3/contract'); -web3.eth.namereg = require('./lib/web3/namereg'); +web3.eth.namereg = namereg.namereg; +web3.eth.ibanNamereg = namereg.ibanNamereg; web3.eth.sendIBANTransaction = require('./lib/web3/transfer'); +web3.eth.iban = require('./lib/web3/iban'); // dont override global variable if (typeof window !== 'undefined' && typeof window.web3 === 'undefined') { @@ -6222,6 +7391,6 @@ if (typeof window !== 'undefined' && typeof window.web3 === 'undefined') { module.exports = web3; -},{"./lib/web3":9,"./lib/web3/contract":12,"./lib/web3/httpprovider":20,"./lib/web3/ipcprovider":22,"./lib/web3/namereg":25,"./lib/web3/transfer":30}]},{},["web3"]) +},{"./lib/web3":22,"./lib/web3/contract":25,"./lib/web3/httpprovider":31,"./lib/web3/iban":32,"./lib/web3/ipcprovider":33,"./lib/web3/namereg":41,"./lib/web3/transfer":44}]},{},["web3"]) //# sourceMappingURL=web3-light.js.map ` diff --git a/p2p/discover/table.go b/p2p/discover/table.go index 67f7ec46f..b077f010c 100644 --- a/p2p/discover/table.go +++ b/p2p/discover/table.go @@ -78,9 +78,8 @@ type transport interface { close() } -// bucket contains nodes, ordered by their last activity. -// the entry that was most recently active is the last element -// in entries. +// bucket contains nodes, ordered by their last activity. the entry +// that was most recently active is the first element in entries. type bucket struct { lastLookup time.Time entries []*Node @@ -235,7 +234,7 @@ func (tab *Table) Lookup(targetID NodeID) []*Node { if fails >= maxFindnodeFailures { glog.V(logger.Detail).Infof("Evacuating node %x: %d findnode failures", n.ID[:8], fails) - tab.del(n) + tab.delete(n) } } reply <- tab.bondall(r) @@ -401,15 +400,11 @@ func (tab *Table) bond(pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16 node = w.n } } - // Even if bonding temporarily failed, give the node a chance if node != nil { - tab.mutex.Lock() - defer tab.mutex.Unlock() - - b := tab.buckets[logdist(tab.self.sha, node.sha)] - if !b.bump(node) { - tab.pingreplace(node, b) - } + // Add the node to the table even if the bonding ping/pong + // fails. It will be relaced quickly if it continues to be + // unresponsive. + tab.add(node) tab.db.updateFindFails(id, 0) } return node, result @@ -420,7 +415,7 @@ func (tab *Table) pingpong(w *bondproc, pinged bool, id NodeID, addr *net.UDPAdd <-tab.bondslots defer func() { tab.bondslots <- struct{}{} }() - // Ping the remote side and wait for a pong + // Ping the remote side and wait for a pong. if w.err = tab.ping(id, addr); w.err != nil { close(w.done) return @@ -431,33 +426,14 @@ func (tab *Table) pingpong(w *bondproc, pinged bool, id NodeID, addr *net.UDPAdd // waitping will simply time out. tab.net.waitping(id) } - // Bonding succeeded, update the node database + // Bonding succeeded, update the node database. w.n = newNode(id, addr.IP, uint16(addr.Port), tcpPort) tab.db.updateNode(w.n) close(w.done) } -func (tab *Table) pingreplace(new *Node, b *bucket) { - if len(b.entries) == bucketSize { - oldest := b.entries[bucketSize-1] - if err := tab.ping(oldest.ID, oldest.addr()); err == nil { - // The node responded, we don't need to replace it. - return - } - } else { - // Add a slot at the end so the last entry doesn't - // fall off when adding the new node. - b.entries = append(b.entries, nil) - } - copy(b.entries[1:], b.entries) - b.entries[0] = new - if tab.nodeAddedHook != nil { - tab.nodeAddedHook(new) - } -} - -// ping a remote endpoint and wait for a reply, also updating the node database -// accordingly. +// ping a remote endpoint and wait for a reply, also updating the node +// database accordingly. func (tab *Table) ping(id NodeID, addr *net.UDPAddr) error { // Update the last ping and send the message tab.db.updateLastPing(id, time.Now()) @@ -467,24 +443,53 @@ func (tab *Table) ping(id NodeID, addr *net.UDPAddr) error { // Pong received, update the database and return tab.db.updateLastPong(id, time.Now()) tab.db.ensureExpirer() - return nil } -// add puts the entries into the table if their corresponding -// bucket is not full. The caller must hold tab.mutex. -func (tab *Table) add(entries []*Node) { +// add attempts to add the given node its corresponding bucket. If the +// bucket has space available, adding the node succeeds immediately. +// Otherwise, the node is added if the least recently active node in +// the bucket does not respond to a ping packet. +// +// The caller must not hold tab.mutex. +func (tab *Table) add(new *Node) { + b := tab.buckets[logdist(tab.self.sha, new.sha)] + tab.mutex.Lock() + if b.bump(new) { + tab.mutex.Unlock() + return + } + var oldest *Node + if len(b.entries) == bucketSize { + oldest = b.entries[bucketSize-1] + // Let go of the mutex so other goroutines can access + // the table while we ping the least recently active node. + tab.mutex.Unlock() + if err := tab.ping(oldest.ID, oldest.addr()); err == nil { + // The node responded, don't replace it. + return + } + tab.mutex.Lock() + } + added := b.replace(new, oldest) + tab.mutex.Unlock() + if added && tab.nodeAddedHook != nil { + tab.nodeAddedHook(new) + } +} + +// stuff adds nodes the table to the end of their corresponding bucket +// if the bucket is not full. The caller must hold tab.mutex. +func (tab *Table) stuff(nodes []*Node) { outer: - for _, n := range entries { + for _, n := range nodes { if n.ID == tab.self.ID { - // don't add self. - continue + continue // don't add self } bucket := tab.buckets[logdist(tab.self.sha, n.sha)] for i := range bucket.entries { if bucket.entries[i].ID == n.ID { - // already in bucket - continue outer + continue outer // already in bucket } } if len(bucket.entries) < bucketSize { @@ -496,12 +501,11 @@ outer: } } -// del removes an entry from the node table (used to evacuate failed/non-bonded -// discovery peers). -func (tab *Table) del(node *Node) { +// delete removes an entry from the node table (used to evacuate +// failed/non-bonded discovery peers). +func (tab *Table) delete(node *Node) { tab.mutex.Lock() defer tab.mutex.Unlock() - bucket := tab.buckets[logdist(tab.self.sha, node.sha)] for i := range bucket.entries { if bucket.entries[i].ID == node.ID { @@ -511,6 +515,27 @@ func (tab *Table) del(node *Node) { } } +func (b *bucket) replace(n *Node, last *Node) bool { + // Don't add if b already contains n. + for i := range b.entries { + if b.entries[i].ID == n.ID { + return false + } + } + // Replace last if it is still the last entry or just add n if b + // isn't full. If is no longer the last entry, it has either been + // replaced with someone else or became active. + if len(b.entries) == bucketSize && (last == nil || b.entries[bucketSize-1].ID != last.ID) { + return false + } + if len(b.entries) < bucketSize { + b.entries = append(b.entries, nil) + } + copy(b.entries[1:], b.entries) + b.entries[0] = n + return true +} + func (b *bucket) bump(n *Node) bool { for i := range b.entries { if b.entries[i].ID == n.ID { diff --git a/p2p/discover/table_test.go b/p2p/discover/table_test.go index d259177bf..426f4e9cc 100644 --- a/p2p/discover/table_test.go +++ b/p2p/discover/table_test.go @@ -178,8 +178,8 @@ func TestTable_closest(t *testing.T) { test := func(test *closeTest) bool { // for any node table, Target and N tab := newTable(nil, test.Self, &net.UDPAddr{}, "") - tab.add(test.All) defer tab.Close() + tab.stuff(test.All) // check that doClosest(Target, N) returns nodes result := tab.closest(test.Target, test.N).entries @@ -240,7 +240,7 @@ func TestTable_ReadRandomNodesGetAll(t *testing.T) { defer tab.Close() for i := 0; i < len(buf); i++ { ld := cfg.Rand.Intn(len(tab.buckets)) - tab.add([]*Node{nodeAtDistance(tab.self.sha, ld)}) + tab.stuff([]*Node{nodeAtDistance(tab.self.sha, ld)}) } gotN := tab.ReadRandomNodes(buf) if gotN != tab.len() { @@ -288,7 +288,7 @@ func TestTable_Lookup(t *testing.T) { } // seed table with initial node (otherwise lookup will terminate immediately) seed := newNode(lookupTestnet.dists[256][0], net.IP{}, 256, 0) - tab.add([]*Node{seed}) + tab.stuff([]*Node{seed}) results := tab.Lookup(lookupTestnet.target) t.Logf("results:") diff --git a/p2p/discover/udp.go b/p2p/discover/udp.go index d7ca9000d..008e63937 100644 --- a/p2p/discover/udp.go +++ b/p2p/discover/udp.go @@ -18,6 +18,7 @@ package discover import ( "bytes" + "container/list" "crypto/ecdsa" "errors" "fmt" @@ -43,6 +44,7 @@ var ( errUnsolicitedReply = errors.New("unsolicited reply") errUnknownNode = errors.New("unknown node") errTimeout = errors.New("RPC timeout") + errClockWarp = errors.New("reply deadline too far in the future") errClosed = errors.New("socket closed") ) @@ -296,7 +298,7 @@ func (t *udp) pending(id NodeID, ptype byte, callback func(interface{}) bool) <- } func (t *udp) handleReply(from NodeID, ptype byte, req packet) bool { - matched := make(chan bool) + matched := make(chan bool, 1) select { case t.gotreply <- reply{from, ptype, req, matched}: // loop will handle it @@ -310,68 +312,82 @@ func (t *udp) handleReply(from NodeID, ptype byte, req packet) bool { // the refresh timer and the pending reply queue. func (t *udp) loop() { var ( - pending []*pending - nextDeadline time.Time - timeout = time.NewTimer(0) - refresh = time.NewTicker(refreshInterval) + plist = list.New() + timeout = time.NewTimer(0) + nextTimeout *pending // head of plist when timeout was last reset + refresh = time.NewTicker(refreshInterval) ) <-timeout.C // ignore first timeout defer refresh.Stop() defer timeout.Stop() - rearmTimeout := func() { - now := time.Now() - if len(pending) == 0 || now.Before(nextDeadline) { + resetTimeout := func() { + if plist.Front() == nil || nextTimeout == plist.Front().Value { return } - nextDeadline = pending[0].deadline - timeout.Reset(nextDeadline.Sub(now)) + // Start the timer so it fires when the next pending reply has expired. + now := time.Now() + for el := plist.Front(); el != nil; el = el.Next() { + nextTimeout = el.Value.(*pending) + if dist := nextTimeout.deadline.Sub(now); dist < 2*respTimeout { + timeout.Reset(dist) + return + } + // Remove pending replies whose deadline is too far in the + // future. These can occur if the system clock jumped + // backwards after the deadline was assigned. + nextTimeout.errc <- errClockWarp + plist.Remove(el) + } + nextTimeout = nil + timeout.Stop() } for { + resetTimeout() + select { case <-refresh.C: go t.refresh() case <-t.closing: - for _, p := range pending { - p.errc <- errClosed + for el := plist.Front(); el != nil; el = el.Next() { + el.Value.(*pending).errc <- errClosed } - pending = nil return case p := <-t.addpending: p.deadline = time.Now().Add(respTimeout) - pending = append(pending, p) - rearmTimeout() + plist.PushBack(p) case r := <-t.gotreply: var matched bool - for i := 0; i < len(pending); i++ { - if p := pending[i]; p.from == r.from && p.ptype == r.ptype { + for el := plist.Front(); el != nil; el = el.Next() { + p := el.Value.(*pending) + if p.from == r.from && p.ptype == r.ptype { matched = true + // Remove the matcher if its callback indicates + // that all replies have been received. This is + // required for packet types that expect multiple + // reply packets. if p.callback(r.data) { - // callback indicates the request is done, remove it. p.errc <- nil - copy(pending[i:], pending[i+1:]) - pending = pending[:len(pending)-1] - i-- + plist.Remove(el) } } } r.matched <- matched case now := <-timeout.C: - // notify and remove callbacks whose deadline is in the past. - i := 0 - for ; i < len(pending) && now.After(pending[i].deadline); i++ { - pending[i].errc <- errTimeout - } - if i > 0 { - copy(pending, pending[i:]) - pending = pending[:len(pending)-i] + nextTimeout = nil + // Notify and remove callbacks whose deadline is in the past. + for el := plist.Front(); el != nil; el = el.Next() { + p := el.Value.(*pending) + if now.After(p.deadline) || now.Equal(p.deadline) { + p.errc <- errTimeout + plist.Remove(el) + } } - rearmTimeout() } } } @@ -385,7 +401,7 @@ const ( var ( headSpace = make([]byte, headSize) - // Neighbors responses are sent across multiple packets to + // Neighbors replies are sent across multiple packets to // stay below the 1280 byte limit. We compute the maximum number // of entries by stuffing a packet until it grows too large. maxNeighbors int diff --git a/p2p/discover/udp_test.go b/p2p/discover/udp_test.go index 8d6d3e855..a86d3737b 100644 --- a/p2p/discover/udp_test.go +++ b/p2p/discover/udp_test.go @@ -19,10 +19,12 @@ package discover import ( "bytes" "crypto/ecdsa" + "encoding/binary" "errors" "fmt" "io" logpkg "log" + "math/rand" "net" "os" "path/filepath" @@ -138,6 +140,77 @@ func TestUDP_pingTimeout(t *testing.T) { } } +func TestUDP_responseTimeouts(t *testing.T) { + t.Parallel() + test := newUDPTest(t) + defer test.table.Close() + + rand.Seed(time.Now().UnixNano()) + randomDuration := func(max time.Duration) time.Duration { + return time.Duration(rand.Int63n(int64(max))) + } + + var ( + nReqs = 200 + nTimeouts = 0 // number of requests with ptype > 128 + nilErr = make(chan error, nReqs) // for requests that get a reply + timeoutErr = make(chan error, nReqs) // for requests that time out + ) + for i := 0; i < nReqs; i++ { + // Create a matcher for a random request in udp.loop. Requests + // with ptype <= 128 will not get a reply and should time out. + // For all other requests, a reply is scheduled to arrive + // within the timeout window. + p := &pending{ + ptype: byte(rand.Intn(255)), + callback: func(interface{}) bool { return true }, + } + binary.BigEndian.PutUint64(p.from[:], uint64(i)) + if p.ptype <= 128 { + p.errc = timeoutErr + nTimeouts++ + } else { + p.errc = nilErr + time.AfterFunc(randomDuration(60*time.Millisecond), func() { + if !test.udp.handleReply(p.from, p.ptype, nil) { + t.Logf("not matched: %v", p) + } + }) + } + test.udp.addpending <- p + time.Sleep(randomDuration(30 * time.Millisecond)) + } + + // Check that all timeouts were delivered and that the rest got nil errors. + // The replies must be delivered. + var ( + recvDeadline = time.After(20 * time.Second) + nTimeoutsRecv, nNil = 0, 0 + ) + for i := 0; i < nReqs; i++ { + select { + case err := <-timeoutErr: + if err != errTimeout { + t.Fatalf("got non-timeout error on timeoutErr %d: %v", i, err) + } + nTimeoutsRecv++ + case err := <-nilErr: + if err != nil { + t.Fatalf("got non-nil error on nilErr %d: %v", i, err) + } + nNil++ + case <-recvDeadline: + t.Fatalf("exceeded recv deadline") + } + } + if nTimeoutsRecv != nTimeouts { + t.Errorf("wrong number of timeout errors received: got %d, want %d", nTimeoutsRecv, nTimeouts) + } + if nNil != nReqs-nTimeouts { + t.Errorf("wrong number of successful replies: got %d, want %d", nNil, nReqs-nTimeouts) + } +} + func TestUDP_findnodeTimeout(t *testing.T) { t.Parallel() test := newUDPTest(t) @@ -167,7 +240,7 @@ func TestUDP_findnode(t *testing.T) { for i := 0; i < bucketSize; i++ { nodes.push(nodeAtDistance(test.table.self.sha, i+2), bucketSize) } - test.table.add(nodes.entries) + test.table.stuff(nodes.entries) // ensure there's a bond with the test node, // findnode won't be accepted otherwise. diff --git a/p2p/peer_error.go b/p2p/peer_error.go index b1762a6ee..62c7b665d 100644 --- a/p2p/peer_error.go +++ b/p2p/peer_error.go @@ -66,7 +66,7 @@ const ( DiscUnexpectedIdentity DiscSelf DiscReadTimeout - DiscSubprotocolError + DiscSubprotocolError = 0x10 ) var discReasonToString = [...]string{ diff --git a/p2p/rlpx.go b/p2p/rlpx.go index fd43f565e..aaa733854 100644 --- a/p2p/rlpx.go +++ b/p2p/rlpx.go @@ -267,6 +267,10 @@ func initiatorEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, remoteID d } func newInitiatorHandshake(remoteID discover.NodeID) (*encHandshake, error) { + rpub, err := remoteID.Pubkey() + if err != nil { + return nil, fmt.Errorf("bad remoteID: %v", err) + } // generate random initiator nonce n := make([]byte, shaLen) if _, err := rand.Read(n); err != nil { @@ -277,10 +281,6 @@ func newInitiatorHandshake(remoteID discover.NodeID) (*encHandshake, error) { if err != nil { return nil, err } - rpub, err := remoteID.Pubkey() - if err != nil { - return nil, fmt.Errorf("bad remoteID: %v", err) - } h := &encHandshake{ initiator: true, remoteID: remoteID, @@ -417,6 +417,14 @@ func decodeAuthMsg(prv *ecdsa.PrivateKey, token []byte, auth []byte) (*encHandsh if err != nil { return nil, err } + + // validate the sha3 of recovered pubkey + remoteRandomPubMAC := msg[sigLen : sigLen+shaLen] + shaRemoteRandomPub := crypto.Sha3(remoteRandomPub[1:]) + if !bytes.Equal(remoteRandomPubMAC, shaRemoteRandomPub) { + return nil, fmt.Errorf("sha3 of recovered ephemeral pubkey does not match checksum in auth message") + } + h.remoteRandomPub, _ = importPublicKey(remoteRandomPub) return h, nil } diff --git a/rlp/decode.go b/rlp/decode.go index c0b5f0699..1381f5274 100644 --- a/rlp/decode.go +++ b/rlp/decode.go @@ -183,6 +183,8 @@ func makeDecoder(typ reflect.Type, tags tags) (dec decoder, err error) { return decodeBigIntNoPtr, nil case isUint(kind): return decodeUint, nil + case kind == reflect.Bool: + return decodeBool, nil case kind == reflect.String: return decodeString, nil case kind == reflect.Slice || kind == reflect.Array: @@ -211,6 +213,15 @@ func decodeUint(s *Stream, val reflect.Value) error { return nil } +func decodeBool(s *Stream, val reflect.Value) error { + b, err := s.Bool() + if err != nil { + return wrapStreamError(err, val.Type()) + } + val.SetBool(b) + return nil +} + func decodeString(s *Stream, val reflect.Value) error { b, err := s.Bytes() if err != nil { @@ -697,6 +708,24 @@ func (s *Stream) uint(maxbits int) (uint64, error) { } } +// Bool reads an RLP string of up to 1 byte and returns its contents +// as an boolean. If the input does not contain an RLP string, the +// returned error will be ErrExpectedString. +func (s *Stream) Bool() (bool, error) { + num, err := s.uint(8) + if err != nil { + return false, err + } + switch num { + case 0: + return false, nil + case 1: + return true, nil + default: + return false, fmt.Errorf("rlp: invalid boolean value: %d", num) + } +} + // List starts decoding an RLP list. If the input does not contain a // list, the returned error will be ErrExpectedList. When the list's // end has been reached, any Stream operation will return EOL. diff --git a/rlp/decode_test.go b/rlp/decode_test.go index 331faa9d8..d6b0611dd 100644 --- a/rlp/decode_test.go +++ b/rlp/decode_test.go @@ -19,6 +19,7 @@ package rlp import ( "bytes" "encoding/hex" + "errors" "fmt" "io" "math/big" @@ -116,6 +117,9 @@ func TestStreamErrors(t *testing.T) { {"817F", calls{"Uint"}, nil, ErrCanonSize}, {"8180", calls{"Uint"}, nil, nil}, + // Non-valid boolean + {"02", calls{"Bool"}, nil, errors.New("rlp: invalid boolean value: 2")}, + // Size tags must use the smallest possible encoding. // Leading zero bytes in the size tag are also rejected. {"8100", calls{"Uint"}, nil, ErrCanonSize}, @@ -315,6 +319,11 @@ var ( ) var decodeTests = []decodeTest{ + // booleans + {input: "01", ptr: new(bool), value: true}, + {input: "80", ptr: new(bool), value: false}, + {input: "02", ptr: new(bool), error: "rlp: invalid boolean value: 2"}, + // integers {input: "05", ptr: new(uint32), value: uint32(5)}, {input: "80", ptr: new(uint32), value: uint32(0)}, diff --git a/rlp/encode.go b/rlp/encode.go index 0ddef7bbb..b525ae4e7 100644 --- a/rlp/encode.go +++ b/rlp/encode.go @@ -361,6 +361,8 @@ func makeWriter(typ reflect.Type) (writer, error) { return writeBigIntNoPtr, nil case isUint(kind): return writeUint, nil + case kind == reflect.Bool: + return writeBool, nil case kind == reflect.String: return writeString, nil case kind == reflect.Slice && isByte(typ.Elem()): @@ -398,6 +400,15 @@ func writeUint(val reflect.Value, w *encbuf) error { return nil } +func writeBool(val reflect.Value, w *encbuf) error { + if val.Bool() { + w.str = append(w.str, 0x01) + } else { + w.str = append(w.str, 0x80) + } + return nil +} + func writeBigIntPtr(val reflect.Value, w *encbuf) error { ptr := val.Interface().(*big.Int) if ptr == nil { diff --git a/rlp/encode_test.go b/rlp/encode_test.go index e83c76b51..60bd95692 100644 --- a/rlp/encode_test.go +++ b/rlp/encode_test.go @@ -71,6 +71,10 @@ type encTest struct { } var encTests = []encTest{ + // booleans + {val: true, output: "01"}, + {val: false, output: "80"}, + // integers {val: uint32(0), output: "80"}, {val: uint32(127), output: "7F"}, |