Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c6e3f314a | ||
|
|
96cbfba916 | ||
|
|
18b8d6aca6 | ||
|
|
6a44a0cf95 | ||
|
|
b7860e4a3f | ||
|
|
4a910a762d | ||
|
|
1626c78ae2 | ||
|
|
7f4e700013 | ||
|
|
987390fb7d | ||
|
|
797f23f30b | ||
|
|
c1da49bbc4 | ||
|
|
1164193019 | ||
|
|
1c217cbf2b | ||
|
|
d24f71f150 | ||
|
|
4c66a021ec | ||
|
|
ccc57328e2 | ||
|
|
77240b6e5a | ||
|
|
2e428ddd58 | ||
|
|
cd4081c149 | ||
|
|
428342d69d | ||
|
|
8ee9b262f9 | ||
|
|
e2bc251ff4 | ||
|
|
d15372c8a6 | ||
|
|
bcf2245110 | ||
|
|
36f74fdf0d | ||
|
|
45402544d3 | ||
|
|
ab236690df | ||
|
|
34e81101d7 | ||
|
|
07d4b9bbc9 | ||
|
|
d25a20b274 | ||
|
|
70d17ce1d8 | ||
|
|
3b14bcf29a | ||
|
|
4fc69e11d6 | ||
|
|
8e4df824b0 | ||
|
|
e1f2e840cb | ||
|
|
2a36a89c36 | ||
|
|
b643f4011d | ||
|
|
814304bdd7 | ||
|
|
f90607302d | ||
|
|
af826f877e | ||
|
|
f9a0aa0022 | ||
|
|
c4196a5de3 | ||
|
|
3b56e8eded | ||
|
|
b3ccbbe913 | ||
|
|
fe0f037f23 |
@@ -1,8 +1,8 @@
|
||||
stages:
|
||||
- test
|
||||
- js-build
|
||||
- build
|
||||
- push-release
|
||||
- build
|
||||
variables:
|
||||
GIT_DEPTH: "3"
|
||||
SIMPLECOV: "true"
|
||||
@@ -26,14 +26,14 @@ linux-stable:
|
||||
- cargo build -j $(nproc) --release -p ethstore
|
||||
- cargo build -j $(nproc) --release -p ethkey
|
||||
- strip target/release/parity
|
||||
- strip target/release/evm
|
||||
- strip target/release/parity-evm
|
||||
- strip target/release/ethstore
|
||||
- strip target/release/ethkey
|
||||
- export SHA3=$(target/release/parity tools hash target/release/parity)
|
||||
- md5sum target/release/parity > parity.md5
|
||||
- sh scripts/deb-build.sh amd64
|
||||
- cp target/release/parity deb/usr/bin/parity
|
||||
- cp target/release/evm deb/usr/bin/evm
|
||||
- cp target/release/parity-evm deb/usr/bin/parity-evm
|
||||
- cp target/release/ethstore deb/usr/bin/ethstore
|
||||
- cp target/release/ethkey deb/usr/bin/ethkey
|
||||
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||
@@ -55,9 +55,9 @@ linux-stable:
|
||||
artifacts:
|
||||
paths:
|
||||
- target/release/parity
|
||||
- target/release/parity/evmbin
|
||||
- target/release/parity/ethstore
|
||||
- target/release/parity/ethkey
|
||||
- target/release/parity-evm
|
||||
- target/release/ethstore
|
||||
- target/release/ethkey
|
||||
name: "stable-x86_64-unknown-linux-gnu_parity"
|
||||
linux-stable-debian:
|
||||
stage: build
|
||||
@@ -73,14 +73,14 @@ linux-stable-debian:
|
||||
- cargo build -j $(nproc) --release -p ethstore
|
||||
- cargo build -j $(nproc) --release -p ethkey
|
||||
- strip target/release/parity
|
||||
- strip target/release/evm
|
||||
- strip target/release/parity-evm
|
||||
- strip target/release/ethstore
|
||||
- strip target/release/ethkey
|
||||
- export SHA3=$(target/release/parity tools hash target/release/parity)
|
||||
- md5sum target/release/parity > parity.md5
|
||||
- sh scripts/deb-build.sh amd64
|
||||
- cp target/release/parity deb/usr/bin/parity
|
||||
- cp target/release/evm deb/usr/bin/evm
|
||||
- cp target/release/parity-evm deb/usr/bin/parity-evm
|
||||
- cp target/release/ethstore deb/usr/bin/ethstore
|
||||
- cp target/release/ethkey deb/usr/bin/ethkey
|
||||
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||
@@ -308,10 +308,10 @@ linux-armv6:
|
||||
stage: build
|
||||
image: ethcore/rust-armv6:latest
|
||||
only:
|
||||
- beta
|
||||
# - beta
|
||||
# - tags
|
||||
# - stable
|
||||
# - triggers
|
||||
- triggers
|
||||
script:
|
||||
- export CC=arm-linux-gnueabi-gcc
|
||||
- export CXX=arm-linux-gnueabi-g++
|
||||
@@ -409,16 +409,16 @@ darwin:
|
||||
packagesbuild -v mac/Parity.pkgproj
|
||||
productsign --sign 'Developer ID Installer: PARITY TECHNOLOGIES LIMITED (P2PX3JU8FT)' target/release/Parity\ Ethereum.pkg target/release/Parity\ Ethereum-signed.pkg
|
||||
export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||
mv target/release/Parity\ Ethereum-signed.pkg "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg"
|
||||
md5sum "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg" >> "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg.md5"
|
||||
mv target/release/Parity\ Ethereum-signed.pkg "parity-"$VER"-osx-installer.pkg"
|
||||
md5sum "parity-"$VER"-osx-installer.pkg" >> "parity-"$VER"-osx-installer.pkg.md5"
|
||||
aws configure set aws_access_key_id $s3_key
|
||||
aws configure set aws_secret_access_key $s3_secret
|
||||
if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||
aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity --body target/release/parity
|
||||
aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
|
||||
aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity-"$VER"-osx-installer-EXPERIMENTAL.pkg" --body "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg"
|
||||
aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity-"$VER"-osx-installer-EXPERIMENTAL.pkg.md5" --body "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg.md5"
|
||||
aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity-"$VER"-osx-installer.pkg" --body "parity-"$VER"-osx-installer.pkg"
|
||||
aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity-"$VER"-osx-installer.pkg.md5" --body "parity-"$VER"-osx-installer.pkg.md5"
|
||||
curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
tags:
|
||||
@@ -460,9 +460,8 @@ windows:
|
||||
- zip win-installer.zip InstallParity.exe InstallParity.exe.md5
|
||||
- md5sums win-installer.zip > win-installer.zip.md5
|
||||
- cd ..\target\release\
|
||||
- md5sums parity.exe parity.pdb > parity.md5
|
||||
- md5sums parity.exe > parity.exe.md5
|
||||
- zip parity.zip parity.exe parity.pdb parity.md5
|
||||
- zip parity.zip parity.exe parity.md5
|
||||
- md5sums parity.zip > parity.zip.md5
|
||||
- cd ..\..
|
||||
- aws configure set aws_access_key_id %s3_key%
|
||||
@@ -488,7 +487,6 @@ windows:
|
||||
artifacts:
|
||||
paths:
|
||||
- target/release/parity.exe
|
||||
- target/release/parity.pdb
|
||||
- nsis/InstallParity.exe
|
||||
name: "x86_64-pc-windows-msvc_parity"
|
||||
docker-build:
|
||||
|
||||
322
Cargo.lock
generated
322
Cargo.lock
generated
@@ -1,6 +1,6 @@
|
||||
[root]
|
||||
name = "parity"
|
||||
version = "1.6.0"
|
||||
version = "1.6.6"
|
||||
dependencies = [
|
||||
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -23,7 +23,7 @@ dependencies = [
|
||||
"ethcore-secretstore 1.0.0",
|
||||
"ethcore-signer 1.6.0",
|
||||
"ethcore-stratum 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"ethsync 1.6.0",
|
||||
"evmbin 0.1.0",
|
||||
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -101,10 +101,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aster"
|
||||
version = "0.17.0"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_syntax 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -204,7 +204,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"integer-encoding 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"multibase 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"multihash 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"multihash 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -253,7 +253,7 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"core-foundation-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -261,7 +261,7 @@ name = "core-foundation-sys"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -284,7 +284,7 @@ version = "1.1.1"
|
||||
source = "git+https://github.com/ethcore/rust-ctrlc.git#f4927770f89eca80ec250911eea3adcbf579ac48"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -293,7 +293,7 @@ name = "daemonize"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -348,7 +348,7 @@ source = "git+https://github.com/ethcore/rust-secp256k1#98ad9b9ecae44a563efdd642
|
||||
dependencies = [
|
||||
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (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)",
|
||||
]
|
||||
@@ -394,13 +394,14 @@ dependencies = [
|
||||
"ethcore-ipc-codegen 1.6.0",
|
||||
"ethcore-ipc-nano 1.6.0",
|
||||
"ethcore-stratum 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"ethjson 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"ethstore 0.1.0",
|
||||
"evmjit 1.6.0",
|
||||
"hardware-wallet 1.6.0",
|
||||
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
|
||||
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -422,7 +423,7 @@ version = "0.1.2"
|
||||
dependencies = [
|
||||
"bigint 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (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)",
|
||||
]
|
||||
@@ -443,7 +444,7 @@ dependencies = [
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-devtools 1.6.0",
|
||||
"ethcore-rpc 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"fetch 0.1.0",
|
||||
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
|
||||
@@ -453,7 +454,7 @@ dependencies = [
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-hash-fetch 1.6.0",
|
||||
"parity-reactor 0.1.0",
|
||||
"parity-ui 1.6.0",
|
||||
@@ -491,8 +492,8 @@ name = "ethcore-ipc"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"ethcore-devtools 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||
"ethcore-util 1.6.6",
|
||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git?branch=parity-1.7)",
|
||||
"semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -500,11 +501,11 @@ dependencies = [
|
||||
name = "ethcore-ipc-codegen"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quasi 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quasi_codegen 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_syntax 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -515,7 +516,7 @@ dependencies = [
|
||||
"ethcore-ipc-codegen 1.6.0",
|
||||
"ethcore-ipc-nano 1.6.0",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git?branch=parity-1.7)",
|
||||
"semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -527,7 +528,7 @@ dependencies = [
|
||||
"ethcore-ipc 1.6.0",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git?branch=parity-1.7)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -538,9 +539,9 @@ dependencies = [
|
||||
"ethcore-ipc 1.6.0",
|
||||
"ethcore-ipc-codegen 1.6.0",
|
||||
"ethcore-ipc-nano 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git?branch=parity-1.7)",
|
||||
"semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -553,13 +554,13 @@ dependencies = [
|
||||
"ethcore-ipc 1.6.0",
|
||||
"ethcore-ipc-codegen 1.6.0",
|
||||
"ethcore-network 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rlp 0.1.0",
|
||||
"smallvec 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"stats 0.1.0",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -569,7 +570,7 @@ name = "ethcore-logger"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -585,11 +586,11 @@ dependencies = [
|
||||
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-devtools 1.6.0",
|
||||
"ethcore-io 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.6.1 (git+https://github.com/ethcore/mio)",
|
||||
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -613,7 +614,7 @@ dependencies = [
|
||||
"ethcore-io 1.6.0",
|
||||
"ethcore-ipc 1.6.0",
|
||||
"ethcore-light 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethjson 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
@@ -648,7 +649,7 @@ dependencies = [
|
||||
"ethcore-ipc 1.6.0",
|
||||
"ethcore-ipc-codegen 1.6.0",
|
||||
"ethcore-ipc-nano 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -666,10 +667,10 @@ dependencies = [
|
||||
"ethcore-devtools 1.6.0",
|
||||
"ethcore-io 1.6.0",
|
||||
"ethcore-rpc 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-ui 1.6.0",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -685,7 +686,7 @@ dependencies = [
|
||||
"ethcore-ipc 1.6.0",
|
||||
"ethcore-ipc-codegen 1.6.0",
|
||||
"ethcore-ipc-nano 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||
"jsonrpc-macros 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||
@@ -699,7 +700,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-util"
|
||||
version = "1.6.0"
|
||||
version = "1.6.6"
|
||||
dependencies = [
|
||||
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -713,7 +714,7 @@ dependencies = [
|
||||
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lru-cache 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -748,7 +749,7 @@ dependencies = [
|
||||
name = "ethjson"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -776,12 +777,12 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-devtools 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -790,7 +791,7 @@ dependencies = [
|
||||
"serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -809,14 +810,15 @@ dependencies = [
|
||||
"ethcore-ipc-nano 1.6.0",
|
||||
"ethcore-light 1.6.0",
|
||||
"ethcore-network 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"ethkey 0.2.0",
|
||||
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rlp 0.1.0",
|
||||
"semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -826,7 +828,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -842,7 +844,7 @@ name = "fdlimit"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -862,7 +864,7 @@ name = "flate2"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"miniz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -937,7 +939,7 @@ version = "0.3.1"
|
||||
source = "git+https://github.com/ethcore/hidapi-rs#9a127c1dca7e327e4fdd428406a76c9f5ef48563"
|
||||
dependencies = [
|
||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1054,7 +1056,7 @@ version = "1.6.0"
|
||||
dependencies = [
|
||||
"ethcore-ipc 1.6.0",
|
||||
"ethcore-ipc-codegen 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -1064,7 +1066,7 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -1173,7 +1175,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.16"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@@ -1182,7 +1184,7 @@ version = "0.3.0"
|
||||
source = "git+https://github.com/ethcore/libusb-rs#32bacf61abd981d5cbd4a8fecca5a2dc0b762a96"
|
||||
dependencies = [
|
||||
"bit-set 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libusb-sys 0.2.3 (git+https://github.com/ethcore/libusb-sys)",
|
||||
]
|
||||
|
||||
@@ -1192,7 +1194,7 @@ version = "0.2.3"
|
||||
source = "git+https://github.com/ethcore/libusb-sys#c10b1180646c9dc3f23a9b6bb825abcd3b7487ce"
|
||||
dependencies = [
|
||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1228,7 +1230,7 @@ name = "memchr"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1256,7 +1258,7 @@ version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1265,7 +1267,7 @@ version = "0.5.1"
|
||||
source = "git+https://github.com/ethcore/mio?branch=v0.5.x#3842d3b250ffd7bd9b16f9586b875ddcbac2b0dd"
|
||||
dependencies = [
|
||||
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1281,7 +1283,7 @@ version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1297,7 +1299,7 @@ version = "0.6.0-dev"
|
||||
source = "git+https://github.com/ethcore/mio?branch=timer-fix#31eccc40ece3d47abaefaf23bb2114033175b972"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1313,7 +1315,7 @@ source = "git+https://github.com/ethcore/mio#ef182bae193a9c7457cd2cf661fcaffb226
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1329,7 +1331,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1369,29 +1371,29 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "multihash"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ring 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nanomsg"
|
||||
version = "0.5.1"
|
||||
source = "git+https://github.com/ethcore/nanomsg.rs.git#c40fe442c9afaea5b38009a3d992ca044dcceb00"
|
||||
source = "git+https://github.com/ethcore/nanomsg.rs.git?branch=parity-1.7#673b79beef6e149273899850d7692335a481a920"
|
||||
dependencies = [
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nanomsg-sys 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nanomsg-sys 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git?branch=parity-1.7)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nanomsg-sys"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/ethcore/nanomsg.rs.git#c40fe442c9afaea5b38009a3d992ca044dcceb00"
|
||||
source = "git+https://github.com/ethcore/nanomsg.rs.git?branch=parity-1.7#673b79beef6e149273899850d7692335a481a920"
|
||||
dependencies = [
|
||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1413,7 +1415,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -1424,7 +1426,7 @@ version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1434,7 +1436,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1447,7 +1449,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1537,7 +1539,7 @@ name = "num_cpus"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1545,7 +1547,7 @@ name = "num_cpus"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1577,7 +1579,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl-sys 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -1587,7 +1589,7 @@ version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"user32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -1604,16 +1606,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "parity-dapps-glue"
|
||||
version = "1.4.0"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quasi 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quasi_codegen 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_syntax 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1621,7 +1623,7 @@ name = "parity-hash-fetch"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"ethabi 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"fetch 0.1.0",
|
||||
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1638,11 +1640,11 @@ version = "1.6.0"
|
||||
dependencies = [
|
||||
"cid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
|
||||
"jsonrpc-http-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"multihash 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"multihash 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rlp 0.1.0",
|
||||
]
|
||||
|
||||
@@ -1652,7 +1654,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"ethcore 1.6.0",
|
||||
"ethcore-io 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"ethkey 0.2.0",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rlp 0.1.0",
|
||||
@@ -1675,7 +1677,7 @@ version = "1.4.0"
|
||||
dependencies = [
|
||||
"ethcore-rpc 1.6.0",
|
||||
"ethcore-signer 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1691,24 +1693,24 @@ dependencies = [
|
||||
name = "parity-ui"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"parity-ui-dev 1.4.0",
|
||||
"parity-ui-precompiled 1.4.0 (git+https://github.com/ethcore/js-precompiled.git)",
|
||||
"parity-ui-dev 1.7.0",
|
||||
"parity-ui-precompiled 1.4.0 (git+https://github.com/ethcore/js-precompiled.git?branch=beta)",
|
||||
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parity-ui-dev"
|
||||
version = "1.4.0"
|
||||
version = "1.7.0"
|
||||
dependencies = [
|
||||
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parity-ui-precompiled"
|
||||
version = "1.4.0"
|
||||
source = "git+https://github.com/ethcore/js-precompiled.git#94da980fb81d6145e38ca87d37a9137e8440086a"
|
||||
source = "git+https://github.com/ethcore/js-precompiled.git?branch=beta#ff37c140c4c73f96c77a77631fca1cbe2bb15af7"
|
||||
dependencies = [
|
||||
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1719,7 +1721,7 @@ dependencies = [
|
||||
"ethcore 1.6.0",
|
||||
"ethcore-ipc 1.6.0",
|
||||
"ethcore-ipc-codegen 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"ethsync 1.6.0",
|
||||
"ipc-common-types 1.6.0",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1743,7 +1745,7 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1836,20 +1838,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quasi"
|
||||
version = "0.11.0"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_errors 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_syntax 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quasi_codegen"
|
||||
version = "0.11.0"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_errors 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_syntax 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1872,7 +1876,7 @@ name = "rand"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1918,10 +1922,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.6.2"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"untrusted 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -1940,7 +1945,7 @@ name = "rocksdb"
|
||||
version = "0.4.5"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#64c63ccbe1f62c2e2b39262486f9ba813793af58"
|
||||
dependencies = [
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)",
|
||||
]
|
||||
|
||||
@@ -1950,7 +1955,7 @@ version = "0.3.0"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#64c63ccbe1f62c2e2b39262486f9ba813793af58"
|
||||
dependencies = [
|
||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1971,7 +1976,7 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termios 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -1982,7 +1987,7 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termios 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -1993,7 +1998,7 @@ version = "1.4.0"
|
||||
dependencies = [
|
||||
"ethcore-bigint 0.1.2",
|
||||
"ethcore-rpc 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcore-util 1.6.6",
|
||||
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-rpc-client 1.4.0",
|
||||
"rpassword 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -2005,7 +2010,7 @@ version = "0.2.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (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)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -2059,7 +2064,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"core-foundation 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-foundation-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"security-framework-sys 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -2069,7 +2074,7 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"core-foundation-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2093,6 +2098,14 @@ dependencies = [
|
||||
"semver-parser 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.6.1"
|
||||
@@ -2102,6 +2115,11 @@ dependencies = [
|
||||
"regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "0.9.6"
|
||||
@@ -2205,8 +2223,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solicit"
|
||||
@@ -2250,23 +2271,44 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syntex"
|
||||
version = "0.33.0"
|
||||
version = "0.58.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_errors 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_syntax 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syntex_errors"
|
||||
version = "0.58.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_pos 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syntex_pos"
|
||||
version = "0.58.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syntex_syntax"
|
||||
version = "0.33.0"
|
||||
version = "0.58.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"term 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_errors 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_pos 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2293,7 +2335,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "term"
|
||||
version = "0.2.14"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -2305,7 +2347,7 @@ name = "termios"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2314,7 +2356,7 @@ version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2331,7 +2373,7 @@ version = "0.1.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -2431,11 +2473,6 @@ name = "unicode-normalization"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.0.4"
|
||||
@@ -2569,7 +2606,7 @@ dependencies = [
|
||||
"checksum antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5"
|
||||
"checksum app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7d1c0d48a81bbb13043847f957971f4d87c81542d80ece5e84ba3cba4058fd4"
|
||||
"checksum arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "16e3bdb2f54b3ace0285975d59a97cf8ed3855294b2b6bc651fcf22a9c352975"
|
||||
"checksum aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07d344974f0a155f091948aa389fb1b912d3a58414fbdb9c8d446d193ee3496a"
|
||||
"checksum aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfdf7355d9db158df68f976ed030ab0f6578af811f5a7bb6dcf221ec24e0e0"
|
||||
"checksum base-x 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f59103b47307f76e03bef1633aec7fa9e29bfb5aa6daf5a334f94233c71f6c1"
|
||||
"checksum base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b9605ba46d61df0410d8ac686b0007add8172eba90e8e909c347856fe794d8c"
|
||||
"checksum bigint 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2311bcd71b281e142a095311c22509f0d6bcd87b3000d7dbaa810929b9d6f6ae"
|
||||
@@ -2635,7 +2672,7 @@ dependencies = [
|
||||
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
|
||||
"checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f"
|
||||
"checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b"
|
||||
"checksum libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)" = "408014cace30ee0f767b1c4517980646a573ec61a57957aeeabcac8ac0a02e8d"
|
||||
"checksum libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "88ee81885f9f04bff991e306fea7c1c60a5f0f9e409e99f6b40e3311a3363135"
|
||||
"checksum libusb 0.3.0 (git+https://github.com/ethcore/libusb-rs)" = "<none>"
|
||||
"checksum libusb-sys 0.2.3 (git+https://github.com/ethcore/libusb-sys)" = "<none>"
|
||||
"checksum linked-hash-map 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bda158e0dabeb97ee8a401f4d17e479d6b891a14de0bba79d5cc2d4d325b5e48"
|
||||
@@ -2655,9 +2692,9 @@ dependencies = [
|
||||
"checksum miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d5bfc6782530ac8ace97af10a540054a37126b63b0702ddaaa243b73b5745b9a"
|
||||
"checksum msdos_time 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c04b68cc63a8480fb2550343695f7be72effdec953a9d4508161c3e69041c7d8"
|
||||
"checksum multibase 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b9c35dac080fd6e16a99924c8dfdef0af89d797dd851adab25feaffacf7850d6"
|
||||
"checksum multihash 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "755d5a39bee3faaf649437e873beab334990221b2faf1f2e56ca10a9e4600235"
|
||||
"checksum nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)" = "<none>"
|
||||
"checksum nanomsg-sys 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git)" = "<none>"
|
||||
"checksum multihash 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c9f70f2402fa07c16c40be8fd0a748a39257c5dc3ff5c857cbbde2f39135c505"
|
||||
"checksum nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git?branch=parity-1.7)" = "<none>"
|
||||
"checksum nanomsg-sys 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git?branch=parity-1.7)" = "<none>"
|
||||
"checksum native-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aa4e52995154bb6f0b41e4379a279482c9387c1632e3798ba4e511ef8c54ee09"
|
||||
"checksum net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)" = "6a816012ca11cb47009693c1e0c6130e26d39e4d97ee2a13c50e868ec83e3204"
|
||||
"checksum nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f05c2fc965fc1cd6b73fa57fa7b89f288178737f2f3ce9e63e4a6a141189000e"
|
||||
@@ -2681,8 +2718,8 @@ dependencies = [
|
||||
"checksum openssl-sys 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d2845e841700e7b04282ceaa115407ea84e0db918ae689ad9ceb6f06fa6046bd"
|
||||
"checksum order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "efa535d5117d3661134dbf1719b6f0ffe06f2375843b13935db186cd094105eb"
|
||||
"checksum owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8d91377085359426407a287ab16884a0111ba473aa6844ff01d4ec20ce3d75e7"
|
||||
"checksum parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98378dec0a185da2b7180308752f0bad73aaa949c3e0a3b0528d0e067945f7ab"
|
||||
"checksum parity-ui-precompiled 1.4.0 (git+https://github.com/ethcore/js-precompiled.git)" = "<none>"
|
||||
"checksum parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1d06f6ee0fda786df3784a96ee3f0629f529b91cbfb7d142f6410e6bcd1ce2c"
|
||||
"checksum parity-ui-precompiled 1.4.0 (git+https://github.com/ethcore/js-precompiled.git?branch=beta)" = "<none>"
|
||||
"checksum parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e1435e7a2a00dfebededd6c6bdbd54008001e94b4a2aadd6aef0dc4c56317621"
|
||||
"checksum parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb1b97670a2ffadce7c397fb80a3d687c4f3060140b885621ef1653d0e5d5068"
|
||||
"checksum phf 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "447d9d45f2e0b4a9b532e808365abf18fc211be6ca217202fcd45236ef12f026"
|
||||
@@ -2696,8 +2733,8 @@ dependencies = [
|
||||
"checksum primal-check 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "647c81b67bb9551a7b88d0bcd785ac35b7d0bf4b2f358683d7c2375d04daec51"
|
||||
"checksum primal-estimate 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "56ea4531dde757b56906493c8604641da14607bf9cdaa80fb9c9cabd2429f8d5"
|
||||
"checksum primal-sieve 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7aa73fd87e5984a00bdb4c1b14d3d5d6d0bad01b2caaaf924c16ab7260ac946c"
|
||||
"checksum quasi 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b26543b563704e7d87f3ec7cfafb713010a905c5f1b155a8ab66863af43ca578"
|
||||
"checksum quasi_codegen 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0881d9a45d5f9ebe4a7e77742f8c604f3658c212baf8dd711a692dd000bc648c"
|
||||
"checksum quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18c45c4854d6d1cf5d531db97c75880feb91c958b0720f4ec1057135fec358b3"
|
||||
"checksum quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9e25fa23c044c1803f43ca59c98dac608976dd04ce799411edd58ece776d4"
|
||||
"checksum quick-error 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0aad603e8d7fb67da22dbdf1f4b826ce8829e406124109e73cf1b2454b93a71c"
|
||||
"checksum quine-mc_cluskey 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6683b0e23d80813b1a535841f0048c1537d3f86d63c999e8373b39a9b0eb74a"
|
||||
"checksum quote 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)" = "6732e32663c9c271bfc7c1823486b471f18c47a2dbf87c066897b7b51afc83be"
|
||||
@@ -2706,7 +2743,7 @@ dependencies = [
|
||||
"checksum regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)" = "b4329b8928a284580a1c63ec9d846b12f6d3472317243ff7077aff11f23f2b29"
|
||||
"checksum regex-syntax 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "841591b1e05609a643e3b4d0045fce04f701daba7151ddcd3ad47b080693d5a9"
|
||||
"checksum reqwest 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bef9ed8fdfcc30947d6b774938dc0c3f369a474efe440df2c7f278180b2d2e6"
|
||||
"checksum ring 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "87ac4fce2ee4bb10dd106788e90fdfa4c5a7f3f9f6aae29824db77dc57e2767d"
|
||||
"checksum ring 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "595afba2db7545b940ec900dc59b5281f719d327fc0674eeadc9953617e55357"
|
||||
"checksum rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)" = "<none>"
|
||||
"checksum rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)" = "<none>"
|
||||
"checksum rotor 0.6.3 (git+https://github.com/ethcore/rotor)" = "<none>"
|
||||
@@ -2723,7 +2760,9 @@ dependencies = [
|
||||
"checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
|
||||
"checksum semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2d5b7638a1f03815d94e88cb3b3c08e87f0db4d683ef499d1836aaf70a45623f"
|
||||
"checksum semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae2ff60ecdb19c255841c066cbfa5f8c2a4ada1eb3ae47c77ab6667128da71f5"
|
||||
"checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537"
|
||||
"checksum semver-parser 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e88e43a5a74dd2a11707f9c21dfd4a423c66bd871df813227bb0a3e78f3a1ae9"
|
||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
"checksum serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0ae9a3c8b07c09dbe43022486d55a18c629a0618d2241e49829aaef9b6d862f9"
|
||||
"checksum serde_codegen_internals 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c3172bf2940b975c0e4f6ab42a511c0a4407d4f46ccef87a9d3615db5c26fa96"
|
||||
"checksum serde_derive 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ecc6e0379ca933ece58302d2d3034443f06fbf38fd535857c1dc516195cbc3bf"
|
||||
@@ -2738,18 +2777,20 @@ dependencies = [
|
||||
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
|
||||
"checksum smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fcc8d19212aacecf95e4a7a2179b26f7aeb9732a915cf01f05b0d3e044865410"
|
||||
"checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013"
|
||||
"checksum smallvec 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a3c84984c278afe61a46e19868e8b23e2ee3be5b3cc6dea6edad4893bc6c841"
|
||||
"checksum smallvec 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dca03f2f42500a9ef8ac0d16183dff8bed40e3dcf98f9d4147928548d5c4236e"
|
||||
"checksum solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "172382bac9424588d7840732b250faeeef88942e37b6e35317dce98cafdd75b2"
|
||||
"checksum spmc 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "93bdab61c1a413e591c4d17388ffa859eaff2df27f1e13a5ec8b716700605adf"
|
||||
"checksum stable-heap 0.1.0 (git+https://github.com/carllerche/stable-heap?rev=3c5cd1ca47)" = "<none>"
|
||||
"checksum strsim 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e4d73a2c36a4d095ed1a6df5cbeac159863173447f7a82b3f4757426844ab825"
|
||||
"checksum syn 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f94368aae82bb29656c98443a7026ca931a659e8d19dcdc41d6e273054e820"
|
||||
"checksum syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393b6dd0889df2b064beeea954cfda6bc2571604ac460deeae0fed55a53988af"
|
||||
"checksum syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44bded3cabafc65c90b663b1071bd2d198a9ab7515e6ce729e4570aaf53c407e"
|
||||
"checksum syntex 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)" = "35f3cc9d446323ef8fefad933b65cd6de271d29fa14a2e9d036a084770c6d6d5"
|
||||
"checksum syntex_errors 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3af03823ea45d420dd2c1a44bb074e13ea55f9b99afe960fd58eb4069b7f6cad"
|
||||
"checksum syntex_pos 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1e502a4a904d9f37cf975dbdbb0b08f2d111322f6792bda6eb095b4112c9a24b"
|
||||
"checksum syntex_syntax 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6cf936464c3863952ea3fab848860ea891eba8647b6008b04c36f0bb007192a3"
|
||||
"checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5"
|
||||
"checksum target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe"
|
||||
"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6"
|
||||
"checksum term 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "f2077e54d38055cf1ca0fd7933a2e00cd3ec8f6fed352b2a377f06dcdaaf3281"
|
||||
"checksum term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d168af3930b369cfe245132550579d47dfd873d69470755a19c2c6568dbbd989"
|
||||
"checksum termios 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d9cf598a6d7ce700a4e6a9199da127e6819a61e64b68609683cc9a01b5683a"
|
||||
"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
|
||||
"checksum thread_local 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0694f51610ef7cfac7a1b81de7f1602ee5356e76541bcd62c40e71933338cab1"
|
||||
@@ -2766,7 +2807,6 @@ dependencies = [
|
||||
"checksum unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "13a5906ca2b98c799f4b1ab4557b76367ebd6ae5ef14930ec841c74aed5f3764"
|
||||
"checksum unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c1f7ceb96afdfeedee42bade65a0d585a6a0106f681b6749c8ff4daa8df30b3f"
|
||||
"checksum unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "26643a2f83bac55f1976fb716c10234485f9202dcd65cfbdf9da49867b271172"
|
||||
"checksum unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "36dff09cafb4ec7c8cf0023eb0b686cb6ce65499116a12201c9e11840ca01beb"
|
||||
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
|
||||
"checksum untrusted 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "193df64312e3515fd983ded55ad5bcaa7647a035804828ed757e832ce6029ef3"
|
||||
"checksum url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "afe9ec54bc4db14bc8744b7fed060d785ac756791450959b2248443319d5b119"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
description = "Parity Ethereum client"
|
||||
name = "parity"
|
||||
version = "1.6.0"
|
||||
version = "1.6.6"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ serde = "0.9"
|
||||
serde_json = "0.9"
|
||||
serde_derive = "0.9"
|
||||
linked-hash-map = "0.3"
|
||||
parity-dapps-glue = "1.4"
|
||||
parity-dapps-glue = "1.7"
|
||||
base32 = "0.3"
|
||||
mime = "0.2"
|
||||
mime_guess = "1.6.1"
|
||||
|
||||
@@ -7,17 +7,17 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
[build-dependencies]
|
||||
quasi_codegen = { version = "0.11", optional = true }
|
||||
syntex = { version = "0.33", optional = true }
|
||||
quasi_codegen = { version = "0.32", optional = true }
|
||||
syntex = { version = "0.58", optional = true }
|
||||
|
||||
[dependencies]
|
||||
glob = { version = "0.2.11" }
|
||||
mime_guess = { version = "1.6.1" }
|
||||
aster = { version = "0.17", default-features = false }
|
||||
quasi = { version = "0.11", default-features = false }
|
||||
quasi_macros = { version = "0.11", optional = true }
|
||||
syntex = { version = "0.33", optional = true }
|
||||
syntex_syntax = { version = "0.33", optional = true }
|
||||
aster = { version = "0.41", default-features = false }
|
||||
quasi = { version = "0.32", default-features = false }
|
||||
quasi_macros = { version = "0.32", optional = true }
|
||||
syntex = { version = "0.58", optional = true }
|
||||
syntex_syntax = { version = "0.58", optional = true }
|
||||
clippy = { version = "0.0.90", optional = true }
|
||||
|
||||
[features]
|
||||
|
||||
@@ -25,13 +25,11 @@ mod inner {
|
||||
|
||||
pub fn main() {
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let mut registry = syntex::Registry::new();
|
||||
quasi_codegen::register(&mut registry);
|
||||
|
||||
let src = Path::new("src/lib.rs.in");
|
||||
let dst = Path::new(&out_dir).join("lib.rs");
|
||||
|
||||
registry.expand("", &src, &dst).unwrap();
|
||||
quasi_codegen::expand(&src, &dst).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,9 +13,8 @@ pub mod inner {
|
||||
|
||||
impl fold::Folder for StripAttributeFolder {
|
||||
fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
|
||||
match attr.node.value.node {
|
||||
ast::MetaItemKind::List(ref n, _) if n == &"webapp" => { return None; }
|
||||
_ => {}
|
||||
if &*attr.value.name.as_str() == "webapp" {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(attr)
|
||||
|
||||
@@ -22,16 +22,12 @@ use self::mime_guess::guess_mime_type;
|
||||
use std::path::{self, Path, PathBuf};
|
||||
use std::ops::Deref;
|
||||
|
||||
use syntax::ast::{MetaItem, Item};
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::attr;
|
||||
use syntax::ast::{self, MetaItem, Item};
|
||||
use syntax::codemap::Span;
|
||||
use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||
use syntax::ptr::P;
|
||||
use syntax::print::pprust::{lit_to_string};
|
||||
use syntax::parse::token::{InternedString};
|
||||
|
||||
use syntax::print::pprust::lit_to_string;
|
||||
use syntax::symbol::InternedString;
|
||||
|
||||
pub fn expand_webapp_implementation(
|
||||
cx: &mut ExtCtxt,
|
||||
@@ -48,7 +44,7 @@ pub fn expand_webapp_implementation(
|
||||
},
|
||||
};
|
||||
let builder = aster::AstBuilder::new().span(span);
|
||||
implement_webapp(cx, &builder, &item, push);
|
||||
implement_webapp(cx, &builder, item, push);
|
||||
}
|
||||
|
||||
fn implement_webapp(cx: &ExtCtxt, builder: &aster::AstBuilder, item: &Item, push: &mut FnMut(Annotatable)) {
|
||||
@@ -117,11 +113,12 @@ fn implement_webapp(cx: &ExtCtxt, builder: &aster::AstBuilder, item: &Item, push
|
||||
}
|
||||
|
||||
fn extract_path(cx: &ExtCtxt, item: &Item) -> String {
|
||||
for meta_items in item.attrs().iter().filter_map(webapp_meta_items) {
|
||||
for meta_items in item.attrs.iter().filter_map(webapp_meta_items) {
|
||||
for meta_item in meta_items {
|
||||
let is_path = &*meta_item.name.as_str() == "path";
|
||||
match meta_item.node {
|
||||
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"path" => {
|
||||
if let Some(s) = get_str_from_lit(cx, name, lit) {
|
||||
ast::MetaItemKind::NameValue(ref lit) if is_path => {
|
||||
if let Some(s) = get_str_from_lit(cx, lit) {
|
||||
return s.deref().to_owned();
|
||||
}
|
||||
},
|
||||
@@ -134,14 +131,32 @@ fn extract_path(cx: &ExtCtxt, item: &Item) -> String {
|
||||
"web".to_owned()
|
||||
}
|
||||
|
||||
fn get_str_from_lit(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Option<InternedString> {
|
||||
fn webapp_meta_items(attr: &ast::Attribute) -> Option<Vec<ast::MetaItem>> {
|
||||
let is_webapp = &*attr.value.name.as_str() == "webapp";
|
||||
match attr.value.node {
|
||||
ast::MetaItemKind::List(ref items) if is_webapp => {
|
||||
attr::mark_used(&attr);
|
||||
Some(
|
||||
items.iter()
|
||||
.map(|item| item.node.clone())
|
||||
.filter_map(|item| match item {
|
||||
ast::NestedMetaItemKind::MetaItem(item) => Some(item),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
)
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_str_from_lit(cx: &ExtCtxt, lit: &ast::Lit) -> Option<InternedString> {
|
||||
match lit.node {
|
||||
ast::LitKind::Str(ref s, _) => Some(s.clone()),
|
||||
ast::LitKind::Str(ref s, _) => Some(s.clone().as_str()),
|
||||
_ => {
|
||||
cx.span_err(
|
||||
lit.span,
|
||||
&format!("webapp annotation `{}` must be a string, not `{}`",
|
||||
name,
|
||||
&format!("webapp annotation path must be a string, not `{}`",
|
||||
lit_to_string(lit)
|
||||
)
|
||||
);
|
||||
@@ -150,16 +165,6 @@ fn get_str_from_lit(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Option<Interned
|
||||
}
|
||||
}
|
||||
|
||||
fn webapp_meta_items(attr: &ast::Attribute) -> Option<&[P<ast::MetaItem>]> {
|
||||
match attr.node.value.node {
|
||||
ast::MetaItemKind::List(ref name, ref items) if name == &"webapp" => {
|
||||
attr::mark_used(&attr);
|
||||
Some(items)
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_uri(path: &Path) -> String {
|
||||
let mut s = String::new();
|
||||
for component in path.iter() {
|
||||
@@ -169,7 +174,6 @@ fn as_uri(path: &Path) -> String {
|
||||
s[0..s.len()-1].into()
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_convert_path_separators_on_all_platforms() {
|
||||
// given
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
extern crate syntex;
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
#[macro_use]
|
||||
extern crate syntex_syntax as syntax;
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
|
||||
@@ -39,7 +39,11 @@ pub struct RestApi {
|
||||
impl RestApi {
|
||||
pub fn new(cors_domains: Vec<String>, endpoints: Arc<Endpoints>, fetcher: Arc<Fetcher>) -> Box<Endpoint> {
|
||||
Box::new(RestApi {
|
||||
cors_domains: Some(cors_domains.into_iter().map(AccessControlAllowOrigin::Value).collect()),
|
||||
cors_domains: Some(cors_domains.into_iter().map(|domain| match domain.as_ref() {
|
||||
"all" | "*" | "any" => AccessControlAllowOrigin::Any,
|
||||
"null" => AccessControlAllowOrigin::Null,
|
||||
other => AccessControlAllowOrigin::Value(other.into()),
|
||||
}).collect()),
|
||||
endpoints: endpoints,
|
||||
fetcher: fetcher,
|
||||
})
|
||||
|
||||
@@ -111,6 +111,7 @@ pub struct ServerBuilder<T: Fetch = FetchClient> {
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
signer_address: Option<(String, u16)>,
|
||||
allowed_hosts: Option<Vec<String>>,
|
||||
extra_cors: Option<Vec<String>>,
|
||||
remote: Remote,
|
||||
fetch: Option<T>,
|
||||
}
|
||||
@@ -126,6 +127,7 @@ impl ServerBuilder {
|
||||
web_proxy_tokens: Arc::new(|_| false),
|
||||
signer_address: None,
|
||||
allowed_hosts: Some(vec![]),
|
||||
extra_cors: None,
|
||||
remote: remote,
|
||||
fetch: None,
|
||||
}
|
||||
@@ -143,6 +145,7 @@ impl<T: Fetch> ServerBuilder<T> {
|
||||
web_proxy_tokens: self.web_proxy_tokens,
|
||||
signer_address: self.signer_address,
|
||||
allowed_hosts: self.allowed_hosts,
|
||||
extra_cors: self.extra_cors,
|
||||
remote: self.remote,
|
||||
fetch: Some(fetch),
|
||||
}
|
||||
@@ -174,6 +177,13 @@ impl<T: Fetch> ServerBuilder<T> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Extra cors headers.
|
||||
/// `None` - no additional CORS URLs
|
||||
pub fn extra_cors_headers(mut self, cors: Option<Vec<String>>) -> Self {
|
||||
self.extra_cors = cors;
|
||||
self
|
||||
}
|
||||
|
||||
/// Change extra dapps paths (apart from `dapps_path`)
|
||||
pub fn extra_dapps<P: AsRef<Path>>(mut self, extra_dapps: &[P]) -> Self {
|
||||
self.extra_dapps = extra_dapps.iter().map(|p| p.as_ref().to_owned()).collect();
|
||||
@@ -187,6 +197,7 @@ impl<T: Fetch> ServerBuilder<T> {
|
||||
Server::start_http(
|
||||
addr,
|
||||
self.allowed_hosts,
|
||||
self.extra_cors,
|
||||
NoAuth,
|
||||
handler,
|
||||
self.dapps_path,
|
||||
@@ -207,6 +218,7 @@ impl<T: Fetch> ServerBuilder<T> {
|
||||
Server::start_http(
|
||||
addr,
|
||||
self.allowed_hosts,
|
||||
self.extra_cors,
|
||||
HttpBasicAuth::single_user(username, password),
|
||||
handler,
|
||||
self.dapps_path,
|
||||
@@ -251,8 +263,8 @@ impl Server {
|
||||
}
|
||||
|
||||
/// Returns a list of CORS domains for API endpoint.
|
||||
fn cors_domains(signer_address: Option<(String, u16)>) -> Vec<String> {
|
||||
match signer_address {
|
||||
fn cors_domains(signer_address: Option<(String, u16)>, extra_cors: Option<Vec<String>>) -> Vec<String> {
|
||||
let basic_cors = match signer_address {
|
||||
Some(signer_address) => vec![
|
||||
format!("http://{}{}", HOME_PAGE, DAPPS_DOMAIN),
|
||||
format!("http://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1),
|
||||
@@ -260,15 +272,20 @@ impl Server {
|
||||
format!("https://{}{}", HOME_PAGE, DAPPS_DOMAIN),
|
||||
format!("https://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1),
|
||||
format!("https://{}", address(&signer_address)),
|
||||
|
||||
],
|
||||
None => vec![],
|
||||
};
|
||||
|
||||
match extra_cors {
|
||||
None => basic_cors,
|
||||
Some(extra_cors) => basic_cors.into_iter().chain(extra_cors).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn start_http<A: Authorization + 'static, F: Fetch, T: Middleware<Metadata>>(
|
||||
addr: &SocketAddr,
|
||||
hosts: Option<Vec<String>>,
|
||||
extra_cors: Option<Vec<String>>,
|
||||
authorization: A,
|
||||
handler: RpcHandler<Metadata, T>,
|
||||
dapps_path: PathBuf,
|
||||
@@ -297,7 +314,7 @@ impl Server {
|
||||
remote.clone(),
|
||||
fetch.clone(),
|
||||
));
|
||||
let cors_domains = Self::cors_domains(signer_address.clone());
|
||||
let cors_domains = Self::cors_domains(signer_address.clone(), extra_cors);
|
||||
|
||||
let special = Arc::new({
|
||||
let mut special = HashMap::new();
|
||||
@@ -413,8 +430,9 @@ mod util_tests {
|
||||
// given
|
||||
|
||||
// when
|
||||
let none = Server::cors_domains(None);
|
||||
let some = Server::cors_domains(Some(("127.0.0.1".into(), 18180)));
|
||||
let none = Server::cors_domains(None, None);
|
||||
let some = Server::cors_domains(Some(("127.0.0.1".into(), 18180)), None);
|
||||
let extra = Server::cors_domains(None, Some(vec!["all".to_owned()]));
|
||||
|
||||
// then
|
||||
assert_eq!(none, Vec::<String>::new());
|
||||
@@ -425,7 +443,7 @@ mod util_tests {
|
||||
"https://parity.web3.site".into(),
|
||||
"https://parity.web3.site:18180".into(),
|
||||
"https://127.0.0.1:18180".into()
|
||||
|
||||
]);
|
||||
assert_eq!(extra, vec!["all".to_owned()]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use tests::helpers::{serve, serve_with_registrar, request, assert_security_headers};
|
||||
use tests::helpers::{serve, serve_with_registrar, serve_extra_cors, request, assert_security_headers};
|
||||
|
||||
#[test]
|
||||
fn should_return_error() {
|
||||
@@ -212,3 +212,25 @@ fn should_return_signer_port_cors_headers_for_home_parity_with_port() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_extra_cors_headers() {
|
||||
// given
|
||||
let server = serve_extra_cors(Some(vec!["all".to_owned()]));
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
POST /api/ping HTTP/1.1\r\n\
|
||||
Host: localhost:8080\r\n\
|
||||
Origin: http://somedomain.io\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
{}
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
response.assert_header("Access-Control-Allow-Origin", "http://somedomain.io");
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,10 @@ pub fn serve_hosts(hosts: Option<Vec<String>>) -> ServerLoop {
|
||||
init_server(|builder| builder.allowed_hosts(hosts), Default::default(), Remote::new_sync()).0
|
||||
}
|
||||
|
||||
pub fn serve_extra_cors(extra_cors: Option<Vec<String>>) -> ServerLoop {
|
||||
init_server(|builder| builder.allowed_hosts(None).extra_cors_headers(extra_cors), Default::default(), Remote::new_sync()).0
|
||||
}
|
||||
|
||||
pub fn serve_with_registrar() -> (ServerLoop, Arc<FakeRegistrar>) {
|
||||
init_server(|builder| builder.allowed_hosts(None), Default::default(), Remote::new_sync())
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ rustc_version = "0.1"
|
||||
|
||||
[dependencies]
|
||||
parity-ui-dev = { path = "../../js", optional = true }
|
||||
parity-ui-precompiled = { git = "https://github.com/ethcore/js-precompiled.git", optional = true }
|
||||
parity-ui-precompiled = { git = "https://github.com/ethcore/js-precompiled.git", branch = "beta", optional = true }
|
||||
|
||||
[features]
|
||||
no-precompiled-js = ["parity-ui-dev"]
|
||||
|
||||
@@ -27,6 +27,7 @@ byteorder = "1.0"
|
||||
transient-hashmap = "0.1"
|
||||
linked-hash-map = "0.3.0"
|
||||
lru-cache = "0.1.0"
|
||||
itertools = "0.5"
|
||||
ethabi = "1.0.0"
|
||||
evmjit = { path = "../evmjit", optional = true }
|
||||
clippy = { version = "0.0.103", optional = true}
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x2fefd8"
|
||||
"gasLimit": "0x222222"
|
||||
},
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
|
||||
@@ -53,7 +53,9 @@
|
||||
"enode://5fbfb426fbb46f8b8c1bd3dd140f5b511da558cd37d60844b525909ab82e13a25ee722293c829e52cb65c2305b1637fa9a2ea4d6634a224d5f400bfe244ac0de@162.243.55.45:30303",
|
||||
"enode://42d8f29d1db5f4b2947cd5c3d76c6d0d3697e6b9b3430c3d41e46b4bb77655433aeedc25d4b4ea9d8214b6a43008ba67199374a9b53633301bca0cd20c6928ab@104.155.176.151:30303",
|
||||
"enode://814920f1ec9510aa9ea1c8f79d8b6e6a462045f09caa2ae4055b0f34f7416fca6facd3dd45f1cf1673c0209e0503f02776b8ff94020e98b6679a0dc561b4eba0@104.154.136.117:30303",
|
||||
"enode://72e445f4e89c0f476d404bc40478b0df83a5b500d2d2e850e08eb1af0cd464ab86db6160d0fde64bd77d5f0d33507ae19035671b3c74fec126d6e28787669740@104.198.71.200:30303"
|
||||
"enode://72e445f4e89c0f476d404bc40478b0df83a5b500d2d2e850e08eb1af0cd464ab86db6160d0fde64bd77d5f0d33507ae19035671b3c74fec126d6e28787669740@104.198.71.200:30303",
|
||||
"enode://39abab9d2a41f53298c0c9dc6bbca57b0840c3ba9dccf42aa27316addc1b7e56ade32a0a9f7f52d6c5db4fe74d8824bcedfeaecf1a4e533cacb71cf8100a9442@144.76.238.49:30303",
|
||||
"enode://f50e675a34f471af2438b921914b5f06499c7438f3146f6b8936f1faeb50b8a91d0d0c24fb05a66f05865cd58c24da3e664d0def806172ddd0d4c5bdbf37747e@144.76.238.49:30306"
|
||||
],
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
|
||||
@@ -23,14 +23,17 @@
|
||||
|
||||
"0x00a0a24b9f0e5ec7aa4c7389b8302fd0123194de"
|
||||
]
|
||||
}
|
||||
},
|
||||
"validateScoreTransition": 1000000,
|
||||
"eip155Transition": 1000000
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x2A"
|
||||
"networkID" : "0x2A",
|
||||
"validateReceiptsTransition" : 1000000
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x3",
|
||||
"forkBlock": 333922,
|
||||
"forkCanonHash": "0x8737eb141d4f05db57af63fc8d3b4d4d8f9cddb0c4e1ab855de8c288fdc1924f",
|
||||
"forkBlock": 641350,
|
||||
"forkCanonHash": "0x8033403e9fe5811a7b6d6b469905915de1c59207ce2172cbcf5d6ff14fa6a2eb",
|
||||
"eip98Transition": "0x7fffffffffffff"
|
||||
},
|
||||
"genesis": {
|
||||
@@ -44,11 +44,8 @@
|
||||
"gasLimit": "0x1000000"
|
||||
},
|
||||
"nodes": [
|
||||
"enode://a22f0977ce02653bf95e38730106356342df48b5222e2c2a1a6f9ef34769bf593bae9ca0a888cf60839edd52efc1b6e393c63a57d76f4c4fe14e641f1f9e637e@128.199.55.137:30303",
|
||||
"enode://012239fccf3ff1d92b036983a430cb6705c6528c96c0354413f8854802138e5135c084ab36e7c54efb621c46728df8c3a6f4c1db9bb48a1330efe3f82f2dd7a6@52.169.94.142:30303",
|
||||
"enode://1462682e4b7ba2258346d55e25e5b9d264b0db40cee12bdfba4e72b1d7050350ea954c006e9106dd96a128e6e0bd6dffb17eed51f9f99bf7f9cdadfeaf8da4ff@51.15.61.253:30303",
|
||||
"enode://98fbb020c799ae39a828bd75dc2bd5d4721539faf317076b275f91182a5c8900b592e8abfdddceae674a7c3bb40ea00a6ca9ccb7805ab58c4b7b29c61c8f7239@51.15.62.44:30303",
|
||||
"enode://d801dd4e3d15a8bf785931add164bd9c313e3f6b5749d9302b311f2b48064cba5c86c32b1302c27cd983fc89ae07d4d306dd1197610835b8782e95dfb1b3f9ea@51.15.43.255:30303"
|
||||
"enode://20c9ad97c081d63397d7b685a412227a40e23c8bdc6688c6f37e97cfbc22d2b4d1db1510d8f61e6a8866ad7f0e17c02b14182d37ea7c3c8b9c2683aeb6b733a1@52.169.14.227:30303",
|
||||
"enode://6ce05930c72abc632c58e2e4324f7c7ea478cec0ed4fa2528982cf34483094e9cbc9216e7aa349691242576d552a2a56aaeae426c5303ded677ce455ba1acd9d@13.84.180.240:30303"
|
||||
],
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "0", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
|
||||
@@ -139,6 +139,7 @@
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"eip98Transition": "0x7fffffffffffffff",
|
||||
"accountStartNonce": "0x00",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x2fefd8"
|
||||
"gasLimit": "0x222222"
|
||||
},
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x2fefd8"
|
||||
"gasLimit": "0x222222"
|
||||
},
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
|
||||
42
ethcore/res/validator_multi.json
Normal file
42
ethcore/res/validator_multi.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "TestMutiValidator",
|
||||
"engine": {
|
||||
"basicAuthority": {
|
||||
"params": {
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"durationLimit": "0x0d",
|
||||
"validators": {
|
||||
"multi": {
|
||||
"0": { "list": ["0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1"] },
|
||||
"2": { "list": ["0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e"] }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x69"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"generic": "0xc180"
|
||||
},
|
||||
"difficulty": "0x20000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x2fefd8"
|
||||
},
|
||||
"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 } } } },
|
||||
"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1": { "balance": "99999999999999999999999" },
|
||||
"0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e": { "balance": "99999999999999999999999" }
|
||||
}
|
||||
}
|
||||
@@ -152,7 +152,7 @@ impl AccountProvider {
|
||||
manager.set_key_path(if settings.hardware_wallet_classic_key { KeyPath::EthereumClassic } else { KeyPath::Ethereum });
|
||||
hardware_store = Some(manager)
|
||||
},
|
||||
Err(e) => warn!("Error initializing hardware wallets: {}", e),
|
||||
Err(e) => debug!("Error initializing hardware wallets: {}", e),
|
||||
}
|
||||
}
|
||||
AccountProvider {
|
||||
|
||||
@@ -493,6 +493,16 @@ impl LockedBlock {
|
||||
_ => Ok(SealedBlock { block: s.block, uncle_bytes: s.uncle_bytes }),
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove state root from transaction receipts to make them EIP-98 compatible.
|
||||
pub fn strip_receipts(self) -> LockedBlock {
|
||||
let mut block = self;
|
||||
for receipt in &mut block.block.receipts {
|
||||
receipt.state_root = None;
|
||||
}
|
||||
block.block.header.set_receipts_root(ordered_trie_root(block.block.receipts.iter().map(|r| r.rlp_bytes().to_vec())));
|
||||
block
|
||||
}
|
||||
}
|
||||
|
||||
impl Drain for LockedBlock {
|
||||
@@ -553,7 +563,6 @@ pub fn enact(
|
||||
b.set_extra_data(header.extra_data().clone()).unwrap_or_else(|e| warn!("Couldn't set extradata: {}. Ignoring.", e));
|
||||
b.set_uncles_hash(header.uncles_hash().clone());
|
||||
b.set_transactions_root(header.transactions_root().clone());
|
||||
b.set_receipts_root(header.receipts_root().clone());
|
||||
|
||||
push_transactions(&mut b, transactions)?;
|
||||
for u in uncles {
|
||||
|
||||
@@ -253,7 +253,7 @@ impl Client {
|
||||
if let Some(reg_addr) = client.additional_params().get("registrar").and_then(|s| Address::from_str(s).ok()) {
|
||||
trace!(target: "client", "Found registrar at {}", reg_addr);
|
||||
let weak = Arc::downgrade(&client);
|
||||
let registrar = Registry::new(reg_addr, move |a, d| weak.upgrade().ok_or("No client!".into()).and_then(|c| c.call_contract(a, d)));
|
||||
let registrar = Registry::new(reg_addr, move |a, d| weak.upgrade().ok_or("No client!".into()).and_then(|c| c.call_contract(BlockId::Latest, a, d)));
|
||||
*client.registrar.lock() = Some(registrar);
|
||||
}
|
||||
Ok(client)
|
||||
@@ -375,10 +375,14 @@ impl Client {
|
||||
let db = self.state_db.lock().boxed_clone_canon(header.parent_hash());
|
||||
|
||||
let enact_result = enact_verified(block, engine, self.tracedb.read().tracing_enabled(), db, &parent, last_hashes, self.factories.clone());
|
||||
let locked_block = enact_result.map_err(|e| {
|
||||
let mut locked_block = enact_result.map_err(|e| {
|
||||
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
||||
})?;
|
||||
|
||||
if header.number() < self.engine().params().validate_receipts_transition && header.receipts_root() != locked_block.block().header().receipts_root() {
|
||||
locked_block = locked_block.strip_receipts();
|
||||
}
|
||||
|
||||
// Final Verification
|
||||
if let Err(e) = self.verifier.verify_block_final(header, locked_block.block().header()) {
|
||||
warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
||||
@@ -1445,7 +1449,7 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
}
|
||||
|
||||
fn call_contract(&self, address: Address, data: Bytes) -> Result<Bytes, String> {
|
||||
fn call_contract(&self, block_id: BlockId, address: Address, data: Bytes) -> Result<Bytes, String> {
|
||||
let from = Address::default();
|
||||
let transaction = Transaction {
|
||||
nonce: self.latest_nonce(&from),
|
||||
@@ -1456,7 +1460,7 @@ impl BlockChainClient for Client {
|
||||
data: data,
|
||||
}.fake_sign(from);
|
||||
|
||||
self.call(&transaction, BlockId::Latest, Default::default())
|
||||
self.call(&transaction, block_id, Default::default())
|
||||
.map_err(|e| format!("{:?}", e))
|
||||
.map(|executed| {
|
||||
executed.output
|
||||
|
||||
@@ -731,7 +731,7 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
}
|
||||
}
|
||||
|
||||
fn call_contract(&self, _address: Address, _data: Bytes) -> Result<Bytes, String> { Ok(vec![]) }
|
||||
fn call_contract(&self, _id: BlockId, _address: Address, _data: Bytes) -> Result<Bytes, String> { Ok(vec![]) }
|
||||
|
||||
fn transact_contract(&self, address: Address, data: Bytes) -> Result<TransactionImportResult, EthcoreError> {
|
||||
let transaction = Transaction {
|
||||
|
||||
@@ -254,7 +254,7 @@ pub trait BlockChainClient : Sync + Send {
|
||||
fn pruning_info(&self) -> PruningInfo;
|
||||
|
||||
/// Like `call`, but with various defaults. Designed to be used for calling contracts.
|
||||
fn call_contract(&self, address: Address, data: Bytes) -> Result<Bytes, String>;
|
||||
fn call_contract(&self, id: BlockId, address: Address, data: Bytes) -> Result<Bytes, String>;
|
||||
|
||||
/// Import a transaction: used for misbehaviour reporting.
|
||||
fn transact_contract(&self, address: Address, data: Bytes) -> Result<TransactionImportResult, EthcoreError>;
|
||||
|
||||
@@ -27,12 +27,13 @@ use block::*;
|
||||
use spec::CommonParams;
|
||||
use engines::{Engine, Seal, EngineError};
|
||||
use header::Header;
|
||||
use error::{Error, BlockError};
|
||||
use error::{Error, TransactionError, BlockError};
|
||||
use evm::Schedule;
|
||||
use ethjson;
|
||||
use io::{IoContext, IoHandler, TimerToken, IoService};
|
||||
use env_info::EnvInfo;
|
||||
use builtin::Builtin;
|
||||
use transaction::UnverifiedTransaction;
|
||||
use client::{Client, EngineClient};
|
||||
use state::CleanupMode;
|
||||
use super::signer::EngineSigner;
|
||||
@@ -53,6 +54,10 @@ pub struct AuthorityRoundParams {
|
||||
pub start_step: Option<u64>,
|
||||
/// Valid validators.
|
||||
pub validators: ethjson::spec::ValidatorSet,
|
||||
/// Chain score validation transition block.
|
||||
pub validate_score_transition: u64,
|
||||
/// Number of first block where EIP-155 rules are validated.
|
||||
pub eip155_transition: u64,
|
||||
}
|
||||
|
||||
impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
|
||||
@@ -64,6 +69,8 @@ impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
|
||||
block_reward: p.block_reward.map_or_else(U256::zero, Into::into),
|
||||
registrar: p.registrar.map_or_else(Address::new, Into::into),
|
||||
start_step: p.start_step.map(Into::into),
|
||||
validate_score_transition: p.validate_score_transition.map_or(0, Into::into),
|
||||
eip155_transition: p.eip155_transition.map_or(0, Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,7 +89,11 @@ pub struct AuthorityRound {
|
||||
proposed: AtomicBool,
|
||||
client: RwLock<Option<Weak<EngineClient>>>,
|
||||
signer: EngineSigner,
|
||||
validators: Box<ValidatorSet + Send + Sync>,
|
||||
validators: Box<ValidatorSet>,
|
||||
/// Is this Engine just for testing (prevents step calibration).
|
||||
calibrate_step: bool,
|
||||
validate_score_transition: u64,
|
||||
eip155_transition: u64,
|
||||
}
|
||||
|
||||
fn header_step(header: &Header) -> Result<usize, ::rlp::DecoderError> {
|
||||
@@ -122,6 +133,9 @@ impl AuthorityRound {
|
||||
client: RwLock::new(None),
|
||||
signer: Default::default(),
|
||||
validators: new_validator_set(our_params.validators),
|
||||
calibrate_step: our_params.start_step.is_none(),
|
||||
validate_score_transition: our_params.validate_score_transition,
|
||||
eip155_transition: our_params.eip155_transition,
|
||||
});
|
||||
// Do not initialize timeouts for tests.
|
||||
if should_timeout {
|
||||
@@ -131,6 +145,12 @@ impl AuthorityRound {
|
||||
Ok(engine)
|
||||
}
|
||||
|
||||
fn calibrate_step(&self) {
|
||||
if self.calibrate_step {
|
||||
self.step.store((unix_now().as_secs() / self.step_duration.as_secs()) as usize, AtomicOrdering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
fn remaining_step_duration(&self) -> Duration {
|
||||
let now = unix_now();
|
||||
let step_end = self.step_duration * (self.step.load(AtomicOrdering::SeqCst) as u32 + 1);
|
||||
@@ -141,12 +161,22 @@ impl AuthorityRound {
|
||||
}
|
||||
}
|
||||
|
||||
fn step_proposer(&self, step: usize) -> Address {
|
||||
self.validators.get(step)
|
||||
fn step_proposer(&self, bh: &H256, step: usize) -> Address {
|
||||
self.validators.get(bh, step)
|
||||
}
|
||||
|
||||
fn is_step_proposer(&self, step: usize, address: &Address) -> bool {
|
||||
self.step_proposer(step) == *address
|
||||
fn is_step_proposer(&self, bh: &H256, step: usize, address: &Address) -> bool {
|
||||
self.step_proposer(bh, step) == *address
|
||||
}
|
||||
|
||||
fn is_future_step(&self, step: usize) -> bool {
|
||||
if step > self.step.load(AtomicOrdering::SeqCst) + 1 {
|
||||
// Make absolutely sure that the step is correct.
|
||||
self.calibrate_step();
|
||||
step > self.step.load(AtomicOrdering::SeqCst) + 1
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,9 +246,8 @@ impl Engine for AuthorityRound {
|
||||
}
|
||||
|
||||
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) {
|
||||
// Chain scoring: total weight is sqrt(U256::max_value())*height - step
|
||||
let new_difficulty = U256::from(U128::max_value()) + header_step(parent).expect("Header has been verified; qed").into() - self.step.load(AtomicOrdering::SeqCst).into();
|
||||
header.set_difficulty(new_difficulty);
|
||||
// Chain scoring: weak height scoring, backported for compatibility.
|
||||
header.set_difficulty(parent.difficulty().clone());
|
||||
header.set_gas_limit({
|
||||
let gas_limit = parent.gas_limit().clone();
|
||||
let bound_divisor = self.gas_limit_bound_divisor;
|
||||
@@ -231,7 +260,7 @@ impl Engine for AuthorityRound {
|
||||
}
|
||||
|
||||
fn seals_internally(&self) -> Option<bool> {
|
||||
Some(self.validators.contains(&self.signer.address()))
|
||||
Some(self.signer.address() != Address::default())
|
||||
}
|
||||
|
||||
/// Attempt to seal the block internally.
|
||||
@@ -242,7 +271,7 @@ impl Engine for AuthorityRound {
|
||||
if self.proposed.load(AtomicOrdering::SeqCst) { return Seal::None; }
|
||||
let header = block.header();
|
||||
let step = self.step.load(AtomicOrdering::SeqCst);
|
||||
if self.is_step_proposer(step, header.author()) {
|
||||
if self.is_step_proposer(header.parent_hash(), step, header.author()) {
|
||||
if let Ok(signature) = self.signer.sign(header.bare_hash()) {
|
||||
trace!(target: "engine", "generate_seal: Issuing a block for step {}.", step);
|
||||
self.proposed.store(true, AtomicOrdering::SeqCst);
|
||||
@@ -276,39 +305,45 @@ impl Engine for AuthorityRound {
|
||||
Err(From::from(BlockError::InvalidSealArity(
|
||||
Mismatch { expected: self.seal_fields(), found: header.seal().len() }
|
||||
)))
|
||||
} else if header.number() >= self.validate_score_transition && *header.difficulty() >= U256::from(U128::max_value()) {
|
||||
Err(From::from(BlockError::DifficultyOutOfBounds(
|
||||
OutOfBounds { min: None, max: Some(U256::from(U128::max_value())), found: *header.difficulty() }
|
||||
)))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the signature belongs to the correct proposer.
|
||||
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
let header_step = header_step(header)?;
|
||||
fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Do the validator and gas limit validation.
|
||||
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
let step = header_step(header)?;
|
||||
// Give one step slack if step is lagging, double vote is still not possible.
|
||||
if header_step <= self.step.load(AtomicOrdering::SeqCst) + 1 {
|
||||
let proposer_signature = header_signature(header)?;
|
||||
let correct_proposer = self.step_proposer(header_step);
|
||||
if verify_address(&correct_proposer, &proposer_signature, &header.bare_hash())? {
|
||||
Ok(())
|
||||
} else {
|
||||
trace!(target: "engine", "verify_block_unordered: bad proposer for step: {}", header_step);
|
||||
Err(EngineError::NotProposer(Mismatch { expected: correct_proposer, found: header.author().clone() }))?
|
||||
}
|
||||
} else {
|
||||
if self.is_future_step(step) {
|
||||
trace!(target: "engine", "verify_block_unordered: block from the future");
|
||||
self.validators.report_benign(header.author());
|
||||
Err(BlockError::InvalidSeal)?
|
||||
} else {
|
||||
// Check if the signature belongs to a validator, can depend on parent state.
|
||||
let proposer_signature = header_signature(header)?;
|
||||
let correct_proposer = self.step_proposer(header.parent_hash(), step);
|
||||
if !verify_address(&correct_proposer, &proposer_signature, &header.bare_hash())? {
|
||||
trace!(target: "engine", "verify_block_unordered: bad proposer for step: {}", step);
|
||||
Err(EngineError::NotProposer(Mismatch { expected: correct_proposer, found: header.author().clone() }))?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
// Do not calculate difficulty for genesis blocks.
|
||||
if header.number() == 0 {
|
||||
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() })));
|
||||
}
|
||||
|
||||
let step = header_step(header)?;
|
||||
// Check if parent is from a previous step.
|
||||
if step == header_step(parent)? {
|
||||
let parent_step = header_step(parent)?;
|
||||
if step == parent_step {
|
||||
trace!(target: "engine", "Multiple blocks proposed for step {}.", step);
|
||||
self.validators.report_malicious(header.author());
|
||||
Err(EngineError::DoubleVote(header.author().clone()))?;
|
||||
@@ -323,6 +358,18 @@ impl Engine for AuthorityRound {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> result::Result<(), Error> {
|
||||
t.check_low_s()?;
|
||||
|
||||
if let Some(n) = t.network_id() {
|
||||
if header.number() >= self.eip155_transition && n != self.params().chain_id {
|
||||
return Err(TransactionError::InvalidNetworkId.into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_client(&self, client: Weak<Client>) {
|
||||
*self.client.write() = Some(client.clone());
|
||||
self.validators.register_contract(client);
|
||||
@@ -394,7 +441,7 @@ mod tests {
|
||||
let mut header: Header = Header::default();
|
||||
header.set_seal(vec![encode(&H520::default()).to_vec()]);
|
||||
|
||||
let verify_result = engine.verify_block_unordered(&header, None);
|
||||
let verify_result = engine.verify_block_family(&header, &Default::default(), None);
|
||||
assert!(verify_result.is_err());
|
||||
}
|
||||
|
||||
@@ -432,10 +479,14 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn proposer_switching() {
|
||||
let mut header: Header = Header::default();
|
||||
let tap = AccountProvider::transient_provider();
|
||||
let addr = tap.insert_account(Secret::from_slice(&"0".sha3()).unwrap(), "0").unwrap();
|
||||
|
||||
let mut parent_header: Header = Header::default();
|
||||
parent_header.set_seal(vec![encode(&0usize).to_vec()]);
|
||||
parent_header.set_gas_limit(U256::from_str("222222").unwrap());
|
||||
let mut header: Header = Header::default();
|
||||
header.set_number(1);
|
||||
header.set_gas_limit(U256::from_str("222222").unwrap());
|
||||
header.set_author(addr);
|
||||
|
||||
let engine = Spec::new_test_round().engine;
|
||||
@@ -444,17 +495,22 @@ mod tests {
|
||||
// Two validators.
|
||||
// Spec starts with step 2.
|
||||
header.set_seal(vec![encode(&2usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||
assert!(engine.verify_block_seal(&header).is_err());
|
||||
assert!(engine.verify_block_family(&header, &parent_header, None).is_err());
|
||||
header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||
assert!(engine.verify_block_seal(&header).is_ok());
|
||||
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_future_block() {
|
||||
let mut header: Header = Header::default();
|
||||
let tap = AccountProvider::transient_provider();
|
||||
let addr = tap.insert_account(Secret::from_slice(&"0".sha3()).unwrap(), "0").unwrap();
|
||||
|
||||
let mut parent_header: Header = Header::default();
|
||||
parent_header.set_seal(vec![encode(&0usize).to_vec()]);
|
||||
parent_header.set_gas_limit(U256::from_str("222222").unwrap());
|
||||
let mut header: Header = Header::default();
|
||||
header.set_number(1);
|
||||
header.set_gas_limit(U256::from_str("222222").unwrap());
|
||||
header.set_author(addr);
|
||||
|
||||
let engine = Spec::new_test_round().engine;
|
||||
@@ -463,8 +519,8 @@ mod tests {
|
||||
// Two validators.
|
||||
// Spec starts with step 2.
|
||||
header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||
assert!(engine.verify_block_seal(&header).is_ok());
|
||||
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
|
||||
header.set_seal(vec![encode(&5usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||
assert!(engine.verify_block_seal(&header).is_err());
|
||||
assert!(engine.verify_block_family(&header, &parent_header, None).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ pub struct BasicAuthority {
|
||||
gas_limit_bound_divisor: U256,
|
||||
builtins: BTreeMap<Address, Builtin>,
|
||||
signer: EngineSigner,
|
||||
validators: Box<ValidatorSet + Send + Sync>,
|
||||
validators: Box<ValidatorSet>,
|
||||
}
|
||||
|
||||
impl BasicAuthority {
|
||||
@@ -104,14 +104,14 @@ impl Engine for BasicAuthority {
|
||||
}
|
||||
|
||||
fn seals_internally(&self) -> Option<bool> {
|
||||
Some(self.validators.contains(&self.signer.address()))
|
||||
Some(self.signer.address() != Address::default())
|
||||
}
|
||||
|
||||
/// Attempt to seal the block internally.
|
||||
fn generate_seal(&self, block: &ExecutedBlock) -> Seal {
|
||||
let header = block.header();
|
||||
let author = header.author();
|
||||
if self.validators.contains(author) {
|
||||
if self.validators.contains(header.parent_hash(), author) {
|
||||
// account should be pernamently unlocked, otherwise sealing will fail
|
||||
if let Ok(signature) = self.signer.sign(header.bare_hash()) {
|
||||
return Seal::Regular(vec![::rlp::encode(&(&H520::from(signature) as &[u8])).to_vec()]);
|
||||
@@ -133,20 +133,20 @@ impl Engine for BasicAuthority {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
||||
use rlp::{UntrustedRlp, View};
|
||||
|
||||
// check the signature is legit.
|
||||
let sig = UntrustedRlp::new(&header.seal()[0]).as_val::<H520>()?;
|
||||
let signer = public_to_address(&recover(&sig.into(), &header.bare_hash())?);
|
||||
if !self.validators.contains(&signer) {
|
||||
return Err(BlockError::InvalidSeal)?;
|
||||
}
|
||||
fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
||||
// we should not calculate difficulty for genesis blocks
|
||||
use rlp::{UntrustedRlp, View};
|
||||
// Check if the signature belongs to a validator, can depend on parent state.
|
||||
let sig = UntrustedRlp::new(&header.seal()[0]).as_val::<H520>()?;
|
||||
let signer = public_to_address(&recover(&sig.into(), &header.bare_hash())?);
|
||||
if !self.validators.contains(header.parent_hash(), &signer) {
|
||||
return Err(BlockError::InvalidSeal)?;
|
||||
}
|
||||
|
||||
// Do not calculate difficulty for genesis blocks.
|
||||
if header.number() == 0 {
|
||||
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() })));
|
||||
}
|
||||
@@ -239,7 +239,7 @@ mod tests {
|
||||
let mut header: Header = Header::default();
|
||||
header.set_seal(vec![::rlp::encode(&H520::default()).to_vec()]);
|
||||
|
||||
let verify_result = engine.verify_block_unordered(&header, None);
|
||||
let verify_result = engine.verify_block_family(&header, &Default::default(), None);
|
||||
assert!(verify_result.is_err());
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ use account_provider::AccountProvider;
|
||||
use block::ExecutedBlock;
|
||||
use builtin::Builtin;
|
||||
use env_info::EnvInfo;
|
||||
use error::Error;
|
||||
use error::{Error, TransactionError};
|
||||
use spec::CommonParams;
|
||||
use evm::Schedule;
|
||||
use header::Header;
|
||||
@@ -157,6 +157,13 @@ pub trait Engine : Sync + Send {
|
||||
// TODO: consider including State in the params.
|
||||
fn verify_transaction_basic(&self, t: &UnverifiedTransaction, _header: &Header) -> Result<(), Error> {
|
||||
t.check_low_s()?;
|
||||
|
||||
if let Some(n) = t.network_id() {
|
||||
if n != self.params().chain_id {
|
||||
return Err(TransactionError::InvalidNetworkId.into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -166,7 +173,9 @@ pub trait Engine : Sync + Send {
|
||||
}
|
||||
|
||||
/// The network ID that transactions should be signed with.
|
||||
fn signing_network_id(&self, _env_info: &EnvInfo) -> Option<u64> { None }
|
||||
fn signing_network_id(&self, _env_info: &EnvInfo) -> Option<u64> {
|
||||
Some(self.params().chain_id)
|
||||
}
|
||||
|
||||
/// Verify the seal of a block. This is an auxilliary method that actually just calls other `verify_` methods
|
||||
/// to get the job done. By default it must pass `verify_basic` and `verify_block_unordered`. If more or fewer
|
||||
|
||||
@@ -95,8 +95,10 @@ pub struct Tendermint {
|
||||
last_lock: AtomicUsize,
|
||||
/// Bare hash of the proposed block, used for seal submission.
|
||||
proposal: RwLock<Option<H256>>,
|
||||
/// Hash of the proposal parent block.
|
||||
proposal_parent: RwLock<H256>,
|
||||
/// Set used to determine the current validators.
|
||||
validators: Box<ValidatorSet + Send + Sync>,
|
||||
validators: Box<ValidatorSet>,
|
||||
}
|
||||
|
||||
impl Tendermint {
|
||||
@@ -114,11 +116,12 @@ impl Tendermint {
|
||||
height: AtomicUsize::new(1),
|
||||
view: AtomicUsize::new(0),
|
||||
step: RwLock::new(Step::Propose),
|
||||
votes: VoteCollector::default(),
|
||||
votes: Default::default(),
|
||||
signer: Default::default(),
|
||||
lock_change: RwLock::new(None),
|
||||
last_lock: AtomicUsize::new(0),
|
||||
proposal: RwLock::new(None),
|
||||
proposal_parent: Default::default(),
|
||||
validators: new_validator_set(our_params.validators),
|
||||
});
|
||||
let handler = TransitionHandler::new(Arc::downgrade(&engine) as Weak<Engine>, Box::new(our_params.timeouts));
|
||||
@@ -232,7 +235,7 @@ impl Tendermint {
|
||||
let height = self.height.load(AtomicOrdering::SeqCst);
|
||||
if let Some(block_hash) = *self.proposal.read() {
|
||||
// Generate seal and remove old votes.
|
||||
if self.is_signer_proposer() {
|
||||
if self.is_signer_proposer(&*self.proposal_parent.read()) {
|
||||
let proposal_step = VoteStep::new(height, view, Step::Propose);
|
||||
let precommit_step = VoteStep::new(proposal_step.height, proposal_step.view, Step::Precommit);
|
||||
if let Some(seal) = self.votes.seal_signatures(proposal_step, precommit_step, &block_hash) {
|
||||
@@ -254,23 +257,23 @@ impl Tendermint {
|
||||
}
|
||||
|
||||
fn is_authority(&self, address: &Address) -> bool {
|
||||
self.validators.contains(address)
|
||||
self.validators.contains(&*self.proposal_parent.read(), address)
|
||||
}
|
||||
|
||||
fn is_above_threshold(&self, n: usize) -> bool {
|
||||
n > self.validators.count() * 2/3
|
||||
n > self.validators.count(&*self.proposal_parent.read()) * 2/3
|
||||
}
|
||||
|
||||
/// Find the designated for the given view.
|
||||
fn view_proposer(&self, height: Height, view: View) -> Address {
|
||||
fn view_proposer(&self, bh: &H256, height: Height, view: View) -> Address {
|
||||
let proposer_nonce = height + view;
|
||||
trace!(target: "engine", "Proposer nonce: {}", proposer_nonce);
|
||||
self.validators.get(proposer_nonce)
|
||||
self.validators.get(bh, proposer_nonce)
|
||||
}
|
||||
|
||||
/// Check if address is a proposer for given view.
|
||||
fn is_view_proposer(&self, height: Height, view: View, address: &Address) -> Result<(), EngineError> {
|
||||
let proposer = self.view_proposer(height, view);
|
||||
fn is_view_proposer(&self, bh: &H256, height: Height, view: View, address: &Address) -> Result<(), EngineError> {
|
||||
let proposer = self.view_proposer(bh, height, view);
|
||||
if proposer == *address {
|
||||
Ok(())
|
||||
} else {
|
||||
@@ -279,8 +282,8 @@ impl Tendermint {
|
||||
}
|
||||
|
||||
/// Check if current signer is the current proposer.
|
||||
fn is_signer_proposer(&self) -> bool {
|
||||
let proposer = self.view_proposer(self.height.load(AtomicOrdering::SeqCst), self.view.load(AtomicOrdering::SeqCst));
|
||||
fn is_signer_proposer(&self, bh: &H256) -> bool {
|
||||
let proposer = self.view_proposer(bh, self.height.load(AtomicOrdering::SeqCst), self.view.load(AtomicOrdering::SeqCst));
|
||||
self.signer.is_address(&proposer)
|
||||
}
|
||||
|
||||
@@ -419,7 +422,7 @@ impl Engine for Tendermint {
|
||||
|
||||
/// Should this node participate.
|
||||
fn seals_internally(&self) -> Option<bool> {
|
||||
Some(self.is_authority(&self.signer.address()))
|
||||
Some(self.signer.address() != Address::default())
|
||||
}
|
||||
|
||||
/// Attempt to seal generate a proposal seal.
|
||||
@@ -427,7 +430,7 @@ impl Engine for Tendermint {
|
||||
let header = block.header();
|
||||
let author = header.author();
|
||||
// Only proposer can generate seal if None was generated.
|
||||
if !self.is_signer_proposer() || self.proposal.read().is_some() {
|
||||
if !self.is_signer_proposer(header.parent_hash()) || self.proposal.read().is_some() {
|
||||
return Seal::None;
|
||||
}
|
||||
|
||||
@@ -441,6 +444,7 @@ impl Engine for Tendermint {
|
||||
self.votes.vote(ConsensusMessage::new(signature, height, view, Step::Propose, bh), author);
|
||||
// Remember proposal for later seal submission.
|
||||
*self.proposal.write() = bh;
|
||||
*self.proposal_parent.write() = header.parent_hash().clone();
|
||||
Seal::Proposal(vec![
|
||||
::rlp::encode(&view).to_vec(),
|
||||
::rlp::encode(&signature).to_vec(),
|
||||
@@ -505,7 +509,12 @@ impl Engine for Tendermint {
|
||||
|
||||
}
|
||||
|
||||
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Verify validators and gas limit.
|
||||
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
let proposal = ConsensusMessage::new_proposal(header)?;
|
||||
let proposer = proposal.verify()?;
|
||||
if !self.is_authority(&proposer) {
|
||||
@@ -522,7 +531,7 @@ impl Engine for Tendermint {
|
||||
Some(a) => a,
|
||||
None => public_to_address(&recover(&precommit.signature.into(), &precommit_hash)?),
|
||||
};
|
||||
if !self.validators.contains(&address) {
|
||||
if !self.validators.contains(header.parent_hash(), &address) {
|
||||
Err(EngineError::NotAuthorized(address.to_owned()))?
|
||||
}
|
||||
|
||||
@@ -545,12 +554,9 @@ impl Engine for Tendermint {
|
||||
found: signatures_len
|
||||
}))?;
|
||||
}
|
||||
self.is_view_proposer(proposal.vote_step.height, proposal.vote_step.view, &proposer)?;
|
||||
self.is_view_proposer(header.parent_hash(), proposal.vote_step.height, proposal.vote_step.view, &proposer)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
if header.number() == 0 {
|
||||
Err(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))?;
|
||||
}
|
||||
@@ -595,6 +601,7 @@ impl Engine for Tendermint {
|
||||
debug!(target: "engine", "Received a new proposal {:?} from {}.", proposal.vote_step, proposer);
|
||||
if self.is_view(&proposal) {
|
||||
*self.proposal.write() = proposal.block_hash.clone();
|
||||
*self.proposal_parent.write() = header.parent_hash().clone();
|
||||
}
|
||||
self.votes.vote(proposal, &proposer);
|
||||
true
|
||||
@@ -607,7 +614,7 @@ impl Engine for Tendermint {
|
||||
trace!(target: "engine", "Propose timeout.");
|
||||
if self.proposal.read().is_none() {
|
||||
// Report the proposer if no proposal was received.
|
||||
let current_proposer = self.view_proposer(self.height.load(AtomicOrdering::SeqCst), self.view.load(AtomicOrdering::SeqCst));
|
||||
let current_proposer = self.view_proposer(&*self.proposal_parent.read(), self.height.load(AtomicOrdering::SeqCst), self.view.load(AtomicOrdering::SeqCst));
|
||||
self.validators.report_benign(¤t_proposer);
|
||||
}
|
||||
Step::Prevote
|
||||
@@ -765,20 +772,25 @@ mod tests {
|
||||
let (spec, tap) = setup();
|
||||
let engine = spec.engine;
|
||||
|
||||
let mut header = Header::default();
|
||||
let validator = insert_and_unlock(&tap, "0");
|
||||
header.set_author(validator);
|
||||
let seal = proposal_seal(&tap, &header, 0);
|
||||
header.set_seal(seal);
|
||||
// Good proposer.
|
||||
assert!(engine.verify_block_unordered(&header.clone(), None).is_ok());
|
||||
let mut parent_header: Header = Header::default();
|
||||
parent_header.set_gas_limit(U256::from_str("222222").unwrap());
|
||||
|
||||
let mut header = Header::default();
|
||||
header.set_number(1);
|
||||
header.set_gas_limit(U256::from_str("222222").unwrap());
|
||||
let validator = insert_and_unlock(&tap, "1");
|
||||
header.set_author(validator);
|
||||
let seal = proposal_seal(&tap, &header, 0);
|
||||
header.set_seal(seal);
|
||||
// Good proposer.
|
||||
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
|
||||
|
||||
let validator = insert_and_unlock(&tap, "0");
|
||||
header.set_author(validator);
|
||||
let seal = proposal_seal(&tap, &header, 0);
|
||||
header.set_seal(seal);
|
||||
// Bad proposer.
|
||||
match engine.verify_block_unordered(&header, None) {
|
||||
match engine.verify_block_family(&header, &parent_header, None) {
|
||||
Err(Error::Engine(EngineError::NotProposer(_))) => {},
|
||||
_ => panic!(),
|
||||
}
|
||||
@@ -788,7 +800,7 @@ mod tests {
|
||||
let seal = proposal_seal(&tap, &header, 0);
|
||||
header.set_seal(seal);
|
||||
// Not authority.
|
||||
match engine.verify_block_unordered(&header, None) {
|
||||
match engine.verify_block_family(&header, &parent_header, None) {
|
||||
Err(Error::Engine(EngineError::NotAuthorized(_))) => {},
|
||||
_ => panic!(),
|
||||
};
|
||||
@@ -800,19 +812,24 @@ mod tests {
|
||||
let (spec, tap) = setup();
|
||||
let engine = spec.engine;
|
||||
|
||||
let mut parent_header: Header = Header::default();
|
||||
parent_header.set_gas_limit(U256::from_str("222222").unwrap());
|
||||
|
||||
let mut header = Header::default();
|
||||
header.set_number(2);
|
||||
header.set_gas_limit(U256::from_str("222222").unwrap());
|
||||
let proposer = insert_and_unlock(&tap, "1");
|
||||
header.set_author(proposer);
|
||||
let mut seal = proposal_seal(&tap, &header, 0);
|
||||
|
||||
let vote_info = message_info_rlp(&VoteStep::new(0, 0, Step::Precommit), Some(header.bare_hash()));
|
||||
let vote_info = message_info_rlp(&VoteStep::new(2, 0, Step::Precommit), Some(header.bare_hash()));
|
||||
let signature1 = tap.sign(proposer, None, vote_info.sha3()).unwrap();
|
||||
|
||||
seal[2] = ::rlp::encode(&vec![H520::from(signature1.clone())]).to_vec();
|
||||
header.set_seal(seal.clone());
|
||||
|
||||
// One good signature is not enough.
|
||||
match engine.verify_block_unordered(&header, None) {
|
||||
match engine.verify_block_family(&header, &parent_header, None) {
|
||||
Err(Error::Engine(EngineError::BadSealFieldSize(_))) => {},
|
||||
_ => panic!(),
|
||||
}
|
||||
@@ -823,7 +840,7 @@ mod tests {
|
||||
seal[2] = ::rlp::encode(&vec![H520::from(signature1.clone()), H520::from(signature0.clone())]).to_vec();
|
||||
header.set_seal(seal.clone());
|
||||
|
||||
assert!(engine.verify_block_unordered(&header, None).is_ok());
|
||||
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
|
||||
|
||||
let bad_voter = insert_and_unlock(&tap, "101");
|
||||
let bad_signature = tap.sign(bad_voter, None, vote_info.sha3()).unwrap();
|
||||
@@ -832,7 +849,7 @@ mod tests {
|
||||
header.set_seal(seal);
|
||||
|
||||
// One good and one bad signature.
|
||||
match engine.verify_block_unordered(&header, None) {
|
||||
match engine.verify_block_family(&header, &parent_header, None) {
|
||||
Err(Error::Engine(EngineError::NotAuthorized(_))) => {},
|
||||
_ => panic!(),
|
||||
};
|
||||
|
||||
@@ -26,30 +26,30 @@ use super::safe_contract::ValidatorSafeContract;
|
||||
/// The validator contract should have the following interface:
|
||||
/// [{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}]
|
||||
pub struct ValidatorContract {
|
||||
validators: Arc<ValidatorSafeContract>,
|
||||
validators: ValidatorSafeContract,
|
||||
provider: RwLock<Option<provider::Contract>>,
|
||||
}
|
||||
|
||||
impl ValidatorContract {
|
||||
pub fn new(contract_address: Address) -> Self {
|
||||
ValidatorContract {
|
||||
validators: Arc::new(ValidatorSafeContract::new(contract_address)),
|
||||
validators: ValidatorSafeContract::new(contract_address),
|
||||
provider: RwLock::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValidatorSet for Arc<ValidatorContract> {
|
||||
fn contains(&self, address: &Address) -> bool {
|
||||
self.validators.contains(address)
|
||||
impl ValidatorSet for ValidatorContract {
|
||||
fn contains(&self, bh: &H256, address: &Address) -> bool {
|
||||
self.validators.contains(bh, address)
|
||||
}
|
||||
|
||||
fn get(&self, nonce: usize) -> Address {
|
||||
self.validators.get(nonce)
|
||||
fn get(&self, bh: &H256, nonce: usize) -> Address {
|
||||
self.validators.get(bh, nonce)
|
||||
}
|
||||
|
||||
fn count(&self) -> usize {
|
||||
self.validators.count()
|
||||
fn count(&self, bh: &H256) -> usize {
|
||||
self.validators.count(bh)
|
||||
}
|
||||
|
||||
fn report_malicious(&self, address: &Address) {
|
||||
@@ -144,6 +144,7 @@ mod tests {
|
||||
use header::Header;
|
||||
use account_provider::AccountProvider;
|
||||
use miner::MinerService;
|
||||
use types::ids::BlockId;
|
||||
use client::BlockChainClient;
|
||||
use tests::helpers::generate_dummy_client_with_spec_and_accounts;
|
||||
use super::super::ValidatorSet;
|
||||
@@ -154,8 +155,9 @@ mod tests {
|
||||
let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_contract, None);
|
||||
let vc = Arc::new(ValidatorContract::new(Address::from_str("0000000000000000000000000000000000000005").unwrap()));
|
||||
vc.register_contract(Arc::downgrade(&client));
|
||||
assert!(vc.contains(&Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap()));
|
||||
assert!(vc.contains(&Address::from_str("82a978b3f5962a5b0957d9ee9eef472ee55b42f1").unwrap()));
|
||||
let last_hash = client.best_block_header().hash();
|
||||
assert!(vc.contains(&last_hash, &Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap()));
|
||||
assert!(vc.contains(&last_hash, &Address::from_str("82a978b3f5962a5b0957d9ee9eef472ee55b42f1").unwrap()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -171,18 +173,21 @@ mod tests {
|
||||
|
||||
client.miner().set_engine_signer(v1, "".into()).unwrap();
|
||||
let mut header = Header::default();
|
||||
let seal = encode(&vec!(5u8)).to_vec();
|
||||
header.set_seal(vec!(seal));
|
||||
let seal = vec![encode(&5u8).to_vec(), encode(&(&H520::default() as &[u8])).to_vec()];
|
||||
header.set_seal(seal);
|
||||
header.set_author(v1);
|
||||
header.set_number(1);
|
||||
header.set_number(2);
|
||||
header.set_parent_hash(client.chain_info().best_block_hash);
|
||||
|
||||
// `reportBenign` when the designated proposer releases block from the future (bad clock).
|
||||
assert!(client.engine().verify_block_unordered(&header, None).is_err());
|
||||
assert!(client.engine().verify_block_family(&header, &header, None).is_err());
|
||||
// Seal a block.
|
||||
client.engine().step();
|
||||
assert_eq!(client.chain_info().best_block_number, 1);
|
||||
// Check if the unresponsive validator is `disliked`.
|
||||
assert_eq!(client.call_contract(validator_contract, "d8f2e0bf".from_hex().unwrap()).unwrap().to_hex(), "0000000000000000000000007d577a597b2742b498cb5cf0c26cdcd726d39e6e");
|
||||
assert_eq!(client.call_contract(BlockId::Latest, validator_contract, "d8f2e0bf".from_hex().unwrap()).unwrap().to_hex(), "0000000000000000000000007d577a597b2742b498cb5cf0c26cdcd726d39e6e");
|
||||
// Simulate a misbehaving validator by handling a double proposal.
|
||||
let header = client.best_block_header().decode();
|
||||
assert!(client.engine().verify_block_family(&header, &header, None).is_err());
|
||||
// Seal a block.
|
||||
client.engine().step();
|
||||
|
||||
@@ -19,31 +19,36 @@
|
||||
mod simple_list;
|
||||
mod safe_contract;
|
||||
mod contract;
|
||||
mod multi;
|
||||
|
||||
use std::sync::Weak;
|
||||
use util::{Address, Arc};
|
||||
use util::{Address, H256};
|
||||
use ethjson::spec::ValidatorSet as ValidatorSpec;
|
||||
use client::Client;
|
||||
use self::simple_list::SimpleList;
|
||||
use self::contract::ValidatorContract;
|
||||
use self::safe_contract::ValidatorSafeContract;
|
||||
use self::multi::Multi;
|
||||
|
||||
/// Creates a validator set from spec.
|
||||
pub fn new_validator_set(spec: ValidatorSpec) -> Box<ValidatorSet + Send + Sync> {
|
||||
pub fn new_validator_set(spec: ValidatorSpec) -> Box<ValidatorSet> {
|
||||
match spec {
|
||||
ValidatorSpec::List(list) => Box::new(SimpleList::new(list.into_iter().map(Into::into).collect())),
|
||||
ValidatorSpec::SafeContract(address) => Box::new(Arc::new(ValidatorSafeContract::new(address.into()))),
|
||||
ValidatorSpec::Contract(address) => Box::new(Arc::new(ValidatorContract::new(address.into()))),
|
||||
ValidatorSpec::SafeContract(address) => Box::new(ValidatorSafeContract::new(address.into())),
|
||||
ValidatorSpec::Contract(address) => Box::new(ValidatorContract::new(address.into())),
|
||||
ValidatorSpec::Multi(sequence) => Box::new(
|
||||
Multi::new(sequence.into_iter().map(|(block, set)| (block.into(), new_validator_set(set))).collect())
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ValidatorSet {
|
||||
pub trait ValidatorSet: Send + Sync {
|
||||
/// Checks if a given address is a validator.
|
||||
fn contains(&self, address: &Address) -> bool;
|
||||
fn contains(&self, parent_block_hash: &H256, address: &Address) -> bool;
|
||||
/// Draws an validator nonce modulo number of validators.
|
||||
fn get(&self, nonce: usize) -> Address;
|
||||
fn get(&self, parent_block_hash: &H256, nonce: usize) -> Address;
|
||||
/// Returns the current number of validators.
|
||||
fn count(&self) -> usize;
|
||||
fn count(&self, parent_block_hash: &H256) -> usize;
|
||||
/// Notifies about malicious behaviour.
|
||||
fn report_malicious(&self, _validator: &Address) {}
|
||||
/// Notifies about benign misbehaviour.
|
||||
|
||||
158
ethcore/src/engines/validator_set/multi.rs
Normal file
158
ethcore/src/engines/validator_set/multi.rs
Normal file
@@ -0,0 +1,158 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/// Validator set changing at fork blocks.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Weak;
|
||||
use util::{H256, Address, RwLock};
|
||||
use ids::BlockId;
|
||||
use header::BlockNumber;
|
||||
use client::{Client, BlockChainClient};
|
||||
use super::ValidatorSet;
|
||||
|
||||
type BlockNumberLookup = Box<Fn(&H256) -> Result<BlockNumber, String> + Send + Sync + 'static>;
|
||||
|
||||
pub struct Multi {
|
||||
sets: BTreeMap<BlockNumber, Box<ValidatorSet>>,
|
||||
block_number: RwLock<BlockNumberLookup>,
|
||||
}
|
||||
|
||||
impl Multi {
|
||||
pub fn new(set_map: BTreeMap<BlockNumber, Box<ValidatorSet>>) -> Self {
|
||||
assert!(set_map.get(&0u64).is_some(), "ValidatorSet has to be specified from block 0.");
|
||||
Multi {
|
||||
sets: set_map,
|
||||
block_number: RwLock::new(Box::new(move |_| Err("No client!".into()))),
|
||||
}
|
||||
}
|
||||
|
||||
fn correct_set(&self, bh: &H256) -> Option<&Box<ValidatorSet>> {
|
||||
match self
|
||||
.block_number
|
||||
.read()(bh)
|
||||
.map(|parent_block| self
|
||||
.sets
|
||||
.iter()
|
||||
.rev()
|
||||
.find(|&(block, _)| *block <= parent_block + 1)
|
||||
.expect("constructor validation ensures that there is at least one validator set for block 0;
|
||||
block 0 is less than any uint;
|
||||
qed")
|
||||
) {
|
||||
Ok((block, set)) => {
|
||||
trace!(target: "engine", "Multi ValidatorSet retrieved for block {}.", block);
|
||||
Some(set)
|
||||
},
|
||||
Err(e) => {
|
||||
debug!(target: "engine", "ValidatorSet could not be recovered: {}", e);
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValidatorSet for Multi {
|
||||
fn contains(&self, bh: &H256, address: &Address) -> bool {
|
||||
self.correct_set(bh).map_or(false, |set| set.contains(bh, address))
|
||||
}
|
||||
|
||||
fn get(&self, bh: &H256, nonce: usize) -> Address {
|
||||
self.correct_set(bh).map_or_else(Default::default, |set| set.get(bh, nonce))
|
||||
}
|
||||
|
||||
fn count(&self, bh: &H256) -> usize {
|
||||
self.correct_set(bh).map_or_else(usize::max_value, |set| set.count(bh))
|
||||
}
|
||||
|
||||
fn report_malicious(&self, validator: &Address) {
|
||||
for set in self.sets.values() {
|
||||
set.report_malicious(validator);
|
||||
}
|
||||
}
|
||||
|
||||
fn report_benign(&self, validator: &Address) {
|
||||
for set in self.sets.values() {
|
||||
set.report_benign(validator);
|
||||
}
|
||||
}
|
||||
|
||||
fn register_contract(&self, client: Weak<Client>) {
|
||||
for set in self.sets.values() {
|
||||
set.register_contract(client.clone());
|
||||
}
|
||||
*self.block_number.write() = Box::new(move |hash| client
|
||||
.upgrade()
|
||||
.ok_or("No client!".into())
|
||||
.and_then(|c| c.block_number(BlockId::Hash(*hash)).ok_or("Unknown block".into())));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use util::*;
|
||||
use types::ids::BlockId;
|
||||
use spec::Spec;
|
||||
use account_provider::AccountProvider;
|
||||
use client::{BlockChainClient, EngineClient};
|
||||
use ethkey::Secret;
|
||||
use miner::MinerService;
|
||||
use tests::helpers::{generate_dummy_client_with_spec_and_accounts, generate_dummy_client_with_spec_and_data};
|
||||
|
||||
#[test]
|
||||
fn uses_current_set() {
|
||||
::env_logger::init().unwrap();
|
||||
let tap = Arc::new(AccountProvider::transient_provider());
|
||||
let s0 = Secret::from_slice(&"0".sha3()).unwrap();
|
||||
let v0 = tap.insert_account(s0.clone(), "").unwrap();
|
||||
let v1 = tap.insert_account(Secret::from_slice(&"1".sha3()).unwrap(), "").unwrap();
|
||||
let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_multi, Some(tap));
|
||||
client.engine().register_client(Arc::downgrade(&client));
|
||||
|
||||
// Make sure txs go through.
|
||||
client.miner().set_gas_floor_target(1_000_000.into());
|
||||
|
||||
// Wrong signer for the first block.
|
||||
client.miner().set_engine_signer(v1, "".into()).unwrap();
|
||||
client.transact_contract(Default::default(), Default::default()).unwrap();
|
||||
client.update_sealing();
|
||||
assert_eq!(client.chain_info().best_block_number, 0);
|
||||
// Right signer for the first block.
|
||||
client.miner().set_engine_signer(v0, "".into()).unwrap();
|
||||
client.update_sealing();
|
||||
assert_eq!(client.chain_info().best_block_number, 1);
|
||||
// This time v0 is wrong.
|
||||
client.transact_contract(Default::default(), Default::default()).unwrap();
|
||||
client.update_sealing();
|
||||
assert_eq!(client.chain_info().best_block_number, 1);
|
||||
client.miner().set_engine_signer(v1, "".into()).unwrap();
|
||||
client.update_sealing();
|
||||
assert_eq!(client.chain_info().best_block_number, 2);
|
||||
// v1 is still good.
|
||||
client.transact_contract(Default::default(), Default::default()).unwrap();
|
||||
client.update_sealing();
|
||||
assert_eq!(client.chain_info().best_block_number, 3);
|
||||
|
||||
// Check syncing.
|
||||
let sync_client = generate_dummy_client_with_spec_and_data(Spec::new_validator_multi, 0, 0, &[]);
|
||||
sync_client.engine().register_client(Arc::downgrade(&sync_client));
|
||||
for i in 1..4 {
|
||||
sync_client.import_block(client.block(BlockId::Number(i)).unwrap().into_inner()).unwrap();
|
||||
}
|
||||
sync_client.flush_queue();
|
||||
assert_eq!(sync_client.chain_info().best_block_number, 3);
|
||||
}
|
||||
}
|
||||
@@ -17,17 +17,23 @@
|
||||
/// Validator set maintained in a contract, updated using `getValidators` method.
|
||||
|
||||
use std::sync::Weak;
|
||||
use ethabi;
|
||||
use util::*;
|
||||
use util::cache::MemoryLruCache;
|
||||
use types::ids::BlockId;
|
||||
use client::{Client, BlockChainClient};
|
||||
use client::chain_notify::ChainNotify;
|
||||
use super::ValidatorSet;
|
||||
use super::simple_list::SimpleList;
|
||||
|
||||
const MEMOIZE_CAPACITY: usize = 500;
|
||||
const CONTRACT_INTERFACE: &'static [u8] = b"[{\"constant\":true,\"inputs\":[],\"name\":\"getValidators\",\"outputs\":[{\"name\":\"\",\"type\":\"address[]\"}],\"payable\":false,\"type\":\"function\"}]";
|
||||
const GET_VALIDATORS: &'static str = "getValidators";
|
||||
|
||||
/// The validator contract should have the following interface:
|
||||
/// [{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}]
|
||||
pub struct ValidatorSafeContract {
|
||||
pub address: Address,
|
||||
validators: RwLock<SimpleList>,
|
||||
validators: RwLock<MemoryLruCache<H256, SimpleList>>,
|
||||
provider: RwLock<Option<provider::Contract>>,
|
||||
}
|
||||
|
||||
@@ -35,102 +41,127 @@ impl ValidatorSafeContract {
|
||||
pub fn new(contract_address: Address) -> Self {
|
||||
ValidatorSafeContract {
|
||||
address: contract_address,
|
||||
validators: Default::default(),
|
||||
validators: RwLock::new(MemoryLruCache::new(MEMOIZE_CAPACITY)),
|
||||
provider: RwLock::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Queries the state and updates the set of validators.
|
||||
pub fn update(&self) {
|
||||
/// Queries the state and gets the set of validators.
|
||||
fn get_list(&self, block_hash: H256) -> Option<SimpleList> {
|
||||
if let Some(ref provider) = *self.provider.read() {
|
||||
match provider.get_validators() {
|
||||
match provider.get_validators(BlockId::Hash(block_hash)) {
|
||||
Ok(new) => {
|
||||
debug!(target: "engine", "Set of validators obtained: {:?}", new);
|
||||
*self.validators.write() = SimpleList::new(new);
|
||||
Some(SimpleList::new(new))
|
||||
},
|
||||
Err(s) => {
|
||||
debug!(target: "engine", "Set of validators could not be updated: {}", s);
|
||||
None
|
||||
},
|
||||
Err(s) => warn!(target: "engine", "Set of validators could not be updated: {}", s),
|
||||
}
|
||||
} else {
|
||||
warn!(target: "engine", "Set of validators could not be updated: no provider contract.")
|
||||
warn!(target: "engine", "Set of validators could not be updated: no provider contract.");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks validators on every block.
|
||||
impl ChainNotify for ValidatorSafeContract {
|
||||
fn new_blocks(
|
||||
&self,
|
||||
_: Vec<H256>,
|
||||
_: Vec<H256>,
|
||||
enacted: Vec<H256>,
|
||||
_: Vec<H256>,
|
||||
_: Vec<H256>,
|
||||
_: Vec<Bytes>,
|
||||
_duration: u64) {
|
||||
if !enacted.is_empty() {
|
||||
self.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValidatorSet for Arc<ValidatorSafeContract> {
|
||||
fn contains(&self, address: &Address) -> bool {
|
||||
self.validators.read().contains(address)
|
||||
impl ValidatorSet for ValidatorSafeContract {
|
||||
fn contains(&self, block_hash: &H256, address: &Address) -> bool {
|
||||
let mut guard = self.validators.write();
|
||||
let maybe_existing = guard
|
||||
.get_mut(block_hash)
|
||||
.map(|list| list.contains(block_hash, address));
|
||||
maybe_existing
|
||||
.unwrap_or_else(|| self
|
||||
.get_list(block_hash.clone())
|
||||
.map_or(false, |list| {
|
||||
let contains = list.contains(block_hash, address);
|
||||
guard.insert(block_hash.clone(), list);
|
||||
contains
|
||||
}))
|
||||
}
|
||||
|
||||
fn get(&self, nonce: usize) -> Address {
|
||||
self.validators.read().get(nonce)
|
||||
fn get(&self, block_hash: &H256, nonce: usize) -> Address {
|
||||
let mut guard = self.validators.write();
|
||||
let maybe_existing = guard
|
||||
.get_mut(block_hash)
|
||||
.map(|list| list.get(block_hash, nonce));
|
||||
maybe_existing
|
||||
.unwrap_or_else(|| self
|
||||
.get_list(block_hash.clone())
|
||||
.map_or_else(Default::default, |list| {
|
||||
let address = list.get(block_hash, nonce);
|
||||
guard.insert(block_hash.clone(), list);
|
||||
address
|
||||
}))
|
||||
}
|
||||
|
||||
fn count(&self) -> usize {
|
||||
self.validators.read().count()
|
||||
fn count(&self, block_hash: &H256) -> usize {
|
||||
let mut guard = self.validators.write();
|
||||
let maybe_existing = guard
|
||||
.get_mut(block_hash)
|
||||
.map(|list| list.count(block_hash));
|
||||
maybe_existing
|
||||
.unwrap_or_else(|| self
|
||||
.get_list(block_hash.clone())
|
||||
.map_or_else(usize::max_value, |list| {
|
||||
let address = list.count(block_hash);
|
||||
guard.insert(block_hash.clone(), list);
|
||||
address
|
||||
}))
|
||||
}
|
||||
|
||||
fn register_contract(&self, client: Weak<Client>) {
|
||||
if let Some(c) = client.upgrade() {
|
||||
c.add_notify(self.clone());
|
||||
}
|
||||
{
|
||||
*self.provider.write() = Some(provider::Contract::new(self.address, move |a, d| client.upgrade().ok_or("No client!".into()).and_then(|c| c.call_contract(a, d))));
|
||||
}
|
||||
self.update();
|
||||
trace!(target: "engine", "Setting up contract caller.");
|
||||
let contract = ethabi::Contract::new(ethabi::Interface::load(CONTRACT_INTERFACE).expect("JSON interface is valid; qed"));
|
||||
let call = contract.function(GET_VALIDATORS.into()).expect("Method name is valid; qed");
|
||||
let data = call.encode_call(vec![]).expect("get_validators does not take any arguments; qed");
|
||||
let contract_address = self.address.clone();
|
||||
let do_call = move |id| client
|
||||
.upgrade()
|
||||
.ok_or("No client!".into())
|
||||
.and_then(|c| c.call_contract(id, contract_address.clone(), data.clone()))
|
||||
.map(|raw_output| call.decode_output(raw_output).expect("ethabi is correct; qed"));
|
||||
*self.provider.write() = Some(provider::Contract::new(do_call));
|
||||
}
|
||||
}
|
||||
|
||||
mod provider {
|
||||
// Autogenerated from JSON contract definition using Rust contract convertor.
|
||||
#![allow(unused_imports)]
|
||||
use std::string::String;
|
||||
use std::result::Result;
|
||||
use std::fmt;
|
||||
use {util, ethabi};
|
||||
use util::{FixedHash, Uint};
|
||||
use types::ids::BlockId;
|
||||
|
||||
pub struct Contract {
|
||||
contract: ethabi::Contract,
|
||||
address: util::Address,
|
||||
do_call: Box<Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send + Sync + 'static>,
|
||||
do_call: Box<Fn(BlockId) -> Result<Vec<ethabi::Token>, String> + Send + Sync + 'static>,
|
||||
}
|
||||
|
||||
impl Contract {
|
||||
pub fn new<F>(address: util::Address, do_call: F) -> Self where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send + Sync + 'static {
|
||||
pub fn new<F>(do_call: F) -> Self where F: Fn(BlockId) -> Result<Vec<ethabi::Token>, String> + Send + Sync + 'static {
|
||||
Contract {
|
||||
contract: ethabi::Contract::new(ethabi::Interface::load(b"[{\"constant\":true,\"inputs\":[],\"name\":\"getValidators\",\"outputs\":[{\"name\":\"\",\"type\":\"address[]\"}],\"payable\":false,\"type\":\"function\"}]").expect("JSON is autogenerated; qed")),
|
||||
address: address,
|
||||
do_call: Box::new(do_call),
|
||||
}
|
||||
}
|
||||
fn as_string<T: fmt::Debug>(e: T) -> String { format!("{:?}", e) }
|
||||
|
||||
/// Auto-generated from: `{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}`
|
||||
#[allow(dead_code)]
|
||||
pub fn get_validators(&self) -> Result<Vec<util::Address>, String> {
|
||||
let call = self.contract.function("getValidators".into()).map_err(Self::as_string)?;
|
||||
let data = call.encode_call(
|
||||
vec![]
|
||||
).map_err(Self::as_string)?;
|
||||
let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_array().and_then(|v| v.into_iter().map(|a| a.to_address()).collect::<Option<Vec<[u8; 20]>>>()).ok_or("Invalid type returned")?; r.into_iter().map(|a| util::Address::from(a)).collect::<Vec<_>>() }))
|
||||
|
||||
/// Gets validators from contract with interface: `{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}`
|
||||
pub fn get_validators(&self, id: BlockId) -> Result<Vec<util::Address>, String> {
|
||||
Ok((self.do_call)(id)?
|
||||
.into_iter()
|
||||
.rev()
|
||||
.collect::<Vec<_>>()
|
||||
.pop()
|
||||
.expect("get_validators returns one argument; qed")
|
||||
.to_array()
|
||||
.and_then(|v| v
|
||||
.into_iter()
|
||||
.map(|a| a.to_address())
|
||||
.collect::<Option<Vec<[u8; 20]>>>())
|
||||
.expect("get_validators returns a list of addresses; qed")
|
||||
.into_iter()
|
||||
.map(util::Address::from)
|
||||
.collect::<Vec<_>>()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,13 +169,14 @@ mod provider {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use util::*;
|
||||
use types::ids::BlockId;
|
||||
use spec::Spec;
|
||||
use account_provider::AccountProvider;
|
||||
use transaction::{Transaction, Action};
|
||||
use client::{BlockChainClient, EngineClient};
|
||||
use ethkey::Secret;
|
||||
use miner::MinerService;
|
||||
use tests::helpers::generate_dummy_client_with_spec_and_accounts;
|
||||
use tests::helpers::{generate_dummy_client_with_spec_and_accounts, generate_dummy_client_with_spec_and_data};
|
||||
use super::super::ValidatorSet;
|
||||
use super::ValidatorSafeContract;
|
||||
|
||||
@@ -153,12 +185,13 @@ mod tests {
|
||||
let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_safe_contract, None);
|
||||
let vc = Arc::new(ValidatorSafeContract::new(Address::from_str("0000000000000000000000000000000000000005").unwrap()));
|
||||
vc.register_contract(Arc::downgrade(&client));
|
||||
assert!(vc.contains(&Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap()));
|
||||
assert!(vc.contains(&Address::from_str("82a978b3f5962a5b0957d9ee9eef472ee55b42f1").unwrap()));
|
||||
let last_hash = client.best_block_header().hash();
|
||||
assert!(vc.contains(&last_hash, &Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap()));
|
||||
assert!(vc.contains(&last_hash, &Address::from_str("82a978b3f5962a5b0957d9ee9eef472ee55b42f1").unwrap()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn updates_validators() {
|
||||
fn knows_validators() {
|
||||
let tap = Arc::new(AccountProvider::transient_provider());
|
||||
let s0 = Secret::from_slice(&"1".sha3()).unwrap();
|
||||
let v0 = tap.insert_account(s0.clone(), "").unwrap();
|
||||
@@ -212,5 +245,14 @@ mod tests {
|
||||
client.update_sealing();
|
||||
// Able to seal again.
|
||||
assert_eq!(client.chain_info().best_block_number, 3);
|
||||
|
||||
// Check syncing.
|
||||
let sync_client = generate_dummy_client_with_spec_and_data(Spec::new_validator_safe_contract, 0, 0, &[]);
|
||||
sync_client.engine().register_client(Arc::downgrade(&sync_client));
|
||||
for i in 1..4 {
|
||||
sync_client.import_block(client.block(BlockId::Number(i)).unwrap().into_inner()).unwrap();
|
||||
}
|
||||
sync_client.flush_queue();
|
||||
assert_eq!(sync_client.chain_info().best_block_number, 3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
/// Preconfigured validator list.
|
||||
|
||||
use util::Address;
|
||||
use util::{H256, Address, HeapSizeOf};
|
||||
use super::ValidatorSet;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Default)]
|
||||
@@ -34,16 +34,22 @@ impl SimpleList {
|
||||
}
|
||||
}
|
||||
|
||||
impl HeapSizeOf for SimpleList {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
self.validators.heap_size_of_children() + self.validator_n.heap_size_of_children()
|
||||
}
|
||||
}
|
||||
|
||||
impl ValidatorSet for SimpleList {
|
||||
fn contains(&self, address: &Address) -> bool {
|
||||
fn contains(&self, _bh: &H256, address: &Address) -> bool {
|
||||
self.validators.contains(address)
|
||||
}
|
||||
|
||||
fn get(&self, nonce: usize) -> Address {
|
||||
fn get(&self, _bh: &H256, nonce: usize) -> Address {
|
||||
self.validators.get(nonce % self.validator_n).expect("There are validator_n authorities; taking number modulo validator_n gives number in validator_n range; qed").clone()
|
||||
}
|
||||
|
||||
fn count(&self) -> usize {
|
||||
fn count(&self, _bh: &H256) -> usize {
|
||||
self.validator_n
|
||||
}
|
||||
}
|
||||
@@ -60,9 +66,9 @@ mod tests {
|
||||
let a1 = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
|
||||
let a2 = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||
let list = SimpleList::new(vec![a1.clone(), a2.clone()]);
|
||||
assert!(list.contains(&a1));
|
||||
assert_eq!(list.get(0), a1);
|
||||
assert_eq!(list.get(1), a2);
|
||||
assert_eq!(list.get(2), a1);
|
||||
assert!(list.contains(&Default::default(), &a1));
|
||||
assert_eq!(list.get(&Default::default(), 0), a1);
|
||||
assert_eq!(list.get(&Default::default(), 1), a2);
|
||||
assert_eq!(list.get(&Default::default(), 2), a1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +107,7 @@ extern crate ethcore_stratum;
|
||||
extern crate ethabi;
|
||||
extern crate hardware_wallet;
|
||||
extern crate stats;
|
||||
extern crate itertools;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
@@ -230,7 +230,8 @@ pub struct Miner {
|
||||
impl Miner {
|
||||
/// Push notifier that will handle new jobs
|
||||
pub fn push_notifier(&self, notifier: Box<NotifyWork>) {
|
||||
self.notifiers.write().push(notifier)
|
||||
self.notifiers.write().push(notifier);
|
||||
self.sealing_work.lock().enabled = true;
|
||||
}
|
||||
|
||||
/// Creates new instance of miner Arc.
|
||||
@@ -298,7 +299,7 @@ impl Miner {
|
||||
}
|
||||
|
||||
fn forced_sealing(&self) -> bool {
|
||||
self.options.force_sealing || !self.options.new_work_notify.is_empty()
|
||||
self.options.force_sealing || !self.notifiers.read().is_empty()
|
||||
}
|
||||
|
||||
/// Clear all pending block states
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use types::ids::BlockId;
|
||||
use client::MiningBlockChainClient;
|
||||
use transaction::SignedTransaction;
|
||||
use util::{U256, Uint, Mutex};
|
||||
@@ -45,7 +46,7 @@ impl ServiceTransactionChecker {
|
||||
debug_assert_eq!(tx.gas_price, U256::zero());
|
||||
|
||||
if let Some(ref contract) = *self.contract.lock() {
|
||||
let do_call = |a, d| client.call_contract(a, d);
|
||||
let do_call = |a, d| client.call_contract(BlockId::Latest, a, d);
|
||||
contract.certified(&do_call, &tx.sender())
|
||||
} else {
|
||||
Err("contract is not configured".to_owned())
|
||||
|
||||
@@ -211,6 +211,8 @@ impl From<AddrParseError> for Error {
|
||||
|
||||
impl super::work_notify::NotifyWork for Stratum {
|
||||
fn notify(&self, pow_hash: H256, difficulty: U256, number: u64) {
|
||||
trace!(target: "stratum", "Notify work");
|
||||
|
||||
self.service.push_work_all(
|
||||
self.dispatcher.payload(pow_hash, difficulty, number)
|
||||
).unwrap_or_else(
|
||||
|
||||
@@ -23,6 +23,7 @@ use snapshot::Error;
|
||||
use util::{U256, FixedHash, H256, Bytes, HashDB, SHA3_EMPTY, SHA3_NULL_RLP};
|
||||
use util::trie::{TrieDB, Trie};
|
||||
use rlp::{RlpStream, Stream, UntrustedRlp, View};
|
||||
use itertools::Itertools;
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
@@ -60,55 +61,53 @@ impl CodeState {
|
||||
}
|
||||
}
|
||||
|
||||
// walk the account's storage trie, returning an RLP item containing the
|
||||
// account properties and the storage.
|
||||
pub fn to_fat_rlp(acc: &BasicAccount, acct_db: &AccountDB, used_code: &mut HashSet<H256>) -> Result<Bytes, Error> {
|
||||
// walk the account's storage trie, returning a vector of RLP items containing the
|
||||
// account properties and the storage. Each item contains at most `max_storage_items`
|
||||
// storage records split according to snapshot format definition.
|
||||
pub fn to_fat_rlps(acc: &BasicAccount, acct_db: &AccountDB, used_code: &mut HashSet<H256>, max_storage_items: usize) -> Result<Vec<Bytes>, Error> {
|
||||
if acc == &ACC_EMPTY {
|
||||
return Ok(::rlp::NULL_RLP.to_vec());
|
||||
return Ok(vec![::rlp::NULL_RLP.to_vec()]);
|
||||
}
|
||||
|
||||
let db = TrieDB::new(acct_db, &acc.storage_root)?;
|
||||
|
||||
let mut pairs = Vec::new();
|
||||
let chunks = db.iter()?.chunks(max_storage_items);
|
||||
let pair_chunks = chunks.into_iter().map(|chunk| chunk.collect());
|
||||
pair_chunks.pad_using(1, |_| Vec::new(), ).map(|pairs| {
|
||||
let mut stream = RlpStream::new_list(pairs.len());
|
||||
|
||||
for item in db.iter()? {
|
||||
let (k, v) = item?;
|
||||
pairs.push((k, v));
|
||||
}
|
||||
for r in pairs {
|
||||
let (k, v) = r?;
|
||||
stream.begin_list(2).append(&k).append(&&*v);
|
||||
}
|
||||
|
||||
let mut stream = RlpStream::new_list(pairs.len());
|
||||
let pairs_rlp = stream.out();
|
||||
|
||||
for (k, v) in pairs {
|
||||
stream.begin_list(2).append(&k).append(&&*v);
|
||||
}
|
||||
let mut account_stream = RlpStream::new_list(5);
|
||||
account_stream.append(&acc.nonce)
|
||||
.append(&acc.balance);
|
||||
|
||||
let pairs_rlp = stream.out();
|
||||
|
||||
let mut account_stream = RlpStream::new_list(5);
|
||||
account_stream.append(&acc.nonce)
|
||||
.append(&acc.balance);
|
||||
|
||||
// [has_code, code_hash].
|
||||
if acc.code_hash == SHA3_EMPTY {
|
||||
account_stream.append(&CodeState::Empty.raw()).append_empty_data();
|
||||
} else if used_code.contains(&acc.code_hash) {
|
||||
account_stream.append(&CodeState::Hash.raw()).append(&acc.code_hash);
|
||||
} else {
|
||||
match acct_db.get(&acc.code_hash) {
|
||||
Some(c) => {
|
||||
used_code.insert(acc.code_hash.clone());
|
||||
account_stream.append(&CodeState::Inline.raw()).append(&&*c);
|
||||
}
|
||||
None => {
|
||||
warn!("code lookup failed during snapshot");
|
||||
account_stream.append(&false).append_empty_data();
|
||||
// [has_code, code_hash].
|
||||
if acc.code_hash == SHA3_EMPTY {
|
||||
account_stream.append(&CodeState::Empty.raw()).append_empty_data();
|
||||
} else if used_code.contains(&acc.code_hash) {
|
||||
account_stream.append(&CodeState::Hash.raw()).append(&acc.code_hash);
|
||||
} else {
|
||||
match acct_db.get(&acc.code_hash) {
|
||||
Some(c) => {
|
||||
used_code.insert(acc.code_hash.clone());
|
||||
account_stream.append(&CodeState::Inline.raw()).append(&&*c);
|
||||
}
|
||||
None => {
|
||||
warn!("code lookup failed during snapshot");
|
||||
account_stream.append(&false).append_empty_data();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
account_stream.append_raw(&pairs_rlp, 1);
|
||||
|
||||
Ok(account_stream.out())
|
||||
account_stream.append_raw(&pairs_rlp, 1);
|
||||
Ok(account_stream.out())
|
||||
}).collect()
|
||||
}
|
||||
|
||||
// decode a fat rlp, and rebuild the storage trie as we go.
|
||||
@@ -117,6 +116,7 @@ pub fn to_fat_rlp(acc: &BasicAccount, acct_db: &AccountDB, used_code: &mut HashS
|
||||
pub fn from_fat_rlp(
|
||||
acct_db: &mut AccountDBMut,
|
||||
rlp: UntrustedRlp,
|
||||
mut storage_root: H256,
|
||||
) -> Result<(BasicAccount, Option<Bytes>), Error> {
|
||||
use util::{TrieDBMut, TrieMut};
|
||||
|
||||
@@ -148,10 +148,12 @@ pub fn from_fat_rlp(
|
||||
}
|
||||
};
|
||||
|
||||
let mut storage_root = H256::zero();
|
||||
|
||||
{
|
||||
let mut storage_trie = TrieDBMut::new(acct_db, &mut storage_root);
|
||||
let mut storage_trie = if storage_root.is_zero() {
|
||||
TrieDBMut::new(acct_db, &mut storage_root)
|
||||
} else {
|
||||
TrieDBMut::from_existing(acct_db, &mut storage_root)?
|
||||
};
|
||||
let pairs = rlp.at(4)?;
|
||||
for pair_rlp in pairs.iter() {
|
||||
let k: Bytes = pair_rlp.val_at(0)?;
|
||||
@@ -184,7 +186,7 @@ mod tests {
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use super::{ACC_EMPTY, to_fat_rlp, from_fat_rlp};
|
||||
use super::{ACC_EMPTY, to_fat_rlps, from_fat_rlp};
|
||||
|
||||
#[test]
|
||||
fn encoding_basic() {
|
||||
@@ -201,9 +203,9 @@ mod tests {
|
||||
let thin_rlp = ::rlp::encode(&account);
|
||||
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp), account);
|
||||
|
||||
let fat_rlp = to_fat_rlp(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default()).unwrap();
|
||||
let fat_rlp = UntrustedRlp::new(&fat_rlp);
|
||||
assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp).unwrap().0, account);
|
||||
let fat_rlps = to_fat_rlps(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default(), usize::max_value()).unwrap();
|
||||
let fat_rlp = UntrustedRlp::new(&fat_rlps[0]);
|
||||
assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp, H256::zero()).unwrap().0, account);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -226,9 +228,40 @@ mod tests {
|
||||
let thin_rlp = ::rlp::encode(&account);
|
||||
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp), account);
|
||||
|
||||
let fat_rlp = to_fat_rlp(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default()).unwrap();
|
||||
let fat_rlp = UntrustedRlp::new(&fat_rlp);
|
||||
assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp).unwrap().0, account);
|
||||
let fat_rlp = to_fat_rlps(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default(), usize::max_value()).unwrap();
|
||||
let fat_rlp = UntrustedRlp::new(&fat_rlp[0]);
|
||||
assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp, H256::zero()).unwrap().0, account);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encoding_storage_split() {
|
||||
let mut db = get_temp_state_db();
|
||||
let addr = Address::random();
|
||||
|
||||
let account = {
|
||||
let acct_db = AccountDBMut::new(db.as_hashdb_mut(), &addr);
|
||||
let mut root = SHA3_NULL_RLP;
|
||||
fill_storage(acct_db, &mut root, &mut H256::zero());
|
||||
BasicAccount {
|
||||
nonce: 25.into(),
|
||||
balance: 987654321.into(),
|
||||
storage_root: root,
|
||||
code_hash: SHA3_EMPTY,
|
||||
}
|
||||
};
|
||||
|
||||
let thin_rlp = ::rlp::encode(&account);
|
||||
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp), account);
|
||||
|
||||
let fat_rlps = to_fat_rlps(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default(), 100).unwrap();
|
||||
let mut root = SHA3_NULL_RLP;
|
||||
let mut restored_account = None;
|
||||
for rlp in fat_rlps {
|
||||
let fat_rlp = UntrustedRlp::new(&rlp);
|
||||
restored_account = Some(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp, root).unwrap().0);
|
||||
root = restored_account.as_ref().unwrap().storage_root.clone();
|
||||
}
|
||||
assert_eq!(restored_account, Some(account));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -264,18 +297,18 @@ mod tests {
|
||||
|
||||
let mut used_code = HashSet::new();
|
||||
|
||||
let fat_rlp1 = to_fat_rlp(&account1, &AccountDB::new(db.as_hashdb(), &addr1), &mut used_code).unwrap();
|
||||
let fat_rlp2 = to_fat_rlp(&account2, &AccountDB::new(db.as_hashdb(), &addr2), &mut used_code).unwrap();
|
||||
let fat_rlp1 = to_fat_rlps(&account1, &AccountDB::new(db.as_hashdb(), &addr1), &mut used_code, usize::max_value()).unwrap();
|
||||
let fat_rlp2 = to_fat_rlps(&account2, &AccountDB::new(db.as_hashdb(), &addr2), &mut used_code, usize::max_value()).unwrap();
|
||||
assert_eq!(used_code.len(), 1);
|
||||
|
||||
let fat_rlp1 = UntrustedRlp::new(&fat_rlp1);
|
||||
let fat_rlp2 = UntrustedRlp::new(&fat_rlp2);
|
||||
let fat_rlp1 = UntrustedRlp::new(&fat_rlp1[0]);
|
||||
let fat_rlp2 = UntrustedRlp::new(&fat_rlp2[0]);
|
||||
|
||||
let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr2), fat_rlp2).unwrap();
|
||||
let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr2), fat_rlp2, H256::zero()).unwrap();
|
||||
assert!(maybe_code.is_none());
|
||||
assert_eq!(acc, account2);
|
||||
|
||||
let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr1), fat_rlp1).unwrap();
|
||||
let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr1), fat_rlp1, H256::zero()).unwrap();
|
||||
assert_eq!(maybe_code, Some(b"this is definitely code".to_vec()));
|
||||
assert_eq!(acc, account1);
|
||||
}
|
||||
@@ -285,7 +318,7 @@ mod tests {
|
||||
let mut db = get_temp_state_db();
|
||||
let mut used_code = HashSet::new();
|
||||
|
||||
assert_eq!(to_fat_rlp(&ACC_EMPTY, &AccountDB::new(db.as_hashdb(), &Address::default()), &mut used_code).unwrap(), ::rlp::NULL_RLP.to_vec());
|
||||
assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &Address::default()), UntrustedRlp::new(&::rlp::NULL_RLP)).unwrap(), (ACC_EMPTY, None));
|
||||
assert_eq!(to_fat_rlps(&ACC_EMPTY, &AccountDB::new(db.as_hashdb(), &Address::default()), &mut used_code, usize::max_value()).unwrap(), vec![::rlp::NULL_RLP.to_vec()]);
|
||||
assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &Address::default()), UntrustedRlp::new(&::rlp::NULL_RLP), H256::zero()).unwrap(), (ACC_EMPTY, None));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,8 @@ pub enum Error {
|
||||
Decoder(DecoderError),
|
||||
/// Io error.
|
||||
Io(::std::io::Error),
|
||||
/// Snapshot version is not supported.
|
||||
VersionNotSupported(u64),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
@@ -73,6 +75,7 @@ impl fmt::Display for Error {
|
||||
Error::Io(ref err) => err.fmt(f),
|
||||
Error::Decoder(ref err) => err.fmt(f),
|
||||
Error::Trie(ref err) => err.fmt(f),
|
||||
Error::VersionNotSupported(ref ver) => write!(f, "Snapshot version {} is not supprted.", ver),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ use rlp::{self, Encodable, RlpStream, UntrustedRlp, Stream, View};
|
||||
|
||||
use super::ManifestData;
|
||||
|
||||
const SNAPSHOT_VERSION: u64 = 2;
|
||||
|
||||
/// Something which can write snapshots.
|
||||
/// Writing the same chunk multiple times will lead to implementation-defined
|
||||
/// behavior, and is not advised.
|
||||
@@ -120,8 +122,9 @@ impl SnapshotWriter for PackedWriter {
|
||||
fn finish(mut self, manifest: ManifestData) -> io::Result<()> {
|
||||
// we ignore the hashes fields of the manifest under the assumption that
|
||||
// they are consistent with ours.
|
||||
let mut stream = RlpStream::new_list(5);
|
||||
let mut stream = RlpStream::new_list(6);
|
||||
stream
|
||||
.append(&SNAPSHOT_VERSION)
|
||||
.append(&self.state_hashes)
|
||||
.append(&self.block_hashes)
|
||||
.append(&manifest.state_root)
|
||||
@@ -223,7 +226,7 @@ impl PackedReader {
|
||||
/// Create a new `PackedReader` for the file at the given path.
|
||||
/// This will fail if any io errors are encountered or the file
|
||||
/// is not a valid packed snapshot.
|
||||
pub fn new(path: &Path) -> Result<Option<Self>, ::error::Error> {
|
||||
pub fn new(path: &Path) -> Result<Option<Self>, ::snapshot::error::Error> {
|
||||
let mut file = File::open(path)?;
|
||||
let file_len = file.metadata()?.len();
|
||||
if file_len < 8 {
|
||||
@@ -257,15 +260,26 @@ impl PackedReader {
|
||||
|
||||
let rlp = UntrustedRlp::new(&manifest_buf);
|
||||
|
||||
let state: Vec<ChunkInfo> = rlp.val_at(0)?;
|
||||
let blocks: Vec<ChunkInfo> = rlp.val_at(1)?;
|
||||
let (start, version) = if rlp.item_count() == 5 {
|
||||
(0, 1)
|
||||
} else {
|
||||
(1, rlp.val_at(0)?)
|
||||
};
|
||||
|
||||
if version > SNAPSHOT_VERSION {
|
||||
return Err(::snapshot::error::Error::VersionNotSupported(version));
|
||||
}
|
||||
|
||||
let state: Vec<ChunkInfo> = rlp.val_at(0 + start)?;
|
||||
let blocks: Vec<ChunkInfo> = rlp.val_at(1 + start)?;
|
||||
|
||||
let manifest = ManifestData {
|
||||
version: version,
|
||||
state_hashes: state.iter().map(|c| c.0).collect(),
|
||||
block_hashes: blocks.iter().map(|c| c.0).collect(),
|
||||
state_root: rlp.val_at(2)?,
|
||||
block_number: rlp.val_at(3)?,
|
||||
block_hash: rlp.val_at(4)?,
|
||||
state_root: rlp.val_at(2 + start)?,
|
||||
block_number: rlp.val_at(3 + start)?,
|
||||
block_hash: rlp.val_at(4 + start)?,
|
||||
};
|
||||
|
||||
Ok(Some(PackedReader {
|
||||
@@ -348,7 +362,7 @@ mod tests {
|
||||
use util::sha3::Hashable;
|
||||
|
||||
use snapshot::ManifestData;
|
||||
use super::{SnapshotWriter, SnapshotReader, PackedWriter, PackedReader, LooseWriter, LooseReader};
|
||||
use super::{SnapshotWriter, SnapshotReader, PackedWriter, PackedReader, LooseWriter, LooseReader, SNAPSHOT_VERSION};
|
||||
|
||||
const STATE_CHUNKS: &'static [&'static [u8]] = &[b"dog", b"cat", b"hello world", b"hi", b"notarealchunk"];
|
||||
const BLOCK_CHUNKS: &'static [&'static [u8]] = &[b"hello!", b"goodbye!", b"abcdefg", b"hijklmnop", b"qrstuvwxy", b"and", b"z"];
|
||||
@@ -374,6 +388,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let manifest = ManifestData {
|
||||
version: SNAPSHOT_VERSION,
|
||||
state_hashes: state_hashes,
|
||||
block_hashes: block_hashes,
|
||||
state_root: b"notarealroot".sha3(),
|
||||
@@ -412,6 +427,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let manifest = ManifestData {
|
||||
version: SNAPSHOT_VERSION,
|
||||
state_hashes: state_hashes,
|
||||
block_hashes: block_hashes,
|
||||
state_root: b"notarealroot".sha3(),
|
||||
@@ -428,4 +444,4 @@ mod tests {
|
||||
reader.chunk(hash.clone()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ pub use self::traits::SnapshotService;
|
||||
pub use self::watcher::Watcher;
|
||||
pub use types::snapshot_manifest::ManifestData;
|
||||
pub use types::restoration_status::RestorationStatus;
|
||||
pub use types::basic_account::BasicAccount;
|
||||
|
||||
pub mod io;
|
||||
pub mod service;
|
||||
@@ -82,6 +83,9 @@ mod traits {
|
||||
// Try to have chunks be around 4MB (before compression)
|
||||
const PREFERRED_CHUNK_SIZE: usize = 4 * 1024 * 1024;
|
||||
|
||||
// Try to have chunks be around 4MB (before compression)
|
||||
const MAX_STORAGE_ENTRIES_PER_ACCOUNT_RECORD: usize = 80_000;
|
||||
|
||||
// How many blocks to include in a snapshot, starting from the head of the chain.
|
||||
const SNAPSHOT_BLOCKS: u64 = 30000;
|
||||
|
||||
@@ -147,6 +151,7 @@ pub fn take_snapshot<W: SnapshotWriter + Send>(
|
||||
info!("produced {} state chunks and {} block chunks.", state_hashes.len(), block_hashes.len());
|
||||
|
||||
let manifest_data = ManifestData {
|
||||
version: 2,
|
||||
state_hashes: state_hashes,
|
||||
block_hashes: block_hashes,
|
||||
state_root: *state_root,
|
||||
@@ -300,14 +305,14 @@ impl<'a> StateChunker<'a> {
|
||||
//
|
||||
// If the buffer is greater than the desired chunk size,
|
||||
// this will write out the data to disk.
|
||||
fn push(&mut self, account_hash: Bytes, data: Bytes) -> Result<(), Error> {
|
||||
fn push(&mut self, account_hash: Bytes, data: Bytes, force_chunk: bool) -> Result<(), Error> {
|
||||
let pair = {
|
||||
let mut stream = RlpStream::new_list(2);
|
||||
stream.append(&account_hash).append_raw(&data, 1);
|
||||
stream.out()
|
||||
};
|
||||
|
||||
if self.cur_size + pair.len() >= PREFERRED_CHUNK_SIZE {
|
||||
if force_chunk || self.cur_size + pair.len() >= PREFERRED_CHUNK_SIZE {
|
||||
self.write_chunk()?;
|
||||
}
|
||||
|
||||
@@ -372,8 +377,10 @@ pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex<SnapshotWriter +
|
||||
|
||||
let account_db = AccountDB::from_hash(db, account_key_hash);
|
||||
|
||||
let fat_rlp = account::to_fat_rlp(&account, &account_db, &mut used_code)?;
|
||||
chunker.push(account_key, fat_rlp)?;
|
||||
let fat_rlps = account::to_fat_rlps(&account, &account_db, &mut used_code, MAX_STORAGE_ENTRIES_PER_ACCOUNT_RECORD)?;
|
||||
for (i, fat_rlp) in fat_rlps.into_iter().enumerate() {
|
||||
chunker.push(account_key.clone(), fat_rlp, i > 0)?;
|
||||
}
|
||||
}
|
||||
|
||||
if chunker.cur_size != 0 {
|
||||
@@ -390,6 +397,7 @@ pub struct StateRebuilder {
|
||||
known_code: HashMap<H256, H256>, // code hashes mapped to first account with this code.
|
||||
missing_code: HashMap<H256, Vec<H256>>, // maps code hashes to lists of accounts missing that code.
|
||||
bloom: Bloom,
|
||||
known_storage_roots: HashMap<H256, H256>, // maps account hashes to last known storage root. Only filled for last account per chunk.
|
||||
}
|
||||
|
||||
impl StateRebuilder {
|
||||
@@ -401,6 +409,7 @@ impl StateRebuilder {
|
||||
known_code: HashMap::new(),
|
||||
missing_code: HashMap::new(),
|
||||
bloom: StateDB::load_bloom(&*db),
|
||||
known_storage_roots: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,6 +427,7 @@ impl StateRebuilder {
|
||||
rlp,
|
||||
&mut pairs,
|
||||
&self.known_code,
|
||||
&mut self.known_storage_roots,
|
||||
flag
|
||||
)?;
|
||||
|
||||
@@ -464,14 +474,18 @@ impl StateRebuilder {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check for accounts missing code. Once all chunks have been fed, there should
|
||||
/// be none.
|
||||
pub fn check_missing(self) -> Result<(), Error> {
|
||||
/// Finalize the restoration. Check for accounts missing code and make a dummy
|
||||
/// journal entry.
|
||||
/// Once all chunks have been fed, there should be nothing missing.
|
||||
pub fn finalize(mut self, era: u64, id: H256) -> Result<(), ::error::Error> {
|
||||
let missing = self.missing_code.keys().cloned().collect::<Vec<_>>();
|
||||
match missing.is_empty() {
|
||||
true => Ok(()),
|
||||
false => Err(Error::MissingCode(missing)),
|
||||
}
|
||||
if !missing.is_empty() { return Err(Error::MissingCode(missing).into()) }
|
||||
|
||||
let mut batch = self.db.backing().transaction();
|
||||
self.db.journal_under(&mut batch, era, &id)?;
|
||||
self.db.backing().write_buffered(batch);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the state root of the rebuilder.
|
||||
@@ -492,10 +506,11 @@ fn rebuild_accounts(
|
||||
account_fat_rlps: UntrustedRlp,
|
||||
out_chunk: &mut [(H256, Bytes)],
|
||||
known_code: &HashMap<H256, H256>,
|
||||
known_storage_roots: &mut HashMap<H256, H256>,
|
||||
abort_flag: &AtomicBool,
|
||||
) -> Result<RebuiltStatus, ::error::Error> {
|
||||
let mut status = RebuiltStatus::default();
|
||||
for (account_rlp, out) in account_fat_rlps.into_iter().zip(out_chunk) {
|
||||
for (account_rlp, out) in account_fat_rlps.into_iter().zip(out_chunk.iter_mut()) {
|
||||
if !abort_flag.load(Ordering::SeqCst) { return Err(Error::RestorationAborted.into()) }
|
||||
|
||||
let hash: H256 = account_rlp.val_at(0)?;
|
||||
@@ -506,7 +521,8 @@ fn rebuild_accounts(
|
||||
// fill out the storage trie and code while decoding.
|
||||
let (acc, maybe_code) = {
|
||||
let mut acct_db = AccountDBMut::from_hash(db, hash);
|
||||
account::from_fat_rlp(&mut acct_db, fat_rlp)?
|
||||
let storage_root = known_storage_roots.get(&hash).cloned().unwrap_or(H256::zero());
|
||||
account::from_fat_rlp(&mut acct_db, fat_rlp, storage_root)?
|
||||
};
|
||||
|
||||
let code_hash = acc.code_hash.clone();
|
||||
@@ -538,6 +554,12 @@ fn rebuild_accounts(
|
||||
|
||||
*out = (hash, thin_rlp);
|
||||
}
|
||||
if let Some(&(ref hash, ref rlp)) = out_chunk.iter().last() {
|
||||
known_storage_roots.insert(*hash, ::rlp::decode::<BasicAccount>(rlp).storage_root);
|
||||
}
|
||||
if let Some(&(ref hash, ref rlp)) = out_chunk.iter().next() {
|
||||
known_storage_roots.insert(*hash, ::rlp::decode::<BasicAccount>(rlp).storage_root);
|
||||
}
|
||||
Ok(status)
|
||||
}
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ impl Restoration {
|
||||
}
|
||||
|
||||
// check for missing code.
|
||||
self.state.check_missing()?;
|
||||
self.state.finalize(self.manifest.block_number, self.manifest.block_hash)?;
|
||||
|
||||
// connect out-of-order chunks and verify chain integrity.
|
||||
self.blocks.finalize(self.canonical_hashes)?;
|
||||
@@ -656,6 +656,7 @@ mod tests {
|
||||
assert_eq!(service.status(), RestorationStatus::Inactive);
|
||||
|
||||
let manifest = ManifestData {
|
||||
version: 2,
|
||||
state_hashes: vec![],
|
||||
block_hashes: vec![],
|
||||
state_root: Default::default(),
|
||||
|
||||
@@ -63,6 +63,7 @@ fn chunk_and_restore(amount: u64) {
|
||||
let writer = Mutex::new(PackedWriter::new(&snapshot_path).unwrap());
|
||||
let block_hashes = chunk_blocks(&bc, best_hash, &writer, &Progress::default()).unwrap();
|
||||
let manifest = ::snapshot::ManifestData {
|
||||
version: 2,
|
||||
state_hashes: Vec::new(),
|
||||
block_hashes: block_hashes,
|
||||
state_root: ::util::sha3::SHA3_NULL_RLP,
|
||||
@@ -125,6 +126,7 @@ fn checks_flag() {
|
||||
let chain = BlockChain::new(Default::default(), &genesis, db.clone());
|
||||
|
||||
let manifest = ::snapshot::ManifestData {
|
||||
version: 2,
|
||||
state_hashes: Vec::new(),
|
||||
block_hashes: Vec::new(),
|
||||
state_root: ::util::sha3::SHA3_NULL_RLP,
|
||||
|
||||
@@ -27,6 +27,7 @@ use super::ManifestData;
|
||||
#[test]
|
||||
fn manifest_rlp() {
|
||||
let manifest = ManifestData {
|
||||
version: 2,
|
||||
block_hashes: Vec::new(),
|
||||
state_hashes: Vec::new(),
|
||||
block_number: 1234567,
|
||||
@@ -35,4 +36,4 @@ fn manifest_rlp() {
|
||||
};
|
||||
let raw = manifest.clone().into_rlp();
|
||||
assert_eq!(ManifestData::from_rlp(&raw).unwrap(), manifest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +122,7 @@ fn guards_delete_folders() {
|
||||
path.push("restoration");
|
||||
|
||||
let manifest = ManifestData {
|
||||
version: 2,
|
||||
state_hashes: vec![],
|
||||
block_hashes: vec![],
|
||||
block_number: 0,
|
||||
|
||||
@@ -58,10 +58,11 @@ fn snap_and_restore() {
|
||||
let state_hashes = chunk_state(&old_db, &state_root, &writer, &Progress::default()).unwrap();
|
||||
|
||||
writer.into_inner().finish(::snapshot::ManifestData {
|
||||
version: 2,
|
||||
state_hashes: state_hashes,
|
||||
block_hashes: Vec::new(),
|
||||
state_root: state_root,
|
||||
block_number: 0,
|
||||
block_number: 1000,
|
||||
block_hash: H256::default(),
|
||||
}).unwrap();
|
||||
|
||||
@@ -69,7 +70,7 @@ fn snap_and_restore() {
|
||||
db_path.push("db");
|
||||
let db = {
|
||||
let new_db = Arc::new(Database::open(&db_cfg, &db_path.to_string_lossy()).unwrap());
|
||||
let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::Archive);
|
||||
let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::OverlayRecent);
|
||||
let reader = PackedReader::new(&snap_file).unwrap().unwrap();
|
||||
|
||||
let flag = AtomicBool::new(true);
|
||||
@@ -82,12 +83,13 @@ fn snap_and_restore() {
|
||||
}
|
||||
|
||||
assert_eq!(rebuilder.state_root(), state_root);
|
||||
rebuilder.check_missing().unwrap();
|
||||
rebuilder.finalize(1000, H256::default()).unwrap();
|
||||
|
||||
new_db
|
||||
};
|
||||
|
||||
let new_db = journaldb::new(db, Algorithm::Archive, ::db::COL_STATE);
|
||||
let new_db = journaldb::new(db, Algorithm::OverlayRecent, ::db::COL_STATE);
|
||||
assert_eq!(new_db.earliest_era(), Some(1000));
|
||||
|
||||
compare_dbs(&old_db, new_db.as_hashdb());
|
||||
}
|
||||
@@ -120,10 +122,10 @@ fn get_code_from_prev_chunk() {
|
||||
let mut db = MemoryDB::new();
|
||||
AccountDBMut::from_hash(&mut db, hash).insert(&code[..]);
|
||||
|
||||
let fat_rlp = account::to_fat_rlp(&acc, &AccountDB::from_hash(&db, hash), &mut used_code).unwrap();
|
||||
let fat_rlp = account::to_fat_rlps(&acc, &AccountDB::from_hash(&db, hash), &mut used_code, usize::max_value()).unwrap();
|
||||
|
||||
let mut stream = RlpStream::new_list(1);
|
||||
stream.begin_list(2).append(&hash).append_raw(&fat_rlp, 1);
|
||||
stream.begin_list(2).append(&hash).append_raw(&fat_rlp[0], 1);
|
||||
stream.out()
|
||||
};
|
||||
|
||||
@@ -134,13 +136,18 @@ fn get_code_from_prev_chunk() {
|
||||
let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS);
|
||||
let new_db = Arc::new(Database::open(&db_cfg, &db_path.to_string_lossy()).unwrap());
|
||||
|
||||
let mut rebuilder = StateRebuilder::new(new_db, Algorithm::Archive);
|
||||
let flag = AtomicBool::new(true);
|
||||
{
|
||||
let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::OverlayRecent);
|
||||
let flag = AtomicBool::new(true);
|
||||
|
||||
rebuilder.feed(&chunk1, &flag).unwrap();
|
||||
rebuilder.feed(&chunk2, &flag).unwrap();
|
||||
rebuilder.feed(&chunk1, &flag).unwrap();
|
||||
rebuilder.feed(&chunk2, &flag).unwrap();
|
||||
|
||||
rebuilder.check_missing().unwrap();
|
||||
rebuilder.finalize(1000, H256::random()).unwrap();
|
||||
}
|
||||
|
||||
let state_db = journaldb::new(new_db, Algorithm::OverlayRecent, ::db::COL_STATE);
|
||||
assert_eq!(state_db.earliest_era(), Some(1000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -164,6 +171,7 @@ fn checks_flag() {
|
||||
let state_hashes = chunk_state(&old_db, &state_root, &writer, &Progress::default()).unwrap();
|
||||
|
||||
writer.into_inner().finish(::snapshot::ManifestData {
|
||||
version: 2,
|
||||
state_hashes: state_hashes,
|
||||
block_hashes: Vec::new(),
|
||||
state_root: state_root,
|
||||
@@ -175,7 +183,7 @@ fn checks_flag() {
|
||||
db_path.push("db");
|
||||
{
|
||||
let new_db = Arc::new(Database::open(&db_cfg, &db_path.to_string_lossy()).unwrap());
|
||||
let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::Archive);
|
||||
let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::OverlayRecent);
|
||||
let reader = PackedReader::new(&snap_file).unwrap().unwrap();
|
||||
|
||||
let flag = AtomicBool::new(false);
|
||||
|
||||
@@ -55,6 +55,8 @@ pub struct CommonParams {
|
||||
pub fork_block: Option<(BlockNumber, H256)>,
|
||||
/// Number of first block where EIP-98 rules begin.
|
||||
pub eip98_transition: BlockNumber,
|
||||
/// Validate block receipts root.
|
||||
pub validate_receipts_transition: u64,
|
||||
}
|
||||
|
||||
impl From<ethjson::spec::Params> for CommonParams {
|
||||
@@ -68,6 +70,7 @@ impl From<ethjson::spec::Params> for CommonParams {
|
||||
min_gas_limit: p.min_gas_limit.into(),
|
||||
fork_block: if let (Some(n), Some(h)) = (p.fork_block, p.fork_hash) { Some((n.into(), h.into())) } else { None },
|
||||
eip98_transition: p.eip98_transition.map_or(0, Into::into),
|
||||
validate_receipts_transition: p.validate_receipts_transition.map_or(0, Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -357,6 +360,10 @@ impl Spec {
|
||||
/// Account is marked with `reportBenign` it can be checked as disliked with "0xd8f2e0bf".
|
||||
/// Validator can be removed with `reportMalicious`.
|
||||
pub fn new_validator_contract() -> Self { load_bundled!("validator_contract") }
|
||||
|
||||
/// Create a new Spec with BasicAuthority which uses multiple validator sets changing with height.
|
||||
/// Account with secrets "0".sha3() is the validator for block 1 and with "1".sha3() onwards.
|
||||
pub fn new_validator_multi() -> Self { load_bundled!("validator_multi") }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -546,7 +546,7 @@ impl<B: Backend> State<B> {
|
||||
|
||||
// TODO uncomment once to_pod() works correctly.
|
||||
// trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod()));
|
||||
let state_root = if env_info.number < engine.params().eip98_transition {
|
||||
let state_root = if env_info.number < engine.params().eip98_transition || env_info.number < engine.params().validate_receipts_transition {
|
||||
self.commit()?;
|
||||
Some(self.root().clone())
|
||||
} else {
|
||||
|
||||
@@ -24,6 +24,8 @@ use util::Bytes;
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "ipc", binary)]
|
||||
pub struct ManifestData {
|
||||
/// Snapshot format version.
|
||||
pub version: u64,
|
||||
/// List of state chunk hashes.
|
||||
pub state_hashes: Vec<H256>,
|
||||
/// List of block chunk hashes.
|
||||
@@ -39,7 +41,8 @@ pub struct ManifestData {
|
||||
impl ManifestData {
|
||||
/// Encode the manifest data to rlp.
|
||||
pub fn into_rlp(self) -> Bytes {
|
||||
let mut stream = RlpStream::new_list(5);
|
||||
let mut stream = RlpStream::new_list(6);
|
||||
stream.append(&self.version);
|
||||
stream.append(&self.state_hashes);
|
||||
stream.append(&self.block_hashes);
|
||||
stream.append(&self.state_root);
|
||||
@@ -52,14 +55,20 @@ impl ManifestData {
|
||||
/// Try to restore manifest data from raw bytes, interpreted as RLP.
|
||||
pub fn from_rlp(raw: &[u8]) -> Result<Self, DecoderError> {
|
||||
let decoder = UntrustedRlp::new(raw);
|
||||
let (start, version) = if decoder.item_count() == 5 {
|
||||
(0, 1)
|
||||
} else {
|
||||
(1, decoder.val_at(0)?)
|
||||
};
|
||||
|
||||
let state_hashes: Vec<H256> = decoder.val_at(0)?;
|
||||
let block_hashes: Vec<H256> = decoder.val_at(1)?;
|
||||
let state_root: H256 = decoder.val_at(2)?;
|
||||
let block_number: u64 = decoder.val_at(3)?;
|
||||
let block_hash: H256 = decoder.val_at(4)?;
|
||||
let state_hashes: Vec<H256> = decoder.val_at(start + 0)?;
|
||||
let block_hashes: Vec<H256> = decoder.val_at(start + 1)?;
|
||||
let state_root: H256 = decoder.val_at(start + 2)?;
|
||||
let block_number: u64 = decoder.val_at(start + 3)?;
|
||||
let block_hash: H256 = decoder.val_at(start + 4)?;
|
||||
|
||||
Ok(ManifestData {
|
||||
version: version,
|
||||
state_hashes: state_hashes,
|
||||
block_hashes: block_hashes,
|
||||
state_root: state_root,
|
||||
|
||||
@@ -5,11 +5,11 @@ version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[lib]
|
||||
name = "evm"
|
||||
name = "evmbin"
|
||||
path = "./src/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "evm"
|
||||
name = "parity-evm"
|
||||
path = "./src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -271,7 +271,7 @@ impl Manager {
|
||||
chunk_size += size;
|
||||
}
|
||||
trace!("writing {:?}", &hid_chunk[..]);
|
||||
let n = handle.write(&hid_chunk[0..chunk_size])?;
|
||||
let n = handle.write(&hid_chunk[..])?;
|
||||
if n < chunk_size {
|
||||
return Err(Error::Protocol("Write data size mismatch"));
|
||||
}
|
||||
|
||||
@@ -14,13 +14,13 @@ nightly-testing = ["clippy"]
|
||||
with-syntex = ["quasi/with-syntex", "quasi_codegen", "quasi_codegen/with-syntex", "syntex", "syntex_syntax"]
|
||||
|
||||
[build-dependencies]
|
||||
quasi_codegen = { version = "0.11", optional = true }
|
||||
syntex = { version = "0.33", optional = true }
|
||||
quasi_codegen = { version = "0.32", optional = true }
|
||||
syntex = { version = "0.58", optional = true }
|
||||
|
||||
[dependencies]
|
||||
aster = { version = "0.17", default-features = false }
|
||||
aster = { version = "0.41", default-features = false }
|
||||
clippy = { version = "^0.*", optional = true }
|
||||
quasi = { version = "0.11", default-features = false }
|
||||
quasi_macros = { version = "0.11", optional = true }
|
||||
syntex = { version = "0.33", optional = true }
|
||||
syntex_syntax = { version = "0.33", optional = true }
|
||||
quasi = { version = "0.32", default-features = false }
|
||||
quasi_macros = { version = "0.32", optional = true }
|
||||
syntex = { version = "0.58", optional = true }
|
||||
syntex_syntax = { version = "0.58", optional = true }
|
||||
|
||||
@@ -25,13 +25,11 @@ mod inner {
|
||||
|
||||
pub fn main() {
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let mut registry = syntex::Registry::new();
|
||||
quasi_codegen::register(&mut registry);
|
||||
|
||||
let src = Path::new("src/lib.rs.in");
|
||||
let dst = Path::new(&out_dir).join("lib.rs");
|
||||
|
||||
registry.expand("", &src, &dst).unwrap();
|
||||
quasi_codegen::expand(&src, &dst).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ pub fn expand_ipc_implementation(
|
||||
|
||||
let builder = aster::AstBuilder::new().span(span);
|
||||
|
||||
let interface_map = match implement_interface(cx, &builder, &item, push) {
|
||||
let interface_map = match implement_interface(cx, &builder, &item, push, meta_item) {
|
||||
Ok(interface_map) => interface_map,
|
||||
Err(Error) => { return; },
|
||||
};
|
||||
@@ -99,9 +99,9 @@ fn push_invoke_signature_aster(
|
||||
let inputs = &named_signature.sig.decl.inputs;
|
||||
let (input_type_name, input_arg_names, input_arg_tys) = if inputs.len() > 0 {
|
||||
let first_field_name = field_name(builder, &inputs[0]).name.as_str();
|
||||
if first_field_name == "self" && inputs.len() == 1 { (None, vec![], vec![]) }
|
||||
if &*first_field_name == "self" && inputs.len() == 1 { (None, vec![], vec![]) }
|
||||
else {
|
||||
let skip = if first_field_name == "self" { 2 } else { 1 };
|
||||
let skip = if &*first_field_name == "self" { 2 } else { 1 };
|
||||
let name_str = format!("{}_input", named_signature.ident.name.as_str());
|
||||
|
||||
let mut arg_names = Vec::new();
|
||||
@@ -181,6 +181,7 @@ fn implement_dispatch_arm_invoke_stmt(
|
||||
dispatch: &Dispatch,
|
||||
) -> ast::Stmt
|
||||
{
|
||||
use ::syntax::tokenstream::TokenTree::Token;
|
||||
let function_name = builder.id(dispatch.function_name.as_str());
|
||||
|
||||
let input_args_exprs = dispatch.input_arg_names.iter().enumerate().map(|(arg_index, arg_name)| {
|
||||
@@ -193,56 +194,56 @@ fn implement_dispatch_arm_invoke_stmt(
|
||||
let ext_cx = &*cx;
|
||||
::quasi::parse_stmt_panic(&mut ::syntax::parse::new_parser_from_tts(
|
||||
ext_cx.parse_sess(),
|
||||
ext_cx.cfg(),
|
||||
{
|
||||
let _sp = ext_cx.call_site();
|
||||
let mut tt = ::std::vec::Vec::new();
|
||||
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace)));
|
||||
|
||||
if dispatch.return_type_ty.is_some() {
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("ipc"))));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("binary"))));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("serialize"))));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::ModSep));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("ipc"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::ModSep));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("binary"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::ModSep));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("serialize"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And)));
|
||||
}
|
||||
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("self"))));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Dot));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("self"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Dot));
|
||||
tt.extend(::quasi::ToTokens::to_tokens(&function_name, ext_cx).into_iter());
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
|
||||
|
||||
for arg_expr in input_args_exprs {
|
||||
tt.extend(::quasi::ToTokens::to_tokens(&arg_expr, ext_cx).into_iter());
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Comma));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Comma));
|
||||
}
|
||||
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
|
||||
|
||||
if dispatch.return_type_ty.is_some() {
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Dot));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("unwrap"))));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Dot));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("unwrap"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
|
||||
}
|
||||
else {
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Semi));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("Vec"))));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("new"))));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Semi));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("Vec"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::ModSep));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("new"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
|
||||
|
||||
}
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace)));
|
||||
|
||||
tt
|
||||
})).unwrap()
|
||||
}
|
||||
)).unwrap()
|
||||
}
|
||||
|
||||
fn implement_dispatch_arm_invoke(
|
||||
@@ -344,6 +345,8 @@ fn implement_client_method_body(
|
||||
interface_map: &InterfaceMap,
|
||||
) -> P<ast::Expr>
|
||||
{
|
||||
use ::syntax::tokenstream::TokenTree::Token;
|
||||
|
||||
let dispatch = &interface_map.dispatches[index as usize];
|
||||
let index_ident = builder.id(format!("{}", index + RESERVED_MESSAGE_IDS).as_str());
|
||||
|
||||
@@ -391,26 +394,25 @@ fn implement_client_method_body(
|
||||
let ext_cx = &*cx;
|
||||
::quasi::parse_stmt_panic(&mut ::syntax::parse::new_parser_from_tts(
|
||||
ext_cx.parse_sess(),
|
||||
ext_cx.cfg(),
|
||||
{
|
||||
let _sp = ext_cx.call_site();
|
||||
let mut tt = ::std::vec::Vec::new();
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("let"))));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("payload"))));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Eq));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("Request"))));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("let"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("payload"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Eq));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("Request"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace)));
|
||||
|
||||
for arg in dispatch.input_arg_names.iter() {
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of(arg.as_str()))));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Colon));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of(arg.as_str()))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Colon));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And)));
|
||||
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of(arg.as_str()))));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Comma));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of(arg.as_str()))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Comma));
|
||||
}
|
||||
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace)));
|
||||
tt
|
||||
}))
|
||||
});
|
||||
@@ -465,6 +467,8 @@ fn implement_client_method(
|
||||
)
|
||||
-> ast::ImplItem
|
||||
{
|
||||
use ::syntax::tokenstream::TokenTree::Token;
|
||||
|
||||
let dispatch = &interface_map.dispatches[index as usize];
|
||||
let method_name = builder.id(dispatch.function_name.as_str());
|
||||
let body = implement_client_method_body(cx, builder, index, interface_map);
|
||||
@@ -476,36 +480,35 @@ fn implement_client_method(
|
||||
let signature = ::syntax::parse::parser::Parser::parse_impl_item(
|
||||
&mut ::syntax::parse::new_parser_from_tts(
|
||||
ext_cx.parse_sess(),
|
||||
ext_cx.cfg(),
|
||||
{
|
||||
let _sp = ext_cx.call_site();
|
||||
let mut tt = ::std::vec::Vec::new();
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("fn"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("fn"))));
|
||||
tt.extend(::quasi::ToTokens::to_tokens(&method_name, ext_cx).into_iter());
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And)));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("self"))));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Comma));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("self"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Comma));
|
||||
|
||||
for arg_idx in 0..dispatch.input_arg_names.len() {
|
||||
let arg_name = dispatch.input_arg_names[arg_idx].as_str();
|
||||
let arg_ty = dispatch.input_arg_tys[arg_idx].clone();
|
||||
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of(arg_name))));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Colon));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of(arg_name))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Colon));
|
||||
tt.extend(::quasi::ToTokens::to_tokens(&arg_ty, ext_cx).into_iter());
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Comma));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Comma));
|
||||
}
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
|
||||
|
||||
if let Some(ref return_ty) = dispatch.return_type_ty {
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::RArrow));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::RArrow));
|
||||
tt.extend(::quasi::ToTokens::to_tokens(return_ty, ext_cx).into_iter());
|
||||
}
|
||||
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace)));
|
||||
tt.extend(::quasi::ToTokens::to_tokens(&body, ext_cx).into_iter());
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace)));
|
||||
|
||||
tt
|
||||
}));
|
||||
@@ -526,7 +529,7 @@ fn client_generics(builder: &aster::AstBuilder, interface_map: &InterfaceMap) ->
|
||||
|
||||
fn client_qualified_ident(cx: &ExtCtxt, builder: &aster::AstBuilder, interface_map: &InterfaceMap) -> P<Ty> {
|
||||
let generics = client_generics(builder, interface_map);
|
||||
aster::ty::TyBuilder::new().path().segment(interface_map.ident_map.client_ident(cx, builder, &interface_map.original_item))
|
||||
aster::ty::TyBuilder::new().path().segment(interface_map.ident_map.client_ident(cx, builder))
|
||||
.with_generics(generics).build()
|
||||
.build()
|
||||
}
|
||||
@@ -542,7 +545,7 @@ fn client_phantom_ident(builder: &aster::AstBuilder, interface_map: &InterfaceMa
|
||||
/// for say `Service` it generates `ServiceClient`
|
||||
fn push_client_struct(cx: &ExtCtxt, builder: &aster::AstBuilder, interface_map: &InterfaceMap, push: &mut FnMut(Annotatable)) {
|
||||
let generics = client_generics(builder, interface_map);
|
||||
let client_short_ident = interface_map.ident_map.client_ident(cx, builder, &interface_map.original_item);
|
||||
let client_short_ident = interface_map.ident_map.client_ident(cx, builder);
|
||||
let phantom = client_phantom_ident(builder, interface_map);
|
||||
|
||||
let client_struct_item = quote_item!(cx,
|
||||
@@ -575,7 +578,7 @@ fn push_with_socket_client_implementation(
|
||||
let generics = client_generics(builder, interface_map);
|
||||
let client_ident = client_qualified_ident(cx, builder, interface_map);
|
||||
let where_clause = &generics.where_clause;
|
||||
let client_short_ident = interface_map.ident_map.client_ident(cx, builder, &interface_map.original_item);
|
||||
let client_short_ident = interface_map.ident_map.client_ident(cx, builder);
|
||||
|
||||
let implement = quote_item!(cx,
|
||||
impl $generics ::ipc::WithSocket<S> for $client_ident $where_clause {
|
||||
@@ -717,33 +720,31 @@ fn get_str_from_lit(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<String,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_ipc_meta_items(attr: &ast::Attribute) -> Option<&[P<ast::MetaItem>]> {
|
||||
match attr.node.value.node {
|
||||
ast::MetaItemKind::List(ref name, ref items) if name == &"ipc" => {
|
||||
Some(items)
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn client_ident_renamed(cx: &ExtCtxt, item: &ast::Item) -> Option<String> {
|
||||
for meta_items in item.attrs().iter().filter_map(get_ipc_meta_items) {
|
||||
for meta_item in meta_items {
|
||||
match meta_item.node {
|
||||
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"client_ident" => {
|
||||
if let Ok(s) = get_str_from_lit(cx, name, lit) {
|
||||
return Some(s);
|
||||
fn client_ident_renamed(cx: &ExtCtxt, meta_item: &MetaItem) -> Option<String> {
|
||||
if let ast::MetaItemKind::List(ref list) = meta_item.node {
|
||||
for nested in list {
|
||||
match nested.node {
|
||||
ast::NestedMetaItemKind::MetaItem(ref meta_item) => {
|
||||
let is_client_ident = &*meta_item.name.as_str() == "client_ident";
|
||||
match meta_item.node {
|
||||
ast::MetaItemKind::NameValue(ref lit) if is_client_ident => {
|
||||
if let Ok(s) = get_str_from_lit(cx, "client_ident", lit) {
|
||||
return Some(s);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
cx.span_err(
|
||||
meta_item.span,
|
||||
&format!("unknown client_ident container attribute `{}`",
|
||||
::syntax::print::pprust::meta_item_to_string(&meta_item)));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
cx.span_err(
|
||||
meta_item.span,
|
||||
&format!("unknown client_ident container attribute `{}`",
|
||||
::syntax::print::pprust::meta_item_to_string(meta_item)));
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
@@ -759,6 +760,7 @@ struct InterfaceMap {
|
||||
|
||||
struct IdentMap {
|
||||
original_path: ast::Path,
|
||||
meta_item: MetaItem,
|
||||
}
|
||||
|
||||
impl IdentMap {
|
||||
@@ -766,8 +768,8 @@ impl IdentMap {
|
||||
builder.id(format!("{}", ::syntax::print::pprust::path_to_string(&self.original_path)))
|
||||
}
|
||||
|
||||
fn client_ident(&self, cx: &ExtCtxt, builder: &aster::AstBuilder, item: &ast::Item) -> Ident {
|
||||
if let Some(new_name) = client_ident_renamed(cx, item) {
|
||||
fn client_ident(&self, cx: &ExtCtxt, builder: &aster::AstBuilder) -> Ident {
|
||||
if let Some(new_name) = client_ident_renamed(cx, &self.meta_item) {
|
||||
builder.id(new_name)
|
||||
}
|
||||
else {
|
||||
@@ -776,12 +778,12 @@ impl IdentMap {
|
||||
}
|
||||
}
|
||||
|
||||
fn ty_ident_map(original_ty: &P<Ty>) -> IdentMap {
|
||||
fn ty_ident_map(original_ty: &P<Ty>, meta_item: &MetaItem) -> IdentMap {
|
||||
let original_path = match original_ty.node {
|
||||
::syntax::ast::TyKind::Path(_, ref path) => path.clone(),
|
||||
_ => { panic!("incompatible implementation"); }
|
||||
};
|
||||
let ident_map = IdentMap { original_path: original_path };
|
||||
let ident_map = IdentMap { original_path: original_path, meta_item: meta_item.clone() };
|
||||
ident_map
|
||||
}
|
||||
|
||||
@@ -791,6 +793,7 @@ fn implement_interface(
|
||||
builder: &aster::AstBuilder,
|
||||
item: &Item,
|
||||
push: &mut FnMut(Annotatable),
|
||||
meta_item: &MetaItem,
|
||||
) -> Result<InterfaceMap, Error> {
|
||||
let (generics, impl_trait, original_ty, dispatch_table) = match item.node {
|
||||
ast::ItemKind::Impl(_, _, ref generics, ref impl_trait, ref ty, ref impl_items) => {
|
||||
@@ -844,7 +847,7 @@ fn implement_interface(
|
||||
|
||||
let (handshake_arm, handshake_arm_buf) = implement_handshake_arm(cx);
|
||||
|
||||
let ty = ty_ident_map(&original_ty).ident(builder);
|
||||
let ty = ty_ident_map(&original_ty, meta_item).ident(builder);
|
||||
let (interface_endpoint, host_generics) = match impl_trait {
|
||||
Some(ref trait_) => (builder.id(::syntax::print::pprust::path_to_string(&trait_.path)), None),
|
||||
None => (ty, Some(&impl_generics)),
|
||||
@@ -884,7 +887,7 @@ fn implement_interface(
|
||||
).unwrap();
|
||||
|
||||
Ok(InterfaceMap {
|
||||
ident_map: ty_ident_map(&original_ty),
|
||||
ident_map: ty_ident_map(&original_ty, meta_item),
|
||||
original_item: item.clone(),
|
||||
item: ipc_item,
|
||||
dispatches: dispatch_table,
|
||||
|
||||
@@ -66,9 +66,11 @@ struct StripAttributeFolder<'a> {
|
||||
#[cfg(feature = "with-syntex")]
|
||||
impl<'a> fold::Folder for StripAttributeFolder<'a> {
|
||||
fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
|
||||
match attr.node.value.node {
|
||||
ast::MetaItemKind::List(ref n, _) if n == self.attr_title => { return None; }
|
||||
ast::MetaItemKind::Word(ref n) if n == self.attr_title => { return None; }
|
||||
let is_self = &*attr.value.name.as_str() == self.attr_title;
|
||||
|
||||
match attr.value.node {
|
||||
ast::MetaItemKind::List(_) if is_self => { return None; }
|
||||
ast::MetaItemKind::Word if is_self => { return None; }
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ fn binary_expr(
|
||||
_ => {
|
||||
cx.span_bug(item.span,
|
||||
"expected ItemStruct or ItemEnum in #[derive(Binary)]");
|
||||
Err(Error)
|
||||
Err(Error) as Result<BinaryExpressions, Error>
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -184,13 +184,17 @@ fn binary_expr_struct(
|
||||
let size_exprs: Vec<P<ast::Expr>> = fields.iter().enumerate().map(|(index, field)| {
|
||||
let raw_ident = ::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty));
|
||||
let index_ident = builder.id(format!("__field{}", index));
|
||||
let field_id = match field.ident {
|
||||
Some(ident) => builder.id(ident),
|
||||
None => builder.id(format!("{}", index)),
|
||||
};
|
||||
|
||||
match raw_ident.as_ref() {
|
||||
"u8" => {
|
||||
quote_expr!(cx, 1)
|
||||
},
|
||||
"[u8]" => {
|
||||
value_ident.and_then(|x| {
|
||||
let field_id = builder.id(field.ident.unwrap());
|
||||
Some(quote_expr!(cx, $x. $field_id .len()))
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
@@ -207,7 +211,6 @@ fn binary_expr_struct(
|
||||
|
||||
value_ident.and_then(|x|
|
||||
{
|
||||
let field_id = builder.id(field.ident.unwrap());
|
||||
Some(quote_expr!(cx,
|
||||
match $field_type_ident_qualified::len_params() {
|
||||
0 => ::std::mem::size_of::<$field_type_ident>(),
|
||||
@@ -232,12 +235,12 @@ fn binary_expr_struct(
|
||||
}
|
||||
|
||||
let mut write_stmts = Vec::<ast::Stmt>::new();
|
||||
write_stmts.push(quote_stmt!(cx, let mut offset = 0usize;).unwrap());
|
||||
write_stmts.push(quote_stmt!(cx, let mut offset = 0usize;).expect("stmt1"));
|
||||
|
||||
let mut map_stmts = Vec::<ast::Stmt>::new();
|
||||
let field_amount = builder.id(&format!("{}",fields.len()));
|
||||
map_stmts.push(quote_stmt!(cx, let mut map = vec![0usize; $field_amount];).unwrap());
|
||||
map_stmts.push(quote_stmt!(cx, let mut total = 0usize;).unwrap());
|
||||
map_stmts.push(quote_stmt!(cx, let mut map = vec![0usize; $field_amount];).expect("stmt2"));
|
||||
map_stmts.push(quote_stmt!(cx, let mut total = 0usize;).expect("stmt3"));
|
||||
|
||||
let mut post_write_stmts = Vec::<ast::Stmt>::new();
|
||||
|
||||
@@ -248,9 +251,12 @@ fn binary_expr_struct(
|
||||
let field_type_ident_qualified = builder.id(
|
||||
replace_qualified(&::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty))));
|
||||
|
||||
let field_id = match field.ident {
|
||||
Some(ident) => builder.id(ident),
|
||||
None => builder.id(format!("{}", index)),
|
||||
};
|
||||
let member_expr = match value_ident {
|
||||
Some(x) => {
|
||||
let field_id = builder.id(field.ident.unwrap());
|
||||
quote_expr!(cx, $x . $field_id)
|
||||
},
|
||||
None => {
|
||||
@@ -267,8 +273,8 @@ fn binary_expr_struct(
|
||||
|
||||
match raw_ident.as_ref() {
|
||||
"u8" => {
|
||||
write_stmts.push(quote_stmt!(cx, let next_line = offset + 1;).unwrap());
|
||||
write_stmts.push(quote_stmt!(cx, buffer[offset] = $member_expr; ).unwrap());
|
||||
write_stmts.push(quote_stmt!(cx, let next_line = offset + 1;).expect("stmt4"));
|
||||
write_stmts.push(quote_stmt!(cx, buffer[offset] = $member_expr; ).expect("stm5"));
|
||||
},
|
||||
"[u8]" => {
|
||||
write_stmts.push(quote_stmt!(cx, let size = $member_expr .len();).unwrap());
|
||||
@@ -374,7 +380,7 @@ fn binary_expr_item_struct(
|
||||
cx.span_bug(span,
|
||||
&format!("#[derive(Binary)] Unsupported struct content, expected tuple/struct, found: {:?}",
|
||||
variant_data));
|
||||
Err(Error)
|
||||
Err(Error) as Result<BinaryExpressions, Error>
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -431,13 +437,12 @@ fn fields_sequence(
|
||||
variant_ident: &ast::Ident,
|
||||
) -> ast::Expr {
|
||||
use syntax::parse::token;
|
||||
use syntax::ast::TokenTree::Token;
|
||||
use syntax::tokenstream::TokenTree::Token;
|
||||
|
||||
let named_members = fields.iter().any(|f| f.ident.is_some());
|
||||
|
||||
::quasi::parse_expr_panic(&mut ::syntax::parse::new_parser_from_tts(
|
||||
ext_cx.parse_sess(),
|
||||
ext_cx.cfg(),
|
||||
{
|
||||
let _sp = ext_cx.call_site();
|
||||
let mut tt = ::std::vec::Vec::new();
|
||||
@@ -569,11 +574,10 @@ fn named_fields_sequence(
|
||||
fields: &[ast::StructField],
|
||||
) -> ast::Stmt {
|
||||
use syntax::parse::token;
|
||||
use syntax::ast::TokenTree::Token;
|
||||
use syntax::tokenstream::TokenTree::Token;
|
||||
|
||||
::quasi::parse_stmt_panic(&mut ::syntax::parse::new_parser_from_tts(
|
||||
ext_cx.parse_sess(),
|
||||
ext_cx.cfg(),
|
||||
{
|
||||
let _sp = ext_cx.call_site();
|
||||
let mut tt = ::std::vec::Vec::new();
|
||||
@@ -590,7 +594,10 @@ fn named_fields_sequence(
|
||||
tt.push(Token(_sp, token::OpenDelim(token::Brace)));
|
||||
|
||||
for (idx, field) in fields.iter().enumerate() {
|
||||
tt.push(Token(_sp, token::Ident(field.ident.clone().expect("function is called for named fields"))));
|
||||
tt.push(Token(_sp, match field.ident {
|
||||
Some(ident) => token::Ident(ident),
|
||||
None => token::Ident(ext_cx.ident_of(&format!("{}", idx))),
|
||||
}));
|
||||
tt.push(Token(_sp, token::Colon));
|
||||
|
||||
// special case for u8, it just takes byte form sequence
|
||||
|
||||
@@ -9,7 +9,7 @@ build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
ethcore-ipc = { path = "../rpc" }
|
||||
nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git" }
|
||||
nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git", branch = "parity-1.7" }
|
||||
ethcore-ipc-nano = { path = "../nano" }
|
||||
semver = "0.5"
|
||||
log = "0.3"
|
||||
|
||||
@@ -8,6 +8,6 @@ license = "GPL-3.0"
|
||||
|
||||
[dependencies]
|
||||
ethcore-ipc = { path = "../rpc" }
|
||||
nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git" }
|
||||
nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git", branch = "parity-1.7" }
|
||||
log = "0.3"
|
||||
lazy_static = "0.2"
|
||||
|
||||
@@ -8,6 +8,6 @@ license = "GPL-3.0"
|
||||
|
||||
[dependencies]
|
||||
ethcore-devtools = { path = "../../devtools" }
|
||||
nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git" }
|
||||
nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git", branch = "parity-1.7" }
|
||||
ethcore-util = { path = "../../util" }
|
||||
semver = "0.5"
|
||||
|
||||
@@ -11,7 +11,7 @@ path = "run.rs"
|
||||
ethcore-ipc = { path = "../rpc" }
|
||||
ethcore-devtools = { path = "../../devtools" }
|
||||
semver = "0.5"
|
||||
nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git" }
|
||||
nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git", branch = "parity-1.7" }
|
||||
ethcore-ipc-nano = { path = "../nano" }
|
||||
ethcore-util = { path = "../../util" }
|
||||
log = "0.3"
|
||||
|
||||
@@ -12,8 +12,8 @@ use-precompiled-js = ["parity-dapps-glue/use-precompiled-js"]
|
||||
with-syntex = ["parity-dapps-glue/with-syntex"]
|
||||
|
||||
[build-dependencies]
|
||||
parity-dapps-glue = "1.4"
|
||||
parity-dapps-glue = "1.7"
|
||||
|
||||
[dependencies]
|
||||
parity-dapps-glue = "1.4"
|
||||
parity-dapps-glue = "1.7"
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
description = "Parity built-in dapps."
|
||||
name = "parity-ui-dev"
|
||||
version = "1.4.0"
|
||||
version = "1.7.0"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
@@ -11,8 +11,8 @@ default = ["with-syntex"]
|
||||
with-syntex = ["parity-dapps-glue/with-syntex"]
|
||||
|
||||
[build-dependencies]
|
||||
parity-dapps-glue = "1.4"
|
||||
parity-dapps-glue = "1.7"
|
||||
|
||||
[dependencies]
|
||||
parity-dapps-glue = "1.4"
|
||||
parity-dapps-glue = "1.7"
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ set -e
|
||||
UTCDATE=`date -u "+%Y%m%d-%H%M%S"`
|
||||
PACKAGES=( "parity" "etherscan" "shapeshift" "jsonrpc" )
|
||||
BRANCH=$CI_BUILD_REF_NAME
|
||||
GIT_JS_PRECOMPILED="https://${GITHUB_JS_PRECOMPILED}:@github.com/ethcore/js-precompiled.git"
|
||||
GIT_PARITY="https://${GITHUB_JS_PRECOMPILED}:@github.com/ethcore/parity.git"
|
||||
GIT_JS_PRECOMPILED="https://${GITHUB_JS_PRECOMPILED}:@github.com/paritytech/js-precompiled.git"
|
||||
GIT_PARITY="https://${GITHUB_JS_PRECOMPILED}:@github.com/paritytech/parity.git"
|
||||
|
||||
# setup the git user defaults for the current repo
|
||||
function setup_git_user {
|
||||
|
||||
@@ -107,34 +107,26 @@ export default class Contract {
|
||||
});
|
||||
}
|
||||
|
||||
deploy (options, values, statecb) {
|
||||
const setState = (state) => {
|
||||
if (!statecb) {
|
||||
return;
|
||||
}
|
||||
|
||||
return statecb(null, state);
|
||||
};
|
||||
|
||||
setState({ state: 'estimateGas' });
|
||||
deploy (options, values, statecb = () => {}) {
|
||||
statecb(null, { state: 'estimateGas' });
|
||||
|
||||
return this
|
||||
.deployEstimateGas(options, values)
|
||||
.then(([gasEst, gas]) => {
|
||||
options.gas = gas.toFixed(0);
|
||||
|
||||
setState({ state: 'postTransaction', gas });
|
||||
statecb(null, { state: 'postTransaction', gas });
|
||||
|
||||
const _options = this._encodeOptions(this.constructors[0], options, values);
|
||||
const encodedOptions = this._encodeOptions(this.constructors[0], options, values);
|
||||
|
||||
return this._api.parity
|
||||
.postTransaction(_options)
|
||||
.postTransaction(encodedOptions)
|
||||
.then((requestId) => {
|
||||
setState({ state: 'checkRequest', requestId });
|
||||
statecb(null, { state: 'checkRequest', requestId });
|
||||
return this._pollCheckRequest(requestId);
|
||||
})
|
||||
.then((txhash) => {
|
||||
setState({ state: 'getTransactionReceipt', txhash });
|
||||
statecb(null, { state: 'getTransactionReceipt', txhash });
|
||||
return this._pollTransactionReceipt(txhash, gas);
|
||||
})
|
||||
.then((receipt) => {
|
||||
@@ -142,23 +134,23 @@ export default class Contract {
|
||||
throw new Error(`Contract not deployed, gasUsed == ${gas.toFixed(0)}`);
|
||||
}
|
||||
|
||||
setState({ state: 'hasReceipt', receipt });
|
||||
statecb(null, { state: 'hasReceipt', receipt });
|
||||
this._receipt = receipt;
|
||||
this._address = receipt.contractAddress;
|
||||
return this._address;
|
||||
});
|
||||
})
|
||||
.then((address) => {
|
||||
setState({ state: 'getCode' });
|
||||
return this._api.eth.getCode(this._address);
|
||||
})
|
||||
.then((code) => {
|
||||
if (code === '0x') {
|
||||
throw new Error('Contract not deployed, getCode returned 0x');
|
||||
}
|
||||
})
|
||||
.then((address) => {
|
||||
statecb(null, { state: 'getCode' });
|
||||
return this._api.eth.getCode(this._address);
|
||||
})
|
||||
.then((code) => {
|
||||
if (code === '0x') {
|
||||
throw new Error('Contract not deployed, getCode returned 0x');
|
||||
}
|
||||
|
||||
setState({ state: 'completed' });
|
||||
return this._address;
|
||||
statecb(null, { state: 'completed' });
|
||||
return this._address;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -143,8 +143,15 @@ export function inOptions (options) {
|
||||
if (options) {
|
||||
Object.keys(options).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'from':
|
||||
case 'to':
|
||||
// Don't encode the `to` option if it's empty
|
||||
// (eg. contract deployments)
|
||||
if (options[key]) {
|
||||
options[key] = inAddress(options[key]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'from':
|
||||
options[key] = inAddress(options[key]);
|
||||
break;
|
||||
|
||||
|
||||
@@ -208,6 +208,13 @@ describe('api/format/input', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('does not encode an empty `to` value', () => {
|
||||
const options = { to: '' };
|
||||
const formatted = inOptions(options);
|
||||
|
||||
expect(formatted.to).to.equal('');
|
||||
});
|
||||
|
||||
['gas', 'gasPrice', 'value', 'minBlock', 'nonce'].forEach((input) => {
|
||||
it(`formats ${input} number as hexnumber`, () => {
|
||||
const block = {};
|
||||
|
||||
466
js/src/contracts/abi/old-wallet.json
Normal file
466
js/src/contracts/abi/old-wallet.json
Normal file
@@ -0,0 +1,466 @@
|
||||
[
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_owner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "removeOwner",
|
||||
"outputs": [],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_addr",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "isOwner",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "m_numOwners",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "m_lastDay",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [],
|
||||
"name": "resetSpentToday",
|
||||
"outputs": [],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "m_spentToday",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_owner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "addOwner",
|
||||
"outputs": [],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "m_required",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_h",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"name": "confirm",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_newLimit",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "setDailyLimit",
|
||||
"outputs": [],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_value",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_data",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"name": "execute",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "_r",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_operation",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"name": "revoke",
|
||||
"outputs": [],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_newRequired",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "changeRequirement",
|
||||
"outputs": [],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_operation",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"name": "_owner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "hasConfirmed",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "ownerIndex",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getOwner",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_to",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "kill",
|
||||
"outputs": [],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_from",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_to",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "changeOwner",
|
||||
"outputs": [],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "m_dailyLimit",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_owners",
|
||||
"type": "address[]"
|
||||
},
|
||||
{
|
||||
"name": "_required",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_daylimit",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"type": "constructor"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "operation",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"name": "Confirmation",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "operation",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"name": "Revoke",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "oldOwner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "newOwner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "OwnerChanged",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "newOwner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "OwnerAdded",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "oldOwner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "OwnerRemoved",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "newRequirement",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "RequirementChanged",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "_from",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Deposit",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "data",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"name": "SingleTransact",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "operation",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "data",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"name": "MultiTransact",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "operation",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "initiator",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "data",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"name": "ConfirmationNeeded",
|
||||
"type": "event"
|
||||
}
|
||||
]
|
||||
@@ -1 +1,476 @@
|
||||
[{"constant":false,"inputs":[{"name":"_owner","type":"address"}],"name":"removeOwner","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_addr","type":"address"}],"name":"isOwner","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":true,"inputs":[],"name":"m_numOwners","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"m_lastDay","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"resetSpentToday","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"m_spentToday","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"_owner","type":"address"}],"name":"addOwner","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"m_required","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"_h","type":"bytes32"}],"name":"confirm","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_newLimit","type":"uint256"}],"name":"setDailyLimit","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"},{"name":"_data","type":"bytes"}],"name":"execute","outputs":[{"name":"_r","type":"bytes32"}],"type":"function"},{"constant":false,"inputs":[{"name":"_operation","type":"bytes32"}],"name":"revoke","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_newRequired","type":"uint256"}],"name":"changeRequirement","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_operation","type":"bytes32"},{"name":"_owner","type":"address"}],"name":"hasConfirmed","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"ownerIndex","type":"uint256"}],"name":"getOwner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"}],"name":"kill","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"}],"name":"changeOwner","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"m_dailyLimit","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"inputs":[{"name":"_owners","type":"address[]"},{"name":"_required","type":"uint256"},{"name":"_daylimit","type":"uint256"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"operation","type":"bytes32"}],"name":"Confirmation","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"operation","type":"bytes32"}],"name":"Revoke","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldOwner","type":"address"},{"indexed":false,"name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newOwner","type":"address"}],"name":"OwnerAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldOwner","type":"address"}],"name":"OwnerRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newRequirement","type":"uint256"}],"name":"RequirementChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_from","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"data","type":"bytes"}],"name":"SingleTransact","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"operation","type":"bytes32"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"data","type":"bytes"}],"name":"MultiTransact","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"operation","type":"bytes32"},{"indexed":false,"name":"initiator","type":"address"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"data","type":"bytes"}],"name":"ConfirmationNeeded","type":"event"}]
|
||||
[
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_owner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "removeOwner",
|
||||
"outputs": [],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_addr",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "isOwner",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "m_numOwners",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "m_lastDay",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [],
|
||||
"name": "resetSpentToday",
|
||||
"outputs": [],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "m_spentToday",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_owner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "addOwner",
|
||||
"outputs": [],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "m_required",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_h",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"name": "confirm",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_newLimit",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "setDailyLimit",
|
||||
"outputs": [],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_value",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_data",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"name": "execute",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "_r",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_operation",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"name": "revoke",
|
||||
"outputs": [],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_newRequired",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "changeRequirement",
|
||||
"outputs": [],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_operation",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"name": "_owner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "hasConfirmed",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "ownerIndex",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getOwner",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_to",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "kill",
|
||||
"outputs": [],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_from",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_to",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "changeOwner",
|
||||
"outputs": [],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "m_dailyLimit",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_owners",
|
||||
"type": "address[]"
|
||||
},
|
||||
{
|
||||
"name": "_required",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_daylimit",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"type": "constructor"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "operation",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"name": "Confirmation",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "operation",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"name": "Revoke",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "oldOwner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "newOwner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "OwnerChanged",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "newOwner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "OwnerAdded",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "oldOwner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "OwnerRemoved",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "newRequirement",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "RequirementChanged",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "_from",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Deposit",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "data",
|
||||
"type": "bytes"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "created",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "SingleTransact",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "operation",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "data",
|
||||
"type": "bytes"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "created",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "MultiTransact",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "operation",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "initiator",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "data",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"name": "ConfirmationNeeded",
|
||||
"type": "event"
|
||||
}
|
||||
]
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -16,7 +16,14 @@
|
||||
|
||||
import * as abis from './abi';
|
||||
|
||||
const REGISTRY_V1_HASHES = [
|
||||
'0x34f7c51bbb1b1902fbdabfdf04811100f5c9f998f26dd535d2f6f977492c748e', // ropsten
|
||||
'0x64c3ee34851517a9faecd995c102b339f03e564ad6772dc43a26f993238b20ec' // homestead
|
||||
];
|
||||
|
||||
export default class Registry {
|
||||
_registryContract = null;
|
||||
|
||||
constructor (api) {
|
||||
this._api = api;
|
||||
|
||||
@@ -43,11 +50,10 @@ export default class Registry {
|
||||
|
||||
this._fetching = true;
|
||||
|
||||
return this._api.parity
|
||||
.registryAddress()
|
||||
.then((address) => {
|
||||
return this.fetchContract()
|
||||
.then((contract) => {
|
||||
this._fetching = false;
|
||||
this._instance = this._api.newContract(abis.registry, address).instance;
|
||||
this._instance = contract.instance;
|
||||
|
||||
this._queue.forEach((queued) => {
|
||||
queued.resolve(this._instance);
|
||||
@@ -89,6 +95,47 @@ export default class Registry {
|
||||
.then((contract) => contract.instance);
|
||||
}
|
||||
|
||||
fetchContract () {
|
||||
if (this._registryContract) {
|
||||
return Promise.resolve(this._registryContract);
|
||||
}
|
||||
|
||||
return this._api.parity
|
||||
.registryAddress()
|
||||
.then((address) => Promise.all([ address, this._api.eth.getCode(address) ]))
|
||||
.then(([ address, code ]) => {
|
||||
const codeHash = this._api.util.sha3(code);
|
||||
const version = REGISTRY_V1_HASHES.includes(codeHash)
|
||||
? 1
|
||||
: 2;
|
||||
const abi = version === 1
|
||||
? abis.registry
|
||||
: abis.registry2;
|
||||
const contract = this._api.newContract(abi, address);
|
||||
|
||||
// Add support for previous `set` and `get` methods
|
||||
if (!contract.instance.get && contract.instance.getData) {
|
||||
contract.instance.get = contract.instance.getData;
|
||||
}
|
||||
|
||||
if (contract.instance.get && !contract.instance.getData) {
|
||||
contract.instance.getData = contract.instance.get;
|
||||
}
|
||||
|
||||
if (!contract.instance.set && contract.instance.setData) {
|
||||
contract.instance.set = contract.instance.setData;
|
||||
}
|
||||
|
||||
if (contract.instance.set && !contract.instance.setData) {
|
||||
contract.instance.setData = contract.instance.set;
|
||||
}
|
||||
|
||||
console.log(`registry at ${address}, code ${codeHash}, version ${version}`);
|
||||
this._registryContract = contract;
|
||||
return this._registryContract;
|
||||
});
|
||||
}
|
||||
|
||||
_createGetParams (_name, key) {
|
||||
const name = _name.toLowerCase();
|
||||
const sha3 = this._api.util.sha3.text(name);
|
||||
|
||||
@@ -35,6 +35,9 @@ function create () {
|
||||
}
|
||||
};
|
||||
api = {
|
||||
eth: {
|
||||
getCode: sinon.stub().resolves(0)
|
||||
},
|
||||
parity: {
|
||||
registryAddress: sinon.stub().resolves('testRegistryAddress')
|
||||
},
|
||||
|
||||
@@ -8,453 +8,454 @@
|
||||
// use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by
|
||||
// some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the
|
||||
// interior is executed.
|
||||
pragma solidity ^0.4.6;
|
||||
|
||||
contract multisig {
|
||||
// EVENTS
|
||||
pragma solidity ^0.4.9;
|
||||
|
||||
// this contract can accept a confirmation, in which case
|
||||
// we record owner and operation (hash) alongside it.
|
||||
event Confirmation(address owner, bytes32 operation);
|
||||
event Revoke(address owner, bytes32 operation);
|
||||
contract WalletEvents {
|
||||
// EVENTS
|
||||
|
||||
// some others are in the case of an owner changing.
|
||||
event OwnerChanged(address oldOwner, address newOwner);
|
||||
event OwnerAdded(address newOwner);
|
||||
event OwnerRemoved(address oldOwner);
|
||||
// this contract only has six types of events: it can accept a confirmation, in which case
|
||||
// we record owner and operation (hash) alongside it.
|
||||
event Confirmation(address owner, bytes32 operation);
|
||||
event Revoke(address owner, bytes32 operation);
|
||||
|
||||
// the last one is emitted if the required signatures change
|
||||
event RequirementChanged(uint newRequirement);
|
||||
// some others are in the case of an owner changing.
|
||||
event OwnerChanged(address oldOwner, address newOwner);
|
||||
event OwnerAdded(address newOwner);
|
||||
event OwnerRemoved(address oldOwner);
|
||||
|
||||
// Funds has arrived into the wallet (record how much).
|
||||
event Deposit(address _from, uint value);
|
||||
// Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
|
||||
event SingleTransact(address owner, uint value, address to, bytes data);
|
||||
// Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
|
||||
event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data);
|
||||
// Confirmation still needed for a transaction.
|
||||
event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
|
||||
// the last one is emitted if the required signatures change
|
||||
event RequirementChanged(uint newRequirement);
|
||||
|
||||
// Funds has arrived into the wallet (record how much).
|
||||
event Deposit(address _from, uint value);
|
||||
// Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
|
||||
event SingleTransact(address owner, uint value, address to, bytes data, address created);
|
||||
// Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
|
||||
event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data, address created);
|
||||
// Confirmation still needed for a transaction.
|
||||
event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
|
||||
}
|
||||
|
||||
contract multisigAbi is multisig {
|
||||
function isOwner(address _addr) returns (bool);
|
||||
contract WalletAbi {
|
||||
// Revokes a prior confirmation of the given operation
|
||||
function revoke(bytes32 _operation) external;
|
||||
|
||||
function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool);
|
||||
// Replaces an owner `_from` with another `_to`.
|
||||
function changeOwner(address _from, address _to) external;
|
||||
|
||||
function confirm(bytes32 _h) returns(bool);
|
||||
function addOwner(address _owner) external;
|
||||
|
||||
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
|
||||
function setDailyLimit(uint _newLimit);
|
||||
function removeOwner(address _owner) external;
|
||||
|
||||
function addOwner(address _owner);
|
||||
function changeRequirement(uint _newRequired) external;
|
||||
|
||||
function removeOwner(address _owner);
|
||||
function isOwner(address _addr) constant returns (bool);
|
||||
|
||||
function changeRequirement(uint _newRequired);
|
||||
function hasConfirmed(bytes32 _operation, address _owner) external constant returns (bool);
|
||||
|
||||
// Revokes a prior confirmation of the given operation
|
||||
function revoke(bytes32 _operation);
|
||||
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
|
||||
function setDailyLimit(uint _newLimit) external;
|
||||
|
||||
function changeOwner(address _from, address _to);
|
||||
|
||||
function execute(address _to, uint _value, bytes _data) returns(bool);
|
||||
function execute(address _to, uint _value, bytes _data) external returns (bytes32 o_hash);
|
||||
function confirm(bytes32 _h) returns (bool o_success);
|
||||
}
|
||||
|
||||
contract WalletLibrary is multisig {
|
||||
// TYPES
|
||||
contract WalletLibrary is WalletEvents {
|
||||
// TYPES
|
||||
|
||||
// struct for the status of a pending operation.
|
||||
struct PendingState {
|
||||
uint yetNeeded;
|
||||
uint ownersDone;
|
||||
uint index;
|
||||
// struct for the status of a pending operation.
|
||||
struct PendingState {
|
||||
uint yetNeeded;
|
||||
uint ownersDone;
|
||||
uint index;
|
||||
}
|
||||
|
||||
// Transaction structure to remember details of transaction lest it need be saved for a later call.
|
||||
struct Transaction {
|
||||
address to;
|
||||
uint value;
|
||||
bytes data;
|
||||
}
|
||||
|
||||
// MODIFIERS
|
||||
|
||||
// simple single-sig function modifier.
|
||||
modifier onlyowner {
|
||||
if (isOwner(msg.sender))
|
||||
_;
|
||||
}
|
||||
// multi-sig function modifier: the operation must have an intrinsic hash in order
|
||||
// that later attempts can be realised as the same underlying operation and
|
||||
// thus count as confirmations.
|
||||
modifier onlymanyowners(bytes32 _operation) {
|
||||
if (confirmAndCheck(_operation))
|
||||
_;
|
||||
}
|
||||
|
||||
// METHODS
|
||||
|
||||
// gets called when no other function matches
|
||||
function() payable {
|
||||
// just being sent some cash?
|
||||
if (msg.value > 0)
|
||||
Deposit(msg.sender, msg.value);
|
||||
}
|
||||
|
||||
// constructor is given number of sigs required to do protected "onlymanyowners" transactions
|
||||
// as well as the selection of addresses capable of confirming them.
|
||||
function initMultiowned(address[] _owners, uint _required) {
|
||||
m_numOwners = _owners.length + 1;
|
||||
m_owners[1] = uint(msg.sender);
|
||||
m_ownerIndex[uint(msg.sender)] = 1;
|
||||
for (uint i = 0; i < _owners.length; ++i)
|
||||
{
|
||||
m_owners[2 + i] = uint(_owners[i]);
|
||||
m_ownerIndex[uint(_owners[i])] = 2 + i;
|
||||
}
|
||||
m_required = _required;
|
||||
}
|
||||
|
||||
// Revokes a prior confirmation of the given operation
|
||||
function revoke(bytes32 _operation) external {
|
||||
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
||||
// make sure they're an owner
|
||||
if (ownerIndex == 0) return;
|
||||
uint ownerIndexBit = 2**ownerIndex;
|
||||
var pending = m_pending[_operation];
|
||||
if (pending.ownersDone & ownerIndexBit > 0) {
|
||||
pending.yetNeeded++;
|
||||
pending.ownersDone -= ownerIndexBit;
|
||||
Revoke(msg.sender, _operation);
|
||||
}
|
||||
}
|
||||
|
||||
// Replaces an owner `_from` with another `_to`.
|
||||
function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) external {
|
||||
if (isOwner(_to)) return;
|
||||
uint ownerIndex = m_ownerIndex[uint(_from)];
|
||||
if (ownerIndex == 0) return;
|
||||
|
||||
clearPending();
|
||||
m_owners[ownerIndex] = uint(_to);
|
||||
m_ownerIndex[uint(_from)] = 0;
|
||||
m_ownerIndex[uint(_to)] = ownerIndex;
|
||||
OwnerChanged(_from, _to);
|
||||
}
|
||||
|
||||
function addOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
|
||||
if (isOwner(_owner)) return;
|
||||
|
||||
clearPending();
|
||||
if (m_numOwners >= c_maxOwners)
|
||||
reorganizeOwners();
|
||||
if (m_numOwners >= c_maxOwners)
|
||||
return;
|
||||
m_numOwners++;
|
||||
m_owners[m_numOwners] = uint(_owner);
|
||||
m_ownerIndex[uint(_owner)] = m_numOwners;
|
||||
OwnerAdded(_owner);
|
||||
}
|
||||
|
||||
function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
|
||||
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
||||
if (ownerIndex == 0) return;
|
||||
if (m_required > m_numOwners - 1) return;
|
||||
|
||||
m_owners[ownerIndex] = 0;
|
||||
m_ownerIndex[uint(_owner)] = 0;
|
||||
clearPending();
|
||||
reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot
|
||||
OwnerRemoved(_owner);
|
||||
}
|
||||
|
||||
function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external {
|
||||
if (_newRequired > m_numOwners) return;
|
||||
m_required = _newRequired;
|
||||
clearPending();
|
||||
RequirementChanged(_newRequired);
|
||||
}
|
||||
|
||||
// Gets an owner by 0-indexed position (using numOwners as the count)
|
||||
function getOwner(uint ownerIndex) external constant returns (address) {
|
||||
return address(m_owners[ownerIndex + 1]);
|
||||
}
|
||||
|
||||
function isOwner(address _addr) constant returns (bool) {
|
||||
return m_ownerIndex[uint(_addr)] > 0;
|
||||
}
|
||||
|
||||
function hasConfirmed(bytes32 _operation, address _owner) external constant returns (bool) {
|
||||
var pending = m_pending[_operation];
|
||||
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
||||
|
||||
// make sure they're an owner
|
||||
if (ownerIndex == 0) return false;
|
||||
|
||||
// determine the bit to set for this owner.
|
||||
uint ownerIndexBit = 2**ownerIndex;
|
||||
return !(pending.ownersDone & ownerIndexBit == 0);
|
||||
}
|
||||
|
||||
// constructor - stores initial daily limit and records the present day's index.
|
||||
function initDaylimit(uint _limit) {
|
||||
m_dailyLimit = _limit;
|
||||
m_lastDay = today();
|
||||
}
|
||||
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
|
||||
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external {
|
||||
m_dailyLimit = _newLimit;
|
||||
}
|
||||
// resets the amount already spent today. needs many of the owners to confirm.
|
||||
function resetSpentToday() onlymanyowners(sha3(msg.data)) external {
|
||||
m_spentToday = 0;
|
||||
}
|
||||
|
||||
// constructor - just pass on the owner array to the multiowned and
|
||||
// the limit to daylimit
|
||||
function initWallet(address[] _owners, uint _required, uint _daylimit) {
|
||||
initDaylimit(_daylimit);
|
||||
initMultiowned(_owners, _required);
|
||||
}
|
||||
|
||||
// kills the contract sending everything to `_to`.
|
||||
function kill(address _to) onlymanyowners(sha3(msg.data)) external {
|
||||
suicide(_to);
|
||||
}
|
||||
|
||||
// Outside-visible transact entry point. Executes transaction immediately if below daily spend limit.
|
||||
// If not, goes into multisig process. We provide a hash on return to allow the sender to provide
|
||||
// shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
|
||||
// and _data arguments). They still get the option of using them if they want, anyways.
|
||||
function execute(address _to, uint _value, bytes _data) external onlyowner returns (bytes32 o_hash) {
|
||||
// first, take the opportunity to check that we're under the daily limit.
|
||||
if ((_data.length == 0 && underLimit(_value)) || m_required == 1) {
|
||||
// yes - just execute the call.
|
||||
address created;
|
||||
if (_to == 0) {
|
||||
created = create(_value, _data);
|
||||
} else {
|
||||
if (!_to.call.value(_value)(_data))
|
||||
throw;
|
||||
}
|
||||
SingleTransact(msg.sender, _value, _to, _data, created);
|
||||
} else {
|
||||
// determine our operation hash.
|
||||
o_hash = sha3(msg.data, block.number);
|
||||
// store if it's new
|
||||
if (m_txs[o_hash].to == 0 && m_txs[o_hash].value == 0 && m_txs[o_hash].data.length == 0) {
|
||||
m_txs[o_hash].to = _to;
|
||||
m_txs[o_hash].value = _value;
|
||||
m_txs[o_hash].data = _data;
|
||||
}
|
||||
if (!confirm(o_hash)) {
|
||||
ConfirmationNeeded(o_hash, msg.sender, _value, _to, _data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function create(uint _value, bytes _code) internal returns (address o_addr) {
|
||||
assembly {
|
||||
o_addr := create(_value, add(_code, 0x20), mload(_code))
|
||||
jumpi(invalidJumpLabel, iszero(extcodesize(o_addr)))
|
||||
}
|
||||
}
|
||||
|
||||
// confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order
|
||||
// to determine the body of the transaction from the hash provided.
|
||||
function confirm(bytes32 _h) onlymanyowners(_h) returns (bool o_success) {
|
||||
if (m_txs[_h].to != 0 || m_txs[_h].value != 0 || m_txs[_h].data.length != 0) {
|
||||
address created;
|
||||
if (m_txs[_h].to == 0) {
|
||||
created = create(m_txs[_h].value, m_txs[_h].data);
|
||||
} else {
|
||||
if (!m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data))
|
||||
throw;
|
||||
}
|
||||
|
||||
MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data, created);
|
||||
delete m_txs[_h];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// INTERNAL METHODS
|
||||
|
||||
function confirmAndCheck(bytes32 _operation) internal returns (bool) {
|
||||
// determine what index the present sender is:
|
||||
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
||||
// make sure they're an owner
|
||||
if (ownerIndex == 0) return;
|
||||
|
||||
var pending = m_pending[_operation];
|
||||
// if we're not yet working on this operation, switch over and reset the confirmation status.
|
||||
if (pending.yetNeeded == 0) {
|
||||
// reset count of confirmations needed.
|
||||
pending.yetNeeded = m_required;
|
||||
// reset which owners have confirmed (none) - set our bitmap to 0.
|
||||
pending.ownersDone = 0;
|
||||
pending.index = m_pendingIndex.length++;
|
||||
m_pendingIndex[pending.index] = _operation;
|
||||
}
|
||||
// determine the bit to set for this owner.
|
||||
uint ownerIndexBit = 2**ownerIndex;
|
||||
// make sure we (the message sender) haven't confirmed this operation previously.
|
||||
if (pending.ownersDone & ownerIndexBit == 0) {
|
||||
Confirmation(msg.sender, _operation);
|
||||
// ok - check if count is enough to go ahead.
|
||||
if (pending.yetNeeded <= 1) {
|
||||
// enough confirmations: reset and run interior.
|
||||
delete m_pendingIndex[m_pending[_operation].index];
|
||||
delete m_pending[_operation];
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// not enough: record that this owner in particular confirmed.
|
||||
pending.yetNeeded--;
|
||||
pending.ownersDone |= ownerIndexBit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function reorganizeOwners() private {
|
||||
uint free = 1;
|
||||
while (free < m_numOwners)
|
||||
{
|
||||
while (free < m_numOwners && m_owners[free] != 0) free++;
|
||||
while (m_numOwners > 1 && m_owners[m_numOwners] == 0) m_numOwners--;
|
||||
if (free < m_numOwners && m_owners[m_numOwners] != 0 && m_owners[free] == 0)
|
||||
{
|
||||
m_owners[free] = m_owners[m_numOwners];
|
||||
m_ownerIndex[m_owners[free]] = free;
|
||||
m_owners[m_numOwners] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
|
||||
// returns true. otherwise just returns false.
|
||||
function underLimit(uint _value) internal onlyowner returns (bool) {
|
||||
// reset the spend limit if we're on a different day to last time.
|
||||
if (today() > m_lastDay) {
|
||||
m_spentToday = 0;
|
||||
m_lastDay = today();
|
||||
}
|
||||
// check to see if there's enough left - if so, subtract and return true.
|
||||
// overflow protection // dailyLimit check
|
||||
if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) {
|
||||
m_spentToday += _value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// determines today's index.
|
||||
function today() private constant returns (uint) { return now / 1 days; }
|
||||
|
||||
function clearPending() internal {
|
||||
uint length = m_pendingIndex.length;
|
||||
|
||||
for (uint i = 0; i < length; ++i) {
|
||||
delete m_txs[m_pendingIndex[i]];
|
||||
|
||||
if (m_pendingIndex[i] != 0)
|
||||
delete m_pending[m_pendingIndex[i]];
|
||||
}
|
||||
|
||||
// Transaction structure to remember details of transaction lest it need be saved for a later call.
|
||||
struct Transaction {
|
||||
address to;
|
||||
uint value;
|
||||
bytes data;
|
||||
}
|
||||
delete m_pendingIndex;
|
||||
}
|
||||
|
||||
/******************************
|
||||
***** MULTI OWNED SECTION ****
|
||||
******************************/
|
||||
// FIELDS
|
||||
address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe;
|
||||
|
||||
// MODIFIERS
|
||||
// the number of owners that must confirm the same operation before it is run.
|
||||
uint public m_required;
|
||||
// pointer used to find a free slot in m_owners
|
||||
uint public m_numOwners;
|
||||
|
||||
// simple single-sig function modifier.
|
||||
modifier onlyowner {
|
||||
if (isOwner(msg.sender))
|
||||
_;
|
||||
}
|
||||
// multi-sig function modifier: the operation must have an intrinsic hash in order
|
||||
// that later attempts can be realised as the same underlying operation and
|
||||
// thus count as confirmations.
|
||||
modifier onlymanyowners(bytes32 _operation) {
|
||||
if (confirmAndCheck(_operation))
|
||||
_;
|
||||
}
|
||||
uint public m_dailyLimit;
|
||||
uint public m_spentToday;
|
||||
uint public m_lastDay;
|
||||
|
||||
// METHODS
|
||||
// list of owners
|
||||
uint[256] m_owners;
|
||||
|
||||
// constructor is given number of sigs required to do protected "onlymanyowners" transactions
|
||||
// as well as the selection of addresses capable of confirming them.
|
||||
function initMultiowned(address[] _owners, uint _required) {
|
||||
m_numOwners = _owners.length + 1;
|
||||
m_owners[1] = uint(msg.sender);
|
||||
m_ownerIndex[uint(msg.sender)] = 1;
|
||||
m_required = _required;
|
||||
uint constant c_maxOwners = 250;
|
||||
// index on the list of owners to allow reverse lookup
|
||||
mapping(uint => uint) m_ownerIndex;
|
||||
// the ongoing operations.
|
||||
mapping(bytes32 => PendingState) m_pending;
|
||||
bytes32[] m_pendingIndex;
|
||||
|
||||
for (uint i = 0; i < _owners.length; ++i)
|
||||
{
|
||||
m_owners[2 + i] = uint(_owners[i]);
|
||||
m_ownerIndex[uint(_owners[i])] = 2 + i;
|
||||
}
|
||||
}
|
||||
|
||||
// Revokes a prior confirmation of the given operation
|
||||
function revoke(bytes32 _operation) {
|
||||
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
||||
// make sure they're an owner
|
||||
if (ownerIndex == 0) return;
|
||||
uint ownerIndexBit = 2**ownerIndex;
|
||||
var pending = m_pending[_operation];
|
||||
if (pending.ownersDone & ownerIndexBit > 0) {
|
||||
pending.yetNeeded++;
|
||||
pending.ownersDone -= ownerIndexBit;
|
||||
Revoke(msg.sender, _operation);
|
||||
}
|
||||
}
|
||||
|
||||
// Replaces an owner `_from` with another `_to`.
|
||||
function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) {
|
||||
if (isOwner(_to)) return;
|
||||
uint ownerIndex = m_ownerIndex[uint(_from)];
|
||||
if (ownerIndex == 0) return;
|
||||
|
||||
clearPending();
|
||||
m_owners[ownerIndex] = uint(_to);
|
||||
m_ownerIndex[uint(_from)] = 0;
|
||||
m_ownerIndex[uint(_to)] = ownerIndex;
|
||||
OwnerChanged(_from, _to);
|
||||
}
|
||||
|
||||
function addOwner(address _owner) onlymanyowners(sha3(msg.data)) {
|
||||
if (isOwner(_owner)) return;
|
||||
|
||||
clearPending();
|
||||
if (m_numOwners >= c_maxOwners)
|
||||
reorganizeOwners();
|
||||
if (m_numOwners >= c_maxOwners)
|
||||
return;
|
||||
m_numOwners++;
|
||||
m_owners[m_numOwners] = uint(_owner);
|
||||
m_ownerIndex[uint(_owner)] = m_numOwners;
|
||||
OwnerAdded(_owner);
|
||||
}
|
||||
|
||||
function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) {
|
||||
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
||||
if (ownerIndex == 0) return;
|
||||
if (m_required > m_numOwners - 1) return;
|
||||
|
||||
m_owners[ownerIndex] = 0;
|
||||
m_ownerIndex[uint(_owner)] = 0;
|
||||
clearPending();
|
||||
reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot
|
||||
OwnerRemoved(_owner);
|
||||
}
|
||||
|
||||
function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) {
|
||||
if (_newRequired > m_numOwners) return;
|
||||
m_required = _newRequired;
|
||||
clearPending();
|
||||
RequirementChanged(_newRequired);
|
||||
}
|
||||
|
||||
function isOwner(address _addr) returns (bool) {
|
||||
return m_ownerIndex[uint(_addr)] > 0;
|
||||
}
|
||||
|
||||
|
||||
function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) {
|
||||
var pending = m_pending[_operation];
|
||||
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
||||
|
||||
// make sure they're an owner
|
||||
if (ownerIndex == 0) return false;
|
||||
|
||||
// determine the bit to set for this owner.
|
||||
uint ownerIndexBit = 2**ownerIndex;
|
||||
return !(pending.ownersDone & ownerIndexBit == 0);
|
||||
}
|
||||
|
||||
// INTERNAL METHODS
|
||||
|
||||
function confirmAndCheck(bytes32 _operation) internal returns (bool) {
|
||||
// determine what index the present sender is:
|
||||
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
||||
// make sure they're an owner
|
||||
if (ownerIndex == 0) return;
|
||||
|
||||
var pending = m_pending[_operation];
|
||||
// if we're not yet working on this operation, switch over and reset the confirmation status.
|
||||
if (pending.yetNeeded == 0) {
|
||||
// reset count of confirmations needed.
|
||||
pending.yetNeeded = m_required;
|
||||
// reset which owners have confirmed (none) - set our bitmap to 0.
|
||||
pending.ownersDone = 0;
|
||||
pending.index = m_pendingIndex.length++;
|
||||
m_pendingIndex[pending.index] = _operation;
|
||||
}
|
||||
// determine the bit to set for this owner.
|
||||
uint ownerIndexBit = 2**ownerIndex;
|
||||
// make sure we (the message sender) haven't confirmed this operation previously.
|
||||
if (pending.ownersDone & ownerIndexBit == 0) {
|
||||
Confirmation(msg.sender, _operation);
|
||||
// ok - check if count is enough to go ahead.
|
||||
if (pending.yetNeeded <= 1) {
|
||||
// enough confirmations: reset and run interior.
|
||||
delete m_pendingIndex[m_pending[_operation].index];
|
||||
delete m_pending[_operation];
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// not enough: record that this owner in particular confirmed.
|
||||
pending.yetNeeded--;
|
||||
pending.ownersDone |= ownerIndexBit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function reorganizeOwners() private {
|
||||
uint free = 1;
|
||||
while (free < m_numOwners)
|
||||
{
|
||||
while (free < m_numOwners && m_owners[free] != 0) free++;
|
||||
while (m_numOwners > 1 && m_owners[m_numOwners] == 0) m_numOwners--;
|
||||
if (free < m_numOwners && m_owners[m_numOwners] != 0 && m_owners[free] == 0)
|
||||
{
|
||||
m_owners[free] = m_owners[m_numOwners];
|
||||
m_ownerIndex[m_owners[free]] = free;
|
||||
m_owners[m_numOwners] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clearPending() internal {
|
||||
uint length = m_pendingIndex.length;
|
||||
for (uint i = 0; i < length; ++i)
|
||||
if (m_pendingIndex[i] != 0)
|
||||
delete m_pending[m_pendingIndex[i]];
|
||||
delete m_pendingIndex;
|
||||
}
|
||||
|
||||
|
||||
/******************************
|
||||
****** DAY LIMIT SECTION *****
|
||||
******************************/
|
||||
|
||||
// MODIFIERS
|
||||
|
||||
// simple modifier for daily limit.
|
||||
modifier limitedDaily(uint _value) {
|
||||
if (underLimit(_value))
|
||||
_;
|
||||
}
|
||||
|
||||
// METHODS
|
||||
|
||||
// constructor - stores initial daily limit and records the present day's index.
|
||||
function initDaylimit(uint _limit) {
|
||||
m_dailyLimit = _limit;
|
||||
m_lastDay = today();
|
||||
}
|
||||
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
|
||||
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) {
|
||||
m_dailyLimit = _newLimit;
|
||||
}
|
||||
// resets the amount already spent today. needs many of the owners to confirm.
|
||||
function resetSpentToday() onlymanyowners(sha3(msg.data)) {
|
||||
m_spentToday = 0;
|
||||
}
|
||||
|
||||
// INTERNAL METHODS
|
||||
|
||||
// checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
|
||||
// returns true. otherwise just returns false.
|
||||
function underLimit(uint _value) internal onlyowner returns (bool) {
|
||||
// reset the spend limit if we're on a different day to last time.
|
||||
if (today() > m_lastDay) {
|
||||
m_spentToday = 0;
|
||||
m_lastDay = today();
|
||||
}
|
||||
// check to see if there's enough left - if so, subtract and return true.
|
||||
// overflow protection // dailyLimit check
|
||||
if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) {
|
||||
m_spentToday += _value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// determines today's index.
|
||||
function today() private constant returns (uint) { return now / 1 days; }
|
||||
|
||||
|
||||
/******************************
|
||||
********* WALLET SECTION *****
|
||||
******************************/
|
||||
|
||||
// METHODS
|
||||
|
||||
// constructor - just pass on the owner array to the multiowned and
|
||||
// the limit to daylimit
|
||||
function initWallet(address[] _owners, uint _required, uint _daylimit) {
|
||||
initMultiowned(_owners, _required);
|
||||
initDaylimit(_daylimit) ;
|
||||
}
|
||||
|
||||
// kills the contract sending everything to `_to`.
|
||||
function kill(address _to) onlymanyowners(sha3(msg.data)) {
|
||||
suicide(_to);
|
||||
}
|
||||
|
||||
// Outside-visible transact entry point. Executes transaction immediately if below daily spend limit.
|
||||
// If not, goes into multisig process. We provide a hash on return to allow the sender to provide
|
||||
// shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
|
||||
// and _data arguments). They still get the option of using them if they want, anyways.
|
||||
function execute(address _to, uint _value, bytes _data) onlyowner returns(bool _callValue) {
|
||||
// first, take the opportunity to check that we're under the daily limit.
|
||||
if (underLimit(_value)) {
|
||||
SingleTransact(msg.sender, _value, _to, _data);
|
||||
// yes - just execute the call.
|
||||
_callValue =_to.call.value(_value)(_data);
|
||||
} else {
|
||||
// determine our operation hash.
|
||||
bytes32 _r = sha3(msg.data, block.number);
|
||||
if (!confirm(_r) && m_txs[_r].to == 0) {
|
||||
m_txs[_r].to = _to;
|
||||
m_txs[_r].value = _value;
|
||||
m_txs[_r].data = _data;
|
||||
ConfirmationNeeded(_r, msg.sender, _value, _to, _data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order
|
||||
// to determine the body of the transaction from the hash provided.
|
||||
function confirm(bytes32 _h) onlymanyowners(_h) returns (bool) {
|
||||
if (m_txs[_h].to != 0) {
|
||||
m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data);
|
||||
MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data);
|
||||
delete m_txs[_h];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// INTERNAL METHODS
|
||||
|
||||
function clearWalletPending() internal {
|
||||
uint length = m_pendingIndex.length;
|
||||
for (uint i = 0; i < length; ++i)
|
||||
delete m_txs[m_pendingIndex[i]];
|
||||
clearPending();
|
||||
}
|
||||
|
||||
// FIELDS
|
||||
address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe;
|
||||
|
||||
// the number of owners that must confirm the same operation before it is run.
|
||||
uint m_required;
|
||||
// pointer used to find a free slot in m_owners
|
||||
uint m_numOwners;
|
||||
|
||||
uint public m_dailyLimit;
|
||||
uint public m_spentToday;
|
||||
uint public m_lastDay;
|
||||
|
||||
// list of owners
|
||||
uint[256] m_owners;
|
||||
uint constant c_maxOwners = 250;
|
||||
|
||||
// index on the list of owners to allow reverse lookup
|
||||
mapping(uint => uint) m_ownerIndex;
|
||||
// the ongoing operations.
|
||||
mapping(bytes32 => PendingState) m_pending;
|
||||
bytes32[] m_pendingIndex;
|
||||
|
||||
// pending transactions we have at present.
|
||||
mapping (bytes32 => Transaction) m_txs;
|
||||
// pending transactions we have at present.
|
||||
mapping (bytes32 => Transaction) m_txs;
|
||||
}
|
||||
|
||||
contract Wallet is WalletEvents {
|
||||
|
||||
contract Wallet is multisig {
|
||||
// WALLET CONSTRUCTOR
|
||||
// calls the `initWallet` method of the Library in this context
|
||||
function Wallet(address[] _owners, uint _required, uint _daylimit) {
|
||||
// Signature of the Wallet Library's init function
|
||||
bytes4 sig = bytes4(sha3("initWallet(address[],uint256,uint256)"));
|
||||
address target = _walletLibrary;
|
||||
|
||||
// WALLET CONSTRUCTOR
|
||||
// calls the `initWallet` method of the Library in this context
|
||||
function Wallet(address[] _owners, uint _required, uint _daylimit) {
|
||||
// Signature of the Wallet Library's init function
|
||||
bytes4 sig = bytes4(sha3("initWallet(address[],uint256,uint256)"));
|
||||
address target = _walletLibrary;
|
||||
// Compute the size of the call data : arrays has 2
|
||||
// 32bytes for offset and length, plus 32bytes per element ;
|
||||
// plus 2 32bytes for each uint
|
||||
uint argarraysize = (2 + _owners.length);
|
||||
uint argsize = (2 + argarraysize) * 32;
|
||||
|
||||
// Compute the size of the call data : arrays has 2
|
||||
// 32bytes for offset and length, plus 32bytes per element ;
|
||||
// plus 2 32bytes for each uint
|
||||
uint argarraysize = (2 + _owners.length);
|
||||
uint argsize = (2 + argarraysize) * 32;
|
||||
|
||||
assembly {
|
||||
// Add the signature first to memory
|
||||
mstore(0x0, sig)
|
||||
// Add the call data, which is at the end of the
|
||||
// code
|
||||
codecopy(0x4, sub(codesize, argsize), argsize)
|
||||
// Delegate call to the library
|
||||
delegatecall(sub(gas, 10000), target, 0x0, add(argsize, 0x4), 0x0, 0x0)
|
||||
}
|
||||
assembly {
|
||||
// Add the signature first to memory
|
||||
mstore(0x0, sig)
|
||||
// Add the call data, which is at the end of the
|
||||
// code
|
||||
codecopy(0x4, sub(codesize, argsize), argsize)
|
||||
// Delegate call to the library
|
||||
delegatecall(sub(gas, 10000), target, 0x0, add(argsize, 0x4), 0x0, 0x0)
|
||||
}
|
||||
}
|
||||
|
||||
// METHODS
|
||||
// METHODS
|
||||
|
||||
// gets called when no other function matches
|
||||
function() payable {
|
||||
// just being sent some cash?
|
||||
if (msg.value > 0)
|
||||
Deposit(msg.sender, msg.value);
|
||||
else if (msg.data.length > 0)
|
||||
_walletLibrary.delegatecall(msg.data);
|
||||
}
|
||||
// gets called when no other function matches
|
||||
function() payable {
|
||||
// just being sent some cash?
|
||||
if (msg.value > 0)
|
||||
Deposit(msg.sender, msg.value);
|
||||
else if (msg.data.length > 0)
|
||||
_walletLibrary.delegatecall(msg.data);
|
||||
}
|
||||
|
||||
// Gets an owner by 0-indexed position (using numOwners as the count)
|
||||
function getOwner(uint ownerIndex) constant returns (address) {
|
||||
return address(m_owners[ownerIndex + 1]);
|
||||
}
|
||||
// Gets an owner by 0-indexed position (using numOwners as the count)
|
||||
function getOwner(uint ownerIndex) constant returns (address) {
|
||||
return address(m_owners[ownerIndex + 1]);
|
||||
}
|
||||
|
||||
// As return statement unavailable in fallback, explicit the method here
|
||||
// As return statement unavailable in fallback, explicit the method here
|
||||
|
||||
function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) {
|
||||
return _walletLibrary.delegatecall(msg.data);
|
||||
}
|
||||
function hasConfirmed(bytes32 _operation, address _owner) external constant returns (bool) {
|
||||
return _walletLibrary.delegatecall(msg.data);
|
||||
}
|
||||
|
||||
function isOwner(address _addr) returns (bool) {
|
||||
return _walletLibrary.delegatecall(msg.data);
|
||||
}
|
||||
function isOwner(address _addr) constant returns (bool) {
|
||||
return _walletLibrary.delegatecall(msg.data);
|
||||
}
|
||||
|
||||
// FIELDS
|
||||
address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe;
|
||||
// FIELDS
|
||||
address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe;
|
||||
|
||||
// the number of owners that must confirm the same operation before it is run.
|
||||
uint public m_required;
|
||||
// pointer used to find a free slot in m_owners
|
||||
uint public m_numOwners;
|
||||
// the number of owners that must confirm the same operation before it is run.
|
||||
uint public m_required;
|
||||
// pointer used to find a free slot in m_owners
|
||||
uint public m_numOwners;
|
||||
|
||||
uint public m_dailyLimit;
|
||||
uint public m_spentToday;
|
||||
uint public m_lastDay;
|
||||
uint public m_dailyLimit;
|
||||
uint public m_spentToday;
|
||||
uint public m_lastDay;
|
||||
|
||||
// list of owners
|
||||
uint[256] m_owners;
|
||||
// list of owners
|
||||
uint[256] m_owners;
|
||||
}
|
||||
|
||||
@@ -8,221 +8,222 @@
|
||||
// use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by
|
||||
// some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the
|
||||
// interior is executed.
|
||||
pragma solidity ^0.4.6;
|
||||
|
||||
pragma solidity ^0.4.9;
|
||||
|
||||
contract multiowned {
|
||||
|
||||
// TYPES
|
||||
// TYPES
|
||||
|
||||
// struct for the status of a pending operation.
|
||||
struct PendingState {
|
||||
uint yetNeeded;
|
||||
uint ownersDone;
|
||||
uint index;
|
||||
// struct for the status of a pending operation.
|
||||
struct PendingState {
|
||||
uint yetNeeded;
|
||||
uint ownersDone;
|
||||
uint index;
|
||||
}
|
||||
|
||||
// EVENTS
|
||||
|
||||
// this contract only has six types of events: it can accept a confirmation, in which case
|
||||
// we record owner and operation (hash) alongside it.
|
||||
event Confirmation(address owner, bytes32 operation);
|
||||
event Revoke(address owner, bytes32 operation);
|
||||
// some others are in the case of an owner changing.
|
||||
event OwnerChanged(address oldOwner, address newOwner);
|
||||
event OwnerAdded(address newOwner);
|
||||
event OwnerRemoved(address oldOwner);
|
||||
// the last one is emitted if the required signatures change
|
||||
event RequirementChanged(uint newRequirement);
|
||||
|
||||
// MODIFIERS
|
||||
|
||||
// simple single-sig function modifier.
|
||||
modifier onlyowner {
|
||||
if (isOwner(msg.sender))
|
||||
_;
|
||||
}
|
||||
// multi-sig function modifier: the operation must have an intrinsic hash in order
|
||||
// that later attempts can be realised as the same underlying operation and
|
||||
// thus count as confirmations.
|
||||
modifier onlymanyowners(bytes32 _operation) {
|
||||
if (confirmAndCheck(_operation))
|
||||
_;
|
||||
}
|
||||
|
||||
// METHODS
|
||||
|
||||
// constructor is given number of sigs required to do protected "onlymanyowners" transactions
|
||||
// as well as the selection of addresses capable of confirming them.
|
||||
function multiowned(address[] _owners, uint _required) {
|
||||
m_numOwners = _owners.length + 1;
|
||||
m_owners[1] = uint(msg.sender);
|
||||
m_ownerIndex[uint(msg.sender)] = 1;
|
||||
for (uint i = 0; i < _owners.length; ++i)
|
||||
{
|
||||
m_owners[2 + i] = uint(_owners[i]);
|
||||
m_ownerIndex[uint(_owners[i])] = 2 + i;
|
||||
}
|
||||
m_required = _required;
|
||||
}
|
||||
|
||||
// EVENTS
|
||||
|
||||
// this contract only has six types of events: it can accept a confirmation, in which case
|
||||
// we record owner and operation (hash) alongside it.
|
||||
event Confirmation(address owner, bytes32 operation);
|
||||
event Revoke(address owner, bytes32 operation);
|
||||
// some others are in the case of an owner changing.
|
||||
event OwnerChanged(address oldOwner, address newOwner);
|
||||
event OwnerAdded(address newOwner);
|
||||
event OwnerRemoved(address oldOwner);
|
||||
// the last one is emitted if the required signatures change
|
||||
event RequirementChanged(uint newRequirement);
|
||||
|
||||
// MODIFIERS
|
||||
|
||||
// simple single-sig function modifier.
|
||||
modifier onlyowner {
|
||||
if (isOwner(msg.sender))
|
||||
_;
|
||||
// Revokes a prior confirmation of the given operation
|
||||
function revoke(bytes32 _operation) external {
|
||||
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
||||
// make sure they're an owner
|
||||
if (ownerIndex == 0) return;
|
||||
uint ownerIndexBit = 2**ownerIndex;
|
||||
var pending = m_pending[_operation];
|
||||
if (pending.ownersDone & ownerIndexBit > 0) {
|
||||
pending.yetNeeded++;
|
||||
pending.ownersDone -= ownerIndexBit;
|
||||
Revoke(msg.sender, _operation);
|
||||
}
|
||||
// multi-sig function modifier: the operation must have an intrinsic hash in order
|
||||
// that later attempts can be realised as the same underlying operation and
|
||||
// thus count as confirmations.
|
||||
modifier onlymanyowners(bytes32 _operation) {
|
||||
if (confirmAndCheck(_operation))
|
||||
_;
|
||||
}
|
||||
|
||||
// Replaces an owner `_from` with another `_to`.
|
||||
function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) external {
|
||||
if (isOwner(_to)) return;
|
||||
uint ownerIndex = m_ownerIndex[uint(_from)];
|
||||
if (ownerIndex == 0) return;
|
||||
|
||||
clearPending();
|
||||
m_owners[ownerIndex] = uint(_to);
|
||||
m_ownerIndex[uint(_from)] = 0;
|
||||
m_ownerIndex[uint(_to)] = ownerIndex;
|
||||
OwnerChanged(_from, _to);
|
||||
}
|
||||
|
||||
function addOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
|
||||
if (isOwner(_owner)) return;
|
||||
|
||||
clearPending();
|
||||
if (m_numOwners >= c_maxOwners)
|
||||
reorganizeOwners();
|
||||
if (m_numOwners >= c_maxOwners)
|
||||
return;
|
||||
m_numOwners++;
|
||||
m_owners[m_numOwners] = uint(_owner);
|
||||
m_ownerIndex[uint(_owner)] = m_numOwners;
|
||||
OwnerAdded(_owner);
|
||||
}
|
||||
|
||||
function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
|
||||
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
||||
if (ownerIndex == 0) return;
|
||||
if (m_required > m_numOwners - 1) return;
|
||||
|
||||
m_owners[ownerIndex] = 0;
|
||||
m_ownerIndex[uint(_owner)] = 0;
|
||||
clearPending();
|
||||
reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot
|
||||
OwnerRemoved(_owner);
|
||||
}
|
||||
|
||||
function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external {
|
||||
if (_newRequired > m_numOwners) return;
|
||||
m_required = _newRequired;
|
||||
clearPending();
|
||||
RequirementChanged(_newRequired);
|
||||
}
|
||||
|
||||
// Gets an owner by 0-indexed position (using numOwners as the count)
|
||||
function getOwner(uint ownerIndex) external constant returns (address) {
|
||||
return address(m_owners[ownerIndex + 1]);
|
||||
}
|
||||
|
||||
function isOwner(address _addr) constant returns (bool) {
|
||||
return m_ownerIndex[uint(_addr)] > 0;
|
||||
}
|
||||
|
||||
function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) {
|
||||
var pending = m_pending[_operation];
|
||||
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
||||
|
||||
// make sure they're an owner
|
||||
if (ownerIndex == 0) return false;
|
||||
|
||||
// determine the bit to set for this owner.
|
||||
uint ownerIndexBit = 2**ownerIndex;
|
||||
return !(pending.ownersDone & ownerIndexBit == 0);
|
||||
}
|
||||
|
||||
// INTERNAL METHODS
|
||||
|
||||
function confirmAndCheck(bytes32 _operation) internal returns (bool) {
|
||||
// determine what index the present sender is:
|
||||
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
||||
// make sure they're an owner
|
||||
if (ownerIndex == 0) return;
|
||||
|
||||
var pending = m_pending[_operation];
|
||||
// if we're not yet working on this operation, switch over and reset the confirmation status.
|
||||
if (pending.yetNeeded == 0) {
|
||||
// reset count of confirmations needed.
|
||||
pending.yetNeeded = m_required;
|
||||
// reset which owners have confirmed (none) - set our bitmap to 0.
|
||||
pending.ownersDone = 0;
|
||||
pending.index = m_pendingIndex.length++;
|
||||
m_pendingIndex[pending.index] = _operation;
|
||||
}
|
||||
|
||||
// METHODS
|
||||
|
||||
// constructor is given number of sigs required to do protected "onlymanyowners" transactions
|
||||
// as well as the selection of addresses capable of confirming them.
|
||||
function multiowned(address[] _owners, uint _required) {
|
||||
m_numOwners = _owners.length + 1;
|
||||
m_owners[1] = uint(msg.sender);
|
||||
m_ownerIndex[uint(msg.sender)] = 1;
|
||||
for (uint i = 0; i < _owners.length; ++i)
|
||||
{
|
||||
m_owners[2 + i] = uint(_owners[i]);
|
||||
m_ownerIndex[uint(_owners[i])] = 2 + i;
|
||||
}
|
||||
m_required = _required;
|
||||
// determine the bit to set for this owner.
|
||||
uint ownerIndexBit = 2**ownerIndex;
|
||||
// make sure we (the message sender) haven't confirmed this operation previously.
|
||||
if (pending.ownersDone & ownerIndexBit == 0) {
|
||||
Confirmation(msg.sender, _operation);
|
||||
// ok - check if count is enough to go ahead.
|
||||
if (pending.yetNeeded <= 1) {
|
||||
// enough confirmations: reset and run interior.
|
||||
delete m_pendingIndex[m_pending[_operation].index];
|
||||
delete m_pending[_operation];
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// not enough: record that this owner in particular confirmed.
|
||||
pending.yetNeeded--;
|
||||
pending.ownersDone |= ownerIndexBit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Revokes a prior confirmation of the given operation
|
||||
function revoke(bytes32 _operation) external {
|
||||
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
||||
// make sure they're an owner
|
||||
if (ownerIndex == 0) return;
|
||||
uint ownerIndexBit = 2**ownerIndex;
|
||||
var pending = m_pending[_operation];
|
||||
if (pending.ownersDone & ownerIndexBit > 0) {
|
||||
pending.yetNeeded++;
|
||||
pending.ownersDone -= ownerIndexBit;
|
||||
Revoke(msg.sender, _operation);
|
||||
}
|
||||
function reorganizeOwners() private {
|
||||
uint free = 1;
|
||||
while (free < m_numOwners)
|
||||
{
|
||||
while (free < m_numOwners && m_owners[free] != 0) free++;
|
||||
while (m_numOwners > 1 && m_owners[m_numOwners] == 0) m_numOwners--;
|
||||
if (free < m_numOwners && m_owners[m_numOwners] != 0 && m_owners[free] == 0)
|
||||
{
|
||||
m_owners[free] = m_owners[m_numOwners];
|
||||
m_ownerIndex[m_owners[free]] = free;
|
||||
m_owners[m_numOwners] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Replaces an owner `_from` with another `_to`.
|
||||
function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) external {
|
||||
if (isOwner(_to)) return;
|
||||
uint ownerIndex = m_ownerIndex[uint(_from)];
|
||||
if (ownerIndex == 0) return;
|
||||
function clearPending() internal {
|
||||
uint length = m_pendingIndex.length;
|
||||
for (uint i = 0; i < length; ++i)
|
||||
if (m_pendingIndex[i] != 0)
|
||||
delete m_pending[m_pendingIndex[i]];
|
||||
delete m_pendingIndex;
|
||||
}
|
||||
|
||||
clearPending();
|
||||
m_owners[ownerIndex] = uint(_to);
|
||||
m_ownerIndex[uint(_from)] = 0;
|
||||
m_ownerIndex[uint(_to)] = ownerIndex;
|
||||
OwnerChanged(_from, _to);
|
||||
}
|
||||
// FIELDS
|
||||
|
||||
function addOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
|
||||
if (isOwner(_owner)) return;
|
||||
// the number of owners that must confirm the same operation before it is run.
|
||||
uint public m_required;
|
||||
// pointer used to find a free slot in m_owners
|
||||
uint public m_numOwners;
|
||||
|
||||
clearPending();
|
||||
if (m_numOwners >= c_maxOwners)
|
||||
reorganizeOwners();
|
||||
if (m_numOwners >= c_maxOwners)
|
||||
return;
|
||||
m_numOwners++;
|
||||
m_owners[m_numOwners] = uint(_owner);
|
||||
m_ownerIndex[uint(_owner)] = m_numOwners;
|
||||
OwnerAdded(_owner);
|
||||
}
|
||||
|
||||
function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
|
||||
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
||||
if (ownerIndex == 0) return;
|
||||
if (m_required > m_numOwners - 1) return;
|
||||
|
||||
m_owners[ownerIndex] = 0;
|
||||
m_ownerIndex[uint(_owner)] = 0;
|
||||
clearPending();
|
||||
reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot
|
||||
OwnerRemoved(_owner);
|
||||
}
|
||||
|
||||
function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external {
|
||||
if (_newRequired > m_numOwners) return;
|
||||
m_required = _newRequired;
|
||||
clearPending();
|
||||
RequirementChanged(_newRequired);
|
||||
}
|
||||
|
||||
// Gets an owner by 0-indexed position (using numOwners as the count)
|
||||
function getOwner(uint ownerIndex) external constant returns (address) {
|
||||
return address(m_owners[ownerIndex + 1]);
|
||||
}
|
||||
|
||||
function isOwner(address _addr) returns (bool) {
|
||||
return m_ownerIndex[uint(_addr)] > 0;
|
||||
}
|
||||
|
||||
function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) {
|
||||
var pending = m_pending[_operation];
|
||||
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
||||
|
||||
// make sure they're an owner
|
||||
if (ownerIndex == 0) return false;
|
||||
|
||||
// determine the bit to set for this owner.
|
||||
uint ownerIndexBit = 2**ownerIndex;
|
||||
return !(pending.ownersDone & ownerIndexBit == 0);
|
||||
}
|
||||
|
||||
// INTERNAL METHODS
|
||||
|
||||
function confirmAndCheck(bytes32 _operation) internal returns (bool) {
|
||||
// determine what index the present sender is:
|
||||
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
||||
// make sure they're an owner
|
||||
if (ownerIndex == 0) return;
|
||||
|
||||
var pending = m_pending[_operation];
|
||||
// if we're not yet working on this operation, switch over and reset the confirmation status.
|
||||
if (pending.yetNeeded == 0) {
|
||||
// reset count of confirmations needed.
|
||||
pending.yetNeeded = m_required;
|
||||
// reset which owners have confirmed (none) - set our bitmap to 0.
|
||||
pending.ownersDone = 0;
|
||||
pending.index = m_pendingIndex.length++;
|
||||
m_pendingIndex[pending.index] = _operation;
|
||||
}
|
||||
// determine the bit to set for this owner.
|
||||
uint ownerIndexBit = 2**ownerIndex;
|
||||
// make sure we (the message sender) haven't confirmed this operation previously.
|
||||
if (pending.ownersDone & ownerIndexBit == 0) {
|
||||
Confirmation(msg.sender, _operation);
|
||||
// ok - check if count is enough to go ahead.
|
||||
if (pending.yetNeeded <= 1) {
|
||||
// enough confirmations: reset and run interior.
|
||||
delete m_pendingIndex[m_pending[_operation].index];
|
||||
delete m_pending[_operation];
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// not enough: record that this owner in particular confirmed.
|
||||
pending.yetNeeded--;
|
||||
pending.ownersDone |= ownerIndexBit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function reorganizeOwners() private {
|
||||
uint free = 1;
|
||||
while (free < m_numOwners)
|
||||
{
|
||||
while (free < m_numOwners && m_owners[free] != 0) free++;
|
||||
while (m_numOwners > 1 && m_owners[m_numOwners] == 0) m_numOwners--;
|
||||
if (free < m_numOwners && m_owners[m_numOwners] != 0 && m_owners[free] == 0)
|
||||
{
|
||||
m_owners[free] = m_owners[m_numOwners];
|
||||
m_ownerIndex[m_owners[free]] = free;
|
||||
m_owners[m_numOwners] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clearPending() internal {
|
||||
uint length = m_pendingIndex.length;
|
||||
for (uint i = 0; i < length; ++i)
|
||||
if (m_pendingIndex[i] != 0)
|
||||
delete m_pending[m_pendingIndex[i]];
|
||||
delete m_pendingIndex;
|
||||
}
|
||||
|
||||
// FIELDS
|
||||
|
||||
// the number of owners that must confirm the same operation before it is run.
|
||||
uint public m_required;
|
||||
// pointer used to find a free slot in m_owners
|
||||
uint public m_numOwners;
|
||||
|
||||
// list of owners
|
||||
uint[256] m_owners;
|
||||
uint constant c_maxOwners = 250;
|
||||
// index on the list of owners to allow reverse lookup
|
||||
mapping(uint => uint) m_ownerIndex;
|
||||
// the ongoing operations.
|
||||
mapping(bytes32 => PendingState) m_pending;
|
||||
bytes32[] m_pendingIndex;
|
||||
// list of owners
|
||||
uint[256] m_owners;
|
||||
uint constant c_maxOwners = 250;
|
||||
// index on the list of owners to allow reverse lookup
|
||||
mapping(uint => uint) m_ownerIndex;
|
||||
// the ongoing operations.
|
||||
mapping(bytes32 => PendingState) m_pending;
|
||||
bytes32[] m_pendingIndex;
|
||||
}
|
||||
|
||||
// inheritable "property" contract that enables methods to be protected by placing a linear limit (specifiable)
|
||||
@@ -230,79 +231,70 @@ contract multiowned {
|
||||
// uses is specified in the modifier.
|
||||
contract daylimit is multiowned {
|
||||
|
||||
// MODIFIERS
|
||||
// METHODS
|
||||
|
||||
// simple modifier for daily limit.
|
||||
modifier limitedDaily(uint _value) {
|
||||
if (underLimit(_value))
|
||||
_;
|
||||
// constructor - stores initial daily limit and records the present day's index.
|
||||
function daylimit(uint _limit) {
|
||||
m_dailyLimit = _limit;
|
||||
m_lastDay = today();
|
||||
}
|
||||
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
|
||||
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external {
|
||||
m_dailyLimit = _newLimit;
|
||||
}
|
||||
// resets the amount already spent today. needs many of the owners to confirm.
|
||||
function resetSpentToday() onlymanyowners(sha3(msg.data)) external {
|
||||
m_spentToday = 0;
|
||||
}
|
||||
|
||||
// INTERNAL METHODS
|
||||
|
||||
// checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
|
||||
// returns true. otherwise just returns false.
|
||||
function underLimit(uint _value) internal onlyowner returns (bool) {
|
||||
// reset the spend limit if we're on a different day to last time.
|
||||
if (today() > m_lastDay) {
|
||||
m_spentToday = 0;
|
||||
m_lastDay = today();
|
||||
}
|
||||
|
||||
// METHODS
|
||||
|
||||
// constructor - stores initial daily limit and records the present day's index.
|
||||
function daylimit(uint _limit) {
|
||||
m_dailyLimit = _limit;
|
||||
m_lastDay = today();
|
||||
}
|
||||
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
|
||||
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external {
|
||||
m_dailyLimit = _newLimit;
|
||||
}
|
||||
// resets the amount already spent today. needs many of the owners to confirm.
|
||||
function resetSpentToday() onlymanyowners(sha3(msg.data)) external {
|
||||
m_spentToday = 0;
|
||||
// check to see if there's enough left - if so, subtract and return true.
|
||||
// overflow protection // dailyLimit check
|
||||
if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) {
|
||||
m_spentToday += _value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// determines today's index.
|
||||
function today() private constant returns (uint) { return now / 1 days; }
|
||||
|
||||
// INTERNAL METHODS
|
||||
// FIELDS
|
||||
|
||||
// checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
|
||||
// returns true. otherwise just returns false.
|
||||
function underLimit(uint _value) internal onlyowner returns (bool) {
|
||||
// reset the spend limit if we're on a different day to last time.
|
||||
if (today() > m_lastDay) {
|
||||
m_spentToday = 0;
|
||||
m_lastDay = today();
|
||||
}
|
||||
// check to see if there's enough left - if so, subtract and return true.
|
||||
// overflow protection // dailyLimit check
|
||||
if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) {
|
||||
m_spentToday += _value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// determines today's index.
|
||||
function today() private constant returns (uint) { return now / 1 days; }
|
||||
|
||||
// FIELDS
|
||||
|
||||
uint public m_dailyLimit;
|
||||
uint public m_spentToday;
|
||||
uint public m_lastDay;
|
||||
uint public m_dailyLimit;
|
||||
uint public m_spentToday;
|
||||
uint public m_lastDay;
|
||||
}
|
||||
|
||||
// interface contract for multisig proxy contracts; see below for docs.
|
||||
contract multisig {
|
||||
|
||||
// EVENTS
|
||||
// EVENTS
|
||||
|
||||
// logged events:
|
||||
// Funds has arrived into the wallet (record how much).
|
||||
event Deposit(address _from, uint value);
|
||||
// Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
|
||||
event SingleTransact(address owner, uint value, address to, bytes data);
|
||||
// Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
|
||||
event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data);
|
||||
// Confirmation still needed for a transaction.
|
||||
event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
|
||||
// logged events:
|
||||
// Funds has arrived into the wallet (record how much).
|
||||
event Deposit(address _from, uint value);
|
||||
// Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
|
||||
event SingleTransact(address owner, uint value, address to, bytes data, address created);
|
||||
// Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
|
||||
event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data, address created);
|
||||
// Confirmation still needed for a transaction.
|
||||
event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
|
||||
|
||||
// FUNCTIONS
|
||||
// FUNCTIONS
|
||||
|
||||
// TODO: document
|
||||
function changeOwner(address _from, address _to) external;
|
||||
function execute(address _to, uint _value, bytes _data) external returns (bytes32);
|
||||
function confirm(bytes32 _h) returns (bool);
|
||||
// TODO: document
|
||||
function execute(address _to, uint _value, bytes _data) external returns (bytes32 o_hash);
|
||||
function confirm(bytes32 _h) external returns (bool o_success);
|
||||
}
|
||||
|
||||
// usage:
|
||||
@@ -310,79 +302,102 @@ contract multisig {
|
||||
// Wallet(w).from(anotherOwner).confirm(h);
|
||||
contract Wallet is multisig, multiowned, daylimit {
|
||||
|
||||
// TYPES
|
||||
// TYPES
|
||||
|
||||
// Transaction structure to remember details of transaction lest it need be saved for a later call.
|
||||
struct Transaction {
|
||||
address to;
|
||||
uint value;
|
||||
bytes data;
|
||||
// Transaction structure to remember details of transaction lest it need be saved for a later call.
|
||||
struct Transaction {
|
||||
address to;
|
||||
uint value;
|
||||
bytes data;
|
||||
}
|
||||
|
||||
// METHODS
|
||||
|
||||
// constructor - just pass on the owner array to the multiowned and
|
||||
// the limit to daylimit
|
||||
function Wallet(address[] _owners, uint _required, uint _daylimit)
|
||||
multiowned(_owners, _required) daylimit(_daylimit) {
|
||||
}
|
||||
|
||||
// kills the contract sending everything to `_to`.
|
||||
function kill(address _to) onlymanyowners(sha3(msg.data)) external {
|
||||
suicide(_to);
|
||||
}
|
||||
|
||||
// gets called when no other function matches
|
||||
function() payable {
|
||||
// just being sent some cash?
|
||||
if (msg.value > 0)
|
||||
Deposit(msg.sender, msg.value);
|
||||
}
|
||||
|
||||
// Outside-visible transact entry point. Executes transaction immediately if below daily spend limit.
|
||||
// If not, goes into multisig process. We provide a hash on return to allow the sender to provide
|
||||
// shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
|
||||
// and _data arguments). They still get the option of using them if they want, anyways.
|
||||
function execute(address _to, uint _value, bytes _data) external onlyowner returns (bytes32 o_hash) {
|
||||
// first, take the opportunity to check that we're under the daily limit.
|
||||
if ((_data.length == 0 && underLimit(_value)) || m_required == 1) {
|
||||
// yes - just execute the call.
|
||||
address created;
|
||||
if (_to == 0) {
|
||||
created = create(_value, _data);
|
||||
} else {
|
||||
if (!_to.call.value(_value)(_data))
|
||||
throw;
|
||||
}
|
||||
SingleTransact(msg.sender, _value, _to, _data, created);
|
||||
} else {
|
||||
// determine our operation hash.
|
||||
o_hash = sha3(msg.data, block.number);
|
||||
// store if it's new
|
||||
if (m_txs[o_hash].to == 0 && m_txs[o_hash].value == 0 && m_txs[o_hash].data.length == 0) {
|
||||
m_txs[o_hash].to = _to;
|
||||
m_txs[o_hash].value = _value;
|
||||
m_txs[o_hash].data = _data;
|
||||
}
|
||||
if (!confirm(o_hash)) {
|
||||
ConfirmationNeeded(o_hash, msg.sender, _value, _to, _data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// METHODS
|
||||
|
||||
// constructor - just pass on the owner array to the multiowned and
|
||||
// the limit to daylimit
|
||||
function Wallet(address[] _owners, uint _required, uint _daylimit)
|
||||
multiowned(_owners, _required) daylimit(_daylimit) {
|
||||
function create(uint _value, bytes _code) internal returns (address o_addr) {
|
||||
assembly {
|
||||
o_addr := create(_value, add(_code, 0x20), mload(_code))
|
||||
jumpi(invalidJumpLabel, iszero(extcodesize(o_addr)))
|
||||
}
|
||||
}
|
||||
|
||||
// kills the contract sending everything to `_to`.
|
||||
function kill(address _to) onlymanyowners(sha3(msg.data)) external {
|
||||
suicide(_to);
|
||||
// confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order
|
||||
// to determine the body of the transaction from the hash provided.
|
||||
function confirm(bytes32 _h) onlymanyowners(_h) returns (bool o_success) {
|
||||
if (m_txs[_h].to != 0 || m_txs[_h].value != 0 || m_txs[_h].data.length != 0) {
|
||||
address created;
|
||||
if (m_txs[_h].to == 0) {
|
||||
created = create(m_txs[_h].value, m_txs[_h].data);
|
||||
} else {
|
||||
if (!m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data))
|
||||
throw;
|
||||
}
|
||||
|
||||
MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data, created);
|
||||
delete m_txs[_h];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// gets called when no other function matches
|
||||
function() payable {
|
||||
// just being sent some cash?
|
||||
if (msg.value > 0)
|
||||
Deposit(msg.sender, msg.value);
|
||||
}
|
||||
// INTERNAL METHODS
|
||||
|
||||
// Outside-visible transact entry point. Executes transaction immediately if below daily spend limit.
|
||||
// If not, goes into multisig process. We provide a hash on return to allow the sender to provide
|
||||
// shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
|
||||
// and _data arguments). They still get the option of using them if they want, anyways.
|
||||
function execute(address _to, uint _value, bytes _data) external onlyowner returns (bytes32 _r) {
|
||||
// first, take the opportunity to check that we're under the daily limit.
|
||||
if (underLimit(_value)) {
|
||||
SingleTransact(msg.sender, _value, _to, _data);
|
||||
// yes - just execute the call.
|
||||
_to.call.value(_value)(_data);
|
||||
return 0;
|
||||
}
|
||||
// determine our operation hash.
|
||||
_r = sha3(msg.data, block.number);
|
||||
if (!confirm(_r) && m_txs[_r].to == 0) {
|
||||
m_txs[_r].to = _to;
|
||||
m_txs[_r].value = _value;
|
||||
m_txs[_r].data = _data;
|
||||
ConfirmationNeeded(_r, msg.sender, _value, _to, _data);
|
||||
}
|
||||
}
|
||||
function clearPending() internal {
|
||||
uint length = m_pendingIndex.length;
|
||||
for (uint i = 0; i < length; ++i)
|
||||
delete m_txs[m_pendingIndex[i]];
|
||||
super.clearPending();
|
||||
}
|
||||
|
||||
// confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order
|
||||
// to determine the body of the transaction from the hash provided.
|
||||
function confirm(bytes32 _h) onlymanyowners(_h) returns (bool) {
|
||||
if (m_txs[_h].to != 0) {
|
||||
m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data);
|
||||
MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data);
|
||||
delete m_txs[_h];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// FIELDS
|
||||
|
||||
// INTERNAL METHODS
|
||||
|
||||
function clearPending() internal {
|
||||
uint length = m_pendingIndex.length;
|
||||
for (uint i = 0; i < length; ++i)
|
||||
delete m_txs[m_pendingIndex[i]];
|
||||
super.clearPending();
|
||||
}
|
||||
|
||||
// FIELDS
|
||||
|
||||
// pending transactions we have at present.
|
||||
mapping (bytes32 => Transaction) m_txs;
|
||||
// pending transactions we have at present.
|
||||
mapping (bytes32 => Transaction) m_txs;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import ReactDOM from 'react-dom';
|
||||
import React from 'react';
|
||||
import { AppContainer } from 'react-hot-loader';
|
||||
|
||||
import injectTapEventPlugin from 'react-tap-event-plugin';
|
||||
injectTapEventPlugin();
|
||||
@@ -27,6 +28,21 @@ import '../../assets/fonts/RobotoMono/font.css';
|
||||
import './style.css';
|
||||
|
||||
ReactDOM.render(
|
||||
<Application />,
|
||||
<AppContainer>
|
||||
<Application />
|
||||
</AppContainer>,
|
||||
document.querySelector('#container')
|
||||
);
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept('./githubhint/Application/index.js', () => {
|
||||
require('./githubhint/Application/index.js');
|
||||
|
||||
ReactDOM.render(
|
||||
<AppContainer>
|
||||
<Application />
|
||||
</AppContainer>,
|
||||
document.querySelector('#container')
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import ReactDOM from 'react-dom';
|
||||
import React from 'react';
|
||||
import { AppContainer } from 'react-hot-loader';
|
||||
|
||||
import injectTapEventPlugin from 'react-tap-event-plugin';
|
||||
injectTapEventPlugin();
|
||||
@@ -27,6 +28,21 @@ import '../../assets/fonts/RobotoMono/font.css';
|
||||
import './style.css';
|
||||
|
||||
ReactDOM.render(
|
||||
<Application />,
|
||||
<AppContainer>
|
||||
<Application />
|
||||
</AppContainer>,
|
||||
document.querySelector('#container')
|
||||
);
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept('./localtx/Application/index.js', () => {
|
||||
require('./localtx/Application/index.js');
|
||||
|
||||
ReactDOM.render(
|
||||
<AppContainer>
|
||||
<Application />
|
||||
</AppContainer>,
|
||||
document.querySelector('#container')
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -15,5 +15,29 @@
|
||||
th {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
td {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: rgba(0, 136, 170, 1);
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
color: white;
|
||||
font-size: 1rem;
|
||||
padding: 0.5em 1em;
|
||||
width: 100%;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 136, 170, 0.8);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
font-size: 1rem;
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,6 @@ export default class Application extends Component {
|
||||
local[tx.hash].transaction = tx;
|
||||
local[tx.hash].stats = data.stats;
|
||||
});
|
||||
|
||||
// Convert local transactions to array
|
||||
const localTransactions = Object.keys(local).map(hash => {
|
||||
const data = local[hash];
|
||||
|
||||
@@ -6,6 +6,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
.txhash {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
padding-right: 3ch;
|
||||
text-overflow: ellipsis;
|
||||
width: 10ch;
|
||||
}
|
||||
|
||||
.transaction {
|
||||
td {
|
||||
padding: 7px 15px;
|
||||
|
||||
@@ -31,8 +31,8 @@ class BaseTransaction extends Component {
|
||||
|
||||
renderHash (hash) {
|
||||
return (
|
||||
<code title={ hash }>
|
||||
{ this.shortHash(hash) }
|
||||
<code title={ hash } className={ styles.txhash }>
|
||||
{ hash }
|
||||
</code>
|
||||
);
|
||||
}
|
||||
@@ -206,7 +206,10 @@ export class LocalTransaction extends BaseTransaction {
|
||||
From
|
||||
</th>
|
||||
<th>
|
||||
Gas Price / Gas
|
||||
Gas Price
|
||||
</th>
|
||||
<th>
|
||||
Gas
|
||||
</th>
|
||||
<th>
|
||||
Status
|
||||
@@ -224,18 +227,18 @@ export class LocalTransaction extends BaseTransaction {
|
||||
|
||||
toggleResubmit = () => {
|
||||
const { transaction } = this.props;
|
||||
const { isResubmitting, gasPrice } = this.state;
|
||||
const { isResubmitting } = this.state;
|
||||
|
||||
this.setState({
|
||||
const nextState = {
|
||||
isResubmitting: !isResubmitting
|
||||
});
|
||||
};
|
||||
|
||||
if (gasPrice === null) {
|
||||
this.setState({
|
||||
gasPrice: `0x${transaction.gasPrice.toString(16)}`,
|
||||
gas: `0x${transaction.gas.toString(16)}`
|
||||
});
|
||||
if (!isResubmitting) {
|
||||
nextState.gasPrice = api.util.fromWei(transaction.gasPrice, 'shannon').toNumber();
|
||||
nextState.gas = transaction.gas.div(1000000).toNumber();
|
||||
}
|
||||
|
||||
this.setState(nextState);
|
||||
};
|
||||
|
||||
setGasPrice = el => {
|
||||
@@ -251,16 +254,15 @@ export class LocalTransaction extends BaseTransaction {
|
||||
};
|
||||
|
||||
sendTransaction = () => {
|
||||
const { transaction } = this.props;
|
||||
const { transaction, status } = this.props;
|
||||
const { gasPrice, gas } = this.state;
|
||||
|
||||
const newTransaction = {
|
||||
from: transaction.from,
|
||||
to: transaction.to,
|
||||
nonce: transaction.nonce,
|
||||
value: transaction.value,
|
||||
data: transaction.input,
|
||||
gasPrice, gas
|
||||
gasPrice: api.util.toWei(gasPrice, 'shannon'),
|
||||
gas: new BigNumber(gas).mul(1000000)
|
||||
};
|
||||
|
||||
this.setState({
|
||||
@@ -268,11 +270,21 @@ export class LocalTransaction extends BaseTransaction {
|
||||
isSending: true
|
||||
});
|
||||
|
||||
const closeSending = () => this.setState({
|
||||
isSending: false,
|
||||
gasPrice: null,
|
||||
gas: null
|
||||
});
|
||||
const closeSending = () => {
|
||||
this.setState({
|
||||
isSending: false,
|
||||
gasPrice: null,
|
||||
gas: null
|
||||
});
|
||||
};
|
||||
|
||||
if (transaction.to) {
|
||||
newTransaction.to = transaction.to;
|
||||
}
|
||||
|
||||
if (!['mined', 'replaced'].includes(status)) {
|
||||
newTransaction.nonce = transaction.nonce;
|
||||
}
|
||||
|
||||
api.eth.sendTransaction(newTransaction)
|
||||
.then(closeSending)
|
||||
@@ -290,9 +302,9 @@ export class LocalTransaction extends BaseTransaction {
|
||||
const resubmit = isSending ? (
|
||||
'sending...'
|
||||
) : (
|
||||
<a href='javascript:void' onClick={ this.toggleResubmit }>
|
||||
<button onClick={ this.toggleResubmit }>
|
||||
resubmit
|
||||
</a>
|
||||
</button>
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -308,7 +320,8 @@ export class LocalTransaction extends BaseTransaction {
|
||||
</td>
|
||||
<td>
|
||||
{ this.renderGasPrice(transaction) }
|
||||
<br />
|
||||
</td>
|
||||
<td>
|
||||
{ this.renderGas(transaction) }
|
||||
</td>
|
||||
<td>
|
||||
@@ -345,9 +358,9 @@ export class LocalTransaction extends BaseTransaction {
|
||||
return (
|
||||
<tr className={ styles.transaction }>
|
||||
<td>
|
||||
<a href='javascript:void' onClick={ this.toggleResubmit }>
|
||||
<button onClick={ this.toggleResubmit }>
|
||||
cancel
|
||||
</a>
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
{ this.renderHash(transaction.hash) }
|
||||
@@ -357,20 +370,24 @@ export class LocalTransaction extends BaseTransaction {
|
||||
</td>
|
||||
<td className={ styles.edit }>
|
||||
<input
|
||||
type='text'
|
||||
type='number'
|
||||
value={ gasPrice }
|
||||
onChange={ this.setGasPrice }
|
||||
/>
|
||||
<span>shannon</span>
|
||||
</td>
|
||||
<td className={ styles.edit }>
|
||||
<input
|
||||
type='text'
|
||||
type='number'
|
||||
value={ gas }
|
||||
onChange={ this.setGas }
|
||||
/>
|
||||
<span>MGas</span>
|
||||
</td>
|
||||
<td colSpan='2'>
|
||||
<a href='javascript:void' onClick={ this.sendTransaction }>
|
||||
<button onClick={ this.sendTransaction }>
|
||||
Send
|
||||
</a>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { sha3 } from '../parity.js';
|
||||
import { api, sha3 } from '../parity.js';
|
||||
import { getOwner } from '../util/registry';
|
||||
|
||||
export const clear = () => ({ type: 'lookup clear' });
|
||||
@@ -34,14 +34,21 @@ export const lookup = (name, key) => (dispatch, getState) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const getAddress = contract.functions
|
||||
.find((f) => f.name === 'getAddress');
|
||||
const method = key === 'A'
|
||||
? contract.instance.getAddress
|
||||
: contract.instance.getData;
|
||||
|
||||
name = name.toLowerCase();
|
||||
dispatch(lookupStart(name, key));
|
||||
|
||||
getAddress.call({}, [ sha3.text(name), key ])
|
||||
.then((address) => dispatch(success('lookup', address)))
|
||||
method.call({}, [ sha3.text(name), key ])
|
||||
.then((result) => {
|
||||
if (key !== 'A') {
|
||||
result = api.util.bytesToHex(result);
|
||||
}
|
||||
|
||||
dispatch(success('lookup', result));
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`could not lookup ${key} for ${name}`);
|
||||
if (err) {
|
||||
|
||||
@@ -46,8 +46,9 @@ export const update = (name, key, value) => (dispatch, getState) => {
|
||||
throw new Error(`you are not the owner of "${name}"`);
|
||||
}
|
||||
|
||||
const fnName = key === 'A' ? 'setAddress' : 'set';
|
||||
const method = contract.instance[fnName];
|
||||
const method = key === 'A'
|
||||
? contract.instance.setAddress
|
||||
: contract.instance.setData || contract.instance.set;
|
||||
|
||||
const options = {
|
||||
from: account.address
|
||||
|
||||
@@ -34,7 +34,7 @@ export default (address) => {
|
||||
|
||||
return (
|
||||
<img
|
||||
src={ `${parityNode}/${address}/` }
|
||||
src={ `${parityNode}/api/content/${address.replace(/^0x/, '')}` }
|
||||
alt={ address }
|
||||
style={ styles }
|
||||
/>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import ReactDOM from 'react-dom';
|
||||
import React from 'react';
|
||||
import { AppContainer } from 'react-hot-loader';
|
||||
|
||||
import injectTapEventPlugin from 'react-tap-event-plugin';
|
||||
injectTapEventPlugin();
|
||||
@@ -27,6 +28,21 @@ import '../../assets/fonts/RobotoMono/font.css';
|
||||
import './style.css';
|
||||
|
||||
ReactDOM.render(
|
||||
<Application />,
|
||||
<AppContainer>
|
||||
<Application />
|
||||
</AppContainer>,
|
||||
document.querySelector('#container')
|
||||
);
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept('./signaturereg/Application/index.js', () => {
|
||||
require('./signaturereg/Application/index.js');
|
||||
|
||||
ReactDOM.render(
|
||||
<AppContainer>
|
||||
<Application />
|
||||
</AppContainer>,
|
||||
document.querySelector('#container')
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import ReactDOM from 'react-dom';
|
||||
import React from 'react';
|
||||
import { Redirect, Router, Route, hashHistory } from 'react-router';
|
||||
import { AppContainer } from 'react-hot-loader';
|
||||
|
||||
import injectTapEventPlugin from 'react-tap-event-plugin';
|
||||
injectTapEventPlugin();
|
||||
@@ -31,13 +32,37 @@ import '../../assets/fonts/RobotoMono/font.css';
|
||||
import './style.css';
|
||||
|
||||
ReactDOM.render(
|
||||
<Router history={ hashHistory }>
|
||||
<Redirect from='/' to='/overview' />
|
||||
<Route path='/' component={ Application }>
|
||||
<Route path='deploy' component={ Deploy } />
|
||||
<Route path='overview' component={ Overview } />
|
||||
<Route path='transfer' component={ Transfer } />
|
||||
</Route>
|
||||
</Router>,
|
||||
<AppContainer>
|
||||
<Router history={ hashHistory }>
|
||||
<Redirect from='/' to='/overview' />
|
||||
<Route path='/' component={ Application }>
|
||||
<Route path='deploy' component={ Deploy } />
|
||||
<Route path='overview' component={ Overview } />
|
||||
<Route path='transfer' component={ Transfer } />
|
||||
</Route>
|
||||
</Router>
|
||||
</AppContainer>,
|
||||
document.querySelector('#container')
|
||||
);
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept('./tokendeploy/Application/index.js', () => {
|
||||
require('./tokendeploy/Application/index.js');
|
||||
require('./tokendeploy/Overview/index.js');
|
||||
require('./tokendeploy/Transfer/index.js');
|
||||
|
||||
ReactDOM.render(
|
||||
<AppContainer>
|
||||
<Router history={ hashHistory }>
|
||||
<Redirect from='/' to='/overview' />
|
||||
<Route path='/' component={ Application }>
|
||||
<Route path='deploy' component={ Deploy } />
|
||||
<Route path='overview' component={ Overview } />
|
||||
<Route path='transfer' component={ Transfer } />
|
||||
</Route>
|
||||
</Router>
|
||||
</AppContainer>,
|
||||
document.querySelector('#container')
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ export function attachInstances () {
|
||||
.all([
|
||||
api.parity.registryAddress(),
|
||||
api.parity.netChain(),
|
||||
api.partiy.netVersion()
|
||||
api.net.version()
|
||||
])
|
||||
.then(([registryAddress, netChain, _netVersion]) => {
|
||||
const registry = api.newContract(abis.registry, registryAddress).instance;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import ReactDOM from 'react-dom';
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { AppContainer } from 'react-hot-loader';
|
||||
|
||||
import injectTapEventPlugin from 'react-tap-event-plugin';
|
||||
injectTapEventPlugin();
|
||||
@@ -29,10 +30,25 @@ import '../../assets/fonts/RobotoMono/font.css';
|
||||
import './style.css';
|
||||
|
||||
ReactDOM.render(
|
||||
(
|
||||
<AppContainer>
|
||||
<Provider store={ store }>
|
||||
<Container />
|
||||
</Provider>
|
||||
),
|
||||
</AppContainer>,
|
||||
document.querySelector('#container')
|
||||
);
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept('./tokenreg/Container.js', () => {
|
||||
require('./tokenreg/Container.js');
|
||||
|
||||
ReactDOM.render(
|
||||
<AppContainer>
|
||||
<Provider store={ store }>
|
||||
<Container />
|
||||
</Provider>
|
||||
</AppContainer>,
|
||||
document.querySelector('#container')
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { Chip } from 'material-ui';
|
||||
|
||||
import IdentityIcon from '../IdentityIcon' ;
|
||||
import IdentityIcon from '../IdentityIcon';
|
||||
|
||||
import styles from './chip.css';
|
||||
|
||||
|
||||
@@ -102,3 +102,4 @@ if (module.hot) {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -120,20 +120,22 @@ export default class HardwareStore {
|
||||
});
|
||||
}
|
||||
|
||||
createAccountInfo (entry) {
|
||||
createAccountInfo (entry, original = {}) {
|
||||
const { address, manufacturer, name } = entry;
|
||||
|
||||
return Promise
|
||||
.all([
|
||||
this._api.parity.setAccountName(address, name),
|
||||
this._api.parity.setAccountMeta(address, {
|
||||
original.name
|
||||
? Promise.resolve(true)
|
||||
: this._api.parity.setAccountName(address, name),
|
||||
this._api.parity.setAccountMeta(address, Object.assign({
|
||||
description: `${manufacturer} ${name}`,
|
||||
hardware: {
|
||||
manufacturer
|
||||
},
|
||||
tags: ['hardware'],
|
||||
timestamp: Date.now()
|
||||
})
|
||||
}, original.meta || {}))
|
||||
])
|
||||
.catch((error) => {
|
||||
console.warn('HardwareStore::createEntry', error);
|
||||
|
||||
@@ -130,25 +130,58 @@ describe('mobx/HardwareStore', () => {
|
||||
|
||||
describe('operations', () => {
|
||||
describe('createAccountInfo', () => {
|
||||
beforeEach(() => {
|
||||
return store.createAccountInfo({
|
||||
address: 'testAddr',
|
||||
manufacturer: 'testMfg',
|
||||
name: 'testName'
|
||||
describe('when not existing', () => {
|
||||
beforeEach(() => {
|
||||
return store.createAccountInfo({
|
||||
address: 'testAddr',
|
||||
manufacturer: 'testMfg',
|
||||
name: 'testName'
|
||||
});
|
||||
});
|
||||
|
||||
it('calls into parity_setAccountName', () => {
|
||||
expect(api.parity.setAccountName).to.have.been.calledWith('testAddr', 'testName');
|
||||
});
|
||||
|
||||
it('calls into parity_setAccountMeta', () => {
|
||||
expect(api.parity.setAccountMeta).to.have.been.calledWith('testAddr', sinon.match({
|
||||
description: 'testMfg testName',
|
||||
hardware: {
|
||||
manufacturer: 'testMfg'
|
||||
},
|
||||
tags: ['hardware']
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
it('calls into parity_setAccountName', () => {
|
||||
expect(api.parity.setAccountName).to.have.been.calledWith('testAddr', 'testName');
|
||||
});
|
||||
describe('when already exists', () => {
|
||||
beforeEach(() => {
|
||||
return store.createAccountInfo({
|
||||
address: 'testAddr',
|
||||
manufacturer: 'testMfg',
|
||||
name: 'testName'
|
||||
}, {
|
||||
name: 'originalName',
|
||||
meta: {
|
||||
description: 'originalDescription',
|
||||
tags: ['tagA', 'tagB']
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('calls into parity_setAccountMeta', () => {
|
||||
expect(api.parity.setAccountMeta).to.have.been.calledWith('testAddr', sinon.match({
|
||||
description: 'testMfg testName',
|
||||
hardware: {
|
||||
manufacturer: 'testMfg'
|
||||
}
|
||||
}));
|
||||
it('does not call into parity_setAccountName', () => {
|
||||
expect(api.parity.setAccountName).not.to.have.been.called;
|
||||
});
|
||||
|
||||
it('calls into parity_setAccountMeta', () => {
|
||||
expect(api.parity.setAccountMeta).to.have.been.calledWith('testAddr', sinon.match({
|
||||
description: 'originalDescription',
|
||||
hardware: {
|
||||
manufacturer: 'testMfg'
|
||||
},
|
||||
tags: ['tagA', 'tagB']
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { omitBy } from 'lodash';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
@@ -106,9 +105,6 @@ export default class WalletDetails extends Component {
|
||||
renderMultisigDetails () {
|
||||
const { accounts, wallet, errors } = this.props;
|
||||
|
||||
// Wallets cannot create contracts
|
||||
const _accounts = omitBy(accounts, (a) => a.wallet);
|
||||
|
||||
return (
|
||||
<Form>
|
||||
<Input
|
||||
@@ -148,7 +144,7 @@ export default class WalletDetails extends Component {
|
||||
/>
|
||||
|
||||
<AddressSelect
|
||||
accounts={ _accounts }
|
||||
accounts={ accounts }
|
||||
error={ errors.account }
|
||||
hint={
|
||||
<FormattedMessage
|
||||
|
||||
@@ -22,10 +22,11 @@ import Contract from '~/api/contract';
|
||||
import { ERROR_CODES } from '~/api/transport/error';
|
||||
import Contracts from '~/contracts';
|
||||
import { wallet as walletAbi } from '~/contracts/abi';
|
||||
import { wallet as walletCode, walletLibraryRegKey, fullWalletCode } from '~/contracts/code/wallet';
|
||||
import { wallet as walletCode, walletLibrary as walletLibraryCode, walletLibraryRegKey, fullWalletCode } from '~/contracts/code/wallet';
|
||||
|
||||
import { validateUint, validateAddress, validateName } from '~/util/validation';
|
||||
import { toWei } from '~/api/util/wei';
|
||||
import { deploy } from '~/util/tx';
|
||||
import WalletsUtils from '~/util/wallets';
|
||||
|
||||
const STEPS = {
|
||||
@@ -179,6 +180,8 @@ export default class CreateWalletStore {
|
||||
this.wallet.owners = owners;
|
||||
this.wallet.required = require.toNumber();
|
||||
this.wallet.dailylimit = dailylimit.limit;
|
||||
|
||||
this.wallet = this.getWalletWithMeta(this.wallet);
|
||||
});
|
||||
|
||||
return this.addWallet(this.wallet);
|
||||
@@ -202,21 +205,51 @@ export default class CreateWalletStore {
|
||||
return null; // exception when registry is not available
|
||||
})
|
||||
.then((address) => {
|
||||
const walletLibraryAddress = (address || '').replace(/^0x/, '').toLowerCase();
|
||||
const code = walletLibraryAddress.length && !/^0+$/.test(walletLibraryAddress)
|
||||
? walletCode.replace(/(_)+WalletLibrary(_)+/g, walletLibraryAddress)
|
||||
: fullWalletCode;
|
||||
if (!address || /^(0x)?0*$/.test(address)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check that it's actually the expected code
|
||||
return this.api.eth
|
||||
.getCode(address)
|
||||
.then((code) => {
|
||||
const strippedCode = code.replace(/^0x/, '');
|
||||
|
||||
// The actual deployed code is included in the wallet
|
||||
// library code (which might have some more data)
|
||||
if (walletLibraryCode.indexOf(strippedCode) >= 0) {
|
||||
return address;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
})
|
||||
.then((address) => {
|
||||
let code = fullWalletCode;
|
||||
|
||||
if (address) {
|
||||
const walletLibraryAddress = address.replace(/^0x/, '').toLowerCase();
|
||||
|
||||
code = walletCode.replace(/(_)+WalletLibrary(_)+/g, walletLibraryAddress);
|
||||
} else {
|
||||
console.warn('wallet library has not been found in the registry');
|
||||
}
|
||||
|
||||
const options = {
|
||||
data: code,
|
||||
from: account
|
||||
};
|
||||
|
||||
return this.api
|
||||
.newContract(walletAbi)
|
||||
.deploy(options, [ owners, required, daylimit ], this.onDeploymentState);
|
||||
const contract = this.api.newContract(walletAbi);
|
||||
|
||||
this.wallet = this.getWalletWithMeta(this.wallet);
|
||||
return deploy(contract, options, [ owners, required, daylimit ], this.wallet.metadata, this.onDeploymentState);
|
||||
})
|
||||
.then((address) => {
|
||||
if (!address || /^(0x)?0*$/.test(address)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.deployed = true;
|
||||
this.wallet.address = address;
|
||||
return this.addWallet(this.wallet);
|
||||
@@ -233,26 +266,37 @@ export default class CreateWalletStore {
|
||||
}
|
||||
|
||||
@action addWallet = (wallet) => {
|
||||
const { address, name, description } = wallet;
|
||||
const { address, name, metadata } = wallet;
|
||||
|
||||
return Promise
|
||||
.all([
|
||||
this.api.parity.setAccountName(address, name),
|
||||
this.api.parity.setAccountMeta(address, {
|
||||
abi: walletAbi,
|
||||
wallet: true,
|
||||
timestamp: Date.now(),
|
||||
deleted: false,
|
||||
description,
|
||||
name,
|
||||
tags: ['wallet']
|
||||
})
|
||||
this.api.parity.setAccountMeta(address, metadata)
|
||||
])
|
||||
.then(() => {
|
||||
this.step = 'INFO';
|
||||
});
|
||||
}
|
||||
|
||||
getWalletWithMeta = (wallet) => {
|
||||
const { name, description } = wallet;
|
||||
|
||||
const metadata = {
|
||||
abi: walletAbi,
|
||||
wallet: true,
|
||||
timestamp: Date.now(),
|
||||
deleted: false,
|
||||
tags: [ 'wallet' ],
|
||||
description,
|
||||
name
|
||||
};
|
||||
|
||||
return {
|
||||
...wallet,
|
||||
metadata
|
||||
};
|
||||
}
|
||||
|
||||
onDeploymentState = (error, data) => {
|
||||
if (error) {
|
||||
return console.error('createWallet::onDeploymentState', error);
|
||||
@@ -298,6 +342,15 @@ export default class CreateWalletStore {
|
||||
);
|
||||
return;
|
||||
|
||||
case 'confirmationNeeded':
|
||||
this.deployState = (
|
||||
<FormattedMessage
|
||||
id='createWallet.states.confirmationNeeded'
|
||||
defaultMessage='The contract deployment needs confirmations from other owners of the Wallet'
|
||||
/>
|
||||
);
|
||||
return;
|
||||
|
||||
case 'completed':
|
||||
this.deployState = (
|
||||
<FormattedMessage
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { pick, omitBy } from 'lodash';
|
||||
import { pick } from 'lodash';
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
@@ -23,6 +23,7 @@ import { connect } from 'react-redux';
|
||||
import { BusyStep, Button, CompletedStep, CopyToClipboard, GasPriceEditor, IdentityIcon, Portal, TxHash, Warning } from '~/ui';
|
||||
import { CancelIcon, DoneIcon } from '~/ui/Icons';
|
||||
import { ERRORS, validateAbi, validateCode, validateName } from '~/util/validation';
|
||||
import { deploy, deployEstimateGas } from '~/util/tx';
|
||||
|
||||
import DetailsStep from './DetailsStep';
|
||||
import ParametersStep from './ParametersStep';
|
||||
@@ -73,7 +74,7 @@ class DeployContract extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired,
|
||||
store: PropTypes.object.isRequired
|
||||
}
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
accounts: PropTypes.object.isRequired,
|
||||
@@ -141,12 +142,13 @@ class DeployContract extends Component {
|
||||
render () {
|
||||
const { step, deployError, rejected, inputs } = this.state;
|
||||
|
||||
const realStep = Object.keys(STEPS).findIndex((k) => k === step);
|
||||
const realSteps = deployError || rejected
|
||||
? null
|
||||
: Object.keys(STEPS)
|
||||
.filter((k) => k !== 'CONTRACT_PARAMETERS' || inputs.length > 0)
|
||||
.map((k) => STEPS[k]);
|
||||
const realStepKeys = deployError || rejected
|
||||
? []
|
||||
: Object.keys(STEPS).filter((k) => k !== 'CONTRACT_PARAMETERS' || inputs.length > 0);
|
||||
const realStep = realStepKeys.findIndex((k) => k === step);
|
||||
const realSteps = realStepKeys.length
|
||||
? realStepKeys.map((k) => STEPS[k])
|
||||
: null;
|
||||
|
||||
const title = realSteps
|
||||
? null
|
||||
@@ -422,9 +424,9 @@ class DeployContract extends Component {
|
||||
from: fromAddress
|
||||
};
|
||||
|
||||
api
|
||||
.newContract(abiParsed)
|
||||
.deployEstimateGas(options, params)
|
||||
const contract = api.newContract(abiParsed);
|
||||
|
||||
deployEstimateGas(contract, options, params)
|
||||
.then(([gasEst, gas]) => {
|
||||
this.gasStore.setEstimated(gasEst.toFixed(0));
|
||||
this.gasStore.setGas(gas.toFixed(0));
|
||||
@@ -490,6 +492,17 @@ class DeployContract extends Component {
|
||||
const { api, store } = this.context;
|
||||
const { source } = this.props;
|
||||
const { abiParsed, code, description, name, params, fromAddress } = this.state;
|
||||
|
||||
const metadata = {
|
||||
abi: abiParsed,
|
||||
contract: true,
|
||||
deleted: false,
|
||||
timestamp: Date.now(),
|
||||
name,
|
||||
description,
|
||||
source
|
||||
};
|
||||
|
||||
const options = {
|
||||
data: code,
|
||||
from: fromAddress
|
||||
@@ -499,28 +512,25 @@ class DeployContract extends Component {
|
||||
|
||||
const contract = api.newContract(abiParsed);
|
||||
|
||||
contract
|
||||
.deploy(options, params, this.onDeploymentState)
|
||||
deploy(contract, options, params, metadata, this.onDeploymentState)
|
||||
.then((address) => {
|
||||
const blockNumber = contract._receipt
|
||||
// No contract address given, might need some confirmations
|
||||
// from the wallet owners...
|
||||
if (!address || /^(0x)?0*$/.test(address)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
metadata.blockNumber = contract._receipt
|
||||
? contract.receipt.blockNumber.toNumber()
|
||||
: null;
|
||||
|
||||
return Promise.all([
|
||||
api.parity.setAccountName(address, name),
|
||||
api.parity.setAccountMeta(address, {
|
||||
abi: abiParsed,
|
||||
contract: true,
|
||||
timestamp: Date.now(),
|
||||
deleted: false,
|
||||
blockNumber,
|
||||
description,
|
||||
source
|
||||
})
|
||||
api.parity.setAccountMeta(address, metadata)
|
||||
])
|
||||
.then(() => {
|
||||
console.log(`contract deployed at ${address}`);
|
||||
this.setState({ step: 'DEPLOYMENT', address });
|
||||
this.setState({ step: 'COMPLETED', address });
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -589,6 +599,17 @@ class DeployContract extends Component {
|
||||
});
|
||||
return;
|
||||
|
||||
case 'confirmationNeeded':
|
||||
this.setState({
|
||||
deployState: (
|
||||
<FormattedMessage
|
||||
id='deployContract.state.confirmationNeeded'
|
||||
defaultMessage='The operation needs confirmations from the other owners of the contract'
|
||||
/>
|
||||
)
|
||||
});
|
||||
return;
|
||||
|
||||
case 'completed':
|
||||
this.setState({
|
||||
deployState: (
|
||||
@@ -614,17 +635,14 @@ class DeployContract extends Component {
|
||||
function mapStateToProps (initState, initProps) {
|
||||
const { accounts } = initProps;
|
||||
|
||||
// Skip Wallet accounts : they can't create Contracts
|
||||
const _accounts = omitBy(accounts, (a) => a.wallet);
|
||||
|
||||
const fromAddresses = Object.keys(_accounts);
|
||||
const fromAddresses = Object.keys(accounts);
|
||||
|
||||
return (state) => {
|
||||
const balances = pick(state.balances.balances, fromAddresses);
|
||||
const { gasLimit } = state.nodeStatus;
|
||||
|
||||
return {
|
||||
accounts: _accounts,
|
||||
accounts,
|
||||
balances,
|
||||
gasLimit
|
||||
};
|
||||
|
||||
162
js/src/modals/Faucet/faucet.js
Normal file
162
js/src/modals/Faucet/faucet.js
Normal file
@@ -0,0 +1,162 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { txLink } from '~/3rdparty/etherscan/links';
|
||||
import { Button, ModalBox, Portal, ShortenedHash } from '~/ui';
|
||||
import { CloseIcon, DialIcon, DoneIcon, ErrorIcon, SendIcon } from '~/ui/Icons';
|
||||
|
||||
import Store from './store';
|
||||
|
||||
@observer
|
||||
export default class Faucet extends Component {
|
||||
static propTypes = {
|
||||
address: PropTypes.string.isRequired,
|
||||
netVersion: PropTypes.string.isRequired,
|
||||
onClose: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
store = new Store(this.props.netVersion, this.props.address);
|
||||
|
||||
render () {
|
||||
const { error, isBusy, isCompleted } = this.store;
|
||||
|
||||
let icon = <DialIcon />;
|
||||
|
||||
if (isCompleted) {
|
||||
icon = error
|
||||
? <ErrorIcon />
|
||||
: <DoneIcon />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Portal
|
||||
buttons={ this.renderActions() }
|
||||
busy={ isBusy }
|
||||
isSmallModal
|
||||
onClose={ this.onClose }
|
||||
open
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='faucet.title'
|
||||
defaultMessage='Kovan ETH Faucet'
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ModalBox
|
||||
icon={ icon }
|
||||
summary={
|
||||
isCompleted
|
||||
? this.renderSummaryDone()
|
||||
: this.renderSummaryRequest()
|
||||
}
|
||||
/>
|
||||
</Portal>
|
||||
);
|
||||
}
|
||||
|
||||
renderActions = () => {
|
||||
const { canTransact, isBusy, isCompleted } = this.store;
|
||||
|
||||
return isCompleted || isBusy
|
||||
? (
|
||||
<Button
|
||||
disabled={ isBusy }
|
||||
icon={ <DoneIcon /> }
|
||||
key='done'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='faucet.buttons.done'
|
||||
defaultMessage='close'
|
||||
/>
|
||||
}
|
||||
onClick={ this.onClose }
|
||||
/>
|
||||
)
|
||||
: [
|
||||
<Button
|
||||
icon={ <CloseIcon /> }
|
||||
key='close'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='faucet.buttons.close'
|
||||
defaultMessage='close'
|
||||
/>
|
||||
}
|
||||
onClick={ this.onClose }
|
||||
/>,
|
||||
<Button
|
||||
disabled={ !canTransact }
|
||||
icon={ <SendIcon /> }
|
||||
key='request'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='faucet.buttons.request'
|
||||
defaultMessage='request'
|
||||
/>
|
||||
}
|
||||
onClick={ this.onExecute }
|
||||
/>
|
||||
];
|
||||
}
|
||||
|
||||
renderSummaryDone () {
|
||||
const { error, responseText, responseTxHash } = this.store;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id='faucet.summary.done'
|
||||
defaultMessage='Your Kovan ETH has been requested from the faucet which responded with -'
|
||||
/>
|
||||
{
|
||||
error
|
||||
? (
|
||||
<p>{ error }</p>
|
||||
)
|
||||
: (
|
||||
<p>
|
||||
<span>{ responseText } </span>
|
||||
<a href={ txLink(responseTxHash, false, '42') } target='_blank'>
|
||||
<ShortenedHash data={ responseTxHash } />
|
||||
</a>
|
||||
</p>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderSummaryRequest () {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='faucet.summary.info'
|
||||
defaultMessage='To request a deposit of Kovan ETH to this address, you need to ensure that the address is sms-verified on the mainnet. Once executed the faucet will deposit Kovan ETH into the current account.'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
onClose = () => {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
||||
onExecute = () => {
|
||||
return this.store.makeItRain();
|
||||
}
|
||||
}
|
||||
17
js/src/modals/Faucet/index.js
Normal file
17
js/src/modals/Faucet/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './faucet';
|
||||
126
js/src/modals/Faucet/store.js
Normal file
126
js/src/modals/Faucet/store.js
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { action, computed, observable, transaction } from 'mobx';
|
||||
import apiutil from '~/api/util';
|
||||
|
||||
const ENDPOINT = 'http://faucet.kovan.network/api/';
|
||||
|
||||
export default class Store {
|
||||
@observable addressReceive = null;
|
||||
@observable addressVerified = null;
|
||||
@observable error = null;
|
||||
@observable responseText = null;
|
||||
@observable responseTxHash = null;
|
||||
@observable isBusy = false;
|
||||
@observable isCompleted = false;
|
||||
@observable isDestination = false;
|
||||
@observable isDone = false;
|
||||
|
||||
constructor (netVersion, address) {
|
||||
transaction(() => {
|
||||
this.setDestination(netVersion === '42');
|
||||
|
||||
this.setAddressReceive(address);
|
||||
this.setAddressVerified(address);
|
||||
});
|
||||
}
|
||||
|
||||
@computed get canTransact () {
|
||||
return !this.isBusy && this.addressReceiveValid && this.addressVerifiedValid;
|
||||
}
|
||||
|
||||
@computed get addressReceiveValid () {
|
||||
return apiutil.isAddressValid(this.addressReceive);
|
||||
}
|
||||
|
||||
@computed get addressVerifiedValid () {
|
||||
return apiutil.isAddressValid(this.addressVerified);
|
||||
}
|
||||
|
||||
@action setAddressReceive = (address) => {
|
||||
this.addressReceive = address;
|
||||
}
|
||||
|
||||
@action setAddressVerified = (address) => {
|
||||
this.addressVerified = address;
|
||||
}
|
||||
|
||||
@action setBusy = (isBusy) => {
|
||||
this.isBusy = isBusy;
|
||||
}
|
||||
|
||||
@action setCompleted = (isCompleted) => {
|
||||
transaction(() => {
|
||||
this.setBusy(false);
|
||||
this.isCompleted = isCompleted;
|
||||
});
|
||||
}
|
||||
|
||||
@action setDestination = (isDestination) => {
|
||||
this.isDestination = isDestination;
|
||||
}
|
||||
|
||||
@action setError = (error) => {
|
||||
if (error.indexOf('not certified') !== -1) {
|
||||
this.error = `${error}. Please ensure that this account is sms certified on the mainnet.`;
|
||||
} else {
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
|
||||
@action setResponse = (response) => {
|
||||
this.responseText = response.result;
|
||||
this.responseTxHash = response.tx;
|
||||
}
|
||||
|
||||
makeItRain = () => {
|
||||
this.setBusy(true);
|
||||
|
||||
const options = {
|
||||
method: 'GET',
|
||||
mode: 'cors'
|
||||
};
|
||||
const url = `${ENDPOINT}${this.addressVerified}`;
|
||||
|
||||
return fetch(url, options)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.catch(() => {
|
||||
return null;
|
||||
})
|
||||
.then((response) => {
|
||||
transaction(() => {
|
||||
if (!response || response.error) {
|
||||
this.setError(
|
||||
response
|
||||
? response.error
|
||||
: 'Unable to complete request to the faucet, the server may be unavailable. Please try again later.'
|
||||
);
|
||||
} else {
|
||||
this.setResponse(response);
|
||||
}
|
||||
|
||||
this.setCompleted(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user