diff --git a/CHANGELOG.md b/CHANGELOG.md index a724287df..606d1129b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +## Parity-Ethereum [v2.6.7](https://github.com/paritytech/parity-ethereum/releases/tag/v2.6.7) + +Parity Ethereum v2.6.7-beta is a patch release that adds Istanbul hardfork +block numbers for POA and xDai networks, implements ECIP-1056 and implements +EIP-2384/2387 - Muir Glacier. + +The full list of included changes: +* Enable EIP-2384 for ice age hard fork (#11281) +* ethcore/res: activate agharta on classic 9573000 (#11331) +* Istanbul HF in xDai (2019-12-12) (#11299) +* Istanbul HF in POA Core (2019-12-19) (#11298) +* Istanbul HF in POA Sokol (2019-12-05) (#11282) +* Activate ecip-1061 on kotti and mordor (#11338) +* Enable basic verification of local transactions (#11332) +* Disallow EIP-86 style null signatures for transactions outside tests (#11335) +* SecretStore database migration to v4 (#11322) + ## Parity-Ethereum [v2.6.6](https://github.com/paritytech/parity-ethereum/releases/tag/v2.6.6) Parity Ethereum v2.6.6-beta is an emergency patch release that adds the missing diff --git a/Cargo.lock b/Cargo.lock index 276798757..80f89eb06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2626,7 +2626,7 @@ dependencies = [ "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "jni 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "panic_hook 0.1.0", - "parity-ethereum 2.6.6", + "parity-ethereum 2.6.7", "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2673,7 +2673,7 @@ dependencies = [ [[package]] name = "parity-ethereum" -version = "2.6.6" +version = "2.6.7" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2726,7 +2726,7 @@ dependencies = [ "parity-runtime 0.1.0", "parity-updater 1.12.0", "parity-util-mem 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-version 2.6.6", + "parity-version 2.6.7", "parity-whisper 0.1.0", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2869,7 +2869,7 @@ dependencies = [ "parity-crypto 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-runtime 0.1.0", "parity-updater 1.12.0", - "parity-version 2.6.6", + "parity-version 2.6.7", "parking_lot 0.9.0 (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.6.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2968,7 +2968,7 @@ dependencies = [ "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-hash-fetch 1.12.0", "parity-path 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-version 2.6.6", + "parity-version 2.6.7", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2993,7 +2993,7 @@ dependencies = [ [[package]] name = "parity-version" -version = "2.6.6" +version = "2.6.7" dependencies = [ "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 93e0e5dd7..97e8c70b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ description = "Parity Ethereum client" name = "parity-ethereum" # NOTE Make sure to update util/version/Cargo.toml as well -version = "2.6.6" +version = "2.6.7" license = "GPL-3.0" authors = ["Parity Technologies "] diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 38515293c..8bad0167b 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -76,6 +76,7 @@ rand_xorshift = "0.1.1" [dev-dependencies] blooms-db = { path = "../util/blooms-db" } +common-types = { path = "types", features = ["test-helpers"] } criterion = "0.3" env_logger = "0.5" ethcore-accounts = { path = "../accounts" } diff --git a/ethcore/res/ethereum/classic.json b/ethcore/res/ethereum/classic.json index 413fcabec..85de09aa0 100644 --- a/ethcore/res/ethereum/classic.json +++ b/ethcore/res/ethereum/classic.json @@ -37,7 +37,10 @@ "eip140Transition": "0x85d9a0", "eip211Transition": "0x85d9a0", "eip214Transition": "0x85d9a0", - "eip658Transition": "0x85d9a0" + "eip658Transition": "0x85d9a0", + "eip145Transition": "0x921288", + "eip1014Transition": "0x921288", + "eip1052Transition": "0x921288" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/foundation.json b/ethcore/res/ethereum/foundation.json index 3eadfe410..86bfa82ab 100644 --- a/ethcore/res/ethereum/foundation.json +++ b/ethcore/res/ethereum/foundation.json @@ -136,7 +136,8 @@ "eip100bTransition": "0x42ae50", "difficultyBombDelays": { "0x42ae50": "0x2dc6c0", - "0x6f1580": "0x1e8480" + "0x6f1580": "0x1e8480", + "0x8c6180": "0x3d0900" } } } diff --git a/ethcore/res/ethereum/kotti.json b/ethcore/res/ethereum/kotti.json index 7876fe8cd..25ba61961 100644 --- a/ethcore/res/ethereum/kotti.json +++ b/ethcore/res/ethereum/kotti.json @@ -24,6 +24,10 @@ "eip658Transition": "0xaef49", "eip1014Transition": "0x1a064d", "eip1052Transition": "0x1a064d", + "eip1283Transition": "0x1f67cf", + "eip1344Transition": "0x1f67cf", + "eip1706Transition": "0x1f67cf", + "eip2028Transition": "0x1f67cf", "gasLimitBoundDivisor": "0x400", "maxCodeSize": "0x6000", "maxCodeSizeTransition": "0xaef49", @@ -123,7 +127,7 @@ "0xaef49": { "price": { "alt_bn128_const_operations": { "price": 500 }} }, - "0x7fffffffffffff": { + "0x1f67cf": { "info": "EIP 1108 transition", "price": { "alt_bn128_const_operations": { "price": 150 }} } @@ -138,7 +142,7 @@ "0xaef49": { "price": { "alt_bn128_const_operations": { "price": 40000 }} }, - "0x7fffffffffffff": { + "0x1f67cf": { "info": "EIP 1108 transition", "price": { "alt_bn128_const_operations": { "price": 6000 }} } @@ -153,7 +157,7 @@ "0xaef49": { "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} }, - "0x7fffffffffffff": { + "0x1f67cf": { "info": "EIP 1108 transition", "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } @@ -161,7 +165,16 @@ } }, "0x0000000000000000000000000000000000000009": { - "balance": "0x1" + "balance": "0x1", + "builtin": { + "name": "blake2_f", + "activate_at": "0x1f67cf", + "pricing": { + "blake2_f": { + "gas_per_round": 1 + } + } + } }, "0x000000000000000000000000000000000000000a": { "balance": "0x1" diff --git a/ethcore/res/ethereum/mordor.json b/ethcore/res/ethereum/mordor.json index ceaa9de6d..c5ae911f0 100644 --- a/ethcore/res/ethereum/mordor.json +++ b/ethcore/res/ethereum/mordor.json @@ -37,7 +37,11 @@ "eip658Transition":"0x0", "eip145Transition":"0x498bb", "eip1014Transition":"0x498bb", - "eip1052Transition":"0x498bb" + "eip1052Transition":"0x498bb", + "eip1283Transition":"0xbe10b", + "eip1344Transition":"0xbe10b", + "eip1706Transition":"0xbe10b", + "eip2028Transition":"0xbe10b" }, "genesis":{ "seal":{ @@ -59,7 +63,9 @@ "enode://1813e90a0afdd7c1e4892c5376960e3577a9e6c5a4f86fa405a405c7421a4a1608248d77cc90333842f13d8954d82113dec480cfb76b4fef8cb475157cf4d5f2@10.28.224.3:30000", "enode://2b69a3926f36a7748c9021c34050be5e0b64346225e477fe7377070f6289bd363b2be73a06010fd516e6ea3ee90778dd0399bc007bb1281923a79374f842675a@51.15.116.226:30303", "enode://621e28e529146fd501709194885f50540c494f1a2985d1fb4ec8769226b5cb0b0d1a11545926077821474c2767cdd87888ead8a2509a2c9069dd5584e4b1c3b8@10.28.223.8:30000", - "enode://a59e33ccd2b3e52d578f1fbd70c6f9babda2650f0760d6ff3b37742fdcdfdb3defba5d56d315b40c46b70198c7621e63ffa3f987389c7118634b0fefbbdfa7fd@51.15.116.226:30303" + "enode://a59e33ccd2b3e52d578f1fbd70c6f9babda2650f0760d6ff3b37742fdcdfdb3defba5d56d315b40c46b70198c7621e63ffa3f987389c7118634b0fefbbdfa7fd@51.15.116.226:30303", + "enode://f840b007500f50c98ea6f9c9e56dabf4690bbbbb7036d43682c531204341aff8315013547e5bee54117eb22bd3603585ae6bf713d9fa710659533fcab65d5b84@35.238.101.58:30000", + "enode://19eda672030ad5debb98c9069b3e99d12438b96506325d9f3f82d76c5f8ce4942d345f41700a5223900e75ad48e76713b74c1b694d67a10c2112540035922256@35.238.101.58:30000" ], "accounts":{ "0x0000000000000000000000000000000000000001":{ @@ -128,7 +134,7 @@ } } }, - "0x7fffffffffffff":{ + "0xbe10b":{ "info":"EIP 1108 transition", "price":{ "alt_bn128_const_operations":{ @@ -150,7 +156,7 @@ } } }, - "0x7fffffffffffff":{ + "0xbe10b":{ "info":"EIP 1108 transition", "price":{ "alt_bn128_const_operations":{ @@ -173,7 +179,7 @@ } } }, - "0x7fffffffffffff":{ + "0xbe10b":{ "info":"EIP 1108 transition", "price":{ "alt_bn128_pairing":{ @@ -184,6 +190,17 @@ } } } + }, + "0x0000000000000000000000000000000000000009": { + "builtin": { + "name": "blake2_f", + "activate_at": "0xbe10b", + "pricing": { + "blake2_f": { + "gas_per_round": 1 + } + } + } } } } diff --git a/ethcore/res/ethereum/poacore.json b/ethcore/res/ethereum/poacore.json index 36c26fc1d..1806f67cf 100644 --- a/ethcore/res/ethereum/poacore.json +++ b/ethcore/res/ethereum/poacore.json @@ -37,7 +37,12 @@ "eip658Transition": "0x0", "eip145Transition": 8582254, "eip1014Transition": 8582254, - "eip1052Transition": 8582254 + "eip1052Transition": 8582254, + "eip1283Transition": 12598600, + "eip1344Transition": 12598600, + "eip1706Transition": 12598600, + "eip1884Transition": 12598600, + "eip2028Transition": 12598600 }, "genesis": { "seal": { @@ -5406,57 +5411,89 @@ "enode://96678da10ac83769ab3f63114a41b57b700476c5ac02719b878fa89909a936551bb7609aa09b068bf89903206fa03f23e1b5b9117ca278de304c2570b87dcb27@35.175.15.164:30303" ], "accounts": { - "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x0", "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { + "0x0000000000000000000000000000000000000005": { + "builtin": { + "name": "modexp", + "pricing": { + "0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0x0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "pricing": { "0": { "price": { "alt_bn128_const_operations": { "price": 500 }} }, - "0x7fffffffffffff": { + "12598600": { "info": "EIP 1108 transition", "price": { "alt_bn128_const_operations": { "price": 150 }} } } } }, - "0000000000000000000000000000000000000007": { + "0x0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "pricing": { "0": { "price": { "alt_bn128_const_operations": { "price": 40000 }} }, - "0x7fffffffffffff": { + "12598600": { "info": "EIP 1108 transition", "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } }, - "0000000000000000000000000000000000000008": { + "0x0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "pricing": { "0": { "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} }, - "0x7fffffffffffff": { + "12598600": { "info": "EIP 1108 transition", "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } }, + "0x0000000000000000000000000000000000000009": { + "builtin": { + "name": "blake2_f", + "pricing": { + "12598600": { + "info": "EIP 1108 transition", + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } + } + } + } + }, "0x0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { - "linear": { - "base": 3000, - "word": 0 + "0": { + "price": { + "linear": { + "base": 3000, + "word": 0 + } + } } } } @@ -5466,9 +5503,13 @@ "builtin": { "name": "sha256", "pricing": { - "linear": { - "base": 60, - "word": 12 + "0": { + "price": { + "linear": { + "base": 60, + "word": 12 + } + } } } } @@ -5478,9 +5519,13 @@ "builtin": { "name": "ripemd160", "pricing": { - "linear": { - "base": 600, - "word": 120 + "0": { + "price": { + "linear": { + "base": 600, + "word": 120 + } + } } } } @@ -5490,9 +5535,13 @@ "builtin": { "name": "identity", "pricing": { - "linear": { - "base": 15, - "word": 3 + "0": { + "price": { + "linear": { + "base": 15, + "word": 3 + } + } } } } diff --git a/ethcore/res/ethereum/poasokol.json b/ethcore/res/ethereum/poasokol.json index c8f1f03e5..15b53132c 100644 --- a/ethcore/res/ethereum/poasokol.json +++ b/ethcore/res/ethereum/poasokol.json @@ -42,7 +42,12 @@ "eip1014Transition": 6464300, "eip1052Transition": 6464300, "eip1283Transition": 6464300, - "eip1283DisableTransition": 7026400 + "eip1283DisableTransition": 7026400, + "eip1283ReenableTransition": 12095200, + "eip1344Transition": 12095200, + "eip1706Transition": 12095200, + "eip1884Transition": 12095200, + "eip2028Transition": 12095200 }, "genesis": { "seal": { @@ -5303,6 +5308,7 @@ ] }, "nodes": [ + "enode://bdcd6f875583df2bd8094f08ae58c7c2db6ed67795ca8c0e6415a30721d3657291aec9b933d15e17e0b36ad7a76424a1447ddbfc75809a04f7a0ffef5617dd56@3.91.206.172:30303", "enode://8e0af07c86ec36590bb6368e7ad0c45b6dc658f5fb66ec68889a614affddda5e021bd513bcf4fb2fae4a3bbe08cf0de84f037cd58478a89665dfce1ded2595c7@34.236.37.74:30303", "enode://f1a5100a81cb73163ae450c584d06b1f644aa4fad4486c6aeb4c384b343c54bb66c744aa5f133af66ea1b25f0f4a454f04878f3e96ee4cd2390c047396d6357b@209.97.158.4:30303", "enode://0d1e0372f63a3f0b82d66635ea101ecc0f6797788a078805cc933dd93e6a22f7c9fa51ab4e2d21da02d04480ef19f3bbb9a2b41dd1c262085d295a354bb8b0f9@18.217.47.209:30303", @@ -5312,7 +5318,20 @@ "enode://b022ff70b5fcaf9596ae5efed99a8198b4ae0578ee9d17b733609d803a75cef95d3a2a18e50dca9a7c3b26139f158c59eaf8b5fb8d1d331c9a46934a78acabe8@206.189.76.128:30303" ], "accounts": { - "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x0", "pricing": { "modexp": { "divisor": 20 } } } }, + "0000000000000000000000000000000000000005": { + "builtin": { + "name": "modexp", + "pricing": { + "0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", @@ -5320,7 +5339,7 @@ "0": { "price": { "alt_bn128_const_operations": { "price": 500 }} }, - "0x7fffffffffffff": { + "12095200": { "info": "EIP 1108 transition", "price": { "alt_bn128_const_operations": { "price": 150 }} } @@ -5334,7 +5353,7 @@ "0": { "price": { "alt_bn128_const_operations": { "price": 40000 }} }, - "0x7fffffffffffff": { + "12095200": { "info": "EIP 1108 transition", "price": { "alt_bn128_const_operations": { "price": 6000 }} } @@ -5348,21 +5367,40 @@ "0": { "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} }, - "0x7fffffffffffff": { + "12095200": { "info": "EIP 1108 transition", "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } }, + "0x0000000000000000000000000000000000000009": { + "builtin": { + "name": "blake2_f", + "pricing": { + "12095200": { + "info": "EIP 1108 transition", + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } + } + } + } + }, "0x0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { - "linear": { - "base": 3000, - "word": 0 + "0": { + "price": { + "linear": { + "base": 3000, + "word": 0 + } + } } } } @@ -5372,9 +5410,13 @@ "builtin": { "name": "sha256", "pricing": { - "linear": { - "base": 60, - "word": 12 + "0": { + "price": { + "linear": { + "base": 60, + "word": 12 + } + } } } } @@ -5384,9 +5426,13 @@ "builtin": { "name": "ripemd160", "pricing": { - "linear": { - "base": 600, - "word": 120 + "0": { + "price": { + "linear": { + "base": 600, + "word": 120 + } + } } } } @@ -5396,9 +5442,13 @@ "builtin": { "name": "identity", "pricing": { - "linear": { - "base": 15, - "word": 3 + "0": { + "price": { + "linear": { + "base": 15, + "word": 3 + } + } } } } diff --git a/ethcore/res/ethereum/ropsten.json b/ethcore/res/ethereum/ropsten.json index cd5352414..477512ec9 100644 --- a/ethcore/res/ethereum/ropsten.json +++ b/ethcore/res/ethereum/ropsten.json @@ -16,7 +16,8 @@ "eip100bTransition": "0x19f0a0", "difficultyBombDelays": { "0x19f0a0": "0x2dc6c0", - "0x408b70": "0x1e8480" + "0x408b70": "0x1e8480", + "0x6c993d": "0x3d0900" } } } diff --git a/ethcore/res/ethereum/xdai.json b/ethcore/res/ethereum/xdai.json index 9e48f13d4..0aa48b431 100644 --- a/ethcore/res/ethereum/xdai.json +++ b/ethcore/res/ethereum/xdai.json @@ -37,6 +37,11 @@ "eip1052Transition": 1604400, "eip1283Transition": 1604400, "eip1283DisableTransition": 2508800, + "eip1283ReenableTransition": 7298030, + "eip1344Transition": 7298030, + "eip1706Transition": 7298030, + "eip1884Transition": 7298030, + "eip2028Transition": 7298030, "registrar": "0x1ec97dc137f5168af053c24460a1200502e1a9d2" }, "genesis": { @@ -2865,10 +2870,13 @@ "0x0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", - "activate_at": "0x0", "pricing": { - "modexp": { - "divisor": 20 + "0": { + "price": { + "modexp": { + "divisor": 20 + } + } } } } @@ -2880,7 +2888,7 @@ "0": { "price": { "alt_bn128_const_operations": { "price": 500 }} }, - "0x7fffffffffffff": { + "7298030": { "info": "EIP 1108 transition", "price": { "alt_bn128_const_operations": { "price": 150 }} } @@ -2894,7 +2902,7 @@ "0": { "price": { "alt_bn128_const_operations": { "price": 40000 }} }, - "0x7fffffffffffff": { + "7298030": { "info": "EIP 1108 transition", "price": { "alt_bn128_const_operations": { "price": 6000 }} } @@ -2908,21 +2916,40 @@ "0": { "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} }, - "0x7fffffffffffff": { + "7298030": { "info": "EIP 1108 transition", "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } }, + "0x0000000000000000000000000000000000000009": { + "builtin": { + "name": "blake2_f", + "pricing": { + "7298030": { + "info": "EIP 1108 transition", + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } + } + } + } + }, "0x0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { - "linear": { - "base": 3000, - "word": 0 + "0": { + "price": { + "linear": { + "base": 3000, + "word": 0 + } + } } } } @@ -2932,9 +2959,13 @@ "builtin": { "name": "sha256", "pricing": { - "linear": { - "base": 60, - "word": 12 + "0": { + "price": { + "linear": { + "base": 60, + "word": 12 + } + } } } } @@ -2944,9 +2975,13 @@ "builtin": { "name": "ripemd160", "pricing": { - "linear": { - "base": 600, - "word": 120 + "0": { + "price": { + "linear": { + "base": 600, + "word": 120 + } + } } } } @@ -2956,9 +2991,13 @@ "builtin": { "name": "identity", "pricing": { - "linear": { - "base": 15, - "word": 3 + "0": { + "price": { + "linear": { + "base": 15, + "word": 3 + } + } } } } diff --git a/ethcore/src/client/evm_test_client.rs b/ethcore/src/client/evm_test_client.rs index ad08ecef5..1034472d0 100644 --- a/ethcore/src/client/evm_test_client.rs +++ b/ethcore/src/client/evm_test_client.rs @@ -245,7 +245,7 @@ impl<'a> EvmTestClient<'a> { ) -> std::result::Result, TransactErr> { let initial_gas = transaction.gas; // Verify transaction - let is_ok = transaction.verify_basic(true, None, false); + let is_ok = transaction.verify_basic(true, None); if let Err(error) = is_ok { return Err( TransactErr{ diff --git a/ethcore/src/machine.rs b/ethcore/src/machine.rs index abac7058a..664631713 100644 --- a/ethcore/src/machine.rs +++ b/ethcore/src/machine.rs @@ -367,7 +367,7 @@ impl Machine { } else { None }; - t.verify_basic(check_low_s, chain_id, false)?; + t.verify_basic(check_low_s, chain_id)?; Ok(()) } diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 6bef3a33f..d492e038c 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -1709,6 +1709,30 @@ mod tests { assert_eq!(miner.prepare_pending_block(&client), BlockPreparationStatus::NotPrepared); } + #[test] + fn should_reject_local_transaction_with_invalid_chain_id() { + let spec = Spec::new_test(); + let miner = Miner::new_for_tests(&spec, None); + let client = TestBlockChainClient::default(); + let chain_id = spec.chain_id(); + + // chain_id + 100500 is invalid + let import = miner.import_claimed_local_transaction( + &client, + PendingTransaction::new(transaction_with_chain_id(chain_id + 10500), None), + false, + ); + assert_eq!(import, Err(transaction::Error::InvalidChainId)); + + // chain_id is valid + let import = miner.import_claimed_local_transaction( + &client, + PendingTransaction::new(transaction_with_chain_id(chain_id), None), + false, + ); + assert_eq!(import, Ok(())); + } + #[test] fn should_prioritize_locals() { let client = TestBlockChainClient::default(); diff --git a/ethcore/src/miner/pool_client.rs b/ethcore/src/miner/pool_client.rs index 32e08a2fb..0f37b4042 100644 --- a/ethcore/src/miner/pool_client.rs +++ b/ethcore/src/miner/pool_client.rs @@ -136,7 +136,12 @@ impl<'a, C: 'a> pool::client::Client for PoolClient<'a, C> where self.chain.transaction_block(TransactionId::Hash(*hash)).is_some() } - fn verify_transaction(&self, tx: UnverifiedTransaction)-> Result { + fn verify_transaction_basic(&self, tx: &UnverifiedTransaction) -> Result<(), transaction::Error> { + self.engine.verify_transaction_basic(tx, &self.best_block_header)?; + Ok(()) + } + + fn verify_transaction(&self, tx: UnverifiedTransaction) -> Result { self.engine.verify_transaction_basic(&tx, &self.best_block_header)?; let tx = self.engine.verify_transaction_unordered(tx, &self.best_block_header)?; diff --git a/ethcore/types/src/transaction/transaction.rs b/ethcore/types/src/transaction/transaction.rs index 08fcc3ea9..0e4e9eebc 100644 --- a/ethcore/types/src/transaction/transaction.rs +++ b/ethcore/types/src/transaction/transaction.rs @@ -138,6 +138,7 @@ impl Transaction { } } +#[cfg(any(test, feature = "test-helpers"))] impl From for SignedTransaction { fn from(t: ethjson::transaction::Transaction) -> Self { let to: Option = t.to.into(); @@ -237,7 +238,10 @@ impl Transaction { } } - /// Add EIP-86 compatible empty signature. + /// Legacy EIP-86 compatible empty signature. + /// This method is used in json tests as well as + /// signature verification tests. + #[cfg(any(test, feature = "test-helpers"))] pub fn null_sign(self, chain_id: u64) -> SignedTransaction { SignedTransaction { transaction: UnverifiedTransaction { @@ -295,7 +299,7 @@ impl rlp::Decodable for UnverifiedTransaction { v: d.val_at(6)?, r: d.val_at(7)?, s: d.val_at(8)?, - hash: hash, + hash, }) } } @@ -312,7 +316,7 @@ impl UnverifiedTransaction { self } - /// Checks is signature is empty. + /// Checks if the signature is empty. pub fn is_unsigned(&self) -> bool { self.r.is_zero() && self.s.is_zero() } @@ -386,17 +390,12 @@ impl UnverifiedTransaction { } /// Verify basic signature params. Does not attempt sender recovery. - pub fn verify_basic(&self, check_low_s: bool, chain_id: Option, allow_empty_signature: bool) -> Result<(), error::Error> { - if check_low_s && !(allow_empty_signature && self.is_unsigned()) { - self.check_low_s()?; - } - // Disallow unsigned transactions in case EIP-86 is disabled. - if !allow_empty_signature && self.is_unsigned() { + pub fn verify_basic(&self, check_low_s: bool, chain_id: Option) -> Result<(), error::Error> { + if self.is_unsigned() { return Err(ethkey::Error::InvalidSignature.into()); } - // EIP-86: Transactions of this form MUST have gasprice = 0, nonce = 0, value = 0, and do NOT increment the nonce of account 0. - if allow_empty_signature && self.is_unsigned() && !(self.gas_price.is_zero() && self.value.is_zero() && self.nonce.is_zero()) { - return Err(ethkey::Error::InvalidSignature.into()) + if check_low_s { + self.check_low_s()?; } match (self.chain_id(), chain_id) { (None, _) => {}, @@ -436,20 +435,15 @@ impl SignedTransaction { /// Try to verify transaction and recover sender. pub fn new(transaction: UnverifiedTransaction) -> Result { if transaction.is_unsigned() { - Ok(SignedTransaction { - transaction: transaction, - sender: UNSIGNED_SENDER, - public: None, - }) - } else { - let public = transaction.recover_public()?; - let sender = public_to_address(&public); - Ok(SignedTransaction { - transaction: transaction, - sender: sender, - public: Some(public), - }) + return Err(ethkey::Error::InvalidSignature); } + let public = transaction.recover_public()?; + let sender = public_to_address(&public); + Ok(SignedTransaction { + transaction, + sender, + public: Some(public), + }) } /// Returns transaction sender. @@ -640,6 +634,24 @@ mod tests { assert_eq!(t.chain_id(), None); } + #[test] + fn should_reject_null_signature() { + let t = Transaction { + nonce: U256::zero(), + gas_price: U256::from(10000000000u64), + gas: U256::from(21000), + action: Action::Call(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), + value: U256::from(1), + data: vec![] + }.null_sign(1); + + let res = SignedTransaction::new(t.transaction); + match res { + Err(ethkey::Error::InvalidSignature) => {} + _ => panic!("null signature should be rejected"), + } + } + #[test] fn should_recover_from_chain_specific_signing() { use ethkey::{Random, Generator}; diff --git a/evmbin/Cargo.toml b/evmbin/Cargo.toml index 6ef9c9459..c9ca63339 100644 --- a/evmbin/Cargo.toml +++ b/evmbin/Cargo.toml @@ -9,7 +9,7 @@ name = "parity-evm" path = "./src/main.rs" [dependencies] -common-types = { path = "../ethcore/types" } +common-types = { path = "../ethcore/types", features = ["test-helpers"] } docopt = "1.0" env_logger = "0.5" ethcore = { path = "../ethcore", features = ["test-helpers", "json-tests", "to-pod-full"] } diff --git a/json/src/spec/builtin.rs b/json/src/spec/builtin.rs index 471b73857..ca734d2ff 100644 --- a/json/src/spec/builtin.rs +++ b/json/src/spec/builtin.rs @@ -203,6 +203,28 @@ mod tests { ]); } + #[test] + fn deserialization_alt_bn128_const_operations() { + let s = r#"{ + "name": "alt_bn128_mul", + "pricing": { + "100500": { + "price": { "alt_bn128_const_operations": { "price": 123 }} + } + } + }"#; + let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); + assert_eq!(builtin.name, "alt_bn128_mul"); + assert_eq!(builtin.pricing, map![ + 100500 => PricingAt { + info: None, + price: Pricing::AltBn128ConstOperations(AltBn128ConstOperations { + price: 123, + }), + } + ]); + } + #[test] fn activate_at() { let s = r#"{ diff --git a/miner/src/pool/client.rs b/miner/src/pool/client.rs index bcd2b9689..1579ba40d 100644 --- a/miner/src/pool/client.rs +++ b/miner/src/pool/client.rs @@ -50,6 +50,15 @@ pub trait Client: fmt::Debug + Sync { /// Is transaction with given hash already in the blockchain? fn transaction_already_included(&self, hash: &H256) -> bool; + /// Perform basic/cheap transaction verification. + /// + /// This should include all cheap checks that can be done before + /// actually checking the signature, like chain-replay protection. + /// + /// This method is currently used only for verifying local transactions. + fn verify_transaction_basic(&self, t: &transaction::UnverifiedTransaction) + -> Result<(), transaction::Error>; + /// Structurarily verify given transaction. fn verify_transaction(&self, tx: transaction::UnverifiedTransaction) -> Result; diff --git a/miner/src/pool/tests/client.rs b/miner/src/pool/tests/client.rs index 4735fbd64..ce927c8e9 100644 --- a/miner/src/pool/tests/client.rs +++ b/miner/src/pool/tests/client.rs @@ -103,6 +103,12 @@ impl pool::client::Client for TestClient { false } + fn verify_transaction_basic(&self, _tx: &UnverifiedTransaction) + -> Result<(), transaction::Error> + { + Ok(()) + } + fn verify_transaction(&self, tx: UnverifiedTransaction) -> Result { diff --git a/miner/src/pool/verifier.rs b/miner/src/pool/verifier.rs index 79d50d713..ae64ac522 100644 --- a/miner/src/pool/verifier.rs +++ b/miner/src/pool/verifier.rs @@ -250,7 +250,13 @@ impl txpool::Verifier for Verifier tx, + Transaction::Local(tx) => match self.client.verify_transaction_basic(&**tx) { + Ok(()) => tx, + Err(err) => { + warn!(target: "txqueue", "[{:?}] Rejected local tx {:?}", hash, err); + return Err(err) + } + }, }; // Verify RLP payload diff --git a/parity/db/mod.rs b/parity/db/mod.rs index 9b4662442..cf68e1418 100644 --- a/parity/db/mod.rs +++ b/parity/db/mod.rs @@ -20,6 +20,3 @@ mod impls; pub use self::impls::{open_db, restoration_db_handler, migrate}; - -#[cfg(feature = "secretstore")] -pub use self::impls::open_secretstore_db; diff --git a/parity/db/rocksdb/migration.rs b/parity/db/rocksdb/migration.rs index eec43d233..8fe3d789f 100644 --- a/parity/db/rocksdb/migration.rs +++ b/parity/db/rocksdb/migration.rs @@ -208,11 +208,11 @@ pub fn migrate(path: &Path, compaction_profile: &DatabaseCompactionProfile) -> R // Further migrations if version < CURRENT_VERSION && exists(&db_path) { - println!("Migrating database from version {} to {}", version, CURRENT_VERSION); + info!(target: "migration", "Migrating database from version {} to {}", version, CURRENT_VERSION); migrate_database(version, &db_path, consolidated_database_migrations(&compaction_profile)?)?; if version < BLOOMS_DB_VERSION { - println!("Migrating blooms to blooms-db..."); + info!(target: "migration", "Migrating blooms to blooms-db..."); let db_config = DatabaseConfig { max_open_files: 64, memory_budget: None, @@ -223,7 +223,7 @@ pub fn migrate(path: &Path, compaction_profile: &DatabaseCompactionProfile) -> R migrate_blooms(&db_path, &db_config).map_err(Error::BloomsDB)?; } - println!("Migration finished"); + info!(target: "migration", "Migration finished"); } // update version file. diff --git a/parity/db/rocksdb/mod.rs b/parity/db/rocksdb/mod.rs index c7aa0a534..8396ee9df 100644 --- a/parity/db/rocksdb/mod.rs +++ b/parity/db/rocksdb/mod.rs @@ -18,6 +18,9 @@ extern crate kvdb_rocksdb; extern crate migration_rocksdb; extern crate ethcore_blockchain; +#[cfg(test)] +extern crate tempdir; + use std::{io, fs}; use std::sync::Arc; use std::path::Path; @@ -37,13 +40,13 @@ mod helpers; pub use self::migration::migrate; struct AppDB { - key_value: Arc, + key_value: Arc, blooms: blooms_db::Database, trace_blooms: blooms_db::Database, } impl BlockChainDB for AppDB { - fn key_value(&self) -> &Arc { + fn key_value(&self) -> &Arc { &self.key_value } @@ -56,19 +59,8 @@ impl BlockChainDB for AppDB { } } -/// Open a secret store DB using the given secret store data path. The DB path is one level beneath the data path. -#[cfg(feature = "secretstore")] -pub fn open_secretstore_db(data_path: &str) -> Result, String> { - use std::path::PathBuf; - - let mut db_path = PathBuf::from(data_path); - db_path.push("db"); - let db_path = db_path.to_str().ok_or_else(|| "Invalid secretstore path".to_string())?; - Ok(Arc::new(Database::open_default(&db_path).map_err(|e| format!("Error opening database: {:?}", e))?)) -} - /// Create a restoration db handler using the config generated by `client_path` and `client_config`. -pub fn restoration_db_handler(client_path: &Path, client_config: &ClientConfig) -> Box { +pub fn restoration_db_handler(client_path: &Path, client_config: &ClientConfig) -> Box { let client_db_config = helpers::client_db_config(client_path, client_config); struct RestorationDBHandler { @@ -76,7 +68,7 @@ pub fn restoration_db_handler(client_path: &Path, client_config: &ClientConfig) } impl BlockChainDBHandler for RestorationDBHandler { - fn open(&self, db_path: &Path) -> io::Result> { + fn open(&self, db_path: &Path) -> io::Result> { open_database(&db_path.to_string_lossy(), &self.config) } } @@ -99,7 +91,7 @@ pub fn open_db(client_path: &str, cache_config: &CacheConfig, compaction: &Datab open_database(client_path, &db_config) } -pub fn open_database(client_path: &str, config: &DatabaseConfig) -> io::Result> { +pub fn open_database(client_path: &str, config: &DatabaseConfig) -> io::Result> { let path = Path::new(client_path); let blooms_path = path.join("blooms"); diff --git a/parity/secretstore.rs b/parity/secretstore.rs index bde67e344..7aeab6509 100644 --- a/parity/secretstore.rs +++ b/parity/secretstore.rs @@ -123,7 +123,6 @@ mod server { use ethcore_secretstore; use ethkey::KeyPair; use ansi_term::Colour::{Red, White}; - use db; use super::{Configuration, Dependencies, NodeSecretKey, ContractAddress, Executor}; fn into_service_contract_address(address: ContractAddress) -> ethcore_secretstore::ContractAddress { @@ -135,13 +134,13 @@ mod server { /// Key server pub struct KeyServer { - _key_server: Box, + _key_server: Box, } impl KeyServer { /// Create new key server pub fn new(mut conf: Configuration, deps: Dependencies, executor: Executor) -> Result { - let self_secret: Arc = match conf.self_secret.take() { + let self_secret: Arc = match conf.self_secret.take() { Some(NodeSecretKey::Plain(secret)) => Arc::new(ethcore_secretstore::PlainNodeKeyPair::new( KeyPair::from_secret(secret).map_err(|e| format!("invalid secret: {}", e))?)), #[cfg(feature = "accounts")] @@ -202,7 +201,7 @@ mod server { cconf.cluster_config.nodes.insert(self_secret.public().clone(), cconf.cluster_config.listener_address.clone()); - let db = db::open_secretstore_db(&conf.data_path)?; + let db = ethcore_secretstore::open_secretstore_db(&conf.data_path)?; let key_server = ethcore_secretstore::start(deps.client, deps.sync, deps.miner, self_secret, cconf, db, executor) .map_err(|e| format!("Error starting KeyServer {}: {}", key_server_name, e))?; diff --git a/secret-store/Cargo.toml b/secret-store/Cargo.toml index bdc7e065b..1cf3bb969 100644 --- a/secret-store/Cargo.toml +++ b/secret-store/Cargo.toml @@ -21,6 +21,7 @@ futures = "0.1" hyper = { version = "0.12", default-features = false } keccak-hash = "0.2.0" kvdb = "0.1" +kvdb-rocksdb = "0.1.5" lazy_static = "1.0" log = "0.4" parity-bytes = "0.1" @@ -43,7 +44,6 @@ jsonrpc-server-utils = "14.0.3" env_logger = "0.5" ethcore = { path = "../ethcore", features = ["test-helpers"] } tempdir = "0.3" -kvdb-rocksdb = "0.1.5" [features] accounts = ["ethcore-accounts"] diff --git a/secret-store/src/key_storage.rs b/secret-store/src/key_storage.rs index 8ba36f708..404edd4eb 100644 --- a/secret-store/src/key_storage.rs +++ b/secret-store/src/key_storage.rs @@ -24,11 +24,6 @@ use kvdb::KeyValueDB; use types::{Error, ServerKeyId, NodeId}; use serialization::{SerializablePublic, SerializableSecret, SerializableH256, SerializableAddress}; -/// Key of version value. -const DB_META_KEY_VERSION: &'static [u8; 7] = b"version"; -/// Current db version. -const CURRENT_VERSION: u8 = 3; - /// Encrypted key share, stored by key storage on the single key server. #[derive(Debug, Default, Clone, PartialEq)] pub struct DocumentKeyShare { @@ -72,17 +67,17 @@ pub trait KeyStorage: Send + Sync { /// Check if storage contains document encryption key fn contains(&self, document: &ServerKeyId) -> bool; /// Iterate through storage - fn iter<'a>(&'a self) -> Box + 'a>; + fn iter<'a>(&'a self) -> Box + 'a>; } /// Persistent document encryption keys storage pub struct PersistentKeyStorage { - db: Arc, + db: Arc, } /// Persistent document encryption keys storage iterator pub struct PersistentKeyStorageIterator<'a> { - iter: Box, Box<[u8]>)> + 'a>, + iter: Box, Box<[u8]>)> + 'a>, } /// V3 of encrypted key share, as it is stored by key storage on the single key server. @@ -115,27 +110,8 @@ struct SerializableDocumentKeyShareVersionV3 { impl PersistentKeyStorage { /// Create new persistent document encryption keys storage - pub fn new(db: Arc) -> Result { - let db = upgrade_db(db)?; - - Ok(PersistentKeyStorage { - db: db, - }) - } -} - -fn upgrade_db(db: Arc) -> Result, Error> { - let version = db.get(None, DB_META_KEY_VERSION)?; - let version = version.and_then(|v| v.get(0).cloned()); - match version { - None => { - let mut batch = db.transaction(); - batch.put(None, DB_META_KEY_VERSION, &[CURRENT_VERSION]); - db.write(batch)?; - Ok(db) - }, - Some(CURRENT_VERSION) => Ok(db), - _ => Err(Error::Database(format!("unsupported SecretStore database version: {:?}", version))), + pub fn new(db: Arc) -> Result { + Ok(Self { db }) } } @@ -144,7 +120,7 @@ impl KeyStorage for PersistentKeyStorage { let key: SerializableDocumentKeyShareV3 = key.into(); let key = serde_json::to_vec(&key).map_err(|e| Error::Database(e.to_string()))?; let mut batch = self.db.transaction(); - batch.put(None, document.as_bytes(), &key); + batch.put(Some(0), document.as_bytes(), &key); self.db.write(batch).map_err(Into::into) } @@ -153,7 +129,7 @@ impl KeyStorage for PersistentKeyStorage { } fn get(&self, document: &ServerKeyId) -> Result, Error> { - self.db.get(None, document.as_bytes()) + self.db.get(Some(0), document.as_bytes()) .map_err(|e| Error::Database(e.to_string())) .and_then(|key| match key { None => Ok(None), @@ -166,28 +142,28 @@ impl KeyStorage for PersistentKeyStorage { fn remove(&self, document: &ServerKeyId) -> Result<(), Error> { let mut batch = self.db.transaction(); - batch.delete(None, document.as_bytes()); + batch.delete(Some(0), document.as_bytes()); self.db.write(batch).map_err(Into::into) } fn clear(&self) -> Result<(), Error> { let mut batch = self.db.transaction(); for (key, _) in self.iter() { - batch.delete(None, key.as_bytes()); + batch.delete(Some(0), key.as_bytes()); } self.db.write(batch) .map_err(|e| Error::Database(e.to_string())) } fn contains(&self, document: &ServerKeyId) -> bool { - self.db.get(None, document.as_bytes()) + self.db.get(Some(0), document.as_bytes()) .map(|k| k.is_some()) .unwrap_or(false) } fn iter<'a>(&'a self) -> Box + 'a> { Box::new(PersistentKeyStorageIterator { - iter: self.db.iter(None), + iter: self.db.iter(Some(0)), }) } } @@ -297,7 +273,7 @@ pub mod tests { use parking_lot::RwLock; use self::tempdir::TempDir; use ethkey::{Random, Generator, Public}; - use kvdb_rocksdb::Database; + use kvdb_rocksdb::{Database, DatabaseConfig}; use types::{Error, ServerKeyId}; use super::{KeyStorage, PersistentKeyStorage, DocumentKeyShare, DocumentKeyShareVersion}; @@ -336,7 +312,7 @@ pub mod tests { self.keys.read().contains_key(document) } - fn iter<'a>(&'a self) -> Box + 'a> { + fn iter<'a>(&'a self) -> Box + 'a> { Box::new(self.keys.read().clone().into_iter()) } } @@ -376,7 +352,8 @@ pub mod tests { }; let key3 = ServerKeyId::from_low_u64_be(3); - let db = Database::open_default(&tempdir.path().display().to_string()).unwrap(); + let db_config = DatabaseConfig::with_columns(Some(1)); + let db = Database::open(&db_config, &tempdir.path().display().to_string()).unwrap(); let key_storage = PersistentKeyStorage::new(Arc::new(db)).unwrap(); key_storage.insert(key1.clone(), value1.clone()).unwrap(); @@ -386,7 +363,7 @@ pub mod tests { assert_eq!(key_storage.get(&key3), Ok(None)); drop(key_storage); - let db = Database::open_default(&tempdir.path().display().to_string()).unwrap(); + let db = Database::open(&db_config, &tempdir.path().display().to_string()).unwrap(); let key_storage = PersistentKeyStorage::new(Arc::new(db)).unwrap(); assert_eq!(key_storage.get(&key1), Ok(Some(value1))); diff --git a/secret-store/src/lib.rs b/secret-store/src/lib.rs index 13dac1968..83111b109 100644 --- a/secret-store/src/lib.rs +++ b/secret-store/src/lib.rs @@ -25,6 +25,7 @@ extern crate ethkey; extern crate hyper; extern crate keccak_hash as hash; extern crate kvdb; +extern crate kvdb_rocksdb; extern crate parity_bytes as bytes; extern crate parity_crypto as crypto; extern crate parity_runtime; @@ -56,7 +57,7 @@ extern crate log; #[cfg(test)] extern crate env_logger; #[cfg(test)] -extern crate kvdb_rocksdb; +extern crate tempdir; #[cfg(feature = "accounts")] extern crate ethcore_accounts as accounts; @@ -74,9 +75,11 @@ mod key_server_set; mod node_key_pair; mod listener; mod trusted_client; +mod migration; use std::sync::Arc; use kvdb::KeyValueDB; +use kvdb_rocksdb::{Database, DatabaseConfig}; use ethcore::client::Client; use ethcore::miner::Miner; use sync::SyncProvider; @@ -89,12 +92,26 @@ pub use self::node_key_pair::PlainNodeKeyPair; #[cfg(feature = "accounts")] pub use self::node_key_pair::KeyStoreNodeKeyPair; +/// Open a secret store DB using the given secret store data path. The DB path is one level beneath the data path. +pub fn open_secretstore_db(data_path: &str) -> Result, String> { + use std::path::PathBuf; + + migration::upgrade_db(data_path).map_err(|e| e.to_string())?; + + let mut db_path = PathBuf::from(data_path); + db_path.push("db"); + let db_path = db_path.to_str().ok_or_else(|| "Invalid secretstore path".to_string())?; + + let config = DatabaseConfig::with_columns(Some(1)); + Ok(Arc::new(Database::open(&config, &db_path).map_err(|e| format!("Error opening database: {:?}", e))?)) +} + /// Start new key server instance -pub fn start(client: Arc, sync: Arc, miner: Arc, self_key_pair: Arc, mut config: ServiceConfiguration, - db: Arc, executor: Executor) -> Result, Error> +pub fn start(client: Arc, sync: Arc, miner: Arc, self_key_pair: Arc, mut config: ServiceConfiguration, + db: Arc, executor: Executor) -> Result, Error> { let trusted_client = trusted_client::TrustedClient::new(self_key_pair.clone(), client.clone(), sync, miner); - let acl_storage: Arc = match config.acl_check_contract_address.take() { + let acl_storage: Arc = match config.acl_check_contract_address.take() { Some(acl_check_contract_address) => acl_storage::OnChainAclStorage::new(trusted_client.clone(), acl_check_contract_address)?, None => Arc::new(acl_storage::DummyAclStorage::default()), }; @@ -105,7 +122,7 @@ pub fn start(client: Arc, sync: Arc, miner: Arc, se let key_server = Arc::new(key_server::KeyServerImpl::new(&config.cluster_config, key_server_set.clone(), self_key_pair.clone(), acl_storage.clone(), key_storage.clone(), executor.clone())?); let cluster = key_server.cluster(); - let key_server: Arc = key_server; + let key_server: Arc = key_server; // prepare HTTP listener let http_listener = match config.listener_address { @@ -122,7 +139,7 @@ pub fn start(client: Arc, sync: Arc, miner: Arc, se address, self_key_pair.clone())); - let mut contracts: Vec> = Vec::new(); + let mut contracts: Vec> = Vec::new(); config.service_contract_address.map(|address| create_service_contract(address, listener::service_contract::SERVICE_CONTRACT_REGISTRY_NAME.to_owned(), @@ -149,7 +166,7 @@ pub fn start(client: Arc, sync: Arc, miner: Arc, se listener::ApiMask { document_key_shadow_retrieval_requests: true, ..Default::default() })) .map(|l| contracts.push(l)); - let contract: Option> = match contracts.len() { + let contract: Option> = match contracts.len() { 0 => None, 1 => Some(contracts.pop().expect("contract.len() is 1; qed")), _ => Some(Arc::new(listener::service_contract_aggregate::OnChainServiceContractAggregate::new(contracts))), diff --git a/secret-store/src/migration.rs b/secret-store/src/migration.rs new file mode 100644 index 000000000..95407061e --- /dev/null +++ b/secret-store/src/migration.rs @@ -0,0 +1,206 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see . + +//! Secret Store DB migration module. + + +use std::fmt::{Display, Error as FmtError, Formatter}; +use std::fs; +use std::io::{Error as IoError, ErrorKind as IoErrorKind, Read as _, Write as _}; +use std::path::PathBuf; + +use kvdb::DBTransaction; +use kvdb_rocksdb::{Database, DatabaseConfig}; + +/// We used to store the version in the database (until version 4). +const LEGACY_DB_META_KEY_VERSION: &[u8; 7] = b"version"; +/// Current db version. +const CURRENT_VERSION: u8 = 4; +/// Database is assumed to be at the default version, when no version file is found. +const DEFAULT_VERSION: u8 = 3; +/// Version file name. +const VERSION_FILE_NAME: &str = "db_version"; + +/// Migration related erorrs. +#[derive(Debug)] +pub enum Error { + /// Returned when current version cannot be read or guessed. + UnknownDatabaseVersion, + /// Existing DB is newer than the known one. + FutureDBVersion, + /// Migration was completed succesfully, + /// but there was a problem with io. + Io(IoError), +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { + let out = match *self { + Error::UnknownDatabaseVersion => + "Current Secret Store database version cannot be read".into(), + Error::FutureDBVersion => + "Secret Store database was created with newer client version.\ + Upgrade your client or delete DB and resync.".into(), + Error::Io(ref err) => + format!("Unexpected io error on Secret Store database migration: {}.", err), + }; + write!(f, "{}", out) + } +} + +impl From for Error { + fn from(err: IoError) -> Self { + Error::Io(err) + } +} + +fn other_io_err(e: E) -> IoError where E: Into> { + IoError::new(std::io::ErrorKind::Other, e) +} + + +// Moves "default" column to column 0 in preparation for a kvdb-rocksdb 0.3 migration. +fn migrate_to_v4(parent_dir: &str) -> Result<(), Error> { + // Naïve implementation until + // https://github.com/facebook/rocksdb/issues/6130 is resolved + let old_db_config = DatabaseConfig::with_columns(Some(1)); + let new_db_config = DatabaseConfig::with_columns(Some(1)); + const BATCH_SIZE: usize = 1024; + + let old_dir = db_dir(parent_dir); + let new_dir = migration_dir(parent_dir); + let old_db = Database::open(&old_db_config, &old_dir)?; + let new_db = Database::open(&new_db_config, &new_dir)?; + + const OLD_COLUMN: Option = None; + const NEW_COLUMN: Option = Some(0); + + // remove legacy version key + { + let mut batch = DBTransaction::with_capacity(1); + batch.delete(OLD_COLUMN, LEGACY_DB_META_KEY_VERSION); + if let Err(err) = old_db.write(batch) { + error!(target: "migration", "Failed to delete db version {}", &err); + return Err(err.into()); + } + } + + let mut batch = DBTransaction::with_capacity(BATCH_SIZE); + let old_db_iter = old_db.iter(OLD_COLUMN).ok_or_else(|| { + IoError::new(std::io::ErrorKind::Other, "No database available to iterate over – shutting down?") + })?; + for (i, (key, value)) in old_db_iter.enumerate() { + batch.put(NEW_COLUMN, &key, &value); + if i % BATCH_SIZE == 0 { + new_db.write(batch)?; + batch = DBTransaction::with_capacity(BATCH_SIZE); + info!(target: "migration", "Migrating Secret Store DB: {} keys written", i); + } + } + new_db.write(batch)?; + drop(new_db); + old_db.restore(&new_dir)?; + + info!(target: "migration", "Secret Store migration finished"); + + Ok(()) +} + +/// Apply all migrations if possible. +pub fn upgrade_db(db_path: &str) -> Result<(), Error> { + match current_version(db_path)? { + old_version if old_version < CURRENT_VERSION => { + migrate_to_v4(db_path)?; + update_version(db_path)?; + Ok(()) + }, + CURRENT_VERSION => Ok(()), + _ => Err(Error::FutureDBVersion), + } +} + +fn db_dir(path: &str) -> String { + let mut dir = PathBuf::from(path); + dir.push("db"); + dir.to_string_lossy().to_string() +} + +fn migration_dir(path: &str) -> String { + let mut dir = PathBuf::from(path); + dir.push("migration"); + dir.to_string_lossy().to_string() +} + +/// Returns the version file path. +fn version_file_path(path: &str) -> PathBuf { + let mut file_path = PathBuf::from(path); + file_path.push(VERSION_FILE_NAME); + file_path +} + +/// Reads current database version from the file at given path. +/// If the file does not exist returns `DEFAULT_VERSION`. +fn current_version(path: &str) -> Result { + match fs::File::open(version_file_path(path)) { + Err(ref err) if err.kind() == IoErrorKind::NotFound => Ok(DEFAULT_VERSION), + Err(err) => Err(err.into()), + Ok(mut file) => { + let mut s = String::new(); + file.read_to_string(&mut s)?; + u8::from_str_radix(&s, 10).map_err(|_| Error::UnknownDatabaseVersion) + }, + } +} + +/// Writes current database version to the file. +/// Creates a new file if the version file does not exist yet. +fn update_version(path: &str) -> Result<(), Error> { + let mut file = fs::File::create(version_file_path(path))?; + file.write_all(format!("{}", CURRENT_VERSION).as_bytes())?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use tempdir::TempDir; + + #[test] + fn migration_works() -> Result<(), Error> { + let parent = TempDir::new("secret_store_migration")?.into_path(); + + let mut db_path = parent.clone(); + db_path.push("db"); + let db_path = db_path.to_str().unwrap(); + let parent_path = parent.to_str().unwrap(); + + let old_db = Database::open(&DatabaseConfig::with_columns(None), db_path)?; + + let mut batch = old_db.transaction(); + batch.put(None, b"key1", b"value1"); + batch.put(None, b"key2", b"value2"); + old_db.write(batch)?; + drop(old_db); + + upgrade_db(parent_path)?; + let migrated = Database::open(&DatabaseConfig::with_columns(Some(1)), db_path)?; + + assert_eq!(migrated.get(Some(0), b"key1")?.expect("key1"), b"value1".to_vec()); + assert_eq!(migrated.get(Some(0), b"key2")?.expect("key2"), b"value2".to_vec()); + + Ok(()) + } +} diff --git a/secret-store/src/node_key_pair.rs b/secret-store/src/node_key_pair.rs index f50f75ad1..44ffbd1a8 100644 --- a/secret-store/src/node_key_pair.rs +++ b/secret-store/src/node_key_pair.rs @@ -74,10 +74,10 @@ mod accounts { pub fn new(account_provider: Arc, address: Address, password: Password) -> Result { let public = account_provider.account_public(address.clone(), &password).map_err(|e| EthKeyError::Custom(format!("{}", e)))?; Ok(KeyStoreNodeKeyPair { - account_provider: account_provider, - address: address, - public: public, - password: password, + account_provider, + address, + public, + password, }) } } diff --git a/util/version/Cargo.toml b/util/version/Cargo.toml index 1783035cb..71ea9ba97 100644 --- a/util/version/Cargo.toml +++ b/util/version/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "parity-version" # NOTE: this value is used for Parity Ethereum version string (via env CARGO_PKG_VERSION) -version = "2.6.6" +version = "2.6.7" authors = ["Parity Technologies "] build = "build.rs"