Make HashDB generic (#8739)

The `patricia_trie` crate is generic over the hasher (by way of HashDB) and node encoding scheme. Adds a new `patricia_trie_ethereum` crate with concrete impls for Keccak/RLP.
This commit is contained in:
David 2018-07-02 18:50:05 +02:00 committed by GitHub
parent 202c54d423
commit 9caa868603
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
89 changed files with 1962 additions and 1282 deletions

161
Cargo.lock generated
View File

@ -388,7 +388,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"app_dirs 1.2.1 (git+https://github.com/paritytech/app-dirs-rs)", "app_dirs 1.2.1 (git+https://github.com/paritytech/app-dirs-rs)",
"ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"journaldb 0.1.0", "journaldb 0.2.0",
] ]
[[package]] [[package]]
@ -435,6 +435,18 @@ dependencies = [
"regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "env_logger"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "error-chain" name = "error-chain"
version = "0.11.0" version = "0.11.0"
@ -548,11 +560,12 @@ dependencies = [
"fake-hardware-wallet 0.0.1", "fake-hardware-wallet 0.0.1",
"fetch 0.1.0", "fetch 0.1.0",
"hardware-wallet 1.12.0", "hardware-wallet 1.12.0",
"hashdb 0.1.1", "hashdb 0.2.0",
"heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
"journaldb 0.1.0", "journaldb 0.2.0",
"keccak-hash 0.1.2", "keccak-hash 0.1.2",
"keccak-hasher 0.1.0",
"kvdb 0.1.0", "kvdb 0.1.0",
"kvdb-memorydb 0.1.0", "kvdb-memorydb 0.1.0",
"kvdb-rocksdb 0.1.0", "kvdb-rocksdb 0.1.0",
@ -561,12 +574,13 @@ dependencies = [
"lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"macros 0.1.0", "macros 0.1.0",
"memory-cache 0.1.0", "memory-cache 0.1.0",
"memorydb 0.1.1", "memorydb 0.2.0",
"num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-machine 0.1.0", "parity-machine 0.1.0",
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
"patricia-trie 0.1.0", "patricia-trie 0.2.0",
"patricia-trie-ethereum 0.1.0",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.2.1", "rlp 0.2.1",
@ -641,18 +655,20 @@ dependencies = [
"ethcore-transaction 0.1.0", "ethcore-transaction 0.1.0",
"ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
"hashdb 0.1.1", "hashdb 0.2.0",
"heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
"keccak-hash 0.1.2", "keccak-hash 0.1.2",
"keccak-hasher 0.1.0",
"kvdb 0.1.0", "kvdb 0.1.0",
"kvdb-memorydb 0.1.0", "kvdb-memorydb 0.1.0",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"memory-cache 0.1.0", "memory-cache 0.1.0",
"memorydb 0.1.1", "memorydb 0.2.0",
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
"patricia-trie 0.1.0", "patricia-trie 0.2.0",
"plain_hasher 0.1.0", "patricia-trie-ethereum 0.1.0",
"plain_hasher 0.2.0",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.2.1", "rlp 0.2.1",
"rlp_derive 0.1.0", "rlp_derive 0.1.0",
@ -784,7 +800,8 @@ dependencies = [
"keccak-hash 0.1.2", "keccak-hash 0.1.2",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
"patricia-trie 0.1.0", "patricia-trie 0.2.0",
"patricia-trie-ethereum 0.1.0",
"rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.2.1", "rlp 0.2.1",
"rlp_derive 0.1.0", "rlp_derive 0.1.0",
@ -885,15 +902,17 @@ dependencies = [
"ethcore-transaction 0.1.0", "ethcore-transaction 0.1.0",
"ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethkey 0.3.0", "ethkey 0.3.0",
"hashdb 0.2.0",
"heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ipnetwork 0.12.7 (registry+https://github.com/rust-lang/crates.io-index)", "ipnetwork 0.12.7 (registry+https://github.com/rust-lang/crates.io-index)",
"keccak-hash 0.1.2", "keccak-hash 0.1.2",
"keccak-hasher 0.1.0",
"kvdb 0.1.0", "kvdb 0.1.0",
"kvdb-memorydb 0.1.0", "kvdb-memorydb 0.1.0",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"macros 0.1.0", "macros 0.1.0",
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
"plain_hasher 0.1.0", "plain_hasher 0.2.0",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.2.1", "rlp 0.2.1",
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1228,10 +1247,10 @@ dependencies = [
[[package]] [[package]]
name = "hashdb" name = "hashdb"
version = "0.1.1" version = "0.2.0"
dependencies = [ dependencies = [
"elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -1269,6 +1288,14 @@ name = "httparse"
version = "1.2.3" version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "humantime"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "0.11.24" version = "0.11.24"
@ -1375,20 +1402,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "journaldb" name = "journaldb"
version = "0.1.0" version = "0.2.0"
dependencies = [ dependencies = [
"ethcore-bytes 0.1.0", "ethcore-bytes 0.1.0",
"ethcore-logger 1.12.0", "ethcore-logger 1.12.0",
"ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"hashdb 0.1.1", "hashdb 0.2.0",
"heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"keccak-hash 0.1.2", "keccak-hash 0.1.2",
"keccak-hasher 0.1.0",
"kvdb 0.1.0", "kvdb 0.1.0",
"kvdb-memorydb 0.1.0", "kvdb-memorydb 0.1.0",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"memorydb 0.1.1", "memorydb 0.2.0",
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
"plain_hasher 0.1.0", "plain_hasher 0.2.0",
"rlp 0.2.1", "rlp 0.2.1",
"util-error 0.1.0", "util-error 0.1.0",
] ]
@ -1498,6 +1526,16 @@ dependencies = [
"tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "keccak-hasher"
version = "0.1.0"
dependencies = [
"ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"hashdb 0.2.0",
"plain_hasher 0.2.0",
"tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "kernel32-sys" name = "kernel32-sys"
version = "0.2.2" version = "0.2.2"
@ -1678,15 +1716,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "memorydb" name = "memorydb"
version = "0.1.1" version = "0.2.0"
dependencies = [ dependencies = [
"elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"hashdb 0.1.1", "hashdb 0.2.0",
"heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"keccak-hash 0.1.2", "keccak-hash 0.1.2",
"plain_hasher 0.1.0", "keccak-hasher 0.1.0",
"plain_hasher 0.2.0",
"rlp 0.2.1", "rlp 0.2.1",
"tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -2035,7 +2075,7 @@ dependencies = [
"futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"ipnetwork 0.12.7 (registry+https://github.com/rust-lang/crates.io-index)", "ipnetwork 0.12.7 (registry+https://github.com/rust-lang/crates.io-index)",
"journaldb 0.1.0", "journaldb 0.2.0",
"jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)",
"keccak-hash 0.1.2", "keccak-hash 0.1.2",
"kvdb 0.1.0", "kvdb 0.1.0",
@ -2254,7 +2294,7 @@ dependencies = [
"parity-updater 1.12.0", "parity-updater 1.12.0",
"parity-version 1.12.0", "parity-version 1.12.0",
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
"patricia-trie 0.1.0", "patricia-trie 0.2.0",
"pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.2.1", "rlp 0.2.1",
@ -2421,22 +2461,37 @@ version = "0.1.0"
[[package]] [[package]]
name = "patricia-trie" name = "patricia-trie"
version = "0.1.0" version = "0.2.0"
dependencies = [ dependencies = [
"elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-bytes 0.1.0", "ethcore-bytes 0.1.0",
"ethcore-logger 1.12.0",
"ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"hashdb 0.1.1", "hashdb 0.2.0",
"keccak-hash 0.1.2", "keccak-hash 0.1.2",
"keccak-hasher 0.1.0",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"memorydb 0.1.1", "memorydb 0.2.0",
"patricia-trie-ethereum 0.1.0",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.2.1", "rlp 0.2.1",
"trie-standardmap 0.1.0", "trie-standardmap 0.1.0",
"triehash 0.1.0", "triehash 0.1.0",
] ]
[[package]]
name = "patricia-trie-ethereum"
version = "0.1.0"
dependencies = [
"elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-bytes 0.1.0",
"ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"hashdb 0.2.0",
"keccak-hasher 0.1.0",
"patricia-trie 0.2.0",
"rlp 0.2.1",
]
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "1.0.0" version = "1.0.0"
@ -2488,10 +2543,11 @@ dependencies = [
[[package]] [[package]]
name = "plain_hasher" name = "plain_hasher"
version = "0.1.0" version = "0.2.0"
dependencies = [ dependencies = [
"crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"hashdb 0.2.0",
] ]
[[package]] [[package]]
@ -2720,11 +2776,31 @@ dependencies = [
"utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "regex"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.4.1" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "regex-syntax"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "registrar" name = "registrar"
version = "0.0.1" version = "0.0.1"
@ -3173,6 +3249,14 @@ dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "termcolor"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "termion" name = "termion"
version = "1.5.1" version = "1.5.1"
@ -3505,6 +3589,11 @@ dependencies = [
"trie-standardmap 0.1.0", "trie-standardmap 0.1.0",
] ]
[[package]]
name = "ucd-util"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "uint" name = "uint"
version = "0.2.1" version = "0.2.1"
@ -3649,7 +3738,8 @@ dependencies = [
"ethjson 0.1.0", "ethjson 0.1.0",
"keccak-hash 0.1.2", "keccak-hash 0.1.2",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"patricia-trie 0.1.0", "patricia-trie 0.2.0",
"patricia-trie-ethereum 0.1.0",
"rlp 0.2.1", "rlp 0.2.1",
] ]
@ -3749,6 +3839,14 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "wincolor"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "ws" name = "ws"
version = "0.7.5" version = "0.7.5"
@ -3858,6 +3956,7 @@ dependencies = [
"checksum either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "740178ddf48b1a9e878e6d6509a1442a2d42fd2928aae8e7a6f8a36fb01981b3" "checksum either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "740178ddf48b1a9e878e6d6509a1442a2d42fd2928aae8e7a6f8a36fb01981b3"
"checksum elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "88d4851b005ef16de812ea9acdb7bece2f0a40dd86c07b85631d7dafa54537bb" "checksum elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "88d4851b005ef16de812ea9acdb7bece2f0a40dd86c07b85631d7dafa54537bb"
"checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b" "checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b"
"checksum env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0e6e40ebb0e66918a37b38c7acab4e10d299e0463fe2af5d29b9cc86710cfd2a"
"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" "checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
"checksum error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02" "checksum error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02"
"checksum eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)" = "<none>" "checksum eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)" = "<none>"
@ -3888,6 +3987,7 @@ dependencies = [
"checksum hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa" "checksum hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa"
"checksum hidapi 0.3.1 (git+https://github.com/paritytech/hidapi-rs)" = "<none>" "checksum hidapi 0.3.1 (git+https://github.com/paritytech/hidapi-rs)" = "<none>"
"checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07" "checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07"
"checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e"
"checksum hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)" = "df4dd5dae401458087396b6db7fabc4d6760aa456a5fa8e92bda549f39cae661" "checksum hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)" = "df4dd5dae401458087396b6db7fabc4d6760aa456a5fa8e92bda549f39cae661"
"checksum hyper-rustls 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d6cdc1751771a14b8175764394f025e309a28c825ed9eaf97fa62bb831dc8c5" "checksum hyper-rustls 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d6cdc1751771a14b8175764394f025e309a28c825ed9eaf97fa62bb831dc8c5"
"checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d"
@ -3988,7 +4088,9 @@ dependencies = [
"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" "checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1"
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "744554e01ccbd98fff8c457c3b092cd67af62a555a43bfe97ae8a0451f7799fa" "checksum regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "744554e01ccbd98fff8c457c3b092cd67af62a555a43bfe97ae8a0451f7799fa"
"checksum regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13c93d55961981ba9226a213b385216f83ab43bd6ac53ab16b2eeb47e337cf4e"
"checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db" "checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db"
"checksum regex-syntax 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05b06a75f5217880fc5e905952a42750bf44787e56a6c6d6852ed0992f5e1d54"
"checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" "checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a"
"checksum ring 0.12.1 (git+https://github.com/paritytech/ring)" = "<none>" "checksum ring 0.12.1 (git+https://github.com/paritytech/ring)" = "<none>"
"checksum rocksdb 0.4.5 (git+https://github.com/paritytech/rust-rocksdb)" = "<none>" "checksum rocksdb 0.4.5 (git+https://github.com/paritytech/rust-rocksdb)" = "<none>"
@ -4038,6 +4140,7 @@ dependencies = [
"checksum tempfile 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "11ce2fe9db64b842314052e2421ac61a73ce41b898dc8e3750398b219c5fc1e0" "checksum tempfile 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "11ce2fe9db64b842314052e2421ac61a73ce41b898dc8e3750398b219c5fc1e0"
"checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" "checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1"
"checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327" "checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327"
"checksum termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "adc4587ead41bf016f11af03e55a624c06568b5a19db4e90fde573d805074f83"
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693"
"checksum thread-id 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" "checksum thread-id 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1"
@ -4065,6 +4168,7 @@ dependencies = [
"checksum toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7540f4ffc193e0d3c94121edb19b055670d369f77d5804db11ae053a45b6e7e" "checksum toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7540f4ffc193e0d3c94121edb19b055670d369f77d5804db11ae053a45b6e7e"
"checksum transient-hashmap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "715254c8f0811be1a79ad3ea5e6fa3c8eddec2b03d7f5ba78cf093e56d79c24f" "checksum transient-hashmap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "715254c8f0811be1a79ad3ea5e6fa3c8eddec2b03d7f5ba78cf093e56d79c24f"
"checksum trezor-sys 1.0.0 (git+https://github.com/paritytech/trezor-sys)" = "<none>" "checksum trezor-sys 1.0.0 (git+https://github.com/paritytech/trezor-sys)" = "<none>"
"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d"
"checksum uint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "38051a96565903d81c9a9210ce11076b2218f3b352926baa1f5f6abbdfce8273" "checksum uint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "38051a96565903d81c9a9210ce11076b2218f3b352926baa1f5f6abbdfce8273"
"checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33"
"checksum unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284b6d3db520d67fbe88fd778c21510d1b0ba4a551e5d0fbb023d33405f6de8a" "checksum unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284b6d3db520d67fbe88fd778c21510d1b0ba4a551e5d0fbb023d33405f6de8a"
@ -4091,6 +4195,7 @@ dependencies = [
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767"
"checksum ws 0.7.5 (git+https://github.com/tomusdrw/ws-rs)" = "<none>" "checksum ws 0.7.5 (git+https://github.com/tomusdrw/ws-rs)" = "<none>"
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
"checksum xdg 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a66b7c2281ebde13cf4391d70d4c7e5946c3c25e72a7b859ca8f677dcd0b0c61" "checksum xdg 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a66b7c2281ebde13cf4391d70d4c7e5946c3c25e72a7b859ca8f677dcd0b0c61"

View File

@ -133,6 +133,8 @@ members = [
"transaction-pool", "transaction-pool",
"whisper", "whisper",
"whisper/cli", "whisper/cli",
"util/keccak-hasher",
"util/patricia-trie-ethereum",
] ]
[patch.crates-io] [patch.crates-io]

View File

@ -20,6 +20,7 @@ fetch = { path = "../util/fetch" }
hashdb = { path = "../util/hashdb" } hashdb = { path = "../util/hashdb" }
memorydb = { path = "../util/memorydb" } memorydb = { path = "../util/memorydb" }
patricia-trie = { path = "../util/patricia_trie" } patricia-trie = { path = "../util/patricia_trie" }
patricia-trie-ethereum = { path = "../util/patricia-trie-ethereum" }
ethcore-crypto = { path = "crypto" } ethcore-crypto = { path = "crypto" }
error-chain = { version = "0.12", default-features = false } error-chain = { version = "0.12", default-features = false }
ethcore-io = { path = "../util/io" } ethcore-io = { path = "../util/io" }
@ -66,8 +67,9 @@ keccak-hash = { path = "../util/hash" }
triehash = { path = "../util/triehash" } triehash = { path = "../util/triehash" }
unexpected = { path = "../util/unexpected" } unexpected = { path = "../util/unexpected" }
journaldb = { path = "../util/journaldb" } journaldb = { path = "../util/journaldb" }
tempdir = { version = "0.3", optional = true } keccak-hasher = { path = "../util/keccak-hasher" }
kvdb-rocksdb = { path = "../util/kvdb-rocksdb" } kvdb-rocksdb = { path = "../util/kvdb-rocksdb" }
tempdir = {version="0.3", optional = true}
[target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))'.dependencies] [target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))'.dependencies]
hardware-wallet = { path = "../hw" } hardware-wallet = { path = "../hw" }
@ -76,8 +78,8 @@ hardware-wallet = { path = "../hw" }
fake-hardware-wallet = { path = "../util/fake-hardware-wallet" } fake-hardware-wallet = { path = "../util/fake-hardware-wallet" }
[dev-dependencies] [dev-dependencies]
trie-standardmap = { path = "../util/trie-standardmap" }
tempdir = "0.3" tempdir = "0.3"
trie-standardmap = { path = "../util/trie-standardmap" }
[features] [features]
# Display EVM debug traces. # Display EVM debug traces.

View File

@ -14,6 +14,7 @@ ethcore-transaction = { path = "../transaction" }
ethereum-types = "0.3" ethereum-types = "0.3"
memorydb = { path = "../../util/memorydb" } memorydb = { path = "../../util/memorydb" }
patricia-trie = { path = "../../util/patricia_trie" } patricia-trie = { path = "../../util/patricia_trie" }
patricia-trie-ethereum = { path = "../../util/patricia-trie-ethereum" }
ethcore-network = { path = "../../util/network" } ethcore-network = { path = "../../util/network" }
ethcore-io = { path = "../../util/io" } ethcore-io = { path = "../../util/io" }
hashdb = { path = "../../util/hashdb" } hashdb = { path = "../../util/hashdb" }
@ -32,6 +33,7 @@ serde_derive = "1.0"
parking_lot = "0.5" parking_lot = "0.5"
stats = { path = "../../util/stats" } stats = { path = "../../util/stats" }
keccak-hash = { path = "../../util/hash" } keccak-hash = { path = "../../util/hash" }
keccak-hasher = { path = "../../util/keccak-hasher" }
triehash = { path = "../../util/triehash" } triehash = { path = "../../util/triehash" }
kvdb = { path = "../../util/kvdb" } kvdb = { path = "../../util/kvdb" }
memory-cache = { path = "../../util/memory_cache" } memory-cache = { path = "../../util/memory_cache" }

View File

@ -26,9 +26,11 @@
use ethcore::ids::BlockId; use ethcore::ids::BlockId;
use ethereum_types::{H256, U256}; use ethereum_types::{H256, U256};
use hashdb::HashDB; use hashdb::HashDB;
use keccak_hasher::KeccakHasher;
use memorydb::MemoryDB; use memorydb::MemoryDB;
use bytes::Bytes; use bytes::Bytes;
use trie::{self, TrieMut, TrieDBMut, Trie, TrieDB, Recorder}; use trie::{TrieMut, Trie, Recorder};
use ethtrie::{self, TrieDB, TrieDBMut};
use rlp::{RlpStream, Rlp}; use rlp::{RlpStream, Rlp};
// encode a key. // encode a key.
@ -50,13 +52,13 @@ pub const SIZE: u64 = 2048;
/// A canonical hash trie. This is generic over any database it can query. /// A canonical hash trie. This is generic over any database it can query.
/// See module docs for more details. /// See module docs for more details.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct CHT<DB: HashDB> { pub struct CHT<DB: HashDB<KeccakHasher>> {
db: DB, db: DB,
root: H256, // the root of this CHT. root: H256, // the root of this CHT.
number: u64, number: u64,
} }
impl<DB: HashDB> CHT<DB> { impl<DB: HashDB<KeccakHasher>> CHT<DB> {
/// Query the root of the CHT. /// Query the root of the CHT.
pub fn root(&self) -> H256 { self.root } pub fn root(&self) -> H256 { self.root }
@ -66,7 +68,7 @@ impl<DB: HashDB> CHT<DB> {
/// Generate an inclusion proof for the entry at a specific block. /// Generate an inclusion proof for the entry at a specific block.
/// Nodes before level `from_level` will be omitted. /// Nodes before level `from_level` will be omitted.
/// Returns an error on an incomplete trie, and `Ok(None)` on an unprovable request. /// Returns an error on an incomplete trie, and `Ok(None)` on an unprovable request.
pub fn prove(&self, num: u64, from_level: u32) -> trie::Result<Option<Vec<Bytes>>> { pub fn prove(&self, num: u64, from_level: u32) -> ethtrie::Result<Option<Vec<Bytes>>> {
if block_to_cht_number(num) != Some(self.number) { return Ok(None) } if block_to_cht_number(num) != Some(self.number) { return Ok(None) }
let mut recorder = Recorder::with_depth(from_level); let mut recorder = Recorder::with_depth(from_level);
@ -90,10 +92,10 @@ pub struct BlockInfo {
/// Build an in-memory CHT from a closure which provides necessary information /// Build an in-memory CHT from a closure which provides necessary information
/// about blocks. If the fetcher ever fails to provide the info, the CHT /// about blocks. If the fetcher ever fails to provide the info, the CHT
/// will not be generated. /// will not be generated.
pub fn build<F>(cht_num: u64, mut fetcher: F) -> Option<CHT<MemoryDB>> pub fn build<F>(cht_num: u64, mut fetcher: F) -> Option<CHT<MemoryDB<KeccakHasher>>>
where F: FnMut(BlockId) -> Option<BlockInfo> where F: FnMut(BlockId) -> Option<BlockInfo>
{ {
let mut db = MemoryDB::new(); let mut db = MemoryDB::<KeccakHasher>::new();
// start from the last block by number and work backwards. // start from the last block by number and work backwards.
let last_num = start_number(cht_num + 1) - 1; let last_num = start_number(cht_num + 1) - 1;
@ -147,7 +149,7 @@ pub fn compute_root<I>(cht_num: u64, iterable: I) -> Option<H256>
/// verify the given trie branch and extract the canonical hash and total difficulty. /// verify the given trie branch and extract the canonical hash and total difficulty.
// TODO: better support for partially-checked queries. // TODO: better support for partially-checked queries.
pub fn check_proof(proof: &[Bytes], num: u64, root: H256) -> Option<(H256, U256)> { pub fn check_proof(proof: &[Bytes], num: u64, root: H256) -> Option<(H256, U256)> {
let mut db = MemoryDB::new(); let mut db = MemoryDB::<KeccakHasher>::new();
for node in proof { db.insert(&node[..]); } for node in proof { db.insert(&node[..]); }
let res = match TrieDB::new(&db, &root) { let res = match TrieDB::new(&db, &root) {

View File

@ -28,28 +28,21 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::sync::Arc; use std::sync::Arc;
use cache::Cache;
use cht; use cht;
use ethcore::block_status::BlockStatus; use ethcore::block_status::BlockStatus;
use ethcore::error::{Error, BlockImportError, BlockImportErrorKind, BlockError};
use ethcore::encoded; use ethcore::encoded;
use ethcore::engines::epoch::{Transition as EpochTransition, PendingTransition as PendingEpochTransition};
use ethcore::error::{Error, BlockImportError, BlockImportErrorKind, BlockError};
use ethcore::header::Header; use ethcore::header::Header;
use ethcore::ids::BlockId; use ethcore::ids::BlockId;
use ethcore::spec::{Spec, SpecHardcodedSync}; use ethcore::spec::{Spec, SpecHardcodedSync};
use ethcore::engines::epoch::{
Transition as EpochTransition,
PendingTransition as PendingEpochTransition
};
use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp};
use heapsize::HeapSizeOf;
use ethereum_types::{H256, H264, U256}; use ethereum_types::{H256, H264, U256};
use plain_hasher::H256FastMap; use heapsize::HeapSizeOf;
use kvdb::{DBTransaction, KeyValueDB}; use kvdb::{DBTransaction, KeyValueDB};
use cache::Cache;
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use plain_hasher::H256FastMap;
use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp};
use smallvec::SmallVec; use smallvec::SmallVec;
/// Store at least this many candidate headers at all times. /// Store at least this many candidate headers at all times.

View File

@ -64,8 +64,10 @@ extern crate hashdb;
extern crate heapsize; extern crate heapsize;
extern crate futures; extern crate futures;
extern crate itertools; extern crate itertools;
extern crate keccak_hasher;
extern crate memorydb; extern crate memorydb;
extern crate patricia_trie as trie; extern crate patricia_trie as trie;
extern crate patricia_trie_ethereum as ethtrie;
extern crate plain_hasher; extern crate plain_hasher;
extern crate rand; extern crate rand;
extern crate rlp; extern crate rlp;

View File

@ -20,22 +20,20 @@
use transaction::UnverifiedTransaction; use transaction::UnverifiedTransaction;
use io::TimerToken;
use network::{NetworkProtocolHandler, NetworkContext, PeerId};
use rlp::{RlpStream, Rlp};
use ethereum_types::{H256, U256}; use ethereum_types::{H256, U256};
use io::TimerToken;
use kvdb::DBValue; use kvdb::DBValue;
use network::{NetworkProtocolHandler, NetworkContext, PeerId};
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use std::time::{Duration, Instant};
use std::collections::{HashMap, HashSet};
use std::fmt;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::ops::{BitOr, BitAnd, Not};
use provider::Provider; use provider::Provider;
use request::{Request, NetworkRequests as Requests, Response}; use request::{Request, NetworkRequests as Requests, Response};
use rlp::{RlpStream, Rlp};
use std::collections::{HashMap, HashSet};
use std::fmt;
use std::ops::{BitOr, BitAnd, Not};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::{Duration, Instant};
use self::request_credits::{Credits, FlowParams}; use self::request_credits::{Credits, FlowParams};
use self::context::{Ctx, TickCtx}; use self::context::{Ctx, TickCtx};

View File

@ -16,8 +16,8 @@
//! Peer status and capabilities. //! Peer status and capabilities.
use rlp::{DecoderError, Encodable, Decodable, RlpStream, Rlp};
use ethereum_types::{H256, U256}; use ethereum_types::{H256, U256};
use rlp::{DecoderError, Encodable, Decodable, RlpStream, Rlp};
use super::request_credits::FlowParams; use super::request_credits::FlowParams;

View File

@ -19,20 +19,18 @@
use ethcore::blockchain_info::BlockChainInfo; use ethcore::blockchain_info::BlockChainInfo;
use ethcore::client::{EachBlockWith, TestBlockChainClient}; use ethcore::client::{EachBlockWith, TestBlockChainClient};
use ethcore::ids::BlockId;
use ethcore::encoded; use ethcore::encoded;
use network::{PeerId, NodeId}; use ethcore::ids::BlockId;
use transaction::{Action, PendingTransaction}; use ethereum_types::{H256, U256, Address};
use net::{LightProtocol, Params, packet, Peer};
use net::context::IoContext; use net::context::IoContext;
use net::status::{Capabilities, Status}; use net::status::{Capabilities, Status};
use net::{LightProtocol, Params, packet, Peer}; use network::{PeerId, NodeId};
use provider::Provider; use provider::Provider;
use request; use request;
use request::*; use request::*;
use rlp::{Rlp, RlpStream}; use rlp::{Rlp, RlpStream};
use ethereum_types::{H256, U256, Address}; use transaction::{Action, PendingTransaction};
use std::sync::Arc; use std::sync::Arc;
use std::time::Instant; use std::time::Instant;

View File

@ -18,26 +18,25 @@
use std::sync::Arc; use std::sync::Arc;
use bytes::Bytes;
use ethcore::basic_account::BasicAccount; use ethcore::basic_account::BasicAccount;
use ethcore::encoded; use ethcore::encoded;
use ethcore::engines::{EthEngine, StateDependentProof}; use ethcore::engines::{EthEngine, StateDependentProof};
use ethcore::machine::EthereumMachine; use ethcore::machine::EthereumMachine;
use ethcore::receipt::Receipt; use ethcore::receipt::Receipt;
use ethcore::state::{self, ProvedExecution}; use ethcore::state::{self, ProvedExecution};
use transaction::SignedTransaction;
use vm::EnvInfo;
use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY, KECCAK_EMPTY_LIST_RLP, keccak};
use request::{self as net_request, IncompleteRequest, CompleteRequest, Output, OutputKind, Field};
use rlp::{RlpStream, Rlp};
use ethereum_types::{H256, U256, Address}; use ethereum_types::{H256, U256, Address};
use parking_lot::Mutex; use ethtrie::{TrieError, TrieDB};
use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY, KECCAK_EMPTY_LIST_RLP, keccak};
use hashdb::HashDB; use hashdb::HashDB;
use kvdb::DBValue; use kvdb::DBValue;
use bytes::Bytes;
use memorydb::MemoryDB; use memorydb::MemoryDB;
use trie::{Trie, TrieDB, TrieError}; use parking_lot::Mutex;
use request::{self as net_request, IncompleteRequest, CompleteRequest, Output, OutputKind, Field};
use rlp::{RlpStream, Rlp};
use transaction::SignedTransaction;
use trie::Trie;
use vm::EnvInfo;
const SUPPLIED_MATCHES: &'static str = "supplied responses always match produced requests; enforced by `check_response`; qed"; const SUPPLIED_MATCHES: &'static str = "supplied responses always match produced requests; enforced by `check_response`; qed";
@ -935,11 +934,12 @@ mod tests {
use ethereum_types::{H256, Address}; use ethereum_types::{H256, Address};
use memorydb::MemoryDB; use memorydb::MemoryDB;
use parking_lot::Mutex; use parking_lot::Mutex;
use trie::{Trie, TrieMut, SecTrieDB, SecTrieDBMut}; use trie::{Trie, TrieMut};
use trie::recorder::Recorder; use ethtrie::{SecTrieDB, SecTrieDBMut};
use trie::Recorder;
use hash::keccak; use hash::keccak;
use ethcore::client::{BlockChainClient, BlockInfo, TestBlockChainClient, EachBlockWith}; use ::ethcore::client::{BlockChainClient, BlockInfo, TestBlockChainClient, EachBlockWith};
use ethcore::header::Header; use ethcore::header::Header;
use ethcore::encoded; use ethcore::encoded;
use ethcore::receipt::{Receipt, TransactionOutcome}; use ethcore::receipt::{Receipt, TransactionOutcome};

View File

@ -26,6 +26,7 @@ keccak-hash = { path = "../../util/hash" }
log = "0.3" log = "0.3"
parking_lot = "0.5" parking_lot = "0.5"
patricia-trie = { path = "../../util/patricia_trie" } patricia-trie = { path = "../../util/patricia_trie" }
patricia-trie-ethereum = { path = "../../util/patricia-trie-ethereum" }
rand = "0.3" rand = "0.3"
rlp = { path = "../../util/rlp" } rlp = { path = "../../util/rlp" }
rlp_derive = { path = "../../util/rlp_derive" } rlp_derive = { path = "../../util/rlp_derive" }

View File

@ -16,7 +16,7 @@
use ethereum_types::Address; use ethereum_types::Address;
use rlp::DecoderError; use rlp::DecoderError;
use trie::TrieError; use ethtrie::TrieError;
use ethcore::account_provider::SignError; use ethcore::account_provider::SignError;
use ethcore::error::{Error as EthcoreError, ExecutionError}; use ethcore::error::{Error as EthcoreError, ExecutionError};
use transaction::Error as TransactionError; use transaction::Error as TransactionError;

View File

@ -40,6 +40,7 @@ extern crate futures;
extern crate keccak_hash as hash; extern crate keccak_hash as hash;
extern crate parking_lot; extern crate parking_lot;
extern crate patricia_trie as trie; extern crate patricia_trie as trie;
extern crate patricia_trie_ethereum as ethtrie;
extern crate rlp; extern crate rlp;
extern crate url; extern crate url;
extern crate rustc_hex; extern crate rustc_hex;

View File

@ -15,12 +15,13 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! DB backend wrapper for Account trie //! DB backend wrapper for Account trie
use std::collections::HashMap;
use hash::{KECCAK_NULL_RLP, keccak};
use ethereum_types::H256; use ethereum_types::H256;
use hash::{KECCAK_NULL_RLP, keccak};
use hashdb::{HashDB, AsHashDB};
use keccak_hasher::KeccakHasher;
use kvdb::DBValue; use kvdb::DBValue;
use hashdb::HashDB;
use rlp::NULL_RLP; use rlp::NULL_RLP;
use std::collections::HashMap;
#[cfg(test)] #[cfg(test)]
use ethereum_types::Address; use ethereum_types::Address;
@ -44,7 +45,7 @@ fn combine_key<'a>(address_hash: &'a H256, key: &'a H256) -> H256 {
/// A factory for different kinds of account dbs. /// A factory for different kinds of account dbs.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Factory { pub enum Factory {
/// Mangle hashes based on address. /// Mangle hashes based on address. This is the default.
Mangled, Mangled,
/// Don't mangle hashes. /// Don't mangle hashes.
Plain, Plain,
@ -57,7 +58,7 @@ impl Default for Factory {
impl Factory { impl Factory {
/// Create a read-only accountdb. /// Create a read-only accountdb.
/// This will panic when write operations are called. /// This will panic when write operations are called.
pub fn readonly<'db>(&self, db: &'db HashDB, address_hash: H256) -> Box<HashDB + 'db> { pub fn readonly<'db>(&self, db: &'db HashDB<KeccakHasher>, address_hash: H256) -> Box<HashDB<KeccakHasher> + 'db> {
match *self { match *self {
Factory::Mangled => Box::new(AccountDB::from_hash(db, address_hash)), Factory::Mangled => Box::new(AccountDB::from_hash(db, address_hash)),
Factory::Plain => Box::new(Wrapping(db)), Factory::Plain => Box::new(Wrapping(db)),
@ -65,7 +66,7 @@ impl Factory {
} }
/// Create a new mutable hashdb. /// Create a new mutable hashdb.
pub fn create<'db>(&self, db: &'db mut HashDB, address_hash: H256) -> Box<HashDB + 'db> { pub fn create<'db>(&self, db: &'db mut HashDB<KeccakHasher>, address_hash: H256) -> Box<HashDB<KeccakHasher> + 'db> {
match *self { match *self {
Factory::Mangled => Box::new(AccountDBMut::from_hash(db, address_hash)), Factory::Mangled => Box::new(AccountDBMut::from_hash(db, address_hash)),
Factory::Plain => Box::new(WrappingMut(db)), Factory::Plain => Box::new(WrappingMut(db)),
@ -77,19 +78,19 @@ impl Factory {
/// DB backend wrapper for Account trie /// DB backend wrapper for Account trie
/// Transforms trie node keys for the database /// Transforms trie node keys for the database
pub struct AccountDB<'db> { pub struct AccountDB<'db> {
db: &'db HashDB, db: &'db HashDB<KeccakHasher>,
address_hash: H256, address_hash: H256,
} }
impl<'db> AccountDB<'db> { impl<'db> AccountDB<'db> {
/// Create a new AccountDB from an address. /// Create a new AccountDB from an address.
#[cfg(test)] #[cfg(test)]
pub fn new(db: &'db HashDB, address: &Address) -> Self { pub fn new(db: &'db HashDB<KeccakHasher>, address: &Address) -> Self {
Self::from_hash(db, keccak(address)) Self::from_hash(db, keccak(address))
} }
/// Create a new AcountDB from an address' hash. /// Create a new AcountDB from an address' hash.
pub fn from_hash(db: &'db HashDB, address_hash: H256) -> Self { pub fn from_hash(db: &'db HashDB<KeccakHasher>, address_hash: H256) -> Self {
AccountDB { AccountDB {
db: db, db: db,
address_hash: address_hash, address_hash: address_hash,
@ -97,7 +98,12 @@ impl<'db> AccountDB<'db> {
} }
} }
impl<'db> HashDB for AccountDB<'db>{ impl<'db> AsHashDB<KeccakHasher> for AccountDB<'db> {
fn as_hashdb(&self) -> &HashDB<KeccakHasher> { self }
fn as_hashdb_mut(&mut self) -> &mut HashDB<KeccakHasher> { self }
}
impl<'db> HashDB<KeccakHasher> for AccountDB<'db> {
fn keys(&self) -> HashMap<H256, i32> { fn keys(&self) -> HashMap<H256, i32> {
unimplemented!() unimplemented!()
} }
@ -131,19 +137,19 @@ impl<'db> HashDB for AccountDB<'db>{
/// DB backend wrapper for Account trie /// DB backend wrapper for Account trie
pub struct AccountDBMut<'db> { pub struct AccountDBMut<'db> {
db: &'db mut HashDB, db: &'db mut HashDB<KeccakHasher>,
address_hash: H256, address_hash: H256,
} }
impl<'db> AccountDBMut<'db> { impl<'db> AccountDBMut<'db> {
/// Create a new AccountDB from an address. /// Create a new AccountDB from an address.
#[cfg(test)] #[cfg(test)]
pub fn new(db: &'db mut HashDB, address: &Address) -> Self { pub fn new(db: &'db mut HashDB<KeccakHasher>, address: &Address) -> Self {
Self::from_hash(db, keccak(address)) Self::from_hash(db, keccak(address))
} }
/// Create a new AcountDB from an address' hash. /// Create a new AcountDB from an address' hash.
pub fn from_hash(db: &'db mut HashDB, address_hash: H256) -> Self { pub fn from_hash(db: &'db mut HashDB<KeccakHasher>, address_hash: H256) -> Self {
AccountDBMut { AccountDBMut {
db: db, db: db,
address_hash: address_hash, address_hash: address_hash,
@ -156,7 +162,7 @@ impl<'db> AccountDBMut<'db> {
} }
} }
impl<'db> HashDB for AccountDBMut<'db>{ impl<'db> HashDB<KeccakHasher> for AccountDBMut<'db>{
fn keys(&self) -> HashMap<H256, i32> { fn keys(&self) -> HashMap<H256, i32> {
unimplemented!() unimplemented!()
} }
@ -202,9 +208,19 @@ impl<'db> HashDB for AccountDBMut<'db>{
} }
} }
struct Wrapping<'db>(&'db HashDB); impl<'db> AsHashDB<KeccakHasher> for AccountDBMut<'db> {
fn as_hashdb(&self) -> &HashDB<KeccakHasher> { self }
fn as_hashdb_mut(&mut self) -> &mut HashDB<KeccakHasher> { self }
}
impl<'db> HashDB for Wrapping<'db> { struct Wrapping<'db>(&'db HashDB<KeccakHasher>);
impl<'db> AsHashDB<KeccakHasher> for Wrapping<'db> {
fn as_hashdb(&self) -> &HashDB<KeccakHasher> { self }
fn as_hashdb_mut(&mut self) -> &mut HashDB<KeccakHasher> { self }
}
impl<'db> HashDB<KeccakHasher> for Wrapping<'db> {
fn keys(&self) -> HashMap<H256, i32> { fn keys(&self) -> HashMap<H256, i32> {
unimplemented!() unimplemented!()
} }
@ -236,9 +252,13 @@ impl<'db> HashDB for Wrapping<'db> {
} }
} }
struct WrappingMut<'db>(&'db mut HashDB); struct WrappingMut<'db>(&'db mut HashDB<KeccakHasher>);
impl<'db> AsHashDB<KeccakHasher> for WrappingMut<'db> {
fn as_hashdb(&self) -> &HashDB<KeccakHasher> { self }
fn as_hashdb_mut(&mut self) -> &mut HashDB<KeccakHasher> { self }
}
impl<'db> HashDB for WrappingMut<'db>{ impl<'db> HashDB<KeccakHasher> for WrappingMut<'db>{
fn keys(&self) -> HashMap<H256, i32> { fn keys(&self) -> HashMap<H256, i32> {
unimplemented!() unimplemented!()
} }

View File

@ -17,28 +17,27 @@
//! Blockchain block. //! Blockchain block.
use std::cmp; use std::cmp;
use std::sync::Arc;
use std::collections::HashSet; use std::collections::HashSet;
use hash::{keccak, KECCAK_NULL_RLP, KECCAK_EMPTY_LIST_RLP}; use std::sync::Arc;
use triehash::ordered_trie_root;
use rlp::{Rlp, RlpStream, Encodable, Decodable, DecoderError, encode_list};
use ethereum_types::{H256, U256, Address, Bloom};
use bytes::Bytes; use bytes::Bytes;
use unexpected::{Mismatch, OutOfBounds};
use vm::{EnvInfo, LastHashes};
use engines::EthEngine; use engines::EthEngine;
use error::{Error, BlockError}; use error::{Error, BlockError};
use ethereum_types::{H256, U256, Address, Bloom};
use factory::Factories; use factory::Factories;
use hash::{keccak, KECCAK_NULL_RLP, KECCAK_EMPTY_LIST_RLP};
use header::{Header, ExtendedHeader}; use header::{Header, ExtendedHeader};
use receipt::{Receipt, TransactionOutcome}; use receipt::{Receipt, TransactionOutcome};
use state::State; use rlp::{Rlp, RlpStream, Encodable, Decodable, DecoderError, encode_list};
use state_db::StateDB; use state_db::StateDB;
use state::State;
use trace::Tracing; use trace::Tracing;
use transaction::{UnverifiedTransaction, SignedTransaction, Error as TransactionError}; use transaction::{UnverifiedTransaction, SignedTransaction, Error as TransactionError};
use triehash::ordered_trie_root;
use unexpected::{Mismatch, OutOfBounds};
use verification::PreverifiedBlock; use verification::PreverifiedBlock;
use views::BlockView; use views::BlockView;
use vm::{EnvInfo, LastHashes};
/// A block, encoded as it is on the block chain. /// A block, encoded as it is on the block chain.
#[derive(Default, Debug, Clone, PartialEq)] #[derive(Default, Debug, Clone, PartialEq)]

View File

@ -17,37 +17,38 @@
//! Blockchain database. //! Blockchain database.
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::{mem, io};
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use std::{mem, io};
use itertools::Itertools; use ansi_term::Colour;
use blooms_db; use blockchain::{CacheSize, ImportRoute, Config};
use heapsize::HeapSizeOf;
use ethereum_types::{H256, Bloom, BloomRef, U256};
use parking_lot::{Mutex, RwLock};
use bytes::Bytes;
use rlp::RlpStream;
use rlp_compress::{compress, decompress, blocks_swapper};
use header::*;
use transaction::*;
use views::{BlockView, HeaderView};
use log_entry::{LogEntry, LocalizedLogEntry};
use receipt::Receipt;
use blockchain::best_block::{BestBlock, BestAncientBlock}; use blockchain::best_block::{BestBlock, BestAncientBlock};
use blockchain::block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainData}; use blockchain::block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainData};
use blockchain::extras::{BlockReceipts, BlockDetails, TransactionAddress, EPOCH_KEY_PREFIX, EpochTransitions}; use blockchain::extras::{BlockReceipts, BlockDetails, TransactionAddress, EPOCH_KEY_PREFIX, EpochTransitions};
use blockchain::update::{ExtrasUpdate, ExtrasInsert};
use blooms_db;
use bytes::Bytes;
use cache_manager::CacheManager;
use db::{self, Writable, Readable, CacheUpdatePolicy};
use encoded;
use engines::epoch::{Transition as EpochTransition, PendingTransition as PendingEpochTransition};
use engines::ForkChoice;
use ethereum_types::{H256, Bloom, BloomRef, U256};
use header::*;
use heapsize::HeapSizeOf;
use itertools::Itertools;
use kvdb::{DBTransaction, KeyValueDB};
use log_entry::{LogEntry, LocalizedLogEntry};
use parking_lot::{Mutex, RwLock};
use rayon::prelude::*;
use receipt::Receipt;
use rlp_compress::{compress, decompress, blocks_swapper};
use rlp::RlpStream;
use transaction::*;
use types::blockchain_info::BlockChainInfo; use types::blockchain_info::BlockChainInfo;
use types::tree_route::TreeRoute; use types::tree_route::TreeRoute;
use blockchain::update::{ExtrasUpdate, ExtrasInsert}; use views::{BlockView, HeaderView};
use blockchain::{CacheSize, ImportRoute, Config};
use db::{self, Writable, Readable, CacheUpdatePolicy};
use cache_manager::CacheManager;
use encoded;
use engines::ForkChoice;
use engines::epoch::{Transition as EpochTransition, PendingTransition as PendingEpochTransition};
use rayon::prelude::*;
use ansi_term::Colour;
use kvdb::{DBTransaction, KeyValueDB};
/// Database backing `BlockChain`. /// Database backing `BlockChain`.
pub trait BlockChainDB: Send + Sync { pub trait BlockChainDB: Send + Sync {

View File

@ -16,18 +16,18 @@
//! Blockchain DB extras. //! Blockchain DB extras.
use std::ops;
use std::io::Write; use std::io::Write;
use std::ops;
use db::Key; use db::Key;
use engines::epoch::{Transition as EpochTransition}; use engines::epoch::{Transition as EpochTransition};
use ethereum_types::{H256, H264, U256};
use header::BlockNumber; use header::BlockNumber;
use heapsize::HeapSizeOf;
use kvdb::PREFIX_LEN as DB_PREFIX_LEN;
use receipt::Receipt; use receipt::Receipt;
use rlp; use rlp;
use heapsize::HeapSizeOf;
use ethereum_types::{H256, H264, U256};
use kvdb::PREFIX_LEN as DB_PREFIX_LEN;
/// Represents index of extra data in database /// Represents index of extra data in database
#[derive(Copy, Debug, Hash, Eq, PartialEq, Clone)] #[derive(Copy, Debug, Hash, Eq, PartialEq, Clone)]
pub enum ExtrasIndex { pub enum ExtrasIndex {
@ -252,6 +252,7 @@ pub struct EpochTransitions {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use rlp::*; use rlp::*;
use super::BlockReceipts; use super::BlockReceipts;
#[test] #[test]

View File

@ -1713,7 +1713,7 @@ impl BlockChainClient for Client {
fn list_storage(&self, id: BlockId, account: &Address, after: Option<&H256>, count: u64) -> Option<Vec<H256>> { fn list_storage(&self, id: BlockId, account: &Address, after: Option<&H256>, count: u64) -> Option<Vec<H256>> {
if !self.factories.trie.is_fat() { if !self.factories.trie.is_fat() {
trace!(target: "fatdb", "list_stroage: Not a fat DB"); trace!(target: "fatdb", "list_storage: Not a fat DB");
return None; return None;
} }

View File

@ -16,7 +16,7 @@
use std::fmt::{Display, Formatter, Error as FmtError}; use std::fmt::{Display, Formatter, Error as FmtError};
use util_error::UtilError; use util_error::UtilError;
use trie::TrieError; use ethtrie::TrieError;
/// Client configuration errors. /// Client configuration errors.
#[derive(Debug)] #[derive(Debug)]

View File

@ -25,12 +25,13 @@ use {state, state_db, client, executive, trace, transaction, db, spec, pod_state
use factory::Factories; use factory::Factories;
use evm::{VMType, FinalizationResult}; use evm::{VMType, FinalizationResult};
use vm::{self, ActionParams}; use vm::{self, ActionParams};
use ethtrie;
/// EVM test Error. /// EVM test Error.
#[derive(Debug)] #[derive(Debug)]
pub enum EvmTestError { pub enum EvmTestError {
/// Trie integrity error. /// Trie integrity error.
Trie(trie::TrieError), Trie(Box<ethtrie::TrieError>),
/// EVM error. /// EVM error.
Evm(vm::Error), Evm(vm::Error),
/// Initialization error. /// Initialization error.

View File

@ -52,7 +52,6 @@ use miner::{self, Miner, MinerService};
use spec::Spec; use spec::Spec;
use types::basic_account::BasicAccount; use types::basic_account::BasicAccount;
use types::pruning_info::PruningInfo; use types::pruning_info::PruningInfo;
use verification::queue::QueueInfo; use verification::queue::QueueInfo;
use block::{OpenBlock, SealedBlock, ClosedBlock}; use block::{OpenBlock, SealedBlock, ClosedBlock};
use executive::Executed; use executive::Executed;
@ -62,7 +61,7 @@ use state_db::StateDB;
use header::Header; use header::Header;
use encoded; use encoded;
use engines::EthEngine; use engines::EthEngine;
use trie; use ethtrie;
use state::StateInfo; use state::StateInfo;
use views::BlockView; use views::BlockView;
@ -581,10 +580,10 @@ impl Call for TestBlockChainClient {
} }
impl StateInfo for () { impl StateInfo for () {
fn nonce(&self, _address: &Address) -> trie::Result<U256> { unimplemented!() } fn nonce(&self, _address: &Address) -> ethtrie::Result<U256> { unimplemented!() }
fn balance(&self, _address: &Address) -> trie::Result<U256> { unimplemented!() } fn balance(&self, _address: &Address) -> ethtrie::Result<U256> { unimplemented!() }
fn storage_at(&self, _address: &Address, _key: &H256) -> trie::Result<H256> { unimplemented!() } fn storage_at(&self, _address: &Address, _key: &H256) -> ethtrie::Result<H256> { unimplemented!() }
fn code(&self, _address: &Address) -> trie::Result<Option<Arc<Bytes>>> { unimplemented!() } fn code(&self, _address: &Address) -> ethtrie::Result<Option<Arc<Bytes>>> { unimplemented!() }
} }
impl StateClient for TestBlockChainClient { impl StateClient for TestBlockChainClient {

View File

@ -34,12 +34,9 @@ use ethjson;
use machine::{AuxiliaryData, Call, EthereumMachine}; use machine::{AuxiliaryData, Call, EthereumMachine};
use hash::keccak; use hash::keccak;
use header::{Header, BlockNumber, ExtendedHeader}; use header::{Header, BlockNumber, ExtendedHeader};
use super::signer::EngineSigner; use super::signer::EngineSigner;
use super::validator_set::{ValidatorSet, SimpleList, new_validator_set}; use super::validator_set::{ValidatorSet, SimpleList, new_validator_set};
use self::finality::RollingFinality; use self::finality::RollingFinality;
use ethkey::{self, Password, Signature}; use ethkey::{self, Password, Signature};
use io::{IoContext, IoHandler, TimerToken, IoService}; use io::{IoContext, IoHandler, TimerToken, IoService};
use itertools::{self, Itertools}; use itertools::{self, Itertools};

View File

@ -16,15 +16,15 @@
//! Tendermint message handling. //! Tendermint message handling.
use std::cmp;
use hash::keccak;
use ethereum_types::{H256, H520, Address};
use bytes::Bytes; use bytes::Bytes;
use super::{Height, View, BlockHash, Step};
use error::Error; use error::Error;
use ethereum_types::{H256, H520, Address};
use ethkey::{recover, public_to_address};
use hash::keccak;
use header::Header; use header::Header;
use rlp::{Rlp, RlpStream, Encodable, Decodable, DecoderError}; use rlp::{Rlp, RlpStream, Encodable, Decodable, DecoderError};
use ethkey::{recover, public_to_address}; use std::cmp;
use super::{Height, View, BlockHash, Step};
use super::super::vote_collector::Message; use super::super::vote_collector::Message;
/// Message transmitted between consensus participants. /// Message transmitted between consensus participants.

View File

@ -145,7 +145,7 @@ impl <F> super::EpochVerifier<EthereumMachine> for EpochVerifier<F>
fn check_finality_proof(&self, proof: &[u8]) -> Option<Vec<H256>> { fn check_finality_proof(&self, proof: &[u8]) -> Option<Vec<H256>> {
match ::rlp::decode(proof) { match ::rlp::decode(proof) {
Ok(header) => self.verify_light(&header).ok().map(|_| vec![header.hash()]), Ok(header) => self.verify_light(&header).ok().map(|_| vec![header.hash()]),
Err(_) => None // REVIEW: log perhaps? Not sure what the policy is. Err(_) => None
} }
} }
} }

View File

@ -16,27 +16,23 @@
/// Validator set maintained in a contract, updated using `getValidators` method. /// Validator set maintained in a contract, updated using `getValidators` method.
use std::sync::{Weak, Arc};
use hash::keccak;
use ethereum_types::{H256, U256, Address, Bloom};
use parking_lot::RwLock;
use bytes::Bytes; use bytes::Bytes;
use memory_cache::MemoryLruCache;
use unexpected::Mismatch;
use rlp::{Rlp, RlpStream};
use kvdb::DBValue;
use client::EngineClient; use client::EngineClient;
use machine::{AuxiliaryData, Call, EthereumMachine, AuxiliaryRequest}; use ethereum_types::{H256, U256, Address, Bloom};
use hash::keccak;
use header::Header; use header::Header;
use ids::BlockId; use ids::BlockId;
use kvdb::DBValue;
use log_entry::LogEntry; use log_entry::LogEntry;
use machine::{AuxiliaryData, Call, EthereumMachine, AuxiliaryRequest};
use memory_cache::MemoryLruCache;
use parking_lot::RwLock;
use receipt::Receipt; use receipt::Receipt;
use rlp::{Rlp, RlpStream};
use std::sync::{Weak, Arc};
use super::{SystemCall, ValidatorSet}; use super::{SystemCall, ValidatorSet};
use super::simple_list::SimpleList; use super::simple_list::SimpleList;
use unexpected::Mismatch;
use_contract!(validator_set, "ValidatorSet", "res/contracts/validator_set.json"); use_contract!(validator_set, "ValidatorSet", "res/contracts/validator_set.json");

View File

@ -22,7 +22,7 @@ use ethereum_types::{H256, U256, Address, Bloom};
use util_error::{self, UtilError}; use util_error::{self, UtilError};
use snappy::InvalidInput; use snappy::InvalidInput;
use unexpected::{Mismatch, OutOfBounds}; use unexpected::{Mismatch, OutOfBounds};
use trie::TrieError; use ethtrie::TrieError;
use io::*; use io::*;
use header::BlockNumber; use header::BlockNumber;
use client::Error as ClientError; use client::Error as ClientError;

View File

@ -18,7 +18,7 @@
use ethereum_types::{U256, U512, Address}; use ethereum_types::{U256, U512, Address};
use bytes::Bytes; use bytes::Bytes;
use trie; use ethtrie;
use vm; use vm;
use trace::{VMTrace, FlatTrace}; use trace::{VMTrace, FlatTrace};
use log_entry::LogEntry; use log_entry::LogEntry;
@ -117,9 +117,14 @@ pub enum ExecutionError {
TransactionMalformed(String), TransactionMalformed(String),
} }
impl From<Box<trie::TrieError>> for ExecutionError { impl From<Box<ethtrie::TrieError>> for ExecutionError {
fn from(err: Box<trie::TrieError>) -> Self { fn from(err: Box<ethtrie::TrieError>) -> Self {
ExecutionError::Internal(format!("{}", err)) ExecutionError::Internal(format!("{:?}", err))
}
}
impl From<ethtrie::TrieError> for ExecutionError {
fn from(err: ethtrie::TrieError) -> Self {
ExecutionError::Internal(format!("{:?}", err))
} }
} }

View File

@ -15,10 +15,12 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use trie::TrieFactory; use trie::TrieFactory;
use ethtrie::RlpCodec;
use account_db::Factory as AccountFactory; use account_db::Factory as AccountFactory;
use evm::{Factory as EvmFactory, VMType}; use evm::{Factory as EvmFactory, VMType};
use vm::{Vm, ActionParams, Schedule}; use vm::{Vm, ActionParams, Schedule};
use wasm::WasmInterpreter; use wasm::WasmInterpreter;
use keccak_hasher::KeccakHasher;
const WASM_MAGIC_NUMBER: &'static [u8; 4] = b"\0asm"; const WASM_MAGIC_NUMBER: &'static [u8; 4] = b"\0asm";
@ -54,7 +56,7 @@ pub struct Factories {
/// factory for evm. /// factory for evm.
pub vm: VmFactory, pub vm: VmFactory,
/// factory for tries. /// factory for tries.
pub trie: TrieFactory, pub trie: TrieFactory<KeccakHasher, RlpCodec>,
/// factory for account databases. /// factory for account databases.
pub accountdb: AccountFactory, pub accountdb: AccountFactory,
} }

View File

@ -31,7 +31,7 @@ use ethjson;
use trace::{Tracer, NoopTracer}; use trace::{Tracer, NoopTracer};
use trace::{VMTracer, NoopVMTracer}; use trace::{VMTracer, NoopVMTracer};
use bytes::{Bytes, BytesRef}; use bytes::{Bytes, BytesRef};
use trie; use ethtrie;
use rlp::RlpStream; use rlp::RlpStream;
use hash::keccak; use hash::keccak;
use machine::EthereumMachine as Machine; use machine::EthereumMachine as Machine;
@ -93,7 +93,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> TestExt<'a, T, V, B>
address: Address, address: Address,
tracer: &'a mut T, tracer: &'a mut T,
vm_tracer: &'a mut V, vm_tracer: &'a mut V,
) -> trie::Result<Self> { ) -> ethtrie::Result<Self> {
let static_call = false; let static_call = false;
Ok(TestExt { Ok(TestExt {
nonce: state.nonce(&address)?, nonce: state.nonce(&address)?,

View File

@ -16,8 +16,10 @@
use ethjson; use ethjson;
use trie::{TrieFactory, TrieSpec}; use trie::{TrieFactory, TrieSpec};
use ethtrie::RlpCodec;
use ethereum_types::H256; use ethereum_types::H256;
use memorydb::MemoryDB; use memorydb::MemoryDB;
use keccak_hasher::KeccakHasher;
use super::HookType; use super::HookType;
@ -28,13 +30,13 @@ pub use self::secure::run_test_file as run_secure_test_file;
fn test_trie<H: FnMut(&str, HookType)>(json: &[u8], trie: TrieSpec, start_stop_hook: &mut H) -> Vec<String> { fn test_trie<H: FnMut(&str, HookType)>(json: &[u8], trie: TrieSpec, start_stop_hook: &mut H) -> Vec<String> {
let tests = ethjson::trie::Test::load(json).unwrap(); let tests = ethjson::trie::Test::load(json).unwrap();
let factory = TrieFactory::new(trie); let factory = TrieFactory::<_, RlpCodec>::new(trie);
let mut result = vec![]; let mut result = vec![];
for (name, test) in tests.into_iter() { for (name, test) in tests.into_iter() {
start_stop_hook(&name, HookType::OnStart); start_stop_hook(&name, HookType::OnStart);
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::default(); let mut root = H256::default();
let mut t = factory.create(&mut memdb, &mut root); let mut t = factory.create(&mut memdb, &mut root);

View File

@ -91,9 +91,11 @@ extern crate rayon;
extern crate rlp; extern crate rlp;
extern crate rlp_compress; extern crate rlp_compress;
extern crate keccak_hash as hash; extern crate keccak_hash as hash;
extern crate keccak_hasher;
extern crate heapsize; extern crate heapsize;
extern crate memorydb; extern crate memorydb;
extern crate patricia_trie as trie; extern crate patricia_trie as trie;
extern crate patricia_trie_ethereum as ethtrie;
extern crate triehash; extern crate triehash;
extern crate ansi_term; extern crate ansi_term;
extern crate unexpected; extern crate unexpected;

View File

@ -20,9 +20,11 @@ use itertools::Itertools;
use hash::{keccak}; use hash::{keccak};
use ethereum_types::{H256, U256}; use ethereum_types::{H256, U256};
use hashdb::HashDB; use hashdb::HashDB;
use keccak_hasher::KeccakHasher;
use triehash::sec_trie_root; use triehash::sec_trie_root;
use bytes::Bytes; use bytes::Bytes;
use trie::TrieFactory; use trie::TrieFactory;
use ethtrie::RlpCodec;
use state::Account; use state::Account;
use ethjson; use ethjson;
use types::account_diff::*; use types::account_diff::*;
@ -65,7 +67,7 @@ impl PodAccount {
} }
/// Place additional data into given hash DB. /// Place additional data into given hash DB.
pub fn insert_additional(&self, db: &mut HashDB, factory: &TrieFactory) { pub fn insert_additional(&self, db: &mut HashDB<KeccakHasher>, factory: &TrieFactory<KeccakHasher, RlpCodec>) {
match self.code { match self.code {
Some(ref c) if !c.is_empty() => { db.insert(c); } Some(ref c) if !c.is_empty() => { db.insert(c); }
_ => {} _ => {}

View File

@ -18,16 +18,15 @@
use account_db::{AccountDB, AccountDBMut}; use account_db::{AccountDB, AccountDBMut};
use basic_account::BasicAccount; use basic_account::BasicAccount;
use snapshot::Error;
use hash::{KECCAK_EMPTY, KECCAK_NULL_RLP};
use ethereum_types::{H256, U256};
use hashdb::HashDB;
use bytes::Bytes; use bytes::Bytes;
use trie::{TrieDB, Trie}; use ethereum_types::{H256, U256};
use ethtrie::{TrieDB, TrieDBMut};
use hash::{KECCAK_EMPTY, KECCAK_NULL_RLP};
use hashdb::HashDB;
use rlp::{RlpStream, Rlp}; use rlp::{RlpStream, Rlp};
use snapshot::Error;
use std::collections::HashSet; use std::collections::HashSet;
use trie::{Trie, TrieMut};
// An empty account -- these were replaced with RLP null data for a space optimization in v1. // An empty account -- these were replaced with RLP null data for a space optimization in v1.
const ACC_EMPTY: BasicAccount = BasicAccount { const ACC_EMPTY: BasicAccount = BasicAccount {
@ -151,7 +150,6 @@ pub fn from_fat_rlp(
rlp: Rlp, rlp: Rlp,
mut storage_root: H256, mut storage_root: H256,
) -> Result<(BasicAccount, Option<Bytes>), Error> { ) -> Result<(BasicAccount, Option<Bytes>), Error> {
use trie::{TrieDBMut, TrieMut};
// check for special case of empty account. // check for special case of empty account.
if rlp.is_empty() { if rlp.is_empty() {

View File

@ -19,7 +19,6 @@
use block::Block; use block::Block;
use header::Header; use header::Header;
use hash::keccak; use hash::keccak;
use views::BlockView; use views::BlockView;
use rlp::{DecoderError, RlpStream, Rlp}; use rlp::{DecoderError, RlpStream, Rlp};
use ethereum_types::H256; use ethereum_types::H256;

View File

@ -38,6 +38,7 @@ use ethereum_types::{H256, U256};
use kvdb::KeyValueDB; use kvdb::KeyValueDB;
use bytes::Bytes; use bytes::Bytes;
/// Snapshot creation and restoration for PoA chains. /// Snapshot creation and restoration for PoA chains.
/// Chunk format: /// Chunk format:
/// ///

View File

@ -21,7 +21,7 @@ use std::fmt;
use ids::BlockId; use ids::BlockId;
use ethereum_types::H256; use ethereum_types::H256;
use trie::TrieError; use ethtrie::TrieError;
use rlp::DecoderError; use rlp::DecoderError;
/// Snapshot-related errors. /// Snapshot-related errors.

View File

@ -32,13 +32,15 @@ use ids::BlockId;
use ethereum_types::{H256, U256}; use ethereum_types::{H256, U256};
use hashdb::HashDB; use hashdb::HashDB;
use keccak_hasher::KeccakHasher;
use kvdb::DBValue; use kvdb::DBValue;
use snappy; use snappy;
use bytes::Bytes; use bytes::Bytes;
use parking_lot::Mutex; use parking_lot::Mutex;
use journaldb::{self, Algorithm, JournalDB}; use journaldb::{self, Algorithm, JournalDB};
use kvdb::KeyValueDB; use kvdb::KeyValueDB;
use trie::{TrieDB, TrieDBMut, Trie, TrieMut}; use trie::{Trie, TrieMut};
use ethtrie::{TrieDB, TrieDBMut};
use rlp::{RlpStream, Rlp}; use rlp::{RlpStream, Rlp};
use bloom_journal::Bloom; use bloom_journal::Bloom;
@ -126,7 +128,7 @@ pub fn take_snapshot<W: SnapshotWriter + Send>(
engine: &EthEngine, engine: &EthEngine,
chain: &BlockChain, chain: &BlockChain,
block_at: H256, block_at: H256,
state_db: &HashDB, state_db: &HashDB<KeccakHasher>,
writer: W, writer: W,
p: &Progress p: &Progress
) -> Result<(), Error> { ) -> Result<(), Error> {
@ -264,7 +266,7 @@ impl<'a> StateChunker<'a> {
/// ///
/// Returns a list of hashes of chunks created, or any error it may /// Returns a list of hashes of chunks created, or any error it may
/// have encountered. /// have encountered.
pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex<SnapshotWriter + 'a>, progress: &'a Progress) -> Result<Vec<H256>, Error> { pub fn chunk_state<'a>(db: &HashDB<KeccakHasher>, root: &H256, writer: &Mutex<SnapshotWriter + 'a>, progress: &'a Progress) -> Result<Vec<H256>, Error> {
let account_trie = TrieDB::new(db, &root)?; let account_trie = TrieDB::new(db, &root)?;
let mut chunker = StateChunker { let mut chunker = StateChunker {
@ -414,7 +416,7 @@ struct RebuiltStatus {
// rebuild a set of accounts and their storage. // rebuild a set of accounts and their storage.
// returns a status detailing newly-loaded code and accounts missing code. // returns a status detailing newly-loaded code and accounts missing code.
fn rebuild_accounts( fn rebuild_accounts(
db: &mut HashDB, db: &mut HashDB<KeccakHasher>,
account_fat_rlps: Rlp, account_fat_rlps: Rlp,
out_chunk: &mut [(H256, Bytes)], out_chunk: &mut [(H256, Bytes)],
known_code: &HashMap<H256, H256>, known_code: &HashMap<H256, H256>,

View File

@ -36,8 +36,10 @@ use rand::Rng;
use kvdb::DBValue; use kvdb::DBValue;
use ethereum_types::H256; use ethereum_types::H256;
use hashdb::HashDB; use hashdb::HashDB;
use keccak_hasher::KeccakHasher;
use journaldb; use journaldb;
use trie::{SecTrieDBMut, TrieMut, TrieDB, TrieDBMut, Trie}; use trie::{TrieMut, Trie};
use ethtrie::{SecTrieDBMut, TrieDB, TrieDBMut};
use self::trie_standardmap::{Alphabet, StandardMap, ValueMode}; use self::trie_standardmap::{Alphabet, StandardMap, ValueMode};
// the proportion of accounts we will alter each tick. // the proportion of accounts we will alter each tick.
@ -60,7 +62,7 @@ impl StateProducer {
/// Tick the state producer. This alters the state, writing new data into /// Tick the state producer. This alters the state, writing new data into
/// the database. /// the database.
pub fn tick<R: Rng>(&mut self, rng: &mut R, db: &mut HashDB) { pub fn tick<R: Rng>(&mut self, rng: &mut R, db: &mut HashDB<KeccakHasher>) {
// modify existing accounts. // modify existing accounts.
let mut accounts_to_modify: Vec<_> = { let mut accounts_to_modify: Vec<_> = {
let trie = TrieDB::new(&*db, &self.state_root).unwrap(); let trie = TrieDB::new(&*db, &self.state_root).unwrap();
@ -129,7 +131,7 @@ pub fn fill_storage(mut db: AccountDBMut, root: &mut H256, seed: &mut H256) {
} }
/// Compare two state dbs. /// Compare two state dbs.
pub fn compare_dbs(one: &HashDB, two: &HashDB) { pub fn compare_dbs(one: &HashDB<KeccakHasher>, two: &HashDB<KeccakHasher>) {
let keys = one.keys(); let keys = one.keys();
for key in keys.keys() { for key in keys.keys() {

View File

@ -23,10 +23,11 @@ use hash::{KECCAK_EMPTY, KECCAK_NULL_RLP, keccak};
use ethereum_types::{H256, U256, Address}; use ethereum_types::{H256, U256, Address};
use error::Error; use error::Error;
use hashdb::HashDB; use hashdb::HashDB;
use keccak_hasher::KeccakHasher;
use kvdb::DBValue; use kvdb::DBValue;
use bytes::{Bytes, ToPretty}; use bytes::{Bytes, ToPretty};
use trie; use trie::{Trie, Recorder};
use trie::{SecTrieDB, Trie, TrieFactory, TrieError}; use ethtrie::{TrieFactory, TrieDB, SecTrieDB, Result as TrieResult};
use pod_account::*; use pod_account::*;
use rlp::{RlpStream, encode}; use rlp::{RlpStream, encode};
use lru_cache::LruCache; use lru_cache::LruCache;
@ -199,7 +200,7 @@ impl Account {
/// Get (and cache) the contents of the trie's storage at `key`. /// Get (and cache) the contents of the trie's storage at `key`.
/// Takes modified storage into account. /// Takes modified storage into account.
pub fn storage_at(&self, db: &HashDB, key: &H256) -> trie::Result<H256> { pub fn storage_at(&self, db: &HashDB<KeccakHasher>, key: &H256) -> TrieResult<H256> {
if let Some(value) = self.cached_storage_at(key) { if let Some(value) = self.cached_storage_at(key) {
return Ok(value); return Ok(value);
} }
@ -278,7 +279,7 @@ impl Account {
} }
/// Provide a database to get `code_hash`. Should not be called if it is a contract without code. /// Provide a database to get `code_hash`. Should not be called if it is a contract without code.
pub fn cache_code(&mut self, db: &HashDB) -> Option<Arc<Bytes>> { pub fn cache_code(&mut self, db: &HashDB<KeccakHasher>) -> Option<Arc<Bytes>> {
// TODO: fill out self.code_cache; // TODO: fill out self.code_cache;
trace!("Account::cache_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty()); trace!("Account::cache_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty());
@ -307,7 +308,7 @@ impl Account {
} }
/// Provide a database to get `code_size`. Should not be called if it is a contract without code. /// Provide a database to get `code_size`. Should not be called if it is a contract without code.
pub fn cache_code_size(&mut self, db: &HashDB) -> bool { pub fn cache_code_size(&mut self, db: &HashDB<KeccakHasher>) -> bool {
// TODO: fill out self.code_cache; // TODO: fill out self.code_cache;
trace!("Account::cache_code_size: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty()); trace!("Account::cache_code_size: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty());
self.code_size.is_some() || self.code_size.is_some() ||
@ -374,7 +375,7 @@ impl Account {
} }
/// Commit the `storage_changes` to the backing DB and update `storage_root`. /// Commit the `storage_changes` to the backing DB and update `storage_root`.
pub fn commit_storage(&mut self, trie_factory: &TrieFactory, db: &mut HashDB) -> trie::Result<()> { pub fn commit_storage(&mut self, trie_factory: &TrieFactory, db: &mut HashDB<KeccakHasher>) -> TrieResult<()> {
let mut t = trie_factory.from_existing(db, &mut self.storage_root)?; let mut t = trie_factory.from_existing(db, &mut self.storage_root)?;
for (k, v) in self.storage_changes.drain() { for (k, v) in self.storage_changes.drain() {
// cast key and value to trait type, // cast key and value to trait type,
@ -390,7 +391,7 @@ impl Account {
} }
/// Commit any unsaved code. `code_hash` will always return the hash of the `code_cache` after this. /// Commit any unsaved code. `code_hash` will always return the hash of the `code_cache` after this.
pub fn commit_code(&mut self, db: &mut HashDB) { pub fn commit_code(&mut self, db: &mut HashDB<KeccakHasher>) {
trace!("Commiting code of {:?} - {:?}, {:?}", self, self.code_filth == Filth::Dirty, self.code_cache.is_empty()); trace!("Commiting code of {:?} - {:?}, {:?}", self, self.code_filth == Filth::Dirty, self.code_cache.is_empty());
match (self.code_filth == Filth::Dirty, self.code_cache.is_empty()) { match (self.code_filth == Filth::Dirty, self.code_cache.is_empty()) {
(true, true) => { (true, true) => {
@ -472,10 +473,7 @@ impl Account {
/// trie. /// trie.
/// `storage_key` is the hash of the desired storage key, meaning /// `storage_key` is the hash of the desired storage key, meaning
/// this will only work correctly under a secure trie. /// this will only work correctly under a secure trie.
pub fn prove_storage(&self, db: &HashDB, storage_key: H256) -> Result<(Vec<Bytes>, H256), Box<TrieError>> { pub fn prove_storage(&self, db: &HashDB<KeccakHasher>, storage_key: H256) -> TrieResult<(Vec<Bytes>, H256)> {
use trie::{Trie, TrieDB};
use trie::recorder::Recorder;
let mut recorder = Recorder::new(); let mut recorder = Recorder::new();
let trie = TrieDB::new(db, &self.storage_root)?; let trie = TrieDB::new(db, &self.storage_root)?;

View File

@ -29,14 +29,15 @@ use parking_lot::Mutex;
use ethereum_types::{Address, H256}; use ethereum_types::{Address, H256};
use memorydb::MemoryDB; use memorydb::MemoryDB;
use hashdb::{AsHashDB, HashDB, DBValue}; use hashdb::{AsHashDB, HashDB, DBValue};
use keccak_hasher::KeccakHasher;
/// State backend. See module docs for more details. /// State backend. See module docs for more details.
pub trait Backend: Send { pub trait Backend: Send {
/// Treat the backend as a read-only hashdb. /// Treat the backend as a read-only hashdb.
fn as_hashdb(&self) -> &HashDB; fn as_hashdb(&self) -> &HashDB<KeccakHasher>;
/// Treat the backend as a writeable hashdb. /// Treat the backend as a writeable hashdb.
fn as_hashdb_mut(&mut self) -> &mut HashDB; fn as_hashdb_mut(&mut self) -> &mut HashDB<KeccakHasher>;
/// Add an account entry to the cache. /// Add an account entry to the cache.
fn add_to_account_cache(&mut self, addr: Address, data: Option<Account>, modified: bool); fn add_to_account_cache(&mut self, addr: Address, data: Option<Account>, modified: bool);
@ -75,18 +76,18 @@ pub trait Backend: Send {
// TODO: when account lookup moved into backends, this won't rely as tenuously on intended // TODO: when account lookup moved into backends, this won't rely as tenuously on intended
// usage. // usage.
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub struct ProofCheck(MemoryDB); pub struct ProofCheck(MemoryDB<KeccakHasher>);
impl ProofCheck { impl ProofCheck {
/// Create a new `ProofCheck` backend from the given state items. /// Create a new `ProofCheck` backend from the given state items.
pub fn new(proof: &[DBValue]) -> Self { pub fn new(proof: &[DBValue]) -> Self {
let mut db = MemoryDB::new(); let mut db = MemoryDB::<KeccakHasher>::new();
for item in proof { db.insert(item); } for item in proof { db.insert(item); }
ProofCheck(db) ProofCheck(db)
} }
} }
impl HashDB for ProofCheck { impl HashDB<KeccakHasher> for ProofCheck {
fn keys(&self) -> HashMap<H256, i32> { self.0.keys() } fn keys(&self) -> HashMap<H256, i32> { self.0.keys() }
fn get(&self, key: &H256) -> Option<DBValue> { fn get(&self, key: &H256) -> Option<DBValue> {
self.0.get(key) self.0.get(key)
@ -107,9 +108,14 @@ impl HashDB for ProofCheck {
fn remove(&mut self, _key: &H256) { } fn remove(&mut self, _key: &H256) { }
} }
impl AsHashDB<KeccakHasher> for ProofCheck {
fn as_hashdb(&self) -> &HashDB<KeccakHasher> { self }
fn as_hashdb_mut(&mut self) -> &mut HashDB<KeccakHasher> { self }
}
impl Backend for ProofCheck { impl Backend for ProofCheck {
fn as_hashdb(&self) -> &HashDB { self } fn as_hashdb(&self) -> &HashDB<KeccakHasher> { self }
fn as_hashdb_mut(&mut self) -> &mut HashDB { self } fn as_hashdb_mut(&mut self) -> &mut HashDB<KeccakHasher> { self }
fn add_to_account_cache(&mut self, _addr: Address, _data: Option<Account>, _modified: bool) {} fn add_to_account_cache(&mut self, _addr: Address, _data: Option<Account>, _modified: bool) {}
fn cache_code(&self, _hash: H256, _code: Arc<Vec<u8>>) {} fn cache_code(&self, _hash: H256, _code: Arc<Vec<u8>>) {}
fn get_cached_account(&self, _addr: &Address) -> Option<Option<Account>> { None } fn get_cached_account(&self, _addr: &Address) -> Option<Option<Account>> { None }
@ -128,13 +134,18 @@ impl Backend for ProofCheck {
/// The proof-of-execution can be extracted with `extract_proof`. /// The proof-of-execution can be extracted with `extract_proof`.
/// ///
/// This doesn't cache anything or rely on the canonical state caches. /// This doesn't cache anything or rely on the canonical state caches.
pub struct Proving<H: AsHashDB> { pub struct Proving<H: AsHashDB<KeccakHasher>> {
base: H, // state we're proving values from. base: H, // state we're proving values from.
changed: MemoryDB, // changed state via insertions. changed: MemoryDB<KeccakHasher>, // changed state via insertions.
proof: Mutex<HashSet<DBValue>>, proof: Mutex<HashSet<DBValue>>,
} }
impl<H: AsHashDB + Send + Sync> HashDB for Proving<H> { impl<AH: AsHashDB<KeccakHasher> + Send + Sync> AsHashDB<KeccakHasher> for Proving<AH> {
fn as_hashdb(&self) -> &HashDB<KeccakHasher> { self }
fn as_hashdb_mut(&mut self) -> &mut HashDB<KeccakHasher> { self }
}
impl<H: AsHashDB<KeccakHasher> + Send + Sync> HashDB<KeccakHasher> for Proving<H> {
fn keys(&self) -> HashMap<H256, i32> { fn keys(&self) -> HashMap<H256, i32> {
let mut keys = self.base.as_hashdb().keys(); let mut keys = self.base.as_hashdb().keys();
keys.extend(self.changed.keys()); keys.extend(self.changed.keys());
@ -171,14 +182,10 @@ impl<H: AsHashDB + Send + Sync> HashDB for Proving<H> {
} }
} }
impl<H: AsHashDB + Send + Sync> Backend for Proving<H> { impl<H: AsHashDB<KeccakHasher> + Send + Sync> Backend for Proving<H> {
fn as_hashdb(&self) -> &HashDB { fn as_hashdb(&self) -> &HashDB<KeccakHasher> { self }
self
}
fn as_hashdb_mut(&mut self) -> &mut HashDB { fn as_hashdb_mut(&mut self) -> &mut HashDB<KeccakHasher> { self }
self
}
fn add_to_account_cache(&mut self, _: Address, _: Option<Account>, _: bool) { } fn add_to_account_cache(&mut self, _: Address, _: Option<Account>, _: bool) { }
@ -197,13 +204,13 @@ impl<H: AsHashDB + Send + Sync> Backend for Proving<H> {
fn is_known_null(&self, _: &Address) -> bool { false } fn is_known_null(&self, _: &Address) -> bool { false }
} }
impl<H: AsHashDB> Proving<H> { impl<H: AsHashDB<KeccakHasher>> Proving<H> {
/// Create a new `Proving` over a base database. /// Create a new `Proving` over a base database.
/// This will store all values ever fetched from that base. /// This will store all values ever fetched from that base.
pub fn new(base: H) -> Self { pub fn new(base: H) -> Self {
Proving { Proving {
base: base, base: base,
changed: MemoryDB::new(), changed: MemoryDB::<KeccakHasher>::new(),
proof: Mutex::new(HashSet::new()), proof: Mutex::new(HashSet::new()),
} }
} }
@ -215,7 +222,7 @@ impl<H: AsHashDB> Proving<H> {
} }
} }
impl<H: AsHashDB + Clone> Clone for Proving<H> { impl<H: AsHashDB<KeccakHasher> + Clone> Clone for Proving<H> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Proving { Proving {
base: self.base.clone(), base: self.base.clone(),
@ -229,12 +236,12 @@ impl<H: AsHashDB + Clone> Clone for Proving<H> {
/// it. Doesn't cache anything. /// it. Doesn't cache anything.
pub struct Basic<H>(pub H); pub struct Basic<H>(pub H);
impl<H: AsHashDB + Send + Sync> Backend for Basic<H> { impl<H: AsHashDB<KeccakHasher> + Send + Sync> Backend for Basic<H> {
fn as_hashdb(&self) -> &HashDB { fn as_hashdb(&self) -> &HashDB<KeccakHasher> {
self.0.as_hashdb() self.0.as_hashdb()
} }
fn as_hashdb_mut(&mut self) -> &mut HashDB { fn as_hashdb_mut(&mut self) -> &mut HashDB<KeccakHasher> {
self.0.as_hashdb_mut() self.0.as_hashdb_mut()
} }

View File

@ -44,12 +44,12 @@ use factory::VmFactory;
use ethereum_types::{H256, U256, Address}; use ethereum_types::{H256, U256, Address};
use hashdb::{HashDB, AsHashDB}; use hashdb::{HashDB, AsHashDB};
use keccak_hasher::KeccakHasher;
use kvdb::DBValue; use kvdb::DBValue;
use bytes::Bytes; use bytes::Bytes;
use trie; use trie::{Trie, TrieError, Recorder};
use trie::{Trie, TrieError, TrieDB}; use ethtrie::{TrieDB, Result as TrieResult};
use trie::recorder::Recorder;
mod account; mod account;
mod substate; mod substate;
@ -225,7 +225,7 @@ pub fn check_proof(
/// Prove a transaction on the given state. /// Prove a transaction on the given state.
/// Returns `None` when the transacion could not be proved, /// Returns `None` when the transacion could not be proved,
/// and a proof otherwise. /// and a proof otherwise.
pub fn prove_transaction<H: AsHashDB + Send + Sync>( pub fn prove_transaction<H: AsHashDB<KeccakHasher> + Send + Sync>(
db: H, db: H,
root: H256, root: H256,
transaction: &SignedTransaction, transaction: &SignedTransaction,
@ -336,23 +336,23 @@ pub enum CleanupMode<'a> {
/// Provides subset of `State` methods to query state information /// Provides subset of `State` methods to query state information
pub trait StateInfo { pub trait StateInfo {
/// Get the nonce of account `a`. /// Get the nonce of account `a`.
fn nonce(&self, a: &Address) -> trie::Result<U256>; fn nonce(&self, a: &Address) -> TrieResult<U256>;
/// Get the balance of account `a`. /// Get the balance of account `a`.
fn balance(&self, a: &Address) -> trie::Result<U256>; fn balance(&self, a: &Address) -> TrieResult<U256>;
/// Mutate storage of account `address` so that it is `value` for `key`. /// Mutate storage of account `address` so that it is `value` for `key`.
fn storage_at(&self, address: &Address, key: &H256) -> trie::Result<H256>; fn storage_at(&self, address: &Address, key: &H256) -> TrieResult<H256>;
/// Get accounts' code. /// Get accounts' code.
fn code(&self, a: &Address) -> trie::Result<Option<Arc<Bytes>>>; fn code(&self, a: &Address) -> TrieResult<Option<Arc<Bytes>>>;
} }
impl<B: Backend> StateInfo for State<B> { impl<B: Backend> StateInfo for State<B> {
fn nonce(&self, a: &Address) -> trie::Result<U256> { State::nonce(self, a) } fn nonce(&self, a: &Address) -> TrieResult<U256> { State::nonce(self, a) }
fn balance(&self, a: &Address) -> trie::Result<U256> { State::balance(self, a) } fn balance(&self, a: &Address) -> TrieResult<U256> { State::balance(self, a) }
fn storage_at(&self, address: &Address, key: &H256) -> trie::Result<H256> { State::storage_at(self, address, key) } fn storage_at(&self, address: &Address, key: &H256) -> TrieResult<H256> { State::storage_at(self, address, key) }
fn code(&self, address: &Address) -> trie::Result<Option<Arc<Bytes>>> { State::code(self, address) } fn code(&self, address: &Address) -> TrieResult<Option<Arc<Bytes>>> { State::code(self, address) }
} }
const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \ const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \
@ -379,9 +379,9 @@ impl<B: Backend> State<B> {
} }
/// Creates new state with existing state root /// Creates new state with existing state root
pub fn from_existing(db: B, root: H256, account_start_nonce: U256, factories: Factories) -> Result<State<B>, TrieError> { pub fn from_existing(db: B, root: H256, account_start_nonce: U256, factories: Factories) -> TrieResult<State<B>> {
if !db.as_hashdb().contains(&root) { if !db.as_hashdb().contains(&root) {
return Err(TrieError::InvalidStateRoot(root)); return Err(Box::new(TrieError::InvalidStateRoot(root)));
} }
let state = State { let state = State {
@ -481,7 +481,7 @@ impl<B: Backend> State<B> {
} }
/// Destroy the current object and return single account data. /// Destroy the current object and return single account data.
pub fn into_account(self, account: &Address) -> trie::Result<(Option<Arc<Bytes>>, HashMap<H256, H256>)> { pub fn into_account(self, account: &Address) -> TrieResult<(Option<Arc<Bytes>>, HashMap<H256, H256>)> {
// TODO: deconstruct without cloning. // TODO: deconstruct without cloning.
let account = self.require(account, true)?; let account = self.require(account, true)?;
Ok((account.code().clone(), account.storage_changes().clone())) Ok((account.code().clone(), account.storage_changes().clone()))
@ -504,43 +504,43 @@ impl<B: Backend> State<B> {
} }
/// Determine whether an account exists. /// Determine whether an account exists.
pub fn exists(&self, a: &Address) -> trie::Result<bool> { pub fn exists(&self, a: &Address) -> TrieResult<bool> {
// Bloom filter does not contain empty accounts, so it is important here to // Bloom filter does not contain empty accounts, so it is important here to
// check if account exists in the database directly before EIP-161 is in effect. // check if account exists in the database directly before EIP-161 is in effect.
self.ensure_cached(a, RequireCache::None, false, |a| a.is_some()) self.ensure_cached(a, RequireCache::None, false, |a| a.is_some())
} }
/// Determine whether an account exists and if not empty. /// Determine whether an account exists and if not empty.
pub fn exists_and_not_null(&self, a: &Address) -> trie::Result<bool> { pub fn exists_and_not_null(&self, a: &Address) -> TrieResult<bool> {
self.ensure_cached(a, RequireCache::None, false, |a| a.map_or(false, |a| !a.is_null())) self.ensure_cached(a, RequireCache::None, false, |a| a.map_or(false, |a| !a.is_null()))
} }
/// Determine whether an account exists and has code or non-zero nonce. /// Determine whether an account exists and has code or non-zero nonce.
pub fn exists_and_has_code_or_nonce(&self, a: &Address) -> trie::Result<bool> { pub fn exists_and_has_code_or_nonce(&self, a: &Address) -> TrieResult<bool> {
self.ensure_cached(a, RequireCache::CodeSize, false, self.ensure_cached(a, RequireCache::CodeSize, false,
|a| a.map_or(false, |a| a.code_hash() != KECCAK_EMPTY || *a.nonce() != self.account_start_nonce)) |a| a.map_or(false, |a| a.code_hash() != KECCAK_EMPTY || *a.nonce() != self.account_start_nonce))
} }
/// Get the balance of account `a`. /// Get the balance of account `a`.
pub fn balance(&self, a: &Address) -> trie::Result<U256> { pub fn balance(&self, a: &Address) -> TrieResult<U256> {
self.ensure_cached(a, RequireCache::None, true, self.ensure_cached(a, RequireCache::None, true,
|a| a.as_ref().map_or(U256::zero(), |account| *account.balance())) |a| a.as_ref().map_or(U256::zero(), |account| *account.balance()))
} }
/// Get the nonce of account `a`. /// Get the nonce of account `a`.
pub fn nonce(&self, a: &Address) -> trie::Result<U256> { pub fn nonce(&self, a: &Address) -> TrieResult<U256> {
self.ensure_cached(a, RequireCache::None, true, self.ensure_cached(a, RequireCache::None, true,
|a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce())) |a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce()))
} }
/// Get the storage root of account `a`. /// Get the storage root of account `a`.
pub fn storage_root(&self, a: &Address) -> trie::Result<Option<H256>> { pub fn storage_root(&self, a: &Address) -> TrieResult<Option<H256>> {
self.ensure_cached(a, RequireCache::None, true, self.ensure_cached(a, RequireCache::None, true,
|a| a.as_ref().and_then(|account| account.storage_root().cloned())) |a| a.as_ref().and_then(|account| account.storage_root().cloned()))
} }
/// Mutate storage of account `address` so that it is `value` for `key`. /// Mutate storage of account `address` so that it is `value` for `key`.
pub fn storage_at(&self, address: &Address, key: &H256) -> trie::Result<H256> { pub fn storage_at(&self, address: &Address, key: &H256) -> TrieResult<H256> {
// Storage key search and update works like this: // Storage key search and update works like this:
// 1. If there's an entry for the account in the local cache check for the key and return it if found. // 1. If there's an entry for the account in the local cache check for the key and return it if found.
// 2. If there's an entry for the account in the global cache check for the key or load it into that account. // 2. If there's an entry for the account in the global cache check for the key or load it into that account.
@ -602,25 +602,25 @@ impl<B: Backend> State<B> {
} }
/// Get accounts' code. /// Get accounts' code.
pub fn code(&self, a: &Address) -> trie::Result<Option<Arc<Bytes>>> { pub fn code(&self, a: &Address) -> TrieResult<Option<Arc<Bytes>>> {
self.ensure_cached(a, RequireCache::Code, true, self.ensure_cached(a, RequireCache::Code, true,
|a| a.as_ref().map_or(None, |a| a.code().clone())) |a| a.as_ref().map_or(None, |a| a.code().clone()))
} }
/// Get an account's code hash. /// Get an account's code hash.
pub fn code_hash(&self, a: &Address) -> trie::Result<H256> { pub fn code_hash(&self, a: &Address) -> TrieResult<H256> {
self.ensure_cached(a, RequireCache::None, true, self.ensure_cached(a, RequireCache::None, true,
|a| a.as_ref().map_or(KECCAK_EMPTY, |a| a.code_hash())) |a| a.as_ref().map_or(KECCAK_EMPTY, |a| a.code_hash()))
} }
/// Get accounts' code size. /// Get accounts' code size.
pub fn code_size(&self, a: &Address) -> trie::Result<Option<usize>> { pub fn code_size(&self, a: &Address) -> TrieResult<Option<usize>> {
self.ensure_cached(a, RequireCache::CodeSize, true, self.ensure_cached(a, RequireCache::CodeSize, true,
|a| a.as_ref().and_then(|a| a.code_size())) |a| a.as_ref().and_then(|a| a.code_size()))
} }
/// Add `incr` to the balance of account `a`. /// Add `incr` to the balance of account `a`.
pub fn add_balance(&mut self, a: &Address, incr: &U256, cleanup_mode: CleanupMode) -> trie::Result<()> { pub fn add_balance(&mut self, a: &Address, incr: &U256, cleanup_mode: CleanupMode) -> TrieResult<()> {
trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a)?); trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a)?);
let is_value_transfer = !incr.is_zero(); let is_value_transfer = !incr.is_zero();
if is_value_transfer || (cleanup_mode == CleanupMode::ForceCreate && !self.exists(a)?) { if is_value_transfer || (cleanup_mode == CleanupMode::ForceCreate && !self.exists(a)?) {
@ -635,7 +635,7 @@ impl<B: Backend> State<B> {
} }
/// Subtract `decr` from the balance of account `a`. /// Subtract `decr` from the balance of account `a`.
pub fn sub_balance(&mut self, a: &Address, decr: &U256, cleanup_mode: &mut CleanupMode) -> trie::Result<()> { pub fn sub_balance(&mut self, a: &Address, decr: &U256, cleanup_mode: &mut CleanupMode) -> TrieResult<()> {
trace!(target: "state", "sub_balance({}, {}): {}", a, decr, self.balance(a)?); trace!(target: "state", "sub_balance({}, {}): {}", a, decr, self.balance(a)?);
if !decr.is_zero() || !self.exists(a)? { if !decr.is_zero() || !self.exists(a)? {
self.require(a, false)?.sub_balance(decr); self.require(a, false)?.sub_balance(decr);
@ -647,19 +647,19 @@ impl<B: Backend> State<B> {
} }
/// Subtracts `by` from the balance of `from` and adds it to that of `to`. /// Subtracts `by` from the balance of `from` and adds it to that of `to`.
pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256, mut cleanup_mode: CleanupMode) -> trie::Result<()> { pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256, mut cleanup_mode: CleanupMode) -> TrieResult<()> {
self.sub_balance(from, by, &mut cleanup_mode)?; self.sub_balance(from, by, &mut cleanup_mode)?;
self.add_balance(to, by, cleanup_mode)?; self.add_balance(to, by, cleanup_mode)?;
Ok(()) Ok(())
} }
/// Increment the nonce of account `a` by 1. /// Increment the nonce of account `a` by 1.
pub fn inc_nonce(&mut self, a: &Address) -> trie::Result<()> { pub fn inc_nonce(&mut self, a: &Address) -> TrieResult<()> {
self.require(a, false).map(|mut x| x.inc_nonce()) self.require(a, false).map(|mut x| x.inc_nonce())
} }
/// Mutate storage of account `a` so that it is `value` for `key`. /// Mutate storage of account `a` so that it is `value` for `key`.
pub fn set_storage(&mut self, a: &Address, key: H256, value: H256) -> trie::Result<()> { pub fn set_storage(&mut self, a: &Address, key: H256, value: H256) -> TrieResult<()> {
trace!(target: "state", "set_storage({}:{:x} to {:x})", a, key, value); trace!(target: "state", "set_storage({}:{:x} to {:x})", a, key, value);
if self.storage_at(a, &key)? != value { if self.storage_at(a, &key)? != value {
self.require(a, false)?.set_storage(key, value) self.require(a, false)?.set_storage(key, value)
@ -670,13 +670,13 @@ impl<B: Backend> State<B> {
/// Initialise the code of account `a` so that it is `code`. /// Initialise the code of account `a` so that it is `code`.
/// NOTE: Account should have been created with `new_contract`. /// NOTE: Account should have been created with `new_contract`.
pub fn init_code(&mut self, a: &Address, code: Bytes) -> trie::Result<()> { pub fn init_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> {
self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce), |_|{})?.init_code(code); self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce), |_|{})?.init_code(code);
Ok(()) Ok(())
} }
/// Reset the code of account `a` so that it is `code`. /// Reset the code of account `a` so that it is `code`.
pub fn reset_code(&mut self, a: &Address, code: Bytes) -> trie::Result<()> { pub fn reset_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> {
self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce), |_|{})?.reset_code(code); self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce), |_|{})?.reset_code(code);
Ok(()) Ok(())
} }
@ -753,7 +753,7 @@ impl<B: Backend> State<B> {
} }
} }
fn touch(&mut self, a: &Address) -> trie::Result<()> { fn touch(&mut self, a: &Address) -> TrieResult<()> {
self.require(a, false)?; self.require(a, false)?;
Ok(()) Ok(())
} }
@ -809,7 +809,7 @@ impl<B: Backend> State<B> {
} }
/// Remove any touched empty or dust accounts. /// Remove any touched empty or dust accounts.
pub fn kill_garbage(&mut self, touched: &HashSet<Address>, remove_empty_touched: bool, min_balance: &Option<U256>, kill_contracts: bool) -> trie::Result<()> { pub fn kill_garbage(&mut self, touched: &HashSet<Address>, remove_empty_touched: bool, min_balance: &Option<U256>, kill_contracts: bool) -> TrieResult<()> {
let to_kill: HashSet<_> = { let to_kill: HashSet<_> = {
self.cache.borrow().iter().filter_map(|(address, ref entry)| self.cache.borrow().iter().filter_map(|(address, ref entry)|
if touched.contains(address) && // Check all touched accounts if touched.contains(address) && // Check all touched accounts
@ -850,7 +850,7 @@ impl<B: Backend> State<B> {
} }
/// Populate a PodAccount map from this state, with another state as the account and storage query. /// Populate a PodAccount map from this state, with another state as the account and storage query.
pub fn to_pod_diff<X: Backend>(&mut self, query: &State<X>) -> trie::Result<PodState> { pub fn to_pod_diff<X: Backend>(&mut self, query: &State<X>) -> TrieResult<PodState> {
assert!(self.checkpoints.borrow().is_empty()); assert!(self.checkpoints.borrow().is_empty());
// Merge PodAccount::to_pod for cache of self and `query`. // Merge PodAccount::to_pod for cache of self and `query`.
@ -858,7 +858,7 @@ impl<B: Backend> State<B> {
.chain(query.cache.borrow().keys().cloned()) .chain(query.cache.borrow().keys().cloned())
.collect::<BTreeSet<_>>(); .collect::<BTreeSet<_>>();
Ok(PodState::from(all_addresses.into_iter().fold(Ok(BTreeMap::new()), |m: trie::Result<_>, address| { Ok(PodState::from(all_addresses.into_iter().fold(Ok(BTreeMap::new()), |m: TrieResult<_>, address| {
let mut m = m?; let mut m = m?;
let account = self.ensure_cached(&address, RequireCache::Code, true, |acc| { let account = self.ensure_cached(&address, RequireCache::Code, true, |acc| {
@ -886,7 +886,7 @@ impl<B: Backend> State<B> {
})?; })?;
if let Some((balance, nonce, storage_keys, code)) = account { if let Some((balance, nonce, storage_keys, code)) = account {
let storage = storage_keys.into_iter().fold(Ok(BTreeMap::new()), |s: trie::Result<_>, key| { let storage = storage_keys.into_iter().fold(Ok(BTreeMap::new()), |s: TrieResult<_>, key| {
let mut s = s?; let mut s = s?;
s.insert(key, self.storage_at(&address, &key)?); s.insert(key, self.storage_at(&address, &key)?);
@ -904,14 +904,14 @@ impl<B: Backend> State<B> {
/// Returns a `StateDiff` describing the difference from `orig` to `self`. /// Returns a `StateDiff` describing the difference from `orig` to `self`.
/// Consumes self. /// Consumes self.
pub fn diff_from<X: Backend>(&self, mut orig: State<X>) -> trie::Result<StateDiff> { pub fn diff_from<X: Backend>(&self, mut orig: State<X>) -> TrieResult<StateDiff> {
let pod_state_post = self.to_pod(); let pod_state_post = self.to_pod();
let pod_state_pre = orig.to_pod_diff(self)?; let pod_state_pre = orig.to_pod_diff(self)?;
Ok(pod_state::diff_pod(&pod_state_pre, &pod_state_post)) Ok(pod_state::diff_pod(&pod_state_pre, &pod_state_post))
} }
// load required account data from the databases. // load required account data from the databases.
fn update_account_cache(require: RequireCache, account: &mut Account, state_db: &B, db: &HashDB) { fn update_account_cache(require: RequireCache, account: &mut Account, state_db: &B, db: &HashDB<KeccakHasher>) {
if let RequireCache::None = require { if let RequireCache::None = require {
return; return;
} }
@ -943,7 +943,7 @@ impl<B: Backend> State<B> {
/// Check caches for required data /// Check caches for required data
/// First searches for account in the local, then the shared cache. /// First searches for account in the local, then the shared cache.
/// Populates local cache if nothing found. /// Populates local cache if nothing found.
fn ensure_cached<F, U>(&self, a: &Address, require: RequireCache, check_null: bool, f: F) -> trie::Result<U> fn ensure_cached<F, U>(&self, a: &Address, require: RequireCache, check_null: bool, f: F) -> TrieResult<U>
where F: Fn(Option<&Account>) -> U { where F: Fn(Option<&Account>) -> U {
// check local cache first // check local cache first
if let Some(ref mut maybe_acc) = self.cache.borrow_mut().get_mut(a) { if let Some(ref mut maybe_acc) = self.cache.borrow_mut().get_mut(a) {
@ -984,13 +984,13 @@ impl<B: Backend> State<B> {
} }
/// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too.
fn require<'a>(&'a self, a: &Address, require_code: bool) -> trie::Result<RefMut<'a, Account>> { fn require<'a>(&'a self, a: &Address, require_code: bool) -> TrieResult<RefMut<'a, Account>> {
self.require_or_from(a, require_code, || Account::new_basic(0u8.into(), self.account_start_nonce), |_|{}) self.require_or_from(a, require_code, || Account::new_basic(0u8.into(), self.account_start_nonce), |_|{})
} }
/// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too.
/// If it doesn't exist, make account equal the evaluation of `default`. /// If it doesn't exist, make account equal the evaluation of `default`.
fn require_or_from<'a, F, G>(&'a self, a: &Address, require_code: bool, default: F, not_default: G) -> trie::Result<RefMut<'a, Account>> fn require_or_from<'a, F, G>(&'a self, a: &Address, require_code: bool, default: F, not_default: G) -> TrieResult<RefMut<'a, Account>>
where F: FnOnce() -> Account, G: FnOnce(&mut Account), where F: FnOnce() -> Account, G: FnOnce(&mut Account),
{ {
let contains_key = self.cache.borrow().contains_key(a); let contains_key = self.cache.borrow().contains_key(a);
@ -1037,7 +1037,7 @@ impl<B: Backend> State<B> {
} }
/// Replace account code and storage. Creates account if it does not exist. /// Replace account code and storage. Creates account if it does not exist.
pub fn patch_account(&self, a: &Address, code: Arc<Bytes>, storage: HashMap<H256, H256>) -> trie::Result<()> { pub fn patch_account(&self, a: &Address, code: Arc<Bytes>, storage: HashMap<H256, H256>) -> TrieResult<()> {
Ok(self.require(a, false)?.reset_code_and_storage(code, storage)) Ok(self.require(a, false)?.reset_code_and_storage(code, storage))
} }
} }
@ -1049,7 +1049,7 @@ impl<B: Backend> State<B> {
/// If the account doesn't exist in the trie, prove that and return defaults. /// If the account doesn't exist in the trie, prove that and return defaults.
/// Requires a secure trie to be used for accurate results. /// Requires a secure trie to be used for accurate results.
/// `account_key` == keccak(address) /// `account_key` == keccak(address)
pub fn prove_account(&self, account_key: H256) -> trie::Result<(Vec<Bytes>, BasicAccount)> { pub fn prove_account(&self, account_key: H256) -> TrieResult<(Vec<Bytes>, BasicAccount)> {
let mut recorder = Recorder::new(); let mut recorder = Recorder::new();
let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?; let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?;
let maybe_account: Option<BasicAccount> = { let maybe_account: Option<BasicAccount> = {
@ -1074,7 +1074,7 @@ impl<B: Backend> State<B> {
/// Requires a secure trie to be used for correctness. /// Requires a secure trie to be used for correctness.
/// `account_key` == keccak(address) /// `account_key` == keccak(address)
/// `storage_key` == keccak(key) /// `storage_key` == keccak(key)
pub fn prove_storage(&self, account_key: H256, storage_key: H256) -> trie::Result<(Vec<Bytes>, H256)> { pub fn prove_storage(&self, account_key: H256, storage_key: H256) -> TrieResult<(Vec<Bytes>, H256)> {
// TODO: probably could look into cache somehow but it's keyed by // TODO: probably could look into cache somehow but it's keyed by
// address, not keccak(address). // address, not keccak(address).
let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?; let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?;

View File

@ -16,22 +16,23 @@
//! State database abstraction. For more info, see the doc for `StateDB` //! State database abstraction. For more info, see the doc for `StateDB`
use std::collections::{VecDeque, HashSet}; use bloom_journal::{Bloom, BloomJournal};
use std::sync::Arc; use byteorder::{LittleEndian, ByteOrder};
use lru_cache::LruCache; use db::COL_ACCOUNT_BLOOM;
use memory_cache::MemoryLruCache; use ethereum_types::{H256, Address};
use hash::keccak;
use hashdb::HashDB;
use keccak_hasher::KeccakHasher;
use header::BlockNumber;
use journaldb::JournalDB; use journaldb::JournalDB;
use kvdb::{KeyValueDB, DBTransaction}; use kvdb::{KeyValueDB, DBTransaction};
use ethereum_types::{H256, Address}; use lru_cache::LruCache;
use hashdb::HashDB; use memory_cache::MemoryLruCache;
use state::{self, Account};
use header::BlockNumber;
use hash::keccak;
use parking_lot::Mutex; use parking_lot::Mutex;
use state::{self, Account};
use std::collections::{VecDeque, HashSet};
use std::sync::Arc;
use util_error::UtilError; use util_error::UtilError;
use bloom_journal::{Bloom, BloomJournal};
use db::COL_ACCOUNT_BLOOM;
use byteorder::{LittleEndian, ByteOrder};
/// Value used to initialize bloom bitmap size. /// Value used to initialize bloom bitmap size.
/// ///
@ -310,12 +311,12 @@ impl StateDB {
} }
/// Conversion method to interpret self as `HashDB` reference /// Conversion method to interpret self as `HashDB` reference
pub fn as_hashdb(&self) -> &HashDB { pub fn as_hashdb(&self) -> &HashDB<KeccakHasher> {
self.db.as_hashdb() self.db.as_hashdb()
} }
/// Conversion method to interpret self as mutable `HashDB` reference /// Conversion method to interpret self as mutable `HashDB` reference
pub fn as_hashdb_mut(&mut self) -> &mut HashDB { pub fn as_hashdb_mut(&mut self) -> &mut HashDB<KeccakHasher> {
self.db.as_hashdb_mut() self.db.as_hashdb_mut()
} }
@ -410,11 +411,9 @@ impl StateDB {
} }
impl state::Backend for StateDB { impl state::Backend for StateDB {
fn as_hashdb(&self) -> &HashDB { fn as_hashdb(&self) -> &HashDB<KeccakHasher> { self.db.as_hashdb() }
self.db.as_hashdb()
}
fn as_hashdb_mut(&mut self) -> &mut HashDB { fn as_hashdb_mut(&mut self) -> &mut HashDB<KeccakHasher> {
self.db.as_hashdb_mut() self.db.as_hashdb_mut()
} }

View File

@ -16,10 +16,12 @@ ethcore-light = { path = "../light" }
ethcore-transaction = { path = "../transaction" } ethcore-transaction = { path = "../transaction" }
ethcore = { path = ".." } ethcore = { path = ".." }
ethereum-types = "0.3" ethereum-types = "0.3"
plain_hasher = { path = "../../util/plain_hasher" } hashdb = { version = "0.2", path = "../../util/hashdb" }
plain_hasher = { version = "0.2", path = "../../util/plain_hasher" }
rlp = { path = "../../util/rlp" } rlp = { path = "../../util/rlp" }
rustc-hex = "1.0" rustc-hex = "1.0"
keccak-hash = { path = "../../util/hash" } keccak-hash = { path = "../../util/hash" }
keccak-hasher = { path = "../../util/keccak-hasher" }
triehash = { path = "../../util/triehash" } triehash = { path = "../../util/triehash" }
kvdb = { path = "../../util/kvdb" } kvdb = { path = "../../util/kvdb" }
macros = { path = "../../util/macros" } macros = { path = "../../util/macros" }

View File

@ -22,6 +22,7 @@ use network::{self, PeerId};
use parking_lot::RwLock; use parking_lot::RwLock;
use rlp::{Rlp, RlpStream}; use rlp::{Rlp, RlpStream};
use std::cmp; use std::cmp;
use sync_io::SyncIo; use sync_io::SyncIo;
use super::{ use super::{

View File

@ -30,6 +30,7 @@ extern crate ethcore_transaction as transaction;
extern crate ethcore; extern crate ethcore;
extern crate ethereum_types; extern crate ethereum_types;
extern crate env_logger; extern crate env_logger;
extern crate hashdb;
extern crate plain_hasher; extern crate plain_hasher;
extern crate rand; extern crate rand;
extern crate semver; extern crate semver;
@ -38,6 +39,7 @@ extern crate smallvec;
extern crate rlp; extern crate rlp;
extern crate ipnetwork; extern crate ipnetwork;
extern crate keccak_hash as hash; extern crate keccak_hash as hash;
extern crate keccak_hasher;
extern crate triehash; extern crate triehash;
extern crate kvdb; extern crate kvdb;

View File

@ -8,6 +8,7 @@ byteorder = "1.0"
ethcore-bytes = { path = "../../util/bytes" } ethcore-bytes = { path = "../../util/bytes" }
ethereum-types = "0.3" ethereum-types = "0.3"
patricia-trie = { path = "../../util/patricia_trie" } patricia-trie = { path = "../../util/patricia_trie" }
patricia-trie-ethereum = { path = "../../util/patricia-trie-ethereum" }
log = "0.3" log = "0.3"
common-types = { path = "../types" } common-types = { path = "../types" }
ethjson = { path = "../../json" } ethjson = { path = "../../json" }

View File

@ -16,8 +16,8 @@
//! VM errors module //! VM errors module
use trie;
use std::fmt; use std::fmt;
use ethtrie;
/// VM errors. /// VM errors.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -71,8 +71,13 @@ pub enum Error {
Reverted, Reverted,
} }
impl From<Box<trie::TrieError>> for Error { impl From<Box<ethtrie::TrieError>> for Error {
fn from(err: Box<trie::TrieError>) -> Self { fn from(err: Box<ethtrie::TrieError>) -> Self {
Error::Internal(format!("Internal error: {}", err))
}
}
impl From<ethtrie::TrieError> for Error {
fn from(err: ethtrie::TrieError) -> Self {
Error::Internal(format!("Internal error: {}", err)) Error::Internal(format!("Internal error: {}", err))
} }
} }

View File

@ -22,6 +22,7 @@ extern crate common_types as types;
extern crate ethjson; extern crate ethjson;
extern crate rlp; extern crate rlp;
extern crate keccak_hash as hash; extern crate keccak_hash as hash;
extern crate patricia_trie_ethereum as ethtrie;
extern crate patricia_trie as trie; extern crate patricia_trie as trie;
mod action_params; mod action_params;

View File

@ -1,10 +1,10 @@
[package] [package]
name = "hashdb" name = "hashdb"
version = "0.1.1" version = "0.2.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
description = "trait for hash-keyed databases." description = "trait for hash-keyed databases."
license = "GPL-3.0" license = "GPL-3.0"
[dependencies] [dependencies]
elastic-array = "0.10" elastic-array = "0.10"
ethereum-types = "0.3" heapsize = "0.4"

View File

@ -14,65 +14,70 @@
// 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/>.
//! Database of byte-slices keyed to their Keccak hash. //! Database of byte-slices keyed to their hash.
extern crate elastic_array; extern crate elastic_array;
extern crate ethereum_types; extern crate heapsize;
use std::collections::HashMap;
use elastic_array::ElasticArray128; use elastic_array::ElasticArray128;
use ethereum_types::H256; use heapsize::HeapSizeOf;
use std::collections::HashMap;
use std::{fmt::Debug, hash::Hash};
/// Trait describing an object that can hash a slice of bytes. Used to abstract
/// other types over the hashing algorithm. Defines a single `hash` method and an
/// `Out` associated type with the necessary bounds.
pub trait Hasher: Sync + Send {
/// The output type of the `Hasher`
type Out: AsRef<[u8]> + AsMut<[u8]> + Default + HeapSizeOf + Debug + PartialEq + Eq + Hash + Send + Sync + Clone + Copy;
/// What to use to build `HashMap`s with this `Hasher`
type StdHasher: Sync + Send + Default + std::hash::Hasher;
/// The length in bytes of the `Hasher` output
const LENGTH: usize;
/// Compute the hash of the provided slice of bytes returning the `Out` type of the `Hasher`
fn hash(x: &[u8]) -> Self::Out;
}
/// `HashDB` value type. /// `HashDB` value type.
pub type DBValue = ElasticArray128<u8>; pub type DBValue = ElasticArray128<u8>;
/// Trait modelling datastore keyed by a 32-byte Keccak hash. /// Trait modelling datastore keyed by a hash defined by the `Hasher`.
pub trait HashDB: AsHashDB + Send + Sync { pub trait HashDB<H: Hasher>: Send + Sync + AsHashDB<H> {
/// Get the keys in the database together with number of underlying references. /// Get the keys in the database together with number of underlying references.
fn keys(&self) -> HashMap<H256, i32>; fn keys(&self) -> HashMap<H::Out, i32>;
/// Look up a given hash into the bytes that hash to it, returning None if the /// Look up a given hash into the bytes that hash to it, returning None if the
/// hash is not known. /// hash is not known.
fn get(&self, key: &H256) -> Option<DBValue>; fn get(&self, key: &H::Out) -> Option<DBValue>;
/// Check for the existance of a hash-key. /// Check for the existance of a hash-key.
fn contains(&self, key: &H256) -> bool; fn contains(&self, key: &H::Out) -> bool;
/// Insert a datum item into the DB and return the datum's hash for a later lookup. Insertions /// Insert a datum item into the DB and return the datum's hash for a later lookup. Insertions
/// are counted and the equivalent number of `remove()`s must be performed before the data /// are counted and the equivalent number of `remove()`s must be performed before the data
/// is considered dead. /// is considered dead.
fn insert(&mut self, value: &[u8]) -> H256; fn insert(&mut self, value: &[u8]) -> H::Out;
/// Like `insert()` , except you provide the key and the data is all moved. /// Like `insert()`, except you provide the key and the data is all moved.
fn emplace(&mut self, key: H256, value: DBValue); fn emplace(&mut self, key: H::Out, value: DBValue);
/// Remove a datum previously inserted. Insertions can be "owed" such that the same number of `insert()`s may /// Remove a datum previously inserted. Insertions can be "owed" such that the same number of `insert()`s may
/// happen without the data being eventually being inserted into the DB. It can be "owed" more than once. /// happen without the data being eventually being inserted into the DB. It can be "owed" more than once.
fn remove(&mut self, key: &H256); fn remove(&mut self, key: &H::Out);
} }
/// Upcast trait. /// Upcast trait.
pub trait AsHashDB { pub trait AsHashDB<H: Hasher> {
/// Perform upcast to HashDB for anything that derives from HashDB. /// Perform upcast to HashDB for anything that derives from HashDB.
fn as_hashdb(&self) -> &HashDB; fn as_hashdb(&self) -> &HashDB<H>;
/// Perform mutable upcast to HashDB for anything that derives from HashDB. /// Perform mutable upcast to HashDB for anything that derives from HashDB.
fn as_hashdb_mut(&mut self) -> &mut HashDB; fn as_hashdb_mut(&mut self) -> &mut HashDB<H>;
} }
impl<T: HashDB> AsHashDB for T { // NOTE: There used to be a `impl<T> AsHashDB for T` but that does not work with generics. See https://stackoverflow.com/questions/48432842/implementing-a-trait-for-reference-and-non-reference-types-causes-conflicting-im
fn as_hashdb(&self) -> &HashDB { // This means we need concrete impls of AsHashDB in several places, which somewhat defeats the point of the trait.
self impl<'a, H: Hasher> AsHashDB<H> for &'a mut HashDB<H> {
} fn as_hashdb(&self) -> &HashDB<H> { &**self }
fn as_hashdb_mut(&mut self) -> &mut HashDB { fn as_hashdb_mut(&mut self) -> &mut HashDB<H> { &mut **self }
self
}
} }
impl<'a> AsHashDB for &'a mut HashDB {
fn as_hashdb(&self) -> &HashDB {
&**self
}
fn as_hashdb_mut(&mut self) -> &mut HashDB {
&mut **self
}
}

View File

@ -1,6 +1,6 @@
[package] [package]
name = "journaldb" name = "journaldb"
version = "0.1.0" version = "0.2.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
description = "A `HashDB` which can manage a short-term journal potentially containing many forks of mutually exclusive actions" description = "A `HashDB` which can manage a short-term journal potentially containing many forks of mutually exclusive actions"
license = "GPL3" license = "GPL3"
@ -8,11 +8,12 @@ license = "GPL3"
[dependencies] [dependencies]
ethcore-bytes = { path = "../bytes" } ethcore-bytes = { path = "../bytes" }
ethereum-types = "0.3" ethereum-types = "0.3"
hashdb = { path = "../hashdb" } hashdb = { version = "0.2.0", path = "../hashdb" }
heapsize = "0.4" heapsize = "0.4"
keccak-hasher = { path = "../keccak-hasher" }
kvdb = { path = "../kvdb" } kvdb = { path = "../kvdb" }
log = "0.3" log = "0.3"
memorydb = { path = "../memorydb" } memorydb = { version="0.2.0", path = "../memorydb" }
parking_lot = "0.5" parking_lot = "0.5"
plain_hasher = { path = "../plain_hasher" } plain_hasher = { path = "../plain_hasher" }
rlp = { path = "../rlp" } rlp = { path = "../rlp" }

View File

@ -19,15 +19,17 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use std::sync::Arc; use std::sync::Arc;
use rlp::{encode, decode};
use hashdb::*;
use super::memorydb::*;
use super::{DB_PREFIX_LEN, LATEST_ERA_KEY};
use traits::JournalDB;
use kvdb::{KeyValueDB, DBTransaction};
use ethereum_types::H256;
use error::{BaseDataError, UtilError};
use bytes::Bytes; use bytes::Bytes;
use error::{BaseDataError, UtilError};
use ethereum_types::H256;
use hashdb::*;
use keccak_hasher::KeccakHasher;
use kvdb::{KeyValueDB, DBTransaction};
use rlp::{encode, decode};
use super::{DB_PREFIX_LEN, LATEST_ERA_KEY};
use super::memorydb::*;
use traits::JournalDB;
/// Implementation of the `HashDB` trait for a disk-backed database with a memory overlay /// Implementation of the `HashDB` trait for a disk-backed database with a memory overlay
/// and latent-removal semantics. /// and latent-removal semantics.
@ -37,7 +39,7 @@ use bytes::Bytes;
/// immediately. As this is an "archive" database, nothing is ever removed. This means /// immediately. As this is an "archive" database, nothing is ever removed. This means
/// that the states of any block the node has ever processed will be accessible. /// that the states of any block the node has ever processed will be accessible.
pub struct ArchiveDB { pub struct ArchiveDB {
overlay: MemoryDB, overlay: MemoryDB<KeccakHasher>,
backing: Arc<KeyValueDB>, backing: Arc<KeyValueDB>,
latest_era: Option<u64>, latest_era: Option<u64>,
column: Option<u32>, column: Option<u32>,
@ -62,7 +64,7 @@ impl ArchiveDB {
} }
} }
impl HashDB for ArchiveDB { impl HashDB<KeccakHasher> for ArchiveDB {
fn keys(&self) -> HashMap<H256, i32> { fn keys(&self) -> HashMap<H256, i32> {
let mut ret: HashMap<H256, i32> = self.backing.iter(self.column) let mut ret: HashMap<H256, i32> = self.backing.iter(self.column)
.map(|(key, _)| (H256::from_slice(&*key), 1)) .map(|(key, _)| (H256::from_slice(&*key), 1))
@ -191,7 +193,7 @@ impl JournalDB for ArchiveDB {
&self.backing &self.backing
} }
fn consolidate(&mut self, with: MemoryDB) { fn consolidate(&mut self, with: MemoryDB<KeccakHasher>) {
self.overlay.consolidate(with); self.overlay.consolidate(with);
} }
} }

View File

@ -0,0 +1,49 @@
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Impls of the `AsHashDB` upcast trait for all different variants of DB
use hashdb::{HashDB, AsHashDB};
use keccak_hasher::KeccakHasher;
use archivedb::ArchiveDB;
use earlymergedb::EarlyMergeDB;
use overlayrecentdb::OverlayRecentDB;
use refcounteddb::RefCountedDB;
use overlaydb::OverlayDB;
impl AsHashDB<KeccakHasher> for ArchiveDB {
fn as_hashdb(&self) -> &HashDB<KeccakHasher> { self }
fn as_hashdb_mut(&mut self) -> &mut HashDB<KeccakHasher> { self }
}
impl AsHashDB<KeccakHasher> for EarlyMergeDB {
fn as_hashdb(&self) -> &HashDB<KeccakHasher> { self }
fn as_hashdb_mut(&mut self) -> &mut HashDB<KeccakHasher> { self }
}
impl AsHashDB<KeccakHasher> for OverlayRecentDB {
fn as_hashdb(&self) -> &HashDB<KeccakHasher> { self }
fn as_hashdb_mut(&mut self) -> &mut HashDB<KeccakHasher> { self }
}
impl AsHashDB<KeccakHasher> for RefCountedDB {
fn as_hashdb(&self) -> &HashDB<KeccakHasher> { self }
fn as_hashdb_mut(&mut self) -> &mut HashDB<KeccakHasher> { self }
}
impl AsHashDB<KeccakHasher> for OverlayDB {
fn as_hashdb(&self) -> &HashDB<KeccakHasher> { self }
fn as_hashdb_mut(&mut self) -> &mut HashDB<KeccakHasher> { self }
}

View File

@ -19,17 +19,19 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use std::sync::Arc; use std::sync::Arc;
use parking_lot::RwLock;
use heapsize::HeapSizeOf; use bytes::Bytes;
use rlp::{encode, decode}; use error::{BaseDataError, UtilError};
use ethereum_types::H256;
use hashdb::*; use hashdb::*;
use heapsize::HeapSizeOf;
use keccak_hasher::KeccakHasher;
use kvdb::{KeyValueDB, DBTransaction};
use memorydb::*; use memorydb::*;
use parking_lot::RwLock;
use rlp::{encode, decode};
use super::{DB_PREFIX_LEN, LATEST_ERA_KEY}; use super::{DB_PREFIX_LEN, LATEST_ERA_KEY};
use super::traits::JournalDB; use super::traits::JournalDB;
use kvdb::{KeyValueDB, DBTransaction};
use ethereum_types::H256;
use error::{BaseDataError, UtilError};
use bytes::Bytes;
use util::{DatabaseKey, DatabaseValueView, DatabaseValueRef}; use util::{DatabaseKey, DatabaseValueView, DatabaseValueRef};
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -105,7 +107,7 @@ enum RemoveFrom {
/// ///
/// TODO: `store_reclaim_period` /// TODO: `store_reclaim_period`
pub struct EarlyMergeDB { pub struct EarlyMergeDB {
overlay: MemoryDB, overlay: MemoryDB<KeccakHasher>,
backing: Arc<KeyValueDB>, backing: Arc<KeyValueDB>,
refs: Option<Arc<RwLock<HashMap<H256, RefInfo>>>>, refs: Option<Arc<RwLock<HashMap<H256, RefInfo>>>>,
latest_era: Option<u64>, latest_era: Option<u64>,
@ -285,7 +287,7 @@ impl EarlyMergeDB {
} }
} }
impl HashDB for EarlyMergeDB { impl HashDB<KeccakHasher> for EarlyMergeDB {
fn keys(&self) -> HashMap<H256, i32> { fn keys(&self) -> HashMap<H256, i32> {
let mut ret: HashMap<H256, i32> = self.backing.iter(self.column) let mut ret: HashMap<H256, i32> = self.backing.iter(self.column)
.map(|(key, _)| (H256::from_slice(&*key), 1)) .map(|(key, _)| (H256::from_slice(&*key), 1))
@ -512,7 +514,7 @@ impl JournalDB for EarlyMergeDB {
Ok(ops) Ok(ops)
} }
fn consolidate(&mut self, with: MemoryDB) { fn consolidate(&mut self, with: MemoryDB<KeccakHasher>) {
self.overlay.consolidate(with); self.overlay.consolidate(with);
} }
} }

View File

@ -23,6 +23,7 @@ extern crate log;
extern crate ethereum_types; extern crate ethereum_types;
extern crate ethcore_bytes as bytes; extern crate ethcore_bytes as bytes;
extern crate hashdb; extern crate hashdb;
extern crate keccak_hasher;
extern crate kvdb; extern crate kvdb;
extern crate memorydb; extern crate memorydb;
extern crate parking_lot; extern crate parking_lot;
@ -47,6 +48,7 @@ mod earlymergedb;
mod overlayrecentdb; mod overlayrecentdb;
mod refcounteddb; mod refcounteddb;
mod util; mod util;
mod as_hash_db_impls;
pub mod overlaydb; pub mod overlaydb;

View File

@ -23,6 +23,7 @@ use error::{Result, BaseDataError};
use ethereum_types::H256; use ethereum_types::H256;
use rlp::{Rlp, RlpStream, Encodable, DecoderError, Decodable, encode, decode}; use rlp::{Rlp, RlpStream, Encodable, DecoderError, Decodable, encode, decode};
use hashdb::*; use hashdb::*;
use keccak_hasher::KeccakHasher;
use memorydb::*; use memorydb::*;
use kvdb::{KeyValueDB, DBTransaction}; use kvdb::{KeyValueDB, DBTransaction};
@ -36,7 +37,7 @@ use kvdb::{KeyValueDB, DBTransaction};
/// queries have an immediate effect in terms of these functions. /// queries have an immediate effect in terms of these functions.
#[derive(Clone)] #[derive(Clone)]
pub struct OverlayDB { pub struct OverlayDB {
overlay: MemoryDB, overlay: MemoryDB<KeccakHasher>,
backing: Arc<KeyValueDB>, backing: Arc<KeyValueDB>,
column: Option<u32>, column: Option<u32>,
} }
@ -152,7 +153,7 @@ impl OverlayDB {
} }
} }
impl HashDB for OverlayDB { impl HashDB<KeccakHasher> for OverlayDB {
fn keys(&self) -> HashMap<H256, i32> { fn keys(&self) -> HashMap<H256, i32> {
let mut ret: HashMap<H256, i32> = self.backing.iter(self.column) let mut ret: HashMap<H256, i32> = self.backing.iter(self.column)
.map(|(key, _)| { .map(|(key, _)| {

View File

@ -19,18 +19,20 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use std::sync::Arc; use std::sync::Arc;
use parking_lot::RwLock;
use heapsize::HeapSizeOf;
use rlp::{Rlp, RlpStream, encode, decode, DecoderError, Decodable, Encodable};
use hashdb::*;
use memorydb::*;
use super::{DB_PREFIX_LEN, LATEST_ERA_KEY};
use kvdb::{KeyValueDB, DBTransaction};
use super::JournalDB;
use ethereum_types::H256;
use plain_hasher::H256FastMap;
use error::{BaseDataError, UtilError};
use bytes::Bytes; use bytes::Bytes;
use error::{BaseDataError, UtilError};
use ethereum_types::H256;
use hashdb::*;
use heapsize::HeapSizeOf;
use keccak_hasher::KeccakHasher;
use kvdb::{KeyValueDB, DBTransaction};
use memorydb::*;
use parking_lot::RwLock;
use plain_hasher::H256FastMap;
use rlp::{Rlp, RlpStream, encode, decode, DecoderError, Decodable, Encodable};
use super::{DB_PREFIX_LEN, LATEST_ERA_KEY};
use super::JournalDB;
use util::DatabaseKey; use util::DatabaseKey;
/// Implementation of the `JournalDB` trait for a disk-backed database with a memory overlay /// Implementation of the `JournalDB` trait for a disk-backed database with a memory overlay
@ -65,7 +67,7 @@ use util::DatabaseKey;
/// 7. Delete ancient record from memory and disk. /// 7. Delete ancient record from memory and disk.
pub struct OverlayRecentDB { pub struct OverlayRecentDB {
transaction_overlay: MemoryDB, transaction_overlay: MemoryDB<KeccakHasher>,
backing: Arc<KeyValueDB>, backing: Arc<KeyValueDB>,
journal_overlay: Arc<RwLock<JournalOverlay>>, journal_overlay: Arc<RwLock<JournalOverlay>>,
column: Option<u32>, column: Option<u32>,
@ -119,7 +121,7 @@ impl<'a> Encodable for DatabaseValueRef<'a> {
#[derive(PartialEq)] #[derive(PartialEq)]
struct JournalOverlay { struct JournalOverlay {
backing_overlay: MemoryDB, // Nodes added in the history period backing_overlay: MemoryDB<KeccakHasher>, // Nodes added in the history period
pending_overlay: H256FastMap<DBValue>, // Nodes being transfered from backing_overlay to backing db pending_overlay: H256FastMap<DBValue>, // Nodes being transfered from backing_overlay to backing db
journal: HashMap<u64, Vec<JournalEntry>>, journal: HashMap<u64, Vec<JournalEntry>>,
latest_era: Option<u64>, latest_era: Option<u64>,
@ -433,12 +435,12 @@ impl JournalDB for OverlayRecentDB {
Ok(ops) Ok(ops)
} }
fn consolidate(&mut self, with: MemoryDB) { fn consolidate(&mut self, with: MemoryDB<KeccakHasher>) {
self.transaction_overlay.consolidate(with); self.transaction_overlay.consolidate(with);
} }
} }
impl HashDB for OverlayRecentDB { impl HashDB<KeccakHasher> for OverlayRecentDB {
fn keys(&self) -> HashMap<H256, i32> { fn keys(&self) -> HashMap<H256, i32> {
let mut ret: HashMap<H256, i32> = self.backing.iter(self.column) let mut ret: HashMap<H256, i32> = self.backing.iter(self.column)
.map(|(key, _)| (H256::from_slice(&*key), 1)) .map(|(key, _)| (H256::from_slice(&*key), 1))

View File

@ -18,17 +18,19 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use heapsize::HeapSizeOf;
use rlp::{encode, decode}; use bytes::Bytes;
use error::UtilError;
use ethereum_types::H256;
use hashdb::*; use hashdb::*;
use overlaydb::OverlayDB; use heapsize::HeapSizeOf;
use keccak_hasher::KeccakHasher;
use kvdb::{KeyValueDB, DBTransaction};
use memorydb::MemoryDB; use memorydb::MemoryDB;
use overlaydb::OverlayDB;
use rlp::{encode, decode};
use super::{DB_PREFIX_LEN, LATEST_ERA_KEY}; use super::{DB_PREFIX_LEN, LATEST_ERA_KEY};
use super::traits::JournalDB; use super::traits::JournalDB;
use kvdb::{KeyValueDB, DBTransaction};
use ethereum_types::H256;
use error::UtilError;
use bytes::Bytes;
use util::{DatabaseKey, DatabaseValueView, DatabaseValueRef}; use util::{DatabaseKey, DatabaseValueView, DatabaseValueRef};
/// Implementation of the `HashDB` trait for a disk-backed database with a memory overlay /// Implementation of the `HashDB` trait for a disk-backed database with a memory overlay
@ -78,7 +80,7 @@ impl RefCountedDB {
} }
} }
impl HashDB for RefCountedDB { impl HashDB<KeccakHasher> for RefCountedDB {
fn keys(&self) -> HashMap<H256, i32> { self.forward.keys() } fn keys(&self) -> HashMap<H256, i32> { self.forward.keys() }
fn get(&self, key: &H256) -> Option<DBValue> { self.forward.get(key) } fn get(&self, key: &H256) -> Option<DBValue> { self.forward.get(key) }
fn contains(&self, key: &H256) -> bool { self.forward.contains(key) } fn contains(&self, key: &H256) -> bool { self.forward.contains(key) }
@ -197,7 +199,7 @@ impl JournalDB for RefCountedDB {
self.forward.commit_to_batch(batch) self.forward.commit_to_batch(batch)
} }
fn consolidate(&mut self, mut with: MemoryDB) { fn consolidate(&mut self, mut with: MemoryDB<KeccakHasher>) {
for (key, (value, rc)) in with.drain() { for (key, (value, rc)) in with.drain() {
for _ in 0..rc { for _ in 0..rc {
self.emplace(key, value.clone()); self.emplace(key, value.clone());

View File

@ -16,16 +16,17 @@
//! Disk-backed `HashDB` implementation. //! Disk-backed `HashDB` implementation.
use std::sync::Arc;
use hashdb::*;
use kvdb::{self, DBTransaction};
use ethereum_types::H256;
use error::UtilError;
use bytes::Bytes; use bytes::Bytes;
use error::UtilError;
use ethereum_types::H256;
use hashdb::*;
use keccak_hasher::KeccakHasher;
use kvdb::{self, DBTransaction};
use std::sync::Arc;
/// A `HashDB` which can manage a short-term journal potentially containing many forks of mutually /// A `HashDB` which can manage a short-term journal potentially containing many forks of mutually
/// exclusive actions. /// exclusive actions.
pub trait JournalDB: HashDB { pub trait JournalDB: HashDB<KeccakHasher> {
/// Return a copy of ourself, in a box. /// Return a copy of ourself, in a box.
fn boxed_clone(&self) -> Box<JournalDB>; fn boxed_clone(&self) -> Box<JournalDB>;
@ -76,7 +77,7 @@ pub trait JournalDB: HashDB {
fn flush(&self) {} fn flush(&self) {}
/// Consolidate all the insertions and deletions in the given memory overlay. /// Consolidate all the insertions and deletions in the given memory overlay.
fn consolidate(&mut self, overlay: ::memorydb::MemoryDB); fn consolidate(&mut self, overlay: ::memorydb::MemoryDB<KeccakHasher>);
/// Commit all changes in a single batch /// Commit all changes in a single batch
#[cfg(test)] #[cfg(test)]

View File

@ -0,0 +1,12 @@
[package]
name = "keccak-hasher"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
description = "Keccak-256 implementation of the Hasher trait"
license = "GPL-3.0"
[dependencies]
ethereum-types = "0.3"
tiny-keccak = "1.4.2"
hashdb = { path = "../hashdb" }
plain_hasher = { path = "../plain_hasher" }

View File

@ -0,0 +1,39 @@
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Hasher implementation for the Keccak-256 hash
extern crate hashdb;
extern crate ethereum_types;
extern crate tiny_keccak;
extern crate plain_hasher;
use hashdb::Hasher;
use ethereum_types::H256;
use tiny_keccak::Keccak;
use plain_hasher::PlainHasher;
/// Concrete `Hasher` impl for the Keccak-256 hash
#[derive(Default, Debug, Clone, PartialEq)]
pub struct KeccakHasher;
impl Hasher for KeccakHasher {
type Out = H256;
type StdHasher = PlainHasher;
const LENGTH: usize = 32;
fn hash(x: &[u8]) -> Self::Out {
let mut out = [0;32];
Keccak::keccak256(x, &mut out);
out.into()
}
}

View File

@ -1,6 +1,6 @@
[package] [package]
name = "memorydb" name = "memorydb"
version = "0.1.1" version = "0.2.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
description = "in-memory implementation of hashdb" description = "in-memory implementation of hashdb"
license = "GPL-3.0" license = "GPL-3.0"
@ -8,8 +8,12 @@ license = "GPL-3.0"
[dependencies] [dependencies]
elastic-array = "0.10" elastic-array = "0.10"
heapsize = "0.4" heapsize = "0.4"
ethereum-types = "0.3" hashdb = { version = "0.2.0", path = "../hashdb" }
keccak-hash = { version = "0.1.0", path = "../hash" }
hashdb = { version = "0.1.1", path = "../hashdb" }
plain_hasher = { path = "../plain_hasher" } plain_hasher = { path = "../plain_hasher" }
rlp = { version = "0.2.1", path = "../rlp" } rlp = { version = "0.2.1", path = "../rlp" }
[dev-dependencies]
tiny-keccak = "1.4.2"
ethereum-types = "0.3"
keccak-hasher = { path = "../keccak-hasher" }
keccak-hash = { path = "../hash" }

View File

@ -0,0 +1,79 @@
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
#![feature(test)]
extern crate hashdb;
extern crate memorydb;
extern crate keccak_hasher;
extern crate keccak_hash;
extern crate rlp;
extern crate test;
use memorydb::MemoryDB;
use keccak_hasher::KeccakHasher;
use hashdb::{HashDB, Hasher};
use keccak_hash::KECCAK_NULL_RLP;
use rlp::NULL_RLP;
use test::{Bencher, black_box};
#[bench]
fn instantiation(b: &mut Bencher) {
b.iter(|| {
MemoryDB::<KeccakHasher>::new();
})
}
#[bench]
fn compare_to_null_embedded_in_struct(b: &mut Bencher) {
struct X {a_hash: <KeccakHasher as Hasher>::Out};
let x = X {a_hash: KeccakHasher::hash(&NULL_RLP)};
let key = KeccakHasher::hash(b"abc");
b.iter(|| {
black_box(key == x.a_hash);
})
}
#[bench]
fn compare_to_null_in_const(b: &mut Bencher) {
let key = KeccakHasher::hash(b"abc");
b.iter(|| {
black_box(key == KECCAK_NULL_RLP);
})
}
#[bench]
fn contains_with_non_null_key(b: &mut Bencher) {
let mut m = MemoryDB::<KeccakHasher>::new();
let key = KeccakHasher::hash(b"abc");
m.insert(b"abcefghijklmnopqrstuvxyz");
b.iter(|| {
m.contains(&key);
})
}
#[bench]
fn contains_with_null_key(b: &mut Bencher) {
let mut m = MemoryDB::<KeccakHasher>::new();
let null_key = KeccakHasher::hash(&NULL_RLP);
m.insert(b"abcefghijklmnopqrstuvxyz");
b.iter(|| {
m.contains(&null_key);
})
}

View File

@ -15,23 +15,25 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Reference-counted memory-based `HashDB` implementation. //! Reference-counted memory-based `HashDB` implementation.
extern crate heapsize;
extern crate ethereum_types;
extern crate hashdb;
extern crate keccak_hash as keccak;
extern crate plain_hasher;
extern crate rlp;
extern crate elastic_array; extern crate elastic_array;
extern crate hashdb;
extern crate heapsize;
extern crate rlp;
#[cfg(test)] extern crate keccak_hasher;
#[cfg(test)] extern crate tiny_keccak;
#[cfg(test)] extern crate ethereum_types;
use std::mem; use hashdb::{HashDB, Hasher as KeyHasher, DBValue, AsHashDB};
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use heapsize::HeapSizeOf; use heapsize::HeapSizeOf;
use ethereum_types::H256;
use hashdb::{HashDB, DBValue};
use keccak::{KECCAK_NULL_RLP, keccak};
use plain_hasher::H256FastMap;
use rlp::NULL_RLP; use rlp::NULL_RLP;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::hash;
use std::mem;
// Backing `HashMap` parametrized with a `Hasher` for the keys `Hasher::Out` and the `Hasher::StdHasher` as hash map builder.
type FastMap<H, T> = HashMap<<H as KeyHasher>::Out, T, hash::BuildHasherDefault<<H as KeyHasher>::StdHasher>>;
/// Reference-counted memory-based `HashDB` implementation. /// Reference-counted memory-based `HashDB` implementation.
/// ///
/// Use `new()` to create a new database. Insert items with `insert()`, remove items /// Use `new()` to create a new database. Insert items with `insert()`, remove items
@ -42,11 +44,14 @@ use rlp::NULL_RLP;
/// # Example /// # Example
/// ```rust /// ```rust
/// extern crate hashdb; /// extern crate hashdb;
/// extern crate keccak_hasher;
/// extern crate memorydb; /// extern crate memorydb;
///
/// use hashdb::*; /// use hashdb::*;
/// use keccak_hasher::KeccakHasher;
/// use memorydb::*; /// use memorydb::*;
/// fn main() { /// fn main() {
/// let mut m = MemoryDB::new(); /// let mut m = MemoryDB::<KeccakHasher>::new();
/// let d = "Hello world!".as_bytes(); /// let d = "Hello world!".as_bytes();
/// ///
/// let k = m.insert(d); /// let k = m.insert(d);
@ -77,15 +82,17 @@ use rlp::NULL_RLP;
/// } /// }
/// ``` /// ```
#[derive(Default, Clone, PartialEq)] #[derive(Default, Clone, PartialEq)]
pub struct MemoryDB { pub struct MemoryDB<H: KeyHasher> {
data: H256FastMap<(DBValue, i32)>, data: FastMap<H, (DBValue, i32)>,
hashed_null_node: H::Out,
} }
impl MemoryDB { impl<H: KeyHasher> MemoryDB<H> {
/// Create a new instance of the memory DB. /// Create a new instance of the memory DB.
pub fn new() -> MemoryDB { pub fn new() -> MemoryDB<H> {
MemoryDB { MemoryDB {
data: H256FastMap::default(), data: FastMap::<H,_>::default(),
hashed_null_node: H::hash(&NULL_RLP)
} }
} }
@ -94,11 +101,15 @@ impl MemoryDB {
/// # Examples /// # Examples
/// ```rust /// ```rust
/// extern crate hashdb; /// extern crate hashdb;
/// extern crate keccak_hasher;
/// extern crate memorydb; /// extern crate memorydb;
///
/// use hashdb::*; /// use hashdb::*;
/// use keccak_hasher::KeccakHasher;
/// use memorydb::*; /// use memorydb::*;
///
/// fn main() { /// fn main() {
/// let mut m = MemoryDB::new(); /// let mut m = MemoryDB::<KeccakHasher>::new();
/// let hello_bytes = "Hello world!".as_bytes(); /// let hello_bytes = "Hello world!".as_bytes();
/// let hash = m.insert(hello_bytes); /// let hash = m.insert(hello_bytes);
/// assert!(m.contains(&hash)); /// assert!(m.contains(&hash));
@ -116,8 +127,8 @@ impl MemoryDB {
} }
/// Return the internal map of hashes to data, clearing the current state. /// Return the internal map of hashes to data, clearing the current state.
pub fn drain(&mut self) -> H256FastMap<(DBValue, i32)> { pub fn drain(&mut self) -> FastMap<H, (DBValue, i32)> {
mem::replace(&mut self.data, H256FastMap::default()) mem::replace(&mut self.data, FastMap::<H,_>::default())
} }
/// Grab the raw information associated with a key. Returns None if the key /// Grab the raw information associated with a key. Returns None if the key
@ -125,8 +136,8 @@ impl MemoryDB {
/// ///
/// Even when Some is returned, the data is only guaranteed to be useful /// Even when Some is returned, the data is only guaranteed to be useful
/// when the refs > 0. /// when the refs > 0.
pub fn raw(&self, key: &H256) -> Option<(DBValue, i32)> { pub fn raw(&self, key: &<H as KeyHasher>::Out) -> Option<(DBValue, i32)> {
if key == &KECCAK_NULL_RLP { if key == &self.hashed_null_node {
return Some((DBValue::from_slice(&NULL_RLP), 1)); return Some((DBValue::from_slice(&NULL_RLP), 1));
} }
self.data.get(key).cloned() self.data.get(key).cloned()
@ -139,8 +150,8 @@ impl MemoryDB {
/// Remove an element and delete it from storage if reference count reaches zero. /// Remove an element and delete it from storage if reference count reaches zero.
/// If the value was purged, return the old value. /// If the value was purged, return the old value.
pub fn remove_and_purge(&mut self, key: &H256) -> Option<DBValue> { pub fn remove_and_purge(&mut self, key: &<H as KeyHasher>::Out) -> Option<DBValue> {
if key == &KECCAK_NULL_RLP { if key == &self.hashed_null_node {
return None; return None;
} }
match self.data.entry(key.clone()) { match self.data.entry(key.clone()) {
@ -177,19 +188,9 @@ impl MemoryDB {
} }
} }
impl HashDB for MemoryDB { impl<H: KeyHasher> HashDB<H> for MemoryDB<H> {
fn get(&self, key: &H256) -> Option<DBValue> {
if key == &KECCAK_NULL_RLP {
return Some(DBValue::from_slice(&NULL_RLP));
}
match self.data.get(key) { fn keys(&self) -> HashMap<H::Out, i32> {
Some(&(ref d, rc)) if rc > 0 => Some(d.clone()),
_ => None
}
}
fn keys(&self) -> HashMap<H256, i32> {
self.data.iter() self.data.iter()
.filter_map(|(k, v)| if v.1 != 0 { .filter_map(|(k, v)| if v.1 != 0 {
Some((*k, v.1)) Some((*k, v.1))
@ -199,8 +200,19 @@ impl HashDB for MemoryDB {
.collect() .collect()
} }
fn contains(&self, key: &H256) -> bool { fn get(&self, key: &H::Out) -> Option<DBValue> {
if key == &KECCAK_NULL_RLP { if key == &self.hashed_null_node {
return Some(DBValue::from_slice(&NULL_RLP));
}
match self.data.get(key) {
Some(&(ref d, rc)) if rc > 0 => Some(d.clone()),
_ => None
}
}
fn contains(&self, key: &H::Out) -> bool {
if key == &self.hashed_null_node {
return true; return true;
} }
@ -210,15 +222,15 @@ impl HashDB for MemoryDB {
} }
} }
fn insert(&mut self, value: &[u8]) -> H256 { fn insert(&mut self, value: &[u8]) -> H::Out {
if value == &NULL_RLP { if value == &NULL_RLP {
return KECCAK_NULL_RLP.clone(); return self.hashed_null_node.clone();
} }
let key = keccak(value); let key = H::hash(value);
match self.data.entry(key) { match self.data.entry(key) {
Entry::Occupied(mut entry) => { Entry::Occupied(mut entry) => {
let &mut (ref mut old_value, ref mut rc) = entry.get_mut(); let &mut (ref mut old_value, ref mut rc) = entry.get_mut();
if *rc >= -0x80000000i32 && *rc <= 0 { if *rc <= 0 {
*old_value = DBValue::from_slice(value); *old_value = DBValue::from_slice(value);
} }
*rc += 1; *rc += 1;
@ -230,7 +242,7 @@ impl HashDB for MemoryDB {
key key
} }
fn emplace(&mut self, key: H256, value: DBValue) { fn emplace(&mut self, key:H::Out, value: DBValue) {
if &*value == &NULL_RLP { if &*value == &NULL_RLP {
return; return;
} }
@ -238,7 +250,7 @@ impl HashDB for MemoryDB {
match self.data.entry(key) { match self.data.entry(key) {
Entry::Occupied(mut entry) => { Entry::Occupied(mut entry) => {
let &mut (ref mut old_value, ref mut rc) = entry.get_mut(); let &mut (ref mut old_value, ref mut rc) = entry.get_mut();
if *rc >= -0x80000000i32 && *rc <= 0 { if *rc <= 0 {
*old_value = value; *old_value = value;
} }
*rc += 1; *rc += 1;
@ -249,8 +261,8 @@ impl HashDB for MemoryDB {
} }
} }
fn remove(&mut self, key: &H256) { fn remove(&mut self, key: &H::Out) {
if key == &KECCAK_NULL_RLP { if key == &self.hashed_null_node {
return; return;
} }
@ -266,17 +278,26 @@ impl HashDB for MemoryDB {
} }
} }
impl<H: KeyHasher> AsHashDB<H> for MemoryDB<H> {
fn as_hashdb(&self) -> &HashDB<H> { self }
fn as_hashdb_mut(&mut self) -> &mut HashDB<H> { self }
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use keccak::keccak;
use super::*; use super::*;
use tiny_keccak::Keccak;
use ethereum_types::H256;
use keccak_hasher::KeccakHasher;
#[test] #[test]
fn memorydb_remove_and_purge() { fn memorydb_remove_and_purge() {
let hello_bytes = b"Hello world!"; let hello_bytes = b"Hello world!";
let hello_key = keccak(hello_bytes); let mut hello_key = [0;32];
Keccak::keccak256(hello_bytes, &mut hello_key);
let hello_key = H256(hello_key);
let mut m = MemoryDB::new(); let mut m = MemoryDB::<KeccakHasher>::new();
m.remove(&hello_key); m.remove(&hello_key);
assert_eq!(m.raw(&hello_key).unwrap().1, -1); assert_eq!(m.raw(&hello_key).unwrap().1, -1);
m.purge(); m.purge();
@ -286,7 +307,7 @@ mod tests {
m.purge(); m.purge();
assert_eq!(m.raw(&hello_key), None); assert_eq!(m.raw(&hello_key), None);
let mut m = MemoryDB::new(); let mut m = MemoryDB::<KeccakHasher>::new();
assert!(m.remove_and_purge(&hello_key).is_none()); assert!(m.remove_and_purge(&hello_key).is_none());
assert_eq!(m.raw(&hello_key).unwrap().1, -1); assert_eq!(m.raw(&hello_key).unwrap().1, -1);
m.insert(hello_bytes); m.insert(hello_bytes);
@ -299,8 +320,8 @@ mod tests {
#[test] #[test]
fn consolidate() { fn consolidate() {
let mut main = MemoryDB::new(); let mut main = MemoryDB::<KeccakHasher>::new();
let mut other = MemoryDB::new(); let mut other = MemoryDB::<KeccakHasher>::new();
let remove_key = other.insert(b"doggo"); let remove_key = other.insert(b"doggo");
main.remove(&remove_key); main.remove(&remove_key);

View File

@ -0,0 +1,15 @@
[package]
name = "patricia-trie-ethereum"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
description = "Merkle-Patricia Trie (Ethereum Style)"
license = "GPL-3.0"
[dependencies]
patricia-trie = { path = "../patricia_trie" }
keccak-hasher = { path = "../keccak-hasher" }
hashdb = { path = "../hashdb" }
rlp = { path = "../rlp" }
ethcore-bytes = { path = "../bytes" }
ethereum-types = "0.3"
elastic-array = "0.10"

View File

@ -0,0 +1,62 @@
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Façade crate for `patricia_trie` for Ethereum specific impls
pub extern crate patricia_trie as trie; // `pub` because we need to import this crate for the tests in `patricia_trie` and there were issues: https://gist.github.com/dvdplm/869251ee557a1b4bd53adc7c971979aa
extern crate elastic_array;
extern crate ethcore_bytes;
extern crate ethereum_types;
extern crate hashdb;
extern crate keccak_hasher;
extern crate rlp;
mod rlp_node_codec;
pub use rlp_node_codec::RlpNodeCodec;
use ethereum_types::H256;
use keccak_hasher::KeccakHasher;
use rlp::DecoderError;
/// Convenience type alias to instantiate a Keccak-flavoured `RlpNodeCodec`
pub type RlpCodec = RlpNodeCodec<KeccakHasher>;
/// Convenience type alias to instantiate a Keccak/Rlp-flavoured `TrieDB`
pub type TrieDB<'db> = trie::TrieDB<'db, KeccakHasher, RlpCodec>;
/// Convenience type alias to instantiate a Keccak/Rlp-flavoured `SecTrieDB`
pub type SecTrieDB<'db> = trie::SecTrieDB<'db, KeccakHasher, RlpCodec>;
/// Convenience type alias to instantiate a Keccak/Rlp-flavoured `FatDB`
pub type FatDB<'db> = trie::FatDB<'db, KeccakHasher, RlpCodec>;
/// Convenience type alias to instantiate a Keccak/Rlp-flavoured `TrieDBMut`
pub type TrieDBMut<'db> = trie::TrieDBMut<'db, KeccakHasher, RlpCodec>;
/// Convenience type alias to instantiate a Keccak/Rlp-flavoured `SecTrieDBMut`
pub type SecTrieDBMut<'db> = trie::SecTrieDBMut<'db, KeccakHasher, RlpCodec>;
/// Convenience type alias to instantiate a Keccak/Rlp-flavoured `FatDBMut`
pub type FatDBMut<'db> = trie::FatDBMut<'db, KeccakHasher, RlpCodec>;
/// Convenience type alias to instantiate a Keccak/Rlp-flavoured `TrieFactory`
pub type TrieFactory = trie::TrieFactory<KeccakHasher, RlpCodec>;
/// Convenience type alias for Keccak/Rlp flavoured trie errors
pub type TrieError = trie::TrieError<H256, DecoderError>;
/// Convenience type alias for Keccak/Rlp flavoured trie results
pub type Result<T> = trie::Result<T, H256, DecoderError>;

View File

@ -0,0 +1,124 @@
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! `NodeCodec` implementation for Rlp
use elastic_array::{ElasticArray1024, ElasticArray128};
use ethereum_types::H256;
use hashdb::Hasher;
use keccak_hasher::KeccakHasher;
use rlp::{DecoderError, RlpStream, Rlp, Prototype};
use std::marker::PhantomData;
use trie::{NibbleSlice, NodeCodec, node::Node, ChildReference};
/// Concrete implementation of a `NodeCodec` with Rlp encoding, generic over the `Hasher`
#[derive(Default, Clone)]
pub struct RlpNodeCodec<H: Hasher> {mark: PhantomData<H>}
// NOTE: what we'd really like here is:
// `impl<H: Hasher> NodeCodec<H> for RlpNodeCodec<H> where H::Out: Decodable`
// but due to the current limitations of Rust const evaluation we can't
// do `const HASHED_NULL_NODE: H::Out = H::Out( … … )`. Perhaps one day soon?
impl NodeCodec<KeccakHasher> for RlpNodeCodec<KeccakHasher> {
type Error = DecoderError;
const HASHED_NULL_NODE : H256 = H256( [0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e, 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0, 0x01, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21] );
fn decode(data: &[u8]) -> ::std::result::Result<Node, Self::Error> {
let r = Rlp::new(data);
match r.prototype()? {
// either leaf or extension - decode first item with NibbleSlice::???
// and use is_leaf return to figure out which.
// if leaf, second item is a value (is_data())
// if extension, second item is a node (either SHA3 to be looked up and
// fed back into this function or inline RLP which can be fed back into this function).
Prototype::List(2) => match NibbleSlice::from_encoded(r.at(0)?.data()?) {
(slice, true) => Ok(Node::Leaf(slice, r.at(1)?.data()?)),
(slice, false) => Ok(Node::Extension(slice, r.at(1)?.as_raw())),
},
// branch - first 16 are nodes, 17th is a value (or empty).
Prototype::List(17) => {
let mut nodes = [&[] as &[u8]; 16];
for i in 0..16 {
nodes[i] = r.at(i)?.as_raw();
}
Ok(Node::Branch(nodes, if r.at(16)?.is_empty() { None } else { Some(r.at(16)?.data()?) }))
},
// an empty branch index.
Prototype::Data(0) => Ok(Node::Empty),
// something went wrong.
_ => Err(DecoderError::Custom("Rlp is not valid."))
}
}
fn try_decode_hash(data: &[u8]) -> Option<<KeccakHasher as Hasher>::Out> {
let r = Rlp::new(data);
if r.is_data() && r.size() == KeccakHasher::LENGTH {
Some(r.as_val().expect("Hash is the correct size; qed"))
} else {
None
}
}
fn is_empty_node(data: &[u8]) -> bool {
Rlp::new(data).is_empty()
}
fn empty_node() -> ElasticArray1024<u8> {
let mut stream = RlpStream::new();
stream.append_empty_data();
stream.drain()
}
fn leaf_node(partial: &[u8], value: &[u8]) -> ElasticArray1024<u8> {
let mut stream = RlpStream::new_list(2);
stream.append(&partial);
stream.append(&value);
stream.drain()
}
fn ext_node(partial: &[u8], child_ref: ChildReference<<KeccakHasher as Hasher>::Out>) -> ElasticArray1024<u8> {
let mut stream = RlpStream::new_list(2);
stream.append(&partial);
match child_ref {
ChildReference::Hash(h) => stream.append(&h),
ChildReference::Inline(inline_data, len) => {
let bytes = &AsRef::<[u8]>::as_ref(&inline_data)[..len];
stream.append_raw(bytes, 1)
},
};
stream.drain()
}
fn branch_node<I>(children: I, value: Option<ElasticArray128<u8>>) -> ElasticArray1024<u8>
where I: IntoIterator<Item=Option<ChildReference<<KeccakHasher as Hasher>::Out>>>
{
let mut stream = RlpStream::new_list(17);
for child_ref in children {
match child_ref {
Some(c) => match c {
ChildReference::Hash(h) => stream.append(&h),
ChildReference::Inline(inline_data, len) => {
let bytes = &AsRef::<[u8]>::as_ref(&inline_data)[..len];
stream.append_raw(bytes, 1)
},
},
None => stream.append_empty_data()
};
}
if let Some(value) = value {
stream.append(&&*value);
} else {
stream.append_empty_data();
}
stream.drain()
}
}

View File

@ -1,22 +1,24 @@
[package] [package]
name = "patricia-trie" name = "patricia-trie"
version = "0.1.0" version = "0.2.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
description = "Merkle-Patricia Trie (Ethereum Style)" description = "Merkle-Patricia Trie generic over key hasher and node encoding"
license = "GPL-3.0" license = "GPL-3.0"
[dependencies] [dependencies]
elastic-array = "0.10" elastic-array = "0.10"
log = "0.3" log = "0.3"
rand = "0.4" rand = "0.4"
hashdb = { version = "0.2", path = "../hashdb" }
ethcore-bytes = { version = "0.1.0", path = "../bytes" } ethcore-bytes = { version = "0.1.0", path = "../bytes" }
ethereum-types = "0.3"
keccak-hash = { version = "0.1.0", path = "../hash" }
hashdb = { version = "0.1.1", path = "../hashdb" }
rlp = { version = "0.2.1", path = "../rlp" }
triehash = { version = "0.1.0", path = "../triehash" }
memorydb = { version = "0.1.0", path = "../memorydb" }
ethcore-logger = { version = "1.9.0", path = "../../logger" }
[dev-dependencies] [dev-dependencies]
env_logger = "0.5"
ethereum-types = "0.3"
keccak-hash = { version = "0.1.0", path = "../hash" }
keccak-hasher = { path = "../keccak-hasher" }
memorydb = { version = "0.2", path = "../memorydb" }
patricia-trie-ethereum = { path = "../patricia-trie-ethereum" }
rlp = { version = "0.2.1", path = "../rlp" }
trie-standardmap = { path = "../trie-standardmap" } trie-standardmap = { path = "../trie-standardmap" }
triehash = { version = "0.1.0", path = "../triehash" }

View File

@ -21,16 +21,21 @@ extern crate ethcore_bytes;
extern crate ethereum_types; extern crate ethereum_types;
extern crate memorydb; extern crate memorydb;
extern crate patricia_trie as trie; extern crate patricia_trie as trie;
extern crate patricia_trie_ethereum as ethtrie;
extern crate keccak_hasher;
extern crate keccak_hash; extern crate keccak_hash;
extern crate trie_standardmap; extern crate trie_standardmap;
extern crate hashdb;
use ethcore_bytes::Bytes; use ethcore_bytes::Bytes;
use ethereum_types::H256; use ethereum_types::H256;
use keccak_hash::keccak; use keccak_hash::keccak;
use memorydb::MemoryDB; use memorydb::MemoryDB;
use test::{Bencher, black_box}; use test::{Bencher, black_box};
use trie::{TrieDBMut, TrieDB, TrieMut, Trie}; use trie::{TrieMut, Trie};
use trie_standardmap::{Alphabet, ValueMode, StandardMap}; use trie_standardmap::{Alphabet, ValueMode, StandardMap};
use keccak_hasher::KeccakHasher;
use ethtrie::{TrieDB, TrieDBMut};
fn random_word(alphabet: &[u8], min_count: usize, diff_count: usize, seed: &mut H256) -> Vec<u8> { fn random_word(alphabet: &[u8], min_count: usize, diff_count: usize, seed: &mut H256) -> Vec<u8> {
assert!(min_count + diff_count <= 32); assert!(min_count + diff_count <= 32);
@ -69,7 +74,7 @@ fn trie_insertions_32_mir_1k(b: &mut Bencher) {
}; };
let d = st.make(); let d = st.make();
b.iter(&mut ||{ b.iter(&mut ||{
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root); let mut t = TrieDBMut::new(&mut memdb, &mut root);
for i in d.iter() { for i in d.iter() {
@ -87,7 +92,7 @@ fn trie_iter(b: &mut Bencher) {
count: 1000, count: 1000,
}; };
let d = st.make(); let d = st.make();
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
{ {
let mut t = TrieDBMut::new(&mut memdb, &mut root); let mut t = TrieDBMut::new(&mut memdb, &mut root);
@ -116,7 +121,7 @@ fn trie_insertions_32_ran_1k(b: &mut Bencher) {
let d = st.make(); let d = st.make();
let mut r = H256::new(); let mut r = H256::new();
b.iter(&mut ||{ b.iter(&mut ||{
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root); let mut t = TrieDBMut::new(&mut memdb, &mut root);
for i in d.iter() { for i in d.iter() {
@ -137,7 +142,7 @@ fn trie_insertions_six_high(b: &mut Bencher) {
} }
b.iter(||{ b.iter(||{
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root); let mut t = TrieDBMut::new(&mut memdb, &mut root);
for i in d.iter() { for i in d.iter() {
@ -157,7 +162,7 @@ fn trie_insertions_six_mid(b: &mut Bencher) {
d.push((k, v)) d.push((k, v))
} }
b.iter(||{ b.iter(||{
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root); let mut t = TrieDBMut::new(&mut memdb, &mut root);
for i in d.iter() { for i in d.iter() {
@ -178,7 +183,7 @@ fn trie_insertions_random_mid(b: &mut Bencher) {
} }
b.iter(||{ b.iter(||{
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root); let mut t = TrieDBMut::new(&mut memdb, &mut root);
for i in d.iter() { for i in d.iter() {
@ -199,7 +204,7 @@ fn trie_insertions_six_low(b: &mut Bencher) {
} }
b.iter(||{ b.iter(||{
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root); let mut t = TrieDBMut::new(&mut memdb, &mut root);
for i in d.iter() { for i in d.iter() {

View File

@ -14,66 +14,77 @@
// 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 ethereum_types::H256; use hashdb::{HashDB, Hasher};
use keccak::keccak; use super::{Result, TrieDB, Trie, TrieDBIterator, TrieItem, TrieIterator, Query};
use hashdb::HashDB; use node_codec::NodeCodec;
use super::{TrieDB, Trie, TrieDBIterator, TrieItem, TrieIterator, Query};
/// A `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. /// A `Trie` implementation which hashes keys and uses a generic `HashDB` backing database.
/// Additionaly it stores inserted hash-key mappings for later retrieval. /// Additionaly it stores inserted hash-key mappings for later retrieval.
/// ///
/// Use it as a `Trie` or `TrieMut` trait object. /// Use it as a `Trie` or `TrieMut` trait object.
pub struct FatDB<'db> { pub struct FatDB<'db, H, C>
raw: TrieDB<'db>, where
H: Hasher + 'db,
C: NodeCodec<H>
{
raw: TrieDB<'db, H, C>,
} }
impl<'db> FatDB<'db> { impl<'db, H, C> FatDB<'db, H, C>
where
H: Hasher,
C: NodeCodec<H>
{
/// Create a new trie with the backing database `db` and empty `root` /// Create a new trie with the backing database `db` and empty `root`
/// Initialise to the state entailed by the genesis block. /// Initialise to the state entailed by the genesis block.
/// This guarantees the trie is built correctly. /// This guarantees the trie is built correctly.
pub fn new(db: &'db HashDB, root: &'db H256) -> super::Result<Self> { pub fn new(db: &'db HashDB<H>, root: &'db H::Out) -> Result<Self, H::Out, C::Error> {
let fatdb = FatDB { Ok(FatDB { raw: TrieDB::new(db, root)? })
raw: TrieDB::new(db, root)?
};
Ok(fatdb)
} }
/// Get the backing database. /// Get the backing database.
pub fn db(&self) -> &HashDB { pub fn db(&self) -> &HashDB<H> { self.raw.db() }
self.raw.db()
}
} }
impl<'db> Trie for FatDB<'db> { impl<'db, H, C> Trie<H, C> for FatDB<'db, H, C>
fn iter<'a>(&'a self) -> super::Result<Box<TrieIterator<Item = TrieItem> + 'a>> { where
FatDBIterator::new(&self.raw).map(|iter| Box::new(iter) as Box<_>) H: Hasher,
C: NodeCodec<H>
{
fn root(&self) -> &H::Out { self.raw.root() }
fn contains(&self, key: &[u8]) -> Result<bool, H::Out, C::Error> {
self.raw.contains(H::hash(key).as_ref())
} }
fn root(&self) -> &H256 { fn get_with<'a, 'key, Q: Query<H>>(&'a self, key: &'key [u8], query: Q) -> Result<Option<Q::Item>, H::Out, C::Error>
self.raw.root()
}
fn contains(&self, key: &[u8]) -> super::Result<bool> {
self.raw.contains(&keccak(key))
}
fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> super::Result<Option<Q::Item>>
where 'a: 'key where 'a: 'key
{ {
self.raw.get_with(&keccak(key), query) self.raw.get_with(H::hash(key).as_ref(), query)
}
fn iter<'a>(&'a self) -> Result<Box<TrieIterator<H, C, Item = TrieItem<H::Out, C::Error>> + 'a>, <H as Hasher>::Out, C::Error> {
FatDBIterator::<H, C>::new(&self.raw).map(|iter| Box::new(iter) as Box<_>)
} }
} }
/// Itarator over inserted pairs of key values. /// Itarator over inserted pairs of key values.
pub struct FatDBIterator<'db> { pub struct FatDBIterator<'db, H, C>
trie_iterator: TrieDBIterator<'db>, where
trie: &'db TrieDB<'db>, H: Hasher + 'db,
C: NodeCodec<H> + 'db
{
trie_iterator: TrieDBIterator<'db, H, C>,
trie: &'db TrieDB<'db, H, C>,
} }
impl<'db> FatDBIterator<'db> { impl<'db, H, C> FatDBIterator<'db, H, C>
where
H: Hasher,
C: NodeCodec<H>
{
/// Creates new iterator. /// Creates new iterator.
pub fn new(trie: &'db TrieDB) -> super::Result<Self> { pub fn new(trie: &'db TrieDB<H, C>) -> Result<Self, H::Out, C::Error> {
Ok(FatDBIterator { Ok(FatDBIterator {
trie_iterator: TrieDBIterator::new(trie)?, trie_iterator: TrieDBIterator::new(trie)?,
trie: trie, trie: trie,
@ -81,40 +92,56 @@ impl<'db> FatDBIterator<'db> {
} }
} }
impl<'db> TrieIterator for FatDBIterator<'db> { impl<'db, H, C> TrieIterator<H, C> for FatDBIterator<'db, H, C>
fn seek(&mut self, key: &[u8]) -> super::Result<()> { where
self.trie_iterator.seek(&keccak(key)) H: Hasher,
C: NodeCodec<H>
{
fn seek(&mut self, key: &[u8]) -> Result<(), H::Out, C::Error> {
let hashed_key = H::hash(key);
self.trie_iterator.seek(hashed_key.as_ref())
} }
} }
impl<'db> Iterator for FatDBIterator<'db> { impl<'db, H, C> Iterator for FatDBIterator<'db, H, C>
type Item = TrieItem<'db>; where
H: Hasher,
C: NodeCodec<H>
{
type Item = TrieItem<'db, H::Out, C::Error>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
self.trie_iterator.next() self.trie_iterator.next()
.map(|res| .map(|res| {
res.map(|(hash, value)| { res.map(|(hash, value)| {
let aux_hash = keccak(hash); let aux_hash = H::hash(&hash);
(self.trie.db().get(&aux_hash).expect("Missing fatdb hash").into_vec(), value) (self.trie.db().get(&aux_hash).expect("Missing fatdb hash").into_vec(), value)
}) })
) })
} }
} }
#[test] #[cfg(test)]
fn fatdb_to_trie() { mod test {
use memorydb::MemoryDB; use memorydb::MemoryDB;
use hashdb::DBValue; use hashdb::DBValue;
use super::fatdbmut::FatDBMut; use keccak_hasher::KeccakHasher;
use super::TrieMut; use ethtrie::trie::{Trie, TrieMut};
use ethtrie::{FatDB, FatDBMut};
use ethereum_types::H256;
let mut memdb = MemoryDB::new(); #[test]
let mut root = H256::default(); fn fatdb_to_trie() {
{ let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut t = FatDBMut::new(&mut memdb, &mut root); let mut root = H256::new();
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); {
let mut t = FatDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
}
let t = FatDB::new(&memdb, &root).unwrap();
assert_eq!(t.get(&[0x01u8, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23]));
assert_eq!(
t.iter().unwrap().map(Result::unwrap).collect::<Vec<_>>(),
vec![(vec![0x01u8, 0x23], DBValue::from_slice(&[0x01u8, 0x23] as &[u8]))]);
} }
let t = FatDB::new(&memdb, &root).unwrap();
assert_eq!(t.get(&[0x01u8, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23]));
assert_eq!(t.iter().unwrap().map(Result::unwrap).collect::<Vec<_>>(), vec![(vec![0x01u8, 0x23], DBValue::from_slice(&[0x01u8, 0x23] as &[u8]))]);
} }

View File

@ -14,105 +14,116 @@
// 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 ethereum_types::H256; use hashdb::{HashDB, DBValue, Hasher};
use keccak::keccak; use super::{Result, TrieDBMut, TrieMut};
use hashdb::{HashDB, DBValue}; use node_codec::NodeCodec;
use super::{TrieDBMut, TrieMut};
/// A mutable `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. /// A mutable `Trie` implementation which hashes keys and uses a generic `HashDB` backing database.
/// Additionaly it stores inserted hash-key mappings for later retrieval. /// Additionaly it stores inserted hash-key mappings for later retrieval.
/// ///
/// Use it as a `Trie` or `TrieMut` trait object. /// Use it as a `Trie` or `TrieMut` trait object.
pub struct FatDBMut<'db> { pub struct FatDBMut<'db, H, C>
raw: TrieDBMut<'db>, where
H: Hasher + 'db,
C: NodeCodec<H>
{
raw: TrieDBMut<'db, H, C>,
} }
impl<'db> FatDBMut<'db> { impl<'db, H, C> FatDBMut<'db, H, C>
where
H: Hasher,
C: NodeCodec<H>
{
/// Create a new trie with the backing database `db` and empty `root` /// Create a new trie with the backing database `db` and empty `root`
/// Initialise to the state entailed by the genesis block. /// Initialise to the state entailed by the genesis block.
/// This guarantees the trie is built correctly. /// This guarantees the trie is built correctly.
pub fn new(db: &'db mut HashDB, root: &'db mut H256) -> Self { pub fn new(db: &'db mut HashDB<H>, root: &'db mut H::Out) -> Self {
FatDBMut { raw: TrieDBMut::new(db, root) } FatDBMut { raw: TrieDBMut::new(db, root) }
} }
/// Create a new trie with the backing database `db` and `root`. /// Create a new trie with the backing database `db` and `root`.
/// ///
/// Returns an error if root does not exist. /// Returns an error if root does not exist.
pub fn from_existing(db: &'db mut HashDB, root: &'db mut H256) -> super::Result<Self> { pub fn from_existing(db: &'db mut HashDB<H>, root: &'db mut H::Out) -> Result<Self, H::Out, C::Error> {
Ok(FatDBMut { raw: TrieDBMut::from_existing(db, root)? }) Ok(FatDBMut { raw: TrieDBMut::from_existing(db, root)? })
} }
/// Get the backing database. /// Get the backing database.
pub fn db(&self) -> &HashDB { pub fn db(&self) -> &HashDB<H> {
self.raw.db() self.raw.db()
} }
/// Get the backing database. /// Get the backing database.
pub fn db_mut(&mut self) -> &mut HashDB { pub fn db_mut(&mut self) -> &mut HashDB<H> {
self.raw.db_mut() self.raw.db_mut()
} }
fn to_aux_key(key: &[u8]) -> H256 {
keccak(key)
}
} }
impl<'db> TrieMut for FatDBMut<'db> { impl<'db, H, C> TrieMut<H, C> for FatDBMut<'db, H, C>
fn root(&mut self) -> &H256 { where
self.raw.root() H: Hasher,
C: NodeCodec<H>
{
fn root(&mut self) -> &H::Out { self.raw.root() }
fn is_empty(&self) -> bool { self.raw.is_empty() }
fn contains(&self, key: &[u8]) -> Result<bool, H::Out, C::Error> {
self.raw.contains(H::hash(key).as_ref())
} }
fn is_empty(&self) -> bool { fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result<Option<DBValue>, H::Out, C::Error>
self.raw.is_empty()
}
fn contains(&self, key: &[u8]) -> super::Result<bool> {
self.raw.contains(&keccak(key))
}
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> super::Result<Option<DBValue>>
where 'a: 'key where 'a: 'key
{ {
self.raw.get(&keccak(key)) self.raw.get(H::hash(key).as_ref())
} }
fn insert(&mut self, key: &[u8], value: &[u8]) -> super::Result<Option<DBValue>> { fn insert(&mut self, key: &[u8], value: &[u8]) -> Result<Option<DBValue>, H::Out, C::Error> {
let hash = keccak(key); let hash = H::hash(key);
let out = self.raw.insert(&hash, value)?; let out = self.raw.insert(hash.as_ref(), value)?;
let db = self.raw.db_mut(); let db = self.raw.db_mut();
// don't insert if it doesn't exist. // don't insert if it doesn't exist.
if out.is_none() { if out.is_none() {
db.emplace(Self::to_aux_key(&hash), DBValue::from_slice(key)); let aux_hash = H::hash(hash.as_ref());
db.emplace(aux_hash, DBValue::from_slice(key));
} }
Ok(out) Ok(out)
} }
fn remove(&mut self, key: &[u8]) -> super::Result<Option<DBValue>> { fn remove(&mut self, key: &[u8]) -> Result<Option<DBValue>, H::Out, C::Error> {
let hash = keccak(key); let hash = H::hash(key);
let out = self.raw.remove(&hash)?; let out = self.raw.remove(hash.as_ref())?;
// don't remove if it already exists. // don't remove if it already exists.
if out.is_some() { if out.is_some() {
self.raw.db_mut().remove(&Self::to_aux_key(&hash)); self.raw.db_mut().remove(&hash);
} }
Ok(out) Ok(out)
} }
} }
#[test] #[cfg(test)]
fn fatdb_to_trie() { mod test {
use hashdb::DBValue;
use memorydb::MemoryDB; use memorydb::MemoryDB;
use super::TrieDB; use ethtrie::trie::{Trie, TrieMut};
use super::Trie; use ethtrie::{TrieDB, FatDBMut};
use keccak_hasher::KeccakHasher;
use keccak;
use ethereum_types::H256;
let mut memdb = MemoryDB::new(); #[test]
let mut root = H256::default(); fn fatdbmut_to_trie() {
{ let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut t = FatDBMut::new(&mut memdb, &mut root); let mut root = H256::new();
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); {
let mut t = FatDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
}
let t = TrieDB::new(&memdb, &root).unwrap();
assert_eq!(t.get(&keccak::keccak(&[0x01u8, 0x23])).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23]));
} }
let t = TrieDB::new(&memdb, &root).unwrap(); }
assert_eq!(t.get(&keccak(&[0x01u8, 0x23])).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23]));
}

View File

@ -15,26 +15,35 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Trie interface and implementation. //! Trie interface and implementation.
extern crate rand;
extern crate ethereum_types;
extern crate keccak_hash as keccak;
extern crate rlp;
extern crate hashdb;
extern crate ethcore_bytes as bytes;
extern crate elastic_array; extern crate elastic_array;
extern crate memorydb; extern crate ethcore_bytes as bytes;
extern crate ethcore_logger; extern crate hashdb;
extern crate rand;
#[cfg(test)]
extern crate trie_standardmap as standardmap;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
#[cfg(test)]
extern crate env_logger;
#[cfg(test)]
extern crate ethereum_types;
#[cfg(test)]
extern crate trie_standardmap as standardmap;
#[cfg(test)]
extern crate patricia_trie_ethereum as ethtrie;
#[cfg(test)]
extern crate memorydb;
#[cfg(test)]
extern crate rlp;
#[cfg(test)]
extern crate keccak_hash as keccak;
#[cfg(test)]
extern crate keccak_hasher;
#[cfg(test)]
extern crate triehash;
use std::{fmt, error}; use std::{fmt, error};
use ethereum_types::H256; use hashdb::{HashDB, DBValue, Hasher};
use keccak::KECCAK_NULL_RLP; use std::marker::PhantomData;
use hashdb::{HashDB, DBValue};
pub mod node; pub mod node;
pub mod triedb; pub mod triedb;
@ -46,158 +55,154 @@ pub mod recorder;
mod fatdb; mod fatdb;
mod fatdbmut; mod fatdbmut;
mod lookup; mod lookup;
mod nibbleslice;
mod nibblevec; mod nibblevec;
mod nibbleslice;
mod node_codec;
pub use self::triedbmut::TrieDBMut;
pub use self::triedb::{TrieDB, TrieDBIterator}; pub use self::triedb::{TrieDB, TrieDBIterator};
pub use self::triedbmut::{TrieDBMut, ChildReference};
pub use self::sectriedbmut::SecTrieDBMut; pub use self::sectriedbmut::SecTrieDBMut;
pub use self::sectriedb::SecTrieDB; pub use self::sectriedb::SecTrieDB;
pub use self::fatdb::{FatDB, FatDBIterator}; pub use self::fatdb::{FatDB, FatDBIterator};
pub use self::fatdbmut::FatDBMut; pub use self::fatdbmut::FatDBMut;
pub use self::recorder::Recorder; pub use self::recorder::Recorder;
pub use self::lookup::Lookup;
pub use self::nibbleslice::NibbleSlice;
pub use node_codec::NodeCodec;
/// Trie Errors. /// Trie Errors.
/// ///
/// These borrow the data within them to avoid excessive copying on every /// These borrow the data within them to avoid excessive copying on every
/// trie operation. /// trie operation.
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
pub enum TrieError { pub enum TrieError<T, E> {
/// Attempted to create a trie with a state root not in the DB. /// Attempted to create a trie with a state root not in the DB.
InvalidStateRoot(H256), InvalidStateRoot(T),
/// Trie item not found in the database, /// Trie item not found in the database,
IncompleteDatabase(H256), IncompleteDatabase(T),
/// Corrupt Trie item /// Corrupt Trie item
DecoderError(rlp::DecoderError), DecoderError(T, E),
} }
impl fmt::Display for TrieError { impl<T, E> fmt::Display for TrieError<T, E> where T: std::fmt::Debug, E: std::fmt::Debug {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match *self {
TrieError::InvalidStateRoot(ref root) => write!(f, "Invalid state root: {}", root), TrieError::InvalidStateRoot(ref root) => write!(f, "Invalid state root: {:?}", root),
TrieError::IncompleteDatabase(ref missing) => TrieError::IncompleteDatabase(ref missing) => write!(f, "Database missing expected key: {:?}", missing),
write!(f, "Database missing expected key: {}", missing), TrieError::DecoderError(ref hash, ref decoder_err) => write!(f, "Decoding failed for hash {:?}; err: {:?}", hash, decoder_err),
TrieError::DecoderError(ref err) => write!(f, "Decoding failed with {}", err),
} }
} }
} }
impl error::Error for TrieError { impl<T, E> error::Error for TrieError<T, E> where T: std::fmt::Debug, E: std::error::Error {
fn description(&self) -> &str { fn description(&self) -> &str {
match *self { match *self {
TrieError::InvalidStateRoot(_) => "Invalid state root", TrieError::InvalidStateRoot(_) => "Invalid state root",
TrieError::IncompleteDatabase(_) => "Incomplete database", TrieError::IncompleteDatabase(_) => "Incomplete database",
TrieError::DecoderError(ref e) => e.description(), TrieError::DecoderError(_, ref err) => err.description(),
} }
} }
} }
impl From<rlp::DecoderError> for Box<TrieError> { /// Trie result type. Boxed to avoid copying around extra space for the `Hasher`s `Out` on successful queries.
fn from(e: rlp::DecoderError) -> Self { Box::new(TrieError::DecoderError(e)) } pub type Result<T, H, E> = ::std::result::Result<T, Box<TrieError<H, E>>>;
}
/// Trie result type. Boxed to avoid copying around extra space for `H256`s on successful queries.
pub type Result<T> = ::std::result::Result<T, Box<TrieError>>;
/// Trie-Item type. /// Trie-Item type used for iterators over trie data.
pub type TrieItem<'a> = Result<(Vec<u8>, DBValue)>; pub type TrieItem<'a, U, E> = Result<(Vec<u8>, DBValue), U, E>;
/// Description of what kind of query will be made to the trie. /// Description of what kind of query will be made to the trie.
/// ///
/// This is implemented for any &mut recorder (where the query will return /// This is implemented for any &mut recorder (where the query will return
/// a DBValue), any function taking raw bytes (where no recording will be made), /// a DBValue), any function taking raw bytes (where no recording will be made),
/// or any tuple of (&mut Recorder, FnOnce(&[u8])) /// or any tuple of (&mut Recorder, FnOnce(&[u8]))
pub trait Query { pub trait Query<H: Hasher> {
/// Output item. /// Output item.
type Item; type Item;
/// Decode a byte-slice into the desired item. /// Decode a byte-slice into the desired item.
fn decode(self, &[u8]) -> Self::Item; fn decode(self, data: &[u8]) -> Self::Item;
/// Record that a node has been passed through. /// Record that a node has been passed through.
fn record(&mut self, &H256, &[u8], u32) { } fn record(&mut self, _hash: &H::Out, _data: &[u8], _depth: u32) {}
} }
impl<'a> Query for &'a mut Recorder { impl<'a, H: Hasher> Query<H> for &'a mut Recorder<H::Out> {
type Item = DBValue; type Item = DBValue;
fn decode(self, value: &[u8]) -> DBValue { DBValue::from_slice(value) } fn decode(self, value: &[u8]) -> DBValue { DBValue::from_slice(value) }
fn record(&mut self, hash: &H256, data: &[u8], depth: u32) { fn record(&mut self, hash: &H::Out, data: &[u8], depth: u32) {
(&mut **self).record(hash, data, depth); (&mut **self).record(hash, data, depth);
} }
} }
impl<F, T> Query for F where F: for<'a> FnOnce(&'a [u8]) -> T { impl<F, T, H: Hasher> Query<H> for F where F: for<'a> FnOnce(&'a [u8]) -> T {
type Item = T; type Item = T;
fn decode(self, value: &[u8]) -> T { (self)(value) } fn decode(self, value: &[u8]) -> T { (self)(value) }
} }
impl<'a, F, T> Query for (&'a mut Recorder, F) where F: FnOnce(&[u8]) -> T { impl<'a, F, T, H: Hasher> Query<H> for (&'a mut Recorder<H::Out>, F) where F: FnOnce(&[u8]) -> T {
type Item = T; type Item = T;
fn decode(self, value: &[u8]) -> T { (self.1)(value) } fn decode(self, value: &[u8]) -> T { (self.1)(value) }
fn record(&mut self, hash: &H256, data: &[u8], depth: u32) { fn record(&mut self, hash: &H::Out, data: &[u8], depth: u32) {
self.0.record(hash, data, depth) self.0.record(hash, data, depth)
} }
} }
/// A key-value datastore implemented as a database-backed modified Merkle tree. /// A key-value datastore implemented as a database-backed modified Merkle tree.
pub trait Trie { pub trait Trie<H: Hasher, C: NodeCodec<H>> {
/// Return the root of the trie. /// Return the root of the trie.
fn root(&self) -> &H256; fn root(&self) -> &H::Out;
/// Is the trie empty? /// Is the trie empty?
fn is_empty(&self) -> bool { *self.root() == KECCAK_NULL_RLP } fn is_empty(&self) -> bool { *self.root() == C::HASHED_NULL_NODE }
/// Does the trie contain a given key? /// Does the trie contain a given key?
fn contains(&self, key: &[u8]) -> Result<bool> { fn contains(&self, key: &[u8]) -> Result<bool, H::Out, C::Error> {
self.get(key).map(|x| x.is_some()) self.get(key).map(|x|x.is_some() )
} }
/// What is the value of the given key in this trie? /// What is the value of the given key in this trie?
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result<Option<DBValue>> where 'a: 'key { fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result<Option<DBValue>, H::Out, C::Error> where 'a: 'key {
self.get_with(key, DBValue::from_slice) self.get_with(key, DBValue::from_slice)
} }
/// Search for the key with the given query parameter. See the docs of the `Query` /// Search for the key with the given query parameter. See the docs of the `Query`
/// trait for more details. /// trait for more details.
fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) fn get_with<'a, 'key, Q: Query<H>>(&'a self, key: &'key [u8], query: Q) -> Result<Option<Q::Item>, H::Out, C::Error> where 'a: 'key;
-> Result<Option<Q::Item>> where 'a: 'key;
/// Returns a depth-first iterator over the elements of trie. /// Returns a depth-first iterator over the elements of trie.
fn iter<'a>(&'a self) -> Result<Box<TrieIterator<Item = TrieItem> + 'a>>; fn iter<'a>(&'a self) -> Result<Box<TrieIterator<H, C, Item = TrieItem<H::Out, C::Error >> + 'a>, H::Out, C::Error>;
} }
/// A key-value datastore implemented as a database-backed modified Merkle tree. /// A key-value datastore implemented as a database-backed modified Merkle tree.
pub trait TrieMut { pub trait TrieMut<H: Hasher, C: NodeCodec<H>> {
/// Return the root of the trie. /// Return the root of the trie.
fn root(&mut self) -> &H256; fn root(&mut self) -> &H::Out;
/// Is the trie empty? /// Is the trie empty?
fn is_empty(&self) -> bool; fn is_empty(&self) -> bool;
/// Does the trie contain a given key? /// Does the trie contain a given key?
fn contains(&self, key: &[u8]) -> Result<bool> { fn contains(&self, key: &[u8]) -> Result<bool, H::Out, C::Error> {
self.get(key).map(|x| x.is_some()) self.get(key).map(|x| x.is_some())
} }
/// What is the value of the given key in this trie? /// What is the value of the given key in this trie?
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result<Option<DBValue>> where 'a: 'key; fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result<Option<DBValue>, H::Out, C::Error> where 'a: 'key;
/// Insert a `key`/`value` pair into the trie. An empty value is equivalent to removing /// Insert a `key`/`value` pair into the trie. An empty value is equivalent to removing
/// `key` from the trie. Returns the old value associated with this key, if it existed. /// `key` from the trie. Returns the old value associated with this key, if it existed.
fn insert(&mut self, key: &[u8], value: &[u8]) -> Result<Option<DBValue>>; fn insert(&mut self, key: &[u8], value: &[u8]) -> Result<Option<DBValue>, H::Out, C::Error>;
/// Remove a `key` from the trie. Equivalent to making it equal to the empty /// Remove a `key` from the trie. Equivalent to making it equal to the empty
/// value. Returns the old value associated with this key, if it existed. /// value. Returns the old value associated with this key, if it existed.
fn remove(&mut self, key: &[u8]) -> Result<Option<DBValue>>; fn remove(&mut self, key: &[u8]) -> Result<Option<DBValue>, H::Out, C::Error>;
} }
/// A trie iterator that also supports random access. /// A trie iterator that also supports random access (`seek()`).
pub trait TrieIterator : Iterator { pub trait TrieIterator<H: Hasher, C: NodeCodec<H>>: Iterator {
/// Position the iterator on the first element with key > `key` /// Position the iterator on the first element with key > `key`
fn seek(&mut self, key: &[u8]) -> Result<()>; fn seek(&mut self, key: &[u8]) -> Result<(), H::Out, <C as NodeCodec<H>>::Error>;
} }
/// Trie types /// Trie types
@ -219,19 +224,21 @@ impl Default for TrieSpec {
/// Trie factory. /// Trie factory.
#[derive(Default, Clone)] #[derive(Default, Clone)]
pub struct TrieFactory { pub struct TrieFactory<H: Hasher, C: NodeCodec<H>> {
spec: TrieSpec, spec: TrieSpec,
mark_hash: PhantomData<H>,
mark_codec: PhantomData<C>,
} }
/// All different kinds of tries. /// All different kinds of tries.
/// This is used to prevent a heap allocation for every created trie. /// This is used to prevent a heap allocation for every created trie.
pub enum TrieKinds<'db> { pub enum TrieKinds<'db, H: Hasher + 'db, C: NodeCodec<H>> {
/// A generic trie db. /// A generic trie db.
Generic(TrieDB<'db>), Generic(TrieDB<'db, H, C>),
/// A secure trie db. /// A secure trie db.
Secure(SecTrieDB<'db>), Secure(SecTrieDB<'db, H, C>),
/// A fat trie db. /// A fat trie db.
Fat(FatDB<'db>), Fat(FatDB<'db, H, C>),
} }
// wrapper macro for making the match easier to deal with. // wrapper macro for making the match easier to deal with.
@ -245,8 +252,8 @@ macro_rules! wrapper {
} }
} }
impl<'db> Trie for TrieKinds<'db> { impl<'db, H: Hasher, C: NodeCodec<H>> Trie<H, C> for TrieKinds<'db, H, C> {
fn root(&self) -> &H256 { fn root(&self) -> &H::Out {
wrapper!(self, root,) wrapper!(self, root,)
} }
@ -254,31 +261,33 @@ impl<'db> Trie for TrieKinds<'db> {
wrapper!(self, is_empty,) wrapper!(self, is_empty,)
} }
fn contains(&self, key: &[u8]) -> Result<bool> { fn contains(&self, key: &[u8]) -> Result<bool, H::Out, C::Error> {
wrapper!(self, contains, key) wrapper!(self, contains, key)
} }
fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> Result<Option<Q::Item>> fn get_with<'a, 'key, Q: Query<H>>(&'a self, key: &'key [u8], query: Q) -> Result<Option<Q::Item>, H::Out, C::Error>
where 'a: 'key where 'a: 'key
{ {
wrapper!(self, get_with, key, query) wrapper!(self, get_with, key, query)
} }
fn iter<'a>(&'a self) -> Result<Box<TrieIterator<Item = TrieItem> + 'a>> { fn iter<'a>(&'a self) -> Result<Box<TrieIterator<H, C, Item = TrieItem<H::Out, C::Error>> + 'a>, H::Out, C::Error> {
wrapper!(self, iter,) wrapper!(self, iter,)
} }
} }
impl TrieFactory { impl<'db, H, C> TrieFactory<H, C>
where
H: Hasher,
C: NodeCodec<H> + 'db
{
/// Creates new factory. /// Creates new factory.
pub fn new(spec: TrieSpec) -> Self { pub fn new(spec: TrieSpec) -> Self {
TrieFactory { TrieFactory { spec, mark_hash: PhantomData, mark_codec: PhantomData }
spec: spec,
}
} }
/// Create new immutable instance of Trie. /// Create new immutable instance of Trie.
pub fn readonly<'db>(&self, db: &'db HashDB, root: &'db H256) -> Result<TrieKinds<'db>> { pub fn readonly(&self, db: &'db HashDB<H>, root: &'db H::Out) -> Result<TrieKinds<'db, H, C>, H::Out, <C as NodeCodec<H>>::Error> {
match self.spec { match self.spec {
TrieSpec::Generic => Ok(TrieKinds::Generic(TrieDB::new(db, root)?)), TrieSpec::Generic => Ok(TrieKinds::Generic(TrieDB::new(db, root)?)),
TrieSpec::Secure => Ok(TrieKinds::Secure(SecTrieDB::new(db, root)?)), TrieSpec::Secure => Ok(TrieKinds::Secure(SecTrieDB::new(db, root)?)),
@ -287,20 +296,20 @@ impl TrieFactory {
} }
/// Create new mutable instance of Trie. /// Create new mutable instance of Trie.
pub fn create<'db>(&self, db: &'db mut HashDB, root: &'db mut H256) -> Box<TrieMut + 'db> { pub fn create(&self, db: &'db mut HashDB<H>, root: &'db mut H::Out) -> Box<TrieMut<H, C> + 'db> {
match self.spec { match self.spec {
TrieSpec::Generic => Box::new(TrieDBMut::new(db, root)), TrieSpec::Generic => Box::new(TrieDBMut::<_, C>::new(db, root)),
TrieSpec::Secure => Box::new(SecTrieDBMut::new(db, root)), TrieSpec::Secure => Box::new(SecTrieDBMut::<_, C>::new(db, root)),
TrieSpec::Fat => Box::new(FatDBMut::new(db, root)), TrieSpec::Fat => Box::new(FatDBMut::<_, C>::new(db, root)),
} }
} }
/// Create new mutable instance of trie and check for errors. /// Create new mutable instance of trie and check for errors.
pub fn from_existing<'db>(&self, db: &'db mut HashDB, root: &'db mut H256) -> Result<Box<TrieMut + 'db>> { pub fn from_existing(&self, db: &'db mut HashDB<H>, root: &'db mut H::Out) -> Result<Box<TrieMut<H,C> + 'db>, H::Out, <C as NodeCodec<H>>::Error> {
match self.spec { match self.spec {
TrieSpec::Generic => Ok(Box::new(TrieDBMut::from_existing(db, root)?)), TrieSpec::Generic => Ok(Box::new(TrieDBMut::<_, C>::from_existing(db, root)?)),
TrieSpec::Secure => Ok(Box::new(SecTrieDBMut::from_existing(db, root)?)), TrieSpec::Secure => Ok(Box::new(SecTrieDBMut::<_, C>::from_existing(db, root)?)),
TrieSpec::Fat => Ok(Box::new(FatDBMut::from_existing(db, root)?)), TrieSpec::Fat => Ok(Box::new(FatDBMut::<_, C>::from_existing(db, root)?)),
} }
} }

View File

@ -16,27 +16,33 @@
//! Trie lookup via HashDB. //! Trie lookup via HashDB.
use hashdb::HashDB; use hashdb::{HashDB, Hasher};
use nibbleslice::NibbleSlice; use nibbleslice::NibbleSlice;
use ethereum_types::H256; use node::Node;
use node_codec::NodeCodec;
use super::{TrieError, Query}; use super::{Result, TrieError, Query};
use super::node::Node; use std::marker::PhantomData;
/// Trie lookup helper object. /// Trie lookup helper object.
pub struct Lookup<'a, Q: Query> { pub struct Lookup<'a, H: Hasher + 'a, C: NodeCodec<H>, Q: Query<H>> {
/// database to query from. /// database to query from.
pub db: &'a HashDB, pub db: &'a HashDB<H>,
/// Query object to record nodes and transform data. /// Query object to record nodes and transform data.
pub query: Q, pub query: Q,
/// Hash to start at /// Hash to start at
pub hash: H256, pub hash: H::Out,
pub marker: PhantomData<C>, // TODO: probably not needed when all is said and done? When Query is made generic?
} }
impl<'a, Q: Query> Lookup<'a, Q> { impl<'a, H, C, Q> Lookup<'a, H, C, Q>
where
H: Hasher + 'a,
C: NodeCodec<H> + 'a,
Q: Query<H>,
{
/// Look up the given key. If the value is found, it will be passed to the given /// Look up the given key. If the value is found, it will be passed to the given
/// function to decode or copy. /// function to decode or copy.
pub fn look_up(mut self, mut key: NibbleSlice) -> super::Result<Option<Q::Item>> { pub fn look_up(mut self, mut key: NibbleSlice) -> Result<Option<Q::Item>, H::Out, C::Error> {
let mut hash = self.hash; let mut hash = self.hash;
// this loop iterates through non-inline nodes. // this loop iterates through non-inline nodes.
@ -55,7 +61,13 @@ impl<'a, Q: Query> Lookup<'a, Q> {
// without incrementing the depth. // without incrementing the depth.
let mut node_data = &node_data[..]; let mut node_data = &node_data[..];
loop { loop {
match Node::decoded(node_data)? { let decoded = match C::decode(node_data) {
Ok(node) => node,
Err(e) => {
return Err(Box::new(TrieError::DecoderError(hash, e)))
}
};
match decoded {
Node::Leaf(slice, value) => { Node::Leaf(slice, value) => {
return Ok(match slice == key { return Ok(match slice == key {
true => Some(self.query.decode(value)), true => Some(self.query.decode(value)),
@ -81,7 +93,7 @@ impl<'a, Q: Query> Lookup<'a, Q> {
} }
// check if new node data is inline or hash. // check if new node data is inline or hash.
if let Some(h) = Node::try_decode_hash(&node_data) { if let Some(h) = C::try_decode_hash(&node_data) {
hash = h; hash = h;
break break
} }

View File

@ -105,9 +105,11 @@ impl<'a> NibbleSlice<'a> {
pub fn is_empty(&self) -> bool { self.len() == 0 } pub fn is_empty(&self) -> bool { self.len() == 0 }
/// Get the length (in nibbles, naturally) of this slice. /// Get the length (in nibbles, naturally) of this slice.
#[inline]
pub fn len(&self) -> usize { (self.data.len() + self.data_encode_suffix.len()) * 2 - self.offset - self.offset_encode_suffix } pub fn len(&self) -> usize { (self.data.len() + self.data_encode_suffix.len()) * 2 - self.offset - self.offset_encode_suffix }
/// Get the nibble at position `i`. /// Get the nibble at position `i`.
#[inline(always)]
pub fn at(&self, i: usize) -> u8 { pub fn at(&self, i: usize) -> u8 {
let l = self.data.len() * 2 - self.offset; let l = self.data.len() * 2 - self.offset;
if i < l { if i < l {
@ -154,6 +156,7 @@ impl<'a> NibbleSlice<'a> {
} }
/// Encode while nibble slice in prefixed hex notation, noting whether it `is_leaf`. /// Encode while nibble slice in prefixed hex notation, noting whether it `is_leaf`.
#[inline]
pub fn encoded(&self, is_leaf: bool) -> ElasticArray36<u8> { pub fn encoded(&self, is_leaf: bool) -> ElasticArray36<u8> {
let l = self.len(); let l = self.len();
let mut r = ElasticArray36::new(); let mut r = ElasticArray36::new();

View File

@ -41,12 +41,14 @@ impl NibbleVec {
} }
/// Length of the `NibbleVec` /// Length of the `NibbleVec`
#[inline(always)]
pub fn len(&self) -> usize { self.len } pub fn len(&self) -> usize { self.len }
/// Retrurns true if `NibbleVec` has zero length /// Retrurns true if `NibbleVec` has zero length
pub fn is_empty(&self) -> bool { self.len == 0 } pub fn is_empty(&self) -> bool { self.len == 0 }
/// Try to get the nibble at the given offset. /// Try to get the nibble at the given offset.
#[inline]
pub fn at(&self, idx: usize) -> u8 { pub fn at(&self, idx: usize) -> u8 {
if idx % 2 == 0 { if idx % 2 == 0 {
self.inner[idx / 2] >> 4 self.inner[idx / 2] >> 4

View File

@ -14,12 +14,9 @@
// 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 ethereum_types::H256;
use elastic_array::ElasticArray36; use elastic_array::ElasticArray36;
use nibbleslice::NibbleSlice; use nibbleslice::NibbleSlice;
use nibblevec::NibbleVec; use nibblevec::NibbleVec;
use bytes::*;
use rlp::{Rlp, RlpStream, Prototype, DecoderError};
use hashdb::DBValue; use hashdb::DBValue;
/// Partial node key type. /// Partial node key type.
@ -35,83 +32,7 @@ pub enum Node<'a> {
/// Extension node; has key slice and node data. Data may not be null. /// Extension node; has key slice and node data. Data may not be null.
Extension(NibbleSlice<'a>, &'a [u8]), Extension(NibbleSlice<'a>, &'a [u8]),
/// Branch node; has array of 16 child nodes (each possibly null) and an optional immediate node data. /// Branch node; has array of 16 child nodes (each possibly null) and an optional immediate node data.
Branch([&'a [u8]; 16], Option<&'a [u8]>) Branch([&'a [u8]; 16], Option<&'a [u8]>),
}
impl<'a> Node<'a> {
/// Decode the `node_rlp` and return the Node.
pub fn decoded(node_rlp: &'a [u8]) -> Result<Self, DecoderError> {
let r = Rlp::new(node_rlp);
match r.prototype()? {
// either leaf or extension - decode first item with NibbleSlice::???
// and use is_leaf return to figure out which.
// if leaf, second item is a value (is_data())
// if extension, second item is a node (either SHA3 to be looked up and
// fed back into this function or inline RLP which can be fed back into this function).
Prototype::List(2) => match NibbleSlice::from_encoded(r.at(0)?.data()?) {
(slice, true) => Ok(Node::Leaf(slice, r.at(1)?.data()?)),
(slice, false) => Ok(Node::Extension(slice, r.at(1)?.as_raw())),
},
// branch - first 16 are nodes, 17th is a value (or empty).
Prototype::List(17) => {
let mut nodes = [&[] as &[u8]; 16];
for i in 0..16 {
nodes[i] = r.at(i)?.as_raw();
}
Ok(Node::Branch(nodes, if r.at(16)?.is_empty() { None } else { Some(r.at(16)?.data()?) }))
},
// an empty branch index.
Prototype::Data(0) => Ok(Node::Empty),
// something went wrong.
_ => Err(DecoderError::Custom("Rlp is not valid."))
}
}
/// Encode the node into RLP.
///
/// Will always return the direct node RLP even if it's 32 or more bytes. To get the
/// RLP which would be valid for using in another node, use `encoded_and_added()`.
pub fn encoded(&self) -> Bytes {
match *self {
Node::Leaf(ref slice, ref value) => {
let mut stream = RlpStream::new_list(2);
stream.append(&&*slice.encoded(true));
stream.append(value);
stream.out()
},
Node::Extension(ref slice, ref raw_rlp) => {
let mut stream = RlpStream::new_list(2);
stream.append(&&*slice.encoded(false));
stream.append_raw(raw_rlp, 1);
stream.out()
},
Node::Branch(ref nodes, ref value) => {
let mut stream = RlpStream::new_list(17);
for i in 0..16 {
stream.append_raw(nodes[i], 1);
}
match *value {
Some(ref n) => { stream.append(n); },
None => { stream.append_empty_data(); },
}
stream.out()
},
Node::Empty => {
let mut stream = RlpStream::new();
stream.append_empty_data();
stream.out()
}
}
}
pub fn try_decode_hash(node_data: &[u8]) -> Option<H256> {
let r = Rlp::new(node_data);
if r.is_data() && r.size() == 32 {
Some(r.as_val().expect("Hash is the correct size of 32 bytes; qed"))
} else {
None
}
}
} }
/// An owning node type. Useful for trie iterators. /// An owning node type. Useful for trie iterators.

View File

@ -0,0 +1,55 @@
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Generic trait for trie node encoding/decoding. Takes a `hashdb::Hasher`
//! to parametrize the hashes used in the codec.
use hashdb::Hasher;
use node::Node;
use ChildReference;
use elastic_array::{ElasticArray1024, ElasticArray128};
/// Trait for trie node encoding/decoding
pub trait NodeCodec<H: Hasher>: Sized {
/// Encoding error type
type Error: ::std::error::Error;
/// Null node type
const HASHED_NULL_NODE: H::Out;
/// Decode bytes to a `Node`. Returns `Self::E` on failure.
fn decode(data: &[u8]) -> Result<Node, Self::Error>;
/// Decode bytes to the `Hasher`s output type. Returns `None` on failure.
fn try_decode_hash(data: &[u8]) -> Option<H::Out>;
/// Check if the provided bytes correspond to the codecs "empty" node.
fn is_empty_node(data: &[u8]) -> bool;
/// Returns an empty node
fn empty_node() -> ElasticArray1024<u8>;
/// Returns an encoded leaft node
fn leaf_node(partial: &[u8], value: &[u8]) -> ElasticArray1024<u8>;
/// Returns an encoded extension node
fn ext_node(partial: &[u8], child_ref: ChildReference<H::Out>) -> ElasticArray1024<u8>;
/// Returns an encoded branch node. Takes an iterator yielding `ChildReference<H::Out>` and an optional value
fn branch_node<I>(children: I, value: Option<ElasticArray128<u8>>) -> ElasticArray1024<u8>
where I: IntoIterator<Item=Option<ChildReference<H::Out>>>;
}

View File

@ -16,13 +16,11 @@
//! Trie query recorder. //! Trie query recorder.
use keccak::keccak;
use ethereum_types::H256;
use bytes::Bytes; use bytes::Bytes;
/// A record of a visited node. /// A record of a visited node.
#[derive(PartialEq, Eq, Debug, Clone)] #[derive(PartialEq, Eq, Debug, Clone)]
pub struct Record { pub struct Record<HO> {
/// The depth of this node. /// The depth of this node.
pub depth: u32, pub depth: u32,
@ -30,23 +28,23 @@ pub struct Record {
pub data: Bytes, pub data: Bytes,
/// The hash of the data. /// The hash of the data.
pub hash: H256, pub hash: HO,
} }
/// Records trie nodes as they pass it. /// Records trie nodes as they pass it.
#[derive(Debug)] #[derive(Debug)]
pub struct Recorder { pub struct Recorder<HO> {
nodes: Vec<Record>, nodes: Vec<Record<HO>>,
min_depth: u32, min_depth: u32,
} }
impl Default for Recorder { impl<HO: Copy> Default for Recorder<HO> {
fn default() -> Self { fn default() -> Self {
Recorder::new() Recorder::new()
} }
} }
impl Recorder { impl<HO: Copy> Recorder<HO> {
/// Create a new `Recorder` which records all given nodes. /// Create a new `Recorder` which records all given nodes.
#[inline] #[inline]
pub fn new() -> Self { pub fn new() -> Self {
@ -62,9 +60,7 @@ impl Recorder {
} }
/// Record a visited node, given its hash, data, and depth. /// Record a visited node, given its hash, data, and depth.
pub fn record(&mut self, hash: &H256, data: &[u8], depth: u32) { pub fn record(&mut self, hash: &HO, data: &[u8], depth: u32) {
debug_assert_eq!(keccak(data), *hash);
if depth >= self.min_depth { if depth >= self.min_depth {
self.nodes.push(Record { self.nodes.push(Record {
depth: depth, depth: depth,
@ -75,7 +71,7 @@ impl Recorder {
} }
/// Drain all visited records. /// Drain all visited records.
pub fn drain(&mut self) -> Vec<Record> { pub fn drain(&mut self) -> Vec<Record<HO>> {
::std::mem::replace(&mut self.nodes, Vec::new()) ::std::mem::replace(&mut self.nodes, Vec::new())
} }
} }
@ -83,11 +79,13 @@ impl Recorder {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use keccak::keccak;
use keccak_hasher::KeccakHasher;
use ethereum_types::H256; use ethereum_types::H256;
#[test] #[test]
fn basic_recorder() { fn basic_recorder() {
let mut basic = Recorder::new(); let mut basic = Recorder::<H256>::new();
let node1 = vec![1, 2, 3, 4]; let node1 = vec![1, 2, 3, 4];
let node2 = vec![4, 5, 6, 7, 8, 9, 10]; let node2 = vec![4, 5, 6, 7, 8, 9, 10];
@ -105,15 +103,16 @@ mod tests {
let record2 = Record { let record2 = Record {
data: node2, data: node2,
hash: hash2, hash: hash2,
depth: 456 depth: 456,
}; };
assert_eq!(basic.drain(), vec![record1, record2]); assert_eq!(basic.drain(), vec![record1, record2]);
} }
#[test] #[test]
fn basic_recorder_min_depth() { fn basic_recorder_min_depth() {
let mut basic = Recorder::with_depth(400); let mut basic = Recorder::<H256>::with_depth(400);
let node1 = vec![1, 2, 3, 4]; let node1 = vec![1, 2, 3, 4];
let node2 = vec![4, 5, 6, 7, 8, 9, 10]; let node2 = vec![4, 5, 6, 7, 8, 9, 10];
@ -136,10 +135,11 @@ mod tests {
#[test] #[test]
fn trie_record() { fn trie_record() {
use super::super::{TrieDB, TrieDBMut, Trie, TrieMut}; use ethtrie::trie::{Trie, TrieMut, Recorder};
use memorydb::MemoryDB; use memorydb::MemoryDB;
use ethtrie::{TrieDB, TrieDBMut};
let mut db = MemoryDB::new(); let mut db = MemoryDB::<KeccakHasher>::new();
let mut root = H256::default(); let mut root = H256::default();
@ -157,7 +157,7 @@ mod tests {
} }
let trie = TrieDB::new(&db, &root).unwrap(); let trie = TrieDB::new(&db, &root).unwrap();
let mut recorder = Recorder::new(); let mut recorder = Recorder::<H256>::new();
trie.get_with(b"pirate", &mut recorder).unwrap().unwrap(); trie.get_with(b"pirate", &mut recorder).unwrap().unwrap();

View File

@ -14,71 +14,87 @@
// 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 ethereum_types::H256; use hashdb::{HashDB, Hasher};
use keccak::keccak;
use hashdb::HashDB;
use super::triedb::TrieDB; use super::triedb::TrieDB;
use super::{Trie, TrieItem, TrieIterator, Query}; use super::{Result, Trie, TrieItem, TrieIterator, Query};
use node_codec::NodeCodec;
/// A `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. /// A `Trie` implementation which hashes keys and uses a generic `HashDB` backing database.
/// ///
/// Use it as a `Trie` trait object. You can use `raw()` to get the backing `TrieDB` object. /// Use it as a `Trie` trait object. You can use `raw()` to get the backing `TrieDB` object.
pub struct SecTrieDB<'db> { pub struct SecTrieDB<'db, H, C>
raw: TrieDB<'db> where
H: Hasher + 'db,
C: NodeCodec<H>
{
raw: TrieDB<'db, H, C>
} }
impl<'db> SecTrieDB<'db> { impl<'db, H, C> SecTrieDB<'db, H, C>
where
H: Hasher,
C: NodeCodec<H>
{
/// Create a new trie with the backing database `db` and empty `root` /// Create a new trie with the backing database `db` and empty `root`
/// ///
/// Initialise to the state entailed by the genesis block. /// Initialise to the state entailed by the genesis block.
/// This guarantees the trie is built correctly. /// This guarantees the trie is built correctly.
/// Returns an error if root does not exist. /// Returns an error if root does not exist.
pub fn new(db: &'db HashDB, root: &'db H256) -> super::Result<Self> { pub fn new(db: &'db HashDB<H>, root: &'db H::Out) -> Result<Self, H::Out, C::Error> {
Ok(SecTrieDB { raw: TrieDB::new(db, root)? }) Ok(SecTrieDB { raw: TrieDB::new(db, root)? })
} }
/// Get a reference to the underlying raw `TrieDB` struct. /// Get a reference to the underlying raw `TrieDB` struct.
pub fn raw(&self) -> &TrieDB { pub fn raw(&self) -> &TrieDB<H, C> {
&self.raw &self.raw
} }
/// Get a mutable reference to the underlying raw `TrieDB` struct. /// Get a mutable reference to the underlying raw `TrieDB` struct.
pub fn raw_mut(&mut self) -> &mut TrieDB<'db> { pub fn raw_mut(&mut self) -> &mut TrieDB<'db, H, C> {
&mut self.raw &mut self.raw
} }
} }
impl<'db> Trie for SecTrieDB<'db> { impl<'db, H, C> Trie<H, C> for SecTrieDB<'db, H, C>
fn iter<'a>(&'a self) -> super::Result<Box<TrieIterator<Item = TrieItem> + 'a>> { where
TrieDB::iter(&self.raw) H: Hasher,
C: NodeCodec<H>
{
fn root(&self) -> &H::Out { self.raw.root() }
fn contains(&self, key: &[u8]) -> Result<bool, H::Out, C::Error> {
self.raw.contains(H::hash(key).as_ref())
} }
fn root(&self) -> &H256 { self.raw.root() } fn get_with<'a, 'key, Q: Query<H>>(&'a self, key: &'key [u8], query: Q) -> Result<Option<Q::Item>, H::Out, C::Error>
fn contains(&self, key: &[u8]) -> super::Result<bool> {
self.raw.contains(&keccak(key))
}
fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> super::Result<Option<Q::Item>>
where 'a: 'key where 'a: 'key
{ {
self.raw.get_with(&keccak(key), query) self.raw.get_with(H::hash(key).as_ref(), query)
}
fn iter<'a>(&'a self) -> Result<Box<TrieIterator<H, C, Item = TrieItem<H::Out, C::Error>> + 'a>, H::Out, C::Error> {
TrieDB::iter(&self.raw)
} }
} }
#[test] #[cfg(test)]
fn trie_to_sectrie() { mod test {
use memorydb::MemoryDB; use memorydb::MemoryDB;
use hashdb::DBValue; use hashdb::DBValue;
use super::triedbmut::TrieDBMut; use keccak;
use super::TrieMut; use keccak_hasher::KeccakHasher;
use ethtrie::{TrieDBMut, SecTrieDB, trie::{Trie, TrieMut}};
use ethereum_types::H256;
let mut memdb = MemoryDB::new(); #[test]
let mut root = H256::default(); fn trie_to_sectrie() {
{ let mut db = MemoryDB::<KeccakHasher>::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root); let mut root = H256::new();
t.insert(&keccak(&[0x01u8, 0x23]), &[0x01u8, 0x23]).unwrap(); {
let mut t = TrieDBMut::new(&mut db, &mut root);
t.insert(&keccak::keccak(&[0x01u8, 0x23]), &[0x01u8, 0x23]).unwrap();
}
let t = SecTrieDB::new(&db, &root).unwrap();
assert_eq!(t.get(&[0x01u8, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23]));
} }
let t = SecTrieDB::new(&memdb, &root).unwrap(); }
assert_eq!(t.get(&[0x01u8, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23]));
}

View File

@ -14,43 +14,53 @@
// 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 ethereum_types::H256; use hashdb::{HashDB, DBValue, Hasher};
use keccak::keccak; use super::{Result, TrieMut, TrieDBMut};
use hashdb::{HashDB, DBValue}; use node_codec::NodeCodec;
use super::triedbmut::TrieDBMut;
use super::TrieMut;
/// A mutable `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. /// A mutable `Trie` implementation which hashes keys and uses a generic `HashDB` backing database.
/// ///
/// Use it as a `Trie` or `TrieMut` trait object. You can use `raw()` to get the backing `TrieDBMut` object. /// Use it as a `Trie` or `TrieMut` trait object. You can use `raw()` to get the backing `TrieDBMut` object.
pub struct SecTrieDBMut<'db> { pub struct SecTrieDBMut<'db, H, C>
raw: TrieDBMut<'db> where
H: Hasher + 'db,
C: NodeCodec<H>
{
raw: TrieDBMut<'db, H, C>
} }
impl<'db> SecTrieDBMut<'db> { impl<'db, H, C> SecTrieDBMut<'db, H, C>
where
H: Hasher,
C: NodeCodec<H>
{
/// Create a new trie with the backing database `db` and empty `root` /// Create a new trie with the backing database `db` and empty `root`
/// Initialise to the state entailed by the genesis block. /// Initialise to the state entailed by the genesis block.
/// This guarantees the trie is built correctly. /// This guarantees the trie is built correctly.
pub fn new(db: &'db mut HashDB, root: &'db mut H256) -> Self { pub fn new(db: &'db mut HashDB<H>, root: &'db mut H::Out) -> Self {
SecTrieDBMut { raw: TrieDBMut::new(db, root) } SecTrieDBMut { raw: TrieDBMut::new(db, root) }
} }
/// Create a new trie with the backing database `db` and `root`. /// Create a new trie with the backing database `db` and `root`.
/// ///
/// Returns an error if root does not exist. /// Returns an error if root does not exist.
pub fn from_existing(db: &'db mut HashDB, root: &'db mut H256) -> super::Result<Self> { pub fn from_existing(db: &'db mut HashDB<H>, root: &'db mut H::Out) -> Result<Self, H::Out, C::Error> {
Ok(SecTrieDBMut { raw: TrieDBMut::from_existing(db, root)? }) Ok(SecTrieDBMut { raw: TrieDBMut::from_existing(db, root)? })
} }
/// Get the backing database. /// Get the backing database.
pub fn db(&self) -> &HashDB { self.raw.db() } pub fn db(&self) -> &HashDB<H> { self.raw.db() }
/// Get the backing database. /// Get the backing database.
pub fn db_mut(&mut self) -> &mut HashDB { self.raw.db_mut() } pub fn db_mut(&mut self) -> &mut HashDB<H> { self.raw.db_mut() }
} }
impl<'db> TrieMut for SecTrieDBMut<'db> { impl<'db, H, C> TrieMut<H, C> for SecTrieDBMut<'db, H, C>
fn root(&mut self) -> &H256 { where
H: Hasher,
C: NodeCodec<H>
{
fn root(&mut self) -> &H::Out {
self.raw.root() self.raw.root()
} }
@ -58,37 +68,43 @@ impl<'db> TrieMut for SecTrieDBMut<'db> {
self.raw.is_empty() self.raw.is_empty()
} }
fn contains(&self, key: &[u8]) -> super::Result<bool> { fn contains(&self, key: &[u8]) -> Result<bool, H::Out, C::Error> {
self.raw.contains(&keccak(key)) self.raw.contains(&H::hash(key).as_ref())
} }
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> super::Result<Option<DBValue>> fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result<Option<DBValue>, H::Out, C::Error>
where 'a: 'key where 'a: 'key
{ {
self.raw.get(&keccak(key)) self.raw.get(&H::hash(key).as_ref())
} }
fn insert(&mut self, key: &[u8], value: &[u8]) -> super::Result<Option<DBValue>> { fn insert(&mut self, key: &[u8], value: &[u8]) -> Result<Option<DBValue>, H::Out, C::Error> {
self.raw.insert(&keccak(key), value) self.raw.insert(&H::hash(key).as_ref(), value)
} }
fn remove(&mut self, key: &[u8]) -> super::Result<Option<DBValue>> { fn remove(&mut self, key: &[u8]) -> Result<Option<DBValue>, H::Out, C::Error> {
self.raw.remove(&keccak(key)) self.raw.remove(&H::hash(key).as_ref())
} }
} }
#[test] #[cfg(test)]
fn sectrie_to_trie() { mod test {
use memorydb::*; use memorydb::MemoryDB;
use super::triedb::*; use hashdb::DBValue;
use super::Trie; use keccak;
use keccak_hasher::KeccakHasher;
use ethtrie::{TrieDB, SecTrieDBMut, trie::{Trie, TrieMut}};
use ethereum_types::H256;
let mut memdb = MemoryDB::new(); #[test]
let mut root = H256::default(); fn sectrie_to_trie() {
{ let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut t = SecTrieDBMut::new(&mut memdb, &mut root); let mut root = H256::new();
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); {
let mut t = SecTrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
}
let t = TrieDB::new(&memdb, &root).unwrap();
assert_eq!(t.get(&keccak::keccak(&[0x01u8, 0x23])).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23]));
} }
let t = TrieDB::new(&memdb, &root).unwrap();
assert_eq!(t.get(&keccak(&[0x01u8, 0x23])).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23]));
} }

View File

@ -18,12 +18,15 @@ use std::fmt;
use hashdb::*; use hashdb::*;
use nibbleslice::NibbleSlice; use nibbleslice::NibbleSlice;
use super::node::{Node, OwnedNode}; use super::node::{Node, OwnedNode};
use node_codec::NodeCodec;
use super::lookup::Lookup; use super::lookup::Lookup;
use super::{Trie, TrieItem, TrieError, TrieIterator, Query}; use super::{Result, Trie, TrieItem, TrieError, TrieIterator, Query};
use ethereum_types::H256;
use bytes::Bytes; use bytes::Bytes;
use std::marker::PhantomData;
/// A `Trie` implementation using a generic `HashDB` backing database. /// A `Trie` implementation using a generic `HashDB` backing database, a `Hasher`
/// implementation to generate keys and a `NodeCodec` implementation to encode/decode
/// the nodes.
/// ///
/// Use it as a `Trie` trait object. You can use `db()` to get the backing database object. /// Use it as a `Trie` trait object. You can use `db()` to get the backing database object.
/// Use `get` and `contains` to query values associated with keys in the trie. /// Use `get` and `contains` to query values associated with keys in the trie.
@ -31,17 +34,22 @@ use bytes::Bytes;
/// # Example /// # Example
/// ``` /// ```
/// extern crate patricia_trie as trie; /// extern crate patricia_trie as trie;
/// extern crate patricia_trie_ethereum as ethtrie;
/// extern crate hashdb; /// extern crate hashdb;
/// extern crate keccak_hasher;
/// extern crate memorydb; /// extern crate memorydb;
/// extern crate ethereum_types; /// extern crate ethereum_types;
/// ///
/// use trie::*; /// use trie::*;
/// use hashdb::*; /// use hashdb::*;
/// use keccak_hasher::KeccakHasher;
/// use memorydb::*; /// use memorydb::*;
/// use ethereum_types::H256; /// use ethereum_types::H256;
/// use ethtrie::{TrieDB, TrieDBMut};
///
/// ///
/// fn main() { /// fn main() {
/// let mut memdb = MemoryDB::new(); /// let mut memdb = MemoryDB::<KeccakHasher>::new();
/// let mut root = H256::new(); /// let mut root = H256::new();
/// TrieDBMut::new(&mut memdb, &mut root).insert(b"foo", b"bar").unwrap(); /// TrieDBMut::new(&mut memdb, &mut root).insert(b"foo", b"bar").unwrap();
/// let t = TrieDB::new(&memdb, &root).unwrap(); /// let t = TrieDB::new(&memdb, &root).unwrap();
@ -49,35 +57,38 @@ use bytes::Bytes;
/// assert_eq!(t.get(b"foo").unwrap().unwrap(), DBValue::from_slice(b"bar")); /// assert_eq!(t.get(b"foo").unwrap().unwrap(), DBValue::from_slice(b"bar"));
/// } /// }
/// ``` /// ```
pub struct TrieDB<'db> { pub struct TrieDB<'db, H, C>
db: &'db HashDB, where
root: &'db H256, H: Hasher + 'db,
C: NodeCodec<H>
{
db: &'db HashDB<H>,
root: &'db H::Out,
/// The number of hashes performed so far in operations on this trie. /// The number of hashes performed so far in operations on this trie.
hash_count: usize, hash_count: usize,
codec_marker: PhantomData<C>,
} }
impl<'db> TrieDB<'db> { impl<'db, H, C> TrieDB<'db, H, C>
where
H: Hasher,
C: NodeCodec<H>
{
/// Create a new trie with the backing database `db` and `root` /// Create a new trie with the backing database `db` and `root`
/// Returns an error if `root` does not exist /// Returns an error if `root` does not exist
pub fn new(db: &'db HashDB, root: &'db H256) -> super::Result<Self> { pub fn new(db: &'db HashDB<H>, root: &'db H::Out) -> Result<Self, H::Out, C::Error> {
if !db.contains(root) { if !db.contains(root) {
Err(Box::new(TrieError::InvalidStateRoot(*root))) Err(Box::new(TrieError::InvalidStateRoot(*root)))
} else { } else {
Ok(TrieDB { Ok(TrieDB {db, root, hash_count: 0, codec_marker: PhantomData})
db: db,
root: root,
hash_count: 0
})
} }
} }
/// Get the backing database. /// Get the backing database.
pub fn db(&'db self) -> &'db HashDB { pub fn db(&'db self) -> &'db HashDB<H> { self.db }
self.db
}
/// Get the data of the root node. /// Get the data of the root node.
fn root_data(&self) -> super::Result<DBValue> { fn root_data(&self) -> Result<DBValue, H::Out, C::Error> {
self.db self.db
.get(self.root) .get(self.root)
.ok_or_else(|| Box::new(TrieError::InvalidStateRoot(*self.root))) .ok_or_else(|| Box::new(TrieError::InvalidStateRoot(*self.root)))
@ -86,49 +97,57 @@ impl<'db> TrieDB<'db> {
/// Given some node-describing data `node`, return the actual node RLP. /// Given some node-describing data `node`, return the actual node RLP.
/// This could be a simple identity operation in the case that the node is sufficiently small, but /// This could be a simple identity operation in the case that the node is sufficiently small, but
/// may require a database lookup. /// may require a database lookup.
fn get_raw_or_lookup(&'db self, node: &'db [u8]) -> super::Result<DBValue> { fn get_raw_or_lookup(&'db self, node: &'db [u8]) -> Result<DBValue, H::Out, C::Error> {
match Node::try_decode_hash(node) { match C::try_decode_hash(node) {
Some(key) => { Some(key) => {
self.db.get(&key).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(key))) self.db.get(&key).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(key)))
} }
None => Ok(DBValue::from_slice(node)) None => Ok(DBValue::from_slice(node))
} }
} }
/// Create a node from raw rlp bytes, assumes valid rlp because encoded locally
fn decode_node(node: &'db [u8]) -> Node {
Node::decoded(node).expect("rlp read from db; qed")
}
} }
impl<'db> Trie for TrieDB<'db> { impl<'db, H, C> Trie<H, C> for TrieDB<'db, H, C>
fn iter<'a>(&'a self) -> super::Result<Box<TrieIterator<Item = TrieItem> + 'a>> { where
TrieDBIterator::new(self).map(|iter| Box::new(iter) as Box<_>) H: Hasher,
} C: NodeCodec<H>
{
fn root(&self) -> &H::Out { self.root }
fn root(&self) -> &H256 { self.root } fn get_with<'a, 'key, Q: Query<H>>(&'a self, key: &'key [u8], query: Q) -> Result<Option<Q::Item>, H::Out, C::Error>
fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> super::Result<Option<Q::Item>>
where 'a: 'key where 'a: 'key
{ {
Lookup { Lookup {
db: self.db, db: self.db,
query: query, query: query,
hash: self.root.clone(), hash: self.root.clone(),
marker: PhantomData::<C>,
}.look_up(NibbleSlice::new(key)) }.look_up(NibbleSlice::new(key))
} }
fn iter<'a>(&'a self) -> Result<Box<TrieIterator<H, C, Item=TrieItem<H::Out, C::Error>> + 'a>, H::Out, C::Error> {
TrieDBIterator::new(self).map(|iter| Box::new(iter) as Box<_>)
}
} }
// This is for pretty debug output only // This is for pretty debug output only
struct TrieAwareDebugNode<'db, 'a> { struct TrieAwareDebugNode<'db, 'a, H, C>
trie: &'db TrieDB<'db>, where
H: Hasher + 'db,
C: NodeCodec<H> + 'db
{
trie: &'db TrieDB<'db, H, C>,
key: &'a[u8] key: &'a[u8]
} }
impl<'db, 'a> fmt::Debug for TrieAwareDebugNode<'db, 'a> { impl<'db, 'a, H, C> fmt::Debug for TrieAwareDebugNode<'db, 'a, H, C>
where
H: Hasher,
C: NodeCodec<H>
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Ok(node) = self.trie.get_raw_or_lookup(self.key) { if let Ok(node) = self.trie.get_raw_or_lookup(self.key) {
match Node::decoded(&node) { match C::decode(&node) {
Ok(Node::Leaf(slice, value)) => f.debug_struct("Node::Leaf") Ok(Node::Leaf(slice, value)) => f.debug_struct("Node::Leaf")
.field("slice", &slice) .field("slice", &slice)
.field("value", &value) .field("value", &value)
@ -138,7 +157,7 @@ impl<'db, 'a> fmt::Debug for TrieAwareDebugNode<'db, 'a> {
.field("item", &TrieAwareDebugNode{trie: self.trie, key: item}) .field("item", &TrieAwareDebugNode{trie: self.trie, key: item})
.finish(), .finish(),
Ok(Node::Branch(ref nodes, ref value)) => { Ok(Node::Branch(ref nodes, ref value)) => {
let nodes: Vec<TrieAwareDebugNode> = nodes.into_iter().map(|n| TrieAwareDebugNode{trie: self.trie, key: n} ).collect(); let nodes: Vec<TrieAwareDebugNode<H, C>> = nodes.into_iter().map(|n| TrieAwareDebugNode{trie: self.trie, key: n} ).collect();
f.debug_struct("Node::Branch") f.debug_struct("Node::Branch")
.field("nodes", &nodes) .field("nodes", &nodes)
.field("value", &value) .field("value", &value)
@ -160,8 +179,11 @@ impl<'db, 'a> fmt::Debug for TrieAwareDebugNode<'db, 'a> {
} }
} }
impl<'db, H, C> fmt::Debug for TrieDB<'db, H, C>
impl<'db> fmt::Debug for TrieDB<'db> { where
H: Hasher,
C: NodeCodec<H>
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let root_rlp = self.db.get(self.root).expect("Trie root not found!"); let root_rlp = self.db.get(self.root).expect("Trie root not found!");
f.debug_struct("TrieDB") f.debug_struct("TrieDB")
@ -202,29 +224,24 @@ impl Crumb {
} }
/// Iterator for going through all values in the trie. /// Iterator for going through all values in the trie.
pub struct TrieDBIterator<'a> { pub struct TrieDBIterator<'a, H: Hasher + 'a, C: NodeCodec<H> + 'a> {
db: &'a TrieDB<'a>, db: &'a TrieDB<'a, H, C>,
trail: Vec<Crumb>, trail: Vec<Crumb>,
key_nibbles: Bytes, key_nibbles: Bytes,
} }
impl<'a> TrieDBIterator<'a> { impl<'a, H: Hasher, C: NodeCodec<H>> TrieDBIterator<'a, H, C> {
/// Create a new iterator. /// Create a new iterator.
pub fn new(db: &'a TrieDB) -> super::Result<TrieDBIterator<'a>> { pub fn new(db: &'a TrieDB<H, C>) -> Result<TrieDBIterator<'a, H, C>, H::Out, C::Error> {
let mut r = TrieDBIterator { let mut r = TrieDBIterator { db, trail: Vec::with_capacity(8), key_nibbles: Vec::with_capacity(64) };
db: db,
trail: vec![],
key_nibbles: Vec::new(),
};
db.root_data().and_then(|root| r.descend(&root))?; db.root_data().and_then(|root| r.descend(&root))?;
Ok(r) Ok(r)
} }
fn seek<'key>(&mut self, mut node_data: DBValue, mut key: NibbleSlice<'key>) -> super::Result<()> { fn seek<'key>(&mut self, mut node_data: DBValue, mut key: NibbleSlice<'key>) -> Result<(), H::Out, C::Error> {
loop { loop {
let (data, mid) = { let (data, mid) = {
let node = TrieDB::decode_node(&node_data); let node = C::decode(&node_data).expect("encoded data read from db; qed");
match node { match node {
Node::Leaf(slice, _) => { Node::Leaf(slice, _) => {
if slice == key { if slice == key {
@ -285,17 +302,15 @@ impl<'a> TrieDBIterator<'a> {
} }
/// Descend into a payload. /// Descend into a payload.
fn descend(&mut self, d: &[u8]) -> super::Result<()> { fn descend(&mut self, d: &[u8]) -> Result<(), H::Out, C::Error> {
let node = TrieDB::decode_node(&self.db.get_raw_or_lookup(d)?).into(); let node_data = &self.db.get_raw_or_lookup(d)?;
Ok(self.descend_into_node(node)) let node = C::decode(&node_data).expect("encoded node read from db; qed");
Ok(self.descend_into_node(node.into()))
} }
/// Descend into a payload. /// Descend into a payload.
fn descend_into_node(&mut self, node: OwnedNode) { fn descend_into_node(&mut self, node: OwnedNode) {
self.trail.push(Crumb { self.trail.push(Crumb { status: Status::Entering, node });
status: Status::Entering,
node: node,
});
match &self.trail.last().expect("just pushed item; qed").node { match &self.trail.last().expect("just pushed item; qed").node {
&OwnedNode::Leaf(ref n, _) | &OwnedNode::Extension(ref n, _) => { &OwnedNode::Leaf(ref n, _) | &OwnedNode::Extension(ref n, _) => {
self.key_nibbles.extend((0..n.len()).map(|i| n.at(i))); self.key_nibbles.extend((0..n.len()).map(|i| n.at(i)));
@ -319,26 +334,25 @@ impl<'a> TrieDBIterator<'a> {
} }
} }
impl<'a> TrieIterator for TrieDBIterator<'a> { impl<'a, H: Hasher, C: NodeCodec<H>> TrieIterator<H, C> for TrieDBIterator<'a, H, C> {
/// Position the iterator on the first element with key >= `key` /// Position the iterator on the first element with key >= `key`
fn seek(&mut self, key: &[u8]) -> super::Result<()> { fn seek(&mut self, key: &[u8]) -> Result<(), H::Out, C::Error> {
self.trail.clear(); self.trail.clear();
self.key_nibbles.clear(); self.key_nibbles.clear();
let root_rlp = self.db.root_data()?; let root_rlp = self.db.root_data()?;
self.seek(root_rlp, NibbleSlice::new(key)) self.seek(root_rlp, NibbleSlice::new(key.as_ref()))
} }
} }
impl<'a> Iterator for TrieDBIterator<'a> { impl<'a, H: Hasher, C: NodeCodec<H>> Iterator for TrieDBIterator<'a, H, C> {
type Item = TrieItem<'a>; type Item = TrieItem<'a, H::Out, C::Error>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
enum IterStep { enum IterStep<O, E> {
Continue, Continue,
PopTrail, PopTrail,
Descend(super::Result<DBValue>), Descend(Result<DBValue, O, E>),
} }
loop { loop {
let iter_step = { let iter_step = {
self.trail.last_mut()?.increment(); self.trail.last_mut()?.increment();
@ -359,7 +373,9 @@ impl<'a> Iterator for TrieDBIterator<'a> {
(Status::At, &OwnedNode::Leaf(_, ref v)) | (Status::At, &OwnedNode::Branch(_, Some(ref v))) => { (Status::At, &OwnedNode::Leaf(_, ref v)) | (Status::At, &OwnedNode::Branch(_, Some(ref v))) => {
return Some(Ok((self.key(), v.clone()))); return Some(Ok((self.key(), v.clone())));
}, },
(Status::At, &OwnedNode::Extension(_, ref d)) => IterStep::Descend(self.db.get_raw_or_lookup(&*d)), (Status::At, &OwnedNode::Extension(_, ref d)) => {
IterStep::Descend::<H::Out, C::Error>(self.db.get_raw_or_lookup(&*d))
},
(Status::At, &OwnedNode::Branch(_, _)) => IterStep::Continue, (Status::At, &OwnedNode::Branch(_, _)) => IterStep::Continue,
(Status::AtChild(i), &OwnedNode::Branch(ref children, _)) if children[i].len() > 0 => { (Status::AtChild(i), &OwnedNode::Branch(ref children, _)) if children[i].len() > 0 => {
match i { match i {
@ -367,7 +383,7 @@ impl<'a> Iterator for TrieDBIterator<'a> {
i => *self.key_nibbles.last_mut() i => *self.key_nibbles.last_mut()
.expect("pushed as 0; moves sequentially; removed afterwards; qed") = i as u8, .expect("pushed as 0; moves sequentially; removed afterwards; qed") = i as u8,
} }
IterStep::Descend(self.db.get_raw_or_lookup(&*children[i])) IterStep::Descend::<H::Out, C::Error>(self.db.get_raw_or_lookup(&*children[i]))
}, },
(Status::AtChild(i), &OwnedNode::Branch(_, _)) => { (Status::AtChild(i), &OwnedNode::Branch(_, _)) => {
if i == 0 { if i == 0 {
@ -383,10 +399,11 @@ impl<'a> Iterator for TrieDBIterator<'a> {
IterStep::PopTrail => { IterStep::PopTrail => {
self.trail.pop(); self.trail.pop();
}, },
IterStep::Descend(Ok(d)) => { IterStep::Descend::<H::Out, C::Error>(Ok(d)) => {
self.descend_into_node(TrieDB::decode_node(&d).into()) let node = C::decode(&d).expect("encoded data read from db; qed");
self.descend_into_node(node.into())
}, },
IterStep::Descend(Err(e)) => { IterStep::Descend::<H::Out, C::Error>(Err(e)) => {
return Some(Err(e)) return Some(Err(e))
} }
IterStep::Continue => {}, IterStep::Continue => {},
@ -395,115 +412,106 @@ impl<'a> Iterator for TrieDBIterator<'a> {
} }
} }
#[test] #[cfg(test)]
fn iterator() { mod tests {
use memorydb::*; use hashdb::DBValue;
use super::TrieMut; use keccak_hasher::KeccakHasher;
use super::triedbmut::*; use memorydb::MemoryDB;
use ethtrie::{TrieDB, TrieDBMut, RlpCodec, trie::{Trie, TrieMut, Lookup}};
use ethereum_types::H256;
let d = vec![ DBValue::from_slice(b"A"), DBValue::from_slice(b"AA"), DBValue::from_slice(b"AB"), DBValue::from_slice(b"B") ]; #[test]
fn iterator() {
let d = vec![DBValue::from_slice(b"A"), DBValue::from_slice(b"AA"), DBValue::from_slice(b"AB"), DBValue::from_slice(b"B")];
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
{ {
let mut t = TrieDBMut::new(&mut memdb, &mut root); let mut t = TrieDBMut::new(&mut memdb, &mut root);
for x in &d { for x in &d {
t.insert(x, x).unwrap(); t.insert(x, x).unwrap();
}
} }
let t = TrieDB::new(&memdb, &root).unwrap();
assert_eq!(d.iter().map(|i| i.clone().into_vec()).collect::<Vec<_>>(), t.iter().unwrap().map(|x| x.unwrap().0).collect::<Vec<_>>());
assert_eq!(d, t.iter().unwrap().map(|x| x.unwrap().1).collect::<Vec<_>>());
} }
let t = TrieDB::new(&memdb, &root).unwrap(); #[test]
assert_eq!(d.iter().map(|i| i.clone().into_vec()).collect::<Vec<_>>(), t.iter().unwrap().map(|x| x.unwrap().0).collect::<Vec<_>>()); fn iterator_seek() {
assert_eq!(d, t.iter().unwrap().map(|x| x.unwrap().1).collect::<Vec<_>>()); let d = vec![ DBValue::from_slice(b"A"), DBValue::from_slice(b"AA"), DBValue::from_slice(b"AB"), DBValue::from_slice(b"B") ];
}
let mut memdb = MemoryDB::<KeccakHasher>::new();
#[test] let mut root = H256::new();
fn iterator_seek() { {
use memorydb::*; let mut t = TrieDBMut::new(&mut memdb, &mut root);
use super::TrieMut; for x in &d {
use super::triedbmut::*; t.insert(x, x).unwrap();
}
let d = vec![ DBValue::from_slice(b"A"), DBValue::from_slice(b"AA"), DBValue::from_slice(b"AB"), DBValue::from_slice(b"B") ];
let mut memdb = MemoryDB::new();
let mut root = H256::new();
{
let mut t = TrieDBMut::new(&mut memdb, &mut root);
for x in &d {
t.insert(x, x).unwrap();
} }
let t = TrieDB::new(&memdb, &root).unwrap();
let mut iter = t.iter().unwrap();
assert_eq!(iter.next().unwrap().unwrap(), (b"A".to_vec(), DBValue::from_slice(b"A")));
iter.seek(b"!").unwrap();
assert_eq!(d, iter.map(|x| x.unwrap().1).collect::<Vec<_>>());
let mut iter = t.iter().unwrap();
iter.seek(b"A").unwrap();
assert_eq!(&d[1..], &iter.map(|x| x.unwrap().1).collect::<Vec<_>>()[..]);
let mut iter = t.iter().unwrap();
iter.seek(b"AA").unwrap();
assert_eq!(&d[2..], &iter.map(|x| x.unwrap().1).collect::<Vec<_>>()[..]);
let mut iter = t.iter().unwrap();
iter.seek(b"A!").unwrap();
assert_eq!(&d[1..], &iter.map(|x| x.unwrap().1).collect::<Vec<_>>()[..]);
let mut iter = t.iter().unwrap();
iter.seek(b"AB").unwrap();
assert_eq!(&d[3..], &iter.map(|x| x.unwrap().1).collect::<Vec<_>>()[..]);
let mut iter = t.iter().unwrap();
iter.seek(b"AB!").unwrap();
assert_eq!(&d[3..], &iter.map(|x| x.unwrap().1).collect::<Vec<_>>()[..]);
let mut iter = t.iter().unwrap();
iter.seek(b"B").unwrap();
assert_eq!(&d[4..], &iter.map(|x| x.unwrap().1).collect::<Vec<_>>()[..]);
let mut iter = t.iter().unwrap();
iter.seek(b"C").unwrap();
assert_eq!(&d[4..], &iter.map(|x| x.unwrap().1).collect::<Vec<_>>()[..]);
} }
let t = TrieDB::new(&memdb, &root).unwrap(); #[test]
let mut iter = t.iter().unwrap(); fn get_len() {
assert_eq!(iter.next(), Some(Ok((b"A".to_vec(), DBValue::from_slice(b"A"))))); let mut memdb = MemoryDB::<KeccakHasher>::new();
iter.seek(b"!").unwrap(); let mut root = H256::new();
assert_eq!(d, iter.map(|x| x.unwrap().1).collect::<Vec<_>>()); {
let mut iter = t.iter().unwrap(); let mut t = TrieDBMut::new(&mut memdb, &mut root);
iter.seek(b"A").unwrap(); t.insert(b"A", b"ABC").unwrap();
assert_eq!(&d[1..], &iter.map(|x| x.unwrap().1).collect::<Vec<_>>()[..]); t.insert(b"B", b"ABCBA").unwrap();
let mut iter = t.iter().unwrap(); }
iter.seek(b"AA").unwrap();
assert_eq!(&d[2..], &iter.map(|x| x.unwrap().1).collect::<Vec<_>>()[..]);
let mut iter = t.iter().unwrap();
iter.seek(b"A!").unwrap();
assert_eq!(&d[1..], &iter.map(|x| x.unwrap().1).collect::<Vec<_>>()[..]);
let mut iter = t.iter().unwrap();
iter.seek(b"AB").unwrap();
assert_eq!(&d[3..], &iter.map(|x| x.unwrap().1).collect::<Vec<_>>()[..]);
let mut iter = t.iter().unwrap();
iter.seek(b"AB!").unwrap();
assert_eq!(&d[3..], &iter.map(|x| x.unwrap().1).collect::<Vec<_>>()[..]);
let mut iter = t.iter().unwrap();
iter.seek(b"B").unwrap();
assert_eq!(&d[4..], &iter.map(|x| x.unwrap().1).collect::<Vec<_>>()[..]);
let mut iter = t.iter().unwrap();
iter.seek(b"C").unwrap();
assert_eq!(&d[4..], &iter.map(|x| x.unwrap().1).collect::<Vec<_>>()[..]);
}
#[test] let t = TrieDB::new(&memdb, &root).unwrap();
fn get_len() { assert_eq!(t.get_with(b"A", |x: &[u8]| x.len()).unwrap(), Some(3));
use memorydb::*; assert_eq!(t.get_with(b"B", |x: &[u8]| x.len()).unwrap(), Some(5));
use super::TrieMut; assert_eq!(t.get_with(b"C", |x: &[u8]| x.len()).unwrap(), None);
use super::triedbmut::*;
let mut memdb = MemoryDB::new();
let mut root = H256::new();
{
let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(b"A", b"ABC").unwrap();
t.insert(b"B", b"ABCBA").unwrap();
} }
let t = TrieDB::new(&memdb, &root).unwrap(); #[test]
assert_eq!(t.get_with(b"A", |x: &[u8]| x.len()), Ok(Some(3))); fn debug_output_supports_pretty_print() {
assert_eq!(t.get_with(b"B", |x: &[u8]| x.len()), Ok(Some(5))); let d = vec![ DBValue::from_slice(b"A"), DBValue::from_slice(b"AA"), DBValue::from_slice(b"AB"), DBValue::from_slice(b"B") ];
assert_eq!(t.get_with(b"C", |x: &[u8]| x.len()), Ok(None));
}
let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new();
let root = {
let mut t = TrieDBMut::new(&mut memdb, &mut root);
for x in &d {
t.insert(x, x).unwrap();
}
t.root().clone()
};
let t = TrieDB::new(&memdb, &root).unwrap();
#[test] assert_eq!(format!("{:?}", t), "TrieDB { hash_count: 0, root: Node::Extension { slice: 4, item: Node::Branch { nodes: [Node::Empty, Node::Branch { nodes: [Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Branch { nodes: [Node::Empty, Node::Leaf { slice: , value: [65, 65] }, Node::Leaf { slice: , value: [65, 66] }, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty], value: None }, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty], value: Some([65]) }, Node::Leaf { slice: , value: [66] }, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty], value: None } } }");
fn debug_output_supports_pretty_print() { assert_eq!(format!("{:#?}", t),
use memorydb::*;
use super::TrieMut;
use super::triedbmut::*;
let d = vec![ DBValue::from_slice(b"A"), DBValue::from_slice(b"AA"), DBValue::from_slice(b"AB"), DBValue::from_slice(b"B") ];
let mut memdb = MemoryDB::new();
let mut root = H256::new();
let root = {
let mut t = TrieDBMut::new(&mut memdb, &mut root);
for x in &d {
t.insert(x, x).unwrap();
}
t.root().clone()
};
let t = TrieDB::new(&memdb, &root).unwrap();
assert_eq!(format!("{:?}", t), "TrieDB { hash_count: 0, root: Node::Extension { slice: 4, item: Node::Branch { nodes: [Node::Empty, Node::Branch { nodes: [Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Branch { nodes: [Node::Empty, Node::Leaf { slice: , value: [65, 65] }, Node::Leaf { slice: , value: [65, 66] }, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty], value: None }, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty], value: Some([65]) }, Node::Leaf { slice: , value: [66] }, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty], value: None } } }");
assert_eq!(format!("{:#?}", t),
"TrieDB { "TrieDB {
hash_count: 0, hash_count: 0,
root: Node::Extension { root: Node::Extension {
@ -592,29 +600,29 @@ fn debug_output_supports_pretty_print() {
} }
} }
}"); }");
}
#[test]
fn test_lookup_with_corrupt_data_returns_decoder_error() {
use memorydb::*;
use super::TrieMut;
use super::triedbmut::*;
use rlp;
use ethereum_types::H512;
let mut memdb = MemoryDB::new();
let mut root = H256::new();
{
let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(b"A", b"ABC").unwrap();
t.insert(b"B", b"ABCBA").unwrap();
} }
let t = TrieDB::new(&memdb, &root).unwrap(); #[test]
fn test_lookup_with_corrupt_data_returns_decoder_error() {
use rlp;
use ethereum_types::H512;
use std::marker::PhantomData;
use ethtrie::trie::NibbleSlice;
// query for an invalid data type to trigger an error let mut memdb = MemoryDB::<KeccakHasher>::new();
let q = rlp::decode::<H512>; let mut root = H256::new();
let lookup = Lookup{ db: t.db, query: q, hash: root }; {
let query_result = lookup.look_up(NibbleSlice::new(b"A")); let mut t = TrieDBMut::new(&mut memdb, &mut root);
assert_eq!(query_result.unwrap().unwrap().unwrap_err(), rlp::DecoderError::RlpIsTooShort); t.insert(b"A", b"ABC").unwrap();
} t.insert(b"B", b"ABCBA").unwrap();
}
let t = TrieDB::new(&memdb, &root).unwrap();
// query for an invalid data type to trigger an error
let q = rlp::decode::<H512>;
let lookup = Lookup::<_, RlpCodec, _>{ db: t.db(), query: q, hash: root, marker: PhantomData };
let query_result = lookup.look_up(NibbleSlice::new(b"A"));
assert_eq!(query_result.unwrap().unwrap().unwrap_err(), rlp::DecoderError::RlpIsTooShort);
}
}

View File

@ -16,23 +16,21 @@
//! In-memory trie representation. //! In-memory trie representation.
use super::{TrieError, TrieMut}; use super::{Result, TrieError, TrieMut};
use super::lookup::Lookup; use super::lookup::Lookup;
use super::node::Node as RlpNode; use super::node::Node as EncodedNode;
use node_codec::NodeCodec;
use super::node::NodeKey; use super::node::NodeKey;
use hashdb::HashDB;
use bytes::ToPretty; use bytes::ToPretty;
use hashdb::{HashDB, Hasher, DBValue};
use nibbleslice::NibbleSlice; use nibbleslice::NibbleSlice;
use rlp::{Rlp, RlpStream};
use hashdb::DBValue;
use elastic_array::ElasticArray1024;
use std::collections::{HashSet, VecDeque}; use std::collections::{HashSet, VecDeque};
use std::marker::PhantomData;
use std::mem; use std::mem;
use std::ops::Index; use std::ops::Index;
use ethereum_types::H256;
use elastic_array::ElasticArray1024;
use keccak::{KECCAK_NULL_RLP};
// For lookups into the Node storage buffer. // For lookups into the Node storage buffer.
// This is deliberately non-copyable. // This is deliberately non-copyable.
@ -41,26 +39,20 @@ struct StorageHandle(usize);
// Handles to nodes in the trie. // Handles to nodes in the trie.
#[derive(Debug)] #[derive(Debug)]
enum NodeHandle { enum NodeHandle<H: Hasher> {
/// Loaded into memory. /// Loaded into memory.
InMemory(StorageHandle), InMemory(StorageHandle),
/// Either a hash or an inline node /// Either a hash or an inline node
Hash(H256), Hash(H::Out),
} }
impl From<StorageHandle> for NodeHandle { impl<H: Hasher> From<StorageHandle> for NodeHandle<H> {
fn from(handle: StorageHandle) -> Self { fn from(handle: StorageHandle) -> Self {
NodeHandle::InMemory(handle) NodeHandle::InMemory(handle)
} }
} }
impl From<H256> for NodeHandle { fn empty_children<H: Hasher>() -> Box<[Option<NodeHandle<H>>; 16]> {
fn from(hash: H256) -> Self {
NodeHandle::Hash(hash)
}
}
fn empty_children() -> Box<[Option<NodeHandle>; 16]> {
Box::new([ Box::new([
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
@ -69,7 +61,7 @@ fn empty_children() -> Box<[Option<NodeHandle>; 16]> {
/// Node types in the Trie. /// Node types in the Trie.
#[derive(Debug)] #[derive(Debug)]
enum Node { enum Node<H: Hasher> {
/// Empty node. /// Empty node.
Empty, Empty,
/// A leaf node contains the end of a key and a value. /// A leaf node contains the end of a key and a value.
@ -80,36 +72,41 @@ enum Node {
/// The shared portion is encoded from a `NibbleSlice` meaning it contains /// The shared portion is encoded from a `NibbleSlice` meaning it contains
/// a flag indicating it is an extension. /// a flag indicating it is an extension.
/// The child node is always a branch. /// The child node is always a branch.
Extension(NodeKey, NodeHandle), Extension(NodeKey, NodeHandle<H>),
/// A branch has up to 16 children and an optional value. /// A branch has up to 16 children and an optional value.
Branch(Box<[Option<NodeHandle>; 16]>, Option<DBValue>) Branch(Box<[Option<NodeHandle<H>>; 16]>, Option<DBValue>)
} }
impl Node { impl<H: Hasher> Node<H> {
// load an inline node into memory or get the hash to do the lookup later. // load an inline node into memory or get the hash to do the lookup later.
fn inline_or_hash(node: &[u8], db: &HashDB, storage: &mut NodeStorage) -> NodeHandle { fn inline_or_hash<C>(node: &[u8], db: &HashDB<H>, storage: &mut NodeStorage<H>) -> NodeHandle<H>
RlpNode::try_decode_hash(&node) where C: NodeCodec<H>
{
C::try_decode_hash(&node)
.map(NodeHandle::Hash) .map(NodeHandle::Hash)
.unwrap_or_else(|| { .unwrap_or_else(|| {
let child = Node::from_rlp(node, db, storage); let child = Node::from_encoded::<C>(node, db, storage);
NodeHandle::InMemory(storage.alloc(Stored::New(child))) NodeHandle::InMemory(storage.alloc(Stored::New(child)))
}) })
} }
// decode a node from rlp without getting its children. // decode a node from encoded bytes without getting its children.
fn from_rlp(rlp: &[u8], db: &HashDB, storage: &mut NodeStorage) -> Self { fn from_encoded<C>(data: &[u8], db: &HashDB<H>, storage: &mut NodeStorage<H>) -> Self
match RlpNode::decoded(rlp).expect("rlp read from db; qed") { where C: NodeCodec<H>
RlpNode::Empty => Node::Empty, {
RlpNode::Leaf(k, v) => Node::Leaf(k.encoded(true), DBValue::from_slice(&v)), match C::decode(data).expect("encoded bytes read from db; qed") {
RlpNode::Extension(key, cb) => { EncodedNode::Empty => Node::Empty,
Node::Extension(key.encoded(false), Self::inline_or_hash(cb, db, storage)) EncodedNode::Leaf(k, v) => Node::Leaf(k.encoded(true), DBValue::from_slice(&v)),
EncodedNode::Extension(key, cb) => {
Node::Extension(
key.encoded(false),
Self::inline_or_hash::<C>(cb, db, storage))
} }
RlpNode::Branch(ref children_rlp, val) => { EncodedNode::Branch(ref encoded_children, val) => {
let mut child = |i| { let mut child = |i:usize| {
let raw = children_rlp[i]; let raw = encoded_children[i];
let child_rlp = Rlp::new(raw); if !C::is_empty_node(raw) {
if !child_rlp.is_empty() { Some(Self::inline_or_hash::<C>(raw, db, storage))
Some(Self::inline_or_hash(raw, db, storage))
} else { } else {
None None
} }
@ -127,70 +124,51 @@ impl Node {
} }
} }
// encode a node to RLP
// TODO: parallelize // TODO: parallelize
fn into_rlp<F>(self, mut child_cb: F) -> ElasticArray1024<u8> fn into_encoded<F, C>(self, mut child_cb: F) -> ElasticArray1024<u8>
where F: FnMut(NodeHandle, &mut RlpStream) where
C: NodeCodec<H>,
F: FnMut(NodeHandle<H>) -> ChildReference<H::Out>
{ {
match self { match self {
Node::Empty => { Node::Empty => C::empty_node(),
let mut stream = RlpStream::new(); Node::Leaf(partial, value) => C::leaf_node(&partial, &value),
stream.append_empty_data(); Node::Extension(partial, child) => C::ext_node(&partial, child_cb(child)),
stream.drain()
}
Node::Leaf(partial, value) => {
let mut stream = RlpStream::new_list(2);
stream.append(&&*partial);
stream.append(&&*value);
stream.drain()
}
Node::Extension(partial, child) => {
let mut stream = RlpStream::new_list(2);
stream.append(&&*partial);
child_cb(child, &mut stream);
stream.drain()
}
Node::Branch(mut children, value) => { Node::Branch(mut children, value) => {
let mut stream = RlpStream::new_list(17); C::branch_node(
for child in children.iter_mut().map(Option::take) { // map the `NodeHandle`s from the Branch to `ChildReferences`
if let Some(handle) = child { children.iter_mut()
child_cb(handle, &mut stream); .map(Option::take)
} else { .map(|maybe_child|
stream.append_empty_data(); maybe_child.map(|child| child_cb(child))
} ),
} value
if let Some(value) = value { )
stream.append(&&*value);
} else {
stream.append_empty_data();
}
stream.drain()
} }
} }
} }
} }
// post-inspect action. // post-inspect action.
enum Action { enum Action<H: Hasher> {
// Replace a node with a new one. // Replace a node with a new one.
Replace(Node), Replace(Node<H>),
// Restore the original node. This trusts that the node is actually the original. // Restore the original node. This trusts that the node is actually the original.
Restore(Node), Restore(Node<H>),
// if it is a new node, just clears the storage. // if it is a new node, just clears the storage.
Delete, Delete,
} }
// post-insert action. Same as action without delete // post-insert action. Same as action without delete
enum InsertAction { enum InsertAction<H: Hasher> {
// Replace a node with a new one. // Replace a node with a new one.
Replace(Node), Replace(Node<H>),
// Restore the original node. // Restore the original node.
Restore(Node), Restore(Node<H>),
} }
impl InsertAction { impl<H: Hasher> InsertAction<H> {
fn into_action(self) -> Action { fn into_action(self) -> Action<H> {
match self { match self {
InsertAction::Replace(n) => Action::Replace(n), InsertAction::Replace(n) => Action::Replace(n),
InsertAction::Restore(n) => Action::Restore(n), InsertAction::Restore(n) => Action::Restore(n),
@ -198,7 +176,7 @@ impl InsertAction {
} }
// unwrap the node, disregarding replace or restore state. // unwrap the node, disregarding replace or restore state.
fn unwrap_node(self) -> Node { fn unwrap_node(self) -> Node<H> {
match self { match self {
InsertAction::Replace(n) | InsertAction::Restore(n) => n, InsertAction::Replace(n) | InsertAction::Restore(n) => n,
} }
@ -206,20 +184,26 @@ impl InsertAction {
} }
// What kind of node is stored here. // What kind of node is stored here.
enum Stored { enum Stored<H: Hasher> {
// A new node. // A new node.
New(Node), New(Node<H>),
// A cached node, loaded from the DB. // A cached node, loaded from the DB.
Cached(Node, H256), Cached(Node<H>, H::Out),
}
/// Used to build a collection of child nodes from a collection of `NodeHandle`s
pub enum ChildReference<HO> { // `HO` is e.g. `H256`, i.e. the output of a `Hasher`
Hash(HO),
Inline(HO, usize), // usize is the length of the node data we store in the `H::Out`
} }
/// Compact and cache-friendly storage for Trie nodes. /// Compact and cache-friendly storage for Trie nodes.
struct NodeStorage { struct NodeStorage<H: Hasher> {
nodes: Vec<Stored>, nodes: Vec<Stored<H>>,
free_indices: VecDeque<usize>, free_indices: VecDeque<usize>,
} }
impl NodeStorage { impl<H: Hasher> NodeStorage<H> {
/// Create a new storage. /// Create a new storage.
fn empty() -> Self { fn empty() -> Self {
NodeStorage { NodeStorage {
@ -229,7 +213,7 @@ impl NodeStorage {
} }
/// Allocate a new node in the storage. /// Allocate a new node in the storage.
fn alloc(&mut self, stored: Stored) -> StorageHandle { fn alloc(&mut self, stored: Stored<H>) -> StorageHandle {
if let Some(idx) = self.free_indices.pop_front() { if let Some(idx) = self.free_indices.pop_front() {
self.nodes[idx] = stored; self.nodes[idx] = stored;
StorageHandle(idx) StorageHandle(idx)
@ -240,7 +224,7 @@ impl NodeStorage {
} }
/// Remove a node from the storage, consuming the handle and returning the node. /// Remove a node from the storage, consuming the handle and returning the node.
fn destroy(&mut self, handle: StorageHandle) -> Stored { fn destroy(&mut self, handle: StorageHandle) -> Stored<H> {
let idx = handle.0; let idx = handle.0;
self.free_indices.push_back(idx); self.free_indices.push_back(idx);
@ -248,10 +232,10 @@ impl NodeStorage {
} }
} }
impl<'a> Index<&'a StorageHandle> for NodeStorage { impl<'a, H: Hasher> Index<&'a StorageHandle> for NodeStorage<H> {
type Output = Node; type Output = Node<H>;
fn index(&self, handle: &'a StorageHandle) -> &Node { fn index(&self, handle: &'a StorageHandle) -> &Node<H> {
match self.nodes[handle.0] { match self.nodes[handle.0] {
Stored::New(ref node) => node, Stored::New(ref node) => node,
Stored::Cached(ref node, _) => node, Stored::Cached(ref node, _) => node,
@ -268,19 +252,22 @@ impl<'a> Index<&'a StorageHandle> for NodeStorage {
/// # Example /// # Example
/// ``` /// ```
/// extern crate patricia_trie as trie; /// extern crate patricia_trie as trie;
/// extern crate keccak_hash; /// extern crate patricia_trie_ethereum as ethtrie;
/// extern crate hashdb; /// extern crate hashdb;
/// extern crate keccak_hash;
/// extern crate keccak_hasher;
/// extern crate memorydb; /// extern crate memorydb;
/// extern crate ethereum_types; /// extern crate ethereum_types;
/// ///
/// use keccak_hash::KECCAK_NULL_RLP; /// use keccak_hash::KECCAK_NULL_RLP;
/// use trie::*; /// use ethtrie::{TrieDBMut, trie::TrieMut};
/// use hashdb::*; /// use hashdb::DBValue;
/// use keccak_hasher::KeccakHasher;
/// use memorydb::*; /// use memorydb::*;
/// use ethereum_types::H256; /// use ethereum_types::H256;
/// ///
/// fn main() { /// fn main() {
/// let mut memdb = MemoryDB::new(); /// let mut memdb = MemoryDB::<KeccakHasher>::new();
/// let mut root = H256::new(); /// let mut root = H256::new();
/// let mut t = TrieDBMut::new(&mut memdb, &mut root); /// let mut t = TrieDBMut::new(&mut memdb, &mut root);
/// assert!(t.is_empty()); /// assert!(t.is_empty());
@ -292,22 +279,31 @@ impl<'a> Index<&'a StorageHandle> for NodeStorage {
/// assert!(!t.contains(b"foo").unwrap()); /// assert!(!t.contains(b"foo").unwrap());
/// } /// }
/// ``` /// ```
pub struct TrieDBMut<'a> { pub struct TrieDBMut<'a, H, C>
storage: NodeStorage, where
db: &'a mut HashDB, H: Hasher + 'a,
root: &'a mut H256, C: NodeCodec<H>
root_handle: NodeHandle, {
death_row: HashSet<H256>, storage: NodeStorage<H>,
db: &'a mut HashDB<H>,
root: &'a mut H::Out,
root_handle: NodeHandle<H>,
death_row: HashSet<H::Out>,
/// The number of hash operations this trie has performed. /// The number of hash operations this trie has performed.
/// Note that none are performed until changes are committed. /// Note that none are performed until changes are committed.
hash_count: usize, hash_count: usize,
marker: PhantomData<C>, // TODO: rpheimer: "we could have the NodeCodec trait take &self to its methods and then we don't need PhantomData. we can just store an instance of C: NodeCodec in the trie struct. If it's a ZST it won't have any additional overhead anyway"
} }
impl<'a> TrieDBMut<'a> { impl<'a, H, C> TrieDBMut<'a, H, C>
where
H: Hasher,
C: NodeCodec<H>
{
/// Create a new trie with backing database `db` and empty `root`. /// Create a new trie with backing database `db` and empty `root`.
pub fn new(db: &'a mut HashDB, root: &'a mut H256) -> Self { pub fn new(db: &'a mut HashDB<H>, root: &'a mut H::Out) -> Self {
*root = KECCAK_NULL_RLP; *root = C::HASHED_NULL_NODE;
let root_handle = NodeHandle::Hash(KECCAK_NULL_RLP); let root_handle = NodeHandle::Hash(C::HASHED_NULL_NODE);
TrieDBMut { TrieDBMut {
storage: NodeStorage::empty(), storage: NodeStorage::empty(),
@ -316,12 +312,13 @@ impl<'a> TrieDBMut<'a> {
root_handle: root_handle, root_handle: root_handle,
death_row: HashSet::new(), death_row: HashSet::new(),
hash_count: 0, hash_count: 0,
marker: PhantomData,
} }
} }
/// Create a new trie with the backing database `db` and `root. /// Create a new trie with the backing database `db` and `root.
/// Returns an error if `root` does not exist. /// Returns an error if `root` does not exist.
pub fn from_existing(db: &'a mut HashDB, root: &'a mut H256) -> super::Result<Self> { pub fn from_existing(db: &'a mut HashDB<H>, root: &'a mut H::Out) -> Result<Self, H::Out, C::Error> {
if !db.contains(root) { if !db.contains(root) {
return Err(Box::new(TrieError::InvalidStateRoot(*root))); return Err(Box::new(TrieError::InvalidStateRoot(*root)));
} }
@ -334,29 +331,34 @@ impl<'a> TrieDBMut<'a> {
root_handle: root_handle, root_handle: root_handle,
death_row: HashSet::new(), death_row: HashSet::new(),
hash_count: 0, hash_count: 0,
marker: PhantomData,
}) })
} }
/// Get the backing database. /// Get the backing database.
pub fn db(&self) -> &HashDB { pub fn db(&self) -> &HashDB<H> {
self.db self.db
} }
/// Get the backing database mutably. /// Get the backing database mutably.
pub fn db_mut(&mut self) -> &mut HashDB { pub fn db_mut(&mut self) -> &mut HashDB<H> {
self.db self.db
} }
// cache a node by hash // cache a node by hash
fn cache(&mut self, hash: H256) -> super::Result<StorageHandle> { fn cache(&mut self, hash: H::Out) -> Result<StorageHandle, H::Out, C::Error> {
let node_rlp = self.db.get(&hash).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(hash)))?; let node_encoded = self.db.get(&hash).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(hash)))?;
let node = Node::from_rlp(&node_rlp, &*self.db, &mut self.storage); let node = Node::from_encoded::<C>(
&node_encoded,
&*self.db,
&mut self.storage
);
Ok(self.storage.alloc(Stored::Cached(node, hash))) Ok(self.storage.alloc(Stored::Cached(node, hash)))
} }
// inspect a node, choosing either to replace, restore, or delete it. // inspect a node, choosing either to replace, restore, or delete it.
// if restored or replaced, returns the new node along with a flag of whether it was changed. // if restored or replaced, returns the new node along with a flag of whether it was changed.
fn inspect<F>(&mut self, stored: Stored, inspector: F) -> super::Result<Option<(Stored, bool)>> fn inspect<F>(&mut self, stored: Stored<H>, inspector: F) -> Result<Option<(Stored<H>, bool)>, H::Out, C::Error>
where F: FnOnce(&mut Self, Node) -> super::Result<Action> { where F: FnOnce(&mut Self, Node<H>) -> Result<Action<H>, H::Out, C::Error> {
Ok(match stored { Ok(match stored {
Stored::New(node) => match inspector(self, node)? { Stored::New(node) => match inspector(self, node)? {
Action::Restore(node) => Some((Stored::New(node), false)), Action::Restore(node) => Some((Stored::New(node), false)),
@ -378,16 +380,17 @@ impl<'a> TrieDBMut<'a> {
} }
// walk the trie, attempting to find the key's node. // walk the trie, attempting to find the key's node.
fn lookup<'x, 'key>(&'x self, mut partial: NibbleSlice<'key>, handle: &NodeHandle) -> super::Result<Option<DBValue>> fn lookup<'x, 'key>(&'x self, mut partial: NibbleSlice<'key>, handle: &NodeHandle<H>) -> Result<Option<DBValue>, H::Out, C::Error>
where 'x: 'key where 'x: 'key
{ {
let mut handle = handle; let mut handle = handle;
loop { loop {
let (mid, child) = match *handle { let (mid, child) = match *handle {
NodeHandle::Hash(ref hash) => return Lookup { NodeHandle::Hash(ref hash) => return Lookup{
db: &*self.db, db: &*self.db,
query: DBValue::from_slice, query: DBValue::from_slice,
hash: hash.clone(), hash: hash.clone(),
marker: PhantomData::<C>,
}.look_up(partial), }.look_up(partial),
NodeHandle::InMemory(ref handle) => match self.storage[handle] { NodeHandle::InMemory(ref handle) => match self.storage[handle] {
Node::Empty => return Ok(None), Node::Empty => return Ok(None),
@ -425,10 +428,8 @@ impl<'a> TrieDBMut<'a> {
} }
} }
/// insert a key, value pair into the trie, creating new nodes if necessary. /// insert a key-value pair into the trie, creating new nodes if necessary.
fn insert_at(&mut self, handle: NodeHandle, partial: NibbleSlice, value: DBValue, old_val: &mut Option<DBValue>) fn insert_at(&mut self, handle: NodeHandle<H>, partial: NibbleSlice, value: DBValue, old_val: &mut Option<DBValue>) -> Result<(StorageHandle, bool), H::Out, C::Error> {
-> super::Result<(StorageHandle, bool)>
{
let h = match handle { let h = match handle {
NodeHandle::InMemory(h) => h, NodeHandle::InMemory(h) => h,
NodeHandle::Hash(h) => self.cache(h)?, NodeHandle::Hash(h) => self.cache(h)?,
@ -442,9 +443,7 @@ impl<'a> TrieDBMut<'a> {
} }
/// the insertion inspector. /// the insertion inspector.
fn insert_inspector(&mut self, node: Node, partial: NibbleSlice, value: DBValue, old_val: &mut Option<DBValue>) fn insert_inspector(&mut self, node: Node<H>, partial: NibbleSlice, value: DBValue, old_val: &mut Option<DBValue>) -> Result<InsertAction<H>, H::Out, C::Error> {
-> super::Result<InsertAction>
{
trace!(target: "trie", "augmented (partial: {:?}, value: {:?})", partial, value.pretty()); trace!(target: "trie", "augmented (partial: {:?}, value: {:?})", partial, value.pretty());
Ok(match node { Ok(match node {
@ -605,9 +604,7 @@ impl<'a> TrieDBMut<'a> {
} }
/// Remove a node from the trie based on key. /// Remove a node from the trie based on key.
fn remove_at(&mut self, handle: NodeHandle, partial: NibbleSlice, old_val: &mut Option<DBValue>) fn remove_at(&mut self, handle: NodeHandle<H>, partial: NibbleSlice, old_val: &mut Option<DBValue>) -> Result<Option<(StorageHandle, bool)>, H::Out, C::Error> {
-> super::Result<Option<(StorageHandle, bool)>>
{
let stored = match handle { let stored = match handle {
NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::InMemory(h) => self.storage.destroy(h),
NodeHandle::Hash(h) => { NodeHandle::Hash(h) => {
@ -622,7 +619,7 @@ impl<'a> TrieDBMut<'a> {
} }
/// the removal inspector /// the removal inspector
fn remove_inspector(&mut self, node: Node, partial: NibbleSlice, old_val: &mut Option<DBValue>) -> super::Result<Action> { fn remove_inspector(&mut self, node: Node<H>, partial: NibbleSlice, old_val: &mut Option<DBValue>) -> Result<Action<H>, H::Out, C::Error> {
Ok(match (node, partial.is_empty()) { Ok(match (node, partial.is_empty()) {
(Node::Empty, _) => Action::Delete, (Node::Empty, _) => Action::Delete,
(Node::Branch(c, None), true) => Action::Restore(Node::Branch(c, None)), (Node::Branch(c, None), true) => Action::Restore(Node::Branch(c, None)),
@ -708,7 +705,7 @@ impl<'a> TrieDBMut<'a> {
/// _invalid state_ means: /// _invalid state_ means:
/// - Branch node where there is only a single entry; /// - Branch node where there is only a single entry;
/// - Extension node followed by anything other than a Branch node. /// - Extension node followed by anything other than a Branch node.
fn fix(&mut self, node: Node) -> super::Result<Node> { fn fix(&mut self, node: Node<H>) -> Result<Node<H>, H::Out, C::Error> {
match node { match node {
Node::Branch(mut children, value) => { Node::Branch(mut children, value) => {
// if only a single value, transmute to leaf/extension and feed through fixed. // if only a single value, transmute to leaf/extension and feed through fixed.
@ -828,11 +825,11 @@ impl<'a> TrieDBMut<'a> {
match self.storage.destroy(handle) { match self.storage.destroy(handle) {
Stored::New(node) => { Stored::New(node) => {
let root_rlp = node.into_rlp(|child, stream| self.commit_node(child, stream)); let encoded_root = node.into_encoded::<_, C>(|child| self.commit_child(child) );
*self.root = self.db.insert(&root_rlp[..]); *self.root = self.db.insert(&encoded_root[..]);
self.hash_count += 1; self.hash_count += 1;
trace!(target: "trie", "root node rlp: {:?}", (&root_rlp[..]).pretty()); trace!(target: "trie", "encoded root node: {:?}", (&encoded_root[..]).pretty());
self.root_handle = NodeHandle::Hash(*self.root); self.root_handle = NodeHandle::Hash(*self.root);
} }
Stored::Cached(node, hash) => { Stored::Cached(node, hash) => {
@ -843,29 +840,38 @@ impl<'a> TrieDBMut<'a> {
} }
} }
/// commit a node, hashing it, committing it to the db, /// Commit a node by hashing it and writing it to the db. Returns a
/// and writing it to the rlp stream as necessary. /// `ChildReference` which in most cases carries a normal hash but for the
fn commit_node(&mut self, handle: NodeHandle, stream: &mut RlpStream) { /// case where we can fit the actual data in the `Hasher`s output type, we
/// store the data inline. This function is used as the callback to the
/// `into_encoded` method of `Node`.
fn commit_child(&mut self, handle: NodeHandle<H>) -> ChildReference<H::Out> {
match handle { match handle {
NodeHandle::Hash(h) => stream.append(&h), NodeHandle::Hash(hash) => ChildReference::Hash(hash),
NodeHandle::InMemory(h) => match self.storage.destroy(h) { NodeHandle::InMemory(storage_handle) => {
Stored::Cached(_, h) => stream.append(&h), match self.storage.destroy(storage_handle) {
Stored::New(node) => { Stored::Cached(_, hash) => ChildReference::Hash(hash),
let node_rlp = node.into_rlp(|child, stream| self.commit_node(child, stream)); Stored::New(node) => {
if node_rlp.len() >= 32 { let encoded = node.into_encoded::<_, C>(|node_handle| self.commit_child(node_handle) );
let hash = self.db.insert(&node_rlp[..]); if encoded.len() >= H::LENGTH {
self.hash_count += 1; let hash = self.db.insert(&encoded[..]);
stream.append(&hash) self.hash_count +=1;
} else { ChildReference::Hash(hash)
stream.append_raw(&node_rlp, 1) } else {
// it's a small value, so we cram it into a `H::Out` and tag with length
let mut h = H::Out::default();
let len = encoded.len();
h.as_mut()[..len].copy_from_slice(&encoded[..len]);
ChildReference::Inline(h, len)
}
} }
} }
} }
}; }
} }
// a hack to get the root node's handle // a hack to get the root node's handle
fn root_handle(&self) -> NodeHandle { fn root_handle(&self) -> NodeHandle<H> {
match self.root_handle { match self.root_handle {
NodeHandle::Hash(h) => NodeHandle::Hash(h), NodeHandle::Hash(h) => NodeHandle::Hash(h),
NodeHandle::InMemory(StorageHandle(x)) => NodeHandle::InMemory(StorageHandle(x)), NodeHandle::InMemory(StorageHandle(x)) => NodeHandle::InMemory(StorageHandle(x)),
@ -873,15 +879,19 @@ impl<'a> TrieDBMut<'a> {
} }
} }
impl<'a> TrieMut for TrieDBMut<'a> { impl<'a, H, C> TrieMut<H, C> for TrieDBMut<'a, H, C>
fn root(&mut self) -> &H256 { where
H: Hasher,
C: NodeCodec<H>
{
fn root(&mut self) -> &H::Out {
self.commit(); self.commit();
self.root self.root
} }
fn is_empty(&self) -> bool { fn is_empty(&self) -> bool {
match self.root_handle { match self.root_handle {
NodeHandle::Hash(h) => h == KECCAK_NULL_RLP, NodeHandle::Hash(h) => h == C::HASHED_NULL_NODE,
NodeHandle::InMemory(ref h) => match self.storage[h] { NodeHandle::InMemory(ref h) => match self.storage[h] {
Node::Empty => true, Node::Empty => true,
_ => false, _ => false,
@ -889,11 +899,13 @@ impl<'a> TrieMut for TrieDBMut<'a> {
} }
} }
fn get<'x, 'key>(&'x self, key: &'key [u8]) -> super::Result<Option<DBValue>> where 'x: 'key { fn get<'x, 'key>(&'x self, key: &'key [u8]) -> Result<Option<DBValue>, H::Out, C::Error>
where 'x: 'key
{
self.lookup(NibbleSlice::new(key), &self.root_handle) self.lookup(NibbleSlice::new(key), &self.root_handle)
} }
fn insert(&mut self, key: &[u8], value: &[u8]) -> super::Result<Option<DBValue>> { fn insert(&mut self, key: &[u8], value: &[u8]) -> Result<Option<DBValue>, H::Out, C::Error> {
if value.is_empty() { return self.remove(key) } if value.is_empty() { return self.remove(key) }
let mut old_val = None; let mut old_val = None;
@ -914,7 +926,7 @@ impl<'a> TrieMut for TrieDBMut<'a> {
Ok(old_val) Ok(old_val)
} }
fn remove(&mut self, key: &[u8]) -> super::Result<Option<DBValue>> { fn remove(&mut self, key: &[u8]) -> Result<Option<DBValue>, H::Out, C::Error> {
trace!(target: "trie", "remove: key={:?}", key.pretty()); trace!(target: "trie", "remove: key={:?}", key.pretty());
let root_handle = self.root_handle(); let root_handle = self.root_handle();
@ -928,8 +940,8 @@ impl<'a> TrieMut for TrieDBMut<'a> {
} }
None => { None => {
trace!(target: "trie", "remove: obliterated trie"); trace!(target: "trie", "remove: obliterated trie");
self.root_handle = NodeHandle::Hash(KECCAK_NULL_RLP); self.root_handle = NodeHandle::Hash(C::HASHED_NULL_NODE);
*self.root = KECCAK_NULL_RLP; *self.root = C::HASHED_NULL_NODE;
} }
} }
@ -937,7 +949,11 @@ impl<'a> TrieMut for TrieDBMut<'a> {
} }
} }
impl<'a> Drop for TrieDBMut<'a> { impl<'a, H, C> Drop for TrieDBMut<'a, H, C>
where
H: Hasher,
C: NodeCodec<H>
{
fn drop(&mut self) { fn drop(&mut self) {
self.commit(); self.commit();
} }
@ -945,18 +961,20 @@ impl<'a> Drop for TrieDBMut<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
extern crate triehash;
use self::triehash::trie_root;
use hashdb::*;
use memorydb::*;
use super::*;
use bytes::ToPretty; use bytes::ToPretty;
use keccak::KECCAK_NULL_RLP; use hashdb::{DBValue, Hasher, HashDB};
use super::super::TrieMut; use keccak_hasher::KeccakHasher;
use memorydb::MemoryDB;
use rlp::{Decodable, Encodable};
use triehash::trie_root;
use standardmap::*; use standardmap::*;
use ethtrie::{TrieDBMut, RlpCodec, trie::{TrieMut, NodeCodec}};
use env_logger;
use ethereum_types::H256;
fn populate_trie<'db>(db: &'db mut HashDB, root: &'db mut H256, v: &[(Vec<u8>, Vec<u8>)]) -> TrieDBMut<'db> { fn populate_trie<'db, H, C>(db: &'db mut HashDB<KeccakHasher>, root: &'db mut H256, v: &[(Vec<u8>, Vec<u8>)]) -> TrieDBMut<'db>
where H: Hasher, H::Out: Decodable + Encodable, C: NodeCodec<H>
{
let mut t = TrieDBMut::new(db, root); let mut t = TrieDBMut::new(db, root);
for i in 0..v.len() { for i in 0..v.len() {
let key: &[u8]= &v[i].0; let key: &[u8]= &v[i].0;
@ -975,8 +993,7 @@ mod tests {
#[test] #[test]
fn playpen() { fn playpen() {
::ethcore_logger::init_log(); env_logger::init();
let mut seed = H256::new(); let mut seed = H256::new();
for test_i in 0..10 { for test_i in 0..10 {
if test_i % 50 == 0 { if test_i % 50 == 0 {
@ -991,9 +1008,9 @@ mod tests {
}.make_with(&mut seed); }.make_with(&mut seed);
let real = trie_root(x.clone()); let real = trie_root(x.clone());
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut memtrie = populate_trie(&mut memdb, &mut root, &x); let mut memtrie = populate_trie::<_, RlpCodec>(&mut memdb, &mut root, &x);
memtrie.commit(); memtrie.commit();
if *memtrie.root() != real { if *memtrie.root() != real {
@ -1007,7 +1024,7 @@ mod tests {
assert_eq!(*memtrie.root(), real); assert_eq!(*memtrie.root(), real);
unpopulate_trie(&mut memtrie, &x); unpopulate_trie(&mut memtrie, &x);
memtrie.commit(); memtrie.commit();
if *memtrie.root() != KECCAK_NULL_RLP { if *memtrie.root() != RlpCodec::HASHED_NULL_NODE {
println!("- TRIE MISMATCH"); println!("- TRIE MISMATCH");
println!(""); println!("");
println!("{:?} vs {:?}", memtrie.root(), real); println!("{:?} vs {:?}", memtrie.root(), real);
@ -1015,21 +1032,21 @@ mod tests {
println!("{:?} -> {:?}", i.0.pretty(), i.1.pretty()); println!("{:?} -> {:?}", i.0.pretty(), i.1.pretty());
} }
} }
assert_eq!(*memtrie.root(), KECCAK_NULL_RLP); assert_eq!(*memtrie.root(), RlpCodec::HASHED_NULL_NODE);
} }
} }
#[test] #[test]
fn init() { fn init() {
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root); let mut t = TrieDBMut::new(&mut memdb, &mut root);
assert_eq!(*t.root(), KECCAK_NULL_RLP); assert_eq!(*t.root(), RlpCodec::HASHED_NULL_NODE);
} }
#[test] #[test]
fn insert_on_empty() { fn insert_on_empty() {
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root); let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
@ -1040,14 +1057,15 @@ mod tests {
fn remove_to_empty() { fn remove_to_empty() {
let big_value = b"00000000000000000000000000000000"; let big_value = b"00000000000000000000000000000000";
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut t1 = TrieDBMut::new(&mut memdb, &mut root); let mut t1 = TrieDBMut::new(&mut memdb, &mut root);
t1.insert(&[0x01, 0x23], big_value).unwrap(); t1.insert(&[0x01, 0x23], big_value).unwrap();
t1.insert(&[0x01, 0x34], big_value).unwrap(); t1.insert(&[0x01, 0x34], big_value).unwrap();
let mut memdb2 = MemoryDB::new(); let mut memdb2 = MemoryDB::<KeccakHasher>::new();
let mut root2 = H256::new(); let mut root2 = H256::new();
let mut t2 = TrieDBMut::new(&mut memdb2, &mut root2); let mut t2 = TrieDBMut::new(&mut memdb2, &mut root2);
t2.insert(&[0x01], big_value).unwrap(); t2.insert(&[0x01], big_value).unwrap();
t2.insert(&[0x01, 0x23], big_value).unwrap(); t2.insert(&[0x01, 0x23], big_value).unwrap();
t2.insert(&[0x01, 0x34], big_value).unwrap(); t2.insert(&[0x01, 0x34], big_value).unwrap();
@ -1056,7 +1074,7 @@ mod tests {
#[test] #[test]
fn insert_replace_root() { fn insert_replace_root() {
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root); let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
@ -1066,7 +1084,7 @@ mod tests {
#[test] #[test]
fn insert_make_branch_root() { fn insert_make_branch_root() {
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root); let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
@ -1079,7 +1097,7 @@ mod tests {
#[test] #[test]
fn insert_into_branch_root() { fn insert_into_branch_root() {
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root); let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
@ -1094,7 +1112,7 @@ mod tests {
#[test] #[test]
fn insert_value_into_branch_root() { fn insert_value_into_branch_root() {
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root); let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
@ -1107,7 +1125,7 @@ mod tests {
#[test] #[test]
fn insert_split_leaf() { fn insert_split_leaf() {
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root); let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
@ -1120,7 +1138,7 @@ mod tests {
#[test] #[test]
fn insert_split_extenstion() { fn insert_split_extenstion() {
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root); let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01, 0x23, 0x45], &[0x01]).unwrap(); t.insert(&[0x01, 0x23, 0x45], &[0x01]).unwrap();
@ -1138,7 +1156,7 @@ mod tests {
let big_value0 = b"00000000000000000000000000000000"; let big_value0 = b"00000000000000000000000000000000";
let big_value1 = b"11111111111111111111111111111111"; let big_value1 = b"11111111111111111111111111111111";
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root); let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], big_value0).unwrap(); t.insert(&[0x01u8, 0x23], big_value0).unwrap();
@ -1153,7 +1171,7 @@ mod tests {
fn insert_duplicate_value() { fn insert_duplicate_value() {
let big_value = b"00000000000000000000000000000000"; let big_value = b"00000000000000000000000000000000";
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root); let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], big_value).unwrap(); t.insert(&[0x01u8, 0x23], big_value).unwrap();
@ -1166,15 +1184,15 @@ mod tests {
#[test] #[test]
fn test_at_empty() { fn test_at_empty() {
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let t = TrieDBMut::new(&mut memdb, &mut root); let t = TrieDBMut::new(&mut memdb, &mut root);
assert_eq!(t.get(&[0x5]), Ok(None)); assert_eq!(t.get(&[0x5]).unwrap(), None);
} }
#[test] #[test]
fn test_at_one() { fn test_at_one() {
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root); let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
@ -1185,7 +1203,7 @@ mod tests {
#[test] #[test]
fn test_at_three() { fn test_at_three() {
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root); let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
@ -1194,12 +1212,12 @@ mod tests {
assert_eq!(t.get(&[0x01, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23])); assert_eq!(t.get(&[0x01, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23]));
assert_eq!(t.get(&[0xf1, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0xf1u8, 0x23])); assert_eq!(t.get(&[0xf1, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0xf1u8, 0x23]));
assert_eq!(t.get(&[0x81, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x81u8, 0x23])); assert_eq!(t.get(&[0x81, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x81u8, 0x23]));
assert_eq!(t.get(&[0x82, 0x23]), Ok(None)); assert_eq!(t.get(&[0x82, 0x23]).unwrap(), None);
t.commit(); t.commit();
assert_eq!(t.get(&[0x01, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23])); assert_eq!(t.get(&[0x01, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23]));
assert_eq!(t.get(&[0xf1, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0xf1u8, 0x23])); assert_eq!(t.get(&[0xf1, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0xf1u8, 0x23]));
assert_eq!(t.get(&[0x81, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x81u8, 0x23])); assert_eq!(t.get(&[0x81, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x81u8, 0x23]));
assert_eq!(t.get(&[0x82, 0x23]), Ok(None)); assert_eq!(t.get(&[0x82, 0x23]).unwrap(), None);
} }
#[test] #[test]
@ -1215,14 +1233,14 @@ mod tests {
}.make_with(&mut seed); }.make_with(&mut seed);
let real = trie_root(x.clone()); let real = trie_root(x.clone());
let mut memdb = MemoryDB::new(); let mut memdb = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut memtrie = populate_trie(&mut memdb, &mut root, &x); let mut memtrie = populate_trie::<_, RlpCodec>(&mut memdb, &mut root, &x);
let mut y = x.clone(); let mut y = x.clone();
y.sort_by(|ref a, ref b| a.0.cmp(&b.0)); y.sort_by(|ref a, ref b| a.0.cmp(&b.0));
let mut memdb2 = MemoryDB::new(); let mut memdb2 = MemoryDB::<KeccakHasher>::new();
let mut root2 = H256::new(); let mut root2 = H256::new();
let mut memtrie_sorted = populate_trie(&mut memdb2, &mut root2, &y); let mut memtrie_sorted = populate_trie::<_, RlpCodec>(&mut memdb2, &mut root2, &y);
if *memtrie.root() != real || *memtrie_sorted.root() != real { if *memtrie.root() != real || *memtrie_sorted.root() != real {
println!("TRIE MISMATCH"); println!("TRIE MISMATCH");
println!(""); println!("");
@ -1242,15 +1260,15 @@ mod tests {
#[test] #[test]
fn test_trie_existing() { fn test_trie_existing() {
let mut db = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut db = MemoryDB::new();
{ {
let mut t = TrieDBMut::new(&mut db, &mut root); let mut t = TrieDBMut::new(&mut db, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
} }
{ {
let _ = TrieDBMut::from_existing(&mut db, &mut root); let _ = TrieDBMut::from_existing(&mut db, &mut root);
} }
} }
@ -1265,7 +1283,7 @@ mod tests {
count: 4, count: 4,
}.make_with(&mut seed); }.make_with(&mut seed);
let mut db = MemoryDB::new(); let mut db = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut t = TrieDBMut::new(&mut db, &mut root); let mut t = TrieDBMut::new(&mut db, &mut root);
for &(ref key, ref value) in &x { for &(ref key, ref value) in &x {
@ -1279,7 +1297,7 @@ mod tests {
} }
assert!(t.is_empty()); assert!(t.is_empty());
assert_eq!(*t.root(), KECCAK_NULL_RLP); assert_eq!(*t.root(), RlpCodec::HASHED_NULL_NODE);
} }
#[test] #[test]
@ -1293,7 +1311,7 @@ mod tests {
count: 4, count: 4,
}.make_with(&mut seed); }.make_with(&mut seed);
let mut db = MemoryDB::new(); let mut db = MemoryDB::<KeccakHasher>::new();
let mut root = H256::new(); let mut root = H256::new();
let mut t = TrieDBMut::new(&mut db, &mut root); let mut t = TrieDBMut::new(&mut db, &mut root);
for &(ref key, ref value) in &x { for &(ref key, ref value) in &x {

View File

@ -1,7 +1,7 @@
[package] [package]
name = "plain_hasher" name = "plain_hasher"
description = "Hasher for 32-bit keys." description = "Hasher for 32-bit keys."
version = "0.1.0" version = "0.2.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
license = "MIT" license = "MIT"
keywords = ["hash", "hasher"] keywords = ["hash", "hasher"]
@ -10,3 +10,4 @@ homepage = "https://github.com/paritytech/plain_hasher"
[dependencies] [dependencies]
crunchy = "0.1.6" crunchy = "0.1.6"
ethereum-types = "0.3" ethereum-types = "0.3"
hashdb = { version = "0.2.0", path = "../hashdb" }

View File

@ -17,11 +17,12 @@
#[macro_use] #[macro_use]
extern crate crunchy; extern crate crunchy;
extern crate ethereum_types; extern crate ethereum_types;
extern crate hashdb;
use ethereum_types::H256; use ethereum_types::H256;
use std::collections::{HashMap, HashSet}; // use hashdb::Hasher;
use std::hash; use std::hash;
use std::collections::{HashMap, HashSet};
/// Specialized version of `HashMap` with H256 keys and fast hashing function. /// Specialized version of `HashMap` with H256 keys and fast hashing function.
pub type H256FastMap<T> = HashMap<H256, T, hash::BuildHasherDefault<PlainHasher>>; pub type H256FastMap<T> = HashMap<H256, T, hash::BuildHasherDefault<PlainHasher>>;
/// Specialized version of `HashSet` with H256 keys and fast hashing function. /// Specialized version of `HashSet` with H256 keys and fast hashing function.

View File

@ -14,13 +14,13 @@
#![feature(test)] #![feature(test)]
extern crate test; extern crate ethereum_types;
extern crate ethcore_bigint as bigint;
extern crate rlp; extern crate rlp;
extern crate test;
use test::Bencher; use ethereum_types::U256;
use bigint::prelude::U256;
use rlp::{RlpStream, Rlp}; use rlp::{RlpStream, Rlp};
use test::Bencher;
#[bench] #[bench]
fn bench_stream_u64_value(b: &mut Bencher) { fn bench_stream_u64_value(b: &mut Bencher) {
@ -38,7 +38,7 @@ fn bench_decode_u64_value(b: &mut Bencher) {
// u64 // u64
let data = vec![0x88, 0x10, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]; let data = vec![0x88, 0x10, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
let rlp = Rlp::new(&data); let rlp = Rlp::new(&data);
let _: u64 = rlp.as_val(); let _: u64 = rlp.as_val().unwrap();
}); });
} }
@ -61,7 +61,7 @@ fn bench_decode_u256_value(b: &mut Bencher) {
0x30, 0x40, 0x50, 0x60, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x40, 0x50, 0x60, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0xf0]; 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0xf0];
let rlp = Rlp::new(&data); let rlp = Rlp::new(&data);
let _ : U256 = rlp.as_val(); let _ : U256 = rlp.as_val().unwrap();
}); });
} }
@ -83,11 +83,11 @@ fn bench_decode_nested_empty_lists(b: &mut Bencher) {
// [ [], [[]], [ [], [[]] ] ] // [ [], [[]], [ [], [[]] ] ]
let data = vec![0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0]; let data = vec![0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0];
let rlp = Rlp::new(&data); let rlp = Rlp::new(&data);
let _v0: Vec<u16> = rlp.at(0).as_list(); let _v0: Vec<u16> = rlp.at(0).unwrap().as_list().unwrap();
let _v1: Vec<u16> = rlp.at(1).at(0).as_list(); let _v1: Vec<u16> = rlp.at(1).unwrap().at(0).unwrap().as_list().unwrap();
let nested_rlp = rlp.at(2); let nested_rlp = rlp.at(2).unwrap();
let _v2a: Vec<u16> = nested_rlp.at(0).as_list(); let _v2a: Vec<u16> = nested_rlp.at(0).unwrap().as_list().unwrap();
let _v2b: Vec<u16> = nested_rlp.at(1).at(0).as_list(); let _v2b: Vec<u16> = nested_rlp.at(1).unwrap().at(0).unwrap().as_list().unwrap();
}); });
} }

View File

@ -43,8 +43,8 @@ mod rlpin;
mod stream; mod stream;
mod impls; mod impls;
use std::borrow::Borrow;
use elastic_array::ElasticArray1024; use elastic_array::ElasticArray1024;
use std::borrow::Borrow;
pub use error::DecoderError; pub use error::DecoderError;
pub use traits::{Decodable, Encodable}; pub use traits::{Decodable, Encodable};

View File

@ -58,6 +58,50 @@ impl RlpStream {
stream stream
} }
/// Apends null to the end of stream, chainable.
///
/// ```rust
/// extern crate rlp;
/// use rlp::*;
///
/// fn main () {
/// let mut stream = RlpStream::new_list(2);
/// stream.append_empty_data().append_empty_data();
/// let out = stream.out();
/// assert_eq!(out, vec![0xc2, 0x80, 0x80]);
/// }
/// ```
pub fn append_empty_data(&mut self) -> &mut Self {
// self push raw item
self.buffer.push(0x80);
// try to finish and prepend the length
self.note_appended(1);
// return chainable self
self
}
/// Drain the object and return the underlying ElasticArray. Panics if it is not finished.
pub fn drain(self) -> ElasticArray1024<u8> {
match self.is_finished() {
true => self.buffer,
false => panic!()
}
}
/// Appends raw (pre-serialised) RLP data. Use with caution. Chainable.
pub fn append_raw<'a>(&'a mut self, bytes: &[u8], item_count: usize) -> &'a mut Self {
// push raw items
self.buffer.append_slice(bytes);
// try to finish and prepend the length
self.note_appended(item_count);
// return chainable self
self
}
/// Appends value to the end of stream, chainable. /// Appends value to the end of stream, chainable.
/// ///
/// ```rust /// ```rust
@ -145,42 +189,6 @@ impl RlpStream {
self self
} }
/// Apends null to the end of stream, chainable.
///
/// ```rust
/// extern crate rlp;
/// use rlp::*;
///
/// fn main () {
/// let mut stream = RlpStream::new_list(2);
/// stream.append_empty_data().append_empty_data();
/// let out = stream.out();
/// assert_eq!(out, vec![0xc2, 0x80, 0x80]);
/// }
/// ```
pub fn append_empty_data(&mut self) -> &mut RlpStream {
// self push raw item
self.buffer.push(0x80);
// try to finish and prepend the length
self.note_appended(1);
// return chainable self
self
}
/// Appends raw (pre-serialised) RLP data. Use with caution. Chainable.
pub fn append_raw<'a>(&'a mut self, bytes: &[u8], item_count: usize) -> &'a mut RlpStream {
// push raw items
self.buffer.append_slice(bytes);
// try to finish and prepend the length
self.note_appended(item_count);
// return chainable self
self
}
/// Appends raw (pre-serialised) RLP data. Checks for size oveflow. /// Appends raw (pre-serialised) RLP data. Checks for size oveflow.
pub fn append_raw_checked<'a>(&'a mut self, bytes: &[u8], item_count: usize, max_size: usize) -> bool { pub fn append_raw_checked<'a>(&'a mut self, bytes: &[u8], item_count: usize, max_size: usize) -> bool {
if self.estimate_size(bytes.len()) > max_size { if self.estimate_size(bytes.len()) > max_size {
@ -215,7 +223,7 @@ impl RlpStream {
/// ```rust /// ```rust
/// extern crate rlp; /// extern crate rlp;
/// use rlp::*; /// use rlp::*;
/// ///
/// fn main () { /// fn main () {
/// let mut stream = RlpStream::new_list(3); /// let mut stream = RlpStream::new_list(3);
/// stream.append(&"cat"); /// stream.append(&"cat");
@ -300,14 +308,6 @@ impl RlpStream {
BasicEncoder::new(self) BasicEncoder::new(self)
} }
/// Drain the object and return the underlying ElasticArray.
pub fn drain(self) -> ElasticArray1024<u8> {
match self.is_finished() {
true => self.buffer,
false => panic!()
}
}
/// Finalize current ubnbound list. Panics if no unbounded list has been opened. /// Finalize current ubnbound list. Panics if no unbounded list has been opened.
pub fn complete_unbounded_list(&mut self) { pub fn complete_unbounded_list(&mut self) {
let list = self.unfinished_lists.pop().expect("No open list."); let list = self.unfinished_lists.pop().expect("No open list.");

View File

@ -423,4 +423,3 @@ fn test_rlp_stream_unbounded_list() {
stream.complete_unbounded_list(); stream.complete_unbounded_list();
assert!(stream.is_finished()); assert!(stream.is_finished());
} }