diff --git a/Cargo.lock b/Cargo.lock index d3b46835c..bd3ae5a31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -576,6 +576,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + [[package]] name = "async-channel" version = "1.9.0" @@ -641,6 +647,26 @@ dependencies = [ "pin-project-lite 0.2.14", ] +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + +[[package]] +name = "auto_impl" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.71", +] + [[package]] name = "autocfg" version = "1.3.0" @@ -1490,6 +1516,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.20" @@ -1924,6 +1959,12 @@ dependencies = [ "walkdir", ] +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + [[package]] name = "downcast" version = "0.11.0" @@ -2045,6 +2086,9 @@ name = "either" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +dependencies = [ + "serde", +] [[package]] name = "elliptic-curve" @@ -2159,11 +2203,31 @@ checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" dependencies = [ "crunchy", "fixed-hash", + "impl-codec", "impl-rlp", "impl-serde", + "scale-info", "tiny-keccak", ] +[[package]] +name = "ethereum" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e04d24d20b8ff2235cffbf242d5092de3aa45f77c5270ddbfadd2778ca13fea" +dependencies = [ + "bytes", + "ethereum-types", + "hash-db", + "hash256-std-hasher", + "parity-scale-codec", + "rlp", + "scale-info", + "serde", + "sha3", + "trie-root", +] + [[package]] name = "ethereum-types" version = "0.14.1" @@ -2172,9 +2236,11 @@ checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" dependencies = [ "ethbloom", "fixed-hash", + "impl-codec", "impl-rlp", "impl-serde", "primitive-types", + "scale-info", "uint", ] @@ -2205,6 +2271,64 @@ dependencies = [ "pin-project-lite 0.2.14", ] +[[package]] +name = "evm" +version = "0.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "767f43e9630cc36cf8ff2777cbb0121b055f0d1fd6eaaa13b46a1808f0d0e7e9" +dependencies = [ + "auto_impl", + "environmental", + "ethereum", + "evm-core", + "evm-gasometer", + "evm-runtime", + "log", + "parity-scale-codec", + "primitive-types", + "rlp", + "scale-info", + "serde", + "sha3", +] + +[[package]] +name = "evm-core" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1da6cedc5cedb4208e59467106db0d1f50db01b920920589f8e672c02fdc04f" +dependencies = [ + "parity-scale-codec", + "primitive-types", + "scale-info", + "serde", +] + +[[package]] +name = "evm-gasometer" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dc0eb591abc5cd7b05bef6a036c2bb6c66ab6c5e0c5ce94bfe377ab670b1fd7" +dependencies = [ + "environmental", + "evm-core", + "evm-runtime", + "primitive-types", +] + +[[package]] +name = "evm-runtime" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84bbe09b64ae13a29514048c1bb6fda6374ac0b4f6a1f15a443348ab88ef42cd" +dependencies = [ + "auto_impl", + "environmental", + "evm-core", + "primitive-types", + "sha3", +] + [[package]] name = "exit-future" version = "0.2.0" @@ -2247,6 +2371,173 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +[[package]] +name = "fc-api" +version = "1.0.0-dev" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "async-trait", + "fp-storage", + "parity-scale-codec", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "fc-consensus" +version = "2.0.0-dev" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "async-trait", + "fp-consensus", + "fp-rpc", + "sc-consensus", + "sp-api", + "sp-block-builder", + "sp-consensus", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "fc-db" +version = "2.0.0-dev" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "async-trait", + "ethereum", + "fc-api", + "fc-storage", + "fp-consensus", + "fp-rpc", + "fp-storage", + "futures", + "kvdb-rocksdb", + "log", + "parity-db", + "parity-scale-codec", + "parking_lot 0.12.3", + "sc-client-api", + "sc-client-db", + "smallvec", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-database", + "sp-runtime", + "sqlx", + "tokio", +] + +[[package]] +name = "fc-mapping-sync" +version = "2.0.0-dev" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "fc-db", + "fc-storage", + "fp-consensus", + "fp-rpc", + "futures", + "futures-timer", + "log", + "parking_lot 0.12.3", + "sc-client-api", + "sc-utils", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", + "tokio", +] + +[[package]] +name = "fc-rpc" +version = "2.0.0-dev" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "ethereum", + "ethereum-types", + "evm", + "fc-api", + "fc-mapping-sync", + "fc-rpc-core", + "fc-storage", + "fp-evm", + "fp-rpc", + "fp-storage", + "futures", + "hex", + "jsonrpsee", + "libsecp256k1", + "log", + "pallet-evm", + "parity-scale-codec", + "prometheus", + "rand 0.8.5", + "rlp", + "sc-client-api", + "sc-consensus-aura", + "sc-network", + "sc-network-sync", + "sc-rpc", + "sc-service", + "sc-transaction-pool", + "sc-transaction-pool-api", + "sc-utils", + "schnellru", + "serde", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-aura", + "sp-core", + "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-timestamp", + "substrate-prometheus-endpoint", + "thiserror", + "tokio", +] + +[[package]] +name = "fc-rpc-core" +version = "1.1.0-dev" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "ethereum", + "ethereum-types", + "jsonrpsee", + "rlp", + "rustc-hex", + "serde", + "serde_json", + "sp-crypto-hashing", +] + +[[package]] +name = "fc-storage" +version = "1.0.0-dev" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "ethereum", + "ethereum-types", + "fp-rpc", + "fp-storage", + "parity-scale-codec", + "sc-client-api", + "sp-api", + "sp-io", + "sp-runtime", + "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", +] + [[package]] name = "fdlimit" version = "0.3.0" @@ -2372,6 +2663,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "futures-core", + "futures-sink", + "spin 0.9.8", +] + [[package]] name = "fnv" version = "1.0.7" @@ -2410,6 +2712,109 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fp-account" +version = "1.0.0-dev" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "hex", + "impl-serde", + "libsecp256k1", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", +] + +[[package]] +name = "fp-consensus" +version = "2.0.0-dev" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "ethereum", + "parity-scale-codec", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "fp-dynamic-fee" +version = "1.0.0" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "async-trait", + "sp-core", + "sp-inherents", +] + +[[package]] +name = "fp-ethereum" +version = "1.0.0-dev" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "ethereum", + "ethereum-types", + "fp-evm", + "frame-support", + "parity-scale-codec", +] + +[[package]] +name = "fp-evm" +version = "3.0.0-dev" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "evm", + "frame-support", + "num_enum", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "fp-rpc" +version = "3.0.0-dev" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "ethereum", + "ethereum-types", + "fp-evm", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-runtime", + "sp-state-machine", +] + +[[package]] +name = "fp-self-contained" +version = "1.0.0-dev" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "frame-support", + "parity-scale-codec", + "scale-info", + "serde", + "sp-runtime", +] + +[[package]] +name = "fp-storage" +version = "2.0.0" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "parity-scale-codec", + "serde", +] + [[package]] name = "fragile" version = "2.0.0" @@ -2743,6 +3148,17 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot 0.12.3", +] + [[package]] name = "futures-io" version = "0.3.30" @@ -3052,6 +3468,9 @@ name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] [[package]] name = "heck" @@ -4170,6 +4589,17 @@ dependencies = [ "libsecp256k1-core", ] +[[package]] +name = "libsqlite3-sys" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "libz-sys" version = "1.1.18" @@ -4760,6 +5190,23 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "ndarray" version = "0.15.6" @@ -4872,7 +5319,17 @@ checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" name = "node-subtensor" version = "4.0.0-dev" dependencies = [ + "async-trait", "clap", + "fc-api", + "fc-consensus", + "fc-db", + "fc-mapping-sync", + "fc-rpc", + "fc-rpc-core", + "fc-storage", + "fp-dynamic-fee", + "fp-rpc", "frame-benchmarking", "frame-benchmarking-cli", "frame-metadata-hash-extension", @@ -4893,10 +5350,12 @@ dependencies = [ "sc-consensus-aura", "sc-consensus-grandpa", "sc-consensus-grandpa-rpc", + "sc-consensus-manual-seal", "sc-consensus-slots", "sc-executor", "sc-keystore", "sc-network", + "sc-network-sync", "sc-offchain", "sc-rpc", "sc-rpc-api", @@ -4920,6 +5379,7 @@ dependencies = [ "sp-timestamp", "substrate-build-script-utils", "substrate-frame-rpc-system", + "substrate-prometheus-endpoint", "subtensor-custom-rpc", "subtensor-custom-rpc-runtime-api", ] @@ -4928,6 +5388,10 @@ dependencies = [ name = "node-subtensor-runtime" version = "4.0.0-dev" dependencies = [ + "fp-account", + "fp-evm", + "fp-rpc", + "fp-self-contained", "frame-benchmarking", "frame-executive", "frame-metadata", @@ -4941,9 +5405,18 @@ dependencies = [ "pallet-admin-utils", "pallet-aura", "pallet-balances", + "pallet-base-fee", "pallet-collective", "pallet-commitments", + "pallet-dynamic-fee", + "pallet-ethereum", + "pallet-evm", + "pallet-evm-chain-id", + "pallet-evm-precompile-modexp", + "pallet-evm-precompile-sha3fips", + "pallet-evm-precompile-simple", "pallet-grandpa", + "pallet-hotfix-sufficients", "pallet-insecure-randomness-collective-flip", "pallet-membership", "pallet-multisig", @@ -5020,6 +5493,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.5" @@ -5064,12 +5551,24 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-rational" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ + "num-bigint", "num-integer", "num-traits", ] @@ -5094,6 +5593,27 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.71", +] + [[package]] name = "object" version = "0.30.4" @@ -5285,42 +5805,157 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", +] + +[[package]] +name = "pallet-base-fee" +version = "1.0.0" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "fp-evm", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "pallet-collective" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "subtensor-macros", +] + +[[package]] +name = "pallet-commitments" +version = "4.0.0-dev" +dependencies = [ + "enumflags2", + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "subtensor-macros", +] + +[[package]] +name = "pallet-dynamic-fee" +version = "4.0.0-dev" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "fp-dynamic-fee", + "fp-evm", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-inherents", +] + +[[package]] +name = "pallet-ethereum" +version = "4.0.0-dev" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "ethereum", + "ethereum-types", + "evm", + "fp-consensus", + "fp-ethereum", + "fp-evm", + "fp-rpc", + "fp-storage", + "frame-support", + "frame-system", + "pallet-evm", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", ] [[package]] -name = "pallet-collective" -version = "4.0.0-dev" +name = "pallet-evm" +version = "6.0.0-dev" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" dependencies = [ + "environmental", + "evm", + "fp-account", + "fp-evm", "frame-benchmarking", "frame-support", "frame-system", + "hash-db", + "hex", + "hex-literal", + "impl-trait-for-tuples", "log", "parity-scale-codec", + "rlp", "scale-info", "sp-core", "sp-io", "sp-runtime", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "subtensor-macros", ] [[package]] -name = "pallet-commitments" -version = "4.0.0-dev" +name = "pallet-evm-chain-id" +version = "1.0.0-dev" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" dependencies = [ - "enumflags2", - "frame-benchmarking", "frame-support", "frame-system", - "pallet-balances", "parity-scale-codec", "scale-info", - "sp-core", +] + +[[package]] +name = "pallet-evm-precompile-modexp" +version = "2.0.0-dev" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "fp-evm", + "num", +] + +[[package]] +name = "pallet-evm-precompile-sha3fips" +version = "2.0.0-dev" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "fp-evm", + "tiny-keccak", +] + +[[package]] +name = "pallet-evm-precompile-simple" +version = "2.0.0-dev" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "fp-evm", + "ripemd", "sp-io", - "sp-runtime", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "subtensor-macros", ] [[package]] @@ -5346,6 +5981,21 @@ dependencies = [ "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", ] +[[package]] +name = "pallet-hotfix-sufficients" +version = "1.0.0" +source = "git+https://github.com/gztensor/frontier?branch=polkadot-v1.11.0-subtensor#09a2d999d6f7b9f5dca25c0679ccdf745db441c2" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-evm", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", +] + [[package]] name = "pallet-insecure-randomness-collective-flip" version = "16.0.0" @@ -6802,6 +7452,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "rlp" version = "0.5.2" @@ -6809,9 +7468,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" dependencies = [ "bytes", + "rlp-derive", "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 1.0.109", +] + [[package]] name = "rocksdb" version = "0.21.0" @@ -7275,6 +7946,55 @@ dependencies = [ "thiserror", ] +[[package]] +name = "sc-consensus-babe" +version = "0.34.0" +source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +dependencies = [ + "async-trait", + "fork-tree", + "futures", + "log", + "num-bigint", + "num-rational", + "num-traits", + "parity-scale-codec", + "parking_lot 0.12.3", + "sc-client-api", + "sc-consensus", + "sc-consensus-epochs", + "sc-consensus-slots", + "sc-telemetry", + "sc-transaction-pool-api", + "sp-api", + "sp-application-crypto", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-consensus-slots", + "sp-core", + "sp-crypto-hashing", + "sp-inherents", + "sp-keystore", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-consensus-epochs" +version = "0.33.0" +source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +dependencies = [ + "fork-tree", + "parity-scale-codec", + "sc-client-api", + "sc-consensus", + "sp-blockchain", + "sp-runtime", +] + [[package]] name = "sc-consensus-grandpa" version = "0.19.0" @@ -7339,6 +8059,41 @@ dependencies = [ "thiserror", ] +[[package]] +name = "sc-consensus-manual-seal" +version = "0.35.0" +source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +dependencies = [ + "assert_matches", + "async-trait", + "futures", + "futures-timer", + "jsonrpsee", + "log", + "parity-scale-codec", + "sc-client-api", + "sc-consensus", + "sc-consensus-aura", + "sc-consensus-babe", + "sc-consensus-epochs", + "sc-transaction-pool", + "sc-transaction-pool-api", + "serde", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-consensus-aura", + "sp-consensus-babe", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-keystore", + "sp-runtime", + "sp-timestamp", + "substrate-prometheus-endpoint", + "thiserror", +] + [[package]] name = "sc-consensus-slots" version = "0.33.0" @@ -8260,9 +9015,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.204" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" dependencies = [ "serde_derive", ] @@ -8287,9 +9042,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", @@ -8729,6 +9484,24 @@ dependencies = [ "sp-timestamp", ] +[[package]] +name = "sp-consensus-babe" +version = "0.32.0" +source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +dependencies = [ + "async-trait", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-timestamp", +] + [[package]] name = "sp-consensus-grandpa" version = "13.0.0" @@ -8807,7 +9580,7 @@ dependencies = [ [[package]] name = "sp-crypto-ec-utils" version = "0.10.0" -source = "git+https://github.com/paritytech/polkadot-sdk#6a5b6e03bfc8d0c6f5f05f3180313902c15aee84" +source = "git+https://github.com/paritytech/polkadot-sdk#56201964f9184004ca17992a3b4778de855b1a35" dependencies = [ "ark-bls12-377", "ark-bls12-377-ext", @@ -8869,7 +9642,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#6a5b6e03bfc8d0c6f5f05f3180313902c15aee84" +source = "git+https://github.com/paritytech/polkadot-sdk#56201964f9184004ca17992a3b4778de855b1a35" dependencies = [ "proc-macro2", "quote", @@ -8889,7 +9662,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.25.0" -source = "git+https://github.com/paritytech/polkadot-sdk#6a5b6e03bfc8d0c6f5f05f3180313902c15aee84" +source = "git+https://github.com/paritytech/polkadot-sdk#56201964f9184004ca17992a3b4778de855b1a35" dependencies = [ "environmental", "parity-scale-codec", @@ -9074,7 +9847,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "24.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#6a5b6e03bfc8d0c6f5f05f3180313902c15aee84" +source = "git+https://github.com/paritytech/polkadot-sdk#56201964f9184004ca17992a3b4778de855b1a35" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -9106,7 +9879,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "17.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#6a5b6e03bfc8d0c6f5f05f3180313902c15aee84" +source = "git+https://github.com/paritytech/polkadot-sdk#56201964f9184004ca17992a3b4778de855b1a35" dependencies = [ "Inflector", "expander", @@ -9195,7 +9968,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polk [[package]] name = "sp-std" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#6a5b6e03bfc8d0c6f5f05f3180313902c15aee84" +source = "git+https://github.com/paritytech/polkadot-sdk#56201964f9184004ca17992a3b4778de855b1a35" [[package]] name = "sp-storage" @@ -9212,7 +9985,7 @@ dependencies = [ [[package]] name = "sp-storage" version = "19.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#6a5b6e03bfc8d0c6f5f05f3180313902c15aee84" +source = "git+https://github.com/paritytech/polkadot-sdk#56201964f9184004ca17992a3b4778de855b1a35" dependencies = [ "impl-serde", "parity-scale-codec", @@ -9247,7 +10020,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "16.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#6a5b6e03bfc8d0c6f5f05f3180313902c15aee84" +source = "git+https://github.com/paritytech/polkadot-sdk#56201964f9184004ca17992a3b4778de855b1a35" dependencies = [ "parity-scale-codec", "tracing", @@ -9344,8 +10117,9 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "20.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#6a5b6e03bfc8d0c6f5f05f3180313902c15aee84" +source = "git+https://github.com/paritytech/polkadot-sdk#56201964f9184004ca17992a3b4778de855b1a35" dependencies = [ + "anyhow", "impl-trait-for-tuples", "log", "parity-scale-codec", @@ -9376,6 +10150,9 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] [[package]] name = "spinning_top" @@ -9396,6 +10173,126 @@ dependencies = [ "der", ] +[[package]] +name = "sqlformat" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f895e3734318cc55f1fe66258926c9b910c124d47520339efecbb6c59cec7c1f" +dependencies = [ + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6" +dependencies = [ + "ahash 0.8.11", + "atoi", + "byteorder", + "bytes", + "crc", + "crossbeam-queue", + "either", + "event-listener 2.5.3", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashlink", + "hex", + "indexmap 2.2.6", + "log", + "memchr", + "native-tls", + "once_cell", + "paste", + "percent-encoding", + "serde", + "sha2 0.10.8", + "smallvec", + "sqlformat", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "url", +] + +[[package]] +name = "sqlx-macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 1.0.109", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8" +dependencies = [ + "dotenvy", + "either", + "heck 0.4.1", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2 0.10.8", + "sqlx-core", + "sqlx-sqlite", + "syn 1.0.109", + "tempfile", + "tokio", + "url", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa" +dependencies = [ + "atoi", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "sqlx-core", + "tracing", + "url", + "urlencoding", +] + [[package]] name = "ss58-registry" version = "1.47.0" @@ -10158,6 +11055,7 @@ dependencies = [ "sharded-slab", "smallvec", "thread_local", + "time", "tracing", "tracing-core", "tracing-log 0.2.0", @@ -10374,6 +11272,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + [[package]] name = "unicode-width" version = "0.1.13" @@ -10386,6 +11290,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + [[package]] name = "universal-hash" version = "0.5.1" @@ -10432,6 +11342,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf-8" version = "0.7.6" diff --git a/Cargo.toml b/Cargo.toml index 6e6c400e3..0d525f966 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ type_complexity = "allow" unwrap-used = "deny" [workspace.dependencies] +async-trait = "0.1" cargo-husky = { version = "1", default-features = false } clap = "4.5.4" codec = { version = "3.2.2", default-features = false } @@ -152,6 +153,37 @@ substrate-build-script-utils = { git = "https://github.com/paritytech/polkadot-s substrate-fixed = { git = "https://github.com/opentensor/substrate-fixed.git", tag = "v0.5.9" } substrate-frame-rpc-system = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } substrate-wasm-builder = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } + +sc-consensus-manual-seal = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } +sc-network-sync = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } +substrate-prometheus-endpoint = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } + +# Frontier +fp-evm = { git = "https://github.com/gztensor/frontier", branch = "polkadot-v1.11.0-subtensor", default-features = false } +fp-rpc = { git = "https://github.com/gztensor/frontier", branch = "polkadot-v1.11.0-subtensor", default-features = false } +fp-self-contained = { git = "https://github.com/gztensor/frontier", branch = "polkadot-v1.11.0-subtensor", default-features = false, features = [ + "serde", +] } +fp-account = { git = "https://github.com/gztensor/frontier", branch = "polkadot-v1.11.0-subtensor", default-features = false } +fc-storage = { git = "https://github.com/gztensor/frontier", branch = "polkadot-v1.11.0-subtensor", default-features = false } +fc-db = { git = "https://github.com/gztensor/frontier", branch = "polkadot-v1.11.0-subtensor", default-features = false } +fc-consensus = { git = "https://github.com/gztensor/frontier", branch = "polkadot-v1.11.0-subtensor", default-features = false } +fp-dynamic-fee = { git = "https://github.com/gztensor/frontier", branch = "polkadot-v1.11.0-subtensor", default-features = false } +fc-api = { git = "https://github.com/gztensor/frontier", branch = "polkadot-v1.11.0-subtensor", default-features = false } +fc-rpc = { git = "https://github.com/gztensor/frontier", branch = "polkadot-v1.11.0-subtensor", default-features = false } +fc-rpc-core = { git = "https://github.com/gztensor/frontier", branch = "polkadot-v1.11.0-subtensor", default-features = false } +fc-mapping-sync = { git = "https://github.com/gztensor/frontier", branch = "polkadot-v1.11.0-subtensor", default-features = false } + +# Frontier FRAME +pallet-base-fee = { git = "https://github.com/gztensor/frontier", branch = "polkadot-v1.11.0-subtensor", default-features = false } +pallet-dynamic-fee = { git = "https://github.com/gztensor/frontier", branch = "polkadot-v1.11.0-subtensor", default-features = false } +pallet-ethereum = { git = "https://github.com/gztensor/frontier", branch = "polkadot-v1.11.0-subtensor", default-features = false } +pallet-evm = { git = "https://github.com/gztensor/frontier", branch = "polkadot-v1.11.0-subtensor", default-features = false } +pallet-evm-chain-id = { git = "https://github.com/gztensor/frontier", branch = "polkadot-v1.11.0-subtensor", default-features = false } +pallet-evm-precompile-modexp = { git = "https://github.com/gztensor/frontier", branch = "polkadot-v1.11.0-subtensor", default-features = false } +pallet-evm-precompile-sha3fips = { git = "https://github.com/gztensor/frontier", branch = "polkadot-v1.11.0-subtensor", default-features = false } +pallet-evm-precompile-simple = { git = "https://github.com/gztensor/frontier", branch = "polkadot-v1.11.0-subtensor", default-features = false } +pallet-hotfix-sufficients = { git = "https://github.com/gztensor/frontier", branch = "polkadot-v1.11.0-subtensor", default-features = false } frame-metadata = "16" [profile.release] diff --git a/node/Cargo.toml b/node/Cargo.toml index 3c5c91b92..efccee28a 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -20,6 +20,7 @@ targets = ["x86_64-unknown-linux-gnu"] name = "node-subtensor" [dependencies] +async-trait = { workspace = true } clap = { workspace = true, features = ["derive"] } futures = { workspace = true, features = ["thread-pool"] } serde = { workspace = true, features = ["derive"] } @@ -74,6 +75,22 @@ pallet-transaction-payment-rpc = { workspace = true } frame-benchmarking = { workspace = true } frame-benchmarking-cli = { workspace = true } +# Needed for Frontier +sc-consensus-manual-seal = { workspace = true } +sc-network-sync = { workspace = true } +substrate-prometheus-endpoint = { workspace = true } + +# Frontier +fc-storage = { workspace = true } +fc-db = { workspace = true } +fc-consensus = { workspace = true } +fp-dynamic-fee = { workspace = true } +fc-api = { workspace = true } +fc-rpc = { workspace = true } +fc-rpc-core = { workspace = true } +fp-rpc = { workspace = true } +fc-mapping-sync = { workspace = true } + # Local Dependencies node-subtensor-runtime = { path = "../runtime" } subtensor-custom-rpc = { path = "../pallets/subtensor/rpc" } @@ -83,7 +100,22 @@ subtensor-custom-rpc-runtime-api = { path = "../pallets/subtensor/runtime-api" } substrate-build-script-utils = { workspace = true } [features] -default = [] +default = [ + "rocksdb", + "sql", + "txpool", +] +sql = [ + "fc-db/sql", + "fc-mapping-sync/sql", +] +rocksdb = [ + "sc-service/rocksdb", + "fc-db/rocksdb", + "fc-mapping-sync/rocksdb", +] +txpool = ["fc-rpc/txpool"] + # Dependencies that are only required if runtime benchmarking should be build. runtime-benchmarks = [ "node-subtensor-runtime/runtime-benchmarks", diff --git a/node/src/cli.rs b/node/src/cli.rs index 2c9c4c9fd..3e81fdd07 100644 --- a/node/src/cli.rs +++ b/node/src/cli.rs @@ -1,4 +1,5 @@ use sc_cli::RunCmd; +use crate::ethereum::EthConfiguration; #[derive(Debug, clap::Parser)] pub struct Cli { @@ -7,6 +8,13 @@ pub struct Cli { #[clap(flatten)] pub run: RunCmd, + + /// Choose sealing method. + #[arg(long, value_enum, ignore_case = true)] + pub sealing: Option, + + #[command(flatten)] + pub eth: EthConfiguration, } #[allow(clippy::large_enum_variant)] @@ -45,3 +53,13 @@ pub enum Subcommand { // Db meta columns information. ChainInfo(sc_cli::ChainInfoCmd), } + +/// Available Sealing methods. +#[derive(Copy, Clone, Debug, Default, clap::ValueEnum)] +pub enum Sealing { + /// Seal using rpc method. + #[default] + Manual, + /// Seal when transaction is executed. + Instant, +} diff --git a/node/src/command.rs b/node/src/command.rs index 5e591d8b1..f83997480 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -1,8 +1,9 @@ use crate::{ chain_spec, cli::{Cli, Subcommand}, - service, + ethereum::db_config_dir, service }; +use fc_db::{kv::frontier_database_dir, DatabaseSource}; #[cfg(feature = "runtime-benchmarks")] pub use crate::benchmarking::{inherent_benchmark_data, RemarkBuilder, TransferKeepAliveBuilder}; @@ -17,7 +18,8 @@ use sp_runtime::traits::HashingFor; use node_subtensor_runtime::Block; use sc_cli::SubstrateCli; -use sc_service::{Configuration, PartialComponents}; +use sc_service::Configuration; +use futures::TryFutureExt; impl SubstrateCli for Cli { fn impl_name() -> String { @@ -66,72 +68,97 @@ pub fn run() -> sc_cli::Result<()> { let runner = cli.create_runner(cmd)?; runner.sync_run(|config| cmd.run(config.chain_spec, config.network)) } - Some(Subcommand::CheckBlock(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let PartialComponents { - client, - task_manager, - import_queue, - .. - } = service::new_partial(&config)?; - Ok((cmd.run(client, import_queue), task_manager)) - }) - } - Some(Subcommand::ExportBlocks(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let PartialComponents { - client, - task_manager, - .. - } = service::new_partial(&config)?; - Ok((cmd.run(client, config.database), task_manager)) - }) - } - Some(Subcommand::ExportState(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let PartialComponents { - client, - task_manager, - .. - } = service::new_partial(&config)?; - Ok((cmd.run(client, config.chain_spec), task_manager)) - }) - } - Some(Subcommand::ImportBlocks(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let PartialComponents { - client, - task_manager, - import_queue, - .. - } = service::new_partial(&config)?; - Ok((cmd.run(client, import_queue), task_manager)) - }) - } + Some(Subcommand::CheckBlock(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|mut config| { + let (client, _, import_queue, task_manager, _) = + service::new_chain_ops(&mut config, &cli.eth)?; + Ok((cmd.run(client, import_queue), task_manager)) + }) + } + Some(Subcommand::ExportBlocks(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|mut config| { + let (client, _, _, task_manager, _) = + service::new_chain_ops(&mut config, &cli.eth)?; + Ok((cmd.run(client, config.database), task_manager)) + }) + } + Some(Subcommand::ExportState(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|mut config| { + let (client, _, _, task_manager, _) = + service::new_chain_ops(&mut config, &cli.eth)?; + Ok((cmd.run(client, config.chain_spec), task_manager)) + }) + } + Some(Subcommand::ImportBlocks(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|mut config| { + let (client, _, import_queue, task_manager, _) = + service::new_chain_ops(&mut config, &cli.eth)?; + Ok((cmd.run(client, import_queue), task_manager)) + }) + } Some(Subcommand::PurgeChain(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.sync_run(|config| cmd.run(config.database)) - } - Some(Subcommand::Revert(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let PartialComponents { - client, - task_manager, - backend, - .. - } = service::new_partial(&config)?; - let aux_revert = Box::new(|client, _, blocks| { - sc_consensus_grandpa::revert(client, blocks)?; - Ok(()) - }); - Ok((cmd.run(client, backend, Some(aux_revert)), task_manager)) - }) + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| { + // Remove Frontier offchain db + let db_config_dir = db_config_dir(&config); + match cli.eth.frontier_backend_type { + crate::ethereum::BackendType::KeyValue => { + let frontier_database_config = match config.database { + DatabaseSource::RocksDb { .. } => DatabaseSource::RocksDb { + path: frontier_database_dir(&db_config_dir, "db"), + cache_size: 0, + }, + DatabaseSource::ParityDb { .. } => DatabaseSource::ParityDb { + path: frontier_database_dir(&db_config_dir, "paritydb"), + }, + _ => { + return Err(format!( + "Cannot purge `{:?}` database", + config.database + ) + .into()) + } + }; + cmd.run(frontier_database_config)?; + } + crate::ethereum::BackendType::Sql => { + let db_path = db_config_dir.join("sql"); + match std::fs::remove_dir_all(&db_path) { + Ok(_) => { + println!("{:?} removed.", &db_path); + } + Err(ref err) if err.kind() == std::io::ErrorKind::NotFound => { + eprintln!("{:?} did not exist.", &db_path); + } + Err(err) => { + return Err(format!( + "Cannot purge `{:?}` database: {:?}", + db_path, err, + ) + .into()) + } + }; + } + }; + cmd.run(config.database) + }) } + Some(Subcommand::Revert(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|mut config| { + let (client, backend, _, task_manager, _) = + service::new_chain_ops(&mut config, &cli.eth)?; + let aux_revert = Box::new(move |client, _, blocks| { + sc_consensus_grandpa::revert(client, blocks)?; + Ok(()) + }); + Ok((cmd.run(client, backend, Some(aux_revert)), task_manager)) + }) + } #[cfg(feature = "runtime-benchmarks")] Some(Subcommand::Benchmark(cmd)) => { let runner = cli.create_runner(cmd)?; @@ -210,8 +237,12 @@ pub fn run() -> sc_cli::Result<()> { let runner = cli.create_runner(&cli.run)?; runner.run_node_until_exit(|config| async move { let config = override_default_heap_pages(config, 60_000); - service::new_full::(config) - .map_err(sc_cli::Error::Service) + service::new_full::( + config, + cli.eth, + cli.sealing + ).map_err(Into::into) + .await }) } } diff --git a/node/src/ethereum.rs b/node/src/ethereum.rs new file mode 100644 index 000000000..163c68ff1 --- /dev/null +++ b/node/src/ethereum.rs @@ -0,0 +1,348 @@ +use std::{collections::BTreeMap, sync::{Arc, Mutex}}; +use std::path::PathBuf; +use std::time::Duration; +use jsonrpsee::RpcModule; +use sc_network_sync::SyncingService; +use sc_rpc::SubscriptionTaskExecutor; +use sc_service::{error::Error as ServiceError, Configuration, TaskManager}; +use futures::future; +pub use fc_rpc::{EthTask, EthConfig}; +use sc_client_api::{ + backend::{Backend, StorageProvider}, + client::BlockchainEvents, + AuxStore, UsageProvider, +}; +use futures::StreamExt; +use sp_consensus_aura::AuraApi; +use sp_core::H256; +use crate::rpc::EthDeps; +use sp_runtime::traits::Block as BlockT; +use sp_api::{CallApiAt, ProvideRuntimeApi}; +use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_block_builder::BlockBuilder as BlockBuilderApi; +use fp_rpc::{ConvertTransaction, ConvertTransactionRuntimeApi, EthereumRuntimeRPCApi}; +use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; +use sc_transaction_pool::ChainApi; +use sc_transaction_pool_api::TransactionPool; +use sp_inherents::CreateInherentDataProviders; + +/// Frontier DB backend type. +pub use fc_storage::{StorageOverride, StorageOverrideHandler}; +pub use fc_consensus::FrontierBlockImport; +pub use fc_rpc_core::types::{FeeHistoryCache, FeeHistoryCacheLimit, FilterPool}; + +use node_subtensor_runtime::opaque::Block; + +use crate::service::{FullBackend, FullClient}; + +pub type FrontierBackend = fc_db::Backend; + +/// Avalailable frontier backend types. +#[derive(Debug, Copy, Clone, Default, clap::ValueEnum)] +pub enum BackendType { + /// Either RocksDb or ParityDb as per inherited from the global backend settings. + #[default] + KeyValue, + /// Sql database with custom log indexing. + Sql, +} + +/// The ethereum-compatibility configuration used to run a node. +#[derive(Clone, Debug, clap::Parser)] +pub struct EthConfiguration { + /// Maximum number of logs in a query. + #[arg(long, default_value = "10000")] + pub max_past_logs: u32, + + /// Maximum fee history cache size. + #[arg(long, default_value = "2048")] + pub fee_history_limit: u64, + + #[arg(long)] + pub enable_dev_signer: bool, + + /// The dynamic-fee pallet target gas price set by block author + #[arg(long, default_value = "1")] + pub target_gas_price: u64, + + /// Maximum allowed gas limit will be `block.gas_limit * execute_gas_limit_multiplier` + /// when using eth_call/eth_estimateGas. + #[arg(long, default_value = "10")] + pub execute_gas_limit_multiplier: u64, + + /// Size in bytes of the LRU cache for block data. + #[arg(long, default_value = "50")] + pub eth_log_block_cache: usize, + + /// Size in bytes of the LRU cache for transactions statuses data. + #[arg(long, default_value = "50")] + pub eth_statuses_cache: usize, + + /// Sets the frontier backend type (KeyValue or Sql) + #[arg(long, value_enum, ignore_case = true, default_value_t = BackendType::default())] + pub frontier_backend_type: BackendType, + + // Sets the SQL backend's pool size. + #[arg(long, default_value = "100")] + pub frontier_sql_backend_pool_size: u32, + + /// Sets the SQL backend's query timeout in number of VM ops. + #[arg(long, default_value = "10000000")] + pub frontier_sql_backend_num_ops_timeout: u32, + + /// Sets the SQL backend's auxiliary thread limit. + #[arg(long, default_value = "4")] + pub frontier_sql_backend_thread_count: u32, + + /// Sets the SQL backend's query timeout in number of VM ops. + /// Default value is 200MB. + #[arg(long, default_value = "209715200")] + pub frontier_sql_backend_cache_size: u64, +} + +pub fn db_config_dir(config: &Configuration) -> PathBuf { + config.base_path.config_dir(config.chain_spec.id()) +} + +pub struct FrontierPartialComponents { + pub filter_pool: Option, + pub fee_history_cache: FeeHistoryCache, + pub fee_history_cache_limit: FeeHistoryCacheLimit, +} + +pub fn new_frontier_partial( + config: &EthConfiguration, +) -> Result { + Ok(FrontierPartialComponents { + filter_pool: Some(Arc::new(Mutex::new(BTreeMap::new()))), + fee_history_cache: Arc::new(Mutex::new(BTreeMap::new())), + fee_history_cache_limit: config.fee_history_limit, + }) +} + +pub async fn spawn_frontier_tasks( + task_manager: &TaskManager, + client: Arc, + backend: Arc, + frontier_backend: Arc>, + filter_pool: Option, + storage_override: Arc>, + fee_history_cache: FeeHistoryCache, + fee_history_cache_limit: FeeHistoryCacheLimit, + sync: Arc>, + pubsub_notification_sinks: Arc< + fc_mapping_sync::EthereumBlockNotificationSinks< + fc_mapping_sync::EthereumBlockNotification, + >, + >, +) // where + // RuntimeApi: ConstructRuntimeApi, + // RuntimeApi: Send + Sync + 'static, + // RuntimeApi::RuntimeApi: EthCompatRuntimeApiCollection, + // Executor: NativeExecutionDispatch + 'static, +{ + // Spawn main mapping sync worker background task. + match &*frontier_backend { + fc_db::Backend::KeyValue(b) => { + task_manager.spawn_essential_handle().spawn( + "frontier-mapping-sync-worker", + Some("frontier"), + fc_mapping_sync::kv::MappingSyncWorker::new( + client.import_notification_stream(), + Duration::new(6, 0), + client.clone(), + backend, + storage_override.clone(), + b.clone(), + 3, + 0, + fc_mapping_sync::SyncStrategy::Normal, + sync, + pubsub_notification_sinks, + ) + .for_each(|()| future::ready(())), + ); + } + fc_db::Backend::Sql(b) => { + task_manager.spawn_essential_handle().spawn_blocking( + "frontier-mapping-sync-worker", + Some("frontier"), + fc_mapping_sync::sql::SyncWorker::run( + client.clone(), + backend, + b.clone(), + client.import_notification_stream(), + fc_mapping_sync::sql::SyncWorkerConfig { + read_notification_timeout: Duration::from_secs(30), + check_indexed_blocks_interval: Duration::from_secs(60), + }, + fc_mapping_sync::SyncStrategy::Parachain, + sync, + pubsub_notification_sinks, + ), + ); + } + } + + // Spawn Frontier EthFilterApi maintenance task. + if let Some(filter_pool) = filter_pool { + // Each filter is allowed to stay in the pool for 100 blocks. + const FILTER_RETAIN_THRESHOLD: u64 = 100; + task_manager.spawn_essential_handle().spawn( + "frontier-filter-pool", + Some("frontier"), + EthTask::filter_pool_task(client.clone(), filter_pool, FILTER_RETAIN_THRESHOLD), + ); + } + + // Spawn Frontier FeeHistory cache maintenance task. + task_manager.spawn_essential_handle().spawn( + "frontier-fee-history", + Some("frontier"), + EthTask::fee_history_task( + client, + storage_override, + fee_history_cache, + fee_history_cache_limit, + ), + ); +} + +/// Instantiate Ethereum-compatible RPC extensions. +pub fn create_eth( + mut io: RpcModule<()>, + deps: EthDeps, + subscription_task_executor: SubscriptionTaskExecutor, + pubsub_notification_sinks: Arc< + fc_mapping_sync::EthereumBlockNotificationSinks< + fc_mapping_sync::EthereumBlockNotification, + >, + >, +) -> Result, Box> +where + B: BlockT, + C: CallApiAt + ProvideRuntimeApi, + C::Api: AuraApi + + BlockBuilderApi + + ConvertTransactionRuntimeApi + + EthereumRuntimeRPCApi, + C: HeaderBackend + HeaderMetadata, + C: BlockchainEvents + AuxStore + UsageProvider + StorageProvider + 'static, + BE: Backend + 'static, + P: TransactionPool + 'static, + A: ChainApi + 'static, + CT: ConvertTransaction<::Extrinsic> + Send + Sync + 'static, + CIDP: CreateInherentDataProviders + Send + 'static, + EC: EthConfig, +{ + use fc_rpc::{ + pending::AuraConsensusDataProvider, Debug, DebugApiServer, Eth, EthApiServer, EthDevSigner, + EthFilter, EthFilterApiServer, EthPubSub, EthPubSubApiServer, EthSigner, Net, NetApiServer, + Web3, Web3ApiServer, + }; + #[cfg(feature = "txpool")] + use fc_rpc::{TxPool, TxPoolApiServer}; + + let EthDeps { + client, + pool, + graph, + converter, + is_authority, + enable_dev_signer, + network, + sync, + frontier_backend, + storage_override, + block_data_cache, + filter_pool, + max_past_logs, + fee_history_cache, + fee_history_cache_limit, + execute_gas_limit_multiplier, + forced_parent_hashes, + pending_create_inherent_data_providers, + } = deps; + + let mut signers = Vec::new(); + if enable_dev_signer { + signers.push(Box::new(EthDevSigner::new()) as Box); + } + + io.merge( + Eth::::new( + client.clone(), + pool.clone(), + graph.clone(), + converter, + sync.clone(), + signers, + storage_override.clone(), + frontier_backend.clone(), + is_authority, + block_data_cache.clone(), + fee_history_cache, + fee_history_cache_limit, + execute_gas_limit_multiplier, + forced_parent_hashes, + pending_create_inherent_data_providers, + Some(Box::new(AuraConsensusDataProvider::new(client.clone()))), + ) + .replace_config::() + .into_rpc(), + )?; + + if let Some(filter_pool) = filter_pool { + io.merge( + EthFilter::new( + client.clone(), + frontier_backend.clone(), + graph.clone(), + filter_pool, + 500_usize, // max stored filters + max_past_logs, + block_data_cache.clone(), + ) + .into_rpc(), + )?; + } + + io.merge( + EthPubSub::new( + pool, + client.clone(), + sync, + subscription_task_executor, + storage_override.clone(), + pubsub_notification_sinks, + ) + .into_rpc(), + )?; + + io.merge( + Net::new( + client.clone(), + network, + // Whether to format the `peer_count` response as Hex (default) or not. + true, + ) + .into_rpc(), + )?; + + io.merge(Web3::new(client.clone()).into_rpc())?; + + io.merge( + Debug::new( + client.clone(), + frontier_backend, + storage_override, + block_data_cache, + ) + .into_rpc(), + )?; + + #[cfg(feature = "txpool")] + io.merge(TxPool::new(client, graph).into_rpc())?; + + Ok(io) +} \ No newline at end of file diff --git a/node/src/lib.rs b/node/src/lib.rs index f117b8aae..3e0f3a11a 100644 --- a/node/src/lib.rs +++ b/node/src/lib.rs @@ -1,3 +1,5 @@ pub mod chain_spec; pub mod rpc; pub mod service; +pub mod ethereum; +pub mod cli; \ No newline at end of file diff --git a/node/src/main.rs b/node/src/main.rs index a79d48b1b..00db180da 100644 --- a/node/src/main.rs +++ b/node/src/main.rs @@ -9,6 +9,7 @@ mod benchmarking; mod cli; mod command; mod rpc; +mod ethereum; fn main() -> sc_cli::Result<()> { command::run() diff --git a/node/src/rpc.rs b/node/src/rpc.rs index 54f82447f..885418132 100644 --- a/node/src/rpc.rs +++ b/node/src/rpc.rs @@ -5,66 +5,138 @@ #![warn(missing_docs)] -use std::sync::Arc; +use std::{collections::BTreeMap, sync::Arc}; + +use fp_rpc::{ConvertTransactionRuntimeApi, EthereumRuntimeRPCApi}; +use futures::channel::mpsc; use jsonrpsee::RpcModule; -use node_subtensor_runtime::{opaque::Block, AccountId, Balance, BlockNumber, Hash, Index}; -use sc_consensus_grandpa::FinalityProofProvider; +use node_subtensor_runtime::{opaque::Block, AccountId, Balance, Hash, Nonce}; +use sc_consensus_manual_seal::EngineCommand; +use sc_network::service::traits::NetworkService; +use sc_network_sync::SyncingService; use sc_transaction_pool_api::TransactionPool; -use sp_api::ProvideRuntimeApi; +use sp_api::{CallApiAt, ProvideRuntimeApi}; use sp_block_builder::BlockBuilder; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; - +use sp_runtime::traits::Block as BlockT; +use sc_transaction_pool::{ChainApi, Pool}; +use fc_storage::StorageOverride; +pub use fc_rpc::{EthBlockDataCacheTask, EthConfig}; +pub use fc_rpc_core::types::{FeeHistoryCache, FeeHistoryCacheLimit, FilterPool}; +use sp_core::H256; +use sp_inherents::CreateInherentDataProviders; +use sc_rpc::SubscriptionTaskExecutor; pub use sc_rpc_api::DenyUnsafe; +use sc_client_api::{ + backend::{Backend, StorageProvider}, + client::BlockchainEvents, + AuxStore, UsageProvider, +}; +use sp_consensus_aura::AuraApi; +use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use crate::ethereum::create_eth; + +/// Extra dependencies for Ethereum compatibility. +pub struct EthDeps { + /// The client instance to use. + pub client: Arc, + /// Transaction pool instance. + pub pool: Arc

, + /// Graph pool instance. + pub graph: Arc>, + /// Ethereum transaction converter. + pub converter: Option, + /// The Node authority flag + pub is_authority: bool, + /// Whether to enable dev signer + pub enable_dev_signer: bool, + /// Network service + pub network: Arc, + /// Chain syncing service + pub sync: Arc>, + /// Frontier Backend. + pub frontier_backend: Arc>, + /// Ethereum data access overrides. + pub storage_override: Arc>, + /// Cache for Ethereum block data. + pub block_data_cache: Arc>, + /// EthFilterApi pool. + pub filter_pool: Option, + /// Maximum number of logs in a query. + pub max_past_logs: u32, + /// Fee history cache. + pub fee_history_cache: FeeHistoryCache, + /// Maximum fee history cache size. + pub fee_history_cache_limit: FeeHistoryCacheLimit, + /// Maximum allowed gas limit will be ` block.gas_limit * execute_gas_limit_multiplier` when + /// using eth_call/eth_estimateGas. + pub execute_gas_limit_multiplier: u64, + /// Mandated parent hashes for a given block hash. + pub forced_parent_hashes: Option>, + /// Something that can create the inherent data providers for pending state + pub pending_create_inherent_data_providers: CIDP, +} + +/// Default Eth RPC configuration +pub struct DefaultEthConfig(std::marker::PhantomData<(C, BE)>); -/// Dependencies for GRANDPA -pub struct GrandpaDeps { - /// Voting round info. - pub shared_voter_state: sc_consensus_grandpa::SharedVoterState, - /// Authority set info. - pub shared_authority_set: sc_consensus_grandpa::SharedAuthoritySet, - /// Receives notifications about justification events from Grandpa. - pub justification_stream: sc_consensus_grandpa::GrandpaJustificationStream, - /// Executor to drive the subscription manager in the Grandpa RPC handler. - pub subscription_executor: sc_rpc::SubscriptionTaskExecutor, - /// Finality proof provider. - pub finality_provider: Arc>, +impl EthConfig for DefaultEthConfig +where + C: StorageProvider + Sync + Send + 'static, + BE: Backend + 'static, +{ + type EstimateGasAdapter = (); + type RuntimeStorageOverride = + fc_rpc::frontier_backend_client::SystemAccountId20StorageOverride; } /// Full client dependencies. -pub struct FullDeps { +pub struct FullDeps { /// The client instance to use. pub client: Arc, /// Transaction pool instance. pub pool: Arc

, /// Whether to deny unsafe calls pub deny_unsafe: DenyUnsafe, - /// Grandpa block import setup. - pub grandpa: GrandpaDeps, - /// Backend used by the node. - pub _backend: Arc, + /// Manual seal command sink + pub command_sink: Option>>, + /// Ethereum-compatibility specific dependencies. + pub eth: EthDeps, } /// Instantiate all full RPC extensions. -pub fn create_full( - deps: FullDeps, +pub fn create_full( + deps: FullDeps, + subscription_task_executor: SubscriptionTaskExecutor, + pubsub_notification_sinks: Arc< + fc_mapping_sync::EthereumBlockNotificationSinks< + fc_mapping_sync::EthereumBlockNotification, + >, + >, ) -> Result, Box> where - C: ProvideRuntimeApi, - C: HeaderBackend + HeaderMetadata + 'static, - C: Send + Sync + 'static, - C::Api: substrate_frame_rpc_system::AccountNonceApi, - C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, - C::Api: BlockBuilder, - C::Api: subtensor_custom_rpc_runtime_api::DelegateInfoRuntimeApi, - C::Api: subtensor_custom_rpc_runtime_api::NeuronInfoRuntimeApi, - C::Api: subtensor_custom_rpc_runtime_api::SubnetInfoRuntimeApi, - C::Api: subtensor_custom_rpc_runtime_api::SubnetRegistrationRuntimeApi, - B: sc_client_api::Backend + Send + Sync + 'static, - P: TransactionPool + 'static, + C: CallApiAt + ProvideRuntimeApi, + C::Api: BlockBuilder, + C::Api: AuraApi, + C::Api: substrate_frame_rpc_system::AccountNonceApi, + C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, + C::Api: ConvertTransactionRuntimeApi, + C::Api: EthereumRuntimeRPCApi, + C::Api: subtensor_custom_rpc_runtime_api::DelegateInfoRuntimeApi, + C::Api: subtensor_custom_rpc_runtime_api::NeuronInfoRuntimeApi, + C::Api: subtensor_custom_rpc_runtime_api::SubnetInfoRuntimeApi, + C::Api: subtensor_custom_rpc_runtime_api::SubnetRegistrationRuntimeApi, + C: HeaderBackend + HeaderMetadata + 'static, + C: BlockchainEvents + AuxStore + UsageProvider + StorageProvider, + BE: Backend + 'static, + P: TransactionPool + 'static, + A: ChainApi + 'static, + CIDP: CreateInherentDataProviders + Send + 'static, + CT: fp_rpc::ConvertTransaction<::Extrinsic> + Send + Sync + 'static, { use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; - use sc_consensus_grandpa_rpc::{Grandpa, GrandpaApiServer}; + use sc_consensus_manual_seal::rpc::{ManualSeal, ManualSealApiServer}; use substrate_frame_rpc_system::{System, SystemApiServer}; use subtensor_custom_rpc::{SubtensorCustom, SubtensorCustomApiServer}; @@ -73,9 +145,9 @@ where client, pool, deny_unsafe, - grandpa, - _backend: _, - } = deps; + command_sink, + eth, + } = deps; // Custom RPC methods for Paratensor module.merge(SubtensorCustom::new(client.clone()).into_rpc())?; @@ -83,29 +155,26 @@ where module.merge(System::new(client.clone(), pool.clone(), deny_unsafe).into_rpc())?; module.merge(TransactionPayment::new(client).into_rpc())?; - let GrandpaDeps { - shared_voter_state, - shared_authority_set, - justification_stream, - subscription_executor, - finality_provider, - } = grandpa; - - module.merge( - Grandpa::new( - subscription_executor, - shared_authority_set.clone(), - shared_voter_state, - justification_stream, - finality_provider, - ) - .into_rpc(), - )?; - // Extend this RPC with a custom API by using the following syntax. // `YourRpcStruct` should have a reference to a client, which is needed // to call into the runtime. // `module.merge(YourRpcTrait::into_rpc(YourRpcStruct::new(ReferenceToClient, ...)))?;` + if let Some(command_sink) = command_sink { + module.merge( + // We provide the rpc handler with the sending end of the channel to allow the rpc + // send EngineCommands to the background block authorship task. + ManualSeal::new(command_sink).into_rpc(), + )?; + } + + // Ethereum compatibility RPCs + let module = create_eth::<_, _, _, _, _, _, _, DefaultEthConfig>( + module, + eth, + subscription_task_executor, + pubsub_notification_sinks, + )?; + Ok(module) } diff --git a/node/src/service.rs b/node/src/service.rs index afeededeb..beadbf6aa 100644 --- a/node/src/service.rs +++ b/node/src/service.rs @@ -1,23 +1,38 @@ //! Service and ServiceFactory implementation. Specialized wrapper over substrate service. -use futures::FutureExt; -use node_subtensor_runtime::{opaque::Block, RuntimeApi}; +use futures::{channel::mpsc, future, FutureExt}; +use node_subtensor_runtime::{Hash, opaque::Block, RuntimeApi, TransactionConverter}; use sc_client_api::{Backend, BlockBackend}; -use sc_consensus_aura::{ImportQueueParams, SlotProportion, StartAuraParams}; +use sc_consensus_aura::{SlotProportion, StartAuraParams}; +use sc_consensus::BasicQueue; use sc_consensus_grandpa::SharedVoterState; use sc_consensus_slots::BackoffAuthoringOnFinalizedHeadLagging; use sc_executor::sp_wasm_interface::{Function, HostFunctionRegistry, HostFunctions}; pub use sc_executor::NativeElseWasmExecutor; -use sc_service::{error::Error as ServiceError, Configuration, TaskManager, WarpSyncParams}; -use sc_telemetry::{Telemetry, TelemetryWorker}; +use sc_service::{error::Error as ServiceError, Configuration, TaskManager, PartialComponents}; +use sc_network_sync::strategy::warp::{WarpSyncParams, WarpSyncProvider}; +use sc_telemetry::{log, Telemetry, TelemetryHandle, TelemetryWorker}; use sc_transaction_pool_api::OffchainTransactionPoolFactory; use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; use std::{sync::Arc, time::Duration}; +use sp_core::U256; +use std::{cell::RefCell, path::Path}; +use sc_transaction_pool::FullPool; +use substrate_prometheus_endpoint::Registry; +use crate::ethereum::{ + FrontierBackend, StorageOverride, StorageOverrideHandler, EthConfiguration, FrontierBlockImport, BackendType, db_config_dir, + FrontierPartialComponents, new_frontier_partial, spawn_frontier_tasks +}; +use crate::cli::Sealing; /// The minimum period of blocks on which justifications will be /// imported and generated. const GRANDPA_JUSTIFICATION_PERIOD: u32 = 512; +type BasicImportQueue = sc_consensus::DefaultImportQueue; +type GrandpaBlockImport = + sc_consensus_grandpa::GrandpaBlockImport; + // Our native executor instance. pub struct ExecutorDispatch; @@ -54,31 +69,41 @@ impl sc_executor::NativeExecutionDispatch for ExecutorDispatch { pub(crate) type FullClient = sc_service::TFullClient>; -type FullBackend = sc_service::TFullBackend; +pub type FullBackend = sc_service::TFullBackend; type FullSelectChain = sc_consensus::LongestChain; +type BoxBlockImport = sc_consensus::BoxBlockImport; -pub fn new_partial( +pub fn new_partial( config: &Configuration, + eth_config: &EthConfiguration, + build_import_queue: BIQ, ) -> Result< sc_service::PartialComponents< FullClient, FullBackend, FullSelectChain, - sc_consensus::DefaultImportQueue, - sc_transaction_pool::FullPool, + BasicImportQueue, + FullPool, ( - sc_consensus_grandpa::GrandpaBlockImport< - FullBackend, - Block, - FullClient, - FullSelectChain, - >, + BoxBlockImport, sc_consensus_grandpa::LinkHalf, Option, + FrontierBackend, + Arc>, ), >, ServiceError, -> { +> +where + BIQ: FnOnce( + Arc, + &Configuration, + &EthConfiguration, + &TaskManager, + Option, + GrandpaBlockImport, + ) -> Result<(BasicImportQueue, BoxBlockImport), ServiceError>, +{ let telemetry = config .telemetry_endpoints .clone() @@ -125,30 +150,44 @@ pub fn new_partial( telemetry.as_ref().map(|x| x.handle()), )?; - let slot_duration = sc_consensus_aura::slot_duration(&*client)?; - - let import_queue = - sc_consensus_aura::import_queue::(ImportQueueParams { - block_import: grandpa_block_import.clone(), - justification_import: Some(Box::new(grandpa_block_import.clone())), - client: client.clone(), - create_inherent_data_providers: move |_, ()| async move { - let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); - - let slot = - sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( - *timestamp, - slot_duration, - ); - - Ok((slot, timestamp)) - }, - spawner: &task_manager.spawn_essential_handle(), - registry: config.prometheus_registry(), - check_for_equivocation: Default::default(), - telemetry: telemetry.as_ref().map(|x| x.handle()), - compatibility_mode: Default::default(), - })?; + let (import_queue, block_import) = build_import_queue( + client.clone(), + config, + eth_config, + &task_manager, + telemetry.as_ref().map(|x| x.handle()), + grandpa_block_import, + )?; + + let storage_override = Arc::new(StorageOverrideHandler::new(client.clone())); + let frontier_backend = match eth_config.frontier_backend_type { + BackendType::KeyValue => FrontierBackend::KeyValue(Arc::new(fc_db::kv::Backend::open( + Arc::clone(&client), + &config.database, + &db_config_dir(config), + )?)), + BackendType::Sql => { + let db_path = db_config_dir(config).join("sql"); + std::fs::create_dir_all(&db_path).expect("failed creating sql db directory"); + let backend = futures::executor::block_on(fc_db::sql::Backend::new( + fc_db::sql::BackendConfig::Sqlite(fc_db::sql::SqliteBackendConfig { + path: Path::new("sqlite:///") + .join(db_path) + .join("frontier.db3") + .to_str() + .unwrap(), + create_if_missing: true, + thread_count: eth_config.frontier_sql_backend_thread_count, + cache_size: eth_config.frontier_sql_backend_cache_size, + }), + eth_config.frontier_sql_backend_pool_size, + std::num::NonZeroU32::new(eth_config.frontier_sql_backend_num_ops_timeout), + storage_override.clone(), + )) + .unwrap_or_else(|err| panic!("failed creating sql backend: {:?}", err)); + FrontierBackend::Sql(Arc::new(backend)) + } + }; Ok(sc_service::PartialComponents { client, @@ -158,16 +197,95 @@ pub fn new_partial( keystore_container, select_chain, transaction_pool, - other: (grandpa_block_import, grandpa_link, telemetry), + other: ( + block_import, + grandpa_link, + telemetry, + frontier_backend, + storage_override, + ), }) } +/// Build the import queue for the template runtime (aura + grandpa). +pub fn build_aura_grandpa_import_queue( + client: Arc, + config: &Configuration, + eth_config: &EthConfiguration, + task_manager: &TaskManager, + telemetry: Option, + grandpa_block_import: GrandpaBlockImport, +) -> Result<(BasicImportQueue, BoxBlockImport), ServiceError> +{ + let frontier_block_import = + FrontierBlockImport::new(grandpa_block_import.clone(), client.clone()); + + let slot_duration = sc_consensus_aura::slot_duration(&*client)?; + let target_gas_price = eth_config.target_gas_price; + let create_inherent_data_providers = move |_, ()| async move { + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + let slot = + sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); + let dynamic_fee = fp_dynamic_fee::InherentDataProvider(U256::from(target_gas_price)); + Ok((slot, timestamp, dynamic_fee)) + }; + + let import_queue = sc_consensus_aura::import_queue::( + sc_consensus_aura::ImportQueueParams { + block_import: frontier_block_import.clone(), + justification_import: Some(Box::new(grandpa_block_import)), + client, + create_inherent_data_providers, + spawner: &task_manager.spawn_essential_handle(), + registry: config.prometheus_registry(), + check_for_equivocation: Default::default(), + telemetry, + compatibility_mode: sc_consensus_aura::CompatibilityMode::None, + }, + ) + .map_err::(Into::into)?; + + Ok((import_queue, Box::new(frontier_block_import))) +} + +/// Build the import queue for the template runtime (manual seal). +pub fn build_manual_seal_import_queue( + client: Arc, + config: &Configuration, + _eth_config: &EthConfiguration, + task_manager: &TaskManager, + _telemetry: Option, + _grandpa_block_import: GrandpaBlockImport, +) -> Result<(BasicImportQueue, BoxBlockImport), ServiceError> +{ + let frontier_block_import = FrontierBlockImport::new(client.clone(), client); + Ok(( + sc_consensus_manual_seal::import_queue( + Box::new(frontier_block_import.clone()), + &task_manager.spawn_essential_handle(), + config.prometheus_registry(), + ), + Box::new(frontier_block_import), + )) +} + // Builds a new service for a full client. -pub fn new_full< +pub async fn new_full< N: sc_network::NetworkBackend::Hash>, >( - config: Configuration, + mut config: Configuration, + eth_config: EthConfiguration, + sealing: Option, ) -> Result { + let build_import_queue = if sealing.is_some() { + build_manual_seal_import_queue + } else { + build_aura_grandpa_import_queue + }; + let sc_service::PartialComponents { client, backend, @@ -176,8 +294,20 @@ pub fn new_full< keystore_container, select_chain, transaction_pool, - other: (block_import, grandpa_link, mut telemetry), - } = new_partial(&config)?; + other: ( + block_import, + grandpa_link, + mut telemetry, + frontier_backend, + storage_override, + ), + } = new_partial(&config, ð_config, build_import_queue)?; + + let FrontierPartialComponents { + filter_pool, + fee_history_cache, + fee_history_cache_limit, + } = new_frontier_partial(ð_config)?; let mut net_config = sc_network::config::FullNetworkConfiguration::< Block, @@ -202,13 +332,19 @@ pub fn new_full< metrics.clone(), peer_store_handle, ); - net_config.add_notification_protocol(grandpa_protocol_config); - let warp_sync = Arc::new(sc_consensus_grandpa::warp_proof::NetworkProvider::new( - backend.clone(), - grandpa_link.shared_authority_set().clone(), - Vec::default(), - )); + let warp_sync_params = if sealing.is_some() { + None + } else { + net_config.add_notification_protocol(grandpa_protocol_config); + let warp_sync: Arc> = + Arc::new(sc_consensus_grandpa::warp_proof::NetworkProvider::new( + backend.clone(), + grandpa_link.shared_authority_set().clone(), + Vec::default(), + )); + Some(WarpSyncParams::WithProvider(warp_sync)) + }; let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = sc_service::build_network(sc_service::BuildNetworkParams { @@ -219,7 +355,7 @@ pub fn new_full< spawn_handle: task_manager.spawn_handle(), import_queue, block_announce_validator_builder: None, - warp_sync_params: Some(WarpSyncParams::WithProvider(warp_sync)), + warp_sync_params, block_relay: None, metrics, })?; @@ -245,15 +381,6 @@ pub fn new_full< ); } - let finality_proof_provider = sc_consensus_grandpa::FinalityProofProvider::new_for_service( - backend.clone(), - Some(grandpa_link.shared_authority_set().clone()), - ); - let rpc_backend = backend.clone(); - let justification_stream = grandpa_link.justification_stream(); - let shared_authority_set = grandpa_link.shared_authority_set().clone(); - let shared_voter_state = SharedVoterState::empty(); - let role = config.role.clone(); let force_authoring = config.force_authoring; let backoff_authoring_blocks = Some(BackoffAuthoringOnFinalizedHeadLagging { @@ -263,27 +390,103 @@ pub fn new_full< let name = config.network.node_name.clone(); let enable_grandpa = !config.disable_grandpa; let prometheus_registry = config.prometheus_registry().cloned(); + let frontier_backend = Arc::new(frontier_backend); + + // Channel for the rpc handler to communicate with the authorship task. + let (command_sink, commands_stream) = mpsc::channel(1000); + + // Sinks for pubsub notifications. + // Everytime a new subscription is created, a new mpsc channel is added to the sink pool. + // The MappingSyncWorker sends through the channel on block import and the subscription emits a notification to the subscriber on receiving a message through this channel. + // This way we avoid race conditions when using native substrate block import notification stream. + let pubsub_notification_sinks: fc_mapping_sync::EthereumBlockNotificationSinks< + fc_mapping_sync::EthereumBlockNotification, + > = Default::default(); + let pubsub_notification_sinks = Arc::new(pubsub_notification_sinks); + + // for ethereum-compatibility rpc. + config.rpc_id_provider = Some(Box::new(fc_rpc::EthereumSubIdProvider)); let rpc_extensions_builder = { let client = client.clone(); let pool = transaction_pool.clone(); + let network = network.clone(); + let sync_service = sync_service.clone(); + + let is_authority = role.is_authority(); + let enable_dev_signer = eth_config.enable_dev_signer; + let max_past_logs = eth_config.max_past_logs; + let execute_gas_limit_multiplier = eth_config.execute_gas_limit_multiplier; + let filter_pool = filter_pool.clone(); + let frontier_backend = frontier_backend.clone(); + let pubsub_notification_sinks = pubsub_notification_sinks.clone(); + let storage_override = storage_override.clone(); + let fee_history_cache = fee_history_cache.clone(); + let block_data_cache = Arc::new(fc_rpc::EthBlockDataCacheTask::new( + task_manager.spawn_handle(), + storage_override.clone(), + eth_config.eth_log_block_cache, + eth_config.eth_statuses_cache, + prometheus_registry.clone(), + )); + + let slot_duration = sc_consensus_aura::slot_duration(&*client)?; + let target_gas_price = eth_config.target_gas_price; + let pending_create_inherent_data_providers = move |_, ()| async move { + let current = sp_timestamp::InherentDataProvider::from_system_time(); + let next_slot = current.timestamp().as_millis() + slot_duration.as_millis(); + let timestamp = sp_timestamp::InherentDataProvider::new(next_slot.into()); + let slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); + let dynamic_fee = fp_dynamic_fee::InherentDataProvider(U256::from(target_gas_price)); + Ok((slot, timestamp, dynamic_fee)) + }; + Box::new( move |deny_unsafe, subscription_executor: sc_rpc::SubscriptionTaskExecutor| { + let eth_deps = crate::rpc::EthDeps { + client: client.clone(), + pool: pool.clone(), + graph: pool.pool().clone(), + converter: Some(TransactionConverter), + is_authority, + enable_dev_signer, + network: network.clone(), + sync: sync_service.clone(), + frontier_backend: match &*frontier_backend { + fc_db::Backend::KeyValue(b) => b.clone(), + fc_db::Backend::Sql(b) => b.clone(), + }, + storage_override: storage_override.clone(), + block_data_cache: block_data_cache.clone(), + filter_pool: filter_pool.clone(), + max_past_logs, + fee_history_cache: fee_history_cache.clone(), + fee_history_cache_limit, + execute_gas_limit_multiplier, + forced_parent_hashes: None, + pending_create_inherent_data_providers, + }; + let deps = crate::rpc::FullDeps { client: client.clone(), pool: pool.clone(), deny_unsafe, - grandpa: crate::rpc::GrandpaDeps { - shared_voter_state: shared_voter_state.clone(), - shared_authority_set: shared_authority_set.clone(), - justification_stream: justification_stream.clone(), - subscription_executor: subscription_executor.clone(), - finality_provider: finality_proof_provider.clone(), + command_sink: if sealing.is_some() { + Some(command_sink.clone()) + } else { + None }, - _backend: rpc_backend.clone(), + eth: eth_deps, }; - crate::rpc::create_full(deps).map_err(Into::into) + crate::rpc::create_full( + deps, + subscription_executor, + pubsub_notification_sinks.clone(), + ).map_err(Into::into) }, ) }; @@ -295,7 +498,7 @@ pub fn new_full< task_manager: &mut task_manager, transaction_pool: transaction_pool.clone(), rpc_builder: rpc_extensions_builder, - backend, + backend: backend.clone(), system_rpc_tx, tx_handler_controller, sync_service: sync_service.clone(), @@ -303,7 +506,41 @@ pub fn new_full< telemetry: telemetry.as_mut(), })?; + spawn_frontier_tasks( + &task_manager, + client.clone(), + backend, + frontier_backend, + filter_pool, + storage_override, + fee_history_cache, + fee_history_cache_limit, + sync_service.clone(), + pubsub_notification_sinks, + ) + .await; + if role.is_authority() { + // manual-seal authorship + if let Some(sealing) = sealing { + run_manual_seal_authorship( + ð_config, + sealing, + client, + transaction_pool, + select_chain, + block_import, + &task_manager, + prometheus_registry.as_ref(), + telemetry.as_ref(), + commands_stream, + )?; + + network_starter.start_network(); + log::info!("Manual Seal Ready"); + return Ok(task_manager); + } + let proposer_factory = sc_basic_authorship::ProposerFactory::new( task_manager.spawn_handle(), client.clone(), @@ -403,3 +640,121 @@ pub fn new_full< network_starter.start_network(); Ok(task_manager) } + +pub fn new_chain_ops( + config: &mut Configuration, + eth_config: &EthConfiguration, +) -> Result< + ( + Arc, + Arc, + BasicQueue, + TaskManager, + FrontierBackend, + ), + ServiceError, +> { + config.keystore = sc_service::config::KeystoreConfig::InMemory; + let PartialComponents { + client, + backend, + import_queue, + task_manager, + other, + .. + } = new_partial::<_>( + config, + eth_config, + build_aura_grandpa_import_queue, + )?; + Ok((client, backend, import_queue, task_manager, other.3)) +} + +fn run_manual_seal_authorship( + eth_config: &EthConfiguration, + sealing: Sealing, + client: Arc, + transaction_pool: Arc>, + select_chain: FullSelectChain, + block_import: BoxBlockImport, + task_manager: &TaskManager, + prometheus_registry: Option<&Registry>, + telemetry: Option<&Telemetry>, + commands_stream: mpsc::Receiver>, +) -> Result<(), ServiceError> +{ + let proposer_factory = sc_basic_authorship::ProposerFactory::new( + task_manager.spawn_handle(), + client.clone(), + transaction_pool.clone(), + prometheus_registry, + telemetry.as_ref().map(|x| x.handle()), + ); + + thread_local!(static TIMESTAMP: RefCell = const { RefCell::new(0) }); + + /// Provide a mock duration starting at 0 in millisecond for timestamp inherent. + /// Each call will increment timestamp by slot_duration making Aura think time has passed. + struct MockTimestampInherentDataProvider; + + #[async_trait::async_trait] + impl sp_inherents::InherentDataProvider for MockTimestampInherentDataProvider { + async fn provide_inherent_data( + &self, + inherent_data: &mut sp_inherents::InherentData, + ) -> Result<(), sp_inherents::Error> { + TIMESTAMP.with(|x| { + *x.borrow_mut() += node_subtensor_runtime::SLOT_DURATION; + inherent_data.put_data(sp_timestamp::INHERENT_IDENTIFIER, &*x.borrow()) + }) + } + + async fn try_handle_error( + &self, + _identifier: &sp_inherents::InherentIdentifier, + _error: &[u8], + ) -> Option> { + // The pallet never reports error. + None + } + } + + let target_gas_price = eth_config.target_gas_price; + let create_inherent_data_providers = move |_, ()| async move { + let timestamp = MockTimestampInherentDataProvider; + let dynamic_fee = fp_dynamic_fee::InherentDataProvider(U256::from(target_gas_price)); + Ok((timestamp, dynamic_fee)) + }; + + let manual_seal = match sealing { + Sealing::Manual => future::Either::Left(sc_consensus_manual_seal::run_manual_seal( + sc_consensus_manual_seal::ManualSealParams { + block_import, + env: proposer_factory, + client, + pool: transaction_pool, + commands_stream, + select_chain, + consensus_data_provider: None, + create_inherent_data_providers, + }, + )), + Sealing::Instant => future::Either::Right(sc_consensus_manual_seal::run_instant_seal( + sc_consensus_manual_seal::InstantSealParams { + block_import, + env: proposer_factory, + client, + pool: transaction_pool, + select_chain, + consensus_data_provider: None, + create_inherent_data_providers, + }, + )), + }; + + // we spawn the future on a background thread managed by service. + task_manager + .spawn_essential_handle() + .spawn_blocking("manual-seal", None, manual_seal); + Ok(()) +} diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 8a2886eb1..9037db57a 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -93,6 +93,23 @@ pallet-registry = { default-features = false, path = "../pallets/registry" } # Metadata commitment pallet pallet-commitments = { default-features = false, path = "../pallets/commitments" } +# Frontier +fp-evm = { workspace = true } +fp-rpc = { workspace = true } +fp-self-contained = { workspace = true } + +# Frontier FRAME +pallet-base-fee = { workspace = true } +pallet-dynamic-fee = { workspace = true } +pallet-ethereum = { workspace = true } +pallet-evm = { workspace = true } +pallet-evm-chain-id = { workspace = true } +pallet-evm-precompile-modexp = { workspace = true } +pallet-evm-precompile-sha3fips = { workspace = true } +pallet-evm-precompile-simple = { workspace = true } +pallet-hotfix-sufficients = { workspace = true } +fp-account = { workspace = true } + [dev-dependencies] frame-metadata = { workspace = true } sp-io = { workspace = true } @@ -156,6 +173,21 @@ std = [ "log/std", "sp-storage/std", "sp-genesis-builder/std", + # Frontier + "fp-evm/std", + "fp-rpc/std", + "fp-self-contained/std", + # Frontier FRAME + "pallet-base-fee/std", + "pallet-dynamic-fee/std", + "pallet-ethereum/std", + "pallet-evm/std", + "pallet-evm-chain-id/std", + "pallet-evm-precompile-modexp/std", + "pallet-evm-precompile-sha3fips/std", + "pallet-evm-precompile-simple/std", + "pallet-hotfix-sufficients/std", + "fp-account/std", ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index b9f0c0fcb..56c527c8b 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -32,14 +32,18 @@ use scale_info::TypeInfo; use smallvec::smallvec; use sp_api::impl_runtime_apis; use sp_consensus_aura::sr25519::AuthorityId as AuraId; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata, RuntimeDebug}; +use sp_core::{ + crypto::{ByteArray, KeyTypeId}, + H160, H256, U256, OpaqueMetadata +}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{ - AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, NumberFor, One, Verify, + BlakeTwo256, Block as BlockT, IdentifyAccount, NumberFor, One, Verify, DispatchInfoOf, PostDispatchInfoOf, Dispatchable, + UniqueSaturatedInto, AccountIdLookup }, - transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, MultiSignature, AccountId32 + transaction_validity::{TransactionSource, TransactionValidity, TransactionValidityError}, + ApplyExtrinsicResult, MultiSignature, ConsensusEngineId, AccountId32 }; use sp_std::cmp::Ordering; use sp_std::prelude::*; @@ -52,8 +56,8 @@ use sp_version::RuntimeVersion; pub use frame_support::{ construct_runtime, parameter_types, traits::{ - ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, InstanceFilter, KeyOwnerProofSystem, - PrivilegeCmp, Randomness, StorageInfo, + ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, FindAuthor, InstanceFilter, KeyOwnerProofSystem, + PrivilegeCmp, Randomness, StorageInfo, OnFinalize, OnTimestampSet }, weights::{ constants::{ @@ -67,11 +71,24 @@ pub use frame_support::{ pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; pub use pallet_timestamp::Call as TimestampCall; -use pallet_transaction_payment::{FungibleAdapter, Multiplier}; +use pallet_transaction_payment::{ConstFeeMultiplier, Multiplier, FungibleAdapter}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; pub use sp_runtime::{Perbill, Permill}; +use core::marker::PhantomData; + +mod precompiles; +use precompiles::FrontierPrecompiles; + +// Frontier +use fp_evm::weight_per_gas; +use fp_rpc::TransactionStatus; +use pallet_ethereum::{Call::transact, PostLogContent, Transaction as EthereumTransaction}; +use pallet_evm::{ + Account as EVMAccount, FeeCalculator, Runner +}; + // Subtensor module pub use pallet_scheduler; pub use pallet_subtensor; @@ -174,6 +191,20 @@ pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); pub const HOURS: BlockNumber = MINUTES * 60; pub const DAYS: BlockNumber = HOURS * 24; +pub const MAXIMUM_BLOCK_WEIGHT: Weight = + Weight::from_parts(4u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX); + +/// Current approximation of the gas/s consumption considering +/// EVM execution over compiled WASM (on 4.4Ghz CPU). +/// Given the 500ms Weight, from which 75% only are used for transactions, +/// the total EVM execution gas limit is: GAS_PER_SECOND * 0.500 * 0.75 ~= 15_000_000. +/// Note: this value has been used in production by (and is copied from) the Moonbeam parachain. +pub const GAS_PER_SECOND: u64 = 40_000_000; + +/// Approximate ratio of the amount of Weight per Gas. +/// u64 works for approximations because Weight is a very small unit compared to gas. +pub const WEIGHT_PER_GAS: u64 = WEIGHT_REF_TIME_PER_SECOND / GAS_PER_SECOND; + // The version information used to identify this runtime when compiled natively. #[cfg(feature = "std")] pub fn native_version() -> NativeVersion { @@ -191,7 +222,7 @@ parameter_types! { // We allow for 2 seconds of compute with a 6 second average block time. pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights::with_sensible_defaults( - Weight::from_parts(4u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX), + MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, ); pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength @@ -390,7 +421,8 @@ where parameter_types! { // Used with LinearWeightToFee conversion. pub const FeeWeightRatio: u64 = 1; - pub const TransactionByteFee: u128 = 1; + pub const TransactionByteFee: u64 = 1; + pub const OperationalFeeMultiplier: u8 = 5; pub FeeMultiplier: Multiplier = Multiplier::one(); } @@ -417,19 +449,13 @@ impl OnUnbalanced< impl pallet_transaction_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; - //type TransactionByteFee = TransactionByteFee; type OnChargeTransaction = FungibleAdapter; - // Convert dispatch weight to a chargeable fee. type WeightToFee = LinearWeightToFee; - - type FeeMultiplierUpdate = (); - - type OperationalFeeMultiplier = ConstU8<1>; - + type OperationalFeeMultiplier = OperationalFeeMultiplier; type LengthToFee = IdentityFee; - //type FeeMultiplierUpdate = ConstFeeMultiplier; + type FeeMultiplierUpdate = ConstFeeMultiplier; } // Configure collective pallet for council @@ -625,7 +651,7 @@ parameter_types! { PartialOrd, Encode, Decode, - RuntimeDebug, + Debug, MaxEncodedLen, TypeInfo, )] @@ -1039,6 +1065,187 @@ impl pallet_admin_utils::Config for Runtime { type WeightInfo = pallet_admin_utils::weights::SubstrateWeight; } +// Define the ChainId +parameter_types! { + pub const SubtensorChainId: u64 = 0x03C4; // Unicode for lowercase tau +} + +impl pallet_evm_chain_id::Config for Runtime {} + +pub struct FindAuthorTruncated(PhantomData); +impl> FindAuthor for FindAuthorTruncated { + fn find_author<'a, I>(digests: I) -> Option + where + I: 'a + IntoIterator, + { + if let Some(author_index) = F::find_author(digests) { + let authority_id = + pallet_aura::Authorities::::get()[author_index as usize].clone(); + return Some(H160::from_slice(&authority_id.to_raw_vec()[4..24])); + } + None + } +} + +const BLOCK_GAS_LIMIT: u64 = 75_000_000; +const MAX_POV_SIZE: u64 = 5 * 1024 * 1024; + +parameter_types! { + pub BlockGasLimit: U256 = U256::from(BLOCK_GAS_LIMIT); + pub const GasLimitPovSizeRatio: u64 = BLOCK_GAS_LIMIT.saturating_div(MAX_POV_SIZE); + pub PrecompilesValue: FrontierPrecompiles = FrontierPrecompiles::<_>::new(); + pub WeightPerGas: Weight = Weight::from_parts(weight_per_gas(BLOCK_GAS_LIMIT, NORMAL_DISPATCH_RATIO, MILLISECS_PER_BLOCK), 0); + pub SuicideQuickClearLimit: u32 = 0; +} + +impl pallet_evm::Config for Runtime { + type FeeCalculator = BaseFee; + type GasWeightMapping = pallet_evm::FixedGasWeightMapping; + type WeightPerGas = WeightPerGas; + type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping; + type CallOrigin = pallet_evm::EnsureAddressTruncated; + type WithdrawOrigin = pallet_evm::EnsureAddressTruncated; + type AddressMapping = pallet_evm::HashedAddressMapping; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type PrecompilesType = FrontierPrecompiles; + type PrecompilesValue = PrecompilesValue; + type ChainId = SubtensorChainId; + type BlockGasLimit = BlockGasLimit; + type Runner = pallet_evm::runner::stack::Runner; + type OnChargeTransaction = (); + type OnCreate = (); + type FindAuthor = FindAuthorTruncated; + type GasLimitPovSizeRatio = GasLimitPovSizeRatio; + type SuicideQuickClearLimit = SuicideQuickClearLimit; + type Timestamp = Timestamp; + type WeightInfo = pallet_evm::weights::SubstrateWeight; +} + +parameter_types! { + pub const PostBlockAndTxnHashes: PostLogContent = PostLogContent::BlockAndTxnHashes; +} + +impl pallet_ethereum::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type StateRoot = pallet_ethereum::IntermediateStateRoot; + type PostLogContent = PostBlockAndTxnHashes; + type ExtraDataLength = ConstU32<30>; +} + +parameter_types! { + pub BoundDivision: U256 = U256::from(1024); +} + +impl pallet_dynamic_fee::Config for Runtime { + type MinGasPriceBoundDivisor = BoundDivision; +} + +parameter_types! { + pub DefaultBaseFeePerGas: U256 = U256::from(1_000_000_000); + pub DefaultElasticity: Permill = Permill::from_parts(125_000); +} +pub struct BaseFeeThreshold; +impl pallet_base_fee::BaseFeeThreshold for BaseFeeThreshold { + fn lower() -> Permill { + Permill::zero() + } + fn ideal() -> Permill { + Permill::from_parts(500_000) + } + fn upper() -> Permill { + Permill::from_parts(1_000_000) + } +} +impl pallet_base_fee::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Threshold = BaseFeeThreshold; + type DefaultBaseFeePerGas = DefaultBaseFeePerGas; + type DefaultElasticity = DefaultElasticity; +} + +#[derive(Clone)] +pub struct TransactionConverter; +impl fp_rpc::ConvertTransaction for TransactionConverter { + fn convert_transaction(&self, transaction: pallet_ethereum::Transaction) -> UncheckedExtrinsic { + UncheckedExtrinsic::new_unsigned( + pallet_ethereum::Call::::transact { transaction }.into(), + ) + } +} + +impl fp_rpc::ConvertTransaction for TransactionConverter { + fn convert_transaction( + &self, + transaction: pallet_ethereum::Transaction, + ) -> opaque::UncheckedExtrinsic { + let extrinsic = UncheckedExtrinsic::new_unsigned( + pallet_ethereum::Call::::transact { transaction }.into(), + ); + let encoded = extrinsic.encode(); + opaque::UncheckedExtrinsic::decode(&mut &encoded[..]) + .expect("Encoded extrinsic is always valid") + } +} + +impl fp_self_contained::SelfContainedCall for RuntimeCall { + type SignedInfo = H160; + + fn is_self_contained(&self) -> bool { + match self { + RuntimeCall::Ethereum(call) => call.is_self_contained(), + _ => false, + } + } + + fn check_self_contained(&self) -> Option> { + match self { + RuntimeCall::Ethereum(call) => call.check_self_contained(), + _ => None, + } + } + + fn validate_self_contained( + &self, + info: &Self::SignedInfo, + dispatch_info: &DispatchInfoOf, + len: usize, + ) -> Option { + match self { + RuntimeCall::Ethereum(call) => call.validate_self_contained(info, dispatch_info, len), + _ => None, + } + } + + fn pre_dispatch_self_contained( + &self, + info: &Self::SignedInfo, + dispatch_info: &DispatchInfoOf, + len: usize, + ) -> Option> { + match self { + RuntimeCall::Ethereum(call) => { + call.pre_dispatch_self_contained(info, dispatch_info, len) + } + _ => None, + } + } + + fn apply_self_contained( + self, + info: Self::SignedInfo, + ) -> Option>> { + match self { + call @ RuntimeCall::Ethereum(pallet_ethereum::Call::transact { .. }) => { + Some(call.dispatch(RuntimeOrigin::from( + pallet_ethereum::RawOrigin::EthereumTransaction(info), + ))) + } + _ => None, + } + } +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub struct Runtime @@ -1064,6 +1271,13 @@ construct_runtime!( Commitments: pallet_commitments = 18, AdminUtils: pallet_admin_utils = 19, SafeMode: pallet_safe_mode = 20, + + // Frontier + Ethereum: pallet_ethereum = 21, + EVM: pallet_evm = 22, + EVMChainId: pallet_evm_chain_id = 23, + DynamicFee: pallet_dynamic_fee = 24, + BaseFee: pallet_base_fee = 25, } ); @@ -1095,7 +1309,12 @@ type Migrations = // Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + fp_self_contained::UncheckedExtrinsic; + +/// Extrinsic type that has already been checked. +pub type CheckedExtrinsic = + fp_self_contained::CheckedExtrinsic; + // The payload being signed in transactions. pub type SignedPayload = generic::SignedPayload; // Executive: handles dispatch to the various modules. @@ -1308,6 +1527,256 @@ impl_runtime_apis! { } } + impl fp_rpc::EthereumRuntimeRPCApi for Runtime { + fn chain_id() -> u64 { + ::ChainId::get() + } + + fn account_basic(address: H160) -> EVMAccount { + let (account, _) = pallet_evm::Pallet::::account_basic(&address); + account + } + + fn gas_price() -> U256 { + let (gas_price, _) = ::FeeCalculator::min_gas_price(); + gas_price + } + + fn account_code_at(address: H160) -> Vec { + pallet_evm::AccountCodes::::get(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::AccountStorages::::get(address, H256::from_slice(&tmp[..])) + } + + fn call( + from: H160, + to: H160, + data: Vec, + value: U256, + gas_limit: U256, + max_fee_per_gas: Option, + max_priority_fee_per_gas: Option, + nonce: Option, + estimate: bool, + access_list: Option)>>, + ) -> Result { + use pallet_evm::GasWeightMapping as _; + + let config = if estimate { + let mut config = ::config().clone(); + config.estimate = true; + Some(config) + } else { + None + }; + + // Estimated encoded transaction size must be based on the heaviest transaction + // type (EIP1559Transaction) to be compatible with all transaction types. + let mut estimated_transaction_len = data.len() + + // pallet ethereum index: 1 + // transact call index: 1 + // Transaction enum variant: 1 + // chain_id 8 bytes + // nonce: 32 + // max_priority_fee_per_gas: 32 + // max_fee_per_gas: 32 + // gas_limit: 32 + // action: 21 (enum varianrt + call address) + // value: 32 + // access_list: 1 (empty vec size) + // 65 bytes signature + 258; + + if access_list.is_some() { + estimated_transaction_len += access_list.encoded_size(); + } + + + let gas_limit = if gas_limit > U256::from(u64::MAX) { + u64::MAX + } else { + gas_limit.low_u64() + }; + let without_base_extrinsic_weight = true; + + let (weight_limit, proof_size_base_cost) = + match ::GasWeightMapping::gas_to_weight( + gas_limit, + without_base_extrinsic_weight + ) { + weight_limit if weight_limit.proof_size() > 0 => { + (Some(weight_limit), Some(estimated_transaction_len as u64)) + } + _ => (None, None), + }; + + ::Runner::call( + from, + to, + data, + value, + gas_limit.unique_saturated_into(), + max_fee_per_gas, + max_priority_fee_per_gas, + nonce, + access_list.unwrap_or_default(), + false, + true, + weight_limit, + proof_size_base_cost, + config.as_ref().unwrap_or(::config()), + ).map_err(|err| err.error.into()) + } + + fn create( + from: H160, + data: Vec, + value: U256, + gas_limit: U256, + max_fee_per_gas: Option, + max_priority_fee_per_gas: Option, + nonce: Option, + estimate: bool, + access_list: Option)>>, + ) -> Result { + use pallet_evm::GasWeightMapping as _; + + let config = if estimate { + let mut config = ::config().clone(); + config.estimate = true; + Some(config) + } else { + None + }; + + + let mut estimated_transaction_len = data.len() + + // from: 20 + // value: 32 + // gas_limit: 32 + // nonce: 32 + // 1 byte transaction action variant + // chain id 8 bytes + // 65 bytes signature + 190; + + if max_fee_per_gas.is_some() { + estimated_transaction_len += 32; + } + if max_priority_fee_per_gas.is_some() { + estimated_transaction_len += 32; + } + if access_list.is_some() { + estimated_transaction_len += access_list.encoded_size(); + } + + + let gas_limit = if gas_limit > U256::from(u64::MAX) { + u64::MAX + } else { + gas_limit.low_u64() + }; + let without_base_extrinsic_weight = true; + + let (weight_limit, proof_size_base_cost) = + match ::GasWeightMapping::gas_to_weight( + gas_limit, + without_base_extrinsic_weight + ) { + weight_limit if weight_limit.proof_size() > 0 => { + (Some(weight_limit), Some(estimated_transaction_len as u64)) + } + _ => (None, None), + }; + + ::Runner::create( + from, + data, + value, + gas_limit.unique_saturated_into(), + max_fee_per_gas, + max_priority_fee_per_gas, + nonce, + access_list.unwrap_or_default(), + false, + true, + weight_limit, + proof_size_base_cost, + config.as_ref().unwrap_or(::config()), + ).map_err(|err| err.error.into()) + } + + fn current_transaction_statuses() -> Option> { + pallet_ethereum::CurrentTransactionStatuses::::get() + } + + fn current_block() -> Option { + pallet_ethereum::CurrentBlock::::get() + } + + fn current_receipts() -> Option> { + pallet_ethereum::CurrentReceipts::::get() + } + + fn current_all() -> ( + Option, + Option>, + Option> + ) { + ( + pallet_ethereum::CurrentBlock::::get(), + pallet_ethereum::CurrentReceipts::::get(), + pallet_ethereum::CurrentTransactionStatuses::::get() + ) + } + + fn extrinsic_filter( + xts: Vec<::Extrinsic>, + ) -> Vec { + xts.into_iter().filter_map(|xt| match xt.0.function { + RuntimeCall::Ethereum(transact { transaction }) => Some(transaction), + _ => None + }).collect::>() + } + + fn elasticity() -> Option { + Some(pallet_base_fee::Elasticity::::get()) + } + + fn gas_limit_multiplier_support() {} + + fn pending_block( + xts: Vec<::Extrinsic>, + ) -> (Option, Option>) { + for ext in xts.into_iter() { + let _ = Executive::apply_extrinsic(ext); + } + + Ethereum::on_finalize(System::block_number() + 1); + + ( + pallet_ethereum::CurrentBlock::::get(), + pallet_ethereum::CurrentTransactionStatuses::::get() + ) + } + } + + impl fp_rpc::ConvertTransactionRuntimeApi for Runtime { + fn convert_transaction(transaction: EthereumTransaction) -> ::Extrinsic { + UncheckedExtrinsic::new_unsigned( + pallet_ethereum::Call::::transact { transaction }.into(), + ) + } + } + #[cfg(feature = "runtime-benchmarks")] impl frame_benchmarking::Benchmark for Runtime { fn benchmark_metadata(extra: bool) -> ( diff --git a/runtime/src/precompiles/balance_transfer.rs b/runtime/src/precompiles/balance_transfer.rs new file mode 100644 index 000000000..9d2dbf36b --- /dev/null +++ b/runtime/src/precompiles/balance_transfer.rs @@ -0,0 +1,60 @@ +use sp_core::U256; +use pallet_evm::{ + ExitError, ExitSucceed, PrecompileFailure, PrecompileHandle, + PrecompileOutput, PrecompileResult +}; +use frame_system::RawOrigin; +use sp_std::vec; +use sp_runtime::traits::Dispatchable; + +use crate::{Runtime, RuntimeCall}; + +use crate::precompiles::{ bytes_to_account_id, get_method_id }; + +pub const BALANCE_TRANSFER_INDEX: u64 = 2048; + +pub struct BalanceTransferPrecompile; + +impl BalanceTransferPrecompile { + pub fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let txdata = handle.input(); + + // Match method ID: keccak256("transfer(bytes32)") + let method: &[u8] = &txdata[0..4]; + if get_method_id("transfer(bytes32)") == method { + // Forward all received value to the destination address + let amount: U256 = handle.context().apparent_value; + + // This is hardcoded hashed address mapping of + // 0x0000000000000000000000000000000000000800 to ss58 public key + // i.e. the contract sends funds it received to the destination address + // from the method parameter + let address_bytes_src: [u8; 32] = [ + 0x07, 0xec, 0x71, 0x2a, 0x5d, 0x38, 0x43, 0x4d, + 0xdd, 0x03, 0x3f, 0x8f, 0x02, 0x4e, 0xcd, 0xfc, + 0x4b, 0xb5, 0x95, 0x1c, 0x13, 0xc3, 0x08, 0x5c, + 0x39, 0x9c, 0x8a, 0x5f, 0x62, 0x93, 0x70, 0x5d + ]; + let address_bytes_dst: &[u8] = &txdata[4..36]; + let account_id_src = bytes_to_account_id(&address_bytes_src)?; + let account_id_dst = bytes_to_account_id(&address_bytes_dst)?; + + let call = RuntimeCall::Balances(pallet_balances::Call::::transfer_allow_death { + dest: account_id_dst.into(), + value: amount.as_u64(), + }); + + let result = call.dispatch(RawOrigin::Signed(account_id_src).into()); + if let Err(_) = result { + return Err(PrecompileFailure::Error { + exit_status: ExitError::OutOfFund + }); + } + } + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: vec![], + }) + } +} diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs new file mode 100644 index 000000000..1cbf3707b --- /dev/null +++ b/runtime/src/precompiles/mod.rs @@ -0,0 +1,101 @@ +use core::marker::PhantomData; +use sp_core::{ + crypto::ByteArray, hashing::keccak_256, H160 +}; +use sp_runtime::AccountId32; + +use pallet_evm::{ + ExitError, IsPrecompileResult, Precompile, PrecompileHandle, PrecompileResult, PrecompileSet, + PrecompileFailure +}; +use pallet_evm_precompile_modexp::Modexp; +use pallet_evm_precompile_sha3fips::Sha3FIPS256; +use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256}; + +// Include custom precompiles +mod balance_transfer; + +use balance_transfer::*; + +pub struct FrontierPrecompiles(PhantomData); + +impl FrontierPrecompiles +where + R: pallet_evm::Config, +{ + pub fn new() -> Self { + Self(Default::default()) + } + pub fn used_addresses() -> [H160; 8] { + [ + hash(1), + hash(2), + hash(3), + hash(4), + hash(5), + hash(1024), + hash(1025), + hash(BALANCE_TRANSFER_INDEX) + ] + } +} +impl PrecompileSet for FrontierPrecompiles +where + R: pallet_evm::Config, +{ + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option { + match handle.code_address() { + // Ethereum precompiles : + a if a == hash(1) => Some(ECRecover::execute(handle)), + a if a == hash(2) => Some(Sha256::execute(handle)), + a if a == hash(3) => Some(Ripemd160::execute(handle)), + a if a == hash(4) => Some(Identity::execute(handle)), + a if a == hash(5) => Some(Modexp::execute(handle)), + // Non-Frontier specific nor Ethereum precompiles : + a if a == hash(1024) => Some(Sha3FIPS256::execute(handle)), + a if a == hash(1025) => Some(ECRecoverPublicKey::execute(handle)), + a if a == hash(BALANCE_TRANSFER_INDEX) => Some(BalanceTransferPrecompile::execute(handle)), + _ => None, + } + } + + fn is_precompile(&self, address: H160, _gas: u64) -> IsPrecompileResult { + IsPrecompileResult::Answer { + is_precompile: Self::used_addresses().contains(&address), + extra_cost: 0, + } + } +} + +fn hash(a: u64) -> H160 { + H160::from_low_u64_be(a) +} + +/// Returns Ethereum method ID from an str method signature +/// +pub fn get_method_id(method_signature: &str) -> [u8; 4] { + // Calculate the full Keccak-256 hash of the method signature + let hash = keccak_256(method_signature.as_bytes()); + + // Extract the first 4 bytes to get the method ID + let method_id = [ + hash[0], + hash[1], + hash[2], + hash[3], + ]; + + method_id +} + +/// Convert bytes to AccountId32 with PrecompileFailure as Error +/// which consumes all gas +/// +pub fn bytes_to_account_id(account_id_bytes: &[u8]) -> Result { + AccountId32::from_slice(&account_id_bytes).map_err(|_| { + log::info!("Error parsing account id bytes {:?}", account_id_bytes); + PrecompileFailure::Error { + exit_status: ExitError::InvalidRange + } + }) +} diff --git a/runtime/src/precompiles/solidity/balanceTransfer.abi b/runtime/src/precompiles/solidity/balanceTransfer.abi new file mode 100644 index 000000000..99913b900 --- /dev/null +++ b/runtime/src/precompiles/solidity/balanceTransfer.abi @@ -0,0 +1,15 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "data", + "type": "bytes32" + } + ], + "name": "transfer", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] \ No newline at end of file diff --git a/runtime/src/precompiles/solidity/balanceTransfer.sol b/runtime/src/precompiles/solidity/balanceTransfer.sol new file mode 100644 index 000000000..42790b900 --- /dev/null +++ b/runtime/src/precompiles/solidity/balanceTransfer.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.8.0; + +address constant ISUBTENSOR_BALANCE_TRANSFER_ADDRESS = 0x0000000000000000000000000000000000000800; + +interface ISubtensorBalanceTransfer { + function transfer(bytes32 data) external payable; +} \ No newline at end of file diff --git a/runtime/src/precompiles/solidity/staking.abi b/runtime/src/precompiles/solidity/staking.abi new file mode 100644 index 000000000..c470bec5c --- /dev/null +++ b/runtime/src/precompiles/solidity/staking.abi @@ -0,0 +1,33 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "addStake", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "amount", + "type": "uint64" + } + ], + "name": "removeStake", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] \ No newline at end of file diff --git a/runtime/src/precompiles/solidity/staking.sol b/runtime/src/precompiles/solidity/staking.sol new file mode 100644 index 000000000..d806b08d0 --- /dev/null +++ b/runtime/src/precompiles/solidity/staking.sol @@ -0,0 +1,42 @@ +pragma solidity ^0.8.0; + +address constant ISTAKING_ADDRESS = 0x0000000000000000000000000000000000000801; + +interface IStaking { + /** + * @dev Adds a subtensor stake corresponding to the value sent with the transaction, associated + * with the `hotkey`. + * + * This function allows external accounts and contracts to stake TAO into the subtensor pallet, + * which effectively calls `add_stake` on the subtensor pallet with specified hotkey as a parameter + * and coldkey being the hashed address mapping of H160 sender address to Substrate ss58 address as + * implemented in Frontier HashedAddressMapping: + * https://github.com/polkadot-evm/frontier/blob/2e219e17a526125da003e64ef22ec037917083fa/frame/evm/src/lib.rs#L739 + * + * @param hotkey The hotkey public key (32 bytes). + * + * Requirements: + * - `hotkey` must be a valid hotkey registered on the network, ensuring that the stake is + * correctly attributed. + */ + function addStake(bytes32 hotkey) external payable; + + /** + * @dev Removes a subtensor stake `amount` from the specified `hotkey`. + * + * This function allows external accounts and contracts to unstake TAO from the subtensor pallet, + * which effectively calls `remove_stake` on the subtensor pallet with specified hotkey as a parameter + * and coldkey being the hashed address mapping of H160 sender address to Substrate ss58 address as + * implemented in Frontier HashedAddressMapping: + * https://github.com/polkadot-evm/frontier/blob/2e219e17a526125da003e64ef22ec037917083fa/frame/evm/src/lib.rs#L739 + * + * @param hotkey The hotkey public key (32 bytes). + * @param amount The amount to unstake in rao. + * + * Requirements: + * - `hotkey` must be a valid hotkey registered on the network, ensuring that the stake is + * correctly attributed. + * - The existing stake amount must be not lower than specified amount + */ + function removeStake(bytes32 hotkey, uint64 amount) external payable; +} \ No newline at end of file diff --git a/scripts/localnet-evm.sh b/scripts/localnet-evm.sh new file mode 100755 index 000000000..0e9c7ad34 --- /dev/null +++ b/scripts/localnet-evm.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +# Determine the directory this script resides in. This allows invoking it from any location. +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" + +# The base directory of the subtensor project +BASE_DIR="$SCRIPT_DIR/.." +CHAIN=local +BUILD_BINARY=1 +FEATURES="" + +SPEC_PATH="${SCRIPT_DIR}/specs/" +FULL_PATH="$SPEC_PATH$CHAIN.json" + +# Kill any existing nodes which may have not exited correctly after a previous +# run. +pkill -9 'node-subtensor' + +if [ ! -d "$SPEC_PATH" ]; then + echo "*** Creating directory ${SPEC_PATH}..." + mkdir $SPEC_PATH +fi + +if [[ $BUILD_BINARY == "1" ]]; then + echo "*** Building substrate binary..." + cargo build --workspace --profile=release --features "$FEATURES" --manifest-path "$BASE_DIR/Cargo.toml" + echo "*** Binary compiled" +fi + +echo "*** Building chainspec..." +"$BASE_DIR/target/release/node-subtensor" build-spec --disable-default-bootnode --raw --chain $CHAIN >$FULL_PATH +echo "*** Chainspec built and output to file" + +# generate node keys +$BASE_DIR/target/release/node-subtensor key generate-node-key --chain="$FULL_PATH" --base-path /tmp/alice +$BASE_DIR/target/release/node-subtensor key generate-node-key --chain="$FULL_PATH" --base-path /tmp/bob + +echo "*** Purging previous state..." +"$BASE_DIR/target/release/node-subtensor" purge-chain -y --base-path /tmp/bob --chain="$FULL_PATH" >/dev/null 2>&1 +"$BASE_DIR/target/release/node-subtensor" purge-chain -y --base-path /tmp/alice --chain="$FULL_PATH" >/dev/null 2>&1 +echo "*** Previous chainstate purged" + +echo "*** Starting localnet nodes..." +alice_start=( + "$BASE_DIR/target/release/node-subtensor" + --base-path /tmp/alice + --chain="$FULL_PATH" + --alice + --port 30334 + --rpc-port 9946 + --validator + --rpc-cors=all + --allow-private-ipv4 + --discover-local +) + +bob_start=( + "$BASE_DIR"/target/release/node-subtensor + --base-path /tmp/bob + --chain="$FULL_PATH" + --bob + --port 30335 + --rpc-port 9945 + --validator + --allow-private-ipv4 + --discover-local +) + +trap 'pkill -P $$' EXIT SIGINT SIGTERM + +( + ("${alice_start[@]}" 2>&1) & + ("${bob_start[@]}" 2>&1) + wait +)