Merge branch 'master' into miner-no-default

Conflicts:
	sync/src/lib.rs
This commit is contained in:
Tomasz Drwięga 2016-06-23 21:04:23 +02:00
commit 6edb981bfa
160 changed files with 29723 additions and 918 deletions

View File

@ -33,10 +33,10 @@ env:
global:
# GH_TOKEN
- secure: bumJASbZSU8bxJ0EyPUJmu16AiV9EXOpyOj86Jlq/Ty9CfwGqsSXt96uDyE+OUJf34RUFQMsw0nk37/zC4lcn6kqk2wpuH3N/o85Zo/cVZY/NusBWLQqtT5VbYWsV+u2Ua4Tmmsw8yVYQhYwU2ZOejNpflL+Cs9XGgORp1L+/gMRMC2y5Se6ZhwnKPQlRJ8LGsG1dzjQULxzADIt3/zuspNBS8a2urJwlHfGMkvHDoUWCviP/GXoSqw3TZR7FmKyxE19I8n9+iSvm9+oZZquvcgfUxMHn8Gq/b44UbPvjtFOg2yam4xdWXF/RyWCHdc/R9EHorSABeCbefIsm+zcUF3/YQxwpSxM4IZEeH2rTiC7dcrsKw3XsO16xFQz5YI5Bay+CT/wTdMmJd7DdYz7Dyf+pOvcM9WOf/zorxYWSBOMYy0uzbusU2iyIghQ82s7E/Ahg+WARtPgkuTLSB5aL1oCTBKHqQscMr7lo5Ti6RpWLxEdTQMBznc+bMr+6dEtkEcG9zqc6cE9XX+ox3wTU6+HVMfQ1ltCntJ4UKcw3A6INEbw9wgocQa812CIASQ2fE+SCAbz6JxBjIAlFUnD1lUB7S8PdMPwn9plfQgKQ2A5YZqg6FnBdf0rQXIJYxQWKHXj/rBHSUCT0tHACDlzTA+EwWggvkP5AGIxRxm8jhw=
- TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethjson -p ethcore-dapps -p ethcore-signer"
- TARGETS="-p ethkey -p ethstore -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethjson -p ethcore-dapps -p ethcore-signer"
- ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}"
- KCOV_FEATURES=""
- KCOV_CMD="./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests target/kcov"
- KCOV_CMD="./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests,ethstore/tests target/kcov"
- RUN_TESTS="false"
- RUN_COVERAGE="false"
- RUN_BUILD="false"
@ -65,6 +65,8 @@ after_success: |
wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz &&
tar xzf master.tar.gz && mkdir kcov-master/build && cd kcov-master/build && cmake .. && make && make install DESTDIR=../tmp && cd ../.. &&
cargo test --no-run ${KCOV_FEATURES} ${TARGETS} &&
$KCOV_CMD target/debug/deps/ethkey-* &&
$KCOV_CMD target/debug/deps/ethstore-* &&
$KCOV_CMD target/debug/deps/ethcore_util-* &&
$KCOV_CMD target/debug/deps/ethash-* &&
$KCOV_CMD target/debug/deps/ethcore-* &&

36
Cargo.lock generated
View File

@ -3,7 +3,7 @@ name = "parity"
version = "1.2.0"
dependencies = [
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.76 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
"ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)",
"daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
@ -67,9 +67,7 @@ dependencies = [
name = "bigint"
version = "0.1.0"
dependencies = [
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
@ -131,15 +129,15 @@ dependencies = [
[[package]]
name = "clippy"
version = "0.0.76"
version = "0.0.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"clippy_lints 0.0.76 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy_lints 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "clippy_lints"
version = "0.0.76"
version = "0.0.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -252,7 +250,7 @@ name = "ethcore"
version = "1.2.0"
dependencies = [
"bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.76 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethash 1.2.0",
@ -277,7 +275,7 @@ dependencies = [
name = "ethcore-dapps"
version = "1.2.0"
dependencies = [
"clippy 0.0.76 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-rpc 1.2.0",
"ethcore-util 1.2.0",
"hyper 0.9.3 (git+https://github.com/ethcore/hyper)",
@ -339,7 +337,7 @@ dependencies = [
name = "ethcore-rpc"
version = "1.2.0"
dependencies = [
"clippy 0.0.76 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
"ethash 1.2.0",
"ethcore 1.2.0",
"ethcore-devtools 1.2.0",
@ -362,7 +360,7 @@ dependencies = [
name = "ethcore-signer"
version = "1.2.0"
dependencies = [
"clippy 0.0.76 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-rpc 1.2.0",
"ethcore-util 1.2.0",
@ -381,7 +379,7 @@ dependencies = [
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
"bigint 0.1.0",
"chrono 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.76 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
"elastic-array 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -426,7 +424,6 @@ dependencies = [
name = "ethkey"
version = "0.2.0"
dependencies = [
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
"eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
@ -438,7 +435,6 @@ dependencies = [
name = "ethstore"
version = "0.1.0"
dependencies = [
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
"ethkey 0.2.0",
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
@ -455,7 +451,7 @@ dependencies = [
name = "ethsync"
version = "1.2.0"
dependencies = [
"clippy 0.0.76 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.2.0",
"ethcore-util 1.2.0",
@ -634,7 +630,7 @@ dependencies = [
[[package]]
name = "jsonrpc-http-server"
version = "5.1.0"
source = "git+https://github.com/ethcore/jsonrpc-http-server.git#6117b1d77b5a60d6fa2dc884f12aa7f5fd4585ca"
source = "git+https://github.com/ethcore/jsonrpc-http-server.git#0c99d308bc15e8fae50642eff77a3e1fd7610652"
dependencies = [
"hyper 0.9.3 (git+https://github.com/ethcore/hyper)",
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
@ -926,7 +922,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "parity-dapps"
version = "0.3.0"
source = "git+https://github.com/ethcore/parity-dapps-rs.git#1f065d93aa49338e4a453c77c839957f2db78895"
source = "git+https://github.com/ethcore/parity-dapps-rs.git#8cc812c26c903cf5764ce0f4cc3f2a7c3ddb0dc2"
dependencies = [
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
@ -940,7 +936,7 @@ dependencies = [
[[package]]
name = "parity-dapps-builtins"
version = "0.5.1"
source = "git+https://github.com/ethcore/parity-dapps-builtins-rs.git#413ef9a6f9c46d16d578a48e39adb44d5650d7d7"
source = "git+https://github.com/ethcore/parity-dapps-builtins-rs.git#650b0d94d076635904b86c1fd45c5f4a2061463f"
dependencies = [
"parity-dapps 0.3.0 (git+https://github.com/ethcore/parity-dapps-rs.git)",
]
@ -964,7 +960,7 @@ dependencies = [
[[package]]
name = "parity-minimal-sysui"
version = "0.1.0"
source = "git+https://github.com/ethcore/parity-dapps-minimal-sysui-rs.git#4c704913f671060bb0e43b5ce4a68d02281115d5"
source = "git+https://github.com/ethcore/parity-dapps-minimal-sysui-rs.git#3c6ad40680126a760eb867b07b506ea996819ce3"
[[package]]
name = "phf"
@ -1107,7 +1103,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rocksdb"
version = "0.4.5"
source = "git+https://github.com/ethcore/rust-rocksdb#9140e37ce0fdb748097f85653c01b0f7e3736ea9"
source = "git+https://github.com/ethcore/rust-rocksdb#6f3c68f5f075433d206be4af6a620651cd9f8541"
dependencies = [
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
"rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)",
@ -1116,7 +1112,7 @@ dependencies = [
[[package]]
name = "rocksdb-sys"
version = "0.3.0"
source = "git+https://github.com/ethcore/rust-rocksdb#9140e37ce0fdb748097f85653c01b0f7e3736ea9"
source = "git+https://github.com/ethcore/rust-rocksdb#6f3c68f5f075433d206be4af6a620651cd9f8541"
dependencies = [
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -22,7 +22,7 @@ fdlimit = { path = "util/fdlimit" }
num_cpus = "0.2"
number_prefix = "0.2"
rpassword = "0.2.1"
clippy = { version = "0.0.76", optional = true}
clippy = { version = "0.0.77", optional = true}
ethcore = { path = "ethcore" }
ethcore-util = { path = "util" }
ethsync = { path = "sync" }

View File

@ -36,7 +36,7 @@ below to build from source.
----
## Building from source
## Build dependencies
Parity is fully compatible with Stable Rust.
@ -55,7 +55,19 @@ We recommend installing Rust through [rustup](https://www.rustup.rs/). If you do
$ rustup default stable-x86_64-pc-windows-msvc
```
Once you have rustup, download and build parity:
Once you have rustup, install parity or download and build from source
----
## Quick install
```bash
cargo install --git https://github.com/ethcore/parity.git parity
```
----
## Build from source
```bash
# download Parity code

6
cov.sh
View File

@ -16,6 +16,8 @@ if ! type kcov > /dev/null; then
fi
cargo test \
-p ethkey \
-p ethstore \
-p ethash \
-p ethcore-util \
-p ethcore \
@ -28,7 +30,9 @@ cargo test \
rm -rf target/coverage
mkdir -p target/coverage
EXCLUDE="~/.multirust,rocksdb,secp256k1,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests"
EXCLUDE="~/.multirust,rocksdb,secp256k1,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests,ethstore/tests"
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethkey-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethstore-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethash-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_util-*

View File

@ -28,7 +28,7 @@ parity-dapps-wallet = { git = "https://github.com/ethcore/parity-dapps-wallet-rs
parity-dapps-dao = { git = "https://github.com/ethcore/parity-dapps-dao-rs.git", version = "0.4.0", optional = true }
parity-dapps-makerotc = { git = "https://github.com/ethcore/parity-dapps-makerotc-rs.git", version = "0.3.0", optional = true }
mime_guess = { version = "1.6.1" }
clippy = { version = "0.0.76", optional = true}
clippy = { version = "0.0.77", optional = true}
[build-dependencies]
serde_codegen = { version = "0.7.0", optional = true }

View File

@ -12,7 +12,7 @@ syntex = "*"
ethcore-ipc-codegen = { path = "../ipc/codegen" }
[dependencies]
clippy = { version = "0.0.67", optional = true}
clippy = { version = "0.0.77", optional = true}
ethcore-devtools = { path = "../devtools" }
ethcore-ipc = { path = "../ipc/rpc" }
rocksdb = { git = "https://github.com/ethcore/rust-rocksdb" }

2
doc.sh
View File

@ -2,6 +2,8 @@
# generate documentation only for partiy and ethcore libraries
cargo doc --no-deps --verbose \
-p ethkey \
-p ethstore \
-p ethash \
-p ethcore-util \
-p ethcore \

25
docker/centos/Dockerfile Normal file
View File

@ -0,0 +1,25 @@
FROM centos:latest
WORKDIR /build
# install tools and dependencies
RUN yum -y update&& \
yum install -y git make gcc-c++ gcc file
# install rustup
RUN curl -sSf https://static.rust-lang.org/rustup.sh -o rustup.sh &&\
ls&&\
sh rustup.sh -s -- --disable-sudo
# show backtraces
ENV RUST_BACKTRACE 1
ENV CXX g++
ENV CC gcc
RUN rustc -vV && \
cargo -V && \
gcc -v &&\
g++ -v
# git clone parity
RUN git clone https://github.com/ethcore/parity && \
cd parity&&\
ls -a&&\
cargo build --release --verbose && \
ls /build/parity/target/release/parity && \
file /build/parity/target/release/parity && \
RUN file /build/parity/target/release/parity

View File

@ -9,12 +9,16 @@ RUN apt-get -y update && \
&& \
apt-get clean
# install multirust
RUN curl -sf https://raw.githubusercontent.com/brson/multirust/master/blastoff.sh | sh -s -- --yes
# install rustup
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
# rustup directory
ENV PATH /root/.cargo/bin:$PATH
ENV RUST_TARGETS="arm-unknown-linux-gnueabihf"
# multirust add arm--linux-gnuabhf toolchain
RUN multirust add-target stable arm-unknown-linux-gnueabihf
RUN rustup target add stable arm-unknown-linux-gnueabihf
# show backtraces
ENV RUST_BACKTRACE 1

View File

@ -27,11 +27,11 @@ RUN git clone https://github.com/debris/evmjit && \
mkdir build && cd build && \
cmake .. && make && make install && cd
# install multirust
RUN curl -sf https://raw.githubusercontent.com/brson/multirust/master/blastoff.sh | sh -s -- --yes
# install rustup
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
# export rust LIBRARY_PATH
ENV LIBRARY_PATH /usr/local/lib
# rustup directory
ENV PATH /root/.cargo/bin:$PATH
# show backtraces
ENV RUST_BACKTRACE 1

View File

@ -27,11 +27,11 @@ RUN git clone https://github.com/debris/evmjit && \
mkdir build && cd build && \
cmake .. && make && make install && cd
# install multirust
RUN curl -sf https://raw.githubusercontent.com/brson/multirust/master/blastoff.sh | sh -s -- --yes
# install rustup
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
# export rust LIBRARY_PATH
ENV LIBRARY_PATH /usr/local/lib
# rustup directory
ENV PATH /root/.cargo/bin:$PATH
# show backtraces
ENV RUST_BACKTRACE 1

View File

@ -8,11 +8,11 @@ RUN apt-get update && \
git \
make
# install multirust
RUN curl -sf https://raw.githubusercontent.com/brson/multirust/master/blastoff.sh | sh -s -- --yes
# install rustup
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
# export rust LIBRARY_PATH
ENV LIBRARY_PATH /usr/local/lib
# rustup directory
ENV PATH /root/.cargo/bin:$PATH
# show backtraces
ENV RUST_BACKTRACE 1

View File

@ -22,7 +22,7 @@ ethcore-util = { path = "../util" }
evmjit = { path = "../evmjit", optional = true }
ethash = { path = "../ethash" }
num_cpus = "0.2"
clippy = { version = "0.0.76", optional = true}
clippy = { version = "0.0.77", optional = true}
crossbeam = "0.2.9"
lazy_static = "0.2"
ethcore-devtools = { path = "../devtools" }

View File

@ -9,7 +9,8 @@
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit": "0x118c30"
"frontierCompatibilityModeLimit": "0x118c30",
"daoRescueSoftFork": false
}
}
},

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,8 @@
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit": "0x118c30"
"frontierCompatibilityModeLimit": "0x118c30",
"daoRescueSoftFork": false
}
}
},

View File

@ -0,0 +1,43 @@
{
"name": "Frontier (Test)",
"engine": {
"Ethash": {
"params": {
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit": "0x118c30",
"daoRescueSoftFork": true
}
}
},
"params": {
"accountStartNonce": "0x00",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1"
},
"genesis": {
"seal": {
"ethereum": {
"nonce": "0x0000000000000042",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
},
"difficulty": "0x400000000",
"author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
"gasLimit": "0x1388"
},
"accounts": {
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
"0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
"0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
"0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }
}
}

View File

@ -9,7 +9,8 @@
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit": "0xffffffffffffffff"
"frontierCompatibilityModeLimit": "0xffffffffffffffff",
"daoRescueSoftFork": false
}
}
},

View File

@ -9,7 +9,8 @@
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit": 0
"frontierCompatibilityModeLimit": 0,
"daoRescueSoftFork": false
}
}
},

View File

@ -0,0 +1,43 @@
{
"name": "Homestead (Test)",
"engine": {
"Ethash": {
"params": {
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit": 0,
"daoRescueSoftFork": true
}
}
},
"params": {
"accountStartNonce": "0x00",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1"
},
"genesis": {
"seal": {
"ethereum": {
"nonce": "0x0000000000000042",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
},
"difficulty": "0x400000000",
"author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
"gasLimit": "0x1388"
},
"accounts": {
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
"0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
"0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
"0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }
}
}

View File

@ -9,7 +9,8 @@
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar": "",
"frontierCompatibilityModeLimit": "0x789b0"
"frontierCompatibilityModeLimit": "0x789b0",
"daoRescueSoftFork": false
}
}
},

View File

@ -9,7 +9,8 @@
"durationLimit": "0x08",
"blockReward": "0x14D1120D7B160000",
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050",
"frontierCompatibilityModeLimit": "0xffffffffffffffff"
"frontierCompatibilityModeLimit": "0xffffffffffffffff",
"daoRescueSoftFork": false
}
}
},

View File

@ -166,16 +166,16 @@ impl Account {
!self.code_cache.is_empty() || (self.code_cache.is_empty() && self.code_hash == Some(SHA3_EMPTY))
}
/// Provide a database to lookup `code_hash`. Should not be called if it is a contract without code.
/// Provide a database to get `code_hash`. Should not be called if it is a contract without code.
pub fn cache_code(&mut self, db: &AccountDB) -> bool {
// TODO: fill out self.code_cache;
trace!("Account::cache_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty());
self.is_cached() ||
match self.code_hash {
Some(ref h) => match db.lookup(h) {
Some(ref h) => match db.get(h) {
Some(x) => { self.code_cache = x.to_vec(); true },
_ => {
warn!("Failed reverse lookup of {}", h);
warn!("Failed reverse get of {}", h);
false
},
},

View File

@ -30,18 +30,18 @@ impl<'db> HashDB for AccountDB<'db>{
unimplemented!()
}
fn lookup(&self, key: &H256) -> Option<&[u8]> {
fn get(&self, key: &H256) -> Option<&[u8]> {
if key == &SHA3_NULL_RLP {
return Some(&NULL_RLP_STATIC);
}
self.db.lookup(&combine_key(&self.address, key))
self.db.get(&combine_key(&self.address, key))
}
fn exists(&self, key: &H256) -> bool {
fn contains(&self, key: &H256) -> bool {
if key == &SHA3_NULL_RLP {
return true;
}
self.db.exists(&combine_key(&self.address, key))
self.db.contains(&combine_key(&self.address, key))
}
fn insert(&mut self, _value: &[u8]) -> H256 {
@ -52,7 +52,7 @@ impl<'db> HashDB for AccountDB<'db>{
unimplemented!()
}
fn kill(&mut self, _key: &H256) {
fn remove(&mut self, _key: &H256) {
unimplemented!()
}
}
@ -82,18 +82,18 @@ impl<'db> HashDB for AccountDBMut<'db>{
unimplemented!()
}
fn lookup(&self, key: &H256) -> Option<&[u8]> {
fn get(&self, key: &H256) -> Option<&[u8]> {
if key == &SHA3_NULL_RLP {
return Some(&NULL_RLP_STATIC);
}
self.db.lookup(&combine_key(&self.address, key))
self.db.get(&combine_key(&self.address, key))
}
fn exists(&self, key: &H256) -> bool {
fn contains(&self, key: &H256) -> bool {
if key == &SHA3_NULL_RLP {
return true;
}
self.db.exists(&combine_key(&self.address, key))
self.db.contains(&combine_key(&self.address, key))
}
fn insert(&mut self, value: &[u8]) -> H256 {
@ -114,12 +114,12 @@ impl<'db> HashDB for AccountDBMut<'db>{
self.db.emplace(key, value.to_vec())
}
fn kill(&mut self, key: &H256) {
fn remove(&mut self, key: &H256) {
if key == &SHA3_NULL_RLP {
return;
}
let key = combine_key(&self.address, key);
self.db.kill(&key)
self.db.remove(&key)
}
}

View File

@ -205,6 +205,13 @@ impl AccountProvider {
self.unlock_account(account, password, Unlock::Temp)
}
/// Checks if given account is unlocked
pub fn is_unlocked<A>(&self, account: A) -> bool where Address: From<A> {
let account = Address::from(account).into();
let unlocked = self.unlocked.read().unwrap();
unlocked.get(&account).is_some()
}
/// Signs the message. Account must be unlocked.
pub fn sign<A, M>(&self, account: A, message: M) -> Result<H520, Error> where Address: From<A>, Message: From<M> {
let account = Address::from(account).into();
@ -236,7 +243,6 @@ impl AccountProvider {
#[cfg(test)]
mod tests {
use super::AccountProvider;
use ethstore::SecretStore;
use ethstore::ethkey::{Generator, Random};
#[test]

View File

@ -203,8 +203,9 @@ mod tests {
timestamp: 0,
difficulty: 0.into(),
last_hashes: vec![],
dao_rescue_block_gas_limit: None,
gas_used: 0.into(),
gas_limit: 0.into()
gas_limit: 0.into(),
});
assert!(schedule.stack_limit > 0);
@ -253,7 +254,7 @@ mod tests {
spec.ensure_db_good(db.as_hashdb_mut());
let last_hashes = vec![genesis_header.hash()];
let vm_factory = Default::default();
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, addr, 3141562.into(), vec![]).unwrap();
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, None, addr, 3141562.into(), vec![]).unwrap();
let b = b.close_and_lock();
let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
assert!(b.try_seal(engine.deref(), seal).is_ok());

View File

@ -177,6 +177,7 @@ pub struct OpenBlock<'x> {
engine: &'x Engine,
vm_factory: &'x EvmFactory,
last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
}
/// Just like `OpenBlock`, except that we've applied `Engine::on_close_block`, finished up the non-seal header fields,
@ -188,6 +189,7 @@ pub struct ClosedBlock {
block: ExecutedBlock,
uncle_bytes: Bytes,
last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
unclosed_state: State,
}
@ -198,7 +200,6 @@ pub struct ClosedBlock {
pub struct LockedBlock {
block: ExecutedBlock,
uncle_bytes: Bytes,
last_hashes: LastHashes,
}
/// A block that has a valid seal.
@ -219,9 +220,10 @@ impl<'x> OpenBlock<'x> {
db: Box<JournalDB>,
parent: &Header,
last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
author: Address,
gas_floor_target: U256,
extra_data: Bytes
extra_data: Bytes,
) -> Result<Self, Error> {
let state = try!(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce()));
let mut r = OpenBlock {
@ -229,6 +231,7 @@ impl<'x> OpenBlock<'x> {
engine: engine,
vm_factory: vm_factory,
last_hashes: last_hashes,
dao_rescue_block_gas_limit: dao_rescue_block_gas_limit,
};
r.block.base.header.parent_hash = parent.hash();
@ -285,6 +288,7 @@ impl<'x> OpenBlock<'x> {
/// Get the environment info concerning this block.
pub fn env_info(&self) -> EnvInfo {
// TODO: memoise.
const SOFT_FORK_BLOCK: u64 = 1775000;
EnvInfo {
number: self.block.base.header.number,
author: self.block.base.header.author.clone(),
@ -293,6 +297,7 @@ impl<'x> OpenBlock<'x> {
last_hashes: self.last_hashes.clone(), // TODO: should be a reference.
gas_used: self.block.receipts.last().map_or(U256::zero(), |r| r.gas_used),
gas_limit: self.block.base.header.gas_limit.clone(),
dao_rescue_block_gas_limit: if self.block.base.header.number == SOFT_FORK_BLOCK { Some(self.block.base.header.gas_limit) } else { self.dao_rescue_block_gas_limit },
}
}
@ -339,6 +344,7 @@ impl<'x> OpenBlock<'x> {
block: s.block,
uncle_bytes: uncle_bytes,
last_hashes: s.last_hashes,
dao_rescue_block_gas_limit: s.dao_rescue_block_gas_limit,
unclosed_state: unclosed_state,
}
}
@ -360,7 +366,6 @@ impl<'x> OpenBlock<'x> {
LockedBlock {
block: s.block,
uncle_bytes: uncle_bytes,
last_hashes: s.last_hashes,
}
}
}
@ -386,7 +391,6 @@ impl ClosedBlock {
LockedBlock {
block: self.block,
uncle_bytes: self.uncle_bytes,
last_hashes: self.last_hashes,
}
}
@ -400,6 +404,7 @@ impl ClosedBlock {
engine: engine,
vm_factory: vm_factory,
last_hashes: self.last_hashes,
dao_rescue_block_gas_limit: self.dao_rescue_block_gas_limit,
}
}
}
@ -456,7 +461,18 @@ impl IsBlock for SealedBlock {
/// Enact the block given by block header, transactions and uncles
#[cfg_attr(feature="dev", allow(too_many_arguments))]
pub fn enact(header: &Header, transactions: &[SignedTransaction], uncles: &[Header], engine: &Engine, tracing: bool, db: Box<JournalDB>, parent: &Header, last_hashes: LastHashes, vm_factory: &EvmFactory) -> Result<LockedBlock, Error> {
pub fn enact(
header: &Header,
transactions: &[SignedTransaction],
uncles: &[Header],
engine: &Engine,
tracing: bool,
db: Box<JournalDB>,
parent: &Header,
last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
vm_factory: &EvmFactory
) -> Result<LockedBlock, Error> {
{
if ::log::max_log_level() >= ::log::LogLevel::Trace {
let s = try!(State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce()));
@ -464,7 +480,7 @@ pub fn enact(header: &Header, transactions: &[SignedTransaction], uncles: &[Head
}
}
let mut b = try!(OpenBlock::new(engine, vm_factory, tracing, db, parent, last_hashes, header.author().clone(), 3141562.into(), header.extra_data().clone()));
let mut b = try!(OpenBlock::new(engine, vm_factory, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, header.author().clone(), 3141562.into(), header.extra_data().clone()));
b.set_difficulty(*header.difficulty());
b.set_gas_limit(*header.gas_limit());
b.set_timestamp(header.timestamp());
@ -474,22 +490,52 @@ pub fn enact(header: &Header, transactions: &[SignedTransaction], uncles: &[Head
}
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
pub fn enact_bytes(block_bytes: &[u8], engine: &Engine, tracing: bool, db: Box<JournalDB>, parent: &Header, last_hashes: LastHashes, vm_factory: &EvmFactory) -> Result<LockedBlock, Error> {
#[cfg_attr(feature="dev", allow(too_many_arguments))]
pub fn enact_bytes(
block_bytes: &[u8],
engine: &Engine,
tracing: bool,
db: Box<JournalDB>,
parent: &Header,
last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
vm_factory: &EvmFactory
) -> Result<LockedBlock, Error> {
let block = BlockView::new(block_bytes);
let header = block.header();
enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, vm_factory)
enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, vm_factory)
}
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
pub fn enact_verified(block: &PreverifiedBlock, engine: &Engine, tracing: bool, db: Box<JournalDB>, parent: &Header, last_hashes: LastHashes, vm_factory: &EvmFactory) -> Result<LockedBlock, Error> {
#[cfg_attr(feature="dev", allow(too_many_arguments))]
pub fn enact_verified(
block: &PreverifiedBlock,
engine: &Engine,
tracing: bool,
db: Box<JournalDB>,
parent: &Header,
last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
vm_factory: &EvmFactory
) -> Result<LockedBlock, Error> {
let view = BlockView::new(&block.bytes);
enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, vm_factory)
enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, vm_factory)
}
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards
pub fn enact_and_seal(block_bytes: &[u8], engine: &Engine, tracing: bool, db: Box<JournalDB>, parent: &Header, last_hashes: LastHashes, vm_factory: &EvmFactory) -> Result<SealedBlock, Error> {
#[cfg_attr(feature="dev", allow(too_many_arguments))]
pub fn enact_and_seal(
block_bytes: &[u8],
engine: &Engine,
tracing: bool,
db: Box<JournalDB>,
parent: &Header,
last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
vm_factory: &EvmFactory
) -> Result<SealedBlock, Error> {
let header = BlockView::new(block_bytes).header_view();
Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, vm_factory)).seal(engine, header.seal())))
Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, vm_factory)).seal(engine, header.seal())))
}
#[cfg(test)]
@ -509,7 +555,7 @@ mod tests {
spec.ensure_db_good(db.as_hashdb_mut());
let last_hashes = vec![genesis_header.hash()];
let vm_factory = Default::default();
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, Address::zero(), 3141562.into(), vec![]).unwrap();
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, None, Address::zero(), 3141562.into(), vec![]).unwrap();
let b = b.close_and_lock();
let _ = b.seal(engine.deref(), vec![]);
}
@ -525,7 +571,7 @@ mod tests {
let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut());
let vm_factory = Default::default();
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), 3141562.into(), vec![]).unwrap()
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, vec![genesis_header.hash()], None, Address::zero(), 3141562.into(), vec![]).unwrap()
.close_and_lock().seal(engine.deref(), vec![]).unwrap();
let orig_bytes = b.rlp_bytes();
let orig_db = b.drain();
@ -533,7 +579,7 @@ mod tests {
let mut db_result = get_temp_journal_db();
let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut());
let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], &Default::default()).unwrap();
let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], None, &Default::default()).unwrap();
assert_eq!(e.rlp_bytes(), orig_bytes);
@ -553,7 +599,7 @@ mod tests {
let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut());
let vm_factory = Default::default();
let mut open_block = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), 3141562.into(), vec![]).unwrap();
let mut open_block = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, vec![genesis_header.hash()], None, Address::zero(), 3141562.into(), vec![]).unwrap();
let mut uncle1_header = Header::new();
uncle1_header.extra_data = b"uncle1".to_vec();
let mut uncle2_header = Header::new();
@ -568,7 +614,7 @@ mod tests {
let mut db_result = get_temp_journal_db();
let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut());
let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], &Default::default()).unwrap();
let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], None, &Default::default()).unwrap();
let bytes = e.rlp_bytes();
assert_eq!(bytes, orig_bytes);

View File

@ -130,7 +130,9 @@ impl QueueSignal {
}
if self.signalled.compare_and_swap(false, true, AtomicOrdering::Relaxed) == false {
self.message_channel.send(UserMessage(SyncMessage::BlockVerified)).expect("Error sending BlockVerified message");
if let Err(e) = self.message_channel.send(UserMessage(SyncMessage::BlockVerified)) {
debug!("Error sending BlockVerified message: {:?}", e);
}
}
}

View File

@ -253,12 +253,22 @@ impl BlockChain {
// open extras db
let mut extras_path = path.to_path_buf();
extras_path.push("extras");
let extras_db = Database::open_default(extras_path.to_str().unwrap()).unwrap();
let extras_db = match config.db_cache_size {
None => Database::open_default(extras_path.to_str().unwrap()).unwrap(),
Some(cache_size) => Database::open(
&DatabaseConfig::with_cache(cache_size/2),
extras_path.to_str().unwrap()).unwrap(),
};
// open blocks db
let mut blocks_path = path.to_path_buf();
blocks_path.push("blocks");
let blocks_db = Database::open_default(blocks_path.to_str().unwrap()).unwrap();
let blocks_db = match config.db_cache_size {
None => Database::open_default(blocks_path.to_str().unwrap()).unwrap(),
Some(cache_size) => Database::open(
&DatabaseConfig::with_cache(cache_size/2),
blocks_path.to_str().unwrap()).unwrap(),
};
let mut cache_man = CacheManager{cache_usage: VecDeque::new(), in_use: HashSet::new()};
(0..COLLECTION_QUEUE_SIZE).foreach(|_| cache_man.cache_usage.push_back(HashSet::new()));

View File

@ -23,6 +23,8 @@ pub struct Config {
pub pref_cache_size: usize,
/// Maximum cache size in bytes.
pub max_cache_size: usize,
/// Backing db cache_size
pub db_cache_size: Option<usize>,
}
impl Default for Config {
@ -30,6 +32,7 @@ impl Default for Config {
Config {
pref_cache_size: 1 << 14,
max_cache_size: 1 << 20,
db_cache_size: None,
}
}
}

View File

@ -18,6 +18,7 @@
use std::marker::PhantomData;
use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
use util::*;
use util::panics::*;
use views::BlockView;
@ -50,6 +51,8 @@ pub use types::block_status::BlockStatus;
use evm::Factory as EvmFactory;
use miner::{Miner, MinerService, TransactionImportResult, AccountDetails};
const MAX_TX_QUEUE_SIZE: usize = 4096;
impl fmt::Display for BlockChainInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "#{}.{}", self.best_block_number, self.best_block_hash)
@ -92,6 +95,8 @@ pub struct Client<V = CanonVerifier> where V: Verifier {
verifier: PhantomData<V>,
vm_factory: Arc<EvmFactory>,
miner: Arc<Miner>,
io_channel: IoChannel<NetSyncMessage>,
queue_transactions: AtomicUsize,
}
const HISTORY: u64 = 1200;
@ -141,7 +146,10 @@ impl<V> Client<V> where V: Verifier {
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, &path));
let tracedb = Arc::new(try!(TraceDB::new(config.tracing, &path, chain.clone())));
let mut state_db = journaldb::new(&append_path(&path, "state"), config.pruning);
let mut state_db = journaldb::new(
&append_path(&path, "state"),
config.pruning,
config.db_cache_size);
if state_db.is_empty() && spec.ensure_db_good(state_db.as_hashdb_mut()) {
state_db.commit(0, &spec.genesis_header().hash(), None).expect("Error commiting genesis state to state DB");
@ -149,7 +157,7 @@ impl<V> Client<V> where V: Verifier {
let engine = Arc::new(spec.engine);
let block_queue = BlockQueue::new(config.queue, engine.clone(), message_channel);
let block_queue = BlockQueue::new(config.queue, engine.clone(), message_channel.clone());
let panic_handler = PanicHandler::new_in_arc();
panic_handler.forward_from(&block_queue);
@ -165,6 +173,8 @@ impl<V> Client<V> where V: Verifier {
verifier: PhantomData,
vm_factory: Arc::new(EvmFactory::new(config.vm_type)),
miner: miner,
io_channel: message_channel,
queue_transactions: AtomicUsize::new(0),
};
Ok(Arc::new(client))
@ -220,7 +230,7 @@ impl<V> Client<V> where V: Verifier {
let last_hashes = self.build_last_hashes(header.parent_hash.clone());
let db = self.state_db.lock().unwrap().boxed_clone();
let enact_result = enact_verified(&block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes, &self.vm_factory);
let enact_result = enact_verified(&block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes, self.dao_rescue_block_gas_limit(header.parent_hash.clone()), &self.vm_factory);
if let Err(e) = enact_result {
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
return Err(());
@ -271,6 +281,7 @@ impl<V> Client<V> where V: Verifier {
let mut import_results = Vec::with_capacity(max_blocks_to_import);
let _import_lock = self.import_lock.lock();
let _timer = PerfTimer::new("import_verified_blocks");
let blocks = self.block_queue.drain(max_blocks_to_import);
let original_best = self.chain_info().best_block_hash;
@ -361,6 +372,19 @@ impl<V> Client<V> where V: Verifier {
imported
}
/// Import transactions from the IO queue
pub fn import_queued_transactions(&self, transactions: &[Bytes]) -> usize {
let _timer = PerfTimer::new("import_queued_transactions");
self.queue_transactions.fetch_sub(transactions.len(), AtomicOrdering::SeqCst);
let fetch_account = |a: &Address| AccountDetails {
nonce: self.latest_nonce(a),
balance: self.latest_balance(a),
};
let tx = transactions.iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect();
let results = self.miner.import_transactions(self, tx, fetch_account);
results.len()
}
/// Attempt to get a copy of a specific block's state.
///
/// This will not fail if given BlockID::Latest.
@ -462,6 +486,7 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
last_hashes: last_hashes,
gas_used: U256::zero(),
gas_limit: U256::max_value(),
dao_rescue_block_gas_limit: self.dao_rescue_block_gas_limit(view.parent_hash()),
};
// that's just a copy of the state.
let mut state = self.state();
@ -747,7 +772,23 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
nonce: self.latest_nonce(a),
balance: self.latest_balance(a),
};
self.miner.import_transactions(transactions, fetch_account)
self.miner.import_transactions(self, transactions, fetch_account)
}
fn queue_transactions(&self, transactions: Vec<Bytes>) {
if self.queue_transactions.load(AtomicOrdering::Relaxed) > MAX_TX_QUEUE_SIZE {
debug!("Ignoring {} transactions: queue is full", transactions.len());
} else {
let len = transactions.len();
match self.io_channel.send(NetworkIoMessage::User(SyncMessage::NewTransactions(transactions))) {
Ok(_) => {
self.queue_transactions.fetch_add(len, AtomicOrdering::SeqCst);
}
Err(e) => {
debug!("Ignoring {} transactions: error queueing: {}", len, e);
}
}
}
}
fn all_transactions(&self) -> Vec<SignedTransaction> {
@ -767,6 +808,7 @@ impl<V> MiningBlockChainClient for Client<V> where V: Verifier {
self.state_db.lock().unwrap().boxed_clone(),
&self.chain.block_header(&h).expect("h is best block hash: so it's header must exist: qed"),
self.build_last_hashes(h.clone()),
self.dao_rescue_block_gas_limit(h.clone()),
author,
gas_floor_target,
extra_data,

View File

@ -35,4 +35,6 @@ pub struct ClientConfig {
pub pruning: journaldb::Algorithm,
/// The name of the client instance.
pub name: String,
/// State db cache-size if not default
pub db_cache_size: Option<usize>,
}

View File

@ -42,7 +42,7 @@ use header::{BlockNumber, Header};
use transaction::{LocalizedTransaction, SignedTransaction};
use log_entry::LocalizedLogEntry;
use filter::Filter;
use views::BlockView;
use views::{HeaderView, BlockView};
use error::{ImportResult, ExecutionError};
use receipt::LocalizedReceipt;
use trace::LocalizedTrace;
@ -193,6 +193,9 @@ pub trait BlockChainClient : Sync + Send {
/// import transactions from network/other 3rd party
fn import_transactions(&self, transactions: Vec<SignedTransaction>) -> Vec<Result<TransactionImportResult, EthError>>;
/// Queue transactions for importing.
fn queue_transactions(&self, transactions: Vec<Bytes>);
/// list all transactions
fn all_transactions(&self) -> Vec<SignedTransaction>;
@ -221,6 +224,28 @@ pub trait BlockChainClient : Sync + Send {
Err(())
}
}
/// Get `Some` gas limit of SOFT_FORK_BLOCK, or `None` if chain is not yet that long.
fn dao_rescue_block_gas_limit(&self, chain_hash: H256) -> Option<U256> {
const SOFT_FORK_BLOCK: u64 = 1775000;
// shortcut if the canon chain is already known.
if self.chain_info().best_block_number > SOFT_FORK_BLOCK + 1000 {
return self.block_header(BlockID::Number(SOFT_FORK_BLOCK)).map(|header| HeaderView::new(&header).gas_limit());
}
// otherwise check according to `chain_hash`.
if let Some(mut header) = self.block_header(BlockID::Hash(chain_hash)) {
if HeaderView::new(&header).number() < SOFT_FORK_BLOCK {
None
} else {
while HeaderView::new(&header).number() != SOFT_FORK_BLOCK {
header = self.block_header(BlockID::Hash(HeaderView::new(&header).parent_hash())).expect("chain is complete; parent of chain entry must be in chain; qed");
}
Some(HeaderView::new(&header).gas_limit())
}
} else {
None
}
}
}
/// Extended client interface used for mining

View File

@ -188,7 +188,7 @@ impl TestBlockChainClient {
txs.append(&signed_tx);
txs.out()
},
_ => rlp::NULL_RLP.to_vec()
_ => rlp::EMPTY_LIST_RLP.to_vec()
};
let mut rlp = RlpStream::new_list(3);
@ -491,7 +491,13 @@ impl BlockChainClient for TestBlockChainClient {
balance: balances[a],
};
self.miner.import_transactions(transactions, &fetch_account)
self.miner.import_transactions(self, transactions, &fetch_account)
}
fn queue_transactions(&self, transactions: Vec<Bytes>) {
// import right here
let tx = transactions.into_iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect();
self.import_transactions(tx);
}
fn all_transactions(&self) -> Vec<SignedTransaction> {

View File

@ -39,6 +39,9 @@ pub struct EnvInfo {
pub last_hashes: LastHashes,
/// The gas used.
pub gas_used: U256,
/// Block gas limit at DAO rescue block SOFT_FORK_BLOCK or None if not yet there.
pub dao_rescue_block_gas_limit: Option<U256>,
}
impl Default for EnvInfo {
@ -51,6 +54,7 @@ impl Default for EnvInfo {
gas_limit: 0.into(),
last_hashes: vec![],
gas_used: 0.into(),
dao_rescue_block_gas_limit: None,
}
}
}
@ -66,6 +70,7 @@ impl From<ethjson::vm::Env> for EnvInfo {
timestamp: e.timestamp.into(),
last_hashes: (1..cmp::min(number + 1, 257)).map(|i| format!("{}", number - i).as_bytes().sha3()).collect(),
gas_used: U256::zero(),
dao_rescue_block_gas_limit: None,
}
}
}

View File

@ -228,7 +228,7 @@ pub enum Error {
/// The value of the nonce or mishash is invalid.
PowInvalid,
/// Error concerning TrieDBs
TrieError(TrieError),
Trie(TrieError),
}
impl fmt::Display for Error {
@ -244,7 +244,7 @@ impl fmt::Display for Error {
f.write_fmt(format_args!("Unknown engine name ({})", name)),
Error::PowHashInvalid => f.write_str("Invalid or out of date PoW hash."),
Error::PowInvalid => f.write_str("Invalid nonce or mishash"),
Error::TrieError(ref err) => f.write_fmt(format_args!("{}", err)),
Error::Trie(ref err) => f.write_fmt(format_args!("{}", err)),
}
}
}
@ -308,7 +308,7 @@ impl From<IoError> for Error {
impl From<TrieError> for Error {
fn from(err: TrieError) -> Error {
Error::TrieError(err)
Error::Trie(err)
}
}

View File

@ -41,6 +41,8 @@ pub struct EthashParams {
pub registrar: Address,
/// Homestead transition block number.
pub frontier_compatibility_mode_limit: u64,
/// Enable the soft-fork logic.
pub dao_rescue_soft_fork: bool,
}
impl From<ethjson::spec::EthashParams> for EthashParams {
@ -53,6 +55,7 @@ impl From<ethjson::spec::EthashParams> for EthashParams {
block_reward: p.block_reward.into(),
registrar: p.registrar.into(),
frontier_compatibility_mode_limit: p.frontier_compatibility_mode_limit.into(),
dao_rescue_soft_fork: p.dao_rescue_soft_fork.into(),
}
}
}
@ -102,8 +105,9 @@ impl Engine for Ethash {
Schedule::new_frontier()
} else {
let mut s = Schedule::new_homestead();
// TODO: make dependent on gaslimit > 4000000 of block 1760000.
s.reject_dao_transactions = env_info.number >= 1760000;
if self.ethash_params.dao_rescue_soft_fork {
s.reject_dao_transactions = env_info.dao_rescue_block_gas_limit.map_or(false, |x| x <= 4_000_000.into());
}
s
}
}
@ -319,7 +323,7 @@ mod tests {
spec.ensure_db_good(db.as_hashdb_mut());
let last_hashes = vec![genesis_header.hash()];
let vm_factory = Default::default();
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, Address::zero(), 3141562.into(), vec![]).unwrap();
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, None, Address::zero(), 3141562.into(), vec![]).unwrap();
let b = b.close();
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap());
}
@ -334,7 +338,7 @@ mod tests {
spec.ensure_db_good(db.as_hashdb_mut());
let last_hashes = vec![genesis_header.hash()];
let vm_factory = Default::default();
let mut b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, Address::zero(), 3141562.into(), vec![]).unwrap();
let mut b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, None, Address::zero(), 3141562.into(), vec![]).unwrap();
let mut uncle = Header::new();
let uncle_author = address_from_hex("ef2d6d194084c2de36e0dabfce45d046b37d1106");
uncle.author = uncle_author.clone();
@ -362,7 +366,8 @@ mod tests {
difficulty: 0.into(),
last_hashes: vec![],
gas_used: 0.into(),
gas_limit: 0.into()
gas_limit: 0.into(),
dao_rescue_block_gas_limit: None,
});
assert!(schedule.stack_limit > 0);
@ -374,7 +379,8 @@ mod tests {
difficulty: 0.into(),
last_hashes: vec![],
gas_used: 0.into(),
gas_limit: 0.into()
gas_limit: 0.into(),
dao_rescue_block_gas_limit: None,
});
assert!(!schedule.have_delegate_call);

View File

@ -33,7 +33,12 @@ use super::spec::*;
pub fn new_olympic() -> Spec { Spec::load(include_bytes!("../../res/ethereum/olympic.json")) }
/// Create a new Frontier mainnet chain spec.
pub fn new_frontier() -> Spec { Spec::load(include_bytes!("../../res/ethereum/frontier.json")) }
pub fn new_frontier(dao_rescue: bool) -> Spec {
Spec::load(match dao_rescue {
true => include_bytes!("../../res/ethereum/frontier_dao_rescue.json"),
false => include_bytes!("../../res/ethereum/frontier.json"),
})
}
/// Create a new Frontier chain spec as though it never changes to Homestead.
pub fn new_frontier_test() -> Spec { Spec::load(include_bytes!("../../res/ethereum/frontier_test.json")) }
@ -84,7 +89,7 @@ mod tests {
#[test]
fn frontier() {
let frontier = new_frontier();
let frontier = new_frontier(true);
assert_eq!(frontier.state_root(), H256::from_str("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544").unwrap());
let genesis = frontier.genesis_block();

View File

@ -16,9 +16,8 @@
//! Just in time compiler execution environment.
use common::*;
use trace::VMTracer;
use evmjit;
use evm::{self, Error, GasLeft};
use evm::{self, GasLeft};
/// Should be used to convert jit types to ethcore
trait FromJit<T>: Sized {
@ -303,7 +302,7 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
#[derive(Default)]
pub struct JitEvm {
ctxt: Option<evmjit::ContextHandle>,
context: Option<evmjit::ContextHandle>,
}
impl evm::Evm for JitEvm {
@ -347,7 +346,7 @@ impl evm::Evm for JitEvm {
data.timestamp = ext.env_info().timestamp as i64;
self.context = Some(unsafe { evmjit::ContextHandle::new(data, schedule, &mut ext_handle) });
let context = self.context.as_ref_mut().unwrap();
let mut context = self.context.as_mut().unwrap();
let res = context.exec();
match res {

View File

@ -318,7 +318,8 @@ mod tests {
difficulty: 0.into(),
last_hashes: vec![],
gas_used: 0.into(),
gas_limit: 0.into()
gas_limit: 0.into(),
dao_rescue_block_gas_limit: None,
}
}

View File

@ -89,10 +89,11 @@ impl Miner {
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
fn prepare_sealing(&self, chain: &MiningBlockChainClient) {
trace!(target: "miner", "prepare_sealing: entering");
let transactions = self.transaction_queue.lock().unwrap().top_transactions();
let (transactions, mut open_block) = {
let transactions = {self.transaction_queue.lock().unwrap().top_transactions()};
let mut sealing_work = self.sealing_work.lock().unwrap();
let best_hash = chain.best_block_header().sha3();
/*
// check to see if last ClosedBlock in would_seals is actually same parent block.
// if so
@ -101,7 +102,7 @@ impl Miner {
// otherwise, leave everything alone.
// otherwise, author a fresh block.
*/
let mut open_block = match sealing_work.pop_if(|b| b.block().fields().header.parent_hash() == &best_hash) {
let open_block = match sealing_work.pop_if(|b| b.block().fields().header.parent_hash() == &best_hash) {
Some(old_block) => {
trace!(target: "miner", "Already have previous work; updating and returning");
// add transactions to old_block
@ -118,6 +119,8 @@ impl Miner {
)
}
};
(transactions, open_block)
};
let mut invalid_transactions = HashSet::new();
let block_number = open_block.block().fields().header.number();
@ -146,15 +149,17 @@ impl Miner {
let block = open_block.close();
let mut queue = self.transaction_queue.lock().unwrap();
let fetch_account = |a: &Address| AccountDetails {
nonce: chain.latest_nonce(a),
balance: chain.latest_balance(a),
};
{
let mut queue = self.transaction_queue.lock().unwrap();
for hash in invalid_transactions.into_iter() {
queue.remove_invalid(&hash, &fetch_account);
}
}
if !block.transactions().is_empty() {
trace!(target: "miner", "prepare_sealing: block has transaction - attempting internal seal.");
@ -179,6 +184,8 @@ impl Miner {
trace!(target: "miner", "prepare_sealing: unable to generate seal internally");
}
}
let mut sealing_work = self.sealing_work.lock().unwrap();
if sealing_work.peek_last_ref().map_or(true, |pb| pb.block().fields().header.hash() != block.block().fields().header.hash()) {
trace!(target: "miner", "Pushing a new, refreshed or borrowed pending {}...", block.block().fields().header.hash());
sealing_work.push(block);
@ -250,6 +257,7 @@ impl MinerService for Miner {
last_hashes: last_hashes,
gas_used: U256::zero(),
gas_limit: U256::max_value(),
dao_rescue_block_gas_limit: chain.dao_rescue_block_gas_limit(header.parent_hash().clone()),
};
// that's just a copy of the state.
let mut state = block.state().clone();
@ -359,13 +367,19 @@ impl MinerService for Miner {
*self.gas_floor_target.read().unwrap()
}
fn import_transactions<T>(&self, transactions: Vec<SignedTransaction>, fetch_account: T) ->
fn import_transactions<T>(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>, fetch_account: T) ->
Vec<Result<TransactionImportResult, Error>>
where T: Fn(&Address) -> AccountDetails {
let results: Vec<Result<TransactionImportResult, Error>> = {
let mut transaction_queue = self.transaction_queue.lock().unwrap();
transactions.into_iter()
.map(|tx| transaction_queue.add(tx, &fetch_account, TransactionOrigin::External))
.collect()
};
if !results.is_empty() {
self.update_sealing(chain);
}
results
}
fn import_own_transaction<T>(
@ -547,7 +561,7 @@ impl MinerService for Miner {
for tx in &txs {
let _sender = tx.sender();
}
let _ = self.import_transactions(txs, |a| AccountDetails {
let _ = self.import_transactions(chain, txs, |a| AccountDetails {
nonce: chain.latest_nonce(a),
balance: chain.latest_balance(a),
});
@ -586,6 +600,7 @@ mod tests {
use util::*;
use client::{TestBlockChainClient, EachBlockWith};
use block::*;
use spec::Spec;
// TODO [ToDr] To uncomment` when TestBlockChainClient can actually return a ClosedBlock.
#[ignore]

View File

@ -95,7 +95,7 @@ pub trait MinerService : Send + Sync {
fn set_transactions_limit(&self, limit: usize);
/// Imports transactions to transaction queue.
fn import_transactions<T>(&self, transactions: Vec<SignedTransaction>, fetch_account: T) ->
fn import_transactions<T>(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>, fetch_account: T) ->
Vec<Result<TransactionImportResult, Error>>
where T: Fn(&Address) -> AccountDetails, Self: Sized;

View File

@ -167,16 +167,17 @@ impl PartialOrd for TransactionOrder {
impl Ord for TransactionOrder {
fn cmp(&self, b: &TransactionOrder) -> Ordering {
// Local transactions should always have priority
if self.origin != b.origin {
return self.origin.cmp(&b.origin);
}
// First check nonce_height
if self.nonce_height != b.nonce_height {
return self.nonce_height.cmp(&b.nonce_height);
}
// Local transactions should always have priority
// NOTE nonce has to be checked first, cause otherwise the order might be wrong.
if self.origin != b.origin {
return self.origin.cmp(&b.origin);
}
// Then compare gas_prices
let a_gas = self.gas_price;
let b_gas = b.gas_price;
@ -235,22 +236,22 @@ impl TransactionSet {
self.by_priority.insert(order.clone());
let r = self.by_address.insert(sender, nonce, order);
// If transaction was replaced remove it from priority queue
if let Some(ref order) = r {
self.by_priority.remove(order);
if let Some(ref old_order) = r {
self.by_priority.remove(old_order);
}
assert_eq!(self.by_priority.len(), self.by_address.len());
r
}
/// Remove low priority transactions if there is more then specified by given `limit`.
///
/// It drops transactions from this set but also removes associated `VerifiedTransaction`.
/// Returns addresses and highes nonces of transactions removed because of limit.
/// Returns addresses and lowest nonces of transactions removed because of limit.
fn enforce_limit(&mut self, by_hash: &mut HashMap<H256, VerifiedTransaction>) -> Option<HashMap<Address, U256>> {
let len = self.by_priority.len();
if len <= self.limit {
return None;
}
let to_drop : Vec<(Address, U256)> = {
self.by_priority
.iter()
@ -269,8 +270,8 @@ impl TransactionSet {
by_hash.remove(&order.hash)
.expect("Hash found in `by_priorty` matches the one dropped; so it is included in `by_hash`");
let max = removed.get(&sender).map_or(nonce, |val| cmp::max(*val, nonce));
removed.insert(sender, max);
let min = removed.get(&sender).map_or(nonce, |val| cmp::min(*val, nonce));
removed.insert(sender, min);
removed
}))
}
@ -279,8 +280,10 @@ impl TransactionSet {
fn drop(&mut self, sender: &Address, nonce: &U256) -> Option<TransactionOrder> {
if let Some(tx_order) = self.by_address.remove(sender, nonce) {
self.by_priority.remove(&tx_order);
assert_eq!(self.by_priority.len(), self.by_address.len());
return Some(tx_order);
}
assert_eq!(self.by_priority.len(), self.by_address.len());
None
}
@ -468,7 +471,9 @@ impl TransactionQueue {
}));
}
self.import_tx(vtx, client_account.nonce).map_err(Error::Transaction)
let r = self.import_tx(vtx, client_account.nonce).map_err(Error::Transaction);
assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len());
r
}
/// Removes all transactions from particular sender up to (excluding) given client (state) nonce.
@ -484,6 +489,7 @@ impl TransactionQueue {
// And now lets check if there is some batch of transactions in future
// that should be placed in current. It should also update last_nonces.
self.move_matching_future_to_current(sender, client_nonce, client_nonce);
assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len());
}
/// Removes invalid transaction identified by hash from queue.
@ -493,6 +499,8 @@ impl TransactionQueue {
/// If gap is introduced marks subsequent transactions as future
pub fn remove_invalid<T>(&mut self, transaction_hash: &H256, fetch_account: &T)
where T: Fn(&Address) -> AccountDetails {
assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len());
let transaction = self.by_hash.remove(transaction_hash);
if transaction.is_none() {
// We don't know this transaction
@ -511,22 +519,17 @@ impl TransactionQueue {
// And now lets check if there is some chain of transactions in future
// that should be placed in current
self.move_matching_future_to_current(sender, current_nonce, current_nonce);
assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len());
return;
}
// Remove from current
let order = self.current.drop(&sender, &nonce);
if order.is_some() {
// We will either move transaction to future or remove it completely
// so there will be no transactions from this sender in current
self.last_nonces.remove(&sender);
// First update height of transactions in future to avoid collisions
self.update_future(&sender, current_nonce);
// This should move all current transactions to future and remove old transactions
self.move_all_to_future(&sender, current_nonce);
// And now lets check if there is some chain of transactions in future
// that should be placed in current. It should also update last_nonces.
self.move_matching_future_to_current(sender, current_nonce, current_nonce);
// This will keep consistency in queue
// Moves all to future and then promotes a batch from current:
self.remove_all(sender, current_nonce);
assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len());
return;
}
}
@ -545,7 +548,7 @@ impl TransactionQueue {
} else {
trace!(target: "miner", "Removing old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce);
// Remove the transaction completely
self.by_hash.remove(&order.hash);
self.by_hash.remove(&order.hash).expect("All transactions in `future` are also in `by_hash`");
}
}
}
@ -565,7 +568,7 @@ impl TransactionQueue {
self.future.insert(*sender, k, order.update_height(k, current_nonce));
} else {
trace!(target: "miner", "Removing old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce);
self.by_hash.remove(&order.hash);
self.by_hash.remove(&order.hash).expect("All transactions in `future` are also in `by_hash`");
}
}
self.future.enforce_limit(&mut self.by_hash);
@ -664,21 +667,27 @@ impl TransactionQueue {
.cloned()
.map_or(state_nonce, |n| n + U256::one());
// Check height
if nonce > next_nonce {
// We have a gap - put to future
try!(check_too_cheap(Self::replace_transaction(tx, next_nonce, &mut self.future, &mut self.by_hash)));
try!(check_if_removed(&address, &nonce, self.future.enforce_limit(&mut self.by_hash)));
return Ok(TransactionImportResult::Future);
} else if nonce < state_nonce {
// The transaction might be old, let's check that.
// This has to be the first test, otherwise calculating
// nonce height would result in overflow.
if nonce < state_nonce {
// Droping transaction
trace!(target: "miner", "Dropping old transaction: {:?} (nonce: {} < {})", tx.hash(), nonce, next_nonce);
return Err(TransactionError::Old);
} else if nonce > next_nonce {
// We have a gap - put to future.
// Update nonces of transactions in future (remove old transactions)
self.update_future(&address, state_nonce);
// Insert transaction (or replace old one with lower gas price)
try!(check_too_cheap(Self::replace_transaction(tx, state_nonce, &mut self.future, &mut self.by_hash)));
// Return an error if this transaction is not imported because of limit.
try!(check_if_removed(&address, &nonce, self.future.enforce_limit(&mut self.by_hash)));
return Ok(TransactionImportResult::Future);
}
try!(check_too_cheap(Self::replace_transaction(tx, state_nonce, &mut self.current, &mut self.by_hash)));
// Keep track of highest nonce stored in current
self.last_nonces.insert(address, nonce);
let new_max = self.last_nonces.get(&address).map_or(nonce, |n| cmp::max(nonce, *n));
self.last_nonces.insert(address, new_max);
// Update nonces of transactions in future
self.update_future(&address, state_nonce);
// Maybe there are some more items waiting in future?
@ -687,15 +696,16 @@ impl TransactionQueue {
// same (sender, nonce), but above function would not move it.
if let Some(order) = self.future.drop(&address, &nonce) {
// Let's insert that transaction to current (if it has higher gas_price)
let future_tx = self.by_hash.remove(&order.hash).unwrap();
try!(check_too_cheap(Self::replace_transaction(future_tx, state_nonce, &mut self.current, &mut self.by_hash)));
let future_tx = self.by_hash.remove(&order.hash).expect("All transactions in `future` are always in `by_hash`.");
// if transaction in `current` (then one we are importing) is replaced it means that it has to low gas_price
try!(check_too_cheap(!Self::replace_transaction(future_tx, state_nonce, &mut self.current, &mut self.by_hash)));
}
// Also enforce the limit
let removed = self.current.enforce_limit(&mut self.by_hash);
// If some transaction were removed because of limit we need to update last_nonces also.
self.update_last_nonces(&removed);
// Trigger error if we were removed.
// Trigger error if the transaction we are importing was removed.
try!(check_if_removed(&address, &nonce, removed));
trace!(target: "miner", "status: {:?}", self.status());
@ -703,9 +713,9 @@ impl TransactionQueue {
}
/// Updates
fn update_last_nonces(&mut self, removed_max_nonces: &Option<HashMap<Address, U256>>) {
if let Some(ref max_nonces) = *removed_max_nonces {
for (sender, nonce) in max_nonces.iter() {
fn update_last_nonces(&mut self, removed_min_nonces: &Option<HashMap<Address, U256>>) {
if let Some(ref min_nonces) = *removed_min_nonces {
for (sender, nonce) in min_nonces.iter() {
if *nonce == U256::zero() {
self.last_nonces.remove(sender);
} else {
@ -728,7 +738,9 @@ impl TransactionQueue {
let address = tx.sender();
let nonce = tx.nonce();
by_hash.insert(hash, tx);
let old_hash = by_hash.insert(hash, tx);
assert!(old_hash.is_none(), "Each hash has to be inserted exactly once.");
if let Some(old) = set.insert(address, nonce, order.clone()) {
// There was already transaction in queue. Let's check which one should stay
@ -738,11 +750,11 @@ impl TransactionQueue {
// Put back old transaction since it has greater priority (higher gas_price)
set.insert(address, nonce, old);
// and remove new one
by_hash.remove(&hash);
by_hash.remove(&hash).expect("The hash has been just inserted and no other line is altering `by_hash`.");
false
} else {
// Make sure we remove old transaction entirely
by_hash.remove(&old.hash);
by_hash.remove(&old.hash).expect("The hash is coming from `future` so it has to be in `by_hash`.");
true
}
} else {
@ -762,7 +774,7 @@ fn check_too_cheap(is_in: bool) -> Result<(), TransactionError> {
fn check_if_removed(sender: &Address, nonce: &U256, dropped: Option<HashMap<Address, U256>>) -> Result<(), TransactionError> {
match dropped {
Some(ref dropped) => match dropped.get(sender) {
Some(max) if nonce <= max => {
Some(min) if nonce >= min => {
Err(TransactionError::LimitReached)
},
_ => Ok(()),
@ -939,7 +951,7 @@ mod test {
let res = txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External);
// and then there should be only one transaction in current (the one with higher gas_price)
assert_eq!(unwrap_tx_err(res), TransactionError::TooCheapToReplace);
assert_eq!(res.unwrap(), TransactionImportResult::Current);
assert_eq!(txq.status().pending, 1);
assert_eq!(txq.status().future, 0);
assert_eq!(txq.current.by_priority.len(), 1);
@ -1087,7 +1099,28 @@ mod test {
}
#[test]
fn should_prioritize_local_transactions() {
fn should_prioritize_local_transactions_within_same_nonce_height() {
// given
let mut txq = TransactionQueue::new();
let tx = new_tx();
// the second one has same nonce but higher `gas_price`
let (_, tx2) = new_similar_txs();
// when
// first insert the one with higher gas price
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
// then the one with lower gas price, but local
txq.add(tx.clone(), &default_nonce, TransactionOrigin::Local).unwrap();
// then
let top = txq.top_transactions();
assert_eq!(top[0], tx); // local should be first
assert_eq!(top[1], tx2);
assert_eq!(top.len(), 2);
}
#[test]
fn should_not_prioritize_local_transactions_with_different_nonce_height() {
// given
let mut txq = TransactionQueue::new();
let (tx, tx2) = new_txs(U256::from(1));
@ -1098,8 +1131,8 @@ mod test {
// then
let top = txq.top_transactions();
assert_eq!(top[0], tx2);
assert_eq!(top[1], tx);
assert_eq!(top[0], tx);
assert_eq!(top[1], tx2);
assert_eq!(top.len(), 2);
}
@ -1555,4 +1588,54 @@ mod test {
assert_eq!(txq.has_local_pending_transactions(), true);
}
#[test]
fn should_keep_right_order_in_future() {
// given
let mut txq = TransactionQueue::with_limit(1);
let (tx1, tx2) = new_txs(U256::from(1));
let prev_nonce = |a: &Address| AccountDetails { nonce: default_nonce(a).nonce - U256::one(), balance:
default_nonce(a).balance };
// when
assert_eq!(txq.add(tx2, &prev_nonce, TransactionOrigin::External).unwrap(), TransactionImportResult::Future);
assert_eq!(txq.add(tx1.clone(), &prev_nonce, TransactionOrigin::External).unwrap(), TransactionImportResult::Future);
// then
assert_eq!(txq.future.by_priority.len(), 1);
assert_eq!(txq.future.by_priority.iter().next().unwrap().hash, tx1.hash());
}
#[test]
fn should_return_correct_last_nonce() {
// given
let mut txq = TransactionQueue::new();
let (tx1, tx2, tx2_2, tx3) = {
let keypair = KeyPair::create().unwrap();
let secret = &keypair.secret();
let nonce = U256::from(123);
let tx = new_unsigned_tx(nonce);
let tx2 = new_unsigned_tx(nonce + 1.into());
let mut tx2_2 = new_unsigned_tx(nonce + 1.into());
tx2_2.gas_price = U256::from(5);
let tx3 = new_unsigned_tx(nonce + 2.into());
(tx.sign(secret), tx2.sign(secret), tx2_2.sign(secret), tx3.sign(secret))
};
let sender = tx1.sender().unwrap();
txq.add(tx1, &default_nonce, TransactionOrigin::Local).unwrap();
txq.add(tx2, &default_nonce, TransactionOrigin::Local).unwrap();
txq.add(tx3, &default_nonce, TransactionOrigin::Local).unwrap();
assert_eq!(txq.future.by_priority.len(), 0);
assert_eq!(txq.current.by_priority.len(), 3);
// when
let res = txq.add(tx2_2, &default_nonce, TransactionOrigin::Local);
// then
assert_eq!(txq.last_nonce(&sender).unwrap(), 125.into());
assert_eq!(res.unwrap(), TransactionImportResult::Current);
assert_eq!(txq.current.by_priority.len(), 3);
}
}

View File

@ -41,6 +41,8 @@ pub enum SyncMessage {
NewChainHead,
/// A block is ready
BlockVerified,
/// New transaction RLPs are ready to be imported
NewTransactions(Vec<Bytes>),
/// Start network command.
StartNetwork,
/// Stop network command.
@ -136,6 +138,9 @@ impl IoHandler<NetSyncMessage> for ClientIoHandler {
SyncMessage::BlockVerified => {
self.client.import_verified_blocks(&io.channel());
},
SyncMessage::NewTransactions(ref transactions) => {
self.client.import_queued_transactions(&transactions);
},
_ => {}, // ignore other messages
}
}
@ -154,9 +159,15 @@ mod tests {
#[test]
fn it_can_be_started() {
let spec = get_test_spec();
let temp_path = RandomTempPath::new();
let service = ClientService::start(ClientConfig::default(), spec, NetworkConfiguration::new_local(), &temp_path.as_path(), Arc::new(Miner::with_spec(spec)), false);
let service = ClientService::start(
ClientConfig::default(),
get_test_spec(),
NetworkConfiguration::new_local(),
&temp_path.as_path(),
Arc::new(Miner::with_spec(get_test_spec())),
false
);
assert!(service.is_ok());
}
}

View File

@ -222,10 +222,15 @@ impl State {
let options = TransactOptions { tracing: tracing, vm_tracing: false, check_nonce: true };
let e = try!(Executive::new(self, env_info, engine, vm_factory).transact(t, options));
let broken_dao = H256::from("7278d050619a624f84f51987149ddb439cdaadfba5966f7cfaea7ad44340a4ba");
let broken_dao = H256::from("6a5d24750f78441e56fec050dc52fe8e911976485b7472faac7464a176a67caa");
// dao attack soft fork
if engine.schedule(&env_info).reject_dao_transactions {
let whitelisted = if let Action::Call(to) = t.action {
to == Address::from("Da4a4626d3E16e094De3225A751aAb7128e96526") ||
to == Address::from("2ba9D006C1D72E67A70b5526Fc6b4b0C0fd6D334")
} else { false };
if !whitelisted {
// collect all the addresses which have changed.
let addresses = self.cache.borrow().iter().map(|(addr, _)| addr.clone()).collect::<Vec<_>>();
@ -239,6 +244,7 @@ impl State {
}
}
}
}
// TODO uncomment once to_pod() works correctly.
// trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod()));

View File

@ -179,6 +179,7 @@ pub fn generate_dummy_client_with_spec_and_data<F>(get_test_spec: F, block_numbe
db,
&last_header,
last_hashes.clone(),
None,
author.clone(),
3141562.into(),
vec![]
@ -302,7 +303,7 @@ pub fn generate_dummy_empty_blockchain() -> GuardedTempResult<BlockChain> {
pub fn get_temp_journal_db() -> GuardedTempResult<Box<JournalDB>> {
let temp = RandomTempPath::new();
let journal_db = journaldb::new(temp.as_str(), journaldb::Algorithm::EarlyMerge);
let journal_db = journaldb::new(temp.as_str(), journaldb::Algorithm::EarlyMerge, None);
GuardedTempResult {
_temp: temp,
result: Some(journal_db)
@ -319,7 +320,7 @@ pub fn get_temp_state() -> GuardedTempResult<State> {
}
pub fn get_temp_journal_db_in(path: &Path) -> Box<JournalDB> {
journaldb::new(path.to_str().unwrap(), journaldb::Algorithm::EarlyMerge)
journaldb::new(path.to_str().unwrap(), journaldb::Algorithm::EarlyMerge, None)
}
pub fn get_temp_state_in(path: &Path) -> State {

View File

@ -48,6 +48,8 @@ pub struct Config {
pub enabled: Switch,
/// Traces blooms configuration.
pub blooms: BloomConfig,
/// Database cache-size if not default
pub db_cache_size: Option<usize>,
}
impl Default for Config {
@ -57,7 +59,8 @@ impl Default for Config {
blooms: BloomConfig {
levels: 3,
elements_per_index: 16,
}
},
db_cache_size: None,
}
}
}

View File

@ -22,7 +22,7 @@ use std::sync::{RwLock, Arc};
use std::path::Path;
use bloomchain::{Number, Config as BloomConfig};
use bloomchain::group::{BloomGroupDatabase, BloomGroupChain, GroupPosition, BloomGroup};
use util::{H256, H264, Database, DBTransaction};
use util::{H256, H264, Database, DatabaseConfig, DBTransaction};
use header::BlockNumber;
use trace::{BlockTraces, LocalizedTrace, Config, Switch, Filter, Database as TraceDatabase, ImportRequest,
DatabaseExtras, Error};
@ -118,7 +118,12 @@ impl<T> TraceDB<T> where T: DatabaseExtras {
pub fn new(config: Config, path: &Path, extras: Arc<T>) -> Result<Self, Error> {
let mut tracedb_path = path.to_path_buf();
tracedb_path.push("tracedb");
let tracesdb = Database::open_default(tracedb_path.to_str().unwrap()).unwrap();
let tracesdb = match config.db_cache_size {
None => Database::open_default(tracedb_path.to_str().unwrap()).unwrap(),
Some(db_cache) => Database::open(
&DatabaseConfig::with_cache(db_cache),
tracedb_path.to_str().unwrap()).unwrap(),
};
// check if in previously tracing was enabled
let old_tracing = match tracesdb.get(b"enabled").unwrap() {

View File

@ -12,7 +12,7 @@ rustc-serialize = "0.3"
docopt = { version = "0.6", optional = true }
[features]
default = ["cli"]
default = []
cli = ["docopt"]
[[bin]]

View File

@ -21,7 +21,8 @@ Usage:
ethkey generate prefix <prefix> <iterations> [options]
ethkey generate brain <seed> [options]
ethkey sign <secret> <message>
ethkey verify <public> <signature> <message>
ethkey verify public <public> <signature> <message>
ethkey verify address <address> <signature> <message>
ethkey [-h | --help]
Options:
@ -126,15 +127,32 @@ c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8
--
#### `verify <public> <signature> <message>`
#### `verify public <public> <signature> <message>`
*Verify the signature.*
- `<secret>` - ethereum public, 64 bytes long
- `<public>` - ethereum public, 64 bytes long
- `<signature>` - message signature, 65 bytes long
- `<message>` - message, 32 bytes long
```
ethkey verify 689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124 c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200 bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987
ethkey verify public 689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124 c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200 bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987
```
```
true
```
--
#### `verify address <address> <signature> <message>`
*Verify the signature.*
- `<address>` - ethereum address, 20 bytes long
- `<signature>` - message signature, 65 bytes long
- `<message>` - message, 32 bytes long
```
ethkey verify address 689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124 c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200 bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987
```
```

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (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/>.
extern crate docopt;
extern crate rustc_serialize;
extern crate ethkey;
@ -7,7 +23,7 @@ use std::{env, fmt, process};
use std::num::ParseIntError;
use docopt::Docopt;
use rustc_serialize::hex::{FromHex, FromHexError};
use ethkey::{KeyPair, Random, Brain, Prefix, Error as EthkeyError, Generator, Secret, Message, Public, Signature, sign, verify};
use ethkey::{KeyPair, Random, Brain, Prefix, Error as EthkeyError, Generator, Secret, Message, Public, Signature, Address, sign, verify_public, verify_address};
pub const USAGE: &'static str = r#"
Ethereum keys generator.
@ -19,7 +35,8 @@ Usage:
ethkey generate prefix <prefix> <iterations> [options]
ethkey generate brain <seed> [options]
ethkey sign <secret> <message>
ethkey verify <public> <signature> <message>
ethkey verify public <public> <signature> <message>
ethkey verify address <address> <signature> <message>
ethkey [-h | --help]
Options:
@ -47,12 +64,15 @@ struct Args {
cmd_brain: bool,
cmd_sign: bool,
cmd_verify: bool,
cmd_public: bool,
cmd_address: bool,
arg_prefix: String,
arg_iterations: String,
arg_seed: String,
arg_secret: String,
arg_message: String,
arg_public: String,
arg_address: String,
arg_signature: String,
flag_secret: bool,
flag_public: bool,
@ -164,10 +184,17 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
let signature = try!(sign(&secret, &message));
Ok(format!("{}", signature))
} else if args.cmd_verify {
let public = try!(Public::from_str(&args.arg_public));
let signature = try!(Signature::from_str(&args.arg_signature));
let message = try!(Message::from_str(&args.arg_message));
let ok = try!(verify(&public, &signature, &message));
let ok = if args.cmd_public {
let public = try!(Public::from_str(&args.arg_public));
try!(verify_public(&public, &signature, &message))
} else if args.cmd_address {
let address = try!(Address::from_str(&args.arg_address));
try!(verify_address(&address, &signature, &message))
} else {
unreachable!();
};
Ok(format!("{}", ok))
} else {
unreachable!();
@ -251,8 +278,19 @@ address: 26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5".to_owned();
}
#[test]
fn verify_valid() {
let command = vec!["ethkey", "verify", "689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987"]
fn verify_valid_public() {
let command = vec!["ethkey", "verify", "public", "689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
let expected = "true".to_owned();
assert_eq!(execute(command).unwrap(), expected);
}
#[test]
fn verify_valid_address() {
let command = vec!["ethkey", "verify", "address", "26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
@ -263,7 +301,7 @@ address: 26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5".to_owned();
#[test]
fn verify_invalid() {
let command = vec!["ethkey", "verify", "689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec986"]
let command = vec!["ethkey", "verify", "public", "689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec986"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (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/>.
#[cfg(feature = "cli")]
include!("ethkey.rs");

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use keccak::Keccak256;
use super::{KeyPair, Error, Generator, Secret};

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::fmt;
#[derive(Debug)]

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use tiny_keccak::Keccak;
pub trait Keccak256<T> {

View File

@ -1,9 +1,32 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::fmt;
use secp256k1::key;
use rustc_serialize::hex::ToHex;
use keccak::Keccak256;
use super::{Secret, Public, Address, SECP256K1, Error};
pub fn public_to_address(public: &Public) -> Address {
let hash = public.keccak256();
let mut result = Address::default();
result.copy_from_slice(&hash[12..]);
result
}
/// secp256k1 key pair
pub struct KeyPair {
secret: Secret,
@ -60,10 +83,7 @@ impl KeyPair {
}
pub fn address(&self) -> Address {
let hash = self.public.keccak256();
let mut result = Address::default();
result.copy_from_slice(&hash[12..]);
result
public_to_address(&self.public)
}
}

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (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/>.
extern crate rand;
#[macro_use]
extern crate lazy_static;
@ -26,8 +42,8 @@ pub trait Generator {
pub use self::brain::Brain;
pub use self::error::Error;
pub use self::keypair::KeyPair;
pub use self::keypair::{KeyPair, public_to_address};
pub use self::primitive::{Secret, Public, Address, Message};
pub use self::prefix::Prefix;
pub use self::random::Random;
pub use self::signature::{sign, verify, Signature};
pub use self::signature::{sign, verify_public, verify_address, recover, Signature};

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use super::{Random, Generator, KeyPair, Error};
/// Tries to find keypair with address starting with given prefix.

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::ops::{Deref, DerefMut};
use std::{fmt, cmp, hash};
use std::str::FromStr;

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use rand::os::OsRng;
use super::{Generator, KeyPair, Error, SECP256K1};

View File

@ -1,10 +1,26 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::ops::{Deref, DerefMut};
use std::{mem, fmt};
use std::str::FromStr;
use secp256k1::{Message as SecpMessage, RecoverableSignature, RecoveryId, Error as SecpError};
use secp256k1::key::{SecretKey, PublicKey};
use rustc_serialize::hex::{ToHex, FromHex};
use {Secret, Public, SECP256K1, Error, Message};
use {Secret, Public, SECP256K1, Error, Message, public_to_address, Address};
#[repr(C)]
#[derive(Eq)]
@ -113,7 +129,7 @@ pub fn sign(secret: &Secret, message: &Message) -> Result<Signature, Error> {
Ok(Signature(data_arr))
}
pub fn verify(public: &Public, signature: &Signature, message: &Message) -> Result<bool, Error> {
pub fn verify_public(public: &Public, signature: &Signature, message: &Message) -> Result<bool, Error> {
let context = &SECP256K1;
let rsig = try!(RecoverableSignature::from_compact(context, &signature[0..64], try!(RecoveryId::from_i32(signature[64] as i32))));
let sig = rsig.to_standard(context);
@ -132,11 +148,28 @@ pub fn verify(public: &Public, signature: &Signature, message: &Message) -> Resu
}
}
pub fn verify_address(address: &Address, signature: &Signature, message: &Message) -> Result<bool, Error> {
let public = try!(recover(signature, message));
let recovered_address = public_to_address(&public);
Ok(address == &recovered_address)
}
pub fn recover(signature: &Signature, message: &Message) -> Result<Public, Error> {
let context = &SECP256K1;
let rsig = try!(RecoverableSignature::from_compact(context, &signature[0..64], try!(RecoveryId::from_i32(signature[64] as i32))));
let pubkey = try!(context.recover(&try!(SecpMessage::from_slice(&message[..])), &rsig));
let serialized = pubkey.serialize_vec(context, false);
let mut public = Public::default();
public.copy_from_slice(&serialized[1..65]);
Ok(public)
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use {Generator, Random, Message};
use super::{sign, verify, Signature};
use super::{sign, verify_public, verify_address, recover, Signature};
#[test]
fn signature_to_and_from_str() {
@ -149,10 +182,26 @@ mod tests {
}
#[test]
fn sign_and_verify() {
fn sign_and_recover_public() {
let keypair = Random.generate().unwrap();
let message = Message::default();
let signature = sign(keypair.secret(), &message).unwrap();
assert!(verify(keypair.public(), &signature, &message).unwrap());
assert_eq!(keypair.public(), &recover(&signature, &message).unwrap());
}
#[test]
fn sign_and_verify_public() {
let keypair = Random.generate().unwrap();
let message = Message::default();
let signature = sign(keypair.secret(), &message).unwrap();
assert!(verify_public(keypair.public(), &signature, &message).unwrap());
}
#[test]
fn sign_and_verify_address() {
let keypair = Random.generate().unwrap();
let message = Message::default();
let signature = sign(keypair.secret(), &message).unwrap();
assert!(verify_address(&keypair.address(), &signature, &message).unwrap());
}
}

View File

@ -21,7 +21,7 @@ serde_codegen = { version = "0.7", optional = true }
syntex = "0.33.0"
[features]
default = ["cli", "serde_codegen"]
default = ["serde_codegen"]
nightly = ["serde_macros"]
cli = ["docopt"]

View File

@ -20,6 +20,7 @@ Usage:
ethstore change-pwd <address> <old-pwd> <new-pwd> [--dir DIR]
ethstore list [--dir DIR]
ethstore import [--src DIR] [--dir DIR]
ethstore import-wallet <path> <password> [--dir DIR]
ethstore remove <address> <password> [--dir DIR]
ethstore sign <address> <password> <message> [--dir DIR]
ethstore [-h | --help]
@ -38,6 +39,7 @@ Commands:
change-pwd Change account password.
list List accounts.
import Import accounts from src.
import-wallet Import presale wallet.
remove Remove account.
sign Sign message.
```
@ -48,11 +50,11 @@ Commands:
*Encrypt secret with a password and save it in secret store.*
- `<secret>` - ethereum secret, 32 bytes long
- `<password>` - account password, any string
- `<password>` - account password, file path
- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity
```
ethstore insert 7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5 "this is sparta"
ethstore insert 7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5 password.txt
```
```
@ -75,12 +77,12 @@ ethstore insert `ethkey generate random -s` "this is sparta"
*Change account password.*
- `<address>` - ethereum address, 20 bytes long
- `<old-pwd>` - old account password, any string
- `<new-pwd>` - new account password, any string
- `<old-pwd>` - old account password, file path
- `<new-pwd>` - new account password, file path
- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity
```
ethstore change-pwd a8fa5dd30a87bb9e3288d604eb74949c515ab66e "this is sparta" "hello world"
ethstore change-pwd a8fa5dd30a87bb9e3288d604eb74949c515ab66e old_pwd.txt new_pwd.txt
```
```
@ -112,6 +114,10 @@ ethstore list
- `[--src DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: geth
- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity
```
ethstore import
```
```
0: e6a3d25a7cb7cd21cb720df5b5e8afd154af1bbb
1: 6edddfc6349aff20bc6467ccf276c5b52487f7a8
@ -119,15 +125,32 @@ ethstore list
--
#### `import-wallet <path> <password> [--dir DIR]`
*Import account from presale wallet.*
- `<path>` - presale wallet path
- `<password>` - account password, file path
- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity
```
ethstore import-wallet ethwallet.json password.txt
```
```
e6a3d25a7cb7cd21cb720df5b5e8afd154af1bbb
```
--
#### `remove <address> <password> [--dir DIR]`
*Remove account from secret store.*
- `<address>` - ethereum address, 20 bytes long
- `<password>` - account password, any string
- `<password>` - account password, file path
- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity
```
ethstore remove a8fa5dd30a87bb9e3288d604eb74949c515ab66e "hello world"
ethstore remove a8fa5dd30a87bb9e3288d604eb74949c515ab66e password.txt
```
```
@ -140,12 +163,12 @@ true
*Sign message with account's secret.*
- `<address>` - ethereum address, 20 bytes long
- `<password>` - account password, any string
- `<password>` - account password, file path
- `<message>` - message to sign, 32 bytes long
- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity
```
ethstore sign 24edfff680d536a5f6fe862d36df6f8f6f40f115 "this is sparta" 7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5
ethstore sign 24edfff680d536a5f6fe862d36df6f8f6f40f115 password.txt 7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5
```
```

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (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/>.
#[cfg(not(feature = "serde_macros"))]
mod inner {
extern crate syntex;

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use json;
#[derive(Debug, PartialEq, Clone)]

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use json;
#[derive(Debug, PartialEq, Clone)]

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (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/>.
mod cipher;
mod kdf;
mod safe_account;

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::ops::{Deref, DerefMut};
use ethkey::{KeyPair, sign, Address, Secret, Signature, Message};
use {json, Error, crypto};
@ -154,7 +170,7 @@ impl SafeAccount {
#[cfg(test)]
mod tests {
use ethkey::{Generator, Random, verify, Message};
use ethkey::{Generator, Random, verify_public, Message};
use super::{Crypto, SafeAccount};
#[test]
@ -174,13 +190,13 @@ mod tests {
}
#[test]
fn sign_and_verify() {
fn sign_and_verify_public() {
let keypair = Random.generate().unwrap();
let password = "hello world";
let message = Message::default();
let account = SafeAccount::create(&keypair, [0u8; 16], password, 10240);
let signature = account.sign(password, &message).unwrap();
assert!(verify(keypair.public(), &signature, &message).unwrap());
assert!(verify_public(keypair.public(), &signature, &message).unwrap());
}
#[test]

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use json;
#[derive(Debug, PartialEq, Clone)]

View File

@ -1,14 +1,31 @@
// Copyright 2015, 2016 Ethcore (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/>.
extern crate rustc_serialize;
extern crate docopt;
extern crate ethstore;
use std::{env, process};
use std::{env, process, fs};
use std::io::Read;
use std::ops::Deref;
use std::str::FromStr;
use docopt::Docopt;
use ethstore::ethkey::{Secret, Address, Message};
use ethstore::dir::{KeyDirectory, ParityDirectory, DiskDirectory, GethDirectory, DirectoryType};
use ethstore::{EthStore, SecretStore, import_accounts, Error};
use ethstore::{EthStore, SecretStore, import_accounts, Error, PresaleWallet};
pub const USAGE: &'static str = r#"
Ethereum key management.
@ -19,6 +36,7 @@ Usage:
ethstore change-pwd <address> <old-pwd> <new-pwd> [--dir DIR]
ethstore list [--dir DIR]
ethstore import [--src DIR] [--dir DIR]
ethstore import-wallet <path> <password> [--dir DIR]
ethstore remove <address> <password> [--dir DIR]
ethstore sign <address> <password> <message> [--dir DIR]
ethstore [-h | --help]
@ -37,6 +55,7 @@ Commands:
change-pwd Change password.
list List accounts.
import Import accounts from src.
import-wallet Import presale wallet.
remove Remove account.
sign Sign message.
"#;
@ -47,6 +66,7 @@ struct Args {
cmd_change_pwd: bool,
cmd_list: bool,
cmd_import: bool,
cmd_import_wallet: bool,
cmd_remove: bool,
cmd_sign: bool,
arg_secret: String,
@ -55,6 +75,7 @@ struct Args {
arg_new_pwd: String,
arg_address: String,
arg_message: String,
arg_path: String,
flag_src: String,
flag_dir: String,
}
@ -89,6 +110,15 @@ fn format_accounts(accounts: &[Address]) -> String {
.join("\n")
}
fn load_password(path: &str) -> Result<String, Error> {
let mut file = try!(fs::File::open(path));
let mut password = String::new();
try!(file.read_to_string(&mut password));
// drop EOF
let _ = password.pop();
Ok(password)
}
fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item=S>, S: AsRef<str> {
let args: Args = Docopt::new(USAGE)
.and_then(|d| d.argv(command).decode())
@ -98,11 +128,14 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
return if args.cmd_insert {
let secret = try!(Secret::from_str(&args.arg_secret));
let address = try!(store.insert_account(secret, &args.arg_password));
let password = try!(load_password(&args.arg_password));
let address = try!(store.insert_account(secret, &password));
Ok(format!("{}", address))
} else if args.cmd_change_pwd {
let address = try!(Address::from_str(&args.arg_address));
let ok = store.change_password(&address, &args.arg_old_pwd, &args.arg_new_pwd).is_ok();
let old_pwd = try!(load_password(&args.arg_old_pwd));
let new_pwd = try!(load_password(&args.arg_new_pwd));
let ok = store.change_password(&address, &old_pwd, &new_pwd).is_ok();
Ok(format!("{}", ok))
} else if args.cmd_list {
let accounts = store.accounts();
@ -112,14 +145,22 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
let dst = try!(key_dir(&args.flag_dir));
let accounts = try!(import_accounts(src.deref(), dst.deref()));
Ok(format_accounts(&accounts))
} else if args.cmd_import_wallet {
let wallet = try!(PresaleWallet::open(&args.arg_path));
let password = try!(load_password(&args.arg_password));
let kp = try!(wallet.decrypt(&password));
let address = try!(store.insert_account(kp.secret().clone(), &password));
Ok(format!("{}", address))
} else if args.cmd_remove {
let address = try!(Address::from_str(&args.arg_address));
let ok = store.remove_account(&address, &args.arg_password).is_ok();
let password = try!(load_password(&args.arg_password));
let ok = store.remove_account(&address, &password).is_ok();
Ok(format!("{}", ok))
} else if args.cmd_sign {
let address = try!(Address::from_str(&args.arg_address));
let message = try!(Message::from_str(&args.arg_message));
let signature = try!(store.sign(&address, &args.arg_password, &message));
let password = try!(load_password(&args.arg_password));
let signature = try!(store.sign(&address, &password, &message));
Ok(format!("{}", signature))
} else {
unreachable!();

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (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/>.
#[cfg(feature = "cli")]
include!("ethstore.rs");

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use tiny_keccak::Keccak;
use rcrypto::pbkdf2::pbkdf2;
use rcrypto::scrypt::{scrypt, ScryptParams};
@ -49,9 +65,9 @@ impl Keccak256<[u8; 32]> for [u8] {
/// AES encryption
pub mod aes {
use rcrypto::blockmodes::CtrMode;
use rcrypto::aessafe::AesSafe128Encryptor;
use rcrypto::symmetriccipher::{Encryptor, Decryptor};
use rcrypto::blockmodes::{CtrMode, CbcDecryptor, PkcsPadding};
use rcrypto::aessafe::{AesSafe128Encryptor, AesSafe128Decryptor};
use rcrypto::symmetriccipher::{Encryptor, Decryptor, SymmetricCipherError};
use rcrypto::buffer::{RefReadBuffer, RefWriteBuffer};
/// Encrypt a message
@ -65,5 +81,13 @@ pub mod aes {
let mut encryptor = CtrMode::new(AesSafe128Encryptor::new(k), iv.to_vec());
encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut RefWriteBuffer::new(dest), true).expect("Invalid length or padding");
}
/// Decrypt a message using cbc mode
pub fn decrypt_cbc(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) -> Result<(), SymmetricCipherError> {
let mut encryptor = CbcDecryptor::new(AesSafe128Decryptor::new(k), PkcsPadding, iv.to_vec());
try!(encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut RefWriteBuffer::new(dest), true));
Ok(())
}
}

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::{fs, ffi, io};
use std::path::{PathBuf, Path};
use std::collections::HashMap;

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::env;
use std::path::PathBuf;
use ethkey::Address;

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use ethkey::Address;
use {SafeAccount, Error};

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::env;
use std::path::PathBuf;
use ethkey::Address;

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::fmt;
use std::io::Error as IoError;
use ethkey::Error as EthKeyError;

View File

@ -1,5 +1,21 @@
// Copyright 2015, 2016 Ethcore (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/>.
//! ethkey reexport to make documentation look pretty.
pub use _ethkey::{Address, Message, Signature, Public, Secret, Generator, sign, verify, Error, KeyPair, Random, Prefix};
pub use _ethkey::*;
use json;
impl Into<json::H160> for Address {
@ -15,3 +31,11 @@ impl From<json::H160> for Address {
From::from(a)
}
}
impl<'a> From<&'a json::H160> for Address {
fn from(json: &'a json::H160) -> Self {
let mut a = [0u8; 20];
a.copy_from_slice(json);
From::from(a)
}
}

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::collections::BTreeMap;
use std::sync::RwLock;
use ethkey::KeyPair;

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use ethkey::Address;
use dir::KeyDirectory;
use Error;

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use serde::{Serialize, Serializer, Deserialize, Deserializer, Error as SerdeError};
use serde::de::Visitor;
use super::{Error, H128};

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use serde::{Deserialize, Deserializer, Serialize, Serializer, Error};
use serde::de::{Visitor, MapVisitor};
use serde::ser;

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::fmt;
#[derive(Debug, PartialEq)]

View File

@ -1,3 +1,21 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::fmt;
use std::ops;
use std::str::FromStr;
use rustc_serialize::hex::{FromHex, ToHex};
use serde::{Serialize, Serializer, Deserialize, Deserializer, Error as SerdeError};
@ -6,9 +24,31 @@ use super::Error;
macro_rules! impl_hash {
($name: ident, $size: expr) => {
#[derive(Debug, PartialEq)]
pub struct $name([u8; $size]);
impl fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let self_ref: &[u8] = &self.0;
write!(f, "{:?}", self_ref)
}
}
impl PartialEq for $name {
fn eq(&self, other: &Self) -> bool {
let self_ref: &[u8] = &self.0;
let other_ref: &[u8] = &other.0;
self_ref == other_ref
}
}
impl ops::Deref for $name {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Serialize for $name {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: Serializer {
@ -69,3 +109,4 @@ macro_rules! impl_hash {
impl_hash!(H128, 16);
impl_hash!(H160, 20);
impl_hash!(H256, 32);
impl_hash!(H768, 96);

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (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/>.
//! Universaly unique identifier.
use std::str::FromStr;
use std::fmt;

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use serde::{Serialize, Serializer, Deserialize, Deserializer, Error as SerdeError};
use serde::de::Visitor;
use serde_json::{Value, value};

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::io::{Read, Write};
use serde::{Deserialize, Deserializer, Error};
use serde::de::{Visitor, MapVisitor};

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (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/>.
//! Contract interface specification.
#[cfg(feature = "serde_macros")]

View File

@ -5,14 +5,16 @@ mod hash;
mod id;
mod kdf;
mod key_file;
mod presale;
mod version;
pub use self::cipher::{Cipher, CipherSer, CipherSerParams, Aes128Ctr};
pub use self::crypto::Crypto;
pub use self::error::Error;
pub use self::hash::{H128, H160, H256};
pub use self::hash::{H128, H160, H256, H768};
pub use self::id::UUID;
pub use self::kdf::{Kdf, KdfSer, Prf, Pbkdf2, Scrypt, KdfSerParams};
pub use self::key_file::KeyFile;
pub use self::presale::PresaleWallet;
pub use self::version::Version;

View File

@ -0,0 +1,42 @@
use std::io::Read;
use serde_json;
use super::{H160, H768};
#[derive(Debug, PartialEq, Deserialize)]
pub struct PresaleWallet {
pub encseed: H768,
#[serde(rename = "ethaddr")]
pub address: H160,
}
impl PresaleWallet {
pub fn load<R>(reader: R) -> Result<Self, serde_json::Error> where R: Read {
serde_json::from_reader(reader)
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use serde_json;
use json::{PresaleWallet, H160, H768};
#[test]
fn presale_wallet() {
let json = r#"
{
"encseed": "137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066",
"ethaddr": "ede84640d1a1d3e06902048e67aa7db8d52c2ce1",
"email": "123@gmail.com",
"btcaddr": "1JvqEc6WLhg6GnyrLBe2ztPAU28KRfuseH"
} "#;
let expected = PresaleWallet {
encseed: H768::from_str("137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066").unwrap(),
address: H160::from_str("ede84640d1a1d3e06902048e67aa7db8d52c2ce1").unwrap(),
};
let wallet: PresaleWallet = serde_json::from_str(json).unwrap();
assert_eq!(expected, wallet);
}
}

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use serde::{Serialize, Serializer, Deserialize, Deserializer, Error as SerdeError};
use serde::de::Visitor;
use super::Error;

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (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/>.
#![cfg_attr(feature="nightly", feature(custom_derive, plugin))]
#![cfg_attr(feature="nightly", plugin(serde_macros))]
@ -21,6 +37,7 @@ mod crypto;
mod error;
mod ethstore;
mod import;
mod presale;
mod random;
mod secret_store;
@ -28,5 +45,6 @@ pub use self::account::SafeAccount;
pub use self::error::Error;
pub use self::ethstore::EthStore;
pub use self::import::import_accounts;
pub use self::presale::PresaleWallet;
pub use self::secret_store::SecretStore;

79
ethstore/src/presale.rs Normal file
View File

@ -0,0 +1,79 @@
use std::fs;
use std::path::Path;
use rcrypto::pbkdf2::pbkdf2;
use rcrypto::sha2::Sha256;
use rcrypto::hmac::Hmac;
use json;
use ethkey::{Address, Secret, KeyPair};
use crypto::Keccak256;
use {crypto, Error};
pub struct PresaleWallet {
iv: [u8; 16],
ciphertext: [u8; 80],
address: Address,
}
impl From<json::PresaleWallet> for PresaleWallet {
fn from(wallet: json::PresaleWallet) -> Self {
let mut iv = [0u8; 16];
iv.copy_from_slice(&wallet.encseed[..16]);
let mut ciphertext = [0u8; 80];
ciphertext.copy_from_slice(&wallet.encseed[16..]);
PresaleWallet {
iv: iv,
ciphertext: ciphertext,
address: Address::from(wallet.address),
}
}
}
impl PresaleWallet {
pub fn open<P>(path: P) -> Result<Self, Error> where P: AsRef<Path> {
let file = try!(fs::File::open(path));
let presale = json::PresaleWallet::load(file).unwrap();
Ok(PresaleWallet::from(presale))
}
pub fn decrypt(&self, password: &str) -> Result<KeyPair, Error> {
let mut h_mac = Hmac::new(Sha256::new(), password.as_bytes());
let mut derived_key = vec![0u8; 16];
pbkdf2(&mut h_mac, password.as_bytes(), 2000, &mut derived_key);
let mut key = [0u8; 64];
try!(crypto::aes::decrypt_cbc(&derived_key, &self.iv, &self.ciphertext, &mut key).map_err(|_| Error::InvalidPassword));
let secret = Secret::from(key.keccak256());
if let Ok(kp) = KeyPair::from_secret(secret) {
if kp.address() == self.address {
return Ok(kp)
}
}
Err(Error::InvalidPassword)
}
}
#[cfg(test)]
mod tests {
use super::PresaleWallet;
use json;
#[test]
fn test() {
let json = r#"
{
"encseed": "137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066",
"ethaddr": "ede84640d1a1d3e06902048e67aa7db8d52c2ce1",
"email": "123@gmail.com",
"btcaddr": "1JvqEc6WLhg6GnyrLBe2ztPAU28KRfuseH"
} "#;
let wallet = json::PresaleWallet::load(json.as_bytes()).unwrap();
let wallet = PresaleWallet::from(wallet);
assert!(wallet.decrypt("123").is_ok());
assert!(wallet.decrypt("124").is_err());
}
}

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use rand::{Rng, OsRng};
pub trait Random {

View File

@ -1,3 +1,19 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use ethkey::{Address, Message, Signature, Secret};
use Error;

View File

@ -1,10 +1,28 @@
// Copyright 2015, 2016 Ethcore (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/>.
extern crate rand;
extern crate ethstore;
mod util;
use std::str::FromStr;
use ethstore::{SecretStore, EthStore};
use ethstore::ethkey::{Random, Generator, Secret};
use ethstore::ethkey::{Random, Generator, Secret, Address};
use ethstore::dir::DiskDirectory;
use util::TransientDir;
#[test]
@ -70,3 +88,40 @@ fn secret_store_remove_account() {
assert_eq!(store.accounts().len(), 0);
assert!(store.remove_account(&accounts[0], "").is_err());
}
fn test_path() -> &'static str {
match ::std::fs::metadata("ethstore") {
Ok(_) => "ethstore/tests/res/geth_keystore",
Err(_) => "tests/res/geth_keystore",
}
}
fn pat_path() -> &'static str {
match ::std::fs::metadata("ethstore") {
Ok(_) => "ethstore/tests/res/pat",
Err(_) => "tests/res/pat",
}
}
#[test]
fn secret_store_laod_geth_files() {
let dir = DiskDirectory::at(test_path());
let store = EthStore::open(Box::new(dir)).unwrap();
assert_eq!(store.accounts(), vec![
Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap(),
Address::from_str("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf").unwrap(),
Address::from_str("63121b431a52f8043c16fcf0d1df9cb7b5f66649").unwrap(),
]);
}
#[test]
fn secret_store_load_pat_files() {
let dir = DiskDirectory::at(pat_path());
let store = EthStore::open(Box::new(dir)).unwrap();
assert_eq!(store.accounts(), vec![
Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap(),
Address::from_str("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf").unwrap(),
]);
}

View File

@ -0,0 +1,16 @@
// Copyright 2015, 2016 Ethcore (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/>.

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