aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/ethtest/main.go8
-rw-r--r--cmd/geth/main.go2
-rw-r--r--jsre/ethereum_js.go3031
-rw-r--r--p2p/discover/table.go121
-rw-r--r--p2p/discover/table_test.go6
-rw-r--r--p2p/discover/udp.go78
-rw-r--r--p2p/discover/udp_test.go75
-rw-r--r--p2p/peer_error.go2
-rw-r--r--p2p/rlpx.go16
-rw-r--r--rlp/decode.go29
-rw-r--r--rlp/decode_test.go9
-rw-r--r--rlp/encode.go11
-rw-r--r--rlp/encode_test.go4
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"},