Merge branch 'master' into tracing

This commit is contained in:
debris 2016-04-11 11:35:07 +02:00
commit 610251fdf7
75 changed files with 1920 additions and 831 deletions

170
Cargo.lock generated
View File

@ -11,6 +11,7 @@ dependencies = [
"ethcore-devtools 1.1.0", "ethcore-devtools 1.1.0",
"ethcore-rpc 1.1.0", "ethcore-rpc 1.1.0",
"ethcore-util 1.1.0", "ethcore-util 1.1.0",
"ethcore-webapp 1.1.0",
"ethminer 1.1.0", "ethminer 1.1.0",
"ethsync 1.1.0", "ethsync 1.1.0",
"fdlimit 0.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)", "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]] [[package]]
name = "cookie" name = "cookie"
version = "0.1.21" version = "0.1.21"
@ -185,6 +194,15 @@ dependencies = [
"regex 0.1.61 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "eth-secp256k1" name = "eth-secp256k1"
version = "0.5.4" version = "0.5.4"
@ -246,7 +264,7 @@ dependencies = [
"ethminer 1.1.0", "ethminer 1.1.0",
"ethsync 1.1.0", "ethsync 1.1.0",
"jsonrpc-core 2.0.1 (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 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)", "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)", "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)", "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)", "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]] [[package]]
name = "ethjson" name = "ethjson"
version = "0.1.0" version = "0.1.0"
@ -415,6 +450,27 @@ dependencies = [
"url 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "igd" name = "igd"
version = "0.4.2" version = "0.4.2"
@ -427,6 +483,23 @@ dependencies = [
"xmltree 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "itertools" name = "itertools"
version = "0.4.11" version = "0.4.11"
@ -453,14 +526,24 @@ dependencies = [
[[package]] [[package]]
name = "jsonrpc-http-server" name = "jsonrpc-http-server"
version = "3.0.1" version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/tomusdrw/jsonrpc-http-server.git?branch=old-hyper#46bd4e7cf8352e0efc940cf76d3dff99f1a3da15"
dependencies = [ dependencies = [
"hyper 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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]] [[package]]
name = "kernel32-sys" name = "kernel32-sys"
version = "0.2.1" version = "0.2.1"
@ -573,6 +656,11 @@ dependencies = [
"ws2_32-sys 0.2.1 (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 = "modifier"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "net2" name = "net2"
version = "0.2.23" version = "0.2.23"
@ -637,6 +725,35 @@ name = "odds"
version = "0.2.12" version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "primal" name = "primal"
version = "0.2.3" version = "0.2.3"
@ -696,6 +813,11 @@ dependencies = [
"syntex_syntax 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "quine-mc_cluskey" name = "quine-mc_cluskey"
version = "0.2.2" version = "0.2.2"
@ -745,6 +867,18 @@ dependencies = [
"librocksdb-sys 0.2.3 (git+https://github.com/arkpar/rust-rocksdb.git)", "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]] [[package]]
name = "rpassword" name = "rpassword"
version = "0.1.3" version = "0.1.3"
@ -938,6 +1072,14 @@ name = "typeable"
version = "0.1.2" version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "unicase" name = "unicase"
version = "1.4.0" version = "1.4.0"
@ -964,6 +1106,14 @@ name = "unicode-xid"
version = "0.0.3" version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "url" name = "url"
version = "0.2.38" version = "0.2.38"
@ -1000,6 +1150,15 @@ dependencies = [
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "vergen" name = "vergen"
version = "0.1.0" version = "0.1.0"
@ -1009,6 +1168,11 @@ dependencies = [
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "winapi" name = "winapi"
version = "0.2.6" version = "0.2.6"

View File

@ -28,15 +28,19 @@ ethsync = { path = "sync" }
ethminer = { path = "miner" } ethminer = { path = "miner" }
ethcore-devtools = { path = "devtools" } ethcore-devtools = { path = "devtools" }
ethcore-rpc = { path = "rpc", optional = true } ethcore-rpc = { path = "rpc", optional = true }
ethcore-webapp = { path = "webapp", optional = true }
[dependencies.hyper] [dependencies.hyper]
version = "0.8" version = "0.8"
default-features = false default-features = false
[features] [features]
default = ["rpc"] default = ["rpc", "webapp"]
rpc = ["ethcore-rpc"] 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-beta = ["ethcore/json-tests"]
travis-nightly = ["ethcore/json-tests", "dev"] travis-nightly = ["ethcore/json-tests", "dev"]

2
cov.sh
View File

@ -23,6 +23,7 @@ cargo test \
-p ethcore-rpc \ -p ethcore-rpc \
-p parity \ -p parity \
-p ethminer \ -p ethminer \
-p ethcore-webapp \
--no-run || exit $? --no-run || exit $?
rm -rf target/coverage rm -rf target/coverage
mkdir -p 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/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/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_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-* kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethminer-*
xdg-open target/coverage/index.html xdg-open target/coverage/index.html

1
doc.sh
View File

@ -7,5 +7,6 @@ cargo doc --no-deps --verbose \
-p ethcore \ -p ethcore \
-p ethsync \ -p ethsync \
-p ethcore-rpc \ -p ethcore-rpc \
-p ethcore-webapp \
-p parity \ -p parity \
-p ethminer -p ethminer

View File

@ -1,24 +1,33 @@
{ {
"name": "Frontier/Homestead", "name": "Frontier/Homestead",
"engineName": "Ethash", "engine": {
"Ethash": {
"params": {
"tieBreakingGas": false,
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
}
}
},
"params": { "params": {
"accountStartNonce": "0x00", "accountStartNonce": "0x00",
"frontierCompatibilityModeLimit": "0x118c30", "frontierCompatibilityModeLimit": "0x118c30",
"maximumExtraDataSize": "0x20", "maximumExtraDataSize": "0x20",
"tieBreakingGas": false,
"minGasLimit": "0x1388", "minGasLimit": "0x1388",
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"networkID" : "0x1" "networkID" : "0x1"
}, },
"genesis": { "genesis": {
"nonce": "0x0000000000000042", "seal": {
"ethereum": {
"nonce": "0x0000000000000042",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
},
"difficulty": "0x400000000", "difficulty": "0x400000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"author": "0x0000000000000000000000000000000000000000", "author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00", "timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",

View File

@ -1,24 +1,33 @@
{ {
"name": "Frontier (Test)", "name": "Frontier (Test)",
"engineName": "Ethash", "engine": {
"Ethash": {
"params": {
"tieBreakingGas": false,
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
}
}
},
"params": { "params": {
"accountStartNonce": "0x00", "accountStartNonce": "0x00",
"frontierCompatibilityModeLimit": "0x118c30", "frontierCompatibilityModeLimit": "0x118c30",
"maximumExtraDataSize": "0x20", "maximumExtraDataSize": "0x20",
"tieBreakingGas": false,
"minGasLimit": "0x1388", "minGasLimit": "0x1388",
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"networkID" : "0x1" "networkID" : "0x1"
}, },
"genesis": { "genesis": {
"nonce": "0x0000000000000042", "seal": {
"ethereum": {
"nonce": "0x0000000000000042",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
},
"difficulty": "0x400000000", "difficulty": "0x400000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"author": "0x0000000000000000000000000000000000000000", "author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00", "timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",

View File

@ -1,24 +1,33 @@
{ {
"name": "Frontier (Test)", "name": "Frontier (Test)",
"engineName": "Ethash", "engine": {
"Ethash": {
"params": {
"tieBreakingGas": false,
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
}
}
},
"params": { "params": {
"accountStartNonce": "0x00", "accountStartNonce": "0x00",
"frontierCompatibilityModeLimit": "0xffffffffffffffff", "frontierCompatibilityModeLimit": "0xffffffffffffffff",
"maximumExtraDataSize": "0x20", "maximumExtraDataSize": "0x20",
"tieBreakingGas": false,
"minGasLimit": "0x1388", "minGasLimit": "0x1388",
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"networkID" : "0x1" "networkID" : "0x1"
}, },
"genesis": { "genesis": {
"nonce": "0x0000000000000042", "seal": {
"ethereum": {
"nonce": "0x0000000000000042",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
},
"difficulty": "0x400000000", "difficulty": "0x400000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"author": "0x0000000000000000000000000000000000000000", "author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00", "timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",

View File

@ -1,24 +1,33 @@
{ {
"name": "Homestead (Test)", "name": "Homestead (Test)",
"engineName": "Ethash", "engine": {
"Ethash": {
"params": {
"tieBreakingGas": false,
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
}
}
},
"params": { "params": {
"accountStartNonce": "0x00", "accountStartNonce": "0x00",
"frontierCompatibilityModeLimit": 0, "frontierCompatibilityModeLimit": 0,
"maximumExtraDataSize": "0x20", "maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388", "minGasLimit": "0x1388",
"tieBreakingGas": false,
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"networkID" : "0x1" "networkID" : "0x1"
}, },
"genesis": { "genesis": {
"nonce": "0x0000000000000042", "seal": {
"ethereum": {
"nonce": "0x0000000000000042",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
},
"difficulty": "0x400000000", "difficulty": "0x400000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"author": "0x0000000000000000000000000000000000000000", "author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00", "timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",

View File

@ -1,24 +1,33 @@
{ {
"name": "Morden", "name": "Morden",
"engineName": "Ethash", "engine": {
"Ethash": {
"params": {
"tieBreakingGas": false,
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar": ""
}
}
},
"params": { "params": {
"accountStartNonce": "0x0100000", "accountStartNonce": "0x0100000",
"frontierCompatibilityModeLimit": "0x789b0", "frontierCompatibilityModeLimit": "0x789b0",
"maximumExtraDataSize": "0x20", "maximumExtraDataSize": "0x20",
"tieBreakingGas": false,
"minGasLimit": "0x1388", "minGasLimit": "0x1388",
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar": "",
"networkID" : "0x2" "networkID" : "0x2"
}, },
"genesis": { "genesis": {
"nonce": "0x00006d6f7264656e", "seal": {
"ethereum": {
"nonce": "0x00006d6f7264656e",
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578"
}
},
"difficulty": "0x20000", "difficulty": "0x20000",
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
"author": "0x0000000000000000000000000000000000000000", "author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00", "timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",

View File

@ -1,24 +1,33 @@
{ {
"name": "Olympic", "name": "Olympic",
"engineName": "Ethash", "engine": {
"Ethash": {
"params": {
"tieBreakingGas": false,
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x08",
"blockReward": "0x14D1120D7B160000",
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050"
}
}
},
"params": { "params": {
"accountStartNonce": "0x00", "accountStartNonce": "0x00",
"frontierCompatibilityModeLimit": "0xffffffffffffffff", "frontierCompatibilityModeLimit": "0xffffffffffffffff",
"maximumExtraDataSize": "0x0400", "maximumExtraDataSize": "0x0400",
"tieBreakingGas": false,
"minGasLimit": "125000", "minGasLimit": "125000",
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x08",
"blockReward": "0x14D1120D7B160000",
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050",
"networkID" : "0x0" "networkID" : "0x0"
}, },
"genesis": { "genesis": {
"nonce": "0x000000000000002a", "seal": {
"ethereum": {
"nonce": "0x000000000000002a",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
},
"difficulty": "0x20000", "difficulty": "0x20000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"author": "0x0000000000000000000000000000000000000000", "author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00", "timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",

View File

@ -1,24 +1,23 @@
{ {
"name": "Morden", "name": "Morden",
"engineName": "NullEngine", "engine": {
"Null": null
},
"params": { "params": {
"accountStartNonce": "0x0100000", "accountStartNonce": "0x0100000",
"frontierCompatibilityModeLimit": "0x0", "frontierCompatibilityModeLimit": "0x0",
"maximumExtraDataSize": "0x20", "maximumExtraDataSize": "0x20",
"tieBreakingGas": false,
"minGasLimit": "0x1388", "minGasLimit": "0x1388",
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar": "",
"networkID" : "0x2" "networkID" : "0x2"
}, },
"genesis": { "genesis": {
"nonce": "0x00006d6f7264656e", "seal": {
"ethereum": {
"nonce": "0x00006d6f7264656e",
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578"
}
},
"difficulty": "0x20000", "difficulty": "0x20000",
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
"author": "0x0000000000000000000000000000000000000000", "author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00", "timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",

View File

@ -1,24 +1,23 @@
{ {
"name": "Morden", "name": "Morden",
"engineName": "NullEngine", "engine": {
"Null": null
},
"params": { "params": {
"accountStartNonce": "0x0100000", "accountStartNonce": "0x0100000",
"frontierCompatibilityModeLimit": "0x789b0", "frontierCompatibilityModeLimit": "0x789b0",
"maximumExtraDataSize": "0x20", "maximumExtraDataSize": "0x20",
"tieBreakingGas": false,
"minGasLimit": "0x1388", "minGasLimit": "0x1388",
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar": "",
"networkID" : "0x2" "networkID" : "0x2"
}, },
"genesis": { "genesis": {
"nonce": "0x00006d6f7264656e", "seal": {
"ethereum": {
"nonce": "0x00006d6f7264656e",
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578"
}
},
"difficulty": "0x20000", "difficulty": "0x20000",
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
"author": "0x0000000000000000000000000000000000000000", "author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00", "timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",

View File

@ -138,7 +138,7 @@ impl Account {
/// get someone who knows to call `note_code`. /// get someone who knows to call `note_code`.
pub fn code(&self) -> Option<&[u8]> { pub fn code(&self) -> Option<&[u8]> {
match self.code_hash { 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), Some(_) if !self.code_cache.is_empty() => Some(&self.code_cache),
None => Some(&self.code_cache), None => Some(&self.code_cache),
_ => None, _ => None,

View File

@ -497,16 +497,16 @@ mod tests {
use tests::helpers::*; use tests::helpers::*;
use super::*; use super::*;
use common::*; use common::*;
use engine::*;
#[test] #[test]
fn open_block() { fn open_block() {
use spec::*; use spec::*;
let engine = Spec::new_test().to_engine().unwrap(); let spec = Spec::new_test();
let genesis_header = engine.spec().genesis_header(); let engine = &spec.engine;
let genesis_header = spec.genesis_header();
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
let mut db = db_result.take(); 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 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 = OpenBlock::new(engine.deref(), false, db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]);
let b = b.close_and_lock(); let b = b.close_and_lock();
@ -516,19 +516,20 @@ mod tests {
#[test] #[test]
fn enact_block() { fn enact_block() {
use spec::*; use spec::*;
let engine = Spec::new_test().to_engine().unwrap(); let spec = Spec::new_test();
let genesis_header = engine.spec().genesis_header(); let engine = &spec.engine;
let genesis_header = spec.genesis_header();
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
let mut db = db_result.take(); 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 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_bytes = b.rlp_bytes();
let orig_db = b.drain(); let orig_db = b.drain();
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
let mut db = db_result.take(); 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 e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()]).unwrap();
assert_eq!(e.rlp_bytes(), orig_bytes); assert_eq!(e.rlp_bytes(), orig_bytes);
@ -541,12 +542,13 @@ mod tests {
#[test] #[test]
fn enact_block_with_uncle() { fn enact_block_with_uncle() {
use spec::*; use spec::*;
let engine = Spec::new_test().to_engine().unwrap(); let spec = Spec::new_test();
let genesis_header = engine.spec().genesis_header(); let engine = &spec.engine;
let genesis_header = spec.genesis_header();
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
let mut db = db_result.take(); 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 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(); let mut uncle1_header = Header::new();
uncle1_header.extra_data = b"uncle1".to_vec(); 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_result = get_temp_journal_db();
let mut db = db_result.take(); 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 e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()]).unwrap();
let bytes = e.rlp_bytes(); let bytes = e.rlp_bytes();

View File

@ -116,6 +116,7 @@ struct VerifyingBlock {
} }
struct QueueSignal { struct QueueSignal {
deleting: Arc<AtomicBool>,
signalled: AtomicBool, signalled: AtomicBool,
message_channel: IoChannel<NetSyncMessage>, message_channel: IoChannel<NetSyncMessage>,
} }
@ -123,10 +124,16 @@ struct QueueSignal {
impl QueueSignal { impl QueueSignal {
#[cfg_attr(feature="dev", allow(bool_comparison))] #[cfg_attr(feature="dev", allow(bool_comparison))]
fn set(&self) { 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 { if self.signalled.compare_and_swap(false, true, AtomicOrdering::Relaxed) == false {
self.message_channel.send(UserMessage(SyncMessage::BlockVerified)).expect("Error sending BlockVerified message"); self.message_channel.send(UserMessage(SyncMessage::BlockVerified)).expect("Error sending BlockVerified message");
} }
} }
fn reset(&self) { fn reset(&self) {
self.signalled.store(false, AtomicOrdering::Relaxed); self.signalled.store(false, AtomicOrdering::Relaxed);
} }
@ -150,8 +157,12 @@ impl BlockQueue {
bad: Mutex::new(HashSet::new()), bad: Mutex::new(HashSet::new()),
}); });
let more_to_verify = Arc::new(Condvar::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 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 empty = Arc::new(Condvar::new());
let panic_handler = PanicHandler::new_in_arc(); let panic_handler = PanicHandler::new_in_arc();
@ -431,12 +442,14 @@ impl MayPanic for BlockQueue {
impl Drop for BlockQueue { impl Drop for BlockQueue {
fn drop(&mut self) { fn drop(&mut self) {
trace!(target: "shutdown", "[BlockQueue] Closing...");
self.clear(); self.clear();
self.deleting.store(true, AtomicOrdering::Release); self.deleting.store(true, AtomicOrdering::Release);
self.more_to_verify.notify_all(); self.more_to_verify.notify_all();
for t in self.verifiers.drain(..) { for t in self.verifiers.drain(..) {
t.join().unwrap(); t.join().unwrap();
} }
trace!(target: "shutdown", "[BlockQueue] Closed.");
} }
} }
@ -451,7 +464,7 @@ mod tests {
fn get_test_queue() -> BlockQueue { fn get_test_queue() -> BlockQueue {
let spec = get_test_spec(); let spec = get_test_spec();
let engine = spec.to_engine().unwrap(); let engine = spec.engine;
BlockQueue::new(BlockQueueConfig::default(), Arc::new(engine), IoChannel::disconnected()) BlockQueue::new(BlockQueueConfig::default(), Arc::new(engine), IoChannel::disconnected())
} }
@ -459,7 +472,7 @@ mod tests {
fn can_be_created() { fn can_be_created() {
// TODO better test // TODO better test
let spec = Spec::new_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()); let _ = BlockQueue::new(BlockQueueConfig::default(), Arc::new(engine), IoChannel::disconnected());
} }
@ -520,7 +533,7 @@ mod tests {
#[test] #[test]
fn test_mem_limit() { fn test_mem_limit() {
let spec = get_test_spec(); let spec = get_test_spec();
let engine = spec.to_engine().unwrap(); let engine = spec.engine;
let mut config = BlockQueueConfig::default(); let mut config = BlockQueueConfig::default();
config.max_mem_use = super::MIN_MEM_LIMIT; // empty queue uses about 15000 config.max_mem_use = super::MIN_MEM_LIMIT; // empty queue uses about 15000
let queue = BlockQueue::new(config, Arc::new(engine), IoChannel::disconnected()); let queue = BlockQueue::new(config, Arc::new(engine), IoChannel::disconnected());

View File

@ -18,6 +18,7 @@ use util::*;
use crypto::sha2::Sha256; use crypto::sha2::Sha256;
use crypto::ripemd160::Ripemd160; use crypto::ripemd160::Ripemd160;
use crypto::digest::Digest; use crypto::digest::Digest;
use ethjson;
/// Definition of a contract whose implementation is built-in. /// Definition of a contract whose implementation is built-in.
pub struct Builtin { 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. /// 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> { pub fn from_named_linear(name: &str, base_cost: usize, word_cost: usize) -> Builtin {
new_builtin_exec(name).map(|b| { let cost = Box::new(move|s: usize| -> U256 {
let cost = Box::new(move|s: usize| -> U256 { U256::from(base_cost) + U256::from(word_cost) * U256::from((s + 31) / 32)
U256::from(base_cost) + U256::from(word_cost) * U256::from((s + 31) / 32) });
});
Self::new(cost, b) Self::new(cost, new_builtin_exec(name))
})
} }
/// Simple forwarder for cost. /// Simple forwarder for cost.
@ -60,24 +60,15 @@ impl Builtin {
/// Simple forwarder for execute. /// Simple forwarder for execute.
pub fn execute(&self, input: &[u8], output: &mut[u8]) { (*self.execute)(input, output); } pub fn execute(&self, input: &[u8], output: &mut[u8]) { (*self.execute)(input, output); }
}
/// Create a builtin from JSON. impl From<ethjson::spec::Builtin> for Builtin {
/// fn from(b: ethjson::spec::Builtin) -> Self {
/// JSON must be of the form `{ "name": "identity", "pricing": {"base": 10, "word": 20} }`. match b.pricing {
pub fn from_json(json: &Json) -> Option<Builtin> { ethjson::spec::Pricing::Linear(linear) => {
// NICE: figure out a more convenient means of handing errors here. Self::from_named_linear(b.name.as_ref(), linear.base, linear.word)
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);
}
}
}
} }
} }
None
} }
} }
@ -92,14 +83,14 @@ pub fn copy_to(src: &[u8], dest: &mut[u8]) {
/// Create a new builtin executor according to `name`. /// Create a new builtin executor according to `name`.
/// TODO: turn in to a factory with dynamic registration. /// 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 { 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()) { for i in 0..min(input.len(), output.len()) {
output[i] = input[i]; 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)] #[repr(packed)]
#[derive(Debug)] #[derive(Debug)]
struct InType { 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(); let mut sha = Sha256::new();
sha.input(input); sha.input(input);
if output.len() >= 32 { 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()); sha.result(ret.as_slice_mut());
copy_to(&ret, output); 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(); let mut sha = Ripemd160::new();
sha.input(input); sha.input(input);
let mut ret = H256::new(); let mut ret = H256::new();
sha.result(&mut ret.as_slice_mut()[12..32]); sha.result(&mut ret.as_slice_mut()[12..32]);
copy_to(&ret, output); copy_to(&ret, output);
})), }),
_ => None _ => {
panic!("invalid builtin name {}", name);
}
} }
} }
#[test] #[test]
fn identity() { fn identity() {
let f = new_builtin_exec("identity").unwrap(); let f = new_builtin_exec("identity");
let i = [0u8, 1, 2, 3]; let i = [0u8, 1, 2, 3];
let mut o2 = [255u8; 2]; let mut o2 = [255u8; 2];
@ -167,7 +160,7 @@ fn identity() {
#[test] #[test]
fn sha256() { fn sha256() {
use rustc_serialize::hex::FromHex; use rustc_serialize::hex::FromHex;
let f = new_builtin_exec("sha256").unwrap(); let f = new_builtin_exec("sha256");
let i = [0u8; 0]; let i = [0u8; 0];
let mut o = [255u8; 32]; let mut o = [255u8; 32];
@ -186,7 +179,7 @@ fn sha256() {
#[test] #[test]
fn ripemd160() { fn ripemd160() {
use rustc_serialize::hex::FromHex; use rustc_serialize::hex::FromHex;
let f = new_builtin_exec("ripemd160").unwrap(); let f = new_builtin_exec("ripemd160");
let i = [0u8; 0]; let i = [0u8; 0];
let mut o = [255u8; 32]; let mut o = [255u8; 32];
@ -213,7 +206,7 @@ fn ecrecover() {
let s = k.sign(&m).unwrap(); let s = k.sign(&m).unwrap();
println!("Signed: {}", s);*/ println!("Signed: {}", s);*/
let f = new_builtin_exec("ecrecover").unwrap(); let f = new_builtin_exec("ecrecover");
let i = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap(); let i = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
let mut o = [255u8; 32]; let mut o = [255u8; 32];
@ -260,9 +253,15 @@ fn ecrecover() {
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);*/ assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);*/
} }
#[test]
#[should_panic]
fn from_unknown_linear() {
let _ = Builtin::from_named_linear("dw", 10, 20);
}
#[test] #[test]
fn from_named_linear() { 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)(0), U256::from(10));
assert_eq!((*b.cost)(1), U256::from(30)); assert_eq!((*b.cost)(1), U256::from(30));
assert_eq!((*b.cost)(32), U256::from(30)); assert_eq!((*b.cost)(32), U256::from(30));
@ -276,9 +275,14 @@ fn from_named_linear() {
#[test] #[test]
fn from_json() { fn from_json() {
let text = r#"{"name": "identity", "pricing": {"linear": {"base": 10, "word": 20}}}"#; let b = Builtin::from(ethjson::spec::Builtin {
let json = Json::from_str(text).unwrap(); name: "identity".to_owned(),
let b = Builtin::from_json(&json).unwrap(); pricing: ethjson::spec::Pricing::Linear(ethjson::spec::Linear {
base: 10,
word: 20,
})
});
assert_eq!((*b.cost)(0), U256::from(10)); assert_eq!((*b.cost)(0), U256::from(10));
assert_eq!((*b.cost)(1), U256::from(30)); assert_eq!((*b.cost)(1), U256::from(30));
assert_eq!((*b.cost)(32), U256::from(30)); assert_eq!((*b.cost)(32), U256::from(30));

View File

@ -140,14 +140,15 @@ impl<V> Client<V> where V: Verifier {
let mut state_path = path.to_path_buf(); let mut state_path = path.to_path_buf();
state_path.push("state"); state_path.push("state");
let engine = Arc::new(try!(spec.to_engine()));
let state_path_str = state_path.to_str().unwrap(); let state_path_str = state_path.to_str().unwrap();
let mut state_db = journaldb::new(state_path_str, config.pruning); 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()) { if state_db.is_empty() && 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"); 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 block_queue = BlockQueue::new(config.queue, engine.clone(), message_channel);
let panic_handler = PanicHandler::new_in_arc(); let panic_handler = PanicHandler::new_in_arc();
panic_handler.forward_from(&block_queue); panic_handler.forward_from(&block_queue);

View File

@ -16,7 +16,7 @@
use common::*; use common::*;
use block::ExecutedBlock; use block::ExecutedBlock;
use spec::Spec; use spec::CommonParams;
use evm::Schedule; use evm::Schedule;
use evm::Factory; use evm::Factory;
@ -25,7 +25,7 @@ use evm::Factory;
pub trait Engine : Sync + Send { pub trait Engine : Sync + Send {
/// The name of this engine. /// The name of this engine.
fn name(&self) -> &str; fn name(&self) -> &str;
/// The version of this engine. Should be of the form /// The version of this engine. Should be of the form
fn version(&self) -> SemanticVersion { SemanticVersion::new(0, 0, 0) } fn version(&self) -> SemanticVersion { SemanticVersion::new(0, 0, 0) }
/// The number of additional header fields required for this engine. /// The number of additional header fields required for this engine.
@ -35,7 +35,7 @@ pub trait Engine : Sync + Send {
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() } fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() }
/// Get the general parameters of the chain. /// Get the general parameters of the chain.
fn spec(&self) -> &Spec; fn params(&self) -> &CommonParams;
/// Get current EVM factory /// Get current EVM factory
fn vm_factory(&self) -> &Factory; fn vm_factory(&self) -> &Factory;
@ -43,29 +43,33 @@ pub trait Engine : Sync + Send {
/// Get the EVM schedule for the given `env_info`. /// Get the EVM schedule for the given `env_info`.
fn schedule(&self, env_info: &EnvInfo) -> Schedule; 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`. /// 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. /// Maximum number of uncles a block is allowed to declare.
fn maximum_uncle_count(&self) -> usize { 2 } fn maximum_uncle_count(&self) -> usize { 2 }
/// The number of generations back that uncles can be. /// The number of generations back that uncles can be.
fn maximum_uncle_age(&self) -> usize { 6 } fn maximum_uncle_age(&self) -> usize { 6 }
/// The nonce with which accounts begin. /// 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. /// Block transformation functions, before the transactions.
fn on_new_block(&self, _block: &mut ExecutedBlock) {} fn on_new_block(&self, _block: &mut ExecutedBlock) {}
/// Block transformation functions, after the transactions. /// Block transformation functions, after the transactions.
fn on_close_block(&self, _block: &mut ExecutedBlock) {} fn on_close_block(&self, _block: &mut ExecutedBlock) {}
/// Phase 1 quick block verification. Only does checks that are cheap. `block` (the header's full block) /// Phase 1 quick block verification. Only does checks that are cheap. `block` (the header's full block)
/// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import. /// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import.
fn verify_block_basic(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) } fn verify_block_basic(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) }
/// Phase 2 verification. Perform costly checks such as transaction signatures. `block` (the header's full block) /// Phase 2 verification. Perform costly checks such as transaction signatures. `block` (the header's full block)
/// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import. /// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import.
fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) } fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) }
/// Phase 3 verification. Check block information against parent and uncles. `block` (the header's full block) /// Phase 3 verification. Check block information against parent and uncles. `block` (the header's full block)
/// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import. /// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import.
fn verify_block_family(&self, _header: &Header, _parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) } fn verify_block_family(&self, _header: &Header, _parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) }
@ -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 // 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. // from Spec into here and removing the Spec::builtins field.
/// Determine whether a particular address is a builtin contract. /// 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`. /// Determine the code execution cost of the builtin contract with address `a`.
/// Panics if `is_builtin(a)` is not true. /// 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`. /// Execution the builtin contract `a` on `input` and return `output`.
/// Panics if `is_builtin(a)` is not true. /// 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. // 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))
}
} }

View File

@ -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 { impl From<ethjson::vm::Env> for EnvInfo {
fn from(e: ethjson::vm::Env) -> Self { fn from(e: ethjson::vm::Env) -> Self {
let number = e.number.into(); let number = e.number.into();
@ -90,24 +75,20 @@ mod tests {
extern crate rustc_serialize; extern crate rustc_serialize;
use super::*; use super::*;
use rustc_serialize::*;
use util::from_json::FromJson;
use util::hash::*; use util::hash::*;
use util::numbers::U256;
use std::str::FromStr; use std::str::FromStr;
use ethjson;
#[test] #[test]
fn it_serializes_form_json() { fn it_serializes_form_json() {
let env_info = EnvInfo::from_json(&json::Json::from_str( let env_info = EnvInfo::from(ethjson::vm::Env {
r#" author: ethjson::hash::Address(Address::from_str("000000f00000000f000000000000f00000000f00").unwrap()),
{ number: ethjson::uint::Uint(U256::from(1_112_339)),
"currentCoinbase": "0x000000f00000000f000000000000f00000000f00", difficulty: ethjson::uint::Uint(U256::from(50_000)),
"currentNumber": 1112339, gas_limit: ethjson::uint::Uint(U256::from(40_000)),
"currentDifficulty": 50000, timestamp: ethjson::uint::Uint(U256::from(1_100))
"currentGasLimit" : 40000, });
"currentTimestamp" : 1100
}
"#
).unwrap());
assert_eq!(env_info.number, 1112339); assert_eq!(env_info.number, 1112339);
assert_eq!(env_info.author, Address::from_str("000000f00000000f000000000000f00000000f00").unwrap()); assert_eq!(env_info.author, Address::from_str("000000f00000000f000000000000f00000000f00").unwrap());

View File

@ -19,42 +19,63 @@ extern crate ethash;
use self::ethash::{quick_get_difficulty, EthashManager, H256 as EH256}; use self::ethash::{quick_get_difficulty, EthashManager, H256 as EH256};
use common::*; use common::*;
use block::*; use block::*;
use spec::*; use spec::CommonParams;
use engine::*; use engine::*;
use evm::Schedule; use evm::{Schedule, Factory};
use evm::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 /// Engine using Ethash proof-of-work consensus algorithm, suitable for Ethereum
/// mainnet chains in the Olympic, Frontier and Homestead eras. /// mainnet chains in the Olympic, Frontier and Homestead eras.
pub struct Ethash { pub struct Ethash {
spec: Spec, params: CommonParams,
ethash_params: EthashParams,
builtins: BTreeMap<Address, Builtin>,
pow: EthashManager, pow: EthashManager,
factory: Factory, factory: Factory,
u64_params: RwLock<HashMap<String, u64>>,
u256_params: RwLock<HashMap<String, U256>>,
} }
impl Ethash { impl Ethash {
/// Create a new boxed instance of Ethash engine /// Create a new instance of Ethash engine
pub fn new_boxed(spec: Spec) -> Box<Engine> { pub fn new(params: CommonParams, ethash_params: EthashParams, builtins: BTreeMap<Address, Builtin>) -> Self {
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 {
Ethash { Ethash {
spec: spec, params: params,
ethash_params: ethash_params,
builtins: builtins,
pow: EthashManager::new(), pow: EthashManager::new(),
factory: Factory::default(), 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 // Two fields - mix
fn seal_fields(&self) -> usize { 2 } 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`. /// Additional engine-specific information for the user/developer concerning `header`.
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() } fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() }
fn spec(&self) -> &Spec { &self.spec }
fn vm_factory(&self) -> &Factory { fn vm_factory(&self) -> &Factory {
&self.factory &self.factory
} }
fn schedule(&self, env_info: &EnvInfo) -> Schedule { fn schedule(&self, env_info: &EnvInfo) -> Schedule {
trace!(target: "client", "Creating schedule. param={:?}, fCML={}", self.spec().engine_params.get("frontierCompatibilityModeLimit"), self.u64_param("frontierCompatibilityModeLimit")); trace!(target: "client", "Creating schedule. fCML={}", self.params.frontier_compatibility_mode_limit);
if env_info.number < self.u64_param("frontierCompatibilityModeLimit") {
if env_info.number < self.params.frontier_compatibility_mode_limit {
Schedule::new_frontier() Schedule::new_frontier()
} else { } else {
Schedule::new_homestead() Schedule::new_homestead()
@ -86,7 +113,7 @@ impl Engine for Ethash {
header.difficulty = self.calculate_difficuty(header, parent); header.difficulty = self.calculate_difficuty(header, parent);
header.gas_limit = { header.gas_limit = {
let gas_limit = parent.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 { if gas_limit < gas_floor_target {
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - x!(1)) min(gas_floor_target, gas_limit + gas_limit / bound_divisor - x!(1))
} else { } else {
@ -100,7 +127,7 @@ impl Engine for Ethash {
/// Apply the block reward on finalisation of the block. /// 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). /// 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) { 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(); let fields = block.fields_mut();
// Bestow block reward // Bestow block reward
@ -125,7 +152,7 @@ impl Engine for Ethash {
try!(UntrustedRlp::new(&header.seal[1]).as_val::<H64>()); try!(UntrustedRlp::new(&header.seal[1]).as_val::<H64>());
// TODO: consider removing these lines. // 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 { if header.difficulty < min_difficulty {
return Err(From::from(BlockError::DifficultyOutOfBounds(OutOfBounds { min: Some(min_difficulty), max: None, found: header.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 { if header.difficulty != expected_difficulty {
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.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 min_gas = parent.gas_limit - parent.gas_limit / gas_limit_divisor;
let max_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 { 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> { 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()); try!(t.check_low_s());
} }
Ok(()) Ok(())
@ -189,16 +216,6 @@ impl Engine for Ethash {
fn verify_transaction(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> { fn verify_transaction(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> {
t.sender().map(|_|()) // Perform EC recovery and cache sender 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 #[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"); panic!("Can't calculate genesis block difficulty");
} }
let min_difficulty = self.u256_param("minimumDifficulty"); let min_difficulty = self.ethash_params.minimum_difficulty;
let difficulty_bound_divisor = self.u256_param("difficultyBoundDivisor"); let difficulty_bound_divisor = self.ethash_params.difficulty_bound_divisor;
let duration_limit = self.u64_param("durationLimit"); let duration_limit = self.ethash_params.duration_limit;
let frontier_limit = self.u64_param("frontierCompatibilityModeLimit"); let frontier_limit = self.params.frontier_compatibility_mode_limit;
let mut target = if header.number < frontier_limit { let mut target = if header.number < frontier_limit {
if header.timestamp >= parent.timestamp + duration_limit { if header.timestamp >= parent.timestamp + duration_limit {
parent.difficulty - (parent.difficulty / difficulty_bound_divisor) parent.difficulty - (parent.difficulty / difficulty_bound_divisor)
@ -283,16 +301,16 @@ mod tests {
use block::*; use block::*;
use engine::*; use engine::*;
use tests::helpers::*; use tests::helpers::*;
use super::{Ethash};
use super::super::new_morden; use super::super::new_morden;
#[test] #[test]
fn on_close_block() { fn on_close_block() {
let engine = new_morden().to_engine().unwrap(); let spec = new_morden();
let genesis_header = engine.spec().genesis_header(); let engine = &spec.engine;
let genesis_header = spec.genesis_header();
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
let mut db = db_result.take(); 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 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 = OpenBlock::new(engine.deref(), false, db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]);
let b = b.close(); let b = b.close();
@ -301,11 +319,12 @@ mod tests {
#[test] #[test]
fn on_close_block_with_uncle() { fn on_close_block_with_uncle() {
let engine = new_morden().to_engine().unwrap(); let spec = new_morden();
let genesis_header = engine.spec().genesis_header(); let engine = &spec.engine;
let genesis_header = spec.genesis_header();
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
let mut db = db_result.take(); 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 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 b = OpenBlock::new(engine.deref(), false, db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]);
let mut uncle = Header::new(); let mut uncle = Header::new();
@ -320,27 +339,20 @@ mod tests {
#[test] #[test]
fn has_valid_metadata() { fn has_valid_metadata() {
let engine = Ethash::new_boxed(new_morden()); let engine = new_morden().engine;
assert!(!engine.name().is_empty()); assert!(!engine.name().is_empty());
assert!(engine.version().major >= 1); 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] #[test]
fn can_return_factory() { fn can_return_factory() {
let engine = Ethash::new_test(new_morden()); let engine = new_morden().engine;
engine.vm_factory(); engine.vm_factory();
} }
#[test] #[test]
fn can_return_schedule() { fn can_return_schedule() {
let engine = Ethash::new_test(new_morden()); let engine = new_morden().engine;
let schedule = engine.schedule(&EnvInfo { let schedule = engine.schedule(&EnvInfo {
number: 10000000, number: 10000000,
author: x!(0), author: x!(0),
@ -368,7 +380,8 @@ mod tests {
#[test] #[test]
fn can_do_seal_verification_fail() { 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 header: Header = Header::default();
let verify_result = engine.verify_block_basic(&header, None); let verify_result = engine.verify_block_basic(&header, None);
@ -382,7 +395,7 @@ mod tests {
#[test] #[test]
fn can_do_difficulty_verification_fail() { fn can_do_difficulty_verification_fail() {
let engine = Ethash::new_test(new_morden()); let engine = new_morden().engine;
let mut header: Header = Header::default(); let mut header: Header = Header::default();
header.set_seal(vec![rlp::encode(&H256::zero()).to_vec(), rlp::encode(&H64::zero()).to_vec()]); header.set_seal(vec![rlp::encode(&H256::zero()).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
@ -397,7 +410,7 @@ mod tests {
#[test] #[test]
fn can_do_proof_of_work_verification_fail() { 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(); let mut header: Header = Header::default();
header.set_seal(vec![rlp::encode(&H256::zero()).to_vec(), rlp::encode(&H64::zero()).to_vec()]); header.set_seal(vec![rlp::encode(&H256::zero()).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap()); header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap());
@ -413,7 +426,7 @@ mod tests {
#[test] #[test]
fn can_do_seal_unordered_verification_fail() { 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 header: Header = Header::default();
let verify_result = engine.verify_block_unordered(&header, None); let verify_result = engine.verify_block_unordered(&header, None);
@ -427,7 +440,7 @@ mod tests {
#[test] #[test]
fn can_do_seal256_verification_fail() { fn can_do_seal256_verification_fail() {
let engine = Ethash::new_test(new_morden()); let engine = new_morden().engine;
let mut header: Header = Header::default(); let mut header: Header = Header::default();
header.set_seal(vec![rlp::encode(&H256::zero()).to_vec(), rlp::encode(&H64::zero()).to_vec()]); 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); let verify_result = engine.verify_block_unordered(&header, None);
@ -441,7 +454,7 @@ mod tests {
#[test] #[test]
fn can_do_proof_of_work_unordered_verification_fail() { 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(); 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_seal(vec![rlp::encode(&H256::from("b251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d")).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap()); header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap());
@ -457,7 +470,7 @@ mod tests {
#[test] #[test]
fn can_verify_block_family_genesis_fail() { 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 header: Header = Header::default();
let parent_header: Header = Header::default(); let parent_header: Header = Header::default();
@ -472,7 +485,7 @@ mod tests {
#[test] #[test]
fn can_verify_block_family_difficulty_fail() { 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(); let mut header: Header = Header::default();
header.set_number(2); header.set_number(2);
let mut parent_header: Header = Header::default(); let mut parent_header: Header = Header::default();
@ -489,7 +502,7 @@ mod tests {
#[test] #[test]
fn can_verify_block_family_gas_fail() { 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(); let mut header: Header = Header::default();
header.set_number(2); header.set_number(2);
header.set_difficulty(U256::from_str("0000000000000000000000000000000000000000000000000000000000020000").unwrap()); header.set_difficulty(U256::from_str("0000000000000000000000000000000000000000000000000000000000020000").unwrap());

View File

@ -30,22 +30,22 @@ pub use self::denominations::*;
use super::spec::*; use super::spec::*;
/// Create a new Olympic chain 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. /// 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. /// 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. /// 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. /// 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. /// 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)] #[cfg(test)]
mod tests { mod tests {
@ -57,11 +57,12 @@ mod tests {
#[test] #[test]
fn ensure_db_good() { fn ensure_db_good() {
let engine = new_morden().to_engine().unwrap(); let spec = new_morden();
let genesis_header = engine.spec().genesis_header(); let engine = &spec.engine;
let genesis_header = spec.genesis_header();
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
let mut db = db_result.take(); 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()); 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("0000000000000000000000000000000000000001")), U256::from(1u64));
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000002")), 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(); let genesis = morden.genesis_block();
assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap()); assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap());
let _ = morden.to_engine(); let _ = morden.engine;
} }
#[test] #[test]
@ -90,6 +91,6 @@ mod tests {
let genesis = frontier.genesis_block(); let genesis = frontier.genesis_block();
assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3").unwrap()); assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3").unwrap());
let _ = frontier.to_engine(); let _ = frontier.engine;
} }
} }

View File

@ -330,7 +330,7 @@ mod tests {
fn new() -> Self { fn new() -> Self {
TestSetup { TestSetup {
state: get_temp_state(), state: get_temp_state(),
engine: get_test_spec().to_engine().unwrap(), engine: get_test_spec().engine,
sub_state: Substate::new(), sub_state: Substate::new(),
env_info: get_test_env_info() env_info: get_test_env_info()
} }

View File

@ -27,33 +27,6 @@ use substate::*;
use tests::helpers::*; use tests::helpers::*;
use ethjson; 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)] #[derive(Debug, PartialEq)]
struct CallCreate { struct CallCreate {
data: Bytes, 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(); let mut state = state_result.reference_mut();
state.populate_from(From::from(vm.pre_state.clone())); state.populate_from(From::from(vm.pre_state.clone()));
let info = From::from(vm.env); 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 params = ActionParams::from(vm.transaction);
let mut substate = Substate::new(); let mut substate = Substate::new();

View File

@ -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 tests = ethjson::state::Test::load(json_data).unwrap();
let mut failed = Vec::new(); let mut failed = Vec::new();
let engine = match era { let engine = match era {
ChainEra::Frontier => ethereum::new_mainnet_like(), ChainEra::Frontier => ethereum::new_mainnet_like().engine,
ChainEra::Homestead => ethereum::new_homestead_test(), ChainEra::Homestead => ethereum::new_homestead_test().engine
}.to_engine().unwrap(); };
for (name, test) in tests.into_iter() { for (name, test) in tests.into_iter() {
let mut fail = false; let mut fail = false;

View File

@ -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. /// Log localized in a blockchain.
#[derive(Default, Debug, PartialEq, Clone)] #[derive(Default, Debug, PartialEq, Clone)]
pub struct LocalizedLogEntry { pub struct LocalizedLogEntry {

View File

@ -14,26 +14,29 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // 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 engine::Engine;
use spec::Spec; use spec::CommonParams;
use evm::Schedule; use evm::{Schedule, Factory};
use evm::Factory;
use env_info::EnvInfo; use env_info::EnvInfo;
/// An engine which does not provide any consensus mechanism. /// An engine which does not provide any consensus mechanism.
pub struct NullEngine { pub struct NullEngine {
spec: Spec, params: CommonParams,
factory: Factory builtins: BTreeMap<Address, Builtin>,
factory: Factory,
} }
impl NullEngine { impl NullEngine {
/// Returns new instance of NullEngine with default VM Factory /// Returns new instance of NullEngine with default VM Factory
pub fn new_boxed(spec: Spec) -> Box<Engine> { pub fn new(params: CommonParams, builtins: BTreeMap<Address, Builtin>) -> Self {
Box::new(NullEngine{ NullEngine{
spec: spec, params: params,
// TODO [todr] should this return any specific factory? builtins: builtins,
factory: Factory::default() factory: Factory::default()
}) }
} }
} }
@ -41,13 +44,21 @@ impl Engine for NullEngine {
fn vm_factory(&self) -> &Factory { fn vm_factory(&self) -> &Factory {
&self.factory &self.factory
} }
fn name(&self) -> &str { "NullEngine" }
fn spec(&self) -> &Spec { &self.spec } fn name(&self) -> &str {
"NullEngine"
}
fn params(&self) -> &CommonParams {
&self.params
}
fn builtins(&self) -> &BTreeMap<Address, Builtin> {
&self.builtins
}
fn schedule(&self, env_info: &EnvInfo) -> Schedule { 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() Schedule::new_frontier()
} else { } else {
Schedule::new_homestead() Schedule::new_homestead()

View File

@ -80,12 +80,22 @@ impl From<ethjson::blockchain::Account> for PodAccount {
balance: a.balance.into(), balance: a.balance.into(),
nonce: a.nonce.into(), nonce: a.nonce.into(),
code: a.code.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 key: U256 = key.into();
let value: U256 = value.into(); let value: U256 = value.into();
acc.insert(H256::from(key), H256::from(value)); (H256::from(key), H256::from(value))
acc }).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()
} }
} }
} }

View File

@ -46,33 +46,20 @@ impl PodState {
pub fn drain(self) -> BTreeMap<Address, PodAccount> { self.0 } pub fn drain(self) -> BTreeMap<Address, PodAccount> { self.0 }
} }
impl FromJson for PodState { impl From<ethjson::blockchain::State> for PodState {
/// Translate the JSON object into a hash map of account information ready for insertion into State. fn from(s: ethjson::blockchain::State) -> PodState {
fn from_json(json: &Json) -> PodState { let state = s.into_iter().map(|(addr, acc)| (addr.into(), PodAccount::from(acc))).collect();
PodState(json.as_object().unwrap().iter().fold(BTreeMap::new(), |mut state, (address, acc)| { PodState(state)
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 { impl From<ethjson::spec::State> for PodState {
fn from(s: ethjson::blockchain::State) -> PodState { fn from(s: ethjson::spec::State) -> PodState {
PodState(s.0.into_iter().fold(BTreeMap::new(), |mut acc, (key, value)| { let state: BTreeMap<_,_> = s.into_iter()
acc.insert(key.into(), PodAccount::from(value)); .filter(|pair| !pair.1.is_empty())
acc .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());
}
}

View File

@ -60,7 +60,7 @@ impl ClientService {
panic_handler.forward_from(&net_service); panic_handler.forward_from(&net_service);
info!("Starting {}", net_service.host_info()); 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())); let client = try!(Client::new(config, spec, db_path, net_service.io().channel()));
panic_handler.forward_from(client.deref()); panic_handler.forward_from(client.deref());
let client_io = Arc::new(ClientIoHandler { let client_io = Arc::new(ClientIoHandler {

View File

@ -16,26 +16,9 @@
use util::rlp::*; use util::rlp::*;
use util::numbers::{Uint, U256}; use util::numbers::{Uint, U256};
use util::hash::{H64, Address, H256}; use util::hash::{Address, H256};
use ethjson; use ethjson;
use super::seal::Seal;
/// 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>,
},
}
/// Genesis components. /// Genesis components.
pub struct Genesis { pub struct Genesis {
@ -66,16 +49,7 @@ pub struct Genesis {
impl From<ethjson::spec::Genesis> for Genesis { impl From<ethjson::spec::Genesis> for Genesis {
fn from(g: ethjson::spec::Genesis) -> Self { fn from(g: ethjson::spec::Genesis) -> Self {
Genesis { Genesis {
seal: match (g.nonce, g.mix_hash) { seal: From::from(g.seal),
(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(),
}
},
difficulty: g.difficulty.into(), difficulty: g.difficulty.into(),
author: g.author.into(), author: g.author.into(),
timestamp: g.timestamp.into(), timestamp: g.timestamp.into(),

View File

@ -17,6 +17,7 @@
//! Blockchain params. //! Blockchain params.
mod genesis; mod genesis;
mod seal;
pub mod spec; pub mod spec;
pub use self::spec::*; pub use self::spec::*;

81
ethcore/src/spec/seal.rs Normal file
View 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()
}
}
}

View File

@ -21,55 +21,51 @@ use engine::*;
use pod_state::*; use pod_state::*;
use null_engine::*; use null_engine::*;
use account_db::*; use account_db::*;
use super::genesis::Genesis;
use super::seal::Generic as GenericSeal;
use ethereum; use ethereum;
use super::genesis::{Seal as GenesisSeal, Genesis}; use ethjson;
/// Convert JSON value to equivalent RLP representation. /// Parameters common to all engines.
// TODO: handle container types. #[derive(Debug, PartialEq, Clone)]
fn json_to_rlp(json: &Json) -> Bytes { pub struct CommonParams {
match *json { /// Account start nonce.
Json::Boolean(o) => encode(&(if o {1u64} else {0})).to_vec(), pub account_start_nonce: U256,
Json::I64(o) => encode(&(o as u64)).to_vec(), /// Frontier compatibility mode limit.
Json::U64(o) => encode(&o).to_vec(), pub frontier_compatibility_mode_limit: u64,
Json::String(ref s) if s.len() >= 2 && &s[0..2] == "0x" && U256::from_str(&s[2..]).is_ok() => { /// Maximum size of extra data.
encode(&U256::from_str(&s[2..]).unwrap()).to_vec() pub maximum_extra_data_size: usize,
}, /// Network id.
Json::String(ref s) => { pub network_id: U256,
encode(s).to_vec() /// Minimum gas limit.
}, pub min_gas_limit: U256,
_ => panic!()
}
} }
/// Convert JSON to a string->RLP map. impl From<ethjson::spec::Params> for CommonParams {
fn json_to_rlp_map(json: &Json) -> HashMap<String, Bytes> { fn from(p: ethjson::spec::Params) -> Self {
json.as_object().unwrap().iter().map(|(k, v)| (k, json_to_rlp(v))).fold(HashMap::new(), |mut acc, kv| { CommonParams {
acc.insert(kv.0.clone(), kv.1); account_start_nonce: p.account_start_nonce.into(),
acc 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 /// 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. /// chain and those to be interpreted by the active chain engine.
#[derive(Debug)]
pub struct Spec { pub struct Spec {
/// User friendly spec name /// User friendly spec name
pub name: String, pub name: String,
/// What engine are we using for this? /// What engine are we using for this?
pub engine_name: String, pub engine: Box<Engine>,
/// Known nodes on the network in enode format. /// Known nodes on the network in enode format.
pub nodes: Vec<String>, pub nodes: Vec<String>,
/// Network ID
pub network_id: U256,
/// Parameters concerning operation of the specific engine we're using. /// Parameters common to all engines.
/// Maps the parameter name to an RLP-encoded value. pub params: CommonParams,
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>,
/// The genesis block's parent hash field. /// The genesis block's parent hash field.
pub parent_hash: H256, pub parent_hash: H256,
@ -101,15 +97,41 @@ pub struct Spec {
genesis_state: PodState, 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 { impl Spec {
/// Convert this object into a boxed Engine of the right underlying type. /// Convert engine spec into a boxed Engine of the right underlying type.
// TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead. /// TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead.
pub fn to_engine(self) -> Result<Box<Engine>, Error> { fn engine(engine_spec: ethjson::spec::Engine, params: CommonParams, builtins: BTreeMap<Address, Builtin>) -> Box<Engine> {
match self.engine_name.as_ref() { match engine_spec {
"NullEngine" => Ok(NullEngine::new_boxed(self)), ethjson::spec::Engine::Null => Box::new(NullEngine::new(params, builtins)),
"Ethash" => Ok(ethereum::Ethash::new_boxed(self)), ethjson::spec::Engine::Ethash(ethash) => Box::new(ethereum::Ethash::new(params, From::from(ethash.params), builtins))
_ => Err(Error::UnknownEngineName(self.engine_name.clone()))
} }
} }
@ -125,7 +147,7 @@ impl Spec {
pub fn nodes(&self) -> &Vec<String> { &self.nodes } pub fn nodes(&self) -> &Vec<String> { &self.nodes }
/// Get the configured Network ID. /// 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. /// Get the header of the genesis block.
pub fn genesis_header(&self) -> Header { pub fn genesis_header(&self) -> Header {
@ -168,49 +190,9 @@ impl Spec {
ret.out() 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. /// Overwrite the genesis components.
pub fn overwrite_genesis_params(&mut self, g: Genesis) { pub fn overwrite_genesis_params(&mut self, g: Genesis) {
let (seal_fields, seal_rlp) = match g.seal { let seal: GenericSeal = g.seal.into();
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())
}
};
self.parent_hash = g.parent_hash; self.parent_hash = g.parent_hash;
self.transactions_root = g.transactions_root; self.transactions_root = g.transactions_root;
self.receipts_root = g.receipts_root; self.receipts_root = g.receipts_root;
@ -220,8 +202,8 @@ impl Spec {
self.gas_used = g.gas_used; self.gas_used = g.gas_used;
self.timestamp = g.timestamp; self.timestamp = g.timestamp;
self.extra_data = g.extra_data; self.extra_data = g.extra_data;
self.seal_fields = seal_fields; self.seal_fields = seal.fields;
self.seal_rlp = seal_rlp; self.seal_rlp = seal.rlp;
self.state_root_memo = RwLock::new(g.state_root); self.state_root_memo = RwLock::new(g.state_root);
} }
@ -235,74 +217,7 @@ impl Spec {
pub fn is_state_root_valid(&self) -> bool { pub fn is_state_root_valid(&self) -> bool {
self.state_root_memo.read().unwrap().clone().map_or(true, |sr| sr == self.genesis_state.root()) 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. /// 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 { pub fn ensure_db_good(&self, db: &mut HashDB) -> bool {
if !db.contains(&self.state_root()) { if !db.contains(&self.state_root()) {
@ -321,21 +236,20 @@ impl Spec {
} else { false } } else { false }
} }
/// Create a new Spec from a JSON UTF-8 data resource `data`. /// Loads spec from json file.
pub fn from_json_utf8(data: &[u8]) -> Spec { pub fn load(reader: &[u8]) -> Self {
Self::from_json_str(::std::str::from_utf8(data).unwrap()) From::from(ethjson::spec::Spec::load(reader).expect("invalid json file"))
}
/// 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"))
} }
/// Create a new Spec which conforms to the Morden chain except that it's a NullEngine consensus. /// 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. /// 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)] #[cfg(test)]
@ -353,7 +267,5 @@ mod tests {
assert_eq!(test_spec.state_root(), H256::from_str("f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9").unwrap()); assert_eq!(test_spec.state_root(), H256::from_str("f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9").unwrap());
let genesis = test_spec.genesis_block(); let genesis = test_spec.genesis_block();
assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap()); assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap());
let _ = test_spec.to_engine();
} }
} }

View File

@ -558,7 +558,7 @@ fn should_not_trace_call_transaction_to_builtin() {
let mut info = EnvInfo::default(); let mut info = EnvInfo::default();
info.gas_limit = x!(1_000_000); info.gas_limit = x!(1_000_000);
let engine = Spec::new_test().to_engine().unwrap(); let engine = Spec::new_test().engine;
let t = Transaction { let t = Transaction {
nonce: x!(0), nonce: x!(0),
@ -583,7 +583,7 @@ fn should_not_trace_subcall_transaction_to_builtin() {
let mut info = EnvInfo::default(); let mut info = EnvInfo::default();
info.gas_limit = x!(1_000_000); info.gas_limit = x!(1_000_000);
let engine = Spec::new_test().to_engine().unwrap(); let engine = Spec::new_test().engine;
let t = Transaction { let t = Transaction {
nonce: x!(0), nonce: x!(0),
@ -624,7 +624,7 @@ fn should_not_trace_callcode() {
let mut info = EnvInfo::default(); let mut info = EnvInfo::default();
info.gas_limit = x!(1_000_000); info.gas_limit = x!(1_000_000);
let engine = Spec::new_test().to_engine().unwrap(); let engine = Spec::new_test().engine;
let t = Transaction { let t = Transaction {
nonce: x!(0), nonce: x!(0),
@ -667,7 +667,7 @@ fn should_not_trace_delegatecall() {
let mut info = EnvInfo::default(); let mut info = EnvInfo::default();
info.gas_limit = x!(1_000_000); info.gas_limit = x!(1_000_000);
info.number = 0x789b0; 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); println!("schedule.have_delegate_call: {:?}", engine.schedule(&info).have_delegate_call);

View File

@ -40,8 +40,7 @@ fn returns_state_root_basic() {
let client_result = generate_dummy_client(6); let client_result = generate_dummy_client(6);
let client = client_result.reference(); let client = client_result.reference();
let test_spec = get_test_spec(); let test_spec = get_test_spec();
let test_engine = test_spec.to_engine().unwrap(); let state_root = test_spec.genesis_header().state_root;
let state_root = test_engine.spec().genesis_header().state_root;
assert!(client.state_data(&state_root).is_some()); assert!(client.state_data(&state_root).is_some());
} }

View File

@ -52,7 +52,7 @@ impl<T> GuardedTempResult<T> {
pub struct TestEngine { pub struct TestEngine {
factory: Factory, factory: Factory,
spec: Spec, engine: Box<Engine>,
max_depth: usize max_depth: usize
} }
@ -60,18 +60,29 @@ impl TestEngine {
pub fn new(max_depth: usize, factory: Factory) -> TestEngine { pub fn new(max_depth: usize, factory: Factory) -> TestEngine {
TestEngine { TestEngine {
factory: factory, factory: factory,
spec: ethereum::new_frontier_test(), engine: ethereum::new_frontier_test().engine,
max_depth: max_depth max_depth: max_depth
} }
} }
} }
impl Engine for TestEngine { impl Engine for TestEngine {
fn name(&self) -> &str { "TestEngine" } fn name(&self) -> &str {
fn spec(&self) -> &Spec { &self.spec } "TestEngine"
}
fn params(&self) -> &CommonParams {
self.engine.params()
}
fn builtins(&self) -> &BTreeMap<Address, Builtin> {
self.engine.builtins()
}
fn vm_factory(&self) -> &Factory { fn vm_factory(&self) -> &Factory {
&self.factory &self.factory
} }
fn schedule(&self, _env_info: &EnvInfo) -> Schedule { fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
let mut schedule = Schedule::new_frontier(); let mut schedule = Schedule::new_frontier();
schedule.max_depth = self.max_depth; 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 client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
let test_spec = get_test_spec(); let test_spec = get_test_spec();
let test_engine = test_spec.to_engine().unwrap(); let test_engine = &test_spec.engine;
let state_root = test_engine.spec().genesis_header().state_root; let state_root = test_spec.genesis_header().state_root;
let mut rolling_hash = test_engine.spec().genesis_header().hash(); let mut rolling_hash = test_spec.genesis_header().hash();
let mut rolling_block_number = 1; let mut rolling_block_number = 1;
let mut rolling_timestamp = 40; let mut rolling_timestamp = 40;
for _ in 0..block_number { for _ in 0..block_number {
let mut header = Header::new(); let mut header = Header::new();
header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap()); header.gas_limit = test_engine.params().min_gas_limit;
header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap()); header.difficulty = U256::from(0x20000);
header.timestamp = rolling_timestamp; header.timestamp = rolling_timestamp;
header.number = rolling_block_number; header.number = rolling_block_number;
header.parent_hash = rolling_hash; 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) { 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_spec = get_test_spec();
let test_engine = test_spec.to_engine().unwrap(); let test_engine = &test_spec.engine;
let state_root = test_engine.spec().genesis_header().state_root; //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_hash = client.chain_info().best_block_hash;
let mut rolling_block_number = starting_number as u64; let mut rolling_block_number = starting_number as u64;
let mut rolling_timestamp = timestamp_salt + starting_number as u64 * 10; 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 { for _ in 0..block_number {
let mut header = Header::new(); let mut header = Header::new();
header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap()); header.gas_limit = test_engine.params().min_gas_limit;
header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap()); header.difficulty = U256::from(0x20000);
header.timestamp = rolling_timestamp; header.timestamp = rolling_timestamp;
header.number = rolling_block_number; header.number = rolling_block_number;
header.parent_hash = rolling_hash; 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> { pub fn get_good_dummy_block_seq(count: usize) -> Vec<Bytes> {
let test_spec = get_test_spec(); let test_spec = get_test_spec();
let test_engine = test_spec.to_engine().unwrap(); get_good_dummy_block_fork_seq(1, count, &test_spec.genesis_header().hash())
get_good_dummy_block_fork_seq(1, count, &test_engine.spec().genesis_header().hash())
} }
pub fn get_good_dummy_block_fork_seq(start_number: usize, count: usize, parent_hash: &H256) -> Vec<Bytes> { 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_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 rolling_timestamp = start_number as u64 * 10;
let mut parent = *parent_hash; let mut parent = *parent_hash;
let mut r = Vec::new(); let mut r = Vec::new();
for i in start_number .. start_number + count + 1 { for i in start_number .. start_number + count + 1 {
let mut block_header = Header::new(); 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.difficulty = U256::from(i).mul(U256([0, 1, 0, 0]));
block_header.timestamp = rolling_timestamp; block_header.timestamp = rolling_timestamp;
block_header.number = i as u64; block_header.number = i as u64;
block_header.parent_hash = parent; 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(); parent = block_header.hash();
rolling_timestamp = rolling_timestamp + 10; 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 { pub fn get_good_dummy_block() -> Bytes {
let mut block_header = Header::new(); let mut block_header = Header::new();
let test_spec = get_test_spec(); let test_spec = get_test_spec();
let test_engine = test_spec.to_engine().unwrap(); let test_engine = &test_spec.engine;
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 = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap()); block_header.difficulty = U256::from(0x20000);
block_header.timestamp = 40; block_header.timestamp = 40;
block_header.number = 1; 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 = test_engine.spec().genesis_header().state_root; block_header.state_root = test_spec.genesis_header().state_root;
create_test_block(&block_header) create_test_block(&block_header)
} }
@ -324,12 +335,12 @@ pub fn get_good_dummy_block() -> Bytes {
pub fn get_bad_state_dummy_block() -> Bytes { pub fn get_bad_state_dummy_block() -> Bytes {
let mut block_header = Header::new(); let mut block_header = Header::new();
let test_spec = get_test_spec(); let test_spec = get_test_spec();
let test_engine = test_spec.to_engine().unwrap(); let test_engine = &test_spec.engine;
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 = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap()); block_header.difficulty = U256::from(0x20000);
block_header.timestamp = 40; block_header.timestamp = 40;
block_header.number = 1; 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); block_header.state_root = x!(0xbad);
create_test_block(&block_header) create_test_block(&block_header)

View File

@ -183,7 +183,7 @@ fn verify_header(header: &Header, engine: &Engine) -> Result<(), Error> {
if header.gas_used > header.gas_limit { 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 }))); 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 { 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 }))); 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 // Test against morden
let mut good = Header::new(); let mut good = Header::new();
let spec = Spec::new_test(); 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_gas_limit = engine.params().min_gas_limit;
let min_difficulty = decode(engine.spec().engine_params.get("minimumDifficulty").unwrap());
good.gas_limit = min_gas_limit; good.gas_limit = min_gas_limit;
good.difficulty = min_difficulty;
good.timestamp = 40; good.timestamp = 40;
good.number = 10; good.number = 10;

1
fmt.sh
View File

@ -9,6 +9,7 @@ $RUSTFMT ./json/src/lib.rs
$RUSTFMT ./miner/src/lib.rs $RUSTFMT ./miner/src/lib.rs
$RUSTFMT ./parity/main.rs $RUSTFMT ./parity/main.rs
$RUSTFMT ./rpc/src/lib.rs $RUSTFMT ./rpc/src/lib.rs
$RUSTFMT ./webapp/src/lib.rs
$RUSTFMT ./sync/src/lib.rs $RUSTFMT ./sync/src/lib.rs
$RUSTFMT ./util/src/lib.rs $RUSTFMT ./util/src/lib.rs

View File

@ -7,6 +7,6 @@ echo "set -e" >> $FILE
echo "cargo build --release --features dev" >> $FILE echo "cargo build --release --features dev" >> $FILE
# Build tests # Build tests
echo "cargo test --no-run --features dev \\" >> $FILE 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 echo "" >> $FILE
chmod +x $FILE chmod +x $FILE

View File

@ -21,7 +21,7 @@ use hash::H256;
use blockchain::state::State; use blockchain::state::State;
use blockchain::header::Header; use blockchain::header::Header;
use blockchain::block::Block; use blockchain::block::Block;
use spec::Genesis; use spec::{Genesis, Seal, Ethereum};
/// Blockchain deserialization. /// Blockchain deserialization.
#[derive(Debug, PartialEq, Deserialize)] #[derive(Debug, PartialEq, Deserialize)]
@ -54,10 +54,10 @@ impl BlockChain {
/// Returns spec compatible genesis struct. /// Returns spec compatible genesis struct.
pub fn genesis(&self) -> Genesis { pub fn genesis(&self) -> Genesis {
Genesis { Genesis {
nonce: Some(self.genesis_block.nonce.clone()), seal: Seal::Ethereum(Ethereum {
mix_hash: Some(self.genesis_block.mix_hash.clone()), nonce: self.genesis_block.nonce.clone(),
seal_fields: None, mix_hash: self.genesis_block.mix_hash.clone(),
seal_rlp: None, }),
difficulty: self.genesis_block.difficulty, difficulty: self.genesis_block.difficulty,
author: self.genesis_block.author.clone(), author: self.genesis_block.author.clone(),
timestamp: self.genesis_block.timestamp, timestamp: self.genesis_block.timestamp,

View File

@ -22,7 +22,7 @@ use blockchain::account::Account;
/// Blockchain test state deserializer. /// Blockchain test state deserializer.
#[derive(Debug, PartialEq, Deserialize, Clone)] #[derive(Debug, PartialEq, Deserialize, Clone)]
pub struct State(pub BTreeMap<Address, Account>); pub struct State(BTreeMap<Address, Account>);
impl IntoIterator for State { impl IntoIterator for State {
type Item = <BTreeMap<Address, Account> as IntoIterator>::Item; type Item = <BTreeMap<Address, Account> as IntoIterator>::Item;

View File

@ -22,9 +22,19 @@ use spec::builtin::Builtin;
/// Spec account. /// Spec account.
#[derive(Debug, PartialEq, Deserialize)] #[derive(Debug, PartialEq, Deserialize)]
pub struct Account { pub struct Account {
builtin: Option<Builtin>, /// Builtin contract.
balance: Option<Uint>, pub builtin: Option<Builtin>,
nonce: Option<Uint>, /// 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)] #[cfg(test)]

View File

@ -17,14 +17,16 @@
//! Spec builtin deserialization. //! Spec builtin deserialization.
/// Linear pricing. /// Linear pricing.
#[derive(Debug, PartialEq, Deserialize)] #[derive(Debug, PartialEq, Deserialize, Clone)]
pub struct Linear { pub struct Linear {
base: u64, /// Base price.
word: u64, pub base: usize,
/// Price for word.
pub word: usize,
} }
/// Pricing variants. /// Pricing variants.
#[derive(Debug, PartialEq, Deserialize)] #[derive(Debug, PartialEq, Deserialize, Clone)]
pub enum Pricing { pub enum Pricing {
/// Linear pricing. /// Linear pricing.
#[serde(rename="linear")] #[serde(rename="linear")]
@ -32,10 +34,12 @@ pub enum Pricing {
} }
/// Spec builtin. /// Spec builtin.
#[derive(Debug, PartialEq, Deserialize)] #[derive(Debug, PartialEq, Deserialize, Clone)]
pub struct Builtin { pub struct Builtin {
name: String, /// Builtin name.
pricing: Pricing, pub name: String,
/// Builtin pricing.
pub pricing: Pricing,
} }
#[cfg(test)] #[cfg(test)]

63
json/src/spec/engine.rs Normal file
View 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
View 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();
}
}

View File

@ -17,27 +17,15 @@
//! Spec genesis deserialization. //! Spec genesis deserialization.
use uint::Uint; use uint::Uint;
use hash::{H64, Address, H256}; use hash::{Address, H256};
use bytes::Bytes; use bytes::Bytes;
use spec::Seal;
/// Spec genesis. /// Spec genesis.
#[derive(Debug, PartialEq, Deserialize)] #[derive(Debug, PartialEq, Deserialize)]
pub struct Genesis { pub struct Genesis {
// old seal /// Seal.
/// Seal nonce. pub seal: Seal,
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>,
/// Difficulty. /// Difficulty.
pub difficulty: Uint, pub difficulty: Uint,
/// Block author. /// Block author.
@ -77,7 +65,12 @@ mod tests {
let s = r#"{ let s = r#"{
"nonce": "0x0000000000000042", "nonce": "0x0000000000000042",
"difficulty": "0x400000000", "difficulty": "0x400000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "seal": {
"ethereum": {
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x00006d6f7264656e"
}
},
"author": "0x0000000000000000000000000000000000000000", "author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00", "timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",

View File

@ -21,9 +21,17 @@ pub mod builtin;
pub mod genesis; pub mod genesis;
pub mod params; pub mod params;
pub mod spec; pub mod spec;
pub mod seal;
pub mod engine;
pub mod state;
pub mod ethash;
pub use self::account::Account; 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::genesis::Genesis;
pub use self::params::Params; pub use self::params::Params;
pub use self::spec::Spec; 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};

View File

@ -17,34 +17,25 @@
//! Spec params deserialization. //! Spec params deserialization.
use uint::Uint; use uint::Uint;
use hash::Address;
/// Spec params. /// Spec params.
#[derive(Debug, PartialEq, Deserialize)] #[derive(Debug, PartialEq, Deserialize)]
pub struct Params { pub struct Params {
/// Account start nonce.
#[serde(rename="accountStartNonce")] #[serde(rename="accountStartNonce")]
account_start_nonce: Uint, pub account_start_nonce: Uint,
/// Homestead transition block number.
#[serde(rename="frontierCompatibilityModeLimit")] #[serde(rename="frontierCompatibilityModeLimit")]
frontier_compatibility_mode_limit: Uint, pub frontier_compatibility_mode_limit: Uint,
/// Maximum size of extra data.
#[serde(rename="maximumExtraDataSize")] #[serde(rename="maximumExtraDataSize")]
maximum_extra_data_size: Uint, pub maximum_extra_data_size: Uint,
#[serde(rename="tieBreakingGas")] /// Network id.
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,
#[serde(rename="networkID")] #[serde(rename="networkID")]
network_id: Uint, pub network_id: Uint,
/// Minimum gas limit.
#[serde(rename="minGasLimit")]
pub min_gas_limit: Uint,
} }
#[cfg(test)] #[cfg(test)]
@ -55,19 +46,13 @@ mod tests {
#[test] #[test]
fn params_deserialization() { fn params_deserialization() {
let s = r#"{ let s = r#"{
"accountStartNonce": "0x00",
"frontierCompatibilityModeLimit": "0x118c30", "frontierCompatibilityModeLimit": "0x118c30",
"maximumExtraDataSize": "0x20", "maximumExtraDataSize": "0x20",
"tieBreakingGas": false, "networkID" : "0x1",
"minGasLimit": "0x1388", "minGasLimit": "0x1388",
"gasLimitBoundDivisor": "0x0400", "accountStartNonce": "0x00"
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"networkID" : "0x1"
}"#; }"#;
let _deserialized: Params = serde_json::from_str(s).unwrap(); let _deserialized: Params = serde_json::from_str(s).unwrap();
// TODO: validate all fields // TODO: validate all fields
} }

73
json/src/spec/seal.rs Normal file
View 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
}
}

View File

@ -16,21 +16,33 @@
//! Spec deserialization. //! Spec deserialization.
use std::collections::BTreeMap; use std::io::Read;
use hash::Address; use serde_json;
use spec::account::Account; use serde_json::Error;
use spec::params::Params; use spec::{Params, Genesis, Engine, State};
use spec::genesis::Genesis;
/// Spec deserialization. /// Spec deserialization.
#[derive(Debug, PartialEq, Deserialize)] #[derive(Debug, PartialEq, Deserialize)]
pub struct Spec { pub struct Spec {
name: String, /// Spec name.
#[serde(rename="engineName")] pub name: String,
engine_name: String, // TODO: consider making it an enum /// Engine.
params: Params, pub engine: Engine,
genesis: Genesis, /// Spec params.
accounts: BTreeMap<Address, Account>, 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)] #[cfg(test)]
@ -42,25 +54,34 @@ mod tests {
fn spec_deserialization() { fn spec_deserialization() {
let s = r#"{ let s = r#"{
"name": "Morden", "name": "Morden",
"engineName": "Ethash", "engine": {
"Ethash": {
"params": {
"tieBreakingGas": false,
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
}
}
},
"params": { "params": {
"accountStartNonce": "0x0100000", "accountStartNonce": "0x0100000",
"frontierCompatibilityModeLimit": "0x789b0", "frontierCompatibilityModeLimit": "0x789b0",
"maximumExtraDataSize": "0x20", "maximumExtraDataSize": "0x20",
"tieBreakingGas": false,
"minGasLimit": "0x1388", "minGasLimit": "0x1388",
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar": "",
"networkID" : "0x2" "networkID" : "0x2"
}, },
"genesis": { "genesis": {
"nonce": "0x00006d6f7264656e", "seal": {
"ethereum": {
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x00006d6f7264656e"
}
},
"difficulty": "0x20000", "difficulty": "0x20000",
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
"author": "0x0000000000000000000000000000000000000000", "author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00", "timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",

44
json/src/spec/state.rs Normal file
View 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()
}
}

View File

@ -23,7 +23,7 @@ use util::numbers::{U256, Uint as U};
/// Lenient uint json deserialization for test json files. /// Lenient uint json deserialization for test json files.
#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] #[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub struct Uint(U256); pub struct Uint(pub U256);
impl Into<U256> for Uint { impl Into<U256> for Uint {
fn into(self) -> U256 { 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 { impl Into<u8> for Uint {
fn into(self) -> u8 { 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 { impl Visitor for UintVisitor {
type Value = Uint; 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 { fn visit_str<E>(&mut self, value: &str) -> Result<Self::Value, E> where E: Error {
let value = match value.len() { let value = match value.len() {
0 => U256::from(0), 0 => U256::from(0),
@ -83,12 +93,13 @@ mod test {
#[test] #[test]
fn uint_deserialization() { 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(); let deserialized: Vec<Uint> = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, vec![ assert_eq!(deserialized, vec![
Uint(U256::from(10)), Uint(U256::from(10)),
Uint(U256::from(10)), Uint(U256::from(10)),
Uint(U256::from(0)), Uint(U256::from(0)),
Uint(U256::from(0)),
Uint(U256::from(0)) Uint(U256::from(0))
]); ]);
} }

View File

@ -105,9 +105,12 @@ pub trait MinerService : Send + Sync {
/// Get the sealing work package and if `Some`, apply some transform. /// 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; 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>; 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. /// Returns highest transaction nonce for given address.
fn last_nonce(&self, address: &Address) -> Option<U256>; fn last_nonce(&self, address: &Address) -> Option<U256>;

View File

@ -228,6 +228,11 @@ impl MinerService for Miner {
queue.find(hash) 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> { fn last_nonce(&self, address: &Address) -> Option<U256> {
self.transaction_queue.lock().unwrap().last_nonce(address) self.transaction_queue.lock().unwrap().last_nonce(address)
} }

View File

@ -42,6 +42,8 @@ extern crate rpassword;
#[cfg(feature = "rpc")] #[cfg(feature = "rpc")]
extern crate ethcore_rpc as rpc; extern crate ethcore_rpc as rpc;
#[cfg(feature = "webapp")]
extern crate ethcore_webapp as webapp;
use std::io::{BufRead, BufReader}; use std::io::{BufRead, BufReader};
use std::fs::File; use std::fs::File;
@ -63,6 +65,10 @@ use ethminer::{Miner, MinerService};
use docopt::Docopt; use docopt::Docopt;
use daemonize::Daemonize; use daemonize::Daemonize;
use number_prefix::{binary_prefix, Standalone, Prefixed}; 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; mod price_info;
@ -98,7 +104,8 @@ Protocol Options:
--identity NAME Specify your node's name. --identity NAME Specify your node's name.
Account Options: 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 --password FILE Provide a file containing a password for unlocking
an account. an account.
@ -118,7 +125,7 @@ Networking Options:
string or input to SHA3 operation. string or input to SHA3 operation.
API and Console Options: 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 --jsonrpc-interface IP Specify the hostname portion of the JSONRPC API
server, IP should be an interface's IP address, or server, IP should be an interface's IP address, or
all (all interfaces) or local [default: local]. all (all interfaces) or local [default: local].
@ -130,6 +137,19 @@ API and Console Options:
interface. APIS is a comma-delimited list of API interface. APIS is a comma-delimited list of API
name. Possible name are web3, eth and net. name. Possible name are web3, eth and net.
[default: web3,eth,net,personal]. [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: Sealing/Mining Options:
--usd-per-tx USD Amount of USD to be paid for a basic transaction --usd-per-tx USD Amount of USD to be paid for a basic transaction
@ -194,7 +214,7 @@ struct Args {
flag_chain: String, flag_chain: String,
flag_db_path: String, flag_db_path: String,
flag_identity: String, flag_identity: String,
flag_unlock: Vec<String>, flag_unlock: Option<String>,
flag_password: Vec<String>, flag_password: Vec<String>,
flag_cache: Option<usize>, flag_cache: Option<usize>,
flag_keys_path: String, flag_keys_path: String,
@ -214,6 +234,11 @@ struct Args {
flag_jsonrpc_port: u16, flag_jsonrpc_port: u16,
flag_jsonrpc_cors: String, flag_jsonrpc_cors: String,
flag_jsonrpc_apis: 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_author: String,
flag_usd_per_tx: String, flag_usd_per_tx: String,
flag_usd_per_eth: String, flag_usd_per_eth: String,
@ -270,10 +295,10 @@ fn setup_rpc_server(
sync: Arc<EthSync>, sync: Arc<EthSync>,
secret_store: Arc<AccountService>, secret_store: Arc<AccountService>,
miner: Arc<Miner>, miner: Arc<Miner>,
url: &str, url: &SocketAddr,
cors_domain: &str, cors_domain: &str,
apis: Vec<&str> apis: Vec<&str>,
) -> Option<Arc<PanicHandler>> { ) -> RpcServer {
use rpc::v1::*; use rpc::v1::*;
let server = rpc::RpcServer::new(); 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"))] #[cfg(not(feature = "rpc"))]
fn setup_rpc_server( fn setup_rpc_server(
_client: Arc<Client>, _client: Arc<Client>,
@ -302,9 +368,24 @@ fn setup_rpc_server(
_miner: Arc<Miner>, _miner: Arc<Miner>,
_url: &str, _url: &str,
_cors_domain: &str, _cors_domain: &str,
_apis: Vec<&str> _apis: Vec<&str>,
) -> Option<Arc<PanicHandler>> { ) -> ! {
None 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() { fn print_version() {
@ -396,7 +477,7 @@ impl Configuration {
"frontier" | "homestead" | "mainnet" => ethereum::new_frontier(), "frontier" | "homestead" | "mainnet" => ethereum::new_frontier(),
"morden" | "testnet" => ethereum::new_morden(), "morden" | "testnet" => ethereum::new_morden(),
"olympic" => ethereum::new_olympic(), "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) die!("{}: Couldn't read chain specification file. Sure it exists?", f)
}).as_ref()), }).as_ref()),
} }
@ -537,14 +618,15 @@ impl Configuration {
.collect::<Vec<_>>() .collect::<Vec<_>>()
.into_iter() .into_iter()
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();
let account_service = AccountService::new_in(Path::new(&self.keys_path())); let account_service = AccountService::new_in(Path::new(&self.keys_path()));
for d in &self.args.flag_unlock { if let Some(ref unlocks) = self.args.flag_unlock {
let a = Address::from_str(clean_0x(&d)).unwrap_or_else(|_| { for d in unlocks.split(',') {
die!("{}: Invalid address for --unlock. Must be 40 hex characters, without the 0x at the beginning.", d) 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 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 account_service
@ -567,7 +649,10 @@ impl Configuration {
let account_service = Arc::new(self.account_service()); let account_service = Arc::new(self.account_service());
// Build client // 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); panic_handler.forward_from(&service);
let client = service.client(); let client = service.client();
@ -582,7 +667,8 @@ impl Configuration {
let sync = EthSync::register(service.network(), sync_config, client.clone(), miner.clone()); let sync = EthSync::register(service.network(), sync_config, client.clone(), miner.clone());
// Setup rpc // 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!("{}:{}", let url = format!("{}:{}",
match self.args.flag_rpcaddr.as_ref().unwrap_or(&self.args.flag_jsonrpc_interface).as_str() { match self.args.flag_rpcaddr.as_ref().unwrap_or(&self.args.flag_jsonrpc_interface).as_str() {
"all" => "0.0.0.0", "all" => "0.0.0.0",
@ -591,23 +677,53 @@ impl Configuration {
}, },
self.args.flag_rpcport.unwrap_or(self.args.flag_jsonrpc_port) 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 addr = 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); let cors_domain = 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); Some(setup_rpc_server(
let server_handler = 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(), service.client(),
sync.clone(), sync.clone(),
account_service.clone(), account_service.clone(),
miner.clone(), miner.clone(),
&url, &url,
cors, auth,
apis.split(',').collect() ))
); } else {
if let Some(handler) = server_handler { None
panic_handler.forward_from(handler.deref()); };
}
}
// Register IO handler // Register IO handler
let io_handler = Arc::new(ClientIoHandler { let io_handler = Arc::new(ClientIoHandler {
@ -619,11 +735,11 @@ impl Configuration {
service.io().register_handler(io_handler).expect("Error registering IO handler"); service.io().register_handler(io_handler).expect("Error registering IO handler");
// Handle exit // 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()); let exit = Arc::new(Condvar::new());
// Handle possible exits // Handle possible exits
@ -637,6 +753,30 @@ fn wait_for_exit(panic_handler: Arc<PanicHandler>) {
// Wait for signal // Wait for signal
let mutex = Mutex::new(()); let mutex = Mutex::new(());
let _ = exit.wait(mutex.lock().unwrap()).unwrap(); 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() { fn main() {

View File

@ -13,7 +13,7 @@ log = "0.3"
serde = "0.7.0" serde = "0.7.0"
serde_json = "0.7.0" serde_json = "0.7.0"
jsonrpc-core = "2.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-util = { path = "../util" }
ethcore = { path = "../ethcore" } ethcore = { path = "../ethcore" }
ethash = { path = "../ethash" } ethash = { path = "../ethash" }

View File

@ -33,10 +33,10 @@ extern crate ethminer;
extern crate transient_hashmap; extern crate transient_hashmap;
use std::sync::Arc; use std::sync::Arc;
use std::thread; use std::net::SocketAddr;
use util::panics::PanicHandler;
use self::jsonrpc_core::{IoHandler, IoDelegate}; use self::jsonrpc_core::{IoHandler, IoDelegate};
pub use jsonrpc_http_server::{Server, RpcServerError};
pub mod v1; pub mod v1;
/// Http server. /// Http server.
@ -45,7 +45,7 @@ pub struct RpcServer {
} }
impl RpcServer { impl RpcServer {
/// Construct new http server object with given number of threads. /// Construct new http server object.
pub fn new() -> RpcServer { pub fn new() -> RpcServer {
RpcServer { RpcServer {
handler: Arc::new(IoHandler::new()), handler: Arc::new(IoHandler::new()),
@ -57,18 +57,9 @@ impl RpcServer {
self.handler.add_delegate(delegate); self.handler.add_delegate(delegate);
} }
/// Start server asynchronously in new thread and returns panic handler. /// Start server asynchronously and returns result with `Server` handle on success or an error.
pub fn start_http(&self, addr: &str, cors_domain: &str, threads: usize) -> Arc<PanicHandler> { pub fn start_http(&self, addr: &SocketAddr, cors_domain: &str) -> Result<Server, RpcServerError> {
let addr = addr.to_owned();
let cors_domain = cors_domain.to_owned(); let cors_domain = cors_domain.to_owned();
let panic_handler = PanicHandler::new_in_arc(); Server::start(addr, self.handler.clone(), jsonrpc_http_server::AccessControlAllowOrigin::Value(cors_domain))
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
} }
} }

View File

@ -61,7 +61,6 @@ impl<F, T> PollManager<F, T> where T: Timer {
} }
// Implementation is always using `poll_mut` // Implementation is always using `poll_mut`
#[cfg(test)]
/// Get a reference to stored poll filter /// Get a reference to stored poll filter
pub fn poll(&mut self, id: &PollId) -> Option<&F> { pub fn poll(&mut self, id: &PollId) -> Option<&F> {
self.polls.prune(); self.polls.prune();

View File

@ -186,7 +186,7 @@ impl<C, S, A, M, EM> EthClient<C, S, A, M, EM>
}.fake_sign(from)) }.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 hash = signed_transaction.hash();
let import = { 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<_>, _>>() { match import.into_iter().collect::<Result<Vec<_>, _>>() {
Ok(_) => { Ok(_) => {
take_weak!(self.sync).new_transaction(raw_transaction);
to_value(&hash) to_value(&hash)
} }
Err(e) => { 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> { fn submit_work(&self, params: Params) -> Result<Value, Error> {
from_params::<(H64, H256, H256)>(params).and_then(|(nonce, pow_hash, mix_hash)| { 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 miner = take_weak!(self.miner);
let client = take_weak!(self.client); let client = take_weak!(self.client);
let seal = vec![encode(&mix_hash).to_vec(), encode(&nonce).to_vec()]; 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()), data: request.data.map_or_else(Vec::new, |d| d.to_vec()),
}.sign(&secret) }.sign(&secret)
}; };
let raw_transaction = encode(&signed_transaction).to_vec(); self.dispatch_transaction(signed_transaction)
self.dispatch_transaction(signed_transaction, raw_transaction)
}, },
Err(_) => { to_value(&H256::zero()) } 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, )| { .and_then(|(raw_transaction, )| {
let raw_transaction = raw_transaction.to_vec(); let raw_transaction = raw_transaction.to_vec();
match UntrustedRlp::new(&raw_transaction).as_val() { 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()), Err(_) => to_value(&H256::zero()),
} }
}) })
@ -638,10 +636,11 @@ impl<C, M> EthFilter for EthFilterClient<C, M>
to_value(&diff) 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.from_block = BlockId::Number(*block_number);
filter.to_block = BlockId::Latest; filter.to_block = BlockId::Latest;
let logs = client.logs(filter.clone()) let logs = client.logs(filter)
.into_iter() .into_iter()
.map(From::from) .map(From::from)
.collect::<Vec<Log>>(); .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> { fn uninstall_filter(&self, params: Params) -> Result<Value, Error> {
from_params::<(Index,)>(params) from_params::<(Index,)>(params)
.and_then(|(index,)| { .and_then(|(index,)| {

View File

@ -98,6 +98,10 @@ impl MinerService for TestMinerService {
self.pending_transactions.lock().unwrap().get(hash).cloned() 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> { fn last_nonce(&self, address: &Address) -> Option<U256> {
self.last_nonces.read().unwrap().get(address).cloned() self.last_nonces.read().unwrap().get(address).cloned()
} }

View File

@ -16,7 +16,7 @@
//! Test implementation of SyncProvider. //! Test implementation of SyncProvider.
use util::{U256, Bytes}; use util::{U256};
use ethsync::{SyncProvider, SyncStatus, SyncState}; use ethsync::{SyncProvider, SyncStatus, SyncState};
use std::sync::{RwLock}; use std::sync::{RwLock};
@ -59,8 +59,5 @@ impl SyncProvider for TestSyncProvider {
fn status(&self) -> SyncStatus { fn status(&self) -> SyncStatus {
self.status.read().unwrap().clone() self.status.read().unwrap().clone()
} }
fn new_transaction(&self, _raw_transaction: Bytes) {
}
} }

View File

@ -190,7 +190,7 @@ pub trait EthFilter: Sized + Send + Sync + 'static {
/// Returns filter changes since last poll. /// Returns filter changes since last poll.
fn filter_changes(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() } 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!() } fn filter_logs(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
/// Uninstalls filter. /// Uninstalls filter.

View File

@ -217,10 +217,6 @@ pub struct ChainSync {
network_id: U256, network_id: U256,
/// Miner /// Miner
miner: Arc<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>; 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), max_download_ahead_blocks: max(MAX_HEADERS_TO_REQUEST, config.max_download_ahead_blocks),
network_id: config.network_id, network_id: config.network_id,
miner: miner, 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 /// Called when peer sends us new transactions
fn on_peer_transactions(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> { fn on_peer_transactions(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> {
// accepting transactions once only fully synced // accepting transactions once only fully synced
@ -1296,11 +1286,16 @@ impl ChainSync {
return 0; return 0;
} }
let mut packet = RlpStream::new_list(self.transactions_to_send.len()); let mut transactions = self.miner.pending_transactions();
for tx in &self.transactions_to_send { if transactions.is_empty() {
packet.append_raw(tx, 1); 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 rlp = packet.out();
let lucky_peers = { let lucky_peers = {
@ -1319,13 +1314,12 @@ impl ChainSync {
for peer_id in lucky_peers { for peer_id in lucky_peers {
self.send_packet(io, peer_id, TRANSACTIONS_PACKET, rlp.clone()); self.send_packet(io, peer_id, TRANSACTIONS_PACKET, rlp.clone());
} }
trace!(target: "sync", "Sent {} transactions to {} peers.", tx_count, sent);
sent sent
} }
fn propagate_latest_blocks(&mut self, io: &mut SyncIo) { 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(); 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 { 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); let blocks = self.propagate_blocks(&chain_info, io);

View File

@ -66,7 +66,7 @@ use std::ops::*;
use std::sync::*; use std::sync::*;
use util::network::{NetworkProtocolHandler, NetworkService, NetworkContext, PeerId}; use util::network::{NetworkProtocolHandler, NetworkService, NetworkContext, PeerId};
use util::TimerToken; use util::TimerToken;
use util::{U256, Bytes, ONE_U256}; use util::{U256, ONE_U256};
use ethcore::client::Client; use ethcore::client::Client;
use ethcore::service::SyncMessage; use ethcore::service::SyncMessage;
use ethminer::Miner; use ethminer::Miner;
@ -101,9 +101,6 @@ impl Default for SyncConfig {
pub trait SyncProvider: Send + Sync { pub trait SyncProvider: Send + Sync {
/// Get sync status /// Get sync status
fn status(&self) -> SyncStatus; fn status(&self) -> SyncStatus;
/// Note that a user has submitted a new transaction.
fn new_transaction(&self, raw_transaction: Bytes);
} }
/// Ethereum network protocol handler /// Ethereum network protocol handler
@ -143,11 +140,6 @@ impl SyncProvider for EthSync {
fn status(&self) -> SyncStatus { fn status(&self) -> SyncStatus {
self.sync.read().unwrap().status() 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 { impl NetworkProtocolHandler<SyncMessage> for EthSync {

View File

@ -7,6 +7,7 @@ cargo test --features ethcore/json-tests $1 \
-p ethcore \ -p ethcore \
-p ethsync \ -p ethsync \
-p ethcore-rpc \ -p ethcore-rpc \
-p ethcore-webapp \
-p parity \ -p parity \
-p ethminer \ -p ethminer \
-p bigint -p bigint

View File

@ -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 { impl<Message> Drop for IoService<Message> where Message: Send + Sync + Clone {
fn drop(&mut self) { fn drop(&mut self) {
trace!(target: "shutdown", "[IoService] Closing...");
self.host_channel.send(IoMessage::Shutdown).unwrap(); self.host_channel.send(IoMessage::Shutdown).unwrap();
self.thread.take().unwrap().join().ok(); self.thread.take().unwrap().join().ok();
trace!(target: "shutdown", "[IoService] Closed.");
} }
} }

View File

@ -120,10 +120,12 @@ impl Worker {
impl Drop for Worker { impl Drop for Worker {
fn drop(&mut self) { fn drop(&mut self) {
trace!(target: "shutdown", "[IoWorker] Closing...");
let _ = self.wait_mutex.lock(); let _ = self.wait_mutex.lock();
self.deleting.store(true, AtomicOrdering::Release); self.deleting.store(true, AtomicOrdering::Release);
self.wait.notify_all(); self.wait.notify_all();
let thread = mem::replace(&mut self.thread, None).unwrap(); let thread = mem::replace(&mut self.thread, None).unwrap();
thread.join().ok(); thread.join().ok();
trace!(target: "shutdown", "[IoWorker] Closed");
} }
} }

View File

@ -121,22 +121,7 @@ impl AccountProvider for AccountService {
} }
} }
impl Default for AccountService {
fn default() -> Self {
AccountService::new()
}
}
impl AccountService { 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 /// New account service with the keys store in specific location
pub fn new_in(path: &Path) -> Self { pub fn new_in(path: &Path) -> Self {
let secret_store = RwLock::new(SecretStore::new_in(path)); 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 { 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 /// new instance of Secret Store in specific directory
pub fn new_in(path: &Path) -> Self { pub fn new_in(path: &Path) -> Self {
::std::fs::create_dir_all(&path).expect("Cannot access requested key directory - critical");
SecretStore { SecretStore {
directory: KeyDirectory::new(path), directory: KeyDirectory::new(path),
unlocks: RwLock::new(HashMap::new()), unlocks: RwLock::new(HashMap::new()),

26
webapp/Cargo.toml Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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)
},
}
}
}