Merge branch 'master' into pip-msg
This commit is contained in:
commit
868f83e6ca
@ -1,8 +1,8 @@
|
|||||||
stages:
|
stages:
|
||||||
- test
|
- test
|
||||||
- js-build
|
- js-build
|
||||||
- push-release
|
|
||||||
- build
|
- build
|
||||||
|
- push-release
|
||||||
variables:
|
variables:
|
||||||
GIT_DEPTH: "3"
|
GIT_DEPTH: "3"
|
||||||
SIMPLECOV: "true"
|
SIMPLECOV: "true"
|
||||||
@ -499,8 +499,7 @@ docker-build:
|
|||||||
before_script:
|
before_script:
|
||||||
- docker info
|
- docker info
|
||||||
script:
|
script:
|
||||||
- cd docker/hub
|
- if [ "$CI_BUILD_REF_NAME" == "beta-release" ]; then DOCKER_TAG="latest"; else DOCKER_TAG=$CI_BUILD_REF_NAME; fi
|
||||||
- if [ "$CI_BUILD_REF_NAME" == "nightly" ]; then DOCKER_TAG="latest"; else DOCKER_TAG=$CI_BUILD_REF_NAME; fi
|
|
||||||
- docker login -u $Docker_Hub_User -p $Docker_Hub_Pass
|
- docker login -u $Docker_Hub_User -p $Docker_Hub_Pass
|
||||||
- sh scripts/docker-build.sh $DOCKER_TAG
|
- sh scripts/docker-build.sh $DOCKER_TAG
|
||||||
tags:
|
tags:
|
||||||
|
276
Cargo.lock
generated
276
Cargo.lock
generated
@ -9,22 +9,22 @@ dependencies = [
|
|||||||
"daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore 1.6.0",
|
"ethcore 1.7.0",
|
||||||
"ethcore-dapps 1.6.0",
|
"ethcore-dapps 1.7.0",
|
||||||
"ethcore-devtools 1.6.0",
|
"ethcore-devtools 1.7.0",
|
||||||
"ethcore-io 1.6.0",
|
"ethcore-io 1.7.0",
|
||||||
"ethcore-ipc 1.6.0",
|
"ethcore-ipc 1.7.0",
|
||||||
"ethcore-ipc-hypervisor 1.2.0",
|
"ethcore-ipc-hypervisor 1.2.0",
|
||||||
"ethcore-ipc-nano 1.6.0",
|
"ethcore-ipc-nano 1.7.0",
|
||||||
"ethcore-ipc-tests 0.1.0",
|
"ethcore-ipc-tests 0.1.0",
|
||||||
"ethcore-light 1.6.0",
|
"ethcore-light 1.7.0",
|
||||||
"ethcore-logger 1.6.0",
|
"ethcore-logger 1.7.0",
|
||||||
"ethcore-rpc 1.6.0",
|
"ethcore-rpc 1.7.0",
|
||||||
"ethcore-secretstore 1.0.0",
|
"ethcore-secretstore 1.0.0",
|
||||||
"ethcore-signer 1.6.0",
|
"ethcore-signer 1.7.0",
|
||||||
"ethcore-stratum 1.6.0",
|
"ethcore-stratum 1.7.0",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"ethsync 1.6.0",
|
"ethsync 1.7.0",
|
||||||
"evmbin 0.1.0",
|
"evmbin 0.1.0",
|
||||||
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
|
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
|
||||||
@ -33,12 +33,12 @@ dependencies = [
|
|||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-hash-fetch 1.6.0",
|
"parity-hash-fetch 1.7.0",
|
||||||
"parity-ipfs-api 1.6.0",
|
"parity-ipfs-api 1.7.0",
|
||||||
"parity-local-store 0.1.0",
|
"parity-local-store 0.1.0",
|
||||||
"parity-reactor 0.1.0",
|
"parity-reactor 0.1.0",
|
||||||
"parity-rpc-client 1.4.0",
|
"parity-rpc-client 1.4.0",
|
||||||
"parity-updater 1.6.0",
|
"parity-updater 1.7.0",
|
||||||
"regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rlp 0.1.0",
|
"rlp 0.1.0",
|
||||||
"rpassword 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rpassword 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -344,7 +344,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "eth-secp256k1"
|
name = "eth-secp256k1"
|
||||||
version = "0.5.6"
|
version = "0.5.6"
|
||||||
source = "git+https://github.com/ethcore/rust-secp256k1#edab95f5569e4fb97579dc8947be96e7ac789c16"
|
source = "git+https://github.com/ethcore/rust-secp256k1#98ad9b9ecae44a563efdd64273bcebc6b4ed81c6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -367,7 +367,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethash"
|
name = "ethash"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -377,7 +377,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore"
|
name = "ethcore"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -386,20 +386,20 @@ dependencies = [
|
|||||||
"crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethabi 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethash 1.6.0",
|
"ethash 1.7.0",
|
||||||
"ethcore-bloom-journal 0.1.0",
|
"ethcore-bloom-journal 0.1.0",
|
||||||
"ethcore-devtools 1.6.0",
|
"ethcore-devtools 1.7.0",
|
||||||
"ethcore-io 1.6.0",
|
"ethcore-io 1.7.0",
|
||||||
"ethcore-ipc 1.6.0",
|
"ethcore-ipc 1.7.0",
|
||||||
"ethcore-ipc-codegen 1.6.0",
|
"ethcore-ipc-codegen 1.7.0",
|
||||||
"ethcore-ipc-nano 1.6.0",
|
"ethcore-ipc-nano 1.7.0",
|
||||||
"ethcore-stratum 1.6.0",
|
"ethcore-stratum 1.7.0",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"ethjson 0.1.0",
|
"ethjson 0.1.0",
|
||||||
"ethkey 0.2.0",
|
"ethkey 0.2.0",
|
||||||
"ethstore 0.1.0",
|
"ethstore 0.1.0",
|
||||||
"evmjit 1.6.0",
|
"evmjit 1.7.0",
|
||||||
"hardware-wallet 1.6.0",
|
"hardware-wallet 1.7.0",
|
||||||
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
|
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
|
||||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -436,14 +436,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore-dapps"
|
name = "ethcore-dapps"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore-devtools 1.6.0",
|
"ethcore-devtools 1.7.0",
|
||||||
"ethcore-rpc 1.6.0",
|
"ethcore-rpc 1.7.0",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"fetch 0.1.0",
|
"fetch 0.1.0",
|
||||||
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
|
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
|
||||||
@ -454,9 +454,9 @@ dependencies = [
|
|||||||
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-hash-fetch 1.6.0",
|
"parity-hash-fetch 1.7.0",
|
||||||
"parity-reactor 0.1.0",
|
"parity-reactor 0.1.0",
|
||||||
"parity-ui 1.6.0",
|
"parity-ui 1.7.0",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -470,14 +470,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore-devtools"
|
name = "ethcore-devtools"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore-io"
|
name = "ethcore-io"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -488,17 +488,17 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore-ipc"
|
name = "ethcore-ipc"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ethcore-devtools 1.6.0",
|
"ethcore-devtools 1.7.0",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||||
"semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore-ipc-codegen"
|
name = "ethcore-ipc-codegen"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"quasi 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"quasi 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -511,9 +511,9 @@ dependencies = [
|
|||||||
name = "ethcore-ipc-hypervisor"
|
name = "ethcore-ipc-hypervisor"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ethcore-ipc 1.6.0",
|
"ethcore-ipc 1.7.0",
|
||||||
"ethcore-ipc-codegen 1.6.0",
|
"ethcore-ipc-codegen 1.7.0",
|
||||||
"ethcore-ipc-nano 1.6.0",
|
"ethcore-ipc-nano 1.7.0",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||||
"semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -522,9 +522,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore-ipc-nano"
|
name = "ethcore-ipc-nano"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ethcore-ipc 1.6.0",
|
"ethcore-ipc 1.7.0",
|
||||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||||
@ -534,11 +534,11 @@ dependencies = [
|
|||||||
name = "ethcore-ipc-tests"
|
name = "ethcore-ipc-tests"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ethcore-devtools 1.6.0",
|
"ethcore-devtools 1.7.0",
|
||||||
"ethcore-ipc 1.6.0",
|
"ethcore-ipc 1.7.0",
|
||||||
"ethcore-ipc-codegen 1.6.0",
|
"ethcore-ipc-codegen 1.7.0",
|
||||||
"ethcore-ipc-nano 1.6.0",
|
"ethcore-ipc-nano 1.7.0",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||||
"semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -546,14 +546,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore-light"
|
name = "ethcore-light"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ethcore 1.6.0",
|
"ethcore 1.7.0",
|
||||||
"ethcore-io 1.6.0",
|
"ethcore-io 1.7.0",
|
||||||
"ethcore-ipc 1.6.0",
|
"ethcore-ipc 1.7.0",
|
||||||
"ethcore-ipc-codegen 1.6.0",
|
"ethcore-ipc-codegen 1.7.0",
|
||||||
"ethcore-network 1.6.0",
|
"ethcore-network 1.7.0",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -566,10 +566,10 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore-logger"
|
name = "ethcore-logger"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -579,13 +579,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore-network"
|
name = "ethcore-network"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore-devtools 1.6.0",
|
"ethcore-devtools 1.7.0",
|
||||||
"ethcore-io 1.6.0",
|
"ethcore-io 1.7.0",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"ethcrypto 0.1.0",
|
"ethcrypto 0.1.0",
|
||||||
"ethkey 0.2.0",
|
"ethkey 0.2.0",
|
||||||
"igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -604,21 +604,21 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore-rpc"
|
name = "ethcore-rpc"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethash 1.6.0",
|
"ethash 1.7.0",
|
||||||
"ethcore 1.6.0",
|
"ethcore 1.7.0",
|
||||||
"ethcore-devtools 1.6.0",
|
"ethcore-devtools 1.7.0",
|
||||||
"ethcore-io 1.6.0",
|
"ethcore-io 1.7.0",
|
||||||
"ethcore-ipc 1.6.0",
|
"ethcore-ipc 1.7.0",
|
||||||
"ethcore-light 1.6.0",
|
"ethcore-light 1.7.0",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"ethcrypto 0.1.0",
|
"ethcrypto 0.1.0",
|
||||||
"ethjson 0.1.0",
|
"ethjson 0.1.0",
|
||||||
"ethkey 0.2.0",
|
"ethkey 0.2.0",
|
||||||
"ethstore 0.1.0",
|
"ethstore 0.1.0",
|
||||||
"ethsync 1.6.0",
|
"ethsync 1.7.0",
|
||||||
"fetch 0.1.0",
|
"fetch 0.1.0",
|
||||||
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||||
@ -628,7 +628,7 @@ dependencies = [
|
|||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-reactor 0.1.0",
|
"parity-reactor 0.1.0",
|
||||||
"parity-updater 1.6.0",
|
"parity-updater 1.7.0",
|
||||||
"rlp 0.1.0",
|
"rlp 0.1.0",
|
||||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -644,11 +644,11 @@ dependencies = [
|
|||||||
name = "ethcore-secretstore"
|
name = "ethcore-secretstore"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ethcore-devtools 1.6.0",
|
"ethcore-devtools 1.7.0",
|
||||||
"ethcore-ipc 1.6.0",
|
"ethcore-ipc 1.7.0",
|
||||||
"ethcore-ipc-codegen 1.6.0",
|
"ethcore-ipc-codegen 1.7.0",
|
||||||
"ethcore-ipc-nano 1.6.0",
|
"ethcore-ipc-nano 1.7.0",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"ethcrypto 0.1.0",
|
"ethcrypto 0.1.0",
|
||||||
"ethkey 0.2.0",
|
"ethkey 0.2.0",
|
||||||
"hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -659,18 +659,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore-signer"
|
name = "ethcore-signer"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore-devtools 1.6.0",
|
"ethcore-devtools 1.7.0",
|
||||||
"ethcore-io 1.6.0",
|
"ethcore-io 1.7.0",
|
||||||
"ethcore-rpc 1.6.0",
|
"ethcore-rpc 1.7.0",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-ui 1.6.0",
|
"parity-ui 1.7.0",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)",
|
"ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)",
|
||||||
@ -678,14 +678,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore-stratum"
|
name = "ethcore-stratum"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore-devtools 1.6.0",
|
"ethcore-devtools 1.7.0",
|
||||||
"ethcore-ipc 1.6.0",
|
"ethcore-ipc 1.7.0",
|
||||||
"ethcore-ipc-codegen 1.6.0",
|
"ethcore-ipc-codegen 1.7.0",
|
||||||
"ethcore-ipc-nano 1.6.0",
|
"ethcore-ipc-nano 1.7.0",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||||
"jsonrpc-macros 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
"jsonrpc-macros 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||||
@ -699,7 +699,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore-util"
|
name = "ethcore-util"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -709,7 +709,7 @@ dependencies = [
|
|||||||
"eth-secp256k1 0.5.6 (git+https://github.com/ethcore/rust-secp256k1)",
|
"eth-secp256k1 0.5.6 (git+https://github.com/ethcore/rust-secp256k1)",
|
||||||
"ethcore-bigint 0.1.2",
|
"ethcore-bigint 0.1.2",
|
||||||
"ethcore-bloom-journal 0.1.0",
|
"ethcore-bloom-journal 0.1.0",
|
||||||
"ethcore-devtools 1.6.0",
|
"ethcore-devtools 1.7.0",
|
||||||
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -748,7 +748,7 @@ dependencies = [
|
|||||||
name = "ethjson"
|
name = "ethjson"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_derive 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_derive 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -775,8 +775,8 @@ name = "ethstore"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore-devtools 1.6.0",
|
"ethcore-devtools 1.7.0",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"ethcrypto 0.1.0",
|
"ethcrypto 0.1.0",
|
||||||
"ethkey 0.2.0",
|
"ethkey 0.2.0",
|
||||||
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -797,19 +797,19 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethsync"
|
name = "ethsync"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore 1.6.0",
|
"ethcore 1.7.0",
|
||||||
"ethcore-devtools 1.6.0",
|
"ethcore-devtools 1.7.0",
|
||||||
"ethcore-io 1.6.0",
|
"ethcore-io 1.7.0",
|
||||||
"ethcore-ipc 1.6.0",
|
"ethcore-ipc 1.7.0",
|
||||||
"ethcore-ipc-codegen 1.6.0",
|
"ethcore-ipc-codegen 1.7.0",
|
||||||
"ethcore-ipc-nano 1.6.0",
|
"ethcore-ipc-nano 1.7.0",
|
||||||
"ethcore-light 1.6.0",
|
"ethcore-light 1.7.0",
|
||||||
"ethcore-network 1.6.0",
|
"ethcore-network 1.7.0",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"ethkey 0.2.0",
|
"ethkey 0.2.0",
|
||||||
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -825,14 +825,14 @@ name = "evmbin"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore 1.6.0",
|
"ethcore 1.7.0",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "evmjit"
|
name = "evmjit"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@ -913,7 +913,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hardware-wallet"
|
name = "hardware-wallet"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ethcore-bigint 0.1.2",
|
"ethcore-bigint 0.1.2",
|
||||||
"ethkey 0.2.0",
|
"ethkey 0.2.0",
|
||||||
@ -976,7 +976,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "0.10.0-a.0"
|
version = "0.10.0-a.0"
|
||||||
source = "git+https://github.com/ethcore/hyper#2e6702984f4f9e99fe251537a755aff0badc0b3a"
|
source = "git+https://github.com/ethcore/hyper#453c683b52208fefc32d29e4ac7c863439b2321f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cookie 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cookie 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1050,11 +1050,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipc-common-types"
|
name = "ipc-common-types"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ethcore-ipc 1.6.0",
|
"ethcore-ipc 1.7.0",
|
||||||
"ethcore-ipc-codegen 1.6.0",
|
"ethcore-ipc-codegen 1.7.0",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1618,10 +1618,10 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-hash-fetch"
|
name = "parity-hash-fetch"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ethabi 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"fetch 0.1.0",
|
"fetch 0.1.0",
|
||||||
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1634,11 +1634,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-ipfs-api"
|
name = "parity-ipfs-api"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore 1.6.0",
|
"ethcore 1.7.0",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
|
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
|
||||||
"jsonrpc-http-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
"jsonrpc-http-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||||
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1650,9 +1650,9 @@ dependencies = [
|
|||||||
name = "parity-local-store"
|
name = "parity-local-store"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ethcore 1.6.0",
|
"ethcore 1.7.0",
|
||||||
"ethcore-io 1.6.0",
|
"ethcore-io 1.7.0",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"ethkey 0.2.0",
|
"ethkey 0.2.0",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rlp 0.1.0",
|
"rlp 0.1.0",
|
||||||
@ -1673,9 +1673,9 @@ dependencies = [
|
|||||||
name = "parity-rpc-client"
|
name = "parity-rpc-client"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ethcore-rpc 1.6.0",
|
"ethcore-rpc 1.7.0",
|
||||||
"ethcore-signer 1.6.0",
|
"ethcore-signer 1.7.0",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1689,7 +1689,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-ui"
|
name = "parity-ui"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-ui-dev 1.4.0",
|
"parity-ui-dev 1.4.0",
|
||||||
"parity-ui-precompiled 1.4.0 (git+https://github.com/ethcore/js-precompiled.git)",
|
"parity-ui-precompiled 1.4.0 (git+https://github.com/ethcore/js-precompiled.git)",
|
||||||
@ -1706,24 +1706,24 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-ui-precompiled"
|
name = "parity-ui-precompiled"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "git+https://github.com/ethcore/js-precompiled.git#aaee793907e4ff61082d83ff44733363dfff6eae"
|
source = "git+https://github.com/ethcore/js-precompiled.git#9eef2b78d363560fe942062caaaa7f6b1d64dd17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-updater"
|
name = "parity-updater"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ethabi 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore 1.6.0",
|
"ethcore 1.7.0",
|
||||||
"ethcore-ipc 1.6.0",
|
"ethcore-ipc 1.7.0",
|
||||||
"ethcore-ipc-codegen 1.6.0",
|
"ethcore-ipc-codegen 1.7.0",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"ethsync 1.6.0",
|
"ethsync 1.7.0",
|
||||||
"ipc-common-types 1.6.0",
|
"ipc-common-types 1.7.0",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-hash-fetch 1.6.0",
|
"parity-hash-fetch 1.7.0",
|
||||||
"parity-reactor 0.1.0",
|
"parity-reactor 0.1.0",
|
||||||
"target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@ -1992,8 +1992,8 @@ name = "rpc-cli"
|
|||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ethcore-bigint 0.1.2",
|
"ethcore-bigint 0.1.2",
|
||||||
"ethcore-rpc 1.6.0",
|
"ethcore-rpc 1.7.0",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.7.0",
|
||||||
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-rpc-client 1.4.0",
|
"parity-rpc-client 1.4.0",
|
||||||
"rpassword 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rpassword 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -100,7 +100,11 @@ $ cargo build --release
|
|||||||
```
|
```
|
||||||
|
|
||||||
This will produce an executable in the `./target/release` subdirectory.
|
This will produce an executable in the `./target/release` subdirectory.
|
||||||
|
Note: if cargo fails to parse manifest try:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ ~/.cargo/bin/cargo build --release
|
||||||
|
```
|
||||||
----
|
----
|
||||||
|
|
||||||
## Simple one-line installer for Mac and Ubuntu
|
## Simple one-line installer for Mac and Ubuntu
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
description = "Parity Dapps crate"
|
description = "Parity Dapps crate"
|
||||||
name = "ethcore-dapps"
|
name = "ethcore-dapps"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
description = "Base Package for all Parity built-in dapps"
|
description = "Base Package for all Parity built-in dapps"
|
||||||
name = "parity-dapps-glue"
|
name = "parity-dapps-glue"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
@ -39,7 +39,11 @@ pub struct RestApi {
|
|||||||
impl RestApi {
|
impl RestApi {
|
||||||
pub fn new(cors_domains: Vec<String>, endpoints: Arc<Endpoints>, fetcher: Arc<Fetcher>) -> Box<Endpoint> {
|
pub fn new(cors_domains: Vec<String>, endpoints: Arc<Endpoints>, fetcher: Arc<Fetcher>) -> Box<Endpoint> {
|
||||||
Box::new(RestApi {
|
Box::new(RestApi {
|
||||||
cors_domains: Some(cors_domains.into_iter().map(AccessControlAllowOrigin::Value).collect()),
|
cors_domains: Some(cors_domains.into_iter().map(|domain| match domain.as_ref() {
|
||||||
|
"all" | "*" | "any" => AccessControlAllowOrigin::Any,
|
||||||
|
"null" => AccessControlAllowOrigin::Null,
|
||||||
|
other => AccessControlAllowOrigin::Value(other.into()),
|
||||||
|
}).collect()),
|
||||||
endpoints: endpoints,
|
endpoints: endpoints,
|
||||||
fetcher: fetcher,
|
fetcher: fetcher,
|
||||||
})
|
})
|
||||||
|
@ -111,6 +111,7 @@ pub struct ServerBuilder<T: Fetch = FetchClient> {
|
|||||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||||
signer_address: Option<(String, u16)>,
|
signer_address: Option<(String, u16)>,
|
||||||
allowed_hosts: Option<Vec<String>>,
|
allowed_hosts: Option<Vec<String>>,
|
||||||
|
extra_cors: Option<Vec<String>>,
|
||||||
remote: Remote,
|
remote: Remote,
|
||||||
fetch: Option<T>,
|
fetch: Option<T>,
|
||||||
}
|
}
|
||||||
@ -126,6 +127,7 @@ impl ServerBuilder {
|
|||||||
web_proxy_tokens: Arc::new(|_| false),
|
web_proxy_tokens: Arc::new(|_| false),
|
||||||
signer_address: None,
|
signer_address: None,
|
||||||
allowed_hosts: Some(vec![]),
|
allowed_hosts: Some(vec![]),
|
||||||
|
extra_cors: None,
|
||||||
remote: remote,
|
remote: remote,
|
||||||
fetch: None,
|
fetch: None,
|
||||||
}
|
}
|
||||||
@ -143,6 +145,7 @@ impl<T: Fetch> ServerBuilder<T> {
|
|||||||
web_proxy_tokens: self.web_proxy_tokens,
|
web_proxy_tokens: self.web_proxy_tokens,
|
||||||
signer_address: self.signer_address,
|
signer_address: self.signer_address,
|
||||||
allowed_hosts: self.allowed_hosts,
|
allowed_hosts: self.allowed_hosts,
|
||||||
|
extra_cors: self.extra_cors,
|
||||||
remote: self.remote,
|
remote: self.remote,
|
||||||
fetch: Some(fetch),
|
fetch: Some(fetch),
|
||||||
}
|
}
|
||||||
@ -174,6 +177,13 @@ impl<T: Fetch> ServerBuilder<T> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extra cors headers.
|
||||||
|
/// `None` - no additional CORS URLs
|
||||||
|
pub fn extra_cors_headers(mut self, cors: Option<Vec<String>>) -> Self {
|
||||||
|
self.extra_cors = cors;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Change extra dapps paths (apart from `dapps_path`)
|
/// Change extra dapps paths (apart from `dapps_path`)
|
||||||
pub fn extra_dapps<P: AsRef<Path>>(mut self, extra_dapps: &[P]) -> Self {
|
pub fn extra_dapps<P: AsRef<Path>>(mut self, extra_dapps: &[P]) -> Self {
|
||||||
self.extra_dapps = extra_dapps.iter().map(|p| p.as_ref().to_owned()).collect();
|
self.extra_dapps = extra_dapps.iter().map(|p| p.as_ref().to_owned()).collect();
|
||||||
@ -187,6 +197,7 @@ impl<T: Fetch> ServerBuilder<T> {
|
|||||||
Server::start_http(
|
Server::start_http(
|
||||||
addr,
|
addr,
|
||||||
self.allowed_hosts,
|
self.allowed_hosts,
|
||||||
|
self.extra_cors,
|
||||||
NoAuth,
|
NoAuth,
|
||||||
handler,
|
handler,
|
||||||
self.dapps_path,
|
self.dapps_path,
|
||||||
@ -207,6 +218,7 @@ impl<T: Fetch> ServerBuilder<T> {
|
|||||||
Server::start_http(
|
Server::start_http(
|
||||||
addr,
|
addr,
|
||||||
self.allowed_hosts,
|
self.allowed_hosts,
|
||||||
|
self.extra_cors,
|
||||||
HttpBasicAuth::single_user(username, password),
|
HttpBasicAuth::single_user(username, password),
|
||||||
handler,
|
handler,
|
||||||
self.dapps_path,
|
self.dapps_path,
|
||||||
@ -251,8 +263,8 @@ impl Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a list of CORS domains for API endpoint.
|
/// Returns a list of CORS domains for API endpoint.
|
||||||
fn cors_domains(signer_address: Option<(String, u16)>) -> Vec<String> {
|
fn cors_domains(signer_address: Option<(String, u16)>, extra_cors: Option<Vec<String>>) -> Vec<String> {
|
||||||
match signer_address {
|
let basic_cors = match signer_address {
|
||||||
Some(signer_address) => vec![
|
Some(signer_address) => vec![
|
||||||
format!("http://{}{}", HOME_PAGE, DAPPS_DOMAIN),
|
format!("http://{}{}", HOME_PAGE, DAPPS_DOMAIN),
|
||||||
format!("http://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1),
|
format!("http://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1),
|
||||||
@ -260,15 +272,20 @@ impl Server {
|
|||||||
format!("https://{}{}", HOME_PAGE, DAPPS_DOMAIN),
|
format!("https://{}{}", HOME_PAGE, DAPPS_DOMAIN),
|
||||||
format!("https://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1),
|
format!("https://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1),
|
||||||
format!("https://{}", address(&signer_address)),
|
format!("https://{}", address(&signer_address)),
|
||||||
|
|
||||||
],
|
],
|
||||||
None => vec![],
|
None => vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
match extra_cors {
|
||||||
|
None => basic_cors,
|
||||||
|
Some(extra_cors) => basic_cors.into_iter().chain(extra_cors).collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_http<A: Authorization + 'static, F: Fetch, T: Middleware<Metadata>>(
|
fn start_http<A: Authorization + 'static, F: Fetch, T: Middleware<Metadata>>(
|
||||||
addr: &SocketAddr,
|
addr: &SocketAddr,
|
||||||
hosts: Option<Vec<String>>,
|
hosts: Option<Vec<String>>,
|
||||||
|
extra_cors: Option<Vec<String>>,
|
||||||
authorization: A,
|
authorization: A,
|
||||||
handler: RpcHandler<Metadata, T>,
|
handler: RpcHandler<Metadata, T>,
|
||||||
dapps_path: PathBuf,
|
dapps_path: PathBuf,
|
||||||
@ -297,7 +314,7 @@ impl Server {
|
|||||||
remote.clone(),
|
remote.clone(),
|
||||||
fetch.clone(),
|
fetch.clone(),
|
||||||
));
|
));
|
||||||
let cors_domains = Self::cors_domains(signer_address.clone());
|
let cors_domains = Self::cors_domains(signer_address.clone(), extra_cors);
|
||||||
|
|
||||||
let special = Arc::new({
|
let special = Arc::new({
|
||||||
let mut special = HashMap::new();
|
let mut special = HashMap::new();
|
||||||
@ -413,8 +430,9 @@ mod util_tests {
|
|||||||
// given
|
// given
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let none = Server::cors_domains(None);
|
let none = Server::cors_domains(None, None);
|
||||||
let some = Server::cors_domains(Some(("127.0.0.1".into(), 18180)));
|
let some = Server::cors_domains(Some(("127.0.0.1".into(), 18180)), None);
|
||||||
|
let extra = Server::cors_domains(None, Some(vec!["all".to_owned()]));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(none, Vec::<String>::new());
|
assert_eq!(none, Vec::<String>::new());
|
||||||
@ -425,7 +443,7 @@ mod util_tests {
|
|||||||
"https://parity.web3.site".into(),
|
"https://parity.web3.site".into(),
|
||||||
"https://parity.web3.site:18180".into(),
|
"https://parity.web3.site:18180".into(),
|
||||||
"https://127.0.0.1:18180".into()
|
"https://127.0.0.1:18180".into()
|
||||||
|
|
||||||
]);
|
]);
|
||||||
|
assert_eq!(extra, vec!["all".to_owned()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use tests::helpers::{serve, serve_with_registrar, request, assert_security_headers};
|
use tests::helpers::{serve, serve_with_registrar, serve_extra_cors, request, assert_security_headers};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_return_error() {
|
fn should_return_error() {
|
||||||
@ -212,3 +212,25 @@ fn should_return_signer_port_cors_headers_for_home_parity_with_port() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_return_extra_cors_headers() {
|
||||||
|
// given
|
||||||
|
let server = serve_extra_cors(Some(vec!["all".to_owned()]));
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
POST /api/ping HTTP/1.1\r\n\
|
||||||
|
Host: localhost:8080\r\n\
|
||||||
|
Origin: http://somedomain.io\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
\r\n\
|
||||||
|
{}
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
response.assert_status("HTTP/1.1 200 OK");
|
||||||
|
response.assert_header("Access-Control-Allow-Origin", "http://somedomain.io");
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -109,6 +109,10 @@ pub fn serve_hosts(hosts: Option<Vec<String>>) -> ServerLoop {
|
|||||||
init_server(|builder| builder.allowed_hosts(hosts), Default::default(), Remote::new_sync()).0
|
init_server(|builder| builder.allowed_hosts(hosts), Default::default(), Remote::new_sync()).0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn serve_extra_cors(extra_cors: Option<Vec<String>>) -> ServerLoop {
|
||||||
|
init_server(|builder| builder.allowed_hosts(None).extra_cors_headers(extra_cors), Default::default(), Remote::new_sync()).0
|
||||||
|
}
|
||||||
|
|
||||||
pub fn serve_with_registrar() -> (ServerLoop, Arc<FakeRegistrar>) {
|
pub fn serve_with_registrar() -> (ServerLoop, Arc<FakeRegistrar>) {
|
||||||
init_server(|builder| builder.allowed_hosts(None), Default::default(), Remote::new_sync())
|
init_server(|builder| builder.allowed_hosts(None), Default::default(), Remote::new_sync())
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ description = "Ethcore Parity UI"
|
|||||||
homepage = "http://parity.io"
|
homepage = "http://parity.io"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
name = "parity-ui"
|
name = "parity-ui"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
@ -3,7 +3,7 @@ description = "Ethcore Database"
|
|||||||
homepage = "http://parity.io"
|
homepage = "http://parity.io"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
name = "ethcore-db"
|
name = "ethcore-db"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ description = "Ethcore development/test/build tools"
|
|||||||
homepage = "http://parity.io"
|
homepage = "http://parity.io"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
name = "ethcore-devtools"
|
name = "ethcore-devtools"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
FROM ubuntu:14.04
|
FROM ubuntu:14.04
|
||||||
MAINTAINER Parity Technologies <devops@parity.io>
|
MAINTAINER Parity Technologies <devops@parity.io>
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
#ENV for build TAG
|
||||||
|
ARG BUILD_TAG
|
||||||
|
ENV BUILD_TAG ${BUILD_TAG:-master}
|
||||||
|
RUN echo $BUILD_TAG
|
||||||
# install tools and dependencies
|
# install tools and dependencies
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y --force-yes --no-install-recommends \
|
apt-get install -y --force-yes --no-install-recommends \
|
||||||
@ -47,7 +51,7 @@ RUN apt-get update && \
|
|||||||
cd /build&&git clone https://github.com/ethcore/parity && \
|
cd /build&&git clone https://github.com/ethcore/parity && \
|
||||||
cd parity && \
|
cd parity && \
|
||||||
git pull&& \
|
git pull&& \
|
||||||
git checkout $CI_BUILD_REF_NAME && \
|
git checkout $BUILD_TAG && \
|
||||||
cargo build --verbose --release --features final && \
|
cargo build --verbose --release --features final && \
|
||||||
#ls /build/parity/target/release/parity && \
|
#ls /build/parity/target/release/parity && \
|
||||||
strip /build/parity/target/release/parity && \
|
strip /build/parity/target/release/parity && \
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ethash"
|
name = "ethash"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
@ -3,7 +3,7 @@ description = "Ethcore library"
|
|||||||
homepage = "http://parity.io"
|
homepage = "http://parity.io"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
name = "ethcore"
|
name = "ethcore"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ description = "Parity Light Client Implementation"
|
|||||||
homepage = "http://parity.io"
|
homepage = "http://parity.io"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
name = "ethcore-light"
|
name = "ethcore-light"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
"timestamp": "0x00",
|
"timestamp": "0x00",
|
||||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"extraData": "0x",
|
"extraData": "0x",
|
||||||
"gasLimit": "0x2fefd8"
|
"gasLimit": "0x222222"
|
||||||
},
|
},
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||||
|
@ -48,18 +48,12 @@
|
|||||||
"stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"
|
"stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"
|
||||||
},
|
},
|
||||||
"nodes": [
|
"nodes": [
|
||||||
"enode://08c7ee6a4f861ff0664a49532bcc86de1363acd608999d1b76609bb9bc278649906f069057630fd9493924a368b5d1dc9b8f8bf13ac26df72512f6d1fabd8c95@45.32.7.81:30303",
|
|
||||||
"enode://e809c4a2fec7daed400e5e28564e23693b23b2cc5a019b612505631bbe7b9ccf709c1796d2a3d29ef2b045f210caf51e3c4f5b6d3587d43ad5d6397526fa6179@174.112.32.157:30303",
|
"enode://e809c4a2fec7daed400e5e28564e23693b23b2cc5a019b612505631bbe7b9ccf709c1796d2a3d29ef2b045f210caf51e3c4f5b6d3587d43ad5d6397526fa6179@174.112.32.157:30303",
|
||||||
"enode://687be94c3a7beaa3d2fde82fa5046cdeb3e8198354e05b29d6e0d4e276713e3707ac10f784a7904938b06b46c764875c241b0337dd853385a4d8bfcbf8190647@95.183.51.229:30303",
|
|
||||||
"enode://6e538e7c1280f0a31ff08b382db5302480f775480b8e68f8febca0ceff81e4b19153c6f8bf60313b93bef2cc34d34e1df41317de0ce613a201d1660a788a03e2@52.206.67.235:30303",
|
"enode://6e538e7c1280f0a31ff08b382db5302480f775480b8e68f8febca0ceff81e4b19153c6f8bf60313b93bef2cc34d34e1df41317de0ce613a201d1660a788a03e2@52.206.67.235:30303",
|
||||||
"enode://ca5ae4eca09ba6787e29cf6d86f7634d07aae6b9e6317a59aff675851c0bf445068173208cf8ef7f5cd783d8e29b85b2fa3fa358124cf0546823149724f9bde1@138.68.1.16:30303",
|
"enode://5fbfb426fbb46f8b8c1bd3dd140f5b511da558cd37d60844b525909ab82e13a25ee722293c829e52cb65c2305b1637fa9a2ea4d6634a224d5f400bfe244ac0de@162.243.55.45:30303",
|
||||||
"enode://217ebe27e89bf4fec8ce06509323ff095b1014378deb75ab2e5f6759a4e8750a3bd8254b8c6833136e4d5e58230d65ee8ab34a5db5abf0640408c4288af3c8a7@188.138.1.237:30303",
|
"enode://42d8f29d1db5f4b2947cd5c3d76c6d0d3697e6b9b3430c3d41e46b4bb77655433aeedc25d4b4ea9d8214b6a43008ba67199374a9b53633301bca0cd20c6928ab@104.155.176.151:30303",
|
||||||
"enode://fa20444ef991596ce99b81652ac4e61de1eddc4ff21d3cd42762abd7ed47e7cf044d3c9ccddaf6035d39725e4eb372806787829ccb9a08ec7cb71883cb8c3abd@50.149.116.182:30303",
|
"enode://814920f1ec9510aa9ea1c8f79d8b6e6a462045f09caa2ae4055b0f34f7416fca6facd3dd45f1cf1673c0209e0503f02776b8ff94020e98b6679a0dc561b4eba0@104.154.136.117:30303",
|
||||||
"enode://4bd6a4df3612c718333eb5ea7f817923a8cdf1bed89cee70d1710b45a0b6b77b2819846440555e451a9b602ad2efa2d2facd4620650249d8468008946887820a@71.178.232.20:30304",
|
"enode://72e445f4e89c0f476d404bc40478b0df83a5b500d2d2e850e08eb1af0cd464ab86db6160d0fde64bd77d5f0d33507ae19035671b3c74fec126d6e28787669740@104.198.71.200:30303"
|
||||||
"enode://921cf8e4c345fe8db913c53964f9cadc667644e7f20195a0b7d877bd689a5934e146ff2c2259f1bae6817b6585153a007ceb67d260b720fa3e6fc4350df25c7f@51.255.49.170:30303",
|
|
||||||
"enode://ffea3b01c000cdd89e1e9229fea3e80e95b646f9b2aa55071fc865e2f19543c9b06045cc2e69453e6b78100a119e66be1b5ad50b36f2ffd27293caa28efdd1b2@128.199.93.177:3030",
|
|
||||||
"enode://ee3da491ce6a155eb132708eb0e8d04b0637926ec0ae1b79e63fc97cb9fc3818f49250a0ae0d7f79ed62b66ec677f408c4e01741504dc7a051e274f1e803d454@91.121.65.179:40404",
|
|
||||||
"enode://48e063a6cf5f335b1ef2ed98126bf522cf254396f850c7d442fe2edbbc23398787e14cd4de7968a00175a82762de9cbe9e1407d8ccbcaeca5004d65f8398d759@159.203.255.59:30303"
|
|
||||||
],
|
],
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "Frontier/Homestead",
|
"name": "Foundation",
|
||||||
"dataDir": "ethereum",
|
"dataDir": "ethereum",
|
||||||
"engine": {
|
"engine": {
|
||||||
"Ethash": {
|
"Ethash": {
|
||||||
@ -9,7 +9,7 @@
|
|||||||
"difficultyBoundDivisor": "0x0800",
|
"difficultyBoundDivisor": "0x0800",
|
||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar" : "0x3bb2bb5c6c9c9b7f4ef430b47dc7e026310042ea",
|
"registrar" : "0xe3389675d0338462dC76C6f9A3e432550c36A142",
|
||||||
"homesteadTransition": "0x118c30",
|
"homesteadTransition": "0x118c30",
|
||||||
"daoHardforkTransition": "0x1d4c00",
|
"daoHardforkTransition": "0x1d4c00",
|
||||||
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
59
ethcore/res/ethereum/kovan.json
Normal file
59
ethcore/res/ethereum/kovan.json
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"name": "Kovan",
|
||||||
|
"dataDir": "kovan",
|
||||||
|
"engine": {
|
||||||
|
"authorityRound": {
|
||||||
|
"params": {
|
||||||
|
"gasLimitBoundDivisor": "0x400",
|
||||||
|
"registrar" : "0xfAb104398BBefbd47752E7702D9fE23047E1Bca3",
|
||||||
|
"stepDuration": "4",
|
||||||
|
"blockReward": "0x4563918244F40000",
|
||||||
|
"validators" : {
|
||||||
|
"list": [
|
||||||
|
"0x00D6Cc1BA9cf89BD2e58009741f4F7325BAdc0ED",
|
||||||
|
"0x00427feae2419c15b89d1c21af10d1b6650a4d3d",
|
||||||
|
"0x4Ed9B08e6354C70fE6F8CB0411b0d3246b424d6c",
|
||||||
|
"0x0020ee4Be0e2027d76603cB751eE069519bA81A1",
|
||||||
|
|
||||||
|
"0x0010f94b296a852aaac52ea6c5ac72e03afd032d",
|
||||||
|
|
||||||
|
"0x007733a1FE69CF3f2CF989F81C7b4cAc1693387A",
|
||||||
|
"0x00E6d2b931F55a3f1701c7389d592a7778897879",
|
||||||
|
"0x00e4a10650e5a6D6001C38ff8E64F97016a1645c",
|
||||||
|
|
||||||
|
"0x00a0a24b9f0e5ec7aa4c7389b8302fd0123194de"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"params": {
|
||||||
|
"maximumExtraDataSize": "0x20",
|
||||||
|
"minGasLimit": "0x1388",
|
||||||
|
"networkID" : "0x2A"
|
||||||
|
},
|
||||||
|
"genesis": {
|
||||||
|
"seal": {
|
||||||
|
"authorityRound": {
|
||||||
|
"step": "0x0",
|
||||||
|
"signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"difficulty": "0x20000",
|
||||||
|
"gasLimit": "0x5B8D80"
|
||||||
|
},
|
||||||
|
"accounts": {
|
||||||
|
"0x0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||||
|
"0x0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
|
||||||
|
"0x0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
||||||
|
"0x0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
|
||||||
|
"0x00521965e7bd230323c423d96c657db5b79d099f": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }
|
||||||
|
},
|
||||||
|
"nodes": [
|
||||||
|
"enode://c005dd308256c60fab247813d8bf6d6e81f9cd354287837eb1c2fcf294adaa913a3208e88900ef5c55a8cba7042c301d80503edec2ad3f92a72e241ee6743854@192.241.230.87:30303",
|
||||||
|
"enode://48caeceb2724f2f71406990aa81efe87f8c53f26441d891473da2ae50cc138f238addc0e46b5aee240db55de8c711daac53d7b32a3f13e30edb86a3ca7c2700b@138.68.143.220:30303",
|
||||||
|
"enode://85705212fd28ebdd56669fb55e958feb9d81f74fe76c82f867564b6c2995e69f596df0f588eba16f1a43b69ce06485d68231a0c83fed8469b41eba0e390c126f@139.59.146.42:30303",
|
||||||
|
"enode://2aa81bd0a761cd4f02c934dcf3f81c5b65953e51ab5ba03ceb1f125eb06418a1cdffb1c9d01871aa7bd456f3fce35e745608189ad1164f72b2161634b0c3f6ea@188.166.240.190:30303",
|
||||||
|
"enode://c5900cdd6d20795d58372f42dfbab9d664c27bb97e9c27972741942736e919122f9bac28e74cbc58e4ff195475ea90d9880b71a37af5b5a8cb41d843f765cff8@174.138.79.48:30303"
|
||||||
|
]
|
||||||
|
}
|
@ -48,7 +48,10 @@
|
|||||||
},
|
},
|
||||||
"nodes": [
|
"nodes": [
|
||||||
"enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303",
|
"enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303",
|
||||||
"enode://ceb5c0f85eb994dbe9693bf46d99b03f6b838d17cc74e68d5eb003171ff39e5f120b17f965b267c319303f94d80b9d994b77062fb1486d76ce95d9f3d8fe1cb4@46.101.122.141:30303"
|
"enode://ceb5c0f85eb994dbe9693bf46d99b03f6b838d17cc74e68d5eb003171ff39e5f120b17f965b267c319303f94d80b9d994b77062fb1486d76ce95d9f3d8fe1cb4@46.101.122.141:30303",
|
||||||
|
"enode://fb28713820e718066a2f5df6250ae9d07cff22f672dbf26be6c75d088f821a9ad230138ba492c533a80407d054b1436ef18e951bb65e6901553516c8dffe8ff0@104.155.176.151:30304",
|
||||||
|
"enode://afdc6076b9bf3e7d3d01442d6841071e84c76c73a7016cb4f35c0437df219db38565766234448f1592a07ba5295a867f0ce87b359bf50311ed0b830a2361392d@104.154.136.117:30403",
|
||||||
|
"enode://21101a9597b79e933e17bc94ef3506fe99a137808907aa8fefa67eea4b789792ad11fb391f38b00087f8800a2d3dff011572b62a31232133dd1591ac2d1502c8@104.198.71.200:30403"
|
||||||
],
|
],
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||||
|
File diff suppressed because one or more lines are too long
@ -38,7 +38,7 @@
|
|||||||
"timestamp": "0x00",
|
"timestamp": "0x00",
|
||||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"extraData": "0x",
|
"extraData": "0x",
|
||||||
"gasLimit": "0x2fefd8"
|
"gasLimit": "0x222222"
|
||||||
},
|
},
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
"timestamp": "0x00",
|
"timestamp": "0x00",
|
||||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"extraData": "0x",
|
"extraData": "0x",
|
||||||
"gasLimit": "0x2fefd8"
|
"gasLimit": "0x222222"
|
||||||
},
|
},
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||||
|
@ -253,7 +253,7 @@ impl Client {
|
|||||||
if let Some(reg_addr) = client.additional_params().get("registrar").and_then(|s| Address::from_str(s).ok()) {
|
if let Some(reg_addr) = client.additional_params().get("registrar").and_then(|s| Address::from_str(s).ok()) {
|
||||||
trace!(target: "client", "Found registrar at {}", reg_addr);
|
trace!(target: "client", "Found registrar at {}", reg_addr);
|
||||||
let weak = Arc::downgrade(&client);
|
let weak = Arc::downgrade(&client);
|
||||||
let registrar = Registry::new(reg_addr, move |a, d| weak.upgrade().ok_or("No client!".into()).and_then(|c| c.call_contract(a, d)));
|
let registrar = Registry::new(reg_addr, move |a, d| weak.upgrade().ok_or("No client!".into()).and_then(|c| c.call_contract(BlockId::Latest, a, d)));
|
||||||
*client.registrar.lock() = Some(registrar);
|
*client.registrar.lock() = Some(registrar);
|
||||||
}
|
}
|
||||||
Ok(client)
|
Ok(client)
|
||||||
@ -1428,7 +1428,7 @@ impl BlockChainClient for Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_contract(&self, address: Address, data: Bytes) -> Result<Bytes, String> {
|
fn call_contract(&self, block_id: BlockId, address: Address, data: Bytes) -> Result<Bytes, String> {
|
||||||
let from = Address::default();
|
let from = Address::default();
|
||||||
let transaction = Transaction {
|
let transaction = Transaction {
|
||||||
nonce: self.latest_nonce(&from),
|
nonce: self.latest_nonce(&from),
|
||||||
@ -1439,7 +1439,7 @@ impl BlockChainClient for Client {
|
|||||||
data: data,
|
data: data,
|
||||||
}.fake_sign(from);
|
}.fake_sign(from);
|
||||||
|
|
||||||
self.call(&transaction, BlockId::Latest, Default::default())
|
self.call(&transaction, block_id, Default::default())
|
||||||
.map_err(|e| format!("{:?}", e))
|
.map_err(|e| format!("{:?}", e))
|
||||||
.map(|executed| {
|
.map(|executed| {
|
||||||
executed.output
|
executed.output
|
||||||
@ -1612,7 +1612,6 @@ impl ::client::ProvingBlockChainClient for Client {
|
|||||||
_ => return Some(state.drop().1.extract_proof()),
|
_ => return Some(state.drop().1.extract_proof()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Client {
|
impl Drop for Client {
|
||||||
|
File diff suppressed because one or more lines are too long
@ -732,7 +732,7 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_contract(&self, _address: Address, _data: Bytes) -> Result<Bytes, String> { Ok(vec![]) }
|
fn call_contract(&self, _id: BlockId, _address: Address, _data: Bytes) -> Result<Bytes, String> { Ok(vec![]) }
|
||||||
|
|
||||||
fn transact_contract(&self, address: Address, data: Bytes) -> Result<TransactionImportResult, EthcoreError> {
|
fn transact_contract(&self, address: Address, data: Bytes) -> Result<TransactionImportResult, EthcoreError> {
|
||||||
let transaction = Transaction {
|
let transaction = Transaction {
|
||||||
|
@ -256,7 +256,7 @@ pub trait BlockChainClient : Sync + Send {
|
|||||||
fn pruning_info(&self) -> PruningInfo;
|
fn pruning_info(&self) -> PruningInfo;
|
||||||
|
|
||||||
/// Like `call`, but with various defaults. Designed to be used for calling contracts.
|
/// Like `call`, but with various defaults. Designed to be used for calling contracts.
|
||||||
fn call_contract(&self, address: Address, data: Bytes) -> Result<Bytes, String>;
|
fn call_contract(&self, id: BlockId, address: Address, data: Bytes) -> Result<Bytes, String>;
|
||||||
|
|
||||||
/// Import a transaction: used for misbehaviour reporting.
|
/// Import a transaction: used for misbehaviour reporting.
|
||||||
fn transact_contract(&self, address: Address, data: Bytes) -> Result<TransactionImportResult, EthcoreError>;
|
fn transact_contract(&self, address: Address, data: Bytes) -> Result<TransactionImportResult, EthcoreError>;
|
||||||
|
@ -47,6 +47,8 @@ pub struct AuthorityRoundParams {
|
|||||||
pub step_duration: Duration,
|
pub step_duration: Duration,
|
||||||
/// Block reward.
|
/// Block reward.
|
||||||
pub block_reward: U256,
|
pub block_reward: U256,
|
||||||
|
/// Namereg contract address.
|
||||||
|
pub registrar: Address,
|
||||||
/// Starting step,
|
/// Starting step,
|
||||||
pub start_step: Option<u64>,
|
pub start_step: Option<u64>,
|
||||||
/// Valid validators.
|
/// Valid validators.
|
||||||
@ -60,6 +62,7 @@ impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
|
|||||||
step_duration: Duration::from_secs(p.step_duration.into()),
|
step_duration: Duration::from_secs(p.step_duration.into()),
|
||||||
validators: p.validators,
|
validators: p.validators,
|
||||||
block_reward: p.block_reward.map_or_else(U256::zero, Into::into),
|
block_reward: p.block_reward.map_or_else(U256::zero, Into::into),
|
||||||
|
registrar: p.registrar.map_or_else(Address::new, Into::into),
|
||||||
start_step: p.start_step.map(Into::into),
|
start_step: p.start_step.map(Into::into),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,6 +74,7 @@ pub struct AuthorityRound {
|
|||||||
params: CommonParams,
|
params: CommonParams,
|
||||||
gas_limit_bound_divisor: U256,
|
gas_limit_bound_divisor: U256,
|
||||||
block_reward: U256,
|
block_reward: U256,
|
||||||
|
registrar: Address,
|
||||||
step_duration: Duration,
|
step_duration: Duration,
|
||||||
builtins: BTreeMap<Address, Builtin>,
|
builtins: BTreeMap<Address, Builtin>,
|
||||||
transition_service: IoService<()>,
|
transition_service: IoService<()>,
|
||||||
@ -79,6 +83,8 @@ pub struct AuthorityRound {
|
|||||||
client: RwLock<Option<Weak<EngineClient>>>,
|
client: RwLock<Option<Weak<EngineClient>>>,
|
||||||
signer: EngineSigner,
|
signer: EngineSigner,
|
||||||
validators: Box<ValidatorSet + Send + Sync>,
|
validators: Box<ValidatorSet + Send + Sync>,
|
||||||
|
/// Is this Engine just for testing (prevents step calibration).
|
||||||
|
calibrate_step: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn header_step(header: &Header) -> Result<usize, ::rlp::DecoderError> {
|
fn header_step(header: &Header) -> Result<usize, ::rlp::DecoderError> {
|
||||||
@ -109,6 +115,7 @@ impl AuthorityRound {
|
|||||||
params: params,
|
params: params,
|
||||||
gas_limit_bound_divisor: our_params.gas_limit_bound_divisor,
|
gas_limit_bound_divisor: our_params.gas_limit_bound_divisor,
|
||||||
block_reward: our_params.block_reward,
|
block_reward: our_params.block_reward,
|
||||||
|
registrar: our_params.registrar,
|
||||||
step_duration: our_params.step_duration,
|
step_duration: our_params.step_duration,
|
||||||
builtins: builtins,
|
builtins: builtins,
|
||||||
transition_service: IoService::<()>::start()?,
|
transition_service: IoService::<()>::start()?,
|
||||||
@ -117,6 +124,7 @@ impl AuthorityRound {
|
|||||||
client: RwLock::new(None),
|
client: RwLock::new(None),
|
||||||
signer: Default::default(),
|
signer: Default::default(),
|
||||||
validators: new_validator_set(our_params.validators),
|
validators: new_validator_set(our_params.validators),
|
||||||
|
calibrate_step: our_params.start_step.is_none(),
|
||||||
});
|
});
|
||||||
// Do not initialize timeouts for tests.
|
// Do not initialize timeouts for tests.
|
||||||
if should_timeout {
|
if should_timeout {
|
||||||
@ -126,6 +134,12 @@ impl AuthorityRound {
|
|||||||
Ok(engine)
|
Ok(engine)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn calibrate_step(&self) {
|
||||||
|
if self.calibrate_step {
|
||||||
|
self.step.store((unix_now().as_secs() / self.step_duration.as_secs()) as usize, AtomicOrdering::SeqCst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn remaining_step_duration(&self) -> Duration {
|
fn remaining_step_duration(&self) -> Duration {
|
||||||
let now = unix_now();
|
let now = unix_now();
|
||||||
let step_end = self.step_duration * (self.step.load(AtomicOrdering::SeqCst) as u32 + 1);
|
let step_end = self.step_duration * (self.step.load(AtomicOrdering::SeqCst) as u32 + 1);
|
||||||
@ -136,12 +150,22 @@ impl AuthorityRound {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step_proposer(&self, step: usize) -> Address {
|
fn step_proposer(&self, bh: &H256, step: usize) -> Address {
|
||||||
self.validators.get(step)
|
self.validators.get(bh, step)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_step_proposer(&self, step: usize, address: &Address) -> bool {
|
fn is_step_proposer(&self, bh: &H256, step: usize, address: &Address) -> bool {
|
||||||
self.step_proposer(step) == *address
|
self.step_proposer(bh, step) == *address
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_future_step(&self, step: usize) -> bool {
|
||||||
|
if step > self.step.load(AtomicOrdering::SeqCst) + 1 {
|
||||||
|
// Make absolutely sure that the step is correct.
|
||||||
|
self.calibrate_step();
|
||||||
|
step > self.step.load(AtomicOrdering::SeqCst) + 1
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,11 +200,16 @@ impl IoHandler<()> for TransitionHandler {
|
|||||||
|
|
||||||
impl Engine for AuthorityRound {
|
impl Engine for AuthorityRound {
|
||||||
fn name(&self) -> &str { "AuthorityRound" }
|
fn name(&self) -> &str { "AuthorityRound" }
|
||||||
|
|
||||||
fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) }
|
fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) }
|
||||||
|
|
||||||
/// Two fields - consensus step and the corresponding proposer signature.
|
/// Two fields - consensus step and the corresponding proposer signature.
|
||||||
fn seal_fields(&self) -> usize { 2 }
|
fn seal_fields(&self) -> usize { 2 }
|
||||||
|
|
||||||
fn params(&self) -> &CommonParams { &self.params }
|
fn params(&self) -> &CommonParams { &self.params }
|
||||||
|
|
||||||
|
fn additional_params(&self) -> HashMap<String, String> { hash_map!["registrar".to_owned() => self.registrar.hex()] }
|
||||||
|
|
||||||
fn builtins(&self) -> &BTreeMap<Address, Builtin> { &self.builtins }
|
fn builtins(&self) -> &BTreeMap<Address, Builtin> { &self.builtins }
|
||||||
|
|
||||||
fn step(&self) {
|
fn step(&self) {
|
||||||
@ -221,7 +250,7 @@ impl Engine for AuthorityRound {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn seals_internally(&self) -> Option<bool> {
|
fn seals_internally(&self) -> Option<bool> {
|
||||||
Some(self.validators.contains(&self.signer.address()))
|
Some(self.signer.address() != Address::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to seal the block internally.
|
/// Attempt to seal the block internally.
|
||||||
@ -232,7 +261,7 @@ impl Engine for AuthorityRound {
|
|||||||
if self.proposed.load(AtomicOrdering::SeqCst) { return Seal::None; }
|
if self.proposed.load(AtomicOrdering::SeqCst) { return Seal::None; }
|
||||||
let header = block.header();
|
let header = block.header();
|
||||||
let step = self.step.load(AtomicOrdering::SeqCst);
|
let step = self.step.load(AtomicOrdering::SeqCst);
|
||||||
if self.is_step_proposer(step, header.author()) {
|
if self.is_step_proposer(header.parent_hash(), step, header.author()) {
|
||||||
if let Ok(signature) = self.signer.sign(header.bare_hash()) {
|
if let Ok(signature) = self.signer.sign(header.bare_hash()) {
|
||||||
trace!(target: "engine", "generate_seal: Issuing a block for step {}.", step);
|
trace!(target: "engine", "generate_seal: Issuing a block for step {}.", step);
|
||||||
self.proposed.store(true, AtomicOrdering::SeqCst);
|
self.proposed.store(true, AtomicOrdering::SeqCst);
|
||||||
@ -271,32 +300,32 @@ impl Engine for AuthorityRound {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the signature belongs to the correct proposer.
|
fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||||
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
|
||||||
let header_step = header_step(header)?;
|
|
||||||
// Give one step slack if step is lagging, double vote is still not possible.
|
|
||||||
if header_step <= self.step.load(AtomicOrdering::SeqCst) + 1 {
|
|
||||||
let proposer_signature = header_signature(header)?;
|
|
||||||
let correct_proposer = self.step_proposer(header_step);
|
|
||||||
if verify_address(&correct_proposer, &proposer_signature, &header.bare_hash())? {
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
}
|
||||||
trace!(target: "engine", "verify_block_unordered: bad proposer for step: {}", header_step);
|
|
||||||
Err(EngineError::NotProposer(Mismatch { expected: correct_proposer, found: header.author().clone() }))?
|
/// Do the validator and gas limit validation.
|
||||||
}
|
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||||
} else {
|
let step = header_step(header)?;
|
||||||
|
// Give one step slack if step is lagging, double vote is still not possible.
|
||||||
|
if self.is_future_step(step) {
|
||||||
trace!(target: "engine", "verify_block_unordered: block from the future");
|
trace!(target: "engine", "verify_block_unordered: block from the future");
|
||||||
self.validators.report_benign(header.author());
|
self.validators.report_benign(header.author());
|
||||||
Err(BlockError::InvalidSeal)?
|
Err(BlockError::InvalidSeal)?
|
||||||
|
} else {
|
||||||
|
let proposer_signature = header_signature(header)?;
|
||||||
|
let correct_proposer = self.step_proposer(header.parent_hash(), step);
|
||||||
|
if !verify_address(&correct_proposer, &proposer_signature, &header.bare_hash())? {
|
||||||
|
trace!(target: "engine", "verify_block_unordered: bad proposer for step: {}", step);
|
||||||
|
Err(EngineError::NotProposer(Mismatch { expected: correct_proposer, found: header.author().clone() }))?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
// Do not calculate difficulty for genesis blocks.
|
||||||
if header.number() == 0 {
|
if header.number() == 0 {
|
||||||
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() })));
|
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() })));
|
||||||
}
|
}
|
||||||
|
|
||||||
let step = header_step(header)?;
|
|
||||||
// Check if parent is from a previous step.
|
// Check if parent is from a previous step.
|
||||||
if step == header_step(parent)? {
|
if step == header_step(parent)? {
|
||||||
trace!(target: "engine", "Multiple blocks proposed for step {}.", step);
|
trace!(target: "engine", "Multiple blocks proposed for step {}.", step);
|
||||||
@ -384,7 +413,7 @@ mod tests {
|
|||||||
let mut header: Header = Header::default();
|
let mut header: Header = Header::default();
|
||||||
header.set_seal(vec![encode(&H520::default()).to_vec()]);
|
header.set_seal(vec![encode(&H520::default()).to_vec()]);
|
||||||
|
|
||||||
let verify_result = engine.verify_block_unordered(&header, None);
|
let verify_result = engine.verify_block_family(&header, &Default::default(), None);
|
||||||
assert!(verify_result.is_err());
|
assert!(verify_result.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -422,10 +451,14 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn proposer_switching() {
|
fn proposer_switching() {
|
||||||
let mut header: Header = Header::default();
|
|
||||||
let tap = AccountProvider::transient_provider();
|
let tap = AccountProvider::transient_provider();
|
||||||
let addr = tap.insert_account(Secret::from_slice(&"0".sha3()).unwrap(), "0").unwrap();
|
let addr = tap.insert_account(Secret::from_slice(&"0".sha3()).unwrap(), "0").unwrap();
|
||||||
|
let mut parent_header: Header = Header::default();
|
||||||
|
parent_header.set_seal(vec![encode(&0usize).to_vec()]);
|
||||||
|
parent_header.set_gas_limit(U256::from_str("222222").unwrap());
|
||||||
|
let mut header: Header = Header::default();
|
||||||
|
header.set_number(1);
|
||||||
|
header.set_gas_limit(U256::from_str("222222").unwrap());
|
||||||
header.set_author(addr);
|
header.set_author(addr);
|
||||||
|
|
||||||
let engine = Spec::new_test_round().engine;
|
let engine = Spec::new_test_round().engine;
|
||||||
@ -434,17 +467,22 @@ mod tests {
|
|||||||
// Two validators.
|
// Two validators.
|
||||||
// Spec starts with step 2.
|
// Spec starts with step 2.
|
||||||
header.set_seal(vec![encode(&2usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
header.set_seal(vec![encode(&2usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||||
assert!(engine.verify_block_seal(&header).is_err());
|
assert!(engine.verify_block_family(&header, &parent_header, None).is_err());
|
||||||
header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||||
assert!(engine.verify_block_seal(&header).is_ok());
|
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rejects_future_block() {
|
fn rejects_future_block() {
|
||||||
let mut header: Header = Header::default();
|
|
||||||
let tap = AccountProvider::transient_provider();
|
let tap = AccountProvider::transient_provider();
|
||||||
let addr = tap.insert_account(Secret::from_slice(&"0".sha3()).unwrap(), "0").unwrap();
|
let addr = tap.insert_account(Secret::from_slice(&"0".sha3()).unwrap(), "0").unwrap();
|
||||||
|
|
||||||
|
let mut parent_header: Header = Header::default();
|
||||||
|
parent_header.set_seal(vec![encode(&0usize).to_vec()]);
|
||||||
|
parent_header.set_gas_limit(U256::from_str("222222").unwrap());
|
||||||
|
let mut header: Header = Header::default();
|
||||||
|
header.set_number(1);
|
||||||
|
header.set_gas_limit(U256::from_str("222222").unwrap());
|
||||||
header.set_author(addr);
|
header.set_author(addr);
|
||||||
|
|
||||||
let engine = Spec::new_test_round().engine;
|
let engine = Spec::new_test_round().engine;
|
||||||
@ -453,8 +491,8 @@ mod tests {
|
|||||||
// Two validators.
|
// Two validators.
|
||||||
// Spec starts with step 2.
|
// Spec starts with step 2.
|
||||||
header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||||
assert!(engine.verify_block_seal(&header).is_ok());
|
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
|
||||||
header.set_seal(vec![encode(&5usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
header.set_seal(vec![encode(&5usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||||
assert!(engine.verify_block_seal(&header).is_err());
|
assert!(engine.verify_block_family(&header, &parent_header, None).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,14 +104,14 @@ impl Engine for BasicAuthority {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn seals_internally(&self) -> Option<bool> {
|
fn seals_internally(&self) -> Option<bool> {
|
||||||
Some(self.validators.contains(&self.signer.address()))
|
Some(self.signer.address() != Address::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to seal the block internally.
|
/// Attempt to seal the block internally.
|
||||||
fn generate_seal(&self, block: &ExecutedBlock) -> Seal {
|
fn generate_seal(&self, block: &ExecutedBlock) -> Seal {
|
||||||
let header = block.header();
|
let header = block.header();
|
||||||
let author = header.author();
|
let author = header.author();
|
||||||
if self.validators.contains(author) {
|
if self.validators.contains(header.parent_hash(), author) {
|
||||||
// account should be pernamently unlocked, otherwise sealing will fail
|
// account should be pernamently unlocked, otherwise sealing will fail
|
||||||
if let Ok(signature) = self.signer.sign(header.bare_hash()) {
|
if let Ok(signature) = self.signer.sign(header.bare_hash()) {
|
||||||
return Seal::Regular(vec![::rlp::encode(&(&H520::from(signature) as &[u8])).to_vec()]);
|
return Seal::Regular(vec![::rlp::encode(&(&H520::from(signature) as &[u8])).to_vec()]);
|
||||||
@ -133,20 +133,20 @@ impl Engine for BasicAuthority {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
||||||
use rlp::{UntrustedRlp, View};
|
|
||||||
|
|
||||||
// check the signature is legit.
|
|
||||||
let sig = UntrustedRlp::new(&header.seal()[0]).as_val::<H520>()?;
|
|
||||||
let signer = public_to_address(&recover(&sig.into(), &header.bare_hash())?);
|
|
||||||
if !self.validators.contains(&signer) {
|
|
||||||
return Err(BlockError::InvalidSeal)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
||||||
// we should not calculate difficulty for genesis blocks
|
use rlp::{UntrustedRlp, View};
|
||||||
|
// Check if the signature belongs to a validator, can depend on parent state.
|
||||||
|
let sig = UntrustedRlp::new(&header.seal()[0]).as_val::<H520>()?;
|
||||||
|
let signer = public_to_address(&recover(&sig.into(), &header.bare_hash())?);
|
||||||
|
if !self.validators.contains(header.parent_hash(), &signer) {
|
||||||
|
return Err(BlockError::InvalidSeal)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not calculate difficulty for genesis blocks.
|
||||||
if header.number() == 0 {
|
if header.number() == 0 {
|
||||||
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() })));
|
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() })));
|
||||||
}
|
}
|
||||||
@ -239,7 +239,7 @@ mod tests {
|
|||||||
let mut header: Header = Header::default();
|
let mut header: Header = Header::default();
|
||||||
header.set_seal(vec![::rlp::encode(&H520::default()).to_vec()]);
|
header.set_seal(vec![::rlp::encode(&H520::default()).to_vec()]);
|
||||||
|
|
||||||
let verify_result = engine.verify_block_unordered(&header, None);
|
let verify_result = engine.verify_block_family(&header, &Default::default(), None);
|
||||||
assert!(verify_result.is_err());
|
assert!(verify_result.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use util::Address;
|
use util::{Address, HashMap};
|
||||||
use builtin::Builtin;
|
use builtin::Builtin;
|
||||||
use engines::{Engine, Seal};
|
use engines::{Engine, Seal};
|
||||||
use env_info::EnvInfo;
|
use env_info::EnvInfo;
|
||||||
@ -26,14 +26,16 @@ use block::ExecutedBlock;
|
|||||||
/// An engine which does not provide any consensus mechanism, just seals blocks internally.
|
/// An engine which does not provide any consensus mechanism, just seals blocks internally.
|
||||||
pub struct InstantSeal {
|
pub struct InstantSeal {
|
||||||
params: CommonParams,
|
params: CommonParams,
|
||||||
|
registrar: Address,
|
||||||
builtins: BTreeMap<Address, Builtin>,
|
builtins: BTreeMap<Address, Builtin>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstantSeal {
|
impl InstantSeal {
|
||||||
/// Returns new instance of InstantSeal with default VM Factory
|
/// Returns new instance of InstantSeal with default VM Factory
|
||||||
pub fn new(params: CommonParams, builtins: BTreeMap<Address, Builtin>) -> Self {
|
pub fn new(params: CommonParams, registrar: Address, builtins: BTreeMap<Address, Builtin>) -> Self {
|
||||||
InstantSeal {
|
InstantSeal {
|
||||||
params: params,
|
params: params,
|
||||||
|
registrar: registrar,
|
||||||
builtins: builtins,
|
builtins: builtins,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -48,6 +50,10 @@ impl Engine for InstantSeal {
|
|||||||
&self.params
|
&self.params
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn additional_params(&self) -> HashMap<String, String> {
|
||||||
|
hash_map!["registrar".to_owned() => self.registrar.hex()]
|
||||||
|
}
|
||||||
|
|
||||||
fn builtins(&self) -> &BTreeMap<Address, Builtin> {
|
fn builtins(&self) -> &BTreeMap<Address, Builtin> {
|
||||||
&self.builtins
|
&self.builtins
|
||||||
}
|
}
|
||||||
@ -76,9 +82,9 @@ mod tests {
|
|||||||
fn instant_can_seal() {
|
fn instant_can_seal() {
|
||||||
let spec = Spec::new_instant();
|
let spec = Spec::new_instant();
|
||||||
let engine = &*spec.engine;
|
let engine = &*spec.engine;
|
||||||
let genesis_header = spec.genesis_header();
|
|
||||||
let mut db_result = get_temp_state_db();
|
let mut db_result = get_temp_state_db();
|
||||||
let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap();
|
let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap();
|
||||||
|
let genesis_header = spec.genesis_header();
|
||||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::default(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::default(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||||
let b = b.close_and_lock();
|
let b = b.close_and_lock();
|
||||||
|
@ -166,7 +166,9 @@ pub trait Engine : Sync + Send {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The network ID that transactions should be signed with.
|
/// The network ID that transactions should be signed with.
|
||||||
fn signing_network_id(&self, _env_info: &EnvInfo) -> Option<u64> { None }
|
fn signing_network_id(&self, _env_info: &EnvInfo) -> Option<u64> {
|
||||||
|
Some(self.params().chain_id)
|
||||||
|
}
|
||||||
|
|
||||||
/// Verify the seal of a block. This is an auxilliary method that actually just calls other `verify_` methods
|
/// Verify the seal of a block. This is an auxilliary method that actually just calls other `verify_` methods
|
||||||
/// to get the job done. By default it must pass `verify_basic` and `verify_block_unordered`. If more or fewer
|
/// to get the job done. By default it must pass `verify_basic` and `verify_block_unordered`. If more or fewer
|
||||||
|
@ -78,6 +78,7 @@ pub struct Tendermint {
|
|||||||
step_service: IoService<Step>,
|
step_service: IoService<Step>,
|
||||||
client: RwLock<Option<Weak<EngineClient>>>,
|
client: RwLock<Option<Weak<EngineClient>>>,
|
||||||
block_reward: U256,
|
block_reward: U256,
|
||||||
|
registrar: Address,
|
||||||
/// Blockchain height.
|
/// Blockchain height.
|
||||||
height: AtomicUsize,
|
height: AtomicUsize,
|
||||||
/// Consensus view.
|
/// Consensus view.
|
||||||
@ -94,6 +95,8 @@ pub struct Tendermint {
|
|||||||
last_lock: AtomicUsize,
|
last_lock: AtomicUsize,
|
||||||
/// Bare hash of the proposed block, used for seal submission.
|
/// Bare hash of the proposed block, used for seal submission.
|
||||||
proposal: RwLock<Option<H256>>,
|
proposal: RwLock<Option<H256>>,
|
||||||
|
/// Hash of the proposal parent block.
|
||||||
|
proposal_parent: RwLock<H256>,
|
||||||
/// Set used to determine the current validators.
|
/// Set used to determine the current validators.
|
||||||
validators: Box<ValidatorSet + Send + Sync>,
|
validators: Box<ValidatorSet + Send + Sync>,
|
||||||
}
|
}
|
||||||
@ -109,14 +112,16 @@ impl Tendermint {
|
|||||||
client: RwLock::new(None),
|
client: RwLock::new(None),
|
||||||
step_service: IoService::<Step>::start()?,
|
step_service: IoService::<Step>::start()?,
|
||||||
block_reward: our_params.block_reward,
|
block_reward: our_params.block_reward,
|
||||||
|
registrar: our_params.registrar,
|
||||||
height: AtomicUsize::new(1),
|
height: AtomicUsize::new(1),
|
||||||
view: AtomicUsize::new(0),
|
view: AtomicUsize::new(0),
|
||||||
step: RwLock::new(Step::Propose),
|
step: RwLock::new(Step::Propose),
|
||||||
votes: VoteCollector::default(),
|
votes: Default::default(),
|
||||||
signer: Default::default(),
|
signer: Default::default(),
|
||||||
lock_change: RwLock::new(None),
|
lock_change: RwLock::new(None),
|
||||||
last_lock: AtomicUsize::new(0),
|
last_lock: AtomicUsize::new(0),
|
||||||
proposal: RwLock::new(None),
|
proposal: RwLock::new(None),
|
||||||
|
proposal_parent: Default::default(),
|
||||||
validators: new_validator_set(our_params.validators),
|
validators: new_validator_set(our_params.validators),
|
||||||
});
|
});
|
||||||
let handler = TransitionHandler::new(Arc::downgrade(&engine) as Weak<Engine>, Box::new(our_params.timeouts));
|
let handler = TransitionHandler::new(Arc::downgrade(&engine) as Weak<Engine>, Box::new(our_params.timeouts));
|
||||||
@ -230,7 +235,7 @@ impl Tendermint {
|
|||||||
let height = self.height.load(AtomicOrdering::SeqCst);
|
let height = self.height.load(AtomicOrdering::SeqCst);
|
||||||
if let Some(block_hash) = *self.proposal.read() {
|
if let Some(block_hash) = *self.proposal.read() {
|
||||||
// Generate seal and remove old votes.
|
// Generate seal and remove old votes.
|
||||||
if self.is_signer_proposer() {
|
if self.is_signer_proposer(&*self.proposal_parent.read()) {
|
||||||
let proposal_step = VoteStep::new(height, view, Step::Propose);
|
let proposal_step = VoteStep::new(height, view, Step::Propose);
|
||||||
let precommit_step = VoteStep::new(proposal_step.height, proposal_step.view, Step::Precommit);
|
let precommit_step = VoteStep::new(proposal_step.height, proposal_step.view, Step::Precommit);
|
||||||
if let Some(seal) = self.votes.seal_signatures(proposal_step, precommit_step, &block_hash) {
|
if let Some(seal) = self.votes.seal_signatures(proposal_step, precommit_step, &block_hash) {
|
||||||
@ -252,23 +257,23 @@ impl Tendermint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_authority(&self, address: &Address) -> bool {
|
fn is_authority(&self, address: &Address) -> bool {
|
||||||
self.validators.contains(address)
|
self.validators.contains(&*self.proposal_parent.read(), address)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_above_threshold(&self, n: usize) -> bool {
|
fn is_above_threshold(&self, n: usize) -> bool {
|
||||||
n > self.validators.count() * 2/3
|
n > self.validators.count(&*self.proposal_parent.read()) * 2/3
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the designated for the given view.
|
/// Find the designated for the given view.
|
||||||
fn view_proposer(&self, height: Height, view: View) -> Address {
|
fn view_proposer(&self, bh: &H256, height: Height, view: View) -> Address {
|
||||||
let proposer_nonce = height + view;
|
let proposer_nonce = height + view;
|
||||||
trace!(target: "engine", "Proposer nonce: {}", proposer_nonce);
|
trace!(target: "engine", "Proposer nonce: {}", proposer_nonce);
|
||||||
self.validators.get(proposer_nonce)
|
self.validators.get(bh, proposer_nonce)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if address is a proposer for given view.
|
/// Check if address is a proposer for given view.
|
||||||
fn is_view_proposer(&self, height: Height, view: View, address: &Address) -> Result<(), EngineError> {
|
fn is_view_proposer(&self, bh: &H256, height: Height, view: View, address: &Address) -> Result<(), EngineError> {
|
||||||
let proposer = self.view_proposer(height, view);
|
let proposer = self.view_proposer(bh, height, view);
|
||||||
if proposer == *address {
|
if proposer == *address {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -277,8 +282,8 @@ impl Tendermint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Check if current signer is the current proposer.
|
/// Check if current signer is the current proposer.
|
||||||
fn is_signer_proposer(&self) -> bool {
|
fn is_signer_proposer(&self, bh: &H256) -> bool {
|
||||||
let proposer = self.view_proposer(self.height.load(AtomicOrdering::SeqCst), self.view.load(AtomicOrdering::SeqCst));
|
let proposer = self.view_proposer(bh, self.height.load(AtomicOrdering::SeqCst), self.view.load(AtomicOrdering::SeqCst));
|
||||||
self.signer.is_address(&proposer)
|
self.signer.is_address(&proposer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,14 +374,20 @@ impl Tendermint {
|
|||||||
|
|
||||||
impl Engine for Tendermint {
|
impl Engine for Tendermint {
|
||||||
fn name(&self) -> &str { "Tendermint" }
|
fn name(&self) -> &str { "Tendermint" }
|
||||||
|
|
||||||
fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) }
|
fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) }
|
||||||
|
|
||||||
/// (consensus view, proposal signature, authority signatures)
|
/// (consensus view, proposal signature, authority signatures)
|
||||||
fn seal_fields(&self) -> usize { 3 }
|
fn seal_fields(&self) -> usize { 3 }
|
||||||
|
|
||||||
fn params(&self) -> &CommonParams { &self.params }
|
fn params(&self) -> &CommonParams { &self.params }
|
||||||
|
|
||||||
|
fn additional_params(&self) -> HashMap<String, String> { hash_map!["registrar".to_owned() => self.registrar.hex()] }
|
||||||
|
|
||||||
fn builtins(&self) -> &BTreeMap<Address, Builtin> { &self.builtins }
|
fn builtins(&self) -> &BTreeMap<Address, Builtin> { &self.builtins }
|
||||||
|
|
||||||
fn maximum_uncle_count(&self) -> usize { 0 }
|
fn maximum_uncle_count(&self) -> usize { 0 }
|
||||||
|
|
||||||
fn maximum_uncle_age(&self) -> usize { 0 }
|
fn maximum_uncle_age(&self) -> usize { 0 }
|
||||||
|
|
||||||
/// Additional engine-specific information for the user/developer concerning `header`.
|
/// Additional engine-specific information for the user/developer concerning `header`.
|
||||||
@ -411,7 +422,7 @@ impl Engine for Tendermint {
|
|||||||
|
|
||||||
/// Should this node participate.
|
/// Should this node participate.
|
||||||
fn seals_internally(&self) -> Option<bool> {
|
fn seals_internally(&self) -> Option<bool> {
|
||||||
Some(self.is_authority(&self.signer.address()))
|
Some(self.signer.address() != Address::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to seal generate a proposal seal.
|
/// Attempt to seal generate a proposal seal.
|
||||||
@ -419,7 +430,7 @@ impl Engine for Tendermint {
|
|||||||
let header = block.header();
|
let header = block.header();
|
||||||
let author = header.author();
|
let author = header.author();
|
||||||
// Only proposer can generate seal if None was generated.
|
// Only proposer can generate seal if None was generated.
|
||||||
if !self.is_signer_proposer() || self.proposal.read().is_some() {
|
if !self.is_signer_proposer(header.parent_hash()) || self.proposal.read().is_some() {
|
||||||
return Seal::None;
|
return Seal::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,6 +444,7 @@ impl Engine for Tendermint {
|
|||||||
self.votes.vote(ConsensusMessage::new(signature, height, view, Step::Propose, bh), author);
|
self.votes.vote(ConsensusMessage::new(signature, height, view, Step::Propose, bh), author);
|
||||||
// Remember proposal for later seal submission.
|
// Remember proposal for later seal submission.
|
||||||
*self.proposal.write() = bh;
|
*self.proposal.write() = bh;
|
||||||
|
*self.proposal_parent.write() = header.parent_hash().clone();
|
||||||
Seal::Proposal(vec![
|
Seal::Proposal(vec![
|
||||||
::rlp::encode(&view).to_vec(),
|
::rlp::encode(&view).to_vec(),
|
||||||
::rlp::encode(&signature).to_vec(),
|
::rlp::encode(&signature).to_vec(),
|
||||||
@ -497,7 +509,12 @@ impl Engine for Tendermint {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verify validators and gas limit.
|
||||||
|
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||||
let proposal = ConsensusMessage::new_proposal(header)?;
|
let proposal = ConsensusMessage::new_proposal(header)?;
|
||||||
let proposer = proposal.verify()?;
|
let proposer = proposal.verify()?;
|
||||||
if !self.is_authority(&proposer) {
|
if !self.is_authority(&proposer) {
|
||||||
@ -514,7 +531,7 @@ impl Engine for Tendermint {
|
|||||||
Some(a) => a,
|
Some(a) => a,
|
||||||
None => public_to_address(&recover(&precommit.signature.into(), &precommit_hash)?),
|
None => public_to_address(&recover(&precommit.signature.into(), &precommit_hash)?),
|
||||||
};
|
};
|
||||||
if !self.validators.contains(&address) {
|
if !self.validators.contains(header.parent_hash(), &address) {
|
||||||
Err(EngineError::NotAuthorized(address.to_owned()))?
|
Err(EngineError::NotAuthorized(address.to_owned()))?
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,12 +554,9 @@ impl Engine for Tendermint {
|
|||||||
found: signatures_len
|
found: signatures_len
|
||||||
}))?;
|
}))?;
|
||||||
}
|
}
|
||||||
self.is_view_proposer(proposal.vote_step.height, proposal.vote_step.view, &proposer)?;
|
self.is_view_proposer(header.parent_hash(), proposal.vote_step.height, proposal.vote_step.view, &proposer)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
|
||||||
if header.number() == 0 {
|
if header.number() == 0 {
|
||||||
Err(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))?;
|
Err(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))?;
|
||||||
}
|
}
|
||||||
@ -587,6 +601,7 @@ impl Engine for Tendermint {
|
|||||||
debug!(target: "engine", "Received a new proposal {:?} from {}.", proposal.vote_step, proposer);
|
debug!(target: "engine", "Received a new proposal {:?} from {}.", proposal.vote_step, proposer);
|
||||||
if self.is_view(&proposal) {
|
if self.is_view(&proposal) {
|
||||||
*self.proposal.write() = proposal.block_hash.clone();
|
*self.proposal.write() = proposal.block_hash.clone();
|
||||||
|
*self.proposal_parent.write() = header.parent_hash().clone();
|
||||||
}
|
}
|
||||||
self.votes.vote(proposal, &proposer);
|
self.votes.vote(proposal, &proposer);
|
||||||
true
|
true
|
||||||
@ -599,7 +614,7 @@ impl Engine for Tendermint {
|
|||||||
trace!(target: "engine", "Propose timeout.");
|
trace!(target: "engine", "Propose timeout.");
|
||||||
if self.proposal.read().is_none() {
|
if self.proposal.read().is_none() {
|
||||||
// Report the proposer if no proposal was received.
|
// Report the proposer if no proposal was received.
|
||||||
let current_proposer = self.view_proposer(self.height.load(AtomicOrdering::SeqCst), self.view.load(AtomicOrdering::SeqCst));
|
let current_proposer = self.view_proposer(&*self.proposal_parent.read(), self.height.load(AtomicOrdering::SeqCst), self.view.load(AtomicOrdering::SeqCst));
|
||||||
self.validators.report_benign(¤t_proposer);
|
self.validators.report_benign(¤t_proposer);
|
||||||
}
|
}
|
||||||
Step::Prevote
|
Step::Prevote
|
||||||
@ -757,20 +772,25 @@ mod tests {
|
|||||||
let (spec, tap) = setup();
|
let (spec, tap) = setup();
|
||||||
let engine = spec.engine;
|
let engine = spec.engine;
|
||||||
|
|
||||||
let mut header = Header::default();
|
let mut parent_header: Header = Header::default();
|
||||||
let validator = insert_and_unlock(&tap, "0");
|
parent_header.set_gas_limit(U256::from_str("222222").unwrap());
|
||||||
header.set_author(validator);
|
|
||||||
let seal = proposal_seal(&tap, &header, 0);
|
|
||||||
header.set_seal(seal);
|
|
||||||
// Good proposer.
|
|
||||||
assert!(engine.verify_block_unordered(&header.clone(), None).is_ok());
|
|
||||||
|
|
||||||
|
let mut header = Header::default();
|
||||||
|
header.set_number(1);
|
||||||
|
header.set_gas_limit(U256::from_str("222222").unwrap());
|
||||||
let validator = insert_and_unlock(&tap, "1");
|
let validator = insert_and_unlock(&tap, "1");
|
||||||
header.set_author(validator);
|
header.set_author(validator);
|
||||||
let seal = proposal_seal(&tap, &header, 0);
|
let seal = proposal_seal(&tap, &header, 0);
|
||||||
header.set_seal(seal);
|
header.set_seal(seal);
|
||||||
|
// Good proposer.
|
||||||
|
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
|
||||||
|
|
||||||
|
let validator = insert_and_unlock(&tap, "0");
|
||||||
|
header.set_author(validator);
|
||||||
|
let seal = proposal_seal(&tap, &header, 0);
|
||||||
|
header.set_seal(seal);
|
||||||
// Bad proposer.
|
// Bad proposer.
|
||||||
match engine.verify_block_unordered(&header, None) {
|
match engine.verify_block_family(&header, &parent_header, None) {
|
||||||
Err(Error::Engine(EngineError::NotProposer(_))) => {},
|
Err(Error::Engine(EngineError::NotProposer(_))) => {},
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
@ -780,7 +800,7 @@ mod tests {
|
|||||||
let seal = proposal_seal(&tap, &header, 0);
|
let seal = proposal_seal(&tap, &header, 0);
|
||||||
header.set_seal(seal);
|
header.set_seal(seal);
|
||||||
// Not authority.
|
// Not authority.
|
||||||
match engine.verify_block_unordered(&header, None) {
|
match engine.verify_block_family(&header, &parent_header, None) {
|
||||||
Err(Error::Engine(EngineError::NotAuthorized(_))) => {},
|
Err(Error::Engine(EngineError::NotAuthorized(_))) => {},
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
};
|
};
|
||||||
@ -792,19 +812,24 @@ mod tests {
|
|||||||
let (spec, tap) = setup();
|
let (spec, tap) = setup();
|
||||||
let engine = spec.engine;
|
let engine = spec.engine;
|
||||||
|
|
||||||
|
let mut parent_header: Header = Header::default();
|
||||||
|
parent_header.set_gas_limit(U256::from_str("222222").unwrap());
|
||||||
|
|
||||||
let mut header = Header::default();
|
let mut header = Header::default();
|
||||||
|
header.set_number(2);
|
||||||
|
header.set_gas_limit(U256::from_str("222222").unwrap());
|
||||||
let proposer = insert_and_unlock(&tap, "1");
|
let proposer = insert_and_unlock(&tap, "1");
|
||||||
header.set_author(proposer);
|
header.set_author(proposer);
|
||||||
let mut seal = proposal_seal(&tap, &header, 0);
|
let mut seal = proposal_seal(&tap, &header, 0);
|
||||||
|
|
||||||
let vote_info = message_info_rlp(&VoteStep::new(0, 0, Step::Precommit), Some(header.bare_hash()));
|
let vote_info = message_info_rlp(&VoteStep::new(2, 0, Step::Precommit), Some(header.bare_hash()));
|
||||||
let signature1 = tap.sign(proposer, None, vote_info.sha3()).unwrap();
|
let signature1 = tap.sign(proposer, None, vote_info.sha3()).unwrap();
|
||||||
|
|
||||||
seal[2] = ::rlp::encode(&vec![H520::from(signature1.clone())]).to_vec();
|
seal[2] = ::rlp::encode(&vec![H520::from(signature1.clone())]).to_vec();
|
||||||
header.set_seal(seal.clone());
|
header.set_seal(seal.clone());
|
||||||
|
|
||||||
// One good signature is not enough.
|
// One good signature is not enough.
|
||||||
match engine.verify_block_unordered(&header, None) {
|
match engine.verify_block_family(&header, &parent_header, None) {
|
||||||
Err(Error::Engine(EngineError::BadSealFieldSize(_))) => {},
|
Err(Error::Engine(EngineError::BadSealFieldSize(_))) => {},
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
@ -815,7 +840,7 @@ mod tests {
|
|||||||
seal[2] = ::rlp::encode(&vec![H520::from(signature1.clone()), H520::from(signature0.clone())]).to_vec();
|
seal[2] = ::rlp::encode(&vec![H520::from(signature1.clone()), H520::from(signature0.clone())]).to_vec();
|
||||||
header.set_seal(seal.clone());
|
header.set_seal(seal.clone());
|
||||||
|
|
||||||
assert!(engine.verify_block_unordered(&header, None).is_ok());
|
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
|
||||||
|
|
||||||
let bad_voter = insert_and_unlock(&tap, "101");
|
let bad_voter = insert_and_unlock(&tap, "101");
|
||||||
let bad_signature = tap.sign(bad_voter, None, vote_info.sha3()).unwrap();
|
let bad_signature = tap.sign(bad_voter, None, vote_info.sha3()).unwrap();
|
||||||
@ -824,7 +849,7 @@ mod tests {
|
|||||||
header.set_seal(seal);
|
header.set_seal(seal);
|
||||||
|
|
||||||
// One good and one bad signature.
|
// One good and one bad signature.
|
||||||
match engine.verify_block_unordered(&header, None) {
|
match engine.verify_block_family(&header, &parent_header, None) {
|
||||||
Err(Error::Engine(EngineError::NotAuthorized(_))) => {},
|
Err(Error::Engine(EngineError::NotAuthorized(_))) => {},
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
};
|
};
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
//! Tendermint specific parameters.
|
//! Tendermint specific parameters.
|
||||||
|
|
||||||
use ethjson;
|
use ethjson;
|
||||||
use util::{U256, Uint};
|
use util::{U256, Uint, Address, FixedHash};
|
||||||
use time::Duration;
|
use time::Duration;
|
||||||
use super::super::transition::Timeouts;
|
use super::super::transition::Timeouts;
|
||||||
use super::Step;
|
use super::Step;
|
||||||
@ -33,6 +33,8 @@ pub struct TendermintParams {
|
|||||||
pub timeouts: TendermintTimeouts,
|
pub timeouts: TendermintTimeouts,
|
||||||
/// Block reward.
|
/// Block reward.
|
||||||
pub block_reward: U256,
|
pub block_reward: U256,
|
||||||
|
/// Namereg contract address.
|
||||||
|
pub registrar: Address,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Base timeout of each step in ms.
|
/// Base timeout of each step in ms.
|
||||||
@ -88,6 +90,7 @@ impl From<ethjson::spec::TendermintParams> for TendermintParams {
|
|||||||
commit: p.timeout_commit.map_or(dt.commit, to_duration),
|
commit: p.timeout_commit.map_or(dt.commit, to_duration),
|
||||||
},
|
},
|
||||||
block_reward: p.block_reward.map_or_else(U256::zero, Into::into),
|
block_reward: p.block_reward.map_or_else(U256::zero, Into::into),
|
||||||
|
registrar: p.registrar.map_or_else(Address::new, Into::into),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,30 +26,30 @@ use super::safe_contract::ValidatorSafeContract;
|
|||||||
/// The validator contract should have the following interface:
|
/// The validator contract should have the following interface:
|
||||||
/// [{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}]
|
/// [{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}]
|
||||||
pub struct ValidatorContract {
|
pub struct ValidatorContract {
|
||||||
validators: Arc<ValidatorSafeContract>,
|
validators: ValidatorSafeContract,
|
||||||
provider: RwLock<Option<provider::Contract>>,
|
provider: RwLock<Option<provider::Contract>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValidatorContract {
|
impl ValidatorContract {
|
||||||
pub fn new(contract_address: Address) -> Self {
|
pub fn new(contract_address: Address) -> Self {
|
||||||
ValidatorContract {
|
ValidatorContract {
|
||||||
validators: Arc::new(ValidatorSafeContract::new(contract_address)),
|
validators: ValidatorSafeContract::new(contract_address),
|
||||||
provider: RwLock::new(None),
|
provider: RwLock::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValidatorSet for Arc<ValidatorContract> {
|
impl ValidatorSet for ValidatorContract {
|
||||||
fn contains(&self, address: &Address) -> bool {
|
fn contains(&self, bh: &H256, address: &Address) -> bool {
|
||||||
self.validators.contains(address)
|
self.validators.contains(bh, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, nonce: usize) -> Address {
|
fn get(&self, bh: &H256, nonce: usize) -> Address {
|
||||||
self.validators.get(nonce)
|
self.validators.get(bh, nonce)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count(&self) -> usize {
|
fn count(&self, bh: &H256) -> usize {
|
||||||
self.validators.count()
|
self.validators.count(bh)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_malicious(&self, address: &Address) {
|
fn report_malicious(&self, address: &Address) {
|
||||||
@ -144,6 +144,7 @@ mod tests {
|
|||||||
use header::Header;
|
use header::Header;
|
||||||
use account_provider::AccountProvider;
|
use account_provider::AccountProvider;
|
||||||
use miner::MinerService;
|
use miner::MinerService;
|
||||||
|
use types::ids::BlockId;
|
||||||
use client::BlockChainClient;
|
use client::BlockChainClient;
|
||||||
use tests::helpers::generate_dummy_client_with_spec_and_accounts;
|
use tests::helpers::generate_dummy_client_with_spec_and_accounts;
|
||||||
use super::super::ValidatorSet;
|
use super::super::ValidatorSet;
|
||||||
@ -154,8 +155,9 @@ mod tests {
|
|||||||
let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_contract, None);
|
let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_contract, None);
|
||||||
let vc = Arc::new(ValidatorContract::new(Address::from_str("0000000000000000000000000000000000000005").unwrap()));
|
let vc = Arc::new(ValidatorContract::new(Address::from_str("0000000000000000000000000000000000000005").unwrap()));
|
||||||
vc.register_contract(Arc::downgrade(&client));
|
vc.register_contract(Arc::downgrade(&client));
|
||||||
assert!(vc.contains(&Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap()));
|
let last_hash = client.best_block_header().hash();
|
||||||
assert!(vc.contains(&Address::from_str("82a978b3f5962a5b0957d9ee9eef472ee55b42f1").unwrap()));
|
assert!(vc.contains(&last_hash, &Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap()));
|
||||||
|
assert!(vc.contains(&last_hash, &Address::from_str("82a978b3f5962a5b0957d9ee9eef472ee55b42f1").unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -171,18 +173,21 @@ mod tests {
|
|||||||
|
|
||||||
client.miner().set_engine_signer(v1, "".into()).unwrap();
|
client.miner().set_engine_signer(v1, "".into()).unwrap();
|
||||||
let mut header = Header::default();
|
let mut header = Header::default();
|
||||||
let seal = encode(&vec!(5u8)).to_vec();
|
let seal = vec![encode(&5u8).to_vec(), encode(&(&H520::default() as &[u8])).to_vec()];
|
||||||
header.set_seal(vec!(seal));
|
header.set_seal(seal);
|
||||||
header.set_author(v1);
|
header.set_author(v1);
|
||||||
header.set_number(1);
|
header.set_number(2);
|
||||||
|
header.set_parent_hash(client.chain_info().best_block_hash);
|
||||||
|
|
||||||
// `reportBenign` when the designated proposer releases block from the future (bad clock).
|
// `reportBenign` when the designated proposer releases block from the future (bad clock).
|
||||||
assert!(client.engine().verify_block_unordered(&header, None).is_err());
|
assert!(client.engine().verify_block_family(&header, &header, None).is_err());
|
||||||
// Seal a block.
|
// Seal a block.
|
||||||
client.engine().step();
|
client.engine().step();
|
||||||
assert_eq!(client.chain_info().best_block_number, 1);
|
assert_eq!(client.chain_info().best_block_number, 1);
|
||||||
// Check if the unresponsive validator is `disliked`.
|
// Check if the unresponsive validator is `disliked`.
|
||||||
assert_eq!(client.call_contract(validator_contract, "d8f2e0bf".from_hex().unwrap()).unwrap().to_hex(), "0000000000000000000000007d577a597b2742b498cb5cf0c26cdcd726d39e6e");
|
assert_eq!(client.call_contract(BlockId::Latest, validator_contract, "d8f2e0bf".from_hex().unwrap()).unwrap().to_hex(), "0000000000000000000000007d577a597b2742b498cb5cf0c26cdcd726d39e6e");
|
||||||
// Simulate a misbehaving validator by handling a double proposal.
|
// Simulate a misbehaving validator by handling a double proposal.
|
||||||
|
let header = client.best_block_header().decode();
|
||||||
assert!(client.engine().verify_block_family(&header, &header, None).is_err());
|
assert!(client.engine().verify_block_family(&header, &header, None).is_err());
|
||||||
// Seal a block.
|
// Seal a block.
|
||||||
client.engine().step();
|
client.engine().step();
|
||||||
|
@ -21,7 +21,7 @@ mod safe_contract;
|
|||||||
mod contract;
|
mod contract;
|
||||||
|
|
||||||
use std::sync::Weak;
|
use std::sync::Weak;
|
||||||
use util::{Address, Arc};
|
use util::{Address, H256};
|
||||||
use ethjson::spec::ValidatorSet as ValidatorSpec;
|
use ethjson::spec::ValidatorSet as ValidatorSpec;
|
||||||
use client::Client;
|
use client::Client;
|
||||||
use self::simple_list::SimpleList;
|
use self::simple_list::SimpleList;
|
||||||
@ -32,18 +32,18 @@ use self::safe_contract::ValidatorSafeContract;
|
|||||||
pub fn new_validator_set(spec: ValidatorSpec) -> Box<ValidatorSet + Send + Sync> {
|
pub fn new_validator_set(spec: ValidatorSpec) -> Box<ValidatorSet + Send + Sync> {
|
||||||
match spec {
|
match spec {
|
||||||
ValidatorSpec::List(list) => Box::new(SimpleList::new(list.into_iter().map(Into::into).collect())),
|
ValidatorSpec::List(list) => Box::new(SimpleList::new(list.into_iter().map(Into::into).collect())),
|
||||||
ValidatorSpec::SafeContract(address) => Box::new(Arc::new(ValidatorSafeContract::new(address.into()))),
|
ValidatorSpec::SafeContract(address) => Box::new(ValidatorSafeContract::new(address.into())),
|
||||||
ValidatorSpec::Contract(address) => Box::new(Arc::new(ValidatorContract::new(address.into()))),
|
ValidatorSpec::Contract(address) => Box::new(ValidatorContract::new(address.into())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ValidatorSet {
|
pub trait ValidatorSet {
|
||||||
/// Checks if a given address is a validator.
|
/// Checks if a given address is a validator.
|
||||||
fn contains(&self, address: &Address) -> bool;
|
fn contains(&self, bh: &H256, address: &Address) -> bool;
|
||||||
/// Draws an validator nonce modulo number of validators.
|
/// Draws an validator nonce modulo number of validators.
|
||||||
fn get(&self, nonce: usize) -> Address;
|
fn get(&self, bh: &H256, nonce: usize) -> Address;
|
||||||
/// Returns the current number of validators.
|
/// Returns the current number of validators.
|
||||||
fn count(&self) -> usize;
|
fn count(&self, bh: &H256) -> usize;
|
||||||
/// Notifies about malicious behaviour.
|
/// Notifies about malicious behaviour.
|
||||||
fn report_malicious(&self, _validator: &Address) {}
|
fn report_malicious(&self, _validator: &Address) {}
|
||||||
/// Notifies about benign misbehaviour.
|
/// Notifies about benign misbehaviour.
|
||||||
|
@ -17,17 +17,23 @@
|
|||||||
/// Validator set maintained in a contract, updated using `getValidators` method.
|
/// Validator set maintained in a contract, updated using `getValidators` method.
|
||||||
|
|
||||||
use std::sync::Weak;
|
use std::sync::Weak;
|
||||||
|
use ethabi;
|
||||||
use util::*;
|
use util::*;
|
||||||
|
use util::cache::MemoryLruCache;
|
||||||
|
use types::ids::BlockId;
|
||||||
use client::{Client, BlockChainClient};
|
use client::{Client, BlockChainClient};
|
||||||
use client::chain_notify::ChainNotify;
|
|
||||||
use super::ValidatorSet;
|
use super::ValidatorSet;
|
||||||
use super::simple_list::SimpleList;
|
use super::simple_list::SimpleList;
|
||||||
|
|
||||||
|
const MEMOIZE_CAPACITY: usize = 500;
|
||||||
|
const CONTRACT_INTERFACE: &'static [u8] = b"[{\"constant\":true,\"inputs\":[],\"name\":\"getValidators\",\"outputs\":[{\"name\":\"\",\"type\":\"address[]\"}],\"payable\":false,\"type\":\"function\"}]";
|
||||||
|
const GET_VALIDATORS: &'static str = "getValidators";
|
||||||
|
|
||||||
/// The validator contract should have the following interface:
|
/// The validator contract should have the following interface:
|
||||||
/// [{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}]
|
/// [{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}]
|
||||||
pub struct ValidatorSafeContract {
|
pub struct ValidatorSafeContract {
|
||||||
pub address: Address,
|
pub address: Address,
|
||||||
validators: RwLock<SimpleList>,
|
validators: RwLock<MemoryLruCache<H256, SimpleList>>,
|
||||||
provider: RwLock<Option<provider::Contract>>,
|
provider: RwLock<Option<provider::Contract>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,102 +41,127 @@ impl ValidatorSafeContract {
|
|||||||
pub fn new(contract_address: Address) -> Self {
|
pub fn new(contract_address: Address) -> Self {
|
||||||
ValidatorSafeContract {
|
ValidatorSafeContract {
|
||||||
address: contract_address,
|
address: contract_address,
|
||||||
validators: Default::default(),
|
validators: RwLock::new(MemoryLruCache::new(MEMOIZE_CAPACITY)),
|
||||||
provider: RwLock::new(None),
|
provider: RwLock::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Queries the state and updates the set of validators.
|
/// Queries the state and gets the set of validators.
|
||||||
pub fn update(&self) {
|
fn get_list(&self, block_hash: H256) -> Option<SimpleList> {
|
||||||
if let Some(ref provider) = *self.provider.read() {
|
if let Some(ref provider) = *self.provider.read() {
|
||||||
match provider.get_validators() {
|
match provider.get_validators(BlockId::Hash(block_hash)) {
|
||||||
Ok(new) => {
|
Ok(new) => {
|
||||||
debug!(target: "engine", "Set of validators obtained: {:?}", new);
|
debug!(target: "engine", "Set of validators obtained: {:?}", new);
|
||||||
*self.validators.write() = SimpleList::new(new);
|
Some(SimpleList::new(new))
|
||||||
|
},
|
||||||
|
Err(s) => {
|
||||||
|
debug!(target: "engine", "Set of validators could not be updated: {}", s);
|
||||||
|
None
|
||||||
},
|
},
|
||||||
Err(s) => warn!(target: "engine", "Set of validators could not be updated: {}", s),
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
warn!(target: "engine", "Set of validators could not be updated: no provider contract.")
|
warn!(target: "engine", "Set of validators could not be updated: no provider contract.");
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks validators on every block.
|
impl ValidatorSet for ValidatorSafeContract {
|
||||||
impl ChainNotify for ValidatorSafeContract {
|
fn contains(&self, block_hash: &H256, address: &Address) -> bool {
|
||||||
fn new_blocks(
|
let mut guard = self.validators.write();
|
||||||
&self,
|
let maybe_existing = guard
|
||||||
_: Vec<H256>,
|
.get_mut(block_hash)
|
||||||
_: Vec<H256>,
|
.map(|list| list.contains(block_hash, address));
|
||||||
enacted: Vec<H256>,
|
maybe_existing
|
||||||
_: Vec<H256>,
|
.unwrap_or_else(|| self
|
||||||
_: Vec<H256>,
|
.get_list(block_hash.clone())
|
||||||
_: Vec<Bytes>,
|
.map_or(false, |list| {
|
||||||
_duration: u64) {
|
let contains = list.contains(block_hash, address);
|
||||||
if !enacted.is_empty() {
|
guard.insert(block_hash.clone(), list);
|
||||||
self.update();
|
contains
|
||||||
}
|
}))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ValidatorSet for Arc<ValidatorSafeContract> {
|
|
||||||
fn contains(&self, address: &Address) -> bool {
|
|
||||||
self.validators.read().contains(address)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, nonce: usize) -> Address {
|
fn get(&self, block_hash: &H256, nonce: usize) -> Address {
|
||||||
self.validators.read().get(nonce)
|
let mut guard = self.validators.write();
|
||||||
|
let maybe_existing = guard
|
||||||
|
.get_mut(block_hash)
|
||||||
|
.map(|list| list.get(block_hash, nonce));
|
||||||
|
maybe_existing
|
||||||
|
.unwrap_or_else(|| self
|
||||||
|
.get_list(block_hash.clone())
|
||||||
|
.map_or_else(Default::default, |list| {
|
||||||
|
let address = list.get(block_hash, nonce);
|
||||||
|
guard.insert(block_hash.clone(), list);
|
||||||
|
address
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count(&self) -> usize {
|
fn count(&self, block_hash: &H256) -> usize {
|
||||||
self.validators.read().count()
|
let mut guard = self.validators.write();
|
||||||
|
let maybe_existing = guard
|
||||||
|
.get_mut(block_hash)
|
||||||
|
.map(|list| list.count(block_hash));
|
||||||
|
maybe_existing
|
||||||
|
.unwrap_or_else(|| self
|
||||||
|
.get_list(block_hash.clone())
|
||||||
|
.map_or_else(usize::max_value, |list| {
|
||||||
|
let address = list.count(block_hash);
|
||||||
|
guard.insert(block_hash.clone(), list);
|
||||||
|
address
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_contract(&self, client: Weak<Client>) {
|
fn register_contract(&self, client: Weak<Client>) {
|
||||||
if let Some(c) = client.upgrade() {
|
trace!(target: "engine", "Setting up contract caller.");
|
||||||
c.add_notify(self.clone());
|
let contract = ethabi::Contract::new(ethabi::Interface::load(CONTRACT_INTERFACE).expect("JSON interface is valid; qed"));
|
||||||
}
|
let call = contract.function(GET_VALIDATORS.into()).expect("Method name is valid; qed");
|
||||||
{
|
let data = call.encode_call(vec![]).expect("get_validators does not take any arguments; qed");
|
||||||
*self.provider.write() = Some(provider::Contract::new(self.address, move |a, d| client.upgrade().ok_or("No client!".into()).and_then(|c| c.call_contract(a, d))));
|
let contract_address = self.address.clone();
|
||||||
}
|
let do_call = move |id| client
|
||||||
self.update();
|
.upgrade()
|
||||||
|
.ok_or("No client!".into())
|
||||||
|
.and_then(|c| c.call_contract(id, contract_address.clone(), data.clone()))
|
||||||
|
.map(|raw_output| call.decode_output(raw_output).expect("ethabi is correct; qed"));
|
||||||
|
*self.provider.write() = Some(provider::Contract::new(do_call));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod provider {
|
mod provider {
|
||||||
// Autogenerated from JSON contract definition using Rust contract convertor.
|
|
||||||
#![allow(unused_imports)]
|
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
use std::fmt;
|
|
||||||
use {util, ethabi};
|
use {util, ethabi};
|
||||||
use util::{FixedHash, Uint};
|
use types::ids::BlockId;
|
||||||
|
|
||||||
pub struct Contract {
|
pub struct Contract {
|
||||||
contract: ethabi::Contract,
|
do_call: Box<Fn(BlockId) -> Result<Vec<ethabi::Token>, String> + Send + Sync + 'static>,
|
||||||
address: util::Address,
|
|
||||||
do_call: Box<Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send + Sync + 'static>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Contract {
|
impl Contract {
|
||||||
pub fn new<F>(address: util::Address, do_call: F) -> Self where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send + Sync + 'static {
|
pub fn new<F>(do_call: F) -> Self where F: Fn(BlockId) -> Result<Vec<ethabi::Token>, String> + Send + Sync + 'static {
|
||||||
Contract {
|
Contract {
|
||||||
contract: ethabi::Contract::new(ethabi::Interface::load(b"[{\"constant\":true,\"inputs\":[],\"name\":\"getValidators\",\"outputs\":[{\"name\":\"\",\"type\":\"address[]\"}],\"payable\":false,\"type\":\"function\"}]").expect("JSON is autogenerated; qed")),
|
|
||||||
address: address,
|
|
||||||
do_call: Box::new(do_call),
|
do_call: Box::new(do_call),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn as_string<T: fmt::Debug>(e: T) -> String { format!("{:?}", e) }
|
|
||||||
|
|
||||||
/// Auto-generated from: `{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}`
|
/// Gets validators from contract with interface: `{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}`
|
||||||
#[allow(dead_code)]
|
pub fn get_validators(&self, id: BlockId) -> Result<Vec<util::Address>, String> {
|
||||||
pub fn get_validators(&self) -> Result<Vec<util::Address>, String> {
|
Ok((self.do_call)(id)?
|
||||||
let call = self.contract.function("getValidators".into()).map_err(Self::as_string)?;
|
.into_iter()
|
||||||
let data = call.encode_call(
|
.rev()
|
||||||
vec![]
|
.collect::<Vec<_>>()
|
||||||
).map_err(Self::as_string)?;
|
.pop()
|
||||||
let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
.expect("get_validators returns one argument; qed")
|
||||||
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
.to_array()
|
||||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_array().and_then(|v| v.into_iter().map(|a| a.to_address()).collect::<Option<Vec<[u8; 20]>>>()).ok_or("Invalid type returned")?; r.into_iter().map(|a| util::Address::from(a)).collect::<Vec<_>>() }))
|
.and_then(|v| v
|
||||||
|
.into_iter()
|
||||||
|
.map(|a| a.to_address())
|
||||||
|
.collect::<Option<Vec<[u8; 20]>>>())
|
||||||
|
.expect("get_validators returns a list of addresses; qed")
|
||||||
|
.into_iter()
|
||||||
|
.map(util::Address::from)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,13 +169,14 @@ mod provider {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use util::*;
|
use util::*;
|
||||||
|
use types::ids::BlockId;
|
||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
use account_provider::AccountProvider;
|
use account_provider::AccountProvider;
|
||||||
use transaction::{Transaction, Action};
|
use transaction::{Transaction, Action};
|
||||||
use client::{BlockChainClient, EngineClient};
|
use client::{BlockChainClient, EngineClient};
|
||||||
use ethkey::Secret;
|
use ethkey::Secret;
|
||||||
use miner::MinerService;
|
use miner::MinerService;
|
||||||
use tests::helpers::generate_dummy_client_with_spec_and_accounts;
|
use tests::helpers::{generate_dummy_client_with_spec_and_accounts, generate_dummy_client_with_spec_and_data};
|
||||||
use super::super::ValidatorSet;
|
use super::super::ValidatorSet;
|
||||||
use super::ValidatorSafeContract;
|
use super::ValidatorSafeContract;
|
||||||
|
|
||||||
@ -153,12 +185,13 @@ mod tests {
|
|||||||
let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_safe_contract, None);
|
let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_safe_contract, None);
|
||||||
let vc = Arc::new(ValidatorSafeContract::new(Address::from_str("0000000000000000000000000000000000000005").unwrap()));
|
let vc = Arc::new(ValidatorSafeContract::new(Address::from_str("0000000000000000000000000000000000000005").unwrap()));
|
||||||
vc.register_contract(Arc::downgrade(&client));
|
vc.register_contract(Arc::downgrade(&client));
|
||||||
assert!(vc.contains(&Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap()));
|
let last_hash = client.best_block_header().hash();
|
||||||
assert!(vc.contains(&Address::from_str("82a978b3f5962a5b0957d9ee9eef472ee55b42f1").unwrap()));
|
assert!(vc.contains(&last_hash, &Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap()));
|
||||||
|
assert!(vc.contains(&last_hash, &Address::from_str("82a978b3f5962a5b0957d9ee9eef472ee55b42f1").unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn updates_validators() {
|
fn knows_validators() {
|
||||||
let tap = Arc::new(AccountProvider::transient_provider());
|
let tap = Arc::new(AccountProvider::transient_provider());
|
||||||
let s0 = Secret::from_slice(&"1".sha3()).unwrap();
|
let s0 = Secret::from_slice(&"1".sha3()).unwrap();
|
||||||
let v0 = tap.insert_account(s0.clone(), "").unwrap();
|
let v0 = tap.insert_account(s0.clone(), "").unwrap();
|
||||||
@ -212,5 +245,14 @@ mod tests {
|
|||||||
client.update_sealing();
|
client.update_sealing();
|
||||||
// Able to seal again.
|
// Able to seal again.
|
||||||
assert_eq!(client.chain_info().best_block_number, 3);
|
assert_eq!(client.chain_info().best_block_number, 3);
|
||||||
|
|
||||||
|
// Check syncing.
|
||||||
|
let sync_client = generate_dummy_client_with_spec_and_data(Spec::new_validator_safe_contract, 0, 0, &[]);
|
||||||
|
sync_client.engine().register_client(Arc::downgrade(&sync_client));
|
||||||
|
for i in 1..4 {
|
||||||
|
sync_client.import_block(client.block(BlockId::Number(i)).unwrap().into_inner()).unwrap();
|
||||||
|
}
|
||||||
|
sync_client.flush_queue();
|
||||||
|
assert_eq!(sync_client.chain_info().best_block_number, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
/// Preconfigured validator list.
|
/// Preconfigured validator list.
|
||||||
|
|
||||||
use util::Address;
|
use util::{H256, Address, HeapSizeOf};
|
||||||
use super::ValidatorSet;
|
use super::ValidatorSet;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Default)]
|
#[derive(Debug, PartialEq, Eq, Default)]
|
||||||
@ -34,16 +34,22 @@ impl SimpleList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HeapSizeOf for SimpleList {
|
||||||
|
fn heap_size_of_children(&self) -> usize {
|
||||||
|
self.validators.heap_size_of_children() + self.validator_n.heap_size_of_children()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ValidatorSet for SimpleList {
|
impl ValidatorSet for SimpleList {
|
||||||
fn contains(&self, address: &Address) -> bool {
|
fn contains(&self, _bh: &H256, address: &Address) -> bool {
|
||||||
self.validators.contains(address)
|
self.validators.contains(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, nonce: usize) -> Address {
|
fn get(&self, _bh: &H256, nonce: usize) -> Address {
|
||||||
self.validators.get(nonce % self.validator_n).expect("There are validator_n authorities; taking number modulo validator_n gives number in validator_n range; qed").clone()
|
self.validators.get(nonce % self.validator_n).expect("There are validator_n authorities; taking number modulo validator_n gives number in validator_n range; qed").clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count(&self) -> usize {
|
fn count(&self, _bh: &H256) -> usize {
|
||||||
self.validator_n
|
self.validator_n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,9 +66,9 @@ mod tests {
|
|||||||
let a1 = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
|
let a1 = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
|
||||||
let a2 = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
let a2 = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
let list = SimpleList::new(vec![a1.clone(), a2.clone()]);
|
let list = SimpleList::new(vec![a1.clone(), a2.clone()]);
|
||||||
assert!(list.contains(&a1));
|
assert!(list.contains(&Default::default(), &a1));
|
||||||
assert_eq!(list.get(0), a1);
|
assert_eq!(list.get(&Default::default(), 0), a1);
|
||||||
assert_eq!(list.get(1), a2);
|
assert_eq!(list.get(&Default::default(), 1), a2);
|
||||||
assert_eq!(list.get(2), a1);
|
assert_eq!(list.get(&Default::default(), 2), a1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,46 +30,52 @@ pub use self::denominations::*;
|
|||||||
use super::spec::*;
|
use super::spec::*;
|
||||||
|
|
||||||
/// Most recent fork block that we support on Mainnet.
|
/// Most recent fork block that we support on Mainnet.
|
||||||
pub const FORK_SUPPORTED_FRONTIER: u64 = 2675000;
|
pub const FORK_SUPPORTED_FOUNDATION: u64 = 2675000;
|
||||||
|
|
||||||
/// Most recent fork block that we support on Ropsten.
|
/// Most recent fork block that we support on Ropsten.
|
||||||
pub const FORK_SUPPORTED_ROPSTEN: u64 = 10;
|
pub const FORK_SUPPORTED_ROPSTEN: u64 = 10;
|
||||||
|
|
||||||
|
/// Most recent fork block that we support on Kovan.
|
||||||
|
pub const FORK_SUPPORTED_KOVAN: u64 = 0;
|
||||||
|
|
||||||
fn load(b: &[u8]) -> Spec {
|
fn load(b: &[u8]) -> Spec {
|
||||||
Spec::load(b).expect("chain spec is invalid")
|
Spec::load(b).expect("chain spec is invalid")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new Olympic chain spec.
|
/// Create a new Foundation Olympic chain spec.
|
||||||
pub fn new_olympic() -> Spec { load(include_bytes!("../../res/ethereum/olympic.json")) }
|
pub fn new_olympic() -> Spec { load(include_bytes!("../../res/ethereum/olympic.json")) }
|
||||||
|
|
||||||
/// Create a new Frontier mainnet chain spec.
|
/// Create a new Foundation Mainnet chain spec.
|
||||||
pub fn new_frontier() -> Spec { load(include_bytes!("../../res/ethereum/frontier.json")) }
|
pub fn new_foundation() -> Spec { load(include_bytes!("../../res/ethereum/foundation.json")) }
|
||||||
|
|
||||||
/// Create a new Frontier mainnet chain spec without the DAO hardfork.
|
/// Create a new Classic Mainnet chain spec without the DAO hardfork.
|
||||||
pub fn new_classic() -> Spec { load(include_bytes!("../../res/ethereum/classic.json")) }
|
pub fn new_classic() -> Spec { load(include_bytes!("../../res/ethereum/classic.json")) }
|
||||||
|
|
||||||
/// Create a new Frontier mainnet chain spec without the DAO hardfork.
|
/// Create a new Expanse mainnet chain spec.
|
||||||
pub fn new_expanse() -> Spec { load(include_bytes!("../../res/ethereum/expanse.json")) }
|
pub fn new_expanse() -> Spec { load(include_bytes!("../../res/ethereum/expanse.json")) }
|
||||||
|
|
||||||
/// Create a new Frontier chain spec as though it never changes to Homestead.
|
/// Create a new Kovan testnet chain spec.
|
||||||
|
pub fn new_kovan() -> Spec { load(include_bytes!("../../res/ethereum/kovan.json")) }
|
||||||
|
|
||||||
|
/// Create a new Foundation Frontier-era chain spec as though it never changes to Homestead.
|
||||||
pub fn new_frontier_test() -> Spec { load(include_bytes!("../../res/ethereum/frontier_test.json")) }
|
pub fn new_frontier_test() -> Spec { load(include_bytes!("../../res/ethereum/frontier_test.json")) }
|
||||||
|
|
||||||
/// Create a new Homestead chain spec as though it never changed from Frontier.
|
/// Create a new Foundation Homestead-era chain spec as though it never changed from Frontier.
|
||||||
pub fn new_homestead_test() -> Spec { load(include_bytes!("../../res/ethereum/homestead_test.json")) }
|
pub fn new_homestead_test() -> Spec { load(include_bytes!("../../res/ethereum/homestead_test.json")) }
|
||||||
|
|
||||||
/// Create a new Homestead-EIP150 chain spec as though it never changed from Homestead/Frontier.
|
/// Create a new Foundation Homestead-EIP150-era chain spec as though it never changed from Homestead/Frontier.
|
||||||
pub fn new_eip150_test() -> Spec { load(include_bytes!("../../res/ethereum/eip150_test.json")) }
|
pub fn new_eip150_test() -> Spec { load(include_bytes!("../../res/ethereum/eip150_test.json")) }
|
||||||
|
|
||||||
/// Create a new Homestead-EIP150 chain spec as though it never changed from Homestead/Frontier.
|
/// Create a new Foundation Homestead-EIP161-era chain spec as though it never changed from Homestead/Frontier.
|
||||||
pub fn new_eip161_test() -> Spec { load(include_bytes!("../../res/ethereum/eip161_test.json")) }
|
pub fn new_eip161_test() -> Spec { load(include_bytes!("../../res/ethereum/eip161_test.json")) }
|
||||||
|
|
||||||
/// Create a new Frontier/Homestead/DAO chain spec with transition points at #5 and #8.
|
/// Create a new Foundation Frontier/Homestead/DAO chain spec with transition points at #5 and #8.
|
||||||
pub fn new_transition_test() -> Spec { load(include_bytes!("../../res/ethereum/transition_test.json")) }
|
pub fn new_transition_test() -> Spec { load(include_bytes!("../../res/ethereum/transition_test.json")) }
|
||||||
|
|
||||||
/// Create a new Frontier main net chain spec without genesis accounts.
|
/// Create a new Foundation Mainnet chain spec without genesis accounts.
|
||||||
pub fn new_mainnet_like() -> Spec { load(include_bytes!("../../res/ethereum/frontier_like_test.json")) }
|
pub fn new_mainnet_like() -> Spec { load(include_bytes!("../../res/ethereum/frontier_like_test.json")) }
|
||||||
|
|
||||||
/// Create a new Ropsten chain spec.
|
/// Create a new Foundation Ropsten chain spec.
|
||||||
pub fn new_ropsten() -> Spec { load(include_bytes!("../../res/ethereum/ropsten.json")) }
|
pub fn new_ropsten() -> Spec { load(include_bytes!("../../res/ethereum/ropsten.json")) }
|
||||||
|
|
||||||
/// Create a new Morden chain spec.
|
/// Create a new Morden chain spec.
|
||||||
@ -112,7 +118,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn frontier() {
|
fn frontier() {
|
||||||
let frontier = new_frontier();
|
let frontier = new_foundation();
|
||||||
|
|
||||||
assert_eq!(frontier.state_root(), "d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544".into());
|
assert_eq!(frontier.state_root(), "d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544".into());
|
||||||
let genesis = frontier.genesis_block();
|
let genesis = frontier.genesis_block();
|
||||||
|
@ -28,4 +28,4 @@ mod v10;
|
|||||||
pub use self::v10::ToV10;
|
pub use self::v10::ToV10;
|
||||||
|
|
||||||
mod v11;
|
mod v11;
|
||||||
pub use self::v11::ToV11;
|
pub use self::v11::TO_V11;
|
||||||
|
@ -14,33 +14,13 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
//! Adds a seventh column for node information.
|
//! Adds a seventh column for node information.
|
||||||
|
|
||||||
use util::kvdb::Database;
|
use util::migration::ChangeColumns;
|
||||||
use util::migration::{Batch, Config, Error, Migration, Progress};
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
/// Copies over data for all existing columns.
|
/// The migration from v10 to v11.
|
||||||
#[derive(Default)]
|
pub const TO_V11: ChangeColumns = ChangeColumns {
|
||||||
pub struct ToV11(Progress);
|
pre_columns: Some(6),
|
||||||
|
post_columns: Some(7),
|
||||||
|
version: 11,
|
||||||
impl Migration for ToV11 {
|
};
|
||||||
fn pre_columns(&self) -> Option<u32> { Some(6) }
|
|
||||||
fn columns(&self) -> Option<u32> { Some(7) }
|
|
||||||
|
|
||||||
fn version(&self) -> u32 { 11 }
|
|
||||||
|
|
||||||
fn migrate(&mut self, source: Arc<Database>, config: &Config, dest: &mut Database, col: Option<u32>) -> Result<(), Error> {
|
|
||||||
// just copy everything over.
|
|
||||||
let mut batch = Batch::new(config, col);
|
|
||||||
|
|
||||||
for (key, value) in source.iter(col) {
|
|
||||||
self.0.tick();
|
|
||||||
batch.insert(key.to_vec(), value.to_vec(), dest)?
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.commit(dest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
//! use ethcore::miner::{Miner, MinerService};
|
//! use ethcore::miner::{Miner, MinerService};
|
||||||
//!
|
//!
|
||||||
//! fn main() {
|
//! fn main() {
|
||||||
//! let miner: Miner = Miner::with_spec(ðereum::new_frontier());
|
//! let miner: Miner = Miner::with_spec(ðereum::new_foundation());
|
||||||
//! // get status
|
//! // get status
|
||||||
//! assert_eq!(miner.status().transactions_in_pending_queue, 0);
|
//! assert_eq!(miner.status().transactions_in_pending_queue, 0);
|
||||||
//!
|
//!
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use types::ids::BlockId;
|
||||||
use client::MiningBlockChainClient;
|
use client::MiningBlockChainClient;
|
||||||
use transaction::SignedTransaction;
|
use transaction::SignedTransaction;
|
||||||
use util::{U256, Uint, Mutex};
|
use util::{U256, Uint, Mutex};
|
||||||
@ -45,7 +46,7 @@ impl ServiceTransactionChecker {
|
|||||||
debug_assert_eq!(tx.gas_price, U256::zero());
|
debug_assert_eq!(tx.gas_price, U256::zero());
|
||||||
|
|
||||||
if let Some(ref contract) = *self.contract.lock() {
|
if let Some(ref contract) = *self.contract.lock() {
|
||||||
let do_call = |a, d| client.call_contract(a, d);
|
let do_call = |a, d| client.call_contract(BlockId::Latest, a, d);
|
||||||
contract.certified(&do_call, &tx.sender())
|
contract.certified(&do_call, &tx.sender())
|
||||||
} else {
|
} else {
|
||||||
Err("contract is not configured".to_owned())
|
Err("contract is not configured".to_owned())
|
||||||
|
@ -160,7 +160,7 @@ impl Spec {
|
|||||||
fn engine(engine_spec: ethjson::spec::Engine, params: CommonParams, builtins: BTreeMap<Address, Builtin>) -> Arc<Engine> {
|
fn engine(engine_spec: ethjson::spec::Engine, params: CommonParams, builtins: BTreeMap<Address, Builtin>) -> Arc<Engine> {
|
||||||
match engine_spec {
|
match engine_spec {
|
||||||
ethjson::spec::Engine::Null => Arc::new(NullEngine::new(params, builtins)),
|
ethjson::spec::Engine::Null => Arc::new(NullEngine::new(params, builtins)),
|
||||||
ethjson::spec::Engine::InstantSeal => Arc::new(InstantSeal::new(params, builtins)),
|
ethjson::spec::Engine::InstantSeal(instant) => Arc::new(InstantSeal::new(params, instant.params.registrar.map_or_else(Address::new, Into::into), builtins)),
|
||||||
ethjson::spec::Engine::Ethash(ethash) => Arc::new(ethereum::Ethash::new(params, From::from(ethash.params), builtins)),
|
ethjson::spec::Engine::Ethash(ethash) => Arc::new(ethereum::Ethash::new(params, From::from(ethash.params), builtins)),
|
||||||
ethjson::spec::Engine::BasicAuthority(basic_authority) => Arc::new(BasicAuthority::new(params, From::from(basic_authority.params), builtins)),
|
ethjson::spec::Engine::BasicAuthority(basic_authority) => Arc::new(BasicAuthority::new(params, From::from(basic_authority.params), builtins)),
|
||||||
ethjson::spec::Engine::AuthorityRound(authority_round) => AuthorityRound::new(params, From::from(authority_round.params), builtins).expect("Failed to start AuthorityRound consensus engine."),
|
ethjson::spec::Engine::AuthorityRound(authority_round) => AuthorityRound::new(params, From::from(authority_round.params), builtins).expect("Failed to start AuthorityRound consensus engine."),
|
||||||
|
@ -44,6 +44,8 @@ pub trait Generator {
|
|||||||
fn generate(self) -> Result<KeyPair, Error>;
|
fn generate(self) -> Result<KeyPair, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod math;
|
||||||
|
|
||||||
pub use self::brain::Brain;
|
pub use self::brain::Brain;
|
||||||
pub use self::error::Error;
|
pub use self::error::Error;
|
||||||
pub use self::keypair::{KeyPair, public_to_address};
|
pub use self::keypair::{KeyPair, public_to_address};
|
||||||
|
66
ethkey/src/math.rs
Normal file
66
ethkey/src/math.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use super::{SECP256K1, Public, Secret, Error};
|
||||||
|
use secp256k1::key;
|
||||||
|
use secp256k1::constants::{GENERATOR_X, GENERATOR_Y};
|
||||||
|
|
||||||
|
/// Inplace multiply public key by secret key (EC point * scalar)
|
||||||
|
pub fn public_mul_secret(public: &mut Public, secret: &Secret) -> Result<(), Error> {
|
||||||
|
let key_secret = secret.to_secp256k1_secret()?;
|
||||||
|
let mut key_public = to_secp256k1_public(public)?;
|
||||||
|
key_public.mul_assign(&SECP256K1, &key_secret)?;
|
||||||
|
set_public(public, &key_public);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inplace add one public key to another (EC point + EC point)
|
||||||
|
pub fn public_add(public: &mut Public, other: &Public) -> Result<(), Error> {
|
||||||
|
let mut key_public = to_secp256k1_public(public)?;
|
||||||
|
let other_public = to_secp256k1_public(other)?;
|
||||||
|
key_public.add_assign(&SECP256K1, &other_public)?;
|
||||||
|
set_public(public, &key_public);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return base point of secp256k1
|
||||||
|
pub fn generation_point() -> Public {
|
||||||
|
let mut public_sec_raw = [0u8; 65];
|
||||||
|
public_sec_raw[0] = 4;
|
||||||
|
public_sec_raw[1..33].copy_from_slice(&GENERATOR_X);
|
||||||
|
public_sec_raw[33..65].copy_from_slice(&GENERATOR_Y);
|
||||||
|
|
||||||
|
let public_key = key::PublicKey::from_slice(&SECP256K1, &public_sec_raw)
|
||||||
|
.expect("constructing using predefined constants; qed");
|
||||||
|
let mut public = Public::default();
|
||||||
|
set_public(&mut public, &public_key);
|
||||||
|
public
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_secp256k1_public(public: &Public) -> Result<key::PublicKey, Error> {
|
||||||
|
let public_data = {
|
||||||
|
let mut temp = [4u8; 65];
|
||||||
|
(&mut temp[1..65]).copy_from_slice(&public[0..64]);
|
||||||
|
temp
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(key::PublicKey::from_slice(&SECP256K1, &public_data)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_public(public: &mut Public, key_public: &key::PublicKey) {
|
||||||
|
let key_public_serialized = key_public.serialize_vec(&SECP256K1, false);
|
||||||
|
public.copy_from_slice(&key_public_serialized[1..65]);
|
||||||
|
}
|
@ -19,7 +19,7 @@ use std::ops::Deref;
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use secp256k1::key;
|
use secp256k1::key;
|
||||||
use bigint::hash::H256;
|
use bigint::hash::H256;
|
||||||
use {Error};
|
use {Error, SECP256K1};
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub struct Secret {
|
pub struct Secret {
|
||||||
@ -45,6 +45,68 @@ impl Secret {
|
|||||||
let secret = key::SecretKey::from_slice(&super::SECP256K1, key)?;
|
let secret = key::SecretKey::from_slice(&super::SECP256K1, key)?;
|
||||||
Ok(secret.into())
|
Ok(secret.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Inplace add one secret key to another (scalar + scalar)
|
||||||
|
pub fn add(&mut self, other: &Secret) -> Result<(), Error> {
|
||||||
|
let mut key_secret = self.to_secp256k1_secret()?;
|
||||||
|
let other_secret = other.to_secp256k1_secret()?;
|
||||||
|
key_secret.add_assign(&SECP256K1, &other_secret)?;
|
||||||
|
|
||||||
|
*self = key_secret.into();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inplace subtract one secret key from another (scalar - scalar)
|
||||||
|
pub fn sub(&mut self, other: &Secret) -> Result<(), Error> {
|
||||||
|
let mut key_secret = self.to_secp256k1_secret()?;
|
||||||
|
let mut other_secret = other.to_secp256k1_secret()?;
|
||||||
|
other_secret.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?;
|
||||||
|
key_secret.add_assign(&SECP256K1, &other_secret)?;
|
||||||
|
|
||||||
|
*self = key_secret.into();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inplace multiply one secret key to another (scalar * scalar)
|
||||||
|
pub fn mul(&mut self, other: &Secret) -> Result<(), Error> {
|
||||||
|
let mut key_secret = self.to_secp256k1_secret()?;
|
||||||
|
let other_secret = other.to_secp256k1_secret()?;
|
||||||
|
key_secret.mul_assign(&SECP256K1, &other_secret)?;
|
||||||
|
|
||||||
|
*self = key_secret.into();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inplace inverse secret key (1 / scalar)
|
||||||
|
pub fn inv(&mut self) -> Result<(), Error> {
|
||||||
|
let mut key_secret = self.to_secp256k1_secret()?;
|
||||||
|
key_secret.inv_assign(&SECP256K1)?;
|
||||||
|
|
||||||
|
*self = key_secret.into();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute power of secret key inplace (secret ^ pow).
|
||||||
|
/// This function is not intended to be used with large powers.
|
||||||
|
pub fn pow(&mut self, pow: usize) -> Result<(), Error> {
|
||||||
|
match pow {
|
||||||
|
0 => *self = key::ONE_KEY.into(),
|
||||||
|
1 => (),
|
||||||
|
_ => {
|
||||||
|
let c = self.clone();
|
||||||
|
for _ in 1..pow {
|
||||||
|
self.mul(&c)?;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create `secp256k1::key::SecretKey` based on this secret
|
||||||
|
pub fn to_secp256k1_secret(&self) -> Result<key::SecretKey, Error> {
|
||||||
|
Ok(key::SecretKey::from_slice(&SECP256K1, &self[..])?)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Secret {
|
impl FromStr for Secret {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "evmjit"
|
name = "evmjit"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
@ -3,7 +3,7 @@ description = "Fetching hash-addressed content."
|
|||||||
homepage = "http://parity.io"
|
homepage = "http://parity.io"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
name = "parity-hash-fetch"
|
name = "parity-hash-fetch"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -3,7 +3,7 @@ description = "Hardware wallet support."
|
|||||||
homepage = "http://parity.io"
|
homepage = "http://parity.io"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
name = "hardware-wallet"
|
name = "hardware-wallet"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
description = "Types that implement IPC and are common to multiple modules."
|
description = "Types that implement IPC and are common to multiple modules."
|
||||||
name = "ipc-common-types"
|
name = "ipc-common-types"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ethcore-ipc-codegen"
|
name = "ethcore-ipc-codegen"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
description = "Macros to auto-generate implementations for ipc call"
|
description = "Macros to auto-generate implementations for ipc call"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ethcore-ipc-nano"
|
name = "ethcore-ipc-nano"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ethcore-ipc"
|
name = "ethcore-ipc"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
description = "Parity IPFS-compatible API"
|
description = "Parity IPFS-compatible API"
|
||||||
name = "parity-ipfs-api"
|
name = "parity-ipfs-api"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
|
1
js/.gitignore
vendored
1
js/.gitignore
vendored
@ -8,3 +8,4 @@ docs
|
|||||||
.happypack
|
.happypack
|
||||||
.npmjs
|
.npmjs
|
||||||
.eslintcache
|
.eslintcache
|
||||||
|
yarn.lock
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "parity.js",
|
"name": "parity.js",
|
||||||
"version": "0.3.105",
|
"version": "1.7.3",
|
||||||
"main": "release/index.js",
|
"main": "release/index.js",
|
||||||
"jsnext:main": "src/index.js",
|
"jsnext:main": "src/index.js",
|
||||||
"author": "Parity Team <admin@parity.io>",
|
"author": "Parity Team <admin@parity.io>",
|
||||||
"maintainers": [
|
"maintainers": [
|
||||||
"Jaco Greeff",
|
"Jaco Greeff",
|
||||||
"Nicolas Gotchac",
|
"Nicolas Gotchac"
|
||||||
|
],
|
||||||
|
"contributors": [
|
||||||
"Jannis Redmann"
|
"Jannis Redmann"
|
||||||
],
|
],
|
||||||
"contributors": [],
|
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -56,28 +57,28 @@
|
|||||||
"prepush": "npm run lint:cached"
|
"prepush": "npm run lint:cached"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-cli": "6.22.2",
|
"babel-cli": "6.23.0",
|
||||||
"babel-core": "6.22.1",
|
"babel-core": "6.23.1",
|
||||||
"babel-eslint": "7.1.1",
|
"babel-eslint": "7.1.1",
|
||||||
"babel-loader": "6.2.10",
|
"babel-loader": "6.3.2",
|
||||||
"babel-plugin-lodash": "3.2.11",
|
"babel-plugin-lodash": "3.2.11",
|
||||||
"babel-plugin-react-intl": "2.3.1",
|
"babel-plugin-react-intl": "2.3.1",
|
||||||
"babel-plugin-recharts": "1.1.0",
|
"babel-plugin-recharts": "1.1.0",
|
||||||
"babel-plugin-transform-class-properties": "6.22.0",
|
"babel-plugin-transform-class-properties": "6.23.0",
|
||||||
"babel-plugin-transform-decorators-legacy": "1.3.4",
|
"babel-plugin-transform-decorators-legacy": "1.3.4",
|
||||||
"babel-plugin-transform-object-rest-spread": "6.22.0",
|
"babel-plugin-transform-object-rest-spread": "6.23.0",
|
||||||
"babel-plugin-transform-react-remove-prop-types": "0.3.0",
|
"babel-plugin-transform-react-remove-prop-types": "0.3.2",
|
||||||
"babel-plugin-transform-runtime": "6.22.0",
|
"babel-plugin-transform-runtime": "6.23.0",
|
||||||
"babel-plugin-webpack-alias": "2.1.2",
|
"babel-plugin-webpack-alias": "2.1.2",
|
||||||
"babel-polyfill": "6.22.0",
|
"babel-polyfill": "6.23.0",
|
||||||
"babel-preset-env": "1.1.8",
|
"babel-preset-env": "1.1.9",
|
||||||
"babel-preset-es2015": "6.22.0",
|
"babel-preset-es2015": "6.22.0",
|
||||||
"babel-preset-es2016": "6.22.0",
|
"babel-preset-es2016": "6.22.0",
|
||||||
"babel-preset-es2017": "6.22.0",
|
"babel-preset-es2017": "6.22.0",
|
||||||
"babel-preset-react": "6.22.0",
|
"babel-preset-react": "6.23.0",
|
||||||
"babel-preset-stage-0": "6.22.0",
|
"babel-preset-stage-0": "6.22.0",
|
||||||
"babel-register": "6.22.0",
|
"babel-register": "6.23.0",
|
||||||
"babel-runtime": "6.22.0",
|
"babel-runtime": "6.23.0",
|
||||||
"chai": "3.5.0",
|
"chai": "3.5.0",
|
||||||
"chai-as-promised": "6.0.0",
|
"chai-as-promised": "6.0.0",
|
||||||
"chai-enzyme": "0.6.1",
|
"chai-enzyme": "0.6.1",
|
||||||
@ -85,62 +86,62 @@
|
|||||||
"circular-dependency-plugin": "2.0.0",
|
"circular-dependency-plugin": "2.0.0",
|
||||||
"copy-webpack-plugin": "4.0.1",
|
"copy-webpack-plugin": "4.0.1",
|
||||||
"core-js": "2.4.1",
|
"core-js": "2.4.1",
|
||||||
"coveralls": "2.11.15",
|
"coveralls": "2.11.16",
|
||||||
"css-loader": "0.26.1",
|
"css-loader": "0.26.1",
|
||||||
"ejs-loader": "0.3.0",
|
"ejs-loader": "0.3.0",
|
||||||
"ejsify": "1.0.0",
|
"ejsify": "1.0.0",
|
||||||
"enzyme": "2.7.0",
|
"enzyme": "2.7.1",
|
||||||
"eslint": "3.11.1",
|
"eslint": "3.16.1",
|
||||||
"eslint-config-semistandard": "7.0.0",
|
"eslint-config-semistandard": "7.0.0",
|
||||||
"eslint-config-standard": "6.2.1",
|
"eslint-config-standard": "6.2.1",
|
||||||
"eslint-config-standard-react": "4.2.0",
|
"eslint-config-standard-react": "4.2.0",
|
||||||
"eslint-plugin-promise": "3.4.0",
|
"eslint-plugin-promise": "3.4.2",
|
||||||
"eslint-plugin-react": "6.8.0",
|
"eslint-plugin-react": "6.10.0",
|
||||||
"eslint-plugin-standard": "2.0.1",
|
"eslint-plugin-standard": "2.0.1",
|
||||||
"express": "4.14.0",
|
"express": "4.14.1",
|
||||||
"extract-loader": "0.1.0",
|
"extract-loader": "0.1.0",
|
||||||
"extract-text-webpack-plugin": "2.0.0-beta.4",
|
"extract-text-webpack-plugin": "2.0.0-beta.4",
|
||||||
"file-loader": "0.9.0",
|
"file-loader": "0.10.0",
|
||||||
"happypack": "3.0.2",
|
"happypack": "3.0.3",
|
||||||
"html-loader": "0.4.4",
|
"html-loader": "0.4.4",
|
||||||
"html-webpack-plugin": "2.24.1",
|
"html-webpack-plugin": "2.28.0",
|
||||||
"http-proxy-middleware": "0.17.3",
|
"http-proxy-middleware": "0.17.3",
|
||||||
"husky": "0.11.9",
|
"husky": "0.13.1",
|
||||||
"ignore-styles": "5.0.1",
|
"ignore-styles": "5.0.1",
|
||||||
"image-webpack-loader": "3.1.0",
|
"image-webpack-loader": "3.2.0",
|
||||||
"istanbul": "1.0.0-alpha.2",
|
"istanbul": "1.0.0-alpha.2",
|
||||||
"jsdom": "9.9.1",
|
"jsdom": "9.11.0",
|
||||||
"json-loader": "0.5.4",
|
"json-loader": "0.5.4",
|
||||||
"mocha": "3.2.0",
|
"mocha": "3.2.0",
|
||||||
"mock-local-storage": "1.0.2",
|
"mock-local-storage": "1.0.2",
|
||||||
"mock-socket": "6.0.4",
|
"mock-socket": "6.0.4",
|
||||||
"nock": "9.0.2",
|
"nock": "9.0.7",
|
||||||
"postcss-import": "9.0.0",
|
"postcss-import": "9.1.0",
|
||||||
"postcss-loader": "1.2.1",
|
"postcss-loader": "1.3.2",
|
||||||
"postcss-nested": "1.0.0",
|
"postcss-nested": "1.0.0",
|
||||||
"postcss-simple-vars": "3.0.0",
|
"postcss-simple-vars": "3.0.0",
|
||||||
"progress": "1.1.8",
|
"progress": "1.1.8",
|
||||||
"progress-bar-webpack-plugin": "1.9.1",
|
"progress-bar-webpack-plugin": "1.9.3",
|
||||||
"raw-loader": "0.5.1",
|
"raw-loader": "0.5.1",
|
||||||
"react-addons-perf": "15.4.1",
|
"react-addons-perf": "15.4.2",
|
||||||
"react-addons-test-utils": "15.4.1",
|
"react-addons-test-utils": "15.4.2",
|
||||||
"react-hot-loader": "3.0.0-beta.6",
|
"react-hot-loader": "3.0.0-beta.6",
|
||||||
"react-intl-aggregate-webpack-plugin": "0.0.1",
|
"react-intl-aggregate-webpack-plugin": "0.0.1",
|
||||||
"rucksack-css": "0.9.1",
|
"rucksack-css": "0.9.1",
|
||||||
"script-ext-html-webpack-plugin": "1.3.5",
|
"script-ext-html-webpack-plugin": "1.7.1",
|
||||||
"serviceworker-webpack-plugin": "0.1.7",
|
"serviceworker-webpack-plugin": "0.2.0",
|
||||||
"sinon": "1.17.6",
|
"sinon": "1.17.7",
|
||||||
"sinon-as-promised": "4.0.2",
|
"sinon-as-promised": "4.0.2",
|
||||||
"sinon-chai": "2.8.0",
|
"sinon-chai": "2.8.0",
|
||||||
"style-loader": "0.13.1",
|
"style-loader": "0.13.1",
|
||||||
"stylelint": "7.7.0",
|
"stylelint": "7.9.0",
|
||||||
"stylelint-config-standard": "15.0.1",
|
"stylelint-config-standard": "16.0.0",
|
||||||
"to-source": "2.0.3",
|
"to-source": "2.0.3",
|
||||||
"url-loader": "0.5.7",
|
"url-loader": "0.5.7",
|
||||||
"webpack": "2.2.1",
|
"webpack": "2.2.1",
|
||||||
"webpack-dev-middleware": "1.9.0",
|
"webpack-dev-middleware": "1.10.1",
|
||||||
"webpack-error-notification": "0.1.6",
|
"webpack-error-notification": "0.1.6",
|
||||||
"webpack-hot-middleware": "2.14.0",
|
"webpack-hot-middleware": "2.17.1",
|
||||||
"websocket": "1.0.24",
|
"websocket": "1.0.24",
|
||||||
"yargs": "6.6.0"
|
"yargs": "6.6.0"
|
||||||
},
|
},
|
||||||
@ -153,7 +154,7 @@
|
|||||||
"debounce": "1.0.0",
|
"debounce": "1.0.0",
|
||||||
"es6-error": "4.0.0",
|
"es6-error": "4.0.0",
|
||||||
"es6-promise": "4.0.5",
|
"es6-promise": "4.0.5",
|
||||||
"ethereumjs-tx": "1.1.4",
|
"ethereumjs-tx": "1.2.5",
|
||||||
"eventemitter3": "2.0.2",
|
"eventemitter3": "2.0.2",
|
||||||
"file-saver": "1.3.3",
|
"file-saver": "1.3.3",
|
||||||
"flat": "2.0.1",
|
"flat": "2.0.1",
|
||||||
@ -200,6 +201,9 @@
|
|||||||
"scryptsy": "2.0.0",
|
"scryptsy": "2.0.0",
|
||||||
"solc": "ngotchac/solc-js",
|
"solc": "ngotchac/solc-js",
|
||||||
"store": "1.3.20",
|
"store": "1.3.20",
|
||||||
|
"u2f-api": "0.0.9",
|
||||||
|
"u2f-api-polyfill": "0.4.3",
|
||||||
|
"uglify-js": "2.8.2",
|
||||||
"useragent.js": "0.5.6",
|
"useragent.js": "0.5.6",
|
||||||
"utf8": "2.1.2",
|
"utf8": "2.1.2",
|
||||||
"valid-url": "1.0.9",
|
"valid-url": "1.0.9",
|
||||||
|
22
js/src/3rdparty/etherscan/account.js
vendored
22
js/src/3rdparty/etherscan/account.js
vendored
@ -21,15 +21,15 @@ const PAGE_SIZE = 25;
|
|||||||
import util from '../../api/util';
|
import util from '../../api/util';
|
||||||
import { call } from './call';
|
import { call } from './call';
|
||||||
|
|
||||||
function _call (method, params, test) {
|
function _call (method, params, test, netVersion) {
|
||||||
return call('account', method, params, test);
|
return call('account', method, params, test, netVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
function balance (address, test = false) {
|
function balance (address, test, netVersion) {
|
||||||
return _call('balance', {
|
return _call('balance', {
|
||||||
address: address,
|
address: address,
|
||||||
tag: 'latest'
|
tag: 'latest'
|
||||||
}, test).then((balance) => {
|
}, test, netVersion).then((balance) => {
|
||||||
// same format as balancemulti below
|
// same format as balancemulti below
|
||||||
return {
|
return {
|
||||||
account: address,
|
account: address,
|
||||||
@ -38,21 +38,21 @@ function balance (address, test = false) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function balances (addresses, test = false) {
|
function balances (addresses, test, netVersion) {
|
||||||
return _call('balancemulti', {
|
return _call('balancemulti', {
|
||||||
address: addresses.join(','),
|
address: addresses.join(','),
|
||||||
tag: 'latest'
|
tag: 'latest'
|
||||||
}, test);
|
}, test, netVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
function transactions (address, page, test = false) {
|
function transactions (address, page, test, netVersion) {
|
||||||
// page offset from 0
|
// page offset from 0
|
||||||
return _call('txlist', {
|
return _call('txlist', {
|
||||||
address: address,
|
address: address,
|
||||||
offset: PAGE_SIZE,
|
offset: PAGE_SIZE,
|
||||||
page: (page || 0) + 1,
|
page: (page || 0) + 1,
|
||||||
sort: 'desc'
|
sort: 'desc'
|
||||||
}, test).then((transactions) => {
|
}, test, netVersion).then((transactions) => {
|
||||||
return transactions.map((tx) => {
|
return transactions.map((tx) => {
|
||||||
return {
|
return {
|
||||||
blockNumber: new BigNumber(tx.blockNumber || 0),
|
blockNumber: new BigNumber(tx.blockNumber || 0),
|
||||||
@ -67,9 +67,9 @@ function transactions (address, page, test = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const account = {
|
const account = {
|
||||||
balance: balance,
|
balance,
|
||||||
balances: balances,
|
balances,
|
||||||
transactions: transactions
|
transactions
|
||||||
};
|
};
|
||||||
|
|
||||||
export { account };
|
export { account };
|
||||||
|
24
js/src/3rdparty/etherscan/call.js
vendored
24
js/src/3rdparty/etherscan/call.js
vendored
@ -23,14 +23,32 @@ const options = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export function call (module, action, _params, test) {
|
export function call (module, action, _params, test, netVersion) {
|
||||||
const host = test ? 'testnet.etherscan.io' : 'api.etherscan.io';
|
let prefix = 'api.';
|
||||||
|
|
||||||
|
switch (netVersion) {
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
prefix = 'testnet.';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '42':
|
||||||
|
prefix = 'kovan.';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '0':
|
||||||
|
default:
|
||||||
|
if (test) {
|
||||||
|
prefix = 'testnet.';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
const query = stringify(Object.assign({
|
const query = stringify(Object.assign({
|
||||||
module, action
|
module, action
|
||||||
}, _params || {}));
|
}, _params || {}));
|
||||||
|
|
||||||
return fetch(`https://${host}/api?${query}`, options)
|
return fetch(`https://${prefix}etherscan.io/api?${query}`, options)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw { code: response.status, message: response.statusText }; // eslint-disable-line
|
throw { code: response.status, message: response.statusText }; // eslint-disable-line
|
||||||
|
4
js/src/3rdparty/etherscan/helpers.spec.js
vendored
4
js/src/3rdparty/etherscan/helpers.spec.js
vendored
@ -19,8 +19,8 @@ import { stringify } from 'qs';
|
|||||||
|
|
||||||
import { url } from './links';
|
import { url } from './links';
|
||||||
|
|
||||||
function mockget (requests, test) {
|
function mockget (requests, test, netVersion) {
|
||||||
let scope = nock(url(test));
|
let scope = nock(url(test, netVersion));
|
||||||
|
|
||||||
requests.forEach((request) => {
|
requests.forEach((request) => {
|
||||||
scope = scope
|
scope = scope
|
||||||
|
33
js/src/3rdparty/etherscan/links.js
vendored
33
js/src/3rdparty/etherscan/links.js
vendored
@ -14,14 +14,35 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
export const url = (isTestnet = false) => {
|
// NOTE: Keep 'isTestnet' for backwards library compatibility
|
||||||
return `https://${isTestnet ? 'testnet.' : ''}etherscan.io`;
|
export const url = (isTestnet = false, netVersion = '0') => {
|
||||||
|
let prefix = '';
|
||||||
|
|
||||||
|
switch (netVersion) {
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
prefix = 'testnet.';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '42':
|
||||||
|
prefix = 'kovan.';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '0':
|
||||||
|
default:
|
||||||
|
if (isTestnet) {
|
||||||
|
prefix = 'testnet.';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `https://${prefix}etherscan.io`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const txLink = (hash, isTestnet = false) => {
|
export const txLink = (hash, isTestnet = false, netVersion = '0') => {
|
||||||
return `${url(isTestnet)}/tx/${hash}`;
|
return `${url(isTestnet, netVersion)}/tx/${hash}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const addressLink = (address, isTestnet = false) => {
|
export const addressLink = (address, isTestnet = false, netVersion = '0') => {
|
||||||
return `${url(isTestnet)}/address/${address}`;
|
return `${url(isTestnet, netVersion)}/address/${address}`;
|
||||||
};
|
};
|
||||||
|
10
js/src/3rdparty/ledger/index.js
vendored
10
js/src/3rdparty/ledger/index.js
vendored
@ -14,12 +14,4 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import Ledger3 from './vendor/ledger3';
|
export default from './ledger';
|
||||||
import LedgerEth from './vendor/ledger-eth';
|
|
||||||
|
|
||||||
export function create () {
|
|
||||||
const ledger = new Ledger3('w0w');
|
|
||||||
const app = new LedgerEth(ledger);
|
|
||||||
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
|
136
js/src/3rdparty/ledger/ledger.js
vendored
Normal file
136
js/src/3rdparty/ledger/ledger.js
vendored
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import 'u2f-api-polyfill';
|
||||||
|
|
||||||
|
import BigNumber from 'bignumber.js';
|
||||||
|
import Transaction from 'ethereumjs-tx';
|
||||||
|
import u2fapi from 'u2f-api';
|
||||||
|
|
||||||
|
import Ledger3 from './vendor/ledger3';
|
||||||
|
import LedgerEth from './vendor/ledger-eth';
|
||||||
|
|
||||||
|
const LEDGER_PATH_ETC = "44’/60’/160720'/0'/0";
|
||||||
|
const LEDGER_PATH_ETH = "44'/60'/0'/0";
|
||||||
|
const SCRAMBLE_KEY = 'w0w';
|
||||||
|
|
||||||
|
function numberToHex (number) {
|
||||||
|
return `0x${new BigNumber(number).toString(16)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Ledger {
|
||||||
|
constructor (api, ledger) {
|
||||||
|
this._api = api;
|
||||||
|
this._ledger = ledger;
|
||||||
|
|
||||||
|
this._isSupported = false;
|
||||||
|
|
||||||
|
this.checkJSSupport();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Until we have https support from Parity u2f will not work. Here we mark it completely
|
||||||
|
// as unsupported until a full end-to-end environment is available.
|
||||||
|
get isSupported () {
|
||||||
|
return false && this._isSupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkJSSupport () {
|
||||||
|
return u2fapi
|
||||||
|
.isSupported()
|
||||||
|
.then((isSupported) => {
|
||||||
|
console.log('Ledger:checkJSSupport', isSupported);
|
||||||
|
|
||||||
|
this._isSupported = isSupported;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getAppConfiguration () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this._ledger.getAppConfiguration((response, error) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(response);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
scan () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this._ledger.getAddress(LEDGER_PATH_ETH, (response, error) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve([response.address]);
|
||||||
|
}, true, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
signTransaction (transaction) {
|
||||||
|
return this._api.net.version().then((_chainId) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const chainId = new BigNumber(_chainId).toNumber();
|
||||||
|
const tx = new Transaction({
|
||||||
|
data: transaction.data || transaction.input,
|
||||||
|
gasPrice: numberToHex(transaction.gasPrice),
|
||||||
|
gasLimit: numberToHex(transaction.gasLimit),
|
||||||
|
nonce: numberToHex(transaction.nonce),
|
||||||
|
to: transaction.to ? transaction.to.toLowerCase() : undefined,
|
||||||
|
value: numberToHex(transaction.value),
|
||||||
|
v: Buffer.from([chainId]), // pass the chainId to the ledger
|
||||||
|
r: Buffer.from([]),
|
||||||
|
s: Buffer.from([])
|
||||||
|
});
|
||||||
|
const rawTransaction = tx.serialize().toString('hex');
|
||||||
|
|
||||||
|
this._ledger.signTransaction(LEDGER_PATH_ETH, rawTransaction, (response, error) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.v = Buffer.from(response.v, 'hex');
|
||||||
|
tx.r = Buffer.from(response.r, 'hex');
|
||||||
|
tx.s = Buffer.from(response.s, 'hex');
|
||||||
|
|
||||||
|
if (chainId !== Math.floor((tx.v[0] - 35) / 2)) {
|
||||||
|
reject(new Error('Invalid EIP155 signature received from Ledger.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(`0x${tx.serialize().toString('hex')}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static create (api, ledger) {
|
||||||
|
if (!ledger) {
|
||||||
|
ledger = new LedgerEth(new Ledger3(SCRAMBLE_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Ledger(api, ledger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
LEDGER_PATH_ETC,
|
||||||
|
LEDGER_PATH_ETH
|
||||||
|
};
|
120
js/src/3rdparty/ledger/ledger.spec.js
vendored
Normal file
120
js/src/3rdparty/ledger/ledger.spec.js
vendored
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import sinon from 'sinon';
|
||||||
|
|
||||||
|
import Ledger from './';
|
||||||
|
|
||||||
|
const TEST_ADDRESS = '0x63Cf90D3f0410092FC0fca41846f596223979195';
|
||||||
|
|
||||||
|
let api;
|
||||||
|
let ledger;
|
||||||
|
let vendor;
|
||||||
|
|
||||||
|
function createApi () {
|
||||||
|
api = {
|
||||||
|
net: {
|
||||||
|
version: sinon.stub().resolves('2')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return api;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createVendor (error = null) {
|
||||||
|
vendor = {
|
||||||
|
getAddress: (path, callback) => {
|
||||||
|
callback({
|
||||||
|
address: TEST_ADDRESS
|
||||||
|
}, error);
|
||||||
|
},
|
||||||
|
getAppConfiguration: (callback) => {
|
||||||
|
callback({}, error);
|
||||||
|
},
|
||||||
|
signTransaction: (path, rawTransaction, callback) => {
|
||||||
|
callback({
|
||||||
|
v: [39],
|
||||||
|
r: [0],
|
||||||
|
s: [0]
|
||||||
|
}, error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return vendor;
|
||||||
|
}
|
||||||
|
|
||||||
|
function create (error) {
|
||||||
|
ledger = new Ledger(createApi(), createVendor(error));
|
||||||
|
|
||||||
|
return ledger;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('3rdparty/ledger', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
create();
|
||||||
|
|
||||||
|
sinon.spy(vendor, 'getAddress');
|
||||||
|
sinon.spy(vendor, 'getAppConfiguration');
|
||||||
|
sinon.spy(vendor, 'signTransaction');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vendor.getAddress.restore();
|
||||||
|
vendor.getAppConfiguration.restore();
|
||||||
|
vendor.signTransaction.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getAppConfiguration', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
return ledger.getAppConfiguration();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls into getAppConfiguration', () => {
|
||||||
|
expect(vendor.getAppConfiguration).to.have.been.called;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('scan', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
return ledger.scan();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls into getAddress', () => {
|
||||||
|
expect(vendor.getAddress).to.have.been.called;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('signTransaction', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
return ledger.signTransaction({
|
||||||
|
data: '0x0',
|
||||||
|
gasPrice: 20000000,
|
||||||
|
gasLimit: 1000000,
|
||||||
|
nonce: 2,
|
||||||
|
to: '0x63Cf90D3f0410092FC0fca41846f596223979195',
|
||||||
|
value: 1
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('retrieves chainId via API', () => {
|
||||||
|
expect(api.net.version).to.have.been.called;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls into signTransaction', () => {
|
||||||
|
expect(vendor.signTransaction).to.have.been.called;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -21,8 +21,9 @@ export function eventSignature (eventName, params) {
|
|||||||
const { strName, name } = parseName(eventName);
|
const { strName, name } = parseName(eventName);
|
||||||
const types = (params || []).map(fromParamType).join(',');
|
const types = (params || []).map(fromParamType).join(',');
|
||||||
const id = `${strName}(${types})`;
|
const id = `${strName}(${types})`;
|
||||||
|
const signature = strName ? keccak_256(id) : '';
|
||||||
|
|
||||||
return { id, name, signature: keccak_256(id) };
|
return { id, name, signature };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function methodSignature (methodName, params) {
|
export function methodSignature (methodName, params) {
|
||||||
|
@ -46,7 +46,7 @@ describe('abi/util/signature', () => {
|
|||||||
expect(eventSignature(undefined, [])).to.deep.equal({
|
expect(eventSignature(undefined, [])).to.deep.equal({
|
||||||
id: '()',
|
id: '()',
|
||||||
name: undefined,
|
name: undefined,
|
||||||
signature: '861731d50c3880a2ca1994d5ec287b94b2f4bd832a67d3e41c08177bdd5674fe'
|
signature: ''
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ describe('abi/util/signature', () => {
|
|||||||
expect(eventSignature(undefined, undefined)).to.deep.equal({
|
expect(eventSignature(undefined, undefined)).to.deep.equal({
|
||||||
id: '()',
|
id: '()',
|
||||||
name: undefined,
|
name: undefined,
|
||||||
signature: '861731d50c3880a2ca1994d5ec287b94b2f4bd832a67d3e41c08177bdd5674fe'
|
signature: ''
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -96,7 +96,7 @@ describe('abi/util/signature', () => {
|
|||||||
expect(methodSignature(undefined, [])).to.deep.equal({
|
expect(methodSignature(undefined, [])).to.deep.equal({
|
||||||
id: '()',
|
id: '()',
|
||||||
name: undefined,
|
name: undefined,
|
||||||
signature: '861731d5'
|
signature: ''
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ describe('abi/util/signature', () => {
|
|||||||
expect(methodSignature(undefined, undefined)).to.deep.equal({
|
expect(methodSignature(undefined, undefined)).to.deep.equal({
|
||||||
id: '()',
|
id: '()',
|
||||||
name: undefined,
|
name: undefined,
|
||||||
signature: '861731d5'
|
signature: ''
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -107,34 +107,26 @@ export default class Contract {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deploy (options, values, statecb) {
|
deploy (options, values, statecb = () => {}) {
|
||||||
const setState = (state) => {
|
statecb(null, { state: 'estimateGas' });
|
||||||
if (!statecb) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return statecb(null, state);
|
|
||||||
};
|
|
||||||
|
|
||||||
setState({ state: 'estimateGas' });
|
|
||||||
|
|
||||||
return this
|
return this
|
||||||
.deployEstimateGas(options, values)
|
.deployEstimateGas(options, values)
|
||||||
.then(([gasEst, gas]) => {
|
.then(([gasEst, gas]) => {
|
||||||
options.gas = gas.toFixed(0);
|
options.gas = gas.toFixed(0);
|
||||||
|
|
||||||
setState({ state: 'postTransaction', gas });
|
statecb(null, { state: 'postTransaction', gas });
|
||||||
|
|
||||||
const _options = this._encodeOptions(this.constructors[0], options, values);
|
const encodedOptions = this._encodeOptions(this.constructors[0], options, values);
|
||||||
|
|
||||||
return this._api.parity
|
return this._api.parity
|
||||||
.postTransaction(_options)
|
.postTransaction(encodedOptions)
|
||||||
.then((requestId) => {
|
.then((requestId) => {
|
||||||
setState({ state: 'checkRequest', requestId });
|
statecb(null, { state: 'checkRequest', requestId });
|
||||||
return this._pollCheckRequest(requestId);
|
return this._pollCheckRequest(requestId);
|
||||||
})
|
})
|
||||||
.then((txhash) => {
|
.then((txhash) => {
|
||||||
setState({ state: 'getTransactionReceipt', txhash });
|
statecb(null, { state: 'getTransactionReceipt', txhash });
|
||||||
return this._pollTransactionReceipt(txhash, gas);
|
return this._pollTransactionReceipt(txhash, gas);
|
||||||
})
|
})
|
||||||
.then((receipt) => {
|
.then((receipt) => {
|
||||||
@ -142,23 +134,23 @@ export default class Contract {
|
|||||||
throw new Error(`Contract not deployed, gasUsed == ${gas.toFixed(0)}`);
|
throw new Error(`Contract not deployed, gasUsed == ${gas.toFixed(0)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
setState({ state: 'hasReceipt', receipt });
|
statecb(null, { state: 'hasReceipt', receipt });
|
||||||
this._receipt = receipt;
|
this._receipt = receipt;
|
||||||
this._address = receipt.contractAddress;
|
this._address = receipt.contractAddress;
|
||||||
return this._address;
|
return this._address;
|
||||||
});
|
})
|
||||||
})
|
.then((address) => {
|
||||||
.then((address) => {
|
statecb(null, { state: 'getCode' });
|
||||||
setState({ state: 'getCode' });
|
return this._api.eth.getCode(this._address);
|
||||||
return this._api.eth.getCode(this._address);
|
})
|
||||||
})
|
.then((code) => {
|
||||||
.then((code) => {
|
if (code === '0x') {
|
||||||
if (code === '0x') {
|
throw new Error('Contract not deployed, getCode returned 0x');
|
||||||
throw new Error('Contract not deployed, getCode returned 0x');
|
}
|
||||||
}
|
|
||||||
|
|
||||||
setState({ state: 'completed' });
|
statecb(null, { state: 'completed' });
|
||||||
return this._address;
|
return this._address;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,6 +128,18 @@ export function outLog (log) {
|
|||||||
return log;
|
return log;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function outHwAccountInfo (infos) {
|
||||||
|
return Object
|
||||||
|
.keys(infos)
|
||||||
|
.reduce((ret, _address) => {
|
||||||
|
const address = outAddress(_address);
|
||||||
|
|
||||||
|
ret[address] = infos[_address];
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
export function outNumber (number) {
|
export function outNumber (number) {
|
||||||
return new BigNumber(number || 0);
|
return new BigNumber(number || 0);
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
|
|
||||||
import { outBlock, outAccountInfo, outAddress, outChainStatus, outDate, outHistogram, outNumber, outPeer, outPeers, outReceipt, outRecentDapps, outSyncing, outTransaction, outTrace, outVaultMeta } from './output';
|
import { outBlock, outAccountInfo, outAddress, outChainStatus, outDate, outHistogram, outHwAccountInfo, outNumber, outPeer, outPeers, outReceipt, outRecentDapps, outSyncing, outTransaction, outTrace, outVaultMeta } from './output';
|
||||||
import { isAddress, isBigNumber, isInstanceOf } from '../../../test/types';
|
import { isAddress, isBigNumber, isInstanceOf } from '../../../test/types';
|
||||||
|
|
||||||
describe('api/format/output', () => {
|
describe('api/format/output', () => {
|
||||||
@ -163,6 +163,16 @@ describe('api/format/output', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('outHwAccountInfo', () => {
|
||||||
|
it('returns objects with formatted addresses', () => {
|
||||||
|
expect(outHwAccountInfo(
|
||||||
|
{ '0x63cf90d3f0410092fc0fca41846f596223979195': { manufacturer: 'mfg', name: 'type' } }
|
||||||
|
)).to.deep.equal({
|
||||||
|
'0x63Cf90D3f0410092FC0fca41846f596223979195': { manufacturer: 'mfg', name: 'type' }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('outNumber', () => {
|
describe('outNumber', () => {
|
||||||
it('returns a BigNumber equalling the value', () => {
|
it('returns a BigNumber equalling the value', () => {
|
||||||
const bn = outNumber('0x123456');
|
const bn = outNumber('0x123456');
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import { inAddress, inAddresses, inData, inHex, inNumber16, inOptions, inBlockNumber } from '../../format/input';
|
import { inAddress, inAddresses, inData, inHex, inNumber16, inOptions, inBlockNumber } from '../../format/input';
|
||||||
import { outAccountInfo, outAddress, outAddresses, outChainStatus, outHistogram, outNumber, outPeers, outRecentDapps, outTransaction, outVaultMeta } from '../../format/output';
|
import { outAccountInfo, outAddress, outAddresses, outChainStatus, outHistogram, outHwAccountInfo, outNumber, outPeers, outRecentDapps, outTransaction, outVaultMeta } from '../../format/output';
|
||||||
|
|
||||||
export default class Parity {
|
export default class Parity {
|
||||||
constructor (transport) {
|
constructor (transport) {
|
||||||
@ -200,6 +200,12 @@ export default class Parity {
|
|||||||
.then(outVaultMeta);
|
.then(outVaultMeta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hardwareAccountsInfo () {
|
||||||
|
return this._transport
|
||||||
|
.execute('parity_hardwareAccountsInfo')
|
||||||
|
.then(outHwAccountInfo);
|
||||||
|
}
|
||||||
|
|
||||||
hashContent (url) {
|
hashContent (url) {
|
||||||
return this._transport
|
return this._transport
|
||||||
.execute('parity_hashContent', url);
|
.execute('parity_hashContent', url);
|
||||||
|
@ -34,7 +34,5 @@ export function abiEncode (methodName, inputTypes, data) {
|
|||||||
})
|
})
|
||||||
}, data);
|
}, data);
|
||||||
|
|
||||||
return methodName === null
|
return result;
|
||||||
? `0x${result.substr(10)}`
|
|
||||||
: result;
|
|
||||||
}
|
}
|
||||||
|
@ -14,34 +14,18 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import badgereg from './badgereg.json';
|
export badgereg from './badgereg.json';
|
||||||
import basiccoin from './basiccoin.json';
|
export basiccoin from './basiccoin.json';
|
||||||
import basiccoinmanager from './basiccoinmanager.json';
|
export basiccoinmanager from './basiccoinmanager.json';
|
||||||
import dappreg from './dappreg.json';
|
export dappreg from './dappreg.json';
|
||||||
import eip20 from './eip20.json';
|
export eip20 from './eip20.json';
|
||||||
import emailverification from './email-verification.json';
|
export emailverification from './email-verification.json';
|
||||||
import gavcoin from './gavcoin.json';
|
export gavcoin from './gavcoin.json';
|
||||||
import githubhint from './githubhint.json';
|
export githubhint from './githubhint.json';
|
||||||
import owned from './owned.json';
|
export owned from './owned.json';
|
||||||
import registry from './registry.json';
|
export registry from './registry.json';
|
||||||
import signaturereg from './signaturereg.json';
|
export registry2 from './registry2.json';
|
||||||
import smsverification from './sms-verification.json';
|
export signaturereg from './signaturereg.json';
|
||||||
import tokenreg from './tokenreg.json';
|
export smsverification from './sms-verification.json';
|
||||||
import wallet from './wallet.json';
|
export tokenreg from './tokenreg.json';
|
||||||
|
export wallet from './wallet.json';
|
||||||
export {
|
|
||||||
badgereg,
|
|
||||||
basiccoin,
|
|
||||||
basiccoinmanager,
|
|
||||||
dappreg,
|
|
||||||
eip20,
|
|
||||||
emailverification,
|
|
||||||
gavcoin,
|
|
||||||
githubhint,
|
|
||||||
owned,
|
|
||||||
registry,
|
|
||||||
signaturereg,
|
|
||||||
smsverification,
|
|
||||||
tokenreg,
|
|
||||||
wallet
|
|
||||||
};
|
|
||||||
|
466
js/src/contracts/abi/old-wallet.json
Normal file
466
js/src/contracts/abi/old-wallet.json
Normal file
@ -0,0 +1,466 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_owner",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "removeOwner",
|
||||||
|
"outputs": [],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_addr",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "isOwner",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "m_numOwners",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "m_lastDay",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "resetSpentToday",
|
||||||
|
"outputs": [],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "m_spentToday",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_owner",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "addOwner",
|
||||||
|
"outputs": [],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "m_required",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_h",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "confirm",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_newLimit",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "setDailyLimit",
|
||||||
|
"outputs": [],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_to",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_value",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_data",
|
||||||
|
"type": "bytes"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "execute",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "_r",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_operation",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "revoke",
|
||||||
|
"outputs": [],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_newRequired",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "changeRequirement",
|
||||||
|
"outputs": [],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_operation",
|
||||||
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_owner",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "hasConfirmed",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "ownerIndex",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "getOwner",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_to",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "kill",
|
||||||
|
"outputs": [],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_from",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_to",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "changeOwner",
|
||||||
|
"outputs": [],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "m_dailyLimit",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_owners",
|
||||||
|
"type": "address[]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_required",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_daylimit",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "constructor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "owner",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "operation",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "Confirmation",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "owner",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "operation",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "Revoke",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "oldOwner",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "newOwner",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "OwnerChanged",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "newOwner",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "OwnerAdded",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "oldOwner",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "OwnerRemoved",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "newRequirement",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "RequirementChanged",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "_from",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "value",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "Deposit",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "owner",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "value",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "to",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "data",
|
||||||
|
"type": "bytes"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "SingleTransact",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "owner",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "operation",
|
||||||
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "value",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "to",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "data",
|
||||||
|
"type": "bytes"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "MultiTransact",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "operation",
|
||||||
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "initiator",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "value",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "to",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "data",
|
||||||
|
"type": "bytes"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "ConfirmationNeeded",
|
||||||
|
"type": "event"
|
||||||
|
}
|
||||||
|
]
|
1
js/src/contracts/abi/registry2.json
Normal file
1
js/src/contracts/abi/registry2.json
Normal file
File diff suppressed because one or more lines are too long
@ -1 +1,476 @@
|
|||||||
[{"constant":false,"inputs":[{"name":"_owner","type":"address"}],"name":"removeOwner","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_addr","type":"address"}],"name":"isOwner","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":true,"inputs":[],"name":"m_numOwners","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"m_lastDay","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"resetSpentToday","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"m_spentToday","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"_owner","type":"address"}],"name":"addOwner","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"m_required","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"_h","type":"bytes32"}],"name":"confirm","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_newLimit","type":"uint256"}],"name":"setDailyLimit","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"},{"name":"_data","type":"bytes"}],"name":"execute","outputs":[{"name":"_r","type":"bytes32"}],"type":"function"},{"constant":false,"inputs":[{"name":"_operation","type":"bytes32"}],"name":"revoke","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_newRequired","type":"uint256"}],"name":"changeRequirement","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_operation","type":"bytes32"},{"name":"_owner","type":"address"}],"name":"hasConfirmed","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"ownerIndex","type":"uint256"}],"name":"getOwner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"}],"name":"kill","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"}],"name":"changeOwner","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"m_dailyLimit","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"inputs":[{"name":"_owners","type":"address[]"},{"name":"_required","type":"uint256"},{"name":"_daylimit","type":"uint256"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"operation","type":"bytes32"}],"name":"Confirmation","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"operation","type":"bytes32"}],"name":"Revoke","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldOwner","type":"address"},{"indexed":false,"name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newOwner","type":"address"}],"name":"OwnerAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldOwner","type":"address"}],"name":"OwnerRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newRequirement","type":"uint256"}],"name":"RequirementChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_from","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"data","type":"bytes"}],"name":"SingleTransact","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"operation","type":"bytes32"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"data","type":"bytes"}],"name":"MultiTransact","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"operation","type":"bytes32"},{"indexed":false,"name":"initiator","type":"address"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"data","type":"bytes"}],"name":"ConfirmationNeeded","type":"event"}]
|
[
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_owner",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "removeOwner",
|
||||||
|
"outputs": [],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_addr",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "isOwner",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "m_numOwners",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "m_lastDay",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "resetSpentToday",
|
||||||
|
"outputs": [],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "m_spentToday",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_owner",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "addOwner",
|
||||||
|
"outputs": [],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "m_required",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_h",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "confirm",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_newLimit",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "setDailyLimit",
|
||||||
|
"outputs": [],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_to",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_value",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_data",
|
||||||
|
"type": "bytes"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "execute",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "_r",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_operation",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "revoke",
|
||||||
|
"outputs": [],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_newRequired",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "changeRequirement",
|
||||||
|
"outputs": [],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_operation",
|
||||||
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_owner",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "hasConfirmed",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "ownerIndex",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "getOwner",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_to",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "kill",
|
||||||
|
"outputs": [],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_from",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_to",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "changeOwner",
|
||||||
|
"outputs": [],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "m_dailyLimit",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_owners",
|
||||||
|
"type": "address[]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_required",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_daylimit",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "constructor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "owner",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "operation",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "Confirmation",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "owner",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "operation",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "Revoke",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "oldOwner",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "newOwner",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "OwnerChanged",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "newOwner",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "OwnerAdded",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "oldOwner",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "OwnerRemoved",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "newRequirement",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "RequirementChanged",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "_from",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "value",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "Deposit",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "owner",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "value",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "to",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "data",
|
||||||
|
"type": "bytes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "created",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "SingleTransact",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "owner",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "operation",
|
||||||
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "value",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "to",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "data",
|
||||||
|
"type": "bytes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "created",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "MultiTransact",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "operation",
|
||||||
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "initiator",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "value",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "to",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "data",
|
||||||
|
"type": "bytes"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "ConfirmationNeeded",
|
||||||
|
"type": "event"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
File diff suppressed because one or more lines are too long
@ -8,453 +8,454 @@
|
|||||||
// use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by
|
// use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by
|
||||||
// some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the
|
// some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the
|
||||||
// interior is executed.
|
// interior is executed.
|
||||||
pragma solidity ^0.4.6;
|
|
||||||
|
|
||||||
contract multisig {
|
pragma solidity ^0.4.9;
|
||||||
// EVENTS
|
|
||||||
|
|
||||||
// this contract can accept a confirmation, in which case
|
contract WalletEvents {
|
||||||
// we record owner and operation (hash) alongside it.
|
// EVENTS
|
||||||
event Confirmation(address owner, bytes32 operation);
|
|
||||||
event Revoke(address owner, bytes32 operation);
|
|
||||||
|
|
||||||
// some others are in the case of an owner changing.
|
// this contract only has six types of events: it can accept a confirmation, in which case
|
||||||
event OwnerChanged(address oldOwner, address newOwner);
|
// we record owner and operation (hash) alongside it.
|
||||||
event OwnerAdded(address newOwner);
|
event Confirmation(address owner, bytes32 operation);
|
||||||
event OwnerRemoved(address oldOwner);
|
event Revoke(address owner, bytes32 operation);
|
||||||
|
|
||||||
// the last one is emitted if the required signatures change
|
// some others are in the case of an owner changing.
|
||||||
event RequirementChanged(uint newRequirement);
|
event OwnerChanged(address oldOwner, address newOwner);
|
||||||
|
event OwnerAdded(address newOwner);
|
||||||
|
event OwnerRemoved(address oldOwner);
|
||||||
|
|
||||||
// Funds has arrived into the wallet (record how much).
|
// the last one is emitted if the required signatures change
|
||||||
event Deposit(address _from, uint value);
|
event RequirementChanged(uint newRequirement);
|
||||||
// Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
|
|
||||||
event SingleTransact(address owner, uint value, address to, bytes data);
|
// Funds has arrived into the wallet (record how much).
|
||||||
// Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
|
event Deposit(address _from, uint value);
|
||||||
event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data);
|
// Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
|
||||||
// Confirmation still needed for a transaction.
|
event SingleTransact(address owner, uint value, address to, bytes data, address created);
|
||||||
event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
|
// Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
|
||||||
|
event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data, address created);
|
||||||
|
// Confirmation still needed for a transaction.
|
||||||
|
event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
|
||||||
}
|
}
|
||||||
|
|
||||||
contract multisigAbi is multisig {
|
contract WalletAbi {
|
||||||
function isOwner(address _addr) returns (bool);
|
// Revokes a prior confirmation of the given operation
|
||||||
|
function revoke(bytes32 _operation) external;
|
||||||
|
|
||||||
function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool);
|
// Replaces an owner `_from` with another `_to`.
|
||||||
|
function changeOwner(address _from, address _to) external;
|
||||||
|
|
||||||
function confirm(bytes32 _h) returns(bool);
|
function addOwner(address _owner) external;
|
||||||
|
|
||||||
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
|
function removeOwner(address _owner) external;
|
||||||
function setDailyLimit(uint _newLimit);
|
|
||||||
|
|
||||||
function addOwner(address _owner);
|
function changeRequirement(uint _newRequired) external;
|
||||||
|
|
||||||
function removeOwner(address _owner);
|
function isOwner(address _addr) constant returns (bool);
|
||||||
|
|
||||||
function changeRequirement(uint _newRequired);
|
function hasConfirmed(bytes32 _operation, address _owner) external constant returns (bool);
|
||||||
|
|
||||||
// Revokes a prior confirmation of the given operation
|
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
|
||||||
function revoke(bytes32 _operation);
|
function setDailyLimit(uint _newLimit) external;
|
||||||
|
|
||||||
function changeOwner(address _from, address _to);
|
function execute(address _to, uint _value, bytes _data) external returns (bytes32 o_hash);
|
||||||
|
function confirm(bytes32 _h) returns (bool o_success);
|
||||||
function execute(address _to, uint _value, bytes _data) returns(bool);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
contract WalletLibrary is multisig {
|
contract WalletLibrary is WalletEvents {
|
||||||
// TYPES
|
// TYPES
|
||||||
|
|
||||||
// struct for the status of a pending operation.
|
// struct for the status of a pending operation.
|
||||||
struct PendingState {
|
struct PendingState {
|
||||||
uint yetNeeded;
|
uint yetNeeded;
|
||||||
uint ownersDone;
|
uint ownersDone;
|
||||||
uint index;
|
uint index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transaction structure to remember details of transaction lest it need be saved for a later call.
|
||||||
|
struct Transaction {
|
||||||
|
address to;
|
||||||
|
uint value;
|
||||||
|
bytes data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MODIFIERS
|
||||||
|
|
||||||
|
// simple single-sig function modifier.
|
||||||
|
modifier onlyowner {
|
||||||
|
if (isOwner(msg.sender))
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
// multi-sig function modifier: the operation must have an intrinsic hash in order
|
||||||
|
// that later attempts can be realised as the same underlying operation and
|
||||||
|
// thus count as confirmations.
|
||||||
|
modifier onlymanyowners(bytes32 _operation) {
|
||||||
|
if (confirmAndCheck(_operation))
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// METHODS
|
||||||
|
|
||||||
|
// gets called when no other function matches
|
||||||
|
function() payable {
|
||||||
|
// just being sent some cash?
|
||||||
|
if (msg.value > 0)
|
||||||
|
Deposit(msg.sender, msg.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// constructor is given number of sigs required to do protected "onlymanyowners" transactions
|
||||||
|
// as well as the selection of addresses capable of confirming them.
|
||||||
|
function initMultiowned(address[] _owners, uint _required) {
|
||||||
|
m_numOwners = _owners.length + 1;
|
||||||
|
m_owners[1] = uint(msg.sender);
|
||||||
|
m_ownerIndex[uint(msg.sender)] = 1;
|
||||||
|
for (uint i = 0; i < _owners.length; ++i)
|
||||||
|
{
|
||||||
|
m_owners[2 + i] = uint(_owners[i]);
|
||||||
|
m_ownerIndex[uint(_owners[i])] = 2 + i;
|
||||||
|
}
|
||||||
|
m_required = _required;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revokes a prior confirmation of the given operation
|
||||||
|
function revoke(bytes32 _operation) external {
|
||||||
|
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
||||||
|
// make sure they're an owner
|
||||||
|
if (ownerIndex == 0) return;
|
||||||
|
uint ownerIndexBit = 2**ownerIndex;
|
||||||
|
var pending = m_pending[_operation];
|
||||||
|
if (pending.ownersDone & ownerIndexBit > 0) {
|
||||||
|
pending.yetNeeded++;
|
||||||
|
pending.ownersDone -= ownerIndexBit;
|
||||||
|
Revoke(msg.sender, _operation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replaces an owner `_from` with another `_to`.
|
||||||
|
function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) external {
|
||||||
|
if (isOwner(_to)) return;
|
||||||
|
uint ownerIndex = m_ownerIndex[uint(_from)];
|
||||||
|
if (ownerIndex == 0) return;
|
||||||
|
|
||||||
|
clearPending();
|
||||||
|
m_owners[ownerIndex] = uint(_to);
|
||||||
|
m_ownerIndex[uint(_from)] = 0;
|
||||||
|
m_ownerIndex[uint(_to)] = ownerIndex;
|
||||||
|
OwnerChanged(_from, _to);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
|
||||||
|
if (isOwner(_owner)) return;
|
||||||
|
|
||||||
|
clearPending();
|
||||||
|
if (m_numOwners >= c_maxOwners)
|
||||||
|
reorganizeOwners();
|
||||||
|
if (m_numOwners >= c_maxOwners)
|
||||||
|
return;
|
||||||
|
m_numOwners++;
|
||||||
|
m_owners[m_numOwners] = uint(_owner);
|
||||||
|
m_ownerIndex[uint(_owner)] = m_numOwners;
|
||||||
|
OwnerAdded(_owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
|
||||||
|
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
||||||
|
if (ownerIndex == 0) return;
|
||||||
|
if (m_required > m_numOwners - 1) return;
|
||||||
|
|
||||||
|
m_owners[ownerIndex] = 0;
|
||||||
|
m_ownerIndex[uint(_owner)] = 0;
|
||||||
|
clearPending();
|
||||||
|
reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot
|
||||||
|
OwnerRemoved(_owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external {
|
||||||
|
if (_newRequired > m_numOwners) return;
|
||||||
|
m_required = _newRequired;
|
||||||
|
clearPending();
|
||||||
|
RequirementChanged(_newRequired);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets an owner by 0-indexed position (using numOwners as the count)
|
||||||
|
function getOwner(uint ownerIndex) external constant returns (address) {
|
||||||
|
return address(m_owners[ownerIndex + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isOwner(address _addr) constant returns (bool) {
|
||||||
|
return m_ownerIndex[uint(_addr)] > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasConfirmed(bytes32 _operation, address _owner) external constant returns (bool) {
|
||||||
|
var pending = m_pending[_operation];
|
||||||
|
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
||||||
|
|
||||||
|
// make sure they're an owner
|
||||||
|
if (ownerIndex == 0) return false;
|
||||||
|
|
||||||
|
// determine the bit to set for this owner.
|
||||||
|
uint ownerIndexBit = 2**ownerIndex;
|
||||||
|
return !(pending.ownersDone & ownerIndexBit == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// constructor - stores initial daily limit and records the present day's index.
|
||||||
|
function initDaylimit(uint _limit) {
|
||||||
|
m_dailyLimit = _limit;
|
||||||
|
m_lastDay = today();
|
||||||
|
}
|
||||||
|
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
|
||||||
|
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external {
|
||||||
|
m_dailyLimit = _newLimit;
|
||||||
|
}
|
||||||
|
// resets the amount already spent today. needs many of the owners to confirm.
|
||||||
|
function resetSpentToday() onlymanyowners(sha3(msg.data)) external {
|
||||||
|
m_spentToday = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// constructor - just pass on the owner array to the multiowned and
|
||||||
|
// the limit to daylimit
|
||||||
|
function initWallet(address[] _owners, uint _required, uint _daylimit) {
|
||||||
|
initDaylimit(_daylimit);
|
||||||
|
initMultiowned(_owners, _required);
|
||||||
|
}
|
||||||
|
|
||||||
|
// kills the contract sending everything to `_to`.
|
||||||
|
function kill(address _to) onlymanyowners(sha3(msg.data)) external {
|
||||||
|
suicide(_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Outside-visible transact entry point. Executes transaction immediately if below daily spend limit.
|
||||||
|
// If not, goes into multisig process. We provide a hash on return to allow the sender to provide
|
||||||
|
// shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
|
||||||
|
// and _data arguments). They still get the option of using them if they want, anyways.
|
||||||
|
function execute(address _to, uint _value, bytes _data) external onlyowner returns (bytes32 o_hash) {
|
||||||
|
// first, take the opportunity to check that we're under the daily limit.
|
||||||
|
if ((_data.length == 0 && underLimit(_value)) || m_required == 1) {
|
||||||
|
// yes - just execute the call.
|
||||||
|
address created;
|
||||||
|
if (_to == 0) {
|
||||||
|
created = create(_value, _data);
|
||||||
|
} else {
|
||||||
|
if (!_to.call.value(_value)(_data))
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
SingleTransact(msg.sender, _value, _to, _data, created);
|
||||||
|
} else {
|
||||||
|
// determine our operation hash.
|
||||||
|
o_hash = sha3(msg.data, block.number);
|
||||||
|
// store if it's new
|
||||||
|
if (m_txs[o_hash].to == 0 && m_txs[o_hash].value == 0 && m_txs[o_hash].data.length == 0) {
|
||||||
|
m_txs[o_hash].to = _to;
|
||||||
|
m_txs[o_hash].value = _value;
|
||||||
|
m_txs[o_hash].data = _data;
|
||||||
|
}
|
||||||
|
if (!confirm(o_hash)) {
|
||||||
|
ConfirmationNeeded(o_hash, msg.sender, _value, _to, _data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function create(uint _value, bytes _code) internal returns (address o_addr) {
|
||||||
|
assembly {
|
||||||
|
o_addr := create(_value, add(_code, 0x20), mload(_code))
|
||||||
|
jumpi(invalidJumpLabel, iszero(extcodesize(o_addr)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order
|
||||||
|
// to determine the body of the transaction from the hash provided.
|
||||||
|
function confirm(bytes32 _h) onlymanyowners(_h) returns (bool o_success) {
|
||||||
|
if (m_txs[_h].to != 0 || m_txs[_h].value != 0 || m_txs[_h].data.length != 0) {
|
||||||
|
address created;
|
||||||
|
if (m_txs[_h].to == 0) {
|
||||||
|
created = create(m_txs[_h].value, m_txs[_h].data);
|
||||||
|
} else {
|
||||||
|
if (!m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data))
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data, created);
|
||||||
|
delete m_txs[_h];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// INTERNAL METHODS
|
||||||
|
|
||||||
|
function confirmAndCheck(bytes32 _operation) internal returns (bool) {
|
||||||
|
// determine what index the present sender is:
|
||||||
|
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
||||||
|
// make sure they're an owner
|
||||||
|
if (ownerIndex == 0) return;
|
||||||
|
|
||||||
|
var pending = m_pending[_operation];
|
||||||
|
// if we're not yet working on this operation, switch over and reset the confirmation status.
|
||||||
|
if (pending.yetNeeded == 0) {
|
||||||
|
// reset count of confirmations needed.
|
||||||
|
pending.yetNeeded = m_required;
|
||||||
|
// reset which owners have confirmed (none) - set our bitmap to 0.
|
||||||
|
pending.ownersDone = 0;
|
||||||
|
pending.index = m_pendingIndex.length++;
|
||||||
|
m_pendingIndex[pending.index] = _operation;
|
||||||
|
}
|
||||||
|
// determine the bit to set for this owner.
|
||||||
|
uint ownerIndexBit = 2**ownerIndex;
|
||||||
|
// make sure we (the message sender) haven't confirmed this operation previously.
|
||||||
|
if (pending.ownersDone & ownerIndexBit == 0) {
|
||||||
|
Confirmation(msg.sender, _operation);
|
||||||
|
// ok - check if count is enough to go ahead.
|
||||||
|
if (pending.yetNeeded <= 1) {
|
||||||
|
// enough confirmations: reset and run interior.
|
||||||
|
delete m_pendingIndex[m_pending[_operation].index];
|
||||||
|
delete m_pending[_operation];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// not enough: record that this owner in particular confirmed.
|
||||||
|
pending.yetNeeded--;
|
||||||
|
pending.ownersDone |= ownerIndexBit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reorganizeOwners() private {
|
||||||
|
uint free = 1;
|
||||||
|
while (free < m_numOwners)
|
||||||
|
{
|
||||||
|
while (free < m_numOwners && m_owners[free] != 0) free++;
|
||||||
|
while (m_numOwners > 1 && m_owners[m_numOwners] == 0) m_numOwners--;
|
||||||
|
if (free < m_numOwners && m_owners[m_numOwners] != 0 && m_owners[free] == 0)
|
||||||
|
{
|
||||||
|
m_owners[free] = m_owners[m_numOwners];
|
||||||
|
m_ownerIndex[m_owners[free]] = free;
|
||||||
|
m_owners[m_numOwners] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
|
||||||
|
// returns true. otherwise just returns false.
|
||||||
|
function underLimit(uint _value) internal onlyowner returns (bool) {
|
||||||
|
// reset the spend limit if we're on a different day to last time.
|
||||||
|
if (today() > m_lastDay) {
|
||||||
|
m_spentToday = 0;
|
||||||
|
m_lastDay = today();
|
||||||
|
}
|
||||||
|
// check to see if there's enough left - if so, subtract and return true.
|
||||||
|
// overflow protection // dailyLimit check
|
||||||
|
if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) {
|
||||||
|
m_spentToday += _value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// determines today's index.
|
||||||
|
function today() private constant returns (uint) { return now / 1 days; }
|
||||||
|
|
||||||
|
function clearPending() internal {
|
||||||
|
uint length = m_pendingIndex.length;
|
||||||
|
|
||||||
|
for (uint i = 0; i < length; ++i) {
|
||||||
|
delete m_txs[m_pendingIndex[i]];
|
||||||
|
|
||||||
|
if (m_pendingIndex[i] != 0)
|
||||||
|
delete m_pending[m_pendingIndex[i]];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transaction structure to remember details of transaction lest it need be saved for a later call.
|
delete m_pendingIndex;
|
||||||
struct Transaction {
|
}
|
||||||
address to;
|
|
||||||
uint value;
|
|
||||||
bytes data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************
|
// FIELDS
|
||||||
***** MULTI OWNED SECTION ****
|
address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe;
|
||||||
******************************/
|
|
||||||
|
|
||||||
// MODIFIERS
|
// the number of owners that must confirm the same operation before it is run.
|
||||||
|
uint public m_required;
|
||||||
|
// pointer used to find a free slot in m_owners
|
||||||
|
uint public m_numOwners;
|
||||||
|
|
||||||
// simple single-sig function modifier.
|
uint public m_dailyLimit;
|
||||||
modifier onlyowner {
|
uint public m_spentToday;
|
||||||
if (isOwner(msg.sender))
|
uint public m_lastDay;
|
||||||
_;
|
|
||||||
}
|
|
||||||
// multi-sig function modifier: the operation must have an intrinsic hash in order
|
|
||||||
// that later attempts can be realised as the same underlying operation and
|
|
||||||
// thus count as confirmations.
|
|
||||||
modifier onlymanyowners(bytes32 _operation) {
|
|
||||||
if (confirmAndCheck(_operation))
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// METHODS
|
// list of owners
|
||||||
|
uint[256] m_owners;
|
||||||
|
|
||||||
// constructor is given number of sigs required to do protected "onlymanyowners" transactions
|
uint constant c_maxOwners = 250;
|
||||||
// as well as the selection of addresses capable of confirming them.
|
// index on the list of owners to allow reverse lookup
|
||||||
function initMultiowned(address[] _owners, uint _required) {
|
mapping(uint => uint) m_ownerIndex;
|
||||||
m_numOwners = _owners.length + 1;
|
// the ongoing operations.
|
||||||
m_owners[1] = uint(msg.sender);
|
mapping(bytes32 => PendingState) m_pending;
|
||||||
m_ownerIndex[uint(msg.sender)] = 1;
|
bytes32[] m_pendingIndex;
|
||||||
m_required = _required;
|
|
||||||
|
|
||||||
for (uint i = 0; i < _owners.length; ++i)
|
// pending transactions we have at present.
|
||||||
{
|
mapping (bytes32 => Transaction) m_txs;
|
||||||
m_owners[2 + i] = uint(_owners[i]);
|
|
||||||
m_ownerIndex[uint(_owners[i])] = 2 + i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Revokes a prior confirmation of the given operation
|
|
||||||
function revoke(bytes32 _operation) {
|
|
||||||
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
|
||||||
// make sure they're an owner
|
|
||||||
if (ownerIndex == 0) return;
|
|
||||||
uint ownerIndexBit = 2**ownerIndex;
|
|
||||||
var pending = m_pending[_operation];
|
|
||||||
if (pending.ownersDone & ownerIndexBit > 0) {
|
|
||||||
pending.yetNeeded++;
|
|
||||||
pending.ownersDone -= ownerIndexBit;
|
|
||||||
Revoke(msg.sender, _operation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replaces an owner `_from` with another `_to`.
|
|
||||||
function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) {
|
|
||||||
if (isOwner(_to)) return;
|
|
||||||
uint ownerIndex = m_ownerIndex[uint(_from)];
|
|
||||||
if (ownerIndex == 0) return;
|
|
||||||
|
|
||||||
clearPending();
|
|
||||||
m_owners[ownerIndex] = uint(_to);
|
|
||||||
m_ownerIndex[uint(_from)] = 0;
|
|
||||||
m_ownerIndex[uint(_to)] = ownerIndex;
|
|
||||||
OwnerChanged(_from, _to);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addOwner(address _owner) onlymanyowners(sha3(msg.data)) {
|
|
||||||
if (isOwner(_owner)) return;
|
|
||||||
|
|
||||||
clearPending();
|
|
||||||
if (m_numOwners >= c_maxOwners)
|
|
||||||
reorganizeOwners();
|
|
||||||
if (m_numOwners >= c_maxOwners)
|
|
||||||
return;
|
|
||||||
m_numOwners++;
|
|
||||||
m_owners[m_numOwners] = uint(_owner);
|
|
||||||
m_ownerIndex[uint(_owner)] = m_numOwners;
|
|
||||||
OwnerAdded(_owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) {
|
|
||||||
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
|
||||||
if (ownerIndex == 0) return;
|
|
||||||
if (m_required > m_numOwners - 1) return;
|
|
||||||
|
|
||||||
m_owners[ownerIndex] = 0;
|
|
||||||
m_ownerIndex[uint(_owner)] = 0;
|
|
||||||
clearPending();
|
|
||||||
reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot
|
|
||||||
OwnerRemoved(_owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) {
|
|
||||||
if (_newRequired > m_numOwners) return;
|
|
||||||
m_required = _newRequired;
|
|
||||||
clearPending();
|
|
||||||
RequirementChanged(_newRequired);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isOwner(address _addr) returns (bool) {
|
|
||||||
return m_ownerIndex[uint(_addr)] > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) {
|
|
||||||
var pending = m_pending[_operation];
|
|
||||||
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
|
||||||
|
|
||||||
// make sure they're an owner
|
|
||||||
if (ownerIndex == 0) return false;
|
|
||||||
|
|
||||||
// determine the bit to set for this owner.
|
|
||||||
uint ownerIndexBit = 2**ownerIndex;
|
|
||||||
return !(pending.ownersDone & ownerIndexBit == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// INTERNAL METHODS
|
|
||||||
|
|
||||||
function confirmAndCheck(bytes32 _operation) internal returns (bool) {
|
|
||||||
// determine what index the present sender is:
|
|
||||||
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
|
||||||
// make sure they're an owner
|
|
||||||
if (ownerIndex == 0) return;
|
|
||||||
|
|
||||||
var pending = m_pending[_operation];
|
|
||||||
// if we're not yet working on this operation, switch over and reset the confirmation status.
|
|
||||||
if (pending.yetNeeded == 0) {
|
|
||||||
// reset count of confirmations needed.
|
|
||||||
pending.yetNeeded = m_required;
|
|
||||||
// reset which owners have confirmed (none) - set our bitmap to 0.
|
|
||||||
pending.ownersDone = 0;
|
|
||||||
pending.index = m_pendingIndex.length++;
|
|
||||||
m_pendingIndex[pending.index] = _operation;
|
|
||||||
}
|
|
||||||
// determine the bit to set for this owner.
|
|
||||||
uint ownerIndexBit = 2**ownerIndex;
|
|
||||||
// make sure we (the message sender) haven't confirmed this operation previously.
|
|
||||||
if (pending.ownersDone & ownerIndexBit == 0) {
|
|
||||||
Confirmation(msg.sender, _operation);
|
|
||||||
// ok - check if count is enough to go ahead.
|
|
||||||
if (pending.yetNeeded <= 1) {
|
|
||||||
// enough confirmations: reset and run interior.
|
|
||||||
delete m_pendingIndex[m_pending[_operation].index];
|
|
||||||
delete m_pending[_operation];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// not enough: record that this owner in particular confirmed.
|
|
||||||
pending.yetNeeded--;
|
|
||||||
pending.ownersDone |= ownerIndexBit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function reorganizeOwners() private {
|
|
||||||
uint free = 1;
|
|
||||||
while (free < m_numOwners)
|
|
||||||
{
|
|
||||||
while (free < m_numOwners && m_owners[free] != 0) free++;
|
|
||||||
while (m_numOwners > 1 && m_owners[m_numOwners] == 0) m_numOwners--;
|
|
||||||
if (free < m_numOwners && m_owners[m_numOwners] != 0 && m_owners[free] == 0)
|
|
||||||
{
|
|
||||||
m_owners[free] = m_owners[m_numOwners];
|
|
||||||
m_ownerIndex[m_owners[free]] = free;
|
|
||||||
m_owners[m_numOwners] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearPending() internal {
|
|
||||||
uint length = m_pendingIndex.length;
|
|
||||||
for (uint i = 0; i < length; ++i)
|
|
||||||
if (m_pendingIndex[i] != 0)
|
|
||||||
delete m_pending[m_pendingIndex[i]];
|
|
||||||
delete m_pendingIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/******************************
|
|
||||||
****** DAY LIMIT SECTION *****
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
// MODIFIERS
|
|
||||||
|
|
||||||
// simple modifier for daily limit.
|
|
||||||
modifier limitedDaily(uint _value) {
|
|
||||||
if (underLimit(_value))
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// METHODS
|
|
||||||
|
|
||||||
// constructor - stores initial daily limit and records the present day's index.
|
|
||||||
function initDaylimit(uint _limit) {
|
|
||||||
m_dailyLimit = _limit;
|
|
||||||
m_lastDay = today();
|
|
||||||
}
|
|
||||||
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
|
|
||||||
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) {
|
|
||||||
m_dailyLimit = _newLimit;
|
|
||||||
}
|
|
||||||
// resets the amount already spent today. needs many of the owners to confirm.
|
|
||||||
function resetSpentToday() onlymanyowners(sha3(msg.data)) {
|
|
||||||
m_spentToday = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// INTERNAL METHODS
|
|
||||||
|
|
||||||
// checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
|
|
||||||
// returns true. otherwise just returns false.
|
|
||||||
function underLimit(uint _value) internal onlyowner returns (bool) {
|
|
||||||
// reset the spend limit if we're on a different day to last time.
|
|
||||||
if (today() > m_lastDay) {
|
|
||||||
m_spentToday = 0;
|
|
||||||
m_lastDay = today();
|
|
||||||
}
|
|
||||||
// check to see if there's enough left - if so, subtract and return true.
|
|
||||||
// overflow protection // dailyLimit check
|
|
||||||
if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) {
|
|
||||||
m_spentToday += _value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// determines today's index.
|
|
||||||
function today() private constant returns (uint) { return now / 1 days; }
|
|
||||||
|
|
||||||
|
|
||||||
/******************************
|
|
||||||
********* WALLET SECTION *****
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
// METHODS
|
|
||||||
|
|
||||||
// constructor - just pass on the owner array to the multiowned and
|
|
||||||
// the limit to daylimit
|
|
||||||
function initWallet(address[] _owners, uint _required, uint _daylimit) {
|
|
||||||
initMultiowned(_owners, _required);
|
|
||||||
initDaylimit(_daylimit) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
// kills the contract sending everything to `_to`.
|
|
||||||
function kill(address _to) onlymanyowners(sha3(msg.data)) {
|
|
||||||
suicide(_to);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Outside-visible transact entry point. Executes transaction immediately if below daily spend limit.
|
|
||||||
// If not, goes into multisig process. We provide a hash on return to allow the sender to provide
|
|
||||||
// shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
|
|
||||||
// and _data arguments). They still get the option of using them if they want, anyways.
|
|
||||||
function execute(address _to, uint _value, bytes _data) onlyowner returns(bool _callValue) {
|
|
||||||
// first, take the opportunity to check that we're under the daily limit.
|
|
||||||
if (underLimit(_value)) {
|
|
||||||
SingleTransact(msg.sender, _value, _to, _data);
|
|
||||||
// yes - just execute the call.
|
|
||||||
_callValue =_to.call.value(_value)(_data);
|
|
||||||
} else {
|
|
||||||
// determine our operation hash.
|
|
||||||
bytes32 _r = sha3(msg.data, block.number);
|
|
||||||
if (!confirm(_r) && m_txs[_r].to == 0) {
|
|
||||||
m_txs[_r].to = _to;
|
|
||||||
m_txs[_r].value = _value;
|
|
||||||
m_txs[_r].data = _data;
|
|
||||||
ConfirmationNeeded(_r, msg.sender, _value, _to, _data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order
|
|
||||||
// to determine the body of the transaction from the hash provided.
|
|
||||||
function confirm(bytes32 _h) onlymanyowners(_h) returns (bool) {
|
|
||||||
if (m_txs[_h].to != 0) {
|
|
||||||
m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data);
|
|
||||||
MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data);
|
|
||||||
delete m_txs[_h];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// INTERNAL METHODS
|
|
||||||
|
|
||||||
function clearWalletPending() internal {
|
|
||||||
uint length = m_pendingIndex.length;
|
|
||||||
for (uint i = 0; i < length; ++i)
|
|
||||||
delete m_txs[m_pendingIndex[i]];
|
|
||||||
clearPending();
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIELDS
|
|
||||||
address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe;
|
|
||||||
|
|
||||||
// the number of owners that must confirm the same operation before it is run.
|
|
||||||
uint m_required;
|
|
||||||
// pointer used to find a free slot in m_owners
|
|
||||||
uint m_numOwners;
|
|
||||||
|
|
||||||
uint public m_dailyLimit;
|
|
||||||
uint public m_spentToday;
|
|
||||||
uint public m_lastDay;
|
|
||||||
|
|
||||||
// list of owners
|
|
||||||
uint[256] m_owners;
|
|
||||||
uint constant c_maxOwners = 250;
|
|
||||||
|
|
||||||
// index on the list of owners to allow reverse lookup
|
|
||||||
mapping(uint => uint) m_ownerIndex;
|
|
||||||
// the ongoing operations.
|
|
||||||
mapping(bytes32 => PendingState) m_pending;
|
|
||||||
bytes32[] m_pendingIndex;
|
|
||||||
|
|
||||||
// pending transactions we have at present.
|
|
||||||
mapping (bytes32 => Transaction) m_txs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
contract Wallet is WalletEvents {
|
||||||
|
|
||||||
contract Wallet is multisig {
|
// WALLET CONSTRUCTOR
|
||||||
|
// calls the `initWallet` method of the Library in this context
|
||||||
|
function Wallet(address[] _owners, uint _required, uint _daylimit) {
|
||||||
|
// Signature of the Wallet Library's init function
|
||||||
|
bytes4 sig = bytes4(sha3("initWallet(address[],uint256,uint256)"));
|
||||||
|
address target = _walletLibrary;
|
||||||
|
|
||||||
// WALLET CONSTRUCTOR
|
// Compute the size of the call data : arrays has 2
|
||||||
// calls the `initWallet` method of the Library in this context
|
// 32bytes for offset and length, plus 32bytes per element ;
|
||||||
function Wallet(address[] _owners, uint _required, uint _daylimit) {
|
// plus 2 32bytes for each uint
|
||||||
// Signature of the Wallet Library's init function
|
uint argarraysize = (2 + _owners.length);
|
||||||
bytes4 sig = bytes4(sha3("initWallet(address[],uint256,uint256)"));
|
uint argsize = (2 + argarraysize) * 32;
|
||||||
address target = _walletLibrary;
|
|
||||||
|
|
||||||
// Compute the size of the call data : arrays has 2
|
assembly {
|
||||||
// 32bytes for offset and length, plus 32bytes per element ;
|
// Add the signature first to memory
|
||||||
// plus 2 32bytes for each uint
|
mstore(0x0, sig)
|
||||||
uint argarraysize = (2 + _owners.length);
|
// Add the call data, which is at the end of the
|
||||||
uint argsize = (2 + argarraysize) * 32;
|
// code
|
||||||
|
codecopy(0x4, sub(codesize, argsize), argsize)
|
||||||
assembly {
|
// Delegate call to the library
|
||||||
// Add the signature first to memory
|
delegatecall(sub(gas, 10000), target, 0x0, add(argsize, 0x4), 0x0, 0x0)
|
||||||
mstore(0x0, sig)
|
|
||||||
// Add the call data, which is at the end of the
|
|
||||||
// code
|
|
||||||
codecopy(0x4, sub(codesize, argsize), argsize)
|
|
||||||
// Delegate call to the library
|
|
||||||
delegatecall(sub(gas, 10000), target, 0x0, add(argsize, 0x4), 0x0, 0x0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
|
|
||||||
// gets called when no other function matches
|
// gets called when no other function matches
|
||||||
function() payable {
|
function() payable {
|
||||||
// just being sent some cash?
|
// just being sent some cash?
|
||||||
if (msg.value > 0)
|
if (msg.value > 0)
|
||||||
Deposit(msg.sender, msg.value);
|
Deposit(msg.sender, msg.value);
|
||||||
else if (msg.data.length > 0)
|
else if (msg.data.length > 0)
|
||||||
_walletLibrary.delegatecall(msg.data);
|
_walletLibrary.delegatecall(msg.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets an owner by 0-indexed position (using numOwners as the count)
|
// Gets an owner by 0-indexed position (using numOwners as the count)
|
||||||
function getOwner(uint ownerIndex) constant returns (address) {
|
function getOwner(uint ownerIndex) constant returns (address) {
|
||||||
return address(m_owners[ownerIndex + 1]);
|
return address(m_owners[ownerIndex + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// As return statement unavailable in fallback, explicit the method here
|
// As return statement unavailable in fallback, explicit the method here
|
||||||
|
|
||||||
function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) {
|
function hasConfirmed(bytes32 _operation, address _owner) external constant returns (bool) {
|
||||||
return _walletLibrary.delegatecall(msg.data);
|
return _walletLibrary.delegatecall(msg.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isOwner(address _addr) returns (bool) {
|
function isOwner(address _addr) constant returns (bool) {
|
||||||
return _walletLibrary.delegatecall(msg.data);
|
return _walletLibrary.delegatecall(msg.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIELDS
|
// FIELDS
|
||||||
address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe;
|
address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe;
|
||||||
|
|
||||||
// the number of owners that must confirm the same operation before it is run.
|
// the number of owners that must confirm the same operation before it is run.
|
||||||
uint public m_required;
|
uint public m_required;
|
||||||
// pointer used to find a free slot in m_owners
|
// pointer used to find a free slot in m_owners
|
||||||
uint public m_numOwners;
|
uint public m_numOwners;
|
||||||
|
|
||||||
uint public m_dailyLimit;
|
uint public m_dailyLimit;
|
||||||
uint public m_spentToday;
|
uint public m_spentToday;
|
||||||
uint public m_lastDay;
|
uint public m_lastDay;
|
||||||
|
|
||||||
// list of owners
|
// list of owners
|
||||||
uint[256] m_owners;
|
uint[256] m_owners;
|
||||||
}
|
}
|
||||||
|
@ -8,221 +8,222 @@
|
|||||||
// use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by
|
// use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by
|
||||||
// some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the
|
// some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the
|
||||||
// interior is executed.
|
// interior is executed.
|
||||||
pragma solidity ^0.4.6;
|
|
||||||
|
pragma solidity ^0.4.9;
|
||||||
|
|
||||||
contract multiowned {
|
contract multiowned {
|
||||||
|
|
||||||
// TYPES
|
// TYPES
|
||||||
|
|
||||||
// struct for the status of a pending operation.
|
// struct for the status of a pending operation.
|
||||||
struct PendingState {
|
struct PendingState {
|
||||||
uint yetNeeded;
|
uint yetNeeded;
|
||||||
uint ownersDone;
|
uint ownersDone;
|
||||||
uint index;
|
uint index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EVENTS
|
||||||
|
|
||||||
|
// this contract only has six types of events: it can accept a confirmation, in which case
|
||||||
|
// we record owner and operation (hash) alongside it.
|
||||||
|
event Confirmation(address owner, bytes32 operation);
|
||||||
|
event Revoke(address owner, bytes32 operation);
|
||||||
|
// some others are in the case of an owner changing.
|
||||||
|
event OwnerChanged(address oldOwner, address newOwner);
|
||||||
|
event OwnerAdded(address newOwner);
|
||||||
|
event OwnerRemoved(address oldOwner);
|
||||||
|
// the last one is emitted if the required signatures change
|
||||||
|
event RequirementChanged(uint newRequirement);
|
||||||
|
|
||||||
|
// MODIFIERS
|
||||||
|
|
||||||
|
// simple single-sig function modifier.
|
||||||
|
modifier onlyowner {
|
||||||
|
if (isOwner(msg.sender))
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
// multi-sig function modifier: the operation must have an intrinsic hash in order
|
||||||
|
// that later attempts can be realised as the same underlying operation and
|
||||||
|
// thus count as confirmations.
|
||||||
|
modifier onlymanyowners(bytes32 _operation) {
|
||||||
|
if (confirmAndCheck(_operation))
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// METHODS
|
||||||
|
|
||||||
|
// constructor is given number of sigs required to do protected "onlymanyowners" transactions
|
||||||
|
// as well as the selection of addresses capable of confirming them.
|
||||||
|
function multiowned(address[] _owners, uint _required) {
|
||||||
|
m_numOwners = _owners.length + 1;
|
||||||
|
m_owners[1] = uint(msg.sender);
|
||||||
|
m_ownerIndex[uint(msg.sender)] = 1;
|
||||||
|
for (uint i = 0; i < _owners.length; ++i)
|
||||||
|
{
|
||||||
|
m_owners[2 + i] = uint(_owners[i]);
|
||||||
|
m_ownerIndex[uint(_owners[i])] = 2 + i;
|
||||||
}
|
}
|
||||||
|
m_required = _required;
|
||||||
|
}
|
||||||
|
|
||||||
// EVENTS
|
// Revokes a prior confirmation of the given operation
|
||||||
|
function revoke(bytes32 _operation) external {
|
||||||
// this contract only has six types of events: it can accept a confirmation, in which case
|
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
||||||
// we record owner and operation (hash) alongside it.
|
// make sure they're an owner
|
||||||
event Confirmation(address owner, bytes32 operation);
|
if (ownerIndex == 0) return;
|
||||||
event Revoke(address owner, bytes32 operation);
|
uint ownerIndexBit = 2**ownerIndex;
|
||||||
// some others are in the case of an owner changing.
|
var pending = m_pending[_operation];
|
||||||
event OwnerChanged(address oldOwner, address newOwner);
|
if (pending.ownersDone & ownerIndexBit > 0) {
|
||||||
event OwnerAdded(address newOwner);
|
pending.yetNeeded++;
|
||||||
event OwnerRemoved(address oldOwner);
|
pending.ownersDone -= ownerIndexBit;
|
||||||
// the last one is emitted if the required signatures change
|
Revoke(msg.sender, _operation);
|
||||||
event RequirementChanged(uint newRequirement);
|
|
||||||
|
|
||||||
// MODIFIERS
|
|
||||||
|
|
||||||
// simple single-sig function modifier.
|
|
||||||
modifier onlyowner {
|
|
||||||
if (isOwner(msg.sender))
|
|
||||||
_;
|
|
||||||
}
|
}
|
||||||
// multi-sig function modifier: the operation must have an intrinsic hash in order
|
}
|
||||||
// that later attempts can be realised as the same underlying operation and
|
|
||||||
// thus count as confirmations.
|
// Replaces an owner `_from` with another `_to`.
|
||||||
modifier onlymanyowners(bytes32 _operation) {
|
function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) external {
|
||||||
if (confirmAndCheck(_operation))
|
if (isOwner(_to)) return;
|
||||||
_;
|
uint ownerIndex = m_ownerIndex[uint(_from)];
|
||||||
|
if (ownerIndex == 0) return;
|
||||||
|
|
||||||
|
clearPending();
|
||||||
|
m_owners[ownerIndex] = uint(_to);
|
||||||
|
m_ownerIndex[uint(_from)] = 0;
|
||||||
|
m_ownerIndex[uint(_to)] = ownerIndex;
|
||||||
|
OwnerChanged(_from, _to);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
|
||||||
|
if (isOwner(_owner)) return;
|
||||||
|
|
||||||
|
clearPending();
|
||||||
|
if (m_numOwners >= c_maxOwners)
|
||||||
|
reorganizeOwners();
|
||||||
|
if (m_numOwners >= c_maxOwners)
|
||||||
|
return;
|
||||||
|
m_numOwners++;
|
||||||
|
m_owners[m_numOwners] = uint(_owner);
|
||||||
|
m_ownerIndex[uint(_owner)] = m_numOwners;
|
||||||
|
OwnerAdded(_owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
|
||||||
|
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
||||||
|
if (ownerIndex == 0) return;
|
||||||
|
if (m_required > m_numOwners - 1) return;
|
||||||
|
|
||||||
|
m_owners[ownerIndex] = 0;
|
||||||
|
m_ownerIndex[uint(_owner)] = 0;
|
||||||
|
clearPending();
|
||||||
|
reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot
|
||||||
|
OwnerRemoved(_owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external {
|
||||||
|
if (_newRequired > m_numOwners) return;
|
||||||
|
m_required = _newRequired;
|
||||||
|
clearPending();
|
||||||
|
RequirementChanged(_newRequired);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets an owner by 0-indexed position (using numOwners as the count)
|
||||||
|
function getOwner(uint ownerIndex) external constant returns (address) {
|
||||||
|
return address(m_owners[ownerIndex + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isOwner(address _addr) constant returns (bool) {
|
||||||
|
return m_ownerIndex[uint(_addr)] > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) {
|
||||||
|
var pending = m_pending[_operation];
|
||||||
|
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
||||||
|
|
||||||
|
// make sure they're an owner
|
||||||
|
if (ownerIndex == 0) return false;
|
||||||
|
|
||||||
|
// determine the bit to set for this owner.
|
||||||
|
uint ownerIndexBit = 2**ownerIndex;
|
||||||
|
return !(pending.ownersDone & ownerIndexBit == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// INTERNAL METHODS
|
||||||
|
|
||||||
|
function confirmAndCheck(bytes32 _operation) internal returns (bool) {
|
||||||
|
// determine what index the present sender is:
|
||||||
|
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
||||||
|
// make sure they're an owner
|
||||||
|
if (ownerIndex == 0) return;
|
||||||
|
|
||||||
|
var pending = m_pending[_operation];
|
||||||
|
// if we're not yet working on this operation, switch over and reset the confirmation status.
|
||||||
|
if (pending.yetNeeded == 0) {
|
||||||
|
// reset count of confirmations needed.
|
||||||
|
pending.yetNeeded = m_required;
|
||||||
|
// reset which owners have confirmed (none) - set our bitmap to 0.
|
||||||
|
pending.ownersDone = 0;
|
||||||
|
pending.index = m_pendingIndex.length++;
|
||||||
|
m_pendingIndex[pending.index] = _operation;
|
||||||
}
|
}
|
||||||
|
// determine the bit to set for this owner.
|
||||||
// METHODS
|
uint ownerIndexBit = 2**ownerIndex;
|
||||||
|
// make sure we (the message sender) haven't confirmed this operation previously.
|
||||||
// constructor is given number of sigs required to do protected "onlymanyowners" transactions
|
if (pending.ownersDone & ownerIndexBit == 0) {
|
||||||
// as well as the selection of addresses capable of confirming them.
|
Confirmation(msg.sender, _operation);
|
||||||
function multiowned(address[] _owners, uint _required) {
|
// ok - check if count is enough to go ahead.
|
||||||
m_numOwners = _owners.length + 1;
|
if (pending.yetNeeded <= 1) {
|
||||||
m_owners[1] = uint(msg.sender);
|
// enough confirmations: reset and run interior.
|
||||||
m_ownerIndex[uint(msg.sender)] = 1;
|
delete m_pendingIndex[m_pending[_operation].index];
|
||||||
for (uint i = 0; i < _owners.length; ++i)
|
delete m_pending[_operation];
|
||||||
{
|
return true;
|
||||||
m_owners[2 + i] = uint(_owners[i]);
|
}
|
||||||
m_ownerIndex[uint(_owners[i])] = 2 + i;
|
else
|
||||||
}
|
{
|
||||||
m_required = _required;
|
// not enough: record that this owner in particular confirmed.
|
||||||
|
pending.yetNeeded--;
|
||||||
|
pending.ownersDone |= ownerIndexBit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Revokes a prior confirmation of the given operation
|
function reorganizeOwners() private {
|
||||||
function revoke(bytes32 _operation) external {
|
uint free = 1;
|
||||||
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
while (free < m_numOwners)
|
||||||
// make sure they're an owner
|
{
|
||||||
if (ownerIndex == 0) return;
|
while (free < m_numOwners && m_owners[free] != 0) free++;
|
||||||
uint ownerIndexBit = 2**ownerIndex;
|
while (m_numOwners > 1 && m_owners[m_numOwners] == 0) m_numOwners--;
|
||||||
var pending = m_pending[_operation];
|
if (free < m_numOwners && m_owners[m_numOwners] != 0 && m_owners[free] == 0)
|
||||||
if (pending.ownersDone & ownerIndexBit > 0) {
|
{
|
||||||
pending.yetNeeded++;
|
m_owners[free] = m_owners[m_numOwners];
|
||||||
pending.ownersDone -= ownerIndexBit;
|
m_ownerIndex[m_owners[free]] = free;
|
||||||
Revoke(msg.sender, _operation);
|
m_owners[m_numOwners] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Replaces an owner `_from` with another `_to`.
|
function clearPending() internal {
|
||||||
function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) external {
|
uint length = m_pendingIndex.length;
|
||||||
if (isOwner(_to)) return;
|
for (uint i = 0; i < length; ++i)
|
||||||
uint ownerIndex = m_ownerIndex[uint(_from)];
|
if (m_pendingIndex[i] != 0)
|
||||||
if (ownerIndex == 0) return;
|
delete m_pending[m_pendingIndex[i]];
|
||||||
|
delete m_pendingIndex;
|
||||||
|
}
|
||||||
|
|
||||||
clearPending();
|
// FIELDS
|
||||||
m_owners[ownerIndex] = uint(_to);
|
|
||||||
m_ownerIndex[uint(_from)] = 0;
|
|
||||||
m_ownerIndex[uint(_to)] = ownerIndex;
|
|
||||||
OwnerChanged(_from, _to);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
|
// the number of owners that must confirm the same operation before it is run.
|
||||||
if (isOwner(_owner)) return;
|
uint public m_required;
|
||||||
|
// pointer used to find a free slot in m_owners
|
||||||
|
uint public m_numOwners;
|
||||||
|
|
||||||
clearPending();
|
// list of owners
|
||||||
if (m_numOwners >= c_maxOwners)
|
uint[256] m_owners;
|
||||||
reorganizeOwners();
|
uint constant c_maxOwners = 250;
|
||||||
if (m_numOwners >= c_maxOwners)
|
// index on the list of owners to allow reverse lookup
|
||||||
return;
|
mapping(uint => uint) m_ownerIndex;
|
||||||
m_numOwners++;
|
// the ongoing operations.
|
||||||
m_owners[m_numOwners] = uint(_owner);
|
mapping(bytes32 => PendingState) m_pending;
|
||||||
m_ownerIndex[uint(_owner)] = m_numOwners;
|
bytes32[] m_pendingIndex;
|
||||||
OwnerAdded(_owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
|
|
||||||
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
|
||||||
if (ownerIndex == 0) return;
|
|
||||||
if (m_required > m_numOwners - 1) return;
|
|
||||||
|
|
||||||
m_owners[ownerIndex] = 0;
|
|
||||||
m_ownerIndex[uint(_owner)] = 0;
|
|
||||||
clearPending();
|
|
||||||
reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot
|
|
||||||
OwnerRemoved(_owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external {
|
|
||||||
if (_newRequired > m_numOwners) return;
|
|
||||||
m_required = _newRequired;
|
|
||||||
clearPending();
|
|
||||||
RequirementChanged(_newRequired);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets an owner by 0-indexed position (using numOwners as the count)
|
|
||||||
function getOwner(uint ownerIndex) external constant returns (address) {
|
|
||||||
return address(m_owners[ownerIndex + 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isOwner(address _addr) returns (bool) {
|
|
||||||
return m_ownerIndex[uint(_addr)] > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) {
|
|
||||||
var pending = m_pending[_operation];
|
|
||||||
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
|
||||||
|
|
||||||
// make sure they're an owner
|
|
||||||
if (ownerIndex == 0) return false;
|
|
||||||
|
|
||||||
// determine the bit to set for this owner.
|
|
||||||
uint ownerIndexBit = 2**ownerIndex;
|
|
||||||
return !(pending.ownersDone & ownerIndexBit == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// INTERNAL METHODS
|
|
||||||
|
|
||||||
function confirmAndCheck(bytes32 _operation) internal returns (bool) {
|
|
||||||
// determine what index the present sender is:
|
|
||||||
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
|
||||||
// make sure they're an owner
|
|
||||||
if (ownerIndex == 0) return;
|
|
||||||
|
|
||||||
var pending = m_pending[_operation];
|
|
||||||
// if we're not yet working on this operation, switch over and reset the confirmation status.
|
|
||||||
if (pending.yetNeeded == 0) {
|
|
||||||
// reset count of confirmations needed.
|
|
||||||
pending.yetNeeded = m_required;
|
|
||||||
// reset which owners have confirmed (none) - set our bitmap to 0.
|
|
||||||
pending.ownersDone = 0;
|
|
||||||
pending.index = m_pendingIndex.length++;
|
|
||||||
m_pendingIndex[pending.index] = _operation;
|
|
||||||
}
|
|
||||||
// determine the bit to set for this owner.
|
|
||||||
uint ownerIndexBit = 2**ownerIndex;
|
|
||||||
// make sure we (the message sender) haven't confirmed this operation previously.
|
|
||||||
if (pending.ownersDone & ownerIndexBit == 0) {
|
|
||||||
Confirmation(msg.sender, _operation);
|
|
||||||
// ok - check if count is enough to go ahead.
|
|
||||||
if (pending.yetNeeded <= 1) {
|
|
||||||
// enough confirmations: reset and run interior.
|
|
||||||
delete m_pendingIndex[m_pending[_operation].index];
|
|
||||||
delete m_pending[_operation];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// not enough: record that this owner in particular confirmed.
|
|
||||||
pending.yetNeeded--;
|
|
||||||
pending.ownersDone |= ownerIndexBit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function reorganizeOwners() private {
|
|
||||||
uint free = 1;
|
|
||||||
while (free < m_numOwners)
|
|
||||||
{
|
|
||||||
while (free < m_numOwners && m_owners[free] != 0) free++;
|
|
||||||
while (m_numOwners > 1 && m_owners[m_numOwners] == 0) m_numOwners--;
|
|
||||||
if (free < m_numOwners && m_owners[m_numOwners] != 0 && m_owners[free] == 0)
|
|
||||||
{
|
|
||||||
m_owners[free] = m_owners[m_numOwners];
|
|
||||||
m_ownerIndex[m_owners[free]] = free;
|
|
||||||
m_owners[m_numOwners] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearPending() internal {
|
|
||||||
uint length = m_pendingIndex.length;
|
|
||||||
for (uint i = 0; i < length; ++i)
|
|
||||||
if (m_pendingIndex[i] != 0)
|
|
||||||
delete m_pending[m_pendingIndex[i]];
|
|
||||||
delete m_pendingIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIELDS
|
|
||||||
|
|
||||||
// the number of owners that must confirm the same operation before it is run.
|
|
||||||
uint public m_required;
|
|
||||||
// pointer used to find a free slot in m_owners
|
|
||||||
uint public m_numOwners;
|
|
||||||
|
|
||||||
// list of owners
|
|
||||||
uint[256] m_owners;
|
|
||||||
uint constant c_maxOwners = 250;
|
|
||||||
// index on the list of owners to allow reverse lookup
|
|
||||||
mapping(uint => uint) m_ownerIndex;
|
|
||||||
// the ongoing operations.
|
|
||||||
mapping(bytes32 => PendingState) m_pending;
|
|
||||||
bytes32[] m_pendingIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// inheritable "property" contract that enables methods to be protected by placing a linear limit (specifiable)
|
// inheritable "property" contract that enables methods to be protected by placing a linear limit (specifiable)
|
||||||
@ -230,79 +231,70 @@ contract multiowned {
|
|||||||
// uses is specified in the modifier.
|
// uses is specified in the modifier.
|
||||||
contract daylimit is multiowned {
|
contract daylimit is multiowned {
|
||||||
|
|
||||||
// MODIFIERS
|
// METHODS
|
||||||
|
|
||||||
// simple modifier for daily limit.
|
// constructor - stores initial daily limit and records the present day's index.
|
||||||
modifier limitedDaily(uint _value) {
|
function daylimit(uint _limit) {
|
||||||
if (underLimit(_value))
|
m_dailyLimit = _limit;
|
||||||
_;
|
m_lastDay = today();
|
||||||
|
}
|
||||||
|
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
|
||||||
|
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external {
|
||||||
|
m_dailyLimit = _newLimit;
|
||||||
|
}
|
||||||
|
// resets the amount already spent today. needs many of the owners to confirm.
|
||||||
|
function resetSpentToday() onlymanyowners(sha3(msg.data)) external {
|
||||||
|
m_spentToday = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// INTERNAL METHODS
|
||||||
|
|
||||||
|
// checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
|
||||||
|
// returns true. otherwise just returns false.
|
||||||
|
function underLimit(uint _value) internal onlyowner returns (bool) {
|
||||||
|
// reset the spend limit if we're on a different day to last time.
|
||||||
|
if (today() > m_lastDay) {
|
||||||
|
m_spentToday = 0;
|
||||||
|
m_lastDay = today();
|
||||||
}
|
}
|
||||||
|
// check to see if there's enough left - if so, subtract and return true.
|
||||||
// METHODS
|
// overflow protection // dailyLimit check
|
||||||
|
if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) {
|
||||||
// constructor - stores initial daily limit and records the present day's index.
|
m_spentToday += _value;
|
||||||
function daylimit(uint _limit) {
|
return true;
|
||||||
m_dailyLimit = _limit;
|
|
||||||
m_lastDay = today();
|
|
||||||
}
|
|
||||||
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
|
|
||||||
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external {
|
|
||||||
m_dailyLimit = _newLimit;
|
|
||||||
}
|
|
||||||
// resets the amount already spent today. needs many of the owners to confirm.
|
|
||||||
function resetSpentToday() onlymanyowners(sha3(msg.data)) external {
|
|
||||||
m_spentToday = 0;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// determines today's index.
|
||||||
|
function today() private constant returns (uint) { return now / 1 days; }
|
||||||
|
|
||||||
// INTERNAL METHODS
|
// FIELDS
|
||||||
|
|
||||||
// checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
|
uint public m_dailyLimit;
|
||||||
// returns true. otherwise just returns false.
|
uint public m_spentToday;
|
||||||
function underLimit(uint _value) internal onlyowner returns (bool) {
|
uint public m_lastDay;
|
||||||
// reset the spend limit if we're on a different day to last time.
|
|
||||||
if (today() > m_lastDay) {
|
|
||||||
m_spentToday = 0;
|
|
||||||
m_lastDay = today();
|
|
||||||
}
|
|
||||||
// check to see if there's enough left - if so, subtract and return true.
|
|
||||||
// overflow protection // dailyLimit check
|
|
||||||
if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) {
|
|
||||||
m_spentToday += _value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// determines today's index.
|
|
||||||
function today() private constant returns (uint) { return now / 1 days; }
|
|
||||||
|
|
||||||
// FIELDS
|
|
||||||
|
|
||||||
uint public m_dailyLimit;
|
|
||||||
uint public m_spentToday;
|
|
||||||
uint public m_lastDay;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// interface contract for multisig proxy contracts; see below for docs.
|
// interface contract for multisig proxy contracts; see below for docs.
|
||||||
contract multisig {
|
contract multisig {
|
||||||
|
|
||||||
// EVENTS
|
// EVENTS
|
||||||
|
|
||||||
// logged events:
|
// logged events:
|
||||||
// Funds has arrived into the wallet (record how much).
|
// Funds has arrived into the wallet (record how much).
|
||||||
event Deposit(address _from, uint value);
|
event Deposit(address _from, uint value);
|
||||||
// Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
|
// Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
|
||||||
event SingleTransact(address owner, uint value, address to, bytes data);
|
event SingleTransact(address owner, uint value, address to, bytes data, address created);
|
||||||
// Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
|
// Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
|
||||||
event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data);
|
event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data, address created);
|
||||||
// Confirmation still needed for a transaction.
|
// Confirmation still needed for a transaction.
|
||||||
event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
|
event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
|
||||||
|
|
||||||
// FUNCTIONS
|
// FUNCTIONS
|
||||||
|
|
||||||
// TODO: document
|
// TODO: document
|
||||||
function changeOwner(address _from, address _to) external;
|
function execute(address _to, uint _value, bytes _data) external returns (bytes32 o_hash);
|
||||||
function execute(address _to, uint _value, bytes _data) external returns (bytes32);
|
function confirm(bytes32 _h) external returns (bool o_success);
|
||||||
function confirm(bytes32 _h) returns (bool);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// usage:
|
// usage:
|
||||||
@ -310,79 +302,102 @@ contract multisig {
|
|||||||
// Wallet(w).from(anotherOwner).confirm(h);
|
// Wallet(w).from(anotherOwner).confirm(h);
|
||||||
contract Wallet is multisig, multiowned, daylimit {
|
contract Wallet is multisig, multiowned, daylimit {
|
||||||
|
|
||||||
// TYPES
|
// TYPES
|
||||||
|
|
||||||
// Transaction structure to remember details of transaction lest it need be saved for a later call.
|
// Transaction structure to remember details of transaction lest it need be saved for a later call.
|
||||||
struct Transaction {
|
struct Transaction {
|
||||||
address to;
|
address to;
|
||||||
uint value;
|
uint value;
|
||||||
bytes data;
|
bytes data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// METHODS
|
||||||
|
|
||||||
|
// constructor - just pass on the owner array to the multiowned and
|
||||||
|
// the limit to daylimit
|
||||||
|
function Wallet(address[] _owners, uint _required, uint _daylimit)
|
||||||
|
multiowned(_owners, _required) daylimit(_daylimit) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// kills the contract sending everything to `_to`.
|
||||||
|
function kill(address _to) onlymanyowners(sha3(msg.data)) external {
|
||||||
|
suicide(_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets called when no other function matches
|
||||||
|
function() payable {
|
||||||
|
// just being sent some cash?
|
||||||
|
if (msg.value > 0)
|
||||||
|
Deposit(msg.sender, msg.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Outside-visible transact entry point. Executes transaction immediately if below daily spend limit.
|
||||||
|
// If not, goes into multisig process. We provide a hash on return to allow the sender to provide
|
||||||
|
// shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
|
||||||
|
// and _data arguments). They still get the option of using them if they want, anyways.
|
||||||
|
function execute(address _to, uint _value, bytes _data) external onlyowner returns (bytes32 o_hash) {
|
||||||
|
// first, take the opportunity to check that we're under the daily limit.
|
||||||
|
if ((_data.length == 0 && underLimit(_value)) || m_required == 1) {
|
||||||
|
// yes - just execute the call.
|
||||||
|
address created;
|
||||||
|
if (_to == 0) {
|
||||||
|
created = create(_value, _data);
|
||||||
|
} else {
|
||||||
|
if (!_to.call.value(_value)(_data))
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
SingleTransact(msg.sender, _value, _to, _data, created);
|
||||||
|
} else {
|
||||||
|
// determine our operation hash.
|
||||||
|
o_hash = sha3(msg.data, block.number);
|
||||||
|
// store if it's new
|
||||||
|
if (m_txs[o_hash].to == 0 && m_txs[o_hash].value == 0 && m_txs[o_hash].data.length == 0) {
|
||||||
|
m_txs[o_hash].to = _to;
|
||||||
|
m_txs[o_hash].value = _value;
|
||||||
|
m_txs[o_hash].data = _data;
|
||||||
|
}
|
||||||
|
if (!confirm(o_hash)) {
|
||||||
|
ConfirmationNeeded(o_hash, msg.sender, _value, _to, _data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// METHODS
|
function create(uint _value, bytes _code) internal returns (address o_addr) {
|
||||||
|
assembly {
|
||||||
// constructor - just pass on the owner array to the multiowned and
|
o_addr := create(_value, add(_code, 0x20), mload(_code))
|
||||||
// the limit to daylimit
|
jumpi(invalidJumpLabel, iszero(extcodesize(o_addr)))
|
||||||
function Wallet(address[] _owners, uint _required, uint _daylimit)
|
|
||||||
multiowned(_owners, _required) daylimit(_daylimit) {
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// kills the contract sending everything to `_to`.
|
// confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order
|
||||||
function kill(address _to) onlymanyowners(sha3(msg.data)) external {
|
// to determine the body of the transaction from the hash provided.
|
||||||
suicide(_to);
|
function confirm(bytes32 _h) onlymanyowners(_h) returns (bool o_success) {
|
||||||
|
if (m_txs[_h].to != 0 || m_txs[_h].value != 0 || m_txs[_h].data.length != 0) {
|
||||||
|
address created;
|
||||||
|
if (m_txs[_h].to == 0) {
|
||||||
|
created = create(m_txs[_h].value, m_txs[_h].data);
|
||||||
|
} else {
|
||||||
|
if (!m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data))
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data, created);
|
||||||
|
delete m_txs[_h];
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// gets called when no other function matches
|
// INTERNAL METHODS
|
||||||
function() payable {
|
|
||||||
// just being sent some cash?
|
|
||||||
if (msg.value > 0)
|
|
||||||
Deposit(msg.sender, msg.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Outside-visible transact entry point. Executes transaction immediately if below daily spend limit.
|
function clearPending() internal {
|
||||||
// If not, goes into multisig process. We provide a hash on return to allow the sender to provide
|
uint length = m_pendingIndex.length;
|
||||||
// shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
|
for (uint i = 0; i < length; ++i)
|
||||||
// and _data arguments). They still get the option of using them if they want, anyways.
|
delete m_txs[m_pendingIndex[i]];
|
||||||
function execute(address _to, uint _value, bytes _data) external onlyowner returns (bytes32 _r) {
|
super.clearPending();
|
||||||
// first, take the opportunity to check that we're under the daily limit.
|
}
|
||||||
if (underLimit(_value)) {
|
|
||||||
SingleTransact(msg.sender, _value, _to, _data);
|
|
||||||
// yes - just execute the call.
|
|
||||||
_to.call.value(_value)(_data);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// determine our operation hash.
|
|
||||||
_r = sha3(msg.data, block.number);
|
|
||||||
if (!confirm(_r) && m_txs[_r].to == 0) {
|
|
||||||
m_txs[_r].to = _to;
|
|
||||||
m_txs[_r].value = _value;
|
|
||||||
m_txs[_r].data = _data;
|
|
||||||
ConfirmationNeeded(_r, msg.sender, _value, _to, _data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order
|
// FIELDS
|
||||||
// to determine the body of the transaction from the hash provided.
|
|
||||||
function confirm(bytes32 _h) onlymanyowners(_h) returns (bool) {
|
|
||||||
if (m_txs[_h].to != 0) {
|
|
||||||
m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data);
|
|
||||||
MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data);
|
|
||||||
delete m_txs[_h];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// INTERNAL METHODS
|
// pending transactions we have at present.
|
||||||
|
mapping (bytes32 => Transaction) m_txs;
|
||||||
function clearPending() internal {
|
|
||||||
uint length = m_pendingIndex.length;
|
|
||||||
for (uint i = 0; i < length; ++i)
|
|
||||||
delete m_txs[m_pendingIndex[i]];
|
|
||||||
super.clearPending();
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIELDS
|
|
||||||
|
|
||||||
// pending transactions we have at present.
|
|
||||||
mapping (bytes32 => Transaction) m_txs;
|
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,13 @@ const renderEvent = (classNames, verb) => (e) => {
|
|||||||
return (
|
return (
|
||||||
<tr key={ e.key } className={ classes }>
|
<tr key={ e.key } className={ classes }>
|
||||||
<td>
|
<td>
|
||||||
<Address address={ e.parameters.owner.value } />
|
<Address
|
||||||
|
address={
|
||||||
|
e.parameters.owner
|
||||||
|
? e.parameters.owner.value
|
||||||
|
: e.from
|
||||||
|
}
|
||||||
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<abbr title={ e.transaction }>{ verb }</abbr>
|
<abbr title={ e.transaction }>{ verb }</abbr>
|
||||||
@ -80,17 +86,23 @@ const renderDataChanged = (e) => {
|
|||||||
return (
|
return (
|
||||||
<tr key={ e.key } className={ classNames }>
|
<tr key={ e.key } className={ classNames }>
|
||||||
<td>
|
<td>
|
||||||
<Address address={ e.parameters.owner.value } />
|
<Address
|
||||||
|
address={
|
||||||
|
e.parameters.owner
|
||||||
|
? e.parameters.owner.value
|
||||||
|
: e.from
|
||||||
|
}
|
||||||
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<abbr title={ e.transaction }>updated</abbr>
|
<abbr title={ e.transaction }>updated</abbr>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{ 'key ' }
|
key
|
||||||
<code>
|
<code>
|
||||||
{ new Buffer(e.parameters.plainKey.value).toString('utf8') }
|
{ new Buffer(e.parameters.plainKey.value).toString('utf8') }
|
||||||
</code>
|
</code>
|
||||||
{ 'of ' }
|
of
|
||||||
<code>
|
<code>
|
||||||
<Hash hash={ bytesToHex(e.parameters.name.value) } />
|
<Hash hash={ bytesToHex(e.parameters.name.value) } />
|
||||||
</code>
|
</code>
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import { registry as registryAbi } from '~/contracts/abi';
|
import { registry as registryAbi, registry2 as registryAbi2 } from '~/contracts/abi';
|
||||||
|
|
||||||
import { api } from './parity.js';
|
import { api } from './parity.js';
|
||||||
import * as addresses from './addresses/actions.js';
|
import * as addresses from './addresses/actions.js';
|
||||||
@ -27,15 +27,17 @@ import * as reverse from './Reverse/actions.js';
|
|||||||
|
|
||||||
export { addresses, accounts, lookup, events, names, records, reverse };
|
export { addresses, accounts, lookup, events, names, records, reverse };
|
||||||
|
|
||||||
export const setIsTestnet = (isTestnet) => ({ type: 'set isTestnet', isTestnet });
|
const REGISTRY_V1_HASHES = [
|
||||||
|
'0x34f7c51bbb1b1902fbdabfdf04811100f5c9f998f26dd535d2f6f977492c748e', // ropsten
|
||||||
|
'0x64c3ee34851517a9faecd995c102b339f03e564ad6772dc43a26f993238b20ec' // homestead
|
||||||
|
];
|
||||||
|
|
||||||
|
export const setNetVersion = (netVersion) => ({ type: 'set netVersion', netVersion });
|
||||||
|
|
||||||
export const fetchIsTestnet = () => (dispatch) =>
|
export const fetchIsTestnet = () => (dispatch) =>
|
||||||
api.net.version()
|
api.net.version()
|
||||||
.then((netVersion) => {
|
.then((netVersion) => {
|
||||||
dispatch(setIsTestnet(
|
dispatch(setNetVersion(netVersion));
|
||||||
netVersion === '2' || // morden
|
|
||||||
netVersion === '3' // ropsten
|
|
||||||
));
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error('could not check if testnet');
|
console.error('could not check if testnet');
|
||||||
@ -47,13 +49,28 @@ export const fetchIsTestnet = () => (dispatch) =>
|
|||||||
export const setContract = (contract) => ({ type: 'set contract', contract });
|
export const setContract = (contract) => ({ type: 'set contract', contract });
|
||||||
|
|
||||||
export const fetchContract = () => (dispatch) =>
|
export const fetchContract = () => (dispatch) =>
|
||||||
api.parity.registryAddress()
|
api.parity
|
||||||
|
.registryAddress()
|
||||||
.then((address) => {
|
.then((address) => {
|
||||||
const contract = api.newContract(registryAbi, address);
|
return api.eth
|
||||||
|
.getCode(address)
|
||||||
|
.then((code) => {
|
||||||
|
const codeHash = api.util.sha3(code);
|
||||||
|
const isVersion1 = REGISTRY_V1_HASHES.includes(codeHash);
|
||||||
|
|
||||||
dispatch(setContract(contract));
|
console.log(`registry at ${address}, code ${codeHash}, version ${isVersion1 ? 1 : 2}`);
|
||||||
dispatch(fetchFee());
|
|
||||||
dispatch(fetchOwner());
|
const contract = api.newContract(
|
||||||
|
isVersion1
|
||||||
|
? registryAbi
|
||||||
|
: registryAbi2,
|
||||||
|
address
|
||||||
|
);
|
||||||
|
|
||||||
|
dispatch(setContract(contract));
|
||||||
|
dispatch(fetchFee());
|
||||||
|
dispatch(fetchOwner());
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error('could not fetch contract');
|
console.error('could not fetch contract');
|
||||||
|
@ -22,8 +22,8 @@ import namesReducer from './Names/reducers.js';
|
|||||||
import recordsReducer from './Records/reducers.js';
|
import recordsReducer from './Records/reducers.js';
|
||||||
import reverseReducer from './Reverse/reducers.js';
|
import reverseReducer from './Reverse/reducers.js';
|
||||||
|
|
||||||
const isTestnetReducer = (state = null, action) =>
|
const netVersionReducer = (state = null, action) =>
|
||||||
action.type === 'set isTestnet' ? action.isTestnet : state;
|
action.type === 'set netVersion' ? action.netVersion : state;
|
||||||
|
|
||||||
const contractReducer = (state = null, action) =>
|
const contractReducer = (state = null, action) =>
|
||||||
action.type === 'set contract' ? action.contract : state;
|
action.type === 'set contract' ? action.contract : state;
|
||||||
@ -35,7 +35,7 @@ const ownerReducer = (state = null, action) =>
|
|||||||
action.type === 'set owner' ? action.owner : state;
|
action.type === 'set owner' ? action.owner : state;
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
isTestnet: isTestnetReducer(undefined, { type: '' }),
|
netVersion: netVersionReducer(undefined, { type: '' }),
|
||||||
accounts: accountsReducer(undefined, { type: '' }),
|
accounts: accountsReducer(undefined, { type: '' }),
|
||||||
contacts: contactsReducer(undefined, { type: '' }),
|
contacts: contactsReducer(undefined, { type: '' }),
|
||||||
contract: contractReducer(undefined, { type: '' }),
|
contract: contractReducer(undefined, { type: '' }),
|
||||||
@ -49,7 +49,7 @@ const initialState = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default (state = initialState, action) => ({
|
export default (state = initialState, action) => ({
|
||||||
isTestnet: isTestnetReducer(state.isTestnet, action),
|
netVersion: netVersionReducer(state.netVersion, action),
|
||||||
accounts: accountsReducer(state.accounts, action),
|
accounts: accountsReducer(state.accounts, action),
|
||||||
contacts: contactsReducer(state.contacts, action),
|
contacts: contactsReducer(state.contacts, action),
|
||||||
contract: contractReducer(state.contract, action),
|
contract: contractReducer(state.contract, action),
|
||||||
|
@ -28,7 +28,7 @@ class Address extends Component {
|
|||||||
static propTypes = {
|
static propTypes = {
|
||||||
address: PropTypes.string.isRequired,
|
address: PropTypes.string.isRequired,
|
||||||
account: nullableProptype(PropTypes.object.isRequired),
|
account: nullableProptype(PropTypes.object.isRequired),
|
||||||
isTestnet: PropTypes.bool.isRequired,
|
netVersion: PropTypes.string.isRequired,
|
||||||
key: PropTypes.string,
|
key: PropTypes.string,
|
||||||
shortenHash: PropTypes.bool
|
shortenHash: PropTypes.bool
|
||||||
};
|
};
|
||||||
@ -56,7 +56,7 @@ class Address extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderCaption () {
|
renderCaption () {
|
||||||
const { address, account, isTestnet, shortenHash } = this.props;
|
const { address, account, netVersion, shortenHash } = this.props;
|
||||||
|
|
||||||
if (account) {
|
if (account) {
|
||||||
const { name } = account;
|
const { name } = account;
|
||||||
@ -64,7 +64,7 @@ class Address extends Component {
|
|||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
className={ styles.link }
|
className={ styles.link }
|
||||||
href={ etherscanUrl(address, isTestnet) }
|
href={ etherscanUrl(address, false, netVersion) }
|
||||||
target='_blank'
|
target='_blank'
|
||||||
>
|
>
|
||||||
<abbr
|
<abbr
|
||||||
@ -103,14 +103,14 @@ function mapStateToProps (initState, initProps) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (state, props) => {
|
return (state, props) => {
|
||||||
const { isTestnet } = state;
|
const { netVersion } = state;
|
||||||
const { address = '' } = props;
|
const { address = '' } = props;
|
||||||
|
|
||||||
const account = allAccounts[address] || null;
|
const account = allAccounts[address] || null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
account,
|
account,
|
||||||
isTestnet
|
netVersion
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ const leading0x = /^0x/;
|
|||||||
class Hash extends Component {
|
class Hash extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
hash: PropTypes.string.isRequired,
|
hash: PropTypes.string.isRequired,
|
||||||
isTestnet: PropTypes.bool.isRequired,
|
netVersion: PropTypes.string.isRequired,
|
||||||
linked: PropTypes.bool
|
linked: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ class Hash extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { hash, isTestnet, linked } = this.props;
|
const { hash, netVersion, linked } = this.props;
|
||||||
|
|
||||||
let shortened = hash.toLowerCase().replace(leading0x, '');
|
let shortened = hash.toLowerCase().replace(leading0x, '');
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ class Hash extends Component {
|
|||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
className={ styles.link }
|
className={ styles.link }
|
||||||
href={ etherscanUrl(hash, isTestnet) }
|
href={ etherscanUrl(hash, false, netVersion) }
|
||||||
target='_blank'
|
target='_blank'
|
||||||
>
|
>
|
||||||
<abbr title={ hash }>{ shortened }</abbr>
|
<abbr title={ hash }>{ shortened }</abbr>
|
||||||
@ -61,7 +61,7 @@ class Hash extends Component {
|
|||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
(state) => ({ // mapStateToProps
|
(state) => ({ // mapStateToProps
|
||||||
isTestnet: state.isTestnet
|
netVersion: state.netVersion
|
||||||
}),
|
}),
|
||||||
null // mapDispatchToProps
|
null // mapDispatchToProps
|
||||||
)(Hash);
|
)(Hash);
|
||||||
|
@ -14,13 +14,15 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { url as externalUrl } from '~/3rdparty/etherscan/links';
|
||||||
|
|
||||||
const leading0x = /^0x/;
|
const leading0x = /^0x/;
|
||||||
|
|
||||||
const etherscanUrl = (hash, isTestnet) => {
|
const etherscanUrl = (hash, isTestnet, netVersion) => {
|
||||||
hash = hash.toLowerCase().replace(leading0x, '');
|
hash = hash.toLowerCase().replace(leading0x, '');
|
||||||
const type = hash.length === 40 ? 'address' : 'tx';
|
const type = hash.length === 40 ? 'address' : 'tx';
|
||||||
|
|
||||||
return `https://${isTestnet ? 'testnet.' : ''}etherscan.io/${type}/0x${hash}`;
|
return `https://${externalUrl(isTestnet, netVersion)}/${type}/0x${hash}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default etherscanUrl;
|
export default etherscanUrl;
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
|
|
||||||
|
import { url as etherscanUrl } from '~/3rdparty/etherscan/links';
|
||||||
import * as abis from '~/contracts/abi';
|
import * as abis from '~/contracts/abi';
|
||||||
import { api } from './parity';
|
import { api } from './parity';
|
||||||
|
|
||||||
@ -28,7 +29,7 @@ const subscriptions = {};
|
|||||||
|
|
||||||
let defaultSubscriptionId;
|
let defaultSubscriptionId;
|
||||||
let nextSubscriptionId = 1000;
|
let nextSubscriptionId = 1000;
|
||||||
let isTest = false;
|
let netVersion = '0';
|
||||||
|
|
||||||
export function subscribeEvents (addresses, callback) {
|
export function subscribeEvents (addresses, callback) {
|
||||||
const subscriptionId = nextSubscriptionId++;
|
const subscriptionId = nextSubscriptionId++;
|
||||||
@ -117,15 +118,16 @@ export function attachInstances () {
|
|||||||
return Promise
|
return Promise
|
||||||
.all([
|
.all([
|
||||||
api.parity.registryAddress(),
|
api.parity.registryAddress(),
|
||||||
api.parity.netChain()
|
api.parity.netChain(),
|
||||||
|
api.partiy.netVersion()
|
||||||
])
|
])
|
||||||
.then(([registryAddress, netChain]) => {
|
.then(([registryAddress, netChain, _netVersion]) => {
|
||||||
const registry = api.newContract(abis.registry, registryAddress).instance;
|
const registry = api.newContract(abis.registry, registryAddress).instance;
|
||||||
|
|
||||||
isTest = ['morden', 'ropsten', 'testnet'].includes(netChain);
|
netVersion = _netVersion;
|
||||||
|
|
||||||
console.log(`contract was found at registry=${registryAddress}`);
|
console.log(`contract was found at registry=${registryAddress}`);
|
||||||
console.log(`running on ${netChain}, isTest=${isTest}`);
|
console.log(`running on ${netChain}, network ${netVersion}`);
|
||||||
|
|
||||||
return Promise
|
return Promise
|
||||||
.all([
|
.all([
|
||||||
@ -287,5 +289,5 @@ export function loadTokenBalance (tokenAddress, address) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function txLink (txHash) {
|
export function txLink (txHash) {
|
||||||
return `https://${isTest ? 'testnet.' : ''}etherscan.io/tx/${txHash}`;
|
return `https://${etherscanUrl(false, netVersion)}/tx/${txHash}`;
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ import React, { Component, PropTypes } from 'react';
|
|||||||
|
|
||||||
import { Chip } from 'material-ui';
|
import { Chip } from 'material-ui';
|
||||||
|
|
||||||
import IdentityIcon from '../IdentityIcon' ;
|
import IdentityIcon from '../IdentityIcon';
|
||||||
|
|
||||||
import styles from './chip.css';
|
import styles from './chip.css';
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ export default {
|
|||||||
connectingAPI: `Connecting to the Parity Secure API.`,
|
connectingAPI: `Connecting to the Parity Secure API.`,
|
||||||
connectingNode: `Connecting to the Parity Node. If this informational message persists, please ensure that your Parity node is running and reachable on the network.`,
|
connectingNode: `Connecting to the Parity Node. If this informational message persists, please ensure that your Parity node is running and reachable on the network.`,
|
||||||
invalidToken: `invalid signer token`,
|
invalidToken: `invalid signer token`,
|
||||||
noConnection: `Unable to make a connection to the Parity Secure API. To update your secure token or to generate a new one, run {newToken} and supply the token below`,
|
noConnection: `Unable to make a connection to the Parity Secure API. To update your secure token or to generate a new one, run {newToken} and paste the generated token into the space below.`,
|
||||||
token: {
|
token: {
|
||||||
hint: `a generated token from Parity`,
|
hint: `a generated token from Parity`,
|
||||||
label: `secure token`
|
label: `secure token`
|
||||||
|
@ -97,54 +97,6 @@ The following options are possible for the \`defaultBlock\` parameter:
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
compileSerpent: {
|
|
||||||
desc: 'Returns compiled serpent code.',
|
|
||||||
params: [
|
|
||||||
{
|
|
||||||
type: String,
|
|
||||||
desc: 'The source code.',
|
|
||||||
example: '/* some serpent */'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
returns: {
|
|
||||||
type: Data,
|
|
||||||
desc: 'The compiled source code.',
|
|
||||||
example: '0x603880600c6000396000f3006001600060e060020a600035048063c6888fa114601857005b6021600435602b565b8060005260206000f35b600081600702905091905056'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
compileSolidity: {
|
|
||||||
desc: 'Returns compiled solidity code.',
|
|
||||||
params: [
|
|
||||||
{
|
|
||||||
type: String,
|
|
||||||
desc: 'The source code.',
|
|
||||||
example: 'contract test { function multiply(uint a) returns(uint d) { return a * 7; } }'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
returns: {
|
|
||||||
type: Data,
|
|
||||||
desc: 'The compiled source code.',
|
|
||||||
example: '0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
compileLLL: {
|
|
||||||
desc: 'Returns compiled LLL code.',
|
|
||||||
params: [
|
|
||||||
{
|
|
||||||
type: String,
|
|
||||||
desc: 'The source code.',
|
|
||||||
example: '(returnlll (suicide (caller)))'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
returns: {
|
|
||||||
type: Data,
|
|
||||||
desc: 'The compiled source code.',
|
|
||||||
example: '0x603880600c6000396000f3006001600060e060020a600035048063c6888fa114601857005b6021600435602b565b8060005260206000f35b600081600702905091905056'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
estimateGas: {
|
estimateGas: {
|
||||||
desc: 'Makes a call or transaction, which won\'t be added to the blockchain and returns the used gas, which can be used for estimating the used gas.',
|
desc: 'Makes a call or transaction, which won\'t be added to the blockchain and returns the used gas, which can be used for estimating the used gas.',
|
||||||
params: [
|
params: [
|
||||||
@ -415,16 +367,6 @@ The following options are possible for the \`defaultBlock\` parameter:
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getCompilers: {
|
|
||||||
desc: 'Returns a list of available compilers in the client.',
|
|
||||||
params: [],
|
|
||||||
returns: {
|
|
||||||
type: Array,
|
|
||||||
desc: 'Array of available compilers.',
|
|
||||||
example: ['solidity', 'lll', 'serpent']
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getFilterChanges: {
|
getFilterChanges: {
|
||||||
desc: 'Polling method for a filter, which returns an array of logs which occurred since last poll.',
|
desc: 'Polling method for a filter, which returns an array of logs which occurred since last poll.',
|
||||||
params: [
|
params: [
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import { Address, Data, Hash, Quantity, BlockNumber, TransactionRequest } from '../types';
|
import { Address, Data, Hash, Quantity, BlockNumber, TransactionRequest, TransactionResponse } from '../types';
|
||||||
import { fromDecimal, withComment, Dummy } from '../helpers';
|
import { fromDecimal, withComment, Dummy } from '../helpers';
|
||||||
|
|
||||||
const SECTION_ACCOUNTS = 'Accounts (read-only) and Signatures';
|
const SECTION_ACCOUNTS = 'Accounts (read-only) and Signatures';
|
||||||
@ -27,86 +27,6 @@ const SECTION_VAULT = 'Account Vaults';
|
|||||||
const SUBDOC_SET = 'set';
|
const SUBDOC_SET = 'set';
|
||||||
const SUBDOC_ACCOUNTS = 'accounts';
|
const SUBDOC_ACCOUNTS = 'accounts';
|
||||||
|
|
||||||
const transactionDetails = {
|
|
||||||
hash: {
|
|
||||||
type: Hash,
|
|
||||||
desc: '32 Bytes - hash of the transaction.'
|
|
||||||
},
|
|
||||||
nonce: {
|
|
||||||
type: Quantity,
|
|
||||||
desc: 'The number of transactions made by the sender prior to this one.'
|
|
||||||
},
|
|
||||||
blockHash: {
|
|
||||||
type: Hash,
|
|
||||||
desc: '32 Bytes - hash of the block where this transaction was in. `null` when its pending.'
|
|
||||||
},
|
|
||||||
blockNumber: {
|
|
||||||
type: BlockNumber,
|
|
||||||
desc: 'Block number where this transaction was in. `null` when its pending.'
|
|
||||||
},
|
|
||||||
transactionIndex: {
|
|
||||||
type: Quantity,
|
|
||||||
desc: 'Integer of the transactions index position in the block. `null` when its pending.'
|
|
||||||
},
|
|
||||||
from: {
|
|
||||||
type: Address,
|
|
||||||
desc: '20 Bytes - address of the sender.'
|
|
||||||
},
|
|
||||||
to: {
|
|
||||||
type: Address,
|
|
||||||
desc: '20 Bytes - address of the receiver. `null` when its a contract creation transaction.'
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
type: Quantity,
|
|
||||||
desc: 'Value transferred in Wei.'
|
|
||||||
},
|
|
||||||
gasPrice: {
|
|
||||||
type: Quantity,
|
|
||||||
desc: 'Gas price provided by the sender in Wei.'
|
|
||||||
},
|
|
||||||
gas: {
|
|
||||||
type: Quantity,
|
|
||||||
desc: 'Gas provided by the sender.'
|
|
||||||
},
|
|
||||||
input: {
|
|
||||||
type: Data,
|
|
||||||
desc: 'The data send along with the transaction.'
|
|
||||||
},
|
|
||||||
raw: {
|
|
||||||
type: Data,
|
|
||||||
desc: 'Raw transaction data.'
|
|
||||||
},
|
|
||||||
publicKey: {
|
|
||||||
type: Data,
|
|
||||||
desc: 'Public key of the signer.'
|
|
||||||
},
|
|
||||||
networkId: {
|
|
||||||
type: Quantity,
|
|
||||||
desc: 'The network id of the transaction, if any.'
|
|
||||||
},
|
|
||||||
standardV: {
|
|
||||||
type: Quantity,
|
|
||||||
desc: 'The standardized V field of the signature (0 or 1).'
|
|
||||||
},
|
|
||||||
v: {
|
|
||||||
type: Quantity,
|
|
||||||
desc: 'The V field of the signature.'
|
|
||||||
},
|
|
||||||
r: {
|
|
||||||
type: Quantity,
|
|
||||||
desc: 'The R field of the signature.'
|
|
||||||
},
|
|
||||||
s: {
|
|
||||||
type: Quantity,
|
|
||||||
desc: 'The S field of the signature.'
|
|
||||||
},
|
|
||||||
condition: {
|
|
||||||
type: Object,
|
|
||||||
optional: true,
|
|
||||||
desc: 'Conditional submission, Block number in `block` or timestamp in `time` or `null`.'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
accountsInfo: {
|
accountsInfo: {
|
||||||
section: SECTION_ACCOUNTS,
|
section: SECTION_ACCOUNTS,
|
||||||
@ -393,6 +313,32 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
hardwareAccountsInfo: {
|
||||||
|
section: SECTION_ACCOUNTS,
|
||||||
|
desc: 'Provides metadata for attached hardware wallets',
|
||||||
|
params: [],
|
||||||
|
returns: {
|
||||||
|
type: Object,
|
||||||
|
desc: 'Maps account address to metadata.',
|
||||||
|
details: {
|
||||||
|
manufacturer: {
|
||||||
|
type: String,
|
||||||
|
desc: 'Manufacturer'
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
desc: 'Account name'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: {
|
||||||
|
'0x0024d0c7ab4c52f723f3aaf0872b9ea4406846a4': {
|
||||||
|
manufacturer: 'Ledger',
|
||||||
|
name: 'Nano S'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
listOpenedVaults: {
|
listOpenedVaults: {
|
||||||
desc: 'Returns a list of all opened vaults',
|
desc: 'Returns a list of all opened vaults',
|
||||||
params: [],
|
params: [],
|
||||||
@ -608,7 +554,7 @@ export default {
|
|||||||
returns: {
|
returns: {
|
||||||
type: Array,
|
type: Array,
|
||||||
desc: 'Transactions ordered by priority',
|
desc: 'Transactions ordered by priority',
|
||||||
details: transactionDetails,
|
details: TransactionResponse.details,
|
||||||
example: [
|
example: [
|
||||||
{
|
{
|
||||||
blockHash: null,
|
blockHash: null,
|
||||||
@ -924,7 +870,7 @@ export default {
|
|||||||
returns: {
|
returns: {
|
||||||
type: Array,
|
type: Array,
|
||||||
desc: 'Transaction list.',
|
desc: 'Transaction list.',
|
||||||
details: transactionDetails,
|
details: TransactionResponse.details,
|
||||||
example: [
|
example: [
|
||||||
{
|
{
|
||||||
hash: '0x80de421cd2e7e46824a91c343ca42b2ff339409eef09e2d9d73882462f8fce31',
|
hash: '0x80de421cd2e7e46824a91c343ca42b2ff339409eef09e2d9d73882462f8fce31',
|
||||||
|
@ -109,3 +109,92 @@ export class TransactionRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class TransactionResponse {
|
||||||
|
static print = '`Object`';
|
||||||
|
|
||||||
|
static details = {
|
||||||
|
hash: {
|
||||||
|
type: Hash,
|
||||||
|
desc: '32 Bytes - hash of the transaction.'
|
||||||
|
},
|
||||||
|
nonce: {
|
||||||
|
type: Quantity,
|
||||||
|
desc: 'The number of transactions made by the sender prior to this one.'
|
||||||
|
},
|
||||||
|
blockHash: {
|
||||||
|
type: Hash,
|
||||||
|
desc: '32 Bytes - hash of the block where this transaction was in. `null` when its pending.'
|
||||||
|
},
|
||||||
|
blockNumber: {
|
||||||
|
type: BlockNumber,
|
||||||
|
desc: 'Block number where this transaction was in. `null` when its pending.'
|
||||||
|
},
|
||||||
|
transactionIndex: {
|
||||||
|
type: Quantity,
|
||||||
|
desc: 'Integer of the transactions index position in the block. `null` when its pending.'
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
type: Address,
|
||||||
|
desc: '20 Bytes - address of the sender.'
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
type: Address,
|
||||||
|
desc: '20 Bytes - address of the receiver. `null` when its a contract creation transaction.'
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: Quantity,
|
||||||
|
desc: 'Value transferred in Wei.'
|
||||||
|
},
|
||||||
|
gasPrice: {
|
||||||
|
type: Quantity,
|
||||||
|
desc: 'Gas price provided by the sender in Wei.'
|
||||||
|
},
|
||||||
|
gas: {
|
||||||
|
type: Quantity,
|
||||||
|
desc: 'Gas provided by the sender.'
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
type: Data,
|
||||||
|
desc: 'The data send along with the transaction.'
|
||||||
|
},
|
||||||
|
creates: {
|
||||||
|
type: Address,
|
||||||
|
optional: true,
|
||||||
|
desc: 'Address of a created contract or `null`.'
|
||||||
|
},
|
||||||
|
raw: {
|
||||||
|
type: Data,
|
||||||
|
desc: 'Raw transaction data.'
|
||||||
|
},
|
||||||
|
publicKey: {
|
||||||
|
type: Data,
|
||||||
|
desc: 'Public key of the signer.'
|
||||||
|
},
|
||||||
|
networkId: {
|
||||||
|
type: Quantity,
|
||||||
|
desc: 'The network id of the transaction, if any.'
|
||||||
|
},
|
||||||
|
standardV: {
|
||||||
|
type: Quantity,
|
||||||
|
desc: 'The standardized V field of the signature (0 or 1).'
|
||||||
|
},
|
||||||
|
v: {
|
||||||
|
type: Quantity,
|
||||||
|
desc: 'The V field of the signature.'
|
||||||
|
},
|
||||||
|
r: {
|
||||||
|
type: Quantity,
|
||||||
|
desc: 'The R field of the signature.'
|
||||||
|
},
|
||||||
|
s: {
|
||||||
|
type: Quantity,
|
||||||
|
desc: 'The S field of the signature.'
|
||||||
|
},
|
||||||
|
condition: {
|
||||||
|
type: Object,
|
||||||
|
optional: true,
|
||||||
|
desc: 'Conditional submission, Block number in `block` or timestamp in `time` or `null`.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
159
js/src/mobx/hardwareStore.js
Normal file
159
js/src/mobx/hardwareStore.js
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { action, computed, observable, transaction } from 'mobx';
|
||||||
|
|
||||||
|
import Ledger from '~/3rdparty/ledger';
|
||||||
|
|
||||||
|
const HW_SCAN_INTERVAL = 5000;
|
||||||
|
let instance = null;
|
||||||
|
|
||||||
|
export default class HardwareStore {
|
||||||
|
@observable isScanning = false;
|
||||||
|
@observable wallets = {};
|
||||||
|
|
||||||
|
constructor (api) {
|
||||||
|
this._api = api;
|
||||||
|
this._ledger = Ledger.create(api);
|
||||||
|
this._pollId = null;
|
||||||
|
|
||||||
|
this._pollScan();
|
||||||
|
}
|
||||||
|
|
||||||
|
isConnected (address) {
|
||||||
|
return computed(() => !!this.wallets[address]).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setScanning = (isScanning) => {
|
||||||
|
this.isScanning = isScanning;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setWallets = (wallets) => {
|
||||||
|
this.wallets = wallets;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pollScan = () => {
|
||||||
|
this._pollId = setTimeout(() => {
|
||||||
|
this.scan().then(this._pollScan);
|
||||||
|
}, HW_SCAN_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
scanLedger () {
|
||||||
|
if (!this._ledger.isSupported) {
|
||||||
|
return Promise.resolve({});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._ledger
|
||||||
|
.scan()
|
||||||
|
.then((wallets) => {
|
||||||
|
console.log('HardwareStore::scanLedger', wallets);
|
||||||
|
|
||||||
|
return wallets.reduce((hwInfo, wallet) => {
|
||||||
|
wallet.manufacturer = 'Ledger';
|
||||||
|
wallet.name = 'Nano S';
|
||||||
|
wallet.via = 'ledger';
|
||||||
|
|
||||||
|
hwInfo[wallet.address] = wallet;
|
||||||
|
|
||||||
|
return hwInfo;
|
||||||
|
}, {});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.warn('HardwareStore::scanLedger', error);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
scanParity () {
|
||||||
|
return this._api.parity
|
||||||
|
.hardwareAccountsInfo()
|
||||||
|
.then((hwInfo) => {
|
||||||
|
Object
|
||||||
|
.keys(hwInfo)
|
||||||
|
.forEach((address) => {
|
||||||
|
const info = hwInfo[address];
|
||||||
|
|
||||||
|
info.address = address;
|
||||||
|
info.via = 'parity';
|
||||||
|
});
|
||||||
|
|
||||||
|
return hwInfo;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.warn('HardwareStore::scanParity', error);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
scan () {
|
||||||
|
this.setScanning(true);
|
||||||
|
|
||||||
|
// NOTE: Depending on how the hardware is configured and how the local env setup
|
||||||
|
// is done, different results will be retrieved via Parity vs. the browser APIs
|
||||||
|
// (latter is Chrome-only, needs the browser app enabled on a Ledger, former is
|
||||||
|
// not intended as a network call, i.e. hw wallet is with the user)
|
||||||
|
return Promise
|
||||||
|
.all([
|
||||||
|
this.scanParity(),
|
||||||
|
this.scanLedger()
|
||||||
|
])
|
||||||
|
.then(([hwAccounts, ledgerAccounts]) => {
|
||||||
|
transaction(() => {
|
||||||
|
this.setWallets(Object.assign({}, hwAccounts, ledgerAccounts));
|
||||||
|
this.setScanning(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createAccountInfo (entry) {
|
||||||
|
const { address, manufacturer, name } = entry;
|
||||||
|
|
||||||
|
return Promise
|
||||||
|
.all([
|
||||||
|
this._api.parity.setAccountName(address, name),
|
||||||
|
this._api.parity.setAccountMeta(address, {
|
||||||
|
description: `${manufacturer} ${name}`,
|
||||||
|
hardware: {
|
||||||
|
manufacturer
|
||||||
|
},
|
||||||
|
tags: ['hardware'],
|
||||||
|
timestamp: Date.now()
|
||||||
|
})
|
||||||
|
])
|
||||||
|
.catch((error) => {
|
||||||
|
console.warn('HardwareStore::createEntry', error);
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
signLedger (transaction) {
|
||||||
|
return this._ledger.signTransaction(transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get (api) {
|
||||||
|
if (!instance) {
|
||||||
|
instance = new HardwareStore(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
HW_SCAN_INTERVAL
|
||||||
|
};
|
220
js/src/mobx/hardwareStore.spec.js
Normal file
220
js/src/mobx/hardwareStore.spec.js
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import sinon from 'sinon';
|
||||||
|
|
||||||
|
import HardwareStore, { HW_SCAN_INTERVAL } from './hardwareStore';
|
||||||
|
|
||||||
|
const ADDRESS = '0x1234567890123456789012345678901234567890';
|
||||||
|
const WALLET = {
|
||||||
|
address: ADDRESS,
|
||||||
|
name: 'testing'
|
||||||
|
};
|
||||||
|
|
||||||
|
let api;
|
||||||
|
let clock;
|
||||||
|
let ledger;
|
||||||
|
let store;
|
||||||
|
|
||||||
|
function createApi () {
|
||||||
|
api = {
|
||||||
|
parity: {
|
||||||
|
hardwareAccountsInfo: sinon.stub().resolves({ ADDRESS: WALLET }),
|
||||||
|
setAccountMeta: sinon.stub().resolves(true),
|
||||||
|
setAccountName: sinon.stub().resolves(true)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return api;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLedger () {
|
||||||
|
ledger = {
|
||||||
|
isSupported: true,
|
||||||
|
getAppConfiguration: sinon.stub().resolves(),
|
||||||
|
scan: sinon.stub().resolves(WALLET),
|
||||||
|
signTransaction: sinon.stub().resolves()
|
||||||
|
};
|
||||||
|
|
||||||
|
return ledger;
|
||||||
|
}
|
||||||
|
|
||||||
|
function create () {
|
||||||
|
clock = sinon.useFakeTimers();
|
||||||
|
store = new HardwareStore(createApi());
|
||||||
|
store._ledger = createLedger();
|
||||||
|
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
|
||||||
|
function teardown () {
|
||||||
|
clock.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('mobx/HardwareStore', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
create();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
teardown();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('@computed', () => {
|
||||||
|
describe('isConnected', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
store.setWallets({ [ADDRESS]: WALLET });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true for available', () => {
|
||||||
|
expect(store.isConnected(ADDRESS)).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false for non-available', () => {
|
||||||
|
expect(store.isConnected('nothing')).to.be.false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('background polling', () => {
|
||||||
|
let pollId;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
pollId = store._pollId;
|
||||||
|
sinon.spy(store, 'scan');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
store.scan.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('starts the polling at creation', () => {
|
||||||
|
expect(pollId).not.to.be.null;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scans when timer elapsed', () => {
|
||||||
|
expect(store.scan).not.to.have.been.called;
|
||||||
|
clock.tick(HW_SCAN_INTERVAL + 1);
|
||||||
|
expect(store.scan).to.have.been.called;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('@action', () => {
|
||||||
|
describe('setScanning', () => {
|
||||||
|
it('sets the flag', () => {
|
||||||
|
store.setScanning('testScanning');
|
||||||
|
expect(store.isScanning).to.equal('testScanning');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setWallets', () => {
|
||||||
|
it('sets the wallets', () => {
|
||||||
|
store.setWallets('testWallet');
|
||||||
|
expect(store.wallets).to.equal('testWallet');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('operations', () => {
|
||||||
|
describe('createAccountInfo', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
return store.createAccountInfo({
|
||||||
|
address: 'testAddr',
|
||||||
|
manufacturer: 'testMfg',
|
||||||
|
name: 'testName'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls into parity_setAccountName', () => {
|
||||||
|
expect(api.parity.setAccountName).to.have.been.calledWith('testAddr', 'testName');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls into parity_setAccountMeta', () => {
|
||||||
|
expect(api.parity.setAccountMeta).to.have.been.calledWith('testAddr', sinon.match({
|
||||||
|
description: 'testMfg testName',
|
||||||
|
hardware: {
|
||||||
|
manufacturer: 'testMfg'
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('scanLedger', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
return store.scanLedger();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls scan on the Ledger APIs', () => {
|
||||||
|
expect(ledger.scan).to.have.been.called;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('scanParity', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
return store.scanParity();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls parity_hardwareAccountsInfo', () => {
|
||||||
|
expect(api.parity.hardwareAccountsInfo).to.have.been.called;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('scan', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
sinon.spy(store, 'setScanning');
|
||||||
|
sinon.spy(store, 'setWallets');
|
||||||
|
sinon.spy(store, 'scanLedger');
|
||||||
|
sinon.spy(store, 'scanParity');
|
||||||
|
|
||||||
|
return store.scan();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
store.setScanning.restore();
|
||||||
|
store.setWallets.restore();
|
||||||
|
store.scanLedger.restore();
|
||||||
|
store.scanParity.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls scanLedger', () => {
|
||||||
|
expect(store.scanLedger).to.have.been.called;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls scanParity', () => {
|
||||||
|
expect(store.scanParity).to.have.been.called;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets and resets the scanning state', () => {
|
||||||
|
expect(store.setScanning).to.have.been.calledWith(true);
|
||||||
|
expect(store.setScanning).to.have.been.calledWith(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the wallets', () => {
|
||||||
|
expect(store.setWallets).to.have.been.called;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('signLedger', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
return store.signLedger('testTx');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls signTransaction on the ledger', () => {
|
||||||
|
expect(ledger.signTransaction).to.have.been.calledWith('testTx');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -29,7 +29,7 @@ function create () {
|
|||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('views/HistoryStore', () => {
|
describe('mobx/HistoryStore', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
create();
|
create();
|
||||||
});
|
});
|
51
js/src/modals/CreateAccount/ChangeVault/changeVault.js
Normal file
51
js/src/modals/CreateAccount/ChangeVault/changeVault.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
|
||||||
|
import { VaultSelect } from '~/ui';
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export default class ChangeVault extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
store: PropTypes.object.isRequired,
|
||||||
|
vaultStore: PropTypes.object
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { store, vaultStore } = this.props;
|
||||||
|
const { vaultName } = store;
|
||||||
|
|
||||||
|
if (!vaultStore || vaultStore.vaultsOpened.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<VaultSelect
|
||||||
|
onSelect={ this.onSelect }
|
||||||
|
value={ vaultName }
|
||||||
|
vaultStore={ vaultStore }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelect = (vaultName) => {
|
||||||
|
const { store } = this.props;
|
||||||
|
|
||||||
|
store.setVaultName(vaultName);
|
||||||
|
}
|
||||||
|
}
|
100
js/src/modals/CreateAccount/ChangeVault/changeVault.spec.js
Normal file
100
js/src/modals/CreateAccount/ChangeVault/changeVault.spec.js
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { shallow } from 'enzyme';
|
||||||
|
import React from 'react';
|
||||||
|
import sinon from 'sinon';
|
||||||
|
|
||||||
|
import ChangeVault from './';
|
||||||
|
|
||||||
|
let component;
|
||||||
|
let instance;
|
||||||
|
let store;
|
||||||
|
let vaultStore;
|
||||||
|
|
||||||
|
function createStore () {
|
||||||
|
store = {
|
||||||
|
setVaultName: sinon.stub(),
|
||||||
|
vaultName: 'testing'
|
||||||
|
};
|
||||||
|
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createVaultStore () {
|
||||||
|
vaultStore = {
|
||||||
|
vaultsOpened: ['testing']
|
||||||
|
};
|
||||||
|
|
||||||
|
return vaultStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
function render () {
|
||||||
|
component = shallow(
|
||||||
|
<ChangeVault
|
||||||
|
store={ createStore() }
|
||||||
|
vaultStore={ createVaultStore() }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
instance = component.instance();
|
||||||
|
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('modals/CreateAccount/ChangeVault', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders defaults', () => {
|
||||||
|
expect(component).to.be.ok;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('components', () => {
|
||||||
|
describe('VaultSelect', () => {
|
||||||
|
let select;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
select = component.find('VaultSelect');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders', () => {
|
||||||
|
expect(select.get(0)).to.be.ok;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('passes onSelect as instance method', () => {
|
||||||
|
expect(select.props().onSelect).to.equal(instance.onSelect);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('passes the value', () => {
|
||||||
|
expect(select.props().value).to.equal('testing');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('passes the vaultStore', () => {
|
||||||
|
expect(select.props().vaultStore).to.equal(vaultStore);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('instance methods', () => {
|
||||||
|
describe('onSelect', () => {
|
||||||
|
it('calls into store setVaultName', () => {
|
||||||
|
instance.onSelect('newName');
|
||||||
|
expect(store.setVaultName).to.have.been.calledWith('newName');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
17
js/src/modals/CreateAccount/ChangeVault/index.js
Normal file
17
js/src/modals/CreateAccount/ChangeVault/index.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
export default from './changeVault';
|
@ -24,13 +24,15 @@ import { Form, Input, IdentityIcon } from '~/ui';
|
|||||||
import PasswordStrength from '~/ui/Form/PasswordStrength';
|
import PasswordStrength from '~/ui/Form/PasswordStrength';
|
||||||
import { RefreshIcon } from '~/ui/Icons';
|
import { RefreshIcon } from '~/ui/Icons';
|
||||||
|
|
||||||
|
import ChangeVault from '../ChangeVault';
|
||||||
import styles from '../createAccount.css';
|
import styles from '../createAccount.css';
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export default class CreateAccount extends Component {
|
export default class CreateAccount extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
newError: PropTypes.func.isRequired,
|
newError: PropTypes.func.isRequired,
|
||||||
store: PropTypes.object.isRequired
|
store: PropTypes.object.isRequired,
|
||||||
|
vaultStore: PropTypes.object
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -123,6 +125,10 @@ export default class CreateAccount extends Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<PasswordStrength input={ password } />
|
<PasswordStrength input={ password } />
|
||||||
|
<ChangeVault
|
||||||
|
store={ this.props.store }
|
||||||
|
vaultStore={ this.props.vaultStore }
|
||||||
|
/>
|
||||||
{ this.renderIdentitySelector() }
|
{ this.renderIdentitySelector() }
|
||||||
{ this.renderIdentities() }
|
{ this.renderIdentities() }
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -14,27 +14,25 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import { FloatingActionButton } from 'material-ui';
|
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Form, Input } from '~/ui';
|
import { Form, FileSelect, Input } from '~/ui';
|
||||||
import { AttachFileIcon } from '~/ui/Icons';
|
|
||||||
|
|
||||||
|
import ChangeVault from '../ChangeVault';
|
||||||
import styles from '../createAccount.css';
|
import styles from '../createAccount.css';
|
||||||
|
|
||||||
const STYLE_HIDDEN = { display: 'none' };
|
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export default class NewImport extends Component {
|
export default class NewImport extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
store: PropTypes.object.isRequired
|
store: PropTypes.object.isRequired,
|
||||||
|
vaultStore: PropTypes.object
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { name, nameError, password, passwordHint, walletFile, walletFileError } = this.props.store;
|
const { name, nameError, password, passwordHint } = this.props.store;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form>
|
<Form>
|
||||||
@ -93,58 +91,52 @@ export default class NewImport extends Component {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<ChangeVault
|
||||||
<Input
|
store={ this.props.store }
|
||||||
disabled
|
vaultStore={ this.props.vaultStore }
|
||||||
error={ walletFileError }
|
/>
|
||||||
hint={
|
{ this.renderFileSelector() }
|
||||||
<FormattedMessage
|
|
||||||
id='createAccount.newImport.file.hint'
|
|
||||||
defaultMessage='the wallet file for import'
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label={
|
|
||||||
<FormattedMessage
|
|
||||||
id='createAccount.newImport.file.label'
|
|
||||||
defaultMessage='wallet file'
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
value={ walletFile }
|
|
||||||
/>
|
|
||||||
<div className={ styles.upload }>
|
|
||||||
<FloatingActionButton
|
|
||||||
mini
|
|
||||||
onTouchTap={ this.openFileDialog }
|
|
||||||
>
|
|
||||||
<AttachFileIcon />
|
|
||||||
</FloatingActionButton>
|
|
||||||
<input
|
|
||||||
onChange={ this.onFileChange }
|
|
||||||
ref='fileUpload'
|
|
||||||
style={ STYLE_HIDDEN }
|
|
||||||
type='file'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onFileChange = (event) => {
|
renderFileSelector () {
|
||||||
const { store } = this.props;
|
const { walletFile, walletFileError } = this.props.store;
|
||||||
|
|
||||||
if (event.target.files.length) {
|
return walletFile
|
||||||
const reader = new FileReader();
|
? (
|
||||||
|
<Input
|
||||||
reader.onload = (event) => store.setWalletJson(event.target.result);
|
disabled
|
||||||
reader.readAsText(event.target.files[0]);
|
error={ walletFileError }
|
||||||
}
|
hint={
|
||||||
|
<FormattedMessage
|
||||||
store.setWalletFile(event.target.value);
|
id='createAccount.newImport.file.hint'
|
||||||
|
defaultMessage='the wallet file for import'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<FormattedMessage
|
||||||
|
id='createAccount.newImport.file.label'
|
||||||
|
defaultMessage='wallet file'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
value={ walletFile }
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<FileSelect
|
||||||
|
className={ styles.fileImport }
|
||||||
|
error={ walletFileError }
|
||||||
|
onSelect={ this.onFileSelect }
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
openFileDialog = () => {
|
onFileSelect = (fileName, fileContent) => {
|
||||||
ReactDOM.findDOMNode(this.refs.fileUpload).click();
|
const { store } = this.props;
|
||||||
|
|
||||||
|
store.setWalletFile(fileName);
|
||||||
|
store.setWalletJson(fileContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
onEditName = (event, name) => {
|
onEditName = (event, name) => {
|
||||||
|
@ -21,6 +21,7 @@ import { FormattedMessage } from 'react-intl';
|
|||||||
import { Form, Input } from '~/ui';
|
import { Form, Input } from '~/ui';
|
||||||
import PasswordStrength from '~/ui/Form/PasswordStrength';
|
import PasswordStrength from '~/ui/Form/PasswordStrength';
|
||||||
|
|
||||||
|
import ChangeVault from '../ChangeVault';
|
||||||
import styles from '../createAccount.css';
|
import styles from '../createAccount.css';
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
@ -30,7 +31,8 @@ export default class RawKey extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
store: PropTypes.object.isRequired
|
store: PropTypes.object.isRequired,
|
||||||
|
vaultStore: PropTypes.object
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
@ -131,6 +133,10 @@ export default class RawKey extends Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<PasswordStrength input={ password } />
|
<PasswordStrength input={ password } />
|
||||||
|
<ChangeVault
|
||||||
|
store={ this.props.store }
|
||||||
|
vaultStore={ this.props.vaultStore }
|
||||||
|
/>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user