Merge branch 'master' into remote-tx-exec

This commit is contained in:
Robert Habermeier 2017-02-26 13:55:29 +01:00
commit 1ff0abc661
242 changed files with 7513 additions and 1957 deletions

View File

@ -33,9 +33,9 @@ linux-stable:
- md5sum target/release/parity > parity.md5 - md5sum target/release/parity > parity.md5
- sh scripts/deb-build.sh amd64 - sh scripts/deb-build.sh amd64
- cp target/release/parity deb/usr/bin/parity - cp target/release/parity deb/usr/bin/parity
- cp target/release/parity/evm deb/usr/bin/evm - cp target/release/evm deb/usr/bin/evm
- cp target/release/parity/ethstore deb/usr/bin/ethstore - cp target/release/ethstore deb/usr/bin/ethstore
- cp target/release/parity/ethkey deb/usr/bin/ethkey - cp target/release/ethkey deb/usr/bin/ethkey
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n") - export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
- dpkg-deb -b deb "parity_"$VER"_amd64.deb" - dpkg-deb -b deb "parity_"$VER"_amd64.deb"
- md5sum "parity_"$VER"_amd64.deb" > "parity_"$VER"_amd64.deb.md5" - md5sum "parity_"$VER"_amd64.deb" > "parity_"$VER"_amd64.deb.md5"
@ -69,11 +69,20 @@ linux-stable-debian:
- triggers - triggers
script: script:
- cargo build -j $(nproc) --release --features final $CARGOFLAGS - cargo build -j $(nproc) --release --features final $CARGOFLAGS
- cargo build -j $(nproc) --release -p evmbin
- cargo build -j $(nproc) --release -p ethstore
- cargo build -j $(nproc) --release -p ethkey
- strip target/release/parity - strip target/release/parity
- strip target/release/evm
- strip target/release/ethstore
- strip target/release/ethkey
- export SHA3=$(target/release/parity tools hash target/release/parity) - export SHA3=$(target/release/parity tools hash target/release/parity)
- md5sum target/release/parity > parity.md5 - md5sum target/release/parity > parity.md5
- sh scripts/deb-build.sh amd64 - sh scripts/deb-build.sh amd64
- cp target/release/parity deb/usr/bin/parity - cp target/release/parity deb/usr/bin/parity
- cp target/release/evm deb/usr/bin/evm
- cp target/release/ethstore deb/usr/bin/ethstore
- cp target/release/ethkey deb/usr/bin/ethkey
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n") - export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
- dpkg-deb -b deb "parity_"$VER"_amd64.deb" - dpkg-deb -b deb "parity_"$VER"_amd64.deb"
- md5sum "parity_"$VER"_amd64.deb" > "parity_"$VER"_amd64.deb.md5" - md5sum "parity_"$VER"_amd64.deb" > "parity_"$VER"_amd64.deb.md5"
@ -486,14 +495,14 @@ docker-build:
stage: build stage: build
only: only:
- tags - tags
- triggers
before_script: before_script:
- docker info - docker info
script: script:
- cd docker/hub - cd docker/hub
- if [ "$CI_BUILD_REF_NAME" == "nightly" ]; 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
- docker build --no-cache=true --tag ethcore/parity:$DOCKER_TAG . - sh scripts/docker-build.sh $DOCKER_TAG
- docker push ethcore/parity:$DOCKER_TAG
tags: tags:
- docker - docker
test-darwin: test-darwin:

55
Cargo.lock generated
View File

@ -411,6 +411,7 @@ dependencies = [
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (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)",
"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)",
"stats 0.1.0",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"transient-hashmap 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "transient-hashmap 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -444,7 +445,7 @@ dependencies = [
"ethcore-rpc 1.6.0", "ethcore-rpc 1.6.0",
"ethcore-util 1.6.0", "ethcore-util 1.6.0",
"fetch 0.1.0", "fetch 0.1.0",
"futures 0.1.6 (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)",
"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-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)",
@ -553,12 +554,13 @@ dependencies = [
"ethcore-ipc-codegen 1.6.0", "ethcore-ipc-codegen 1.6.0",
"ethcore-network 1.6.0", "ethcore-network 1.6.0",
"ethcore-util 1.6.0", "ethcore-util 1.6.0",
"futures 0.1.6 (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)",
"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)",
"rlp 0.1.0", "rlp 0.1.0",
"smallvec 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"stats 0.1.0",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -618,7 +620,7 @@ dependencies = [
"ethstore 0.1.0", "ethstore 0.1.0",
"ethsync 1.6.0", "ethsync 1.6.0",
"fetch 0.1.0", "fetch 0.1.0",
"futures 0.1.6 (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-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)",
"jsonrpc-ipc-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "jsonrpc-ipc-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
@ -633,6 +635,7 @@ dependencies = [
"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)",
"serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
"stats 0.1.0",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"transient-hashmap 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "transient-hashmap 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -648,7 +651,7 @@ dependencies = [
"ethcore-util 1.6.0", "ethcore-util 1.6.0",
"ethcrypto 0.1.0", "ethcrypto 0.1.0",
"ethkey 0.2.0", "ethkey 0.2.0",
"hyper 0.9.14 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.10.4 (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)",
"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)",
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -683,7 +686,7 @@ dependencies = [
"ethcore-ipc-codegen 1.6.0", "ethcore-ipc-codegen 1.6.0",
"ethcore-ipc-nano 1.6.0", "ethcore-ipc-nano 1.6.0",
"ethcore-util 1.6.0", "ethcore-util 1.6.0",
"futures 0.1.6 (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)",
"jsonrpc-tcp-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "jsonrpc-tcp-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
@ -846,7 +849,7 @@ dependencies = [
name = "fetch" name = "fetch"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.2 (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)",
"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)",
@ -865,7 +868,7 @@ dependencies = [
[[package]] [[package]]
name = "futures" name = "futures"
version = "0.1.6" version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
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)",
@ -877,7 +880,7 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
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)",
"futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -952,7 +955,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "0.9.14" version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
@ -973,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#6baea9d444dd1652220ee9b4aeadaebb3de6a955" source = "git+https://github.com/ethcore/hyper#2e6702984f4f9e99fe251537a755aff0badc0b3a"
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)",
@ -1033,7 +1036,7 @@ name = "igd"
version = "0.5.1" version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"hyper 0.9.14 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.9.18 (registry+https://github.com/rust-lang/crates.io-index)",
"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)",
"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)",
"xml-rs 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "xml-rs 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1083,7 +1086,7 @@ name = "jsonrpc-core"
version = "6.0.0" version = "6.0.0"
source = "git+https://github.com/ethcore/jsonrpc.git#86d7a89c85f324b5f6671315d9b71010ca995300" source = "git+https://github.com/ethcore/jsonrpc.git#86d7a89c85f324b5f6671315d9b71010ca995300"
dependencies = [ dependencies = [
"futures 0.1.6 (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)",
"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)",
@ -1620,7 +1623,7 @@ 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.6.0",
"fetch 0.1.0", "fetch 0.1.0",
"futures 0.1.6 (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)",
"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)",
@ -1637,6 +1640,7 @@ dependencies = [
"ethcore 1.6.0", "ethcore 1.6.0",
"ethcore-util 1.6.0", "ethcore-util 1.6.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)",
"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)",
"multihash 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "multihash 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.1.0", "rlp 0.1.0",
@ -1661,7 +1665,7 @@ dependencies = [
name = "parity-reactor" name = "parity-reactor"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -1672,7 +1676,7 @@ dependencies = [
"ethcore-rpc 1.6.0", "ethcore-rpc 1.6.0",
"ethcore-signer 1.6.0", "ethcore-signer 1.6.0",
"ethcore-util 1.6.0", "ethcore-util 1.6.0",
"futures 0.1.6 (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)",
"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)",
@ -1702,7 +1706,7 @@ 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#9fb4ab9d8ffaca9cd9f07270bf69681c2081050f" source = "git+https://github.com/ethcore/js-precompiled.git#aaee793907e4ff61082d83ff44733363dfff6eae"
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)",
] ]
@ -1990,7 +1994,7 @@ dependencies = [
"ethcore-bigint 0.1.2", "ethcore-bigint 0.1.2",
"ethcore-rpc 1.6.0", "ethcore-rpc 1.6.0",
"ethcore-util 1.6.0", "ethcore-util 1.6.0",
"futures 0.1.6 (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)",
] ]
@ -2223,6 +2227,13 @@ name = "stable-heap"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/carllerche/stable-heap?rev=3c5cd1ca47#3c5cd1ca4706f167a1de85658b5af0d6d3e65165" source = "git+https://github.com/carllerche/stable-heap?rev=3c5cd1ca47#3c5cd1ca4706f167a1de85658b5af0d6d3e65165"
[[package]]
name = "stats"
version = "0.1.0"
dependencies = [
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.3.0" version = "0.3.0"
@ -2334,7 +2345,7 @@ name = "tokio-core"
version = "0.1.2" version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"futures 0.1.6 (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)",
"mio 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2346,7 +2357,7 @@ name = "tokio-proto"
version = "0.1.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"futures 0.1.6 (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)",
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
"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)",
@ -2362,7 +2373,7 @@ name = "tokio-service"
version = "0.1.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -2595,7 +2606,7 @@ dependencies = [
"checksum ethabi 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d8f6cc4c1acd005f48e1d17b06a461adac8fb6eeeb331fbf19a0e656fba91cd" "checksum ethabi 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d8f6cc4c1acd005f48e1d17b06a461adac8fb6eeeb331fbf19a0e656fba91cd"
"checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa" "checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa"
"checksum flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "3eeb481e957304178d2e782f2da1257f1434dfecbae883bafb61ada2a9fea3bb" "checksum flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "3eeb481e957304178d2e782f2da1257f1434dfecbae883bafb61ada2a9fea3bb"
"checksum futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bad0a2ac64b227fdc10c254051ae5af542cf19c9328704fd4092f7914196897" "checksum futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "c1913eb7083840b1bbcbf9631b7fda55eaf35fe7ead13cca034e8946f9e2bc41"
"checksum futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bb982bb25cd8fa5da6a8eb3a460354c984ff1113da82bcb4f0b0862b5795db82" "checksum futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bb982bb25cd8fa5da6a8eb3a460354c984ff1113da82bcb4f0b0862b5795db82"
"checksum gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "91ecd03771effb0c968fd6950b37e89476a578aaf1c70297d8e92b6516ec3312" "checksum gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "91ecd03771effb0c968fd6950b37e89476a578aaf1c70297d8e92b6516ec3312"
"checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518" "checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518"
@ -2607,7 +2618,7 @@ dependencies = [
"checksum httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46534074dbb80b070d60a5cb8ecadd8963a00a438ae1a95268850a7ef73b67ae" "checksum httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46534074dbb80b070d60a5cb8ecadd8963a00a438ae1a95268850a7ef73b67ae"
"checksum hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)" = "<none>" "checksum hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)" = "<none>"
"checksum hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)" = "220407e5a263f110ec30a071787c9535918fdfc97def5680c90013c3f30c38c1" "checksum hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)" = "220407e5a263f110ec30a071787c9535918fdfc97def5680c90013c3f30c38c1"
"checksum hyper 0.9.14 (registry+https://github.com/rust-lang/crates.io-index)" = "bcb3fc65554155980167fb821d05c7c66177f92464976c0b676a19d9e03387a7" "checksum hyper 0.9.18 (registry+https://github.com/rust-lang/crates.io-index)" = "1b9bf64f730d6ee4b0528a5f0a316363da9d8104318731509d4ccc86248f82b3"
"checksum hyper-native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "afe68f772f0497a7205e751626bb8e1718568b58534b6108c73a74ef80483409" "checksum hyper-native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "afe68f772f0497a7205e751626bb8e1718568b58534b6108c73a74ef80483409"
"checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11" "checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11"
"checksum igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c8c12b1795b8b168f577c45fa10379b3814dcb11b7ab702406001f0d63f40484" "checksum igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c8c12b1795b8b168f577c45fa10379b3814dcb11b7ab702406001f0d63f40484"

View File

@ -32,7 +32,7 @@ Be sure to check out [our wiki][wiki-url] for more information.
Parity's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing Parity using the sophisticated and Parity's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing Parity using the sophisticated and
cutting-edge Rust programming language. Parity is licensed under the GPLv3, and can be used for all your Ethereum needs. cutting-edge Rust programming language. Parity is licensed under the GPLv3, and can be used for all your Ethereum needs.
Parity comes with a built-in wallet. To access [Parity Wallet](http://127.0.0.1:8080/) this simply go to http://127.0.0.1:8080/. It Parity comes with a built-in wallet. To access [Parity Wallet](http://127.0.0.1:8080/) simply go to http://127.0.0.1:8080/. It
includes various functionality allowing you to: includes various functionality allowing you to:
- create and manage your Ethereum accounts; - create and manage your Ethereum accounts;
- manage your Ether and any Ethereum tokens; - manage your Ether and any Ethereum tokens;

View File

@ -43,6 +43,7 @@ rlp = { path = "../util/rlp" }
ethcore-stratum = { path = "../stratum" } ethcore-stratum = { path = "../stratum" }
ethcore-bloom-journal = { path = "../util/bloom" } ethcore-bloom-journal = { path = "../util/bloom" }
hardware-wallet = { path = "../hw" } hardware-wallet = { path = "../hw" }
stats = { path = "../util/stats" }
[dependencies.hyper] [dependencies.hyper]
git = "https://github.com/ethcore/hyper" git = "https://github.com/ethcore/hyper"

View File

@ -23,6 +23,7 @@ smallvec = "0.3.1"
futures = "0.1" futures = "0.1"
rand = "0.3" rand = "0.3"
itertools = "0.5" itertools = "0.5"
stats = { path = "../../util/stats" }
[features] [features]
default = [] default = []

175
ethcore/light/src/cache.rs Normal file
View File

@ -0,0 +1,175 @@
// 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/>.
//! Cache for data fetched from the network.
//!
//! Stores ancient block headers, bodies, receipts, and total difficulties.
//! Furthermore, stores a "gas price corpus" of relative recency, which is a sorted
//! vector of all gas prices from a recent range of blocks.
use ethcore::encoded;
use ethcore::header::BlockNumber;
use ethcore::receipt::Receipt;
use stats::Corpus;
use time::{SteadyTime, Duration};
use util::{U256, H256};
use util::cache::MemoryLruCache;
/// Configuration for how much data to cache.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CacheSizes {
/// Maximum size, in bytes, of cached headers.
pub headers: usize,
/// Maximum size, in bytes, of cached canonical hashes.
pub canon_hashes: usize,
/// Maximum size, in bytes, of cached block bodies.
pub bodies: usize,
/// Maximum size, in bytes, of cached block receipts.
pub receipts: usize,
/// Maximum size, in bytes, of cached chain score for the block.
pub chain_score: usize,
}
impl Default for CacheSizes {
fn default() -> Self {
const MB: usize = 1024 * 1024;
CacheSizes {
headers: 10 * MB,
canon_hashes: 3 * MB,
bodies: 20 * MB,
receipts: 10 * MB,
chain_score: 7 * MB,
}
}
}
/// The light client data cache.
///
/// Note that almost all getter methods take `&mut self` due to the necessity to update
/// the underlying LRU-caches on read.
pub struct Cache {
headers: MemoryLruCache<H256, encoded::Header>,
canon_hashes: MemoryLruCache<BlockNumber, H256>,
bodies: MemoryLruCache<H256, encoded::Body>,
receipts: MemoryLruCache<H256, Vec<Receipt>>,
chain_score: MemoryLruCache<H256, U256>,
corpus: Option<(Corpus<U256>, SteadyTime)>,
corpus_expiration: Duration,
}
impl Cache {
/// Create a new data cache with the given sizes and gas price corpus expiration time.
pub fn new(sizes: CacheSizes, corpus_expiration: Duration) -> Self {
Cache {
headers: MemoryLruCache::new(sizes.headers),
canon_hashes: MemoryLruCache::new(sizes.canon_hashes),
bodies: MemoryLruCache::new(sizes.bodies),
receipts: MemoryLruCache::new(sizes.receipts),
chain_score: MemoryLruCache::new(sizes.chain_score),
corpus: None,
corpus_expiration: corpus_expiration,
}
}
/// Query header by hash.
pub fn block_header(&mut self, hash: &H256) -> Option<encoded::Header> {
self.headers.get_mut(hash).map(|x| x.clone())
}
/// Query hash by number.
pub fn block_hash(&mut self, num: &BlockNumber) -> Option<H256> {
self.canon_hashes.get_mut(num).map(|x| x.clone())
}
/// Query block body by block hash.
pub fn block_body(&mut self, hash: &H256) -> Option<encoded::Body> {
self.bodies.get_mut(hash).map(|x| x.clone())
}
/// Query block receipts by block hash.
pub fn block_receipts(&mut self, hash: &H256) -> Option<Vec<Receipt>> {
self.receipts.get_mut(hash).map(|x| x.clone())
}
/// Query chain score by block hash.
pub fn chain_score(&mut self, hash: &H256) -> Option<U256> {
self.chain_score.get_mut(hash).map(|x| x.clone())
}
/// Cache the given header.
pub fn insert_block_header(&mut self, hash: H256, hdr: encoded::Header) {
self.headers.insert(hash, hdr);
}
/// Cache the given canonical block hash.
pub fn insert_block_hash(&mut self, num: BlockNumber, hash: H256) {
self.canon_hashes.insert(num, hash);
}
/// Cache the given block body.
pub fn insert_block_body(&mut self, hash: H256, body: encoded::Body) {
self.bodies.insert(hash, body);
}
/// Cache the given block receipts.
pub fn insert_block_receipts(&mut self, hash: H256, receipts: Vec<Receipt>) {
self.receipts.insert(hash, receipts);
}
/// Cache the given chain scoring.
pub fn insert_chain_score(&mut self, hash: H256, score: U256) {
self.chain_score.insert(hash, score);
}
/// Get gas price corpus, if recent enough.
pub fn gas_price_corpus(&self) -> Option<Corpus<U256>> {
let now = SteadyTime::now();
self.corpus.as_ref().and_then(|&(ref corpus, ref tm)| {
if *tm + self.corpus_expiration >= now {
Some(corpus.clone())
} else {
None
}
})
}
/// Set the cached gas price corpus.
pub fn set_gas_price_corpus(&mut self, corpus: Corpus<U256>) {
self.corpus = Some((corpus, SteadyTime::now()))
}
}
#[cfg(test)]
mod tests {
use super::Cache;
use time::Duration;
#[test]
fn corpus_inaccessible() {
let mut cache = Cache::new(Default::default(), Duration::hours(5));
cache.set_gas_price_corpus(vec![].into());
assert_eq!(cache.gas_price_corpus(), Some(vec![].into()));
{
let corpus_time = &mut cache.corpus.as_mut().unwrap().1;
*corpus_time = *corpus_time - Duration::hours(6);
}
assert!(cache.gas_price_corpus().is_none());
}
}

View File

@ -241,6 +241,14 @@ impl HeaderChain {
self.block_header(BlockId::Latest).expect("Header for best block always stored; qed") self.block_header(BlockId::Latest).expect("Header for best block always stored; qed")
} }
/// Get an iterator over a block and its ancestry.
pub fn ancestry_iter(&self, start: BlockId) -> AncestryIter {
AncestryIter {
next: self.block_header(start),
chain: self,
}
}
/// Get the nth CHT root, if it's been computed. /// Get the nth CHT root, if it's been computed.
/// ///
/// CHT root 0 is from block `1..2048`. /// CHT root 0 is from block `1..2048`.
@ -295,6 +303,25 @@ impl HeapSizeOf for HeaderChain {
} }
} }
/// Iterator over a block's ancestry.
pub struct AncestryIter<'a> {
next: Option<encoded::Header>,
chain: &'a HeaderChain,
}
impl<'a> Iterator for AncestryIter<'a> {
type Item = encoded::Header;
fn next(&mut self) -> Option<encoded::Header> {
let next = self.next.take();
if let Some(p_hash) = next.as_ref().map(|hdr| hdr.parent_hash()) {
self.next = self.chain.block_header(BlockId::Hash(p_hash));
}
next
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::HeaderChain; use super::HeaderChain;

View File

@ -20,7 +20,7 @@ use std::sync::Arc;
use ethcore::block_import_error::BlockImportError; use ethcore::block_import_error::BlockImportError;
use ethcore::block_status::BlockStatus; use ethcore::block_status::BlockStatus;
use ethcore::client::ClientReport; use ethcore::client::{ClientReport, EnvInfo};
use ethcore::engines::Engine; use ethcore::engines::Engine;
use ethcore::ids::BlockId; use ethcore::ids::BlockId;
use ethcore::header::Header; use ethcore::header::Header;
@ -33,7 +33,7 @@ use io::IoChannel;
use util::{Bytes, DBValue, H256, Mutex, RwLock}; use util::{Bytes, DBValue, H256, Mutex, RwLock};
use self::header_chain::HeaderChain; use self::header_chain::{AncestryIter, HeaderChain};
pub use self::service::Service; pub use self::service::Service;
@ -62,6 +62,12 @@ pub trait LightChainClient: Send + Sync {
/// Get the best block header. /// Get the best block header.
fn best_block_header(&self) -> encoded::Header; fn best_block_header(&self) -> encoded::Header;
/// Get an iterator over a block and its ancestry.
fn ancestry_iter<'a>(&'a self, start: BlockId) -> Box<Iterator<Item=encoded::Header> + 'a>;
/// Get the signing network ID.
fn signing_network_id(&self) -> Option<u64>;
/// Query whether a block is known. /// Query whether a block is known.
fn is_known(&self, hash: &H256) -> bool; fn is_known(&self, hash: &H256) -> bool;
@ -164,6 +170,16 @@ impl Client {
self.chain.best_header() self.chain.best_header()
} }
/// Get an iterator over a block and its ancestry.
pub fn ancestry_iter(&self, start: BlockId) -> AncestryIter {
self.chain.ancestry_iter(start)
}
/// Get the signing network id.
pub fn signing_network_id(&self) -> Option<u64> {
self.engine.signing_network_id(&self.latest_env_info())
}
/// Flush the header queue. /// Flush the header queue.
pub fn flush_queue(&self) { pub fn flush_queue(&self) {
self.queue.flush() self.queue.flush()
@ -217,6 +233,33 @@ impl Client {
pub fn engine(&self) -> &Engine { pub fn engine(&self) -> &Engine {
&*self.engine &*self.engine
} }
fn latest_env_info(&self) -> EnvInfo {
let header = self.best_block_header();
EnvInfo {
number: header.number(),
author: header.author(),
timestamp: header.timestamp(),
difficulty: header.difficulty(),
last_hashes: self.build_last_hashes(header.hash()),
gas_used: Default::default(),
gas_limit: header.gas_limit(),
}
}
fn build_last_hashes(&self, mut parent_hash: H256) -> Arc<Vec<H256>> {
let mut v = Vec::with_capacity(256);
for _ in 0..255 {
v.push(parent_hash);
match self.block_header(BlockId::Hash(parent_hash)) {
Some(header) => parent_hash = header.hash(),
None => break,
}
}
Arc::new(v)
}
} }
impl LightChainClient for Client { impl LightChainClient for Client {
@ -234,6 +277,14 @@ impl LightChainClient for Client {
Client::best_block_header(self) Client::best_block_header(self)
} }
fn ancestry_iter<'a>(&'a self, start: BlockId) -> Box<Iterator<Item=encoded::Header> + 'a> {
Box::new(Client::ancestry_iter(self, start))
}
fn signing_network_id(&self) -> Option<u64> {
Client::signing_network_id(self)
}
fn is_known(&self, hash: &H256) -> bool { fn is_known(&self, hash: &H256) -> bool {
self.status(hash) == BlockStatus::InChain self.status(hash) == BlockStatus::InChain
} }

View File

@ -37,6 +37,7 @@ pub mod cht;
pub mod net; pub mod net;
pub mod on_demand; pub mod on_demand;
pub mod transaction_queue; pub mod transaction_queue;
pub mod cache;
#[cfg(not(feature = "ipc"))] #[cfg(not(feature = "ipc"))]
pub mod provider; pub mod provider;
@ -71,6 +72,7 @@ extern crate time;
extern crate futures; extern crate futures;
extern crate rand; extern crate rand;
extern crate itertools; extern crate itertools;
extern crate stats;
#[cfg(feature = "ipc")] #[cfg(feature = "ipc")]
extern crate ethcore_ipc as ipc; extern crate ethcore_ipc as ipc;

View File

@ -329,6 +329,16 @@ impl LightProtocol {
.map(|peer| peer.lock().status.clone()) .map(|peer| peer.lock().status.clone())
} }
/// Get number of (connected, active) peers.
pub fn peer_count(&self) -> (usize, usize) {
let num_pending = self.pending_peers.read().len();
let peers = self.peers.read();
(
num_pending + peers.len(),
peers.values().filter(|p| !p.lock().pending_requests.is_empty()).count(),
)
}
/// Check the maximum amount of requests of a specific type /// Check the maximum amount of requests of a specific type
/// which a peer would be able to serve. Returns zero if the /// which a peer would be able to serve. Returns zero if the
/// peer is unknown or has no credit parameters. /// peer is unknown or has no credit parameters.

View File

@ -111,6 +111,14 @@ impl RequestSet {
pub fn collect_ids<F>(&self) -> F where F: FromIterator<ReqId> { pub fn collect_ids<F>(&self) -> F where F: FromIterator<ReqId> {
self.ids.keys().cloned().collect() self.ids.keys().cloned().collect()
} }
/// Number of requests in the set.
pub fn len(&self) -> usize {
self.ids.len()
}
/// Whether the set is empty.
pub fn is_empty(&self) -> bool { self.len() == 0 }
} }
#[cfg(test)] #[cfg(test)]

View File

@ -19,6 +19,7 @@
//! will take the raw data received here and extract meaningful results from it. //! will take the raw data received here and extract meaningful results from it.
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc;
use ethcore::basic_account::BasicAccount; use ethcore::basic_account::BasicAccount;
use ethcore::encoded; use ethcore::encoded;
@ -30,10 +31,11 @@ use futures::{Async, Poll, Future};
use futures::sync::oneshot::{self, Sender, Receiver}; use futures::sync::oneshot::{self, Sender, Receiver};
use network::PeerId; use network::PeerId;
use rlp::{RlpStream, Stream}; use rlp::{RlpStream, Stream};
use util::{Bytes, DBValue, RwLock, U256}; use util::{Bytes, DBValue, RwLock, Mutex, U256};
use util::sha3::{SHA3_NULL_RLP, SHA3_EMPTY_LIST_RLP}; use util::sha3::{SHA3_NULL_RLP, SHA3_EMPTY_LIST_RLP};
use net::{Handler, Status, Capabilities, Announcement, EventContext, BasicContext, ReqId}; use net::{Handler, Status, Capabilities, Announcement, EventContext, BasicContext, ReqId};
use cache::Cache;
use types::les_request::{self as les_request, Request as LesRequest}; use types::les_request::{self as les_request, Request as LesRequest};
pub mod request; pub mod request;
@ -44,9 +46,16 @@ struct Peer {
capabilities: Capabilities, capabilities: Capabilities,
} }
// Which portions of a CHT proof should be sent.
enum ChtProofSender {
Both(Sender<(encoded::Header, U256)>),
Header(Sender<encoded::Header>),
ChainScore(Sender<U256>),
}
// Attempted request info and sender to put received value. // Attempted request info and sender to put received value.
enum Pending { enum Pending {
HeaderByNumber(request::HeaderByNumber, Sender<(encoded::Header, U256)>), // num + CHT root HeaderByNumber(request::HeaderByNumber, ChtProofSender),
HeaderByHash(request::HeaderByHash, Sender<encoded::Header>), HeaderByHash(request::HeaderByHash, Sender<encoded::Header>),
Block(request::Body, Sender<encoded::Block>), Block(request::Body, Sender<encoded::Block>),
BlockReceipts(request::BlockReceipts, Sender<Vec<Receipt>>), BlockReceipts(request::BlockReceipts, Sender<Vec<Receipt>>),
@ -61,30 +70,77 @@ enum Pending {
pub struct OnDemand { pub struct OnDemand {
peers: RwLock<HashMap<PeerId, Peer>>, peers: RwLock<HashMap<PeerId, Peer>>,
pending_requests: RwLock<HashMap<ReqId, Pending>>, pending_requests: RwLock<HashMap<ReqId, Pending>>,
cache: Arc<Mutex<Cache>>,
orphaned_requests: RwLock<Vec<Pending>>, orphaned_requests: RwLock<Vec<Pending>>,
} }
impl Default for OnDemand { impl OnDemand {
fn default() -> Self { /// Create a new `OnDemand` service with the given cache.
pub fn new(cache: Arc<Mutex<Cache>>) -> Self {
OnDemand { OnDemand {
peers: RwLock::new(HashMap::new()), peers: RwLock::new(HashMap::new()),
pending_requests: RwLock::new(HashMap::new()), pending_requests: RwLock::new(HashMap::new()),
cache: cache,
orphaned_requests: RwLock::new(Vec::new()), orphaned_requests: RwLock::new(Vec::new()),
} }
} }
}
impl OnDemand {
/// Request a header by block number and CHT root hash. /// Request a header by block number and CHT root hash.
/// Returns the header and the total difficulty. /// Returns the header.
pub fn header_by_number(&self, ctx: &BasicContext, req: request::HeaderByNumber) -> Receiver<(encoded::Header, U256)> { pub fn header_by_number(&self, ctx: &BasicContext, req: request::HeaderByNumber) -> Receiver<encoded::Header> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.dispatch_header_by_number(ctx, req, sender); let cached = {
let mut cache = self.cache.lock();
cache.block_hash(&req.num()).and_then(|hash| cache.block_header(&hash))
};
match cached {
Some(hdr) => sender.complete(hdr),
None => self.dispatch_header_by_number(ctx, req, ChtProofSender::Header(sender)),
}
receiver
}
/// Request a canonical block's chain score.
/// Returns the chain score.
pub fn chain_score_by_number(&self, ctx: &BasicContext, req: request::HeaderByNumber) -> Receiver<U256> {
let (sender, receiver) = oneshot::channel();
let cached = {
let mut cache = self.cache.lock();
cache.block_hash(&req.num()).and_then(|hash| cache.chain_score(&hash))
};
match cached {
Some(score) => sender.complete(score),
None => self.dispatch_header_by_number(ctx, req, ChtProofSender::ChainScore(sender)),
}
receiver
}
/// Request a canonical block's chain score.
/// Returns the header and chain score.
pub fn header_and_score_by_number(&self, ctx: &BasicContext, req: request::HeaderByNumber) -> Receiver<(encoded::Header, U256)> {
let (sender, receiver) = oneshot::channel();
let cached = {
let mut cache = self.cache.lock();
let hash = cache.block_hash(&req.num());
(
hash.clone().and_then(|hash| cache.block_header(&hash)),
hash.and_then(|hash| cache.chain_score(&hash)),
)
};
match cached {
(Some(hdr), Some(score)) => sender.complete((hdr, score)),
_ => self.dispatch_header_by_number(ctx, req, ChtProofSender::Both(sender)),
}
receiver receiver
} }
// dispatch the request, completing the request if no peers available. // dispatch the request, completing the request if no peers available.
fn dispatch_header_by_number(&self, ctx: &BasicContext, req: request::HeaderByNumber, sender: Sender<(encoded::Header, U256)>) { fn dispatch_header_by_number(&self, ctx: &BasicContext, req: request::HeaderByNumber, sender: ChtProofSender) {
let num = req.num(); let num = req.num();
let cht_num = req.cht_num(); let cht_num = req.cht_num();
@ -126,7 +182,10 @@ impl OnDemand {
/// it as easily. /// it as easily.
pub fn header_by_hash(&self, ctx: &BasicContext, req: request::HeaderByHash) -> Receiver<encoded::Header> { pub fn header_by_hash(&self, ctx: &BasicContext, req: request::HeaderByHash) -> Receiver<encoded::Header> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.dispatch_header_by_hash(ctx, req, sender); match self.cache.lock().block_header(&req.0) {
Some(hdr) => sender.complete(hdr),
None => self.dispatch_header_by_hash(ctx, req, sender),
}
receiver receiver
} }
@ -184,7 +243,16 @@ impl OnDemand {
sender.complete(encoded::Block::new(stream.out())) sender.complete(encoded::Block::new(stream.out()))
} else { } else {
self.dispatch_block(ctx, req, sender); match self.cache.lock().block_body(&req.hash) {
Some(body) => {
let mut stream = RlpStream::new_list(3);
stream.append_raw(&req.header.into_inner(), 1);
stream.append_raw(&body.into_inner(), 2);
sender.complete(encoded::Block::new(stream.out()));
}
None => self.dispatch_block(ctx, req, sender),
}
} }
receiver receiver
} }
@ -227,7 +295,10 @@ impl OnDemand {
if req.0.receipts_root() == SHA3_NULL_RLP { if req.0.receipts_root() == SHA3_NULL_RLP {
sender.complete(Vec::new()) sender.complete(Vec::new())
} else { } else {
self.dispatch_block_receipts(ctx, req, sender); match self.cache.lock().block_receipts(&req.0.hash()) {
Some(receipts) => sender.complete(receipts),
None => self.dispatch_block_receipts(ctx, req, sender),
}
} }
receiver receiver
@ -425,8 +496,15 @@ impl OnDemand {
for orphaned in to_dispatch { for orphaned in to_dispatch {
match orphaned { match orphaned {
Pending::HeaderByNumber(req, mut sender) => Pending::HeaderByNumber(req, mut sender) => {
if !check_hangup(&mut sender) { self.dispatch_header_by_number(ctx, req, sender) }, let hangup = match sender {
ChtProofSender::Both(ref mut s) => check_hangup(s),
ChtProofSender::Header(ref mut s) => check_hangup(s),
ChtProofSender::ChainScore(ref mut s) => check_hangup(s),
};
if !hangup { self.dispatch_header_by_number(ctx, req, sender) }
}
Pending::HeaderByHash(req, mut sender) => Pending::HeaderByHash(req, mut sender) =>
if !check_hangup(&mut sender) { self.dispatch_header_by_hash(ctx, req, sender) }, if !check_hangup(&mut sender) { self.dispatch_header_by_hash(ctx, req, sender) },
Pending::Block(req, mut sender) => Pending::Block(req, mut sender) =>
@ -488,8 +566,19 @@ impl Handler for OnDemand {
Pending::HeaderByNumber(req, sender) => { Pending::HeaderByNumber(req, sender) => {
if let Some(&(ref header, ref proof)) = proofs.get(0) { if let Some(&(ref header, ref proof)) = proofs.get(0) {
match req.check_response(header, proof) { match req.check_response(header, proof) {
Ok(header) => { Ok((header, score)) => {
sender.complete(header); let mut cache = self.cache.lock();
let hash = header.hash();
cache.insert_block_header(hash, header.clone());
cache.insert_block_hash(header.number(), hash);
cache.insert_chain_score(hash, score);
match sender {
ChtProofSender::Both(sender) => sender.complete((header, score)),
ChtProofSender::Header(sender) => sender.complete(header),
ChtProofSender::ChainScore(sender) => sender.complete(score),
}
return return
} }
Err(e) => { Err(e) => {
@ -517,6 +606,7 @@ impl Handler for OnDemand {
if let Some(ref header) = headers.get(0) { if let Some(ref header) = headers.get(0) {
match req.check_response(header) { match req.check_response(header) {
Ok(header) => { Ok(header) => {
self.cache.lock().insert_block_header(req.0, header.clone());
sender.complete(header); sender.complete(header);
return return
} }
@ -542,9 +632,11 @@ impl Handler for OnDemand {
match req { match req {
Pending::Block(req, sender) => { Pending::Block(req, sender) => {
if let Some(ref block) = bodies.get(0) { if let Some(ref body) = bodies.get(0) {
match req.check_response(block) { match req.check_response(body) {
Ok(block) => { Ok(block) => {
let body = encoded::Body::new(body.to_vec());
self.cache.lock().insert_block_body(req.hash, body);
sender.complete(block); sender.complete(block);
return return
} }
@ -573,6 +665,8 @@ impl Handler for OnDemand {
if let Some(ref receipts) = receipts.get(0) { if let Some(ref receipts) = receipts.get(0) {
match req.check_response(receipts) { match req.check_response(receipts) {
Ok(receipts) => { Ok(receipts) => {
let hash = req.0.hash();
self.cache.lock().insert_block_receipts(hash, receipts.clone());
sender.complete(receipts); sender.complete(receipts);
return return
} }
@ -683,10 +777,16 @@ impl Handler for OnDemand {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use std::sync::Arc;
use cache::Cache;
use net::{Announcement, BasicContext, ReqId, Error as LesError}; use net::{Announcement, BasicContext, ReqId, Error as LesError};
use request::{Request as LesRequest, Kind as LesRequestKind}; use request::{Request as LesRequest, Kind as LesRequestKind};
use network::{PeerId, NodeId}; use network::{PeerId, NodeId};
use util::H256; use time::Duration;
use util::{H256, Mutex};
struct FakeContext; struct FakeContext;
@ -703,7 +803,8 @@ mod tests {
#[test] #[test]
fn detects_hangup() { fn detects_hangup() {
let on_demand = OnDemand::default(); let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::hours(6))));
let on_demand = OnDemand::new(cache);
let result = on_demand.header_by_hash(&FakeContext, request::HeaderByHash(H256::default())); let result = on_demand.header_by_hash(&FakeContext, request::HeaderByHash(H256::default()));
assert!(on_demand.orphaned_requests.read().len() == 1); assert!(on_demand.orphaned_requests.read().len() == 1);

View File

@ -245,6 +245,31 @@ impl TransactionQueue {
.collect() .collect()
} }
/// Get all transactions not ready to be propagated.
/// `best_block_number` and `best_block_timestamp` are used to filter out conditionally
/// propagated transactions.
///
/// Returned transactions are batched by sender, in order of ascending nonce.
pub fn future_transactions(&self, best_block_number: u64, best_block_timestamp: u64) -> Vec<PendingTransaction> {
self.by_account.values()
.flat_map(|acct_txs| {
acct_txs.current.iter().skip_while(|tx| match tx.condition {
None => true,
Some(Condition::Number(blk_num)) => blk_num <= best_block_number,
Some(Condition::Timestamp(time)) => time <= best_block_timestamp,
}).chain(acct_txs.future.values()).map(|info| info.hash)
})
.filter_map(|hash| match self.by_hash.get(&hash) {
Some(tx) => Some(tx.clone()),
None => {
warn!(target: "txqueue", "Inconsistency detected between `by_hash` and `by_account`: {} not stored.",
hash);
None
}
})
.collect()
}
/// Addresses for which we store transactions. /// Addresses for which we store transactions.
pub fn queued_senders(&self) -> Vec<Address> { pub fn queued_senders(&self) -> Vec<Address> {
self.by_account.keys().cloned().collect() self.by_account.keys().cloned().collect()
@ -471,4 +496,22 @@ mod tests {
assert!(txq.transaction(&hash).is_none()); assert!(txq.transaction(&hash).is_none());
} }
#[test]
fn future_transactions() {
let sender = Address::default();
let mut txq = TransactionQueue::default();
for i in (0..1).chain(3..10) {
let mut tx = Transaction::default();
tx.nonce = i.into();
let tx = tx.fake_sign(sender);
txq.import(tx.into()).unwrap();
}
assert_eq!(txq.future_transactions(0, 0).len(), 7);
assert_eq!(txq.next_nonce(&sender).unwrap(), 1.into());
}
} }

View File

@ -964,13 +964,13 @@ impl BlockChainClient for Client {
{ {
while upper - lower > 1.into() { while upper - lower > 1.into() {
let mid = (lower + upper) / 2.into(); let mid = (lower + upper) / 2.into();
trace!(target: "binary_chop", "{} .. {} .. {}", lower, mid, upper); trace!(target: "estimate_gas", "{} .. {} .. {}", lower, mid, upper);
let c = cond(mid)?; let c = cond(mid)?;
match c { match c {
true => upper = mid, true => upper = mid,
false => lower = mid, false => lower = mid,
}; };
trace!(target: "binary_chop", "{} => {} .. {}", c, lower, upper); trace!(target: "estimate_gas", "{} => {} .. {}", c, lower, upper);
} }
Ok(upper) Ok(upper)
} }

View File

@ -17,7 +17,6 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use util::{U256, Address, H256, H2048, Bytes, Itertools}; use util::{U256, Address, H256, H2048, Bytes, Itertools};
use util::hashdb::DBValue; use util::hashdb::DBValue;
use util::stats::Histogram;
use blockchain::TreeRoute; use blockchain::TreeRoute;
use verification::queue::QueueInfo as BlockQueueInfo; use verification::queue::QueueInfo as BlockQueueInfo;
use block::{OpenBlock, SealedBlock}; use block::{OpenBlock, SealedBlock};
@ -213,38 +212,24 @@ pub trait BlockChainClient : Sync + Send {
fn ready_transactions(&self) -> Vec<PendingTransaction>; fn ready_transactions(&self) -> Vec<PendingTransaction>;
/// Sorted list of transaction gas prices from at least last sample_size blocks. /// Sorted list of transaction gas prices from at least last sample_size blocks.
fn gas_price_corpus(&self, sample_size: usize) -> Vec<U256> { fn gas_price_corpus(&self, sample_size: usize) -> ::stats::Corpus<U256> {
let mut h = self.chain_info().best_block_hash; let mut h = self.chain_info().best_block_hash;
let mut corpus = Vec::new(); let mut corpus = Vec::new();
while corpus.is_empty() { while corpus.is_empty() {
for _ in 0..sample_size { for _ in 0..sample_size {
let block = self.block(BlockId::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed"); let block = match self.block(BlockId::Hash(h)) {
let header = block.header_view(); Some(block) => block,
if header.number() == 0 { None => return corpus.into(),
corpus.sort(); };
return corpus;
if block.number() == 0 {
return corpus.into();
} }
block.transaction_views().iter().foreach(|t| corpus.push(t.gas_price())); block.transaction_views().iter().foreach(|t| corpus.push(t.gas_price()));
h = header.parent_hash().clone(); h = block.parent_hash().clone();
} }
} }
corpus.sort(); corpus.into()
corpus
}
/// Calculate median gas price from recent blocks if they have any transactions.
fn gas_price_median(&self, sample_size: usize) -> Option<U256> {
let corpus = self.gas_price_corpus(sample_size);
corpus.get(corpus.len() / 2).cloned()
}
/// Get the gas price distribution based on recent blocks if they have any transactions.
fn gas_price_histogram(&self, sample_size: usize, bucket_number: usize) -> Option<Histogram> {
let raw_corpus = self.gas_price_corpus(sample_size);
let raw_len = raw_corpus.len();
// Throw out outliers.
let (corpus, _) = raw_corpus.split_at(raw_len - raw_len / 40);
Histogram::new(corpus, bucket_number)
} }
/// Get the preferred network ID to sign on /// Get the preferred network ID to sign on

View File

@ -151,12 +151,12 @@ impl Tendermint {
fn generate_message(&self, block_hash: Option<BlockHash>) -> Option<Bytes> { fn generate_message(&self, block_hash: Option<BlockHash>) -> Option<Bytes> {
let h = self.height.load(AtomicOrdering::SeqCst); let h = self.height.load(AtomicOrdering::SeqCst);
let r = self.view.load(AtomicOrdering::SeqCst); let r = self.view.load(AtomicOrdering::SeqCst);
let s = self.step.read(); let s = *self.step.read();
let vote_info = message_info_rlp(&VoteStep::new(h, r, *s), block_hash); let vote_info = message_info_rlp(&VoteStep::new(h, r, s), block_hash);
match self.signer.sign(vote_info.sha3()).map(Into::into) { match self.signer.sign(vote_info.sha3()).map(Into::into) {
Ok(signature) => { Ok(signature) => {
let message_rlp = message_full_rlp(&signature, &vote_info); let message_rlp = message_full_rlp(&signature, &vote_info);
let message = ConsensusMessage::new(signature, h, r, *s, block_hash); let message = ConsensusMessage::new(signature, h, r, s, block_hash);
let validator = self.signer.address(); let validator = self.signer.address();
self.votes.vote(message.clone(), &validator); self.votes.vote(message.clone(), &validator);
debug!(target: "engine", "Generated {:?} as {}.", message, validator); debug!(target: "engine", "Generated {:?} as {}.", message, validator);

View File

@ -79,6 +79,14 @@ pub struct EthashParams {
pub ecip1010_continue_transition: u64, pub ecip1010_continue_transition: u64,
/// Maximum amount of code that can be deploying into a contract. /// Maximum amount of code that can be deploying into a contract.
pub max_code_size: u64, pub max_code_size: u64,
/// Number of first block where the max gas limit becomes effective.
pub max_gas_limit_transition: u64,
/// Maximum valid block gas limit,
pub max_gas_limit: U256,
/// Number of first block where the minimum gas price becomes effective.
pub min_gas_price_transition: u64,
/// Do not alow transactions with lower gas price.
pub min_gas_price: U256,
} }
impl From<ethjson::spec::EthashParams> for EthashParams { impl From<ethjson::spec::EthashParams> for EthashParams {
@ -106,6 +114,10 @@ impl From<ethjson::spec::EthashParams> for EthashParams {
ecip1010_pause_transition: p.ecip1010_pause_transition.map_or(u64::max_value(), Into::into), ecip1010_pause_transition: p.ecip1010_pause_transition.map_or(u64::max_value(), Into::into),
ecip1010_continue_transition: p.ecip1010_continue_transition.map_or(u64::max_value(), Into::into), ecip1010_continue_transition: p.ecip1010_continue_transition.map_or(u64::max_value(), Into::into),
max_code_size: p.max_code_size.map_or(u64::max_value(), Into::into), max_code_size: p.max_code_size.map_or(u64::max_value(), Into::into),
max_gas_limit_transition: p.max_gas_limit_transition.map_or(u64::max_value(), Into::into),
max_gas_limit: p.max_gas_limit.map_or(U256::max_value(), Into::into),
min_gas_price_transition: p.min_gas_price_transition.map_or(u64::max_value(), Into::into),
min_gas_price: p.min_gas_price.map_or(U256::zero(), Into::into),
} }
} }
} }
@ -174,8 +186,12 @@ impl Engine for Ethash {
} }
} }
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, gas_ceil_target: U256) { fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, mut gas_ceil_target: U256) {
let difficulty = self.calculate_difficulty(header, parent); let difficulty = self.calculate_difficulty(header, parent);
if header.number() >= self.ethash_params.max_gas_limit_transition && gas_ceil_target > self.ethash_params.max_gas_limit {
warn!("Gas limit target is limited to {}", self.ethash_params.max_gas_limit);
gas_ceil_target = self.ethash_params.max_gas_limit;
}
let gas_limit = { let gas_limit = {
let gas_limit = parent.gas_limit().clone(); let gas_limit = parent.gas_limit().clone();
let bound_divisor = self.ethash_params.gas_limit_bound_divisor; let bound_divisor = self.ethash_params.gas_limit_bound_divisor;
@ -334,11 +350,15 @@ impl Engine for Ethash {
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty().clone() }))) return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty().clone() })))
} }
let gas_limit_divisor = self.ethash_params.gas_limit_bound_divisor; let gas_limit_divisor = self.ethash_params.gas_limit_bound_divisor;
let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor; let parent_gas_limit = *parent.gas_limit();
let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor; let min_gas = parent_gas_limit - parent_gas_limit / gas_limit_divisor;
let max_gas = parent_gas_limit + parent_gas_limit / gas_limit_divisor;
if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas { if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() }))); return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() })));
} }
if header.number() >= self.ethash_params.max_gas_limit_transition && header.gas_limit() > &self.ethash_params.max_gas_limit && header.gas_limit() > &parent_gas_limit {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(self.ethash_params.max_gas_limit), found: header.gas_limit().clone() })));
}
Ok(()) Ok(())
} }
@ -353,6 +373,10 @@ impl Engine for Ethash {
} }
} }
if header.number() >= self.ethash_params.min_gas_price_transition && t.gas_price < self.ethash_params.min_gas_price {
return Err(TransactionError::InsufficientGasPrice { minimal: self.ethash_params.min_gas_price, got: t.gas_price }.into());
}
Ok(()) Ok(())
} }
} }
@ -857,4 +881,100 @@ mod tests {
ethash.populate_from_parent(&mut header, &parent, U256::from(150_000), U256::from(150_002)); ethash.populate_from_parent(&mut header, &parent, U256::from(150_000), U256::from(150_002));
assert_eq!(*header.gas_limit(), U256::from(150_002)); assert_eq!(*header.gas_limit(), U256::from(150_002));
} }
#[test]
fn difficulty_max_timestamp() {
let spec = new_homestead_test();
let ethparams = get_default_ethash_params();
let ethash = Ethash::new(spec.params, ethparams, BTreeMap::new());
let mut parent_header = Header::default();
parent_header.set_number(1000000);
parent_header.set_difficulty(U256::from_str("b69de81a22b").unwrap());
parent_header.set_timestamp(1455404053);
let mut header = Header::default();
header.set_number(parent_header.number() + 1);
header.set_timestamp(u64::max_value());
let difficulty = ethash.calculate_difficulty(&header, &parent_header);
assert_eq!(U256::from(12543204905719u64), difficulty);
}
#[test]
fn rejects_blocks_over_max_gas_limit() {
let spec = new_homestead_test();
let mut ethparams = get_default_ethash_params();
ethparams.max_gas_limit_transition = 10;
ethparams.max_gas_limit = 100_000.into();
let mut parent_header = Header::default();
parent_header.set_number(1);
parent_header.set_gas_limit(100_000.into());
let mut header = Header::default();
header.set_number(parent_header.number() + 1);
header.set_gas_limit(100_001.into());
header.set_difficulty(ethparams.minimum_difficulty);
let ethash = Ethash::new(spec.params, ethparams, BTreeMap::new());
assert!(ethash.verify_block_family(&header, &parent_header, None).is_ok());
parent_header.set_number(9);
header.set_number(parent_header.number() + 1);
parent_header.set_gas_limit(99_999.into());
header.set_gas_limit(100_000.into());
assert!(ethash.verify_block_family(&header, &parent_header, None).is_ok());
parent_header.set_gas_limit(200_000.into());
header.set_gas_limit(200_000.into());
assert!(ethash.verify_block_family(&header, &parent_header, None).is_ok());
parent_header.set_gas_limit(100_000.into());
header.set_gas_limit(100_001.into());
assert!(ethash.verify_block_family(&header, &parent_header, None).is_err());
parent_header.set_gas_limit(200_000.into());
header.set_gas_limit(200_001.into());
assert!(ethash.verify_block_family(&header, &parent_header, None).is_err());
}
#[test]
fn rejects_transactions_below_min_gas_price() {
use ethkey::{Generator, Random};
use types::transaction::{Transaction, Action};
let spec = new_homestead_test();
let mut ethparams = get_default_ethash_params();
ethparams.min_gas_price_transition = 10;
ethparams.min_gas_price = 100000.into();
let mut header = Header::default();
header.set_number(1);
let keypair = Random.generate().unwrap();
let tx1 = Transaction {
action: Action::Create,
value: U256::zero(),
data: Vec::new(),
gas: 100_000.into(),
gas_price: 100_000.into(),
nonce: U256::zero(),
}.sign(keypair.secret(), None).into();
let tx2 = Transaction {
action: Action::Create,
value: U256::zero(),
data: Vec::new(),
gas: 100_000.into(),
gas_price: 99_999.into(),
nonce: U256::zero(),
}.sign(keypair.secret(), None).into();
let ethash = Ethash::new(spec.params, ethparams, BTreeMap::new());
assert!(ethash.verify_transaction_basic(&tx1, &header).is_ok());
assert!(ethash.verify_transaction_basic(&tx2, &header).is_ok());
header.set_number(10);
assert!(ethash.verify_transaction_basic(&tx1, &header).is_ok());
assert!(ethash.verify_transaction_basic(&tx2, &header).is_err());
}
} }

View File

@ -276,7 +276,7 @@ impl Decodable for Header {
number: r.val_at(8)?, number: r.val_at(8)?,
gas_limit: r.val_at(9)?, gas_limit: r.val_at(9)?,
gas_used: r.val_at(10)?, gas_used: r.val_at(10)?,
timestamp: r.val_at(11)?, timestamp: min(r.val_at::<U256>(11)?, u64::max_value().into()).as_u64(),
extra_data: r.val_at(12)?, extra_data: r.val_at(12)?,
seal: vec![], seal: vec![],
hash: RefCell::new(Some(r.as_raw().sha3())), hash: RefCell::new(Some(r.as_raw().sha3())),

View File

@ -105,6 +105,7 @@ extern crate lru_cache;
extern crate ethcore_stratum; extern crate ethcore_stratum;
extern crate ethabi; extern crate ethabi;
extern crate hardware_wallet; extern crate hardware_wallet;
extern crate stats;
#[macro_use] #[macro_use]
extern crate log; extern crate log;

View File

@ -28,7 +28,6 @@ use miner::Miner;
use rlp::View; use rlp::View;
use spec::Spec; use spec::Spec;
use views::BlockView; use views::BlockView;
use util::stats::Histogram;
use ethkey::{KeyPair, Secret}; use ethkey::{KeyPair, Secret};
use transaction::{PendingTransaction, Transaction, Action, Condition}; use transaction::{PendingTransaction, Transaction, Action, Condition};
use miner::MinerService; use miner::MinerService;
@ -209,11 +208,11 @@ fn can_collect_garbage() {
fn can_generate_gas_price_median() { fn can_generate_gas_price_median() {
let client_result = generate_dummy_client_with_data(3, 1, slice_into![1, 2, 3]); let client_result = generate_dummy_client_with_data(3, 1, slice_into![1, 2, 3]);
let client = client_result.reference(); let client = client_result.reference();
assert_eq!(Some(U256::from(2)), client.gas_price_median(3)); assert_eq!(Some(&U256::from(2)), client.gas_price_corpus(3).median());
let client_result = generate_dummy_client_with_data(4, 1, slice_into![1, 4, 3, 2]); let client_result = generate_dummy_client_with_data(4, 1, slice_into![1, 4, 3, 2]);
let client = client_result.reference(); let client = client_result.reference();
assert_eq!(Some(U256::from(3)), client.gas_price_median(4)); assert_eq!(Some(&U256::from(3)), client.gas_price_corpus(3).median());
} }
#[test] #[test]
@ -221,8 +220,8 @@ fn can_generate_gas_price_histogram() {
let client_result = generate_dummy_client_with_data(20, 1, slice_into![6354,8593,6065,4842,7845,7002,689,4958,4250,6098,5804,4320,643,8895,2296,8589,7145,2000,2512,1408]); let client_result = generate_dummy_client_with_data(20, 1, slice_into![6354,8593,6065,4842,7845,7002,689,4958,4250,6098,5804,4320,643,8895,2296,8589,7145,2000,2512,1408]);
let client = client_result.reference(); let client = client_result.reference();
let hist = client.gas_price_histogram(20, 5).unwrap(); let hist = client.gas_price_corpus(20).histogram(5).unwrap();
let correct_hist = Histogram { bucket_bounds: vec_into![643, 2294, 3945, 5596, 7247, 8898], counts: vec![4,2,4,6,4] }; let correct_hist = ::stats::Histogram { bucket_bounds: vec_into![643, 2294, 3945, 5596, 7247, 8898], counts: vec![4,2,4,6,4] };
assert_eq!(hist, correct_hist); assert_eq!(hist, correct_hist);
} }
@ -231,7 +230,7 @@ fn empty_gas_price_histogram() {
let client_result = generate_dummy_client_with_data(20, 0, slice_into![]); let client_result = generate_dummy_client_with_data(20, 0, slice_into![]);
let client = client_result.reference(); let client = client_result.reference();
assert!(client.gas_price_histogram(20, 5).is_none()); assert!(client.gas_price_corpus(20).histogram(5).is_none());
} }
#[test] #[test]

View File

@ -456,5 +456,9 @@ pub fn get_default_ethash_params() -> EthashParams{
ecip1010_pause_transition: u64::max_value(), ecip1010_pause_transition: u64::max_value(),
ecip1010_continue_transition: u64::max_value(), ecip1010_continue_transition: u64::max_value(),
max_code_size: u64::max_value(), max_code_size: u64::max_value(),
max_gas_limit_transition: u64::max_value(),
max_gas_limit: U256::max_value(),
min_gas_price_transition: u64::max_value(),
min_gas_price: U256::zero(),
} }
} }

View File

@ -185,7 +185,7 @@ pub mod headers {
type Verified = Header; type Verified = Header;
fn create(input: Self::Input, engine: &Engine) -> Result<Self::Unverified, Error> { fn create(input: Self::Input, engine: &Engine) -> Result<Self::Unverified, Error> {
verify_header_params(&input, engine).map(|_| input) verify_header_params(&input, engine, true).map(|_| input)
} }
fn verify(unverified: Self::Unverified, engine: &Engine, check_seal: bool) -> Result<Self::Verified, Error> { fn verify(unverified: Self::Unverified, engine: &Engine, check_seal: bool) -> Result<Self::Verified, Error> {

View File

@ -51,12 +51,12 @@ impl HeapSizeOf for PreverifiedBlock {
/// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block /// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block
pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Result<(), Error> { pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Result<(), Error> {
verify_header_params(&header, engine)?; verify_header_params(&header, engine, true)?;
verify_block_integrity(bytes, &header.transactions_root(), &header.uncles_hash())?; verify_block_integrity(bytes, &header.transactions_root(), &header.uncles_hash())?;
engine.verify_block_basic(&header, Some(bytes))?; engine.verify_block_basic(&header, Some(bytes))?;
for u in UntrustedRlp::new(bytes).at(2)?.iter().map(|rlp| rlp.as_val::<Header>()) { for u in UntrustedRlp::new(bytes).at(2)?.iter().map(|rlp| rlp.as_val::<Header>()) {
let u = u?; let u = u?;
verify_header_params(&u, engine)?; verify_header_params(&u, engine, false)?;
engine.verify_block_basic(&u, None)?; engine.verify_block_basic(&u, None)?;
} }
// Verify transactions. // Verify transactions.
@ -195,7 +195,7 @@ pub fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error>
} }
/// Check basic header parameters. /// Check basic header parameters.
pub fn verify_header_params(header: &Header, engine: &Engine) -> Result<(), Error> { pub fn verify_header_params(header: &Header, engine: &Engine, is_full: bool) -> Result<(), Error> {
if header.number() >= From::from(BlockNumber::max_value()) { if header.number() >= From::from(BlockNumber::max_value()) {
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { max: Some(From::from(BlockNumber::max_value())), min: None, found: header.number() }))) return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { max: Some(From::from(BlockNumber::max_value())), min: None, found: header.number() })))
} }
@ -210,9 +210,11 @@ pub fn verify_header_params(header: &Header, engine: &Engine) -> Result<(), Erro
if header.number() != 0 && header.extra_data().len() > maximum_extra_data_size { if header.number() != 0 && header.extra_data().len() > maximum_extra_data_size {
return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: Some(maximum_extra_data_size), found: header.extra_data().len() }))); return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: Some(maximum_extra_data_size), found: header.extra_data().len() })));
} }
let max_time = get_time().sec as u64 + 30; if is_full {
if header.timestamp() > max_time { let max_time = get_time().sec as u64 + 30;
return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: Some(max_time), min: None, found: header.timestamp() }))) if header.timestamp() > max_time {
return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: Some(max_time), min: None, found: header.timestamp() })))
}
} }
Ok(()) Ok(())
} }

View File

@ -135,6 +135,10 @@ impl VaultKeyDirectory for VaultDiskDirectory {
let temp_vault = VaultDiskDirectory::create_temp_vault(self, new_key.clone()).map_err(|err| SetKeyError::NonFatalOld(err))?; let temp_vault = VaultDiskDirectory::create_temp_vault(self, new_key.clone()).map_err(|err| SetKeyError::NonFatalOld(err))?;
let mut source_path = temp_vault.path().expect("temp_vault is instance of DiskDirectory; DiskDirectory always returns path; qed").clone(); let mut source_path = temp_vault.path().expect("temp_vault is instance of DiskDirectory; DiskDirectory always returns path; qed").clone();
let mut target_path = self.path().expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed").clone(); let mut target_path = self.path().expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed").clone();
// preserve meta
temp_vault.set_meta(&self.meta()).map_err(SetKeyError::NonFatalOld)?;
// jump to next fs level // jump to next fs level
source_path.push("next"); source_path.push("next");
target_path.push("next"); target_path.push("next");

View File

@ -1015,4 +1015,21 @@ mod tests {
// and we can sign with the derived contract // and we can sign with the derived contract
assert!(store.sign(&derived, "test", &Default::default()).is_ok(), "Second password should work for second store."); assert!(store.sign(&derived, "test", &Default::default()).is_ok(), "Second password should work for second store.");
} }
#[test]
fn should_save_meta_when_setting_before_password() {
// given
let mut dir = RootDiskDirectoryGuard::new();
let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap();
let name = "vault"; let password = "password1";
let new_password = "password2";
// when
store.create_vault(name, password).unwrap();
store.set_vault_meta(name, "OldMeta").unwrap();
store.change_vault_password(name, new_password).unwrap();
// then
assert_eq!(store.get_vault_meta(name).unwrap(), "OldMeta".to_owned());
}
} }

View File

@ -8,6 +8,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
[dependencies] [dependencies]
ethcore = { path = "../ethcore" } ethcore = { path = "../ethcore" }
ethcore-util = { path = "../util" } ethcore-util = { path = "../util" }
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc.git" }
rlp = { path = "../util/rlp" } rlp = { path = "../util/rlp" }
mime = "0.2" mime = "0.2"
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" } hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }

View File

@ -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 {multihash, cid, hyper}; use {multihash, cid, hyper};
use handler::Out; use route::Out;
pub type Result<T> = ::std::result::Result<T, Error>; pub type Result<T> = ::std::result::Result<T, Error>;
@ -26,6 +26,8 @@ pub enum ServerError {
IoError(::std::io::Error), IoError(::std::io::Error),
/// Other `hyper` error /// Other `hyper` error
Other(hyper::error::Error), Other(hyper::error::Error),
/// Invalid --ipfs-api-interface
InvalidInterface
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -89,6 +91,7 @@ impl From<ServerError> for String {
match err { match err {
ServerError::IoError(err) => err.to_string(), ServerError::IoError(err) => err.to_string(),
ServerError::Other(err) => err.to_string(), ServerError::Other(err) => err.to_string(),
ServerError::InvalidInterface => "Invalid --ipfs-api-interface parameter".into(),
} }
} }
} }

View File

@ -23,21 +23,91 @@ extern crate cid;
extern crate rlp; extern crate rlp;
extern crate ethcore; extern crate ethcore;
extern crate ethcore_util as util; extern crate ethcore_util as util;
extern crate jsonrpc_http_server;
mod error; pub mod error;
mod handler; mod route;
use std::io::Write; use std::io::Write;
use std::sync::Arc; use std::sync::Arc;
use std::net::{SocketAddr, IpAddr, Ipv4Addr}; use std::net::{SocketAddr, IpAddr};
use error::ServerError; use error::ServerError;
use handler::{IpfsHandler, Out}; use route::Out;
use jsonrpc_http_server::cors;
use hyper::server::{Listening, Handler, Request, Response}; use hyper::server::{Listening, Handler, Request, Response};
use hyper::net::HttpStream; use hyper::net::HttpStream;
use hyper::header::{ContentLength, ContentType, Origin}; use hyper::header::{Vary, ContentLength, ContentType, AccessControlAllowOrigin};
use hyper::{Next, Encoder, Decoder, Method, RequestUri, StatusCode}; use hyper::{Next, Encoder, Decoder, Method, RequestUri, StatusCode};
use ethcore::client::BlockChainClient; use ethcore::client::BlockChainClient;
/// Request/response handler
pub struct IpfsHandler {
/// Response to send out
out: Out,
/// How many bytes from the response have been written
out_progress: usize,
/// Origin request header
origin: Option<String>,
/// Allowed CORS domains
cors_domains: Option<Vec<AccessControlAllowOrigin>>,
/// Hostnames allowed in the `Host` request header
allowed_hosts: Option<Vec<String>>,
/// Reference to the Blockchain Client
client: Arc<BlockChainClient>,
}
impl IpfsHandler {
pub fn client(&self) -> &BlockChainClient {
&*self.client
}
pub fn new(cors: Option<Vec<String>>, hosts: Option<Vec<String>>, client: Arc<BlockChainClient>) -> Self {
fn origin_to_header(origin: String) -> AccessControlAllowOrigin {
match origin.as_str() {
"*" => AccessControlAllowOrigin::Any,
"null" | "" => AccessControlAllowOrigin::Null,
_ => AccessControlAllowOrigin::Value(origin),
}
}
IpfsHandler {
out: Out::Bad("Invalid Request"),
out_progress: 0,
origin: None,
cors_domains: cors.map(|vec| vec.into_iter().map(origin_to_header).collect()),
allowed_hosts: hosts,
client: client,
}
}
fn is_host_allowed(&self, req: &Request<HttpStream>) -> bool {
match self.allowed_hosts {
Some(ref hosts) => jsonrpc_http_server::is_host_header_valid(&req, hosts),
None => true,
}
}
fn is_origin_allowed(&self) -> bool {
// Check origin header first, no header passed is good news
let origin = match self.origin {
Some(ref origin) => origin,
None => return true,
};
let cors_domains = match self.cors_domains {
Some(ref domains) => domains,
None => return false,
};
cors_domains.iter().any(|domain| match *domain {
AccessControlAllowOrigin::Value(ref allowed) => origin == allowed,
AccessControlAllowOrigin::Any => true,
AccessControlAllowOrigin::Null => origin == "",
})
}
}
/// Implement Hyper's HTTP handler /// Implement Hyper's HTTP handler
impl Handler<HttpStream> for IpfsHandler { impl Handler<HttpStream> for IpfsHandler {
fn on_request(&mut self, req: Request<HttpStream>) -> Next { fn on_request(&mut self, req: Request<HttpStream>) -> Next {
@ -45,9 +115,16 @@ impl Handler<HttpStream> for IpfsHandler {
return Next::write(); return Next::write();
} }
// Reject requests if the Origin header isn't valid self.origin = cors::read_origin(&req);
if req.headers().get::<Origin>().map(|o| "127.0.0.1" != &o.host.hostname).unwrap_or(false) {
self.out = Out::Bad("Illegal Origin"); if !self.is_host_allowed(&req) {
self.out = Out::Bad("Disallowed Host header");
return Next::write();
}
if !self.is_origin_allowed() {
self.out = Out::Bad("Disallowed Origin header");
return Next::write(); return Next::write();
} }
@ -57,7 +134,9 @@ impl Handler<HttpStream> for IpfsHandler {
_ => return Next::write(), _ => return Next::write(),
}; };
self.route(path, query) self.out = self.route(path, query);
Next::write()
} }
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next { fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
@ -82,25 +161,27 @@ impl Handler<HttpStream> for IpfsHandler {
res.headers_mut().set(ContentLength(bytes.len() as u64)); res.headers_mut().set(ContentLength(bytes.len() as u64));
res.headers_mut().set(ContentType(content_type)); res.headers_mut().set(ContentType(content_type));
Next::write()
}, },
NotFound(reason) => { NotFound(reason) => {
res.set_status(StatusCode::NotFound); res.set_status(StatusCode::NotFound);
res.headers_mut().set(ContentLength(reason.len() as u64)); res.headers_mut().set(ContentLength(reason.len() as u64));
res.headers_mut().set(ContentType(mime!(Text/Plain))); res.headers_mut().set(ContentType(mime!(Text/Plain)));
Next::write()
}, },
Bad(reason) => { Bad(reason) => {
res.set_status(StatusCode::BadRequest); res.set_status(StatusCode::BadRequest);
res.headers_mut().set(ContentLength(reason.len() as u64)); res.headers_mut().set(ContentLength(reason.len() as u64));
res.headers_mut().set(ContentType(mime!(Text/Plain))); res.headers_mut().set(ContentType(mime!(Text/Plain)));
Next::write()
} }
} }
if let Some(cors_header) = cors::get_cors_header(&self.cors_domains, &self.origin) {
res.headers_mut().set(cors_header);
res.headers_mut().set(Vary::Items(vec!["Origin".into()]));
}
Next::write()
} }
fn on_response_writable(&mut self, transport: &mut Encoder<HttpStream>) -> Next { fn on_response_writable(&mut self, transport: &mut Encoder<HttpStream>) -> Next {
@ -116,11 +197,12 @@ impl Handler<HttpStream> for IpfsHandler {
} }
} }
/// Attempt to write entire `data` from current `progress`
fn write_chunk<W: Write>(transport: &mut W, progress: &mut usize, data: &[u8]) -> Next { fn write_chunk<W: Write>(transport: &mut W, progress: &mut usize, data: &[u8]) -> Next {
// Skip any bytes that have already been written // Skip any bytes that have already been written
let chunk = &data[*progress..]; let chunk = &data[*progress..];
// Write an get written count // Write an get the amount of bytes written. End the connection in case of an error.
let written = match transport.write(chunk) { let written = match transport.write(chunk) {
Ok(written) => written, Ok(written) => written,
Err(_) => return Next::end(), Err(_) => return Next::end(),
@ -128,7 +210,7 @@ fn write_chunk<W: Write>(transport: &mut W, progress: &mut usize, data: &[u8]) -
*progress += written; *progress += written;
// Close the connection if the entire chunk has been written, otherwise increment progress // Close the connection if the entire remaining chunk has been written
if written < chunk.len() { if written < chunk.len() {
Next::write() Next::write()
} else { } else {
@ -136,12 +218,31 @@ fn write_chunk<W: Write>(transport: &mut W, progress: &mut usize, data: &[u8]) -
} }
} }
pub fn start_server(port: u16, client: Arc<BlockChainClient>) -> Result<Listening, ServerError> { /// Add current interface (default: "127.0.0.1:5001") to list of allowed hosts
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port); fn include_current_interface(mut hosts: Vec<String>, interface: String, port: u16) -> Vec<String> {
hosts.push(match port {
80 => interface,
_ => format!("{}:{}", interface, port),
});
hosts
}
pub fn start_server(
port: u16,
interface: String,
cors: Option<Vec<String>>,
hosts: Option<Vec<String>>,
client: Arc<BlockChainClient>
) -> Result<Listening, ServerError> {
let ip: IpAddr = interface.parse().map_err(|_| ServerError::InvalidInterface)?;
let addr = SocketAddr::new(ip, port);
let hosts = hosts.map(move |hosts| include_current_interface(hosts, interface, port));
Ok( Ok(
hyper::Server::http(&addr)? hyper::Server::http(&addr)?
.handle(move |_| IpfsHandler::new(client.clone())) .handle(move |_| IpfsHandler::new(cors.clone(), hosts.clone(), client.clone()))
.map(|(listening, srv)| { .map(|(listening, srv)| {
::std::thread::spawn(move || { ::std::thread::spawn(move || {

View File

@ -14,15 +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/>.
use {rlp, multihash}; use {rlp, multihash, IpfsHandler};
use error::{Error, Result}; use error::{Error, Result};
use cid::{ToCid, Codec}; use cid::{ToCid, Codec};
use std::sync::Arc;
use multihash::Hash; use multihash::Hash;
use hyper::Next;
use util::{Bytes, H256}; use util::{Bytes, H256};
use ethcore::client::{BlockId, TransactionId, BlockChainClient}; use ethcore::client::{BlockId, TransactionId};
type Reason = &'static str; type Reason = &'static str;
@ -34,30 +32,10 @@ pub enum Out {
Bad(Reason), Bad(Reason),
} }
/// Request/response handler
pub struct IpfsHandler {
/// Reference to the Blockchain Client
client: Arc<BlockChainClient>,
/// Response to send out
pub out: Out,
/// How many bytes from the response have been written
pub out_progress: usize,
}
impl IpfsHandler { impl IpfsHandler {
pub fn new(client: Arc<BlockChainClient>) -> Self {
IpfsHandler {
client: client,
out: Out::Bad("Invalid Request"),
out_progress: 0,
}
}
/// Route path + query string to a specialized method /// Route path + query string to a specialized method
pub fn route(&mut self, path: &str, query: Option<&str>) -> Next { pub fn route(&self, path: &str, query: Option<&str>) -> Out {
self.out = match path { match path {
"/api/v0/block/get" => { "/api/v0/block/get" => {
let arg = query.and_then(|q| get_param(q, "arg")).unwrap_or(""); let arg = query.and_then(|q| get_param(q, "arg")).unwrap_or("");
@ -65,9 +43,7 @@ impl IpfsHandler {
}, },
_ => Out::NotFound("Route not found") _ => Out::NotFound("Route not found")
}; }
Next::write()
} }
/// Attempt to read Content ID from `arg` query parameter, get a hash and /// Attempt to read Content ID from `arg` query parameter, get a hash and
@ -94,14 +70,14 @@ impl IpfsHandler {
/// Get block header by hash as raw binary. /// Get block header by hash as raw binary.
fn block(&self, hash: H256) -> Result<Out> { fn block(&self, hash: H256) -> Result<Out> {
let block_id = BlockId::Hash(hash); let block_id = BlockId::Hash(hash);
let block = self.client.block_header(block_id).ok_or(Error::BlockNotFound)?; let block = self.client().block_header(block_id).ok_or(Error::BlockNotFound)?;
Ok(Out::OctetStream(block.into_inner())) Ok(Out::OctetStream(block.into_inner()))
} }
/// Get list of block ommers by hash as raw binary. /// Get list of block ommers by hash as raw binary.
fn block_list(&self, hash: H256) -> Result<Out> { fn block_list(&self, hash: H256) -> Result<Out> {
let uncles = self.client.find_uncles(&hash).ok_or(Error::BlockNotFound)?; let uncles = self.client().find_uncles(&hash).ok_or(Error::BlockNotFound)?;
Ok(Out::OctetStream(rlp::encode(&uncles).to_vec())) Ok(Out::OctetStream(rlp::encode(&uncles).to_vec()))
} }
@ -109,21 +85,21 @@ impl IpfsHandler {
/// Get transaction by hash and return as raw binary. /// Get transaction by hash and return as raw binary.
fn transaction(&self, hash: H256) -> Result<Out> { fn transaction(&self, hash: H256) -> Result<Out> {
let tx_id = TransactionId::Hash(hash); let tx_id = TransactionId::Hash(hash);
let tx = self.client.transaction(tx_id).ok_or(Error::TransactionNotFound)?; let tx = self.client().transaction(tx_id).ok_or(Error::TransactionNotFound)?;
Ok(Out::OctetStream(rlp::encode(&*tx).to_vec())) Ok(Out::OctetStream(rlp::encode(&*tx).to_vec()))
} }
/// Get state trie node by hash and return as raw binary. /// Get state trie node by hash and return as raw binary.
fn state_trie(&self, hash: H256) -> Result<Out> { fn state_trie(&self, hash: H256) -> Result<Out> {
let data = self.client.state_data(&hash).ok_or(Error::StateRootNotFound)?; let data = self.client().state_data(&hash).ok_or(Error::StateRootNotFound)?;
Ok(Out::OctetStream(data)) Ok(Out::OctetStream(data))
} }
/// Get state trie node by hash and return as raw binary. /// Get state trie node by hash and return as raw binary.
fn contract_code(&self, hash: H256) -> Result<Out> { fn contract_code(&self, hash: H256) -> Result<Out> {
let data = self.client.state_data(&hash).ok_or(Error::ContractNotFound)?; let data = self.client().state_data(&hash).ok_or(Error::ContractNotFound)?;
Ok(Out::OctetStream(data)) Ok(Out::OctetStream(data))
} }
@ -138,11 +114,12 @@ fn get_param<'a>(query: &'a str, name: &str) -> Option<&'a str> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::sync::Arc;
use super::*; use super::*;
use ethcore::client::TestBlockChainClient; use ethcore::client::TestBlockChainClient;
fn get_mocked_handler() -> IpfsHandler { fn get_mocked_handler() -> IpfsHandler {
IpfsHandler::new(Arc::new(TestBlockChainClient::new())) IpfsHandler::new(None, None, Arc::new(TestBlockChainClient::new()))
} }
#[test] #[test]
@ -232,37 +209,37 @@ mod tests {
#[test] #[test]
fn route_block() { fn route_block() {
let mut handler = get_mocked_handler(); let handler = get_mocked_handler();
let _ = handler.route("/api/v0/block/get", Some("arg=z43AaGF5tmkT9SEX6urrhwpEW5ZSaACY73Vw357ZXTsur2fR8BM")); let out = handler.route("/api/v0/block/get", Some("arg=z43AaGF5tmkT9SEX6urrhwpEW5ZSaACY73Vw357ZXTsur2fR8BM"));
assert_eq!(handler.out, Out::NotFound("Block not found")); assert_eq!(out, Out::NotFound("Block not found"));
} }
#[test] #[test]
fn route_block_missing_query() { fn route_block_missing_query() {
let mut handler = get_mocked_handler(); let handler = get_mocked_handler();
let _ = handler.route("/api/v0/block/get", None); let out = handler.route("/api/v0/block/get", None);
assert_eq!(handler.out, Out::Bad("CID parsing failed")); assert_eq!(out, Out::Bad("CID parsing failed"));
} }
#[test] #[test]
fn route_block_invalid_query() { fn route_block_invalid_query() {
let mut handler = get_mocked_handler(); let handler = get_mocked_handler();
let _ = handler.route("/api/v0/block/get", Some("arg=foobarz43AaGF5tmkT9SEX6urrhwpEW5ZSaACY73Vw357ZXTsur2fR8BM")); let out = handler.route("/api/v0/block/get", Some("arg=foobarz43AaGF5tmkT9SEX6urrhwpEW5ZSaACY73Vw357ZXTsur2fR8BM"));
assert_eq!(handler.out, Out::Bad("CID parsing failed")); assert_eq!(out, Out::Bad("CID parsing failed"));
} }
#[test] #[test]
fn route_invalid_route() { fn route_invalid_route() {
let mut handler = get_mocked_handler(); let handler = get_mocked_handler();
let _ = handler.route("/foo/bar/baz", Some("arg=z43AaGF5tmkT9SEX6urrhwpEW5ZSaACY73Vw357ZXTsur2fR8BM")); let out = handler.route("/foo/bar/baz", Some("arg=z43AaGF5tmkT9SEX6urrhwpEW5ZSaACY73Vw357ZXTsur2fR8BM"));
assert_eq!(handler.out, Out::NotFound("Route not found")); assert_eq!(out, Out::NotFound("Route not found"));
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "parity.js", "name": "parity.js",
"version": "0.3.94", "version": "0.3.105",
"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>",

View File

@ -207,7 +207,7 @@ export default class Parity {
importGethAccounts (accounts) { importGethAccounts (accounts) {
return this._transport return this._transport
.execute('parity_importGethAccounts', inAddresses) .execute('parity_importGethAccounts', inAddresses(accounts))
.then(outAddresses); .then(outAddresses);
} }

View File

@ -73,6 +73,21 @@ describe('api/rpc/parity', () => {
}); });
}); });
describe('importGethAccounts', () => {
const ACCOUNTS = ['0x63cf90d3f0410092fc0fca41846f596223979195'];
let scope;
beforeEach(() => {
scope = mockHttp([{ method: 'parity_importGethAccounts', reply: { result: ACCOUNTS } }]);
});
it('passes the addresses through', () => {
return instance.importGethAccounts(ACCOUNTS).then((result) => {
expect(scope.body['parity_importGethAccounts'].params).to.deep.equal([ACCOUNTS]);
});
});
});
describe('minGasPrice', () => { describe('minGasPrice', () => {
it('returns the min gasprice, formatted', () => { it('returns the min gasprice, formatted', () => {
mockHttp([{ method: 'parity_minGasPrice', reply: { result: '0x123456' } }]); mockHttp([{ method: 'parity_minGasPrice', reply: { result: '0x123456' } }]);

View File

@ -15,7 +15,17 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default { export default {
button: {
newAccount: `new account`,
newWallet: `new wallet`,
vaults: `vaults`
},
summary: { summary: {
minedBlock: `Mined at block #{blockNumber}` minedBlock: `Mined at block #{blockNumber}`
},
title: `Accounts Overview`,
tooltip: {
actions: `actions relating to the current view are available on the toolbar for quick access, be it for performing actions or creating a new item`,
overview: `your accounts are visible for easy access, allowing you to edit the meta information, make transfers, view transactions and fund the account`
} }
}; };

View File

@ -61,13 +61,6 @@ export default {
label: `Import raw private key` label: `Import raw private key`
} }
}, },
error: {
invalidKey: `the raw key needs to be hex, 64 characters in length and contain the prefix "0x"`,
noFile: `select a valid wallet file to import`,
noKey: `you need to provide the raw private key`,
noMatchPassword: `the supplied passwords does not match`,
noName: `you need to specify a valid name for the account`
},
newAccount: { newAccount: {
hint: { hint: {
hint: `(optional) a hint to help with remembering the password`, hint: `(optional) a hint to help with remembering the password`,

View File

@ -0,0 +1,24 @@
// 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 {
duplicateName: `the name already exists`,
invalidKey: `the raw key needs to be hex, 64 characters in length and contain the prefix "0x"`,
noFile: `select a valid wallet file to import`,
noKey: `you need to provide the raw private key`,
noMatchPassword: `the supplied passwords does not match`,
noName: `you need to specify a valid name`
};

View File

@ -29,6 +29,7 @@ export dapps from './dapps';
export deleteAccount from './deleteAccount'; export deleteAccount from './deleteAccount';
export deployContract from './deployContract'; export deployContract from './deployContract';
export editMeta from './editMeta'; export editMeta from './editMeta';
export errors from './errors';
export executeContract from './executeContract'; export executeContract from './executeContract';
export extension from './extension'; export extension from './extension';
export firstRun from './firstRun'; export firstRun from './firstRun';
@ -38,9 +39,11 @@ export parityBar from './parityBar';
export passwordChange from './passwordChange'; export passwordChange from './passwordChange';
export settings from './settings'; export settings from './settings';
export shapeshift from './shapeshift'; export shapeshift from './shapeshift';
export tabBar from './tabBar';
export transfer from './transfer'; export transfer from './transfer';
export txEditor from './txEditor'; export txEditor from './txEditor';
export ui from './ui'; export ui from './ui';
export upgradeParity from './upgradeParity'; export upgradeParity from './upgradeParity';
export vaults from './vaults';
export walletSettings from './walletSettings'; export walletSettings from './walletSettings';
export web from './web'; export web from './web';

View File

@ -0,0 +1,21 @@
// 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 {
tooltip: {
overview: `navigate between the different parts and views of the application, switching between an account view, token view and distributed application view`
}
};

View File

@ -35,6 +35,13 @@ export default {
passwordStrength: { passwordStrength: {
label: `password strength` label: `password strength`
}, },
tooltips: {
button: {
done: `Done`,
next: `Next`,
skip: `Skip`
}
},
txHash: { txHash: {
confirmations: `{count} {value, plural, one {confirmation} other {confirmations}}`, confirmations: `{count} {value, plural, one {confirmation} other {confirmations}}`,
oog: `The transaction might have gone out of gas. Try again with more gas.`, oog: `The transaction might have gone out of gas. Try again with more gas.`,

View File

@ -0,0 +1,75 @@
// 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 {
accounts: {
button: {
cancel: `Cancel`,
execute: `Set`
},
empty: `There are no accounts in this vault`,
title: `Manage Vault Accounts`
},
button: {
accounts: `accounts`,
add: `create vault`,
close: `close vault`,
open: `open vault`
},
confirmClose: {
info: `You are about to close a vault. Any accounts associated with the vault won't be visible after this operation concludes. To view the associated accounts, open the vault again.`,
title: `Close Vault`
},
confirmOpen: {
info: `You are about to open a vault. After confirming your password, all accounts associated with this vault will be visible. Closing the vault will remove the accounts from view until the vault is opened again.`,
password: {
hint: `the password specified when creating the vault`,
label: `vault password`
},
title: `Open Vault`
},
create: {
button: {
close: `close`,
vault: `create vault`
},
description: {
hint: `an extended description for the vault`
},
descriptions: {
label: `(optional) description`
},
hint: {
hint: `(optional) a hint to help with remembering the password`,
label: `password hint`
},
name: {
hint: `a name for the vault`,
label: `vault name`
},
password: {
hint: `a strong, unique password`,
label: `password`
},
password2: {
hint: `verify your password`,
label: `password (repeat)`
},
title: `Create a new vault`
},
empty: `There are currently no vaults to display.`,
title: `Vault Management`
};

35
js/src/i18n/nl/account.js Executable file
View File

@ -0,0 +1,35 @@
// 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 {
button: {
delete: `verwijder account`,
edit: `bewerk`,
password: `wachtwoord`,
shapeshift: `shapeshift`,
transfer: `verzend`,
verify: `verifieer`
},
header: {
outgoingTransactions: `{count} uitgaande transacties`,
uuid: `uuid: {uuid}`
},
title: `Account Beheer`,
transactions: {
poweredBy: `Transactie lijst mede mogelijk door {etherscan}`,
title: `transacties`
}
};

31
js/src/i18n/nl/accounts.js Executable file
View File

@ -0,0 +1,31 @@
// 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 {
button: {
newAccount: `nieuw account`,
newWallet: `nieuw wallet`,
vaults: `kluizen`
},
summary: {
minedBlock: `Opgenomen in blok #{blockNumber}`
},
title: `Accounts Overzicht`,
tooltip: {
actions: `voor de huidige weergave zijn koppelingen beschikbaar op de werkbalk voor snelle toegang: het uitvoeren van acties of het creëren van een nieuw item`,
overview: `hier vind je een overzichtelijke weergave van je accounts, waarin je meta informatie kunt bewerken en transacties kunt uitvoeren en bekijken`
}
};

37
js/src/i18n/nl/addAddress.js Executable file
View File

@ -0,0 +1,37 @@
// 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 {
button: {
add: `Adres Opslaan`,
close: `Annuleer`
},
input: {
address: {
hint: `het netwerk adres van het item`,
label: `Netwerk Adres`
},
description: {
hint: `een uitgebereide beschrijving voor het adres`,
label: `(optioneel) adres beschrijving`
},
name: {
hint: `een beschrijvende naam van het adres`,
label: `Adres Naam`
}
},
label: `voeg opgeslagen adres toe`
};

60
js/src/i18n/nl/addContract.js Executable file
View File

@ -0,0 +1,60 @@
// 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 {
abi: {
hint: `de abi van het contract`,
label: `contract abi`
},
abiType: {
custom: {
description: `Contract aangemaakt met een custom ABI`,
label: `Custom Contract`
},
multisigWallet: {
description: `Ethereum Multisig contract {link}`,
label: `Multisig Wallet`,
link: `zie contract code`
},
token: {
description: `Een standaard {erc20} token`,
erc20: `ERC 20`,
label: `Token`
}
},
address: {
hint: `het netwerk adres van het contract`,
label: `netwerk adres`
},
button: {
add: `Voeg Contract toe`,
cancel: `Annuleer`,
next: `Volgende`,
prev: `Terug`
},
description: {
hint: `een uitgebreide omschrijving van het contract`,
label: `(optioneel) contract beschrijving`
},
name: {
hint: `een beschrijvende naam van het contract`,
label: `contract naam`
},
title: {
details: `voer contract details in`,
type: `kies een contract type`
}
};

26
js/src/i18n/nl/addressSelect.js Executable file
View File

@ -0,0 +1,26 @@
// 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 {
fromEmail: `Geverifieerd met behulp van e-mail {email}`,
fromRegistry: `{name} (from registry)`,
labels: {
accounts: `accounts`,
contacts: `contacten`,
contracts: `contracts`
},
noAccount: `Geen account gevonden voor deze zoekopdracht...`
};

27
js/src/i18n/nl/application.js Executable file
View File

@ -0,0 +1,27 @@
// 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 {
status: {
consensus: {
capable: `Capable`,
capableUntil: `Capable tot #{blockNumber}`,
incapableSince: `Incapable sinds #{blockNumber}`,
unknown: `Onbekende capability`
},
upgrade: `Upgrade`
}
};

26
js/src/i18n/nl/connection.js Executable file
View File

@ -0,0 +1,26 @@
// 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 {
connectingAPI: `Verbinden met de Parity Secure API.`,
connectingNode: `Verbinden met de Parity Node. Conroleer of je Parity node actief en op het netwerk bereikbaar is als dit bericht op je scherm blijft staan.`,
invalidToken: `ongeldige signer token`,
noConnection: `Kan geen verbinding maken met de Parity Secure API. Voer {newToken} uit en geef het token hieronder in om het secure token bij te werken of een nieuwe te genereren.`,
token: {
hint: `een Parity gegenereerd token`,
label: `secure token`
}
};

19
js/src/i18n/nl/contract.js Executable file
View File

@ -0,0 +1,19 @@
// 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 {
minedBlock: `Opgenomen in blok #{blockNumber}`
};

156
js/src/i18n/nl/createAccount.js Executable file
View File

@ -0,0 +1,156 @@
// 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 {
accountDetails: {
address: {
hint: `Het netwerk adres van het account`,
label: `adres`
},
name: {
hint: `Een beschrijvende naam van het account`,
label: `account naam`
},
phrase: {
hint: `De account herstel zin`,
label: `Eigenaar's herstel zin (houd deze woorden veilig en prive want hiermee kun je volledige, ongelimiteerde toegang tot het account verkrijgen).`
}
},
accountDetailsGeth: {
imported: `Je hebt {number} adressen geïmporteerd uit de Geth keystore:`
},
button: {
back: `Terug`,
cancel: `Annuleer`,
close: `Sluit`,
create: `Aanmaken`,
import: `Importeer`,
next: `Volgende`,
print: `Herstel zin afdrukken`
},
creationType: {
fromGeth: {
label: `Importeer accounts uit Geth keystore`
},
fromJSON: {
label: `Importeer account uit een opgeslagen JSON file`
},
fromNew: {
label: `Handmatig account aanmaken`
},
fromPhrase: {
label: `Herstel account met een herstel zin`
},
fromPresale: {
label: `Importeer account van een Ethereum voor-verkoop (pre-sale) wallet`
},
fromRaw: {
label: `Importeer een prive sleutel (raw private key)`
}
},
newAccount: {
hint: {
hint: `(optioneel) een hint om je te helpen het wachtwoord te herinneren`,
label: `wachtwoord hint`
},
name: {
hint: `een beschrijvende naam van het account`,
label: `account naam`
},
password: {
hint: `een sterk en uniek wachtwoord`,
label: `wachtwoord`
},
password2: {
hint: `bevestig je wachtwoord`,
label: `wachtwoord (herhaal)`
}
},
newGeth: {
noKeys: `Er zijn momenteel geen importeerbare sleutels (keys) beschikbaar in de Geth keystore; of ze zijn al in je Parity installatie beschikbaar`
},
newImport: {
file: {
hint: `het te importeren wallet bestand`,
label: `wallet bestand`
},
hint: {
hint: `(optioneel) een hint om je te helpen het wachtwoord te herinneren`,
label: `wachtwoord hint`
},
name: {
hint: `een beschrijvende naam van het account`,
label: `account naam`
},
password: {
hint: `het wachtwoord om je wallet te openen`,
label: `wachtwoord`
}
},
rawKey: {
hint: {
hint: `(optioneel) een hint om je te helpen het wachtwoord te herinneren`,
label: `wachtwoord hint`
},
name: {
hint: `een beschrijvende naam van het account`,
label: `account naam`
},
password: {
hint: `een sterk en uniek wachtwoord`,
label: `wachtwoord`
},
password2: {
hint: `herhaal je wachtwoord ter controle`,
label: `wachtwoord (herhaling)`
},
private: {
hint: `de hexadecimaal gecodeerde prive sleutel (raw private key)`,
label: `prive sleutel`
}
},
recoveryPhrase: {
hint: {
hint: `(optioneel) een hint om je te helpen het wachtwoord te herinneren`,
label: `wachtwoord hint`
},
name: {
hint: `een beschrijvende naam van het account`,
label: `account naam`
},
password: {
hint: `een sterk en uniek wachtwoord`,
label: `wachtwoord`
},
password2: {
hint: `herhaal je wachtwoord ter controle`,
label: `wachtwoord (herhaling)`
},
phrase: {
hint: `de account herstel zin opgebouwd uit een aantal willekeurige woorden`,
label: `account herstel zin`
},
windowsKey: {
label: `Sleutel (key) is aangemaakt met Parity <1.4.5 op Windows`
}
},
title: {
accountInfo: `account informatie`,
createAccount: `account aanmaken`,
createType: `manier van aanmaken`,
importWallet: `importeer wallet`
}
};

View File

@ -0,0 +1,105 @@
// 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 {
button: {
add: `Voeg toe`,
cancel: `Annuleer`,
close: `Sluit`,
create: `Creëer`,
done: `Klaar`,
next: `Volgende`,
sending: `Verzenden...`
},
deployment: {
message: `Het aanmaken wordt momenteel uitgevoerd`
},
details: {
address: {
hint: `het wallet contract adres`,
label: `wallet adres`
},
dayLimitMulti: {
hint: `hoeveelheid ETH die dagelijks kan worden uitgegeven zonder bevestigingen`,
label: `wallet dag limiet`
},
description: {
hint: `de lokale omschrijving voor dit wallet`,
label: `wallet omschrijving (optioneel)`
},
descriptionMulti: {
hint: `de lokale omschrijving voor dit wallet`,
label: `wallet omschrijving (optioneel)`
},
name: {
hint: `de lokale naam voor dit wallet`,
label: `wallet naam`
},
nameMulti: {
hint: `de lokale naam voor dit wallet`,
label: `wallet naam`
},
ownerMulti: {
hint: `het eigenaars account van dit contract`,
label: `van account (contract eigenaar)`
},
ownersMulti: {
label: `andere wallet eigenaren`
},
ownersMultiReq: {
hint: `vereiste aantal eigenaren om de transactie goed te keuren`,
label: `vereiste eigenaren`
}
},
info: {
added: `toegevoegd`,
copyAddress: `kopier adres naar klembord`,
created: `{name} is {deployedOrAdded} in`,
dayLimit: `De dag limiet is ingestel op {dayLimit} ETH.`,
deployed: `aangemaakt`,
numOwners: `{numOwners} eigenaren zijn vereist om de transactie goed te keuren.`,
owners: `De wallet eigenaren zijn:`
},
rejected: {
message: `Het aanmaken is mislukt`,
state: `Je wallet zal niet worden aangemaakt. Je kunt dit venster nu veilig sluiten.`,
title: `mislukt`
},
states: {
completed: `Het contract is succesvol aangemaakt`,
preparing: `Transactie aan het voorbereiden voor verzending op het netwerk`,
validatingCode: `De contract code van het aangemaakte contract wordt gevalideerd`,
waitingConfirm: `Wachten tot de transactie bevestigd is in de Parity Secure Signer`,
waitingReceipt: `Wachten tot het aanmaken van het contract bevestigd is`
},
steps: {
deployment: `wallet aanmaken`,
details: `wallet details`,
info: `wallet informatie`,
type: `wallet type`
},
type: {
multisig: {
description: `Creëer/Maak een {link} Wallet aan`,
label: `Multi-Sig wallet`,
link: `standaard multi-signature`
},
watch: {
description: `Voeg een bestaand wallet toe aan je accounts`,
label: `Monitor/volg een wallet`
}
}
};

20
js/src/i18n/nl/dapp.js Executable file
View File

@ -0,0 +1,20 @@
// 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 {
loading: `Bezig met laden`,
unavailable: `De dapp is niet bereikbaar`
};

46
js/src/i18n/nl/dapps.js Normal file
View File

@ -0,0 +1,46 @@
// 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 {
add: {
builtin: {
desc: `Experimentele applicaties, ontwikkeld door het Parity team om te demonstreren wat de dapp mogelijkheden, integratie en experimentele opties zijn; en om netwerkbreed client gedrag te controleren.`,
label: `Applicaties gebundeld met Parity`
},
label: `zichtbare applicaties`,
local: {
desc: `Alle lokaal door de gebruiker geinstalleerde applicaties die toegang hebben tot de Parity client.`,
label: `Lokaal beschikbare applicaties`
},
network: {
desc: `Deze applicaties zijn niet bij Parity aangesloten, noch worden ze gepubliceerd door Parity. Alle applicaties blijven in beheer van hun eigen auteur. Zorg ervoor dat je snapt wat het doel van een applicatie is, voordat je ermee aan de slag gaat.`,
label: `Applicaties op het wereldwijde netwerk`
}
},
button: {
edit: `bewerk`,
permissions: `toestemming`
},
external: {
accept: `Ik begrijp dat deze toepassingen niet bij Parity zijn aangesloten`,
warning: `Deze applicaties gepuliceerd door derde partijen zijn niet bij Parity aangesloten, noch worden ze gepubliceerd door Parity. Alle applicaties blijven in beheer van hun eigen auteur. Zorg ervoor dat je snapt wat het doel van een applicatie is voordat je ermee aan de slag gaat.`
},
label: `Gedecentraliseerde Applicaties`,
permissions: {
description: `{activeIcon} account is beschikbaar voor applicaties, {defaultIcon} account is het standaard account`,
label: `zichtbare dapp accounts`
}
};

24
js/src/i18n/nl/deleteAccount.js Executable file
View File

@ -0,0 +1,24 @@
// 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 {
password: {
hint: `Voer ter bevestiging het wachtwoord in om je account te verwijderen`,
label: `account wachtwoord`
},
question: `Weet je zeker dat je het volgende account permanent wilt verwijderen?`,
title: `bevestig verwijderen`
};

View File

@ -0,0 +1,81 @@
// 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 {
busy: {
title: `Het contract wordt momenteel aangemaakt`
},
button: {
cancel: `Annuleer`,
close: `Sluit`,
create: `Creëer`,
done: `Klaar`,
next: `Volgende`
},
completed: {
description: `Je contract is aangemaakt en opgenomen in`
},
details: {
abi: {
hint: `de abi van het aan te maken contract of solc combined-output`,
label: `abi / solc combined-output`
},
address: {
hint: `het account wat eigenaar is van dit contract`,
label: `van account (contract eigenaar)`
},
code: {
hint: `de gecompileerde code van het aan te maken contract`,
label: `code`
},
contract: {
label: `selecteer een contract`
},
description: {
hint: `een beschrijving van het contract`,
label: `contract omschrijving (optioneel)`
},
name: {
hint: `een naam voor het aangemaakte contract`,
label: `contract naam`
}
},
owner: {
noneSelected: `er dient een geldig account als contract eigenaar geselecteerd te zijn`
},
parameters: {
choose: `Kies de contract parameters`
},
rejected: {
description: `Je kunt dit scherm veilig sluiten, het contract zal niet worden aangemaakt.`,
title: `Het aanmaken van het contract is afgewezen`
},
state: {
completed: `Het contract is succesvol aangemaakt`,
preparing: `Transactie aan het voorbereiden om te verzenden op het netwerk`,
validatingCode: `De contract code van het aangemaakte contract valideren`,
waitReceipt: `Wachten tot het aanmaken van het contract bevestigd is`,
waitSigner: `Wachten tot de transactie bevestigd is in de Parity Secure Signer`
},
title: {
completed: `voltooid`,
deployment: `aangemaakt`,
details: `contract details`,
failed: `aanmaken mislukt`,
parameters: `contract parameters`,
rejected: `afgewezen`
}
};

View 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 `Windows`;

View File

@ -0,0 +1,34 @@
// 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 {
description: {
hint: `omschrijving van dit adres`,
label: `adres omschrijving`
},
name: {
label: `naam`
},
passwordHint: {
hint: `een hint om je wachtwoord te herstellen`,
label: `(optioneel) wachtwoord hint`
},
tags: {
hint: `druk op <Enter> om een label toe te voegen`,
label: `(optional) labels`
},
title: `bewerk metadata`
};

24
js/src/i18n/nl/errors.js Normal file
View File

@ -0,0 +1,24 @@
// 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 {
duplicateName: `deze naam bestaat al`,
invalidKey: `de prive sleutel (raw key) is hexadecimaal, 64 karakters lang en en begint met "0x"`,
noFile: `selecteer een geldig wallet bestand om te importeren`,
noKey: `je dient de prive sleutel (raw private key) in te voeren`,
noMatchPassword: `het ingevoerde wachtwoord is onjuist`,
noName: `je dient een geldige naam voor het account op te geven`
};

View File

@ -0,0 +1,58 @@
// 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 {
busy: {
posted: `Je transactie is op het netwerk gepubliceerd`,
title: `De functie wordt uitgevoerd`,
waitAuth: `Wachten op autorisatie in de Parity Signer`
},
button: {
cancel: `annuleer`,
done: `klaar`,
next: `volgende`,
post: `verzend transactie`,
prev: `vorige`
},
details: {
address: {
hint: `van account`,
label: `het account waarmee je de transactie wilt uitvoeren`
},
advancedCheck: {
label: `geavanceerde verzend opties`
},
amount: {
hint: `de in deze transactie te verzenden hoeveelheid`,
label: `transactie waarde (in ETH)`
},
function: {
hint: `de uit het contract aan te roepen functie`,
label: `uit te voeren functie`
}
},
rejected: {
state: `Je kunt dit venster veilig sluiten, de functie zal niet worden uitgevoerd.`,
title: `De uitvoering is afgewezen`
},
steps: {
advanced: `geavanceerde opties`,
complete: `voltooi`,
rejected: `afgewezen`,
sending: `verzenden`,
transfer: `functie details`
}
};

View File

@ -0,0 +1,20 @@
// 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 {
install: `Installeer de extensie nu`,
intro: `Parity heeft nu een extensie voor Chrome beschikbaar waarmee je veillig Ethereum-enabled gedistribueerde applicaties kunt bekijken. Het wordt ten zeerste aanbevolen om deze extensie te installeren om je Parity ervaring nog beter te maken.`
};

32
js/src/i18n/nl/firstRun.js Executable file
View File

@ -0,0 +1,32 @@
// 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 {
button: {
close: `Sluiten`,
create: `creëer`,
next: `volgende`,
print: `Woorden Afdrukken`,
skip: `Overslaan`
},
title: {
completed: `voltooid`,
newAccount: `nieuw account`,
recovery: `herstelzin`,
terms: `voorwaarden`,
welcome: `welkom`
}
};

38
js/src/i18n/nl/home.js Normal file
View File

@ -0,0 +1,38 @@
// 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 {
account: {
visited: `bekeken {when}`
},
accounts: {
none: `Geen recente account geschiedenis beschikbaar`,
title: `Recente Accounts`
},
dapp: {
visited: `bekeken {when}`
},
dapps: {
none: `Geen recente applicatie geschiedenis beschikbaar`,
title: `Recente Dapps`
},
title: `Parity Home`,
url: {
none: `Geen recente URL geschiedenis beschikbaar`,
title: `Web Applicaties`,
visited: `bezocht {when}`
}
};

66
js/src/i18n/nl/index.js Normal file → Executable file
View File

@ -14,8 +14,72 @@
// 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 account from './account';
import accounts from './accounts';
import addAddress from './addAddress';
import addContract from './addContract';
import addressSelect from './addressSelect';
import application from './application';
import connection from './connection';
import contract from './contract';
import createAccount from './createAccount';
import createWallet from './createWallet';
import dapp from './dapp';
import dapps from './dapps';
import deleteAccount from './deleteAccount';
import deployContract from './deployContract';
import editMeta from './editMeta';
import errors from './errors';
import executeContract from './executeContract';
import extension from './extension';
import firstRun from './firstRun';
import home from './home';
import loadContract from './loadContract';
import parityBar from './parityBar';
import passwordChange from './passwordChange';
import settings from './settings'; import settings from './settings';
import shapeshift from './shapeshift';
import tabBar from './tabBar';
import transfer from './transfer';
import txEditor from './txEditor';
import ui from './ui';
import upgradeParity from './upgradeParity';
import vaults from './vaults';
import walletSettings from './walletSettings';
import web from './web';
export default { export default {
settings account,
accounts,
addAddress,
addContract,
addressSelect,
application,
connection,
contract,
createAccount,
createWallet,
dapp,
dapps,
deleteAccount,
deployContract,
editMeta,
errors,
executeContract,
extension,
firstRun,
home,
loadContract,
parityBar,
passwordChange,
settings,
shapeshift,
tabBar,
transfer,
txEditor,
ui,
upgradeParity,
vaults,
walletSettings,
web
}; };

View File

@ -0,0 +1,43 @@
// 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 {
button: {
cancel: `Annuleer`,
load: `Laad`,
no: `Nee`,
yes: `Ja`
},
contract: {
savedAt: `Opgeslagen {when}`
},
header: {
saved: `Opgeslagen Contracten`,
snippets: `Contract Snippets`
},
removal: {
confirm: `Weet je zeker dat je het volgende contract van je opgeslagen contracten wilt verwijderen?`,
savedAt: `Opgeslagen {when}`
},
tab: {
local: `Lokaal`,
snippets: `Snippets`
},
title: {
remove: `bevestig verwijderen`,
view: `bekijk contracten`
}
};

View File

@ -0,0 +1,29 @@
// 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 {
button: {
close: `Sluit`
},
label: {
parity: `Parity`,
signer: `Signer`
},
title: {
accounts: `Standaard Account`,
signer: `Parity Signer: Wachten`
}
};

View File

@ -0,0 +1,53 @@
// 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 {
button: {
cancel: `Annuleer`,
change: `Wijzig`,
test: `Test`,
wait: `Wacht...`
},
currentPassword: {
hint: `je huidige wachtwoord voor dit account`,
label: `huidig wachtwoord`
},
newPassword: {
hint: `het nieuwe wachtwoord vor dit account`,
label: `nieuw wachtwoord`
},
passwordHint: {
hint: `hint voor het nieuwe wachtwoord`,
label: `(optioneel) nieuwe wachtwoord hint`
},
repeatPassword: {
error: `de ingevoerde wachtwoorden zijn niet gelijk`,
hint: `herhaal het nieuwe wachtwoord voor dit account`,
label: `herhaal nieuw wachtwoord`
},
success: `Je wachtwoord is succesvol aangepast`,
tabChange: {
label: `Wijzig wachtwoord`
},
tabTest: {
label: `Test wachtwoord`
},
testPassword: {
hint: `je account wachtwoord`,
label: `wachtwoord`
},
title: `Wachtwoord Manager`
};

View File

@ -15,49 +15,75 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default { export default {
label: 'Instellingen',
background: { background: {
label: 'Achtergrond' button_more: `genereer meer`,
overview_0: `Het achtergrond patroon dat je nu kunt zien is uniek voor jouw Parity installatie. Het veranderd elke keer als je een nieuw Signer token genereerd. Op deze manier kunnen gedecentraliseerde applicaties niet doen alsof ze betrouwbaar zijn.`,
overview_1: `Kies het patroon dat je wilt en onthoud het. Dit patroon wordt vanaf nu altijd getoond, tenzij je je browser cache wist of een nieuw Signer token genereerd.`,
label: `achtergrond`
}, },
parity: { parity: {
label: 'Parity' languages: {
hint: `de taal waarin deze interface wordt weergegeven`,
label: `Weergave taal`
},
loglevels: `Kies hoeveel details er in het logboek worden bijgehouden.`,
modes: {
hint: `de synchronisatie modus van de Parity node`,
label: `Synchronisatie modus`,
mode_active: `Parity synchroniseert de chain continu`,
mode_dark: `Parity synchroniseert alleen als de RPC actief is`,
mode_offline: `Parity synchroniseert niet`,
mode_passive: `Parity synchroniseert in het begin. Daarna slaapt Parity en wordt regelmatig wakker voor synchronisatie`
},
overview_0: `Pas de Parity node instellingen aan en kies de synchronisatie modus in dit menu.`,
label: `parity`
}, },
proxy: { proxy: {
label: 'Proxy' details_0: `In plaats van Parity te openen via het IP adres en poort-nummer, kun je toegang verkrijgen tot het .parity sub-domein door {homeProxy} te bezoeken. Om sub-domein gebaseerde routing in te stellen, dien je de proxy vermelding aan je browser proxy instellingen toe te voegen,`,
details_1: `Om je te helpen met het configureren van je proxy, zijn er instructies beschikbaar voor {windowsLink}, {macOSLink} or {ubuntuLink}.`,
details_macos: `macOS`,
details_ubuntu: `Ubuntu`,
details_windows: `Windows`,
overview_0: `Met de proxy instellingen heb je de mogelijkheid om via een makkelijk te onthouden adres toegang te verkrijgen tot Parity en alle onderliggende decentrale applicaties.`,
label: `proxy`
}, },
views: { views: {
label: 'Weergaven',
accounts: { accounts: {
label: 'Accounts' description: `Een overzicht van alle aan deze Parity installatie verbonden accounts, inclusief geimporteerde accounts. Verzend transacties, ontvang inkomende transacties, berheer je saldo en financier je accounts.`,
label: `Accounts`
}, },
addresses: { addresses: {
label: 'Adresboek' description: `Een overzicht van alle contacten en adresboek items die door deze Parity installatie worden beheerd. Monitor en volg accounts waarbij je transactie details met slechts een muisklik kunt weergeven.`,
label: `Adresboek`
}, },
apps: { apps: {
label: 'Applicaties' description: `Gedistibueerde applicaties die gebruik maken van het onderliggende Ethereum netwerk. Voeg applicaties toe, beheer je applicatie portfolio en maak gebruik van applicaties op het wereldwijde netwerk.`,
label: `Applicaties`
}, },
contracts: { contracts: {
label: 'Contracten' description: `Monitor, volg en maak gebruik van specifieke contracten die op het netwerk zijn gezet. Dit is een meer technisch gerichte omgeving, voornamelijk bedoeld voor geavanceerde gebruikers die de werking van bepaalde contracten goed begrijpen.`,
label: `Contracten`
}, },
overview_0: `Beheer de beschikbare weergaven van deze interface en selecteer enkel de delen van de applicatie die voor jou van belang zijn.`,
status: { overview_1: `Ben je een eind gebruiker? De standaard instellingen zijn geschikt voor zowel beginners als gevorderde gebruikers.`,
label: 'Status' overview_2: `Ben je een ontwikkelaar? Voeg enkele functies toe om je contracten te beheren en gebruik te maken van gedecentraliseerde applicaties.`,
}, overview_3: `Ben je een miner of run je een grootschalige node? Voeg enkele functies toe om je alle informatie te geven die je nodig hebt om je node te monitoren.`,
signer: {
label: 'Signer'
},
settings: { settings: {
label: 'Instellingen' description: `Deze weergave. Hiermee kun je Parity aan passen in termen van opties, bediening en look en feel.`,
label: `Instellingen`
},
signer: {
description: `Het beveiligde transactie beheergebied van de applicatie waar je elke uitgaande transactie die je hebt gemaakt met Parity evenals de transacties die in de wachtrij zijn geplaatst door gedistribueerde applicaties kan goedkeuren.`,
label: `Signer`
},
status: {
description: `Volg hoe de Parity node zijn werk doet en je verbind met het netwerk en bekijk de logboeken van de momenteel draaiende node met mining details (indien geconfigureerd en ingeschakeld).`,
label: `Status`
},
label: `weergaven`,
home: {
label: `Thuis`
} }
} },
label: `instellingen`
}; };

66
js/src/i18n/nl/shapeshift.js Executable file
View 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/>.
export default {
awaitingDepositStep: {
awaitingConfirmation: `Wachten tot bevestigd is dat je {typeSymbol} storting op het account van het wisselkantoor is aangekomen.`,
awaitingDeposit: `{shapeshiftLink} is aan het wachten op {typeSymbol} storting. Verzend de valuta vanuit je {typeSymbol} netwerk client naar -`,
minimumMaximum: `{minimum} minimum, {maximum} maximum`
},
awaitingExchangeStep: {
awaitingCompletion: `Wachten op de voltooiing van het omwisselen van de valuta en op de overschrijving van de valuta naar je Parity account.`,
receivedInfo: `{shapeshiftLink} heeft een storting ontvangen van -`
},
button: {
cancel: `Annuleer`,
done: `Sluit`,
shift: `Wissel valuta om`
},
completedStep: {
completed: `{shapeshiftLink} heeft het omwisselen van de valuta voltooid.`,
parityFunds: `De saldo wijziging zal spoedig in je Parity client worden weergegeven.`
},
errorStep: {
info: `Het omwisselen van de valuta via {shapeshiftLink} is mislukt door een fout bij het wisselkantoor. De ontvangen foutmelding van het wisselkantoor is als volgt:`
},
optionsStep: {
noPairs: `Er is momenteel geen wisselkoers voor het valuta-paar beschikbaar om de transactie mee uit te voeren.`,
returnAddr: {
hint: `het retouradres voor wanneer het verzenden mislukt`,
label: `(optioneel) {coinSymbol} retouradres`
},
terms: {
label: `Ik begrijp dat ShapeShift.io een dienst is van een derde partij en dat bij gebruik van deze service de overdracht van informatie en/of financiele middelen volledig buiten het beheer van Parity vallen`
},
typeSelect: {
hint: `het type crypto valuta om te wisselen`,
label: `verzend naar account vanuit`
}
},
price: {
minMax: `({minimum} minimum, {maximum} maximum)`
},
title: {
completed: `voltooid`,
deposit: `wachten op storting`,
details: `details`,
error: `omwisselen mislukt`,
exchange: `wachten op omwisselen`
},
warning: {
noPrice: `Geen prijs gevonden voor het gekozen type`
}
};

21
js/src/i18n/nl/tabBar.js Normal file
View File

@ -0,0 +1,21 @@
// 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 {
tooltip: {
overview: `navigeer tussen de verschillende onderdelen en weergaven in de applicatie, wissel tussen de account weergave, token weergave en gedistribueerde applicatie weergave`
}
};

View File

@ -0,0 +1,27 @@
// 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 {
advanced: {
data: {
hint: `de data om door te geven met de transactie`,
label: `transactie data`
}
},
warning: {
wallet_spent_limit: `Deze transactie waarde is boven de toegestane dag limiet en zal moeten worden bevestigd door andere eigenaren.`
}
};

View File

@ -0,0 +1,39 @@
// 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 {
condition: {
block: {
hint: `Het minimum blok voor het verzenden`,
label: `Transactie verzend blok`
},
blocknumber: `Verzend na bloknummer`,
date: {
hint: `De minimale datum voor het verzenden`,
label: `Transactie verzend datum`
},
datetime: `Verzend na datum & tijdstip`,
label: `Conditie waarbij transactie activeert`,
none: `Geen condities`,
time: {
hint: `Het minimale tijdstip voor het verzenden`,
label: `Transactie verzend tijdstip`
}
},
gas: {
info: `Je kunt de gas prijs kiezen op basis van de gas prijs van de transacties die recentelijk in de blokken werden opgenomen. Een lagere gas prijs betekend een goedkopere transactie. Een hogere gas prijs betekend dat je transactie sneller in een blok wordt opgenomen.`
}
};

84
js/src/i18n/nl/ui.js Normal file
View File

@ -0,0 +1,84 @@
// 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 {
balance: {
none: `Er zijn geen tegoeden gekoppeld aan dit account`
},
blockStatus: {
bestBlock: `{blockNumber} beste blok`,
syncStatus: `{currentBlock}/{highestBlock} synchroniseren`,
warpRestore: `{percentage}% warp restore`,
warpStatus: `, {percentage}% historic`
},
confirmDialog: {
no: `nee`,
yes: `ja`
},
identityName: {
null: `NUL`,
unnamed: `NAAMLOOS`
},
passwordStrength: {
label: `wachtwoord sterkte`
},
tooltips: {
button: {
done: `Klaar`,
next: `Volgende`,
skip: `Overslaan`
}
},
txHash: {
confirmations: `{count} {value, plural, one {confirmation} other {confirmations}}`,
oog: `De transactie heeft misschien al zijn gas verbruikt. Probeer het opnieuw met meer gas.`,
posted: `De transactie is op het netwerk geplaatst met hash {hashLink}`,
waiting: `wachten op bevestigingen`
},
verification: {
gatherData: {
accountHasRequested: {
false: `Je hebt nog geen verificatie aangevraagd voor dit account.`,
pending: `Aan het controleren of je verificatie hebt aangevraagd…`,
true: `Je hebt al verificatie aangevraagd voor dit account.`
},
accountIsVerified: {
false: `Je account is nog niet geverifieerd`,
pending: `Aan het controleren of je account is geverifieerd…`,
true: `Je account is al geverifieerd.`
},
email: {
hint: `de code zal naar dit adres worden verzonden`,
label: `e-mail adres`
},
fee: `De extra vergoeding is {amount} ETH.`,
isAbleToRequest: {
pending: `Valideren van je invoer…`
},
isServerRunning: {
false: `De verificatie server is niet actief.`,
pending: `Controleren of de verificatie server actief is…`,
true: `De verificatie server is actief.`
},
nofee: `Er zijn geen extra kosten.`,
phoneNumber: {
hint: `De SMS zal naar dit nummer worden verstuurd`,
label: `telefoonnummer in internationaal formaat`
},
termsOfService: `Ik ga akkoord met de voorwaarden en condities hieronder.`
}
}
};

View File

@ -0,0 +1,44 @@
// 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 {
busy: `Parity wordt momenteel bijgewerkt naar versie {newversion}`,
button: {
close: `sluiten`,
done: `klaar`,
upgrade: `werk nu bij`
},
completed: `Het bijwerken naar Parity {newversion} is succesvol voltooid.`,
consensus: {
capable: `Je huidige versie van Parity voldoet aan de netwerk vereisten.`,
capableUntil: `Je huidige versie van Parity voldoet aan de netwerk vereisten tot aan blok {blockNumber}`,
incapableSince: `Je huidige versie van Parity voldoet aan de netwerk vereisten vanaf blok {blockNumber}`,
unknown: `Je huidige versie van Parity voldoet aan de netwerk vereisten.`
},
failed: `Het bijwerken naar Parity {newversion} gaf een fout en is mislukt.`,
info: {
upgrade: `Een nieuwe versie van Parity, version {newversion} is beschikbaar als upgrade vanaf je huidige versie {currentversion}`
},
step: {
completed: `bijwerken voltooid`,
error: `fout`,
info: `nieuwe versie beschikbaar`,
updating: `Parity bijwerken`
},
version: {
unknown: `onbekend`
}
};

75
js/src/i18n/nl/vaults.js Normal file
View File

@ -0,0 +1,75 @@
// 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 {
accounts: {
button: {
cancel: `Annuleer`,
execute: `Stel in`
},
empty: `Er zitten geen accounts in deze kluis`,
title: `Beheer Kluis Accounts`
},
button: {
accounts: `accounts`,
add: `Maak kluis`,
close: `sluit kluis`,
open: `open kluis`
},
confirmClose: {
info: `Je staat op het punt op een kluis te sluiten. Alle aan deze kluis verbonden accounts zullen niet meer zichtbaar zijn na het voltooien van deze actie. Om deze accounts weer zichtbaar te maken dien je de kluis weer te openen.`,
title: `Sluit Kluis`
},
confirmOpen: {
info: `Je staat op het punt om een kluis te openen. Na de bevestiging met je wachtwoord zullen alle aan deze kluis verbonden account zichtbaar worden. Wanneer je de kluis weer sluit zullen deze accounts weer onzichtbaar worden, tot je de kluis weer opent.`,
password: {
hint: `het wachtwoord wat je hebt gekozen bij het aanmaken van de kluis`,
label: `kluis wachtwoord`
},
title: `Open Kluis`
},
create: {
button: {
close: `sluit`,
vault: `maak kluis`
},
description: {
hint: `een uitgebereide omschrijving voor de kluis`
},
descriptions: {
label: `(optioneel) omschrijving`
},
hint: {
hint: `(optioneel) een hint om je het wachtwoord te helpen herinneren`,
label: `wachtwoord hint`
},
name: {
hint: `een naam voor de kluis`,
label: `kluis naam`
},
password: {
hint: `een sterk en uniek wachtwoord`,
label: `wachtwoord`
},
password2: {
hint: `verifieer je wachtwoord`,
label: `wachtwoord (herhaal)`
},
title: `Maak een nieuwe kluis aan`
},
empty: `Er zijn momenteel geen kluizen om weer tegeven.`,
title: `Kluis Beheer`
};

View File

@ -0,0 +1,57 @@
// 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 {
changes: {
modificationString: `Om je wijzigingen door te voeren zullen
andere eigenaren deze zelfde wijzigingen moeten verzenden. Om het
makkelijk te maken kunnen ze deze string kopieren-plakken:`,
none: `Er zijn van deze Wallet geen instellingen gewijzigd.`,
overview: `Je staat op het punt om de volgende wijzignen te maken`
},
edit: {
message: `Om de instellingen van dit contract de wijzigen zullen
minimaal {owners, number} {owners, plural, one {owner } other {owners }} precies dezelfde
wijzigingen moeten verzenden. Je kunt hier de wijzigingen in string-vorm plakken.`
},
modifications: {
daylimit: {
hint: `hoeveelheid uit te geven ETH zonder bevestiging met wachtwoord`,
label: `wallet dag limiet`
},
fromString: {
label: `wijzigingen`
},
owners: {
label: `andere wallet eigenaren`
},
required: {
hint: `vereiste aantal eigenaren om een transactie goed te keuren`,
label: `vereiste eigenaren`
},
sender: {
hint: `verzend wijzigingen als deze eigenaar`,
label: `van account (wallet eigenaar)`
}
},
rejected: {
busyStep: {
state: `De wallet instellingen zullen niet worden gewijzigd. Je kunt dit venster veilig sluiten.`,
title: `De wijzigingen zijn afgewezen.`
},
title: `afgewezen`
}
};

19
js/src/i18n/nl/web.js Normal file
View File

@ -0,0 +1,19 @@
// 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 {
requestToken: `Aanvragen toegangs token...`
};

View File

@ -6,12 +6,15 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<title><%= htmlWebpackPlugin.options.title %></title> <title><%= htmlWebpackPlugin.options.title %></title>
<style> <style>
html {
background: white;
}
html, body, #container { html, body, #container {
width: 100%; width: 100%;
height: 100%; height: 100%;
margin: 0; margin: 0;
padding: 0; padding: 0;
background: white;
font-family: 'Roboto', sans-serif; font-family: 'Roboto', sans-serif;
font-size: 16px; font-size: 16px;
font-weight: 300; font-weight: 300;

View File

@ -100,10 +100,10 @@ const transactionDetails = {
type: Quantity, type: Quantity,
desc: 'The S field of the signature.' desc: 'The S field of the signature.'
}, },
minBlock: { condition: {
type: BlockNumber, type: Object,
optional: true, optional: true,
desc: 'Block number, tag or `null`.' desc: 'Conditional submission, Block number in `block` or timestamp in `time` or `null`.'
} }
}; };

View File

@ -14,13 +14,12 @@
// 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 ContentAdd from 'material-ui/svg-icons/content/add';
import ContentClear from 'material-ui/svg-icons/content/clear';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { Button, Form, Input, InputAddress, Modal } from '~/ui'; import { Button, Form, Input, InputAddress, ModalBox, Portal } from '~/ui';
import { AddIcon, AddressesIcon, CancelIcon } from '~/ui/Icons';
import Store from './store'; import Store from './store';
@ -46,8 +45,10 @@ export default class AddAddress extends Component {
render () { render () {
return ( return (
<Modal <Portal
actions={ this.renderDialogActions() } buttons={ this.renderDialogActions() }
onClose={ this.onClose }
open
title={ title={
<FormattedMessage <FormattedMessage
id='addAddress.label' id='addAddress.label'
@ -57,16 +58,17 @@ export default class AddAddress extends Component {
visible visible
> >
{ this.renderFields() } { this.renderFields() }
</Modal> </Portal>
); );
} }
renderDialogActions () { renderDialogActions () {
const { hasError } = this.store; const { hasError } = this.store;
return ([ return [
<Button <Button
icon={ <ContentClear /> } icon={ <CancelIcon /> }
key='cancel'
label={ label={
<FormattedMessage <FormattedMessage
id='addAddress.button.close' id='addAddress.button.close'
@ -74,11 +76,11 @@ export default class AddAddress extends Component {
/> />
} }
onClick={ this.onClose } onClick={ this.onClose }
ref='closeButton'
/>, />,
<Button <Button
disabled={ hasError } disabled={ hasError }
icon={ <ContentAdd /> } icon={ <AddIcon /> }
key='save'
label={ label={
<FormattedMessage <FormattedMessage
id='addAddress.button.add' id='addAddress.button.add'
@ -86,72 +88,79 @@ export default class AddAddress extends Component {
/> />
} }
onClick={ this.onAdd } onClick={ this.onAdd }
ref='addButton'
/> />
]); ];
} }
renderFields () { renderFields () {
const { address, addressError, description, name, nameError } = this.store; const { address, addressError, description, name, nameError } = this.store;
return ( return (
<Form> <ModalBox
<InputAddress icon={ <AddressesIcon /> }
allowCopy={ false } summary={
disabled={ !!this.props.address } <FormattedMessage
error={ addressError } id='addAddress.header'
hint={ defaultMessage='To add a new entry to your addressbook, you need the network address of the account and can supply an optional description. Once added it will reflect in your address book.'
<FormattedMessage />
id='addAddress.input.address.hint' }
defaultMessage='the network address for the entry' >
/> <Form>
} <InputAddress
label={ allowCopy={ false }
<FormattedMessage autoFocus
id='addAddress.input.address.label' disabled={ !!this.props.address }
defaultMessage='network address' error={ addressError }
/> hint={
} <FormattedMessage
onChange={ this.onEditAddress } id='addAddress.input.address.hint'
ref='inputAddress' defaultMessage='the network address for the entry'
value={ address } />
/> }
<Input label={
error={ nameError } <FormattedMessage
hint={ id='addAddress.input.address.label'
<FormattedMessage defaultMessage='network address'
id='addAddress.input.name.hint' />
defaultMessage='a descriptive name for the entry' }
/> onChange={ this.onEditAddress }
} value={ address }
label={ />
<FormattedMessage <Input
id='addAddress.input.name.label' error={ nameError }
defaultMessage='address name' hint={
/> <FormattedMessage
} id='addAddress.input.name.hint'
onChange={ this.onEditName } defaultMessage='a descriptive name for the entry'
ref='inputName' />
value={ name } }
/> label={
<Input <FormattedMessage
hint={ id='addAddress.input.name.label'
<FormattedMessage defaultMessage='address name'
id='addAddress.input.description.hint' />
defaultMessage='an expanded description for the entry' }
/> onChange={ this.onEditName }
} value={ name }
label={ />
<FormattedMessage <Input
id='addAddress.input.description.label' hint={
defaultMessage='(optional) address description' <FormattedMessage
/> id='addAddress.input.description.hint'
} defaultMessage='an expanded description for the entry'
onChange={ this.onEditDescription } />
ref='inputDescription' }
value={ description } label={
/> <FormattedMessage
</Form> id='addAddress.input.description.label'
defaultMessage='(optional) address description'
/>
}
onChange={ this.onEditDescription }
value={ description }
/>
</Form>
</ModalBox>
); );
} }
@ -169,7 +178,7 @@ export default class AddAddress extends Component {
onAdd = () => { onAdd = () => {
this.store.add(); this.store.add();
this.props.onClose(); this.onClose();
} }
onClose = () => { onClose = () => {

View File

@ -21,7 +21,7 @@ import { connect } from 'react-redux';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { newError } from '~/redux/actions'; import { newError } from '~/redux/actions';
import { Button, Modal, Form, Input, InputAddress, RadioButtons } from '~/ui'; import { Button, Form, Input, InputAddress, Portal, RadioButtons } from '~/ui';
import { AddIcon, CancelIcon, NextIcon, PrevIcon } from '~/ui/Icons'; import { AddIcon, CancelIcon, NextIcon, PrevIcon } from '~/ui/Icons';
import Store from './store'; import Store from './store';
@ -44,9 +44,11 @@ class AddContract extends Component {
const { step } = this.store; const { step } = this.store;
return ( return (
<Modal <Portal
actions={ this.renderDialogActions() } activeStep={ step }
current={ step } buttons={ this.renderDialogActions() }
onClose={ this.onClose }
open
steps={ [ steps={ [
<FormattedMessage <FormattedMessage
id='addContract.title.type' id='addContract.title.type'
@ -59,10 +61,9 @@ class AddContract extends Component {
key='details' key='details'
/> />
] } ] }
visible
> >
{ this.renderStep() } { this.renderStep() }
</Modal> </Portal>
); );
} }
@ -159,6 +160,7 @@ class AddContract extends Component {
return ( return (
<Form> <Form>
<InputAddress <InputAddress
autoFocus
error={ addressError } error={ addressError }
hint={ hint={
<FormattedMessage <FormattedMessage

View File

@ -19,10 +19,6 @@
flex-direction: column; flex-direction: column;
} }
.container {
overflow-y: auto;
}
.description { .description {
margin-top: .5em !important; margin-top: .5em !important;
} }
@ -49,26 +45,3 @@
opacity: 0.75; opacity: 0.75;
} }
} }
.selectIcon {
position: absolute;
right: 0.5em;
top: 0.5em;
}
.selected,
.unselected {
position: relative;
}
.unselected {
background: rgba(0, 0, 0, 0.4) !important;
.selectIcon {
opacity: 0.15;
}
}
.selected {
background: rgba(255, 255, 255, 0.15) !important;
}

View File

@ -18,8 +18,7 @@ import { observer } from 'mobx-react';
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { DappCard, Portal, SectionList } from '~/ui'; import { DappCard, Portal, SelectionList } from '~/ui';
import { CheckIcon } from '~/ui/Icons';
import styles from './addDapps.css'; import styles from './addDapps.css';
@ -48,45 +47,43 @@ export default class AddDapps extends Component {
/> />
} }
> >
<div className={ styles.container }> <div className={ styles.warning } />
<div className={ styles.warning } /> {
{ this.renderList(store.sortedLocal, store.displayApps,
this.renderList(store.sortedLocal, store.displayApps, <FormattedMessage
<FormattedMessage id='dapps.add.local.label'
id='dapps.add.local.label' defaultMessage='Applications locally available'
defaultMessage='Applications locally available' />,
/>, <FormattedMessage
<FormattedMessage id='dapps.add.local.desc'
id='dapps.add.local.desc' defaultMessage='All applications installed locally on the machine by the user for access by the Parity client.'
defaultMessage='All applications installed locally on the machine by the user for access by the Parity client.' />
/> )
) }
} {
{ this.renderList(store.sortedBuiltin, store.displayApps,
this.renderList(store.sortedBuiltin, store.displayApps, <FormattedMessage
<FormattedMessage id='dapps.add.builtin.label'
id='dapps.add.builtin.label' defaultMessage='Applications bundled with Parity'
defaultMessage='Applications bundled with Parity' />,
/>, <FormattedMessage
<FormattedMessage id='dapps.add.builtin.desc'
id='dapps.add.builtin.desc' defaultMessage='Experimental applications developed by the Parity team to show off dapp capabilities, integration, experimental features and to control certain network-wide client behaviour.'
defaultMessage='Experimental applications developed by the Parity team to show off dapp capabilities, integration, experimental features and to control certain network-wide client behaviour.' />
/> )
) }
} {
{ this.renderList(store.sortedNetwork, store.displayApps,
this.renderList(store.sortedNetwork, store.displayApps, <FormattedMessage
<FormattedMessage id='dapps.add.network.label'
id='dapps.add.network.label' defaultMessage='Applications on the global network'
defaultMessage='Applications on the global network' />,
/>, <FormattedMessage
<FormattedMessage id='dapps.add.network.desc'
id='dapps.add.network.desc' defaultMessage='These applications are not affiliated with Parity nor are they published by Parity. Each remain under the control of their respective authors. Please ensure that you understand the goals for each application before interacting.'
defaultMessage='These applications are not affiliated with Parity nor are they published by Parity. Each remain under the control of their respective authors. Please ensure that you understand the goals for each application before interacting.' />
/> )
) }
}
</div>
</Portal> </Portal>
); );
} }
@ -102,9 +99,11 @@ export default class AddDapps extends Component {
<div className={ styles.header }>{ header }</div> <div className={ styles.header }>{ header }</div>
<div className={ styles.byline }>{ byline }</div> <div className={ styles.byline }>{ byline }</div>
</div> </div>
<SectionList <SelectionList
isChecked={ this.isVisible }
items={ items } items={ items }
noStretch noStretch
onSelectClick={ this.onSelect }
renderItem={ this.renderApp } renderItem={ this.renderApp }
/> />
</div> </div>
@ -112,30 +111,27 @@ export default class AddDapps extends Component {
} }
renderApp = (app) => { renderApp = (app) => {
const { store } = this.props;
const isVisible = store.displayApps[app.id].visible;
const onClick = () => {
if (isVisible) {
store.hideApp(app.id);
} else {
store.showApp(app.id);
}
};
return ( return (
<DappCard <DappCard
app={ app } app={ app }
className={
isVisible
? styles.selected
: styles.unselected
}
key={ app.id } key={ app.id }
onClick={ onClick } />
>
<CheckIcon className={ styles.selectIcon } />
</DappCard>
); );
} }
isVisible = (app) => {
const { store } = this.props;
return store.displayApps[app.id].visible;
}
onSelect = (app) => {
const { store } = this.props;
if (this.isVisible(app)) {
store.hideApp(app.id);
} else {
store.showApp(app.id);
}
}
} }

View File

@ -18,7 +18,9 @@ import { observer } from 'mobx-react';
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { Form, Input, InputAddress } from '~/ui'; import { IdentityIcon, Input, QrCode, Title } from '~/ui';
import styles from '../createAccount.css';
@observer @observer
export default class AccountDetails extends Component { export default class AccountDetails extends Component {
@ -27,45 +29,52 @@ export default class AccountDetails extends Component {
} }
render () { render () {
const { address, name } = this.props.store; const { address, description, name } = this.props.store;
return ( return (
<Form> <div className={ styles.details }>
<Input <div className={ styles.info }>
allowCopy <div className={ styles.account }>
hint={ <div className={ styles.name }>
<FormattedMessage <IdentityIcon
id='createAccount.accountDetails.name.hint' address={ address }
defaultMessage='a descriptive name for the account' className={ styles.icon }
/> center
} />
label={ <Title
<FormattedMessage byline={ description }
id='createAccount.accountDetails.name.label' className={ styles.title }
defaultMessage='account name' title={ name }
/> />
} </div>
readOnly <div className={ styles.description }>
value={ name } <Input
/> readOnly
<InputAddress hideUnderline
disabled hint={
hint={ <FormattedMessage
<FormattedMessage id='createAccount.accountDetails.address.hint'
id='createAccount.accountDetails.address.hint' defaultMessage='the network address for the account'
defaultMessage='the network address for the account' />
/> }
} label={
label={ <FormattedMessage
<FormattedMessage id='createAccount.accountDetails.address.label'
id='createAccount.accountDetails.address.label' defaultMessage='address'
defaultMessage='address' />
/> }
} value={ address }
value={ address } allowCopy={ address }
/> />
{ this.renderPhrase() } { this.renderPhrase() }
</Form> </div>
</div>
<QrCode
className={ styles.qr }
value={ address }
/>
</div>
</div>
); );
} }

View File

@ -1,23 +0,0 @@
/* 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/>.
*/
.address {
color: #999;
line-height: 1.618em;
padding-left: 2em;
padding-top: 1em;
}

View File

@ -18,7 +18,10 @@ import { observer } from 'mobx-react';
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import styles from './accountDetailsGeth.css'; import { SectionList } from '~/ui';
import GethCard from '../GethCard';
import styles from '../createAccount.css';
@observer @observer
export default class AccountDetailsGeth extends Component { export default class AccountDetailsGeth extends Component {
@ -27,33 +30,37 @@ export default class AccountDetailsGeth extends Component {
} }
render () { render () {
const { gethAddresses } = this.props.store; const { gethAccountsAvailable, gethImported } = this.props.store;
const accounts = gethAccountsAvailable.filter((account) => gethImported.includes(account.address));
return ( return (
<div> <div>
<div> <div className={ styles.summary }>
<FormattedMessage <FormattedMessage
id='createAccount.accountDetailsGeth.imported' id='createAccount.accountDetailsGeth.imported'
defaultMessage='You have imported {number} addresses from the Geth keystore:' defaultMessage='You have completed the import of {number} addresses from the Geth keystore. These will now be available in your accounts list as a normal account, along with their associated balances on the network.'
values={ { values={ {
number: gethAddresses.length number: gethImported.length
} } } }
/> />
</div> </div>
<div className={ styles.address }> <SectionList
{ this.formatAddresses(gethAddresses) } items={ accounts }
</div> noStretch
renderItem={ this.renderAccount }
/>
</div> </div>
); );
} }
formatAddresses (addresses) { renderAccount = (account, index) => {
return addresses.map((address, index) => { return (
const comma = !index <GethCard
? '' address={ account.address }
: ((index === addresses.length - 1) ? ' & ' : ', '); balance={ account.balance }
name={ `Geth Import ${index + 1}` }
return `${comma}${address}`; />
}).join(''); );
} }
} }

View File

@ -39,22 +39,4 @@ describe('modals/CreateAccount/AccountDetailsGeth', () => {
it('renders with defaults', () => { it('renders with defaults', () => {
expect(render()).to.be.ok; expect(render()).to.be.ok;
}); });
describe('utility', () => {
describe('formatAddresses', () => {
let instance;
beforeEach(() => {
instance = component.instance();
});
it('renders a single item', () => {
expect(instance.formatAddresses(['one'])).to.equal('one');
});
it('renders multiple items', () => {
expect(instance.formatAddresses(['one', 'two', 'three'])).to.equal('one, two & three');
});
});
});
}); });

View File

@ -17,10 +17,105 @@
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { RadioButton, RadioButtonGroup } from 'material-ui/RadioButton';
import { Container, SelectionList, Title } from '~/ui';
import TypeIcon from '../TypeIcon';
import styles from '../createAccount.css'; import styles from '../createAccount.css';
const TYPES = [
{
description: (
<FormattedMessage
id='createAccount.creationType.fromNew.description'
defaultMessage='Selecting your identity icon and specifying the password'
/>
),
label: (
<FormattedMessage
id='createAccount.creationType.fromNew.label'
defaultMessage='New Account'
/>
),
key: 'fromNew'
},
{
description: (
<FormattedMessage
id='createAccount.creationType.fromPhrase.description'
defaultMessage='Recover using a previously stored recovery phrase and new password'
/>
),
label: (
<FormattedMessage
id='createAccount.creationType.fromPhrase.label'
defaultMessage='Recovery phrase'
/>
),
key: 'fromPhrase'
},
{
description: (
<FormattedMessage
id='createAccount.creationType.fromGeth.description'
defaultMessage='Import accounts from the Geth keystore with the original password'
/>
),
label: (
<FormattedMessage
id='createAccount.creationType.fromGeth.label'
defaultMessage='Geth keystore'
/>
),
key: 'fromGeth'
},
{
description: (
<FormattedMessage
id='createAccount.creationType.fromJSON.description'
defaultMessage='Import an industry-standard JSON keyfile with the original password'
/>
),
label: (
<FormattedMessage
id='createAccount.creationType.fromJSON.label'
defaultMessage='JSON file'
/>
),
key: 'fromJSON'
},
{
description: (
<FormattedMessage
id='createAccount.creationType.fromPresale.description'
defaultMessage='Import an Ethereum presale wallet file with the original password'
/>
),
label: (
<FormattedMessage
id='createAccount.creationType.fromPresale.label'
defaultMessage='Presale wallet'
/>
),
key: 'fromPresale'
},
{
description: (
<FormattedMessage
id='createAccount.creationType.fromRaw.description'
defaultMessage='Enter a previously created raw private key with a new password'
/>
),
label: (
<FormattedMessage
id='createAccount.creationType.fromRaw.label'
defaultMessage='Private key'
/>
),
key: 'fromRaw'
}
];
@observer @observer
export default class CreationType extends Component { export default class CreationType extends Component {
static propTypes = { static propTypes = {
@ -31,74 +126,58 @@ export default class CreationType extends Component {
const { createType } = this.props.store; const { createType } = this.props.store;
return ( return (
<div className={ styles.spaced }> <div>
<RadioButtonGroup <div className={ styles.summary }>
defaultSelected={ createType } <FormattedMessage
name='creationType' id='createAccount.creationType.info'
onChange={ this.onChange } defaultMessage='Please select the type of account you want to create. Either create an account via name & password, or import it from a variety of existing sources. From here the wizard will guid you through the process of completing your account creation.'
>
<RadioButton
label={
<FormattedMessage
id='createAccount.creationType.fromNew.label'
defaultMessage='Create new account manually'
/>
}
value='fromNew'
/> />
<RadioButton </div>
label={ { this.renderList(createType) }
<FormattedMessage
id='createAccount.creationType.fromPhrase.label'
defaultMessage='Recover account from recovery phrase'
/>
}
value='fromPhrase'
/>
<RadioButton
label={
<FormattedMessage
id='createAccount.creationType.fromGeth.label'
defaultMessage='Import accounts from Geth keystore'
/>
}
value='fromGeth'
/>
<RadioButton
label={
<FormattedMessage
id='createAccount.creationType.fromJSON.label'
defaultMessage='Import account from a backup JSON file'
/>
}
value='fromJSON'
/>
<RadioButton
label={
<FormattedMessage
id='createAccount.creationType.fromPresale.label'
defaultMessage='Import account from an Ethereum pre-sale wallet'
/>
}
value='fromPresale'
/>
<RadioButton
label={
<FormattedMessage
id='createAccount.creationType.fromRaw.label'
defaultMessage='Import raw private key'
/>
}
value='fromRaw'
/>
</RadioButtonGroup>
</div> </div>
); );
} }
onChange = (event) => { renderList () {
return (
<SelectionList
isChecked={ this.isSelected }
items={ TYPES }
noStretch
onSelectClick={ this.onChange }
renderItem={ this.renderItem }
/>
);
}
renderItem = (item) => {
return (
<Container>
<div className={ styles.selectItem }>
<TypeIcon
className={ styles.icon }
store={ this.props.store }
type={ item.key }
/>
<Title
byline={ item.description }
className={ styles.info }
title={ item.label }
/>
</div>
</Container>
);
}
isSelected = (item) => {
const { createType } = this.props.store;
return item.key === createType;
}
onChange = (item) => {
const { store } = this.props; const { store } = this.props;
store.setCreateType(event.target.value); store.setCreateType(item.key);
} }
} }

View File

@ -22,6 +22,7 @@ import { createStore } from '../createAccount.test.js';
import CreationType from './'; import CreationType from './';
let component; let component;
let instance;
let store; let store;
function render () { function render () {
@ -31,6 +32,7 @@ function render () {
store={ store } store={ store }
/> />
); );
instance = component.instance();
return component; return component;
} }
@ -44,28 +46,10 @@ describe('modals/CreateAccount/CreationType', () => {
expect(component).to.be.ok; expect(component).to.be.ok;
}); });
describe('selector', () => {
const SELECT_TYPE = 'fromRaw';
let selector;
beforeEach(() => {
store.setCreateType(SELECT_TYPE);
selector = component.find('RadioButtonGroup');
});
it('renders the selector', () => {
expect(selector.get(0)).to.be.ok;
});
it('passes the store type to defaultSelected', () => {
expect(selector.props().defaultSelected).to.equal(SELECT_TYPE);
});
});
describe('events', () => { describe('events', () => {
describe('onChange', () => { describe('onChange', () => {
beforeEach(() => { beforeEach(() => {
component.instance().onChange({ target: { value: 'testing' } }); instance.onChange({ key: 'testing' });
}); });
it('changes the store createType', () => { it('changes the store createType', () => {

View 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 React, { Component, PropTypes } from 'react';
import imagesEthereum from '~/../assets/images/contracts/ethereum-black-64x64.png';
import { AccountCard } from '~/ui';
export default class GethCard extends Component {
static propTypes = {
address: PropTypes.string.isRequired,
balance: PropTypes.string.isRequired,
name: PropTypes.string.isRequired
}
render () {
const { address, balance, name } = this.props;
return (
<AccountCard
account={ {
address,
name
} }
balance={ {
tokens: [ {
value: balance,
token: {
image: imagesEthereum,
native: true,
tag: 'ETH'
}
} ]
} }
/>
);
}
}

View 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 './gethCard';

View File

@ -48,6 +48,7 @@ export default class CreateAccount extends Component {
return ( return (
<Form> <Form>
<Input <Input
autoFocus
error={ nameError } error={ nameError }
hint={ hint={
<FormattedMessage <FormattedMessage

View File

@ -59,7 +59,7 @@ describe('modals/CreateAccount/NewAccount', () => {
}); });
it('creates initial accounts', () => { it('creates initial accounts', () => {
expect(Object.keys(instance.state.accounts).length).to.equal(5); expect(Object.keys(instance.state.accounts).length).to.equal(7);
}); });
it('sets the initial selected value', () => { it('sets the initial selected value', () => {

View File

@ -17,11 +17,11 @@
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { Checkbox } from 'material-ui';
import { IdentityIcon } from '~/ui'; import { SelectionList } from '~/ui';
import styles from './newGeth.css'; import GethCard from '../GethCard';
import styles from '../createAccount.css';
@observer @observer
export default class NewGeth extends Component { export default class NewGeth extends Component {
@ -36,60 +36,62 @@ export default class NewGeth extends Component {
render () { render () {
const { gethAccountsAvailable, gethAddresses } = this.props.store; const { gethAccountsAvailable, gethAddresses } = this.props.store;
if (!gethAccountsAvailable.length) { return gethAccountsAvailable.length
return ( ? (
<div className={ styles.list }> <div>
<div className={ styles.summary }>
<FormattedMessage
id='createAccount.newGeth.available'
defaultMessage='There are currently {count} importable keys available from the Geth keystore which are not already available on your Parity instance. Select the accounts you wish to import and move to the next step to complete the import.'
values={ {
count: gethAccountsAvailable.length
} }
/>
</div>
{ this.renderList(gethAccountsAvailable, gethAddresses) }
</div>
)
: (
<div className={ styles.summary }>
<FormattedMessage <FormattedMessage
id='createAccount.newGeth.noKeys' id='createAccount.newGeth.noKeys'
defaultMessage='There are currently no importable keys available from the Geth keystore, which are not already available on your Parity instance' defaultMessage='There are currently no importable keys available from the Geth keystore, which are not already available on your Parity instance'
/> />
</div> </div>
); );
} }
const checkboxes = gethAccountsAvailable.map((account) => {
const onSelect = (event) => this.onSelectAddress(event, account.address);
const label = (
<div className={ styles.selection }>
<div className={ styles.icon }>
<IdentityIcon
address={ account.address }
center
inline
/>
</div>
<div className={ styles.detail }>
<div className={ styles.address }>
{ account.address }
</div>
<div className={ styles.balance }>
{ account.balance } ETH
</div>
</div>
</div>
);
return (
<Checkbox
checked={ gethAddresses.includes(account.address) }
key={ account.address }
label={ label }
onCheck={ onSelect }
/>
);
});
renderList (gethAccountsAvailable) {
return ( return (
<div className={ styles.list }> <SelectionList
{ checkboxes } isChecked={ this.isSelected }
</div> items={ gethAccountsAvailable }
noStretch
onSelectClick={ this.onSelect }
renderItem={ this.renderAccount }
/>
); );
} }
onSelectAddress = (event, address) => { renderAccount = (account, index) => {
return (
<GethCard
address={ account.address }
balance={ account.balance }
name={ `Geth Account ${index + 1}` }
/>
);
}
isSelected = (account) => {
const { gethAddresses } = this.props.store;
return gethAddresses.includes(account.address);
}
onSelect = (account) => {
const { store } = this.props; const { store } = this.props;
store.selectGethAccount(address); store.selectGethAccount(account.address);
} }
} }

View File

@ -48,10 +48,10 @@ describe('modals/CreateAccount/NewGeth', () => {
}); });
describe('events', () => { describe('events', () => {
describe('onSelectAddress', () => { describe('onSelect', () => {
beforeEach(() => { beforeEach(() => {
sinon.spy(store, 'selectGethAccount'); sinon.spy(store, 'selectGethAccount');
instance.onSelectAddress(null, 'testAddress'); instance.onSelect({ address: 'testAddress' });
}); });
afterEach(() => { afterEach(() => {

View File

@ -39,6 +39,7 @@ export default class NewImport extends Component {
return ( return (
<Form> <Form>
<Input <Input
autoFocus
error={ nameError } error={ nameError }
hint={ hint={
<FormattedMessage <FormattedMessage

View File

@ -39,6 +39,7 @@ export default class RawKey extends Component {
return ( return (
<Form> <Form>
<Input <Input
autoFocus
error={ rawKeyError } error={ rawKeyError }
hint={ hint={
<FormattedMessage <FormattedMessage

View File

@ -36,6 +36,7 @@ export default class RecoveryPhrase extends Component {
return ( return (
<Form> <Form>
<Input <Input
autoFocus
hint={ hint={
<FormattedMessage <FormattedMessage
id='createAccount.recoveryPhrase.phrase.hint' id='createAccount.recoveryPhrase.phrase.hint'

View 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 './typeIcon';

View File

@ -0,0 +1,59 @@
// 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 React, { Component, PropTypes } from 'react';
import { AccountsIcon, DoneIcon, FileIcon, FileUploadIcon, KeyboardIcon, KeyIcon, MembershipIcon } from '~/ui/Icons';
import { STAGE_INFO } from '../store';
export default class TypeIcon extends Component {
static propTypes = {
className: PropTypes.string,
store: PropTypes.object.isRequired,
type: PropTypes.string
}
render () {
const { className, store, type } = this.props;
const { createType, stage } = store;
if (stage === STAGE_INFO) {
return <DoneIcon className={ className } />;
}
switch (type || createType) {
case 'fromGeth':
return <FileUploadIcon className={ className } />;
case 'fromPhrase':
return <KeyboardIcon className={ className } />;
case 'fromRaw':
return <KeyIcon className={ className } />;
case 'fromJSON':
return <FileIcon className={ className } />;
case 'fromPresale':
return <MembershipIcon className={ className } />;
case 'fromNew':
default:
return <AccountsIcon className={ className } />;
}
}
}

View File

@ -15,6 +15,46 @@
/* along with Parity. If not, see <http://www.gnu.org/licenses/>. /* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/ */
.details {
width: 100%;
.info {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
.account {
display: flex;
flex-direction: column;
flex: 1;
.name {
display: flex;
flex-direction: row;
flex: 1;
.icon {
flex: 0 0 56px;
margin: 0 1em 0 0;
}
.title {
flex: 1;
}
}
.description {
flex: 1;
}
}
.qr {
flex: 0 0 136px;
margin: 1.5em 0 0 1.5em;
}
}
}
.spaced { .spaced {
line-height: 1.618em; line-height: 1.618em;
} }
@ -39,7 +79,8 @@
} }
} }
.identities, .selector { .identities,
.selector {
display: flex; display: flex;
} }
@ -47,9 +88,10 @@
margin-top: 1.5em; margin-top: 1.5em;
} }
.identities .identity, .selector .button { .identities .identity,
flex: 0 1 18%; .selector .button {
width: 18% !important; flex: 0 1 12.5%;
width: 12.5% !important;
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
} }
@ -73,3 +115,24 @@
.checkbox { .checkbox {
margin-top: 2em; margin-top: 2em;
} }
.selectItem {
display: flex;
.icon {
flex: 0 0 56px;
height: 56px !important;
margin-right: 0.75em;
width: 56px !important;
}
.info {
flex: 1 1;
}
}
.summary {
line-height: 1.618em;
padding: 0 4em 1.5em 4em;
text-align: center;
}

View File

@ -22,7 +22,7 @@ import { bindActionCreators } from 'redux';
import { createIdentityImg } from '~/api/util/identity'; import { createIdentityImg } from '~/api/util/identity';
import { newError } from '~/redux/actions'; import { newError } from '~/redux/actions';
import { Button, Modal } from '~/ui'; import { Button, ModalBox, Portal } from '~/ui';
import { CancelIcon, CheckIcon, DoneIcon, NextIcon, PrevIcon, PrintIcon } from '~/ui/Icons'; import { CancelIcon, CheckIcon, DoneIcon, NextIcon, PrevIcon, PrintIcon } from '~/ui/Icons';
import ParityLogo from '~/../assets/images/parity-logo-black-no-text.svg'; import ParityLogo from '~/../assets/images/parity-logo-black-no-text.svg';
@ -35,6 +35,7 @@ import NewImport from './NewImport';
import RawKey from './RawKey'; import RawKey from './RawKey';
import RecoveryPhrase from './RecoveryPhrase'; import RecoveryPhrase from './RecoveryPhrase';
import Store, { STAGE_CREATE, STAGE_INFO, STAGE_SELECT_TYPE } from './store'; import Store, { STAGE_CREATE, STAGE_INFO, STAGE_SELECT_TYPE } from './store';
import TypeIcon from './TypeIcon';
import print from './print'; import print from './print';
import recoveryPage from './recoveryPage.ejs'; import recoveryPage from './recoveryPage.ejs';
@ -86,18 +87,21 @@ class CreateAccount extends Component {
const { createType, stage } = this.store; const { createType, stage } = this.store;
return ( return (
<Modal <Portal
visible buttons={ this.renderDialogActions() }
actions={ this.renderDialogActions() } activeStep={ stage }
current={ stage } onClose={ this.onClose }
open
steps={ steps={
createType === 'fromNew' createType === 'fromNew'
? STAGE_NAMES ? STAGE_NAMES
: STAGE_IMPORT : STAGE_IMPORT
} }
> >
{ this.renderPage() } <ModalBox icon={ <TypeIcon store={ this.store } /> }>
</Modal> { this.renderPage() }
</ModalBox>
</Portal>
); );
} }
@ -245,11 +249,11 @@ class CreateAccount extends Component {
: null, : null,
<Button <Button
icon={ <DoneIcon /> } icon={ <DoneIcon /> }
key='close' key='done'
label={ label={
<FormattedMessage <FormattedMessage
id='createAccount.button.close' id='createAccount.button.done'
defaultMessage='Close' defaultMessage='Done'
/> />
} }
onClick={ this.onClose } onClick={ this.onClose }

View File

@ -35,7 +35,7 @@ function createApi () {
}, },
parity: { parity: {
generateSecretPhrase: sinon.stub().resolves('some account phrase'), generateSecretPhrase: sinon.stub().resolves('some account phrase'),
importGethAccounts: sinon.stub().resolves(), importGethAccounts: sinon.stub().resolves(GETH_ADDRESSES),
listGethAccounts: sinon.stub().resolves(GETH_ADDRESSES), listGethAccounts: sinon.stub().resolves(GETH_ADDRESSES),
newAccountFromPhrase: sinon.stub().resolves(ADDRESS), newAccountFromPhrase: sinon.stub().resolves(ADDRESS),
newAccountFromSecret: sinon.stub().resolves(ADDRESS), newAccountFromSecret: sinon.stub().resolves(ADDRESS),

Some files were not shown because too many files have changed in this diff Show More