Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5fcf3b8ed | ||
|
|
6bb3edeb8a | ||
|
|
16c091590f | ||
|
|
eb7c648250 | ||
|
|
be23afd1cd | ||
|
|
19535333c9 | ||
|
|
5a1a7f6f45 | ||
|
|
148b20978c | ||
|
|
75b4bea521 | ||
|
|
69f53ffb2b | ||
|
|
eee0e6f295 | ||
|
|
822fa82056 | ||
|
|
83825befea | ||
|
|
4e93537e45 | ||
|
|
c0e32bac0e | ||
|
|
8b459b7bd3 | ||
|
|
51f830e922 | ||
|
|
6d0dfa6dab | ||
|
|
d30c715134 | ||
|
|
a21db69686 | ||
|
|
9afd1006ed | ||
|
|
d097956ede | ||
|
|
949d2b7fd0 | ||
|
|
2d4f4bdd61 | ||
|
|
67bd9a33cf | ||
|
|
109168b990 | ||
|
|
e535174604 | ||
|
|
ea6d517a87 | ||
|
|
965727c5d3 | ||
|
|
083b69ffb1 |
72
Cargo.lock
generated
72
Cargo.lock
generated
@@ -277,7 +277,7 @@ dependencies = [
|
||||
name = "common-types"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ethcore-util 1.7.2",
|
||||
"ethcore-util 1.7.8",
|
||||
"ethjson 0.1.0",
|
||||
"rlp 0.2.0",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -434,7 +434,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethabi"
|
||||
version = "2.0.0"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -466,7 +466,7 @@ dependencies = [
|
||||
"common-types 0.1.0",
|
||||
"crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethabi 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethash 1.7.0",
|
||||
"ethcore-bloom-journal 0.1.0",
|
||||
"ethcore-devtools 1.7.0",
|
||||
@@ -476,7 +476,7 @@ dependencies = [
|
||||
"ethcore-ipc-nano 1.7.0",
|
||||
"ethcore-logger 1.7.0",
|
||||
"ethcore-stratum 1.7.0",
|
||||
"ethcore-util 1.7.2",
|
||||
"ethcore-util 1.7.8",
|
||||
"ethjson 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"ethstore 0.1.0",
|
||||
@@ -545,7 +545,7 @@ name = "ethcore-ipc"
|
||||
version = "1.7.0"
|
||||
dependencies = [
|
||||
"ethcore-devtools 1.7.0",
|
||||
"ethcore-util 1.7.2",
|
||||
"ethcore-util 1.7.8",
|
||||
"nanomsg 0.5.1 (git+https://github.com/paritytech/nanomsg.rs.git?branch=parity-1.7)",
|
||||
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -594,7 +594,7 @@ dependencies = [
|
||||
"ethcore-ipc 1.7.0",
|
||||
"ethcore-ipc-codegen 1.7.0",
|
||||
"ethcore-ipc-nano 1.7.0",
|
||||
"ethcore-util 1.7.2",
|
||||
"ethcore-util 1.7.8",
|
||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nanomsg 0.5.1 (git+https://github.com/paritytech/nanomsg.rs.git?branch=parity-1.7)",
|
||||
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -611,7 +611,7 @@ dependencies = [
|
||||
"ethcore-ipc 1.7.0",
|
||||
"ethcore-ipc-codegen 1.7.0",
|
||||
"ethcore-network 1.7.0",
|
||||
"ethcore-util 1.7.2",
|
||||
"ethcore-util 1.7.8",
|
||||
"evm 0.1.0",
|
||||
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -650,7 +650,7 @@ dependencies = [
|
||||
"ethcore-devtools 1.7.0",
|
||||
"ethcore-io 1.7.0",
|
||||
"ethcore-logger 1.7.0",
|
||||
"ethcore-util 1.7.2",
|
||||
"ethcore-util 1.7.8",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"igd 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -674,14 +674,14 @@ name = "ethcore-secretstore"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethabi 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore 1.7.0",
|
||||
"ethcore-devtools 1.7.0",
|
||||
"ethcore-ipc 1.7.0",
|
||||
"ethcore-ipc-codegen 1.7.0",
|
||||
"ethcore-ipc-nano 1.7.0",
|
||||
"ethcore-logger 1.7.0",
|
||||
"ethcore-util 1.7.2",
|
||||
"ethcore-util 1.7.8",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -711,7 +711,7 @@ dependencies = [
|
||||
"ethcore-ipc-codegen 1.7.0",
|
||||
"ethcore-ipc-nano 1.7.0",
|
||||
"ethcore-logger 1.7.0",
|
||||
"ethcore-util 1.7.2",
|
||||
"ethcore-util 1.7.8",
|
||||
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-macros 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
@@ -724,7 +724,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-util"
|
||||
version = "1.7.2"
|
||||
version = "1.7.8"
|
||||
dependencies = [
|
||||
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -774,7 +774,7 @@ name = "ethjson"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-util 1.7.2",
|
||||
"ethcore-util 1.7.8",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -855,7 +855,7 @@ dependencies = [
|
||||
"ethcore-ipc-nano 1.7.0",
|
||||
"ethcore-light 1.7.0",
|
||||
"ethcore-network 1.7.0",
|
||||
"ethcore-util 1.7.2",
|
||||
"ethcore-util 1.7.8",
|
||||
"ethkey 0.2.0",
|
||||
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -874,7 +874,7 @@ dependencies = [
|
||||
"bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"common-types 0.1.0",
|
||||
"ethcore-util 1.7.2",
|
||||
"ethcore-util 1.7.8",
|
||||
"ethjson 0.1.0",
|
||||
"evmjit 1.7.0",
|
||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -891,7 +891,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore 1.7.0",
|
||||
"ethcore-util 1.7.2",
|
||||
"ethcore-util 1.7.8",
|
||||
"ethjson 0.1.0",
|
||||
"evm 0.1.0",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1136,7 +1136,7 @@ version = "1.7.0"
|
||||
dependencies = [
|
||||
"ethcore-ipc 1.7.0",
|
||||
"ethcore-ipc-codegen 1.7.0",
|
||||
"ethcore-util 1.7.2",
|
||||
"ethcore-util 1.7.8",
|
||||
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -1520,7 +1520,7 @@ dependencies = [
|
||||
name = "native-contract-generator"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethabi 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"heck 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -1529,8 +1529,8 @@ name = "native-contracts"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-util 1.7.2",
|
||||
"ethabi 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-util 1.7.8",
|
||||
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"native-contract-generator 0.1.0",
|
||||
]
|
||||
@@ -1735,7 +1735,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parity"
|
||||
version = "1.7.2"
|
||||
version = "1.7.8"
|
||||
dependencies = [
|
||||
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1755,7 +1755,7 @@ dependencies = [
|
||||
"ethcore-logger 1.7.0",
|
||||
"ethcore-secretstore 1.0.0",
|
||||
"ethcore-stratum 1.7.0",
|
||||
"ethcore-util 1.7.2",
|
||||
"ethcore-util 1.7.8",
|
||||
"ethkey 0.2.0",
|
||||
"ethsync 1.7.0",
|
||||
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1801,7 +1801,7 @@ dependencies = [
|
||||
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-devtools 1.7.0",
|
||||
"ethcore-util 1.7.2",
|
||||
"ethcore-util 1.7.8",
|
||||
"fetch 0.1.0",
|
||||
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
@@ -1844,8 +1844,8 @@ dependencies = [
|
||||
name = "parity-hash-fetch"
|
||||
version = "1.7.0"
|
||||
dependencies = [
|
||||
"ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-util 1.7.2",
|
||||
"ethabi 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-util 1.7.8",
|
||||
"fetch 0.1.0",
|
||||
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1863,7 +1863,7 @@ version = "1.7.0"
|
||||
dependencies = [
|
||||
"cid 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore 1.7.0",
|
||||
"ethcore-util 1.7.2",
|
||||
"ethcore-util 1.7.8",
|
||||
"jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"multihash 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1876,7 +1876,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"ethcore 1.7.0",
|
||||
"ethcore-io 1.7.0",
|
||||
"ethcore-util 1.7.2",
|
||||
"ethcore-util 1.7.8",
|
||||
"ethkey 0.2.0",
|
||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rlp 0.2.0",
|
||||
@@ -1906,7 +1906,7 @@ dependencies = [
|
||||
"ethcore-ipc 1.7.0",
|
||||
"ethcore-light 1.7.0",
|
||||
"ethcore-logger 1.7.0",
|
||||
"ethcore-util 1.7.2",
|
||||
"ethcore-util 1.7.8",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethjson 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
@@ -1948,7 +1948,7 @@ dependencies = [
|
||||
name = "parity-rpc-client"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"ethcore-util 1.7.2",
|
||||
"ethcore-util 1.7.8",
|
||||
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-ws-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
@@ -1984,7 +1984,7 @@ name = "parity-ui"
|
||||
version = "1.7.0"
|
||||
dependencies = [
|
||||
"parity-ui-dev 1.7.0",
|
||||
"parity-ui-precompiled 1.4.0 (git+https://github.com/paritytech/js-precompiled.git?branch=beta)",
|
||||
"parity-ui-precompiled 1.4.0 (git+https://github.com/paritytech/js-precompiled.git?branch=stable)",
|
||||
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -1998,7 +1998,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "parity-ui-precompiled"
|
||||
version = "1.4.0"
|
||||
source = "git+https://github.com/paritytech/js-precompiled.git?branch=beta#5f2cbb92c06e0943225e7d7517ab44ccf07ba92a"
|
||||
source = "git+https://github.com/paritytech/js-precompiled.git?branch=stable#1f3321388c17206628aeb22db00f0988b1510f50"
|
||||
dependencies = [
|
||||
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -2007,11 +2007,11 @@ dependencies = [
|
||||
name = "parity-updater"
|
||||
version = "1.7.0"
|
||||
dependencies = [
|
||||
"ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethabi 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore 1.7.0",
|
||||
"ethcore-ipc 1.7.0",
|
||||
"ethcore-ipc-codegen 1.7.0",
|
||||
"ethcore-util 1.7.2",
|
||||
"ethcore-util 1.7.8",
|
||||
"ethsync 1.7.0",
|
||||
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ipc-common-types 1.7.0",
|
||||
@@ -2367,7 +2367,7 @@ name = "rpc-cli"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"bigint 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-util 1.7.2",
|
||||
"ethcore-util 1.7.8",
|
||||
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-rpc 1.7.0",
|
||||
"parity-rpc-client 1.4.0",
|
||||
@@ -3163,7 +3163,7 @@ dependencies = [
|
||||
"checksum env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e3856f1697098606fc6cb97a93de88ca3f3bc35bb878c725920e6e82ecf05e83"
|
||||
"checksum error-chain 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd5c82c815138e278b8dcdeffc49f27ea6ffb528403e9dea4194f2e3dd40b143"
|
||||
"checksum eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)" = "<none>"
|
||||
"checksum ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0c3d62319ee0f35abf20afe8859dd2668195912614346447bb2dee9fb8da7c62"
|
||||
"checksum ethabi 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8a6af84a622f9169efc6ead7f70043ca2058ef261c50e5e1e2ac60ccd381e386"
|
||||
"checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa"
|
||||
"checksum flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "3eeb481e957304178d2e782f2da1257f1434dfecbae883bafb61ada2a9fea3bb"
|
||||
"checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
|
||||
@@ -3250,7 +3250,7 @@ dependencies = [
|
||||
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
|
||||
"checksum parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1d06f6ee0fda786df3784a96ee3f0629f529b91cbfb7d142f6410e6bcd1ce2c"
|
||||
"checksum parity-tokio-ipc 0.1.5 (git+https://github.com/nikvolf/parity-tokio-ipc)" = "<none>"
|
||||
"checksum parity-ui-precompiled 1.4.0 (git+https://github.com/paritytech/js-precompiled.git?branch=beta)" = "<none>"
|
||||
"checksum parity-ui-precompiled 1.4.0 (git+https://github.com/paritytech/js-precompiled.git?branch=stable)" = "<none>"
|
||||
"checksum parity-wasm 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "51104c8b8da5cd0ebe0ab765dfab37bc1927b4a01a3d870b0fe09d9ee65e35ea"
|
||||
"checksum parity-wordlist 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "52142d717754f7ff7ef0fc8da1bdce4f302dd576fb9bf8b727d6a5fdef33348d"
|
||||
"checksum parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aebb68eebde2c99f89592d925288600fde220177e46b5c9a91ca218d245aeedf"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
description = "Parity Ethereum client"
|
||||
name = "parity"
|
||||
version = "1.7.2"
|
||||
version = "1.7.8"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
@@ -12,7 +12,7 @@ rustc_version = "0.1"
|
||||
[dependencies]
|
||||
parity-ui-dev = { path = "../../js", optional = true }
|
||||
# This is managed by the js/scripts/release.sh script on CI - keep it in a single line
|
||||
parity-ui-precompiled = { git = "https://github.com/paritytech/js-precompiled.git", optional = true, branch = "beta" }
|
||||
parity-ui-precompiled = { git = "https://github.com/paritytech/js-precompiled.git", optional = true, branch = "stable" }
|
||||
|
||||
[features]
|
||||
no-precompiled-js = ["parity-ui-dev"]
|
||||
|
||||
@@ -30,9 +30,6 @@ pub enum ContractCreateResult {
|
||||
/// Returned when contract creation failed.
|
||||
/// VM doesn't have to know the reason.
|
||||
Failed,
|
||||
/// Returned when contract creation failed.
|
||||
/// VM doesn't have to know the reason.
|
||||
FailedInStaticCall,
|
||||
/// Reverted with REVERT.
|
||||
Reverted(U256, ReturnData),
|
||||
}
|
||||
|
||||
@@ -313,20 +313,24 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
let init_off = stack.pop_back();
|
||||
let init_size = stack.pop_back();
|
||||
|
||||
let address_scheme = if instruction == instructions::CREATE { CreateContractAddress::FromSenderAndNonce } else { CreateContractAddress::FromSenderAndCodeHash };
|
||||
let create_gas = provided.expect("`provided` comes through Self::exec from `Gasometer::get_gas_cost_mem`; `gas_gas_mem_cost` guarantees `Some` when instruction is `CALL`/`CALLCODE`/`DELEGATECALL`/`CREATE`; this is `CREATE`; qed");
|
||||
|
||||
let contract_code = self.mem.read_slice(init_off, init_size);
|
||||
let can_create = ext.balance(¶ms.address)? >= endowment && ext.depth() < ext.schedule().max_depth;
|
||||
if ext.is_static() {
|
||||
return Err(evm::Error::MutableCallInStaticContext);
|
||||
}
|
||||
|
||||
// clear return data buffer before creating new call frame.
|
||||
self.return_data = ReturnData::empty();
|
||||
|
||||
let can_create = ext.balance(¶ms.address)? >= endowment && ext.depth() < ext.schedule().max_depth;
|
||||
if !can_create {
|
||||
stack.push(U256::zero());
|
||||
return Ok(InstructionResult::UnusedGas(create_gas));
|
||||
}
|
||||
|
||||
let contract_code = self.mem.read_slice(init_off, init_size);
|
||||
let address_scheme = if instruction == instructions::CREATE { CreateContractAddress::FromSenderAndNonce } else { CreateContractAddress::FromSenderAndCodeHash };
|
||||
|
||||
let create_result = ext.create(&create_gas.as_u256(), &endowment, contract_code, address_scheme);
|
||||
return match create_result {
|
||||
ContractCreateResult::Created(address, gas_left) => {
|
||||
@@ -342,9 +346,6 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
stack.push(U256::zero());
|
||||
Ok(InstructionResult::Ok)
|
||||
},
|
||||
ContractCreateResult::FailedInStaticCall => {
|
||||
Err(evm::Error::MutableCallInStaticContext)
|
||||
},
|
||||
};
|
||||
},
|
||||
instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL | instructions::STATICCALL => {
|
||||
@@ -565,7 +566,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
let source_offset = stack.peek(1);
|
||||
let size = stack.peek(2);
|
||||
let return_data_len = U256::from(self.return_data.len());
|
||||
if source_offset.overflow_add(*size).0 > return_data_len {
|
||||
if source_offset.saturating_add(*size) > return_data_len {
|
||||
return Err(evm::Error::OutOfBounds);
|
||||
}
|
||||
}
|
||||
@@ -906,8 +907,8 @@ mod tests {
|
||||
use rustc_hex::FromHex;
|
||||
use vmtype::VMType;
|
||||
use factory::Factory;
|
||||
use vm::{self, ActionParams, ActionValue};
|
||||
use vm::tests::{FakeExt, test_finalize};
|
||||
use action_params::{ActionParams, ActionValue};
|
||||
use ::tests::{FakeExt, test_finalize};
|
||||
|
||||
#[test]
|
||||
fn should_not_fail_on_tracing_mem() {
|
||||
@@ -931,4 +932,25 @@ mod tests {
|
||||
assert_eq!(ext.calls.len(), 1);
|
||||
assert_eq!(gas_left, 248_212.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_overflow_returndata() {
|
||||
let code = "6001600160000360003e00".from_hex().unwrap();
|
||||
|
||||
let mut params = ActionParams::default();
|
||||
params.address = 5.into();
|
||||
params.gas = 300_000.into();
|
||||
params.gas_price = 1.into();
|
||||
params.code = Some(Arc::new(code));
|
||||
let mut ext = FakeExt::new_byzantium();
|
||||
ext.balances.insert(5.into(), 1_000_000_000.into());
|
||||
ext.tracing = true;
|
||||
|
||||
let err = {
|
||||
let mut vm = Factory::new(VMType::Interpreter, 1).create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).err().unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(err, ::Error::OutOfBounds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,18 +55,20 @@ pub struct FakeExt {
|
||||
pub store: HashMap<H256, H256>,
|
||||
pub suicides: HashSet<Address>,
|
||||
pub calls: HashSet<FakeCall>,
|
||||
sstore_clears: usize,
|
||||
depth: usize,
|
||||
blockhashes: HashMap<U256, H256>,
|
||||
codes: HashMap<Address, Arc<Bytes>>,
|
||||
logs: Vec<FakeLogEntry>,
|
||||
info: EnvInfo,
|
||||
schedule: Schedule,
|
||||
balances: HashMap<Address, U256>,
|
||||
pub sstore_clears: usize,
|
||||
pub depth: usize,
|
||||
pub blockhashes: HashMap<U256, H256>,
|
||||
pub codes: HashMap<Address, Arc<Bytes>>,
|
||||
pub logs: Vec<FakeLogEntry>,
|
||||
pub info: EnvInfo,
|
||||
pub schedule: Schedule,
|
||||
pub balances: HashMap<Address, U256>,
|
||||
pub is_static: bool,
|
||||
pub tracing: bool,
|
||||
}
|
||||
|
||||
// similar to the normal `finalize` function, but ignoring NeedsReturn.
|
||||
fn test_finalize(res: Result<GasLeft, evm::Error>) -> Result<U256, evm::Error> {
|
||||
pub fn test_finalize(res: Result<GasLeft, evm::Error>) -> Result<U256, evm::Error> {
|
||||
match res {
|
||||
Ok(GasLeft::Known(gas)) => Ok(gas),
|
||||
Ok(GasLeft::NeedsReturn{..}) => unimplemented!(), // since ret is unimplemented.
|
||||
@@ -78,6 +80,12 @@ impl FakeExt {
|
||||
pub fn new() -> Self {
|
||||
FakeExt::default()
|
||||
}
|
||||
|
||||
pub fn new_byzantium() -> Self {
|
||||
let mut ext = FakeExt::new();
|
||||
ext.schedule = Schedule::new_byzantium();
|
||||
ext
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Schedule {
|
||||
@@ -168,7 +176,7 @@ impl Ext for FakeExt {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ret(self, _gas: &U256, _data: &ReturnData) -> evm::Result<U256> {
|
||||
fn ret(self, _gas: &U256, _data: &ReturnData, _apply_state: bool) -> evm::Result<U256> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
@@ -192,6 +200,10 @@ impl Ext for FakeExt {
|
||||
fn inc_sstore_clears(&mut self) {
|
||||
self.sstore_clears += 1;
|
||||
}
|
||||
|
||||
fn is_static(&self) -> bool {
|
||||
self.is_static
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -917,7 +929,6 @@ fn test_jumps(factory: super::Factory) {
|
||||
assert_eq!(gas_left, U256::from(54_117));
|
||||
}
|
||||
|
||||
|
||||
evm_test!{test_calls: test_calls_jit, test_calls_int}
|
||||
fn test_calls(factory: super::Factory) {
|
||||
let code = "600054602d57600160005560006000600060006050610998610100f160006000600060006050610998610100f25b".from_hex().unwrap();
|
||||
@@ -962,6 +973,27 @@ fn test_calls(factory: super::Factory) {
|
||||
assert_eq!(ext.calls.len(), 2);
|
||||
}
|
||||
|
||||
evm_test!{test_create_in_staticcall: test_create_in_staticcall_jit, test_create_in_staticcall_int}
|
||||
fn test_create_in_staticcall(factory: super::Factory) {
|
||||
let code = "600060006064f000".from_hex().unwrap();
|
||||
|
||||
let address = Address::from(0x155);
|
||||
let mut params = ActionParams::default();
|
||||
params.gas = U256::from(100_000);
|
||||
params.code = Some(Arc::new(code));
|
||||
params.address = address.clone();
|
||||
let mut ext = FakeExt::new_byzantium();
|
||||
ext.is_static = true;
|
||||
|
||||
let err = {
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap_err()
|
||||
};
|
||||
|
||||
assert_eq!(err, ::Error::MutableCallInStaticContext);
|
||||
assert_eq!(ext.calls.len(), 0);
|
||||
}
|
||||
|
||||
fn assert_set_contains<T : Debug + Eq + PartialEq + Hash>(set: &HashSet<T>, val: &T) {
|
||||
let contains = set.contains(val);
|
||||
if !contains {
|
||||
|
||||
@@ -169,10 +169,6 @@ impl<'a> Runtime<'a> {
|
||||
self.gas_counter = self.gas_limit - gas_left.low_u64();
|
||||
Ok(Some((-1i32).into()))
|
||||
},
|
||||
ext::ContractCreateResult::FailedInStaticCall => {
|
||||
trace!(target: "wasm", "runtime: create contract called in static context");
|
||||
Err(interpreter::Error::Trap("CREATE in static context".to_owned()))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ use std::sync::Arc;
|
||||
use ethcore::basic_account::BasicAccount;
|
||||
use ethcore::encoded;
|
||||
use ethcore::engines::Engine;
|
||||
use ethcore::receipt::Receipt;
|
||||
use ethcore::receipt::{Receipt, TransactionOutcome};
|
||||
use ethcore::state::{self, ProvedExecution};
|
||||
use ethcore::transaction::SignedTransaction;
|
||||
use evm::env_info::EnvInfo;
|
||||
@@ -926,8 +926,7 @@ mod tests {
|
||||
#[test]
|
||||
fn check_receipts() {
|
||||
let receipts = (0..5).map(|_| Receipt {
|
||||
state_root: Some(H256::random()),
|
||||
status_code: None,
|
||||
outcome: TransactionOutcome::StateRoot(H256::random()),
|
||||
gas_used: 21_000u64.into(),
|
||||
log_bloom: Default::default(),
|
||||
logs: Vec::new(),
|
||||
|
||||
@@ -1859,13 +1859,15 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn receipts_roundtrip() {
|
||||
use ethcore::receipt::{Receipt, TransactionOutcome};
|
||||
let req = IncompleteReceiptsRequest {
|
||||
hash: Field::Scalar(Default::default()),
|
||||
};
|
||||
|
||||
let full_req = Request::Receipts(req.clone());
|
||||
let receipt = Receipt::new(TransactionOutcome::Unknown, Default::default(), Vec::new());
|
||||
let res = ReceiptsResponse {
|
||||
receipts: vec![Default::default(), Default::default()],
|
||||
receipts: vec![receipt.clone(), receipt],
|
||||
};
|
||||
let full_res = Response::Receipts(res.clone());
|
||||
|
||||
@@ -2020,6 +2022,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn responses_vec() {
|
||||
use ethcore::receipt::{Receipt, TransactionOutcome};
|
||||
let mut stream = RlpStream::new_list(2);
|
||||
stream.begin_list(0).begin_list(0);
|
||||
|
||||
@@ -2027,7 +2030,7 @@ mod tests {
|
||||
let reqs = vec![
|
||||
Response::Headers(HeadersResponse { headers: vec![] }),
|
||||
Response::HeaderProof(HeaderProofResponse { proof: vec![], hash: Default::default(), td: 100.into()}),
|
||||
Response::Receipts(ReceiptsResponse { receipts: vec![Default::default()] }),
|
||||
Response::Receipts(ReceiptsResponse { receipts: vec![Receipt::new(TransactionOutcome::Unknown, Default::default(), Vec::new())] }),
|
||||
Response::Body(BodyResponse { body: body }),
|
||||
Response::Account(AccountResponse {
|
||||
proof: vec![],
|
||||
|
||||
@@ -136,7 +136,10 @@
|
||||
"eip160Transition": 2675000,
|
||||
"eip161abcTransition": 2675000,
|
||||
"eip161dTransition": 2675000,
|
||||
"maxCodeSize": 24576
|
||||
"maxCodeSize": 24576,
|
||||
"eip649Reward": "0x29A2241AF62C0000",
|
||||
"eip100bTransition": 4370000,
|
||||
"eip649Transition": 4370000
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -148,7 +151,11 @@
|
||||
"forkBlock": "0x1d4c00",
|
||||
"forkCanonHash": "0x4985f5ca3d2afbec36529aa96f74de3cc10a2a4a6c44f2157a57d2c6059a11bb",
|
||||
"eip98Transition": "0x7fffffffffffff",
|
||||
"eip86Transition": "0x7fffffffffffff"
|
||||
"eip86Transition": "0x7fffffffffffff",
|
||||
"eip140Transition": 4370000,
|
||||
"eip211Transition": 4370000,
|
||||
"eip214Transition": 4370000,
|
||||
"eip658Transition": 4370000
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
@@ -190,10 +197,10 @@
|
||||
"0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
|
||||
"0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
||||
"0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
|
||||
"0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x7fffffffffffff", "pricing": { "modexp": { "divisor": 100 } } } },
|
||||
"0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": "0x7fffffffffffff", "pricing": { "linear": { "base": 500, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": "0x7fffffffffffff", "pricing": { "linear": { "base": 2000, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": "0x7fffffffffffff", "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } },
|
||||
"0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": 4370000, "pricing": { "modexp": { "divisor": 20 } } } },
|
||||
"0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": 4370000, "pricing": { "linear": { "base": 500, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": 4370000, "pricing": { "linear": { "base": 40000, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": 4370000, "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } },
|
||||
"3282791d6fd713f1e94f4bfd565eaa78b3a0599d": {
|
||||
"balance": "1337000000000000000000"
|
||||
},
|
||||
|
||||
@@ -30,7 +30,7 @@ use engines::Engine;
|
||||
use error::{Error, BlockError, TransactionError};
|
||||
use factory::Factories;
|
||||
use header::Header;
|
||||
use receipt::Receipt;
|
||||
use receipt::{Receipt, TransactionOutcome};
|
||||
use state::State;
|
||||
use state_db::StateDB;
|
||||
use trace::FlatTrace;
|
||||
@@ -524,7 +524,7 @@ impl LockedBlock {
|
||||
pub fn strip_receipts(self) -> LockedBlock {
|
||||
let mut block = self;
|
||||
for receipt in &mut block.block.receipts {
|
||||
receipt.state_root = None;
|
||||
receipt.outcome = TransactionOutcome::Unknown;
|
||||
}
|
||||
block.block.header.set_receipts_root(ordered_trie_root(block.block.receipts.iter().map(|r| r.rlp_bytes().into_vec())));
|
||||
block
|
||||
|
||||
@@ -1471,7 +1471,7 @@ mod tests {
|
||||
use util::kvdb::KeyValueDB;
|
||||
use util::hash::*;
|
||||
use util::sha3::Hashable;
|
||||
use receipt::Receipt;
|
||||
use receipt::{Receipt, TransactionOutcome};
|
||||
use blockchain::{BlockProvider, BlockChain, Config, ImportRoute};
|
||||
use tests::helpers::*;
|
||||
use blockchain::generator::{ChainGenerator, ChainIterator, BlockFinalizer};
|
||||
@@ -2038,8 +2038,7 @@ mod tests {
|
||||
let db = new_db();
|
||||
let bc = new_chain(&genesis, db.clone());
|
||||
insert_block(&db, &bc, &b1, vec![Receipt {
|
||||
state_root: Some(H256::default()),
|
||||
status_code: None,
|
||||
outcome: TransactionOutcome::StateRoot(H256::default()),
|
||||
gas_used: 10_000.into(),
|
||||
log_bloom: Default::default(),
|
||||
logs: vec![
|
||||
@@ -2048,8 +2047,7 @@ mod tests {
|
||||
],
|
||||
},
|
||||
Receipt {
|
||||
state_root: Some(H256::default()),
|
||||
status_code: None,
|
||||
outcome: TransactionOutcome::StateRoot(H256::default()),
|
||||
gas_used: 10_000.into(),
|
||||
log_bloom: Default::default(),
|
||||
logs: vec![
|
||||
@@ -2058,8 +2056,7 @@ mod tests {
|
||||
}]);
|
||||
insert_block(&db, &bc, &b2, vec![
|
||||
Receipt {
|
||||
state_root: Some(H256::default()),
|
||||
status_code: None,
|
||||
outcome: TransactionOutcome::StateRoot(H256::default()),
|
||||
gas_used: 10_000.into(),
|
||||
log_bloom: Default::default(),
|
||||
logs: vec![
|
||||
|
||||
@@ -98,18 +98,17 @@ impl Pricer for ModexpPricer {
|
||||
let exp_len = read_len();
|
||||
let mod_len = read_len();
|
||||
|
||||
let max_len = U256::from(u32::max_value() / 2);
|
||||
if base_len > max_len || mod_len > max_len {
|
||||
return U256::max_value();
|
||||
if mod_len.is_zero() && base_len.is_zero() {
|
||||
return U256::zero()
|
||||
}
|
||||
|
||||
let base_len = base_len.low_u64();
|
||||
let exp_len = exp_len.low_u64();
|
||||
let mod_len = mod_len.low_u64();
|
||||
let m = max(mod_len, base_len);
|
||||
if m == 0 {
|
||||
return U256::zero();
|
||||
let max_len = U256::from(u32::max_value() / 2);
|
||||
if base_len > max_len || mod_len > max_len || exp_len > max_len {
|
||||
return U256::max_value();
|
||||
}
|
||||
let (base_len, exp_len, mod_len) = (base_len.low_u64(), exp_len.low_u64(), mod_len.low_u64());
|
||||
|
||||
let m = max(mod_len, base_len);
|
||||
// read fist 32-byte word of the exponent.
|
||||
let exp_low = if base_len + 96 >= input.len() as u64 { U256::zero() } else {
|
||||
let mut buf = [0; 32];
|
||||
@@ -121,7 +120,11 @@ impl Pricer for ModexpPricer {
|
||||
|
||||
let adjusted_exp_len = Self::adjusted_exp_len(exp_len, exp_low);
|
||||
|
||||
(Self::mult_complexity(m) * max(adjusted_exp_len, 1) / self.divisor as u64).into()
|
||||
let (gas, overflow) = Self::mult_complexity(m).overflowing_mul(max(adjusted_exp_len, 1));
|
||||
if overflow {
|
||||
return U256::max_value();
|
||||
}
|
||||
(gas / self.divisor as u64).into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,8 +133,7 @@ impl ModexpPricer {
|
||||
let bit_index = if exp_low.is_zero() { 0 } else { (255 - exp_low.leading_zeros()) as u64 };
|
||||
if len <= 32 {
|
||||
bit_index
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
8 * (len - 32) + bit_index
|
||||
}
|
||||
}
|
||||
@@ -712,6 +714,32 @@ mod tests {
|
||||
native: ethereum_builtin("modexp"),
|
||||
activate_at: 0,
|
||||
};
|
||||
|
||||
// test for potential gas cost multiplication overflow
|
||||
{
|
||||
let input = FromHex::from_hex("0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000003b27bafd00000000000000000000000000000000000000000000000000000000503c8ac3").unwrap();
|
||||
let expected_cost = U256::max_value();
|
||||
assert_eq!(f.cost(&input[..]), expected_cost.into());
|
||||
}
|
||||
|
||||
|
||||
// test for potential exp len overflow
|
||||
{
|
||||
let input = FromHex::from_hex("\
|
||||
00000000000000000000000000000000000000000000000000000000000000ff\
|
||||
2a1e530000000000000000000000000000000000000000000000000000000000\
|
||||
0000000000000000000000000000000000000000000000000000000000000000"
|
||||
).unwrap();
|
||||
|
||||
let mut output = vec![0u8; 32];
|
||||
let expected = FromHex::from_hex("0000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||
let expected_cost = U256::max_value();
|
||||
|
||||
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should fail");
|
||||
assert_eq!(output, expected);
|
||||
assert_eq!(f.cost(&input[..]), expected_cost.into());
|
||||
}
|
||||
|
||||
// fermat's little theorem example.
|
||||
{
|
||||
let input = FromHex::from_hex("\
|
||||
|
||||
@@ -970,9 +970,11 @@ impl Client {
|
||||
|
||||
/// Tick the client.
|
||||
// TODO: manage by real events.
|
||||
pub fn tick(&self) {
|
||||
pub fn tick(&self, prevent_sleep: bool) {
|
||||
self.check_garbage();
|
||||
self.check_snooze();
|
||||
if !prevent_sleep {
|
||||
self.check_snooze();
|
||||
}
|
||||
}
|
||||
|
||||
fn check_garbage(&self) {
|
||||
@@ -1079,7 +1081,7 @@ impl Client {
|
||||
if !self.liveness.load(AtomicOrdering::Relaxed) {
|
||||
self.liveness.store(true, AtomicOrdering::Relaxed);
|
||||
self.notify(|n| n.start());
|
||||
trace!(target: "mode", "wake_up: Waking.");
|
||||
info!(target: "mode", "wake_up: Waking.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1089,11 +1091,11 @@ impl Client {
|
||||
if self.queue_info().total_queue_size() <= MAX_QUEUE_SIZE_TO_SLEEP_ON {
|
||||
self.liveness.store(false, AtomicOrdering::Relaxed);
|
||||
self.notify(|n| n.stop());
|
||||
trace!(target: "mode", "sleep: Sleeping.");
|
||||
info!(target: "mode", "sleep: Sleeping.");
|
||||
} else {
|
||||
trace!(target: "mode", "sleep: Cannot sleep - syncing ongoing.");
|
||||
info!(target: "mode", "sleep: Cannot sleep - syncing ongoing.");
|
||||
// TODO: Consider uncommenting.
|
||||
//*self.last_activity.lock() = Some(Instant::now());
|
||||
//(*self.sleep_state.lock()).last_activity = Some(Instant::now());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1978,7 +1980,7 @@ fn transaction_receipt(engine: &Engine, mut tx: LocalizedTransaction, mut receip
|
||||
log_index: no_of_logs + i,
|
||||
}).collect(),
|
||||
log_bloom: receipt.log_bloom,
|
||||
state_root: receipt.state_root,
|
||||
outcome: receipt.outcome,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2023,7 +2025,7 @@ mod tests {
|
||||
use super::transaction_receipt;
|
||||
use ethkey::KeyPair;
|
||||
use log_entry::{LogEntry, LocalizedLogEntry};
|
||||
use receipt::{Receipt, LocalizedReceipt};
|
||||
use receipt::{Receipt, LocalizedReceipt, TransactionOutcome};
|
||||
use transaction::{Transaction, LocalizedTransaction, Action};
|
||||
use util::Hashable;
|
||||
use tests::helpers::TestEngine;
|
||||
@@ -2035,7 +2037,7 @@ mod tests {
|
||||
|
||||
let block_number = 1;
|
||||
let block_hash = 5.into();
|
||||
let state_root = Some(99.into());
|
||||
let state_root = 99.into();
|
||||
let gas_used = 10.into();
|
||||
let raw_tx = Transaction {
|
||||
nonce: 0.into(),
|
||||
@@ -2063,14 +2065,12 @@ mod tests {
|
||||
data: vec![],
|
||||
}];
|
||||
let receipts = vec![Receipt {
|
||||
state_root: state_root,
|
||||
status_code: None,
|
||||
outcome: TransactionOutcome::StateRoot(state_root),
|
||||
gas_used: 5.into(),
|
||||
log_bloom: Default::default(),
|
||||
logs: vec![logs[0].clone()],
|
||||
}, Receipt {
|
||||
state_root: state_root,
|
||||
status_code: None,
|
||||
outcome: TransactionOutcome::StateRoot(state_root),
|
||||
gas_used: gas_used,
|
||||
log_bloom: Default::default(),
|
||||
logs: logs.clone(),
|
||||
@@ -2106,7 +2106,7 @@ mod tests {
|
||||
log_index: 2,
|
||||
}],
|
||||
log_bloom: Default::default(),
|
||||
state_root: state_root,
|
||||
outcome: TransactionOutcome::StateRoot(state_root),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ use db::{NUM_COLUMNS, COL_STATE};
|
||||
use header::{Header as BlockHeader, BlockNumber};
|
||||
use filter::Filter;
|
||||
use log_entry::LocalizedLogEntry;
|
||||
use receipt::{Receipt, LocalizedReceipt};
|
||||
use receipt::{Receipt, LocalizedReceipt, TransactionOutcome};
|
||||
use blockchain::extras::BlockReceipts;
|
||||
use error::{ImportResult, Error as EthcoreError};
|
||||
use evm::{Factory as EvmFactory, VMType, Schedule};
|
||||
@@ -594,8 +594,7 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
// starts with 'f' ?
|
||||
if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") {
|
||||
let receipt = BlockReceipts::new(vec![Receipt::new(
|
||||
Some(H256::zero()),
|
||||
None,
|
||||
TransactionOutcome::StateRoot(H256::zero()),
|
||||
U256::zero(),
|
||||
vec![])]);
|
||||
let mut rlp = RlpStream::new();
|
||||
|
||||
@@ -31,7 +31,7 @@ use std::path::Path;
|
||||
use super::spec::*;
|
||||
|
||||
/// Most recent fork block that we support on Mainnet.
|
||||
pub const FORK_SUPPORTED_FOUNDATION: u64 = 2675000;
|
||||
pub const FORK_SUPPORTED_FOUNDATION: u64 = 4370000;
|
||||
|
||||
/// Most recent fork block that we support on Ropsten.
|
||||
pub const FORK_SUPPORTED_ROPSTEN: u64 = 10;
|
||||
|
||||
@@ -412,13 +412,19 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
||||
|
||||
let cost = builtin.cost(data);
|
||||
if cost <= params.gas {
|
||||
if let Err(e) = builtin.execute(data, &mut output) {
|
||||
let mut builtin_out_buffer = Vec::new();
|
||||
let result = {
|
||||
let mut builtin_output = BytesRef::Flexible(&mut builtin_out_buffer);
|
||||
builtin.execute(data, &mut builtin_output)
|
||||
};
|
||||
if let Err(e) = result {
|
||||
self.state.revert_to_checkpoint();
|
||||
let evm_err: evm::evm::Error = e.into();
|
||||
tracer.trace_failed_call(trace_info, vec![], evm_err.clone().into());
|
||||
Err(evm_err)
|
||||
} else {
|
||||
self.state.discard_checkpoint();
|
||||
output.write(0, &builtin_out_buffer);
|
||||
|
||||
// trace only top level calls to builtins to avoid DDoS attacks
|
||||
if self.depth == 0 {
|
||||
@@ -435,9 +441,10 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
||||
);
|
||||
}
|
||||
|
||||
let out_len = builtin_out_buffer.len();
|
||||
Ok(FinalizationResult {
|
||||
gas_left: params.gas - cost,
|
||||
return_data: ReturnData::new(output.to_owned(), 0, output.len()),
|
||||
return_data: ReturnData::new(builtin_out_buffer, 0, out_len),
|
||||
apply_state: true,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -234,7 +234,6 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E>
|
||||
Ok(FinalizationResult{ gas_left, apply_state: false, return_data }) => {
|
||||
ContractCreateResult::Reverted(gas_left, return_data)
|
||||
},
|
||||
Err(evm::Error::MutableCallInStaticContext) => ContractCreateResult::FailedInStaticCall,
|
||||
_ => ContractCreateResult::Failed,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1078,7 +1078,7 @@ impl MinerService for Miner {
|
||||
},
|
||||
logs: receipt.logs.clone(),
|
||||
log_bloom: receipt.log_bloom,
|
||||
state_root: receipt.state_root,
|
||||
outcome: receipt.outcome.clone(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ use error::*;
|
||||
use client::{Client, ClientConfig, ChainNotify};
|
||||
use miner::Miner;
|
||||
|
||||
use snapshot::ManifestData;
|
||||
use snapshot::{ManifestData, RestorationStatus};
|
||||
use snapshot::service::{Service as SnapshotService, ServiceParams as SnapServiceParams};
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
@@ -175,7 +175,11 @@ impl IoHandler<ClientIoMessage> for ClientIoHandler {
|
||||
|
||||
fn timeout(&self, _io: &IoContext<ClientIoMessage>, timer: TimerToken) {
|
||||
match timer {
|
||||
CLIENT_TICK_TIMER => self.client.tick(),
|
||||
CLIENT_TICK_TIMER => {
|
||||
use snapshot::SnapshotService;
|
||||
let snapshot_restoration = if let RestorationStatus::Ongoing{..} = self.snapshot.status() { true } else { false };
|
||||
self.client.tick(snapshot_restoration)
|
||||
},
|
||||
SNAPSHOT_TICK_TIMER => self.snapshot.tick(),
|
||||
_ => warn!("IO service triggered unregistered timer '{}'", timer),
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
use std::cell::{RefCell, RefMut};
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
use receipt::Receipt;
|
||||
use receipt::{Receipt, TransactionOutcome};
|
||||
use engines::Engine;
|
||||
use evm::env_info::EnvInfo;
|
||||
use error::Error;
|
||||
@@ -655,21 +655,19 @@ impl<B: Backend> State<B> {
|
||||
eip658 ||
|
||||
(env_info.number >= engine.params().eip98_transition && env_info.number >= engine.params().validate_receipts_transition);
|
||||
|
||||
let state_root = if no_intermediate_commits {
|
||||
None
|
||||
let outcome = if no_intermediate_commits {
|
||||
if eip658 {
|
||||
TransactionOutcome::StatusCode(if e.exception.is_some() { 0 } else { 1 })
|
||||
} else {
|
||||
TransactionOutcome::Unknown
|
||||
}
|
||||
} else {
|
||||
self.commit()?;
|
||||
Some(self.root().clone())
|
||||
};
|
||||
|
||||
let status_byte = if eip658 {
|
||||
Some(if e.exception.is_some() { 0 } else { 1 })
|
||||
} else {
|
||||
None
|
||||
TransactionOutcome::StateRoot(self.root().clone())
|
||||
};
|
||||
|
||||
let output = e.output;
|
||||
let receipt = Receipt::new(state_root, status_byte, e.cumulative_gas_used, e.logs);
|
||||
let receipt = Receipt::new(outcome, e.cumulative_gas_used, e.logs);
|
||||
trace!(target: "state", "Transaction receipt: {:?}", receipt);
|
||||
|
||||
Ok(ApplyOutcome {
|
||||
|
||||
@@ -190,7 +190,7 @@ fn imports_block_sequence() {
|
||||
#[test]
|
||||
fn can_collect_garbage() {
|
||||
let client = generate_dummy_client(100);
|
||||
client.tick();
|
||||
client.tick(true);
|
||||
assert!(client.blockchain_cache_info().blocks < 100 * 1024);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,44 +23,56 @@ use rlp::*;
|
||||
use {BlockNumber};
|
||||
use log_entry::{LogBloom, LogEntry, LocalizedLogEntry};
|
||||
|
||||
/// Transaction outcome store in the receipt.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum TransactionOutcome {
|
||||
/// Status and state root are unknown under EIP-98 rules.
|
||||
Unknown,
|
||||
/// State root is known. Pre EIP-98 and EIP-658 rules.
|
||||
StateRoot(H256),
|
||||
/// Status code is known. EIP-658 rules.
|
||||
StatusCode(u8),
|
||||
}
|
||||
|
||||
/// Information describing execution of a transaction.
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Receipt {
|
||||
/// The state root after executing the transaction. Optional since EIP98
|
||||
pub state_root: Option<H256>,
|
||||
/// The total gas used in the block following execution of the transaction.
|
||||
pub gas_used: U256,
|
||||
/// The OR-wide combination of all logs' blooms for this transaction.
|
||||
pub log_bloom: LogBloom,
|
||||
/// The logs stemming from this transaction.
|
||||
pub logs: Vec<LogEntry>,
|
||||
/// Status byte. Optional before EIP-658.
|
||||
pub status_code: Option<u8>,
|
||||
/// Transaction outcome.
|
||||
pub outcome: TransactionOutcome,
|
||||
}
|
||||
|
||||
impl Receipt {
|
||||
/// Create a new receipt.
|
||||
pub fn new(state_root: Option<H256>, status_code: Option<u8>, gas_used: U256, logs: Vec<LogEntry>) -> Receipt {
|
||||
pub fn new(outcome: TransactionOutcome, gas_used: U256, logs: Vec<LogEntry>) -> Receipt {
|
||||
Receipt {
|
||||
state_root: state_root,
|
||||
gas_used: gas_used,
|
||||
log_bloom: logs.iter().fold(LogBloom::default(), |mut b, l| { b = &b | &l.bloom(); b }), //TODO: use |= operator
|
||||
logs: logs,
|
||||
status_code: status_code,
|
||||
outcome: outcome,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for Receipt {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
if let Some(ref status) = self.status_code {
|
||||
s.begin_list(4);
|
||||
s.append(status);
|
||||
} else if let Some(ref root) = self.state_root {
|
||||
s.begin_list(4);
|
||||
s.append(root);
|
||||
} else {
|
||||
s.begin_list(3);
|
||||
match self.outcome {
|
||||
TransactionOutcome::Unknown => {
|
||||
s.begin_list(3);
|
||||
},
|
||||
TransactionOutcome::StateRoot(ref root) => {
|
||||
s.begin_list(4);
|
||||
s.append(root);
|
||||
},
|
||||
TransactionOutcome::StatusCode(ref status_code) => {
|
||||
s.begin_list(4);
|
||||
s.append(status_code);
|
||||
},
|
||||
}
|
||||
s.append(&self.gas_used);
|
||||
s.append(&self.log_bloom);
|
||||
@@ -72,28 +84,25 @@ impl Decodable for Receipt {
|
||||
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
|
||||
if rlp.item_count()? == 3 {
|
||||
Ok(Receipt {
|
||||
state_root: None,
|
||||
status_code: None,
|
||||
outcome: TransactionOutcome::Unknown,
|
||||
gas_used: rlp.val_at(0)?,
|
||||
log_bloom: rlp.val_at(1)?,
|
||||
logs: rlp.list_at(2)?,
|
||||
})
|
||||
} else {
|
||||
let mut receipt = Receipt {
|
||||
Ok(Receipt {
|
||||
gas_used: rlp.val_at(1)?,
|
||||
log_bloom: rlp.val_at(2)?,
|
||||
logs: rlp.list_at(3)?,
|
||||
state_root: None,
|
||||
status_code: None,
|
||||
};
|
||||
|
||||
let first = rlp.at(0)?;
|
||||
if first.is_data() && first.data()?.len() == 1 {
|
||||
receipt.status_code = Some(first.as_val()?);
|
||||
} else {
|
||||
receipt.state_root = Some(first.as_val()?);
|
||||
}
|
||||
Ok(receipt)
|
||||
outcome: {
|
||||
let first = rlp.at(0)?;
|
||||
if first.is_data() && first.data()?.len() <= 1 {
|
||||
TransactionOutcome::StatusCode(first.as_val()?)
|
||||
} else {
|
||||
TransactionOutcome::StateRoot(first.as_val()?)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,8 +130,8 @@ pub struct RichReceipt {
|
||||
pub logs: Vec<LogEntry>,
|
||||
/// Logs bloom
|
||||
pub log_bloom: LogBloom,
|
||||
/// State root
|
||||
pub state_root: Option<H256>,
|
||||
/// Transaction outcome.
|
||||
pub outcome: TransactionOutcome,
|
||||
}
|
||||
|
||||
/// Receipt with additional info.
|
||||
@@ -146,21 +155,20 @@ pub struct LocalizedReceipt {
|
||||
pub logs: Vec<LocalizedLogEntry>,
|
||||
/// Logs bloom
|
||||
pub log_bloom: LogBloom,
|
||||
/// State root
|
||||
pub state_root: Option<H256>,
|
||||
/// Transaction outcome.
|
||||
pub outcome: TransactionOutcome,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Receipt;
|
||||
use super::{Receipt, TransactionOutcome};
|
||||
use log_entry::LogEntry;
|
||||
|
||||
#[test]
|
||||
fn test_no_state_root() {
|
||||
let expected = ::rustc_hex::FromHex::from_hex("f9014183040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||
let r = Receipt::new(
|
||||
None,
|
||||
None,
|
||||
TransactionOutcome::Unknown,
|
||||
0x40cae.into(),
|
||||
vec![LogEntry {
|
||||
address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(),
|
||||
@@ -175,8 +183,25 @@ mod tests {
|
||||
fn test_basic() {
|
||||
let expected = ::rustc_hex::FromHex::from_hex("f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||
let r = Receipt::new(
|
||||
Some("2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into()),
|
||||
None,
|
||||
TransactionOutcome::StateRoot("2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into()),
|
||||
0x40cae.into(),
|
||||
vec![LogEntry {
|
||||
address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(),
|
||||
topics: vec![],
|
||||
data: vec![0u8; 32]
|
||||
}]
|
||||
);
|
||||
let encoded = ::rlp::encode(&r);
|
||||
assert_eq!(&encoded[..], &expected[..]);
|
||||
let decoded: Receipt = ::rlp::decode(&encoded);
|
||||
assert_eq!(decoded, r);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_status_code() {
|
||||
let expected = ::rustc_hex::FromHex::from_hex("f901428083040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||
let r = Receipt::new(
|
||||
TransactionOutcome::StatusCode(0),
|
||||
0x40cae.into(),
|
||||
vec![LogEntry {
|
||||
address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(),
|
||||
|
||||
48
js/package-lock.json
generated
48
js/package-lock.json
generated
@@ -9,6 +9,16 @@
|
||||
"resolved": "https://registry.npmjs.org/@parity/wordlist/-/wordlist-1.0.1.tgz",
|
||||
"integrity": "sha1-wn5A4as2OKCe1TtKLoHVMbXrWjE="
|
||||
},
|
||||
"JSONStream": {
|
||||
"version": "0.8.4",
|
||||
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.8.4.tgz",
|
||||
"integrity": "sha1-kWV9/m/4V0gwZhMrRhi2Lo9Ih70=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"jsonparse": "0.0.5",
|
||||
"through": "2.3.8"
|
||||
}
|
||||
},
|
||||
"abab": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-1.0.3.tgz",
|
||||
@@ -418,7 +428,7 @@
|
||||
"glob": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
||||
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
|
||||
"integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "1.0.0",
|
||||
@@ -800,7 +810,7 @@
|
||||
"glob": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
||||
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
|
||||
"integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "1.0.0",
|
||||
@@ -4404,7 +4414,7 @@
|
||||
"glob": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
||||
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
|
||||
"integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "1.0.0",
|
||||
@@ -7154,16 +7164,6 @@
|
||||
"integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=",
|
||||
"dev": true
|
||||
},
|
||||
"JSONStream": {
|
||||
"version": "0.8.4",
|
||||
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.8.4.tgz",
|
||||
"integrity": "sha1-kWV9/m/4V0gwZhMrRhi2Lo9Ih70=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"jsonparse": "0.0.5",
|
||||
"through": "2.3.8"
|
||||
}
|
||||
},
|
||||
"jsprim": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
|
||||
@@ -11649,14 +11649,6 @@
|
||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
|
||||
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
||||
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||
@@ -11672,6 +11664,14 @@
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-0.2.0.tgz",
|
||||
"integrity": "sha1-q6Nt4I3O5qWjN9SbLqHaGyj8Ds8="
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
||||
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
},
|
||||
"stringify-object": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.2.0.tgz",
|
||||
@@ -11958,7 +11958,7 @@
|
||||
"string-width": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
|
||||
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
|
||||
"integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-fullwidth-code-point": "2.0.0",
|
||||
@@ -12923,7 +12923,7 @@
|
||||
"async": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz",
|
||||
"integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==",
|
||||
"integrity": "sha1-hDGQ/WtzV6C54clW7d3V7IRitU0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash": "4.17.2"
|
||||
@@ -13328,7 +13328,7 @@
|
||||
"commander": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz",
|
||||
"integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ=="
|
||||
"integrity": "sha1-FXFS/R56bI2YpbcVzzdt+SgARWM="
|
||||
},
|
||||
"detect-indent": {
|
||||
"version": "5.0.0",
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
// test script 10
|
||||
// test script 11
|
||||
// beta trigger 01
|
||||
|
||||
@@ -75,7 +75,13 @@ export function bytesToAscii (bytes) {
|
||||
}
|
||||
|
||||
export function asciiToHex (string) {
|
||||
return '0x' + string.split('').map((s) => s.charCodeAt(0).toString(16)).join('');
|
||||
let result = '0x';
|
||||
|
||||
for (let i = 0; i < string.length; ++i) {
|
||||
result += ('0' + string.charCodeAt(i).toString(16)).substr(-2);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function padRight (input, length) {
|
||||
|
||||
@@ -68,6 +68,14 @@ describe('api/util/format', () => {
|
||||
it('correctly converts a non-empty string', () => {
|
||||
expect(asciiToHex('abc')).to.equal('0x616263');
|
||||
});
|
||||
|
||||
it('correctly converts where charCode < 0x10', () => {
|
||||
expect(
|
||||
asciiToHex(
|
||||
[32, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0].map((v) => String.fromCharCode(v)).join('')
|
||||
)
|
||||
).to.equal('0x20100f0e0d0c0b0a09080706050403020100');
|
||||
});
|
||||
});
|
||||
|
||||
describe('hexToAscii', () => {
|
||||
|
||||
@@ -29,3 +29,4 @@ export signaturereg from './signaturereg.json';
|
||||
export smsverification from './sms-verification.json';
|
||||
export tokenreg from './tokenreg.json';
|
||||
export foundationWallet from './foundation-multisig-wallet.json';
|
||||
export vouchfor from './vouchfor.json';
|
||||
|
||||
1
js/src/contracts/abi/vouchfor.json
Normal file
1
js/src/contracts/abi/vouchfor.json
Normal file
@@ -0,0 +1 @@
|
||||
[{"constant":true,"inputs":[],"name":"certifier","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_what","type":"bytes32"}],"name":"vouch","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_what","type":"bytes32"},{"name":"_index","type":"uint256"}],"name":"vouched","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_what","type":"bytes32"},{"name":"_index","type":"uint256"}],"name":"unvouch","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"},{"name":"","type":"uint256"}],"name":"vouchers","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_certifier","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"who","type":"address"},{"indexed":false,"name":"what","type":"bytes32"}],"name":"Vouched","type":"event"}]
|
||||
@@ -105,7 +105,7 @@ export default class BadgeReg {
|
||||
]);
|
||||
})
|
||||
.then(([ title, icon ]) => {
|
||||
title = bytesToHex(title);
|
||||
title = bytesToHex(title).replace(/(00)+$/, '');
|
||||
title = title === ZERO32 ? null : hexToAscii(title);
|
||||
|
||||
if (bytesToHex(icon) === ZERO32) {
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
left: 0;
|
||||
opacity: 1;
|
||||
padding: 1.5em;
|
||||
cursor: pointer;
|
||||
position: fixed;
|
||||
right: 50%;
|
||||
z-index: 100;
|
||||
|
||||
@@ -43,8 +43,13 @@ export default class Application extends Component {
|
||||
contract: PropTypes.object
|
||||
};
|
||||
|
||||
state = {
|
||||
hideWarning: false
|
||||
};
|
||||
|
||||
render () {
|
||||
const { isLoading, contract } = this.props;
|
||||
const { hideWarning } = this.state;
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
@@ -62,9 +67,15 @@ export default class Application extends Component {
|
||||
<Actions />
|
||||
|
||||
<Tokens />
|
||||
<div className={ styles.warning }>
|
||||
WARNING: The token registry is experimental. Please ensure that you understand the steps, risks, benefits & consequences of registering a token before doing so. A non-refundable fee of { api.util.fromWei(contract.fee).toFormat(3) }<small>ETH</small> is required for all registrations.
|
||||
</div>
|
||||
{
|
||||
hideWarning
|
||||
? null
|
||||
: (
|
||||
<div className={ styles.warning } onClick={ this.handleHideWarning }>
|
||||
WARNING: The token registry is experimental. Please ensure that you understand the steps, risks, benefits & consequences of registering a token before doing so. A non-refundable fee of { api.util.fromWei(contract.fee).toFormat(3) }<small>ETH</small> is required for all registrations.
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -74,4 +85,8 @@ export default class Application extends Component {
|
||||
muiTheme
|
||||
};
|
||||
}
|
||||
|
||||
handleHideWarning = () => {
|
||||
this.setState({ hideWarning: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ export default class Store {
|
||||
@observable gethAddresses = [];
|
||||
@observable gethImported = [];
|
||||
@observable isBusy = false;
|
||||
@observable isTest = false;
|
||||
@observable isWindowsPhrase = false;
|
||||
@observable name = '';
|
||||
@observable nameError = ERRORS.noName;
|
||||
@@ -310,6 +311,10 @@ export default class Store {
|
||||
this.stage--;
|
||||
}
|
||||
|
||||
@action setIsTest = isTest => {
|
||||
this.isTest = isTest;
|
||||
}
|
||||
|
||||
createAccount = (vaultStore) => {
|
||||
if (!this.canCreate) {
|
||||
return false;
|
||||
|
||||
@@ -16,18 +16,21 @@
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { Form, TypedInput, Input, AddressSelect, InputAddress } from '~/ui';
|
||||
|
||||
import styles from '../createWallet.css';
|
||||
|
||||
export default class WalletDetails extends Component {
|
||||
class WalletDetails extends Component {
|
||||
static propTypes = {
|
||||
accounts: PropTypes.object.isRequired,
|
||||
wallet: PropTypes.object.isRequired,
|
||||
errors: PropTypes.object.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
walletType: PropTypes.string.isRequired
|
||||
walletType: PropTypes.string.isRequired,
|
||||
|
||||
knownAddresses: PropTypes.array
|
||||
};
|
||||
|
||||
render () {
|
||||
@@ -103,7 +106,10 @@ export default class WalletDetails extends Component {
|
||||
}
|
||||
|
||||
renderMultisigDetails () {
|
||||
const { accounts, wallet, errors } = this.props;
|
||||
const { accounts, knownAddresses, wallet, errors } = this.props;
|
||||
const allowedOwners = knownAddresses
|
||||
// Exclude sender and already owners of the wallet
|
||||
.filter((address) => !wallet.owners.includes(address) && address !== wallet.account);
|
||||
|
||||
return (
|
||||
<Form>
|
||||
@@ -163,7 +169,7 @@ export default class WalletDetails extends Component {
|
||||
/>
|
||||
|
||||
<TypedInput
|
||||
accounts={ accounts }
|
||||
allowedValues={ allowedOwners }
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='createWallet.details.ownersMulti.label'
|
||||
@@ -249,3 +255,21 @@ export default class WalletDetails extends Component {
|
||||
this.props.onChange({ daylimit });
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (initState) {
|
||||
const { accounts, contacts, contracts } = initState.personal;
|
||||
const knownAddresses = [].concat(
|
||||
Object.keys(accounts),
|
||||
Object.keys(contacts),
|
||||
Object.keys(contracts)
|
||||
);
|
||||
|
||||
return () => ({
|
||||
knownAddresses
|
||||
});
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(WalletDetails);
|
||||
|
||||
@@ -25,6 +25,22 @@ import { ACCOUNTS } from '../createWallet.test.js';
|
||||
let component;
|
||||
let onChange;
|
||||
|
||||
function createRedux () {
|
||||
return {
|
||||
dispatch: sinon.stub(),
|
||||
subscribe: sinon.stub(),
|
||||
getState: () => {
|
||||
return {
|
||||
personal: {
|
||||
accounts: {},
|
||||
contacts: {},
|
||||
contracts: {}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function render (walletType = 'MULTISIG') {
|
||||
onChange = sinon.stub();
|
||||
component = shallow(
|
||||
@@ -36,7 +52,12 @@ function render (walletType = 'MULTISIG') {
|
||||
owners: []
|
||||
} }
|
||||
walletType={ walletType }
|
||||
/>
|
||||
/>,
|
||||
{
|
||||
context: {
|
||||
store: createRedux()
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return component;
|
||||
|
||||
@@ -281,6 +281,13 @@ export default class CreateWalletStore {
|
||||
const daylimitValidation = validateUint(_wallet.daylimit);
|
||||
const nameValidation = validateName(_wallet.name);
|
||||
|
||||
const owners = _wallet.owners.filter((owner) => !/^(0x)?0*$/.test(owner));
|
||||
|
||||
// Real number of owners is owners + creator
|
||||
if (_wallet.required > owners.length + 1) {
|
||||
requiredValidation.valueError = 'the number of required validators should be lower or equal the number of owners';
|
||||
}
|
||||
|
||||
const errors = {
|
||||
address: addressValidation.addressError,
|
||||
account: accountValidation.addressError,
|
||||
|
||||
@@ -43,7 +43,6 @@ export default class ParametersStep extends Component {
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
accounts: PropTypes.object.isRequired,
|
||||
onParamsChange: PropTypes.func.isRequired,
|
||||
|
||||
inputs: PropTypes.array,
|
||||
@@ -60,7 +59,7 @@ export default class ParametersStep extends Component {
|
||||
}
|
||||
|
||||
renderConstructorInputs () {
|
||||
const { accounts, params, paramsError } = this.props;
|
||||
const { params, paramsError } = this.props;
|
||||
const { inputs } = this.props;
|
||||
|
||||
if (!inputs || !inputs.length) {
|
||||
@@ -78,7 +77,6 @@ export default class ParametersStep extends Component {
|
||||
return (
|
||||
<div key={ index } className={ styles.funcparams }>
|
||||
<TypedInput
|
||||
accounts={ accounts }
|
||||
error={ error }
|
||||
isEth={ false }
|
||||
label={ label }
|
||||
|
||||
@@ -313,7 +313,6 @@ class DeployContract extends Component {
|
||||
return (
|
||||
<ParametersStep
|
||||
{ ...this.state }
|
||||
accounts={ accounts }
|
||||
onParamsChange={ this.onParamsChange }
|
||||
readOnly={ readOnly }
|
||||
/>
|
||||
|
||||
@@ -177,7 +177,7 @@ export default class DetailsStep extends Component {
|
||||
}
|
||||
|
||||
renderParameters () {
|
||||
const { accounts, func, values, valuesError, onValueChange } = this.props;
|
||||
const { func, values, valuesError, onValueChange } = this.props;
|
||||
|
||||
if (!func) {
|
||||
return null;
|
||||
@@ -197,7 +197,6 @@ export default class DetailsStep extends Component {
|
||||
value={ values[index] }
|
||||
error={ valuesError[index] }
|
||||
onChange={ onChange }
|
||||
accounts={ accounts }
|
||||
param={ input.type }
|
||||
isEth={ false }
|
||||
/>
|
||||
|
||||
@@ -78,16 +78,23 @@ class FirstRun extends Component {
|
||||
hasAccounts: PropTypes.bool.isRequired,
|
||||
newError: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
visible: PropTypes.bool.isRequired
|
||||
visible: PropTypes.bool.isRequired,
|
||||
isTest: PropTypes.bool.isRequired
|
||||
}
|
||||
|
||||
createStore = new CreateStore(this.context.api, {}, true, false);
|
||||
createStore = new CreateStore(this.context.api, {}, this.props.isTest, false);
|
||||
|
||||
state = {
|
||||
stage: 0,
|
||||
hasAcceptedTnc: false
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.isTest !== this.props.isTest) {
|
||||
this.createStore.setIsTest(nextProps.isTest);
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { visible } = this.props;
|
||||
const { stage } = this.state;
|
||||
@@ -348,9 +355,10 @@ class FirstRun extends Component {
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const { hasAccounts } = state.personal;
|
||||
const { isTest } = state.nodeStatus;
|
||||
|
||||
return {
|
||||
hasAccounts
|
||||
hasAccounts, isTest
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,9 @@ function createRedux () {
|
||||
return {
|
||||
personal: {
|
||||
hasAccounts: false
|
||||
},
|
||||
nodeStatus: {
|
||||
isTest: false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ class WalletSettings extends Component {
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
accountsInfo: PropTypes.object.isRequired,
|
||||
wallet: PropTypes.object.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
senders: PropTypes.object.isRequired
|
||||
@@ -74,7 +73,7 @@ class WalletSettings extends Component {
|
||||
default:
|
||||
case 'EDIT':
|
||||
const { errors, fromString, wallet } = this.store;
|
||||
const { accountsInfo, senders } = this.props;
|
||||
const { senders } = this.props;
|
||||
|
||||
return (
|
||||
<Form>
|
||||
@@ -143,7 +142,6 @@ class WalletSettings extends Component {
|
||||
}
|
||||
value={ wallet.owners.slice() }
|
||||
onChange={ this.store.onOwnersChange }
|
||||
accounts={ accountsInfo }
|
||||
param='address[]'
|
||||
/>
|
||||
|
||||
@@ -443,13 +441,13 @@ class WalletSettings extends Component {
|
||||
}
|
||||
|
||||
function mapStateToProps (initState, initProps) {
|
||||
const { accountsInfo, accounts } = initState.personal;
|
||||
const { accounts } = initState.personal;
|
||||
const { owners } = initProps.wallet;
|
||||
|
||||
const senders = pick(accounts, owners);
|
||||
|
||||
return () => {
|
||||
return { accountsInfo, senders };
|
||||
return { senders };
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -14,14 +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/>.
|
||||
|
||||
export const fetchCertifiers = () => ({
|
||||
type: 'fetchCertifiers'
|
||||
});
|
||||
|
||||
export const fetchCertifications = (address) => ({
|
||||
type: 'fetchCertifications', address
|
||||
});
|
||||
|
||||
export const addCertification = (address, id, name, title, icon) => ({
|
||||
type: 'addCertification', address, id, name, title, icon
|
||||
});
|
||||
|
||||
343
js/src/redux/providers/certifications/certifiers.monitor.js
Normal file
343
js/src/redux/providers/certifications/certifiers.monitor.js
Normal file
@@ -0,0 +1,343 @@
|
||||
// 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 { range } from 'lodash';
|
||||
|
||||
import { addCertification, removeCertification } from './actions';
|
||||
|
||||
import { getLogger, LOG_KEYS } from '~/config';
|
||||
import Contract from '~/api/contract';
|
||||
import { bytesToHex, hexToAscii } from '~/api/util/format';
|
||||
import Contracts from '~/contracts';
|
||||
import CertifierABI from '~/contracts/abi/certifier.json';
|
||||
import { querier } from './enhanced-querier';
|
||||
|
||||
const log = getLogger(LOG_KEYS.CertificationsMiddleware);
|
||||
|
||||
let self = null;
|
||||
|
||||
export default class CertifiersMonitor {
|
||||
constructor (api, store) {
|
||||
this._api = api;
|
||||
this._name = 'Certifiers';
|
||||
this._store = store;
|
||||
|
||||
this._contract = new Contract(this.api, CertifierABI);
|
||||
this._contractEvents = [ 'Confirmed', 'Revoked' ]
|
||||
.map((name) => this.contract.events.find((e) => e.name === name));
|
||||
|
||||
this.certifiers = {};
|
||||
this.fetchedAccounts = {};
|
||||
|
||||
this.load();
|
||||
}
|
||||
|
||||
static get () {
|
||||
if (self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
self = new CertifiersMonitor();
|
||||
return self;
|
||||
}
|
||||
|
||||
static init (api, store) {
|
||||
if (!self) {
|
||||
self = new CertifiersMonitor(api, store);
|
||||
}
|
||||
}
|
||||
|
||||
get api () {
|
||||
return this._api;
|
||||
}
|
||||
|
||||
get contract () {
|
||||
return this._contract;
|
||||
}
|
||||
|
||||
get contractEvents () {
|
||||
return this._contractEvents;
|
||||
}
|
||||
|
||||
get name () {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get store () {
|
||||
return this._store;
|
||||
}
|
||||
|
||||
get registry () {
|
||||
return this._registry;
|
||||
}
|
||||
|
||||
get registryEvents () {
|
||||
return this._registryEvents;
|
||||
}
|
||||
|
||||
checkFilters () {
|
||||
this.checkCertifiersFilter();
|
||||
this.checkRegistryFilter();
|
||||
}
|
||||
|
||||
checkCertifiersFilter () {
|
||||
if (!this.certifiersFilter) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.api.eth.getFilterChanges(this.certifiersFilter)
|
||||
.then((logs) => {
|
||||
if (logs.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedLogs = this.contract.parseEventLogs(logs).filter((log) => log.params);
|
||||
|
||||
log.debug('received certifiers logs', parsedLogs);
|
||||
|
||||
const promises = parsedLogs.map((log) => {
|
||||
const account = log.params.who.value;
|
||||
const certifier = Object.values(this.certifiers).find((c) => c.address === log.address);
|
||||
|
||||
if (!certifier) {
|
||||
log.warn('could not find the certifier', { certifiers: this.certifiers, log });
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.fetchAccount(account, { ids: [ certifier.id ] });
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
checkRegistryFilter () {
|
||||
if (!this.registryFilter) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.api.eth.getFilterChanges(this.registryFilter)
|
||||
.then((logs) => {
|
||||
if (logs.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedLogs = this.contract.parseEventLogs(logs).filter((log) => log.params);
|
||||
const indexes = parsedLogs.map((log) => log.params && log.params.id.value.toNumber());
|
||||
|
||||
log.debug('received registry logs', parsedLogs);
|
||||
return this.fetchElements(indexes);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initial load of the Monitor.
|
||||
* Fetch the contract from the Registry, and
|
||||
* load the elements addresses
|
||||
*/
|
||||
load () {
|
||||
const badgeReg = Contracts.get().badgeReg;
|
||||
|
||||
log.debug(`loading the ${this.name} monitor...`);
|
||||
return badgeReg.getContract()
|
||||
.then((registryContract) => {
|
||||
this._registry = registryContract;
|
||||
this._registryEvents = [ 'Registered', 'Unregistered', 'MetaChanged', 'AddressChanged' ]
|
||||
.map((name) => this.registry.events.find((e) => e.name === name));
|
||||
|
||||
return this.registry.instance.badgeCount.call({});
|
||||
})
|
||||
.then((count) => {
|
||||
log.debug(`found ${count.toFormat()} registered contracts for ${this.name}`);
|
||||
return this.fetchElements(range(count.toNumber()));
|
||||
})
|
||||
.then(() => {
|
||||
return this.setRegistryFilter();
|
||||
})
|
||||
.then(() => {
|
||||
// Listen for new blocks
|
||||
return this.api.subscribe('eth_blockNumber', (err) => {
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.checkFilters();
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
log.debug(`loaded the ${this.name} monitor!`, this.certifiers);
|
||||
})
|
||||
.catch((error) => {
|
||||
log.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the given registered element
|
||||
*/
|
||||
fetchElements (indexes) {
|
||||
const badgeReg = Contracts.get().badgeReg;
|
||||
const { instance } = this.registry;
|
||||
|
||||
const sorted = indexes.sort();
|
||||
const from = sorted[0];
|
||||
const last = sorted[sorted.length - 1];
|
||||
const limit = last - from + 1;
|
||||
|
||||
// Fetch the address, name and owner in one batch
|
||||
return querier(this.api, { address: instance.address, from, limit }, instance.badge)
|
||||
.then((results) => {
|
||||
const certifiers = results
|
||||
.map(([ address, name, owner ], index) => ({
|
||||
address, owner,
|
||||
id: index + from,
|
||||
name: hexToAscii(bytesToHex(name).replace(/(00)+$/, ''))
|
||||
}))
|
||||
.reduce((certifiers, certifier) => {
|
||||
const { id } = certifier;
|
||||
|
||||
if (!/^(0x)?0+$/.test(certifier.address)) {
|
||||
certifiers[id] = certifier;
|
||||
} else if (certifiers[id]) {
|
||||
delete certifiers[id];
|
||||
}
|
||||
|
||||
return certifiers;
|
||||
}, {});
|
||||
|
||||
// Fetch the meta-data in serie
|
||||
return Object.values(certifiers).reduce((promise, certifier) => {
|
||||
return promise.then(() => badgeReg.fetchMeta(certifier.id))
|
||||
.then((meta) => {
|
||||
this.certifiers[certifier.id] = { ...certifier, ...meta };
|
||||
});
|
||||
}, Promise.resolve());
|
||||
})
|
||||
.then(() => log.debug('fetched certifiers', { certifiers: this.certifiers }))
|
||||
// Fetch the know accounts in case it's an update of the certifiers
|
||||
.then(() => this.fetchAccounts(Object.keys(this.fetchedAccounts), { ids: indexes, force: true }));
|
||||
}
|
||||
|
||||
fetchAccounts (addresses, { ids = null, force = false } = {}) {
|
||||
const newAddresses = force
|
||||
? addresses
|
||||
: addresses.filter((address) => !this.fetchedAccounts[address]);
|
||||
|
||||
if (newAddresses.length === 0) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
log.debug(`fetching values for "${addresses.join(' ; ')}" in ${this.name}...`);
|
||||
return newAddresses
|
||||
.reduce((promise, address) => {
|
||||
return promise.then(() => this.fetchAccount(address, { ids }));
|
||||
}, Promise.resolve())
|
||||
.then(() => {
|
||||
log.debug(`fetched values for "${addresses.join(' ; ')}" in ${this.name}!`);
|
||||
})
|
||||
.then(() => this.setCertifiersFilter());
|
||||
}
|
||||
|
||||
fetchAccount (address, { ids = null } = {}) {
|
||||
let certifiers = Object.values(this.certifiers);
|
||||
|
||||
// Only fetch values for the givens ids, if any
|
||||
if (ids) {
|
||||
certifiers = certifiers.filter((certifier) => ids.includes(certifier.id));
|
||||
}
|
||||
|
||||
certifiers
|
||||
.reduce((promise, certifier) => {
|
||||
return promise
|
||||
.then(() => {
|
||||
return this.contract.at(certifier.address).instance.certified.call({}, [ address ]);
|
||||
})
|
||||
.then((certified) => {
|
||||
const { id, title, icon, name } = certifier;
|
||||
|
||||
if (!certified) {
|
||||
return this.store.dispatch(removeCertification(address, id));
|
||||
}
|
||||
|
||||
log.debug('seen as certified', { address, id, name, icon });
|
||||
this.store.dispatch(addCertification(address, id, name, title, icon));
|
||||
});
|
||||
}, Promise.resolve())
|
||||
.then(() => {
|
||||
this.fetchedAccounts[address] = true;
|
||||
});
|
||||
}
|
||||
|
||||
setCertifiersFilter () {
|
||||
const accounts = Object.keys(this.fetchedAccounts);
|
||||
const addresses = Object.values(this.certifiers).map((c) => c.address);
|
||||
// The events have as first indexed data the account address
|
||||
const topics = [
|
||||
this.contractEvents.map((event) => '0x' + event.signature),
|
||||
accounts
|
||||
];
|
||||
|
||||
if (accounts.length === 0 || addresses.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const promise = this.certifiersFilter
|
||||
? this.api.eth.uninstallFilter(this.certifiersFilter)
|
||||
: Promise.resolve();
|
||||
|
||||
log.debug('setting up registry filter', { topics, accounts, addresses });
|
||||
|
||||
return promise
|
||||
.then(() => this.api.eth.newFilter({
|
||||
fromBlock: 'latest',
|
||||
toBlock: 'latest',
|
||||
address: addresses,
|
||||
topics
|
||||
}))
|
||||
.then((filterId) => {
|
||||
this.certifiersFilter = filterId;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
setRegistryFilter () {
|
||||
const { address } = this.registry.instance;
|
||||
const topics = [ this.registryEvents.map((event) => '0x' + event.signature) ];
|
||||
|
||||
log.debug('setting up registry filter', { topics, address });
|
||||
|
||||
return this.api.eth
|
||||
.newFilter({
|
||||
fromBlock: 'latest',
|
||||
toBlock: 'latest',
|
||||
address, topics
|
||||
})
|
||||
.then((filterId) => {
|
||||
this.registryFilter = filterId;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
96
js/src/redux/providers/certifications/enhanced-querier.js
Normal file
96
js/src/redux/providers/certifications/enhanced-querier.js
Normal file
@@ -0,0 +1,96 @@
|
||||
// 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 { padRight, padLeft } from '~/api/util/format';
|
||||
|
||||
/**
|
||||
* Bytecode of this contract:
|
||||
*
|
||||
*
|
||||
pragma solidity ^0.4.10;
|
||||
|
||||
contract Querier {
|
||||
function Querier
|
||||
(address addr, bytes32 sign, uint out_size, uint from, uint limit)
|
||||
public
|
||||
{
|
||||
// The size is 32 bytes for each
|
||||
// value, plus 32 bytes for the count
|
||||
uint m_size = out_size * limit + 32;
|
||||
|
||||
bytes32 p_return;
|
||||
uint p_in;
|
||||
uint p_out;
|
||||
|
||||
assembly {
|
||||
p_return := mload(0x40)
|
||||
mstore(0x40, add(p_return, m_size))
|
||||
|
||||
mstore(p_return, limit)
|
||||
|
||||
p_in := mload(0x40)
|
||||
mstore(0x40, add(p_in, 0x24))
|
||||
|
||||
mstore(p_in, sign)
|
||||
|
||||
p_out := add(p_return, 0x20)
|
||||
}
|
||||
|
||||
for (uint i = from; i < from + limit; i++) {
|
||||
assembly {
|
||||
mstore(add(p_in, 0x4), i)
|
||||
call(gas, addr, 0x0, p_in, 0x24, p_out, out_size)
|
||||
p_out := add(p_out, out_size)
|
||||
pop
|
||||
}
|
||||
}
|
||||
|
||||
assembly {
|
||||
return (p_return, m_size)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
export const bytecode = '0x60606040523415600e57600080fd5b60405160a0806099833981016040528080519190602001805191906020018051919060200180519190602001805191505082810260200160008080806040519350848401604052858452604051602481016040528981529250505060208201855b858701811015609457806004840152878260248560008e5af15090870190600101606f565b8484f300';
|
||||
|
||||
export const querier = (api, { address, from, limit }, method) => {
|
||||
const { outputs, signature } = method;
|
||||
const outLength = 32 * outputs.length;
|
||||
const callargs = [
|
||||
padLeft(address, 32),
|
||||
padRight(signature, 32),
|
||||
padLeft(outLength, 32),
|
||||
padLeft(from, 32),
|
||||
padLeft(limit, 32)
|
||||
].map((v) => v.slice(2)).join('');
|
||||
const calldata = bytecode + callargs;
|
||||
|
||||
return api.eth.call({ data: calldata })
|
||||
.then((result) => {
|
||||
const data = result.slice(2);
|
||||
const results = [];
|
||||
|
||||
for (let i = 0; i < limit; i++) {
|
||||
const datum = data.substr(2 * (32 + i * outLength), 2 * outLength);
|
||||
const decoded = method.decodeOutput('0x' + datum).map((t) => t.value);
|
||||
|
||||
results.push(decoded);
|
||||
}
|
||||
|
||||
return results;
|
||||
});
|
||||
};
|
||||
@@ -14,222 +14,22 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { uniq, range, debounce } from 'lodash';
|
||||
|
||||
import { addCertification, removeCertification } from './actions';
|
||||
|
||||
import { getLogger, LOG_KEYS } from '~/config';
|
||||
import Contract from '~/api/contract';
|
||||
import Contracts from '~/contracts';
|
||||
import CertifierABI from '~/contracts/abi/certifier.json';
|
||||
|
||||
const log = getLogger(LOG_KEYS.CertificationsMiddleware);
|
||||
|
||||
// TODO: move this to a more general place
|
||||
const updatableFilter = (api, onFilter) => {
|
||||
let filter = null;
|
||||
|
||||
const update = (address, topics) => {
|
||||
if (filter) {
|
||||
filter = filter.then((filterId) => {
|
||||
api.eth.uninstallFilter(filterId);
|
||||
});
|
||||
}
|
||||
|
||||
filter = (filter || Promise.resolve())
|
||||
.then(() => api.eth.newFilter({
|
||||
fromBlock: 'latest',
|
||||
toBlock: 'latest',
|
||||
address,
|
||||
topics
|
||||
}))
|
||||
.then((filterId) => {
|
||||
onFilter(filterId);
|
||||
return filterId;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Failed to create certifications filter:', err);
|
||||
});
|
||||
|
||||
return filter;
|
||||
};
|
||||
|
||||
return update;
|
||||
};
|
||||
import Monitor from './certifiers.monitor';
|
||||
|
||||
export default class CertificationsMiddleware {
|
||||
toMiddleware () {
|
||||
const api = Contracts.get()._api;
|
||||
const badgeReg = Contracts.get().badgeReg;
|
||||
|
||||
const contract = new Contract(api, CertifierABI);
|
||||
const Confirmed = contract.events.find((e) => e.name === 'Confirmed');
|
||||
const Revoked = contract.events.find((e) => e.name === 'Revoked');
|
||||
|
||||
return (store) => {
|
||||
let certifiers = [];
|
||||
let addresses = [];
|
||||
let filterChanged = false;
|
||||
let filter = null;
|
||||
let badgeRegFilter = null;
|
||||
let fetchCertifiersPromise = null;
|
||||
|
||||
const updateFilter = updatableFilter(api, (filterId) => {
|
||||
filterChanged = true;
|
||||
filter = filterId;
|
||||
});
|
||||
|
||||
const badgeRegUpdateFilter = updatableFilter(api, (filterId) => {
|
||||
filterChanged = true;
|
||||
badgeRegFilter = filterId;
|
||||
});
|
||||
|
||||
badgeReg
|
||||
.getContract()
|
||||
.then((badgeRegContract) => {
|
||||
return badgeRegUpdateFilter(badgeRegContract.address, [ [
|
||||
badgeRegContract.instance.Registered.signature,
|
||||
badgeRegContract.instance.Unregistered.signature,
|
||||
badgeRegContract.instance.MetaChanged.signature,
|
||||
badgeRegContract.instance.AddressChanged.signature
|
||||
] ]);
|
||||
})
|
||||
.then(() => {
|
||||
shortFetchChanges();
|
||||
|
||||
api.subscribe('eth_blockNumber', (err) => {
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetchChanges();
|
||||
});
|
||||
});
|
||||
|
||||
function onLogs (logs) {
|
||||
logs = contract.parseEventLogs(logs);
|
||||
logs.forEach((log) => {
|
||||
const certifier = certifiers.find((c) => c.address === log.address);
|
||||
|
||||
if (!certifier) {
|
||||
throw new Error(`Could not find certifier at ${log.address}.`);
|
||||
}
|
||||
const { id, name, title, icon } = certifier;
|
||||
|
||||
if (log.event === 'Revoked') {
|
||||
store.dispatch(removeCertification(log.params.who.value, id));
|
||||
} else {
|
||||
store.dispatch(addCertification(log.params.who.value, id, name, title, icon));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onBadgeRegLogs (logs) {
|
||||
return badgeReg.getContract()
|
||||
.then((badgeRegContract) => {
|
||||
logs = badgeRegContract.parseEventLogs(logs);
|
||||
|
||||
const ids = logs.map((log) => log.params && log.params.id.value.toNumber());
|
||||
|
||||
return fetchCertifiers(uniq(ids));
|
||||
});
|
||||
}
|
||||
|
||||
function _fetchChanges () {
|
||||
const method = filterChanged
|
||||
? 'getFilterLogs'
|
||||
: 'getFilterChanges';
|
||||
|
||||
filterChanged = false;
|
||||
|
||||
api.eth[method](badgeRegFilter)
|
||||
.then(onBadgeRegLogs)
|
||||
.catch((err) => {
|
||||
console.error('Failed to fetch badge reg events:', err);
|
||||
})
|
||||
.then(() => api.eth[method](filter))
|
||||
.then(onLogs)
|
||||
.catch((err) => {
|
||||
console.error('Failed to fetch new certifier events:', err);
|
||||
});
|
||||
}
|
||||
|
||||
const shortFetchChanges = debounce(_fetchChanges, 0.5 * 1000, { leading: true });
|
||||
const fetchChanges = debounce(shortFetchChanges, 10 * 1000, { leading: true });
|
||||
|
||||
function fetchConfirmedEvents () {
|
||||
return updateFilter(certifiers.map((c) => c.address), [
|
||||
[ Confirmed.signature, Revoked.signature ],
|
||||
addresses
|
||||
]).then(() => shortFetchChanges());
|
||||
}
|
||||
|
||||
function fetchCertifiers (ids = []) {
|
||||
if (fetchCertifiersPromise) {
|
||||
return fetchCertifiersPromise;
|
||||
}
|
||||
|
||||
let fetchEvents = false;
|
||||
|
||||
const idsPromise = (certifiers.length === 0)
|
||||
? badgeReg.certifierCount().then((count) => {
|
||||
return range(count);
|
||||
})
|
||||
: Promise.resolve(ids);
|
||||
|
||||
fetchCertifiersPromise = idsPromise
|
||||
.then((ids) => {
|
||||
const promises = ids.map((id) => {
|
||||
return badgeReg.fetchCertifier(id)
|
||||
.then((cert) => {
|
||||
if (!certifiers.some((c) => c.id === cert.id)) {
|
||||
certifiers = certifiers.concat(cert);
|
||||
fetchEvents = true;
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
if (/does not exist/.test(err.toString())) {
|
||||
return log.info(err.toString());
|
||||
}
|
||||
|
||||
log.warn(`Could not fetch certifier ${id}:`, err);
|
||||
});
|
||||
});
|
||||
|
||||
return Promise
|
||||
.all(promises)
|
||||
.then(() => {
|
||||
fetchCertifiersPromise = null;
|
||||
|
||||
if (fetchEvents) {
|
||||
return fetchConfirmedEvents();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return fetchCertifiersPromise;
|
||||
}
|
||||
Monitor.init(api, store);
|
||||
|
||||
return (next) => (action) => {
|
||||
switch (action.type) {
|
||||
case 'fetchCertifiers':
|
||||
fetchConfirmedEvents();
|
||||
|
||||
break;
|
||||
case 'fetchCertifications':
|
||||
const { address } = action;
|
||||
|
||||
if (!addresses.includes(address)) {
|
||||
addresses = addresses.concat(address);
|
||||
fetchConfirmedEvents();
|
||||
}
|
||||
|
||||
break;
|
||||
case 'setVisibleAccounts':
|
||||
const _addresses = action.addresses || [];
|
||||
const { addresses = [] } = action;
|
||||
|
||||
addresses = uniq(addresses.concat(_addresses));
|
||||
fetchConfirmedEvents();
|
||||
Monitor.get().fetchAccounts(addresses);
|
||||
next(action);
|
||||
|
||||
break;
|
||||
|
||||
@@ -20,24 +20,32 @@ export default (state = initialState, action) => {
|
||||
if (action.type === 'addCertification') {
|
||||
const { address, id, name, icon, title } = action;
|
||||
const certifications = state[address] || [];
|
||||
const certifierIndex = certifications.findIndex((c) => c.id === id);
|
||||
const data = { id, name, icon, title };
|
||||
const nextCertifications = certifications.slice();
|
||||
|
||||
if (certifications.some((c) => c.id === id)) {
|
||||
return state;
|
||||
if (certifierIndex >= 0) {
|
||||
nextCertifications[certifierIndex] = data;
|
||||
} else {
|
||||
nextCertifications.push(data);
|
||||
}
|
||||
|
||||
const newCertifications = certifications.concat({
|
||||
id, name, icon, title
|
||||
});
|
||||
|
||||
return { ...state, [address]: newCertifications };
|
||||
return { ...state, [address]: nextCertifications };
|
||||
}
|
||||
|
||||
if (action.type === 'removeCertification') {
|
||||
const { address, id } = action;
|
||||
const certifications = state[address] || [];
|
||||
const certifierIndex = certifications.findIndex((c) => c.id === id);
|
||||
|
||||
const newCertifications = certifications.filter((c) => c.id !== id);
|
||||
// Don't remove if not there
|
||||
if (certifierIndex < 0) {
|
||||
return state;
|
||||
}
|
||||
|
||||
const newCertifications = certifications.slice();
|
||||
|
||||
newCertifications.splice(certifierIndex, 1);
|
||||
return { ...state, [address]: newCertifications };
|
||||
}
|
||||
|
||||
|
||||
@@ -54,13 +54,24 @@ export const watchRequest = (request) => (dispatch, getState) => {
|
||||
dispatch(trackRequest(requestId, request));
|
||||
};
|
||||
|
||||
export const trackRequest = (requestId, { transactionHash = null } = {}) => (dispatch, getState) => {
|
||||
export const trackRequest = (requestId, { transactionHash = null, retries = 0 } = {}) => (dispatch, getState) => {
|
||||
const { api } = getState();
|
||||
|
||||
trackRequestUtil(api, { requestId, transactionHash }, (error, _data = {}) => {
|
||||
const data = { ..._data };
|
||||
|
||||
if (error) {
|
||||
// Retry in 500ms if request not found, max 5 times
|
||||
if (error.type === 'REQUEST_NOT_FOUND') {
|
||||
if (retries > 5) {
|
||||
return dispatch(deleteRequest(requestId));
|
||||
}
|
||||
|
||||
return setTimeout(() => {
|
||||
trackRequest(requestId, { transactionHash, retries: retries + 1 })(dispatch, getState);
|
||||
}, 500);
|
||||
}
|
||||
|
||||
console.error(error);
|
||||
return dispatch(setRequest(requestId, { error }));
|
||||
}
|
||||
|
||||
@@ -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 { isEqual } from 'lodash';
|
||||
import { isEqual, debounce } from 'lodash';
|
||||
|
||||
import { LOG_KEYS, getLogger } from '~/config';
|
||||
import UpgradeStore from '~/modals/UpgradeParity/store';
|
||||
@@ -179,6 +179,12 @@ export default class Status {
|
||||
});
|
||||
}
|
||||
|
||||
_updateStatus = debounce(status => {
|
||||
this._store.dispatch(statusCollection(status));
|
||||
}, 2500, {
|
||||
maxWait: 5000
|
||||
});
|
||||
|
||||
_subscribeEthSyncing = () => {
|
||||
return this._api.pubsub
|
||||
.eth
|
||||
@@ -187,7 +193,7 @@ export default class Status {
|
||||
return;
|
||||
}
|
||||
|
||||
this._store.dispatch(statusCollection({ syncing }));
|
||||
this._updateStatus({ syncing });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -198,6 +204,7 @@ export default class Status {
|
||||
if (error || !netPeers) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._store.dispatch(statusCollection({ netPeers }));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -115,9 +115,11 @@ export function loadTokensBasics (_tokenIndexes, options) {
|
||||
const prevTokensIndexes = Object.values(tokens).map((t) => t.index);
|
||||
|
||||
// Only fetch tokens we don't have yet
|
||||
const tokenIndexes = _tokenIndexes.filter((tokenIndex) => {
|
||||
return !prevTokensIndexes.includes(tokenIndex);
|
||||
});
|
||||
const tokenIndexes = _tokenIndexes
|
||||
.filter((tokenIndex) => {
|
||||
return !prevTokensIndexes.includes(tokenIndex);
|
||||
})
|
||||
.sort();
|
||||
|
||||
const count = tokenIndexes.length;
|
||||
|
||||
@@ -130,10 +132,15 @@ export function loadTokensBasics (_tokenIndexes, options) {
|
||||
return tokenReg.getContract()
|
||||
.then((tokenRegContract) => {
|
||||
let promise = Promise.resolve();
|
||||
const first = tokenIndexes[0];
|
||||
const last = tokenIndexes[tokenIndexes.length - 1];
|
||||
|
||||
for (let from = first; from <= last; from += limit) {
|
||||
// No need to fetch `limit` elements
|
||||
const lowerLimit = Math.min(limit, last - from + 1);
|
||||
|
||||
for (let start = 0; start < count; start += limit) {
|
||||
promise = promise
|
||||
.then(() => fetchTokensBasics(api, tokenRegContract, start, limit))
|
||||
.then(() => fetchTokensBasics(api, tokenRegContract, from, lowerLimit))
|
||||
.then((results) => {
|
||||
results
|
||||
.forEach((token) => {
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
transition: transform ease-out 0.1s;
|
||||
transform: scale(1);
|
||||
|
||||
@@ -15,11 +15,12 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { pick } from 'lodash';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import TokenImage from '~/ui/TokenImage';
|
||||
import TokenValue from './tokenValue';
|
||||
|
||||
import styles from './balance.css';
|
||||
|
||||
@@ -69,58 +70,19 @@ export class Balance extends Component {
|
||||
const balanceValue = balance[tokenId];
|
||||
|
||||
const isEthToken = token.native;
|
||||
const isFullToken = !showOnlyEth || isEthToken;
|
||||
const hasBalance = (balanceValue instanceof BigNumber) && balanceValue.gt(0);
|
||||
|
||||
if (!hasBalance && !isEthToken) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const bnf = new BigNumber(token.format || 1);
|
||||
let decimals = 0;
|
||||
|
||||
if (bnf.gte(1000)) {
|
||||
decimals = 3;
|
||||
} else if (bnf.gte(100)) {
|
||||
decimals = 2;
|
||||
} else if (bnf.gte(10)) {
|
||||
decimals = 1;
|
||||
}
|
||||
|
||||
const rawValue = new BigNumber(balanceValue).div(bnf);
|
||||
const value = rawValue.toFormat(decimals);
|
||||
|
||||
const classNames = [styles.balance];
|
||||
let details = null;
|
||||
|
||||
if (isFullToken) {
|
||||
classNames.push(styles.full);
|
||||
details = [
|
||||
<div
|
||||
className={ styles.value }
|
||||
key='value'
|
||||
>
|
||||
<span title={ `${rawValue.toFormat()} ${token.tag}` }>
|
||||
{ value }
|
||||
</span>
|
||||
</div>,
|
||||
<div
|
||||
className={ styles.tag }
|
||||
key='tag'
|
||||
>
|
||||
{ token.tag }
|
||||
</div>
|
||||
];
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={ classNames.join(' ') }
|
||||
<TokenValue
|
||||
key={ tokenId }
|
||||
>
|
||||
<TokenImage token={ token } />
|
||||
{ details }
|
||||
</div>
|
||||
showOnlyEth={ showOnlyEth }
|
||||
token={ token }
|
||||
value={ balanceValue }
|
||||
/>
|
||||
);
|
||||
})
|
||||
.filter((node) => node);
|
||||
@@ -155,11 +117,15 @@ export class Balance extends Component {
|
||||
}
|
||||
|
||||
function mapStateToProps (state, props) {
|
||||
const { balances, tokens } = state;
|
||||
const { balances, tokens: allTokens } = state;
|
||||
const { address } = props;
|
||||
const balance = balances[address] || props.balance || {};
|
||||
|
||||
const tokenIds = Object.keys(balance);
|
||||
const tokens = pick(allTokens, tokenIds);
|
||||
|
||||
return {
|
||||
balance: balances[address] || props.balance || {},
|
||||
balance,
|
||||
tokens
|
||||
};
|
||||
}
|
||||
|
||||
@@ -84,13 +84,13 @@ describe('ui/Balance', () => {
|
||||
});
|
||||
|
||||
it('renders all the non-zero balances', () => {
|
||||
expect(component.find('Connect(TokenImage)')).to.have.length(2);
|
||||
expect(component.find('Connect(TokenValue)')).to.have.length(2);
|
||||
});
|
||||
|
||||
describe('render specifiers', () => {
|
||||
it('renders all the tokens with showZeroValues', () => {
|
||||
render({ showZeroValues: true });
|
||||
expect(component.find('Connect(TokenImage)')).to.have.length(2);
|
||||
expect(component.find('Connect(TokenValue)')).to.have.length(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
109
js/src/ui/Balance/tokenValue.js
Normal file
109
js/src/ui/Balance/tokenValue.js
Normal file
@@ -0,0 +1,109 @@
|
||||
// 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 BigNumber from 'bignumber.js';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
|
||||
import { fetchTokens } from '~/redux/providers/tokensActions';
|
||||
import TokenImage from '~/ui/TokenImage';
|
||||
|
||||
import styles from './balance.css';
|
||||
|
||||
class TokenValue extends Component {
|
||||
static propTypes = {
|
||||
token: PropTypes.object.isRequired,
|
||||
value: PropTypes.object.isRequired,
|
||||
|
||||
// Redux injection
|
||||
fetchTokens: PropTypes.func.isRequired,
|
||||
|
||||
showOnlyEth: PropTypes.bool
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
const { token } = this.props;
|
||||
|
||||
if (token.native) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!token.fetched) {
|
||||
if (!Number.isFinite(token.index)) {
|
||||
return console.warn('no token index', token);
|
||||
}
|
||||
|
||||
this.props.fetchTokens([ token.index ]);
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { token, showOnlyEth, value } = this.props;
|
||||
|
||||
const isEthToken = token.native;
|
||||
const isFullToken = !showOnlyEth || isEthToken;
|
||||
|
||||
const bnf = new BigNumber(token.format || 1);
|
||||
let decimals = 0;
|
||||
|
||||
if (bnf.gte(1000)) {
|
||||
decimals = 3;
|
||||
} else if (bnf.gte(100)) {
|
||||
decimals = 2;
|
||||
} else if (bnf.gte(10)) {
|
||||
decimals = 1;
|
||||
}
|
||||
|
||||
const rawValue = new BigNumber(value).div(bnf);
|
||||
const classNames = [styles.balance];
|
||||
|
||||
if (isFullToken) {
|
||||
classNames.push(styles.full);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ classNames.join(' ') }>
|
||||
<TokenImage token={ token } />
|
||||
{
|
||||
isFullToken
|
||||
? [
|
||||
<div className={ styles.value } key='value'>
|
||||
<span title={ `${rawValue.toFormat()} ${token.tag}` }>
|
||||
{ rawValue.toFormat(decimals) }
|
||||
</span>
|
||||
</div>,
|
||||
<div className={ styles.tag } key='tag'>
|
||||
{ token.tag }
|
||||
</div>
|
||||
]
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return bindActionCreators({
|
||||
fetchTokens
|
||||
}, dispatch);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
mapDispatchToProps
|
||||
)(TokenValue);
|
||||
@@ -19,6 +19,7 @@ import React, { Component, PropTypes } from 'react';
|
||||
import Container, { Title as ContainerTitle } from '~/ui/Container';
|
||||
import DappIcon from '~/ui/DappIcon';
|
||||
import Tags from '~/ui/Tags';
|
||||
import DappVouchFor from '../DappVouchFor';
|
||||
|
||||
import styles from './dappCard.css';
|
||||
|
||||
@@ -61,6 +62,7 @@ export default class DappCard extends Component {
|
||||
app={ app }
|
||||
className={ styles.image }
|
||||
/>
|
||||
<DappVouchFor app={ app } />
|
||||
<Tags
|
||||
className={ styles.tags }
|
||||
tags={
|
||||
|
||||
43
js/src/ui/DappVouchFor/dappVouchFor.css
Normal file
43
js/src/ui/DappVouchFor/dappVouchFor.css
Normal file
@@ -0,0 +1,43 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity is free software: you can redistribute it and/or modify
|
||||
/* it under the terms of the GNU General Public License as published by
|
||||
/* the Free Software Foundation, either version 3 of the License, or
|
||||
/* (at your option) any later version.
|
||||
/*
|
||||
/* Parity is distributed in the hope that it will be useful,
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/* GNU General Public License for more details.
|
||||
/*
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.tag {
|
||||
color: inherit;
|
||||
position: absolute;
|
||||
top: 1em;
|
||||
right: 1em;
|
||||
|
||||
.image {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.bubble {
|
||||
background: red;
|
||||
border-radius: 0.25em;
|
||||
color: white;
|
||||
font-size: 0.75em;
|
||||
padding: 0.1em 0.5em;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
}
|
||||
57
js/src/ui/DappVouchFor/dappVouchFor.js
Normal file
57
js/src/ui/DappVouchFor/dappVouchFor.js
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
import IdentityIcon from '../IdentityIcon';
|
||||
|
||||
import Store from './store';
|
||||
import styles from './dappVouchFor.css';
|
||||
|
||||
@observer
|
||||
export default class DappVouchFor extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
app: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
store = new Store(this.context.api, this.props.app);
|
||||
|
||||
render () {
|
||||
const count = this.store.vouchers.length;
|
||||
|
||||
if (!count) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.tag }>
|
||||
<IdentityIcon
|
||||
address={ this.store.vouchers[0] }
|
||||
className={ styles.image }
|
||||
alt={ `${count} identities vouch for this dapp` }
|
||||
/>
|
||||
<div className={ styles.bubble }>
|
||||
{ count }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
17
js/src/ui/DappVouchFor/index.js
Normal file
17
js/src/ui/DappVouchFor/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 './dappVouchFor';
|
||||
87
js/src/ui/DappVouchFor/store.js
Normal file
87
js/src/ui/DappVouchFor/store.js
Normal file
@@ -0,0 +1,87 @@
|
||||
// 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, observable } from 'mobx';
|
||||
import { uniq } from 'lodash';
|
||||
|
||||
import Contracts from '~/contracts';
|
||||
import { vouchfor as vouchForAbi } from '~/contracts/abi';
|
||||
|
||||
let contractPromise = null;
|
||||
|
||||
export default class Store {
|
||||
@observable vouchers = [];
|
||||
|
||||
constructor (api, app) {
|
||||
this._api = api;
|
||||
|
||||
this.findVouchers(app);
|
||||
}
|
||||
|
||||
async attachContract () {
|
||||
const address = await Contracts.get().registry.lookupAddress('vouchfor');
|
||||
|
||||
if (!address || /^0x0*$/.test(address)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const contract = await this._api.newContract(vouchForAbi, address);
|
||||
|
||||
return contract;
|
||||
}
|
||||
|
||||
async findVouchers ({ contentHash, id }) {
|
||||
if (!contentHash) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!contractPromise) {
|
||||
contractPromise = this.attachContract();
|
||||
}
|
||||
|
||||
const contract = await contractPromise;
|
||||
|
||||
if (!contract) {
|
||||
return;
|
||||
}
|
||||
|
||||
const vouchHash = await this.lookupHash(contract, `0x${contentHash}`);
|
||||
const vouchId = await this.lookupHash(contract, id);
|
||||
|
||||
this.addVouchers(vouchHash, vouchId);
|
||||
}
|
||||
|
||||
async lookupHash (contract, hash) {
|
||||
const vouchers = [];
|
||||
let lastItem = false;
|
||||
|
||||
for (let index = 0; !lastItem; index++) {
|
||||
const voucher = await contract.instance.vouched.call({}, [hash, index]);
|
||||
|
||||
if (/^0x0*$/.test(voucher)) {
|
||||
lastItem = true;
|
||||
} else {
|
||||
vouchers.push(voucher);
|
||||
}
|
||||
}
|
||||
|
||||
return vouchers;
|
||||
}
|
||||
|
||||
@action addVouchers = (vouchHash, vouchId) => {
|
||||
this.vouchers = uniq([].concat(this.vouchers.peek(), vouchHash, vouchId));
|
||||
}
|
||||
}
|
||||
@@ -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/>.
|
||||
|
||||
import { eq } from 'lodash';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { connect } from 'react-redux';
|
||||
@@ -93,6 +94,18 @@ class AddressSelect extends Component {
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (!eq(Object.keys(this.props.accounts), Object.keys(nextProps.accounts))) {
|
||||
return this.setValues(nextProps);
|
||||
}
|
||||
|
||||
if (!eq(Object.keys(this.props.contacts), Object.keys(nextProps.contacts))) {
|
||||
return this.setValues(nextProps);
|
||||
}
|
||||
|
||||
if (!eq(Object.keys(this.props.contracts), Object.keys(nextProps.contracts))) {
|
||||
return this.setValues(nextProps);
|
||||
}
|
||||
|
||||
if (this.store.values && this.store.values.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -165,7 +165,8 @@ export default class AddressSelectStore {
|
||||
const contactsN = Object.keys(contacts).length;
|
||||
|
||||
if (accountsN + contractsN + contactsN === 0) {
|
||||
return;
|
||||
this.initValues = [];
|
||||
return this.handleChange();
|
||||
}
|
||||
|
||||
this.initValues = [
|
||||
|
||||
@@ -18,6 +18,7 @@ import React, { Component, PropTypes } from 'react';
|
||||
import { TextField } from 'material-ui';
|
||||
import { noop } from 'lodash';
|
||||
import keycode from 'keycode';
|
||||
import localStore from 'store';
|
||||
|
||||
import { nodeOrStringProptype } from '~/util/proptypes';
|
||||
import { toString } from '~/util/messages';
|
||||
@@ -223,7 +224,9 @@ export default class Input extends Component {
|
||||
}
|
||||
|
||||
onChange = (event, value) => {
|
||||
if (!this.props.allowPaste) {
|
||||
const isDev = localStore.get('allYourBaseAreBelongToUs') || false;
|
||||
|
||||
if (!this.props.allowPaste && !isDev) {
|
||||
if (value.length - this.state.value.length > 8) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -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/>.
|
||||
|
||||
import { pick } from 'lodash';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
@@ -28,6 +29,7 @@ class InputAddressSelect extends Component {
|
||||
contracts: PropTypes.object.isRequired,
|
||||
|
||||
allowCopy: PropTypes.bool,
|
||||
allowedValues: PropTypes.array,
|
||||
className: PropTypes.string,
|
||||
error: nodeOrStringProptype(),
|
||||
hint: nodeOrStringProptype(),
|
||||
@@ -38,16 +40,33 @@ class InputAddressSelect extends Component {
|
||||
};
|
||||
|
||||
render () {
|
||||
const { accounts, allowCopy, className, contacts, contracts, label, hint, error, value, onChange, readOnly } = this.props;
|
||||
const { accounts, allowCopy, allowedValues, className, contacts, contracts, label, hint, error, value, onChange, readOnly } = this.props;
|
||||
// Add the currently selected value to the list
|
||||
// of allowed values, if any given
|
||||
const nextAllowedValues = allowedValues
|
||||
? [].concat(allowedValues, value || [])
|
||||
: null;
|
||||
|
||||
const filteredAccounts = nextAllowedValues
|
||||
? pick(accounts, nextAllowedValues)
|
||||
: accounts;
|
||||
|
||||
const filteredContacts = nextAllowedValues
|
||||
? pick(contacts, nextAllowedValues)
|
||||
: contacts;
|
||||
|
||||
const filteredContracts = nextAllowedValues
|
||||
? pick(contracts, nextAllowedValues)
|
||||
: contracts;
|
||||
|
||||
return (
|
||||
<AddressSelect
|
||||
allowCopy={ allowCopy }
|
||||
allowInput
|
||||
accounts={ accounts }
|
||||
accounts={ filteredAccounts }
|
||||
className={ className }
|
||||
contacts={ contacts }
|
||||
contracts={ contracts }
|
||||
contacts={ filteredContacts }
|
||||
contracts={ filteredContracts }
|
||||
error={ error }
|
||||
hint={ hint }
|
||||
label={ label }
|
||||
|
||||
@@ -40,8 +40,8 @@ export default class TypedInput extends Component {
|
||||
PropTypes.string
|
||||
]).isRequired,
|
||||
|
||||
accounts: PropTypes.object,
|
||||
allowCopy: PropTypes.bool,
|
||||
allowedValues: PropTypes.array,
|
||||
className: PropTypes.string,
|
||||
error: PropTypes.any,
|
||||
hint: nodeOrStringProptype(),
|
||||
@@ -97,7 +97,7 @@ export default class TypedInput extends Component {
|
||||
const { type } = param;
|
||||
|
||||
if (type === ABI_TYPES.ARRAY) {
|
||||
const { accounts, className, label } = this.props;
|
||||
const { allowedValues, className, label } = this.props;
|
||||
const { subtype, length } = param;
|
||||
const value = this.getValue() || param.default;
|
||||
|
||||
@@ -113,8 +113,8 @@ export default class TypedInput extends Component {
|
||||
|
||||
return (
|
||||
<TypedInput
|
||||
accounts={ accounts }
|
||||
allowCopy={ allowCopy }
|
||||
allowedValues={ allowedValues }
|
||||
className={ className }
|
||||
key={ `${subtype.type}_${index}` }
|
||||
onChange={ onChange }
|
||||
@@ -340,13 +340,13 @@ export default class TypedInput extends Component {
|
||||
}
|
||||
|
||||
renderAddress () {
|
||||
const { accounts, allowCopy, className, label, error, hint, readOnly } = this.props;
|
||||
const { allowCopy, allowedValues, className, label, error, hint, readOnly } = this.props;
|
||||
const value = this.getValue();
|
||||
|
||||
return (
|
||||
<InputAddressSelect
|
||||
allowCopy={ allowCopy }
|
||||
accounts={ accounts }
|
||||
allowedValues={ allowedValues }
|
||||
className={ className }
|
||||
error={ error }
|
||||
hint={ hint }
|
||||
|
||||
@@ -30,6 +30,7 @@ class IdentityIcon extends Component {
|
||||
|
||||
static propTypes = {
|
||||
address: PropTypes.string,
|
||||
alt: PropTypes.string,
|
||||
button: PropTypes.bool,
|
||||
center: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
@@ -84,7 +85,7 @@ class IdentityIcon extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { address, button, className, center, disabled, inline, padded, tiny } = this.props;
|
||||
const { address, alt, button, className, center, disabled, inline, padded, tiny } = this.props;
|
||||
const { iconsrc } = this.state;
|
||||
const classes = [
|
||||
styles.icon,
|
||||
@@ -135,6 +136,7 @@ class IdentityIcon extends Component {
|
||||
|
||||
return (
|
||||
<img
|
||||
alt={ alt || address }
|
||||
className={ classes }
|
||||
data-address-img
|
||||
height={ size }
|
||||
|
||||
@@ -23,6 +23,7 @@ import { connect } from 'react-redux';
|
||||
|
||||
import { TypedInput, InputAddress } from '../Form';
|
||||
import MethodDecodingStore from './methodDecodingStore';
|
||||
import TokenValue from './tokenValue';
|
||||
|
||||
import styles from './methodDecoding.css';
|
||||
|
||||
@@ -602,9 +603,10 @@ class MethodDecoding extends Component {
|
||||
const { token } = this.props;
|
||||
|
||||
return (
|
||||
<span className={ styles.tokenValue }>
|
||||
{ value.div(token.format).toFormat(5) }<small> { token.tag }</small>
|
||||
</span>
|
||||
<TokenValue
|
||||
id={ token.id }
|
||||
value={ value }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
102
js/src/ui/MethodDecoding/tokenValue.js
Normal file
102
js/src/ui/MethodDecoding/tokenValue.js
Normal file
@@ -0,0 +1,102 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
|
||||
import { fetchTokens } from '~/redux/providers/tokensActions';
|
||||
import styles from './methodDecoding.css';
|
||||
|
||||
class TokenValue extends Component {
|
||||
static propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
value: PropTypes.object.isRequired,
|
||||
|
||||
fetchTokens: PropTypes.func,
|
||||
token: PropTypes.object
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
const { token } = this.props;
|
||||
|
||||
if (!token.fetched) {
|
||||
this.props.fetchTokens([ token.index ]);
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { token, value } = this.props;
|
||||
|
||||
if (!token.format) {
|
||||
console.warn('token with no format', token);
|
||||
}
|
||||
|
||||
const format = token.format
|
||||
? token.format
|
||||
: 1;
|
||||
|
||||
const precision = token.format
|
||||
? 5
|
||||
: 0;
|
||||
|
||||
const tag = token.format
|
||||
? token.tag
|
||||
: 'TOKENS';
|
||||
|
||||
return (
|
||||
<span className={ styles.tokenValue }>
|
||||
{ value.div(format).toFormat(precision) }<small> { tag }</small>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (initState, initProps) {
|
||||
const { id } = initProps;
|
||||
let token = Object.assign({}, initState.tokens[id]);
|
||||
|
||||
if (token.fetched) {
|
||||
return () => ({ token });
|
||||
}
|
||||
|
||||
let update = true;
|
||||
|
||||
return (state) => {
|
||||
if (update) {
|
||||
const { tokens } = state;
|
||||
const nextToken = tokens[id];
|
||||
|
||||
if (nextToken.fetched) {
|
||||
token = Object.assign({}, nextToken);
|
||||
update = false;
|
||||
}
|
||||
}
|
||||
|
||||
return { token };
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return bindActionCreators({
|
||||
fetchTokens
|
||||
}, dispatch);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(TokenValue);
|
||||
@@ -81,12 +81,11 @@ export function getTxOptions (api, func, _options, values = []) {
|
||||
options.to = options.to || func.contract.address;
|
||||
}
|
||||
|
||||
if (!address) {
|
||||
return Promise.resolve({ func, options, values });
|
||||
}
|
||||
const promise = (!address)
|
||||
? Promise.resolve(false)
|
||||
: WalletsUtils.isWallet(api, address);
|
||||
|
||||
return WalletsUtils
|
||||
.isWallet(api, address)
|
||||
return promise
|
||||
.then((isWallet) => {
|
||||
if (!isWallet) {
|
||||
return { func, options, values };
|
||||
|
||||
@@ -78,13 +78,39 @@ export default class WalletsUtils {
|
||||
.delegateCall(api, walletContract.address, 'fetchTransactions', [ walletContract ])
|
||||
.then((transactions) => {
|
||||
return transactions.sort((txA, txB) => {
|
||||
const comp = txB.blockNumber.comparedTo(txA.blockNumber);
|
||||
const bnA = txA.blockNumber;
|
||||
const bnB = txB.blockNumber;
|
||||
|
||||
if (!bnA) {
|
||||
console.warn('could not find block number in transaction', txA);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!bnB) {
|
||||
console.warn('could not find block number in transaction', txB);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const comp = bnA.comparedTo(bnB);
|
||||
|
||||
if (comp !== 0) {
|
||||
return comp;
|
||||
}
|
||||
|
||||
return txB.transactionIndex.comparedTo(txA.transactionIndex);
|
||||
const txIdxA = txA.transactionIndex;
|
||||
const txIdxB = txB.transactionIndex;
|
||||
|
||||
if (!txIdxA) {
|
||||
console.warn('could not find transaction index in transaction', txA);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!txIdxB) {
|
||||
console.warn('could not find transaction index in transaction', txB);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return txIdxA.comparedTo(txIdxB);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -212,6 +212,7 @@ export default class ConsensysWalletUtils {
|
||||
|
||||
const transaction = {
|
||||
transactionHash: log.transactionHash,
|
||||
transactionIndex: log.transactionIndex,
|
||||
blockNumber: log.blockNumber
|
||||
};
|
||||
|
||||
|
||||
@@ -130,27 +130,67 @@ export default class FoundationWalletUtils {
|
||||
.ConfirmationNeeded
|
||||
.getAllLogs()
|
||||
.then((logs) => {
|
||||
return logs.map((log) => ({
|
||||
initiator: log.params.initiator.value,
|
||||
to: log.params.to.value,
|
||||
data: log.params.data.value,
|
||||
value: log.params.value.value,
|
||||
operation: bytesToHex(log.params.operation.value),
|
||||
transactionIndex: log.transactionIndex,
|
||||
transactionHash: log.transactionHash,
|
||||
blockNumber: log.blockNumber,
|
||||
confirmedBy: []
|
||||
}));
|
||||
return logs
|
||||
.filter((log) => {
|
||||
if (!log.blockNumber) {
|
||||
console.warn('got a log without blockNumber', log);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!log.transactionIndex) {
|
||||
console.warn('got a log without transactionIndex', log);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.map((log) => ({
|
||||
initiator: log.params.initiator.value,
|
||||
to: log.params.to.value,
|
||||
data: log.params.data.value,
|
||||
value: log.params.value.value,
|
||||
operation: bytesToHex(log.params.operation.value),
|
||||
transactionIndex: log.transactionIndex,
|
||||
transactionHash: log.transactionHash,
|
||||
blockNumber: log.blockNumber,
|
||||
confirmedBy: []
|
||||
}));
|
||||
})
|
||||
.then((logs) => {
|
||||
return logs.sort((logA, logB) => {
|
||||
const comp = logA.blockNumber.comparedTo(logB.blockNumber);
|
||||
const bnA = logA.blockNumber;
|
||||
const bnB = logA.blockNumber;
|
||||
|
||||
if (!bnA) {
|
||||
console.warn('could not find block number in log', logA);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!bnB) {
|
||||
console.warn('could not find block number in log', logB);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const comp = bnA.comparedTo(bnB);
|
||||
|
||||
if (comp !== 0) {
|
||||
return comp;
|
||||
}
|
||||
|
||||
return logA.transactionIndex.comparedTo(logB.transactionIndex);
|
||||
const txIdxA = logA.transactionIndex;
|
||||
const txIdxB = logB.transactionIndex;
|
||||
|
||||
if (!txIdxA) {
|
||||
console.warn('could not find transaction index in log', logA);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!txIdxB) {
|
||||
console.warn('could not find transaction index in log', logB);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return txIdxA.comparedTo(txIdxB);
|
||||
});
|
||||
})
|
||||
.then((pendingTxs) => {
|
||||
@@ -205,40 +245,48 @@ export default class FoundationWalletUtils {
|
||||
] ]
|
||||
})
|
||||
.then((logs) => {
|
||||
const transactions = logs.map((log) => {
|
||||
const signature = toHex(log.topics[0]);
|
||||
const transactions = logs
|
||||
.map((log) => {
|
||||
const signature = toHex(log.topics[0]);
|
||||
|
||||
const value = log.params.value.value;
|
||||
const from = signature === WalletSignatures.Deposit
|
||||
? log.params['_from'].value
|
||||
: walletContract.address;
|
||||
const value = log.params.value.value;
|
||||
const from = signature === WalletSignatures.Deposit
|
||||
? log.params['_from'].value
|
||||
: walletContract.address;
|
||||
|
||||
const to = signature === WalletSignatures.Deposit
|
||||
? walletContract.address
|
||||
: log.params.to.value;
|
||||
const to = signature === WalletSignatures.Deposit
|
||||
? walletContract.address
|
||||
: log.params.to.value;
|
||||
|
||||
const transaction = {
|
||||
transactionHash: log.transactionHash,
|
||||
blockNumber: log.blockNumber,
|
||||
from, to, value
|
||||
};
|
||||
const transaction = {
|
||||
transactionHash: log.transactionHash,
|
||||
transactionIndex: log.transactionIndex,
|
||||
blockNumber: log.blockNumber,
|
||||
from, to, value
|
||||
};
|
||||
|
||||
if (log.params.created && log.params.created.value && !/^(0x)?0*$/.test(log.params.created.value)) {
|
||||
transaction.creates = log.params.created.value;
|
||||
delete transaction.to;
|
||||
}
|
||||
if (!transaction.blockNumber) {
|
||||
console.warn('log without block number', log);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (log.params.operation) {
|
||||
transaction.operation = bytesToHex(log.params.operation.value);
|
||||
checkPendingOperation(api, log, transaction.operation);
|
||||
}
|
||||
if (log.params.created && log.params.created.value && !/^(0x)?0*$/.test(log.params.created.value)) {
|
||||
transaction.creates = log.params.created.value;
|
||||
delete transaction.to;
|
||||
}
|
||||
|
||||
if (log.params.data) {
|
||||
transaction.data = log.params.data.value;
|
||||
}
|
||||
if (log.params.operation) {
|
||||
transaction.operation = bytesToHex(log.params.operation.value);
|
||||
checkPendingOperation(api, log, transaction.operation);
|
||||
}
|
||||
|
||||
return transaction;
|
||||
});
|
||||
if (log.params.data) {
|
||||
transaction.data = log.params.data.value;
|
||||
}
|
||||
|
||||
return transaction;
|
||||
})
|
||||
.filter((tx) => tx);
|
||||
|
||||
return transactions;
|
||||
});
|
||||
|
||||
@@ -26,7 +26,6 @@ import HardwareStore from '~/mobx/hardwareStore';
|
||||
import ExportStore from '~/modals/ExportAccount/exportStore';
|
||||
import { DeleteAccount, EditMeta, Faucet, PasswordManager, Shapeshift, Transfer, Verification } from '~/modals';
|
||||
import { setVisibleAccounts } from '~/redux/providers/personalActions';
|
||||
import { fetchCertifiers, fetchCertifications } from '~/redux/providers/certifications/actions';
|
||||
import { Actionbar, Button, ConfirmDialog, Input, Page, Portal } from '~/ui';
|
||||
import { DeleteIcon, DialIcon, EditIcon, LockedIcon, SendIcon, VerifyIcon, FileDownloadIcon } from '~/ui/Icons';
|
||||
|
||||
@@ -45,8 +44,6 @@ class Account extends Component {
|
||||
|
||||
static propTypes = {
|
||||
accounts: PropTypes.object.isRequired,
|
||||
fetchCertifiers: PropTypes.func.isRequired,
|
||||
fetchCertifications: PropTypes.func.isRequired,
|
||||
setVisibleAccounts: PropTypes.func.isRequired,
|
||||
|
||||
account: PropTypes.object,
|
||||
@@ -67,7 +64,6 @@ class Account extends Component {
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.props.fetchCertifiers();
|
||||
this.setVisibleAccounts();
|
||||
}
|
||||
|
||||
@@ -90,11 +86,10 @@ class Account extends Component {
|
||||
}
|
||||
|
||||
setVisibleAccounts (props = this.props) {
|
||||
const { params, setVisibleAccounts, fetchCertifications } = props;
|
||||
const { params, setVisibleAccounts } = props;
|
||||
const addresses = [params.address];
|
||||
|
||||
setVisibleAccounts(addresses);
|
||||
fetchCertifications(params.address);
|
||||
}
|
||||
|
||||
render () {
|
||||
@@ -524,8 +519,6 @@ function mapStateToProps (state, props) {
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return bindActionCreators({
|
||||
fetchCertifiers,
|
||||
fetchCertifications,
|
||||
newError,
|
||||
setVisibleAccounts
|
||||
}, dispatch);
|
||||
|
||||
@@ -17,10 +17,8 @@
|
||||
import { pick } from 'lodash';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
|
||||
import { Container, SectionList } from '~/ui';
|
||||
import { fetchCertifiers, fetchCertifications } from '~/redux/providers/certifications/actions';
|
||||
import { ETH_TOKEN } from '~/util/tokens';
|
||||
|
||||
import Summary from '../Summary';
|
||||
@@ -38,20 +36,9 @@ class List extends Component {
|
||||
orderFallback: PropTypes.string,
|
||||
search: PropTypes.array,
|
||||
|
||||
fetchCertifiers: PropTypes.func.isRequired,
|
||||
fetchCertifications: PropTypes.func.isRequired,
|
||||
handleAddSearchToken: PropTypes.func
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
const { accounts, fetchCertifiers, fetchCertifications } = this.props;
|
||||
|
||||
fetchCertifiers();
|
||||
for (let address in accounts) {
|
||||
fetchCertifications(address);
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { accounts, disabled, empty } = this.props;
|
||||
|
||||
@@ -264,14 +251,7 @@ function mapStateToProps (state, props) {
|
||||
return { balances, certifications };
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return bindActionCreators({
|
||||
fetchCertifiers,
|
||||
fetchCertifications
|
||||
}, dispatch);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
null
|
||||
)(List);
|
||||
|
||||
@@ -35,7 +35,6 @@ class InputQuery extends Component {
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
accountsInfo: PropTypes.object.isRequired,
|
||||
contract: PropTypes.object.isRequired,
|
||||
inputs: arrayOrObjectProptype().isRequired,
|
||||
outputs: arrayOrObjectProptype().isRequired,
|
||||
@@ -122,7 +121,7 @@ class InputQuery extends Component {
|
||||
|
||||
renderResults () {
|
||||
const { results, isLoading } = this.state;
|
||||
const { accountsInfo, outputs } = this.props;
|
||||
const { outputs } = this.props;
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
@@ -143,7 +142,6 @@ class InputQuery extends Component {
|
||||
.map((out, index) => {
|
||||
const input = (
|
||||
<TypedInput
|
||||
accounts={ accountsInfo }
|
||||
allowCopy
|
||||
isEth={ false }
|
||||
param={ out.type }
|
||||
|
||||
@@ -29,7 +29,6 @@ export default class Queries extends Component {
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
accountsInfo: PropTypes.object.isRequired,
|
||||
contract: PropTypes.object,
|
||||
values: PropTypes.object
|
||||
}
|
||||
@@ -94,12 +93,11 @@ export default class Queries extends Component {
|
||||
|
||||
renderInputQuery (fn) {
|
||||
const { abi, name, signature } = fn;
|
||||
const { accountsInfo, contract } = this.props;
|
||||
const { contract } = this.props;
|
||||
|
||||
return (
|
||||
<div className={ styles.container } key={ fn.signature }>
|
||||
<InputQuery
|
||||
accountsInfo={ accountsInfo }
|
||||
className={ styles.method }
|
||||
inputs={ abi.inputs }
|
||||
outputs={ abi.outputs }
|
||||
@@ -144,13 +142,11 @@ export default class Queries extends Component {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { accountsInfo } = this.props;
|
||||
const { name, type } = output;
|
||||
const label = `${name ? `${name}: ` : ''}${type}`;
|
||||
|
||||
return (
|
||||
<TypedInput
|
||||
accounts={ accountsInfo }
|
||||
allowCopy
|
||||
key={ key }
|
||||
isEth={ false }
|
||||
|
||||
@@ -45,7 +45,6 @@ class Contract extends Component {
|
||||
setVisibleAccounts: PropTypes.func.isRequired,
|
||||
|
||||
accounts: PropTypes.object,
|
||||
accountsInfo: PropTypes.object,
|
||||
contracts: PropTypes.object,
|
||||
netVersion: PropTypes.string.isRequired,
|
||||
params: PropTypes.object
|
||||
@@ -128,7 +127,7 @@ class Contract extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { accountsInfo, contracts, netVersion, params } = this.props;
|
||||
const { contracts, netVersion, params } = this.props;
|
||||
const { allEvents, contract, queryValues, loadingEvents } = this.state;
|
||||
const account = contracts[params.address];
|
||||
|
||||
@@ -150,7 +149,6 @@ class Contract extends Component {
|
||||
{ this.renderBlockNumber(account.meta) }
|
||||
</Header>
|
||||
<Queries
|
||||
accountsInfo={ accountsInfo }
|
||||
contract={ contract }
|
||||
values={ queryValues }
|
||||
/>
|
||||
@@ -530,12 +528,11 @@ class Contract extends Component {
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const { accounts, accountsInfo, contracts } = state.personal;
|
||||
const { accounts, contracts } = state.personal;
|
||||
const { netVersion } = state.nodeStatus;
|
||||
|
||||
return {
|
||||
accounts,
|
||||
accountsInfo,
|
||||
contracts,
|
||||
netVersion
|
||||
};
|
||||
|
||||
@@ -28,12 +28,12 @@
|
||||
}
|
||||
|
||||
.signData {
|
||||
background: rgba(255,255,255, 0.25);
|
||||
border: 0.25em solid red;
|
||||
margin-left: 2em;
|
||||
padding: 0.5em;
|
||||
font-size: 0.75em;
|
||||
padding: 0.5rem;
|
||||
overflow: auto;
|
||||
max-height: 6em;
|
||||
max-width: calc(100% - 2em);
|
||||
max-height: 10em;
|
||||
}
|
||||
|
||||
.signData > p {
|
||||
|
||||
@@ -18,7 +18,9 @@ import { observer } from 'mobx-react';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { connect } from 'react-redux';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
|
||||
import { hexToAscii } from '~/api/util/format';
|
||||
import HardwareStore from '~/mobx/hardwareStore';
|
||||
|
||||
import Account from '../Account';
|
||||
@@ -28,16 +30,39 @@ import RequestOrigin from '../RequestOrigin';
|
||||
import styles from './signRequest.css';
|
||||
|
||||
function isAscii (data) {
|
||||
for (var i = 2; i < data.length; i += 2) {
|
||||
for (let i = 2; i < data.length; i += 2) {
|
||||
let n = parseInt(data.substr(i, 2), 16);
|
||||
|
||||
if (n < 32 || n >= 128) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function decodeMarkdown (data) {
|
||||
return decodeURIComponent(escape(hexToAscii(data)));
|
||||
}
|
||||
|
||||
export function isMarkdown (data) {
|
||||
try {
|
||||
const decoded = decodeMarkdown(data);
|
||||
|
||||
for (let i = 0; i < decoded.length; i++) {
|
||||
const code = decoded.charCodeAt(i);
|
||||
|
||||
if (code < 32 && code !== 10) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return decoded.indexOf('#') !== -1 || decoded.indexOf('*') !== -1;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class SignRequest extends Component {
|
||||
static contextTypes = {
|
||||
@@ -89,29 +114,26 @@ class SignRequest extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
renderAsciiDetails (ascii) {
|
||||
return (
|
||||
<div className={ styles.signData }>
|
||||
<p>{ascii}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
renderData (data) {
|
||||
if (isAscii(data)) {
|
||||
return hexToAscii(data);
|
||||
}
|
||||
|
||||
if (isMarkdown(data)) {
|
||||
return (
|
||||
<ReactMarkdown source={ decodeMarkdown(data) } />
|
||||
);
|
||||
}
|
||||
|
||||
renderBinaryDetails (data) {
|
||||
return (
|
||||
<div className={ styles.signData }>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id='signer.signRequest.unknownBinary'
|
||||
defaultMessage='(Unknown binary data)'
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<FormattedMessage
|
||||
id='signer.signRequest.unknownBinary'
|
||||
defaultMessage='(Unknown binary data)'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderDetails () {
|
||||
const { api } = this.context;
|
||||
const { address, data, netVersion, origin, signerStore } = this.props;
|
||||
const { balances, externalLink } = signerStore;
|
||||
|
||||
@@ -121,6 +143,8 @@ class SignRequest extends Component {
|
||||
return <div />;
|
||||
}
|
||||
|
||||
const hashToSign = this.context.api.util.sha3(data);
|
||||
|
||||
return (
|
||||
<div className={ styles.signDetails }>
|
||||
<div className={ styles.address }>
|
||||
@@ -133,18 +157,16 @@ class SignRequest extends Component {
|
||||
/>
|
||||
<RequestOrigin origin={ origin } />
|
||||
</div>
|
||||
<div className={ styles.info } title={ api.util.sha3(data) }>
|
||||
<div className={ styles.info } title={ hashToSign }>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id='signer.signRequest.request'
|
||||
defaultMessage='A request to sign data using your account:'
|
||||
/>
|
||||
</p>
|
||||
{
|
||||
isAscii(data)
|
||||
? this.renderAsciiDetails(api.util.hexToAscii(data))
|
||||
: this.renderBinaryDetails(data)
|
||||
}
|
||||
<div className={ styles.signData }>
|
||||
<p>{ this.renderData(data) }</p>
|
||||
</div>
|
||||
<p>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
|
||||
@@ -18,7 +18,9 @@ import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import SignRequest from './';
|
||||
import { asciiToHex } from '~/api/util/format';
|
||||
|
||||
import SignRequest, { isMarkdown } from './signRequest';
|
||||
|
||||
let component;
|
||||
let reduxStore;
|
||||
@@ -81,4 +83,24 @@ describe('views/Signer/components/SignRequest', () => {
|
||||
it('renders', () => {
|
||||
expect(component).to.be.ok;
|
||||
});
|
||||
|
||||
describe('isMarkdown', () => {
|
||||
it('returns true for markdown', () => {
|
||||
const testMd = '# this is some\n\n*markdown*';
|
||||
const encodedMd = asciiToHex(unescape(encodeURIComponent(testMd)));
|
||||
|
||||
expect(isMarkdown(encodedMd)).to.be.true;
|
||||
});
|
||||
|
||||
it('return true with utf-8 markdown', () => {
|
||||
const testMd = '# header\n\n(n) you are not a citizen of, or resident in, or located in, or incorporated or otherwise established in, the People\'s Republic of China 参与方并非中华人民共和国公民,或不常住中华人民共和国,或不位于中华人民共和国境内,或并非在中华人民共和国设立或以其他方式组建; and';
|
||||
const encodedMd = asciiToHex(unescape(encodeURIComponent(testMd)));
|
||||
|
||||
expect(isMarkdown(encodedMd)).to.be.true;
|
||||
});
|
||||
|
||||
it('returns false for random data', () => {
|
||||
expect(isMarkdown('0x1234')).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ export default class StatusStore {
|
||||
@observable netPort = new BigNumber(0);
|
||||
@observable nodeName = '';
|
||||
@observable rpcSettings = {};
|
||||
|
||||
@observable blockNumber = new BigNumber(0);
|
||||
@observable coinbase = '';
|
||||
@observable extraData = '';
|
||||
@observable gasFloorTarget = new BigNumber(0);
|
||||
@@ -46,9 +46,10 @@ export default class StatusStore {
|
||||
});
|
||||
}
|
||||
|
||||
@action setStatus ({ hashrate }) {
|
||||
@action setStatus ({ hashrate, blockNumber }) {
|
||||
transaction(() => {
|
||||
this.hashrate = hashrate;
|
||||
this.blockNumber = blockNumber;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -108,13 +109,14 @@ export default class StatusStore {
|
||||
|
||||
return Promise
|
||||
.all([
|
||||
this.api.eth.hashrate()
|
||||
this.api.eth.hashrate(),
|
||||
this.api.eth.blockNumber()
|
||||
])
|
||||
.then(([
|
||||
hashrate
|
||||
hashrate, blockNumber
|
||||
]) => {
|
||||
this.setStatus({
|
||||
hashrate
|
||||
hashrate, blockNumber
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
<key>OVERWRITE_PERMISSIONS</key>
|
||||
<false/>
|
||||
<key>VERSION</key>
|
||||
<string>1.7.2</string>
|
||||
<string>1.7.8</string>
|
||||
</dict>
|
||||
<key>UUID</key>
|
||||
<string>2DCD5B81-7BAF-4DA1-9251-6274B089FD36</string>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
!define DESCRIPTION "Fast, light, robust Ethereum implementation"
|
||||
!define VERSIONMAJOR 1
|
||||
!define VERSIONMINOR 7
|
||||
!define VERSIONBUILD 2
|
||||
!define VERSIONBUILD 8
|
||||
!define ARGS "--warp"
|
||||
!define FIRST_START_ARGS "ui --warp --mode=passive"
|
||||
|
||||
@@ -116,7 +116,6 @@ section "install"
|
||||
# Firewall exception rules
|
||||
SimpleFC::AdvAddRule "Parity incoming peers (TCP:30303)" "" 6 1 1 2147483647 1 "$INSTDIR\parity.exe" "" "" "Parity" 30303 "" "" ""
|
||||
SimpleFC::AdvAddRule "Parity outgoing peers (TCP:30303)" "" 6 2 1 2147483647 1 "$INSTDIR\parity.exe" "" "" "Parity" "" 30303 "" ""
|
||||
SimpleFC::AdvAddRule "Parity web queries (TCP:80)" "" 6 2 1 2147483647 1 "$INSTDIR\parity.exe" "" "" "Parity" "" 80 "" ""
|
||||
SimpleFC::AdvAddRule "Parity UDP discovery (UDP:30303)" "" 17 2 1 2147483647 1 "$INSTDIR\parity.exe" "" "" "Parity" "" 30303 "" ""
|
||||
|
||||
# Registry information for add/remove programs
|
||||
|
||||
@@ -23,7 +23,6 @@ use std::cmp::max;
|
||||
use std::str::FromStr;
|
||||
use cli::{Args, ArgsError};
|
||||
use util::{Hashable, H256, U256, Bytes, version_data, Address};
|
||||
use util::journaldb::Algorithm;
|
||||
use util::Colour;
|
||||
use ethsync::{NetworkConfiguration, is_valid_node_url, AllowIP};
|
||||
use ethcore::ethstore::ethkey::{Secret, Public};
|
||||
@@ -37,7 +36,7 @@ use parity_rpc::NetworkSettings;
|
||||
use cache::CacheConfig;
|
||||
use helpers::{to_duration, to_mode, to_block_id, to_u256, to_pending_set, to_price, replace_home, replace_home_and_local,
|
||||
geth_ipc_path, parity_ipc_path, to_bootnodes, to_addresses, to_address, to_gas_limit, to_queue_strategy};
|
||||
use params::{SpecType, ResealPolicy, AccountsConfig, GasPricerConfig, MinerExtras, Pruning, Switch};
|
||||
use params::{SpecType, ResealPolicy, AccountsConfig, GasPricerConfig, MinerExtras};
|
||||
use ethcore_logger::Config as LogConfig;
|
||||
use dir::{self, Directories, default_hypervisor_path, default_local_path, default_data_path};
|
||||
use dapps::Configuration as DappsConfiguration;
|
||||
@@ -123,15 +122,8 @@ impl Configuration {
|
||||
let fat_db = self.args.flag_fat_db.parse()?;
|
||||
let compaction = self.args.flag_db_compaction.parse()?;
|
||||
let wal = !self.args.flag_fast_and_loose;
|
||||
match self.args.flag_warp {
|
||||
// Logging is not initialized yet, so we print directly to stderr
|
||||
Some(true) if fat_db == Switch::On => writeln!(&mut stderr(), "Warning: Warp Sync is disabled because Fat DB is turned on").expect("Error writing to stderr"),
|
||||
Some(true) if tracing == Switch::On => writeln!(&mut stderr(), "Warning: Warp Sync is disabled because tracing is turned on").expect("Error writing to stderr"),
|
||||
Some(true) if pruning == Pruning::Specific(Algorithm::Archive) => writeln!(&mut stderr(), "Warning: Warp Sync is disabled because pruning mode is set to archive").expect("Error writing to stderr"),
|
||||
_ => {},
|
||||
};
|
||||
let public_node = self.args.flag_public_node;
|
||||
let warp_sync = !self.args.flag_no_warp && fat_db != Switch::On && tracing != Switch::On && pruning != Pruning::Specific(Algorithm::Archive);
|
||||
let warp_sync = !self.args.flag_no_warp;
|
||||
let geth_compatibility = self.args.flag_geth;
|
||||
let mut dapps_conf = self.dapps_config();
|
||||
let ipfs_conf = self.ipfs_config();
|
||||
|
||||
@@ -88,6 +88,7 @@ pub struct SyncInfo {
|
||||
last_imported_old_block_number: Option<BlockNumber>,
|
||||
num_peers: usize,
|
||||
max_peers: u32,
|
||||
snapshot_sync: bool,
|
||||
}
|
||||
|
||||
pub struct Report {
|
||||
@@ -150,6 +151,7 @@ impl InformantData for FullNodeInformantData {
|
||||
last_imported_old_block_number: status.last_imported_old_block_number,
|
||||
num_peers: status.num_peers,
|
||||
max_peers: status.current_max_peers(net_config.min_peers, net_config.max_peers),
|
||||
snapshot_sync: status.is_snapshot_syncing(),
|
||||
}))
|
||||
}
|
||||
_ => (is_major_importing(self.sync.as_ref().map(|s| s.status().state), queue_info.clone()), None),
|
||||
@@ -194,6 +196,7 @@ impl InformantData for LightNodeInformantData {
|
||||
last_imported_old_block_number: None,
|
||||
num_peers: peer_numbers.connected,
|
||||
max_peers: peer_numbers.max as u32,
|
||||
snapshot_sync: false,
|
||||
});
|
||||
|
||||
Report {
|
||||
@@ -280,7 +283,7 @@ impl<T: InformantData> Informant<T> {
|
||||
_ => (false, 0, 0),
|
||||
}
|
||||
);
|
||||
|
||||
let snapshot_sync = snapshot_sync && sync_info.as_ref().map_or(false, |s| s.snapshot_sync);
|
||||
if !importing && !snapshot_sync && elapsed < Duration::from_secs(30) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ use parity_reactor::EventLoop;
|
||||
use parity_rpc::{NetworkSettings, informant, is_major_importing};
|
||||
use updater::{UpdatePolicy, Updater};
|
||||
use util::{Colour, version, Mutex, Condvar};
|
||||
use util::journaldb::Algorithm;
|
||||
|
||||
use params::{
|
||||
SpecType, Pruning, AccountsConfig, GasPricerConfig, MinerExtras, Switch,
|
||||
@@ -474,7 +475,21 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
|
||||
}
|
||||
|
||||
sync_config.fork_block = spec.fork_block();
|
||||
sync_config.warp_sync = spec.engine.supports_warp() && cmd.warp_sync;
|
||||
let mut warp_sync = cmd.warp_sync;
|
||||
if warp_sync {
|
||||
// Logging is not initialized yet, so we print directly to stderr
|
||||
if fat_db {
|
||||
warn!("Warning: Warp Sync is disabled because Fat DB is turned on.");
|
||||
warp_sync = false;
|
||||
} else if tracing {
|
||||
warn!("Warning: Warp Sync is disabled because tracing is turned on.");
|
||||
warp_sync = false;
|
||||
} else if algorithm != Algorithm::OverlayRecent {
|
||||
warn!("Warning: Warp Sync is disabled because of non-default pruning mode.");
|
||||
warp_sync = false;
|
||||
}
|
||||
}
|
||||
sync_config.warp_sync = spec.engine.supports_warp() && warp_sync;
|
||||
sync_config.download_old_blocks = cmd.download_old_blocks;
|
||||
sync_config.serve_light = cmd.serve_light;
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@ impl MinerService for TestMinerService {
|
||||
contract_address: None,
|
||||
logs: r.logs.clone(),
|
||||
log_bloom: r.log_bloom,
|
||||
state_root: r.state_root,
|
||||
outcome: r.outcome.clone(),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ use ethkey::Secret;
|
||||
use ethcore::account_provider::AccountProvider;
|
||||
use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionId};
|
||||
use ethcore::log_entry::{LocalizedLogEntry, LogEntry};
|
||||
use ethcore::receipt::LocalizedReceipt;
|
||||
use ethcore::receipt::{LocalizedReceipt, TransactionOutcome};
|
||||
use ethcore::transaction::{Transaction, Action};
|
||||
use ethcore::miner::{ExternalMiner, MinerService};
|
||||
use ethsync::SyncState;
|
||||
@@ -1006,7 +1006,7 @@ fn rpc_eth_transaction_receipt() {
|
||||
log_index: 1,
|
||||
}],
|
||||
log_bloom: 0.into(),
|
||||
state_root: Some(0.into()),
|
||||
outcome: TransactionOutcome::StateRoot(0.into()),
|
||||
};
|
||||
|
||||
let hash = H256::from_str("b903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238").unwrap();
|
||||
@@ -1019,7 +1019,7 @@ fn rpc_eth_transaction_receipt() {
|
||||
"params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"],
|
||||
"id": 1
|
||||
}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","contractAddress":null,"cumulativeGasUsed":"0x20","gasUsed":"0x10","logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","data":"0x","logIndex":"0x1","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"}],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","root":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0"},"id":1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","contractAddress":null,"cumulativeGasUsed":"0x20","gasUsed":"0x10","logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","data":"0x","logIndex":"0x1","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"}],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","root":"0x0000000000000000000000000000000000000000000000000000000000000000","status":null,"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0"},"id":1}"#;
|
||||
|
||||
assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned()));
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use v1::types::{Log, H160, H256, H2048, U256};
|
||||
use ethcore::receipt::{Receipt as EthReceipt, RichReceipt, LocalizedReceipt};
|
||||
use ethcore::receipt::{Receipt as EthReceipt, RichReceipt, LocalizedReceipt, TransactionOutcome};
|
||||
|
||||
/// Receipt
|
||||
#[derive(Debug, Serialize)]
|
||||
@@ -49,6 +49,25 @@ pub struct Receipt {
|
||||
/// Logs bloom
|
||||
#[serde(rename="logsBloom")]
|
||||
pub logs_bloom: H2048,
|
||||
/// Status code
|
||||
#[serde(rename="status")]
|
||||
pub status_code: Option<u8>,
|
||||
}
|
||||
|
||||
impl Receipt {
|
||||
fn outcome_to_state_root(outcome: TransactionOutcome) -> Option<H256> {
|
||||
match outcome {
|
||||
TransactionOutcome::Unknown | TransactionOutcome::StatusCode(_) => None,
|
||||
TransactionOutcome::StateRoot(root) => Some(root.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn outcome_to_status_code(outcome: &TransactionOutcome) -> Option<u8> {
|
||||
match *outcome {
|
||||
TransactionOutcome::Unknown | TransactionOutcome::StateRoot(_) => None,
|
||||
TransactionOutcome::StatusCode(ref code) => Some(*code),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LocalizedReceipt> for Receipt {
|
||||
@@ -62,7 +81,8 @@ impl From<LocalizedReceipt> for Receipt {
|
||||
gas_used: Some(r.gas_used.into()),
|
||||
contract_address: r.contract_address.map(Into::into),
|
||||
logs: r.logs.into_iter().map(Into::into).collect(),
|
||||
state_root: r.state_root.map(Into::into),
|
||||
status_code: Self::outcome_to_status_code(&r.outcome),
|
||||
state_root: Self::outcome_to_state_root(r.outcome),
|
||||
logs_bloom: r.log_bloom.into(),
|
||||
}
|
||||
}
|
||||
@@ -79,7 +99,8 @@ impl From<RichReceipt> for Receipt {
|
||||
gas_used: Some(r.gas_used.into()),
|
||||
contract_address: r.contract_address.map(Into::into),
|
||||
logs: r.logs.into_iter().map(Into::into).collect(),
|
||||
state_root: r.state_root.map(Into::into),
|
||||
status_code: Self::outcome_to_status_code(&r.outcome),
|
||||
state_root: Self::outcome_to_state_root(r.outcome),
|
||||
logs_bloom: r.log_bloom.into(),
|
||||
}
|
||||
}
|
||||
@@ -96,7 +117,8 @@ impl From<EthReceipt> for Receipt {
|
||||
gas_used: None,
|
||||
contract_address: None,
|
||||
logs: r.logs.into_iter().map(Into::into).collect(),
|
||||
state_root: r.state_root.map(Into::into),
|
||||
status_code: Self::outcome_to_status_code(&r.outcome),
|
||||
state_root: Self::outcome_to_state_root(r.outcome),
|
||||
logs_bloom: r.log_bloom.into(),
|
||||
}
|
||||
}
|
||||
@@ -109,7 +131,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn receipt_serialization() {
|
||||
let s = r#"{"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","cumulativeGasUsed":"0x20","gasUsed":"0x10","contractAddress":null,"logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","logIndex":"0x1","transactionLogIndex":null,"type":"mined"}],"root":"0x000000000000000000000000000000000000000000000000000000000000000a","logsBloom":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f"}"#;
|
||||
let s = r#"{"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","cumulativeGasUsed":"0x20","gasUsed":"0x10","contractAddress":null,"logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","logIndex":"0x1","transactionLogIndex":null,"type":"mined"}],"root":"0x000000000000000000000000000000000000000000000000000000000000000a","logsBloom":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f","status":null}"#;
|
||||
|
||||
let receipt = Receipt {
|
||||
transaction_hash: Some(0.into()),
|
||||
@@ -136,6 +158,7 @@ mod tests {
|
||||
}],
|
||||
logs_bloom: 15.into(),
|
||||
state_root: Some(10.into()),
|
||||
status_code: None,
|
||||
};
|
||||
|
||||
let serialized = serde_json::to_string(&receipt).unwrap();
|
||||
|
||||
@@ -3,8 +3,18 @@ Description=Parity Daemon
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
EnvironmentFile=-%h/.parity/parity.conf
|
||||
ExecStart=/usr/bin/parity $ARGS
|
||||
# run as root, set base_path in config.toml
|
||||
ExecStart=/usr/bin/parity --config /etc/parity/config.toml
|
||||
# To run as user, comment out above and uncomment below, fill in user and group
|
||||
# picks up users default config.toml in $HOME/.local/.share/io.parity.ethereum/
|
||||
# User=username
|
||||
# Group=groupname
|
||||
# ExecStart=/usr/bin/parity
|
||||
Restart=on-failure
|
||||
|
||||
# Specifies which signal to use when killing a service. Defaults to SIGTERM.
|
||||
# SIGHUP gives parity time to exit cleanly before SIGKILL (default 90s)
|
||||
KillSignal=SIGHUP
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
|
||||
@@ -19,4 +19,5 @@ export TARGETS="
|
||||
-p ethcore-ipc-tests \
|
||||
-p ethcore-ipc-nano \
|
||||
-p ethcore-light \
|
||||
-p evm \
|
||||
-p parity"
|
||||
|
||||
@@ -29,7 +29,7 @@ use sync_io::SyncIo;
|
||||
use blocks::BlockCollection;
|
||||
|
||||
const MAX_HEADERS_TO_REQUEST: usize = 128;
|
||||
const MAX_BODIES_TO_REQUEST: usize = 64;
|
||||
const MAX_BODIES_TO_REQUEST: usize = 32;
|
||||
const MAX_RECEPITS_TO_REQUEST: usize = 128;
|
||||
const SUBCHAIN_SIZE: u64 = 256;
|
||||
const MAX_ROUND_PARENTS: usize = 16;
|
||||
|
||||
@@ -163,7 +163,7 @@ const MAX_SNAPSHOT_CHUNKS_DOWNLOAD_AHEAD: usize = 3;
|
||||
const WAIT_PEERS_TIMEOUT_SEC: u64 = 5;
|
||||
const STATUS_TIMEOUT_SEC: u64 = 5;
|
||||
const HEADERS_TIMEOUT_SEC: u64 = 15;
|
||||
const BODIES_TIMEOUT_SEC: u64 = 10;
|
||||
const BODIES_TIMEOUT_SEC: u64 = 20;
|
||||
const RECEIPTS_TIMEOUT_SEC: u64 = 10;
|
||||
const FORK_HEADER_TIMEOUT_SEC: u64 = 3;
|
||||
const SNAPSHOT_MANIFEST_TIMEOUT_SEC: u64 = 5;
|
||||
@@ -688,7 +688,7 @@ impl ChainSync {
|
||||
}
|
||||
} else {
|
||||
trace!(target: "sync", "{}: Fork mismatch", peer_id);
|
||||
io.disconnect_peer(peer_id);
|
||||
io.disable_peer(peer_id);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
@@ -1146,7 +1146,7 @@ impl ChainSync {
|
||||
trace!(target: "sync", "== Connected {}: {}", peer, io.peer_info(peer));
|
||||
if let Err(e) = self.send_status(io, peer) {
|
||||
debug!(target:"sync", "Error sending status request: {:?}", e);
|
||||
io.disable_peer(peer);
|
||||
io.disconnect_peer(peer);
|
||||
} else {
|
||||
self.handshaking_peers.insert(peer, time::precise_time_ns());
|
||||
}
|
||||
@@ -1447,7 +1447,7 @@ impl ChainSync {
|
||||
};
|
||||
if let Err(e) = result {
|
||||
debug!(target:"sync", "Error sending request: {:?}", e);
|
||||
sync.disable_peer(peer_id);
|
||||
sync.disconnect_peer(peer_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1456,7 +1456,7 @@ impl ChainSync {
|
||||
fn send_packet(&mut self, sync: &mut SyncIo, peer_id: PeerId, packet_id: PacketId, packet: Bytes) {
|
||||
if let Err(e) = sync.send(peer_id, packet_id, packet) {
|
||||
debug!(target:"sync", "Error sending packet: {:?}", e);
|
||||
sync.disable_peer(peer_id);
|
||||
sync.disconnect_peer(peer_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ description = "Ethcore utility library"
|
||||
homepage = "http://parity.io"
|
||||
license = "GPL-3.0"
|
||||
name = "ethcore-util"
|
||||
version = "1.7.2"
|
||||
version = "1.7.8"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
|
||||
@@ -511,6 +511,7 @@ impl Session {
|
||||
i += 1;
|
||||
}
|
||||
debug!(target: "network", "Hello: {} v{} {} {:?}", client_version, protocol, id, caps);
|
||||
let protocol = ::std::cmp::min(protocol, host.protocol_version);
|
||||
self.info.protocol_version = protocol;
|
||||
self.info.client_version = client_version;
|
||||
self.info.capabilities = caps;
|
||||
|
||||
@@ -24,7 +24,7 @@ include!(concat!(env!("OUT_DIR"), "/version.rs"));
|
||||
include!(concat!(env!("OUT_DIR"), "/rustc_version.rs"));
|
||||
|
||||
#[cfg(feature = "final")]
|
||||
const THIS_TRACK: &'static str = "beta";
|
||||
const THIS_TRACK: &'static str = "stable";
|
||||
// ^^^ should be reset to "stable" or "beta" according to the release branch.
|
||||
|
||||
#[cfg(not(feature = "final"))]
|
||||
|
||||
Reference in New Issue
Block a user