Merge branch 'master' into tracing
This commit is contained in:
commit
610251fdf7
170
Cargo.lock
generated
170
Cargo.lock
generated
@ -11,6 +11,7 @@ dependencies = [
|
||||
"ethcore-devtools 1.1.0",
|
||||
"ethcore-rpc 1.1.0",
|
||||
"ethcore-util 1.1.0",
|
||||
"ethcore-webapp 1.1.0",
|
||||
"ethminer 1.1.0",
|
||||
"ethsync 1.1.0",
|
||||
"fdlimit 0.1.0",
|
||||
@ -107,6 +108,14 @@ dependencies = [
|
||||
"unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "conduit-mime-types"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
version = "0.1.21"
|
||||
@ -185,6 +194,15 @@ dependencies = [
|
||||
"regex 0.1.61 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "error"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eth-secp256k1"
|
||||
version = "0.5.4"
|
||||
@ -246,7 +264,7 @@ dependencies = [
|
||||
"ethminer 1.1.0",
|
||||
"ethsync 1.1.0",
|
||||
"jsonrpc-core 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-http-server 3.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-http-server 5.0.0 (git+https://github.com/debris/jsonrpc-http-server.git)",
|
||||
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -291,6 +309,23 @@ dependencies = [
|
||||
"vergen 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-webapp"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"clippy 0.0.61 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-rpc 1.1.0",
|
||||
"ethcore-util 1.1.0",
|
||||
"hyper 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"iron 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-http-server 4.0.0 (git+https://github.com/tomusdrw/jsonrpc-http-server.git?branch=old-hyper)",
|
||||
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-status 0.1.5 (git+https://github.com/tomusdrw/parity-status.git)",
|
||||
"parity-wallet 0.1.0 (git+https://github.com/tomusdrw/parity-wallet.git)",
|
||||
"parity-webapp 0.1.0 (git+https://github.com/tomusdrw/parity-webapp.git)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ethjson"
|
||||
version = "0.1.0"
|
||||
@ -415,6 +450,27 @@ dependencies = [
|
||||
"url 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.9.0-mio"
|
||||
source = "git+https://github.com/hyperium/hyper?branch=mio#d55a70dc56dac1f0f03bc4c3a83db0314d48e69a"
|
||||
dependencies = [
|
||||
"cookie 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rotor 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vecio 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "igd"
|
||||
version = "0.4.2"
|
||||
@ -427,6 +483,23 @@ dependencies = [
|
||||
"xmltree 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iron"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"conduit-mime-types 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"error 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"modifier 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.4.11"
|
||||
@ -453,14 +526,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jsonrpc-http-server"
|
||||
version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
version = "4.0.0"
|
||||
source = "git+https://github.com/tomusdrw/jsonrpc-http-server.git?branch=old-hyper#46bd4e7cf8352e0efc940cf76d3dff99f1a3da15"
|
||||
dependencies = [
|
||||
"hyper 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonrpc-http-server"
|
||||
version = "5.0.0"
|
||||
source = "git+https://github.com/debris/jsonrpc-http-server.git#76fa443982b40665721fe6b1ece42fc0a53be996"
|
||||
dependencies = [
|
||||
"hyper 0.9.0-mio (git+https://github.com/hyperium/hyper?branch=mio)",
|
||||
"jsonrpc-core 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.1"
|
||||
@ -573,6 +656,11 @@ dependencies = [
|
||||
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "modifier"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "net2"
|
||||
version = "0.2.23"
|
||||
@ -637,6 +725,35 @@ name = "odds"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "parity-status"
|
||||
version = "0.1.5"
|
||||
source = "git+https://github.com/tomusdrw/parity-status.git#6a075228e9248055a37c55dec41461856f5a9f19"
|
||||
dependencies = [
|
||||
"parity-webapp 0.1.0 (git+https://github.com/tomusdrw/parity-webapp.git)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parity-wallet"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/tomusdrw/parity-wallet.git#9b0253f5cb88b31417450ca8be708cab2e437dfc"
|
||||
dependencies = [
|
||||
"parity-webapp 0.1.0 (git+https://github.com/tomusdrw/parity-webapp.git)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parity-webapp"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/tomusdrw/parity-webapp.git#a24297256bae0ae0712c6478cd1ad681828b3800"
|
||||
|
||||
[[package]]
|
||||
name = "plugin"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "primal"
|
||||
version = "0.2.3"
|
||||
@ -696,6 +813,11 @@ dependencies = [
|
||||
"syntex_syntax 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "quine-mc_cluskey"
|
||||
version = "0.2.2"
|
||||
@ -745,6 +867,18 @@ dependencies = [
|
||||
"librocksdb-sys 0.2.3 (git+https://github.com/arkpar/rust-rocksdb.git)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rotor"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quick-error 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"void 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rpassword"
|
||||
version = "0.1.3"
|
||||
@ -938,6 +1072,14 @@ name = "typeable"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "typemap"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unsafe-any 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "1.4.0"
|
||||
@ -964,6 +1106,14 @@ name = "unicode-xid"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-any"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "0.2.38"
|
||||
@ -1000,6 +1150,15 @@ dependencies = [
|
||||
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vecio"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vergen"
|
||||
version = "0.1.0"
|
||||
@ -1009,6 +1168,11 @@ dependencies = [
|
||||
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.6"
|
||||
|
@ -28,15 +28,19 @@ ethsync = { path = "sync" }
|
||||
ethminer = { path = "miner" }
|
||||
ethcore-devtools = { path = "devtools" }
|
||||
ethcore-rpc = { path = "rpc", optional = true }
|
||||
ethcore-webapp = { path = "webapp", optional = true }
|
||||
|
||||
|
||||
[dependencies.hyper]
|
||||
version = "0.8"
|
||||
default-features = false
|
||||
|
||||
[features]
|
||||
default = ["rpc"]
|
||||
default = ["rpc", "webapp"]
|
||||
rpc = ["ethcore-rpc"]
|
||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethminer/dev"]
|
||||
webapp = ["ethcore-webapp"]
|
||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethminer/dev",
|
||||
"ethcore-webapp/dev"]
|
||||
travis-beta = ["ethcore/json-tests"]
|
||||
travis-nightly = ["ethcore/json-tests", "dev"]
|
||||
|
||||
|
2
cov.sh
2
cov.sh
@ -23,6 +23,7 @@ cargo test \
|
||||
-p ethcore-rpc \
|
||||
-p parity \
|
||||
-p ethminer \
|
||||
-p ethcore-webapp \
|
||||
--no-run || exit $?
|
||||
rm -rf target/coverage
|
||||
mkdir -p target/coverage
|
||||
@ -33,5 +34,6 @@ kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage t
|
||||
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_util-*
|
||||
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethsync-*
|
||||
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_rpc-*
|
||||
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_webapp-*
|
||||
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethminer-*
|
||||
xdg-open target/coverage/index.html
|
||||
|
1
doc.sh
1
doc.sh
@ -7,5 +7,6 @@ cargo doc --no-deps --verbose \
|
||||
-p ethcore \
|
||||
-p ethsync \
|
||||
-p ethcore-rpc \
|
||||
-p ethcore-webapp \
|
||||
-p parity \
|
||||
-p ethminer
|
||||
|
@ -1,24 +1,33 @@
|
||||
{
|
||||
"name": "Frontier/Homestead",
|
||||
"engineName": "Ethash",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"tieBreakingGas": false,
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"tieBreakingGas": false,
|
||||
"minGasLimit": "0x1388",
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"networkID" : "0x1"
|
||||
},
|
||||
"genesis": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x400000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
@ -1,24 +1,33 @@
|
||||
{
|
||||
"name": "Frontier (Test)",
|
||||
"engineName": "Ethash",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"tieBreakingGas": false,
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"tieBreakingGas": false,
|
||||
"minGasLimit": "0x1388",
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"networkID" : "0x1"
|
||||
},
|
||||
"genesis": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x400000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
@ -1,24 +1,33 @@
|
||||
{
|
||||
"name": "Frontier (Test)",
|
||||
"engineName": "Ethash",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"tieBreakingGas": false,
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"tieBreakingGas": false,
|
||||
"minGasLimit": "0x1388",
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"networkID" : "0x1"
|
||||
},
|
||||
"genesis": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x400000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
@ -1,24 +1,33 @@
|
||||
{
|
||||
"name": "Homestead (Test)",
|
||||
"engineName": "Ethash",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"tieBreakingGas": false,
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"frontierCompatibilityModeLimit": 0,
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"tieBreakingGas": false,
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"networkID" : "0x1"
|
||||
},
|
||||
"genesis": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x400000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
@ -1,24 +1,33 @@
|
||||
{
|
||||
"name": "Morden",
|
||||
"engineName": "Ethash",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"tieBreakingGas": false,
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x0100000",
|
||||
"frontierCompatibilityModeLimit": "0x789b0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"tieBreakingGas": false,
|
||||
"minGasLimit": "0x1388",
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar": "",
|
||||
"networkID" : "0x2"
|
||||
},
|
||||
"genesis": {
|
||||
"nonce": "0x00006d6f7264656e",
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x00006d6f7264656e",
|
||||
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x20000",
|
||||
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
@ -1,24 +1,33 @@
|
||||
{
|
||||
"name": "Olympic",
|
||||
"engineName": "Ethash",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"tieBreakingGas": false,
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x08",
|
||||
"blockReward": "0x14D1120D7B160000",
|
||||
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff",
|
||||
"maximumExtraDataSize": "0x0400",
|
||||
"tieBreakingGas": false,
|
||||
"minGasLimit": "125000",
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x08",
|
||||
"blockReward": "0x14D1120D7B160000",
|
||||
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050",
|
||||
"networkID" : "0x0"
|
||||
},
|
||||
"genesis": {
|
||||
"nonce": "0x000000000000002a",
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x000000000000002a",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x20000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
@ -1,24 +1,23 @@
|
||||
{
|
||||
"name": "Morden",
|
||||
"engineName": "NullEngine",
|
||||
"engine": {
|
||||
"Null": null
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x0100000",
|
||||
"frontierCompatibilityModeLimit": "0x0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"tieBreakingGas": false,
|
||||
"minGasLimit": "0x1388",
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar": "",
|
||||
"networkID" : "0x2"
|
||||
},
|
||||
"genesis": {
|
||||
"nonce": "0x00006d6f7264656e",
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x00006d6f7264656e",
|
||||
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x20000",
|
||||
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
@ -1,24 +1,23 @@
|
||||
{
|
||||
"name": "Morden",
|
||||
"engineName": "NullEngine",
|
||||
"engine": {
|
||||
"Null": null
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x0100000",
|
||||
"frontierCompatibilityModeLimit": "0x789b0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"tieBreakingGas": false,
|
||||
"minGasLimit": "0x1388",
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar": "",
|
||||
"networkID" : "0x2"
|
||||
},
|
||||
"genesis": {
|
||||
"nonce": "0x00006d6f7264656e",
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x00006d6f7264656e",
|
||||
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x20000",
|
||||
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
@ -138,7 +138,7 @@ impl Account {
|
||||
/// get someone who knows to call `note_code`.
|
||||
pub fn code(&self) -> Option<&[u8]> {
|
||||
match self.code_hash {
|
||||
Some(c) if c == SHA3_EMPTY && self.code_cache.is_empty() => Some(&self.code_cache),
|
||||
Some(c) if c == SHA3_EMPTY && self.code_cache.is_empty() => Some(&self.code_cache),
|
||||
Some(_) if !self.code_cache.is_empty() => Some(&self.code_cache),
|
||||
None => Some(&self.code_cache),
|
||||
_ => None,
|
||||
|
@ -497,16 +497,16 @@ mod tests {
|
||||
use tests::helpers::*;
|
||||
use super::*;
|
||||
use common::*;
|
||||
use engine::*;
|
||||
|
||||
#[test]
|
||||
fn open_block() {
|
||||
use spec::*;
|
||||
let engine = Spec::new_test().to_engine().unwrap();
|
||||
let genesis_header = engine.spec().genesis_header();
|
||||
let spec = Spec::new_test();
|
||||
let engine = &spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db = db_result.take();
|
||||
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let last_hashes = vec![genesis_header.hash()];
|
||||
let b = OpenBlock::new(engine.deref(), false, db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]);
|
||||
let b = b.close_and_lock();
|
||||
@ -516,19 +516,20 @@ mod tests {
|
||||
#[test]
|
||||
fn enact_block() {
|
||||
use spec::*;
|
||||
let engine = Spec::new_test().to_engine().unwrap();
|
||||
let genesis_header = engine.spec().genesis_header();
|
||||
let spec = Spec::new_test();
|
||||
let engine = &spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db = db_result.take();
|
||||
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let b = OpenBlock::new(engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), x!(3141562), vec![]).close_and_lock().seal(engine.deref(), vec![]).unwrap();
|
||||
let orig_bytes = b.rlp_bytes();
|
||||
let orig_db = b.drain();
|
||||
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db = db_result.take();
|
||||
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()]).unwrap();
|
||||
|
||||
assert_eq!(e.rlp_bytes(), orig_bytes);
|
||||
@ -541,12 +542,13 @@ mod tests {
|
||||
#[test]
|
||||
fn enact_block_with_uncle() {
|
||||
use spec::*;
|
||||
let engine = Spec::new_test().to_engine().unwrap();
|
||||
let genesis_header = engine.spec().genesis_header();
|
||||
let spec = Spec::new_test();
|
||||
let engine = &spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db = db_result.take();
|
||||
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let mut open_block = OpenBlock::new(engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), x!(3141562), vec![]);
|
||||
let mut uncle1_header = Header::new();
|
||||
uncle1_header.extra_data = b"uncle1".to_vec();
|
||||
@ -561,7 +563,7 @@ mod tests {
|
||||
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db = db_result.take();
|
||||
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()]).unwrap();
|
||||
|
||||
let bytes = e.rlp_bytes();
|
||||
|
@ -116,6 +116,7 @@ struct VerifyingBlock {
|
||||
}
|
||||
|
||||
struct QueueSignal {
|
||||
deleting: Arc<AtomicBool>,
|
||||
signalled: AtomicBool,
|
||||
message_channel: IoChannel<NetSyncMessage>,
|
||||
}
|
||||
@ -123,10 +124,16 @@ struct QueueSignal {
|
||||
impl QueueSignal {
|
||||
#[cfg_attr(feature="dev", allow(bool_comparison))]
|
||||
fn set(&self) {
|
||||
// Do not signal when we are about to close
|
||||
if self.deleting.load(AtomicOrdering::Relaxed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.signalled.compare_and_swap(false, true, AtomicOrdering::Relaxed) == false {
|
||||
self.message_channel.send(UserMessage(SyncMessage::BlockVerified)).expect("Error sending BlockVerified message");
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&self) {
|
||||
self.signalled.store(false, AtomicOrdering::Relaxed);
|
||||
}
|
||||
@ -150,8 +157,12 @@ impl BlockQueue {
|
||||
bad: Mutex::new(HashSet::new()),
|
||||
});
|
||||
let more_to_verify = Arc::new(Condvar::new());
|
||||
let ready_signal = Arc::new(QueueSignal { signalled: AtomicBool::new(false), message_channel: message_channel });
|
||||
let deleting = Arc::new(AtomicBool::new(false));
|
||||
let ready_signal = Arc::new(QueueSignal {
|
||||
deleting: deleting.clone(),
|
||||
signalled: AtomicBool::new(false),
|
||||
message_channel: message_channel
|
||||
});
|
||||
let empty = Arc::new(Condvar::new());
|
||||
let panic_handler = PanicHandler::new_in_arc();
|
||||
|
||||
@ -431,12 +442,14 @@ impl MayPanic for BlockQueue {
|
||||
|
||||
impl Drop for BlockQueue {
|
||||
fn drop(&mut self) {
|
||||
trace!(target: "shutdown", "[BlockQueue] Closing...");
|
||||
self.clear();
|
||||
self.deleting.store(true, AtomicOrdering::Release);
|
||||
self.more_to_verify.notify_all();
|
||||
for t in self.verifiers.drain(..) {
|
||||
t.join().unwrap();
|
||||
}
|
||||
trace!(target: "shutdown", "[BlockQueue] Closed.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -451,7 +464,7 @@ mod tests {
|
||||
|
||||
fn get_test_queue() -> BlockQueue {
|
||||
let spec = get_test_spec();
|
||||
let engine = spec.to_engine().unwrap();
|
||||
let engine = spec.engine;
|
||||
BlockQueue::new(BlockQueueConfig::default(), Arc::new(engine), IoChannel::disconnected())
|
||||
}
|
||||
|
||||
@ -459,7 +472,7 @@ mod tests {
|
||||
fn can_be_created() {
|
||||
// TODO better test
|
||||
let spec = Spec::new_test();
|
||||
let engine = spec.to_engine().unwrap();
|
||||
let engine = spec.engine;
|
||||
let _ = BlockQueue::new(BlockQueueConfig::default(), Arc::new(engine), IoChannel::disconnected());
|
||||
}
|
||||
|
||||
@ -520,7 +533,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_mem_limit() {
|
||||
let spec = get_test_spec();
|
||||
let engine = spec.to_engine().unwrap();
|
||||
let engine = spec.engine;
|
||||
let mut config = BlockQueueConfig::default();
|
||||
config.max_mem_use = super::MIN_MEM_LIMIT; // empty queue uses about 15000
|
||||
let queue = BlockQueue::new(config, Arc::new(engine), IoChannel::disconnected());
|
||||
|
@ -18,6 +18,7 @@ use util::*;
|
||||
use crypto::sha2::Sha256;
|
||||
use crypto::ripemd160::Ripemd160;
|
||||
use crypto::digest::Digest;
|
||||
use ethjson;
|
||||
|
||||
/// Definition of a contract whose implementation is built-in.
|
||||
pub struct Builtin {
|
||||
@ -46,13 +47,12 @@ impl Builtin {
|
||||
}
|
||||
|
||||
/// Create a new object from a builtin-function name with a linear cost associated with input size.
|
||||
pub fn from_named_linear(name: &str, base_cost: usize, word_cost: usize) -> Option<Builtin> {
|
||||
new_builtin_exec(name).map(|b| {
|
||||
let cost = Box::new(move|s: usize| -> U256 {
|
||||
U256::from(base_cost) + U256::from(word_cost) * U256::from((s + 31) / 32)
|
||||
});
|
||||
Self::new(cost, b)
|
||||
})
|
||||
pub fn from_named_linear(name: &str, base_cost: usize, word_cost: usize) -> Builtin {
|
||||
let cost = Box::new(move|s: usize| -> U256 {
|
||||
U256::from(base_cost) + U256::from(word_cost) * U256::from((s + 31) / 32)
|
||||
});
|
||||
|
||||
Self::new(cost, new_builtin_exec(name))
|
||||
}
|
||||
|
||||
/// Simple forwarder for cost.
|
||||
@ -60,24 +60,15 @@ impl Builtin {
|
||||
|
||||
/// Simple forwarder for execute.
|
||||
pub fn execute(&self, input: &[u8], output: &mut[u8]) { (*self.execute)(input, output); }
|
||||
}
|
||||
|
||||
/// Create a builtin from JSON.
|
||||
///
|
||||
/// JSON must be of the form `{ "name": "identity", "pricing": {"base": 10, "word": 20} }`.
|
||||
pub fn from_json(json: &Json) -> Option<Builtin> {
|
||||
// NICE: figure out a more convenient means of handing errors here.
|
||||
if let Json::String(ref name) = json["name"] {
|
||||
if let Json::Object(ref o) = json["pricing"] {
|
||||
if let Json::Object(ref o) = o["linear"] {
|
||||
if let Json::U64(ref word) = o["word"] {
|
||||
if let Json::U64(ref base) = o["base"] {
|
||||
return Self::from_named_linear(&name[..], *base as usize, *word as usize);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<ethjson::spec::Builtin> for Builtin {
|
||||
fn from(b: ethjson::spec::Builtin) -> Self {
|
||||
match b.pricing {
|
||||
ethjson::spec::Pricing::Linear(linear) => {
|
||||
Self::from_named_linear(b.name.as_ref(), linear.base, linear.word)
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,14 +83,14 @@ pub fn copy_to(src: &[u8], dest: &mut[u8]) {
|
||||
|
||||
/// Create a new builtin executor according to `name`.
|
||||
/// TODO: turn in to a factory with dynamic registration.
|
||||
pub fn new_builtin_exec(name: &str) -> Option<Box<Fn(&[u8], &mut [u8])>> {
|
||||
pub fn new_builtin_exec(name: &str) -> Box<Fn(&[u8], &mut [u8])> {
|
||||
match name {
|
||||
"identity" => Some(Box::new(move|input: &[u8], output: &mut[u8]| {
|
||||
"identity" => Box::new(move|input: &[u8], output: &mut[u8]| {
|
||||
for i in 0..min(input.len(), output.len()) {
|
||||
output[i] = input[i];
|
||||
}
|
||||
})),
|
||||
"ecrecover" => Some(Box::new(move|input: &[u8], output: &mut[u8]| {
|
||||
}),
|
||||
"ecrecover" => Box::new(move|input: &[u8], output: &mut[u8]| {
|
||||
#[repr(packed)]
|
||||
#[derive(Debug)]
|
||||
struct InType {
|
||||
@ -122,8 +113,8 @@ pub fn new_builtin_exec(name: &str) -> Option<Box<Fn(&[u8], &mut [u8])>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
})),
|
||||
"sha256" => Some(Box::new(move|input: &[u8], output: &mut[u8]| {
|
||||
}),
|
||||
"sha256" => Box::new(move|input: &[u8], output: &mut[u8]| {
|
||||
let mut sha = Sha256::new();
|
||||
sha.input(input);
|
||||
if output.len() >= 32 {
|
||||
@ -133,21 +124,23 @@ pub fn new_builtin_exec(name: &str) -> Option<Box<Fn(&[u8], &mut [u8])>> {
|
||||
sha.result(ret.as_slice_mut());
|
||||
copy_to(&ret, output);
|
||||
}
|
||||
})),
|
||||
"ripemd160" => Some(Box::new(move|input: &[u8], output: &mut[u8]| {
|
||||
}),
|
||||
"ripemd160" => Box::new(move|input: &[u8], output: &mut[u8]| {
|
||||
let mut sha = Ripemd160::new();
|
||||
sha.input(input);
|
||||
let mut ret = H256::new();
|
||||
sha.result(&mut ret.as_slice_mut()[12..32]);
|
||||
copy_to(&ret, output);
|
||||
})),
|
||||
_ => None
|
||||
}),
|
||||
_ => {
|
||||
panic!("invalid builtin name {}", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn identity() {
|
||||
let f = new_builtin_exec("identity").unwrap();
|
||||
let f = new_builtin_exec("identity");
|
||||
let i = [0u8, 1, 2, 3];
|
||||
|
||||
let mut o2 = [255u8; 2];
|
||||
@ -167,7 +160,7 @@ fn identity() {
|
||||
#[test]
|
||||
fn sha256() {
|
||||
use rustc_serialize::hex::FromHex;
|
||||
let f = new_builtin_exec("sha256").unwrap();
|
||||
let f = new_builtin_exec("sha256");
|
||||
let i = [0u8; 0];
|
||||
|
||||
let mut o = [255u8; 32];
|
||||
@ -186,7 +179,7 @@ fn sha256() {
|
||||
#[test]
|
||||
fn ripemd160() {
|
||||
use rustc_serialize::hex::FromHex;
|
||||
let f = new_builtin_exec("ripemd160").unwrap();
|
||||
let f = new_builtin_exec("ripemd160");
|
||||
let i = [0u8; 0];
|
||||
|
||||
let mut o = [255u8; 32];
|
||||
@ -213,7 +206,7 @@ fn ecrecover() {
|
||||
let s = k.sign(&m).unwrap();
|
||||
println!("Signed: {}", s);*/
|
||||
|
||||
let f = new_builtin_exec("ecrecover").unwrap();
|
||||
let f = new_builtin_exec("ecrecover");
|
||||
let i = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
|
||||
|
||||
let mut o = [255u8; 32];
|
||||
@ -260,9 +253,15 @@ fn ecrecover() {
|
||||
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);*/
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn from_unknown_linear() {
|
||||
let _ = Builtin::from_named_linear("dw", 10, 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_named_linear() {
|
||||
let b = Builtin::from_named_linear("identity", 10, 20).unwrap();
|
||||
let b = Builtin::from_named_linear("identity", 10, 20);
|
||||
assert_eq!((*b.cost)(0), U256::from(10));
|
||||
assert_eq!((*b.cost)(1), U256::from(30));
|
||||
assert_eq!((*b.cost)(32), U256::from(30));
|
||||
@ -276,9 +275,14 @@ fn from_named_linear() {
|
||||
|
||||
#[test]
|
||||
fn from_json() {
|
||||
let text = r#"{"name": "identity", "pricing": {"linear": {"base": 10, "word": 20}}}"#;
|
||||
let json = Json::from_str(text).unwrap();
|
||||
let b = Builtin::from_json(&json).unwrap();
|
||||
let b = Builtin::from(ethjson::spec::Builtin {
|
||||
name: "identity".to_owned(),
|
||||
pricing: ethjson::spec::Pricing::Linear(ethjson::spec::Linear {
|
||||
base: 10,
|
||||
word: 20,
|
||||
})
|
||||
});
|
||||
|
||||
assert_eq!((*b.cost)(0), U256::from(10));
|
||||
assert_eq!((*b.cost)(1), U256::from(30));
|
||||
assert_eq!((*b.cost)(32), U256::from(30));
|
||||
|
@ -140,14 +140,15 @@ impl<V> Client<V> where V: Verifier {
|
||||
let mut state_path = path.to_path_buf();
|
||||
state_path.push("state");
|
||||
|
||||
let engine = Arc::new(try!(spec.to_engine()));
|
||||
let state_path_str = state_path.to_str().unwrap();
|
||||
let mut state_db = journaldb::new(state_path_str, config.pruning);
|
||||
|
||||
if state_db.is_empty() && engine.spec().ensure_db_good(state_db.as_hashdb_mut()) {
|
||||
state_db.commit(0, &engine.spec().genesis_header().hash(), None).expect("Error commiting genesis state to state DB");
|
||||
if state_db.is_empty() && spec.ensure_db_good(state_db.as_hashdb_mut()) {
|
||||
state_db.commit(0, &spec.genesis_header().hash(), None).expect("Error commiting genesis state to state DB");
|
||||
}
|
||||
|
||||
let engine = Arc::new(spec.engine);
|
||||
|
||||
let block_queue = BlockQueue::new(config.queue, engine.clone(), message_channel);
|
||||
let panic_handler = PanicHandler::new_in_arc();
|
||||
panic_handler.forward_from(&block_queue);
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
use common::*;
|
||||
use block::ExecutedBlock;
|
||||
use spec::Spec;
|
||||
use spec::CommonParams;
|
||||
use evm::Schedule;
|
||||
use evm::Factory;
|
||||
|
||||
@ -35,7 +35,7 @@ pub trait Engine : Sync + Send {
|
||||
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() }
|
||||
|
||||
/// Get the general parameters of the chain.
|
||||
fn spec(&self) -> &Spec;
|
||||
fn params(&self) -> &CommonParams;
|
||||
|
||||
/// Get current EVM factory
|
||||
fn vm_factory(&self) -> &Factory;
|
||||
@ -43,14 +43,18 @@ pub trait Engine : Sync + Send {
|
||||
/// Get the EVM schedule for the given `env_info`.
|
||||
fn schedule(&self, env_info: &EnvInfo) -> Schedule;
|
||||
|
||||
/// Builtin-contracts we would like to see in the chain.
|
||||
/// (In principle these are just hints for the engine since that has the last word on them.)
|
||||
fn builtins(&self) -> &BTreeMap<Address, Builtin>;
|
||||
|
||||
/// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`.
|
||||
fn maximum_extra_data_size(&self) -> usize { decode(&self.spec().engine_params.get("maximumExtraDataSize").unwrap()) }
|
||||
fn maximum_extra_data_size(&self) -> usize { self.params().maximum_extra_data_size }
|
||||
/// Maximum number of uncles a block is allowed to declare.
|
||||
fn maximum_uncle_count(&self) -> usize { 2 }
|
||||
/// The number of generations back that uncles can be.
|
||||
fn maximum_uncle_age(&self) -> usize { 6 }
|
||||
/// The nonce with which accounts begin.
|
||||
fn account_start_nonce(&self) -> U256 { decode(&self.spec().engine_params.get("accountStartNonce").unwrap()) }
|
||||
fn account_start_nonce(&self) -> U256 { self.params().account_start_nonce }
|
||||
|
||||
/// Block transformation functions, before the transactions.
|
||||
fn on_new_block(&self, _block: &mut ExecutedBlock) {}
|
||||
@ -94,23 +98,13 @@ pub trait Engine : Sync + Send {
|
||||
// TODO: builtin contract routing - to do this properly, it will require removing the built-in configuration-reading logic
|
||||
// from Spec into here and removing the Spec::builtins field.
|
||||
/// Determine whether a particular address is a builtin contract.
|
||||
fn is_builtin(&self, a: &Address) -> bool { self.spec().builtins.contains_key(a) }
|
||||
fn is_builtin(&self, a: &Address) -> bool { self.builtins().contains_key(a) }
|
||||
/// Determine the code execution cost of the builtin contract with address `a`.
|
||||
/// Panics if `is_builtin(a)` is not true.
|
||||
fn cost_of_builtin(&self, a: &Address, input: &[u8]) -> U256 { self.spec().builtins.get(a).unwrap().cost(input.len()) }
|
||||
fn cost_of_builtin(&self, a: &Address, input: &[u8]) -> U256 { self.builtins().get(a).unwrap().cost(input.len()) }
|
||||
/// Execution the builtin contract `a` on `input` and return `output`.
|
||||
/// Panics if `is_builtin(a)` is not true.
|
||||
fn execute_builtin(&self, a: &Address, input: &[u8], output: &mut [u8]) { self.spec().builtins.get(a).unwrap().execute(input, output); }
|
||||
fn execute_builtin(&self, a: &Address, input: &[u8], output: &mut [u8]) { self.builtins().get(a).unwrap().execute(input, output); }
|
||||
|
||||
// TODO: sealing stuff - though might want to leave this for later.
|
||||
|
||||
/// Get a named parameter from the `spec()`'s `engine_params` item and convert to u64, or 0 if that fails.
|
||||
fn u64_param(&self, name: &str) -> u64 {
|
||||
self.spec().engine_params.get(name).map_or(0u64, |a| decode(&a))
|
||||
}
|
||||
|
||||
/// Get a named parameter from the `spec()`'s `engine_params` item and convert to U256, or 0 if that fails.
|
||||
fn u256_param(&self, name: &str) -> U256 {
|
||||
self.spec().engine_params.get(name).map_or(x!(0), |a| decode(&a))
|
||||
}
|
||||
}
|
||||
|
@ -55,21 +55,6 @@ impl Default for EnvInfo {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromJson for EnvInfo {
|
||||
fn from_json(json: &Json) -> EnvInfo {
|
||||
let current_number: u64 = xjson!(&json["currentNumber"]);
|
||||
EnvInfo {
|
||||
number: current_number,
|
||||
author: xjson!(&json["currentCoinbase"]),
|
||||
difficulty: xjson!(&json["currentDifficulty"]),
|
||||
gas_limit: xjson!(&json["currentGasLimit"]),
|
||||
timestamp: xjson!(&json["currentTimestamp"]),
|
||||
last_hashes: (1..cmp::min(current_number + 1, 257)).map(|i| format!("{}", current_number - i).as_bytes().sha3()).collect(),
|
||||
gas_used: x!(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ethjson::vm::Env> for EnvInfo {
|
||||
fn from(e: ethjson::vm::Env) -> Self {
|
||||
let number = e.number.into();
|
||||
@ -90,24 +75,20 @@ mod tests {
|
||||
extern crate rustc_serialize;
|
||||
|
||||
use super::*;
|
||||
use rustc_serialize::*;
|
||||
use util::from_json::FromJson;
|
||||
use util::hash::*;
|
||||
use util::numbers::U256;
|
||||
use std::str::FromStr;
|
||||
use ethjson;
|
||||
|
||||
#[test]
|
||||
fn it_serializes_form_json() {
|
||||
let env_info = EnvInfo::from_json(&json::Json::from_str(
|
||||
r#"
|
||||
{
|
||||
"currentCoinbase": "0x000000f00000000f000000000000f00000000f00",
|
||||
"currentNumber": 1112339,
|
||||
"currentDifficulty": 50000,
|
||||
"currentGasLimit" : 40000,
|
||||
"currentTimestamp" : 1100
|
||||
}
|
||||
"#
|
||||
).unwrap());
|
||||
let env_info = EnvInfo::from(ethjson::vm::Env {
|
||||
author: ethjson::hash::Address(Address::from_str("000000f00000000f000000000000f00000000f00").unwrap()),
|
||||
number: ethjson::uint::Uint(U256::from(1_112_339)),
|
||||
difficulty: ethjson::uint::Uint(U256::from(50_000)),
|
||||
gas_limit: ethjson::uint::Uint(U256::from(40_000)),
|
||||
timestamp: ethjson::uint::Uint(U256::from(1_100))
|
||||
});
|
||||
|
||||
assert_eq!(env_info.number, 1112339);
|
||||
assert_eq!(env_info.author, Address::from_str("000000f00000000f000000000000f00000000f00").unwrap());
|
||||
|
@ -19,42 +19,63 @@ extern crate ethash;
|
||||
use self::ethash::{quick_get_difficulty, EthashManager, H256 as EH256};
|
||||
use common::*;
|
||||
use block::*;
|
||||
use spec::*;
|
||||
use spec::CommonParams;
|
||||
use engine::*;
|
||||
use evm::Schedule;
|
||||
use evm::Factory;
|
||||
use evm::{Schedule, Factory};
|
||||
use ethjson;
|
||||
|
||||
/// Ethash params.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct EthashParams {
|
||||
/// Tie breaking gas.
|
||||
pub tie_breaking_gas: bool,
|
||||
/// Gas limit divisor.
|
||||
pub gas_limit_bound_divisor: U256,
|
||||
/// Minimum difficulty.
|
||||
pub minimum_difficulty: U256,
|
||||
/// Difficulty bound divisor.
|
||||
pub difficulty_bound_divisor: U256,
|
||||
/// Block duration.
|
||||
pub duration_limit: u64,
|
||||
/// Block reward.
|
||||
pub block_reward: U256,
|
||||
/// Namereg contract address.
|
||||
pub registrar: Address,
|
||||
}
|
||||
|
||||
impl From<ethjson::spec::EthashParams> for EthashParams {
|
||||
fn from(p: ethjson::spec::EthashParams) -> Self {
|
||||
EthashParams {
|
||||
tie_breaking_gas: p.tie_breaking_gas,
|
||||
gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
|
||||
minimum_difficulty: p.minimum_difficulty.into(),
|
||||
difficulty_bound_divisor: p.difficulty_bound_divisor.into(),
|
||||
duration_limit: p.duration_limit.into(),
|
||||
block_reward: p.block_reward.into(),
|
||||
registrar: p.registrar.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Engine using Ethash proof-of-work consensus algorithm, suitable for Ethereum
|
||||
/// mainnet chains in the Olympic, Frontier and Homestead eras.
|
||||
pub struct Ethash {
|
||||
spec: Spec,
|
||||
params: CommonParams,
|
||||
ethash_params: EthashParams,
|
||||
builtins: BTreeMap<Address, Builtin>,
|
||||
pow: EthashManager,
|
||||
factory: Factory,
|
||||
u64_params: RwLock<HashMap<String, u64>>,
|
||||
u256_params: RwLock<HashMap<String, U256>>,
|
||||
}
|
||||
|
||||
impl Ethash {
|
||||
/// Create a new boxed instance of Ethash engine
|
||||
pub fn new_boxed(spec: Spec) -> Box<Engine> {
|
||||
Box::new(Ethash {
|
||||
spec: spec,
|
||||
pow: EthashManager::new(),
|
||||
// TODO [todr] should this return any specific factory?
|
||||
factory: Factory::default(),
|
||||
u64_params: RwLock::new(HashMap::new()),
|
||||
u256_params: RwLock::new(HashMap::new())
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn new_test(spec: Spec) -> Ethash {
|
||||
/// Create a new instance of Ethash engine
|
||||
pub fn new(params: CommonParams, ethash_params: EthashParams, builtins: BTreeMap<Address, Builtin>) -> Self {
|
||||
Ethash {
|
||||
spec: spec,
|
||||
params: params,
|
||||
ethash_params: ethash_params,
|
||||
builtins: builtins,
|
||||
pow: EthashManager::new(),
|
||||
factory: Factory::default(),
|
||||
u64_params: RwLock::new(HashMap::new()),
|
||||
u256_params: RwLock::new(HashMap::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -65,17 +86,23 @@ impl Engine for Ethash {
|
||||
// Two fields - mix
|
||||
fn seal_fields(&self) -> usize { 2 }
|
||||
|
||||
fn params(&self) -> &CommonParams { &self.params }
|
||||
|
||||
fn builtins(&self) -> &BTreeMap<Address, Builtin> {
|
||||
&self.builtins
|
||||
}
|
||||
|
||||
/// Additional engine-specific information for the user/developer concerning `header`.
|
||||
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() }
|
||||
fn spec(&self) -> &Spec { &self.spec }
|
||||
|
||||
fn vm_factory(&self) -> &Factory {
|
||||
&self.factory
|
||||
}
|
||||
|
||||
fn schedule(&self, env_info: &EnvInfo) -> Schedule {
|
||||
trace!(target: "client", "Creating schedule. param={:?}, fCML={}", self.spec().engine_params.get("frontierCompatibilityModeLimit"), self.u64_param("frontierCompatibilityModeLimit"));
|
||||
if env_info.number < self.u64_param("frontierCompatibilityModeLimit") {
|
||||
trace!(target: "client", "Creating schedule. fCML={}", self.params.frontier_compatibility_mode_limit);
|
||||
|
||||
if env_info.number < self.params.frontier_compatibility_mode_limit {
|
||||
Schedule::new_frontier()
|
||||
} else {
|
||||
Schedule::new_homestead()
|
||||
@ -86,7 +113,7 @@ impl Engine for Ethash {
|
||||
header.difficulty = self.calculate_difficuty(header, parent);
|
||||
header.gas_limit = {
|
||||
let gas_limit = parent.gas_limit;
|
||||
let bound_divisor = self.u256_param("gasLimitBoundDivisor");
|
||||
let bound_divisor = self.ethash_params.gas_limit_bound_divisor;
|
||||
if gas_limit < gas_floor_target {
|
||||
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - x!(1))
|
||||
} else {
|
||||
@ -100,7 +127,7 @@ impl Engine for Ethash {
|
||||
/// Apply the block reward on finalisation of the block.
|
||||
/// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current).
|
||||
fn on_close_block(&self, block: &mut ExecutedBlock) {
|
||||
let reward = self.spec().engine_params.get("blockReward").map_or(U256::from(0u64), |a| decode(&a));
|
||||
let reward = self.ethash_params.block_reward;
|
||||
let fields = block.fields_mut();
|
||||
|
||||
// Bestow block reward
|
||||
@ -125,7 +152,7 @@ impl Engine for Ethash {
|
||||
try!(UntrustedRlp::new(&header.seal[1]).as_val::<H64>());
|
||||
|
||||
// TODO: consider removing these lines.
|
||||
let min_difficulty = decode(self.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||
let min_difficulty = self.ethash_params.minimum_difficulty;
|
||||
if header.difficulty < min_difficulty {
|
||||
return Err(From::from(BlockError::DifficultyOutOfBounds(OutOfBounds { min: Some(min_difficulty), max: None, found: header.difficulty })))
|
||||
}
|
||||
@ -170,7 +197,7 @@ impl Engine for Ethash {
|
||||
if header.difficulty != expected_difficulty {
|
||||
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty })))
|
||||
}
|
||||
let gas_limit_divisor = decode(self.spec().engine_params.get("gasLimitBoundDivisor").unwrap());
|
||||
let gas_limit_divisor = self.ethash_params.gas_limit_bound_divisor;
|
||||
let min_gas = parent.gas_limit - parent.gas_limit / gas_limit_divisor;
|
||||
let max_gas = parent.gas_limit + parent.gas_limit / gas_limit_divisor;
|
||||
if header.gas_limit <= min_gas || header.gas_limit >= max_gas {
|
||||
@ -180,7 +207,7 @@ impl Engine for Ethash {
|
||||
}
|
||||
|
||||
fn verify_transaction_basic(&self, t: &SignedTransaction, header: &Header) -> result::Result<(), Error> {
|
||||
if header.number() >= self.u64_param("frontierCompatibilityModeLimit") {
|
||||
if header.number() >= self.params.frontier_compatibility_mode_limit {
|
||||
try!(t.check_low_s());
|
||||
}
|
||||
Ok(())
|
||||
@ -189,16 +216,6 @@ impl Engine for Ethash {
|
||||
fn verify_transaction(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> {
|
||||
t.sender().map(|_|()) // Perform EC recovery and cache sender
|
||||
}
|
||||
|
||||
fn u64_param(&self, name: &str) -> u64 {
|
||||
*self.u64_params.write().unwrap().entry(name.to_owned()).or_insert_with(||
|
||||
self.spec().engine_params.get(name).map_or(0u64, |a| decode(&a)))
|
||||
}
|
||||
|
||||
fn u256_param(&self, name: &str) -> U256 {
|
||||
*self.u256_params.write().unwrap().entry(name.to_owned()).or_insert_with(||
|
||||
self.spec().engine_params.get(name).map_or(x!(0), |a| decode(&a)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature="dev", allow(wrong_self_convention))] // to_ethash should take self
|
||||
@ -209,10 +226,11 @@ impl Ethash {
|
||||
panic!("Can't calculate genesis block difficulty");
|
||||
}
|
||||
|
||||
let min_difficulty = self.u256_param("minimumDifficulty");
|
||||
let difficulty_bound_divisor = self.u256_param("difficultyBoundDivisor");
|
||||
let duration_limit = self.u64_param("durationLimit");
|
||||
let frontier_limit = self.u64_param("frontierCompatibilityModeLimit");
|
||||
let min_difficulty = self.ethash_params.minimum_difficulty;
|
||||
let difficulty_bound_divisor = self.ethash_params.difficulty_bound_divisor;
|
||||
let duration_limit = self.ethash_params.duration_limit;
|
||||
let frontier_limit = self.params.frontier_compatibility_mode_limit;
|
||||
|
||||
let mut target = if header.number < frontier_limit {
|
||||
if header.timestamp >= parent.timestamp + duration_limit {
|
||||
parent.difficulty - (parent.difficulty / difficulty_bound_divisor)
|
||||
@ -283,16 +301,16 @@ mod tests {
|
||||
use block::*;
|
||||
use engine::*;
|
||||
use tests::helpers::*;
|
||||
use super::{Ethash};
|
||||
use super::super::new_morden;
|
||||
|
||||
#[test]
|
||||
fn on_close_block() {
|
||||
let engine = new_morden().to_engine().unwrap();
|
||||
let genesis_header = engine.spec().genesis_header();
|
||||
let spec = new_morden();
|
||||
let engine = &spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db = db_result.take();
|
||||
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let last_hashes = vec![genesis_header.hash()];
|
||||
let b = OpenBlock::new(engine.deref(), false, db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]);
|
||||
let b = b.close();
|
||||
@ -301,11 +319,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn on_close_block_with_uncle() {
|
||||
let engine = new_morden().to_engine().unwrap();
|
||||
let genesis_header = engine.spec().genesis_header();
|
||||
let spec = new_morden();
|
||||
let engine = &spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db = db_result.take();
|
||||
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let last_hashes = vec![genesis_header.hash()];
|
||||
let mut b = OpenBlock::new(engine.deref(), false, db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]);
|
||||
let mut uncle = Header::new();
|
||||
@ -320,27 +339,20 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn has_valid_metadata() {
|
||||
let engine = Ethash::new_boxed(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
assert!(!engine.name().is_empty());
|
||||
assert!(engine.version().major >= 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_return_params() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
assert!(engine.u64_param("durationLimit") > 0);
|
||||
assert!(engine.u256_param("minimumDifficulty") > U256::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_return_factory() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
engine.vm_factory();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_return_schedule() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
let schedule = engine.schedule(&EnvInfo {
|
||||
number: 10000000,
|
||||
author: x!(0),
|
||||
@ -368,7 +380,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_do_seal_verification_fail() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
//let engine = Ethash::new_test(new_morden());
|
||||
let header: Header = Header::default();
|
||||
|
||||
let verify_result = engine.verify_block_basic(&header, None);
|
||||
@ -382,7 +395,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_do_difficulty_verification_fail() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
let mut header: Header = Header::default();
|
||||
header.set_seal(vec![rlp::encode(&H256::zero()).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
|
||||
|
||||
@ -397,7 +410,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_do_proof_of_work_verification_fail() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
let mut header: Header = Header::default();
|
||||
header.set_seal(vec![rlp::encode(&H256::zero()).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
|
||||
header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap());
|
||||
@ -413,7 +426,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_do_seal_unordered_verification_fail() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
let header: Header = Header::default();
|
||||
|
||||
let verify_result = engine.verify_block_unordered(&header, None);
|
||||
@ -427,7 +440,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_do_seal256_verification_fail() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
let mut header: Header = Header::default();
|
||||
header.set_seal(vec![rlp::encode(&H256::zero()).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
|
||||
let verify_result = engine.verify_block_unordered(&header, None);
|
||||
@ -441,7 +454,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_do_proof_of_work_unordered_verification_fail() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
let mut header: Header = Header::default();
|
||||
header.set_seal(vec![rlp::encode(&H256::from("b251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d")).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
|
||||
header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap());
|
||||
@ -457,7 +470,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_verify_block_family_genesis_fail() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
let header: Header = Header::default();
|
||||
let parent_header: Header = Header::default();
|
||||
|
||||
@ -472,7 +485,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_verify_block_family_difficulty_fail() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
let mut header: Header = Header::default();
|
||||
header.set_number(2);
|
||||
let mut parent_header: Header = Header::default();
|
||||
@ -489,7 +502,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_verify_block_family_gas_fail() {
|
||||
let engine = Ethash::new_test(new_morden());
|
||||
let engine = new_morden().engine;
|
||||
let mut header: Header = Header::default();
|
||||
header.set_number(2);
|
||||
header.set_difficulty(U256::from_str("0000000000000000000000000000000000000000000000000000000000020000").unwrap());
|
||||
|
@ -30,22 +30,22 @@ pub use self::denominations::*;
|
||||
use super::spec::*;
|
||||
|
||||
/// Create a new Olympic chain spec.
|
||||
pub fn new_olympic() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/olympic.json")) }
|
||||
pub fn new_olympic() -> Spec { Spec::load(include_bytes!("../../res/ethereum/olympic.json")) }
|
||||
|
||||
/// Create a new Frontier mainnet chain spec.
|
||||
pub fn new_frontier() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/frontier.json")) }
|
||||
pub fn new_frontier() -> Spec { Spec::load(include_bytes!("../../res/ethereum/frontier.json")) }
|
||||
|
||||
/// Create a new Frontier chain spec as though it never changes to Homestead.
|
||||
pub fn new_frontier_test() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/frontier_test.json")) }
|
||||
pub fn new_frontier_test() -> Spec { Spec::load(include_bytes!("../../res/ethereum/frontier_test.json")) }
|
||||
|
||||
/// Create a new Homestead chain spec as though it never changed from Frontier.
|
||||
pub fn new_homestead_test() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/homestead_test.json")) }
|
||||
pub fn new_homestead_test() -> Spec { Spec::load(include_bytes!("../../res/ethereum/homestead_test.json")) }
|
||||
|
||||
/// Create a new Frontier main net chain spec without genesis accounts.
|
||||
pub fn new_mainnet_like() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/frontier_like_test.json")) }
|
||||
pub fn new_mainnet_like() -> Spec { Spec::load(include_bytes!("../../res/ethereum/frontier_like_test.json")) }
|
||||
|
||||
/// Create a new Morden chain spec.
|
||||
pub fn new_morden() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/morden.json")) }
|
||||
pub fn new_morden() -> Spec { Spec::load(include_bytes!("../../res/ethereum/morden.json")) }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
@ -57,11 +57,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn ensure_db_good() {
|
||||
let engine = new_morden().to_engine().unwrap();
|
||||
let genesis_header = engine.spec().genesis_header();
|
||||
let spec = new_morden();
|
||||
let engine = &spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db = db_result.take();
|
||||
engine.spec().ensure_db_good(db.as_hashdb_mut());
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let s = State::from_existing(db, genesis_header.state_root.clone(), engine.account_start_nonce());
|
||||
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000001")), U256::from(1u64));
|
||||
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000002")), U256::from(1u64));
|
||||
@ -79,7 +80,7 @@ mod tests {
|
||||
let genesis = morden.genesis_block();
|
||||
assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap());
|
||||
|
||||
let _ = morden.to_engine();
|
||||
let _ = morden.engine;
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -90,6 +91,6 @@ mod tests {
|
||||
let genesis = frontier.genesis_block();
|
||||
assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3").unwrap());
|
||||
|
||||
let _ = frontier.to_engine();
|
||||
let _ = frontier.engine;
|
||||
}
|
||||
}
|
||||
|
@ -330,7 +330,7 @@ mod tests {
|
||||
fn new() -> Self {
|
||||
TestSetup {
|
||||
state: get_temp_state(),
|
||||
engine: get_test_spec().to_engine().unwrap(),
|
||||
engine: get_test_spec().engine,
|
||||
sub_state: Substate::new(),
|
||||
env_info: get_test_env_info()
|
||||
}
|
||||
|
@ -27,33 +27,6 @@ use substate::*;
|
||||
use tests::helpers::*;
|
||||
use ethjson;
|
||||
|
||||
struct TestEngineFrontier {
|
||||
vm_factory: Factory,
|
||||
spec: Spec,
|
||||
max_depth: usize
|
||||
}
|
||||
|
||||
impl TestEngineFrontier {
|
||||
fn new(max_depth: usize, vm_type: VMType) -> TestEngineFrontier {
|
||||
TestEngineFrontier {
|
||||
vm_factory: Factory::new(vm_type),
|
||||
spec: ethereum::new_frontier_test(),
|
||||
max_depth: max_depth
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Engine for TestEngineFrontier {
|
||||
fn name(&self) -> &str { "TestEngine" }
|
||||
fn spec(&self) -> &Spec { &self.spec }
|
||||
fn vm_factory(&self) -> &Factory { &self.vm_factory }
|
||||
fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
|
||||
let mut schedule = Schedule::new_frontier();
|
||||
schedule.max_depth = self.max_depth;
|
||||
schedule
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct CallCreate {
|
||||
data: Bytes,
|
||||
@ -207,7 +180,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec<String> {
|
||||
let mut state = state_result.reference_mut();
|
||||
state.populate_from(From::from(vm.pre_state.clone()));
|
||||
let info = From::from(vm.env);
|
||||
let engine = TestEngineFrontier::new(1, vm_type.clone());
|
||||
let engine = TestEngine::new(1, Factory::new(vm_type.clone()));
|
||||
let params = ActionParams::from(vm.transaction);
|
||||
|
||||
let mut substate = Substate::new();
|
||||
|
@ -30,9 +30,9 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
|
||||
let tests = ethjson::state::Test::load(json_data).unwrap();
|
||||
let mut failed = Vec::new();
|
||||
let engine = match era {
|
||||
ChainEra::Frontier => ethereum::new_mainnet_like(),
|
||||
ChainEra::Homestead => ethereum::new_homestead_test(),
|
||||
}.to_engine().unwrap();
|
||||
ChainEra::Frontier => ethereum::new_mainnet_like().engine,
|
||||
ChainEra::Homestead => ethereum::new_homestead_test().engine
|
||||
};
|
||||
|
||||
for (name, test) in tests.into_iter() {
|
||||
let mut fail = false;
|
||||
|
@ -76,18 +76,6 @@ impl From<ethjson::state::Log> for LogEntry {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromJson for LogEntry {
|
||||
/// Convert given JSON object to a LogEntry.
|
||||
fn from_json(json: &Json) -> LogEntry {
|
||||
// TODO: check bloom.
|
||||
LogEntry {
|
||||
address: xjson!(&json["address"]),
|
||||
topics: xjson!(&json["topics"]),
|
||||
data: xjson!(&json["data"]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Log localized in a blockchain.
|
||||
#[derive(Default, Debug, PartialEq, Clone)]
|
||||
pub struct LocalizedLogEntry {
|
||||
|
@ -14,26 +14,29 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use util::hash::Address;
|
||||
use builtin::Builtin;
|
||||
use engine::Engine;
|
||||
use spec::Spec;
|
||||
use evm::Schedule;
|
||||
use evm::Factory;
|
||||
use spec::CommonParams;
|
||||
use evm::{Schedule, Factory};
|
||||
use env_info::EnvInfo;
|
||||
|
||||
/// An engine which does not provide any consensus mechanism.
|
||||
pub struct NullEngine {
|
||||
spec: Spec,
|
||||
factory: Factory
|
||||
params: CommonParams,
|
||||
builtins: BTreeMap<Address, Builtin>,
|
||||
factory: Factory,
|
||||
}
|
||||
|
||||
impl NullEngine {
|
||||
/// Returns new instance of NullEngine with default VM Factory
|
||||
pub fn new_boxed(spec: Spec) -> Box<Engine> {
|
||||
Box::new(NullEngine{
|
||||
spec: spec,
|
||||
// TODO [todr] should this return any specific factory?
|
||||
pub fn new(params: CommonParams, builtins: BTreeMap<Address, Builtin>) -> Self {
|
||||
NullEngine{
|
||||
params: params,
|
||||
builtins: builtins,
|
||||
factory: Factory::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,12 +45,20 @@ impl Engine for NullEngine {
|
||||
&self.factory
|
||||
}
|
||||
|
||||
fn name(&self) -> &str { "NullEngine" }
|
||||
fn name(&self) -> &str {
|
||||
"NullEngine"
|
||||
}
|
||||
|
||||
fn spec(&self) -> &Spec { &self.spec }
|
||||
fn params(&self) -> &CommonParams {
|
||||
&self.params
|
||||
}
|
||||
|
||||
fn builtins(&self) -> &BTreeMap<Address, Builtin> {
|
||||
&self.builtins
|
||||
}
|
||||
|
||||
fn schedule(&self, env_info: &EnvInfo) -> Schedule {
|
||||
if env_info.number < self.u64_param("frontierCompatibilityModeLimit") {
|
||||
if env_info.number < self.params.frontier_compatibility_mode_limit {
|
||||
Schedule::new_frontier()
|
||||
} else {
|
||||
Schedule::new_homestead()
|
||||
|
@ -80,12 +80,22 @@ impl From<ethjson::blockchain::Account> for PodAccount {
|
||||
balance: a.balance.into(),
|
||||
nonce: a.nonce.into(),
|
||||
code: a.code.into(),
|
||||
storage: a.storage.into_iter().fold(BTreeMap::new(), |mut acc, (key, value)| {
|
||||
storage: a.storage.into_iter().map(|(key, value)| {
|
||||
let key: U256 = key.into();
|
||||
let value: U256 = value.into();
|
||||
acc.insert(H256::from(key), H256::from(value));
|
||||
acc
|
||||
})
|
||||
(H256::from(key), H256::from(value))
|
||||
}).collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ethjson::spec::Account> for PodAccount {
|
||||
fn from(a: ethjson::spec::Account) -> Self {
|
||||
PodAccount {
|
||||
balance: a.balance.map_or_else(U256::zero, Into::into),
|
||||
nonce: a.nonce.map_or_else(U256::zero, Into::into),
|
||||
code: vec![],
|
||||
storage: BTreeMap::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,33 +46,20 @@ impl PodState {
|
||||
pub fn drain(self) -> BTreeMap<Address, PodAccount> { self.0 }
|
||||
}
|
||||
|
||||
impl FromJson for PodState {
|
||||
/// Translate the JSON object into a hash map of account information ready for insertion into State.
|
||||
fn from_json(json: &Json) -> PodState {
|
||||
PodState(json.as_object().unwrap().iter().fold(BTreeMap::new(), |mut state, (address, acc)| {
|
||||
let balance = acc.find("balance").map(&U256::from_json);
|
||||
let nonce = acc.find("nonce").map(&U256::from_json);
|
||||
let storage = acc.find("storage").map(&BTreeMap::from_json);
|
||||
let code = acc.find("code").map(&Bytes::from_json);
|
||||
if balance.is_some() || nonce.is_some() || storage.is_some() || code.is_some() {
|
||||
state.insert(address_from_hex(address), PodAccount{
|
||||
balance: balance.unwrap_or_else(U256::zero),
|
||||
nonce: nonce.unwrap_or_else(U256::zero),
|
||||
storage: storage.unwrap_or_else(BTreeMap::new),
|
||||
code: code.unwrap_or_else(Vec::new)
|
||||
});
|
||||
}
|
||||
state
|
||||
}))
|
||||
impl From<ethjson::blockchain::State> for PodState {
|
||||
fn from(s: ethjson::blockchain::State) -> PodState {
|
||||
let state = s.into_iter().map(|(addr, acc)| (addr.into(), PodAccount::from(acc))).collect();
|
||||
PodState(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ethjson::blockchain::State> for PodState {
|
||||
fn from(s: ethjson::blockchain::State) -> PodState {
|
||||
PodState(s.0.into_iter().fold(BTreeMap::new(), |mut acc, (key, value)| {
|
||||
acc.insert(key.into(), PodAccount::from(value));
|
||||
acc
|
||||
}))
|
||||
impl From<ethjson::spec::State> for PodState {
|
||||
fn from(s: ethjson::spec::State) -> PodState {
|
||||
let state: BTreeMap<_,_> = s.into_iter()
|
||||
.filter(|pair| !pair.1.is_empty())
|
||||
.map(|(addr, acc)| (addr.into(), PodAccount::from(acc)))
|
||||
.collect();
|
||||
PodState(state)
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,30 +72,3 @@ impl fmt::Display for PodState {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate rustc_serialize;
|
||||
|
||||
use super::*;
|
||||
use rustc_serialize::*;
|
||||
use util::from_json::FromJson;
|
||||
use util::hash::*;
|
||||
|
||||
#[test]
|
||||
fn it_serializes_form_json() {
|
||||
let pod_state = PodState::from_json(&json::Json::from_str(
|
||||
r#"
|
||||
{
|
||||
"0000000000000000000000000000000000000000": {
|
||||
"balance": "1000",
|
||||
"nonce": "100",
|
||||
"storage": {},
|
||||
"code" : []
|
||||
}
|
||||
}
|
||||
"#
|
||||
).unwrap());
|
||||
|
||||
assert!(pod_state.get().get(&ZERO_ADDRESS).is_some());
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ impl ClientService {
|
||||
panic_handler.forward_from(&net_service);
|
||||
|
||||
info!("Starting {}", net_service.host_info());
|
||||
info!("Configured for {} using {} engine", spec.name, spec.engine_name);
|
||||
info!("Configured for {} using {:?} engine", spec.name, spec.engine.name());
|
||||
let client = try!(Client::new(config, spec, db_path, net_service.io().channel()));
|
||||
panic_handler.forward_from(client.deref());
|
||||
let client_io = Arc::new(ClientIoHandler {
|
||||
|
@ -16,26 +16,9 @@
|
||||
|
||||
use util::rlp::*;
|
||||
use util::numbers::{Uint, U256};
|
||||
use util::hash::{H64, Address, H256};
|
||||
use util::hash::{Address, H256};
|
||||
use ethjson;
|
||||
|
||||
/// Genesis seal type.
|
||||
pub enum Seal {
|
||||
/// Classic ethereum seal.
|
||||
Ethereum {
|
||||
/// Seal nonce.
|
||||
nonce: H64,
|
||||
/// Seal mix hash.
|
||||
mix_hash: H256,
|
||||
},
|
||||
/// Generic seal.
|
||||
Generic {
|
||||
/// Number of seal fields.
|
||||
fields: usize,
|
||||
/// Seal rlp.
|
||||
rlp: Vec<u8>,
|
||||
},
|
||||
}
|
||||
use super::seal::Seal;
|
||||
|
||||
/// Genesis components.
|
||||
pub struct Genesis {
|
||||
@ -66,16 +49,7 @@ pub struct Genesis {
|
||||
impl From<ethjson::spec::Genesis> for Genesis {
|
||||
fn from(g: ethjson::spec::Genesis) -> Self {
|
||||
Genesis {
|
||||
seal: match (g.nonce, g.mix_hash) {
|
||||
(Some(nonce), Some(mix_hash)) => Seal::Ethereum {
|
||||
nonce: nonce.into(),
|
||||
mix_hash: mix_hash.into(),
|
||||
},
|
||||
_ => Seal::Generic {
|
||||
fields: g.seal_fields.unwrap(),
|
||||
rlp: g.seal_rlp.unwrap().into(),
|
||||
}
|
||||
},
|
||||
seal: From::from(g.seal),
|
||||
difficulty: g.difficulty.into(),
|
||||
author: g.author.into(),
|
||||
timestamp: g.timestamp.into(),
|
||||
|
@ -17,6 +17,7 @@
|
||||
//! Blockchain params.
|
||||
|
||||
mod genesis;
|
||||
mod seal;
|
||||
pub mod spec;
|
||||
|
||||
pub use self::spec::*;
|
||||
|
81
ethcore/src/spec/seal.rs
Normal file
81
ethcore/src/spec/seal.rs
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Spec seal.
|
||||
|
||||
use util::rlp::*;
|
||||
use util::hash::{H64, H256};
|
||||
use ethjson;
|
||||
|
||||
/// Classic ethereum seal.
|
||||
pub struct Ethereum {
|
||||
/// Seal nonce.
|
||||
pub nonce: H64,
|
||||
/// Seal mix hash.
|
||||
pub mix_hash: H256,
|
||||
}
|
||||
|
||||
impl Into<Generic> for Ethereum {
|
||||
fn into(self) -> Generic {
|
||||
let mut s = RlpStream::new();
|
||||
s.append(&self.mix_hash);
|
||||
s.append(&self.nonce);
|
||||
Generic {
|
||||
fields: 2,
|
||||
rlp: s.out()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic seal.
|
||||
pub struct Generic {
|
||||
/// Number of seal fields.
|
||||
pub fields: usize,
|
||||
/// Seal rlp.
|
||||
pub rlp: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Genesis seal type.
|
||||
pub enum Seal {
|
||||
/// Classic ethereum seal.
|
||||
Ethereum(Ethereum),
|
||||
/// Generic seal.
|
||||
Generic(Generic),
|
||||
}
|
||||
|
||||
impl From<ethjson::spec::Seal> for Seal {
|
||||
fn from(s: ethjson::spec::Seal) -> Self {
|
||||
match s {
|
||||
ethjson::spec::Seal::Ethereum(eth) => Seal::Ethereum(Ethereum {
|
||||
nonce: eth.nonce.into(),
|
||||
mix_hash: eth.mix_hash.into()
|
||||
}),
|
||||
ethjson::spec::Seal::Generic(g) => Seal::Generic(Generic {
|
||||
fields: g.fields,
|
||||
rlp: g.rlp.into()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Generic> for Seal {
|
||||
fn into(self) -> Generic {
|
||||
match self {
|
||||
Seal::Generic(generic) => generic,
|
||||
Seal::Ethereum(eth) => eth.into()
|
||||
}
|
||||
}
|
||||
}
|
@ -21,55 +21,51 @@ use engine::*;
|
||||
use pod_state::*;
|
||||
use null_engine::*;
|
||||
use account_db::*;
|
||||
use super::genesis::Genesis;
|
||||
use super::seal::Generic as GenericSeal;
|
||||
use ethereum;
|
||||
use super::genesis::{Seal as GenesisSeal, Genesis};
|
||||
use ethjson;
|
||||
|
||||
/// Convert JSON value to equivalent RLP representation.
|
||||
// TODO: handle container types.
|
||||
fn json_to_rlp(json: &Json) -> Bytes {
|
||||
match *json {
|
||||
Json::Boolean(o) => encode(&(if o {1u64} else {0})).to_vec(),
|
||||
Json::I64(o) => encode(&(o as u64)).to_vec(),
|
||||
Json::U64(o) => encode(&o).to_vec(),
|
||||
Json::String(ref s) if s.len() >= 2 && &s[0..2] == "0x" && U256::from_str(&s[2..]).is_ok() => {
|
||||
encode(&U256::from_str(&s[2..]).unwrap()).to_vec()
|
||||
},
|
||||
Json::String(ref s) => {
|
||||
encode(s).to_vec()
|
||||
},
|
||||
_ => panic!()
|
||||
}
|
||||
/// Parameters common to all engines.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct CommonParams {
|
||||
/// Account start nonce.
|
||||
pub account_start_nonce: U256,
|
||||
/// Frontier compatibility mode limit.
|
||||
pub frontier_compatibility_mode_limit: u64,
|
||||
/// Maximum size of extra data.
|
||||
pub maximum_extra_data_size: usize,
|
||||
/// Network id.
|
||||
pub network_id: U256,
|
||||
/// Minimum gas limit.
|
||||
pub min_gas_limit: U256,
|
||||
}
|
||||
|
||||
/// Convert JSON to a string->RLP map.
|
||||
fn json_to_rlp_map(json: &Json) -> HashMap<String, Bytes> {
|
||||
json.as_object().unwrap().iter().map(|(k, v)| (k, json_to_rlp(v))).fold(HashMap::new(), |mut acc, kv| {
|
||||
acc.insert(kv.0.clone(), kv.1);
|
||||
acc
|
||||
})
|
||||
impl From<ethjson::spec::Params> for CommonParams {
|
||||
fn from(p: ethjson::spec::Params) -> Self {
|
||||
CommonParams {
|
||||
account_start_nonce: p.account_start_nonce.into(),
|
||||
frontier_compatibility_mode_limit: p.frontier_compatibility_mode_limit.into(),
|
||||
maximum_extra_data_size: p.maximum_extra_data_size.into(),
|
||||
network_id: p.network_id.into(),
|
||||
min_gas_limit: p.min_gas_limit.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parameters for a block chain; includes both those intrinsic to the design of the
|
||||
/// chain and those to be interpreted by the active chain engine.
|
||||
#[derive(Debug)]
|
||||
pub struct Spec {
|
||||
/// User friendly spec name
|
||||
pub name: String,
|
||||
/// What engine are we using for this?
|
||||
pub engine_name: String,
|
||||
pub engine: Box<Engine>,
|
||||
|
||||
/// Known nodes on the network in enode format.
|
||||
pub nodes: Vec<String>,
|
||||
/// Network ID
|
||||
pub network_id: U256,
|
||||
|
||||
/// Parameters concerning operation of the specific engine we're using.
|
||||
/// Maps the parameter name to an RLP-encoded value.
|
||||
pub engine_params: HashMap<String, Bytes>,
|
||||
|
||||
/// Builtin-contracts we would like to see in the chain.
|
||||
/// (In principle these are just hints for the engine since that has the last word on them.)
|
||||
pub builtins: BTreeMap<Address, Builtin>,
|
||||
/// Parameters common to all engines.
|
||||
pub params: CommonParams,
|
||||
|
||||
/// The genesis block's parent hash field.
|
||||
pub parent_hash: H256,
|
||||
@ -101,15 +97,41 @@ pub struct Spec {
|
||||
genesis_state: PodState,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature="dev", allow(wrong_self_convention))] // because to_engine(self) should be to_engine(&self)
|
||||
impl From<ethjson::spec::Spec> for Spec {
|
||||
fn from(s: ethjson::spec::Spec) -> Self {
|
||||
let builtins = s.accounts.builtins().into_iter().map(|p| (p.0.into(), From::from(p.1))).collect();
|
||||
let g = Genesis::from(s.genesis);
|
||||
let seal: GenericSeal = g.seal.into();
|
||||
let params = CommonParams::from(s.params);
|
||||
Spec {
|
||||
name: s.name.into(),
|
||||
params: params.clone(),
|
||||
engine: Spec::engine(s.engine, params, builtins),
|
||||
nodes: s.nodes.unwrap_or_else(Vec::new),
|
||||
parent_hash: g.parent_hash,
|
||||
transactions_root: g.transactions_root,
|
||||
receipts_root: g.receipts_root,
|
||||
author: g.author,
|
||||
difficulty: g.difficulty,
|
||||
gas_limit: g.gas_limit,
|
||||
gas_used: g.gas_used,
|
||||
timestamp: g.timestamp,
|
||||
extra_data: g.extra_data,
|
||||
seal_fields: seal.fields,
|
||||
seal_rlp: seal.rlp,
|
||||
state_root_memo: RwLock::new(g.state_root),
|
||||
genesis_state: From::from(s.accounts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Spec {
|
||||
/// Convert this object into a boxed Engine of the right underlying type.
|
||||
// TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead.
|
||||
pub fn to_engine(self) -> Result<Box<Engine>, Error> {
|
||||
match self.engine_name.as_ref() {
|
||||
"NullEngine" => Ok(NullEngine::new_boxed(self)),
|
||||
"Ethash" => Ok(ethereum::Ethash::new_boxed(self)),
|
||||
_ => Err(Error::UnknownEngineName(self.engine_name.clone()))
|
||||
/// Convert engine spec into a boxed Engine of the right underlying type.
|
||||
/// TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead.
|
||||
fn engine(engine_spec: ethjson::spec::Engine, params: CommonParams, builtins: BTreeMap<Address, Builtin>) -> Box<Engine> {
|
||||
match engine_spec {
|
||||
ethjson::spec::Engine::Null => Box::new(NullEngine::new(params, builtins)),
|
||||
ethjson::spec::Engine::Ethash(ethash) => Box::new(ethereum::Ethash::new(params, From::from(ethash.params), builtins))
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,7 +147,7 @@ impl Spec {
|
||||
pub fn nodes(&self) -> &Vec<String> { &self.nodes }
|
||||
|
||||
/// Get the configured Network ID.
|
||||
pub fn network_id(&self) -> U256 { self.network_id }
|
||||
pub fn network_id(&self) -> U256 { self.params.network_id }
|
||||
|
||||
/// Get the header of the genesis block.
|
||||
pub fn genesis_header(&self) -> Header {
|
||||
@ -168,49 +190,9 @@ impl Spec {
|
||||
ret.out()
|
||||
}
|
||||
|
||||
/// Overwrite the genesis components with the given JSON, assuming standard Ethereum test format.
|
||||
pub fn overwrite_genesis(&mut self, genesis: &Json) {
|
||||
let (seal_fields, seal_rlp) = {
|
||||
if genesis.find("mixHash").is_some() && genesis.find("nonce").is_some() {
|
||||
let mut s = RlpStream::new();
|
||||
s.append(&H256::from_json(&genesis["mixHash"]));
|
||||
s.append(&H64::from_json(&genesis["nonce"]));
|
||||
(2, s.out())
|
||||
} else {
|
||||
// backup algo that will work with sealFields/sealRlp (and without).
|
||||
(
|
||||
u64::from_json(&genesis["sealFields"]) as usize,
|
||||
Bytes::from_json(&genesis["sealRlp"])
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
self.parent_hash = H256::from_json(&genesis["parentHash"]);
|
||||
self.transactions_root = genesis.find("transactionsTrie").and_then(|_| Some(H256::from_json(&genesis["transactionsTrie"]))).unwrap_or(SHA3_NULL_RLP.clone());
|
||||
self.receipts_root = genesis.find("receiptTrie").and_then(|_| Some(H256::from_json(&genesis["receiptTrie"]))).unwrap_or(SHA3_NULL_RLP.clone());
|
||||
self.author = Address::from_json(&genesis["coinbase"]);
|
||||
self.difficulty = U256::from_json(&genesis["difficulty"]);
|
||||
self.gas_limit = U256::from_json(&genesis["gasLimit"]);
|
||||
self.gas_used = U256::from_json(&genesis["gasUsed"]);
|
||||
self.timestamp = u64::from_json(&genesis["timestamp"]);
|
||||
self.extra_data = Bytes::from_json(&genesis["extraData"]);
|
||||
self.seal_fields = seal_fields;
|
||||
self.seal_rlp = seal_rlp;
|
||||
self.state_root_memo = RwLock::new(genesis.find("stateRoot").and_then(|_| Some(H256::from_json(&genesis["stateRoot"]))));
|
||||
}
|
||||
|
||||
/// Overwrite the genesis components.
|
||||
pub fn overwrite_genesis_params(&mut self, g: Genesis) {
|
||||
let (seal_fields, seal_rlp) = match g.seal {
|
||||
GenesisSeal::Generic { fields, rlp } => (fields, rlp),
|
||||
GenesisSeal::Ethereum { nonce, mix_hash } => {
|
||||
let mut s = RlpStream::new();
|
||||
s.append(&mix_hash);
|
||||
s.append(&nonce);
|
||||
(2, s.out())
|
||||
}
|
||||
};
|
||||
|
||||
let seal: GenericSeal = g.seal.into();
|
||||
self.parent_hash = g.parent_hash;
|
||||
self.transactions_root = g.transactions_root;
|
||||
self.receipts_root = g.receipts_root;
|
||||
@ -220,8 +202,8 @@ impl Spec {
|
||||
self.gas_used = g.gas_used;
|
||||
self.timestamp = g.timestamp;
|
||||
self.extra_data = g.extra_data;
|
||||
self.seal_fields = seal_fields;
|
||||
self.seal_rlp = seal_rlp;
|
||||
self.seal_fields = seal.fields;
|
||||
self.seal_rlp = seal.rlp;
|
||||
self.state_root_memo = RwLock::new(g.state_root);
|
||||
}
|
||||
|
||||
@ -235,74 +217,7 @@ impl Spec {
|
||||
pub fn is_state_root_valid(&self) -> bool {
|
||||
self.state_root_memo.read().unwrap().clone().map_or(true, |sr| sr == self.genesis_state.root())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromJson for Spec {
|
||||
/// Loads a chain-specification from a json data structure
|
||||
fn from_json(json: &Json) -> Spec {
|
||||
// once we commit ourselves to some json parsing library (serde?)
|
||||
// move it to proper data structure
|
||||
let mut builtins = BTreeMap::new();
|
||||
let mut state = PodState::new();
|
||||
|
||||
if let Some(&Json::Object(ref accounts)) = json.find("accounts") {
|
||||
for (address, acc) in accounts.iter() {
|
||||
let addr = Address::from_str(address).unwrap();
|
||||
if let Some(ref builtin_json) = acc.find("builtin") {
|
||||
if let Some(builtin) = Builtin::from_json(builtin_json) {
|
||||
builtins.insert(addr.clone(), builtin);
|
||||
}
|
||||
}
|
||||
}
|
||||
state = xjson!(&json["accounts"]);
|
||||
}
|
||||
|
||||
let nodes = if let Some(&Json::Array(ref ns)) = json.find("nodes") {
|
||||
ns.iter().filter_map(|n| if let Json::String(ref s) = *n { Some(s.clone()) } else {None}).collect()
|
||||
} else { Vec::new() };
|
||||
|
||||
let genesis = &json["genesis"];//.as_object().expect("No genesis object in JSON");
|
||||
|
||||
let (seal_fields, seal_rlp) = {
|
||||
if genesis.find("mixHash").is_some() && genesis.find("nonce").is_some() {
|
||||
let mut s = RlpStream::new();
|
||||
s.append(&H256::from_str(&genesis["mixHash"].as_string().expect("mixHash not a string.")[2..]).expect("Invalid mixHash string value"));
|
||||
s.append(&H64::from_str(&genesis["nonce"].as_string().expect("nonce not a string.")[2..]).expect("Invalid nonce string value"));
|
||||
(2, s.out())
|
||||
} else {
|
||||
// backup algo that will work with sealFields/sealRlp (and without).
|
||||
(
|
||||
usize::from_str(&genesis["sealFields"].as_string().unwrap_or("0x")[2..]).expect("Invalid sealFields integer data"),
|
||||
genesis["sealRlp"].as_string().unwrap_or("0x")[2..].from_hex().expect("Invalid sealRlp hex data")
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
Spec {
|
||||
name: json.find("name").map_or("unknown", |j| j.as_string().unwrap()).to_owned(),
|
||||
engine_name: json["engineName"].as_string().unwrap().to_owned(),
|
||||
engine_params: json_to_rlp_map(&json["params"]),
|
||||
nodes: nodes,
|
||||
network_id: U256::from_str(&json["params"]["networkID"].as_string().unwrap()[2..]).unwrap(),
|
||||
builtins: builtins,
|
||||
parent_hash: H256::from_str(&genesis["parentHash"].as_string().unwrap()[2..]).unwrap(),
|
||||
author: Address::from_str(&genesis["author"].as_string().unwrap()[2..]).unwrap(),
|
||||
difficulty: U256::from_str(&genesis["difficulty"].as_string().unwrap()[2..]).unwrap(),
|
||||
gas_limit: U256::from_str(&genesis["gasLimit"].as_string().unwrap()[2..]).unwrap(),
|
||||
gas_used: U256::from(0u8),
|
||||
timestamp: u64::from_str(&genesis["timestamp"].as_string().unwrap()[2..]).unwrap(),
|
||||
transactions_root: SHA3_NULL_RLP.clone(),
|
||||
receipts_root: SHA3_NULL_RLP.clone(),
|
||||
extra_data: genesis["extraData"].as_string().unwrap()[2..].from_hex().unwrap(),
|
||||
genesis_state: state,
|
||||
seal_fields: seal_fields,
|
||||
seal_rlp: seal_rlp,
|
||||
state_root_memo: RwLock::new(genesis.find("stateRoot").and_then(|_| genesis["stateRoot"].as_string()).map(|s| H256::from_str(&s[2..]).unwrap())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Spec {
|
||||
/// Ensure that the given state DB has the trie nodes in for the genesis state.
|
||||
pub fn ensure_db_good(&self, db: &mut HashDB) -> bool {
|
||||
if !db.contains(&self.state_root()) {
|
||||
@ -321,21 +236,20 @@ impl Spec {
|
||||
} else { false }
|
||||
}
|
||||
|
||||
/// Create a new Spec from a JSON UTF-8 data resource `data`.
|
||||
pub fn from_json_utf8(data: &[u8]) -> Spec {
|
||||
Self::from_json_str(::std::str::from_utf8(data).unwrap())
|
||||
}
|
||||
|
||||
/// Create a new Spec from a JSON string.
|
||||
pub fn from_json_str(s: &str) -> Spec {
|
||||
Self::from_json(&Json::from_str(s).expect("Json is invalid"))
|
||||
/// Loads spec from json file.
|
||||
pub fn load(reader: &[u8]) -> Self {
|
||||
From::from(ethjson::spec::Spec::load(reader).expect("invalid json file"))
|
||||
}
|
||||
|
||||
/// Create a new Spec which conforms to the Morden chain except that it's a NullEngine consensus.
|
||||
pub fn new_test() -> Spec { Self::from_json_utf8(include_bytes!("../../res/null_morden.json")) }
|
||||
pub fn new_test() -> Spec {
|
||||
Spec::load(include_bytes!("../../res/null_morden.json"))
|
||||
}
|
||||
|
||||
/// Create a new Spec which conforms to the Morden chain except that it's a NullEngine consensus.
|
||||
pub fn new_homestead_test() -> Spec { Self::from_json_utf8(include_bytes!("../../res/null_homestead_morden.json")) }
|
||||
pub fn new_homestead_test() -> Spec {
|
||||
Spec::load(include_bytes!("../../res/null_homestead_morden.json"))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -353,7 +267,5 @@ mod tests {
|
||||
assert_eq!(test_spec.state_root(), H256::from_str("f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9").unwrap());
|
||||
let genesis = test_spec.genesis_block();
|
||||
assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap());
|
||||
|
||||
let _ = test_spec.to_engine();
|
||||
}
|
||||
}
|
||||
|
@ -558,7 +558,7 @@ fn should_not_trace_call_transaction_to_builtin() {
|
||||
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
let engine = Spec::new_test().to_engine().unwrap();
|
||||
let engine = Spec::new_test().engine;
|
||||
|
||||
let t = Transaction {
|
||||
nonce: x!(0),
|
||||
@ -583,7 +583,7 @@ fn should_not_trace_subcall_transaction_to_builtin() {
|
||||
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
let engine = Spec::new_test().to_engine().unwrap();
|
||||
let engine = Spec::new_test().engine;
|
||||
|
||||
let t = Transaction {
|
||||
nonce: x!(0),
|
||||
@ -624,7 +624,7 @@ fn should_not_trace_callcode() {
|
||||
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
let engine = Spec::new_test().to_engine().unwrap();
|
||||
let engine = Spec::new_test().engine;
|
||||
|
||||
let t = Transaction {
|
||||
nonce: x!(0),
|
||||
@ -667,7 +667,7 @@ fn should_not_trace_delegatecall() {
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
info.number = 0x789b0;
|
||||
let engine = Spec::new_test().to_engine().unwrap();
|
||||
let engine = Spec::new_test().engine;
|
||||
|
||||
println!("schedule.have_delegate_call: {:?}", engine.schedule(&info).have_delegate_call);
|
||||
|
||||
|
@ -40,8 +40,7 @@ fn returns_state_root_basic() {
|
||||
let client_result = generate_dummy_client(6);
|
||||
let client = client_result.reference();
|
||||
let test_spec = get_test_spec();
|
||||
let test_engine = test_spec.to_engine().unwrap();
|
||||
let state_root = test_engine.spec().genesis_header().state_root;
|
||||
let state_root = test_spec.genesis_header().state_root;
|
||||
|
||||
assert!(client.state_data(&state_root).is_some());
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ impl<T> GuardedTempResult<T> {
|
||||
|
||||
pub struct TestEngine {
|
||||
factory: Factory,
|
||||
spec: Spec,
|
||||
engine: Box<Engine>,
|
||||
max_depth: usize
|
||||
}
|
||||
|
||||
@ -60,18 +60,29 @@ impl TestEngine {
|
||||
pub fn new(max_depth: usize, factory: Factory) -> TestEngine {
|
||||
TestEngine {
|
||||
factory: factory,
|
||||
spec: ethereum::new_frontier_test(),
|
||||
engine: ethereum::new_frontier_test().engine,
|
||||
max_depth: max_depth
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Engine for TestEngine {
|
||||
fn name(&self) -> &str { "TestEngine" }
|
||||
fn spec(&self) -> &Spec { &self.spec }
|
||||
fn name(&self) -> &str {
|
||||
"TestEngine"
|
||||
}
|
||||
|
||||
fn params(&self) -> &CommonParams {
|
||||
self.engine.params()
|
||||
}
|
||||
|
||||
fn builtins(&self) -> &BTreeMap<Address, Builtin> {
|
||||
self.engine.builtins()
|
||||
}
|
||||
|
||||
fn vm_factory(&self) -> &Factory {
|
||||
&self.factory
|
||||
}
|
||||
|
||||
fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
|
||||
let mut schedule = Schedule::new_frontier();
|
||||
schedule.max_depth = self.max_depth;
|
||||
@ -136,17 +147,17 @@ pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult<Arc<Client>
|
||||
|
||||
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
|
||||
let test_spec = get_test_spec();
|
||||
let test_engine = test_spec.to_engine().unwrap();
|
||||
let state_root = test_engine.spec().genesis_header().state_root;
|
||||
let mut rolling_hash = test_engine.spec().genesis_header().hash();
|
||||
let test_engine = &test_spec.engine;
|
||||
let state_root = test_spec.genesis_header().state_root;
|
||||
let mut rolling_hash = test_spec.genesis_header().hash();
|
||||
let mut rolling_block_number = 1;
|
||||
let mut rolling_timestamp = 40;
|
||||
|
||||
for _ in 0..block_number {
|
||||
let mut header = Header::new();
|
||||
|
||||
header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
|
||||
header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||
header.gas_limit = test_engine.params().min_gas_limit;
|
||||
header.difficulty = U256::from(0x20000);
|
||||
header.timestamp = rolling_timestamp;
|
||||
header.number = rolling_block_number;
|
||||
header.parent_hash = rolling_hash;
|
||||
@ -171,8 +182,9 @@ pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult<Arc<Client>
|
||||
|
||||
pub fn push_blocks_to_client(client: &Arc<Client>, timestamp_salt: u64, starting_number: usize, block_number: usize) {
|
||||
let test_spec = get_test_spec();
|
||||
let test_engine = test_spec.to_engine().unwrap();
|
||||
let state_root = test_engine.spec().genesis_header().state_root;
|
||||
let test_engine = &test_spec.engine;
|
||||
//let test_engine = test_spec.to_engine().unwrap();
|
||||
let state_root = test_spec.genesis_header().state_root;
|
||||
let mut rolling_hash = client.chain_info().best_block_hash;
|
||||
let mut rolling_block_number = starting_number as u64;
|
||||
let mut rolling_timestamp = timestamp_salt + starting_number as u64 * 10;
|
||||
@ -180,8 +192,8 @@ pub fn push_blocks_to_client(client: &Arc<Client>, timestamp_salt: u64, starting
|
||||
for _ in 0..block_number {
|
||||
let mut header = Header::new();
|
||||
|
||||
header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
|
||||
header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||
header.gas_limit = test_engine.params().min_gas_limit;
|
||||
header.difficulty = U256::from(0x20000);
|
||||
header.timestamp = rolling_timestamp;
|
||||
header.number = rolling_block_number;
|
||||
header.parent_hash = rolling_hash;
|
||||
@ -279,24 +291,23 @@ pub fn get_temp_state_in(path: &Path) -> State {
|
||||
|
||||
pub fn get_good_dummy_block_seq(count: usize) -> Vec<Bytes> {
|
||||
let test_spec = get_test_spec();
|
||||
let test_engine = test_spec.to_engine().unwrap();
|
||||
get_good_dummy_block_fork_seq(1, count, &test_engine.spec().genesis_header().hash())
|
||||
get_good_dummy_block_fork_seq(1, count, &test_spec.genesis_header().hash())
|
||||
}
|
||||
|
||||
pub fn get_good_dummy_block_fork_seq(start_number: usize, count: usize, parent_hash: &H256) -> Vec<Bytes> {
|
||||
let test_spec = get_test_spec();
|
||||
let test_engine = test_spec.to_engine().unwrap();
|
||||
let test_engine = &test_spec.engine;
|
||||
let mut rolling_timestamp = start_number as u64 * 10;
|
||||
let mut parent = *parent_hash;
|
||||
let mut r = Vec::new();
|
||||
for i in start_number .. start_number + count + 1 {
|
||||
let mut block_header = Header::new();
|
||||
block_header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
|
||||
block_header.gas_limit = test_engine.params().min_gas_limit;
|
||||
block_header.difficulty = U256::from(i).mul(U256([0, 1, 0, 0]));
|
||||
block_header.timestamp = rolling_timestamp;
|
||||
block_header.number = i as u64;
|
||||
block_header.parent_hash = parent;
|
||||
block_header.state_root = test_engine.spec().genesis_header().state_root;
|
||||
block_header.state_root = test_spec.genesis_header().state_root;
|
||||
|
||||
parent = block_header.hash();
|
||||
rolling_timestamp = rolling_timestamp + 10;
|
||||
@ -310,13 +321,13 @@ pub fn get_good_dummy_block_fork_seq(start_number: usize, count: usize, parent_h
|
||||
pub fn get_good_dummy_block() -> Bytes {
|
||||
let mut block_header = Header::new();
|
||||
let test_spec = get_test_spec();
|
||||
let test_engine = test_spec.to_engine().unwrap();
|
||||
block_header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
|
||||
block_header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||
let test_engine = &test_spec.engine;
|
||||
block_header.gas_limit = test_engine.params().min_gas_limit;
|
||||
block_header.difficulty = U256::from(0x20000);
|
||||
block_header.timestamp = 40;
|
||||
block_header.number = 1;
|
||||
block_header.parent_hash = test_engine.spec().genesis_header().hash();
|
||||
block_header.state_root = test_engine.spec().genesis_header().state_root;
|
||||
block_header.parent_hash = test_spec.genesis_header().hash();
|
||||
block_header.state_root = test_spec.genesis_header().state_root;
|
||||
|
||||
create_test_block(&block_header)
|
||||
}
|
||||
@ -324,12 +335,12 @@ pub fn get_good_dummy_block() -> Bytes {
|
||||
pub fn get_bad_state_dummy_block() -> Bytes {
|
||||
let mut block_header = Header::new();
|
||||
let test_spec = get_test_spec();
|
||||
let test_engine = test_spec.to_engine().unwrap();
|
||||
block_header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
|
||||
block_header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||
let test_engine = &test_spec.engine;
|
||||
block_header.gas_limit = test_engine.params().min_gas_limit;
|
||||
block_header.difficulty = U256::from(0x20000);
|
||||
block_header.timestamp = 40;
|
||||
block_header.number = 1;
|
||||
block_header.parent_hash = test_engine.spec().genesis_header().hash();
|
||||
block_header.parent_hash = test_spec.genesis_header().hash();
|
||||
block_header.state_root = x!(0xbad);
|
||||
|
||||
create_test_block(&block_header)
|
||||
|
@ -183,7 +183,7 @@ fn verify_header(header: &Header, engine: &Engine) -> Result<(), Error> {
|
||||
if header.gas_used > header.gas_limit {
|
||||
return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit), min: None, found: header.gas_used })));
|
||||
}
|
||||
let min_gas_limit = decode(engine.spec().engine_params.get("minGasLimit").unwrap());
|
||||
let min_gas_limit = engine.params().min_gas_limit;
|
||||
if header.gas_limit < min_gas_limit {
|
||||
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: header.gas_limit })));
|
||||
}
|
||||
@ -336,12 +336,10 @@ mod tests {
|
||||
// Test against morden
|
||||
let mut good = Header::new();
|
||||
let spec = Spec::new_test();
|
||||
let engine = spec.to_engine().unwrap();
|
||||
let engine = &spec.engine;
|
||||
|
||||
let min_gas_limit = decode(engine.spec().engine_params.get("minGasLimit").unwrap());
|
||||
let min_difficulty = decode(engine.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||
let min_gas_limit = engine.params().min_gas_limit;
|
||||
good.gas_limit = min_gas_limit;
|
||||
good.difficulty = min_difficulty;
|
||||
good.timestamp = 40;
|
||||
good.number = 10;
|
||||
|
||||
|
1
fmt.sh
1
fmt.sh
@ -9,6 +9,7 @@ $RUSTFMT ./json/src/lib.rs
|
||||
$RUSTFMT ./miner/src/lib.rs
|
||||
$RUSTFMT ./parity/main.rs
|
||||
$RUSTFMT ./rpc/src/lib.rs
|
||||
$RUSTFMT ./webapp/src/lib.rs
|
||||
$RUSTFMT ./sync/src/lib.rs
|
||||
$RUSTFMT ./util/src/lib.rs
|
||||
|
||||
|
2
hook.sh
2
hook.sh
@ -7,6 +7,6 @@ echo "set -e" >> $FILE
|
||||
echo "cargo build --release --features dev" >> $FILE
|
||||
# Build tests
|
||||
echo "cargo test --no-run --features dev \\" >> $FILE
|
||||
echo " -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethminer" >> $FILE
|
||||
echo " -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethminer -p ethcore-webapp" >> $FILE
|
||||
echo "" >> $FILE
|
||||
chmod +x $FILE
|
||||
|
@ -21,7 +21,7 @@ use hash::H256;
|
||||
use blockchain::state::State;
|
||||
use blockchain::header::Header;
|
||||
use blockchain::block::Block;
|
||||
use spec::Genesis;
|
||||
use spec::{Genesis, Seal, Ethereum};
|
||||
|
||||
/// Blockchain deserialization.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
@ -54,10 +54,10 @@ impl BlockChain {
|
||||
/// Returns spec compatible genesis struct.
|
||||
pub fn genesis(&self) -> Genesis {
|
||||
Genesis {
|
||||
nonce: Some(self.genesis_block.nonce.clone()),
|
||||
mix_hash: Some(self.genesis_block.mix_hash.clone()),
|
||||
seal_fields: None,
|
||||
seal_rlp: None,
|
||||
seal: Seal::Ethereum(Ethereum {
|
||||
nonce: self.genesis_block.nonce.clone(),
|
||||
mix_hash: self.genesis_block.mix_hash.clone(),
|
||||
}),
|
||||
difficulty: self.genesis_block.difficulty,
|
||||
author: self.genesis_block.author.clone(),
|
||||
timestamp: self.genesis_block.timestamp,
|
||||
|
@ -22,7 +22,7 @@ use blockchain::account::Account;
|
||||
|
||||
/// Blockchain test state deserializer.
|
||||
#[derive(Debug, PartialEq, Deserialize, Clone)]
|
||||
pub struct State(pub BTreeMap<Address, Account>);
|
||||
pub struct State(BTreeMap<Address, Account>);
|
||||
|
||||
impl IntoIterator for State {
|
||||
type Item = <BTreeMap<Address, Account> as IntoIterator>::Item;
|
||||
|
@ -22,9 +22,19 @@ use spec::builtin::Builtin;
|
||||
/// Spec account.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct Account {
|
||||
builtin: Option<Builtin>,
|
||||
balance: Option<Uint>,
|
||||
nonce: Option<Uint>,
|
||||
/// Builtin contract.
|
||||
pub builtin: Option<Builtin>,
|
||||
/// Balance.
|
||||
pub balance: Option<Uint>,
|
||||
/// Nonce.
|
||||
pub nonce: Option<Uint>,
|
||||
}
|
||||
|
||||
impl Account {
|
||||
/// Returns true if account does not have nonce and balance.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.balance.is_none() && self.nonce.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -17,14 +17,16 @@
|
||||
//! Spec builtin deserialization.
|
||||
|
||||
/// Linear pricing.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[derive(Debug, PartialEq, Deserialize, Clone)]
|
||||
pub struct Linear {
|
||||
base: u64,
|
||||
word: u64,
|
||||
/// Base price.
|
||||
pub base: usize,
|
||||
/// Price for word.
|
||||
pub word: usize,
|
||||
}
|
||||
|
||||
/// Pricing variants.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[derive(Debug, PartialEq, Deserialize, Clone)]
|
||||
pub enum Pricing {
|
||||
/// Linear pricing.
|
||||
#[serde(rename="linear")]
|
||||
@ -32,10 +34,12 @@ pub enum Pricing {
|
||||
}
|
||||
|
||||
/// Spec builtin.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[derive(Debug, PartialEq, Deserialize, Clone)]
|
||||
pub struct Builtin {
|
||||
name: String,
|
||||
pricing: Pricing,
|
||||
/// Builtin name.
|
||||
pub name: String,
|
||||
/// Builtin pricing.
|
||||
pub pricing: Pricing,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
63
json/src/spec/engine.rs
Normal file
63
json/src/spec/engine.rs
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Engine deserialization.
|
||||
|
||||
use serde::{Deserializer, Error};
|
||||
use serde::de::Visitor;
|
||||
use spec::Ethash;
|
||||
|
||||
/// Engine deserialization.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub enum Engine {
|
||||
/// Null engine.
|
||||
Null,
|
||||
/// Ethash engine.
|
||||
Ethash(Ethash),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json;
|
||||
use spec::Engine;
|
||||
|
||||
#[test]
|
||||
fn engine_deserialization() {
|
||||
let s = r#"{
|
||||
"Null": null
|
||||
}"#;
|
||||
|
||||
let deserialized: Engine = serde_json::from_str(s).unwrap();
|
||||
assert_eq!(Engine::Null, deserialized);
|
||||
|
||||
let s = r#"{
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"tieBreakingGas": false,
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
|
||||
let _deserialized: Engine = serde_json::from_str(s).unwrap();
|
||||
}
|
||||
}
|
||||
|
75
json/src/spec/ethash.rs
Normal file
75
json/src/spec/ethash.rs
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Ethash params deserialization.
|
||||
|
||||
use uint::Uint;
|
||||
use hash::Address;
|
||||
|
||||
/// Ethash params deserialization.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct EthashParams {
|
||||
/// Tie breaking gas.
|
||||
#[serde(rename="tieBreakingGas")]
|
||||
pub tie_breaking_gas: bool,
|
||||
/// Gas limit divisor.
|
||||
#[serde(rename="gasLimitBoundDivisor")]
|
||||
pub gas_limit_bound_divisor: Uint,
|
||||
/// Minimum difficulty.
|
||||
#[serde(rename="minimumDifficulty")]
|
||||
pub minimum_difficulty: Uint,
|
||||
/// Difficulty bound divisor.
|
||||
#[serde(rename="difficultyBoundDivisor")]
|
||||
pub difficulty_bound_divisor: Uint,
|
||||
/// Block duration.
|
||||
#[serde(rename="durationLimit")]
|
||||
pub duration_limit: Uint,
|
||||
/// Block reward.
|
||||
#[serde(rename="blockReward")]
|
||||
pub block_reward: Uint,
|
||||
/// Namereg contract address.
|
||||
pub registrar: Address,
|
||||
}
|
||||
|
||||
/// Ethash engine deserialization.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct Ethash {
|
||||
/// Ethash params.
|
||||
pub params: EthashParams,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json;
|
||||
use spec::ethash::Ethash;
|
||||
|
||||
#[test]
|
||||
fn ethash_deserialization() {
|
||||
let s = r#"{
|
||||
"params": {
|
||||
"tieBreakingGas": false,
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
||||
}
|
||||
}"#;
|
||||
|
||||
let _deserialized: Ethash = serde_json::from_str(s).unwrap();
|
||||
}
|
||||
}
|
@ -17,27 +17,15 @@
|
||||
//! Spec genesis deserialization.
|
||||
|
||||
use uint::Uint;
|
||||
use hash::{H64, Address, H256};
|
||||
use hash::{Address, H256};
|
||||
use bytes::Bytes;
|
||||
use spec::Seal;
|
||||
|
||||
/// Spec genesis.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct Genesis {
|
||||
// old seal
|
||||
/// Seal nonce.
|
||||
pub nonce: Option<H64>,
|
||||
#[serde(rename="mixHash")]
|
||||
/// Seal mix hash.
|
||||
pub mix_hash: Option<H256>,
|
||||
|
||||
// new seal // TODO: consider moving it to a separate seal structure
|
||||
#[serde(rename="sealFields")]
|
||||
/// Number of seal fields.
|
||||
pub seal_fields: Option<usize>,
|
||||
#[serde(rename="sealRlp")]
|
||||
/// Seal rlp.
|
||||
pub seal_rlp: Option<Bytes>,
|
||||
|
||||
/// Seal.
|
||||
pub seal: Seal,
|
||||
/// Difficulty.
|
||||
pub difficulty: Uint,
|
||||
/// Block author.
|
||||
@ -77,7 +65,12 @@ mod tests {
|
||||
let s = r#"{
|
||||
"nonce": "0x0000000000000042",
|
||||
"difficulty": "0x400000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x00006d6f7264656e"
|
||||
}
|
||||
},
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
@ -21,9 +21,17 @@ pub mod builtin;
|
||||
pub mod genesis;
|
||||
pub mod params;
|
||||
pub mod spec;
|
||||
pub mod seal;
|
||||
pub mod engine;
|
||||
pub mod state;
|
||||
pub mod ethash;
|
||||
|
||||
pub use self::account::Account;
|
||||
pub use self::builtin::Builtin;
|
||||
pub use self::builtin::{Builtin, Pricing, Linear};
|
||||
pub use self::genesis::Genesis;
|
||||
pub use self::params::Params;
|
||||
pub use self::spec::Spec;
|
||||
pub use self::seal::{Seal, Ethereum, Generic};
|
||||
pub use self::engine::Engine;
|
||||
pub use self::state::State;
|
||||
pub use self::ethash::{Ethash, EthashParams};
|
||||
|
@ -17,34 +17,25 @@
|
||||
//! Spec params deserialization.
|
||||
|
||||
use uint::Uint;
|
||||
use hash::Address;
|
||||
|
||||
/// Spec params.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct Params {
|
||||
/// Account start nonce.
|
||||
#[serde(rename="accountStartNonce")]
|
||||
account_start_nonce: Uint,
|
||||
pub account_start_nonce: Uint,
|
||||
/// Homestead transition block number.
|
||||
#[serde(rename="frontierCompatibilityModeLimit")]
|
||||
frontier_compatibility_mode_limit: Uint,
|
||||
pub frontier_compatibility_mode_limit: Uint,
|
||||
/// Maximum size of extra data.
|
||||
#[serde(rename="maximumExtraDataSize")]
|
||||
maximum_extra_data_size: Uint,
|
||||
#[serde(rename="tieBreakingGas")]
|
||||
tie_breaking_gas: bool,
|
||||
#[serde(rename="minGasLimit")]
|
||||
min_gas_limit: Uint,
|
||||
#[serde(rename="gasLimitBoundDivisor")]
|
||||
gas_limit_bound_divisor: Uint,
|
||||
#[serde(rename="minimumDifficulty")]
|
||||
minimum_difficulty: Uint,
|
||||
#[serde(rename="difficultyBoundDivisor")]
|
||||
difficulty_bound_divisor: Uint,
|
||||
#[serde(rename="durationLimit")]
|
||||
duration_limit: Uint,
|
||||
#[serde(rename="blockReward")]
|
||||
block_reward: Uint,
|
||||
registrar: Address,
|
||||
pub maximum_extra_data_size: Uint,
|
||||
/// Network id.
|
||||
#[serde(rename="networkID")]
|
||||
network_id: Uint,
|
||||
pub network_id: Uint,
|
||||
/// Minimum gas limit.
|
||||
#[serde(rename="minGasLimit")]
|
||||
pub min_gas_limit: Uint,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -55,19 +46,13 @@ mod tests {
|
||||
#[test]
|
||||
fn params_deserialization() {
|
||||
let s = r#"{
|
||||
"accountStartNonce": "0x00",
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"tieBreakingGas": false,
|
||||
"networkID" : "0x1",
|
||||
"minGasLimit": "0x1388",
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"networkID" : "0x1"
|
||||
"accountStartNonce": "0x00"
|
||||
}"#;
|
||||
|
||||
let _deserialized: Params = serde_json::from_str(s).unwrap();
|
||||
// TODO: validate all fields
|
||||
}
|
||||
|
73
json/src/spec/seal.rs
Normal file
73
json/src/spec/seal.rs
Normal file
@ -0,0 +1,73 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Spec seal deserialization.
|
||||
|
||||
use hash::{H64, H256};
|
||||
use bytes::Bytes;
|
||||
|
||||
/// Ethereum seal.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct Ethereum {
|
||||
/// Seal nonce.
|
||||
pub nonce: H64,
|
||||
/// Seal mix hash.
|
||||
#[serde(rename="mixHash")]
|
||||
pub mix_hash: H256,
|
||||
}
|
||||
|
||||
/// Generic seal.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct Generic {
|
||||
/// Number of fields.
|
||||
pub fields: usize,
|
||||
/// Their rlp.
|
||||
pub rlp: Bytes,
|
||||
}
|
||||
|
||||
/// Seal variants.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub enum Seal {
|
||||
/// Ethereum seal.
|
||||
#[serde(rename="ethereum")]
|
||||
Ethereum(Ethereum),
|
||||
/// Generic seal.
|
||||
#[serde(rename="generic")]
|
||||
Generic(Generic),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json;
|
||||
use spec::Seal;
|
||||
|
||||
#[test]
|
||||
fn builtin_deserialization() {
|
||||
let s = r#"[{
|
||||
"ethereum": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},{
|
||||
"generic": {
|
||||
"fields": 1,
|
||||
"rlp": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"
|
||||
}
|
||||
}]"#;
|
||||
let _deserialized: Vec<Seal> = serde_json::from_str(s).unwrap();
|
||||
// TODO: validate all fields
|
||||
}
|
||||
}
|
@ -16,21 +16,33 @@
|
||||
|
||||
//! Spec deserialization.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use hash::Address;
|
||||
use spec::account::Account;
|
||||
use spec::params::Params;
|
||||
use spec::genesis::Genesis;
|
||||
use std::io::Read;
|
||||
use serde_json;
|
||||
use serde_json::Error;
|
||||
use spec::{Params, Genesis, Engine, State};
|
||||
|
||||
/// Spec deserialization.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct Spec {
|
||||
name: String,
|
||||
#[serde(rename="engineName")]
|
||||
engine_name: String, // TODO: consider making it an enum
|
||||
params: Params,
|
||||
genesis: Genesis,
|
||||
accounts: BTreeMap<Address, Account>,
|
||||
/// Spec name.
|
||||
pub name: String,
|
||||
/// Engine.
|
||||
pub engine: Engine,
|
||||
/// Spec params.
|
||||
pub params: Params,
|
||||
/// Genesis header.
|
||||
pub genesis: Genesis,
|
||||
/// Genesis state.
|
||||
pub accounts: State,
|
||||
/// Boot nodes.
|
||||
pub nodes: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl Spec {
|
||||
/// Loads test from json.
|
||||
pub fn load<R>(reader: R) -> Result<Self, Error> where R: Read {
|
||||
serde_json::from_reader(reader)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -42,25 +54,34 @@ mod tests {
|
||||
fn spec_deserialization() {
|
||||
let s = r#"{
|
||||
"name": "Morden",
|
||||
"engineName": "Ethash",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"tieBreakingGas": false,
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x0100000",
|
||||
"frontierCompatibilityModeLimit": "0x789b0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"tieBreakingGas": false,
|
||||
"minGasLimit": "0x1388",
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar": "",
|
||||
"networkID" : "0x2"
|
||||
},
|
||||
"genesis": {
|
||||
"nonce": "0x00006d6f7264656e",
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x00006d6f7264656e"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x20000",
|
||||
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
44
json/src/spec/state.rs
Normal file
44
json/src/spec/state.rs
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Blockchain test state deserializer.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use hash::Address;
|
||||
use spec::{Account, Builtin};
|
||||
|
||||
/// Blockchain test state deserializer.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct State(BTreeMap<Address, Account>);
|
||||
|
||||
impl State {
|
||||
/// Returns all builtins.
|
||||
pub fn builtins(&self) -> BTreeMap<Address, Builtin> {
|
||||
self.0
|
||||
.iter()
|
||||
.filter_map(|ref pair| pair.1.builtin.clone().map(|b| (pair.0.clone(), b.clone())))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for State {
|
||||
type Item = <BTreeMap<Address, Account> as IntoIterator>::Item;
|
||||
type IntoIter = <BTreeMap<Address, Account> as IntoIterator>::IntoIter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@ use util::numbers::{U256, Uint as U};
|
||||
|
||||
/// Lenient uint json deserialization for test json files.
|
||||
#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||
pub struct Uint(U256);
|
||||
pub struct Uint(pub U256);
|
||||
|
||||
impl Into<U256> for Uint {
|
||||
fn into(self) -> U256 {
|
||||
@ -37,9 +37,15 @@ impl Into<u64> for Uint {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<usize> for Uint {
|
||||
fn into(self) -> usize {
|
||||
// TODO: clean it after util conversions refactored.
|
||||
u64::from(self.0) as usize
|
||||
}
|
||||
}
|
||||
impl Into<u8> for Uint {
|
||||
fn into(self) -> u8 {
|
||||
<Uint as Into<u64>>::into(self) as u8
|
||||
u64::from(self.0) as u8
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,6 +61,10 @@ struct UintVisitor;
|
||||
impl Visitor for UintVisitor {
|
||||
type Value = Uint;
|
||||
|
||||
fn visit_u64<E>(&mut self, value: u64) -> Result<Self::Value, E> where E: Error {
|
||||
Ok(Uint(U256::from(value)))
|
||||
}
|
||||
|
||||
fn visit_str<E>(&mut self, value: &str) -> Result<Self::Value, E> where E: Error {
|
||||
let value = match value.len() {
|
||||
0 => U256::from(0),
|
||||
@ -83,12 +93,13 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn uint_deserialization() {
|
||||
let s = r#"["0xa", "10", "", "0x"]"#;
|
||||
let s = r#"["0xa", "10", "", "0x", 0]"#;
|
||||
let deserialized: Vec<Uint> = serde_json::from_str(s).unwrap();
|
||||
assert_eq!(deserialized, vec![
|
||||
Uint(U256::from(10)),
|
||||
Uint(U256::from(10)),
|
||||
Uint(U256::from(0)),
|
||||
Uint(U256::from(0)),
|
||||
Uint(U256::from(0))
|
||||
]);
|
||||
}
|
||||
|
@ -105,9 +105,12 @@ pub trait MinerService : Send + Sync {
|
||||
/// Get the sealing work package and if `Some`, apply some transform.
|
||||
fn map_sealing_work<F, T>(&self, chain: &BlockChainClient, f: F) -> Option<T> where F: FnOnce(&ClosedBlock) -> T;
|
||||
|
||||
/// Query pending transactions for hash
|
||||
/// Query pending transactions for hash.
|
||||
fn transaction(&self, hash: &H256) -> Option<SignedTransaction>;
|
||||
|
||||
/// Get a list of all pending transactions.
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction>;
|
||||
|
||||
/// Returns highest transaction nonce for given address.
|
||||
fn last_nonce(&self, address: &Address) -> Option<U256>;
|
||||
|
||||
|
@ -228,6 +228,11 @@ impl MinerService for Miner {
|
||||
queue.find(hash)
|
||||
}
|
||||
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction> {
|
||||
let queue = self.transaction_queue.lock().unwrap();
|
||||
queue.top_transactions()
|
||||
}
|
||||
|
||||
fn last_nonce(&self, address: &Address) -> Option<U256> {
|
||||
self.transaction_queue.lock().unwrap().last_nonce(address)
|
||||
}
|
||||
|
208
parity/main.rs
208
parity/main.rs
@ -42,6 +42,8 @@ extern crate rpassword;
|
||||
|
||||
#[cfg(feature = "rpc")]
|
||||
extern crate ethcore_rpc as rpc;
|
||||
#[cfg(feature = "webapp")]
|
||||
extern crate ethcore_webapp as webapp;
|
||||
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::fs::File;
|
||||
@ -63,6 +65,10 @@ use ethminer::{Miner, MinerService};
|
||||
use docopt::Docopt;
|
||||
use daemonize::Daemonize;
|
||||
use number_prefix::{binary_prefix, Standalone, Prefixed};
|
||||
#[cfg(feature = "rpc")]
|
||||
use rpc::Server as RpcServer;
|
||||
#[cfg(feature = "webapp")]
|
||||
use webapp::Listening as WebappServer;
|
||||
|
||||
mod price_info;
|
||||
|
||||
@ -98,7 +104,8 @@ Protocol Options:
|
||||
--identity NAME Specify your node's name.
|
||||
|
||||
Account Options:
|
||||
--unlock ACCOUNT Unlock ACCOUNT for the duration of the execution.
|
||||
--unlock ACCOUNTS Unlock ACCOUNTS for the duration of the execution.
|
||||
ACCOUNTS is a comma-delimited list of addresses.
|
||||
--password FILE Provide a file containing a password for unlocking
|
||||
an account.
|
||||
|
||||
@ -118,7 +125,7 @@ Networking Options:
|
||||
string or input to SHA3 operation.
|
||||
|
||||
API and Console Options:
|
||||
-j --jsonrpc Enable the JSON-RPC API sever.
|
||||
-j --jsonrpc Enable the JSON-RPC API server.
|
||||
--jsonrpc-interface IP Specify the hostname portion of the JSONRPC API
|
||||
server, IP should be an interface's IP address, or
|
||||
all (all interfaces) or local [default: local].
|
||||
@ -130,6 +137,19 @@ API and Console Options:
|
||||
interface. APIS is a comma-delimited list of API
|
||||
name. Possible name are web3, eth and net.
|
||||
[default: web3,eth,net,personal].
|
||||
-w --webapp Enable the web applications server (e.g.
|
||||
status page).
|
||||
--webapp-port PORT Specify the port portion of the WebApps server
|
||||
[default: 8080].
|
||||
--webapp-interface IP Specify the hostname portion of the WebApps
|
||||
server, IP should be an interface's IP address, or
|
||||
all (all interfaces) or local [default: local].
|
||||
--webapp-user USERNAME Specify username for WebApps server. It will be
|
||||
used in HTTP Basic Authentication Scheme.
|
||||
If --webapp-pass is not specified you will be
|
||||
asked for password on startup.
|
||||
--webapp-pass PASSWORD Specify password for WebApps server. Use only in
|
||||
conjunction with --webapp-user.
|
||||
|
||||
Sealing/Mining Options:
|
||||
--usd-per-tx USD Amount of USD to be paid for a basic transaction
|
||||
@ -194,7 +214,7 @@ struct Args {
|
||||
flag_chain: String,
|
||||
flag_db_path: String,
|
||||
flag_identity: String,
|
||||
flag_unlock: Vec<String>,
|
||||
flag_unlock: Option<String>,
|
||||
flag_password: Vec<String>,
|
||||
flag_cache: Option<usize>,
|
||||
flag_keys_path: String,
|
||||
@ -214,6 +234,11 @@ struct Args {
|
||||
flag_jsonrpc_port: u16,
|
||||
flag_jsonrpc_cors: String,
|
||||
flag_jsonrpc_apis: String,
|
||||
flag_webapp: bool,
|
||||
flag_webapp_port: u16,
|
||||
flag_webapp_interface: String,
|
||||
flag_webapp_user: Option<String>,
|
||||
flag_webapp_pass: Option<String>,
|
||||
flag_author: String,
|
||||
flag_usd_per_tx: String,
|
||||
flag_usd_per_eth: String,
|
||||
@ -270,10 +295,10 @@ fn setup_rpc_server(
|
||||
sync: Arc<EthSync>,
|
||||
secret_store: Arc<AccountService>,
|
||||
miner: Arc<Miner>,
|
||||
url: &str,
|
||||
url: &SocketAddr,
|
||||
cors_domain: &str,
|
||||
apis: Vec<&str>
|
||||
) -> Option<Arc<PanicHandler>> {
|
||||
apis: Vec<&str>,
|
||||
) -> RpcServer {
|
||||
use rpc::v1::*;
|
||||
|
||||
let server = rpc::RpcServer::new();
|
||||
@ -291,9 +316,50 @@ fn setup_rpc_server(
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(server.start_http(url, cors_domain, ::num_cpus::get()))
|
||||
let start_result = server.start_http(url, cors_domain);
|
||||
match start_result {
|
||||
Err(rpc::RpcServerError::IoError(err)) => die_with_io_error(err),
|
||||
Err(e) => die!("{:?}", e),
|
||||
Ok(server) => server,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "webapp")]
|
||||
fn setup_webapp_server(
|
||||
client: Arc<Client>,
|
||||
sync: Arc<EthSync>,
|
||||
secret_store: Arc<AccountService>,
|
||||
miner: Arc<Miner>,
|
||||
url: &str,
|
||||
auth: Option<(String, String)>,
|
||||
) -> WebappServer {
|
||||
use rpc::v1::*;
|
||||
|
||||
let server = webapp::WebappServer::new();
|
||||
server.add_delegate(Web3Client::new().to_delegate());
|
||||
server.add_delegate(NetClient::new(&sync).to_delegate());
|
||||
server.add_delegate(EthClient::new(&client, &sync, &secret_store, &miner).to_delegate());
|
||||
server.add_delegate(EthFilterClient::new(&client, &miner).to_delegate());
|
||||
server.add_delegate(PersonalClient::new(&secret_store).to_delegate());
|
||||
let start_result = match auth {
|
||||
None => {
|
||||
server.start_unsecure_http(url, ::num_cpus::get())
|
||||
},
|
||||
Some((username, password)) => {
|
||||
server.start_basic_auth_http(url, ::num_cpus::get(), &username, &password)
|
||||
},
|
||||
};
|
||||
match start_result {
|
||||
Err(webapp::WebappServerError::IoError(err)) => die_with_io_error(err),
|
||||
Err(e) => die!("{:?}", e),
|
||||
Ok(handle) => handle,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "rpc"))]
|
||||
struct RpcServer;
|
||||
|
||||
#[cfg(not(feature = "rpc"))]
|
||||
fn setup_rpc_server(
|
||||
_client: Arc<Client>,
|
||||
@ -302,9 +368,24 @@ fn setup_rpc_server(
|
||||
_miner: Arc<Miner>,
|
||||
_url: &str,
|
||||
_cors_domain: &str,
|
||||
_apis: Vec<&str>
|
||||
) -> Option<Arc<PanicHandler>> {
|
||||
None
|
||||
_apis: Vec<&str>,
|
||||
) -> ! {
|
||||
die!("Your Parity version has been compiled without JSON-RPC support.")
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "webapp"))]
|
||||
struct WebappServer;
|
||||
|
||||
#[cfg(not(feature = "webapp"))]
|
||||
fn setup_webapp_server(
|
||||
_client: Arc<Client>,
|
||||
_sync: Arc<EthSync>,
|
||||
_secret_store: Arc<AccountService>,
|
||||
_miner: Arc<Miner>,
|
||||
_url: &str,
|
||||
_auth: Option<(String, String)>,
|
||||
) -> ! {
|
||||
die!("Your Parity version has been compiled without WebApps support.")
|
||||
}
|
||||
|
||||
fn print_version() {
|
||||
@ -396,7 +477,7 @@ impl Configuration {
|
||||
"frontier" | "homestead" | "mainnet" => ethereum::new_frontier(),
|
||||
"morden" | "testnet" => ethereum::new_morden(),
|
||||
"olympic" => ethereum::new_olympic(),
|
||||
f => Spec::from_json_utf8(contents(f).unwrap_or_else(|_| {
|
||||
f => Spec::load(contents(f).unwrap_or_else(|_| {
|
||||
die!("{}: Couldn't read chain specification file. Sure it exists?", f)
|
||||
}).as_ref()),
|
||||
}
|
||||
@ -537,14 +618,15 @@ impl Configuration {
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
let account_service = AccountService::new_in(Path::new(&self.keys_path()));
|
||||
for d in &self.args.flag_unlock {
|
||||
let a = Address::from_str(clean_0x(&d)).unwrap_or_else(|_| {
|
||||
die!("{}: Invalid address for --unlock. Must be 40 hex characters, without the 0x at the beginning.", d)
|
||||
});
|
||||
if passwords.iter().find(|p| account_service.unlock_account_no_expire(&a, p).is_ok()).is_none() {
|
||||
die!("No password given to unlock account {}. Pass the password using `--password`.", a);
|
||||
if let Some(ref unlocks) = self.args.flag_unlock {
|
||||
for d in unlocks.split(',') {
|
||||
let a = Address::from_str(clean_0x(&d)).unwrap_or_else(|_| {
|
||||
die!("{}: Invalid address for --unlock. Must be 40 hex characters, without the 0x at the beginning.", d)
|
||||
});
|
||||
if passwords.iter().find(|p| account_service.unlock_account_no_expire(&a, p).is_ok()).is_none() {
|
||||
die!("No password given to unlock account {}. Pass the password using `--password`.", a);
|
||||
}
|
||||
}
|
||||
}
|
||||
account_service
|
||||
@ -567,7 +649,10 @@ impl Configuration {
|
||||
let account_service = Arc::new(self.account_service());
|
||||
|
||||
// Build client
|
||||
let mut service = ClientService::start(self.client_config(), spec, net_settings, &Path::new(&self.path())).unwrap();
|
||||
let mut service = ClientService::start(
|
||||
self.client_config(), spec, net_settings, &Path::new(&self.path())
|
||||
).unwrap_or_else(|e| die_with_error(e));
|
||||
|
||||
panic_handler.forward_from(&service);
|
||||
let client = service.client();
|
||||
|
||||
@ -582,7 +667,8 @@ impl Configuration {
|
||||
let sync = EthSync::register(service.network(), sync_config, client.clone(), miner.clone());
|
||||
|
||||
// Setup rpc
|
||||
if self.args.flag_jsonrpc || self.args.flag_rpc {
|
||||
let rpc_server = if self.args.flag_jsonrpc || self.args.flag_rpc {
|
||||
let apis = self.args.flag_rpcapi.as_ref().unwrap_or(&self.args.flag_jsonrpc_apis);
|
||||
let url = format!("{}:{}",
|
||||
match self.args.flag_rpcaddr.as_ref().unwrap_or(&self.args.flag_jsonrpc_interface).as_str() {
|
||||
"all" => "0.0.0.0",
|
||||
@ -591,23 +677,53 @@ impl Configuration {
|
||||
},
|
||||
self.args.flag_rpcport.unwrap_or(self.args.flag_jsonrpc_port)
|
||||
);
|
||||
SocketAddr::from_str(&url).unwrap_or_else(|_| die!("{}: Invalid JSONRPC listen host/port given.", url));
|
||||
let cors = self.args.flag_rpccorsdomain.as_ref().unwrap_or(&self.args.flag_jsonrpc_cors);
|
||||
// TODO: use this as the API list.
|
||||
let apis = self.args.flag_rpcapi.as_ref().unwrap_or(&self.args.flag_jsonrpc_apis);
|
||||
let server_handler = setup_rpc_server(
|
||||
let addr = SocketAddr::from_str(&url).unwrap_or_else(|_| die!("{}: Invalid JSONRPC listen host/port given.", url));
|
||||
let cors_domain = self.args.flag_rpccorsdomain.as_ref().unwrap_or(&self.args.flag_jsonrpc_cors);
|
||||
|
||||
Some(setup_rpc_server(
|
||||
service.client(),
|
||||
sync.clone(),
|
||||
account_service.clone(),
|
||||
miner.clone(),
|
||||
&addr,
|
||||
&cors_domain,
|
||||
apis.split(',').collect()
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let webapp_server = if self.args.flag_webapp {
|
||||
let url = format!("{}:{}",
|
||||
match self.args.flag_webapp_interface.as_str() {
|
||||
"all" => "0.0.0.0",
|
||||
"local" => "127.0.0.1",
|
||||
x => x,
|
||||
},
|
||||
self.args.flag_webapp_port
|
||||
);
|
||||
let auth = self.args.flag_webapp_user.as_ref().map(|username| {
|
||||
let password = self.args.flag_webapp_pass.as_ref().map_or_else(|| {
|
||||
use rpassword::read_password;
|
||||
println!("Type password for WebApps server (user: {}): ", username);
|
||||
let pass = read_password().unwrap();
|
||||
println!("OK, got it. Starting server...");
|
||||
pass
|
||||
}, |pass| pass.to_owned());
|
||||
(username.to_owned(), password)
|
||||
});
|
||||
|
||||
Some(setup_webapp_server(
|
||||
service.client(),
|
||||
sync.clone(),
|
||||
account_service.clone(),
|
||||
miner.clone(),
|
||||
&url,
|
||||
cors,
|
||||
apis.split(',').collect()
|
||||
);
|
||||
if let Some(handler) = server_handler {
|
||||
panic_handler.forward_from(handler.deref());
|
||||
}
|
||||
}
|
||||
auth,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Register IO handler
|
||||
let io_handler = Arc::new(ClientIoHandler {
|
||||
@ -619,11 +735,11 @@ impl Configuration {
|
||||
service.io().register_handler(io_handler).expect("Error registering IO handler");
|
||||
|
||||
// Handle exit
|
||||
wait_for_exit(panic_handler);
|
||||
wait_for_exit(panic_handler, rpc_server, webapp_server);
|
||||
}
|
||||
}
|
||||
|
||||
fn wait_for_exit(panic_handler: Arc<PanicHandler>) {
|
||||
fn wait_for_exit(panic_handler: Arc<PanicHandler>, _rpc_server: Option<RpcServer>, _webapp_server: Option<WebappServer>) {
|
||||
let exit = Arc::new(Condvar::new());
|
||||
|
||||
// Handle possible exits
|
||||
@ -637,6 +753,30 @@ fn wait_for_exit(panic_handler: Arc<PanicHandler>) {
|
||||
// Wait for signal
|
||||
let mutex = Mutex::new(());
|
||||
let _ = exit.wait(mutex.lock().unwrap()).unwrap();
|
||||
info!("Finishing work, please wait...");
|
||||
}
|
||||
|
||||
fn die_with_error(e: ethcore::error::Error) -> ! {
|
||||
use ethcore::error::Error;
|
||||
|
||||
match e {
|
||||
Error::Util(UtilError::StdIo(e)) => die_with_io_error(e),
|
||||
_ => die!("{:?}", e),
|
||||
}
|
||||
}
|
||||
fn die_with_io_error(e: std::io::Error) -> ! {
|
||||
match e.kind() {
|
||||
std::io::ErrorKind::PermissionDenied => {
|
||||
die!("No permissions to bind to specified port.")
|
||||
},
|
||||
std::io::ErrorKind::AddrInUse => {
|
||||
die!("Specified address is already in use. Please make sure that nothing is listening on the same port or try using a different one.")
|
||||
},
|
||||
std::io::ErrorKind::AddrNotAvailable => {
|
||||
die!("Could not use specified interface or given address is invalid.")
|
||||
},
|
||||
_ => die!("{:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -13,7 +13,7 @@ log = "0.3"
|
||||
serde = "0.7.0"
|
||||
serde_json = "0.7.0"
|
||||
jsonrpc-core = "2.0"
|
||||
jsonrpc-http-server = "3.0"
|
||||
jsonrpc-http-server = { git = "https://github.com/debris/jsonrpc-http-server.git" }
|
||||
ethcore-util = { path = "../util" }
|
||||
ethcore = { path = "../ethcore" }
|
||||
ethash = { path = "../ethash" }
|
||||
|
@ -33,10 +33,10 @@ extern crate ethminer;
|
||||
extern crate transient_hashmap;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use util::panics::PanicHandler;
|
||||
use std::net::SocketAddr;
|
||||
use self::jsonrpc_core::{IoHandler, IoDelegate};
|
||||
|
||||
pub use jsonrpc_http_server::{Server, RpcServerError};
|
||||
pub mod v1;
|
||||
|
||||
/// Http server.
|
||||
@ -45,7 +45,7 @@ pub struct RpcServer {
|
||||
}
|
||||
|
||||
impl RpcServer {
|
||||
/// Construct new http server object with given number of threads.
|
||||
/// Construct new http server object.
|
||||
pub fn new() -> RpcServer {
|
||||
RpcServer {
|
||||
handler: Arc::new(IoHandler::new()),
|
||||
@ -57,18 +57,9 @@ impl RpcServer {
|
||||
self.handler.add_delegate(delegate);
|
||||
}
|
||||
|
||||
/// Start server asynchronously in new thread and returns panic handler.
|
||||
pub fn start_http(&self, addr: &str, cors_domain: &str, threads: usize) -> Arc<PanicHandler> {
|
||||
let addr = addr.to_owned();
|
||||
/// Start server asynchronously and returns result with `Server` handle on success or an error.
|
||||
pub fn start_http(&self, addr: &SocketAddr, cors_domain: &str) -> Result<Server, RpcServerError> {
|
||||
let cors_domain = cors_domain.to_owned();
|
||||
let panic_handler = PanicHandler::new_in_arc();
|
||||
let ph = panic_handler.clone();
|
||||
let server = jsonrpc_http_server::Server::new(self.handler.clone());
|
||||
thread::Builder::new().name("jsonrpc_http".to_string()).spawn(move || {
|
||||
ph.catch_panic(move || {
|
||||
server.start(addr.as_ref(), jsonrpc_http_server::AccessControlAllowOrigin::Value(cors_domain), threads);
|
||||
}).unwrap()
|
||||
}).expect("Error while creating jsonrpc http thread");
|
||||
panic_handler
|
||||
Server::start(addr, self.handler.clone(), jsonrpc_http_server::AccessControlAllowOrigin::Value(cors_domain))
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,6 @@ impl<F, T> PollManager<F, T> where T: Timer {
|
||||
}
|
||||
|
||||
// Implementation is always using `poll_mut`
|
||||
#[cfg(test)]
|
||||
/// Get a reference to stored poll filter
|
||||
pub fn poll(&mut self, id: &PollId) -> Option<&F> {
|
||||
self.polls.prune();
|
||||
|
@ -186,7 +186,7 @@ impl<C, S, A, M, EM> EthClient<C, S, A, M, EM>
|
||||
}.fake_sign(from))
|
||||
}
|
||||
|
||||
fn dispatch_transaction(&self, signed_transaction: SignedTransaction, raw_transaction: Vec<u8>) -> Result<Value, Error> {
|
||||
fn dispatch_transaction(&self, signed_transaction: SignedTransaction) -> Result<Value, Error> {
|
||||
let hash = signed_transaction.hash();
|
||||
|
||||
let import = {
|
||||
@ -203,7 +203,6 @@ impl<C, S, A, M, EM> EthClient<C, S, A, M, EM>
|
||||
|
||||
match import.into_iter().collect::<Result<Vec<_>, _>>() {
|
||||
Ok(_) => {
|
||||
take_weak!(self.sync).new_transaction(raw_transaction);
|
||||
to_value(&hash)
|
||||
}
|
||||
Err(e) => {
|
||||
@ -466,7 +465,7 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
|
||||
|
||||
fn submit_work(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(H64, H256, H256)>(params).and_then(|(nonce, pow_hash, mix_hash)| {
|
||||
// trace!("Decoded: nonce={}, pow_hash={}, mix_hash={}", nonce, pow_hash, mix_hash);
|
||||
trace!(target: "miner", "submit_work: Decoded: nonce={}, pow_hash={}, mix_hash={}", nonce, pow_hash, mix_hash);
|
||||
let miner = take_weak!(self.miner);
|
||||
let client = take_weak!(self.client);
|
||||
let seal = vec![encode(&mix_hash).to_vec(), encode(&nonce).to_vec()];
|
||||
@ -504,8 +503,7 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
|
||||
data: request.data.map_or_else(Vec::new, |d| d.to_vec()),
|
||||
}.sign(&secret)
|
||||
};
|
||||
let raw_transaction = encode(&signed_transaction).to_vec();
|
||||
self.dispatch_transaction(signed_transaction, raw_transaction)
|
||||
self.dispatch_transaction(signed_transaction)
|
||||
},
|
||||
Err(_) => { to_value(&H256::zero()) }
|
||||
}
|
||||
@ -517,7 +515,7 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
|
||||
.and_then(|(raw_transaction, )| {
|
||||
let raw_transaction = raw_transaction.to_vec();
|
||||
match UntrustedRlp::new(&raw_transaction).as_val() {
|
||||
Ok(signed_transaction) => self.dispatch_transaction(signed_transaction, raw_transaction),
|
||||
Ok(signed_transaction) => self.dispatch_transaction(signed_transaction),
|
||||
Err(_) => to_value(&H256::zero()),
|
||||
}
|
||||
})
|
||||
@ -638,10 +636,11 @@ impl<C, M> EthFilter for EthFilterClient<C, M>
|
||||
|
||||
to_value(&diff)
|
||||
},
|
||||
PollFilter::Logs(ref mut block_number, ref mut filter) => {
|
||||
PollFilter::Logs(ref mut block_number, ref filter) => {
|
||||
let mut filter = filter.clone();
|
||||
filter.from_block = BlockId::Number(*block_number);
|
||||
filter.to_block = BlockId::Latest;
|
||||
let logs = client.logs(filter.clone())
|
||||
let logs = client.logs(filter)
|
||||
.into_iter()
|
||||
.map(From::from)
|
||||
.collect::<Vec<Log>>();
|
||||
@ -656,6 +655,24 @@ impl<C, M> EthFilter for EthFilterClient<C, M>
|
||||
})
|
||||
}
|
||||
|
||||
fn filter_logs(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(Index,)>(params)
|
||||
.and_then(|(index,)| {
|
||||
let mut polls = self.polls.lock().unwrap();
|
||||
match polls.poll(&index.value()) {
|
||||
Some(&PollFilter::Logs(ref _block_number, ref filter)) => {
|
||||
let logs = take_weak!(self.client).logs(filter.clone())
|
||||
.into_iter()
|
||||
.map(From::from)
|
||||
.collect::<Vec<Log>>();
|
||||
to_value(&logs)
|
||||
},
|
||||
// just empty array
|
||||
_ => Ok(Value::Array(vec![] as Vec<Value>)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn uninstall_filter(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(Index,)>(params)
|
||||
.and_then(|(index,)| {
|
||||
|
@ -98,6 +98,10 @@ impl MinerService for TestMinerService {
|
||||
self.pending_transactions.lock().unwrap().get(hash).cloned()
|
||||
}
|
||||
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction> {
|
||||
self.pending_transactions.lock().unwrap().values().cloned().collect()
|
||||
}
|
||||
|
||||
fn last_nonce(&self, address: &Address) -> Option<U256> {
|
||||
self.last_nonces.read().unwrap().get(address).cloned()
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
//! Test implementation of SyncProvider.
|
||||
|
||||
use util::{U256, Bytes};
|
||||
use util::{U256};
|
||||
use ethsync::{SyncProvider, SyncStatus, SyncState};
|
||||
use std::sync::{RwLock};
|
||||
|
||||
@ -59,8 +59,5 @@ impl SyncProvider for TestSyncProvider {
|
||||
fn status(&self) -> SyncStatus {
|
||||
self.status.read().unwrap().clone()
|
||||
}
|
||||
|
||||
fn new_transaction(&self, _raw_transaction: Bytes) {
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,7 +190,7 @@ pub trait EthFilter: Sized + Send + Sync + 'static {
|
||||
/// Returns filter changes since last poll.
|
||||
fn filter_changes(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
|
||||
/// Returns filter logs.
|
||||
/// Returns all logs matching given filter (in a range 'from' - 'to').
|
||||
fn filter_logs(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
|
||||
/// Uninstalls filter.
|
||||
|
@ -217,10 +217,6 @@ pub struct ChainSync {
|
||||
network_id: U256,
|
||||
/// Miner
|
||||
miner: Arc<Miner>,
|
||||
|
||||
/// Transactions to propagate
|
||||
// TODO: reconsider where this is in the codebase - seems a little dodgy to have here.
|
||||
transactions_to_send: Vec<Bytes>,
|
||||
}
|
||||
|
||||
type RlpResponseResult = Result<Option<(PacketId, RlpStream)>, PacketDecodeError>;
|
||||
@ -247,7 +243,6 @@ impl ChainSync {
|
||||
max_download_ahead_blocks: max(MAX_HEADERS_TO_REQUEST, config.max_download_ahead_blocks),
|
||||
network_id: config.network_id,
|
||||
miner: miner,
|
||||
transactions_to_send: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@ -950,11 +945,6 @@ impl ChainSync {
|
||||
}
|
||||
}
|
||||
|
||||
/// Place a new transaction on the wire.
|
||||
pub fn new_transaction(&mut self, raw_transaction: Bytes) {
|
||||
self.transactions_to_send.push(raw_transaction);
|
||||
}
|
||||
|
||||
/// Called when peer sends us new transactions
|
||||
fn on_peer_transactions(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> {
|
||||
// accepting transactions once only fully synced
|
||||
@ -1296,11 +1286,16 @@ impl ChainSync {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mut packet = RlpStream::new_list(self.transactions_to_send.len());
|
||||
for tx in &self.transactions_to_send {
|
||||
packet.append_raw(tx, 1);
|
||||
let mut transactions = self.miner.pending_transactions();
|
||||
if transactions.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mut packet = RlpStream::new_list(transactions.len());
|
||||
let tx_count = transactions.len();
|
||||
for tx in transactions.drain(..) {
|
||||
packet.append(&tx);
|
||||
}
|
||||
self.transactions_to_send.clear();
|
||||
let rlp = packet.out();
|
||||
|
||||
let lucky_peers = {
|
||||
@ -1319,13 +1314,12 @@ impl ChainSync {
|
||||
for peer_id in lucky_peers {
|
||||
self.send_packet(io, peer_id, TRANSACTIONS_PACKET, rlp.clone());
|
||||
}
|
||||
trace!(target: "sync", "Sent {} transactions to {} peers.", tx_count, sent);
|
||||
sent
|
||||
}
|
||||
|
||||
fn propagate_latest_blocks(&mut self, io: &mut SyncIo) {
|
||||
if !self.transactions_to_send.is_empty() {
|
||||
self.propagate_new_transactions(io);
|
||||
}
|
||||
self.propagate_new_transactions(io);
|
||||
let chain_info = io.chain().chain_info();
|
||||
if (((chain_info.best_block_number as i64) - (self.last_sent_block_number as i64)).abs() as BlockNumber) < MAX_PEER_LAG_PROPAGATION {
|
||||
let blocks = self.propagate_blocks(&chain_info, io);
|
||||
|
@ -66,7 +66,7 @@ use std::ops::*;
|
||||
use std::sync::*;
|
||||
use util::network::{NetworkProtocolHandler, NetworkService, NetworkContext, PeerId};
|
||||
use util::TimerToken;
|
||||
use util::{U256, Bytes, ONE_U256};
|
||||
use util::{U256, ONE_U256};
|
||||
use ethcore::client::Client;
|
||||
use ethcore::service::SyncMessage;
|
||||
use ethminer::Miner;
|
||||
@ -101,9 +101,6 @@ impl Default for SyncConfig {
|
||||
pub trait SyncProvider: Send + Sync {
|
||||
/// Get sync status
|
||||
fn status(&self) -> SyncStatus;
|
||||
|
||||
/// Note that a user has submitted a new transaction.
|
||||
fn new_transaction(&self, raw_transaction: Bytes);
|
||||
}
|
||||
|
||||
/// Ethereum network protocol handler
|
||||
@ -143,11 +140,6 @@ impl SyncProvider for EthSync {
|
||||
fn status(&self) -> SyncStatus {
|
||||
self.sync.read().unwrap().status()
|
||||
}
|
||||
|
||||
/// Note that a user has submitted a new transaction.
|
||||
fn new_transaction(&self, raw_transaction: Bytes) {
|
||||
self.sync.write().unwrap().new_transaction(raw_transaction);
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkProtocolHandler<SyncMessage> for EthSync {
|
||||
|
1
test.sh
1
test.sh
@ -7,6 +7,7 @@ cargo test --features ethcore/json-tests $1 \
|
||||
-p ethcore \
|
||||
-p ethsync \
|
||||
-p ethcore-rpc \
|
||||
-p ethcore-webapp \
|
||||
-p parity \
|
||||
-p ethminer \
|
||||
-p bigint
|
||||
|
@ -376,8 +376,10 @@ impl<Message> IoService<Message> where Message: Send + Sync + Clone + 'static {
|
||||
|
||||
impl<Message> Drop for IoService<Message> where Message: Send + Sync + Clone {
|
||||
fn drop(&mut self) {
|
||||
trace!(target: "shutdown", "[IoService] Closing...");
|
||||
self.host_channel.send(IoMessage::Shutdown).unwrap();
|
||||
self.thread.take().unwrap().join().ok();
|
||||
trace!(target: "shutdown", "[IoService] Closed.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,10 +120,12 @@ impl Worker {
|
||||
|
||||
impl Drop for Worker {
|
||||
fn drop(&mut self) {
|
||||
trace!(target: "shutdown", "[IoWorker] Closing...");
|
||||
let _ = self.wait_mutex.lock();
|
||||
self.deleting.store(true, AtomicOrdering::Release);
|
||||
self.wait.notify_all();
|
||||
let thread = mem::replace(&mut self.thread, None).unwrap();
|
||||
thread.join().ok();
|
||||
trace!(target: "shutdown", "[IoWorker] Closed");
|
||||
}
|
||||
}
|
||||
|
@ -121,22 +121,7 @@ impl AccountProvider for AccountService {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AccountService {
|
||||
fn default() -> Self {
|
||||
AccountService::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl AccountService {
|
||||
/// New account service with the keys store in default location
|
||||
pub fn new() -> Self {
|
||||
let secret_store = RwLock::new(SecretStore::new());
|
||||
secret_store.write().unwrap().try_import_existing();
|
||||
AccountService {
|
||||
secret_store: secret_store
|
||||
}
|
||||
}
|
||||
|
||||
/// New account service with the keys store in specific location
|
||||
pub fn new_in(path: &Path) -> Self {
|
||||
let secret_store = RwLock::new(SecretStore::new_in(path));
|
||||
@ -165,25 +150,10 @@ impl AccountService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Default for SecretStore {
|
||||
fn default() -> Self {
|
||||
SecretStore::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl SecretStore {
|
||||
/// new instance of Secret Store in default home directory
|
||||
pub fn new() -> Self {
|
||||
let mut path = ::std::env::home_dir().expect("Failed to get home dir");
|
||||
path.push(".parity");
|
||||
path.push("keys");
|
||||
::std::fs::create_dir_all(&path).expect("Should panic since it is critical to be able to access home dir");
|
||||
Self::new_in(&path)
|
||||
}
|
||||
|
||||
/// new instance of Secret Store in specific directory
|
||||
pub fn new_in(path: &Path) -> Self {
|
||||
::std::fs::create_dir_all(&path).expect("Cannot access requested key directory - critical");
|
||||
SecretStore {
|
||||
directory: KeyDirectory::new(path),
|
||||
unlocks: RwLock::new(HashMap::new()),
|
||||
|
26
webapp/Cargo.toml
Normal file
26
webapp/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
||||
[package]
|
||||
description = "Parity WebApplications crate"
|
||||
name = "ethcore-webapp"
|
||||
version = "1.1.0"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Ethcore <admin@ethcore.io"]
|
||||
|
||||
[lib]
|
||||
|
||||
[dependencies]
|
||||
log = "0.3"
|
||||
jsonrpc-core = "2.0"
|
||||
jsonrpc-http-server = { git = "https://github.com/tomusdrw/jsonrpc-http-server.git", branch="old-hyper" }
|
||||
hyper = { version = "0.8", default-features = false }
|
||||
iron = { version = "0.3" }
|
||||
ethcore-rpc = { path = "../rpc" }
|
||||
ethcore-util = { path = "../util" }
|
||||
parity-webapp = { git = "https://github.com/tomusdrw/parity-webapp.git" }
|
||||
# List of apps
|
||||
parity-status = { git = "https://github.com/tomusdrw/parity-status.git", version = "0.1.5" }
|
||||
parity-wallet = { git = "https://github.com/tomusdrw/parity-wallet.git", optional = true }
|
||||
clippy = { version = "0.0.61", optional = true}
|
||||
|
||||
[features]
|
||||
default = ["parity-wallet"]
|
||||
dev = ["clippy", "ethcore-rpc/dev", "ethcore-util/dev"]
|
41
webapp/src/apps.rs
Normal file
41
webapp/src/apps.rs
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use page::{Page, PageHandler};
|
||||
|
||||
extern crate parity_status;
|
||||
extern crate parity_wallet;
|
||||
|
||||
pub type Pages = HashMap<String, Box<Page>>;
|
||||
|
||||
pub fn main_page() -> Box<Page> {
|
||||
Box::new(PageHandler { app: parity_status::App::default() })
|
||||
}
|
||||
|
||||
pub fn all_pages() -> Pages {
|
||||
let mut pages = Pages::new();
|
||||
wallet_page(&mut pages);
|
||||
pages
|
||||
}
|
||||
|
||||
#[cfg(feature = "parity-wallet")]
|
||||
fn wallet_page(pages: &mut Pages) {
|
||||
pages.insert("wallet".to_owned(), Box::new(PageHandler { app: parity_wallet::App::default() }));
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "parity-wallet"))]
|
||||
fn wallet_page(_pages: &mut Pages) {}
|
112
webapp/src/lib.rs
Normal file
112
webapp/src/lib.rs
Normal file
@ -0,0 +1,112 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Ethcore Webapplications for Parity
|
||||
#![warn(missing_docs)]
|
||||
#![cfg_attr(feature="nightly", plugin(clippy))]
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate hyper;
|
||||
extern crate iron;
|
||||
extern crate jsonrpc_core;
|
||||
extern crate jsonrpc_http_server;
|
||||
extern crate ethcore_rpc as rpc;
|
||||
extern crate parity_webapp;
|
||||
|
||||
use std::sync::Arc;
|
||||
use self::jsonrpc_core::{IoHandler, IoDelegate};
|
||||
use jsonrpc_http_server::ServerHandler;
|
||||
|
||||
mod apps;
|
||||
mod page;
|
||||
mod router;
|
||||
|
||||
use router::auth::{Authorization, NoAuth, HttpBasicAuth};
|
||||
|
||||
/// Http server.
|
||||
pub struct WebappServer {
|
||||
handler: Arc<IoHandler>,
|
||||
}
|
||||
|
||||
impl WebappServer {
|
||||
/// Construct new http server object
|
||||
pub fn new() -> Self {
|
||||
WebappServer {
|
||||
handler: Arc::new(IoHandler::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add io delegate.
|
||||
pub fn add_delegate<D>(&self, delegate: IoDelegate<D>) where D: Send + Sync + 'static {
|
||||
self.handler.add_delegate(delegate);
|
||||
}
|
||||
|
||||
/// Asynchronously start server with no authentication,
|
||||
/// return result with `Listening` handle on success or an error.
|
||||
pub fn start_unsecure_http(&self, addr: &str, threads: usize) -> Result<Listening, WebappServerError> {
|
||||
self.start_http(addr, threads, NoAuth)
|
||||
}
|
||||
|
||||
/// Asynchronously start server with `HTTP Basic Authentication`,
|
||||
/// return result with `Listening` handle on success or an error.
|
||||
pub fn start_basic_auth_http(&self, addr: &str, threads: usize, username: &str, password: &str) -> Result<Listening, WebappServerError> {
|
||||
self.start_http(addr, threads, HttpBasicAuth::single_user(username, password))
|
||||
}
|
||||
|
||||
fn start_http<A: Authorization + 'static>(&self, addr: &str, threads: usize, authorization: A) -> Result<Listening, WebappServerError> {
|
||||
let addr = addr.to_owned();
|
||||
let handler = self.handler.clone();
|
||||
|
||||
let cors_domain = jsonrpc_http_server::AccessControlAllowOrigin::Null;
|
||||
let rpc = ServerHandler::new(handler, cors_domain);
|
||||
let router = router::Router::new(rpc, apps::main_page(), apps::all_pages(), authorization);
|
||||
|
||||
try!(hyper::Server::http(addr.as_ref() as &str))
|
||||
.handle_threads(router, threads)
|
||||
.map(|l| Listening { listening: l })
|
||||
.map_err(WebappServerError::from)
|
||||
}
|
||||
}
|
||||
|
||||
/// Listening handle
|
||||
pub struct Listening {
|
||||
listening: hyper::server::Listening
|
||||
}
|
||||
|
||||
impl Drop for Listening {
|
||||
fn drop(&mut self) {
|
||||
self.listening.close().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Webapp Server startup error
|
||||
#[derive(Debug)]
|
||||
pub enum WebappServerError {
|
||||
/// Wrapped `std::io::Error`
|
||||
IoError(std::io::Error),
|
||||
/// Other `hyper` error
|
||||
Other(hyper::error::Error),
|
||||
}
|
||||
|
||||
impl From<hyper::error::Error> for WebappServerError {
|
||||
fn from(err: hyper::error::Error) -> Self {
|
||||
match err {
|
||||
hyper::error::Error::Io(e) => WebappServerError::IoError(e),
|
||||
e => WebappServerError::Other(e),
|
||||
}
|
||||
}
|
||||
}
|
67
webapp/src/page/mod.rs
Normal file
67
webapp/src/page/mod.rs
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::io::Write;
|
||||
use hyper::uri::RequestUri;
|
||||
use hyper::server;
|
||||
use hyper::header;
|
||||
use hyper::status::StatusCode;
|
||||
use parity_webapp::WebApp;
|
||||
|
||||
pub trait Page : Send + Sync {
|
||||
fn serve_file(&self, mut path: &str, mut res: server::Response);
|
||||
}
|
||||
|
||||
pub struct PageHandler<T : WebApp> {
|
||||
pub app: T,
|
||||
}
|
||||
|
||||
impl<T: WebApp> Page for PageHandler<T> {
|
||||
fn serve_file(&self, mut path: &str, mut res: server::Response) {
|
||||
// Support index file
|
||||
if path == "" {
|
||||
path = "index.html"
|
||||
}
|
||||
let file = self.app.file(path);
|
||||
if let Some(f) = file {
|
||||
*res.status_mut() = StatusCode::Ok;
|
||||
res.headers_mut().set(header::ContentType(f.content_type.parse().unwrap()));
|
||||
|
||||
let _ = match res.start() {
|
||||
Ok(mut raw_res) => {
|
||||
for chunk in f.content.chunks(1024 * 20) {
|
||||
let _ = raw_res.write(chunk);
|
||||
}
|
||||
raw_res.end()
|
||||
},
|
||||
Err(_) => {
|
||||
println!("Error while writing response.");
|
||||
Ok(())
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Handler for Page {
|
||||
fn handle(&self, req: server::Request, mut res: server::Response) {
|
||||
*res.status_mut() = StatusCode::NotFound;
|
||||
|
||||
if let RequestUri::AbsolutePath(ref path) = req.uri {
|
||||
self.serve_file(path, res);
|
||||
}
|
||||
}
|
||||
}
|
53
webapp/src/router/api.rs
Normal file
53
webapp/src/router/api.rs
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Simple REST API
|
||||
|
||||
use std::sync::Arc;
|
||||
use hyper;
|
||||
use hyper::status::StatusCode;
|
||||
use hyper::header;
|
||||
use hyper::uri::RequestUri::AbsolutePath as Path;
|
||||
use apps::Pages;
|
||||
|
||||
pub struct RestApi {
|
||||
pub pages: Arc<Pages>,
|
||||
}
|
||||
|
||||
impl RestApi {
|
||||
fn list_pages(&self) -> String {
|
||||
let mut s = "[".to_owned();
|
||||
for name in self.pages.keys() {
|
||||
s.push_str(&format!("\"{}\",", name));
|
||||
}
|
||||
s.push_str("\"rpc\"");
|
||||
s.push_str("]");
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
impl hyper::server::Handler for RestApi {
|
||||
fn handle<'b, 'a>(&'a self, req: hyper::server::Request<'a, 'b>, mut res: hyper::server::Response<'a>) {
|
||||
match req.uri {
|
||||
Path(ref path) if path == "apps" => {
|
||||
*res.status_mut() = StatusCode::Ok;
|
||||
res.headers_mut().set(header::ContentType("application/json".parse().unwrap()));
|
||||
let _ = res.send(self.list_pages().as_bytes());
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
112
webapp/src/router/auth.rs
Normal file
112
webapp/src/router/auth.rs
Normal file
@ -0,0 +1,112 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! HTTP Authorization implementations
|
||||
|
||||
use std::collections::HashMap;
|
||||
use hyper::{header, server};
|
||||
use hyper::status::StatusCode;
|
||||
|
||||
/// Authorization result
|
||||
pub enum Authorized<'a, 'b> where 'b : 'a {
|
||||
/// Authorization was successful. Request and Response are returned for further processing.
|
||||
Yes(server::Request<'a, 'b>, server::Response<'a>),
|
||||
/// Unsuccessful authorization. Request and Response has been consumed.
|
||||
No,
|
||||
}
|
||||
|
||||
/// Authorization interface
|
||||
pub trait Authorization : Send + Sync {
|
||||
/// Handle authorization process and return `Request` and `Response` when authorization is successful.
|
||||
fn handle<'b, 'a>(&'a self, req: server::Request<'a, 'b>, res: server::Response<'a>)-> Authorized<'a, 'b>;
|
||||
}
|
||||
|
||||
/// HTTP Basic Authorization handler
|
||||
pub struct HttpBasicAuth {
|
||||
users: HashMap<String, String>,
|
||||
}
|
||||
|
||||
/// No-authorization implementation (authorization disabled)
|
||||
pub struct NoAuth;
|
||||
|
||||
impl Authorization for NoAuth {
|
||||
fn handle<'b, 'a>(&'a self, req: server::Request<'a, 'b>, res: server::Response<'a>)-> Authorized<'a, 'b> {
|
||||
Authorized::Yes(req, res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Authorization for HttpBasicAuth {
|
||||
|
||||
fn handle<'b, 'a>(&'a self, req: server::Request<'a, 'b>, res: server::Response<'a>)-> Authorized<'a, 'b> {
|
||||
let auth = self.check_auth(&req);
|
||||
|
||||
match auth {
|
||||
Access::Denied => {
|
||||
self.respond_with_unauthorized(res);
|
||||
Authorized::No
|
||||
},
|
||||
Access::AuthRequired => {
|
||||
self.respond_with_auth_required(res);
|
||||
Authorized::No
|
||||
},
|
||||
Access::Granted => {
|
||||
Authorized::Yes(req, res)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Access {
|
||||
Granted,
|
||||
Denied,
|
||||
AuthRequired,
|
||||
}
|
||||
|
||||
impl HttpBasicAuth {
|
||||
/// Creates `HttpBasicAuth` instance with only one user.
|
||||
pub fn single_user(username: &str, password: &str) -> Self {
|
||||
let mut users = HashMap::new();
|
||||
users.insert(username.to_owned(), password.to_owned());
|
||||
HttpBasicAuth {
|
||||
users: users
|
||||
}
|
||||
}
|
||||
|
||||
fn is_authorized(&self, username: &str, password: &str) -> bool {
|
||||
self.users.get(&username.to_owned()).map_or(false, |pass| pass == password)
|
||||
}
|
||||
|
||||
fn check_auth(&self, req: &server::Request) -> Access {
|
||||
match req.headers.get::<header::Authorization<header::Basic>>() {
|
||||
Some(&header::Authorization(
|
||||
header::Basic { ref username, password: Some(ref password) }
|
||||
)) if self.is_authorized(username, password) => Access::Granted,
|
||||
Some(_) => Access::Denied,
|
||||
None => Access::AuthRequired,
|
||||
}
|
||||
}
|
||||
|
||||
fn respond_with_unauthorized(&self, mut res: server::Response) {
|
||||
*res.status_mut() = StatusCode::Unauthorized;
|
||||
let _ = res.send(b"Unauthorized");
|
||||
}
|
||||
|
||||
fn respond_with_auth_required(&self, mut res: server::Response) {
|
||||
*res.status_mut() = StatusCode::Unauthorized;
|
||||
res.headers_mut().set_raw("WWW-Authenticate", vec![b"Basic realm=\"Parity\"".to_vec()]);
|
||||
}
|
||||
}
|
||||
|
123
webapp/src/router/mod.rs
Normal file
123
webapp/src/router/mod.rs
Normal file
@ -0,0 +1,123 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Router implementation
|
||||
//! Processes request handling authorization and dispatching it to proper application.
|
||||
|
||||
mod api;
|
||||
pub mod auth;
|
||||
|
||||
use std::sync::Arc;
|
||||
use hyper;
|
||||
use hyper::{server, uri, header};
|
||||
use page::Page;
|
||||
use apps::Pages;
|
||||
use iron::request::Url;
|
||||
use jsonrpc_http_server::ServerHandler;
|
||||
use self::auth::{Authorization, Authorized};
|
||||
|
||||
pub struct Router<A: Authorization> {
|
||||
authorization: A,
|
||||
rpc: ServerHandler,
|
||||
api: api::RestApi,
|
||||
main_page: Box<Page>,
|
||||
pages: Arc<Pages>,
|
||||
}
|
||||
|
||||
impl<A: Authorization> server::Handler for Router<A> {
|
||||
fn handle<'b, 'a>(&'a self, req: server::Request<'a, 'b>, res: server::Response<'a>) {
|
||||
let auth = self.authorization.handle(req, res);
|
||||
|
||||
if let Authorized::Yes(req, res) = auth {
|
||||
let (path, req) = self.extract_request_path(req);
|
||||
match path {
|
||||
Some(ref url) if self.pages.contains_key(url) => {
|
||||
self.pages.get(url).unwrap().handle(req, res);
|
||||
},
|
||||
Some(ref url) if url == "api" => {
|
||||
self.api.handle(req, res);
|
||||
},
|
||||
_ if req.method == hyper::method::Method::Post => {
|
||||
self.rpc.handle(req, res)
|
||||
},
|
||||
_ => self.main_page.handle(req, res),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Authorization> Router<A> {
|
||||
pub fn new(
|
||||
rpc: ServerHandler,
|
||||
main_page: Box<Page>,
|
||||
pages: Pages,
|
||||
authorization: A) -> Self {
|
||||
let pages = Arc::new(pages);
|
||||
Router {
|
||||
authorization: authorization,
|
||||
rpc: rpc,
|
||||
api: api::RestApi { pages: pages.clone() },
|
||||
main_page: main_page,
|
||||
pages: pages,
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_url(&self, req: &server::Request) -> Option<Url> {
|
||||
match req.uri {
|
||||
uri::RequestUri::AbsoluteUri(ref url) => {
|
||||
match Url::from_generic_url(url.clone()) {
|
||||
Ok(url) => Some(url),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
uri::RequestUri::AbsolutePath(ref path) => {
|
||||
// Attempt to prepend the Host header (mandatory in HTTP/1.1)
|
||||
let url_string = match req.headers.get::<header::Host>() {
|
||||
Some(ref host) => {
|
||||
format!("http://{}:{}{}", host.hostname, host.port.unwrap_or(80), path)
|
||||
},
|
||||
None => return None,
|
||||
};
|
||||
|
||||
match Url::parse(&url_string) {
|
||||
Ok(url) => Some(url),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_request_path<'a, 'b>(&self, mut req: server::Request<'a, 'b>) -> (Option<String>, server::Request<'a, 'b>) {
|
||||
let url = self.extract_url(&req);
|
||||
match url {
|
||||
Some(ref url) if url.path.len() > 1 => {
|
||||
let part = url.path[0].clone();
|
||||
let url = url.path[1..].join("/");
|
||||
req.uri = uri::RequestUri::AbsolutePath(url);
|
||||
(Some(part), req)
|
||||
},
|
||||
Some(url) => {
|
||||
let url = url.path.join("/");
|
||||
req.uri = uri::RequestUri::AbsolutePath(url);
|
||||
(None, req)
|
||||
},
|
||||
_ => {
|
||||
(None, req)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user