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
- sh scripts/deb-build.sh amd64
- cp target/release/parity deb/usr/bin/parity
- cp target/release/parity/evm deb/usr/bin/evm
- cp target/release/parity/ethstore deb/usr/bin/ethstore
- cp target/release/parity/ethkey deb/usr/bin/ethkey
- 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")
- dpkg-deb -b deb "parity_"$VER"_amd64.deb"
- md5sum "parity_"$VER"_amd64.deb" > "parity_"$VER"_amd64.deb.md5"
@ -69,11 +69,20 @@ linux-stable-debian:
- triggers
script:
- 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/evm
- strip target/release/ethstore
- strip target/release/ethkey
- export SHA3=$(target/release/parity tools hash target/release/parity)
- md5sum target/release/parity > parity.md5
- sh scripts/deb-build.sh amd64
- 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")
- dpkg-deb -b deb "parity_"$VER"_amd64.deb"
- md5sum "parity_"$VER"_amd64.deb" > "parity_"$VER"_amd64.deb.md5"
@ -486,14 +495,14 @@ docker-build:
stage: build
only:
- tags
- triggers
before_script:
- docker info
script:
- cd docker/hub
- 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 build --no-cache=true --tag ethcore/parity:$DOCKER_TAG .
- docker push ethcore/parity:$DOCKER_TAG
- sh scripts/docker-build.sh $DOCKER_TAG
tags:
- docker
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)",
"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)",
"stats 0.1.0",
"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)",
]
@ -444,7 +445,7 @@ dependencies = [
"ethcore-rpc 1.6.0",
"ethcore-util 1.6.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)",
"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)",
@ -553,12 +554,13 @@ dependencies = [
"ethcore-ipc-codegen 1.6.0",
"ethcore-network 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)",
"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)",
"rlp 0.1.0",
"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)",
]
@ -618,7 +620,7 @@ dependencies = [
"ethstore 0.1.0",
"ethsync 1.6.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-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)",
@ -633,6 +635,7 @@ dependencies = [
"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_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)",
"transient-hashmap 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -648,7 +651,7 @@ dependencies = [
"ethcore-util 1.6.0",
"ethcrypto 0.1.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)",
"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)",
@ -683,7 +686,7 @@ dependencies = [
"ethcore-ipc-codegen 1.6.0",
"ethcore-ipc-nano 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-macros 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"
version = "0.1.0"
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)",
"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)",
@ -865,7 +868,7 @@ dependencies = [
[[package]]
name = "futures"
version = "0.1.6"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"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"
dependencies = [
"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)",
]
@ -952,7 +955,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "hyper"
version = "0.9.14"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
@ -973,7 +976,7 @@ dependencies = [
[[package]]
name = "hyper"
version = "0.10.0-a.0"
source = "git+https://github.com/ethcore/hyper#6baea9d444dd1652220ee9b4aeadaebb3de6a955"
source = "git+https://github.com/ethcore/hyper#2e6702984f4f9e99fe251537a755aff0badc0b3a"
dependencies = [
"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)",
@ -1033,7 +1036,7 @@ name = "igd"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
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)",
"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)",
@ -1083,7 +1086,7 @@ name = "jsonrpc-core"
version = "6.0.0"
source = "git+https://github.com/ethcore/jsonrpc.git#86d7a89c85f324b5f6671315d9b71010ca995300"
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)",
"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)",
@ -1620,7 +1623,7 @@ dependencies = [
"ethabi 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-util 1.6.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)",
"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)",
@ -1637,6 +1640,7 @@ dependencies = [
"ethcore 1.6.0",
"ethcore-util 1.6.0",
"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)",
"multihash 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.1.0",
@ -1661,7 +1665,7 @@ dependencies = [
name = "parity-reactor"
version = "0.1.0"
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)",
]
@ -1672,7 +1676,7 @@ dependencies = [
"ethcore-rpc 1.6.0",
"ethcore-signer 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)",
"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)",
@ -1702,7 +1706,7 @@ dependencies = [
[[package]]
name = "parity-ui-precompiled"
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 = [
"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-rpc 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",
"rpassword 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -2223,6 +2227,13 @@ name = "stable-heap"
version = "0.1.0"
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]]
name = "strsim"
version = "0.3.0"
@ -2334,7 +2345,7 @@ name = "tokio-core"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
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)",
"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)",
@ -2346,7 +2357,7 @@ name = "tokio-proto"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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)",
"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)",
@ -2362,7 +2373,7 @@ name = "tokio-service"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
@ -2595,7 +2606,7 @@ dependencies = [
"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 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 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"
@ -2607,7 +2618,7 @@ dependencies = [
"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.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 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"

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
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:
- create and manage your Ethereum accounts;
- manage your Ether and any Ethereum tokens;

View File

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

View File

@ -23,6 +23,7 @@ smallvec = "0.3.1"
futures = "0.1"
rand = "0.3"
itertools = "0.5"
stats = { path = "../../util/stats" }
[features]
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")
}
/// 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.
///
/// 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)]
mod tests {
use super::HeaderChain;

View File

@ -20,7 +20,7 @@ use std::sync::Arc;
use ethcore::block_import_error::BlockImportError;
use ethcore::block_status::BlockStatus;
use ethcore::client::ClientReport;
use ethcore::client::{ClientReport, EnvInfo};
use ethcore::engines::Engine;
use ethcore::ids::BlockId;
use ethcore::header::Header;
@ -33,7 +33,7 @@ use io::IoChannel;
use util::{Bytes, DBValue, H256, Mutex, RwLock};
use self::header_chain::HeaderChain;
use self::header_chain::{AncestryIter, HeaderChain};
pub use self::service::Service;
@ -62,6 +62,12 @@ pub trait LightChainClient: Send + Sync {
/// Get the best block 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.
fn is_known(&self, hash: &H256) -> bool;
@ -164,6 +170,16 @@ impl Client {
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.
pub fn flush_queue(&self) {
self.queue.flush()
@ -217,6 +233,33 @@ impl Client {
pub fn 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 {
@ -234,6 +277,14 @@ impl LightChainClient for Client {
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 {
self.status(hash) == BlockStatus::InChain
}

View File

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

View File

@ -329,6 +329,16 @@ impl LightProtocol {
.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
/// which a peer would be able to serve. Returns zero if the
/// 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> {
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)]

View File

@ -19,6 +19,7 @@
//! will take the raw data received here and extract meaningful results from it.
use std::collections::HashMap;
use std::sync::Arc;
use ethcore::basic_account::BasicAccount;
use ethcore::encoded;
@ -30,10 +31,11 @@ use futures::{Async, Poll, Future};
use futures::sync::oneshot::{self, Sender, Receiver};
use network::PeerId;
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 net::{Handler, Status, Capabilities, Announcement, EventContext, BasicContext, ReqId};
use cache::Cache;
use types::les_request::{self as les_request, Request as LesRequest};
pub mod request;
@ -44,9 +46,16 @@ struct Peer {
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.
enum Pending {
HeaderByNumber(request::HeaderByNumber, Sender<(encoded::Header, U256)>), // num + CHT root
HeaderByNumber(request::HeaderByNumber, ChtProofSender),
HeaderByHash(request::HeaderByHash, Sender<encoded::Header>),
Block(request::Body, Sender<encoded::Block>),
BlockReceipts(request::BlockReceipts, Sender<Vec<Receipt>>),
@ -61,30 +70,77 @@ enum Pending {
pub struct OnDemand {
peers: RwLock<HashMap<PeerId, Peer>>,
pending_requests: RwLock<HashMap<ReqId, Pending>>,
cache: Arc<Mutex<Cache>>,
orphaned_requests: RwLock<Vec<Pending>>,
}
impl Default for OnDemand {
fn default() -> Self {
impl OnDemand {
/// Create a new `OnDemand` service with the given cache.
pub fn new(cache: Arc<Mutex<Cache>>) -> Self {
OnDemand {
peers: RwLock::new(HashMap::new()),
pending_requests: RwLock::new(HashMap::new()),
cache: cache,
orphaned_requests: RwLock::new(Vec::new()),
}
}
/// Request a header by block number and CHT root hash.
/// Returns the header.
pub fn header_by_number(&self, ctx: &BasicContext, req: request::HeaderByNumber) -> Receiver<encoded::Header> {
let (sender, receiver) = oneshot::channel();
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
}
impl OnDemand {
/// Request a header by block number and CHT root hash.
/// Returns the header and the total difficulty.
pub fn header_by_number(&self, ctx: &BasicContext, req: request::HeaderByNumber) -> Receiver<(encoded::Header, U256)> {
/// 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();
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.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
}
// 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 cht_num = req.cht_num();
@ -126,7 +182,10 @@ impl OnDemand {
/// it as easily.
pub fn header_by_hash(&self, ctx: &BasicContext, req: request::HeaderByHash) -> Receiver<encoded::Header> {
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
}
@ -184,7 +243,16 @@ impl OnDemand {
sender.complete(encoded::Block::new(stream.out()))
} 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
}
@ -227,7 +295,10 @@ impl OnDemand {
if req.0.receipts_root() == SHA3_NULL_RLP {
sender.complete(Vec::new())
} 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
@ -425,8 +496,15 @@ impl OnDemand {
for orphaned in to_dispatch {
match orphaned {
Pending::HeaderByNumber(req, mut sender) =>
if !check_hangup(&mut sender) { self.dispatch_header_by_number(ctx, req, sender) },
Pending::HeaderByNumber(req, mut 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) =>
if !check_hangup(&mut sender) { self.dispatch_header_by_hash(ctx, req, sender) },
Pending::Block(req, mut sender) =>
@ -488,8 +566,19 @@ impl Handler for OnDemand {
Pending::HeaderByNumber(req, sender) => {
if let Some(&(ref header, ref proof)) = proofs.get(0) {
match req.check_response(header, proof) {
Ok(header) => {
sender.complete(header);
Ok((header, score)) => {
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
}
Err(e) => {
@ -517,6 +606,7 @@ impl Handler for OnDemand {
if let Some(ref header) = headers.get(0) {
match req.check_response(header) {
Ok(header) => {
self.cache.lock().insert_block_header(req.0, header.clone());
sender.complete(header);
return
}
@ -542,9 +632,11 @@ impl Handler for OnDemand {
match req {
Pending::Block(req, sender) => {
if let Some(ref block) = bodies.get(0) {
match req.check_response(block) {
if let Some(ref body) = bodies.get(0) {
match req.check_response(body) {
Ok(block) => {
let body = encoded::Body::new(body.to_vec());
self.cache.lock().insert_block_body(req.hash, body);
sender.complete(block);
return
}
@ -573,6 +665,8 @@ impl Handler for OnDemand {
if let Some(ref receipts) = receipts.get(0) {
match req.check_response(receipts) {
Ok(receipts) => {
let hash = req.0.hash();
self.cache.lock().insert_block_receipts(hash, receipts.clone());
sender.complete(receipts);
return
}
@ -683,10 +777,16 @@ impl Handler for OnDemand {
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
use cache::Cache;
use net::{Announcement, BasicContext, ReqId, Error as LesError};
use request::{Request as LesRequest, Kind as LesRequestKind};
use network::{PeerId, NodeId};
use util::H256;
use time::Duration;
use util::{H256, Mutex};
struct FakeContext;
@ -703,7 +803,8 @@ mod tests {
#[test]
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()));
assert!(on_demand.orphaned_requests.read().len() == 1);

View File

@ -245,6 +245,31 @@ impl TransactionQueue {
.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.
pub fn queued_senders(&self) -> Vec<Address> {
self.by_account.keys().cloned().collect()
@ -471,4 +496,22 @@ mod tests {
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() {
let mid = (lower + upper) / 2.into();
trace!(target: "binary_chop", "{} .. {} .. {}", lower, mid, upper);
trace!(target: "estimate_gas", "{} .. {} .. {}", lower, mid, upper);
let c = cond(mid)?;
match c {
true => upper = mid,
false => lower = mid,
};
trace!(target: "binary_chop", "{} => {} .. {}", c, lower, upper);
trace!(target: "estimate_gas", "{} => {} .. {}", c, lower, upper);
}
Ok(upper)
}

View File

@ -17,7 +17,6 @@
use std::collections::BTreeMap;
use util::{U256, Address, H256, H2048, Bytes, Itertools};
use util::hashdb::DBValue;
use util::stats::Histogram;
use blockchain::TreeRoute;
use verification::queue::QueueInfo as BlockQueueInfo;
use block::{OpenBlock, SealedBlock};
@ -213,38 +212,24 @@ pub trait BlockChainClient : Sync + Send {
fn ready_transactions(&self) -> Vec<PendingTransaction>;
/// 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 corpus = Vec::new();
while corpus.is_empty() {
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 header = block.header_view();
if header.number() == 0 {
corpus.sort();
return corpus;
let block = match self.block(BlockId::Hash(h)) {
Some(block) => block,
None => return corpus.into(),
};
if block.number() == 0 {
return corpus.into();
}
block.transaction_views().iter().foreach(|t| corpus.push(t.gas_price()));
h = header.parent_hash().clone();
h = block.parent_hash().clone();
}
}
corpus.sort();
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)
corpus.into()
}
/// 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> {
let h = self.height.load(AtomicOrdering::SeqCst);
let r = self.view.load(AtomicOrdering::SeqCst);
let s = self.step.read();
let vote_info = message_info_rlp(&VoteStep::new(h, r, *s), block_hash);
let s = *self.step.read();
let vote_info = message_info_rlp(&VoteStep::new(h, r, s), block_hash);
match self.signer.sign(vote_info.sha3()).map(Into::into) {
Ok(signature) => {
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();
self.votes.vote(message.clone(), &validator);
debug!(target: "engine", "Generated {:?} as {}.", message, validator);

View File

@ -79,6 +79,14 @@ pub struct EthashParams {
pub ecip1010_continue_transition: u64,
/// Maximum amount of code that can be deploying into a contract.
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 {
@ -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_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_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);
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 = parent.gas_limit().clone();
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() })))
}
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 max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor;
let parent_gas_limit = *parent.gas_limit();
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 {
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(())
}
@ -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(())
}
}
@ -857,4 +881,100 @@ mod tests {
ethash.populate_from_parent(&mut header, &parent, U256::from(150_000), 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)?,
gas_limit: r.val_at(9)?,
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)?,
seal: vec![],
hash: RefCell::new(Some(r.as_raw().sha3())),

View File

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

View File

@ -28,7 +28,6 @@ use miner::Miner;
use rlp::View;
use spec::Spec;
use views::BlockView;
use util::stats::Histogram;
use ethkey::{KeyPair, Secret};
use transaction::{PendingTransaction, Transaction, Action, Condition};
use miner::MinerService;
@ -209,11 +208,11 @@ fn can_collect_garbage() {
fn can_generate_gas_price_median() {
let client_result = generate_dummy_client_with_data(3, 1, slice_into![1, 2, 3]);
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 = 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]
@ -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 = client_result.reference();
let hist = client.gas_price_histogram(20, 5).unwrap();
let correct_hist = Histogram { bucket_bounds: vec_into![643, 2294, 3945, 5596, 7247, 8898], counts: vec![4,2,4,6,4] };
let hist = client.gas_price_corpus(20).histogram(5).unwrap();
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);
}
@ -231,7 +230,7 @@ fn empty_gas_price_histogram() {
let client_result = generate_dummy_client_with_data(20, 0, slice_into![]);
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]

View File

@ -456,5 +456,9 @@ pub fn get_default_ethash_params() -> EthashParams{
ecip1010_pause_transition: u64::max_value(),
ecip1010_continue_transition: 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;
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> {

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
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())?;
engine.verify_block_basic(&header, Some(bytes))?;
for u in UntrustedRlp::new(bytes).at(2)?.iter().map(|rlp| rlp.as_val::<Header>()) {
let u = u?;
verify_header_params(&u, engine)?;
verify_header_params(&u, engine, false)?;
engine.verify_block_basic(&u, None)?;
}
// Verify transactions.
@ -195,7 +195,7 @@ pub fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error>
}
/// 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()) {
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { max: Some(From::from(BlockNumber::max_value())), min: None, found: header.number() })))
}
@ -210,10 +210,12 @@ pub fn verify_header_params(header: &Header, engine: &Engine) -> Result<(), Erro
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() })));
}
if is_full {
let max_time = get_time().sec as u64 + 30;
if header.timestamp() > max_time {
return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: Some(max_time), min: None, found: header.timestamp() })))
}
}
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 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();
// preserve meta
temp_vault.set_meta(&self.meta()).map_err(SetKeyError::NonFatalOld)?;
// jump to next fs level
source_path.push("next");
target_path.push("next");

View File

@ -1015,4 +1015,21 @@ mod tests {
// and we can sign with the derived contract
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]
ethcore = { path = "../ethcore" }
ethcore-util = { path = "../util" }
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc.git" }
rlp = { path = "../util/rlp" }
mime = "0.2"
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/>.
use {multihash, cid, hyper};
use handler::Out;
use route::Out;
pub type Result<T> = ::std::result::Result<T, Error>;
@ -26,6 +26,8 @@ pub enum ServerError {
IoError(::std::io::Error),
/// Other `hyper` error
Other(hyper::error::Error),
/// Invalid --ipfs-api-interface
InvalidInterface
}
#[derive(Debug, PartialEq)]
@ -89,6 +91,7 @@ impl From<ServerError> for String {
match err {
ServerError::IoError(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 ethcore;
extern crate ethcore_util as util;
extern crate jsonrpc_http_server;
mod error;
mod handler;
pub mod error;
mod route;
use std::io::Write;
use std::sync::Arc;
use std::net::{SocketAddr, IpAddr, Ipv4Addr};
use std::net::{SocketAddr, IpAddr};
use error::ServerError;
use handler::{IpfsHandler, Out};
use route::Out;
use jsonrpc_http_server::cors;
use hyper::server::{Listening, Handler, Request, Response};
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 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
impl Handler<HttpStream> for IpfsHandler {
fn on_request(&mut self, req: Request<HttpStream>) -> Next {
@ -45,9 +115,16 @@ impl Handler<HttpStream> for IpfsHandler {
return Next::write();
}
// Reject requests if the Origin header isn't valid
if req.headers().get::<Origin>().map(|o| "127.0.0.1" != &o.host.hostname).unwrap_or(false) {
self.out = Out::Bad("Illegal Origin");
self.origin = cors::read_origin(&req);
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();
}
@ -57,7 +134,9 @@ impl Handler<HttpStream> for IpfsHandler {
_ => 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 {
@ -82,26 +161,28 @@ impl Handler<HttpStream> for IpfsHandler {
res.headers_mut().set(ContentLength(bytes.len() as u64));
res.headers_mut().set(ContentType(content_type));
Next::write()
},
NotFound(reason) => {
res.set_status(StatusCode::NotFound);
res.headers_mut().set(ContentLength(reason.len() as u64));
res.headers_mut().set(ContentType(mime!(Text/Plain)));
Next::write()
},
Bad(reason) => {
res.set_status(StatusCode::BadRequest);
res.headers_mut().set(ContentLength(reason.len() as u64));
res.headers_mut().set(ContentType(mime!(Text/Plain)));
}
}
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 {
use Out::*;
@ -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 {
// Skip any bytes that have already been written
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) {
Ok(written) => written,
Err(_) => return Next::end(),
@ -128,7 +210,7 @@ fn write_chunk<W: Write>(transport: &mut W, progress: &mut usize, data: &[u8]) -
*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() {
Next::write()
} 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> {
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port);
/// Add current interface (default: "127.0.0.1:5001") to list of allowed hosts
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(
hyper::Server::http(&addr)?
.handle(move |_| IpfsHandler::new(client.clone()))
.handle(move |_| IpfsHandler::new(cors.clone(), hosts.clone(), client.clone()))
.map(|(listening, srv)| {
::std::thread::spawn(move || {

View File

@ -14,15 +14,13 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use {rlp, multihash};
use {rlp, multihash, IpfsHandler};
use error::{Error, Result};
use cid::{ToCid, Codec};
use std::sync::Arc;
use multihash::Hash;
use hyper::Next;
use util::{Bytes, H256};
use ethcore::client::{BlockId, TransactionId, BlockChainClient};
use ethcore::client::{BlockId, TransactionId};
type Reason = &'static str;
@ -34,30 +32,10 @@ pub enum Out {
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 {
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
pub fn route(&mut self, path: &str, query: Option<&str>) -> Next {
self.out = match path {
pub fn route(&self, path: &str, query: Option<&str>) -> Out {
match path {
"/api/v0/block/get" => {
let arg = query.and_then(|q| get_param(q, "arg")).unwrap_or("");
@ -65,9 +43,7 @@ impl IpfsHandler {
},
_ => Out::NotFound("Route not found")
};
Next::write()
}
}
/// 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.
fn block(&self, hash: H256) -> Result<Out> {
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()))
}
/// Get list of block ommers by hash as raw binary.
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()))
}
@ -109,21 +85,21 @@ impl IpfsHandler {
/// Get transaction by hash and return as raw binary.
fn transaction(&self, hash: H256) -> Result<Out> {
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()))
}
/// Get state trie node by hash and return as raw binary.
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))
}
/// Get state trie node by hash and return as raw binary.
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))
}
@ -138,11 +114,12 @@ fn get_param<'a>(query: &'a str, name: &str) -> Option<&'a str> {
#[cfg(test)]
mod tests {
use std::sync::Arc;
use super::*;
use ethcore::client::TestBlockChainClient;
fn get_mocked_handler() -> IpfsHandler {
IpfsHandler::new(Arc::new(TestBlockChainClient::new()))
IpfsHandler::new(None, None, Arc::new(TestBlockChainClient::new()))
}
#[test]
@ -232,37 +209,37 @@ mod tests {
#[test]
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]
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]
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]
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",
"version": "0.3.94",
"version": "0.3.105",
"main": "release/index.js",
"jsnext:main": "src/index.js",
"author": "Parity Team <admin@parity.io>",

View File

@ -207,7 +207,7 @@ export default class Parity {
importGethAccounts (accounts) {
return this._transport
.execute('parity_importGethAccounts', inAddresses)
.execute('parity_importGethAccounts', inAddresses(accounts))
.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', () => {
it('returns the min gasprice, formatted', () => {
mockHttp([{ method: 'parity_minGasPrice', reply: { result: '0x123456' } }]);

View File

@ -15,7 +15,17 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default {
button: {
newAccount: `new account`,
newWallet: `new wallet`,
vaults: `vaults`
},
summary: {
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`
}
},
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: {
hint: {
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 deployContract from './deployContract';
export editMeta from './editMeta';
export errors from './errors';
export executeContract from './executeContract';
export extension from './extension';
export firstRun from './firstRun';
@ -38,9 +39,11 @@ export parityBar from './parityBar';
export passwordChange from './passwordChange';
export settings from './settings';
export shapeshift from './shapeshift';
export tabBar from './tabBar';
export transfer from './transfer';
export txEditor from './txEditor';
export ui from './ui';
export upgradeParity from './upgradeParity';
export vaults from './vaults';
export walletSettings from './walletSettings';
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: {
label: `password strength`
},
tooltips: {
button: {
done: `Done`,
next: `Next`,
skip: `Skip`
}
},
txHash: {
confirmations: `{count} {value, plural, one {confirmation} other {confirmations}}`,
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
// 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 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 {
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/>.
export default {
label: 'Instellingen',
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: {
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: {
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: {
label: 'Weergaven',
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: {
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: {
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: {
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`
},
status: {
label: 'Status'
},
signer: {
label: 'Signer'
},
overview_0: `Beheer de beschikbare weergaven van deze interface en selecteer enkel de delen van de applicatie die voor jou van belang zijn.`,
overview_1: `Ben je een eind gebruiker? De standaard instellingen zijn geschikt voor zowel beginners als gevorderde gebruikers.`,
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.`,
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">
<title><%= htmlWebpackPlugin.options.title %></title>
<style>
html {
background: white;
}
html, body, #container {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
background: white;
font-family: 'Roboto', sans-serif;
font-size: 16px;
font-weight: 300;

View File

@ -100,10 +100,10 @@ const transactionDetails = {
type: Quantity,
desc: 'The S field of the signature.'
},
minBlock: {
type: BlockNumber,
condition: {
type: Object,
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
// 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 React, { Component, PropTypes } from 'react';
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';
@ -46,8 +45,10 @@ export default class AddAddress extends Component {
render () {
return (
<Modal
actions={ this.renderDialogActions() }
<Portal
buttons={ this.renderDialogActions() }
onClose={ this.onClose }
open
title={
<FormattedMessage
id='addAddress.label'
@ -57,16 +58,17 @@ export default class AddAddress extends Component {
visible
>
{ this.renderFields() }
</Modal>
</Portal>
);
}
renderDialogActions () {
const { hasError } = this.store;
return ([
return [
<Button
icon={ <ContentClear /> }
icon={ <CancelIcon /> }
key='cancel'
label={
<FormattedMessage
id='addAddress.button.close'
@ -74,11 +76,11 @@ export default class AddAddress extends Component {
/>
}
onClick={ this.onClose }
ref='closeButton'
/>,
<Button
disabled={ hasError }
icon={ <ContentAdd /> }
icon={ <AddIcon /> }
key='save'
label={
<FormattedMessage
id='addAddress.button.add'
@ -86,18 +88,27 @@ export default class AddAddress extends Component {
/>
}
onClick={ this.onAdd }
ref='addButton'
/>
]);
];
}
renderFields () {
const { address, addressError, description, name, nameError } = this.store;
return (
<ModalBox
icon={ <AddressesIcon /> }
summary={
<FormattedMessage
id='addAddress.header'
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.'
/>
}
>
<Form>
<InputAddress
allowCopy={ false }
autoFocus
disabled={ !!this.props.address }
error={ addressError }
hint={
@ -113,7 +124,6 @@ export default class AddAddress extends Component {
/>
}
onChange={ this.onEditAddress }
ref='inputAddress'
value={ address }
/>
<Input
@ -131,7 +141,6 @@ export default class AddAddress extends Component {
/>
}
onChange={ this.onEditName }
ref='inputName'
value={ name }
/>
<Input
@ -148,10 +157,10 @@ export default class AddAddress extends Component {
/>
}
onChange={ this.onEditDescription }
ref='inputDescription'
value={ description }
/>
</Form>
</ModalBox>
);
}
@ -169,7 +178,7 @@ export default class AddAddress extends Component {
onAdd = () => {
this.store.add();
this.props.onClose();
this.onClose();
}
onClose = () => {

View File

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

View File

@ -19,10 +19,6 @@
flex-direction: column;
}
.container {
overflow-y: auto;
}
.description {
margin-top: .5em !important;
}
@ -49,26 +45,3 @@
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 { FormattedMessage } from 'react-intl';
import { DappCard, Portal, SectionList } from '~/ui';
import { CheckIcon } from '~/ui/Icons';
import { DappCard, Portal, SelectionList } from '~/ui';
import styles from './addDapps.css';
@ -48,7 +47,6 @@ export default class AddDapps extends Component {
/>
}
>
<div className={ styles.container }>
<div className={ styles.warning } />
{
this.renderList(store.sortedLocal, store.displayApps,
@ -86,7 +84,6 @@ export default class AddDapps extends Component {
/>
)
}
</div>
</Portal>
);
}
@ -102,9 +99,11 @@ export default class AddDapps extends Component {
<div className={ styles.header }>{ header }</div>
<div className={ styles.byline }>{ byline }</div>
</div>
<SectionList
<SelectionList
isChecked={ this.isVisible }
items={ items }
noStretch
onSelectClick={ this.onSelect }
renderItem={ this.renderApp }
/>
</div>
@ -112,30 +111,27 @@ export default class AddDapps extends Component {
}
renderApp = (app) => {
const { store } = this.props;
const isVisible = store.displayApps[app.id].visible;
return (
<DappCard
app={ app }
key={ app.id }
/>
);
}
const onClick = () => {
if (isVisible) {
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);
}
};
return (
<DappCard
app={ app }
className={
isVisible
? styles.selected
: styles.unselected
}
key={ app.id }
onClick={ onClick }
>
<CheckIcon className={ styles.selectIcon } />
</DappCard>
);
}
}

View File

@ -18,7 +18,9 @@ import { observer } from 'mobx-react';
import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import { Form, Input, InputAddress } from '~/ui';
import { IdentityIcon, Input, QrCode, Title } from '~/ui';
import styles from '../createAccount.css';
@observer
export default class AccountDetails extends Component {
@ -27,29 +29,28 @@ export default class AccountDetails extends Component {
}
render () {
const { address, name } = this.props.store;
const { address, description, name } = this.props.store;
return (
<Form>
<div className={ styles.details }>
<div className={ styles.info }>
<div className={ styles.account }>
<div className={ styles.name }>
<IdentityIcon
address={ address }
className={ styles.icon }
center
/>
<Title
byline={ description }
className={ styles.title }
title={ name }
/>
</div>
<div className={ styles.description }>
<Input
allowCopy
hint={
<FormattedMessage
id='createAccount.accountDetails.name.hint'
defaultMessage='a descriptive name for the account'
/>
}
label={
<FormattedMessage
id='createAccount.accountDetails.name.label'
defaultMessage='account name'
/>
}
readOnly
value={ name }
/>
<InputAddress
disabled
hideUnderline
hint={
<FormattedMessage
id='createAccount.accountDetails.address.hint'
@ -63,9 +64,17 @@ export default class AccountDetails extends Component {
/>
}
value={ address }
allowCopy={ address }
/>
{ 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 { FormattedMessage } from 'react-intl';
import styles from './accountDetailsGeth.css';
import { SectionList } from '~/ui';
import GethCard from '../GethCard';
import styles from '../createAccount.css';
@observer
export default class AccountDetailsGeth extends Component {
@ -27,33 +30,37 @@ export default class AccountDetailsGeth extends Component {
}
render () {
const { gethAddresses } = this.props.store;
const { gethAccountsAvailable, gethImported } = this.props.store;
const accounts = gethAccountsAvailable.filter((account) => gethImported.includes(account.address));
return (
<div>
<div>
<div className={ styles.summary }>
<FormattedMessage
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={ {
number: gethAddresses.length
number: gethImported.length
} }
/>
</div>
<div className={ styles.address }>
{ this.formatAddresses(gethAddresses) }
</div>
<SectionList
items={ accounts }
noStretch
renderItem={ this.renderAccount }
/>
</div>
);
}
formatAddresses (addresses) {
return addresses.map((address, index) => {
const comma = !index
? ''
: ((index === addresses.length - 1) ? ' & ' : ', ');
return `${comma}${address}`;
}).join('');
renderAccount = (account, index) => {
return (
<GethCard
address={ account.address }
balance={ account.balance }
name={ `Geth Import ${index + 1}` }
/>
);
}
}

View File

@ -39,22 +39,4 @@ describe('modals/CreateAccount/AccountDetailsGeth', () => {
it('renders with defaults', () => {
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 React, { Component, PropTypes } from 'react';
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';
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
export default class CreationType extends Component {
static propTypes = {
@ -31,74 +126,58 @@ export default class CreationType extends Component {
const { createType } = this.props.store;
return (
<div className={ styles.spaced }>
<RadioButtonGroup
defaultSelected={ createType }
name='creationType'
onChange={ this.onChange }
>
<RadioButton
label={
<div>
<div className={ styles.summary }>
<FormattedMessage
id='createAccount.creationType.fromNew.label'
defaultMessage='Create new account manually'
id='createAccount.creationType.info'
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.'
/>
}
value='fromNew'
/>
<RadioButton
label={
<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>
{ this.renderList(createType) }
</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;
store.setCreateType(event.target.value);
store.setCreateType(item.key);
}
}

View File

@ -22,6 +22,7 @@ import { createStore } from '../createAccount.test.js';
import CreationType from './';
let component;
let instance;
let store;
function render () {
@ -31,6 +32,7 @@ function render () {
store={ store }
/>
);
instance = component.instance();
return component;
}
@ -44,28 +46,10 @@ describe('modals/CreateAccount/CreationType', () => {
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('onChange', () => {
beforeEach(() => {
component.instance().onChange({ target: { value: 'testing' } });
instance.onChange({ key: 'testing' });
});
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 (
<Form>
<Input
autoFocus
error={ nameError }
hint={
<FormattedMessage

View File

@ -59,7 +59,7 @@ describe('modals/CreateAccount/NewAccount', () => {
});
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', () => {

View File

@ -17,11 +17,11 @@
import { observer } from 'mobx-react';
import React, { Component, PropTypes } from 'react';
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
export default class NewGeth extends Component {
@ -36,9 +36,23 @@ export default class NewGeth extends Component {
render () {
const { gethAccountsAvailable, gethAddresses } = this.props.store;
if (!gethAccountsAvailable.length) {
return (
<div className={ styles.list }>
return gethAccountsAvailable.length
? (
<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
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'
@ -47,49 +61,37 @@ export default class NewGeth extends Component {
);
}
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>
);
renderList (gethAccountsAvailable) {
return (
<Checkbox
checked={ gethAddresses.includes(account.address) }
key={ account.address }
label={ label }
onCheck={ onSelect }
<SelectionList
isChecked={ this.isSelected }
items={ gethAccountsAvailable }
noStretch
onSelectClick={ this.onSelect }
renderItem={ this.renderAccount }
/>
);
});
return (
<div className={ styles.list }>
{ checkboxes }
</div>
);
}
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;
store.selectGethAccount(address);
store.selectGethAccount(account.address);
}
}

View File

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

View File

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

View File

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

View File

@ -36,6 +36,7 @@ export default class RecoveryPhrase extends Component {
return (
<Form>
<Input
autoFocus
hint={
<FormattedMessage
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/>.
*/
.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 {
line-height: 1.618em;
}
@ -39,7 +79,8 @@
}
}
.identities, .selector {
.identities,
.selector {
display: flex;
}
@ -47,9 +88,10 @@
margin-top: 1.5em;
}
.identities .identity, .selector .button {
flex: 0 1 18%;
width: 18% !important;
.identities .identity,
.selector .button {
flex: 0 1 12.5%;
width: 12.5% !important;
text-align: center;
cursor: pointer;
}
@ -73,3 +115,24 @@
.checkbox {
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 { 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 ParityLogo from '~/../assets/images/parity-logo-black-no-text.svg';
@ -35,6 +35,7 @@ import NewImport from './NewImport';
import RawKey from './RawKey';
import RecoveryPhrase from './RecoveryPhrase';
import Store, { STAGE_CREATE, STAGE_INFO, STAGE_SELECT_TYPE } from './store';
import TypeIcon from './TypeIcon';
import print from './print';
import recoveryPage from './recoveryPage.ejs';
@ -86,18 +87,21 @@ class CreateAccount extends Component {
const { createType, stage } = this.store;
return (
<Modal
visible
actions={ this.renderDialogActions() }
current={ stage }
<Portal
buttons={ this.renderDialogActions() }
activeStep={ stage }
onClose={ this.onClose }
open
steps={
createType === 'fromNew'
? STAGE_NAMES
: STAGE_IMPORT
}
>
<ModalBox icon={ <TypeIcon store={ this.store } /> }>
{ this.renderPage() }
</Modal>
</ModalBox>
</Portal>
);
}
@ -245,11 +249,11 @@ class CreateAccount extends Component {
: null,
<Button
icon={ <DoneIcon /> }
key='close'
key='done'
label={
<FormattedMessage
id='createAccount.button.close'
defaultMessage='Close'
id='createAccount.button.done'
defaultMessage='Done'
/>
}
onClick={ this.onClose }

View File

@ -35,7 +35,7 @@ function createApi () {
},
parity: {
generateSecretPhrase: sinon.stub().resolves('some account phrase'),
importGethAccounts: sinon.stub().resolves(),
importGethAccounts: sinon.stub().resolves(GETH_ADDRESSES),
listGethAccounts: sinon.stub().resolves(GETH_ADDRESSES),
newAccountFromPhrase: 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