diff --git a/Cargo.lock b/Cargo.lock index 75cecfc08a..34a32f7966 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -161,7 +161,7 @@ checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" [[package]] name = "array-bytes" -version = "1.0.0" +version = "1.2.2" dependencies = [ "sp-std 2.0.0", ] @@ -312,7 +312,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df097e3f506bec0e1a24f06bb3c962c228f36671de841ff579cb99f371772634" dependencies = [ - "futures 0.3.6", + "futures 0.3.7", "rustls", "webpki", "webpki-roots 0.19.0", @@ -417,7 +417,7 @@ dependencies = [ "cfg-if 0.1.10", "clang-sys", "clap", - "env_logger", + "env_logger 0.7.1", "lazy_static", "lazycell", "log", @@ -1166,7 +1166,7 @@ dependencies = [ [[package]] name = "darwinia-balances" -version = "1.0.0" +version = "1.2.2" dependencies = [ "darwinia-balances-rpc-runtime-api", "darwinia-support", @@ -1183,7 +1183,7 @@ dependencies = [ [[package]] name = "darwinia-balances-rpc" -version = "1.0.0" +version = "1.2.2" dependencies = [ "darwinia-balances-rpc-runtime-api", "jsonrpc-core", @@ -1197,7 +1197,7 @@ dependencies = [ [[package]] name = "darwinia-balances-rpc-runtime-api" -version = "1.0.0" +version = "1.2.2" dependencies = [ "darwinia-support", "parity-scale-codec", @@ -1208,7 +1208,7 @@ dependencies = [ [[package]] name = "darwinia-claims" -version = "1.0.0" +version = "1.2.2" dependencies = [ "array-bytes", "darwinia-balances", @@ -1227,7 +1227,7 @@ dependencies = [ [[package]] name = "darwinia-cli" -version = "1.0.0" +version = "1.2.2" dependencies = [ "sc-cli", "sc-client-api", @@ -1239,7 +1239,7 @@ dependencies = [ [[package]] name = "darwinia-crab-backing" -version = "1.0.0" +version = "1.2.2" dependencies = [ "frame-support", "frame-system", @@ -1250,7 +1250,7 @@ dependencies = [ [[package]] name = "darwinia-crab-issuing" -version = "1.0.0" +version = "1.2.2" dependencies = [ "darwinia-balances", "darwinia-support", @@ -1265,7 +1265,7 @@ dependencies = [ [[package]] name = "darwinia-democracy" -version = "1.0.0" +version = "1.2.2" dependencies = [ "darwinia-balances", "darwinia-support", @@ -1285,7 +1285,7 @@ dependencies = [ [[package]] name = "darwinia-elections-phragmen" -version = "1.0.0" +version = "1.2.2" dependencies = [ "darwinia-balances", "darwinia-support", @@ -1304,7 +1304,7 @@ dependencies = [ [[package]] name = "darwinia-ethereum-backing" -version = "1.0.0" +version = "1.2.2" dependencies = [ "array-bytes", "darwinia-balances", @@ -1333,7 +1333,7 @@ dependencies = [ [[package]] name = "darwinia-ethereum-linear-relay" -version = "1.0.0" +version = "1.2.2" dependencies = [ "array-bytes", "darwinia-balances", @@ -1354,7 +1354,7 @@ dependencies = [ [[package]] name = "darwinia-ethereum-relay" -version = "1.0.0" +version = "1.2.2" dependencies = [ "array-bytes", "blake2-rfc", @@ -1379,7 +1379,7 @@ dependencies = [ [[package]] name = "darwinia-header-mmr" -version = "1.0.0" +version = "1.2.2" dependencies = [ "array-bytes", "ckb-merkle-mountain-range", @@ -1398,7 +1398,7 @@ dependencies = [ [[package]] name = "darwinia-header-mmr-rpc" -version = "1.0.0" +version = "1.2.2" dependencies = [ "darwinia-header-mmr-rpc-runtime-api", "jsonrpc-core", @@ -1412,7 +1412,7 @@ dependencies = [ [[package]] name = "darwinia-header-mmr-rpc-runtime-api" -version = "1.0.0" +version = "1.2.2" dependencies = [ "darwinia-support", "parity-scale-codec", @@ -1424,7 +1424,7 @@ dependencies = [ [[package]] name = "darwinia-relay-primitives" -version = "1.0.0" +version = "1.2.2" dependencies = [ "frame-support", "parity-scale-codec", @@ -1434,12 +1434,12 @@ dependencies = [ [[package]] name = "darwinia-relayer-game" -version = "1.0.0" +version = "1.2.2" dependencies = [ "darwinia-balances", "darwinia-relay-primitives", "darwinia-support", - "env_logger", + "env_logger 0.8.1", "frame-support", "frame-system", "parity-scale-codec", @@ -1452,7 +1452,7 @@ dependencies = [ [[package]] name = "darwinia-staking" -version = "1.0.0" +version = "1.2.2" dependencies = [ "darwinia-balances", "darwinia-staking-rpc-runtime-api", @@ -1481,7 +1481,7 @@ dependencies = [ [[package]] name = "darwinia-staking-rpc" -version = "1.0.0" +version = "1.2.2" dependencies = [ "darwinia-staking-rpc-runtime-api", "jsonrpc-core", @@ -1495,7 +1495,7 @@ dependencies = [ [[package]] name = "darwinia-staking-rpc-runtime-api" -version = "1.0.0" +version = "1.2.2" dependencies = [ "darwinia-support", "parity-scale-codec", @@ -1506,7 +1506,7 @@ dependencies = [ [[package]] name = "darwinia-support" -version = "1.0.0" +version = "1.2.2" dependencies = [ "ethereum-primitives", "frame-support", @@ -1519,7 +1519,7 @@ dependencies = [ [[package]] name = "darwinia-treasury" -version = "1.0.0" +version = "1.2.2" dependencies = [ "darwinia-balances", "darwinia-support", @@ -1536,7 +1536,7 @@ dependencies = [ [[package]] name = "darwinia-tron-backing" -version = "1.0.0" +version = "1.2.2" dependencies = [ "frame-support", "frame-system", @@ -1547,7 +1547,7 @@ dependencies = [ [[package]] name = "darwinia-vesting" -version = "1.0.0" +version = "1.2.2" dependencies = [ "darwinia-balances", "darwinia-support", @@ -1630,6 +1630,124 @@ dependencies = [ "quick-error", ] +[[package]] +name = "dvm-consensus" +version = "1.2.2" +dependencies = [ + "derive_more", + "dvm-consensus-primitives", + "ethereum 0.3.3", + "futures 0.3.7", + "log", + "parity-scale-codec", + "sc-client-api", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-timestamp", + "substrate-prometheus-endpoint", +] + +[[package]] +name = "dvm-consensus-primitives" +version = "1.2.2" +dependencies = [ + "parity-scale-codec", + "sp-core", + "sp-runtime", + "sp-std 2.0.0", +] + +[[package]] +name = "dvm-ethereum" +version = "1.2.2" +dependencies = [ + "darwinia-balances", + "darwinia-support", + "dvm-consensus-primitives", + "dvm-rpc-primitives", + "ethereum 0.3.3", + "ethereum-types 0.9.2", + "evm", + "frame-support", + "frame-system", + "hex-literal", + "libsecp256k1", + "pallet-evm", + "pallet-timestamp", + "parity-scale-codec", + "rlp 0.4.6", + "rustc-hex", + "serde", + "sha3 0.8.2", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 2.0.0", +] + +[[package]] +name = "dvm-rpc" +version = "1.2.2" +dependencies = [ + "dvm-consensus", + "dvm-rpc-core", + "dvm-rpc-primitives", + "ethereum 0.3.3", + "ethereum-types 0.9.2", + "futures 0.3.7", + "jsonrpc-core", + "jsonrpc-core-client", + "jsonrpc-derive", + "jsonrpc-pubsub", + "log", + "parity-scale-codec", + "rlp 0.4.6", + "sc-client-api", + "sc-network", + "sc-rpc", + "sc-service", + "sha3 0.8.2", + "sp-api", + "sp-blockchain", + "sp-io", + "sp-runtime", + "sp-storage", + "sp-transaction-pool", +] + +[[package]] +name = "dvm-rpc-core" +version = "1.2.2" +dependencies = [ + "ethereum-types 0.9.2", + "jsonrpc-core", + "jsonrpc-core-client", + "jsonrpc-derive", + "jsonrpc-pubsub", + "rustc-hex", + "serde", + "serde_json", +] + +[[package]] +name = "dvm-rpc-primitives" +version = "1.2.2" +dependencies = [ + "ethereum 0.3.3", + "ethereum-types 0.9.2", + "pallet-evm", + "parity-scale-codec", + "sp-api", + "sp-core", + "sp-runtime", + "sp-std 2.0.0", +] + [[package]] name = "dyn-clonable" version = "0.9.0" @@ -1713,7 +1831,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" dependencies = [ "atty", - "humantime", + "humantime 1.3.0", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "env_logger" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54532e3223c5af90a6a757c90b5c5521564b07e5e7a958681bcd2afad421cdcd" +dependencies = [ + "atty", + "humantime 2.0.1", "log", "regex", "termcolor", @@ -1807,9 +1938,50 @@ dependencies = [ "tiny-keccak 2.0.2", ] +[[package]] +name = "ethbloom" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71a6567e6fd35589fea0c63b94b4cf2e55573e413901bdbe60ab15cf0e25e5df" +dependencies = [ + "crunchy", + "fixed-hash 0.6.1", + "impl-codec 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-rlp 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-serde 0.3.1", + "tiny-keccak 2.0.2", +] + +[[package]] +name = "ethereum" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7fef4d2da1de3a4f4f85408379644276db9b46c4af7b0fe38a3debec5cb7cd" +dependencies = [ + "ethereum-types 0.9.2", + "parity-scale-codec", + "rlp 0.4.6", + "rlp-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sha3 0.8.2", +] + +[[package]] +name = "ethereum" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7538b2bb55bde83b500c4e6ea0bdb3b1c0e9e5ae57951941e5412db32c2a8344" +dependencies = [ + "ethereum-types 0.9.2", + "parity-scale-codec", + "rlp 0.4.6", + "rlp-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", + "sha3 0.9.1", +] + [[package]] name = "ethereum-primitives" -version = "1.0.0" +version = "1.2.2" dependencies = [ "array-bytes", "ethash", @@ -1825,7 +1997,7 @@ dependencies = [ "parity-scale-codec", "primitive-types 0.6.2", "rlp 0.4.4", - "rlp-derive", + "rlp-derive 0.1.0 (git+https://github.com/darwinia-network/parity-common.git)", "serde", "serde_json", "sp-io", @@ -1862,6 +2034,21 @@ dependencies = [ "uint 0.8.2", ] +[[package]] +name = "ethereum-types" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "473aecff686bd8e7b9db0165cbbb53562376b39bf35b427f0c60446a9e1634b0" +dependencies = [ + "ethbloom 0.9.2", + "fixed-hash 0.6.1", + "impl-codec 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-rlp 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-serde 0.3.1", + "primitive-types 0.7.2", + "uint 0.8.5", +] + [[package]] name = "ethereum-types-serialize" version = "0.2.2" @@ -1877,13 +2064,63 @@ version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" +[[package]] +name = "evm" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16c8deca0ec3efa361b03d9cae6fe94321a1d2d0a523437edd720b3d140e3c08" +dependencies = [ + "ethereum 0.4.1", + "evm-core", + "evm-gasometer", + "evm-runtime", + "log", + "parity-scale-codec", + "primitive-types 0.7.2", + "rlp 0.4.6", + "serde", + "sha3 0.8.2", +] + +[[package]] +name = "evm-core" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2d732b3c36df36833761cf67df8f65866be1d368d20508bc3e13e6f256c8c5" +dependencies = [ + "log", + "primitive-types 0.7.2", +] + +[[package]] +name = "evm-gasometer" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46de1b91ccd744627484183729f1b5af484b3bf15505007fc28cc54264cb9ea1" +dependencies = [ + "evm-core", + "evm-runtime", + "primitive-types 0.7.2", +] + +[[package]] +name = "evm-runtime" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2c1d1ffe96f833788512c890d702457d790dba4917ac6f64f8f60fbd9bc40b8" +dependencies = [ + "evm-core", + "primitive-types 0.7.2", + "sha3 0.8.2", +] + [[package]] name = "exit-future" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e43f2f1833d64e33f15592464d6fdd70f349dda7b1a53088eb83cd94014008c5" dependencies = [ - "futures 0.3.6", + "futures 0.3.7", ] [[package]] @@ -1944,7 +2181,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fdbe0d94371f9ce939b555dd342d0686cc4c0cadbcd4b61d70af5ff97eb4126" dependencies = [ - "env_logger", + "env_logger 0.7.1", "log", ] @@ -1955,7 +2192,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8feb87a63249689640ac9c011742c33139204e3c134293d3054022276869133b" dependencies = [ "either", - "futures 0.3.6", + "futures 0.3.7", "futures-timer 2.0.2", "log", "num-traits", @@ -2202,9 +2439,9 @@ checksum = "4c7e4c2612746b0df8fed4ce0c69156021b704c9aefa360311c04e6e9e002eed" [[package]] name = "futures" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8e3078b7b2a8a671cb7a3d17b4760e4181ea243227776ba83fd043b4ca034e" +checksum = "95314d38584ffbfda215621d723e0a3906f032e03ae5551e650058dac83d4797" dependencies = [ "futures-channel", "futures-core", @@ -2217,9 +2454,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a4d35f7401e948629c9c3d6638fb9bf94e0b2121e96c3b428cc4e631f3eb74" +checksum = "0448174b01148032eed37ac4aed28963aaaa8cfa93569a08e5b479bbc6c2c151" dependencies = [ "futures-core", "futures-sink", @@ -2236,9 +2473,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d674eaa0056896d5ada519900dbf97ead2e46a7b6621e8160d79e2f2e1e2784b" +checksum = "18eaa56102984bed2c88ea39026cff3ce3b4c7f508ca970cedf2450ea10d4e46" [[package]] name = "futures-core-preview" @@ -2263,7 +2500,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdcef58a173af8148b182684c9f2d5250875adbcaff7b5794073894f9d8634a9" dependencies = [ "futures 0.1.30", - "futures 0.3.6", + "futures 0.3.7", "lazy_static", "log", "parking_lot 0.9.0", @@ -2274,9 +2511,9 @@ dependencies = [ [[package]] name = "futures-executor" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc709ca1da6f66143b8c9bec8e6260181869893714e9b5a490b169b0414144ab" +checksum = "f5f8e0c9258abaea85e78ebdda17ef9666d390e987f006be6080dfe354b708cb" dependencies = [ "futures-core", "futures-task", @@ -2286,9 +2523,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc94b64bb39543b4e432f1790b6bf18e3ee3b74653c5449f63310e9a74b123c" +checksum = "6e1798854a4727ff944a7b12aa999f58ce7aa81db80d2dfaaf2ba06f065ddd2b" [[package]] name = "futures-lite" @@ -2307,9 +2544,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f57ed14da4603b2554682e9f2ff3c65d7567b53188db96cb71538217fc64581b" +checksum = "e36fccf3fc58563b4a14d265027c627c3b665d7fed489427e88e7cc929559efe" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -2319,15 +2556,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8764258ed64ebc5d9ed185cf86a95db5cac810269c5d20ececb32e0088abbd" +checksum = "0e3ca3f17d6e8804ae5d3df7a7d35b2b3a6fe89dac84b31872720fc3060a0b11" [[package]] name = "futures-task" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd26820a9f3637f1302da8bceba3ff33adbe53464b54ca24d4e2d4f1db30f94" +checksum = "96d502af37186c4fef99453df03e374683f8a1eec9dcc1e66b3b82dc8278ce3c" dependencies = [ "once_cell 1.4.1", ] @@ -2346,9 +2583,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a894a0acddba51a2d49a6f4263b1e64b8c579ece8af50fa86503d52cd1eea34" +checksum = "abcb44342f62e6f3e8ac427b8aa815f724fd705dfad060b18ac7866c15bb8e34" dependencies = [ "futures 0.1.30", "futures-channel", @@ -2358,7 +2595,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project 0.4.27", + "pin-project 1.0.1", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -2384,7 +2621,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce54d63f8b0c75023ed920d46fd71d0cbbb830b0ee012726b5b4f506fb6dea5b" dependencies = [ "bytes 0.5.6", - "futures 0.3.6", + "futures 0.3.7", "memchr", "pin-project 0.4.27", ] @@ -2744,6 +2981,12 @@ dependencies = [ "quick-error", ] +[[package]] +name = "humantime" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a" + [[package]] name = "hyper" version = "0.12.35" @@ -2955,7 +3198,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64fa110ec7b8f493f416eed552740d10e7030ad5f63b2308f82c9608ec2df275" dependencies = [ - "futures 0.3.6", + "futures 0.3.7", "futures-timer 2.0.2", ] @@ -3276,7 +3519,7 @@ checksum = "571f5a4604c1a40d75651da141dfde29ad15329f537a779528803297d2220274" dependencies = [ "atomic", "bytes 0.5.6", - "futures 0.3.6", + "futures 0.3.7", "lazy_static", "libp2p-core", "libp2p-core-derive", @@ -3318,7 +3561,7 @@ dependencies = [ "ed25519-dalek", "either", "fnv", - "futures 0.3.6", + "futures 0.3.7", "futures-timer 3.0.2", "lazy_static", "libsecp256k1", @@ -3358,7 +3601,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74029ae187f35f4b8ddf26b9779a68b340045d708528a103917cdca49a296db5" dependencies = [ "flate2", - "futures 0.3.6", + "futures 0.3.7", "libp2p-core", ] @@ -3368,7 +3611,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cf319822e08dd65c8e060d2354e9f952895bbc433f5706c75ed010c152aee5e" dependencies = [ - "futures 0.3.6", + "futures 0.3.7", "libp2p-core", "log", ] @@ -3381,7 +3624,7 @@ checksum = "d8a9acb43a3e4a4e413e0c4abe0fa49308df7c6335c88534757b647199cb8a51" dependencies = [ "cuckoofilter", "fnv", - "futures 0.3.6", + "futures 0.3.7", "libp2p-core", "libp2p-swarm", "prost", @@ -3400,7 +3643,7 @@ dependencies = [ "byteorder 1.3.4", "bytes 0.5.6", "fnv", - "futures 0.3.6", + "futures 0.3.7", "futures_codec", "hex_fmt", "libp2p-core", @@ -3422,7 +3665,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56396ee63aa9164eacf40c2c5d2bda8c4133c2f57e1b0425d51d3a4e362583b1" dependencies = [ - "futures 0.3.6", + "futures 0.3.7", "libp2p-core", "libp2p-swarm", "log", @@ -3442,7 +3685,7 @@ dependencies = [ "bytes 0.5.6", "either", "fnv", - "futures 0.3.6", + "futures 0.3.7", "futures_codec", "libp2p-core", "libp2p-swarm", @@ -3469,7 +3712,7 @@ dependencies = [ "data-encoding", "dns-parser", "either", - "futures 0.3.6", + "futures 0.3.7", "lazy_static", "libp2p-core", "libp2p-swarm", @@ -3489,7 +3732,7 @@ checksum = "8a73a799cc8410b36e40b8f4c4b6babbcb9efd3727111bf517876e4acfa612d3" dependencies = [ "bytes 0.5.6", "fnv", - "futures 0.3.6", + "futures 0.3.7", "futures_codec", "libp2p-core", "log", @@ -3505,7 +3748,7 @@ checksum = "6ef6c490042f549fb1025f2892dfe6083d97a77558f450c1feebe748ca9eb15a" dependencies = [ "bytes 0.5.6", "curve25519-dalek 2.1.0", - "futures 0.3.6", + "futures 0.3.7", "lazy_static", "libp2p-core", "log", @@ -3525,7 +3768,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad063c21dfcea4518ac9e8bd4119d33a5b26c41e674f602f41f05617a368a5c8" dependencies = [ - "futures 0.3.6", + "futures 0.3.7", "libp2p-core", "libp2p-swarm", "log", @@ -3541,7 +3784,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "903a12e99c72dbebefea258de887982adeacc7025baa1ceb10b7fa9928f54791" dependencies = [ "bytes 0.5.6", - "futures 0.3.6", + "futures 0.3.7", "futures_codec", "libp2p-core", "log", @@ -3558,7 +3801,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96b3c2d5d26a9500e959a0e19743897239a6c4be78dadf99b70414301a70c006" dependencies = [ - "futures 0.3.6", + "futures 0.3.7", "log", "pin-project 0.4.27", "rand 0.7.3", @@ -3574,7 +3817,7 @@ checksum = "9c0c9e8a4cd69d97e9646c54313d007512f411aba8c5226cfcda16df6a6e84a3" dependencies = [ "async-trait", "bytes 0.5.6", - "futures 0.3.6", + "futures 0.3.7", "libp2p-core", "libp2p-swarm", "log", @@ -3593,7 +3836,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7193e444210132237b81b755ec7fe53f1c4bd2f53cf719729b94c0c72eb6eaa1" dependencies = [ "either", - "futures 0.3.6", + "futures 0.3.7", "libp2p-core", "log", "rand 0.7.3", @@ -3609,7 +3852,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44f42ec130d7a37a7e47bf4398026b7ad9185c08ed26972e2720f8b94112796f" dependencies = [ "async-std", - "futures 0.3.6", + "futures 0.3.7", "futures-timer 3.0.2", "get_if_addrs", "ipnet", @@ -3625,7 +3868,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea7acb0a034f70d7db94c300eba3f65c0f6298820105624088a9609c9974d77" dependencies = [ "async-std", - "futures 0.3.6", + "futures 0.3.7", "libp2p-core", "log", ] @@ -3636,7 +3879,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34c1faac6f92c21fbe155417957863ea822fba9e9fd5eb24c0912336a100e63f" dependencies = [ - "futures 0.3.6", + "futures 0.3.7", "js-sys", "libp2p-core", "parity-send-wrapper", @@ -3652,7 +3895,7 @@ checksum = "d650534ebd99f48f6fa292ed5db10d30df2444943afde4407ceeddab8e513fca" dependencies = [ "async-tls", "either", - "futures 0.3.6", + "futures 0.3.7", "libp2p-core", "log", "quicksink", @@ -3670,7 +3913,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "781d9b9f043dcdabc40640807125368596b849fd4d96cdca2dcf052fdf6f33fd" dependencies = [ - "futures 0.3.6", + "futures 0.3.7", "libp2p-core", "parking_lot 0.11.0", "thiserror", @@ -3885,7 +4128,7 @@ checksum = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" [[package]] name = "merkle-patricia-trie" -version = "1.0.0" +version = "1.2.2" dependencies = [ "criterion", "ethereum-types 0.5.2", @@ -4050,7 +4293,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36a6aa6e32fbaf16795142335967214b8564a7a4661eb6dc846ef343a6e00ac1" dependencies = [ "bytes 0.5.6", - "futures 0.3.6", + "futures 0.3.7", "log", "pin-project 1.0.1", "smallvec 1.4.2", @@ -4119,7 +4362,7 @@ dependencies = [ [[package]] name = "node-template" -version = "1.0.0" +version = "1.2.2" dependencies = [ "array-bytes", "darwinia-balances-rpc", @@ -4132,11 +4375,16 @@ dependencies = [ "darwinia-staking", "darwinia-staking-rpc", "darwinia-staking-rpc-runtime-api", + "dvm-consensus", + "dvm-rpc", + "dvm-rpc-primitives", "frame-system-rpc-runtime-api", - "futures 0.3.6", + "futures 0.3.7", "jsonrpc-core", + "jsonrpc-pubsub", "log", "node-template-runtime", + "pallet-evm", "pallet-im-online", "pallet-transaction-payment-rpc", "pallet-transaction-payment-rpc-runtime-api", @@ -4179,7 +4427,7 @@ dependencies = [ [[package]] name = "node-template-runtime" -version = "1.0.0" +version = "1.2.2" dependencies = [ "darwinia-balances", "darwinia-balances-rpc-runtime-api", @@ -4200,6 +4448,8 @@ dependencies = [ "darwinia-treasury", "darwinia-tron-backing", "darwinia-vesting", + "dvm-ethereum", + "dvm-rpc-primitives", "ethereum-primitives", "frame-executive", "frame-support", @@ -4209,6 +4459,7 @@ dependencies = [ "pallet-authorship", "pallet-babe", "pallet-collective", + "pallet-evm", "pallet-finality-tracker", "pallet-grandpa", "pallet-im-online", @@ -4311,9 +4562,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ "autocfg 1.0.1", "libm", @@ -4488,6 +4739,29 @@ dependencies = [ "sp-std 2.0.0", ] +[[package]] +name = "pallet-evm" +version = "2.0.0" +source = "git+https://github.com/darwinia-network/substrate.git?branch=common-library#7314a78d49d0dc3862c6ef7c8115cb1b3d0c0fd2" +dependencies = [ + "evm", + "frame-support", + "frame-system", + "impl-trait-for-tuples 0.1.3", + "pallet-balances", + "pallet-timestamp", + "parity-scale-codec", + "primitive-types 0.7.2", + "ripemd160", + "rlp 0.4.6", + "serde", + "sha3 0.8.2", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 2.0.0", +] + [[package]] name = "pallet-finality-tracker" version = "2.0.0" @@ -5152,6 +5426,7 @@ checksum = "c55c21c64d0eaa4d7ed885d959ef2d62d9e488c27c0e02d9aa5ce6c877b7d5f8" dependencies = [ "fixed-hash 0.6.1", "impl-codec 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-rlp 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "impl-serde 0.3.1", "uint 0.8.5", ] @@ -5680,6 +5955,17 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "ripemd160" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + [[package]] name = "rlp" version = "0.4.4" @@ -5697,6 +5983,17 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "rlp-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "rlp-derive" version = "0.1.0" @@ -5797,7 +6094,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4da5fcb054c46f5a5dff833b129285a93d3f0179531735e6c866e8cc307d2020" dependencies = [ - "futures 0.3.6", + "futures 0.3.7", "pin-project 0.4.27", "static_assertions 1.1.0", ] @@ -5840,7 +6137,7 @@ name = "sc-basic-authorship" version = "0.8.0" source = "git+https://github.com/darwinia-network/substrate.git?branch=common-library#7314a78d49d0dc3862c6ef7c8115cb1b3d0c0fd2" dependencies = [ - "futures 0.3.6", + "futures 0.3.7", "futures-timer 3.0.2", "log", "parity-scale-codec", @@ -5915,7 +6212,7 @@ dependencies = [ "chrono", "derive_more", "fdlimit", - "futures 0.3.6", + "futures 0.3.7", "hex", "lazy_static", "libp2p", @@ -5960,7 +6257,7 @@ source = "git+https://github.com/darwinia-network/substrate.git?branch=common-li dependencies = [ "derive_more", "fnv", - "futures 0.3.6", + "futures 0.3.7", "hash-db", "hex-literal", "kvdb", @@ -6037,7 +6334,7 @@ source = "git+https://github.com/darwinia-network/substrate.git?branch=common-li dependencies = [ "derive_more", "fork-tree", - "futures 0.3.6", + "futures 0.3.7", "futures-timer 3.0.2", "log", "merlin", @@ -6080,7 +6377,7 @@ version = "0.8.0" source = "git+https://github.com/darwinia-network/substrate.git?branch=common-library#7314a78d49d0dc3862c6ef7c8115cb1b3d0c0fd2" dependencies = [ "derive_more", - "futures 0.3.6", + "futures 0.3.7", "jsonrpc-core", "jsonrpc-core-client", "jsonrpc-derive", @@ -6116,7 +6413,7 @@ name = "sc-consensus-slots" version = "0.8.0" source = "git+https://github.com/darwinia-network/substrate.git?branch=common-library#7314a78d49d0dc3862c6ef7c8115cb1b3d0c0fd2" dependencies = [ - "futures 0.3.6", + "futures 0.3.7", "futures-timer 3.0.2", "log", "parity-scale-codec", @@ -6234,7 +6531,7 @@ dependencies = [ "derive_more", "finality-grandpa", "fork-tree", - "futures 0.3.6", + "futures 0.3.7", "futures-timer 3.0.2", "log", "parity-scale-codec", @@ -6270,7 +6567,7 @@ source = "git+https://github.com/darwinia-network/substrate.git?branch=common-li dependencies = [ "derive_more", "finality-grandpa", - "futures 0.3.6", + "futures 0.3.7", "jsonrpc-core", "jsonrpc-core-client", "jsonrpc-derive", @@ -6293,7 +6590,7 @@ version = "0.8.0" source = "git+https://github.com/darwinia-network/substrate.git?branch=common-library#7314a78d49d0dc3862c6ef7c8115cb1b3d0c0fd2" dependencies = [ "ansi_term 0.12.1", - "futures 0.3.6", + "futures 0.3.7", "log", "parity-util-mem", "sc-client-api", @@ -6355,7 +6652,7 @@ dependencies = [ "erased-serde", "fnv", "fork-tree", - "futures 0.3.6", + "futures 0.3.7", "futures-timer 3.0.2", "futures_codec", "hex", @@ -6399,7 +6696,7 @@ name = "sc-network-gossip" version = "0.8.0" source = "git+https://github.com/darwinia-network/substrate.git?branch=common-library#7314a78d49d0dc3862c6ef7c8115cb1b3d0c0fd2" dependencies = [ - "futures 0.3.6", + "futures 0.3.7", "futures-timer 3.0.2", "libp2p", "log", @@ -6416,7 +6713,7 @@ source = "git+https://github.com/darwinia-network/substrate.git?branch=common-li dependencies = [ "bytes 0.5.6", "fnv", - "futures 0.3.6", + "futures 0.3.7", "futures-timer 3.0.2", "hyper 0.13.8", "hyper-rustls", @@ -6441,7 +6738,7 @@ name = "sc-peerset" version = "2.0.0" source = "git+https://github.com/darwinia-network/substrate.git?branch=common-library#7314a78d49d0dc3862c6ef7c8115cb1b3d0c0fd2" dependencies = [ - "futures 0.3.6", + "futures 0.3.7", "libp2p", "log", "serde_json", @@ -6463,7 +6760,7 @@ name = "sc-rpc" version = "2.0.0" source = "git+https://github.com/darwinia-network/substrate.git?branch=common-library#7314a78d49d0dc3862c6ef7c8115cb1b3d0c0fd2" dependencies = [ - "futures 0.3.6", + "futures 0.3.7", "hash-db", "jsonrpc-core", "jsonrpc-pubsub", @@ -6496,7 +6793,7 @@ version = "0.8.0" source = "git+https://github.com/darwinia-network/substrate.git?branch=common-library#7314a78d49d0dc3862c6ef7c8115cb1b3d0c0fd2" dependencies = [ "derive_more", - "futures 0.3.6", + "futures 0.3.7", "jsonrpc-core", "jsonrpc-core-client", "jsonrpc-derive", @@ -6541,7 +6838,7 @@ dependencies = [ "directories", "exit-future", "futures 0.1.30", - "futures 0.3.6", + "futures 0.3.7", "futures-timer 3.0.2", "hash-db", "jsonrpc-core", @@ -6613,7 +6910,7 @@ name = "sc-telemetry" version = "2.0.0" source = "git+https://github.com/darwinia-network/substrate.git?branch=common-library#7314a78d49d0dc3862c6ef7c8115cb1b3d0c0fd2" dependencies = [ - "futures 0.3.6", + "futures 0.3.7", "futures-timer 3.0.2", "libp2p", "log", @@ -6654,7 +6951,7 @@ version = "2.0.0" source = "git+https://github.com/darwinia-network/substrate.git?branch=common-library#7314a78d49d0dc3862c6ef7c8115cb1b3d0c0fd2" dependencies = [ "derive_more", - "futures 0.3.6", + "futures 0.3.7", "linked-hash-map", "log", "parity-util-mem", @@ -6675,7 +6972,7 @@ version = "2.0.0" source = "git+https://github.com/darwinia-network/substrate.git?branch=common-library#7314a78d49d0dc3862c6ef7c8115cb1b3d0c0fd2" dependencies = [ "derive_more", - "futures 0.3.6", + "futures 0.3.7", "futures-diagnose", "intervalier", "log", @@ -7069,7 +7366,7 @@ dependencies = [ "base64 0.12.3", "bytes 0.5.6", "flate2", - "futures 0.3.6", + "futures 0.3.7", "httparse", "log", "rand 0.7.3", @@ -7207,7 +7504,7 @@ version = "0.8.0" source = "git+https://github.com/darwinia-network/substrate.git?branch=common-library#7314a78d49d0dc3862c6ef7c8115cb1b3d0c0fd2" dependencies = [ "derive_more", - "futures 0.3.6", + "futures 0.3.7", "futures-timer 3.0.2", "libp2p", "log", @@ -7278,7 +7575,7 @@ dependencies = [ "derive_more", "dyn-clonable", "ed25519-dalek", - "futures 0.3.6", + "futures 0.3.7", "hash-db", "hash256-std-hasher", "hex", @@ -7384,7 +7681,7 @@ name = "sp-io" version = "2.0.0" source = "git+https://github.com/darwinia-network/substrate.git?branch=common-library#7314a78d49d0dc3862c6ef7c8115cb1b3d0c0fd2" dependencies = [ - "futures 0.3.6", + "futures 0.3.7", "hash-db", "libsecp256k1", "log", @@ -7623,7 +7920,7 @@ version = "2.0.0" source = "git+https://github.com/darwinia-network/substrate.git?branch=common-library#7314a78d49d0dc3862c6ef7c8115cb1b3d0c0fd2" dependencies = [ "derive_more", - "futures 0.3.6", + "futures 0.3.7", "log", "parity-scale-codec", "serde", @@ -7651,7 +7948,7 @@ name = "sp-utils" version = "2.0.0" source = "git+https://github.com/darwinia-network/substrate.git?branch=common-library#7314a78d49d0dc3862c6ef7c8115cb1b3d0c0fd2" dependencies = [ - "futures 0.3.6", + "futures 0.3.7", "futures-core", "futures-timer 3.0.2", "lazy_static", @@ -7820,7 +8117,7 @@ version = "2.0.0" source = "git+https://github.com/darwinia-network/substrate.git?branch=common-library#7314a78d49d0dc3862c6ef7c8115cb1b3d0c0fd2" dependencies = [ "frame-system-rpc-runtime-api", - "futures 0.3.6", + "futures 0.3.7", "jsonrpc-core", "jsonrpc-core-client", "jsonrpc-derive", @@ -7856,7 +8153,7 @@ name = "substrate-test-utils" version = "2.0.0" source = "git+https://github.com/darwinia-network/substrate.git?branch=common-library#7314a78d49d0dc3862c6ef7c8115cb1b3d0c0fd2" dependencies = [ - "futures 0.3.6", + "futures 0.3.7", "substrate-test-utils-derive", "tokio 0.2.22", ] @@ -8809,7 +9106,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" dependencies = [ - "futures 0.3.6", + "futures 0.3.7", "js-sys", "parking_lot 0.11.0", "pin-utils", @@ -9163,7 +9460,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aeb8c4043cac71c3c299dff107171c220d179492350ea198e109a414981b83c" dependencies = [ - "futures 0.3.6", + "futures 0.3.7", "log", "nohash-hasher", "parking_lot 0.11.0", diff --git a/Cargo.toml b/Cargo.toml index 45de62a807..b8493f76e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,14 @@ [workspace] members = [ + # node "bin/node-template/node", "bin/node-template/runtime", + # client "client/cli", + # frame "frame/balances", "frame/balances/rpc", "frame/balances/rpc/runtime-api", - "frame/claims", "frame/bridge/crab/backing", "frame/bridge/crab/issuing", "frame/bridge/ethereum/backing", @@ -14,7 +16,14 @@ members = [ "frame/bridge/ethereum/relay", "frame/bridge/relayer-game", "frame/bridge/tron/backing", + "frame/claims", "frame/democracy", + "frame/dvm/ethereum", + "frame/dvm/rpc", + "frame/dvm/rpc/core", + "frame/dvm/rpc/primitives", + "frame/dvm/consensus", + "frame/dvm/consensus/primitives", "frame/elections-phragmen", "frame/header-mmr", "frame/header-mmr/rpc", @@ -25,6 +34,7 @@ members = [ "frame/support", "frame/treasury", "frame/vesting", + # primitives "primitives/array-bytes", "primitives/ethereum-primitives", "primitives/merkle-patricia-trie", diff --git a/bin/node-template/node/Cargo.toml b/bin/node-template/node/Cargo.toml index fa561e1a13..0c6f2918b1 100644 --- a/bin/node-template/node/Cargo.toml +++ b/bin/node-template/node/Cargo.toml @@ -8,7 +8,7 @@ license = "Unlicense" name = "node-template" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [[bin]] name = "node-template" @@ -17,12 +17,13 @@ name = "node-template" # anonymous node-template-runtime = { path = "../runtime" } # crates -codec = { package = "parity-scale-codec", version = "1.3.5" } -futures = { version = "0.3.6" } -jsonrpc-core = { version = "15.1.0" } -log = { version = "0.4.11" } -structopt = { version = "0.3.20" } -tokio = { version = "0.3.1", optional = true, features = ["rt-multi-thread"] } +codec = { package = "parity-scale-codec", version = "1.3.5" } +futures = { version = "0.3.7" } +jsonrpc-core = { version = "15.1.0" } +jsonrpc-pubsub = { version = "15.1.0" } +log = { version = "0.4.11" } +structopt = { version = "0.3.20" } +tokio = { version = "0.3.1", optional = true, features = ["rt-multi-thread"] } # darwinia array-bytes = { path = "../../../primitives/array-bytes" } darwinia-balances-rpc = { path = "../../../frame/balances/rpc" } @@ -35,8 +36,12 @@ darwinia-header-mmr-rpc-runtime-api = { path = "../../../frame/header-mmr/rpc/ru darwinia-staking = { path = "../../../frame/staking" } darwinia-staking-rpc = { path = "../../../frame/staking/rpc" } darwinia-staking-rpc-runtime-api = { path = "../../../frame/staking/rpc/runtime-api" } +dvm-consensus = { path = "../../../frame/dvm/consensus" } +dvm-rpc = { path = "../../../frame/dvm/rpc" } +dvm-rpc-primitives = { path = "../../../frame/dvm/rpc/primitives" } # substrate frame-system-rpc-runtime-api = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +pallet-evm = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } pallet-im-online = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } pallet-transaction-payment-rpc = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } diff --git a/bin/node-template/node/src/chain_spec.rs b/bin/node-template/node/src/chain_spec.rs index ffa3a8ae06..4af9c827cd 100644 --- a/bin/node-template/node/src/chain_spec.rs +++ b/bin/node-template/node/src/chain_spec.rs @@ -1,3 +1,5 @@ +// --- std --- +use std::collections::BTreeMap; // --- substrate --- use pallet_im_online::sr25519::AuthorityId as ImOnlineId; use sc_service::{ChainType, Properties}; @@ -159,6 +161,20 @@ fn testnet_genesis( root_key: AccountId, endowed_accounts: Vec, ) -> GenesisConfig { + let gerald_evm_account_id = + fixed_hex_bytes_unchecked!("0x6be02d1d3665660d22ff9624b7be0551ee1ac91b", 20).into(); + let mut evm_accounts = BTreeMap::new(); + + evm_accounts.insert( + gerald_evm_account_id, + pallet_evm::GenesisAccount { + nonce: 0.into(), + balance: 123_456_123_000_000_000_000_000u128.into(), + storage: BTreeMap::new(), + code: vec![], + }, + ); + GenesisConfig { frame_system: Some(SystemConfig { code: wasm_binary_unwrap().to_vec(), @@ -258,6 +274,10 @@ fn testnet_genesis( darwinia_tron_backing: Some(TronBackingConfig { backed_ring: 1 << 56, backed_kton: 1 << 56, - }) + }), + pallet_evm: Some(EVMConfig { + accounts: evm_accounts, + }), + dvm_ethereum: Some(Default::default()), } } diff --git a/bin/node-template/node/src/rpc.rs b/bin/node-template/node/src/rpc.rs index 254b282936..39e936d7bf 100644 --- a/bin/node-template/node/src/rpc.rs +++ b/bin/node-template/node/src/rpc.rs @@ -18,8 +18,6 @@ pub use sc_rpc::{DenyUnsafe, SubscriptionTaskExecutor}; // --- std --- use std::sync::Arc; -// --- substrate --- -use sp_api::ProvideRuntimeApi; // --- darwinia --- use node_template_runtime::{ opaque::Block, @@ -64,6 +62,10 @@ pub struct FullDeps { pub select_chain: SC, /// Whether to deny unsafe calls pub deny_unsafe: sc_rpc::DenyUnsafe, + /// The Node authority flag + pub is_authority: bool, + /// Network service + pub network: Arc>, /// BABE specific dependencies. pub babe: BabeDeps, /// GRANDPA specific dependencies. @@ -83,11 +85,19 @@ pub struct LightDeps { } /// Instantiate all RPC extensions. -pub fn create_full(deps: FullDeps) -> RpcExtension +pub fn create_full( + deps: FullDeps, + subscription_task_executor: sc_rpc::SubscriptionTaskExecutor, +) -> RpcExtension where - C: 'static + Send + Sync, - C: ProvideRuntimeApi, - C: sp_blockchain::HeaderBackend + C: 'static + + Send + + Sync + + sp_api::ProvideRuntimeApi + + sc_client_api::AuxStore + + sc_client_api::BlockchainEvents + + sc_client_api::StorageProvider + + sp_blockchain::HeaderBackend + sp_blockchain::HeaderMetadata, C::Api: substrate_frame_rpc_system::AccountNonceApi, C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, @@ -96,11 +106,15 @@ where C::Api: darwinia_balances_rpc::BalancesRuntimeApi, C::Api: darwinia_header_mmr_rpc::HeaderMMRRuntimeApi, C::Api: darwinia_staking_rpc::StakingRuntimeApi, - P: 'static + sp_transaction_pool::TransactionPool, + C::Api: dvm_rpc_primitives::EthereumRuntimeRPCApi, + ::Error: std::fmt::Debug, + P: 'static + Sync + Send + sp_transaction_pool::TransactionPool, SC: 'static + sp_consensus::SelectChain, B: 'static + Send + Sync + sc_client_api::Backend, B::State: sc_client_api::StateBackend>, { + // --- crates --- + use jsonrpc_pubsub::manager::SubscriptionManager; // --- substrate --- use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApi}; use sc_consensus_babe_rpc::{BabeApi, BabeRpcHandler}; @@ -110,12 +124,16 @@ where use darwinia_balances_rpc::{Balances, BalancesApi}; use darwinia_header_mmr_rpc::{HeaderMMR, HeaderMMRApi}; use darwinia_staking_rpc::{Staking, StakingApi}; + use dvm_rpc::{EthApi, EthApiServer, EthPubSubApi, EthPubSubApiServer, NetApi, NetApiServer}; + use node_template_runtime::TransactionConverter; let FullDeps { client, pool, select_chain, deny_unsafe, + is_authority, + network, babe, grandpa, } = deps; @@ -123,7 +141,7 @@ where io.extend_with(SystemApi::to_delegate(FullSystem::new( client.clone(), - pool, + pool.clone(), deny_unsafe, ))); io.extend_with(TransactionPaymentApi::to_delegate(TransactionPayment::new( @@ -164,7 +182,20 @@ where } io.extend_with(BalancesApi::to_delegate(Balances::new(client.clone()))); io.extend_with(HeaderMMRApi::to_delegate(HeaderMMR::new(client.clone()))); - io.extend_with(StakingApi::to_delegate(Staking::new(client))); + io.extend_with(StakingApi::to_delegate(Staking::new(client.clone()))); + io.extend_with(EthApiServer::to_delegate(EthApi::new( + client.clone(), + pool.clone(), + TransactionConverter, + is_authority, + ))); + io.extend_with(EthPubSubApiServer::to_delegate(EthPubSubApi::new( + pool.clone(), + client.clone(), + network.clone(), + SubscriptionManager::new(Arc::new(subscription_task_executor)), + ))); + io.extend_with(NetApiServer::to_delegate(NetApi::new(client))); io } @@ -172,9 +203,11 @@ where /// Instantiate all RPC extensions for light node. pub fn create_light(deps: LightDeps) -> RpcExtension where - C: 'static + Send + Sync, - C: ProvideRuntimeApi, - C: sp_blockchain::HeaderBackend, + C: 'static + + Send + + Sync + + sp_api::ProvideRuntimeApi + + sp_blockchain::HeaderBackend, C::Api: substrate_frame_rpc_system::AccountNonceApi, C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, P: 'static + sp_transaction_pool::TransactionPool, diff --git a/bin/node-template/node/src/service.rs b/bin/node-template/node/src/service.rs index 3cd52f74d2..99525d3cf1 100644 --- a/bin/node-template/node/src/service.rs +++ b/bin/node-template/node/src/service.rs @@ -18,6 +18,7 @@ use sc_finality_grandpa::{ LinkHalf, SharedVoterState as GrandpaSharedVoterState, VotingRulesBuilder as GrandpaVotingRulesBuilder, }; +use sc_network::NetworkService; use sc_service::{ config::{KeystoreConfig, PrometheusConfig}, BuildNetworkParams, Configuration, Error as ServiceError, NoopRpcExtensionBuilder, @@ -38,6 +39,8 @@ use crate::rpc::{ self, BabeDeps, DenyUnsafe, FullDeps, GrandpaDeps, LightDeps, RpcExtension, SubscriptionTaskExecutor, }; +use dvm_consensus::FrontierBlockImport; + use node_template_runtime::{ opaque::Block, primitives::{AccountId, Balance, Hash, Nonce, Power}, @@ -78,6 +81,7 @@ pub trait RuntimeApiCollection: + darwinia_balances_rpc_runtime_api::BalancesApi + darwinia_header_mmr_rpc_runtime_api::HeaderMMRApi + darwinia_staking_rpc_runtime_api::StakingApi + + dvm_rpc_primitives::EthereumRuntimeRPCApi where >::StateBackend: sp_api::StateBackend, { @@ -97,7 +101,8 @@ where + pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi + darwinia_balances_rpc_runtime_api::BalancesApi + darwinia_header_mmr_rpc_runtime_api::HeaderMMRApi - + darwinia_staking_rpc_runtime_api::StakingApi, + + darwinia_staking_rpc_runtime_api::StakingApi + + dvm_rpc_primitives::EthereumRuntimeRPCApi, >::StateBackend: sp_api::StateBackend, { } @@ -152,12 +157,21 @@ fn new_partial( DefaultImportQueue>, FullPool>, ( - impl Fn(DenyUnsafe, SubscriptionTaskExecutor) -> RpcExtension, + impl Fn( + DenyUnsafe, + bool, + Arc>, + SubscriptionTaskExecutor, + ) -> RpcExtension, ( BabeBlockImport< Block, FullClient, - FullGrandpaBlockImport, + FrontierBlockImport< + Block, + FullGrandpaBlockImport, + FullClient, + >, >, LinkHalf, FullSelectChain>, BabeLink, @@ -199,9 +213,11 @@ where grandpa_hard_forks, )?; let justification_import = grandpa_block_import.clone(); + let frontier_block_import = + FrontierBlockImport::new(grandpa_block_import.clone(), client.clone(), true); let (babe_import, babe_link) = sc_consensus_babe::block_import( BabeConfig::get_or_compute(&*client)?, - grandpa_block_import, + frontier_block_import, client.clone(), )?; let import_queue = sc_consensus_babe::import_queue( @@ -225,18 +241,22 @@ where let rpc_setup = (shared_voter_state.clone(), finality_proof_provider.clone()); let babe_config = babe_link.config().clone(); let shared_epoch_changes = babe_link.epoch_changes().clone(); + let subscription_task_executor = + sc_rpc::SubscriptionTaskExecutor::new(task_manager.spawn_handle()); let rpc_extensions_builder = { let client = client.clone(); let keystore = keystore.clone(); let transaction_pool = transaction_pool.clone(); let select_chain = select_chain.clone(); - move |deny_unsafe, subscription_executor| -> RpcExtension { + move |deny_unsafe, is_authority, network, subscription_executor| -> RpcExtension { let deps = FullDeps { client: client.clone(), pool: transaction_pool.clone(), select_chain: select_chain.clone(), deny_unsafe, + is_authority, + network, babe: BabeDeps { babe_config: babe_config.clone(), shared_epoch_changes: shared_epoch_changes.clone(), @@ -251,7 +271,7 @@ where }, }; - rpc::create_full(deps) + rpc::create_full(deps, subscription_task_executor.clone()) } }; @@ -321,14 +341,28 @@ where } let telemetry_connection_sinks = TelemetryConnectionSinks::default(); - sc_service::spawn_tasks(SpawnTasksParams { config, backend: backend.clone(), client: client.clone(), keystore: keystore.clone(), network: network.clone(), - rpc_extensions_builder: Box::new(rpc_extensions_builder), + rpc_extensions_builder: { + let wrap_rpc_extensions_builder = { + let network = network.clone(); + + move |deny_unsafe, subscription_executor| -> RpcExtension { + rpc_extensions_builder( + deny_unsafe, + is_authority, + network.clone(), + subscription_executor, + ) + } + }; + + Box::new(wrap_rpc_extensions_builder) + }, transaction_pool: transaction_pool.clone(), task_manager: &mut task_manager, on_demand: None, diff --git a/bin/node-template/runtime/Cargo.toml b/bin/node-template/runtime/Cargo.toml index 2af1cccd1f..c341c51186 100644 --- a/bin/node-template/runtime/Cargo.toml +++ b/bin/node-template/runtime/Cargo.toml @@ -7,7 +7,7 @@ license = "Unlicense" name = "node-template-runtime" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates @@ -35,6 +35,8 @@ darwinia-support = { default-features = false, path = "../../ darwinia-treasury = { default-features = false, path = "../../../frame/treasury" } darwinia-tron-backing = { default-features = false, path = "../../../frame/bridge/tron/backing" } darwinia-vesting = { default-features = false, path = "../../../frame/vesting" } +dvm-ethereum = { default-features = false, path = "../../../frame/dvm/ethereum" } +dvm-rpc-primitives = { default-features = false, path = "../../../frame/dvm/rpc/primitives" } ethereum-primitives = { default-features = false, path = "../../../primitives/ethereum-primitives" } # substrate frame-executive = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } @@ -45,6 +47,7 @@ pallet-authority-discovery = { default-features = false, git = " pallet-authorship = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } pallet-babe = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } pallet-collective = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +pallet-evm = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } pallet-finality-tracker = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } pallet-grandpa = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } pallet-im-online = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } @@ -80,8 +83,7 @@ wasm-builder-runner = { package = "substrate-wasm-builder-runner", git = "https: [features] default = ["std"] - -std = [ +std = [ "codec/std", "serde", "darwinia-balances/std", @@ -103,6 +105,8 @@ std = [ "darwinia-treasury/std", "darwinia-tron-backing/std", "darwinia-vesting/std", + "dvm-ethereum/std", + "dvm-rpc-primitives/std", "ethereum-primitives/std", "frame-executive/std", "frame-support/std", @@ -112,6 +116,7 @@ std = [ "pallet-authorship/std", "pallet-babe/std", "pallet-collective/std", + "pallet-evm/std", "pallet-finality-tracker/std", "pallet-grandpa/std", "pallet-im-online/std", diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index c9d626b7b7..a514dd35f1 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -214,7 +214,9 @@ pub mod primitives { pub const KEY_TYPE: KeyTypeId = key_types::REPORTING; mod app { + // --- substrate --- use sp_application_crypto::{app_crypto, sr25519}; + app_crypto!(sr25519, super::KEY_TYPE); } @@ -265,7 +267,7 @@ pub mod primitives { pub type Nonce = u32; /// A hash of some data used by the chain. - pub type Hash = sp_core::H256; + pub type Hash = H256; /// Digest item type. pub type DigestItem = generic::DigestItem; @@ -366,13 +368,17 @@ use static_assertions::const_assert; // --- substrate --- use frame_support::{ construct_runtime, debug, parameter_types, - traits::{ChangeMembers, InstanceFilter, KeyOwnerProofSystem, LockIdentifier, Randomness}, + traits::{ + ChangeMembers, FindAuthor, InstanceFilter, KeyOwnerProofSystem, LockIdentifier, Randomness, + }, weights::{ constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND}, Weight, }, + ConsensusEngineId, }; use frame_system::{EnsureOneOf, EnsureRoot}; +use pallet_evm::{Account as EVMAccount, EnsureAddressTruncated, FeeCalculator}; use pallet_grandpa::{ fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, }; @@ -383,9 +389,9 @@ use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo as Transacti use sp_api::impl_runtime_apis; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use sp_core::{ - crypto::KeyTypeId, + crypto::{KeyTypeId, Public}, u32_trait::{_1, _2, _3, _5}, - OpaqueMetadata, + OpaqueMetadata, H160, H256, U256, }; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, @@ -408,6 +414,8 @@ use darwinia_balances_rpc_runtime_api::RuntimeDispatchInfo as BalancesRuntimeDis use darwinia_header_mmr_rpc_runtime_api::RuntimeDispatchInfo as HeaderMMRRuntimeDispatchInfo; use darwinia_staking::EraIndex; use darwinia_staking_rpc_runtime_api::RuntimeDispatchInfo as StakingRuntimeDispatchInfo; +use dvm_ethereum::precompiles::{ConcatAddressMapping, NativeTransfer}; +use dvm_rpc_primitives::TransactionStatus; use impls::*; /// This runtime version. @@ -1133,6 +1141,54 @@ impl darwinia_tron_backing::Trait for Runtime { impl darwinia_header_mmr::Trait for Runtime {} +/// Fixed gas price of `1`. +pub struct FixedGasPrice; +impl FeeCalculator for FixedGasPrice { + fn min_gas_price() -> U256 { + // Gas price is always one token per gas. + 0.into() + } +} +parameter_types! { + pub const ChainId: u64 = 43; +} +impl pallet_evm::Trait for Runtime { + type FeeCalculator = FixedGasPrice; + type CallOrigin = EnsureAddressTruncated; + type WithdrawOrigin = EnsureAddressTruncated; + type AddressMapping = ConcatAddressMapping; + type Currency = Balances; + type Event = Event; + type Precompiles = ( + pallet_evm::precompiles::ECRecover, + pallet_evm::precompiles::Sha256, + pallet_evm::precompiles::Ripemd160, + pallet_evm::precompiles::Identity, + NativeTransfer, + ); + type ChainId = ChainId; +} + +pub struct EthereumFindAuthor(sp_std::marker::PhantomData); +impl> FindAuthor for EthereumFindAuthor { + fn find_author<'a, I>(digests: I) -> Option + where + I: 'a + IntoIterator, + { + if let Some(author_index) = F::find_author(digests) { + let authority_id = Babe::authorities()[author_index as usize].clone(); + return Some(H160::from_slice(&authority_id.0.to_raw_vec()[4..24])); + } + None + } +} +impl dvm_ethereum::Trait for Runtime { + type Event = Event; + type FindAuthor = EthereumFindAuthor; + type AddressMapping = ConcatAddressMapping; + type RingCurrency = Balances; +} + construct_runtime!( pub enum Runtime where @@ -1195,6 +1251,9 @@ construct_runtime!( TronBacking: darwinia_tron_backing::{Module, Storage, Config}, HeaderMMR: darwinia_header_mmr::{Module, Call, Storage}, + + EVM: pallet_evm::{Module, Config, Call, Storage, Event}, + Ethereum: dvm_ethereum::{Module, Call, Storage, Event, Config, ValidateUnsigned}, } ); @@ -1447,4 +1506,128 @@ impl_runtime_apis! { Staking::power_of_rpc(account) } } + + impl dvm_rpc_primitives::EthereumRuntimeRPCApi for Runtime { + fn chain_id() -> u64 { + ChainId::get() + } + + fn account_basic(address: H160) -> EVMAccount { + pallet_evm::Module::::account_basic(&address) + } + + fn gas_price() -> U256 { + FixedGasPrice::min_gas_price() + } + + fn account_code_at(address: H160) -> Vec { + pallet_evm::Module::::account_codes(address) + } + + fn author() -> H160 { + >::find_author() + } + + fn storage_at(address: H160, index: U256) -> H256 { + let mut tmp = [0u8; 32]; + index.to_big_endian(&mut tmp); + pallet_evm::Module::::account_storages(address, H256::from_slice(&tmp[..])) + } + + fn call( + from: H160, + data: Vec, + value: U256, + gas_limit: U256, + gas_price: Option, + nonce: Option, + action: dvm_ethereum::TransactionAction, + ) -> Result<(Vec, U256), sp_runtime::DispatchError> { + // --- substrate --- + use sp_runtime::traits::UniqueSaturatedInto; + + // ensure that the gas_limit fits within a u32; otherwise the wrong value will be passed + let gas_limit_considered: u32 = gas_limit.unique_saturated_into(); + let gas_limit_considered_256: U256 = gas_limit_considered.into(); + if gas_limit_considered_256 != gas_limit { + frame_support::debug::warn!("WARNING: An invalid gas_limit amount was submitted. + Make sure your gas_limit fits within a 32-bit integer + (gas_limit: {:?})", gas_limit); + } + match action { + dvm_ethereum::TransactionAction::Call(to) => + EVM::execute_call( + from, + to, + data, + value, + gas_limit_considered, + gas_price.unwrap_or(U256::from(0)), + nonce, + false, + ) + .map(|(_, ret, gas, _)| (ret, gas)) + .map_err(|err| err.into()), + dvm_ethereum::TransactionAction::Create => + EVM::execute_create( + from, + data, + value, + gas_limit_considered, + gas_price.unwrap_or(U256::from(0)), + nonce, + false, + ) + .map(|(_, _, gas, _)| (vec![], gas)) + .map_err(|err| err.into()), + } + } + + fn current_transaction_statuses() -> Option> { + Ethereum::current_transaction_statuses() + } + + fn current_block() -> Option { + Ethereum::current_block() + } + + fn current_receipts() -> Option> { + Ethereum::current_receipts() + } + + fn current_all() -> ( + Option, + Option>, + Option> + ) { + ( + Ethereum::current_block(), + Ethereum::current_receipts(), + Ethereum::current_transaction_statuses() + ) + } + } +} + +pub struct TransactionConverter; +impl dvm_rpc_primitives::ConvertTransaction for TransactionConverter { + fn convert_transaction(&self, transaction: dvm_ethereum::Transaction) -> UncheckedExtrinsic { + UncheckedExtrinsic::new_unsigned( + >::transact(transaction).into(), + ) + } +} +impl dvm_rpc_primitives::ConvertTransaction for TransactionConverter { + fn convert_transaction( + &self, + transaction: dvm_ethereum::Transaction, + ) -> opaque::UncheckedExtrinsic { + let extrinsic = UncheckedExtrinsic::new_unsigned( + >::transact(transaction).into(), + ); + let encoded = extrinsic.encode(); + + opaque::UncheckedExtrinsic::decode(&mut &encoded[..]) + .expect("Encoded extrinsic is always valid") + } } diff --git a/client/cli/Cargo.toml b/client/cli/Cargo.toml index 86720e552f..88156ac5e0 100644 --- a/client/cli/Cargo.toml +++ b/client/cli/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "darwinia-cli" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/frame/balances/Cargo.toml b/frame/balances/Cargo.toml index 4e4a9dc05c..58579890ff 100644 --- a/frame/balances/Cargo.toml +++ b/frame/balances/Cargo.toml @@ -1,13 +1,13 @@ [package] authors = ["Darwinia Network "] -description = "FRAME pallet to manage RING" +description = "FRAME pallet to manage balances" edition = "2018" homepage = "https://darwinia.network/" license = "GPL-3.0" name = "darwinia-balances" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/frame/balances/rpc/Cargo.toml b/frame/balances/rpc/Cargo.toml index 603a6ce6dc..4f3faf6912 100644 --- a/frame/balances/rpc/Cargo.toml +++ b/frame/balances/rpc/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "darwinia-balances-rpc" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/frame/balances/rpc/runtime-api/Cargo.toml b/frame/balances/rpc/runtime-api/Cargo.toml index 9b5607dbc0..c985f050d7 100644 --- a/frame/balances/rpc/runtime-api/Cargo.toml +++ b/frame/balances/rpc/runtime-api/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "darwinia-balances-rpc-runtime-api" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/frame/bridge/crab/backing/Cargo.toml b/frame/bridge/crab/backing/Cargo.toml index b17d9fa556..46b800bd38 100644 --- a/frame/bridge/crab/backing/Cargo.toml +++ b/frame/bridge/crab/backing/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "darwinia-crab-backing" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/frame/bridge/crab/issuing/Cargo.toml b/frame/bridge/crab/issuing/Cargo.toml index 1698042ad0..3f16659d75 100644 --- a/frame/bridge/crab/issuing/Cargo.toml +++ b/frame/bridge/crab/issuing/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "darwinia-crab-issuing" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/frame/bridge/crab/issuing/src/mock.rs b/frame/bridge/crab/issuing/src/mock.rs index 9b938401cf..a05996437e 100644 --- a/frame/bridge/crab/issuing/src/mock.rs +++ b/frame/bridge/crab/issuing/src/mock.rs @@ -5,7 +5,7 @@ pub mod crab_issuing { pub use crate::Event; } -// --- crate --- +// --- crates --- use codec::{Decode, Encode}; // --- substrate --- use frame_support::{impl_outer_event, impl_outer_origin, parameter_types, weights::Weight}; diff --git a/frame/bridge/ethereum/backing/Cargo.toml b/frame/bridge/ethereum/backing/Cargo.toml index 8be5c4768b..b1c46a3527 100644 --- a/frame/bridge/ethereum/backing/Cargo.toml +++ b/frame/bridge/ethereum/backing/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "darwinia-ethereum-backing" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/frame/bridge/ethereum/linear-relay/Cargo.toml b/frame/bridge/ethereum/linear-relay/Cargo.toml index d431400c23..a79fdd03ef 100644 --- a/frame/bridge/ethereum/linear-relay/Cargo.toml +++ b/frame/bridge/ethereum/linear-relay/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0" name = "darwinia-ethereum-linear-relay" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/frame/bridge/ethereum/offchain/Cargo.toml b/frame/bridge/ethereum/offchain/Cargo.toml index 414cfed727..71e0cb76ad 100644 --- a/frame/bridge/ethereum/offchain/Cargo.toml +++ b/frame/bridge/ethereum/offchain/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "darwinia-ethereum-offchain" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/frame/bridge/ethereum/relay/Cargo.toml b/frame/bridge/ethereum/relay/Cargo.toml index 0e03c8668e..867c7fa6ca 100644 --- a/frame/bridge/ethereum/relay/Cargo.toml +++ b/frame/bridge/ethereum/relay/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0" name = "darwinia-ethereum-relay" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/frame/bridge/relayer-game/Cargo.toml b/frame/bridge/relayer-game/Cargo.toml index a791fd8f25..a1d1e37fb4 100644 --- a/frame/bridge/relayer-game/Cargo.toml +++ b/frame/bridge/relayer-game/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "darwinia-relayer-game" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates @@ -23,11 +23,11 @@ sp-runtime = { default-features = false, git = "https://github.com/darwinia-n sp-std = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } [dev-dependencies] -# --- crates --- -env_logger = { version = "0.7.1" } -# --- darwinia --- +# crates +env_logger = { version = "0.8.1" } +# darwinia darwinia-balances = { path = "../../balances" } -# --- substrate --- +# substrate sp-core = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } sp-io = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } diff --git a/frame/bridge/tron/backing/Cargo.toml b/frame/bridge/tron/backing/Cargo.toml index 8defa69b7b..0dcf904077 100644 --- a/frame/bridge/tron/backing/Cargo.toml +++ b/frame/bridge/tron/backing/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "darwinia-tron-backing" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/frame/claims/Cargo.toml b/frame/claims/Cargo.toml index f234634f7b..cef84639ab 100644 --- a/frame/claims/Cargo.toml +++ b/frame/claims/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "darwinia-claims" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/frame/democracy/Cargo.toml b/frame/democracy/Cargo.toml index 61f14ff54c..16e19f4cb2 100644 --- a/frame/democracy/Cargo.toml +++ b/frame/democracy/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "darwinia-democracy" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/frame/dvm/consensus/Cargo.toml b/frame/dvm/consensus/Cargo.toml new file mode 100644 index 0000000000..8244c559c8 --- /dev/null +++ b/frame/dvm/consensus/Cargo.toml @@ -0,0 +1,31 @@ +[package] +authors = ["Darwinia Network "] +description = "Consensus for darwinia dvm" +edition = "2018" +homepage = "https://darwinia.network/" +license = "GPL-3.0" +name = "dvm-consensus" +readme = "README.md" +repository = "https://github.com/darwinia-network/darwinia-common/" +version = "1.2.2" + +[dependencies] +# crates +codec = { package = "parity-scale-codec", version = "1.3.5", features = ["derive"] } +derive_more = "0.99.11" +ethereum = { version = "0.3", features = ["codec"] } +futures = { version = "0.3.7", features = ["compat"] } +log = "0.4.11" +# darwinia +dvm-consensus-primitives = { path = "primitives" } +# substrate +prometheus-endpoint = { package = "substrate-prometheus-endpoint", git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sc-client-api = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-api = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-block-builder = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-blockchain = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-consensus = { version = "0.8.0", git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-core = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-inherents = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-runtime = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-timestamp = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } diff --git a/frame/dvm/consensus/primitives/Cargo.toml b/frame/dvm/consensus/primitives/Cargo.toml new file mode 100644 index 0000000000..9ae3fc93d1 --- /dev/null +++ b/frame/dvm/consensus/primitives/Cargo.toml @@ -0,0 +1,33 @@ +[package] +authors = ["Darwinia Network "] +description = "Primitives for Dvm consensus" +edition = "2018" +homepage = "https://substrate.dev" +license = "Apache-2.0" +name = "dvm-consensus-primitives" +readme = "README.md" +repository = "https://github.com/darwinia-network/darwinia-common/" +version = "1.2.2" + +[dependencies] +# crates +codec = { package = "parity-scale-codec", version = "1.3.5", default-features = false, features = ["derive"] } +# substrate +sp-core = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-runtime = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-std = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } + +[features] +default = ["std"] + +std = [ + "crates-std", + "substrate-std", +] + +crates-std = ["codec/std"] +substrate-std = [ + "sp-std/std", + "sp-runtime/std", + "sp-core/std", +] diff --git a/frame/dvm/consensus/primitives/src/lib.rs b/frame/dvm/consensus/primitives/src/lib.rs new file mode 100644 index 0000000000..61661855af --- /dev/null +++ b/frame/dvm/consensus/primitives/src/lib.rs @@ -0,0 +1,36 @@ +// This file is part of Frontier. + +// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::{Decode, Encode}; +use sp_core::H256; +use sp_runtime::ConsensusEngineId; +use sp_std::vec::Vec; + +pub const FRONTIER_ENGINE_ID: ConsensusEngineId = [b'f', b'r', b'o', b'n']; + +#[derive(Decode, Encode, Clone, PartialEq, Eq)] +pub enum ConsensusLog { + #[codec(index = "1")] + EndBlock { + /// Ethereum block hash. + block_hash: H256, + /// Transaction hashes of the Ethereum block. + transaction_hashes: Vec, + }, +} diff --git a/frame/dvm/consensus/src/aux_schema.rs b/frame/dvm/consensus/src/aux_schema.rs new file mode 100644 index 0000000000..7b4c0116e4 --- /dev/null +++ b/frame/dvm/consensus/src/aux_schema.rs @@ -0,0 +1,98 @@ +// This file is part of Frontier. + +// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use codec::{Decode, Encode}; +use sc_client_api::backend::AuxStore; +use sp_blockchain::{Error as ClientError, Result as ClientResult}; +use sp_core::H256; +use sp_runtime::traits::Block as BlockT; + +fn load_decode(backend: &B, key: &[u8]) -> ClientResult> { + let corrupt = |e: codec::Error| { + ClientError::Backend(format!( + "Frontier DB is corrupted. Decode error: {}", + e.what() + )) + }; + match backend.get_aux(key)? { + None => Ok(None), + Some(t) => T::decode(&mut &t[..]).map(Some).map_err(corrupt), + } +} + +/// Map an Ethereum block hash into a Substrate block hash. +pub fn block_hash_key(ethereum_block_hash: H256) -> Vec { + let mut ret = b"ethereum_block_hash:".to_vec(); + ret.append(&mut ethereum_block_hash.as_ref().to_vec()); + ret +} + +/// Given an Ethereum block hash, get the corresponding Substrate block hash from AuxStore. +pub fn load_block_hash( + backend: &B, + hash: H256, +) -> ClientResult>> { + let key = block_hash_key(hash); + load_decode(backend, &key) +} + +/// Update Aux block hash. +pub fn write_block_hash( + client: &Backend, + ethereum_hash: H256, + block_hash: Hash, + write_aux: F, +) -> R +where + F: FnOnce(&[(&[u8], &[u8])]) -> R, +{ + let key = block_hash_key(ethereum_hash); + + let mut data: Vec = match load_decode(client, &key) { + Ok(Some(hashes)) => hashes, + _ => Vec::new(), + }; + data.push(block_hash); + + write_aux(&[(&key, &data.encode()[..])]) +} + +/// Map an Ethereum transaction hash into its corresponding Ethereum block hash and index. +pub fn transaction_metadata_key(ethereum_transaction_hash: H256) -> Vec { + let mut ret = b"ethereum_transaction_hash:".to_vec(); + ret.append(&mut ethereum_transaction_hash.as_ref().to_vec()); + ret +} + +/// Given an Ethereum transaction hash, get the corresponding Ethereum block hash and index. +pub fn load_transaction_metadata( + backend: &B, + hash: H256, +) -> ClientResult> { + let key = transaction_metadata_key(hash); + load_decode(backend, &key) +} + +/// Update Aux transaction metadata. +pub fn write_transaction_metadata(hash: H256, metadata: (H256, u32), write_aux: F) -> R +where + F: FnOnce(&[(&[u8], &[u8])]) -> R, +{ + let key = transaction_metadata_key(hash); + write_aux(&[(&key, &metadata.encode())]) +} diff --git a/frame/dvm/consensus/src/lib.rs b/frame/dvm/consensus/src/lib.rs new file mode 100644 index 0000000000..737ea24dfe --- /dev/null +++ b/frame/dvm/consensus/src/lib.rs @@ -0,0 +1,173 @@ +// This file is part of Frontier. + +// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +mod aux_schema; + +pub use crate::aux_schema::{load_block_hash, load_transaction_metadata}; + +use dvm_consensus_primitives::{ConsensusLog, FRONTIER_ENGINE_ID}; +use log::*; +use sc_client_api; +use sc_client_api::{backend::AuxStore, BlockOf}; +use sp_api::ProvideRuntimeApi; +use sp_block_builder::BlockBuilder as BlockBuilderApi; +use sp_blockchain::{well_known_cache_keys::Id as CacheKeyId, HeaderBackend, ProvideCache}; +use sp_consensus::{ + BlockCheckParams, BlockImport, BlockImportParams, Error as ConsensusError, ImportResult, +}; +use sp_runtime::generic::OpaqueDigestItemId; +use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; +use std::collections::HashMap; +use std::marker::PhantomData; +use std::sync::Arc; + +#[derive(derive_more::Display, Debug)] +pub enum Error { + #[display(fmt = "Multiple post-runtime Ethereum blocks, rejecting!")] + MultiplePostRuntimeLogs, + #[display(fmt = "Post-runtime Ethereum block not found, rejecting!")] + NoPostRuntimeLog, +} + +impl From for String { + fn from(error: Error) -> String { + error.to_string() + } +} + +impl std::convert::From for ConsensusError { + fn from(error: Error) -> ConsensusError { + ConsensusError::ClientImport(error.to_string()) + } +} + +pub struct FrontierBlockImport { + inner: I, + client: Arc, + enabled: bool, + _marker: PhantomData, +} + +impl, C> Clone for FrontierBlockImport { + fn clone(&self) -> Self { + FrontierBlockImport { + inner: self.inner.clone(), + client: self.client.clone(), + enabled: self.enabled, + _marker: PhantomData, + } + } +} + +impl FrontierBlockImport +where + B: BlockT, + I: BlockImport> + Send + Sync, + I::Error: Into, + C: ProvideRuntimeApi + Send + Sync + HeaderBackend + AuxStore + ProvideCache + BlockOf, + C::Api: BlockBuilderApi, +{ + pub fn new(inner: I, client: Arc, enabled: bool) -> Self { + Self { + inner, + client, + enabled, + _marker: PhantomData, + } + } +} + +impl BlockImport for FrontierBlockImport +where + B: BlockT, + I: BlockImport> + Send + Sync, + I::Error: Into, + C: ProvideRuntimeApi + Send + Sync + HeaderBackend + AuxStore + ProvideCache + BlockOf, + C::Api: BlockBuilderApi, +{ + type Error = ConsensusError; + type Transaction = sp_api::TransactionFor; + + fn check_block(&mut self, block: BlockCheckParams) -> Result { + self.inner.check_block(block).map_err(Into::into) + } + + fn import_block( + &mut self, + mut block: BlockImportParams, + new_cache: HashMap>, + ) -> Result { + macro_rules! insert_closure { + () => { + |insert| { + block + .auxiliary + .extend(insert.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec())))) + } + }; + } + + let client = self.client.clone(); + + if self.enabled { + let log = find_frontier_log::(&block.header)?; + let hash = block.post_hash(); + + match log { + ConsensusLog::EndBlock { + block_hash, + transaction_hashes, + } => { + aux_schema::write_block_hash( + client.as_ref(), + block_hash, + hash, + insert_closure!(), + ); + + for (index, transaction_hash) in transaction_hashes.into_iter().enumerate() { + aux_schema::write_transaction_metadata( + transaction_hash, + (block_hash, index as u32), + insert_closure!(), + ); + } + } + } + } + + self.inner + .import_block(block, new_cache) + .map_err(Into::into) + } +} + +fn find_frontier_log(header: &B::Header) -> Result { + let mut frontier_log: Option<_> = None; + for log in header.digest().logs() { + trace!(target: "dvm-consensus", "Checking log {:?}, looking for ethereum block.", log); + let log = log.try_to::(OpaqueDigestItemId::Consensus(&FRONTIER_ENGINE_ID)); + match (log, frontier_log.is_some()) { + (Some(_), true) => return Err(Error::MultiplePostRuntimeLogs), + (Some(log), false) => frontier_log = Some(log), + _ => trace!(target: "dvm-consensus", "Ignoring digest not meant for us"), + } + } + + Ok(frontier_log.ok_or(Error::NoPostRuntimeLog)?) +} diff --git a/frame/dvm/ethereum/Cargo.toml b/frame/dvm/ethereum/Cargo.toml new file mode 100644 index 0000000000..3c92f890bd --- /dev/null +++ b/frame/dvm/ethereum/Cargo.toml @@ -0,0 +1,77 @@ +[package] +authors = ["Darwinia Network "] +description = "Ethereum compatibility full block processing emulation pallet for Darwinia." +edition = "2018" +homepage = "https://darwinia.network/" +license = "GPL-3.0" +name = "dvm-ethereum" +readme = "README.md" +repository = "https://github.com/darwinia-network/darwinia-common/" +version = "1.2.2" + +[dependencies] +# crates +codec = { package = "parity-scale-codec", version = "1.3.5", default-features = false } +ethereum = { version = "0.3", default-features = false, features = ["codec"] } +ethereum-types = { version = "0.9", default-features = false } +evm = { version = "0.17.0", default-features = false } +libsecp256k1 = { version = "0.3", default-features = false } +rlp = { version = "0.4", default-features = false } +rustc-hex = { version = "2.1.0", default-features = false } +serde = { version = "1.0.117", optional = true } +sha3 = { version = "0.8", default-features = false } +# darwinia +darwinia-balances = { default-features = false, path = "../../../frame/balances" } +darwinia-support = { default-features = false, path = "../../../frame/support" } +dvm-consensus-primitives = { default-features = false, path = "../consensus/primitives" } +dvm-rpc-primitives = { default-features = false, path = "../rpc/primitives" } +# substrate +frame-support = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +frame-system = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +pallet-evm = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +pallet-timestamp = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-core = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-io = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-runtime = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-std = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } + +[dev-dependencies] +# crates +hex-literal = "0.3.1" + +[features] +default = ["std"] + +std = [ + "crates-std", + "darwinia-std", + "substrate-std", +] + +crates-std = [ + "codec/std", + "ethereum/std", + "ethereum-types/std", + "evm/std", + "libsecp256k1/std", + "rlp/std", + "rustc-hex/std", + "serde", + "sha3/std", +] +darwinia-std = [ + "darwinia-balances/std", + "darwinia-support/std", + "dvm-consensus-primitives/std", + "dvm-rpc-primitives/std", +] +substrate-std = [ + "frame-support/std", + "frame-system/std", + "pallet-evm/std", + "pallet-timestamp/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/frame/dvm/ethereum/src/lib.rs b/frame/dvm/ethereum/src/lib.rs new file mode 100644 index 0000000000..4fe66c9b30 --- /dev/null +++ b/frame/dvm/ethereum/src/lib.rs @@ -0,0 +1,464 @@ +// Copyright 2017-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +//! # Ethereum pallet +//! +//! The Ethereum pallet works together with EVM pallet to provide full emulation +//! for Ethereum block processing. + +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::Encode; +use dvm_consensus_primitives::{ConsensusLog, FRONTIER_ENGINE_ID}; +use ethereum_types::{Bloom, H160, H256, H64, U256}; +use evm::{ExitError, ExitFatal, ExitReason, ExitRevert}; +use frame_support::{ + decl_error, decl_event, decl_module, decl_storage, traits::FindAuthor, traits::Get, +}; +use frame_system::ensure_none; +use sha3::{Digest, Keccak256}; +use sp_runtime::{ + generic::DigestItem, + traits::UniqueSaturatedInto, + transaction_validity::{ + InvalidTransaction, TransactionSource, TransactionValidity, ValidTransaction, + }, + DispatchResult, +}; +use sp_std::prelude::*; + +pub use dvm_rpc_primitives::TransactionStatus; +pub use ethereum::{Block, Log, Receipt, Transaction, TransactionAction}; +use frame_support::traits::Currency; +use pallet_evm::AddressMapping; + +#[cfg(all(feature = "std", test))] +mod tests; + +#[cfg(all(feature = "std", test))] +mod mock; +// Precomile contract for dvm +pub mod precompiles; + +/// A type alias for the balance type from this pallet's point of view. +pub type BalanceOf = ::Balance; +type RingInstance = darwinia_balances::Instance0; + +/// Trait for Ethereum pallet. +pub trait Trait: + frame_system::Trait + + darwinia_balances::Trait + + pallet_timestamp::Trait + + pallet_evm::Trait +{ + /// The overarching event type. + type Event: From + Into<::Event>; + /// Find author for Ethereum. + type FindAuthor: FindAuthor; + type AddressMapping: AddressMapping; + type RingCurrency: Currency; +} + +decl_storage! { + trait Store for Module as Ethereum { + /// Current building block's transactions and receipts. + Pending: Vec<(ethereum::Transaction, TransactionStatus, ethereum::Receipt)>; + + /// The current Ethereum block. + CurrentBlock: Option; + /// The current Ethereum receipts. + CurrentReceipts: Option>; + /// The current transaction statuses. + CurrentTransactionStatuses: Option>; + } + add_extra_genesis { + build(|_config: &GenesisConfig| { + >::store_block(); + }); + } +} + +decl_event!( + /// Ethereum pallet events. + pub enum Event { + /// An ethereum transaction was successfully executed. [from, transaction_hash] + Executed(H160, H256), + } +); + +decl_error! { + /// Ethereum pallet errors. + pub enum Error for Module { + /// Signature is invalid. + InvalidSignature, + /// Transaction signed with wrong chain id + InvalidChainId, + /// Trying to pop from an empty stack. + StackUnderflow, + /// Trying to push into a stack over stack limit. + StackOverflow, + /// Jump destination is invalid. + InvalidJump, + /// An opcode accesses memory region, but the region is invalid. + InvalidRange, + /// Encountered the designated invalid opcode. + DesignatedInvalid, + /// Call stack is too deep (runtime). + CallTooDeep, + /// Create opcode encountered collision (runtime). + CreateCollision, + /// Create init code exceeds limit (runtime). + CreateContractLimit, + /// An opcode accesses external information, but the request is off offset + /// limit (runtime). + OutOfOffset, + /// Execution runs out of gas (runtime). + OutOfGas, + /// Not enough fund to start the execution (runtime). + OutOfFund, + /// PC underflowed (unused). + PCUnderflow, + /// Attempt to create an empty account (runtime, unused). + CreateEmpty, + /// Other normal errors. + ExitErrorOther, + /// The operation is not supported. + NotSupported, + /// The trap (interrupt) is unhandled. + UnhandledInterrupt, + /// The environment explictly set call errors as fatal error. + CallErrorAsFatal, + /// Other fatal errors. + ExitFatalOther, + /// Machine encountered an explict revert. + Reverted, + /// If call itself fails + FailedExecution + } +} + +decl_module! { + /// Ethereum pallet module. + pub struct Module for enum Call where origin: T::Origin { + /// Deposit one of this pallet's events by using the default implementation. + fn deposit_event() = default; + + /// Transact an Ethereum transaction. + #[weight = 0] + fn transact(origin, transaction: ethereum::Transaction) { + ensure_none(origin)?; + + let source = Self::recover_signer(&transaction).ok_or_else(|| Error::::InvalidSignature)?; + + let hash = transaction.message_hash(Some(T::ChainId::get())); + + Self::execute(source, transaction)?; + + Self::deposit_event(Event::Executed(source, hash)); + } + + fn on_finalize(n: T::BlockNumber) { + >::store_block(); + } + + fn on_initialize(n: T::BlockNumber) -> frame_support::weights::Weight { + Pending::kill(); + 0 + } + } +} + +#[repr(u8)] +enum TransactionValidationError { + #[allow(dead_code)] + UnknownError, + InvalidChainId, + InvalidSignature, +} + +impl frame_support::unsigned::ValidateUnsigned for Module { + type Call = Call; + + fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity { + if let Call::transact(transaction) = call { + if transaction.signature.chain_id().unwrap_or_default() != T::ChainId::get() { + return InvalidTransaction::Custom( + TransactionValidationError::InvalidChainId as u8, + ) + .into(); + } + + let origin = Self::recover_signer(&transaction).ok_or_else(|| { + InvalidTransaction::Custom(TransactionValidationError::InvalidSignature as u8) + })?; + + let account_data = pallet_evm::Module::::account_basic(&origin); + + if transaction.nonce < account_data.nonce { + return InvalidTransaction::Stale.into(); + } + + let fee = transaction.gas_price.saturating_mul(transaction.gas_limit); + + if account_data.balance < fee { + return InvalidTransaction::Payment.into(); + } + + let mut builder = ValidTransaction::with_tag_prefix("Ethereum") + .and_provides((&origin, transaction.nonce)); + + if transaction.nonce > account_data.nonce { + if let Some(prev_nonce) = transaction.nonce.checked_sub(1.into()) { + builder = builder.and_requires((origin, prev_nonce)) + } + } + + builder.build() + } else { + Err(InvalidTransaction::Call.into()) + } + } +} + +impl Module { + fn recover_signer(transaction: ðereum::Transaction) -> Option { + let mut sig = [0u8; 65]; + let mut msg = [0u8; 32]; + sig[0..32].copy_from_slice(&transaction.signature.r()[..]); + sig[32..64].copy_from_slice(&transaction.signature.s()[..]); + sig[64] = transaction.signature.standard_v(); + msg.copy_from_slice(&transaction.message_hash(Some(T::ChainId::get()))[..]); + + let pubkey = sp_io::crypto::secp256k1_ecdsa_recover(&sig, &msg).ok()?; + Some(H160::from(H256::from_slice( + Keccak256::digest(&pubkey).as_slice(), + ))) + } + + fn store_block() { + let mut transactions = Vec::new(); + let mut statuses = Vec::new(); + let mut receipts = Vec::new(); + for (transaction, status, receipt) in Pending::get() { + transactions.push(transaction); + statuses.push(status); + receipts.push(receipt); + } + + let ommers = Vec::::new(); + let partial_header = ethereum::PartialHeader { + parent_hash: Self::current_block_hash().unwrap_or_default(), + beneficiary: >::find_author(), + // TODO: figure out if there's better way to get a sort-of-valid state root. + state_root: H256::default(), + receipts_root: H256::from_slice( + Keccak256::digest(&rlp::encode_list(&receipts)[..]).as_slice(), + ), // TODO: check receipts hash. + logs_bloom: Bloom::default(), // TODO: gather the logs bloom from receipts. + difficulty: U256::zero(), + number: U256::from(UniqueSaturatedInto::::unique_saturated_into( + frame_system::Module::::block_number(), + )), + gas_limit: U256::zero(), // TODO: set this using Ethereum's gas limit change algorithm. + gas_used: receipts + .clone() + .into_iter() + .fold(U256::zero(), |acc, r| acc + r.used_gas), + timestamp: UniqueSaturatedInto::::unique_saturated_into( + pallet_timestamp::Module::::get(), + ), + extra_data: H256::default(), + mix_hash: H256::default(), + nonce: H64::default(), + }; + let block = ethereum::Block::new(partial_header, transactions.clone(), ommers); + + let mut transaction_hashes = Vec::new(); + + for t in &transactions { + let transaction_hash = H256::from_slice(Keccak256::digest(&rlp::encode(t)).as_slice()); + transaction_hashes.push(transaction_hash); + } + + CurrentBlock::put(block.clone()); + CurrentReceipts::put(receipts.clone()); + CurrentTransactionStatuses::put(statuses.clone()); + + let digest = DigestItem::::Consensus( + FRONTIER_ENGINE_ID, + ConsensusLog::EndBlock { + block_hash: block.header.hash(), + transaction_hashes, + } + .encode(), + ); + frame_system::Module::::deposit_log(digest.into()); + } + + /// Get the author using the FindAuthor trait. + pub fn find_author() -> H160 { + let digest = >::digest(); + let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime()); + + T::FindAuthor::find_author(pre_runtime_digests).unwrap_or_default() + } + + /// Get the transaction status with given index. + pub fn current_transaction_statuses() -> Option> { + CurrentTransactionStatuses::get() + } + + /// Get current block. + pub fn current_block() -> Option { + CurrentBlock::get() + } + + /// Get current block hash + pub fn current_block_hash() -> Option { + Self::current_block().map(|block| block.header.hash()) + } + + /// Get receipts by number. + pub fn current_receipts() -> Option> { + CurrentReceipts::get() + } + + /// Execute an Ethereum transaction, ignoring transaction signatures. + pub fn execute(source: H160, transaction: ethereum::Transaction) -> DispatchResult { + let transaction_hash = + H256::from_slice(Keccak256::digest(&rlp::encode(&transaction)).as_slice()); + let transaction_index = Pending::get().len() as u32; + + let (status, gas_used) = match transaction.action { + ethereum::TransactionAction::Call(target) => { + let (_, _, gas_used, logs) = + Self::handle_exec(pallet_evm::Module::::execute_call( + source, + target, + transaction.input.clone(), + transaction.value, + transaction.gas_limit.low_u32(), + transaction.gas_price, + Some(transaction.nonce), + true, + )?)?; + + ( + TransactionStatus { + transaction_hash, + transaction_index, + from: source, + to: Some(target), + contract_address: None, + logs: { + logs.into_iter() + .map(|log| Log { + address: log.address, + topics: log.topics, + data: log.data, + }) + .collect() + }, + logs_bloom: Bloom::default(), // TODO: feed in bloom. + }, + gas_used, + ) + } + ethereum::TransactionAction::Create => { + let (_, contract_address, gas_used, logs) = + Self::handle_exec(pallet_evm::Module::::execute_create( + source, + transaction.input.clone(), + transaction.value, + transaction.gas_limit.low_u32(), + transaction.gas_price, + Some(transaction.nonce), + true, + )?)?; + + ( + TransactionStatus { + transaction_hash, + transaction_index, + from: source, + to: None, + contract_address: Some(contract_address), + logs: { + logs.into_iter() + .map(|log| Log { + address: log.address, + topics: log.topics, + data: log.data, + }) + .collect() + }, + logs_bloom: Bloom::default(), // TODO: feed in bloom. + }, + gas_used, + ) + } + }; + + let receipt = ethereum::Receipt { + state_root: H256::default(), // TODO: should be okay / error status. + used_gas: gas_used, + logs_bloom: Bloom::default(), // TODO: set this. + logs: status.clone().logs, + }; + + Pending::append((transaction, status, receipt)); + + Ok(()) + } + + fn handle_exec( + res: (ExitReason, R, U256, Vec), + ) -> Result<(ExitReason, R, U256, Vec), Error> { + match res.0 { + ExitReason::Succeed(_s) => Ok(res), + ExitReason::Error(e) => Err(Self::parse_exit_error(e)), + ExitReason::Revert(e) => match e { + ExitRevert::Reverted => Err(Error::::Reverted), + }, + ExitReason::Fatal(e) => match e { + ExitFatal::NotSupported => Err(Error::::NotSupported), + ExitFatal::UnhandledInterrupt => Err(Error::::UnhandledInterrupt), + ExitFatal::CallErrorAsFatal(e_error) => Err(Self::parse_exit_error(e_error)), + ExitFatal::Other(_s) => Err(Error::::ExitFatalOther), + }, + } + } + + fn parse_exit_error(exit_error: ExitError) -> Error { + match exit_error { + ExitError::StackUnderflow => return Error::::StackUnderflow, + ExitError::StackOverflow => return Error::::StackOverflow, + ExitError::InvalidJump => return Error::::InvalidJump, + ExitError::InvalidRange => return Error::::InvalidRange, + ExitError::DesignatedInvalid => return Error::::DesignatedInvalid, + ExitError::CallTooDeep => return Error::::CallTooDeep, + ExitError::CreateCollision => return Error::::CreateCollision, + ExitError::CreateContractLimit => return Error::::CreateContractLimit, + ExitError::OutOfOffset => return Error::::OutOfOffset, + ExitError::OutOfGas => return Error::::OutOfGas, + ExitError::OutOfFund => return Error::::OutOfFund, + ExitError::PCUnderflow => return Error::::PCUnderflow, + ExitError::CreateEmpty => return Error::::CreateEmpty, + ExitError::Other(_s) => return Error::::ExitErrorOther, + } + } +} diff --git a/frame/dvm/ethereum/src/mock.rs b/frame/dvm/ethereum/src/mock.rs new file mode 100644 index 0000000000..5be2912267 --- /dev/null +++ b/frame/dvm/ethereum/src/mock.rs @@ -0,0 +1,290 @@ +// Copyright 2017-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +//! Test utilities + +use super::*; +use crate::{Module, Trait}; +use ethereum::{TransactionAction, TransactionSignature}; +use frame_support::{impl_outer_origin, parameter_types, weights::Weight, ConsensusEngineId}; +use pallet_evm::{AddressMapping, EnsureAddressTruncated, FeeCalculator}; +use rlp::*; +use sp_core::{H160, H256, U256}; +use sp_runtime::AccountId32; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, + ModuleId, Perbill, RuntimeDebug, +}; + +use codec::{Decode, Encode}; + +impl_outer_origin! { + pub enum Origin for Test where system = frame_system {} +} + +type Balance = u64; +darwinia_support::impl_account_data! { + struct AccountData + for + RingInstance, + KtonInstance + where + Balance = Balance + { + // other data + } +} + +// For testing the pallet, we construct most of a mock runtime. This means +// first constructing a configuration type (`Test`) which `impl`s each of the +// configuration traits of pallets we want to use. +#[derive(Clone, Eq, PartialEq)] +pub struct Test; +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: Weight = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); +} +impl frame_system::Trait for Test { + type BaseCallFilter = (); + type SystemWeightInfo = (); + type Origin = Origin; + type Call = (); + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId32; + type Lookup = IdentityLookup; + type Header = Header; + type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type DbWeight = (); + type BlockExecutionWeight = (); + type ExtrinsicBaseWeight = (); + type MaximumExtrinsicWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type AvailableBlockRatio = AvailableBlockRatio; + type Version = (); + type PalletInfo = (); + type AccountData = AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); +} + +parameter_types! { + // For weight estimation, we assume that the most locks on an individual account will be 50. + // This number may need to be adjusted in the future if this assumption no longer holds true. + pub const MaxLocks: u32 = 50; + pub const ExistentialDeposit: u64 = 500; +} + +type RingInstance = darwinia_balances::Instance0; +type KtonInstance = darwinia_balances::Instance1; +impl darwinia_balances::Trait for Test { + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type MaxLocks = (); + type OtherCurrencies = (); + type WeightInfo = (); + type Balance = Balance; + type Event = (); + type BalanceInfo = AccountData; +} + +parameter_types! { + pub const MinimumPeriod: u64 = 6000 / 2; +} + +impl pallet_timestamp::Trait for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +pub struct FixedGasPrice; +impl FeeCalculator for FixedGasPrice { + fn min_gas_price() -> U256 { + 1.into() + } +} + +pub struct EthereumFindAuthor; +impl FindAuthor for EthereumFindAuthor { + fn find_author<'a, I>(_digests: I) -> Option + where + I: 'a + IntoIterator, + { + Some(address_build(0).address) + } +} + +parameter_types! { + pub const TransactionByteFee: u64 = 1; + pub const ChainId: u64 = 42; + pub const EVMModuleId: ModuleId = ModuleId(*b"py/evmpa"); +} + +pub struct HashedAddressMapping; + +impl AddressMapping for HashedAddressMapping { + fn into_account_id(address: H160) -> AccountId32 { + let mut data = [0u8; 32]; + data[0..20].copy_from_slice(&address[..]); + AccountId32::from(Into::<[u8; 32]>::into(data)) + } +} + +impl pallet_evm::Trait for Test { + type FeeCalculator = FixedGasPrice; + type CallOrigin = EnsureAddressTruncated; + type WithdrawOrigin = EnsureAddressTruncated; + type AddressMapping = HashedAddressMapping; + type Currency = Balances; + type Event = (); + type Precompiles = (); + type ChainId = ChainId; +} + +impl Trait for Test { + type Event = (); + type FindAuthor = EthereumFindAuthor; + type AddressMapping = HashedAddressMapping; + type RingCurrency = Balances; +} + +pub type System = frame_system::Module; +pub type Balances = darwinia_balances::Module; +pub type Ethereum = Module; +pub type Evm = pallet_evm::Module; + +pub struct AccountInfo { + pub address: H160, + pub account_id: AccountId32, + pub private_key: H256, +} + +fn address_build(seed: u8) -> AccountInfo { + let private_key = H256::from_slice(&[(seed + 1) as u8; 32]); //H256::from_low_u64_be((i + 1) as u64); + let secret_key = secp256k1::SecretKey::parse_slice(&private_key[..]).unwrap(); + let public_key = &secp256k1::PublicKey::from_secret_key(&secret_key).serialize()[1..65]; + let address = H160::from(H256::from_slice(&Keccak256::digest(public_key)[..])); + + let mut data = [0u8; 32]; + data[0..20].copy_from_slice(&address[..]); + + AccountInfo { + private_key, + account_id: AccountId32::from(Into::<[u8; 32]>::into(data)), + address, + } +} + +// This function basically just builds a genesis storage key/value store according to +// our desired mockup. +pub fn new_test_ext(accounts_len: usize) -> (Vec, sp_io::TestExternalities) { + // sc_cli::init_logger(""); + let mut ext = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + let pairs = (0..accounts_len) + .map(|i| address_build(i as u8)) + .collect::>(); + + let balances: Vec<_> = (0..accounts_len) + .map(|i| (pairs[i].account_id.clone(), 10_000_000)) + .collect(); + + darwinia_balances::GenesisConfig:: { balances } + .assimilate_storage(&mut ext) + .unwrap(); + + (pairs, ext.into()) +} + +pub fn contract_address(sender: H160, nonce: u64) -> H160 { + let mut rlp = RlpStream::new_list(2); + rlp.append(&sender); + rlp.append(&nonce); + + H160::from_slice(&Keccak256::digest(rlp.out().as_slice())[12..]) +} + +pub fn storage_address(sender: H160, slot: H256) -> H256 { + H256::from_slice(&Keccak256::digest( + [&H256::from(sender)[..], &slot[..]].concat().as_slice(), + )) +} + +pub struct UnsignedTransaction { + pub nonce: U256, + pub gas_price: U256, + pub gas_limit: U256, + pub action: TransactionAction, + pub value: U256, + pub input: Vec, +} + +impl UnsignedTransaction { + fn signing_rlp_append(&self, s: &mut RlpStream) { + s.begin_list(9); + s.append(&self.nonce); + s.append(&self.gas_price); + s.append(&self.gas_limit); + s.append(&self.action); + s.append(&self.value); + s.append(&self.input); + s.append(&ChainId::get()); + s.append(&0u8); + s.append(&0u8); + } + + fn signing_hash(&self) -> H256 { + let mut stream = RlpStream::new(); + self.signing_rlp_append(&mut stream); + H256::from_slice(&Keccak256::digest(&stream.drain()).as_slice()) + } + + pub fn sign(&self, key: &H256) -> Transaction { + let hash = self.signing_hash(); + let msg = secp256k1::Message::parse(hash.as_fixed_bytes()); + let s = secp256k1::sign(&msg, &secp256k1::SecretKey::parse_slice(&key[..]).unwrap()); + let sig = s.0.serialize(); + + let sig = TransactionSignature::new( + s.1.serialize() as u64 % 2 + ChainId::get() * 2 + 35, + H256::from_slice(&sig[0..32]), + H256::from_slice(&sig[32..64]), + ) + .unwrap(); + + Transaction { + nonce: self.nonce, + gas_price: self.gas_price, + gas_limit: self.gas_limit, + action: self.action, + value: self.value, + input: self.input.clone(), + signature: sig, + } + } +} diff --git a/frame/dvm/ethereum/src/precompiles.rs b/frame/dvm/ethereum/src/precompiles.rs new file mode 100644 index 0000000000..a5d046175b --- /dev/null +++ b/frame/dvm/ethereum/src/precompiles.rs @@ -0,0 +1,242 @@ +pub use codec::Decode; +use frame_support::traits::ExistenceRequirement; +use pallet_evm::{AddressMapping, ExitError, ExitSucceed, Precompile}; +use sp_core::H160; +use sp_runtime::traits::{BlakeTwo256, Hash}; +use sp_std::marker::PhantomData; +use sp_std::prelude::*; + +use crate::*; +use sp_runtime::traits::UniqueSaturatedFrom; +use sp_runtime::AccountId32; + +pub struct ConcatAddressMapping; + +/// The ConcatAddressMapping used for transfer from evm 20-length to substrate 32-length address +/// The concat rule inclued three parts: +/// 1. AccountId Prefix: concat("dvm", "0x00000000000000"), length: 11 byetes +/// 2. Evm address: the original evm address, length: 20 bytes +/// 3. CheckSum: byte_xor(AccountId Prefix + Evm address), length: 1 bytes +impl AddressMapping for ConcatAddressMapping { + fn into_account_id(address: H160) -> AccountId32 { + let mut data = [0u8; 32]; + data[0..4].copy_from_slice(b"dvm:"); + data[11..31].copy_from_slice(&address[..]); + let checksum: u8 = data[1..31].iter().fold(data[0], |sum, &byte| sum ^ byte); + data[31] = checksum; + AccountId32::from(data) + } +} + +fn ensure_linear_cost( + target_gas: Option, + len: usize, + base: usize, + word: usize, +) -> Result { + let cost = base + .checked_add( + word.checked_mul(len.saturating_add(31) / 32) + .ok_or(ExitError::OutOfGas)?, + ) + .ok_or(ExitError::OutOfGas)?; + + if let Some(target_gas) = target_gas { + if cost > target_gas { + return Err(ExitError::OutOfGas); + } + } + + Ok(cost) +} + +/// Precompile for withdrawing from evm address +pub struct NativeTransfer(PhantomData); + +impl Precompile for NativeTransfer { + fn execute( + input: &[u8], + target_gas: Option, + ) -> core::result::Result<(ExitSucceed, Vec, usize), ExitError> { + if input.len() != 177 { + Err(ExitError::Other("InputDataLenErr")) + } else { + let cost = ensure_linear_cost(target_gas, input.len(), 60, 12)?; + + let params = decode_params(input); + // from account id + let from = + ::AddressMapping::into_account_id(H160::from_slice(¶ms.from)); + // to account id + let to = AccountId32::from(params.to); + let to = ::AccountId::decode(&mut to.as_ref()).unwrap(); + // value + let value = u128::from_be_bytes(params.value); + let value = <::RingCurrency as Currency>::Balance::unique_saturated_from(value); + + if params.is_valid_signature() { + let result = ::RingCurrency::transfer( + &from, + &to, + value, + ExistenceRequirement::KeepAlive, + ); + match result { + Ok(()) => Ok((ExitSucceed::Returned, vec![], cost)), + Err(error) => match error { + sp_runtime::DispatchError::BadOrigin => Err(ExitError::Other("BadOrigin")), + sp_runtime::DispatchError::CannotLookup => { + Err(ExitError::Other("CannotLookup")) + } + sp_runtime::DispatchError::Other(message) => Err(ExitError::Other(message)), + sp_runtime::DispatchError::Module { message, .. } => { + Err(ExitError::Other(message.unwrap())) + } + }, + } + } else { + Err(ExitError::Other("BadSignature")) + } + } + } +} + +struct Params { + from: [u8; 20], + to: [u8; 32], + value: [u8; 16], + signature: [u8; 65], +} + +impl Params { + pub fn is_valid_signature(&self) -> bool { + let mut data = [0u8; 68]; + data[0..20].copy_from_slice(&self.from); + data[20..52].copy_from_slice(&self.to); + data[52..68].copy_from_slice(&self.value); + let hash = BlakeTwo256::hash_of(&data); + + match recover_signer(self.signature, hash.to_fixed_bytes()) { + Some(signer) => signer[..] == self.from, + None => false, + } + } +} + +fn decode_params(input: &[u8]) -> Params { + let offset = 44; + + let mut from = [0u8; 20]; + from.copy_from_slice(&input[offset..offset + 20]); + + let mut to = [0u8; 32]; + to.copy_from_slice(&input[offset + 20..offset + 52]); + + let mut value = [0u8; 16]; + value.copy_from_slice(&input[offset + 52..offset + 68]); + + let mut signature = [0u8; 65]; + signature.copy_from_slice(&input[offset + 68..offset + 133]); + + Params { + from, + to, + value, + signature, + } +} + +fn recover_signer(sig: [u8; 65], msg: [u8; 32]) -> Option { + match sp_io::crypto::secp256k1_ecdsa_recover(&sig, &msg) { + Ok(pubkey) => Some(H160::from(H256::from_slice( + Keccak256::digest(&pubkey).as_slice(), + ))), + Err(_) => None, + } +} + +#[cfg(test)] +mod tests { + use crate::*; + use ethereum::TransactionSignature; + + #[cfg(test)] + fn sign(hash: H256, key: H256) -> [u8; 65] { + let msg = secp256k1::Message::parse(hash.as_fixed_bytes()); + let s = secp256k1::sign(&msg, &secp256k1::SecretKey::parse_slice(&key[..]).unwrap()); + let sig = s.0.serialize(); + let tran_sig = TransactionSignature::new( + s.1.serialize() as u64 % 2 + 42 * 2 + 35, + H256::from_slice(&sig[0..32]), + H256::from_slice(&sig[32..64]), + ) + .unwrap(); + + let mut res: [u8; 65] = [0u8; 65]; + res[0..32].copy_from_slice(&tran_sig.r()[..]); + res[32..64].copy_from_slice(&tran_sig.s()[..]); + res[64] = tran_sig.standard_v(); + res + } + + #[test] + fn test_concat_address_mapping() { + use crate::precompiles::ConcatAddressMapping; + // 0x182c00A789A7cC6BeA8fbc627121022D6029a416 + let evm_address = [ + 24u8, 44, 0, 167, 137, 167, 204, 107, 234, 143, 188, 98, 113, 33, 2, 45, 96, 41, 164, + 22, + ]; + + // same evm address's result should be equal + let account_id = ConcatAddressMapping::into_account_id(H160::from_slice(&evm_address)); + let account_id_2 = ConcatAddressMapping::into_account_id(H160::from_slice(&evm_address)); + assert_eq!(account_id, account_id_2); + + // the prefix should equal to the original evm address + let account_id: &[u8] = &account_id.as_ref(); + let account_id_part_1 = &account_id[11..31]; + assert_eq!(account_id_part_1, &evm_address); + } + + #[test] + fn test_signature() { + use super::{AccountId32, BlakeTwo256}; + use sp_runtime::traits::Hash; + + // from + let private_key = H256::from_slice(&[(8 + 1) as u8; 32]); + let secret_key = secp256k1::SecretKey::parse_slice(&private_key[..]).unwrap(); + let public_key = &secp256k1::PublicKey::from_secret_key(&secret_key).serialize()[1..65]; + let from = H160::from(H256::from_slice(&Keccak256::digest(public_key)[..])).0; + + // to + let account: AccountId32 = + hex_literal::hex!["d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"] + .into(); + let to: &[u8] = &account.as_ref(); + + // value = 10 + let value: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10]; + + // signature + let mut data = [0u8; 68]; + data[0..20].copy_from_slice(&from); + data[20..52].copy_from_slice(to); + data[52..68].copy_from_slice(&value); + let hash = BlakeTwo256::hash_of(&data); + let signature = sign(hash, private_key); + + let mut input = [0u8; 177]; + input[44..64].copy_from_slice(&from); + input[64..96].copy_from_slice(to); + input[96..112].copy_from_slice(&value); + input[112..177].copy_from_slice(&signature); + + let param = super::decode_params(&input); + assert_eq!(param.from, from); + assert_eq!(param.to, to); + assert_eq!(param.value, value); + assert_eq!(param.is_valid_signature(), true); + } +} diff --git a/frame/dvm/ethereum/src/tests.rs b/frame/dvm/ethereum/src/tests.rs new file mode 100644 index 0000000000..b97c07a39b --- /dev/null +++ b/frame/dvm/ethereum/src/tests.rs @@ -0,0 +1,222 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Consensus extension module tests for BABE consensus. + +use super::*; +use ethereum::TransactionSignature; +use frame_support::{assert_err, assert_noop, assert_ok, unsigned::ValidateUnsigned}; +use mock::*; +use rustc_hex::FromHex; +use sp_runtime::transaction_validity::{InvalidTransaction, TransactionSource}; +use std::str::FromStr; + +// This ERC-20 contract mints the maximum amount of tokens to the contract creator. +// pragma solidity ^0.5.0; +// import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.1/contracts/token/ERC20/ERC20.sol"; +// contract MyToken is ERC20 { +// constructor() public { _mint(msg.sender, 2**256 - 1); } +// } +const ERC20_CONTRACT_BYTECODE: &str = "608060405234801561001057600080fd5b50610041337fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61004660201b60201c565b610291565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156100e9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f45524332303a206d696e7420746f20746865207a65726f20616464726573730081525060200191505060405180910390fd5b6101028160025461020960201b610c7c1790919060201c565b60028190555061015d816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461020960201b610c7c1790919060201c565b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a35050565b600080828401905083811015610287576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b610e3a806102a06000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806370a082311161005b57806370a08231146101fd578063a457c2d714610255578063a9059cbb146102bb578063dd62ed3e1461032157610088565b8063095ea7b31461008d57806318160ddd146100f357806323b872dd146101115780633950935114610197575b600080fd5b6100d9600480360360408110156100a357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610399565b604051808215151515815260200191505060405180910390f35b6100fb6103b7565b6040518082815260200191505060405180910390f35b61017d6004803603606081101561012757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506103c1565b604051808215151515815260200191505060405180910390f35b6101e3600480360360408110156101ad57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061049a565b604051808215151515815260200191505060405180910390f35b61023f6004803603602081101561021357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061054d565b6040518082815260200191505060405180910390f35b6102a16004803603604081101561026b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610595565b604051808215151515815260200191505060405180910390f35b610307600480360360408110156102d157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610662565b604051808215151515815260200191505060405180910390f35b6103836004803603604081101561033757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610680565b6040518082815260200191505060405180910390f35b60006103ad6103a6610707565b848461070f565b6001905092915050565b6000600254905090565b60006103ce848484610906565b61048f846103da610707565b61048a85604051806060016040528060288152602001610d7060289139600160008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610440610707565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610bbc9092919063ffffffff16565b61070f565b600190509392505050565b60006105436104a7610707565b8461053e85600160006104b8610707565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610c7c90919063ffffffff16565b61070f565b6001905092915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60006106586105a2610707565b8461065385604051806060016040528060258152602001610de160259139600160006105cc610707565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610bbc9092919063ffffffff16565b61070f565b6001905092915050565b600061067661066f610707565b8484610906565b6001905092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610795576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526024815260200180610dbd6024913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561081b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180610d286022913960400191505060405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040518082815260200191505060405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561098c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526025815260200180610d986025913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610a12576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180610d056023913960400191505060405180910390fd5b610a7d81604051806060016040528060268152602001610d4a602691396000808773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610bbc9092919063ffffffff16565b6000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610b10816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610c7c90919063ffffffff16565b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a3505050565b6000838311158290610c69576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610c2e578082015181840152602081019050610c13565b50505050905090810190601f168015610c5b5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b600080828401905083811015610cfa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b809150509291505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa265627a7a72315820c7a5ffabf642bda14700b2de42f8c57b36621af020441df825de45fd2b3e1c5c64736f6c63430005100032"; + +fn default_erc20_creation_unsigned_transaction() -> UnsignedTransaction { + UnsignedTransaction { + nonce: U256::zero(), + gas_price: U256::from(1), + gas_limit: U256::from(0x100000), + action: ethereum::TransactionAction::Create, + value: U256::zero(), + input: FromHex::from_hex(ERC20_CONTRACT_BYTECODE).unwrap(), + } +} + +fn default_erc20_creation_transaction(account: &AccountInfo) -> Transaction { + default_erc20_creation_unsigned_transaction().sign(&account.private_key) +} + +#[test] +fn transaction_should_increment_nonce() { + let (pairs, mut ext) = new_test_ext(1); + let alice = &pairs[0]; + + ext.execute_with(|| { + assert_ok!(Ethereum::execute( + alice.address, + default_erc20_creation_transaction(alice), + )); + assert_eq!(Evm::account_basic(&alice.address).nonce, U256::from(1)); + }); +} + +#[test] +fn transaction_should_be_added_to_pending() { + let (pairs, mut ext) = new_test_ext(1); + let alice = &pairs[0]; + + ext.execute_with(|| { + let transaction = default_erc20_creation_transaction(alice); + assert_ok!(Ethereum::execute(alice.address, transaction.clone(),)); + assert_eq!(Pending::get().len(), 1); + assert_eq!(Pending::get()[0].0.input, transaction.input); + }); +} + +#[test] +fn transaction_without_enough_gas_should_not_work() { + let (pairs, mut ext) = new_test_ext(1); + let alice = &pairs[0]; + + ext.execute_with(|| { + let mut transaction = default_erc20_creation_transaction(alice); + transaction.gas_price = U256::from(11_000_000); + + assert_err!( + Ethereum::validate_unsigned(TransactionSource::External, &Call::transact(transaction)), + InvalidTransaction::Payment + ); + }); +} + +#[test] +fn transaction_with_invalid_nonce_should_not_work() { + let (pairs, mut ext) = new_test_ext(1); + let alice = &pairs[0]; + + ext.execute_with(|| { + // nonce is 0 + let mut transaction = default_erc20_creation_unsigned_transaction(); + transaction.nonce = U256::from(1); + + let signed = transaction.sign(&alice.private_key); + + assert_eq!( + Ethereum::validate_unsigned(TransactionSource::External, &Call::transact(signed)), + ValidTransaction::with_tag_prefix("Ethereum") + .and_provides((&alice.address, U256::from(1))) + .and_requires((&alice.address, U256::from(0))) + .build() + ); + + // nonce is 1 + assert_ok!(Ethereum::execute( + alice.address, + default_erc20_creation_transaction(alice), + )); + + transaction.nonce = U256::from(0); + + let signed2 = transaction.sign(&alice.private_key); + + assert_err!( + Ethereum::validate_unsigned(TransactionSource::External, &Call::transact(signed2)), + InvalidTransaction::Stale + ); + }); +} + +#[test] +fn contract_constructor_should_get_executed() { + let (pairs, mut ext) = new_test_ext(1); + let alice = &pairs[0]; + let erc20_address = contract_address(alice.address, 0); + let alice_storage_address = storage_address(alice.address, H256::zero()); + + ext.execute_with(|| { + assert_ok!(Ethereum::execute( + alice.address, + default_erc20_creation_transaction(alice), + )); + assert_eq!( + Evm::account_storages(erc20_address, alice_storage_address), + H256::from_str("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + .unwrap() + ) + }); +} + +#[test] +fn source_should_be_derived_from_signature() { + let (pairs, mut ext) = new_test_ext(1); + let alice = &pairs[0]; + + let erc20_address = contract_address(alice.address, 0); + let alice_storage_address = storage_address(alice.address, H256::zero()); + + ext.execute_with(|| { + Ethereum::transact(Origin::none(), default_erc20_creation_transaction(alice)) + .expect("Failed to execute transaction"); + + // We verify the transaction happened with alice account. + assert_eq!( + Evm::account_storages(erc20_address, alice_storage_address), + H256::from_str("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + .unwrap() + ) + }); +} + +#[test] +fn invalid_signature_should_be_ignored() { + let (pairs, mut ext) = new_test_ext(1); + let alice = &pairs[0]; + + let mut transaction = default_erc20_creation_transaction(alice); + transaction.signature = TransactionSignature::new( + 0x78, + H256::from_slice(&[55u8; 32]), + H256::from_slice(&[55u8; 32]), + ) + .unwrap(); + ext.execute_with(|| { + assert_noop!( + Ethereum::transact(Origin::none(), transaction,), + Error::::InvalidSignature + ); + }); +} + +#[test] +fn contract_should_be_created_at_given_address() { + let (pairs, mut ext) = new_test_ext(1); + let alice = &pairs[0]; + + let erc20_address = contract_address(alice.address, 0); + + ext.execute_with(|| { + assert_ok!(Ethereum::execute( + alice.address, + default_erc20_creation_transaction(alice), + )); + assert_ne!(Evm::account_codes(erc20_address).len(), 0); + }); +} + +#[test] +fn transaction_should_generate_correct_gas_used() { + let (pairs, mut ext) = new_test_ext(1); + let alice = &pairs[0]; + + let expected_gas = U256::from(891328); + + ext.execute_with(|| { + assert_ok!(Ethereum::execute( + alice.address, + default_erc20_creation_transaction(alice), + )); + assert_eq!(Pending::get()[0].2.used_gas, expected_gas); + }); +} diff --git a/frame/dvm/rpc/Cargo.toml b/frame/dvm/rpc/Cargo.toml new file mode 100644 index 0000000000..13fe055696 --- /dev/null +++ b/frame/dvm/rpc/Cargo.toml @@ -0,0 +1,39 @@ +[package] +authors = ["Darwinia Network "] +description = "Ethereum RPC (web3) compatibility layer for Darwinia." +edition = "2018" +homepage = "https://darwinia.network/" +license = "GPL-3.0" +name = "dvm-rpc" +readme = "README.md" +repository = "https://github.com/darwinia-network/darwinia-common/" +version = "1.2.2" + +[dependencies] +# crates +codec = { package = "parity-scale-codec", version = "1.3.5" } +ethereum = { version = "0.3", features = ["codec"] } +ethereum-types = "0.9.0" +futures = { version = "0.3.7", features = ["compat"] } +jsonrpc-core = "15.1.0" +jsonrpc-core-client = "15.1.0" +jsonrpc-derive = "15.1.0" +jsonrpc-pubsub = "15.1.0" +log = "0.4.11" +rlp = "0.4" +sha3 = "0.8" +# darwinia +dvm-consensus = { path = "../consensus" } +dvm-rpc-core = { path = "core" } +dvm-rpc-primitives = { path = "primitives" } +# substrate +sc-client-api = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sc-network = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sc-rpc = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sc-service = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-api = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-blockchain = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-io = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-runtime = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-storage = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-transaction-pool = { git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } diff --git a/frame/dvm/rpc/core/Cargo.toml b/frame/dvm/rpc/core/Cargo.toml new file mode 100644 index 0000000000..e1b19e464d --- /dev/null +++ b/frame/dvm/rpc/core/Cargo.toml @@ -0,0 +1,21 @@ +[package] +authors = ["Darwinia Network "] +description = "RPC traits of Ethereum." +edition = "2018" +homepage = "https://darwinia.network/" +license = "GPL-3.0" +name = "dvm-rpc-core" +readme = "README.md" +repository = "https://github.com/darwinia-network/darwinia-common/" +version = "1.2.2" + +[dependencies] +# crates +ethereum-types = "0.9.0" +jsonrpc-core = "15.1.0" +jsonrpc-core-client = "15.1.0" +jsonrpc-derive = "15.1.0" +jsonrpc-pubsub = "15.1.0" +rustc-hex = "2.1.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" diff --git a/frame/dvm/rpc/core/src/eth.rs b/frame/dvm/rpc/core/src/eth.rs new file mode 100644 index 0000000000..420089d86c --- /dev/null +++ b/frame/dvm/rpc/core/src/eth.rs @@ -0,0 +1,198 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Open 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. + +// Open 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 Open Ethereum. If not, see . + +//! Eth rpc interface. + +use ethereum_types::{H160, H256, H64, U256, U64}; +use jsonrpc_core::{BoxFuture, Result}; +use jsonrpc_derive::rpc; + +use crate::types::{ + BlockNumber, Bytes, CallRequest, Filter, FilterChanges, Index, Log, Receipt, RichBlock, + SyncStatus, Transaction, Work, +}; +pub use rpc_impl_EthApi::gen_server::EthApi as EthApiServer; + +/// Eth rpc interface. +#[rpc(server)] +pub trait EthApi { + /// Returns protocol version encoded as a string (quotes are necessary). + #[rpc(name = "eth_protocolVersion")] + fn protocol_version(&self) -> Result; + + /// Returns an object with data about the sync status or false. (wtf?) + #[rpc(name = "eth_syncing")] + fn syncing(&self) -> Result; + + /// Returns the number of hashes per second that the node is mining with. + #[rpc(name = "eth_hashrate")] + fn hashrate(&self) -> Result; + + /// Returns block author. + #[rpc(name = "eth_coinbase")] + fn author(&self) -> Result; + + /// Returns true if client is actively mining new blocks. + #[rpc(name = "eth_mining")] + fn is_mining(&self) -> Result; + + /// Returns the chain ID used for transaction signing at the + /// current best block. None is returned if not + /// available. + #[rpc(name = "eth_chainId")] + fn chain_id(&self) -> Result>; + + /// Returns current gas_price. + #[rpc(name = "eth_gasPrice")] + fn gas_price(&self) -> Result; + + /// Returns accounts list. + #[rpc(name = "eth_accounts")] + fn accounts(&self) -> Result>; + + /// Returns highest block number. + #[rpc(name = "eth_blockNumber")] + fn block_number(&self) -> Result; + + /// Returns balance of the given account. + #[rpc(name = "eth_getBalance")] + fn balance(&self, _: H160, _: Option) -> Result; + + /// Returns content of the storage at given address. + #[rpc(name = "eth_getStorageAt")] + fn storage_at(&self, _: H160, _: U256, _: Option) -> Result; + + /// Returns block with given hash. + #[rpc(name = "eth_getBlockByHash")] + fn block_by_hash(&self, _: H256, _: bool) -> Result>; + + /// Returns block with given number. + #[rpc(name = "eth_getBlockByNumber")] + fn block_by_number(&self, _: BlockNumber, _: bool) -> Result>; + + /// Returns the number of transactions sent from given address at given time (block number). + #[rpc(name = "eth_getTransactionCount")] + fn transaction_count(&self, _: H160, _: Option) -> Result; + + /// Returns the number of transactions in a block with given hash. + #[rpc(name = "eth_getBlockTransactionCountByHash")] + fn block_transaction_count_by_hash(&self, _: H256) -> Result>; + + /// Returns the number of transactions in a block with given block number. + #[rpc(name = "eth_getBlockTransactionCountByNumber")] + fn block_transaction_count_by_number(&self, _: BlockNumber) -> Result>; + + /// Returns the number of uncles in a block with given hash. + #[rpc(name = "eth_getUncleCountByBlockHash")] + fn block_uncles_count_by_hash(&self, _: H256) -> Result; + + /// Returns the number of uncles in a block with given block number. + #[rpc(name = "eth_getUncleCountByBlockNumber")] + fn block_uncles_count_by_number(&self, _: BlockNumber) -> Result; + + /// Returns the code at given address at given time (block number). + #[rpc(name = "eth_getCode")] + fn code_at(&self, _: H160, _: Option) -> Result; + + /// Sends signed transaction, returning its hash. + #[rpc(name = "eth_sendRawTransaction")] + fn send_raw_transaction(&self, _: Bytes) -> BoxFuture; + + /// Call contract, returning the output data. + #[rpc(name = "eth_call")] + fn call(&self, _: CallRequest, _: Option) -> Result; + + /// Estimate gas needed for execution of given contract. + #[rpc(name = "eth_estimateGas")] + fn estimate_gas(&self, _: CallRequest, _: Option) -> Result; + + /// Get transaction by its hash. + #[rpc(name = "eth_getTransactionByHash")] + fn transaction_by_hash(&self, _: H256) -> Result>; + + /// Returns transaction at given block hash and index. + #[rpc(name = "eth_getTransactionByBlockHashAndIndex")] + fn transaction_by_block_hash_and_index(&self, _: H256, _: Index) + -> Result>; + + /// Returns transaction by given block number and index. + #[rpc(name = "eth_getTransactionByBlockNumberAndIndex")] + fn transaction_by_block_number_and_index( + &self, + _: BlockNumber, + _: Index, + ) -> Result>; + + /// Returns transaction receipt by transaction hash. + #[rpc(name = "eth_getTransactionReceipt")] + fn transaction_receipt(&self, _: H256) -> Result>; + + /// Returns an uncles at given block and index. + #[rpc(name = "eth_getUncleByBlockHashAndIndex")] + fn uncle_by_block_hash_and_index(&self, _: H256, _: Index) -> Result>; + + /// Returns an uncles at given block and index. + #[rpc(name = "eth_getUncleByBlockNumberAndIndex")] + fn uncle_by_block_number_and_index( + &self, + _: BlockNumber, + _: Index, + ) -> Result>; + + /// Returns logs matching given filter object. + #[rpc(name = "eth_getLogs")] + fn logs(&self, _: Filter) -> Result>; + + /// Returns the hash of the current block, the seedHash, and the boundary condition to be met. + #[rpc(name = "eth_getWork")] + fn work(&self) -> Result; + + /// Used for submitting a proof-of-work solution. + #[rpc(name = "eth_submitWork")] + fn submit_work(&self, _: H64, _: H256, _: H256) -> Result; + + /// Used for submitting mining hashrate. + #[rpc(name = "eth_submitHashrate")] + fn submit_hashrate(&self, _: U256, _: H256) -> Result; +} + +/// Eth filters rpc api (polling). +#[rpc(server)] +pub trait EthFilterApi { + /// Returns id of new filter. + #[rpc(name = "eth_newFilter")] + fn new_filter(&self, _: Filter) -> Result; + + /// Returns id of new block filter. + #[rpc(name = "eth_newBlockFilter")] + fn new_block_filter(&self) -> Result; + + /// Returns id of new block filter. + #[rpc(name = "eth_newPendingTransactionFilter")] + fn new_pending_transaction_filter(&self) -> Result; + + /// Returns filter changes since last poll. + #[rpc(name = "eth_getFilterChanges")] + fn filter_changes(&self, _: Index) -> BoxFuture; + + /// Returns all logs matching given filter (in a range 'from' - 'to'). + #[rpc(name = "eth_getFilterLogs")] + fn filter_logs(&self, _: Index) -> BoxFuture>; + + /// Uninstalls filter. + #[rpc(name = "eth_uninstallFilter")] + fn uninstall_filter(&self, _: Index) -> Result; +} diff --git a/frame/dvm/rpc/core/src/eth_pubsub.rs b/frame/dvm/rpc/core/src/eth_pubsub.rs new file mode 100644 index 0000000000..10fae6a590 --- /dev/null +++ b/frame/dvm/rpc/core/src/eth_pubsub.rs @@ -0,0 +1,50 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Open 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. + +// Open 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 Open Ethereum. If not, see . + +//! Eth PUB-SUB rpc interface. + +use jsonrpc_core::Result; +use jsonrpc_derive::rpc; +use jsonrpc_pubsub::{typed, SubscriptionId}; + +use crate::types::pubsub; + +pub use rpc_impl_EthPubSubApi::gen_server::EthPubSubApi as EthPubSubApiServer; + +/// Eth PUB-SUB rpc interface. +#[rpc(server)] +pub trait EthPubSubApi { + /// RPC Metadata + type Metadata; + + /// Subscribe to Eth subscription. + #[pubsub(subscription = "eth_subscription", subscribe, name = "eth_subscribe")] + fn subscribe( + &self, + _: Self::Metadata, + _: typed::Subscriber, + _: pubsub::Kind, + _: Option, + ); + + /// Unsubscribe from existing Eth subscription. + #[pubsub( + subscription = "eth_subscription", + unsubscribe, + name = "eth_unsubscribe" + )] + fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; +} diff --git a/frame/dvm/rpc/core/src/eth_signing.rs b/frame/dvm/rpc/core/src/eth_signing.rs new file mode 100644 index 0000000000..f7ddf28891 --- /dev/null +++ b/frame/dvm/rpc/core/src/eth_signing.rs @@ -0,0 +1,50 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Open 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. + +// Open 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 Open Ethereum. If not, see . + +//! Eth rpc interface. + +use jsonrpc_core::BoxFuture; +use jsonrpc_derive::rpc; + +use crate::types::{Bytes, RichRawTransaction, TransactionRequest}; +use ethereum_types::{H160, H256, H520}; + +/// Signing methods implementation relying on unlocked accounts. +#[rpc(server)] +pub trait EthSigningApi { + /// RPC Metadata + type Metadata; + + /// Signs the hash of data with given address signature. + #[rpc(meta, name = "eth_sign")] + fn sign(&self, _: Self::Metadata, _: H160, _: Bytes) -> BoxFuture; + + /// Sends transaction; will block waiting for signer to return the + /// transaction hash. + /// If Signer is disable it will require the account to be unlocked. + #[rpc(meta, name = "eth_sendTransaction")] + fn send_transaction(&self, _: Self::Metadata, _: TransactionRequest) -> BoxFuture; + + /// Signs transactions without dispatching it to the network. + /// Returns signed transaction RLP representation and the transaction itself. + /// It can be later submitted using `eth_sendRawTransaction/eth_submitTransaction`. + #[rpc(meta, name = "eth_signTransaction")] + fn sign_transaction( + &self, + _: Self::Metadata, + _: TransactionRequest, + ) -> BoxFuture; +} diff --git a/frame/dvm/rpc/core/src/lib.rs b/frame/dvm/rpc/core/src/lib.rs new file mode 100644 index 0000000000..55d3f7c3e4 --- /dev/null +++ b/frame/dvm/rpc/core/src/lib.rs @@ -0,0 +1,29 @@ +// Copyright 2017-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +pub mod types; + +mod eth; +mod eth_pubsub; +mod eth_signing; +mod net; +mod web3; + +pub use eth::{EthApi, EthApiServer, EthFilterApi}; +pub use eth_pubsub::{EthPubSubApi, EthPubSubApiServer}; +pub use eth_signing::EthSigningApi; +pub use net::{NetApi, NetApiServer}; +pub use web3::Web3Api; diff --git a/frame/dvm/rpc/core/src/net.rs b/frame/dvm/rpc/core/src/net.rs new file mode 100644 index 0000000000..2622b4795b --- /dev/null +++ b/frame/dvm/rpc/core/src/net.rs @@ -0,0 +1,38 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Open 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. + +// Open 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 Open Ethereum. If not, see . + +//! Net rpc interface. +use jsonrpc_core::Result; +use jsonrpc_derive::rpc; + +pub use rpc_impl_NetApi::gen_server::NetApi as NetApiServer; + +/// Net rpc interface. +#[rpc(server)] +pub trait NetApi { + /// Returns protocol version. + #[rpc(name = "net_version")] + fn version(&self) -> Result; + + /// Returns number of peers connected to node. + #[rpc(name = "net_peerCount")] + fn peer_count(&self) -> Result; + + /// Returns true if client is actively listening for network connections. + /// Otherwise false. + #[rpc(name = "net_listening")] + fn is_listening(&self) -> Result; +} diff --git a/frame/dvm/rpc/core/src/types/account_info.rs b/frame/dvm/rpc/core/src/types/account_info.rs new file mode 100644 index 0000000000..2f5ae58f8d --- /dev/null +++ b/frame/dvm/rpc/core/src/types/account_info.rs @@ -0,0 +1,78 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Open 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. + +// Open 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 Open Ethereum. If not, see . + +//! Return types for RPC calls + +use crate::types::Bytes; +use ethereum_types::{Address, Public, H160, H256, U256}; +use serde::Serialize; + +/// Account information. +#[derive(Debug, Default, Clone, PartialEq, Serialize)] +pub struct AccountInfo { + /// Account name + pub name: String, +} + +/// Data structure with proof for one single storage-entry +#[derive(Debug, Default, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct StorageProof { + pub key: U256, + pub value: U256, + pub proof: Vec, +} + +/// Account information. +#[derive(Debug, Default, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct EthAccount { + pub address: H160, + pub balance: U256, + pub nonce: U256, + pub code_hash: H256, + pub storage_hash: H256, + pub account_proof: Vec, + pub storage_proof: Vec, +} + +/// Extended account information (used by `parity_allAccountInfo`). +#[derive(Debug, Default, Clone, PartialEq, Serialize)] +pub struct ExtAccountInfo { + /// Account name + pub name: String, + /// Account meta JSON + pub meta: String, + /// Account UUID (`None` for address book entries) + #[serde(skip_serializing_if = "Option::is_none")] + pub uuid: Option, +} + +/// account derived from a signature +/// as well as information that tells if it is valid for +/// the current chain +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RecoveredAccount { + /// address of the recovered account + pub address: Address, + /// public key of the recovered account + pub public_key: Public, + /// If the signature contains chain replay protection, + /// And the chain_id encoded within the signature + /// matches the current chain this would be true, otherwise false. + pub is_valid_for_current_chain: bool, +} diff --git a/frame/dvm/rpc/core/src/types/block.rs b/frame/dvm/rpc/core/src/types/block.rs new file mode 100644 index 0000000000..39033aaa62 --- /dev/null +++ b/frame/dvm/rpc/core/src/types/block.rs @@ -0,0 +1,177 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Open 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. + +// Open 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 Open Ethereum. If not, see . + +use std::collections::BTreeMap; +use std::ops::Deref; + +use crate::types::{Bytes, Transaction}; +use ethereum_types::{Bloom as H2048, H160, H256, U256}; +use serde::ser::Error; +use serde::{Serialize, Serializer}; + +/// Block Transactions +#[derive(Debug)] +pub enum BlockTransactions { + /// Only hashes + Hashes(Vec), + /// Full transactions + Full(Vec), +} + +impl Serialize for BlockTransactions { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + BlockTransactions::Hashes(ref hashes) => hashes.serialize(serializer), + BlockTransactions::Full(ref ts) => ts.serialize(serializer), + } + } +} + +/// Block representation +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Block { + /// Hash of the block + pub hash: Option, + /// Hash of the parent + pub parent_hash: H256, + /// Hash of the uncles + #[serde(rename = "sha3Uncles")] + pub uncles_hash: H256, + /// Authors address + pub author: H160, + /// Alias of `author` + pub miner: H160, + /// State root hash + pub state_root: H256, + /// Transactions root hash + pub transactions_root: H256, + /// Transactions receipts root hash + pub receipts_root: H256, + /// Block number + pub number: Option, + /// Gas Used + pub gas_used: U256, + /// Gas Limit + pub gas_limit: U256, + /// Extra data + pub extra_data: Bytes, + /// Logs bloom + pub logs_bloom: Option, + /// Timestamp + pub timestamp: U256, + /// Difficulty + pub difficulty: U256, + /// Total difficulty + pub total_difficulty: Option, + /// Seal fields + pub seal_fields: Vec, + /// Uncles' hashes + pub uncles: Vec, + /// Transactions + pub transactions: BlockTransactions, + /// Size in bytes + pub size: Option, +} + +/// Block header representation. +#[derive(Debug, Clone, Serialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct Header { + /// Hash of the block + pub hash: Option, + /// Hash of the parent + pub parent_hash: H256, + /// Hash of the uncles + #[serde(rename = "sha3Uncles")] + pub uncles_hash: H256, + /// Authors address + pub author: H160, + /// Alias of `author` + pub miner: H160, + /// State root hash + pub state_root: H256, + /// Transactions root hash + pub transactions_root: H256, + /// Transactions receipts root hash + pub receipts_root: H256, + /// Block number + pub number: Option, + /// Gas Used + pub gas_used: U256, + /// Gas Limit + pub gas_limit: U256, + /// Extra data + pub extra_data: Bytes, + /// Logs bloom + pub logs_bloom: H2048, + /// Timestamp + pub timestamp: U256, + /// Difficulty + pub difficulty: U256, + /// Seal fields + pub seal_fields: Vec, + /// Size in bytes + pub size: Option, +} + +/// Block representation with additional info. +pub type RichBlock = Rich; + +/// Header representation with additional info. +pub type RichHeader = Rich
; + +/// Value representation with additional info +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Rich { + /// Standard value. + pub inner: T, + /// Engine-specific fields with additional description. + /// Should be included directly to serialized block object. + // TODO [ToDr] #[serde(skip_serializing)] + pub extra_info: BTreeMap, +} + +impl Deref for Rich { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl Serialize for Rich { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use serde_json::{to_value, Value}; + + let serialized = (to_value(&self.inner), to_value(&self.extra_info)); + if let (Ok(Value::Object(mut value)), Ok(Value::Object(extras))) = serialized { + // join two objects + value.extend(extras); + // and serialize + value.serialize(serializer) + } else { + Err(S::Error::custom( + "Unserializable structures: expected objects", + )) + } + } +} diff --git a/frame/dvm/rpc/core/src/types/block_number.rs b/frame/dvm/rpc/core/src/types/block_number.rs new file mode 100644 index 0000000000..827abf851d --- /dev/null +++ b/frame/dvm/rpc/core/src/types/block_number.rs @@ -0,0 +1,176 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Open 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. + +// Open 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 Open Ethereum. If not, see . + +use ethereum_types::H256; +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt; + +/// Represents rpc api block number param. +#[derive(Debug, PartialEq, Clone, Hash, Eq)] +pub enum BlockNumber { + /// Hash + Hash { + /// block hash + hash: H256, + /// only return blocks part of the canon chain + require_canonical: bool, + }, + /// Number + Num(u64), + /// Latest block + Latest, + /// Earliest block (genesis) + Earliest, + /// Pending block (being mined) + Pending, +} + +impl Default for BlockNumber { + fn default() -> Self { + BlockNumber::Latest + } +} + +impl<'a> Deserialize<'a> for BlockNumber { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + deserializer.deserialize_any(BlockNumberVisitor) + } +} + +impl BlockNumber { + /// Convert block number to min block target. + pub fn to_min_block_num(&self) -> Option { + match *self { + BlockNumber::Num(ref x) => Some(*x), + _ => None, + } + } +} + +impl Serialize for BlockNumber { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + BlockNumber::Hash { + hash, + require_canonical, + } => serializer.serialize_str(&format!( + "{{ 'hash': '{}', 'requireCanonical': '{}' }}", + hash, require_canonical + )), + BlockNumber::Num(ref x) => serializer.serialize_str(&format!("0x{:x}", x)), + BlockNumber::Latest => serializer.serialize_str("latest"), + BlockNumber::Earliest => serializer.serialize_str("earliest"), + BlockNumber::Pending => serializer.serialize_str("pending"), + } + } +} + +struct BlockNumberVisitor; + +impl<'a> Visitor<'a> for BlockNumberVisitor { + type Value = BlockNumber; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!( + formatter, + "a block number or 'latest', 'earliest' or 'pending'" + ) + } + + fn visit_map(self, mut visitor: V) -> Result + where + V: MapAccess<'a>, + { + let (mut require_canonical, mut block_number, mut block_hash) = + (false, None::, None::); + + loop { + let key_str: Option = visitor.next_key()?; + + match key_str { + Some(key) => match key.as_str() { + "blockNumber" => { + let value: String = visitor.next_value()?; + if value.starts_with("0x") { + let number = u64::from_str_radix(&value[2..], 16).map_err(|e| { + Error::custom(format!("Invalid block number: {}", e)) + })?; + + block_number = Some(number); + break; + } else { + return Err(Error::custom( + "Invalid block number: missing 0x prefix".to_string(), + )); + } + } + "blockHash" => { + block_hash = Some(visitor.next_value()?); + } + "requireCanonical" => { + require_canonical = visitor.next_value()?; + } + key => return Err(Error::custom(format!("Unknown key: {}", key))), + }, + None => break, + }; + } + + if let Some(number) = block_number { + return Ok(BlockNumber::Num(number)); + } + + if let Some(hash) = block_hash { + return Ok(BlockNumber::Hash { + hash, + require_canonical, + }); + } + + return Err(Error::custom("Invalid input")); + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + match value { + "latest" => Ok(BlockNumber::Latest), + "earliest" => Ok(BlockNumber::Earliest), + "pending" => Ok(BlockNumber::Pending), + _ if value.starts_with("0x") => u64::from_str_radix(&value[2..], 16) + .map(BlockNumber::Num) + .map_err(|e| Error::custom(format!("Invalid block number: {}", e))), + _ => Err(Error::custom( + "Invalid block number: missing 0x prefix".to_string(), + )), + } + } + + fn visit_string(self, value: String) -> Result + where + E: Error, + { + self.visit_str(value.as_ref()) + } +} diff --git a/frame/dvm/rpc/core/src/types/bytes.rs b/frame/dvm/rpc/core/src/types/bytes.rs new file mode 100644 index 0000000000..c445460f9b --- /dev/null +++ b/frame/dvm/rpc/core/src/types/bytes.rs @@ -0,0 +1,134 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Open 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. + +// Open 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 Open Ethereum. If not, see . + +//! Serializable wrapper around vector of bytes + +use rustc_hex::{FromHex, ToHex}; +use serde::de::{Error, Visitor}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt; + +/// Wrapper structure around vector of bytes. +#[derive(Debug, PartialEq, Eq, Default, Hash, Clone)] +pub struct Bytes(pub Vec); + +impl Bytes { + /// Simple constructor. + pub fn new(bytes: Vec) -> Bytes { + Bytes(bytes) + } + /// Convert back to vector + pub fn into_vec(self) -> Vec { + self.0 + } +} + +impl From> for Bytes { + fn from(bytes: Vec) -> Bytes { + Bytes(bytes) + } +} + +impl Into> for Bytes { + fn into(self) -> Vec { + self.0 + } +} + +impl Serialize for Bytes { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut serialized = "0x".to_owned(); + serialized.push_str(self.0.to_hex::().as_ref()); + serializer.serialize_str(serialized.as_ref()) + } +} + +impl<'a> Deserialize<'a> for Bytes { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + deserializer.deserialize_any(BytesVisitor) + } +} + +struct BytesVisitor; + +impl<'a> Visitor<'a> for BytesVisitor { + type Value = Bytes; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a 0x-prefixed, hex-encoded vector of bytes") + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + if value.len() >= 2 && value.starts_with("0x") && value.len() & 1 == 0 { + Ok(Bytes::new(FromHex::from_hex(&value[2..]).map_err(|e| { + Error::custom(format!("Invalid hex: {}", e)) + })?)) + } else { + Err(Error::custom( + "Invalid bytes format. Expected a 0x-prefixed hex string with even length", + )) + } + } + + fn visit_string(self, value: String) -> Result + where + E: Error, + { + self.visit_str(value.as_ref()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use rustc_hex::FromHex; + + #[test] + fn test_bytes_serialize() { + let bytes = Bytes("0123456789abcdef".from_hex().unwrap()); + let serialized = serde_json::to_string(&bytes).unwrap(); + assert_eq!(serialized, r#""0x0123456789abcdef""#); + } + + #[test] + fn test_bytes_deserialize() { + let bytes0: Result = serde_json::from_str(r#""∀∂""#); + let bytes1: Result = serde_json::from_str(r#""""#); + let bytes2: Result = serde_json::from_str(r#""0x123""#); + let bytes3: Result = serde_json::from_str(r#""0xgg""#); + + let bytes4: Bytes = serde_json::from_str(r#""0x""#).unwrap(); + let bytes5: Bytes = serde_json::from_str(r#""0x12""#).unwrap(); + let bytes6: Bytes = serde_json::from_str(r#""0x0123""#).unwrap(); + + assert!(bytes0.is_err()); + assert!(bytes1.is_err()); + assert!(bytes2.is_err()); + assert!(bytes3.is_err()); + assert_eq!(bytes4, Bytes(vec![])); + assert_eq!(bytes5, Bytes(vec![0x12])); + assert_eq!(bytes6, Bytes(vec![0x1, 0x23])); + } +} diff --git a/frame/dvm/rpc/core/src/types/call_request.rs b/frame/dvm/rpc/core/src/types/call_request.rs new file mode 100644 index 0000000000..fb47c6915e --- /dev/null +++ b/frame/dvm/rpc/core/src/types/call_request.rs @@ -0,0 +1,40 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Open 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. + +// Open 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 Open Ethereum. If not, see . + +use crate::types::Bytes; +use ethereum_types::{H160, U256}; +use serde::Deserialize; + +/// Call request +#[derive(Debug, Default, PartialEq, Deserialize)] +#[serde(deny_unknown_fields)] +#[serde(rename_all = "camelCase")] +pub struct CallRequest { + /// From + pub from: Option, + /// To + pub to: Option, + /// Gas Price + pub gas_price: Option, + /// Gas + pub gas: Option, + /// Value + pub value: Option, + /// Data + pub data: Option, + /// Nonce + pub nonce: Option, +} diff --git a/frame/dvm/rpc/core/src/types/filter.rs b/frame/dvm/rpc/core/src/types/filter.rs new file mode 100644 index 0000000000..3773b562a9 --- /dev/null +++ b/frame/dvm/rpc/core/src/types/filter.rs @@ -0,0 +1,103 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Open 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. + +// Open 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 Open Ethereum. If not, see . + +use ethereum_types::{H160, H256}; +use serde::de::{DeserializeOwned, Error}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde_json::{from_value, Value}; + +use crate::types::{BlockNumber, Log}; + +/// Variadic value +#[derive(Debug, PartialEq, Eq, Clone, Hash)] +pub enum VariadicValue +where + T: DeserializeOwned, +{ + /// Single + Single(T), + /// List + Multiple(Vec), + /// None + Null, +} + +impl<'a, T> Deserialize<'a> for VariadicValue +where + T: DeserializeOwned, +{ + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'a>, + { + let v: Value = Deserialize::deserialize(deserializer)?; + + if v.is_null() { + return Ok(VariadicValue::Null); + } + + from_value(v.clone()) + .map(VariadicValue::Single) + .or_else(|_| from_value(v).map(VariadicValue::Multiple)) + .map_err(|err| D::Error::custom(format!("Invalid variadic value type: {}", err))) + } +} + +/// Filter Address +pub type FilterAddress = VariadicValue; +/// Topic +pub type Topic = VariadicValue; + +/// Filter +#[derive(Debug, PartialEq, Clone, Deserialize, Eq, Hash)] +#[serde(deny_unknown_fields)] +#[serde(rename_all = "camelCase")] +pub struct Filter { + /// From Block + pub from_block: Option, + /// To Block + pub to_block: Option, + /// Block hash + pub block_hash: Option, + /// Address + pub address: Option, + /// Topics + pub topics: Option, +} + +/// Results of the filter_changes RPC. +#[derive(Debug, PartialEq)] +pub enum FilterChanges { + /// New logs. + Logs(Vec), + /// New hashes (block or transactions) + Hashes(Vec), + /// Empty result, + Empty, +} + +impl Serialize for FilterChanges { + fn serialize(&self, s: S) -> Result + where + S: Serializer, + { + match *self { + FilterChanges::Logs(ref logs) => logs.serialize(s), + FilterChanges::Hashes(ref hashes) => hashes.serialize(s), + FilterChanges::Empty => (&[] as &[Value]).serialize(s), + } + } +} diff --git a/frame/dvm/rpc/core/src/types/index.rs b/frame/dvm/rpc/core/src/types/index.rs new file mode 100644 index 0000000000..f3744811ee --- /dev/null +++ b/frame/dvm/rpc/core/src/types/index.rs @@ -0,0 +1,84 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Open 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. + +// Open 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 Open Ethereum. If not, see . + +use serde::de::{Error, Visitor}; +use serde::{Deserialize, Deserializer}; +use std::fmt; + +/// Represents usize. +#[derive(Debug, PartialEq)] +pub struct Index(usize); + +impl Index { + /// Convert to usize + pub fn value(&self) -> usize { + self.0 + } +} + +impl<'a> Deserialize<'a> for Index { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + deserializer.deserialize_any(IndexVisitor) + } +} + +struct IndexVisitor; + +impl<'a> Visitor<'a> for IndexVisitor { + type Value = Index; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a hex-encoded or decimal index") + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + match value { + _ if value.starts_with("0x") => usize::from_str_radix(&value[2..], 16) + .map(Index) + .map_err(|e| Error::custom(format!("Invalid index: {}", e))), + _ => value + .parse::() + .map(Index) + .map_err(|e| Error::custom(format!("Invalid index: {}", e))), + } + } + + fn visit_string(self, value: String) -> Result + where + E: Error, + { + self.visit_str(value.as_ref()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json; + + #[test] + fn block_number_deserialization() { + let s = r#"["0xa", "10"]"#; + let deserialized: Vec = serde_json::from_str(s).unwrap(); + assert_eq!(deserialized, vec![Index(10), Index(10)]); + } +} diff --git a/frame/dvm/rpc/core/src/types/log.rs b/frame/dvm/rpc/core/src/types/log.rs new file mode 100644 index 0000000000..b0005aff48 --- /dev/null +++ b/frame/dvm/rpc/core/src/types/log.rs @@ -0,0 +1,46 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Open 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. + +// Open 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 Open Ethereum. If not, see . + +use crate::types::Bytes; +use ethereum_types::{H160, H256, U256}; +use serde::Serialize; + +/// Log +#[derive(Debug, Serialize, PartialEq, Eq, Hash, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Log { + /// H160 + pub address: H160, + /// Topics + pub topics: Vec, + /// Data + pub data: Bytes, + /// Block Hash + pub block_hash: Option, + /// Block Number + pub block_number: Option, + /// Transaction Hash + pub transaction_hash: Option, + /// Transaction Index + pub transaction_index: Option, + /// Log Index in Block + pub log_index: Option, + /// Log Index in Transaction + pub transaction_log_index: Option, + /// Whether Log Type is Removed (Geth Compatibility Field) + #[serde(default)] + pub removed: bool, +} diff --git a/frame/dvm/rpc/core/src/types/mod.rs b/frame/dvm/rpc/core/src/types/mod.rs new file mode 100644 index 0000000000..1a03bd24d5 --- /dev/null +++ b/frame/dvm/rpc/core/src/types/mod.rs @@ -0,0 +1,54 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Open 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. + +// Open 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 Open Ethereum. If not, see . + +//! RPC types + +mod account_info; +mod block; +mod block_number; +mod bytes; +mod call_request; +mod filter; +mod index; +mod log; +mod receipt; +mod sync; +mod transaction; +mod transaction_condition; +mod transaction_request; +mod work; + +pub mod pubsub; + +pub use self::account_info::{ + AccountInfo, EthAccount, ExtAccountInfo, RecoveredAccount, StorageProof, +}; +pub use self::block::{Block, BlockTransactions, Header, Rich, RichBlock, RichHeader}; +pub use self::block_number::BlockNumber; +pub use self::bytes::Bytes; +pub use self::call_request::CallRequest; +pub use self::filter::{Filter, FilterAddress, FilterChanges, Topic, VariadicValue}; +pub use self::index::Index; +pub use self::log::Log; +pub use self::receipt::Receipt; +pub use self::sync::{ + ChainStatus, EthProtocolInfo, PeerInfo, PeerNetworkInfo, PeerProtocolsInfo, Peers, + PipProtocolInfo, SyncInfo, SyncStatus, TransactionStats, +}; +pub use self::transaction::{LocalTransactionStatus, RichRawTransaction, Transaction}; +pub use self::transaction_condition::TransactionCondition; +pub use self::transaction_request::TransactionRequest; +pub use self::work::Work; diff --git a/frame/dvm/rpc/core/src/types/pubsub.rs b/frame/dvm/rpc/core/src/types/pubsub.rs new file mode 100644 index 0000000000..c900e0eb59 --- /dev/null +++ b/frame/dvm/rpc/core/src/types/pubsub.rs @@ -0,0 +1,105 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Open 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. + +// Open 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 Open Ethereum. If not, see . + +//! Pub-Sub types. + +use crate::types::{Filter, Log, RichHeader}; +use ethereum_types::H256; +use serde::de::Error; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde_json::{from_value, Value}; + +/// Subscription result. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Result { + /// New block header. + Header(Box), + /// Log + Log(Box), + /// Transaction hash + TransactionHash(H256), + /// SyncStatus + SyncState(PubSubSyncStatus), +} + +/// PubSbub sync status +#[derive(Debug, Serialize, Eq, PartialEq, Clone)] +#[serde(rename_all = "camelCase")] +pub struct PubSubSyncStatus { + /// is_major_syncing? + pub syncing: bool, +} + +impl Serialize for Result { + fn serialize(&self, serializer: S) -> ::std::result::Result + where + S: Serializer, + { + match *self { + Result::Header(ref header) => header.serialize(serializer), + Result::Log(ref log) => log.serialize(serializer), + Result::TransactionHash(ref hash) => hash.serialize(serializer), + Result::SyncState(ref sync) => sync.serialize(serializer), + } + } +} + +/// Subscription kind. +#[derive(Debug, Deserialize, PartialEq, Eq, Hash, Clone)] +#[serde(deny_unknown_fields)] +#[serde(rename_all = "camelCase")] +pub enum Kind { + /// New block headers subscription. + NewHeads, + /// Logs subscription. + Logs, + /// New Pending Transactions subscription. + NewPendingTransactions, + /// Node syncing status subscription. + Syncing, +} + +/// Subscription kind. +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub enum Params { + /// No parameters passed. + None, + /// Log parameters. + Logs(Filter), +} + +impl Default for Params { + fn default() -> Self { + Params::None + } +} + +impl<'a> Deserialize<'a> for Params { + fn deserialize(deserializer: D) -> ::std::result::Result + where + D: Deserializer<'a>, + { + let v: Value = Deserialize::deserialize(deserializer)?; + + if v.is_null() { + return Ok(Params::None); + } + + from_value(v.clone()) + .map(Params::Logs) + .map_err(|e| D::Error::custom(format!("Invalid Pub-Sub parameters: {}", e))) + } +} diff --git a/frame/dvm/rpc/core/src/types/receipt.rs b/frame/dvm/rpc/core/src/types/receipt.rs new file mode 100644 index 0000000000..85bfed6869 --- /dev/null +++ b/frame/dvm/rpc/core/src/types/receipt.rs @@ -0,0 +1,55 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Open 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. + +// Open 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 Open Ethereum. If not, see . + +use crate::types::Log; +use ethereum_types::{Bloom as H2048, H160, H256, U256, U64}; +use serde::Serialize; + +/// Receipt +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Receipt { + /// Transaction Hash + pub transaction_hash: Option, + /// Transaction index + pub transaction_index: Option, + /// Block hash + pub block_hash: Option, + /// Sender + pub from: Option, + /// Recipient + pub to: Option, + /// Block number + pub block_number: Option, + /// Cumulative gas used + pub cumulative_gas_used: U256, + /// Gas used + pub gas_used: Option, + /// Contract address + pub contract_address: Option, + /// Logs + pub logs: Vec, + /// State Root + // NOTE(niklasad1): EIP98 makes this optional field, if it's missing then skip serializing it + #[serde(skip_serializing_if = "Option::is_none", rename = "root")] + pub state_root: Option, + /// Logs bloom + pub logs_bloom: H2048, + /// Status code + // NOTE(niklasad1): Unknown after EIP98 rules, if it's missing then skip serializing it + #[serde(skip_serializing_if = "Option::is_none", rename = "status")] + pub status_code: Option, +} diff --git a/frame/dvm/rpc/core/src/types/sync.rs b/frame/dvm/rpc/core/src/types/sync.rs new file mode 100644 index 0000000000..9ea380e7c8 --- /dev/null +++ b/frame/dvm/rpc/core/src/types/sync.rs @@ -0,0 +1,144 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Open 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. + +// Open 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 Open Ethereum. If not, see . + +use std::collections::BTreeMap; + +use ethereum_types::{H512, U256}; +use serde::{Serialize, Serializer}; + +/// Sync info +#[derive(Default, Debug, Serialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct SyncInfo { + /// Starting block + pub starting_block: U256, + /// Current block + pub current_block: U256, + /// Highest block seen so far + pub highest_block: U256, + /// Warp sync snapshot chunks total. + pub warp_chunks_amount: Option, + /// Warp sync snpashot chunks processed. + pub warp_chunks_processed: Option, +} + +/// Peers info +#[derive(Default, Debug, Serialize)] +pub struct Peers { + /// Number of active peers + pub active: usize, + /// Number of connected peers + pub connected: usize, + /// Max number of peers + pub max: u32, + /// Detailed information on peers + pub peers: Vec, +} + +/// Peer connection information +#[derive(Default, Debug, Serialize)] +pub struct PeerInfo { + /// Public node id + pub id: Option, + /// Node client ID + pub name: String, + /// Capabilities + pub caps: Vec, + /// Network information + pub network: PeerNetworkInfo, + /// Protocols information + pub protocols: PeerProtocolsInfo, +} + +/// Peer network information +#[derive(Default, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PeerNetworkInfo { + /// Remote endpoint address + pub remote_address: String, + /// Local endpoint address + pub local_address: String, +} + +/// Peer protocols information +#[derive(Default, Debug, Serialize)] +pub struct PeerProtocolsInfo { + /// Ethereum protocol information + pub eth: Option, + /// PIP protocol information. + pub pip: Option, +} + +/// Peer Ethereum protocol information +#[derive(Default, Debug, Serialize)] +pub struct EthProtocolInfo { + /// Negotiated ethereum protocol version + pub version: u32, + /// Peer total difficulty if known + pub difficulty: Option, + /// SHA3 of peer best block hash + pub head: String, +} + +/// Peer PIP protocol information +#[derive(Default, Debug, Serialize)] +pub struct PipProtocolInfo { + /// Negotiated PIP protocol version + pub version: u32, + /// Peer total difficulty + pub difficulty: U256, + /// SHA3 of peer best block hash + pub head: String, +} + +/// Sync status +#[derive(Debug, PartialEq)] +pub enum SyncStatus { + /// Info when syncing + Info(SyncInfo), + /// Not syncing + None, +} + +impl Serialize for SyncStatus { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + SyncStatus::Info(ref info) => info.serialize(serializer), + SyncStatus::None => false.serialize(serializer), + } + } +} + +/// Propagation statistics for pending transaction. +#[derive(Default, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct TransactionStats { + /// Block no this transaction was first seen. + pub first_seen: u64, + /// Peers this transaction was propagated to with count. + pub propagated_to: BTreeMap, +} + +/// Chain status. +#[derive(Default, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ChainStatus { + /// Describes the gap in the blockchain, if there is one: (first, last) + pub block_gap: Option<(U256, U256)>, +} diff --git a/frame/dvm/rpc/core/src/types/transaction.rs b/frame/dvm/rpc/core/src/types/transaction.rs new file mode 100644 index 0000000000..32fc675b1d --- /dev/null +++ b/frame/dvm/rpc/core/src/types/transaction.rs @@ -0,0 +1,157 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Open 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. + +// Open 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 Open Ethereum. If not, see . + +use crate::types::{Bytes, TransactionCondition}; +use ethereum_types::{H160, H256, H512, U256, U64}; +use serde::ser::SerializeStruct; +use serde::{Serialize, Serializer}; + +/// Transaction +#[derive(Debug, Default, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Transaction { + /// Hash + pub hash: H256, + /// Nonce + pub nonce: U256, + /// Block hash + pub block_hash: Option, + /// Block number + pub block_number: Option, + /// Transaction Index + pub transaction_index: Option, + /// Sender + pub from: H160, + /// Recipient + pub to: Option, + /// Transfered value + pub value: U256, + /// Gas Price + pub gas_price: U256, + /// Gas + pub gas: U256, + /// Data + pub input: Bytes, + /// Creates contract + pub creates: Option, + /// Raw transaction data + pub raw: Bytes, + /// Public key of the signer. + pub public_key: Option, + /// The network id of the transaction, if any. + pub chain_id: Option, + /// The standardised V field of the signature (0 or 1). + pub standard_v: U256, + /// The standardised V field of the signature. + pub v: U256, + /// The R field of the signature. + pub r: U256, + /// The S field of the signature. + pub s: U256, + /// Transaction activates at specified block. + pub condition: Option, +} + +/// Local Transaction Status +#[derive(Debug)] +pub enum LocalTransactionStatus { + /// Transaction is pending + Pending, + /// Transaction is in future part of the queue + Future, + /// Transaction was mined. + Mined(Transaction), + /// Transaction was removed from the queue, but not mined. + Culled(Transaction), + /// Transaction was dropped because of limit. + Dropped(Transaction), + /// Transaction was replaced by transaction with higher gas price. + Replaced(Transaction, U256, H256), + /// Transaction never got into the queue. + Rejected(Transaction, String), + /// Transaction is invalid. + Invalid(Transaction), + /// Transaction was canceled. + Canceled(Transaction), +} + +impl Serialize for LocalTransactionStatus { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use self::LocalTransactionStatus::*; + + let elems = match *self { + Pending | Future => 1, + Mined(..) | Culled(..) | Dropped(..) | Invalid(..) | Canceled(..) => 2, + Rejected(..) => 3, + Replaced(..) => 4, + }; + + let status = "status"; + let transaction = "transaction"; + + let mut struc = serializer.serialize_struct("LocalTransactionStatus", elems)?; + match *self { + Pending => struc.serialize_field(status, "pending")?, + Future => struc.serialize_field(status, "future")?, + Mined(ref tx) => { + struc.serialize_field(status, "mined")?; + struc.serialize_field(transaction, tx)?; + } + Culled(ref tx) => { + struc.serialize_field(status, "culled")?; + struc.serialize_field(transaction, tx)?; + } + Dropped(ref tx) => { + struc.serialize_field(status, "dropped")?; + struc.serialize_field(transaction, tx)?; + } + Canceled(ref tx) => { + struc.serialize_field(status, "canceled")?; + struc.serialize_field(transaction, tx)?; + } + Invalid(ref tx) => { + struc.serialize_field(status, "invalid")?; + struc.serialize_field(transaction, tx)?; + } + Rejected(ref tx, ref reason) => { + struc.serialize_field(status, "rejected")?; + struc.serialize_field(transaction, tx)?; + struc.serialize_field("error", reason)?; + } + Replaced(ref tx, ref gas_price, ref hash) => { + struc.serialize_field(status, "replaced")?; + struc.serialize_field(transaction, tx)?; + struc.serialize_field("hash", hash)?; + struc.serialize_field("gasPrice", gas_price)?; + } + } + + struc.end() + } +} + +/// Geth-compatible output for eth_signTransaction method +#[derive(Debug, Default, Clone, PartialEq, Serialize)] +pub struct RichRawTransaction { + /// Raw transaction RLP + pub raw: Bytes, + /// Transaction details + #[serde(rename = "tx")] + pub transaction: Transaction, +} diff --git a/frame/dvm/rpc/core/src/types/transaction_condition.rs b/frame/dvm/rpc/core/src/types/transaction_condition.rs new file mode 100644 index 0000000000..003e6286a3 --- /dev/null +++ b/frame/dvm/rpc/core/src/types/transaction_condition.rs @@ -0,0 +1,29 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Open 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. + +// Open 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 Open Ethereum. If not, see . + +use serde::{Deserialize, Serialize}; + +/// Represents condition on minimum block number or block timestamp. +#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub enum TransactionCondition { + /// Valid at this minimum block number. + #[serde(rename = "block")] + Number(u64), + /// Valid at given unix time. + #[serde(rename = "time")] + Timestamp(u64), +} diff --git a/frame/dvm/rpc/core/src/types/transaction_request.rs b/frame/dvm/rpc/core/src/types/transaction_request.rs new file mode 100644 index 0000000000..76e8aed5d3 --- /dev/null +++ b/frame/dvm/rpc/core/src/types/transaction_request.rs @@ -0,0 +1,44 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Open 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. + +// Open 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 Open Ethereum. If not, see . + +//! `TransactionRequest` type + +use crate::types::{Bytes, TransactionCondition}; +use ethereum_types::{H160, U256}; +use serde::{Deserialize, Serialize}; + +/// Transaction request coming from RPC +#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +#[serde(rename_all = "camelCase")] +pub struct TransactionRequest { + /// Sender + pub from: Option, + /// Recipient + pub to: Option, + /// Gas Price + pub gas_price: Option, + /// Gas + pub gas: Option, + /// Value of transaction in wei + pub value: Option, + /// Additional data sent with transaction + pub data: Option, + /// Transaction's nonce + pub nonce: Option, + /// Delay until this block condition. + pub condition: Option, +} diff --git a/frame/dvm/rpc/core/src/types/work.rs b/frame/dvm/rpc/core/src/types/work.rs new file mode 100644 index 0000000000..ec13c6328a --- /dev/null +++ b/frame/dvm/rpc/core/src/types/work.rs @@ -0,0 +1,51 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Open 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. + +// Open 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 Open Ethereum. If not, see . + +use ethereum_types::{H256, U256}; + +use serde::{Serialize, Serializer}; + +/// The result of an `eth_getWork` call: it differs based on an option +/// whether to send the block number. +#[derive(Debug, PartialEq, Eq)] +pub struct Work { + /// The proof-of-work hash. + pub pow_hash: H256, + /// The seed hash. + pub seed_hash: H256, + /// The target. + pub target: H256, + /// The block number: this isn't always stored. + pub number: Option, +} + +impl Serialize for Work { + fn serialize(&self, s: S) -> Result + where + S: Serializer, + { + match self.number.as_ref() { + Some(num) => ( + &self.pow_hash, + &self.seed_hash, + &self.target, + U256::from(*num), + ) + .serialize(s), + None => (&self.pow_hash, &self.seed_hash, &self.target).serialize(s), + } + } +} diff --git a/frame/dvm/rpc/core/src/web3.rs b/frame/dvm/rpc/core/src/web3.rs new file mode 100644 index 0000000000..5a07e6e769 --- /dev/null +++ b/frame/dvm/rpc/core/src/web3.rs @@ -0,0 +1,34 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Open 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. + +// Open 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 Open Ethereum. If not, see . + +//! Web3 rpc interface. +use ethereum_types::H256; +use jsonrpc_core::Result; +use jsonrpc_derive::rpc; + +use crate::types::Bytes; + +/// Web3 rpc interface. +#[rpc(server)] +pub trait Web3Api { + /// Returns current client version. + #[rpc(name = "web3_clientVersion")] + fn client_version(&self) -> Result; + + /// Returns sha3 of the given data + #[rpc(name = "web3_sha3")] + fn sha3(&self, _: Bytes) -> Result; +} diff --git a/frame/dvm/rpc/primitives/Cargo.toml b/frame/dvm/rpc/primitives/Cargo.toml new file mode 100644 index 0000000000..1292ba8a5d --- /dev/null +++ b/frame/dvm/rpc/primitives/Cargo.toml @@ -0,0 +1,43 @@ +[package] +authors = ["Darwinia Network "] +description = "Runtime primitives for Ethereum RPC (web3) compatibility layer for Darwinia." +edition = "2018" +homepage = "https://darwinia.network/" +license = "GPL-3.0" +name = "dvm-rpc-primitives" +readme = "README.md" +repository = "https://github.com/darwinia-network/darwinia-common/" +version = "1.2.2" + +[dependencies] +# crates +codec = { package = "parity-scale-codec", version = "1.3.5", default-features = false } +ethereum = { version = "0.3", default-features = false, features = ["codec"] } +ethereum-types = { version = "0.9", default-features = false } +# substrate +pallet-evm = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-api = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-core = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-runtime = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } +sp-std = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } + +[features] +default = ["std"] + +std = [ + "crates-std", + "substrate-std", +] + +crates-std = [ + "codec/std", + "ethereum/std", + "ethereum-types/std", + "pallet-evm/std", +] +substrate-std = [ + "sp-api/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/frame/dvm/rpc/primitives/src/lib.rs b/frame/dvm/rpc/primitives/src/lib.rs new file mode 100644 index 0000000000..95c74f826c --- /dev/null +++ b/frame/dvm/rpc/primitives/src/lib.rs @@ -0,0 +1,92 @@ +// Copyright 2017-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::{Decode, Encode}; +use ethereum::{Block as EthereumBlock, Log, TransactionAction}; +use ethereum_types::Bloom; +use sp_core::{H160, H256, U256}; +use sp_std::vec::Vec; + +#[derive(Eq, PartialEq, Clone, Encode, Decode, sp_runtime::RuntimeDebug)] +pub struct TransactionStatus { + pub transaction_hash: H256, + pub transaction_index: u32, + pub from: H160, + pub to: Option, + pub contract_address: Option, + pub logs: Vec, + pub logs_bloom: Bloom, +} + +impl Default for TransactionStatus { + fn default() -> Self { + TransactionStatus { + transaction_hash: H256::default(), + transaction_index: 0 as u32, + from: H160::default(), + to: None, + contract_address: None, + logs: Vec::new(), + logs_bloom: Bloom::default(), + } + } +} + +sp_api::decl_runtime_apis! { + /// API necessary for Ethereum-compatibility layer. + pub trait EthereumRuntimeRPCApi { + /// Returns runtime defined pallet_evm::ChainId. + fn chain_id() -> u64; + /// Returns pallet_evm::Accounts by address. + fn account_basic(address: H160) -> pallet_evm::Account; + /// Returns FixedGasPrice::min_gas_price + fn gas_price() -> U256; + /// For a given account address, returns pallet_evm::AccountCodes. + fn account_code_at(address: H160) -> Vec; + /// Returns the converted FindAuthor::find_author authority id. + fn author() -> H160; + /// For a given account address and index, returns pallet_evm::AccountStorages. + fn storage_at(address: H160, index: U256) -> H256; + /// Returns a pallet_evm::execute_call response. + fn call( + from: H160, + data: Vec, + value: U256, + gas_limit: U256, + gas_price: Option, + nonce: Option, + action: TransactionAction + ) -> Result<(Vec, U256), sp_runtime::DispatchError>; + /// Return the current block. + fn current_block() -> Option; + /// Return the current receipt. + fn current_receipts() -> Option>; + /// Return the current transaction status. + fn current_transaction_statuses() -> Option>; + /// Return all the current data for a block in a single runtime call. + fn current_all() -> ( + Option, + Option>, + Option> + ); + } +} + +pub trait ConvertTransaction { + fn convert_transaction(&self, transaction: ethereum::Transaction) -> E; +} diff --git a/frame/dvm/rpc/src/eth.rs b/frame/dvm/rpc/src/eth.rs new file mode 100644 index 0000000000..c6b6bbb5f3 --- /dev/null +++ b/frame/dvm/rpc/src/eth.rs @@ -0,0 +1,952 @@ +// Copyright 2017-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +use crate::internal_err; +use dvm_rpc_core::types::{ + Block, BlockNumber, BlockTransactions, Bytes, CallRequest, Filter, Index, Log, Receipt, Rich, + RichBlock, SyncInfo, SyncStatus, Transaction, VariadicValue, Work, +}; +use dvm_rpc_core::{EthApi as EthApiT, NetApi as NetApiT}; +use dvm_rpc_primitives::{ConvertTransaction, EthereumRuntimeRPCApi, TransactionStatus}; +use ethereum::{Block as EthereumBlock, Transaction as EthereumTransaction}; +use ethereum_types::{H160, H256, H512, H64, U256, U64}; +use futures::future::TryFutureExt; +use jsonrpc_core::{ + futures::future::{self, Future}, + BoxFuture, Result, +}; +use sc_client_api::backend::{AuxStore, Backend, StateBackend, StorageProvider}; +use sha3::{Digest, Keccak256}; +use sp_api::{BlockId, ProvideRuntimeApi}; +use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; +use sp_runtime::traits::BlakeTwo256; +use sp_runtime::traits::{Block as BlockT, One, Saturating, UniqueSaturatedInto, Zero}; +use sp_runtime::transaction_validity::TransactionSource; +use sp_transaction_pool::TransactionPool; +use std::collections::BTreeMap; +use std::{marker::PhantomData, sync::Arc}; + +pub use dvm_rpc_core::{EthApiServer, NetApiServer}; + +pub struct EthApi { + pool: Arc

, + client: Arc, + convert_transaction: CT, + is_authority: bool, + _marker: PhantomData<(B, BE)>, +} + +impl EthApi { + pub fn new(client: Arc, pool: Arc

, convert_transaction: CT, is_authority: bool) -> Self { + Self { + client, + pool, + convert_transaction, + is_authority, + _marker: PhantomData, + } + } +} + +fn rich_block_build( + block: ethereum::Block, + statuses: Vec>, + hash: Option, + full_transactions: bool, +) -> RichBlock { + Rich { + inner: Block { + hash: Some(hash.unwrap_or_else(|| { + H256::from_slice(Keccak256::digest(&rlp::encode(&block.header)).as_slice()) + })), + parent_hash: block.header.parent_hash, + uncles_hash: block.header.ommers_hash, + author: block.header.beneficiary, + miner: block.header.beneficiary, + state_root: block.header.state_root, + transactions_root: block.header.transactions_root, + receipts_root: block.header.receipts_root, + number: Some(block.header.number), + gas_used: block.header.gas_used, + gas_limit: block.header.gas_limit, + extra_data: Bytes(block.header.extra_data.as_bytes().to_vec()), + logs_bloom: Some(block.header.logs_bloom), + timestamp: U256::from(block.header.timestamp / 1000), + difficulty: block.header.difficulty, + total_difficulty: None, + seal_fields: vec![ + Bytes(block.header.mix_hash.as_bytes().to_vec()), + Bytes(block.header.nonce.as_bytes().to_vec()), + ], + uncles: vec![], + transactions: { + if full_transactions { + BlockTransactions::Full( + block + .transactions + .iter() + .enumerate() + .map(|(index, transaction)| { + transaction_build( + transaction.clone(), + block.clone(), + statuses[index].clone().unwrap_or_default(), + ) + }) + .collect(), + ) + } else { + BlockTransactions::Hashes( + block + .transactions + .iter() + .map(|transaction| { + H256::from_slice( + Keccak256::digest(&rlp::encode(&transaction.clone())) + .as_slice(), + ) + }) + .collect(), + ) + } + }, + size: Some(U256::from(rlp::encode(&block).len() as u32)), + }, + extra_info: BTreeMap::new(), + } +} + +fn transaction_build( + transaction: EthereumTransaction, + block: EthereumBlock, + status: TransactionStatus, +) -> Transaction { + let mut sig = [0u8; 65]; + let mut msg = [0u8; 32]; + sig[0..32].copy_from_slice(&transaction.signature.r()[..]); + sig[32..64].copy_from_slice(&transaction.signature.s()[..]); + sig[64] = transaction.signature.standard_v(); + msg.copy_from_slice( + &transaction.message_hash(transaction.signature.chain_id().map(u64::from))[..], + ); + + let pubkey = match sp_io::crypto::secp256k1_ecdsa_recover(&sig, &msg) { + Ok(p) => Some(H512::from(p)), + Err(_e) => None, + }; + + Transaction { + hash: H256::from_slice(Keccak256::digest(&rlp::encode(&transaction)).as_slice()), + nonce: transaction.nonce, + block_hash: Some(H256::from_slice( + Keccak256::digest(&rlp::encode(&block.header)).as_slice(), + )), + block_number: Some(block.header.number), + transaction_index: Some(U256::from( + UniqueSaturatedInto::::unique_saturated_into(status.transaction_index), + )), + from: status.from, + to: status.to, + value: transaction.value, + gas_price: transaction.gas_price, + gas: transaction.gas_limit, + input: Bytes(transaction.clone().input), + creates: status.contract_address, + raw: Bytes(rlp::encode(&transaction)), + public_key: pubkey, + chain_id: transaction.signature.chain_id().map(U64::from), + standard_v: U256::from(transaction.signature.standard_v()), + v: U256::from(transaction.signature.v()), + r: U256::from(transaction.signature.r().as_bytes()), + s: U256::from(transaction.signature.s().as_bytes()), + condition: None, // TODO + } +} + +impl EthApi +where + C: ProvideRuntimeApi + StorageProvider + AuxStore, + C: HeaderBackend + HeaderMetadata + 'static, + C::Api: EthereumRuntimeRPCApi, + BE: Backend + 'static, + BE::State: StateBackend, + B: BlockT + Send + Sync + 'static, + C: Send + Sync + 'static, + P: TransactionPool + Send + Sync + 'static, + CT: ConvertTransaction<::Extrinsic> + Send + Sync + 'static, +{ + fn native_block_id(&self, number: Option) -> Result>> { + Ok(match number.unwrap_or(BlockNumber::Latest) { + BlockNumber::Hash { hash, .. } => self.load_hash(hash).unwrap_or(None), + BlockNumber::Num(number) => Some(BlockId::Number(number.unique_saturated_into())), + BlockNumber::Latest => Some(BlockId::Hash(self.client.info().best_hash)), + BlockNumber::Earliest => Some(BlockId::Number(Zero::zero())), + BlockNumber::Pending => None, + }) + } + + // Asumes there is only one mapped canonical block in the AuxStore, otherwise something is wrong + fn load_hash(&self, hash: H256) -> Result>> { + let hashes = match dvm_consensus::load_block_hash::(self.client.as_ref(), hash) + .map_err(|err| internal_err(format!("fetch aux store failed: {:?}", err)))? + { + Some(hashes) => hashes, + None => return Ok(None), + }; + let out: Vec = hashes + .into_iter() + .filter_map(|h| { + if let Ok(Some(_)) = self.client.header(BlockId::Hash(h)) { + Some(h) + } else { + None + } + }) + .collect(); + + if out.len() == 1 { + return Ok(Some(BlockId::Hash(out[0]))); + } + Ok(None) + } +} + +impl EthApiT for EthApi +where + C: ProvideRuntimeApi + StorageProvider + AuxStore, + C: HeaderBackend + HeaderMetadata + 'static, + C::Api: EthereumRuntimeRPCApi, + BE: Backend + 'static, + BE::State: StateBackend, + B: BlockT + Send + Sync + 'static, + C: Send + Sync + 'static, + P: TransactionPool + Send + Sync + 'static, + CT: ConvertTransaction<::Extrinsic> + Send + Sync + 'static, +{ + fn protocol_version(&self) -> Result { + Ok(1) + } + + fn syncing(&self) -> Result { + let block_number = U256::from( + self.client + .info() + .best_number + .clone() + .unique_saturated_into(), + ); + + Ok(SyncStatus::Info(SyncInfo { + starting_block: U256::zero(), + current_block: block_number, + highest_block: block_number, + warp_chunks_amount: None, + warp_chunks_processed: None, + })) + } + + fn hashrate(&self) -> Result { + Ok(U256::zero()) + } + + fn author(&self) -> Result { + let hash = self.client.info().best_hash; + + Ok(self + .client + .runtime_api() + .author(&BlockId::Hash(hash)) + .map_err(|err| internal_err(format!("fetch runtime chain id failed: {:?}", err)))? + .into()) + } + + fn is_mining(&self) -> Result { + Ok(self.is_authority) + } + + fn chain_id(&self) -> Result> { + let hash = self.client.info().best_hash; + Ok(Some( + self.client + .runtime_api() + .chain_id(&BlockId::Hash(hash)) + .map_err(|err| internal_err(format!("fetch runtime chain id failed: {:?}", err)))? + .into(), + )) + } + + fn gas_price(&self) -> Result { + let hash = self.client.info().best_hash; + Ok(self + .client + .runtime_api() + .gas_price(&BlockId::Hash(hash)) + .map_err(|err| internal_err(format!("fetch runtime chain id failed: {:?}", err)))? + .into()) + } + + fn accounts(&self) -> Result> { + Ok(vec![]) + } + + fn block_number(&self) -> Result { + Ok(U256::from( + self.client + .info() + .best_number + .clone() + .unique_saturated_into(), + )) + } + + fn balance(&self, address: H160, number: Option) -> Result { + if let Ok(Some(id)) = self.native_block_id(number) { + return Ok(self + .client + .runtime_api() + .account_basic(&id, address) + .map_err(|err| internal_err(format!("fetch runtime chain id failed: {:?}", err)))? + .balance + .into()); + } + Ok(U256::zero()) + } + + fn storage_at(&self, address: H160, index: U256, number: Option) -> Result { + if let Ok(Some(id)) = self.native_block_id(number) { + return Ok(self + .client + .runtime_api() + .storage_at(&id, address, index) + .map_err(|err| internal_err(format!("fetch runtime chain id failed: {:?}", err)))? + .into()); + } + Ok(H256::default()) + } + + fn block_by_hash(&self, hash: H256, full: bool) -> Result> { + let id = match self + .load_hash(hash) + .map_err(|err| internal_err(format!("{:?}", err)))? + { + Some(hash) => hash, + _ => return Ok(None), + }; + + let block = self + .client + .runtime_api() + .current_block(&id) + .map_err(|err| internal_err(format!("call runtime failed: {:?}", err)))?; + let statuses = self + .client + .runtime_api() + .current_transaction_statuses(&id) + .map_err(|err| internal_err(format!("call runtime failed: {:?}", err)))?; + + match (block, statuses) { + (Some(block), Some(statuses)) => Ok(Some(rich_block_build( + block, + statuses.into_iter().map(|s| Some(s)).collect(), + Some(hash), + full, + ))), + _ => Ok(None), + } + } + + fn block_by_number(&self, number: BlockNumber, full: bool) -> Result> { + let id = match self.native_block_id(Some(number))? { + Some(id) => id, + None => return Ok(None), + }; + + let block = self + .client + .runtime_api() + .current_block(&id) + .map_err(|err| internal_err(format!("call runtime failed: {:?}", err)))?; + let statuses = self + .client + .runtime_api() + .current_transaction_statuses(&id) + .map_err(|err| internal_err(format!("call runtime failed: {:?}", err)))?; + + match (block, statuses) { + (Some(block), Some(statuses)) => { + let hash = + H256::from_slice(Keccak256::digest(&rlp::encode(&block.header)).as_slice()); + + Ok(Some(rich_block_build( + block, + statuses.into_iter().map(|s| Some(s)).collect(), + Some(hash), + full, + ))) + } + _ => Ok(None), + } + } + + fn transaction_count(&self, address: H160, number: Option) -> Result { + let id = match self.native_block_id(number)? { + Some(id) => id, + None => return Ok(U256::zero()), + }; + + let nonce = self + .client + .runtime_api() + .account_basic(&id, address) + .map_err(|err| internal_err(format!("fetch runtime account basic failed: {:?}", err)))? + .nonce + .into(); + + Ok(nonce) + } + + fn block_transaction_count_by_hash(&self, hash: H256) -> Result> { + let id = match self + .load_hash(hash) + .map_err(|err| internal_err(format!("{:?}", err)))? + { + Some(hash) => hash, + _ => return Ok(None), + }; + + let block = self + .client + .runtime_api() + .current_block(&id) + .map_err(|err| { + internal_err(format!("fetch runtime account basic failed: {:?}", err)) + })?; + + match block { + Some(block) => Ok(Some(U256::from(block.transactions.len()))), + None => Ok(None), + } + } + + fn block_transaction_count_by_number(&self, number: BlockNumber) -> Result> { + let id = match self.native_block_id(Some(number))? { + Some(id) => id, + None => return Ok(None), + }; + + let block = self + .client + .runtime_api() + .current_block(&id) + .map_err(|err| { + internal_err(format!("fetch runtime account basic failed: {:?}", err)) + })?; + + match block { + Some(block) => Ok(Some(U256::from(block.transactions.len()))), + None => Ok(None), + } + } + + fn block_uncles_count_by_hash(&self, _: H256) -> Result { + Ok(U256::zero()) + } + + fn block_uncles_count_by_number(&self, _: BlockNumber) -> Result { + Ok(U256::zero()) + } + + fn code_at(&self, address: H160, number: Option) -> Result { + if let Ok(Some(id)) = self.native_block_id(number) { + return Ok(self + .client + .runtime_api() + .account_code_at(&id, address) + .map_err(|err| internal_err(format!("fetch runtime chain id failed: {:?}", err)))? + .into()); + } + Ok(Bytes(vec![])) + } + + fn send_raw_transaction(&self, bytes: Bytes) -> BoxFuture { + let transaction = match rlp::decode::(&bytes.0[..]) { + Ok(transaction) => transaction, + Err(_) => { + return Box::new(future::result(Err(internal_err( + "decode transaction failed", + )))) + } + }; + let transaction_hash = + H256::from_slice(Keccak256::digest(&rlp::encode(&transaction)).as_slice()); + let hash = self.client.info().best_hash; + Box::new( + self.pool + .submit_one( + &BlockId::hash(hash), + TransactionSource::Local, + self.convert_transaction.convert_transaction(transaction), + ) + .compat() + .map(move |_| transaction_hash) + .map_err(|err| { + internal_err(format!("submit transaction to pool failed: {:?}", err)) + }), + ) + } + + fn call(&self, request: CallRequest, _: Option) -> Result { + let hash = self.client.info().best_hash; + + let from = request.from.unwrap_or_default(); + let to = request.to.unwrap_or_default(); + let gas_price = request.gas_price; + let gas_limit = request.gas.unwrap_or(U256::max_value()); + let value = request.value.unwrap_or_default(); + let data = request.data.map(|d| d.0).unwrap_or_default(); + let nonce = request.nonce; + + let (ret, _) = self + .client + .runtime_api() + .call( + &BlockId::Hash(hash), + from, + data, + value, + gas_limit, + gas_price, + nonce, + ethereum::TransactionAction::Call(to), + ) + .map_err(|err| internal_err(format!("internal error: {:?}", err)))? + .map_err(|err| internal_err(format!("executing call failed: {:?}", err)))?; + + Ok(Bytes(ret)) + } + + fn estimate_gas(&self, request: CallRequest, _: Option) -> Result { + let hash = self.client.info().best_hash; + + let from = request.from.unwrap_or_default(); + let gas_price = request.gas_price; + let gas_limit = request.gas.unwrap_or(U256::max_value()); // TODO: this isn't safe + let value = request.value.unwrap_or_default(); + let data = request.data.map(|d| d.0).unwrap_or_default(); + let nonce = request.nonce; + + let (_, used_gas) = self + .client + .runtime_api() + .call( + &BlockId::Hash(hash), + from, + data, + value, + gas_limit, + gas_price, + nonce, + match request.to { + Some(to) => ethereum::TransactionAction::Call(to), + _ => ethereum::TransactionAction::Create, + }, + ) + .map_err(|err| internal_err(format!("internal error: {:?}", err)))? + .map_err(|err| internal_err(format!("executing call failed: {:?}", err)))?; + + Ok(used_gas) + } + + fn transaction_by_hash(&self, hash: H256) -> Result> { + let (hash, index) = + match dvm_consensus::load_transaction_metadata(self.client.as_ref(), hash) + .map_err(|err| internal_err(format!("fetch aux store failed: {:?})", err)))? + { + Some((hash, index)) => (hash, index as usize), + None => return Ok(None), + }; + + let id = match self + .load_hash(hash) + .map_err(|err| internal_err(format!("{:?}", err)))? + { + Some(hash) => hash, + _ => return Ok(None), + }; + + let block = self + .client + .runtime_api() + .current_block(&id) + .map_err(|err| internal_err(format!("call runtime failed: {:?}", err)))?; + let statuses = self + .client + .runtime_api() + .current_transaction_statuses(&id) + .map_err(|err| internal_err(format!("call runtime failed: {:?}", err)))?; + + match (block, statuses) { + (Some(block), Some(statuses)) => Ok(Some(transaction_build( + block.transactions[index].clone(), + block, + statuses[index].clone(), + ))), + _ => Ok(None), + } + } + + fn transaction_by_block_hash_and_index( + &self, + hash: H256, + index: Index, + ) -> Result> { + let id = match self + .load_hash(hash) + .map_err(|err| internal_err(format!("{:?}", err)))? + { + Some(hash) => hash, + _ => return Ok(None), + }; + let index = index.value(); + + let block = self + .client + .runtime_api() + .current_block(&id) + .map_err(|err| internal_err(format!("call runtime failed: {:?}", err)))?; + let statuses = self + .client + .runtime_api() + .current_transaction_statuses(&id) + .map_err(|err| internal_err(format!("call runtime failed: {:?}", err)))?; + + match (block, statuses) { + (Some(block), Some(statuses)) => Ok(Some(transaction_build( + block.transactions[index].clone(), + block, + statuses[index].clone(), + ))), + _ => Ok(None), + } + } + + fn transaction_by_block_number_and_index( + &self, + number: BlockNumber, + index: Index, + ) -> Result> { + let id = match self.native_block_id(Some(number))? { + Some(id) => id, + None => return Ok(None), + }; + let index = index.value(); + + let block = self + .client + .runtime_api() + .current_block(&id) + .map_err(|err| internal_err(format!("call runtime failed: {:?}", err)))?; + let statuses = self + .client + .runtime_api() + .current_transaction_statuses(&id) + .map_err(|err| internal_err(format!("call runtime failed: {:?}", err)))?; + + match (block, statuses) { + (Some(block), Some(statuses)) => Ok(Some(transaction_build( + block.transactions[index].clone(), + block, + statuses[index].clone(), + ))), + _ => Ok(None), + } + } + + fn transaction_receipt(&self, hash: H256) -> Result> { + let (hash, index) = + match dvm_consensus::load_transaction_metadata(self.client.as_ref(), hash) + .map_err(|err| internal_err(format!("fetch aux store failed : {:?}", err)))? + { + Some((hash, index)) => (hash, index as usize), + None => return Ok(None), + }; + + let id = match self + .load_hash(hash) + .map_err(|err| internal_err(format!("{:?}", err)))? + { + Some(hash) => hash, + _ => return Ok(None), + }; + + let block = self + .client + .runtime_api() + .current_block(&id) + .map_err(|err| internal_err(format!("call runtime failed: {:?}", err)))?; + let receipts = self + .client + .runtime_api() + .current_receipts(&id) + .map_err(|err| internal_err(format!("call runtime failed: {:?}", err)))?; + let statuses = self + .client + .runtime_api() + .current_transaction_statuses(&id) + .map_err(|err| internal_err(format!("call runtime failed: {:?}", err)))?; + + match (block, statuses, receipts) { + (Some(block), Some(statuses), Some(receipts)) => { + let block_hash = + H256::from_slice(Keccak256::digest(&rlp::encode(&block.header)).as_slice()); + let receipt = receipts[index].clone(); + let status = statuses[index].clone(); + let mut cumulative_receipts = receipts.clone(); + cumulative_receipts.truncate((status.transaction_index + 1) as usize); + + return Ok(Some(Receipt { + transaction_hash: Some(status.transaction_hash), + transaction_index: Some(status.transaction_index.into()), + block_hash: Some(block_hash), + from: Some(status.from), + to: status.to, + block_number: Some(block.header.number), + cumulative_gas_used: { + let cumulative_gas: u32 = cumulative_receipts + .iter() + .map(|r| r.used_gas.as_u32()) + .sum(); + U256::from(cumulative_gas) + }, + gas_used: Some(receipt.used_gas), + contract_address: status.contract_address, + logs: { + let mut pre_receipts_log_index = None; + if cumulative_receipts.len() > 0 { + cumulative_receipts.truncate(cumulative_receipts.len() - 1); + pre_receipts_log_index = Some( + cumulative_receipts + .iter() + .map(|r| r.logs.len() as u32) + .sum::(), + ); + } + receipt + .logs + .iter() + .enumerate() + .map(|(i, log)| Log { + address: log.address, + topics: log.topics.clone(), + data: Bytes(log.data.clone()), + block_hash: Some(block_hash), + block_number: Some(block.header.number), + transaction_hash: Some(hash), + transaction_index: Some(status.transaction_index.into()), + log_index: Some(U256::from( + (pre_receipts_log_index.unwrap_or(0)) + i as u32, + )), + transaction_log_index: Some(U256::from(i)), + removed: false, + }) + .collect() + }, + state_root: Some(receipt.state_root), + logs_bloom: receipt.logs_bloom, + status_code: None, + })); + } + _ => Ok(None), + } + } + + fn uncle_by_block_hash_and_index(&self, _: H256, _: Index) -> Result> { + Ok(None) + } + + fn uncle_by_block_number_and_index( + &self, + _: BlockNumber, + _: Index, + ) -> Result> { + Ok(None) + } + + fn logs(&self, filter: Filter) -> Result> { + let mut blocks_and_statuses = Vec::new(); + let mut ret = Vec::new(); + + if let Some(hash) = filter.block_hash { + let id = match self + .load_hash(hash) + .map_err(|err| internal_err(format!("{:?}", err)))? + { + Some(hash) => hash, + _ => return Ok(Vec::new()), + }; + + let (block, _, statuses) = + self.client.runtime_api().current_all(&id).map_err(|err| { + internal_err(format!("fetch runtime account basic failed: {:?}", err)) + })?; + + if let (Some(block), Some(statuses)) = (block, statuses) { + blocks_and_statuses.push((block, statuses)); + } + } else { + let mut current_number = filter + .to_block + .and_then(|v| v.to_min_block_num()) + .map(|s| s.unique_saturated_into()) + .unwrap_or(self.client.info().best_number); + + let from_number = filter + .from_block + .and_then(|v| v.to_min_block_num()) + .map(|s| s.unique_saturated_into()) + .unwrap_or(self.client.info().best_number); + while current_number >= from_number { + let id = BlockId::Number(current_number); + + let (block, _, statuses) = + self.client.runtime_api().current_all(&id).map_err(|err| { + internal_err(format!("fetch runtime account basic failed: {:?}", err)) + })?; + + if let (Some(block), Some(statuses)) = (block, statuses) { + blocks_and_statuses.push((block, statuses)); + } + + if current_number == Zero::zero() { + break; + } else { + current_number = current_number.saturating_sub(One::one()); + } + } + } + + for (block, statuses) in blocks_and_statuses { + let mut block_log_index: u32 = 0; + let block_hash = + H256::from_slice(Keccak256::digest(&rlp::encode(&block.header)).as_slice()); + for status in statuses.iter() { + let logs = status.logs.clone(); + let mut transaction_log_index: u32 = 0; + let transaction_hash = status.transaction_hash; + for log in logs { + let mut add: bool = false; + if let ( + Some(VariadicValue::Single(address)), + Some(VariadicValue::Multiple(topics)), + ) = (filter.address.clone(), filter.topics.clone()) + { + if address == log.address && log.topics.starts_with(&topics) { + add = true; + } + } else if let Some(VariadicValue::Single(address)) = filter.address { + if address == log.address { + add = true; + } + } else if let Some(VariadicValue::Multiple(topics)) = &filter.topics { + if log.topics.starts_with(&topics) { + add = true; + } + } else { + add = true; + } + if add { + ret.push(Log { + address: log.address.clone(), + topics: log.topics.clone(), + data: Bytes(log.data.clone()), + block_hash: Some(block_hash), + block_number: Some(block.header.number.clone()), + transaction_hash: Some(transaction_hash), + transaction_index: Some(U256::from(status.transaction_index)), + log_index: Some(U256::from(block_log_index)), + transaction_log_index: Some(U256::from(transaction_log_index)), + removed: false, + }); + } + transaction_log_index += 1; + block_log_index += 1; + } + } + } + + Ok(ret) + } + + fn work(&self) -> Result { + Ok(Work { + pow_hash: H256::default(), + seed_hash: H256::default(), + target: H256::default(), + number: None, + }) + } + + fn submit_work(&self, _: H64, _: H256, _: H256) -> Result { + Ok(false) + } + + fn submit_hashrate(&self, _: U256, _: H256) -> Result { + Ok(false) + } +} + +pub struct NetApi { + client: Arc, + _marker: PhantomData<(B, BE)>, +} + +impl NetApi { + pub fn new(client: Arc) -> Self { + Self { + client, + _marker: PhantomData, + } + } +} + +impl NetApiT for NetApi +where + C: ProvideRuntimeApi + StorageProvider + AuxStore, + C: HeaderBackend + HeaderMetadata + 'static, + C::Api: EthereumRuntimeRPCApi, + BE: Backend + 'static, + BE::State: StateBackend, + C: Send + Sync + 'static, + B: BlockT + Send + Sync + 'static, +{ + fn is_listening(&self) -> Result { + Ok(true) + } + + fn peer_count(&self) -> Result { + Ok("0".to_string()) + } + + fn version(&self) -> Result { + let hash = self.client.info().best_hash; + Ok(self + .client + .runtime_api() + .chain_id(&BlockId::Hash(hash)) + .map_err(|_| internal_err("fetch runtime chain id failed"))? + .to_string()) + } +} diff --git a/frame/dvm/rpc/src/eth_pubsub.rs b/frame/dvm/rpc/src/eth_pubsub.rs new file mode 100644 index 0000000000..5e7ba275cc --- /dev/null +++ b/frame/dvm/rpc/src/eth_pubsub.rs @@ -0,0 +1,534 @@ +use log::warn; +use sc_client_api::{ + backend::{AuxStore, Backend, StateBackend, StorageProvider}, + client::BlockchainEvents, +}; +use sc_rpc::Metadata; +use sp_api::{BlockId, ProvideRuntimeApi}; +use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; +use sp_io::hashing::twox_128; +use sp_runtime::traits::{BlakeTwo256, Block as BlockT, UniqueSaturatedInto}; +use sp_storage::{StorageData, StorageKey}; +use sp_transaction_pool::TransactionPool; +use std::collections::BTreeMap; +use std::{marker::PhantomData, sync::Arc}; + +use codec::Decode; +use dvm_rpc_core::types::{ + pubsub::{Kind, Params, PubSubSyncStatus, Result as PubSubResult}, + BlockNumber, Bytes, FilterAddress, Header, Log, Rich, Topic, VariadicValue, +}; +use dvm_rpc_core::EthPubSubApi::{self as EthPubSubApiT}; +use ethereum_types::{H256, U256}; +use jsonrpc_pubsub::{manager::SubscriptionManager, typed::Subscriber, SubscriptionId}; +use sha3::{Digest, Keccak256}; + +pub use dvm_rpc_core::EthPubSubApiServer; +use futures::{StreamExt as _, TryStreamExt as _}; + +use dvm_rpc_primitives::{EthereumRuntimeRPCApi, TransactionStatus}; +use jsonrpc_core::{ + futures::{Future, Sink}, + Result as JsonRpcResult, +}; + +use crate::internal_err; +use sc_network::{ExHashT, NetworkService}; + +pub struct EthPubSubApi { + _pool: Arc

, + client: Arc, + network: Arc>, + subscriptions: SubscriptionManager, + _marker: PhantomData<(B, BE)>, +} + +impl EthPubSubApi { + pub fn new( + _pool: Arc

, + client: Arc, + network: Arc>, + subscriptions: SubscriptionManager, + ) -> Self { + Self { + _pool, + client, + network, + subscriptions, + _marker: PhantomData, + } + } +} + +impl EthPubSubApi +where + B: BlockT + Send + Sync + 'static, + P: TransactionPool + Send + Sync + 'static, + C: ProvideRuntimeApi + StorageProvider + BlockchainEvents + AuxStore, + C: HeaderBackend + HeaderMetadata + 'static, + C::Api: EthereumRuntimeRPCApi, + C: Send + Sync + 'static, + BE: Backend + 'static, + BE::State: StateBackend, +{ + fn native_block_number(&self, number: Option) -> JsonRpcResult> { + let mut native_number: Option = None; + + if let Some(number) = number { + match number { + BlockNumber::Hash { hash, .. } => { + let id = self.load_hash(hash).unwrap_or(None); + if let Some(id) = id { + if let Ok(Some(block)) = self.client.runtime_api().current_block(&id) { + native_number = Some(block.header.number.as_u32()); + } + } + } + BlockNumber::Num(_) => { + if let Some(number) = number.to_min_block_num() { + native_number = Some(number.unique_saturated_into()); + } + } + BlockNumber::Latest => { + native_number = Some( + self.client + .info() + .best_number + .clone() + .unique_saturated_into() as u32, + ); + } + BlockNumber::Earliest => { + native_number = Some(0); + } + BlockNumber::Pending => { + native_number = None; + } + }; + } else { + native_number = Some( + self.client + .info() + .best_number + .clone() + .unique_saturated_into() as u32, + ); + } + Ok(native_number) + } + + fn filter_block(&self, params: Option) -> (Option, Option) { + if let Some(Params::Logs(f)) = params { + let to_block: Option = if f.to_block.is_some() { + self.native_block_number(f.to_block).unwrap_or(None) + } else { + None + }; + return ( + self.native_block_number(f.from_block).unwrap_or(None), + to_block, + ); + } + (None, None) + } + + // Asumes there is only one mapped canonical block in the AuxStore, otherwise something is wrong + fn load_hash(&self, hash: H256) -> JsonRpcResult>> { + let hashes = match dvm_consensus::load_block_hash::(self.client.as_ref(), hash) + .map_err(|err| internal_err(format!("fetch aux store failed: {:?}", err)))? + { + Some(hashes) => hashes, + None => return Ok(None), + }; + let out: Vec = hashes + .into_iter() + .filter_map(|h| { + if let Ok(Some(_)) = self.client.header(BlockId::Hash(h)) { + Some(h) + } else { + None + } + }) + .collect(); + + if out.len() == 1 { + return Ok(Some(BlockId::Hash(out[0]))); + } + Ok(None) + } +} + +struct FilteredParams { + from_block: Option, + to_block: Option, + block_hash: Option, + address: Option, + topics: Option, +} + +impl FilteredParams { + pub fn new(params: Option, block_range: (Option, Option)) -> Self { + if let Some(Params::Logs(d)) = params { + return FilteredParams { + from_block: block_range.0, + to_block: block_range.1, + block_hash: d.block_hash, + address: d.address, + topics: d.topics, + }; + } + FilteredParams { + from_block: None, + to_block: None, + block_hash: None, + address: None, + topics: None, + } + } + + pub fn filter_block_range(&self, block: ðereum::Block) -> bool { + let mut out = true; + let number: u32 = UniqueSaturatedInto::::unique_saturated_into(block.header.number); + if let Some(from) = self.from_block { + if from > number { + out = false; + } + } + if let Some(to) = self.to_block { + if to < number { + out = false; + } + } + out + } + + fn filter_block_hash(&self, block_hash: H256) -> bool { + if let Some(h) = self.block_hash { + if h != block_hash { + return false; + } + } + true + } + + fn filter_address(&self, log: ðereum::Log) -> bool { + if let Some(input_address) = &self.address { + match input_address { + VariadicValue::Single(x) => { + if log.address != *x { + return false; + } + } + VariadicValue::Multiple(x) => { + if !x.contains(&log.address) { + return false; + } + } + _ => { + return true; + } + } + } + true + } + + fn filter_topics(&self, log: ðereum::Log) -> bool { + if let Some(input_topics) = &self.topics { + match input_topics { + VariadicValue::Single(x) => { + if !log.topics.starts_with(&vec![*x]) { + return false; + } + } + VariadicValue::Multiple(x) => { + if !log.topics.starts_with(&x) { + return false; + } + } + _ => { + return true; + } + } + } + true + } +} + +struct SubscriptionResult {} +impl SubscriptionResult { + pub fn new() -> Self { + SubscriptionResult {} + } + pub fn new_heads(&self, block: ethereum::Block) -> PubSubResult { + PubSubResult::Header(Box::new(Rich { + inner: Header { + hash: Some(H256::from_slice( + Keccak256::digest(&rlp::encode(&block.header)).as_slice(), + )), + parent_hash: block.header.parent_hash, + uncles_hash: block.header.ommers_hash, + author: block.header.beneficiary, + miner: block.header.beneficiary, + state_root: block.header.state_root, + transactions_root: block.header.transactions_root, + receipts_root: block.header.receipts_root, + number: Some(block.header.number), + gas_used: block.header.gas_used, + gas_limit: block.header.gas_limit, + extra_data: Bytes(block.header.extra_data.as_bytes().to_vec()), + logs_bloom: block.header.logs_bloom, + timestamp: U256::from(block.header.timestamp), + difficulty: block.header.difficulty, + seal_fields: vec![ + Bytes(block.header.mix_hash.as_bytes().to_vec()), + Bytes(block.header.nonce.as_bytes().to_vec()), + ], + size: Some(U256::from(rlp::encode(&block).len() as u32)), + }, + extra_info: BTreeMap::new(), + })) + } + pub fn logs( + &self, + block: ethereum::Block, + receipts: Vec, + params: &FilteredParams, + ) -> Vec { + let block_hash = Some(H256::from_slice( + Keccak256::digest(&rlp::encode(&block.header)).as_slice(), + )); + let mut logs: Vec = vec![]; + let mut log_index: u32 = 0; + for (receipt_index, receipt) in receipts.into_iter().enumerate() { + let mut transaction_log_index: u32 = 0; + let transaction_hash: Option = if receipt.logs.len() > 0 { + Some(H256::from_slice( + Keccak256::digest(&rlp::encode(&block.transactions[receipt_index as usize])) + .as_slice(), + )) + } else { + None + }; + for log in receipt.logs { + if self.add_log(block_hash.unwrap(), &log, &block, params) { + logs.push(Log { + address: log.address, + topics: log.topics, + data: Bytes(log.data), + block_hash, + block_number: Some(block.header.number), + transaction_hash, + transaction_index: Some(U256::from(log_index)), + log_index: Some(U256::from(log_index)), + transaction_log_index: Some(U256::from(transaction_log_index)), + removed: false, + }); + } + log_index += 1; + transaction_log_index += 1; + } + } + logs + } + fn add_log( + &self, + block_hash: H256, + log: ðereum::Log, + block: ðereum::Block, + params: &FilteredParams, + ) -> bool { + if !params.filter_block_range(block) + || !params.filter_block_hash(block_hash) + || !params.filter_address(log) + || !params.filter_topics(log) + { + return false; + } + true + } +} + +fn storage_prefix_build(module: &[u8], storage: &[u8]) -> Vec { + [twox_128(module), twox_128(storage)].concat().to_vec() +} + +macro_rules! stream_build { + ($context:expr => $module:expr, $storage:expr) => {{ + let key: StorageKey = StorageKey(storage_prefix_build($module, $storage)); + match $context + .client + .storage_changes_notification_stream(Some(&[key]), None) + { + Ok(stream) => Some(stream), + Err(_err) => None, + } + }}; +} + +impl EthPubSubApiT for EthPubSubApi +where + B: BlockT + Send + Sync + 'static, + P: TransactionPool + Send + Sync + 'static, + C: ProvideRuntimeApi + StorageProvider + BlockchainEvents + AuxStore, + C: HeaderBackend + HeaderMetadata + 'static, + C: Send + Sync + 'static, + C::Api: EthereumRuntimeRPCApi, + BE: Backend + 'static, + BE::State: StateBackend, +{ + type Metadata = Metadata; + fn subscribe( + &self, + _metadata: Self::Metadata, + subscriber: Subscriber, + kind: Kind, + params: Option, + ) { + let filter_block = self.filter_block(params.clone()); + let filtered_params = FilteredParams::new(params, filter_block); + let client = self.client.clone(); + let network = self.network.clone(); + match kind { + Kind::Logs => { + if let Some(stream) = stream_build!( + self => b"Ethereum", b"CurrentReceipts" + ) { + self.subscriptions.add(subscriber, |sink| { + let stream = stream + .flat_map(move |(block_hash, changes)| { + let id = BlockId::Hash(block_hash); + let data = changes.iter().last().unwrap().2.unwrap(); + let receipts: Vec = + Decode::decode(&mut &data.0[..]).unwrap(); + let block: ethereum::Block = + client.runtime_api().current_block(&id).unwrap().unwrap(); + futures::stream::iter(SubscriptionResult::new().logs( + block, + receipts, + &filtered_params, + )) + }) + .map(|x| { + return Ok::< + Result, + (), + >(Ok(PubSubResult::Log(Box::new(x)))); + }) + .compat(); + + sink.sink_map_err(|e| warn!("Error sending notifications: {:?}", e)) + .send_all(stream) + .map(|_| ()) + }); + } + } + Kind::NewHeads => { + if let Some(stream) = stream_build!( + self => b"Ethereum", b"CurrentBlock" + ) { + self.subscriptions.add(subscriber, |sink| { + let stream = stream + .map(|(_block, changes)| { + let data = changes.iter().last().unwrap().2.unwrap(); + let block: ethereum::Block = + Decode::decode(&mut &data.0[..]).unwrap(); + return Ok::<_, ()>(Ok(SubscriptionResult::new().new_heads(block))); + }) + .compat(); + + sink.sink_map_err(|e| warn!("Error sending notifications: {:?}", e)) + .send_all(stream) + .map(|_| ()) + }); + } + } + Kind::NewPendingTransactions => { + if let Some(stream) = stream_build!( + self => b"Ethereum", b"Pending" + ) { + self.subscriptions.add(subscriber, |sink| { + let stream = stream + .flat_map(|(_block, changes)| { + let mut transactions: Vec = vec![]; + let storage: Vec> = changes + .iter() + .filter_map(|(o_sk, _k, v)| { + if o_sk.is_none() { + Some(v.cloned()) + } else { + None + } + }) + .collect(); + for change in storage { + if let Some(data) = change { + let storage: Vec<( + ethereum::Transaction, + TransactionStatus, + ethereum::Receipt, + )> = Decode::decode(&mut &data.0[..]).unwrap(); + let tmp: Vec = + storage.iter().map(|x| x.0.clone()).collect(); + transactions.extend(tmp); + } + } + futures::stream::iter(transactions) + }) + .map(|transaction| { + return Ok::< + Result, + (), + >(Ok(PubSubResult::TransactionHash(H256::from_slice( + Keccak256::digest(&rlp::encode(&transaction)).as_slice(), + )))); + }) + .compat(); + + sink.sink_map_err(|e| warn!("Error sending notifications: {:?}", e)) + .send_all(stream) + .map(|_| ()) + }); + } + } + Kind::Syncing => { + if let Some(stream) = stream_build!( + self => b"Ethereum", b"CurrentBlock" + ) { + self.subscriptions.add(subscriber, |sink| { + let mut previous_syncing = network.is_major_syncing(); + let stream = stream + .filter_map(move |(_, _)| { + let syncing = network.is_major_syncing(); + if previous_syncing != syncing { + previous_syncing = syncing; + futures::future::ready(Some(syncing)) + } else { + futures::future::ready(None) + } + }) + .map(|syncing| { + return Ok::< + Result, + (), + >(Ok(PubSubResult::SyncState(PubSubSyncStatus { + syncing, + }))); + }) + .compat(); + sink.sink_map_err(|e| warn!("Error sending notifications: {:?}", e)) + .send_all(stream) + .map(|_| ()) + }); + } + } + } + } + + fn unsubscribe( + &self, + _metadata: Option, + subscription_id: SubscriptionId, + ) -> JsonRpcResult { + Ok(self.subscriptions.cancel(subscription_id)) + } +} diff --git a/frame/dvm/rpc/src/lib.rs b/frame/dvm/rpc/src/lib.rs new file mode 100644 index 0000000000..7b53ee0f73 --- /dev/null +++ b/frame/dvm/rpc/src/lib.rs @@ -0,0 +1,31 @@ +// Copyright 2017-2020 Parity Technologies (UK) Ltd. +// This file is part of Frontier. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +mod eth; +mod eth_pubsub; + +pub use eth::{EthApi, EthApiServer, NetApi, NetApiServer}; +pub use eth_pubsub::{EthPubSubApi, EthPubSubApiServer}; + +use jsonrpc_core::{Error, ErrorCode}; + +pub fn internal_err(message: T) -> Error { + Error { + code: ErrorCode::InternalError, + message: message.to_string(), + data: None, + } +} diff --git a/frame/elections-phragmen/Cargo.toml b/frame/elections-phragmen/Cargo.toml index d6a55834b6..83a4848ad5 100644 --- a/frame/elections-phragmen/Cargo.toml +++ b/frame/elections-phragmen/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "darwinia-elections-phragmen" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/frame/header-mmr/Cargo.toml b/frame/header-mmr/Cargo.toml index acb9f2bd75..f577004a35 100644 --- a/frame/header-mmr/Cargo.toml +++ b/frame/header-mmr/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "darwinia-header-mmr" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/frame/header-mmr/rpc/Cargo.toml b/frame/header-mmr/rpc/Cargo.toml index 15d3532f96..360cd08823 100644 --- a/frame/header-mmr/rpc/Cargo.toml +++ b/frame/header-mmr/rpc/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "darwinia-header-mmr-rpc" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/frame/header-mmr/rpc/runtime-api/Cargo.toml b/frame/header-mmr/rpc/runtime-api/Cargo.toml index b9e370d001..14adeb6526 100644 --- a/frame/header-mmr/rpc/runtime-api/Cargo.toml +++ b/frame/header-mmr/rpc/runtime-api/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "darwinia-header-mmr-rpc-runtime-api" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index 542401966a..c77edf525b 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "darwinia-staking" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/frame/staking/rpc/Cargo.toml b/frame/staking/rpc/Cargo.toml index f0ba4ad460..bcc05559df 100644 --- a/frame/staking/rpc/Cargo.toml +++ b/frame/staking/rpc/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "darwinia-staking-rpc" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/frame/staking/rpc/runtime-api/Cargo.toml b/frame/staking/rpc/runtime-api/Cargo.toml index 3d9a5bad6f..c71c36c79c 100644 --- a/frame/staking/rpc/runtime-api/Cargo.toml +++ b/frame/staking/rpc/runtime-api/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "darwinia-staking-rpc-runtime-api" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/frame/support/Cargo.toml b/frame/support/Cargo.toml index 406da83b9f..3281644a50 100644 --- a/frame/support/Cargo.toml +++ b/frame/support/Cargo.toml @@ -7,13 +7,13 @@ license = "GPL-3.0" name = "darwinia-support" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates codec = { package = "parity-scale-codec", version = "1.3.5", default-features = false, features = ["derive"] } impl-trait-for-tuples = { version = "0.2.0" } -num-traits = { version = "0.2.12", default-features = false } +num-traits = { version = "0.2.14", default-features = false } # darwinia ethereum-primitives = { default-features = false, path = "../../primitives/ethereum-primitives" } # substrate diff --git a/frame/treasury/Cargo.toml b/frame/treasury/Cargo.toml index 6bbd403976..65fb3eaa7b 100644 --- a/frame/treasury/Cargo.toml +++ b/frame/treasury/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "darwinia-treasury" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/frame/vesting/Cargo.toml b/frame/vesting/Cargo.toml index 7c393d5a5f..ad7693859a 100644 --- a/frame/vesting/Cargo.toml +++ b/frame/vesting/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "darwinia-vesting" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/primitives/array-bytes/Cargo.toml b/primitives/array-bytes/Cargo.toml index d368d8f7ac..3c3c3abcc1 100644 --- a/primitives/array-bytes/Cargo.toml +++ b/primitives/array-bytes/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "array-bytes" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # substrate diff --git a/primitives/ethereum-primitives/Cargo.toml b/primitives/ethereum-primitives/Cargo.toml index dfbe79b0a1..bae2f05d1a 100644 --- a/primitives/ethereum-primitives/Cargo.toml +++ b/primitives/ethereum-primitives/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "ethereum-primitives" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates diff --git a/primitives/merkle-patricia-trie/Cargo.toml b/primitives/merkle-patricia-trie/Cargo.toml index 5f147e570a..ef6babea02 100644 --- a/primitives/merkle-patricia-trie/Cargo.toml +++ b/primitives/merkle-patricia-trie/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "merkle-patricia-trie" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [[bench]] harness = false diff --git a/primitives/relay/Cargo.toml b/primitives/relay/Cargo.toml index e4bc11d18c..b5d8c53fc2 100644 --- a/primitives/relay/Cargo.toml +++ b/primitives/relay/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "darwinia-relay-primitives" readme = "README.md" repository = "https://github.com/darwinia-network/darwinia-common/" -version = "1.0.0" +version = "1.2.2" [dependencies] # crates