diff --git a/Cargo.lock b/Cargo.lock index c7d9b2cd55..d458637d47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2425,7 +2425,7 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "evm" version = "0.39.1" -source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" +source = "git+https://github.com/bifrost-platform/evm?branch=bifrost-polkadot-v1.3.0#e14df5ea28a431ed17a828c6c2597e0ccedfb0e7" dependencies = [ "auto_impl", "environmental", @@ -2445,7 +2445,7 @@ dependencies = [ [[package]] name = "evm-core" version = "0.39.0" -source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" +source = "git+https://github.com/bifrost-platform/evm?branch=bifrost-polkadot-v1.3.0#e14df5ea28a431ed17a828c6c2597e0ccedfb0e7" dependencies = [ "parity-scale-codec", "primitive-types", @@ -2456,7 +2456,7 @@ dependencies = [ [[package]] name = "evm-gasometer" version = "0.39.0" -source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" +source = "git+https://github.com/bifrost-platform/evm?branch=bifrost-polkadot-v1.3.0#e14df5ea28a431ed17a828c6c2597e0ccedfb0e7" dependencies = [ "environmental", "evm-core", @@ -2467,7 +2467,7 @@ dependencies = [ [[package]] name = "evm-runtime" version = "0.39.0" -source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" +source = "git+https://github.com/bifrost-platform/evm?branch=bifrost-polkadot-v1.3.0#e14df5ea28a431ed17a828c6c2597e0ccedfb0e7" dependencies = [ "auto_impl", "environmental", @@ -2476,13 +2476,32 @@ dependencies = [ "sha3", ] +[[package]] +name = "evm-tracer" +version = "0.1.0" +dependencies = [ + "ethereum-types", + "evm", + "evm-gasometer", + "evm-runtime", + "fp-evm", + "fp-ext", + "fp-rpc-evm-tracing-events", + "pallet-evm", + "parity-scale-codec", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "exit-future" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e43f2f1833d64e33f15592464d6fdd70f349dda7b1a53088eb83cd94014008c5" dependencies = [ - "futures", + "futures 0.3.28", ] [[package]] @@ -2547,7 +2566,7 @@ dependencies = [ "fp-rpc", "fp-storage", "frontier-template-runtime", - "futures", + "futures 0.3.28", "parity-scale-codec", "sc-block-builder", "sc-cli", @@ -2589,7 +2608,7 @@ dependencies = [ "fp-consensus", "fp-rpc", "fp-storage", - "futures", + "futures 0.3.28", "kvdb-rocksdb", "log", "maplit", @@ -2613,6 +2632,20 @@ dependencies = [ "tokio", ] +[[package]] +name = "fc-evm-tracing" +version = "0.1.0" +dependencies = [ + "ethereum-types", + "fp-rpc-debug", + "fp-rpc-evm-tracing-events", + "hex", + "parity-scale-codec", + "serde", + "serde_json", + "sp-std", +] + [[package]] name = "fc-mapping-sync" version = "2.0.0-dev" @@ -2625,7 +2658,7 @@ dependencies = [ "fp-rpc", "fp-storage", "frontier-template-runtime", - "futures", + "futures 0.3.28", "futures-timer", "log", "parity-scale-codec", @@ -2661,7 +2694,7 @@ dependencies = [ "fp-evm", "fp-rpc", "fp-storage", - "futures", + "futures 0.3.28", "hex", "jsonrpsee", "libsecp256k1", @@ -2716,6 +2749,141 @@ dependencies = [ "serde_json", ] +[[package]] +name = "fc-rpc-core-debug" +version = "0.1.0" +dependencies = [ + "ethereum-types", + "fc-evm-tracing", + "fc-rpc-core-types", + "futures 0.3.28", + "jsonrpsee", + "serde", + "serde_json", + "sp-core", +] + +[[package]] +name = "fc-rpc-core-trace" +version = "0.6.0" +dependencies = [ + "ethereum-types", + "fc-evm-tracing", + "fc-rpc-core-types", + "futures 0.3.28", + "jsonrpsee", + "serde", + "serde_json", +] + +[[package]] +name = "fc-rpc-core-txpool" +version = "0.6.0" +dependencies = [ + "ethereum", + "ethereum-types", + "fc-rpc-core", + "jsonrpsee", + "serde", + "serde_json", +] + +[[package]] +name = "fc-rpc-core-types" +version = "0.1.0" +dependencies = [ + "ethereum-types", + "serde", + "serde_json", +] + +[[package]] +name = "fc-rpc-debug" +version = "0.1.0" +dependencies = [ + "ethereum", + "ethereum-types", + "fc-api", + "fc-consensus", + "fc-db", + "fc-evm-tracing", + "fc-rpc", + "fc-rpc-core-debug", + "fc-rpc-core-types", + "fc-storage", + "fp-rpc", + "fp-rpc-debug", + "futures 0.3.28", + "hex-literal", + "jsonrpsee", + "sc-client-api", + "sc-utils", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-core", + "sp-io", + "sp-runtime", + "tokio", +] + +[[package]] +name = "fc-rpc-trace" +version = "0.6.0" +dependencies = [ + "ethereum", + "ethereum-types", + "fc-consensus", + "fc-evm-tracing", + "fc-rpc", + "fc-rpc-core", + "fc-rpc-core-trace", + "fc-rpc-core-types", + "fc-storage", + "fp-rpc", + "fp-rpc-debug", + "futures 0.3.28", + "jsonrpsee", + "log", + "sc-client-api", + "sc-network", + "sc-utils", + "serde", + "sha3", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-io", + "sp-runtime", + "sp-std", + "sp-transaction-pool", + "substrate-prometheus-endpoint", + "tokio", + "tracing", +] + +[[package]] +name = "fc-rpc-txpool" +version = "0.6.0" +dependencies = [ + "ethereum-types", + "fc-rpc", + "fc-rpc-core-txpool", + "fp-rpc-txpool", + "frame-system", + "jsonrpsee", + "rlp", + "sc-transaction-pool", + "sc-transaction-pool-api", + "serde", + "sha3", + "sp-api", + "sp-blockchain", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "fc-storage" version = "1.0.0-dev" @@ -2810,7 +2978,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36530797b9bf31cd4ff126dcfee8170f86b00cfdcea3269d73133cc0415945c3" dependencies = [ "either", - "futures", + "futures 0.3.28", "futures-timer", "log", "num-traits", @@ -2893,7 +3061,7 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "fork-tree" version = "3.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "parity-scale-codec", ] @@ -2972,6 +3140,25 @@ dependencies = [ "sp-std", ] +[[package]] +name = "fp-ext" +version = "0.1.0" +dependencies = [ + "ethereum-types", + "fp-rpc-evm-tracing-events", + "parity-scale-codec", + "sp-externalities", + "sp-runtime-interface", + "sp-std", +] + +[[package]] +name = "fp-rent" +version = "2.0.0-dev" +dependencies = [ + "sp-core", +] + [[package]] name = "fp-rpc" version = "3.0.0-dev" @@ -2988,6 +3175,50 @@ dependencies = [ "sp-std", ] +[[package]] +name = "fp-rpc-debug" +version = "0.1.0" +dependencies = [ + "environmental", + "ethereum", + "ethereum-types", + "hex", + "parity-scale-codec", + "serde", + "sp-api", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "fp-rpc-evm-tracing-events" +version = "0.1.0" +dependencies = [ + "environmental", + "ethereum", + "ethereum-types", + "evm", + "evm-gasometer", + "evm-runtime", + "parity-scale-codec", + "sp-runtime-interface", +] + +[[package]] +name = "fp-rpc-txpool" +version = "0.6.0" +dependencies = [ + "ethereum", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "fp-self-contained" version = "1.0.0-dev" @@ -3016,7 +3247,7 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "frame-support", "frame-support-procedural", @@ -3041,7 +3272,7 @@ dependencies = [ [[package]] name = "frame-benchmarking-cli" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "Inflector", "array-bytes 6.1.0", @@ -3089,7 +3320,7 @@ dependencies = [ [[package]] name = "frame-executive" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "frame-support", "frame-system", @@ -3119,7 +3350,7 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "aquamarine", "bitflags 1.3.2", @@ -3159,7 +3390,7 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "Inflector", "cfg-expr", @@ -3178,10 +3409,10 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "frame-support-procedural-tools-derive", - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn 2.0.39", @@ -3190,7 +3421,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "proc-macro2", "quote", @@ -3200,7 +3431,7 @@ dependencies = [ [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "cfg-if", "frame-support", @@ -3219,7 +3450,7 @@ dependencies = [ [[package]] name = "frame-system-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "frame-benchmarking", "frame-support", @@ -3234,7 +3465,7 @@ dependencies = [ [[package]] name = "frame-system-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "parity-scale-codec", "sp-api", @@ -3243,7 +3474,7 @@ dependencies = [ [[package]] name = "frame-try-runtime" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "frame-support", "parity-scale-codec", @@ -3265,20 +3496,27 @@ dependencies = [ "fc-mapping-sync", "fc-rpc", "fc-rpc-core", + "fc-rpc-debug", + "fc-rpc-trace", + "fc-rpc-txpool", "fc-storage", "fp-account", "fp-dynamic-fee", "fp-evm", + "fp-ext", "fp-rpc", + "fp-rpc-debug", + "fp-rpc-txpool", "frame-benchmarking", "frame-benchmarking-cli", "frame-system", "frame-system-rpc-runtime-api", "frontier-template-runtime", - "futures", + "futures 0.3.28", "hex-literal", "jsonrpsee", "log", + "pallet-ethereum", "pallet-transaction-payment", "pallet-transaction-payment-rpc", "pallet-transaction-payment-rpc-runtime-api", @@ -3318,15 +3556,20 @@ dependencies = [ "substrate-build-script-utils", "substrate-frame-rpc-system", "substrate-prometheus-endpoint", + "tokio", ] [[package]] name = "frontier-template-runtime" version = "0.0.0" dependencies = [ + "evm-tracer", "fp-account", "fp-evm", "fp-rpc", + "fp-rpc-debug", + "fp-rpc-evm-tracing-events", + "fp-rpc-txpool", "fp-self-contained", "frame-benchmarking", "frame-executive", @@ -3344,6 +3587,7 @@ dependencies = [ "pallet-evm-precompile-modexp", "pallet-evm-precompile-sha3fips", "pallet-evm-precompile-simple", + "pallet-evm-rent", "pallet-grandpa", "pallet-hotfix-sufficients", "pallet-sudo", @@ -3357,6 +3601,7 @@ dependencies = [ "sp-consensus-aura", "sp-core", "sp-inherents", + "sp-io", "sp-offchain", "sp-runtime", "sp-session", @@ -3388,6 +3633,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "futures" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" + [[package]] name = "futures" version = "0.3.28" @@ -3509,6 +3760,7 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ + "futures 0.1.31", "futures-channel", "futures-core", "futures-io", @@ -3774,6 +4026,9 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] [[package]] name = "hex-literal" @@ -3996,7 +4251,7 @@ dependencies = [ "async-io", "core-foundation", "fnv", - "futures", + "futures 0.3.28", "if-addrs", "ipnet", "log", @@ -4255,7 +4510,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44e8ab85614a08792b9bff6c8feee23be78c98d0182d4c622c05256ab553892a" dependencies = [ "heck", - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn 1.0.109", @@ -4402,7 +4657,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f210d259724eae82005b5c48078619b7745edb7b76de370b03f8ba59ea103097" dependencies = [ "bytes", - "futures", + "futures 0.3.28", "futures-timer", "getrandom 0.2.10", "instant", @@ -4461,7 +4716,7 @@ checksum = "3c1df63c0b582aa434fb09b2d86897fa2b419ffeccf934b36f87fcedc8e835c2" dependencies = [ "either", "fnv", - "futures", + "futures 0.3.28", "futures-timer", "instant", "libp2p-identity", @@ -4487,7 +4742,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "146ff7034daae62077c415c2376b8057368042df6ab95f5432ad5e88568b1554" dependencies = [ - "futures", + "futures 0.3.28", "libp2p-core", "log", "parking_lot 0.12.1", @@ -4503,7 +4758,7 @@ checksum = "5455f472243e63b9c497ff320ded0314254a9eb751799a39c283c6f20b793f3c" dependencies = [ "asynchronous-codec", "either", - "futures", + "futures 0.3.28", "futures-timer", "libp2p-core", "libp2p-identity", @@ -4546,7 +4801,7 @@ dependencies = [ "bytes", "either", "fnv", - "futures", + "futures 0.3.28", "futures-timer", "instant", "libp2p-core", @@ -4570,7 +4825,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19983e1f949f979a928f2c603de1cf180cc0dc23e4ac93a62651ccb18341460b" dependencies = [ "data-encoding", - "futures", + "futures 0.3.28", "if-watch", "libp2p-core", "libp2p-identity", @@ -4606,7 +4861,7 @@ checksum = "9c3673da89d29936bc6435bafc638e2f184180d554ce844db65915113f86ec5e" dependencies = [ "bytes", "curve25519-dalek 3.2.0", - "futures", + "futures 0.3.28", "libp2p-core", "libp2p-identity", "log", @@ -4628,7 +4883,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e57759c19c28a73ef1eb3585ca410cefb72c1a709fcf6de1612a378e4219202" dependencies = [ "either", - "futures", + "futures 0.3.28", "futures-timer", "instant", "libp2p-core", @@ -4645,7 +4900,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6b26abd81cd2398382a1edfe739b539775be8a90fa6914f39b2ab49571ec735" dependencies = [ "bytes", - "futures", + "futures 0.3.28", "futures-timer", "if-watch", "libp2p-core", @@ -4667,7 +4922,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffdb374267d42dc5ed5bc53f6e601d4a64ac5964779c6e40bb9e4f14c1e30d5" dependencies = [ "async-trait", - "futures", + "futures 0.3.28", "instant", "libp2p-core", "libp2p-identity", @@ -4684,7 +4939,7 @@ checksum = "903b3d592d7694e56204d211f29d31bc004be99386644ba8731fc3e3ef27b296" dependencies = [ "either", "fnv", - "futures", + "futures 0.3.28", "futures-timer", "instant", "libp2p-core", @@ -4714,7 +4969,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d33698596d7722d85d3ab0c86c2c322254fce1241e91208e3679b4eb3026cf" dependencies = [ - "futures", + "futures 0.3.28", "futures-timer", "if-watch", "libc", @@ -4730,7 +4985,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff08d13d0dc66e5e9ba6279c1de417b84fa0d0adc3b03e5732928c180ec02781" dependencies = [ - "futures", + "futures 0.3.28", "futures-rustls", "libp2p-core", "libp2p-identity", @@ -4749,7 +5004,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77dff9d32353a5887adb86c8afc1de1a94d9e8c3bc6df8b2201d7cdf5c848f43" dependencies = [ - "futures", + "futures 0.3.28", "js-sys", "libp2p-core", "parity-send-wrapper", @@ -4766,7 +5021,7 @@ dependencies = [ "async-trait", "asynchronous-codec", "bytes", - "futures", + "futures 0.3.28", "futures-timer", "hex", "if-watch", @@ -4795,7 +5050,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "111273f7b3d3510524c752e8b7a5314b7f7a1fee7e68161c01a7d72cbb06db9f" dependencies = [ "either", - "futures", + "futures 0.3.28", "futures-rustls", "libp2p-core", "log", @@ -4813,7 +5068,7 @@ version = "0.43.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd21d950662700a385d4c6d68e2f5f54d778e97068cdd718522222ef513bda" dependencies = [ - "futures", + "futures 0.3.28", "libp2p-core", "log", "thiserror", @@ -5355,7 +5610,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro-error", "proc-macro2", "quote", @@ -5376,7 +5631,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8552ab875c1313b97b8d20cb857b9fd63e2d1d6a0a1b53ce9821e575405f27a" dependencies = [ "bytes", - "futures", + "futures 0.3.28", "log", "pin-project", "smallvec", @@ -5482,7 +5737,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65b4b14489ab424703c092062176d52ba55485a89c076b4f9db05092b7223aa6" dependencies = [ "bytes", - "futures", + "futures 0.3.28", "log", "netlink-packet-core", "netlink-sys", @@ -5497,7 +5752,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" dependencies = [ "bytes", - "futures", + "futures 0.3.28", "libc", "log", "tokio", @@ -5649,7 +5904,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ea360eafe1022f7cc56cd7b869ed57330fb2453d0c7831d99b74c65d2f5597" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn 2.0.39", @@ -5787,7 +6042,7 @@ dependencies = [ [[package]] name = "pallet-aura" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "frame-support", "frame-system", @@ -5804,7 +6059,7 @@ dependencies = [ [[package]] name = "pallet-authorship" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "frame-support", "frame-system", @@ -5818,7 +6073,7 @@ dependencies = [ [[package]] name = "pallet-babe" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "frame-benchmarking", "frame-support", @@ -5842,7 +6097,7 @@ dependencies = [ [[package]] name = "pallet-balances" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "frame-benchmarking", "frame-support", @@ -5896,6 +6151,7 @@ dependencies = [ "fp-consensus", "fp-ethereum", "fp-evm", + "fp-rent", "fp-rpc", "fp-self-contained", "fp-storage", @@ -5923,6 +6179,7 @@ dependencies = [ "evm", "fp-account", "fp-evm", + "fp-rent", "frame-benchmarking", "frame-support", "frame-system", @@ -6058,6 +6315,22 @@ dependencies = [ "sp-io", ] +[[package]] +name = "pallet-evm-rent" +version = "1.0.0" +dependencies = [ + "fp-rent", + "frame-support", + "frame-system", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-evm-test-vector-support" version = "1.0.0-dev" @@ -6073,7 +6346,7 @@ dependencies = [ [[package]] name = "pallet-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "frame-benchmarking", "frame-support", @@ -6112,7 +6385,7 @@ dependencies = [ [[package]] name = "pallet-session" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "frame-support", "frame-system", @@ -6134,7 +6407,7 @@ dependencies = [ [[package]] name = "pallet-sudo" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "docify", "frame-benchmarking", @@ -6150,7 +6423,7 @@ dependencies = [ [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "docify", "frame-benchmarking", @@ -6170,7 +6443,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "frame-support", "frame-system", @@ -6186,7 +6459,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "jsonrpsee", "pallet-transaction-payment-rpc-runtime-api", @@ -6202,7 +6475,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "pallet-transaction-payment", "parity-scale-codec", @@ -6214,7 +6487,7 @@ dependencies = [ [[package]] name = "pallet-utility" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "frame-benchmarking", "frame-support", @@ -6249,9 +6522,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.6.4" +version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8e946cc0cc711189c0b0249fb8b599cbeeab9784d83c415719368bb8d4ac64" +checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" dependencies = [ "arrayvec 0.7.4", "bitvec", @@ -6264,11 +6537,11 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.4" +version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a296c3079b5fefbc499e1de58dc26c09b1b9a5952d26694ee89f04a43ebbb3e" +checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 2.0.2", "proc-macro2", "quote", "syn 1.0.109", @@ -6722,6 +6995,16 @@ dependencies = [ "toml 0.5.11", ] +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime", + "toml_edit 0.20.2", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -6765,9 +7048,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -7323,7 +7606,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "322c53fd76a18698f1c27381d58091de3a043d356aa5bd0d510608b565f469a0" dependencies = [ - "futures", + "futures 0.3.28", "log", "netlink-packet-route", "netlink-proto", @@ -7513,7 +7796,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26338f5e09bb721b85b135ea05af7767c90b52f6de4f087d4f4a3a9d64e7dc04" dependencies = [ - "futures", + "futures 0.3.28", "pin-project", "static_assertions", ] @@ -7545,7 +7828,7 @@ dependencies = [ [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "log", "sp-core", @@ -7556,9 +7839,9 @@ dependencies = [ [[package]] name = "sc-basic-authorship" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ - "futures", + "futures 0.3.28", "futures-timer", "log", "parity-scale-codec", @@ -7579,7 +7862,7 @@ dependencies = [ [[package]] name = "sc-block-builder" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "parity-scale-codec", "sc-client-api", @@ -7594,7 +7877,7 @@ dependencies = [ [[package]] name = "sc-chain-spec" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "memmap2", "sc-chain-spec-derive", @@ -7613,9 +7896,9 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn 2.0.39", @@ -7624,13 +7907,13 @@ dependencies = [ [[package]] name = "sc-cli" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "array-bytes 6.1.0", "chrono", "clap", "fdlimit", - "futures", + "futures 0.3.28", "libp2p-identity", "log", "names", @@ -7664,10 +7947,10 @@ dependencies = [ [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "fnv", - "futures", + "futures 0.3.28", "log", "parity-scale-codec", "parking_lot 0.12.1", @@ -7691,7 +7974,7 @@ dependencies = [ [[package]] name = "sc-client-db" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "hash-db 0.16.0", "kvdb", @@ -7717,10 +8000,10 @@ dependencies = [ [[package]] name = "sc-consensus" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "async-trait", - "futures", + "futures 0.3.28", "futures-timer", "libp2p-identity", "log", @@ -7742,10 +8025,10 @@ dependencies = [ [[package]] name = "sc-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "async-trait", - "futures", + "futures 0.3.28", "log", "parity-scale-codec", "sc-block-builder", @@ -7771,11 +8054,11 @@ dependencies = [ [[package]] name = "sc-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "async-trait", "fork-tree", - "futures", + "futures 0.3.28", "log", "num-bigint", "num-rational", @@ -7806,7 +8089,7 @@ dependencies = [ [[package]] name = "sc-consensus-epochs" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "fork-tree", "parity-scale-codec", @@ -7819,7 +8102,7 @@ dependencies = [ [[package]] name = "sc-consensus-grandpa" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "ahash 0.8.3", "array-bytes 6.1.0", @@ -7827,7 +8110,7 @@ dependencies = [ "dyn-clone", "finality-grandpa", "fork-tree", - "futures", + "futures 0.3.28", "futures-timer", "log", "parity-scale-codec", @@ -7860,11 +8143,11 @@ dependencies = [ [[package]] name = "sc-consensus-manual-seal" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "assert_matches", "async-trait", - "futures", + "futures 0.3.28", "futures-timer", "jsonrpsee", "log", @@ -7895,10 +8178,10 @@ dependencies = [ [[package]] name = "sc-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "async-trait", - "futures", + "futures 0.3.28", "futures-timer", "log", "parity-scale-codec", @@ -7918,7 +8201,7 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "parity-scale-codec", "parking_lot 0.12.1", @@ -7940,7 +8223,7 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", @@ -7952,7 +8235,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "anyhow", "cfg-if", @@ -7970,10 +8253,10 @@ dependencies = [ [[package]] name = "sc-informant" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "ansi_term", - "futures", + "futures 0.3.28", "futures-timer", "log", "sc-client-api", @@ -7986,7 +8269,7 @@ dependencies = [ [[package]] name = "sc-keystore" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "array-bytes 6.1.0", "parking_lot 0.12.1", @@ -8000,12 +8283,12 @@ dependencies = [ [[package]] name = "sc-mixnet" version = "0.1.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "array-bytes 4.2.0", "arrayvec 0.7.4", "blake2 0.10.6", - "futures", + "futures 0.3.28", "futures-timer", "libp2p-identity", "log", @@ -8028,7 +8311,7 @@ dependencies = [ [[package]] name = "sc-network" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "array-bytes 6.1.0", "async-channel", @@ -8037,7 +8320,7 @@ dependencies = [ "bytes", "either", "fnv", - "futures", + "futures 0.3.28", "futures-timer", "ip_network", "libp2p", @@ -8069,11 +8352,11 @@ dependencies = [ [[package]] name = "sc-network-bitswap" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "async-channel", "cid", - "futures", + "futures 0.3.28", "libp2p-identity", "log", "prost", @@ -8089,11 +8372,11 @@ dependencies = [ [[package]] name = "sc-network-common" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "async-trait", "bitflags 1.3.2", - "futures", + "futures 0.3.28", "libp2p-identity", "parity-scale-codec", "prost-build", @@ -8106,10 +8389,10 @@ dependencies = [ [[package]] name = "sc-network-gossip" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "ahash 0.8.3", - "futures", + "futures 0.3.28", "futures-timer", "libp2p", "log", @@ -8124,11 +8407,11 @@ dependencies = [ [[package]] name = "sc-network-light" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "array-bytes 6.1.0", "async-channel", - "futures", + "futures 0.3.28", "libp2p-identity", "log", "parity-scale-codec", @@ -8145,13 +8428,13 @@ dependencies = [ [[package]] name = "sc-network-sync" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "array-bytes 6.1.0", "async-channel", "async-trait", "fork-tree", - "futures", + "futures 0.3.28", "futures-timer", "libp2p", "log", @@ -8180,10 +8463,10 @@ dependencies = [ [[package]] name = "sc-network-transactions" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "array-bytes 6.1.0", - "futures", + "futures 0.3.28", "libp2p", "log", "parity-scale-codec", @@ -8198,12 +8481,12 @@ dependencies = [ [[package]] name = "sc-offchain" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "array-bytes 6.1.0", "bytes", "fnv", - "futures", + "futures 0.3.28", "futures-timer", "hyper", "hyper-rustls", @@ -8232,7 +8515,7 @@ dependencies = [ [[package]] name = "sc-proposer-metrics" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -8241,9 +8524,9 @@ dependencies = [ [[package]] name = "sc-rpc" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ - "futures", + "futures 0.3.28", "jsonrpsee", "log", "parity-scale-codec", @@ -8273,7 +8556,7 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -8293,7 +8576,7 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "http", "jsonrpsee", @@ -8308,10 +8591,10 @@ dependencies = [ [[package]] name = "sc-rpc-spec-v2" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "array-bytes 6.1.0", - "futures", + "futures 0.3.28", "futures-util", "hex", "jsonrpsee", @@ -8336,12 +8619,12 @@ dependencies = [ [[package]] name = "sc-service" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "async-trait", "directories", "exit-future", - "futures", + "futures 0.3.28", "futures-timer", "jsonrpsee", "log", @@ -8400,7 +8683,7 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "log", "parity-scale-codec", @@ -8411,9 +8694,9 @@ dependencies = [ [[package]] name = "sc-sysinfo" version = "6.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ - "futures", + "futures 0.3.28", "libc", "log", "rand 0.8.5", @@ -8430,10 +8713,10 @@ dependencies = [ [[package]] name = "sc-telemetry" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "chrono", - "futures", + "futures 0.3.28", "libp2p", "log", "parking_lot 0.12.1", @@ -8449,7 +8732,7 @@ dependencies = [ [[package]] name = "sc-tracing" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "ansi_term", "atty", @@ -8478,9 +8761,9 @@ dependencies = [ [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn 2.0.39", @@ -8489,10 +8772,10 @@ dependencies = [ [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "async-trait", - "futures", + "futures 0.3.28", "futures-timer", "linked-hash-map", "log", @@ -8515,10 +8798,10 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "async-trait", - "futures", + "futures 0.3.28", "log", "parity-scale-codec", "serde", @@ -8531,10 +8814,10 @@ dependencies = [ [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "async-channel", - "futures", + "futures 0.3.28", "futures-timer", "lazy_static", "log", @@ -8563,7 +8846,7 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn 1.0.109", @@ -8755,18 +9038,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", @@ -9019,7 +9302,7 @@ dependencies = [ "base64 0.13.1", "bytes", "flate2", - "futures", + "futures 0.3.28", "http", "httparse", "log", @@ -9030,7 +9313,7 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "hash-db 0.16.0", "log", @@ -9051,12 +9334,12 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "Inflector", "blake2 0.10.6", "expander", - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn 2.0.39", @@ -9065,7 +9348,7 @@ dependencies = [ [[package]] name = "sp-application-crypto" version = "23.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "parity-scale-codec", "scale-info", @@ -9078,7 +9361,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" version = "16.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "integer-sqrt", "num-traits", @@ -9092,7 +9375,7 @@ dependencies = [ [[package]] name = "sp-block-builder" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "sp-api", "sp-inherents", @@ -9103,9 +9386,9 @@ dependencies = [ [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ - "futures", + "futures 0.3.28", "log", "parity-scale-codec", "parking_lot 0.12.1", @@ -9121,10 +9404,10 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "async-trait", - "futures", + "futures 0.3.28", "log", "sp-core", "sp-inherents", @@ -9136,7 +9419,7 @@ dependencies = [ [[package]] name = "sp-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "async-trait", "parity-scale-codec", @@ -9153,7 +9436,7 @@ dependencies = [ [[package]] name = "sp-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "async-trait", "parity-scale-codec", @@ -9172,7 +9455,7 @@ dependencies = [ [[package]] name = "sp-consensus-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "finality-grandpa", "log", @@ -9190,7 +9473,7 @@ dependencies = [ [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "parity-scale-codec", "scale-info", @@ -9202,7 +9485,7 @@ dependencies = [ [[package]] name = "sp-core" version = "21.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "array-bytes 6.1.0", "bandersnatch_vrfs", @@ -9212,7 +9495,7 @@ dependencies = [ "bs58 0.5.0", "dyn-clonable", "ed25519-zebra", - "futures", + "futures 0.3.28", "hash-db 0.16.0", "hash256-std-hasher", "impl-serde", @@ -9249,7 +9532,7 @@ dependencies = [ [[package]] name = "sp-core-hashing" version = "9.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "blake2b_simd", "byteorder", @@ -9262,7 +9545,7 @@ dependencies = [ [[package]] name = "sp-core-hashing-proc-macro" version = "9.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "quote", "sp-core-hashing", @@ -9272,7 +9555,7 @@ dependencies = [ [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "kvdb", "parking_lot 0.12.1", @@ -9281,7 +9564,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "8.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "proc-macro2", "quote", @@ -9291,7 +9574,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.19.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "environmental", "parity-scale-codec", @@ -9302,7 +9585,7 @@ dependencies = [ [[package]] name = "sp-genesis-builder" version = "0.1.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "serde_json", "sp-api", @@ -9313,7 +9596,7 @@ dependencies = [ [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "async-trait", "impl-trait-for-tuples", @@ -9327,7 +9610,7 @@ dependencies = [ [[package]] name = "sp-io" version = "23.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "bytes", "ed25519-dalek", @@ -9351,7 +9634,7 @@ dependencies = [ [[package]] name = "sp-keyring" version = "24.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "lazy_static", "sp-core", @@ -9362,7 +9645,7 @@ dependencies = [ [[package]] name = "sp-keystore" version = "0.27.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "parity-scale-codec", "parking_lot 0.12.1", @@ -9374,7 +9657,7 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "thiserror", "zstd 0.12.4", @@ -9383,7 +9666,7 @@ dependencies = [ [[package]] name = "sp-metadata-ir" version = "0.1.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "frame-metadata", "parity-scale-codec", @@ -9394,7 +9677,7 @@ dependencies = [ [[package]] name = "sp-mixnet" version = "0.1.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "parity-scale-codec", "scale-info", @@ -9406,7 +9689,7 @@ dependencies = [ [[package]] name = "sp-offchain" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "sp-api", "sp-core", @@ -9416,7 +9699,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" version = "8.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "backtrace", "lazy_static", @@ -9426,7 +9709,7 @@ dependencies = [ [[package]] name = "sp-rpc" version = "6.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "rustc-hash", "serde", @@ -9436,7 +9719,7 @@ dependencies = [ [[package]] name = "sp-runtime" version = "24.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "either", "hash256-std-hasher", @@ -9458,7 +9741,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "17.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -9476,10 +9759,10 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "11.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "Inflector", - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn 2.0.39", @@ -9488,7 +9771,7 @@ dependencies = [ [[package]] name = "sp-session" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "parity-scale-codec", "scale-info", @@ -9503,7 +9786,7 @@ dependencies = [ [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", @@ -9517,7 +9800,7 @@ dependencies = [ [[package]] name = "sp-state-machine" version = "0.28.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "hash-db 0.16.0", "log", @@ -9538,7 +9821,7 @@ dependencies = [ [[package]] name = "sp-statement-store" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "aes-gcm 0.10.2", "curve25519-dalek 4.1.0", @@ -9562,12 +9845,12 @@ dependencies = [ [[package]] name = "sp-std" version = "8.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" [[package]] name = "sp-storage" version = "13.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "impl-serde", "parity-scale-codec", @@ -9580,7 +9863,7 @@ dependencies = [ [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "async-trait", "parity-scale-codec", @@ -9593,7 +9876,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "10.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "parity-scale-codec", "sp-std", @@ -9605,7 +9888,7 @@ dependencies = [ [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "sp-api", "sp-runtime", @@ -9614,7 +9897,7 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "async-trait", "parity-scale-codec", @@ -9629,7 +9912,7 @@ dependencies = [ [[package]] name = "sp-trie" version = "22.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "ahash 0.8.3", "hash-db 0.16.0", @@ -9653,7 +9936,7 @@ dependencies = [ [[package]] name = "sp-version" version = "22.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "impl-serde", "parity-scale-codec", @@ -9670,7 +9953,7 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "8.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "parity-scale-codec", "proc-macro2", @@ -9681,7 +9964,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "14.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "anyhow", "impl-trait-for-tuples", @@ -9694,7 +9977,7 @@ dependencies = [ [[package]] name = "sp-weights" version = "20.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "parity-scale-codec", "scale-info", @@ -9993,15 +10276,15 @@ dependencies = [ [[package]] name = "substrate-build-script-utils" version = "3.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" [[package]] name = "substrate-frame-rpc-system" version = "4.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "frame-system-rpc-runtime-api", - "futures", + "futures 0.3.28", "jsonrpsee", "log", "parity-scale-codec", @@ -10017,7 +10300,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.10.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "hyper", "log", @@ -10029,11 +10312,11 @@ dependencies = [ [[package]] name = "substrate-test-client" version = "2.0.1" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "array-bytes 6.1.0", "async-trait", - "futures", + "futures 0.3.28", "parity-scale-codec", "sc-client-api", "sc-client-db", @@ -10055,7 +10338,7 @@ dependencies = [ [[package]] name = "substrate-test-runtime" version = "2.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "array-bytes 6.1.0", "frame-executive", @@ -10098,9 +10381,9 @@ dependencies = [ [[package]] name = "substrate-test-runtime-client" version = "2.0.0" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ - "futures", + "futures 0.3.28", "sc-block-builder", "sc-client-api", "sc-consensus", @@ -10116,7 +10399,7 @@ dependencies = [ [[package]] name = "substrate-wasm-builder" version = "5.0.0-dev" -source = "git+https://github.com/btclayer2/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" +source = "git+https://github.com/GEBcore/polkadot-sdk?branch=polkadot-v1.3.0#7c9fd83805cc446983a7698c7a3281677cf655c8" dependencies = [ "ansi_term", "build-helper", @@ -10468,7 +10751,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.19.14", ] [[package]] @@ -10493,6 +10776,17 @@ dependencies = [ "winnow", ] +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.0.0", + "toml_datetime", + "winnow", +] + [[package]] name = "tower" version = "0.4.13" @@ -10735,7 +11029,7 @@ checksum = "4712ee30d123ec7ae26d1e1b218395a16c87cdbaf4b3925d170d684af62ea5e8" dependencies = [ "async-trait", "base64 0.13.1", - "futures", + "futures 0.3.28", "log", "md-5", "rand 0.8.5", @@ -11106,7 +11400,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" dependencies = [ - "futures", + "futures 0.3.28", "js-sys", "parking_lot 0.11.2", "pin-utils", @@ -11904,7 +12198,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5d9ba232399af1783a58d8eb26f6b5006fbefe2dc9ef36bd283324792d03ea5" dependencies = [ - "futures", + "futures 0.3.28", "log", "nohash-hasher", "parking_lot 0.12.1", diff --git a/Cargo.toml b/Cargo.toml index 36da33db68..cda133ec08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ members = [ "frame/evm/precompile/bls12377", "frame/evm/precompile/dispatch", "frame/evm/precompile/curve25519", + "frame/evm-rent", "client/api", "client/consensus", "client/rpc-core", @@ -23,12 +24,26 @@ members = [ "client/db", "client/storage", "client/mapping-sync", + "client/bifrost/evm-tracing", + "client/bifrost/rpc-core/types", + "client/bifrost/rpc-core/trace", + "client/bifrost/rpc-core/debug", + "client/bifrost/rpc-core/txpool", + "client/bifrost/rpc/debug", + "client/bifrost/rpc/trace", + "client/bifrost/rpc/txpool", "primitives/account", "primitives/consensus", "primitives/dynamic-fee", "primitives/evm", "primitives/rpc", "primitives/self-contained", + "primitives/bifrost/ext", + "primitives/bifrost/rpc/evm-tracing-events", + "primitives/bifrost/rpc/debug", + "primitives/bifrost/rpc/txpool", + "primitives/rent", + "runtime/evm-tracer", "template/node", "template/runtime", "precompiles", @@ -48,9 +63,11 @@ bn = { package = "substrate-bn", version = "0.6", default-features = false } clap = { version = "4.4.3", features = ["derive", "deprecated"] } derive_more = "0.99" environmental = { version = "1.1.4", default-features = false } -ethereum = { version = "0.14.0", default-features = false } +ethereum = { version = "0.14.0", default-features = false, features = ["with-codec"] } ethereum-types = { version = "0.14.1", default-features = false } -evm = { git = "https://github.com/rust-blockchain/evm", rev = "b7b82c7e1fc57b7449d6dfa6826600de37cc1e65", default-features = false } +evm = { git = "https://github.com/bifrost-platform/evm", default-features = false, branch = "bifrost-polkadot-v1.3.0" } +evm-gasometer = { git = "https://github.com/bifrost-platform/evm", default-features = false, branch = "bifrost-polkadot-v1.3.0" } +evm-runtime = { git = "https://github.com/bifrost-platform/evm", default-features = false, branch = "bifrost-polkadot-v1.3.0" } futures = "0.3.28" hash-db = { version = "0.16.0", default-features = false } hex = { version = "0.4.3", default-features = false, features = ["alloc"] } @@ -65,7 +82,8 @@ num_enum = { version = "0.7.0", default-features = false } parity-db = "0.4.12" parking_lot = "0.12.1" rlp = { version = "0.5.2", default-features = false } -scale-codec = { package = "parity-scale-codec", version = "3.6.4", default-features = false, features = ["derive"] } +sha3 = { version = "0.10", default-features = false } +scale-codec = { package = "parity-scale-codec", version = "3.6.9", default-features = false, features = ["derive"] } scale-info = { version = "2.9.0", default-features = false, features = ["derive"] } serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] } serde_json = "1.0" @@ -73,78 +91,80 @@ similar-asserts = "1.1.0" sqlx = { version = "0.7.1", default-features = false, features = ["macros"] } thiserror = "1.0" tokio = "1.32.0" +tracing = "0.1.34" # Substrate Client -sc-basic-authorship = { version = "0.10.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sc-block-builder = { version = "0.10.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sc-chain-spec = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sc-cli = { version = "0.10.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -sc-client-api = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sc-client-db = { version = "0.10.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -sc-consensus = { version = "0.10.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sc-consensus-aura = { version = "0.10.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sc-consensus-grandpa = { version = "0.10.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sc-consensus-manual-seal = { version = "0.10.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sc-executor = { version = "0.10.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sc-keystore = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sc-network = { version = "0.10.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sc-network-common = { version = "0.10.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sc-network-sync = { version = "0.10.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sc-offchain = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sc-rpc = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sc-rpc-api = { version = "0.10.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sc-service = { version = "0.10.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -sc-telemetry = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sc-transaction-pool = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sc-transaction-pool-api = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sc-utils = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } +sc-basic-authorship = { version = "0.10.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sc-block-builder = { version = "0.10.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sc-chain-spec = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sc-cli = { version = "0.10.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sc-client-api = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sc-client-db = { version = "0.10.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sc-consensus = { version = "0.10.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sc-consensus-aura = { version = "0.10.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sc-consensus-grandpa = { version = "0.10.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sc-consensus-manual-seal = { version = "0.10.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sc-executor = { version = "0.10.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sc-keystore = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sc-network = { version = "0.10.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sc-network-common = { version = "0.10.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sc-network-sync = { version = "0.10.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sc-offchain = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sc-rpc = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sc-rpc-api = { version = "0.10.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sc-service = { version = "0.10.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sc-telemetry = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sc-transaction-pool = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sc-transaction-pool-api = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sc-utils = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } # Substrate Primitive -sp-api = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -sp-block-builder = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -sp-blockchain = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sp-consensus = { version = "0.10.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sp-consensus-aura = { version = "0.10.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -sp-consensus-grandpa = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -sp-core = { version = "21.0.0", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -sp-core-hashing = { version = "9.0.0", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -sp-core-hashing-proc-macro = { version = "9.0.0", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -sp-database = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sp-inherents = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -sp-io = { version = "23.0.0", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -sp-keyring = { version = "24.0.0", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -sp-offchain = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -sp-runtime = { version = "24.0.0", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -sp-runtime-interface = { version = "17.0.0", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -sp-session = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -sp-state-machine = { version = "0.28.0", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -sp-std = { version = "8.0.0", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -sp-storage = { version = "13.0.0", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -sp-timestamp = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -sp-transaction-pool = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -sp-version = { version = "22.0.0", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sp-api = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sp-block-builder = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sp-blockchain = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sp-consensus = { version = "0.10.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sp-consensus-aura = { version = "0.10.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sp-consensus-grandpa = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sp-core = { version = "21.0.0", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sp-core-hashing = { version = "9.0.0", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sp-core-hashing-proc-macro = { version = "9.0.0", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sp-database = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sp-externalities = { version = "0.19.0", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sp-inherents = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sp-io = { version = "23.0.0", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sp-keyring = { version = "24.0.0", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +sp-offchain = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sp-runtime = { version = "24.0.0", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sp-runtime-interface = { version = "17.0.0", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sp-session = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sp-state-machine = { version = "0.28.0", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sp-std = { version = "8.0.0", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sp-storage = { version = "13.0.0", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sp-timestamp = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sp-transaction-pool = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +sp-version = { version = "22.0.0", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } # Substrate FRAME -frame-benchmarking = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -frame-executive = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -frame-support = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -frame-system = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -frame-system-benchmarking = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -frame-system-rpc-runtime-api = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -pallet-aura = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -pallet-balances = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -pallet-grandpa = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -pallet-sudo = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -pallet-timestamp = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -pallet-transaction-payment = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -pallet-transaction-payment-rpc = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -pallet-transaction-payment-rpc-runtime-api = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } -pallet-utility = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +frame-benchmarking = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +frame-executive = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +frame-support = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +frame-system = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +frame-system-benchmarking = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +frame-system-rpc-runtime-api = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +pallet-aura = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +pallet-balances = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +pallet-grandpa = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +pallet-sudo = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +pallet-timestamp = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +pallet-transaction-payment = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +pallet-transaction-payment-rpc = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +pallet-transaction-payment-rpc-runtime-api = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } +pallet-utility = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0", default-features = false } # Substrate Utility -frame-benchmarking-cli = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -substrate-build-script-utils = { version = "3.0.0", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -substrate-frame-rpc-system = { version = "4.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -substrate-test-runtime-client = { version = "2.0.0", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } -substrate-wasm-builder = { version = "5.0.0-dev", git = "https://github.com/btclayer2/polkadot-sdk", branch = "polkadot-v1.3.0" } +frame-benchmarking-cli = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +substrate-build-script-utils = { version = "3.0.0", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +substrate-frame-rpc-system = { version = "4.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +substrate-test-runtime-client = { version = "2.0.0", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } +substrate-wasm-builder = { version = "5.0.0-dev", git = "https://github.com/GEBcore/polkadot-sdk", branch = "polkadot-v1.3.0" } # Frontier Client fc-api = { version = "1.0.0-dev", path = "client/api" } @@ -155,6 +175,16 @@ fc-mapping-sync = { version = "2.0.0-dev", path = "client/mapping-sync", default fc-rpc = { version = "2.0.0-dev", path = "client/rpc", default-features = false } fc-rpc-core = { version = "1.1.0-dev", path = "client/rpc-core" } fc-storage = { version = "1.0.0-dev", path = "client/storage" } +# Frontier BIFROST Client +fc-evm-tracing = { version = "0.1.0", path = "client/bifrost/evm-tracing" } +fc-rpc-core-debug = { version = "0.1.0", path = "client/bifrost/rpc-core/debug" } +fc-rpc-core-trace = { version = "0.6.0", path = "client/bifrost/rpc-core/trace" } +fc-rpc-core-txpool = { version = "0.6.0", path = "client/bifrost/rpc-core/txpool" } +fc-rpc-core-types = { version = "0.1.0", path = "client/bifrost/rpc-core/types" } +fc-rpc-debug = { version = "0.1.0", path = "client/bifrost/rpc/debug" } +fc-rpc-trace = { version = "0.6.0", path = "client/bifrost/rpc/trace" } +fc-rpc-txpool = { version = "0.6.0", path = "client/bifrost/rpc/txpool" } + # Frontier Primitive fp-account = { version = "1.0.0-dev", path = "primitives/account", default-features = false } fp-consensus = { version = "2.0.0-dev", path = "primitives/consensus", default-features = false } @@ -164,6 +194,15 @@ fp-evm = { version = "3.0.0-dev", path = "primitives/evm", default-features = fa fp-rpc = { version = "3.0.0-dev", path = "primitives/rpc", default-features = false } fp-self-contained = { version = "1.0.0-dev", path = "primitives/self-contained", default-features = false } fp-storage = { version = "2.0.0", path = "primitives/storage", default-features = false } +fp-rent = { version = "2.0.0-dev", path = "primitives/rent", default-features = false } +# Frontier BIFROST Primitive +fp-ext = { version = "0.1.0", path = "primitives/bifrost/ext", default-features = false } +fp-rpc-debug = { version = "0.1.0", path = "primitives/bifrost/rpc/debug", default-features = false } +fp-rpc-evm-tracing-events = { version = "0.1.0", path = "primitives/bifrost/rpc/evm-tracing-events", default-features = false } +fp-rpc-txpool = { version = "0.6.0", path = "primitives/bifrost/rpc/txpool", default-features = false } +# Frontier Runtime +evm-tracer = { version = "0.1.0", path = "runtime/evm-tracer", default-features = false } + # Frontier FRAME pallet-base-fee = { version = "1.0.0", path = "frame/base-fee", default-features = false } pallet-dynamic-fee = { version = "4.0.0-dev", path = "frame/dynamic-fee", default-features = false } @@ -175,6 +214,7 @@ pallet-evm-precompile-sha3fips = { version = "2.0.0-dev", path = "frame/evm/prec pallet-evm-precompile-simple = { version = "2.0.0-dev", path = "frame/evm/precompile/simple", default-features = false } pallet-evm-test-vector-support = { version = "1.0.0-dev", path = "frame/evm/test-vector-support" } pallet-hotfix-sufficients = { version = "1.0.0", path = "frame/hotfix-sufficients", default-features = false } +pallet-evm-rent = { version = "1.0.0", path = "frame/evm-rent", default-features = false} # Frontier Template frontier-template-runtime = { path = "template/runtime", default-features = false } # Frontier Utility diff --git a/client/api/Cargo.toml b/client/api/Cargo.toml index 0a70e8db39..4fa9a31476 100644 --- a/client/api/Cargo.toml +++ b/client/api/Cargo.toml @@ -12,7 +12,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = { workspace = true } -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } # Substrate sp-core = { workspace = true, features = ["default"] } sp-runtime = { workspace = true, features = ["default"] } diff --git a/client/bifrost/evm-tracing/Cargo.toml b/client/bifrost/evm-tracing/Cargo.toml new file mode 100644 index 0000000000..7a76f5e6fb --- /dev/null +++ b/client/bifrost/evm-tracing/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "fc-evm-tracing" +authors = ["bifrost-platform"] +edition = "2021" +homepage = "https://www.bifrostnetwork.com" +license = "Apache-2.0" +repository = "https://github.com/bifrost-platform/bifrost-frontier" +version = "0.1.0" + +[dependencies] +ethereum-types = { workspace = true, features = ["std"] } +hex = { workspace = true, features = ["serde"] } +serde = { workspace = true, features = ["derive", "std"] } +serde_json = { workspace = true } + +fp-rpc-debug = { workspace = true, features = ["std"] } +fp-rpc-evm-tracing-events = { workspace = true, features = ["std"] } + +# Substrate +scale-codec = { workspace = true, features = ["std"] } +sp-std = { workspace = true, features = ["std"] } diff --git a/client/bifrost/evm-tracing/src/formatters/blockscout.rs b/client/bifrost/evm-tracing/src/formatters/blockscout.rs new file mode 100644 index 0000000000..f3f45157b0 --- /dev/null +++ b/client/bifrost/evm-tracing/src/formatters/blockscout.rs @@ -0,0 +1,78 @@ +use crate::{ + listeners::call_list::Listener, + types::{ + serialization::*, + single::{Call, TransactionTrace}, + CallResult, CallType, CreateResult, + }, +}; +use ethereum_types::{H160, U256}; +use scale_codec::{Decode, Encode}; +use serde::Serialize; + +pub struct Formatter; + +impl super::ResponseFormatter for Formatter { + type Listener = Listener; + type Response = TransactionTrace; + + fn format(listener: Listener) -> Option { + if let Some(entry) = listener.entries.last() { + return Some(TransactionTrace::CallList( + entry + .into_iter() + .map(|(_, value)| Call::Blockscout(value.clone())) + .collect(), + )); + } + None + } +} + +#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, Serialize)] +#[serde(rename_all = "lowercase", tag = "type")] +pub enum BlockscoutCallInner { + Call { + #[serde(rename(serialize = "callType"))] + /// Type of call. + call_type: CallType, + to: H160, + #[serde(serialize_with = "bytes_0x_serialize")] + input: Vec, + /// "output" or "error" field + #[serde(flatten)] + res: CallResult, + }, + Create { + #[serde(serialize_with = "bytes_0x_serialize")] + init: Vec, + #[serde(flatten)] + res: CreateResult, + }, + SelfDestruct { + #[serde(skip)] + balance: U256, + to: H160, + }, +} + +#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct BlockscoutCall { + pub from: H160, + /// Indices of parent calls. + pub trace_address: Vec, + /// Number of children calls. + /// Not needed for Blockscout, but needed for `crate::block` + /// types that are build from this type. + #[serde(skip)] + pub subtraces: u32, + /// Sends funds to the (payable) function + pub value: U256, + /// Remaining gas in the runtime. + pub gas: U256, + /// Gas used by this context. + pub gas_used: U256, + #[serde(flatten)] + pub inner: BlockscoutCallInner, +} diff --git a/client/bifrost/evm-tracing/src/formatters/call_tracer.rs b/client/bifrost/evm-tracing/src/formatters/call_tracer.rs new file mode 100644 index 0000000000..e05ef2cc27 --- /dev/null +++ b/client/bifrost/evm-tracing/src/formatters/call_tracer.rs @@ -0,0 +1,303 @@ +use super::blockscout::BlockscoutCallInner; +use crate::types::{ + single::{Call, TransactionTrace}, + CallResult, CallType, CreateResult, +}; + +use crate::listeners::call_list::Listener; + +use crate::types::serialization::*; +use serde::Serialize; + +use ethereum_types::{H160, U256}; +use scale_codec::{Decode, Encode}; +use sp_std::{cmp::Ordering, vec::Vec}; + +pub struct Formatter; + +impl super::ResponseFormatter for Formatter { + type Listener = Listener; + type Response = Vec; + + fn format(mut listener: Listener) -> Option> { + // Remove empty BTreeMaps pushed to `entries`. + // I.e. InvalidNonce or other pallet_evm::runner exits + listener.entries.retain(|x| !x.is_empty()); + let mut traces = Vec::new(); + for entry in listener.entries.iter() { + let mut result: Vec = entry + .into_iter() + .map(|(_, it)| { + let from = it.from; + let trace_address = it.trace_address.clone(); + let value = it.value; + let gas = it.gas; + let gas_used = it.gas_used; + let inner = it.inner.clone(); + Call::CallTracer(CallTracerCall { + from, + gas, + gas_used, + trace_address: Some(trace_address.clone()), + inner: match inner.clone() { + BlockscoutCallInner::Call { + input, + to, + res, + call_type, + } => CallTracerInner::Call { + call_type: match call_type { + CallType::Call => "CALL".as_bytes().to_vec(), + CallType::CallCode => "CALLCODE".as_bytes().to_vec(), + CallType::DelegateCall => "DELEGATECALL".as_bytes().to_vec(), + CallType::StaticCall => "STATICCALL".as_bytes().to_vec(), + }, + to, + input, + res, + value: Some(value), + }, + BlockscoutCallInner::Create { init, res } => CallTracerInner::Create { + input: init, + error: match res { + CreateResult::Success { .. } => None, + CreateResult::Error { ref error } => Some(error.clone()), + }, + to: match res { + CreateResult::Success { + created_contract_address_hash, + .. + } => Some(created_contract_address_hash), + CreateResult::Error { .. } => None, + }, + output: match res { + CreateResult::Success { + created_contract_code, + .. + } => Some(created_contract_code), + CreateResult::Error { .. } => None, + }, + value, + call_type: "CREATE".as_bytes().to_vec(), + }, + BlockscoutCallInner::SelfDestruct { balance, to } => { + CallTracerInner::SelfDestruct { + value: balance, + to, + call_type: "SELFDESTRUCT".as_bytes().to_vec(), + } + } + }, + calls: Vec::new(), + }) + }) + .collect(); + // Geth's `callTracer` expects a tree of nested calls and we have a stack. + // + // We iterate over the sorted stack, and push each children to it's + // parent (the item which's `trace_address` matches &T[0..T.len()-1]) until there + // is a single item on the list. + // + // The last remaining item is the context call with all it's descendants. I.e. + // + // # Input + // [] + // [0] + // [0,0] + // [0,0,0] + // [0,1] + // [0,1,0] + // [0,1,1] + // [0,1,2] + // [1] + // [1,0] + // + // # Sorted + // [0,0,0] -> pop 0 and push to [0,0] + // [0,1,0] -> pop 0 and push to [0,1] + // [0,1,1] -> pop 1 and push to [0,1] + // [0,1,2] -> pop 2 and push to [0,1] + // [0,0] -> pop 0 and push to [0] + // [0,1] -> pop 1 and push to [0] + // [1,0] -> pop 0 and push to [1] + // [0] -> pop 0 and push to root + // [1] -> pop 1 and push to root + // [] + // + // # Result + // root { + // calls: { + // 0 { 0 { 0 }, 1 { 0, 1, 2 }}, + // 1 { 0 }, + // } + // } + if result.len() > 1 { + // Sort the stack. Assume there is no `Ordering::Equal`, as we are + // sorting by index. + // + // We consider an item to be `Ordering::Less` when: + // - Is closer to the root or + // - Is greater than its sibling. + result.sort_by(|a, b| match (a, b) { + ( + Call::CallTracer(CallTracerCall { + trace_address: Some(a), + .. + }), + Call::CallTracer(CallTracerCall { + trace_address: Some(b), + .. + }), + ) => { + let a_len = a.len(); + let b_len = b.len(); + let sibling_greater_than = |a: &Vec, b: &Vec| -> bool { + for (i, a_value) in a.iter().enumerate() { + if a_value > &b[i] { + return true; + } else if a_value < &b[i] { + return false; + } else { + continue; + } + } + return false; + }; + if b_len > a_len || (a_len == b_len && sibling_greater_than(&a, &b)) { + Ordering::Less + } else { + Ordering::Greater + } + } + _ => unreachable!(), + }); + // Stack pop-and-push. + while result.len() > 1 { + let mut last = result + .pop() + .expect("result.len() > 1, so pop() necessarily returns an element"); + // Find the parent index. + if let Some(index) = + result + .iter() + .position(|current| match (last.clone(), current) { + ( + Call::CallTracer(CallTracerCall { + trace_address: Some(a), + .. + }), + Call::CallTracer(CallTracerCall { + trace_address: Some(b), + .. + }), + ) => { + match a.get(0..a.len() - 1) { + Some(slice) => { + &b[..] == slice + } + // "non-root element while traversing trace result" + None => { + false + } + } + } + _ => unreachable!(), + }) { + // Remove `trace_address` from result. + if let Call::CallTracer(CallTracerCall { + ref mut trace_address, + .. + }) = last + { + *trace_address = None; + } + // Push the children to parent. + if let Some(Call::CallTracer(CallTracerCall { calls, .. })) = + result.get_mut(index) + { + calls.push(last); + } + } + } + } + // Remove `trace_address` from result. + if let Some(Call::CallTracer(CallTracerCall { trace_address, .. })) = result.get_mut(0) + { + *trace_address = None; + } + if result.len() == 1 { + traces.push(TransactionTrace::CallListNested(result.pop().expect( + "result.len() == 1, so pop() necessarily returns this element", + ))); + } + } + if traces.is_empty() { + return None; + } + return Some(traces); + } +} + +#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CallTracerCall { + pub from: H160, + + /// Indices of parent calls. Used to build the Etherscan nested response. + #[serde(skip_serializing_if = "Option::is_none")] + pub trace_address: Option>, + + /// Remaining gas in the runtime. + pub gas: U256, + /// Gas used by this context. + pub gas_used: U256, + + #[serde(flatten)] + pub inner: CallTracerInner, + + #[serde(skip_serializing_if = "Vec::is_empty")] + pub calls: Vec, +} + +#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, Serialize)] +#[serde(untagged)] +pub enum CallTracerInner { + Call { + #[serde(rename = "type", serialize_with = "opcode_serialize")] + call_type: Vec, + to: H160, + #[serde(serialize_with = "bytes_0x_serialize")] + input: Vec, + /// "output" or "error" field + #[serde(flatten)] + res: CallResult, + + #[serde(skip_serializing_if = "Option::is_none")] + value: Option, + }, + Create { + #[serde(rename = "type", serialize_with = "opcode_serialize")] + call_type: Vec, + #[serde(serialize_with = "bytes_0x_serialize")] + input: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + to: Option, + #[serde( + skip_serializing_if = "Option::is_none", + serialize_with = "option_bytes_0x_serialize" + )] + output: Option>, + #[serde( + skip_serializing_if = "Option::is_none", + serialize_with = "option_string_serialize" + )] + error: Option>, + value: U256, + }, + SelfDestruct { + #[serde(rename = "type", serialize_with = "opcode_serialize")] + call_type: Vec, + to: H160, + value: U256, + }, +} diff --git a/client/bifrost/evm-tracing/src/formatters/mod.rs b/client/bifrost/evm-tracing/src/formatters/mod.rs new file mode 100644 index 0000000000..6c3df5c3f3 --- /dev/null +++ b/client/bifrost/evm-tracing/src/formatters/mod.rs @@ -0,0 +1,19 @@ +pub mod blockscout; +pub mod call_tracer; +pub mod raw; +pub mod trace_filter; + +pub use blockscout::Formatter as Blockscout; +pub use call_tracer::Formatter as CallTracer; +pub use raw::Formatter as Raw; +pub use trace_filter::Formatter as TraceFilter; + +use fp_rpc_evm_tracing_events::Listener; +use serde::Serialize; + +pub trait ResponseFormatter { + type Listener: Listener; + type Response: Serialize; + + fn format(listener: Self::Listener) -> Option; +} diff --git a/client/bifrost/evm-tracing/src/formatters/raw.rs b/client/bifrost/evm-tracing/src/formatters/raw.rs new file mode 100644 index 0000000000..3c4ea7b358 --- /dev/null +++ b/client/bifrost/evm-tracing/src/formatters/raw.rs @@ -0,0 +1,20 @@ +use crate::{listeners::raw::Listener, types::single::TransactionTrace}; + +pub struct Formatter; + +impl super::ResponseFormatter for Formatter { + type Listener = Listener; + type Response = TransactionTrace; + + fn format(listener: Listener) -> Option { + if listener.remaining_memory_usage.is_none() { + None + } else { + Some(TransactionTrace::Raw { + struct_logs: listener.struct_logs, + gas: listener.final_gas.into(), + return_value: listener.return_value, + }) + } + } +} diff --git a/client/bifrost/evm-tracing/src/formatters/trace_filter.rs b/client/bifrost/evm-tracing/src/formatters/trace_filter.rs new file mode 100644 index 0000000000..5e7c0168c7 --- /dev/null +++ b/client/bifrost/evm-tracing/src/formatters/trace_filter.rs @@ -0,0 +1,121 @@ +use super::blockscout::BlockscoutCallInner as CallInner; +use crate::{ + listeners::call_list::Listener, + types::{ + block::{ + TransactionTrace, TransactionTraceAction, TransactionTraceOutput, + TransactionTraceResult, + }, + CallResult, CreateResult, CreateType, + }, +}; +use ethereum_types::H256; + +pub struct Formatter; + +impl super::ResponseFormatter for Formatter { + type Listener = Listener; + type Response = Vec; + + fn format(mut listener: Listener) -> Option> { + // Remove empty BTreeMaps pushed to `entries`. + // I.e. InvalidNonce or other pallet_evm::runner exits + listener.entries.retain(|x| !x.is_empty()); + let mut traces = Vec::new(); + for (eth_tx_index, entry) in listener.entries.iter().enumerate() { + let mut tx_traces: Vec<_> = entry + .into_iter() + .map(|(_, trace)| match trace.inner.clone() { + CallInner::Call { + input, + to, + res, + call_type, + } => TransactionTrace { + action: TransactionTraceAction::Call { + call_type, + from: trace.from, + gas: trace.gas, + input, + to, + value: trace.value, + }, + // Can't be known here, must be inserted upstream. + block_hash: H256::default(), + // Can't be known here, must be inserted upstream. + block_number: 0, + output: match res { + CallResult::Output(output) => { + TransactionTraceOutput::Result(TransactionTraceResult::Call { + gas_used: trace.gas_used, + output, + }) + } + CallResult::Error(error) => TransactionTraceOutput::Error(error), + }, + subtraces: trace.subtraces, + trace_address: trace.trace_address.clone(), + // Can't be known here, must be inserted upstream. + transaction_hash: H256::default(), + transaction_position: eth_tx_index as u32, + }, + CallInner::Create { init, res } => { + TransactionTrace { + action: TransactionTraceAction::Create { + creation_method: CreateType::Create, + from: trace.from, + gas: trace.gas, + init, + value: trace.value, + }, + // Can't be known here, must be inserted upstream. + block_hash: H256::default(), + // Can't be known here, must be inserted upstream. + block_number: 0, + output: match res { + CreateResult::Success { + created_contract_address_hash, + created_contract_code, + } => { + TransactionTraceOutput::Result(TransactionTraceResult::Create { + gas_used: trace.gas_used, + code: created_contract_code, + address: created_contract_address_hash, + }) + } + CreateResult::Error { error } => { + TransactionTraceOutput::Error(error) + } + }, + subtraces: trace.subtraces, + trace_address: trace.trace_address.clone(), + // Can't be known here, must be inserted upstream. + transaction_hash: H256::default(), + transaction_position: eth_tx_index as u32, + } + } + CallInner::SelfDestruct { balance, to } => TransactionTrace { + action: TransactionTraceAction::Suicide { + address: trace.from, + balance, + refund_address: to, + }, + // Can't be known here, must be inserted upstream. + block_hash: H256::default(), + // Can't be known here, must be inserted upstream. + block_number: 0, + output: TransactionTraceOutput::Result(TransactionTraceResult::Suicide), + subtraces: trace.subtraces, + trace_address: trace.trace_address.clone(), + // Can't be known here, must be inserted upstream. + transaction_hash: H256::default(), + transaction_position: eth_tx_index as u32, + }, + }) + .collect(); + + traces.append(&mut tx_traces); + } + Some(traces) + } +} diff --git a/client/bifrost/evm-tracing/src/lib.rs b/client/bifrost/evm-tracing/src/lib.rs new file mode 100644 index 0000000000..3b1e72dbed --- /dev/null +++ b/client/bifrost/evm-tracing/src/lib.rs @@ -0,0 +1,5 @@ +//! This crate contains the client-side part that interacts with our "v2" tracing design. + +pub mod formatters; +pub mod listeners; +pub mod types; diff --git a/client/bifrost/evm-tracing/src/listeners/call_list.rs b/client/bifrost/evm-tracing/src/listeners/call_list.rs new file mode 100644 index 0000000000..efb71252d3 --- /dev/null +++ b/client/bifrost/evm-tracing/src/listeners/call_list.rs @@ -0,0 +1,1115 @@ +use crate::{ + formatters::blockscout::{BlockscoutCall as Call, BlockscoutCallInner as CallInner}, + types::{CallResult, CallType, ContextType, CreateResult}, +}; +use ethereum_types::{H160, U256}; +use fp_rpc_evm_tracing_events::{ + runtime::{Capture, ExitError, ExitReason, ExitSucceed}, + Event, EvmEvent, GasometerEvent, Listener as ListenerT, RuntimeEvent, StepEventFilter, +}; +use std::{collections::btree_map::BTreeMap, vec, vec::Vec}; + +/// Enum of the different "modes" of tracer for multiple runtime versions and +/// the kind of EVM events that are emitted. +enum TracingVersion { + /// The first event of the transaction is `EvmEvent::TransactX`. It goes along other events + /// such as `EvmEvent::Exit`. All contexts should have clear start/end boundaries. + EarlyTransact, + /// Older version in which the events above didn't existed. + /// It means that we cannot rely on those events to perform any task, and must rely only + /// on other events. + Legacy, +} + +pub struct Listener { + /// Version of the tracing. + /// Defaults to legacy, and switch to a more modern version if recently added events are + /// received. + version: TracingVersion, + + // Transaction cost that must be added to the first context cost. + transaction_cost: u64, + + // Final logs. + pub entries: Vec>, + // Next index to use. + entries_next_index: u32, + // Stack of contexts with data to keep between events. + context_stack: Vec, + + // Type of the next call. + // By default is None and corresponds to the root call, which + // can be determined using the `is_static` field of the `Call` event. + // Then by looking at call traps events we can set this value to the correct + // call type, to be used when the following `Call` event is received. + call_type: Option, + + /// When `EvmEvent::TransactX` is received it creates its own context. However it will usually + /// be followed by an `EvmEvent::Call/Create` that will also create a context, which must be + /// prevented. It must however not be skipped if `EvmEvent::TransactX` was not received + /// (in legacy mode). + skip_next_context: bool, + + // /// To handle EvmEvent::Exit no emitted by previous runtimes versions, + // /// entries are not inserted directly in `self.entries`. + // pending_entries: Vec<(u32, Call)>, + /// See `RuntimeEvent::StepResult` event explanatioins. + step_result_entry: Option<(u32, Call)>, + + /// When tracing a block `Event::CallListNew` is emitted before each Ethereum transaction is + /// processed. Since we use that event to **finish** the transaction, we must ignore the first + /// one. + call_list_first_transaction: bool, + + /// True if only the `GasometerEvent::RecordTransaction` event has been received. + /// Allow to correctly handle transactions that cannot pay for the tx data in Legacy mode. + record_transaction_event_only: bool, +} + +struct Context { + entries_index: u32, + + context_type: ContextType, + + from: H160, + trace_address: Vec, + subtraces: u32, + value: U256, + + gas: u64, + start_gas: Option, + + // input / data + data: Vec, + // to / create address + to: H160, +} + +impl Default for Listener { + fn default() -> Self { + Self { + version: TracingVersion::Legacy, + transaction_cost: 0, + + entries: vec![], + entries_next_index: 0, + + context_stack: vec![], + + call_type: None, + step_result_entry: None, + skip_next_context: false, + call_list_first_transaction: true, + record_transaction_event_only: false, + } + } +} + +impl Listener { + pub fn using R>(&mut self, f: F) -> R { + fp_rpc_evm_tracing_events::using(self, f) + } + + /// Called at the end of each transaction when tracing. + /// Allow to insert the pending entries regardless of which runtime version + /// is used (with or without EvmEvent::Exit). + pub fn finish_transaction(&mut self) { + // remove any leftover context + let mut context_stack = vec![]; + core::mem::swap(&mut self.context_stack, &mut context_stack); + + // if there is a left over there have been an early exit. + // we generate an entry from it and discord any inner context. + if let Some(context) = context_stack.into_iter().next() { + let mut gas_used = context.start_gas.unwrap_or(0) - context.gas; + if context.entries_index == 0 { + gas_used += self.transaction_cost; + } + + let entry = match context.context_type { + ContextType::Call(call_type) => { + let res = CallResult::Error( + b"early exit (out of gas, stack overflow, direct call to precompile, ...)" + .to_vec(), + ); + Call { + from: context.from, + trace_address: context.trace_address, + subtraces: context.subtraces, + value: context.value, + gas: context.gas.into(), + gas_used: gas_used.into(), + inner: CallInner::Call { + call_type, + to: context.to, + input: context.data, + res, + }, + } + } + ContextType::Create => { + let res = CreateResult::Error { + error: b"early exit (out of gas, stack overflow, direct call to precompile, ...)".to_vec(), + }; + + Call { + value: context.value, + trace_address: context.trace_address, + subtraces: context.subtraces, + gas: context.gas.into(), + gas_used: gas_used.into(), + from: context.from, + inner: CallInner::Create { + init: context.data, + res, + }, + } + } + }; + + self.insert_entry(context.entries_index, entry); + // Since only this context/entry is kept, we need update entries_next_index too. + self.entries_next_index = context.entries_index + 1; + } + // However if the transaction had a too low gas limit to pay for the data cost itself, + // and `EvmEvent::Exit` is not emitted in **Legacy mode**, then it has never produced any + // context (and exited **early in the transaction**). + else if self.record_transaction_event_only { + let res = CallResult::Error( + b"transaction could not pay its own data cost (impossible to gather more info)" + .to_vec(), + ); + + let entry = Call { + from: H160::repeat_byte(0), + trace_address: vec![], + subtraces: 0, + value: 0.into(), + gas: 0.into(), + gas_used: 0.into(), + inner: CallInner::Call { + call_type: CallType::Call, + to: H160::repeat_byte(0), + input: vec![], + res, + }, + }; + + self.insert_entry(self.entries_next_index, entry); + self.entries_next_index += 1; + } + } + + pub fn gasometer_event(&mut self, event: GasometerEvent) { + match event { + GasometerEvent::RecordCost { snapshot, .. } + | GasometerEvent::RecordDynamicCost { snapshot, .. } + | GasometerEvent::RecordStipend { snapshot, .. } => { + if let Some(context) = self.context_stack.last_mut() { + if context.start_gas.is_none() { + context.start_gas = Some(snapshot.gas()); + } + context.gas = snapshot.gas(); + } + } + GasometerEvent::RecordTransaction { cost, .. } => { + self.transaction_cost = cost; + self.record_transaction_event_only = true; + } + // We ignore other kinds of message if any (new ones may be added in the future). + #[allow(unreachable_patterns)] + _ => (), + } + } + + pub fn runtime_event(&mut self, event: RuntimeEvent) { + match event { + RuntimeEvent::StepResult { + result: Err(Capture::Trap(opcode)), + .. + } => { + if let Some(ContextType::Call(call_type)) = ContextType::from(opcode) { + self.call_type = Some(call_type) + } + } + RuntimeEvent::StepResult { + result: Err(Capture::Exit(reason)), + return_value, + } => { + if let Some((key, entry)) = self.pop_context_to_entry(reason, return_value) { + match self.version { + TracingVersion::Legacy => { + // In Legacy mode we directly insert the entry. + self.insert_entry(key, entry); + } + TracingVersion::EarlyTransact => { + // In EarlyTransact mode this context must be used if this event is + // emitted. However the context of `EvmEvent::Exit` must be used if + // `StepResult` is skipped. For that reason we store this generated + // entry in a temporary value, and deal with it in `EvmEvent::Exit` that + // will be called in all cases. + self.step_result_entry = Some((key, entry)); + } + } + } + } + // We ignore other kinds of message if any (new ones may be added in the future). + #[allow(unreachable_patterns)] + _ => (), + } + } + + pub fn evm_event(&mut self, event: EvmEvent) { + match event { + EvmEvent::TransactCall { + caller, + address, + value, + data, + .. + } => { + self.record_transaction_event_only = false; + self.version = TracingVersion::EarlyTransact; + self.context_stack.push(Context { + entries_index: self.entries_next_index, + + context_type: ContextType::Call(CallType::Call), + + from: caller, + trace_address: vec![], + subtraces: 0, + value, + + gas: 0, + start_gas: None, + + data, + to: address, + }); + + self.entries_next_index += 1; + self.skip_next_context = true; + } + + EvmEvent::TransactCreate { + caller, + value, + init_code, + address, + .. + } => { + self.record_transaction_event_only = false; + self.version = TracingVersion::EarlyTransact; + self.context_stack.push(Context { + entries_index: self.entries_next_index, + + context_type: ContextType::Create, + + from: caller, + trace_address: vec![], + subtraces: 0, + value, + + gas: 0, + start_gas: None, + + data: init_code, + to: address, + }); + + self.entries_next_index += 1; + self.skip_next_context = true; + } + + EvmEvent::TransactCreate2 { + caller, + value, + init_code, + address, + .. + } => { + self.record_transaction_event_only = false; + self.version = TracingVersion::EarlyTransact; + self.context_stack.push(Context { + entries_index: self.entries_next_index, + + context_type: ContextType::Create, + + from: caller, + trace_address: vec![], + subtraces: 0, + value, + + gas: 0, + start_gas: None, + + data: init_code, + to: address, + }); + + self.entries_next_index += 1; + self.skip_next_context = true; + } + + EvmEvent::Call { + code_address, + input, + is_static, + context, + .. + } => { + self.record_transaction_event_only = false; + + let call_type = match (self.call_type, is_static) { + (None, true) => CallType::StaticCall, + (None, false) => CallType::Call, + (Some(call_type), _) => call_type, + }; + + if !self.skip_next_context { + let trace_address = if let Some(context) = self.context_stack.last_mut() { + let mut trace_address = context.trace_address.clone(); + trace_address.push(context.subtraces); + context.subtraces += 1; + trace_address + } else { + vec![] + }; + + // For subcalls we want to have "from" always be the parent context address + // instead of `context.caller`, since the latter will not have the correct + // value inside a DelegateCall. + let from = if let Some(parent_context) = self.context_stack.last() { + parent_context.to.clone() + } else { + context.caller + }; + + self.context_stack.push(Context { + entries_index: self.entries_next_index, + + context_type: ContextType::Call(call_type), + + from, + trace_address, + subtraces: 0, + value: context.apparent_value, + + gas: 0, + start_gas: None, + + data: input.to_vec(), + to: code_address, + }); + + self.entries_next_index += 1; + } else { + self.skip_next_context = false; + } + } + + EvmEvent::Create { + caller, + address, + // scheme, + value, + init_code, + .. + } => { + self.record_transaction_event_only = false; + + if !self.skip_next_context { + let trace_address = if let Some(context) = self.context_stack.last_mut() { + let mut trace_address = context.trace_address.clone(); + trace_address.push(context.subtraces); + context.subtraces += 1; + trace_address + } else { + vec![] + }; + + self.context_stack.push(Context { + entries_index: self.entries_next_index, + + context_type: ContextType::Create, + + from: caller, + trace_address, + subtraces: 0, + value, + + gas: 0, + start_gas: None, + + data: init_code.to_vec(), + to: address, + }); + + self.entries_next_index += 1; + } else { + self.skip_next_context = false; + } + } + EvmEvent::Suicide { + address, + target, + balance, + } => { + let trace_address = if let Some(context) = self.context_stack.last_mut() { + let mut trace_address = context.trace_address.clone(); + trace_address.push(context.subtraces); + context.subtraces += 1; + trace_address + } else { + vec![] + }; + + self.insert_entry( + self.entries_next_index, + Call { + from: address, // this contract is self destructing + trace_address, + subtraces: 0, + value: 0.into(), + gas: 0.into(), + gas_used: 0.into(), + inner: CallInner::SelfDestruct { + to: target, + balance, + }, + }, + ); + self.entries_next_index += 1; + } + EvmEvent::Exit { + reason, + return_value, + } => { + // We know we're in `TracingVersion::EarlyTransact` mode. + + self.record_transaction_event_only = false; + + let entry = self + .step_result_entry + .take() + .or_else(|| self.pop_context_to_entry(reason, return_value)); + + if let Some((key, entry)) = entry { + self.insert_entry(key, entry); + } + } + EvmEvent::PrecompileSubcall { .. } => { + // In a precompile subcall there is no CALL opcode result to observe, thus + // we need this new event. Precompile subcall might use non-standard call + // behavior (like batch precompile does) thus we simply consider this a call. + self.call_type = Some(CallType::Call); + } + + // We ignore other kinds of message if any (new ones may be added in the future). + #[allow(unreachable_patterns)] + _ => (), + } + } + + fn insert_entry(&mut self, key: u32, entry: Call) { + if let Some(ref mut last) = self.entries.last_mut() { + last.insert(key, entry); + } else { + let mut btree_map = BTreeMap::new(); + btree_map.insert(key, entry); + self.entries.push(btree_map); + } + } + + fn pop_context_to_entry( + &mut self, + reason: ExitReason, + return_value: Vec, + ) -> Option<(u32, Call)> { + if let Some(context) = self.context_stack.pop() { + let mut gas_used = context.start_gas.unwrap_or(0) - context.gas; + if context.entries_index == 0 { + gas_used += self.transaction_cost; + } + + Some(( + context.entries_index, + match context.context_type { + ContextType::Call(call_type) => { + let res = match &reason { + ExitReason::Succeed(ExitSucceed::Returned) => { + CallResult::Output(return_value.to_vec()) + } + ExitReason::Succeed(_) => CallResult::Output(vec![]), + ExitReason::Error(error) => CallResult::Error(error_message(error)), + + ExitReason::Revert(_) => { + CallResult::Error(b"execution reverted".to_vec()) + } + ExitReason::Fatal(_) => CallResult::Error(vec![]), + }; + + Call { + from: context.from, + trace_address: context.trace_address, + subtraces: context.subtraces, + value: context.value, + gas: context.gas.into(), + gas_used: gas_used.into(), + inner: CallInner::Call { + call_type, + to: context.to, + input: context.data, + res, + }, + } + } + ContextType::Create => { + let res = match &reason { + ExitReason::Succeed(_) => CreateResult::Success { + created_contract_address_hash: context.to, + created_contract_code: return_value.to_vec(), + }, + ExitReason::Error(error) => CreateResult::Error { + error: error_message(error), + }, + ExitReason::Revert(_) => CreateResult::Error { + error: b"execution reverted".to_vec(), + }, + ExitReason::Fatal(_) => CreateResult::Error { error: vec![] }, + }; + + Call { + value: context.value, + trace_address: context.trace_address, + subtraces: context.subtraces, + gas: context.gas.into(), + gas_used: gas_used.into(), + from: context.from, + inner: CallInner::Create { + init: context.data, + res, + }, + } + } + }, + )) + } else { + None + } + } +} + +fn error_message(error: &ExitError) -> Vec { + match error { + ExitError::StackUnderflow => "stack underflow", + ExitError::StackOverflow => "stack overflow", + ExitError::InvalidJump => "invalid jump", + ExitError::InvalidRange => "invalid range", + ExitError::DesignatedInvalid => "designated invalid", + ExitError::CallTooDeep => "call too deep", + ExitError::CreateCollision => "create collision", + ExitError::CreateContractLimit => "create contract limit", + ExitError::OutOfOffset => "out of offset", + ExitError::OutOfGas => "out of gas", + ExitError::OutOfFund => "out of funds", + ExitError::Other(err) => err, + _ => "unexpected error", + } + .as_bytes() + .to_vec() +} + +impl ListenerT for Listener { + fn event(&mut self, event: Event) { + match event { + Event::Gasometer(gasometer_event) => self.gasometer_event(gasometer_event), + Event::Runtime(runtime_event) => self.runtime_event(runtime_event), + Event::Evm(evm_event) => self.evm_event(evm_event), + Event::CallListNew() => { + if !self.call_list_first_transaction { + self.finish_transaction(); + self.skip_next_context = false; + self.entries.push(BTreeMap::new()); + } else { + self.call_list_first_transaction = false; + } + } + }; + } + + fn step_event_filter(&self) -> StepEventFilter { + StepEventFilter { + enable_memory: false, + enable_stack: false, + } + } +} + +#[cfg(test)] +#[allow(unused)] +mod tests { + use super::*; + use ethereum_types::H256; + use fp_rpc_evm_tracing_events::{ + evm::CreateScheme, + gasometer::Snapshot, + runtime::{Memory, Stack}, + Context as EvmContext, + }; + + enum TestEvmEvent { + Call, + Create, + Suicide, + Exit, + TransactCall, + TransactCreate, + TransactCreate2, + } + + enum TestRuntimeEvent { + Step, + StepResult, + SLoad, + SStore, + } + + enum TestGasometerEvent { + RecordCost, + RecordRefund, + RecordStipend, + RecordDynamicCost, + RecordTransaction, + } + + fn test_context() -> EvmContext { + EvmContext { + address: H160::default(), + caller: H160::default(), + apparent_value: U256::zero(), + } + } + + fn test_create_scheme() -> CreateScheme { + CreateScheme::Legacy { + caller: H160::default(), + } + } + + fn test_stack() -> Option { + None + } + + fn test_memory() -> Option { + None + } + + fn test_snapshot() -> Snapshot { + Snapshot { + gas_limit: 0u64, + memory_gas: 0u64, + used_gas: 0u64, + refunded_gas: 0i64, + } + } + + fn test_emit_evm_event( + event_type: TestEvmEvent, + is_static: bool, + exit_reason: Option, + ) -> EvmEvent { + match event_type { + TestEvmEvent::Call => EvmEvent::Call { + code_address: H160::default(), + transfer: None, + input: Vec::new(), + target_gas: None, + is_static, + context: test_context(), + }, + TestEvmEvent::Create => EvmEvent::Create { + caller: H160::default(), + address: H160::default(), + scheme: test_create_scheme(), + value: U256::zero(), + init_code: Vec::new(), + target_gas: None, + }, + TestEvmEvent::Suicide => EvmEvent::Suicide { + address: H160::default(), + target: H160::default(), + balance: U256::zero(), + }, + TestEvmEvent::Exit => EvmEvent::Exit { + reason: exit_reason.unwrap(), + return_value: Vec::new(), + }, + TestEvmEvent::TransactCall => EvmEvent::TransactCall { + caller: H160::default(), + address: H160::default(), + value: U256::zero(), + data: Vec::new(), + gas_limit: 0u64, + }, + TestEvmEvent::TransactCreate => EvmEvent::TransactCreate { + caller: H160::default(), + value: U256::zero(), + init_code: Vec::new(), + gas_limit: 0u64, + address: H160::default(), + }, + TestEvmEvent::TransactCreate2 => EvmEvent::TransactCreate2 { + caller: H160::default(), + value: U256::zero(), + init_code: Vec::new(), + salt: H256::default(), + gas_limit: 0u64, + address: H160::default(), + }, + } + } + + fn test_emit_runtime_event(event_type: TestRuntimeEvent) -> RuntimeEvent { + match event_type { + TestRuntimeEvent::Step => RuntimeEvent::Step { + context: test_context(), + opcode: Vec::new(), + position: Ok(0u64), + stack: test_stack(), + memory: test_memory(), + }, + TestRuntimeEvent::StepResult => RuntimeEvent::StepResult { + result: Ok(()), + return_value: Vec::new(), + }, + TestRuntimeEvent::SLoad => RuntimeEvent::SLoad { + address: H160::default(), + index: H256::default(), + value: H256::default(), + }, + TestRuntimeEvent::SStore => RuntimeEvent::SStore { + address: H160::default(), + index: H256::default(), + value: H256::default(), + }, + } + } + + fn test_emit_gasometer_event(event_type: TestGasometerEvent) -> GasometerEvent { + match event_type { + TestGasometerEvent::RecordCost => GasometerEvent::RecordCost { + cost: 0u64, + snapshot: test_snapshot(), + }, + TestGasometerEvent::RecordRefund => GasometerEvent::RecordRefund { + refund: 0i64, + snapshot: test_snapshot(), + }, + TestGasometerEvent::RecordStipend => GasometerEvent::RecordStipend { + stipend: 0u64, + snapshot: test_snapshot(), + }, + TestGasometerEvent::RecordDynamicCost => GasometerEvent::RecordDynamicCost { + gas_cost: 0u64, + memory_gas: 0u64, + gas_refund: 0i64, + snapshot: test_snapshot(), + }, + TestGasometerEvent::RecordTransaction => GasometerEvent::RecordTransaction { + cost: 0u64, + snapshot: test_snapshot(), + }, + } + } + + fn do_transact_call_event(listener: &mut Listener) { + listener.evm_event(test_emit_evm_event(TestEvmEvent::TransactCall, false, None)); + } + + fn do_transact_create_event(listener: &mut Listener) { + listener.evm_event(test_emit_evm_event( + TestEvmEvent::TransactCreate, + false, + None, + )); + } + + fn do_gasometer_event(listener: &mut Listener) { + listener.gasometer_event(test_emit_gasometer_event( + TestGasometerEvent::RecordTransaction, + )); + } + + fn do_exit_event(listener: &mut Listener) { + listener.evm_event(test_emit_evm_event( + TestEvmEvent::Exit, + false, + Some(ExitReason::Error(ExitError::OutOfGas)), + )); + } + + fn do_evm_call_event(listener: &mut Listener) { + listener.evm_event(test_emit_evm_event(TestEvmEvent::Call, false, None)); + } + + fn do_evm_create_event(listener: &mut Listener) { + listener.evm_event(test_emit_evm_event(TestEvmEvent::Create, false, None)); + } + + fn do_evm_suicide_event(listener: &mut Listener) { + listener.evm_event(test_emit_evm_event(TestEvmEvent::Suicide, false, None)); + } + + fn do_runtime_step_event(listener: &mut Listener) { + listener.runtime_event(test_emit_runtime_event(TestRuntimeEvent::Step)); + } + + fn do_runtime_step_result_event(listener: &mut Listener) { + listener.runtime_event(test_emit_runtime_event(TestRuntimeEvent::StepResult)); + } + + // Call context + + // Early exit on TransactionCost. + #[test] + fn call_early_exit_tx_cost() { + let mut listener = Listener::default(); + do_transact_call_event(&mut listener); + do_gasometer_event(&mut listener); + do_exit_event(&mut listener); + listener.finish_transaction(); + assert_eq!(listener.entries.len(), 1); + assert_eq!(listener.entries[0].len(), 1); + } + + // Early exit somewhere between the first callstack event and stepping the bytecode. + // I.e. precompile call. + #[test] + fn call_early_exit_before_runtime() { + let mut listener = Listener::default(); + do_transact_call_event(&mut listener); + do_gasometer_event(&mut listener); + do_evm_call_event(&mut listener); + do_exit_event(&mut listener); + listener.finish_transaction(); + assert_eq!(listener.entries.len(), 1); + assert_eq!(listener.entries[0].len(), 1); + } + + // Exit after Step without StepResult. + #[test] + fn call_step_without_step_result() { + let mut listener = Listener::default(); + do_transact_call_event(&mut listener); + do_gasometer_event(&mut listener); + do_evm_call_event(&mut listener); + do_runtime_step_event(&mut listener); + do_exit_event(&mut listener); + listener.finish_transaction(); + assert_eq!(listener.entries.len(), 1); + assert_eq!(listener.entries[0].len(), 1); + } + + // Exit after StepResult. + #[test] + fn call_step_result() { + let mut listener = Listener::default(); + do_transact_call_event(&mut listener); + do_gasometer_event(&mut listener); + do_evm_call_event(&mut listener); + do_runtime_step_event(&mut listener); + do_runtime_step_result_event(&mut listener); + do_exit_event(&mut listener); + listener.finish_transaction(); + assert_eq!(listener.entries.len(), 1); + assert_eq!(listener.entries[0].len(), 1); + } + + // Suicide. + #[test] + fn call_suicide() { + let mut listener = Listener::default(); + do_transact_call_event(&mut listener); + do_gasometer_event(&mut listener); + do_evm_call_event(&mut listener); + do_runtime_step_event(&mut listener); + do_evm_suicide_event(&mut listener); + do_exit_event(&mut listener); + listener.finish_transaction(); + assert_eq!(listener.entries.len(), 1); + assert_eq!(listener.entries[0].len(), 2); + } + + // Create context + + // Early exit on TransactionCost. + #[test] + fn create_early_exit_tx_cost() { + let mut listener = Listener::default(); + do_transact_create_event(&mut listener); + do_gasometer_event(&mut listener); + do_exit_event(&mut listener); + listener.finish_transaction(); + assert_eq!(listener.entries.len(), 1); + assert_eq!(listener.entries[0].len(), 1); + } + + // Early exit somewhere between the first callstack event and stepping the bytecode + // I.e. precompile call.. + #[test] + fn create_early_exit_before_runtime() { + let mut listener = Listener::default(); + do_transact_create_event(&mut listener); + do_gasometer_event(&mut listener); + do_evm_create_event(&mut listener); + do_exit_event(&mut listener); + listener.finish_transaction(); + assert_eq!(listener.entries.len(), 1); + assert_eq!(listener.entries[0].len(), 1); + } + + // Exit after Step without StepResult. + #[test] + fn create_step_without_step_result() { + let mut listener = Listener::default(); + do_transact_create_event(&mut listener); + do_gasometer_event(&mut listener); + do_evm_create_event(&mut listener); + do_runtime_step_event(&mut listener); + do_exit_event(&mut listener); + listener.finish_transaction(); + assert_eq!(listener.entries.len(), 1); + assert_eq!(listener.entries[0].len(), 1); + } + + // Exit after StepResult. + #[test] + fn create_step_result() { + let mut listener = Listener::default(); + do_transact_create_event(&mut listener); + do_gasometer_event(&mut listener); + do_evm_create_event(&mut listener); + do_runtime_step_event(&mut listener); + do_runtime_step_result_event(&mut listener); + do_exit_event(&mut listener); + listener.finish_transaction(); + assert_eq!(listener.entries.len(), 1); + assert_eq!(listener.entries[0].len(), 1); + } + + // Call Context Nested + + // Nested call early exit before stepping. + #[test] + fn nested_call_early_exit_before_runtime() { + let mut listener = Listener::default(); + // Main + do_transact_call_event(&mut listener); + do_gasometer_event(&mut listener); + do_evm_call_event(&mut listener); + do_runtime_step_event(&mut listener); + do_runtime_step_result_event(&mut listener); + // Nested + do_evm_call_event(&mut listener); + do_exit_event(&mut listener); + // Main exit + do_exit_event(&mut listener); + listener.finish_transaction(); + assert_eq!(listener.entries.len(), 1); + assert_eq!(listener.entries[0].len(), 2); + } + + // Nested exit before step result. + #[test] + fn nested_call_without_step_result() { + let mut listener = Listener::default(); + // Main + do_transact_call_event(&mut listener); + do_gasometer_event(&mut listener); + do_evm_call_event(&mut listener); + do_runtime_step_event(&mut listener); + do_runtime_step_result_event(&mut listener); + // Nested + do_evm_call_event(&mut listener); + do_runtime_step_event(&mut listener); + do_exit_event(&mut listener); + // Main exit + do_exit_event(&mut listener); + listener.finish_transaction(); + assert_eq!(listener.entries.len(), 1); + assert_eq!(listener.entries[0].len(), 2); + } + + // Nested exit. + #[test] + fn nested_call_step_result() { + let depth = 5; + let mut listener = Listener::default(); + // Main + do_transact_call_event(&mut listener); + do_gasometer_event(&mut listener); + do_evm_call_event(&mut listener); + do_runtime_step_event(&mut listener); + do_runtime_step_result_event(&mut listener); + // 5 nested calls + for d in 0..depth { + do_evm_call_event(&mut listener); + do_runtime_step_event(&mut listener); + do_runtime_step_result_event(&mut listener); + do_exit_event(&mut listener); + } + // Main exit + do_exit_event(&mut listener); + listener.finish_transaction(); + assert_eq!(listener.entries.len(), 1); + assert_eq!(listener.entries[0].len(), depth + 1); + } + + // Call + Create mixed subnesting. + + #[test] + fn subnested_call_and_create_mixbag() { + let depth = 5; + let subdepth = 10; + let mut listener = Listener::default(); + // Main + do_transact_call_event(&mut listener); + do_gasometer_event(&mut listener); + do_evm_call_event(&mut listener); + do_runtime_step_event(&mut listener); + do_runtime_step_result_event(&mut listener); + // 5 nested call/creates, each with 10 nested call/creates + for d in 0..depth { + if d % 2 == 0 { + do_evm_call_event(&mut listener); + } else { + do_evm_create_event(&mut listener); + } + do_runtime_step_event(&mut listener); + do_runtime_step_result_event(&mut listener); + for s in 0..subdepth { + // Some mixed call/create and early exits. + if s % 2 == 0 { + do_evm_call_event(&mut listener); + } else { + do_evm_create_event(&mut listener); + } + if s % 3 == 0 { + do_runtime_step_event(&mut listener); + do_runtime_step_result_event(&mut listener); + } + do_exit_event(&mut listener); + } + // Nested exit + do_exit_event(&mut listener); + } + // Main exit + do_exit_event(&mut listener); + listener.finish_transaction(); + assert_eq!(listener.entries.len(), 1); + // Each nested call contains 11 elements in the callstack (main + 10 subcalls). + // There are 5 main nested calls for a total of 56 elements in the callstack: 1 main + 55 nested. + assert_eq!(listener.entries[0].len(), (depth * (subdepth + 1)) + 1); + } +} diff --git a/client/bifrost/evm-tracing/src/listeners/mod.rs b/client/bifrost/evm-tracing/src/listeners/mod.rs new file mode 100644 index 0000000000..8b127d152a --- /dev/null +++ b/client/bifrost/evm-tracing/src/listeners/mod.rs @@ -0,0 +1,5 @@ +pub mod call_list; +pub mod raw; + +pub use call_list::Listener as CallList; +pub use raw::Listener as Raw; diff --git a/client/bifrost/evm-tracing/src/listeners/raw.rs b/client/bifrost/evm-tracing/src/listeners/raw.rs new file mode 100644 index 0000000000..9b4df6145b --- /dev/null +++ b/client/bifrost/evm-tracing/src/listeners/raw.rs @@ -0,0 +1,323 @@ +use ethereum_types::{H160, H256}; +use std::{collections::btree_map::BTreeMap, vec, vec::Vec}; + +use crate::types::{convert_memory, single::RawStepLog, ContextType}; +use fp_rpc_evm_tracing_events::{ + runtime::{Capture, ExitReason}, + Event, GasometerEvent, Listener as ListenerT, RuntimeEvent, StepEventFilter, +}; + +#[derive(Debug)] +pub struct Listener { + disable_storage: bool, + disable_memory: bool, + disable_stack: bool, + + new_context: bool, + context_stack: Vec, + + pub struct_logs: Vec, + pub return_value: Vec, + pub final_gas: u64, + pub remaining_memory_usage: Option, +} + +#[derive(Debug)] +struct Context { + storage_cache: BTreeMap, + address: H160, + current_step: Option, + global_storage_changes: BTreeMap>, +} + +#[derive(Debug)] +struct Step { + /// Current opcode. + opcode: Vec, + /// Depth of the context. + depth: usize, + /// Remaining gas. + gas: u64, + /// Gas cost of the following opcode. + gas_cost: u64, + /// Program counter position. + position: usize, + /// EVM memory copy (if not disabled). + memory: Option>, + /// EVM stack copy (if not disabled). + stack: Option>, +} + +impl Listener { + pub fn new( + disable_storage: bool, + disable_memory: bool, + disable_stack: bool, + raw_max_memory_usage: usize, + ) -> Self { + Self { + disable_storage, + disable_memory, + disable_stack, + remaining_memory_usage: Some(raw_max_memory_usage), + + struct_logs: vec![], + return_value: vec![], + final_gas: 0, + + new_context: false, + context_stack: vec![], + } + } + + pub fn using R>(&mut self, f: F) -> R { + fp_rpc_evm_tracing_events::using(self, f) + } + + pub fn gasometer_event(&mut self, event: GasometerEvent) { + match event { + GasometerEvent::RecordTransaction { cost, .. } => { + // First event of a transaction. + // Next step will be the first context. + self.new_context = true; + self.final_gas = cost; + } + GasometerEvent::RecordCost { cost, snapshot } => { + if let Some(context) = self.context_stack.last_mut() { + // Register opcode cost. (ignore costs not between Step and StepResult) + if let Some(step) = &mut context.current_step { + step.gas = snapshot.gas(); + step.gas_cost = cost; + } + + self.final_gas = snapshot.used_gas; + } + } + GasometerEvent::RecordDynamicCost { + gas_cost, snapshot, .. + } => { + if let Some(context) = self.context_stack.last_mut() { + // Register opcode cost. (ignore costs not between Step and StepResult) + if let Some(step) = &mut context.current_step { + step.gas = snapshot.gas(); + step.gas_cost = gas_cost; + } + + self.final_gas = snapshot.used_gas; + } + } + // We ignore other kinds of message if any (new ones may be added in the future). + #[allow(unreachable_patterns)] + _ => (), + } + } + + pub fn runtime_event(&mut self, event: RuntimeEvent) { + match event { + RuntimeEvent::Step { + context, + opcode, + position, + stack, + memory, + } => { + // Create a context if needed. + if self.new_context { + self.new_context = false; + + self.context_stack.push(Context { + storage_cache: BTreeMap::new(), + address: context.address, + current_step: None, + global_storage_changes: BTreeMap::new(), + }); + } + + let depth = self.context_stack.len(); + + // Ignore steps outside of any context (shouldn't even be possible). + if let Some(context) = self.context_stack.last_mut() { + context.current_step = Some(Step { + opcode, + depth, + gas: 0, // 0 for now, will add with gas events + gas_cost: 0, // 0 for now, will add with gas events + position: *position.as_ref().unwrap_or(&0) as usize, + memory: if self.disable_memory { + None + } else { + let memory = memory.expect("memory data to not be filtered out"); + + self.remaining_memory_usage = self + .remaining_memory_usage + .and_then(|inner| inner.checked_sub(memory.data.len())); + + if self.remaining_memory_usage.is_none() { + return; + } + + Some(memory.data.clone()) + }, + stack: if self.disable_stack { + None + } else { + let stack = stack.expect("stack data to not be filtered out"); + + self.remaining_memory_usage = self + .remaining_memory_usage + .and_then(|inner| inner.checked_sub(stack.data.len())); + + if self.remaining_memory_usage.is_none() { + return; + } + + Some(stack.data.clone()) + }, + }); + } + } + RuntimeEvent::StepResult { + result, + return_value, + } => { + // StepResult is expected to be emited after a step (in a context). + // Only case StepResult will occur without a Step before is in a transfer + // transaction to a non-contract address. However it will not contain any + // steps and return an empty trace, so we can ignore this edge case. + if let Some(context) = self.context_stack.last_mut() { + if let Some(current_step) = context.current_step.take() { + let Step { + opcode, + depth, + gas, + gas_cost, + position, + memory, + stack, + } = current_step; + + let memory = memory.map(convert_memory); + + let storage = if self.disable_storage { + None + } else { + self.remaining_memory_usage = + self.remaining_memory_usage.and_then(|inner| { + inner.checked_sub(context.storage_cache.len() * 64) + }); + + if self.remaining_memory_usage.is_none() { + return; + } + + Some(context.storage_cache.clone()) + }; + + self.struct_logs.push(RawStepLog { + depth: depth.into(), + gas: gas.into(), + gas_cost: gas_cost.into(), + memory, + op: opcode, + pc: position.into(), + stack, + storage, + }); + } + } + + // We match on the capture to handle traps/exits. + match result { + Err(Capture::Exit(reason)) => { + // Exit = we exit the context (should always be some) + if let Some(mut context) = self.context_stack.pop() { + // If final context is exited, we store gas and return value. + if self.context_stack.is_empty() { + self.return_value = return_value.to_vec(); + } + + // If the context exited without revert we must keep track of the + // updated storage keys. + if !self.disable_storage && matches!(reason, ExitReason::Succeed(_)) { + if let Some(parent_context) = self.context_stack.last_mut() { + // Add cache to storage changes. + context + .global_storage_changes + .insert(context.address, context.storage_cache); + + // Apply storage changes to parent, either updating its cache or map of changes. + for (address, mut storage) in + context.global_storage_changes.into_iter() + { + // Same address => We update its cache (only tracked keys) + if parent_context.address == address { + for (cached_key, cached_value) in + parent_context.storage_cache.iter_mut() + { + if let Some(value) = storage.remove(cached_key) { + *cached_value = value; + } + } + } + // Otherwise, update the storage changes. + else { + parent_context + .global_storage_changes + .entry(address) + .or_insert_with(BTreeMap::new) + .append(&mut storage); + } + } + } + } + } + } + Err(Capture::Trap(opcode)) if ContextType::from(opcode.clone()).is_some() => { + self.new_context = true; + } + _ => (), + } + } + RuntimeEvent::SLoad { + address: _, + index, + value, + } + | RuntimeEvent::SStore { + address: _, + index, + value, + } => { + if let Some(context) = self.context_stack.last_mut() { + if !self.disable_storage { + context.storage_cache.insert(index, value); + } + } + } + // We ignore other kinds of message if any (new ones may be added in the future). + #[allow(unreachable_patterns)] + _ => (), + } + } +} + +impl ListenerT for Listener { + fn event(&mut self, event: Event) { + if self.remaining_memory_usage.is_none() { + return; + } + + match event { + Event::Gasometer(e) => self.gasometer_event(e), + Event::Runtime(e) => self.runtime_event(e), + _ => {} + }; + } + + fn step_event_filter(&self) -> StepEventFilter { + StepEventFilter { + enable_memory: !self.disable_memory, + enable_stack: !self.disable_stack, + } + } +} diff --git a/client/bifrost/evm-tracing/src/types/block.rs b/client/bifrost/evm-tracing/src/types/block.rs new file mode 100644 index 0000000000..266cf4fe2d --- /dev/null +++ b/client/bifrost/evm-tracing/src/types/block.rs @@ -0,0 +1,81 @@ +//! Types for tracing all Ethereum transactions of a block. + +use super::serialization::*; +use serde::Serialize; + +use ethereum_types::{H160, H256, U256}; +use scale_codec::{Decode, Encode}; +use sp_std::vec::Vec; + +#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct TransactionTrace { + #[serde(flatten)] + pub action: TransactionTraceAction, + #[serde(serialize_with = "h256_0x_serialize")] + pub block_hash: H256, + pub block_number: u32, + #[serde(flatten)] + pub output: TransactionTraceOutput, + pub subtraces: u32, + pub trace_address: Vec, + #[serde(serialize_with = "h256_0x_serialize")] + pub transaction_hash: H256, + pub transaction_position: u32, +} + +#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, Serialize)] +#[serde(rename_all = "camelCase", tag = "type", content = "action")] +pub enum TransactionTraceAction { + #[serde(rename_all = "camelCase")] + Call { + call_type: super::CallType, + from: H160, + gas: U256, + #[serde(serialize_with = "bytes_0x_serialize")] + input: Vec, + to: H160, + value: U256, + }, + #[serde(rename_all = "camelCase")] + Create { + creation_method: super::CreateType, + from: H160, + gas: U256, + #[serde(serialize_with = "bytes_0x_serialize")] + init: Vec, + value: U256, + }, + #[serde(rename_all = "camelCase")] + Suicide { + address: H160, + balance: U256, + refund_address: H160, + }, +} + +#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum TransactionTraceOutput { + Result(TransactionTraceResult), + Error(#[serde(serialize_with = "string_serialize")] Vec), +} + +#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, Serialize)] +#[serde(rename_all = "camelCase", untagged)] +pub enum TransactionTraceResult { + #[serde(rename_all = "camelCase")] + Call { + gas_used: U256, + #[serde(serialize_with = "bytes_0x_serialize")] + output: Vec, + }, + #[serde(rename_all = "camelCase")] + Create { + address: H160, + #[serde(serialize_with = "bytes_0x_serialize")] + code: Vec, + gas_used: U256, + }, + Suicide, +} diff --git a/client/bifrost/evm-tracing/src/types/mod.rs b/client/bifrost/evm-tracing/src/types/mod.rs new file mode 100644 index 0000000000..224281b87e --- /dev/null +++ b/client/bifrost/evm-tracing/src/types/mod.rs @@ -0,0 +1,97 @@ +//! Runtime API allowing to debug/trace Ethereum + +extern crate alloc; + +use ethereum_types::{H160, H256}; +use scale_codec::{Decode, Encode}; +use sp_std::vec::Vec; + +pub mod block; +pub mod serialization; +pub mod single; + +use serde::Serialize; +use serialization::*; + +pub const MANUAL_BLOCK_INITIALIZATION_RUNTIME_VERSION: u32 = 159; + +#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum CallResult { + Output(#[serde(serialize_with = "bytes_0x_serialize")] Vec), + // field "error" + Error(#[serde(serialize_with = "string_serialize")] Vec), +} + +#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, Serialize)] +#[serde(rename_all = "camelCase", untagged)] +pub enum CreateResult { + Error { + #[serde(serialize_with = "string_serialize")] + error: Vec, + }, + Success { + #[serde(rename = "createdContractAddressHash")] + created_contract_address_hash: H160, + #[serde(serialize_with = "bytes_0x_serialize", rename = "createdContractCode")] + created_contract_code: Vec, + }, +} + +#[derive(Clone, Copy, Eq, PartialEq, Debug, Encode, Decode, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum CallType { + Call, + CallCode, + DelegateCall, + StaticCall, +} + +#[derive(Clone, Copy, Eq, PartialEq, Debug, Encode, Decode, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum CreateType { + Create, +} + +#[derive(Debug)] +pub enum ContextType { + Call(CallType), + Create, +} + +impl ContextType { + pub fn from(opcode: Vec) -> Option { + let opcode = match alloc::str::from_utf8(&opcode[..]) { + Ok(op) => op.to_uppercase(), + _ => return None, + }; + match &opcode[..] { + "CREATE" | "CREATE2" => Some(ContextType::Create), + "CALL" => Some(ContextType::Call(CallType::Call)), + "CALLCODE" => Some(ContextType::Call(CallType::CallCode)), + "DELEGATECALL" => Some(ContextType::Call(CallType::DelegateCall)), + "STATICCALL" => Some(ContextType::Call(CallType::StaticCall)), + _ => None, + } + } +} + +pub fn convert_memory(memory: Vec) -> Vec { + let size = 32; + memory + .chunks(size) + .map(|c| { + let mut msg = [0u8; 32]; + let chunk = c.len(); + if chunk < size { + let left = size - chunk; + let remainder = vec![0; left]; + msg[0..left].copy_from_slice(&remainder[..]); + msg[left..size].copy_from_slice(c); + } else { + msg[0..size].copy_from_slice(c) + } + H256::from_slice(&msg[..]) + }) + .collect() +} diff --git a/client/bifrost/evm-tracing/src/types/serialization.rs b/client/bifrost/evm-tracing/src/types/serialization.rs new file mode 100644 index 0000000000..83f3c8fd92 --- /dev/null +++ b/client/bifrost/evm-tracing/src/types/serialization.rs @@ -0,0 +1,97 @@ +//! Provide serialization functions for various types and formats. + +use ethereum_types::{H256, U256}; +use serde::{ + ser::{Error, SerializeSeq}, + Serializer, +}; + +pub fn seq_h256_serialize(data: &Option>, serializer: S) -> Result +where + S: Serializer, +{ + if let Some(vec) = data { + let mut seq = serializer.serialize_seq(Some(vec.len()))?; + for hash in vec { + seq.serialize_element(&format!("{:x}", hash))?; + } + seq.end() + } else { + let seq = serializer.serialize_seq(Some(0))?; + seq.end() + } +} + +pub fn bytes_0x_serialize(bytes: &[u8], serializer: S) -> Result +where + S: Serializer, +{ + serializer.serialize_str(&format!("0x{}", hex::encode(bytes))) +} + +pub fn option_bytes_0x_serialize( + bytes: &Option>, + serializer: S, +) -> Result +where + S: Serializer, +{ + if let Some(bytes) = bytes.as_ref() { + return serializer.serialize_str(&format!("0x{}", hex::encode(&bytes[..]))); + } + Err(S::Error::custom("String serialize error.")) +} + +pub fn opcode_serialize(opcode: &[u8], serializer: S) -> Result +where + S: Serializer, +{ + let d = std::str::from_utf8(opcode) + .map_err(|_| S::Error::custom("Opcode serialize error."))? + .to_uppercase(); + serializer.serialize_str(&d) +} + +pub fn string_serialize(value: &[u8], serializer: S) -> Result +where + S: Serializer, +{ + let d = std::str::from_utf8(value) + .map_err(|_| S::Error::custom("String serialize error."))? + .to_string(); + serializer.serialize_str(&d) +} + +pub fn option_string_serialize(value: &Option>, serializer: S) -> Result +where + S: Serializer, +{ + if let Some(value) = value.as_ref() { + let d = std::str::from_utf8(&value[..]) + .map_err(|_| S::Error::custom("String serialize error."))? + .to_string(); + return serializer.serialize_str(&d); + } + Err(S::Error::custom("String serialize error.")) +} + +pub fn u256_serialize(data: &U256, serializer: S) -> Result +where + S: Serializer, +{ + serializer.serialize_u64(data.low_u64()) +} + +pub fn h256_serialize(data: &H256, serializer: S) -> Result +where + S: Serializer, +{ + serializer.serialize_str(&format!("{:x}", data)) +} + +pub fn h256_0x_serialize(data: &H256, serializer: S) -> Result +where + S: Serializer, +{ + serializer.serialize_str(&format!("0x{:x}", data)) +} diff --git a/client/bifrost/evm-tracing/src/types/single.rs b/client/bifrost/evm-tracing/src/types/single.rs new file mode 100644 index 0000000000..237cf8b11d --- /dev/null +++ b/client/bifrost/evm-tracing/src/types/single.rs @@ -0,0 +1,86 @@ +//! Types for the tracing of a single Ethereum transaction. +//! Structure from "raw" debug_trace and a "call list" matching +//! Blockscout formatter. This "call list" is also used to build +//! the whole block tracing output. + +use super::serialization::*; +use serde::Serialize; + +use ethereum_types::{H256, U256}; +use scale_codec::{Decode, Encode}; +use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; + +#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, Serialize)] +#[serde(rename_all = "camelCase", untagged)] +pub enum Call { + Blockscout(crate::formatters::blockscout::BlockscoutCall), + CallTracer(crate::formatters::call_tracer::CallTracerCall), +} + +#[derive(Clone, Copy, Eq, PartialEq, Debug, Encode, Decode)] +pub enum TraceType { + /// Classic geth with no javascript based tracing. + Raw { + disable_storage: bool, + disable_memory: bool, + disable_stack: bool, + }, + /// List of calls and subcalls formatted with an input tracer (i.e. callTracer or Blockscout). + CallList, + /// A single block trace. Use in `debug_traceTransactionByNumber` / `traceTransactionByHash`. + Block, +} + +/// Single transaction trace. +#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, Serialize)] +#[serde(rename_all = "camelCase", untagged)] +pub enum TransactionTrace { + /// Classical output of `debug_trace`. + #[serde(rename_all = "camelCase")] + Raw { + gas: U256, + #[serde(with = "hex")] + return_value: Vec, + struct_logs: Vec, + }, + /// Matches the formatter used by Blockscout. + /// Is also used to built output of OpenEthereum's `trace_filter`. + CallList(Vec), + /// Used by Geth's callTracer. + CallListNested(Call), +} + +#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RawStepLog { + #[serde(serialize_with = "u256_serialize")] + pub depth: U256, + + //error: TODO + #[serde(serialize_with = "u256_serialize")] + pub gas: U256, + + #[serde(serialize_with = "u256_serialize")] + pub gas_cost: U256, + + #[serde( + serialize_with = "seq_h256_serialize", + skip_serializing_if = "Option::is_none" + )] + pub memory: Option>, + + #[serde(serialize_with = "opcode_serialize")] + pub op: Vec, + + #[serde(serialize_with = "u256_serialize")] + pub pc: U256, + + #[serde( + serialize_with = "seq_h256_serialize", + skip_serializing_if = "Option::is_none" + )] + pub stack: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + pub storage: Option>, +} diff --git a/client/bifrost/rpc-core/debug/Cargo.toml b/client/bifrost/rpc-core/debug/Cargo.toml new file mode 100644 index 0000000000..a4458c8d9e --- /dev/null +++ b/client/bifrost/rpc-core/debug/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "fc-rpc-core-debug" +authors = ["bifrost-platform"] +edition = "2021" +homepage = "https://www.bifrostnetwork.com" +license = "Apache-2.0" +repository = "https://github.com/bifrost-platform/bifrost-frontier" +version = "0.1.0" + +[dependencies] +ethereum-types = { workspace = true, features = ["std"] } +futures = { workspace = true, features = ["compat"] } +jsonrpsee = { workspace = true, features = ["macros", "server"] } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } + +# Substrate +sp-core = { workspace = true, features = ["std"] } + +# Frontier +fc-evm-tracing = { workspace = true } +fc-rpc-core-types = { workspace = true } diff --git a/client/bifrost/rpc-core/debug/src/lib.rs b/client/bifrost/rpc-core/debug/src/lib.rs new file mode 100644 index 0000000000..ea2fee4bc5 --- /dev/null +++ b/client/bifrost/rpc-core/debug/src/lib.rs @@ -0,0 +1,33 @@ +use ethereum_types::H256; +use fc_evm_tracing::types::single; +use fc_rpc_core_types::RequestBlockId; +use jsonrpsee::{core::RpcResult, proc_macros::rpc}; +use serde::Deserialize; + +#[derive(Clone, Eq, PartialEq, Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TraceParams { + pub disable_storage: Option, + pub disable_memory: Option, + pub disable_stack: Option, + /// Javascript tracer (we just check if it's Blockscout tracer string) + pub tracer: Option, + pub timeout: Option, +} + +#[rpc(server)] +#[jsonrpsee::core::async_trait] +pub trait Debug { + #[method(name = "debug_traceTransaction")] + async fn trace_transaction( + &self, + transaction_hash: H256, + params: Option, + ) -> RpcResult; + #[method(name = "debug_traceBlockByNumber", aliases = ["debug_traceBlockByHash"])] + async fn trace_block( + &self, + id: RequestBlockId, + params: Option, + ) -> RpcResult>; +} diff --git a/client/bifrost/rpc-core/trace/Cargo.toml b/client/bifrost/rpc-core/trace/Cargo.toml new file mode 100644 index 0000000000..66e903a097 --- /dev/null +++ b/client/bifrost/rpc-core/trace/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "fc-rpc-core-trace" +authors = ["bifrost-platform"] +edition = "2021" +homepage = "https://www.bifrostnetwork.com" +license = "Apache-2.0" +repository = "https://github.com/bifrost-platform/bifrost-frontier" +version = "0.6.0" + +[dependencies] +ethereum-types = { workspace = true, features = ["std"] } +futures = { workspace = true, features = ["compat"] } +jsonrpsee = { workspace = true, features = ["macros", "server"] } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } + +# Frontier +fc-evm-tracing = { workspace = true } +fc-rpc-core-types = { workspace = true } diff --git a/client/bifrost/rpc-core/trace/src/lib.rs b/client/bifrost/rpc-core/trace/src/lib.rs new file mode 100644 index 0000000000..7a0d166695 --- /dev/null +++ b/client/bifrost/rpc-core/trace/src/lib.rs @@ -0,0 +1,34 @@ +use ethereum_types::H160; +use fc_evm_tracing::types::block::TransactionTrace; +use fc_rpc_core_types::RequestBlockId; +use jsonrpsee::{core::RpcResult, proc_macros::rpc}; +use serde::Deserialize; + +#[rpc(server)] +#[jsonrpsee::core::async_trait] +pub trait Trace { + #[method(name = "trace_filter")] + async fn filter(&self, filter: FilterRequest) -> RpcResult>; +} + +#[derive(Clone, Eq, PartialEq, Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct FilterRequest { + /// (optional?) From this block. + pub from_block: Option, + + /// (optional?) To this block. + pub to_block: Option, + + /// (optional) Sent from these addresses. + pub from_address: Option>, + + /// (optional) Sent to these addresses. + pub to_address: Option>, + + /// (optional) The offset trace number + pub after: Option, + + /// (optional) Integer number of traces to display in a batch. + pub count: Option, +} diff --git a/client/bifrost/rpc-core/txpool/Cargo.toml b/client/bifrost/rpc-core/txpool/Cargo.toml new file mode 100644 index 0000000000..f5284def33 --- /dev/null +++ b/client/bifrost/rpc-core/txpool/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "fc-rpc-core-txpool" +authors = ["bifrost-platform"] +edition = "2021" +homepage = "https://www.bifrostnetwork.com" +license = "Apache-2.0" +repository = "https://github.com/bifrost-platform/bifrost-frontier" +version = "0.6.0" + +[dependencies] +ethereum = { workspace = true, features = ["std", "with-codec"] } +ethereum-types = { workspace = true, features = ["std"] } +jsonrpsee = { workspace = true, features = ["macros", "server"] } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } + +# Frontier +fc-rpc-core = { workspace = true } diff --git a/client/bifrost/rpc-core/txpool/src/lib.rs b/client/bifrost/rpc-core/txpool/src/lib.rs new file mode 100644 index 0000000000..06502427ca --- /dev/null +++ b/client/bifrost/rpc-core/txpool/src/lib.rs @@ -0,0 +1,18 @@ +use ethereum_types::U256; +use jsonrpsee::{core::RpcResult, proc_macros::rpc}; + +mod types; + +pub use crate::types::{Get as GetT, Summary, Transaction, TransactionMap, TxPoolResult}; + +#[rpc(server)] +pub trait TxPool { + #[method(name = "txpool_content")] + fn content(&self) -> RpcResult>>; + + #[method(name = "txpool_inspect")] + fn inspect(&self) -> RpcResult>>; + + #[method(name = "txpool_status")] + fn status(&self) -> RpcResult>; +} diff --git a/client/bifrost/rpc-core/txpool/src/types/content.rs b/client/bifrost/rpc-core/txpool/src/types/content.rs new file mode 100644 index 0000000000..8a7e1eae31 --- /dev/null +++ b/client/bifrost/rpc-core/txpool/src/types/content.rs @@ -0,0 +1,95 @@ +use crate::GetT; +use ethereum::{TransactionAction, TransactionV2 as EthereumTransaction}; +use ethereum_types::{H160, H256, U256}; +use fc_rpc_core::types::Bytes; +use serde::{Serialize, Serializer}; + +#[derive(Debug, Default, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Transaction { + /// Hash + pub hash: H256, + /// Nonce + pub nonce: U256, + /// Block hash + #[serde(serialize_with = "block_hash_serialize")] + pub block_hash: Option, + /// Block number + pub block_number: Option, + /// Sender + pub from: H160, + /// Recipient + #[serde(serialize_with = "to_serialize")] + pub to: Option, + /// Transfered value + pub value: U256, + /// Gas Price + pub gas_price: U256, + /// Gas + pub gas: U256, + /// Data + pub input: Bytes, + /// Transaction Index + pub transaction_index: Option, +} + +fn block_hash_serialize(hash: &Option, serializer: S) -> Result +where + S: Serializer, +{ + serializer.serialize_str(&format!("0x{:x}", hash.unwrap_or_default())) +} + +fn to_serialize(hash: &Option, serializer: S) -> Result +where + S: Serializer, +{ + serializer.serialize_str(&format!("0x{:x}", hash.unwrap_or_default())) +} + +impl GetT for Transaction { + fn get(hash: H256, from_address: H160, txn: &EthereumTransaction) -> Self { + let (nonce, action, value, gas_price, gas_limit, input) = match txn { + EthereumTransaction::Legacy(t) => ( + t.nonce, + t.action, + t.value, + t.gas_price, + t.gas_limit, + t.input.clone(), + ), + EthereumTransaction::EIP2930(t) => ( + t.nonce, + t.action, + t.value, + t.gas_price, + t.gas_limit, + t.input.clone(), + ), + EthereumTransaction::EIP1559(t) => ( + t.nonce, + t.action, + t.value, + t.max_fee_per_gas, + t.gas_limit, + t.input.clone(), + ), + }; + Self { + hash, + nonce, + block_hash: None, + block_number: None, + from: from_address, + to: match action { + TransactionAction::Call(to) => Some(to), + _ => None, + }, + value, + gas_price, + gas: gas_limit, + input: Bytes(input), + transaction_index: None, + } + } +} diff --git a/client/bifrost/rpc-core/txpool/src/types/inspect.rs b/client/bifrost/rpc-core/txpool/src/types/inspect.rs new file mode 100644 index 0000000000..5dd4c1b708 --- /dev/null +++ b/client/bifrost/rpc-core/txpool/src/types/inspect.rs @@ -0,0 +1,47 @@ +use crate::GetT; +use ethereum::{TransactionAction, TransactionV2 as EthereumTransaction}; +use ethereum_types::{H160, H256, U256}; +use serde::{Serialize, Serializer}; + +#[derive(Clone, Debug)] +pub struct Summary { + pub to: Option, + pub value: U256, + pub gas: U256, + pub gas_price: U256, +} + +impl Serialize for Summary { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let res = format!( + "0x{:x}: {} wei + {} gas x {} wei", + self.to.unwrap_or_default(), + self.value, + self.gas, + self.gas_price + ); + serializer.serialize_str(&res) + } +} + +impl GetT for Summary { + fn get(_hash: H256, _from_address: H160, txn: &EthereumTransaction) -> Self { + let (action, value, gas_price, gas_limit) = match txn { + EthereumTransaction::Legacy(t) => (t.action, t.value, t.gas_price, t.gas_limit), + EthereumTransaction::EIP2930(t) => (t.action, t.value, t.gas_price, t.gas_limit), + EthereumTransaction::EIP1559(t) => (t.action, t.value, t.max_fee_per_gas, t.gas_limit), + }; + Self { + to: match action { + TransactionAction::Call(to) => Some(to), + _ => None, + }, + value, + gas_price, + gas: gas_limit, + } + } +} diff --git a/client/bifrost/rpc-core/txpool/src/types/mod.rs b/client/bifrost/rpc-core/txpool/src/types/mod.rs new file mode 100644 index 0000000000..99259a56b6 --- /dev/null +++ b/client/bifrost/rpc-core/txpool/src/types/mod.rs @@ -0,0 +1,21 @@ +mod content; +mod inspect; + +use ethereum::TransactionV2 as EthereumTransaction; +use ethereum_types::{H160, H256, U256}; +use serde::Serialize; +use std::collections::HashMap; + +pub use self::{content::Transaction, inspect::Summary}; + +pub type TransactionMap = HashMap>; + +#[derive(Debug, Serialize)] +pub struct TxPoolResult { + pub pending: T, + pub queued: T, +} + +pub trait Get { + fn get(hash: H256, from_address: H160, txn: &EthereumTransaction) -> Self; +} diff --git a/client/bifrost/rpc-core/types/Cargo.toml b/client/bifrost/rpc-core/types/Cargo.toml new file mode 100644 index 0000000000..d32f20b75f --- /dev/null +++ b/client/bifrost/rpc-core/types/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "fc-rpc-core-types" +authors = ["bifrost-platform"] +edition = "2021" +homepage = "https://www.bifrostnetwork.com" +license = "Apache-2.0" +repository = "https://github.com/bifrost-platform/bifrost-frontier" +version = "0.1.0" + +[dependencies] +ethereum-types = { workspace = true, features = ["std"] } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } diff --git a/client/bifrost/rpc-core/types/src/lib.rs b/client/bifrost/rpc-core/types/src/lib.rs new file mode 100644 index 0000000000..bbefec0f0e --- /dev/null +++ b/client/bifrost/rpc-core/types/src/lib.rs @@ -0,0 +1,32 @@ +use ethereum_types::H256; +use serde::{de::Error, Deserialize, Deserializer}; + +#[derive(Copy, Clone, Eq, PartialEq, Debug, Deserialize)] +#[serde(rename_all = "camelCase", untagged)] +pub enum RequestBlockId { + Number(#[serde(deserialize_with = "deserialize_u32_0x")] u32), + Hash(H256), + Tag(RequestBlockTag), +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum RequestBlockTag { + Earliest, + Latest, + Pending, +} + +fn deserialize_u32_0x<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let buf = String::deserialize(deserializer)?; + + let parsed = match buf.strip_prefix("0x") { + Some(buf) => u32::from_str_radix(&buf, 16), + None => u32::from_str_radix(&buf, 10), + }; + + parsed.map_err(|e| Error::custom(format!("parsing error: {:?} from '{}'", e, buf))) +} diff --git a/client/bifrost/rpc/debug/Cargo.toml b/client/bifrost/rpc/debug/Cargo.toml new file mode 100644 index 0000000000..7af309d659 --- /dev/null +++ b/client/bifrost/rpc/debug/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "fc-rpc-debug" +authors = ["bifrost-platform"] +edition = "2021" +homepage = "https://www.bifrostnetwork.com" +license = "Apache-2.0" +repository = "https://github.com/bifrost-platform/bifrost-frontier" +version = "0.1.0" + +[dependencies] +futures = { workspace = true, features = ["compat"] } +hex-literal = { workspace = true } +jsonrpsee = { workspace = true, features = ["macros", "server"] } +tokio = { workspace = true, features = ["sync", "time"] } + +# Bifrost +fc-evm-tracing = { workspace = true } +fc-rpc-core-debug = { workspace = true } +fc-rpc-core-types = { workspace = true } +fp-rpc-debug = { workspace = true, features = ["std"] } + +# Substrate +sc-client-api = { workspace = true } +sc-utils = { workspace = true } +sp-api = { workspace = true, features = ["std"] } +sp-block-builder = { workspace = true, features = ["std"] } +sp-blockchain = { workspace = true } +sp-core = { workspace = true, features = ["std"] } +sp-io = { workspace = true, features = ["std"] } +sp-runtime = { workspace = true, features = ["std"] } + +# Frontier +ethereum = { workspace = true, features = ["std", "with-codec"] } +ethereum-types = { workspace = true, features = ["std"] } +fc-api = { workspace = true } +fc-consensus = { workspace = true } +fc-db = { workspace = true } +fc-rpc = { workspace = true, features = ["rpc-binary-search-estimate"] } +fc-storage = { workspace = true } +fp-rpc = { workspace = true, features = ["std"] } diff --git a/client/bifrost/rpc/debug/src/lib.rs b/client/bifrost/rpc/debug/src/lib.rs new file mode 100644 index 0000000000..e1b38ca350 --- /dev/null +++ b/client/bifrost/rpc/debug/src/lib.rs @@ -0,0 +1,578 @@ +pub use fc_rpc_core_debug::{DebugServer, TraceParams}; +use futures::StreamExt; +use jsonrpsee::core::{async_trait, RpcResult}; + +use tokio::{ + self, + sync::{oneshot, Semaphore}, +}; + +use ethereum_types::H256; +use fc_evm_tracing::{formatters::ResponseFormatter, types::single}; +use fc_rpc::{frontier_backend_client, internal_err, OverrideHandle}; +use fc_rpc_core_types::{RequestBlockId, RequestBlockTag}; +use fp_rpc::EthereumRuntimeRPCApi; +use fp_rpc_debug::{DebugRuntimeApi, TracerInput}; +use sc_client_api::backend::{Backend, StateBackend, StorageProvider}; +use sc_utils::mpsc::TracingUnboundedSender; +use sp_api::{ApiExt, BlockId, Core, HeaderT, ProvideRuntimeApi}; +use sp_block_builder::BlockBuilder; +use sp_blockchain::{ + Backend as BlockchainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata, +}; +use sp_runtime::traits::{BlakeTwo256, Block as BlockT, UniqueSaturatedInto}; +use std::{future::Future, marker::PhantomData, sync::Arc}; + +pub enum RequesterInput { + Transaction(H256), + Block(RequestBlockId), +} + +pub enum Response { + Single(single::TransactionTrace), + Block(Vec), +} + +pub type Responder = oneshot::Sender>; +pub type DebugRequester = + TracingUnboundedSender<((RequesterInput, Option), Responder)>; + +pub struct Debug { + pub requester: DebugRequester, +} + +impl Debug { + pub fn new(requester: DebugRequester) -> Self { + Self { requester } + } +} + +#[async_trait] +impl DebugServer for Debug { + /// Handler for `debug_traceTransaction` request. Communicates with the service-defined task + /// using channels. + async fn trace_transaction( + &self, + transaction_hash: H256, + params: Option, + ) -> RpcResult { + let requester = self.requester.clone(); + + let (tx, rx) = oneshot::channel(); + // Send a message from the rpc handler to the service level task. + requester + .unbounded_send(((RequesterInput::Transaction(transaction_hash), params), tx)) + .map_err(|err| { + internal_err(format!( + "failed to send request to debug service : {:?}", + err + )) + })?; + + // Receive a message from the service level task and send the rpc response. + rx.await + .map_err(|err| internal_err(format!("debug service dropped the channel : {:?}", err)))? + .map(|res| match res { + Response::Single(res) => res, + _ => unreachable!(), + }) + } + + async fn trace_block( + &self, + id: RequestBlockId, + params: Option, + ) -> RpcResult> { + let requester = self.requester.clone(); + + let (tx, rx) = oneshot::channel(); + // Send a message from the rpc handler to the service level task. + requester + .unbounded_send(((RequesterInput::Block(id), params), tx)) + .map_err(|err| { + internal_err(format!( + "failed to send request to debug service : {:?}", + err + )) + })?; + + // Receive a message from the service level task and send the rpc response. + rx.await + .map_err(|err| internal_err(format!("debug service dropped the channel : {:?}", err)))? + .map(|res| match res { + Response::Block(res) => res, + _ => unreachable!(), + }) + } +} + +pub struct DebugHandler(PhantomData<(B, C, BE)>); + +impl DebugHandler +where + BE: Backend + 'static, + BE::State: StateBackend, + C: ProvideRuntimeApi, + C: StorageProvider, + C: HeaderMetadata + HeaderBackend, + C: Send + Sync + 'static, + B: BlockT + Send + Sync + 'static, + C::Api: BlockBuilder, + C::Api: DebugRuntimeApi, + C::Api: EthereumRuntimeRPCApi, + C::Api: ApiExt, +{ + /// Task spawned at service level that listens for messages on the rpc channel and spawns + /// blocking tasks using a permit pool. + pub fn task( + client: Arc, + backend: Arc, + frontier_backend: Arc + Send + Sync>, + permit_pool: Arc, + overrides: Arc>, + raw_max_memory_usage: usize, + ) -> (impl Future, DebugRequester) { + let (tx, mut rx): (DebugRequester, _) = + sc_utils::mpsc::tracing_unbounded("debug-requester", 100_000); + + let fut = async move { + loop { + match rx.next().await { + Some(( + (RequesterInput::Transaction(transaction_hash), params), + response_tx, + )) => { + let client = client.clone(); + let backend = backend.clone(); + let frontier_backend = frontier_backend.clone(); + let permit_pool = permit_pool.clone(); + let overrides = overrides.clone(); + + tokio::task::spawn(async move { + let _ = response_tx.send( + async { + let _permit = permit_pool.acquire().await; + tokio::task::spawn_blocking(move || { + Self::handle_transaction_request( + client.clone(), + backend.clone(), + frontier_backend.clone(), + transaction_hash, + params, + overrides.clone(), + raw_max_memory_usage, + ) + }) + .await + .map_err(|e| { + internal_err(format!( + "Internal error on spawned task : {:?}", + e + )) + })? + } + .await, + ); + }); + } + Some(((RequesterInput::Block(request_block_id), params), response_tx)) => { + let client = client.clone(); + let backend = backend.clone(); + let frontier_backend = frontier_backend.clone(); + let permit_pool = permit_pool.clone(); + let overrides = overrides.clone(); + + tokio::task::spawn(async move { + let _ = response_tx.send( + async { + let _permit = permit_pool.acquire().await; + + tokio::task::spawn_blocking(move || { + Self::handle_block_request( + client.clone(), + backend.clone(), + frontier_backend.clone(), + request_block_id, + params, + overrides.clone(), + ) + }) + .await + .map_err(|e| { + internal_err(format!( + "Internal error on spawned task : {:?}", + e + )) + })? + } + .await, + ); + }); + } + _ => {} + } + } + }; + (fut, tx) + } + + fn handle_params(params: Option) -> RpcResult<(TracerInput, single::TraceType)> { + // Set trace input and type + match params { + Some(TraceParams { + tracer: Some(tracer), + .. + }) => { + const BLOCKSCOUT_JS_CODE_HASH: [u8; 16] = + hex_literal::hex!("94d9f08796f91eb13a2e82a6066882f7"); + const BLOCKSCOUT_JS_CODE_HASH_V2: [u8; 16] = + hex_literal::hex!("89db13694675692951673a1e6e18ff02"); + let hash = sp_io::hashing::twox_128(&tracer.as_bytes()); + let tracer = + if hash == BLOCKSCOUT_JS_CODE_HASH || hash == BLOCKSCOUT_JS_CODE_HASH_V2 { + Some(TracerInput::Blockscout) + } else if tracer == "callTracer" { + Some(TracerInput::CallTracer) + } else { + None + }; + if let Some(tracer) = tracer { + Ok((tracer, single::TraceType::CallList)) + } else { + return Err(internal_err(format!( + "javascript based tracing is not available (hash :{:?})", + hash + ))); + } + } + Some(params) => Ok(( + TracerInput::None, + single::TraceType::Raw { + disable_storage: params.disable_storage.unwrap_or(false), + disable_memory: params.disable_memory.unwrap_or(false), + disable_stack: params.disable_stack.unwrap_or(false), + }, + )), + _ => Ok(( + TracerInput::None, + single::TraceType::Raw { + disable_storage: false, + disable_memory: false, + disable_stack: false, + }, + )), + } + } + + fn handle_block_request( + client: Arc, + backend: Arc, + frontier_backend: Arc + Send + Sync>, + request_block_id: RequestBlockId, + params: Option, + overrides: Arc>, + ) -> RpcResult { + let (tracer_input, trace_type) = Self::handle_params(params)?; + + let reference_id: BlockId = match request_block_id { + RequestBlockId::Number(n) => Ok(BlockId::Number(n.unique_saturated_into())), + RequestBlockId::Tag(RequestBlockTag::Latest) => { + Ok(BlockId::Number(client.info().best_number)) + } + RequestBlockId::Tag(RequestBlockTag::Earliest) => { + Ok(BlockId::Number(0u32.unique_saturated_into())) + } + RequestBlockId::Tag(RequestBlockTag::Pending) => { + Err(internal_err("'pending' blocks are not supported")) + } + RequestBlockId::Hash(eth_hash) => { + match futures::executor::block_on(frontier_backend_client::load_hash::( + client.as_ref(), + frontier_backend.as_ref(), + eth_hash, + )) { + Ok(Some(hash)) => Ok(BlockId::Hash(hash)), + Ok(_) => Err(internal_err("Block hash not found".to_string())), + Err(e) => Err(e), + } + } + }?; + + // Get ApiRef. This handle allow to keep changes between txs in an internal buffer. + let api = client.runtime_api(); + // Get Blockchain backend + let blockchain = backend.blockchain(); + // Get the header I want to work with. + let Ok(hash) = client.expect_block_hash_from_id(&reference_id) else { + return Err(internal_err("Block header not found")) + }; + let header = match client.header(hash) { + Ok(Some(h)) => h, + _ => return Err(internal_err("Block header not found")), + }; + + // Get parent blockid. + let parent_block_hash = *header.parent_hash(); + + let schema = fc_storage::onchain_storage_schema::(client.as_ref(), hash); + + // Using storage overrides we align with `:ethereum_schema` which will result in proper + // SCALE decoding in case of migration. + let statuses = match overrides.schemas.get(&schema) { + Some(schema) => schema + .current_transaction_statuses(hash) + .unwrap_or_default(), + _ => { + return Err(internal_err(format!( + "No storage override at {:?}", + reference_id + ))) + } + }; + + // Known ethereum transaction hashes. + let eth_tx_hashes: Vec<_> = statuses.iter().map(|t| t.transaction_hash).collect(); + + // If there are no ethereum transactions in the block return empty trace right away. + if eth_tx_hashes.is_empty() { + return Ok(Response::Block(vec![])); + } + + // Get block extrinsics. + let exts = blockchain + .body(hash) + .map_err(|e| internal_err(format!("Fail to read blockchain db: {:?}", e)))? + .unwrap_or_default(); + + // Trace the block. + let f = || -> RpcResult<_> { + api.initialize_block(parent_block_hash, &header) + .map_err(|e| internal_err(format!("Runtime api access error: {:?}", e)))?; + + let _result = api + .trace_block(parent_block_hash, exts, eth_tx_hashes) + .map_err(|e| { + internal_err(format!( + "Blockchain error when replaying block {} : {:?}", + reference_id, e + )) + })? + .map_err(|e| { + internal_err(format!( + "Internal runtime error when replaying block {} : {:?}", + reference_id, e + )) + })?; + Ok(fp_rpc_debug::Response::Block) + }; + + return match trace_type { + single::TraceType::CallList => { + let mut proxy = fc_evm_tracing::listeners::CallList::default(); + proxy.using(f)?; + proxy.finish_transaction(); + let response = match tracer_input { + TracerInput::CallTracer => { + fc_evm_tracing::formatters::CallTracer::format(proxy) + .ok_or("Trace result is empty.") + .map_err(|e| internal_err(format!("{:?}", e))) + } + _ => Err(internal_err( + "Bug: failed to resolve the tracer format.".to_string(), + )), + }?; + + Ok(Response::Block(response)) + } + _ => Err(internal_err( + "debug_traceBlock functions currently only support callList mode (enabled + by providing `{{'tracer': 'callTracer'}}` in the request)." + .to_string(), + )), + }; + } + + /// Replays a transaction in the Runtime at a given block height. + /// + /// In order to succesfully reproduce the result of the original transaction we need a correct + /// state to replay over. + /// + /// Substrate allows to apply extrinsics in the Runtime and thus creating an overlayed state. + /// This overlayed changes will live in-memory for the lifetime of the ApiRef. + fn handle_transaction_request( + client: Arc, + backend: Arc, + frontier_backend: Arc + Send + Sync>, + transaction_hash: H256, + params: Option, + overrides: Arc>, + raw_max_memory_usage: usize, + ) -> RpcResult { + let (tracer_input, trace_type) = Self::handle_params(params)?; + + let (hash, index) = + match futures::executor::block_on(frontier_backend_client::load_transactions::( + client.as_ref(), + frontier_backend.as_ref(), + transaction_hash, + false, + )) { + Ok(Some((hash, index))) => (hash, index as usize), + Ok(None) => return Err(internal_err("Transaction hash not found".to_string())), + Err(e) => return Err(e), + }; + + let reference_id = + match futures::executor::block_on(frontier_backend_client::load_hash::( + client.as_ref(), + frontier_backend.as_ref(), + hash, + )) { + Ok(Some(hash)) => BlockId::Hash(hash), + Ok(_) => return Err(internal_err("Block hash not found".to_string())), + Err(e) => return Err(e), + }; + // Get ApiRef. This handle allow to keep changes between txs in an internal buffer. + let api = client.runtime_api(); + // Get Blockchain backend + let blockchain = backend.blockchain(); + // Get the header I want to work with. + let Ok(reference_hash) = client.expect_block_hash_from_id(&reference_id) else { + return Err(internal_err("Block header not found")) + }; + let header = match client.header(reference_hash) { + Ok(Some(h)) => h, + _ => return Err(internal_err("Block header not found")), + }; + // Get parent blockid. + let parent_block_hash = *header.parent_hash(); + + // Get block extrinsics. + let exts = blockchain + .body(reference_hash) + .map_err(|e| internal_err(format!("Fail to read blockchain db: {:?}", e)))? + .unwrap_or_default(); + + // Get DebugRuntimeApi version + let trace_api_version = if let Ok(Some(api_version)) = + api.api_version::>(parent_block_hash) + { + api_version + } else { + return Err(internal_err( + "Runtime api version call failed (trace)".to_string(), + )); + }; + + let schema = + fc_storage::onchain_storage_schema::(client.as_ref(), reference_hash); + + // Get the block that contains the requested transaction. Using storage overrides we align + // with `:ethereum_schema` which will result in proper SCALE decoding in case of migration. + let reference_block = match overrides.schemas.get(&schema) { + Some(schema) => schema.current_block(reference_hash), + _ => { + return Err(internal_err(format!( + "No storage override at {:?}", + reference_hash + ))) + } + }; + + // Get the actual ethereum transaction. + if let Some(block) = reference_block { + let transactions = block.transactions; + if let Some(transaction) = transactions.get(index) { + let f = || -> RpcResult<_> { + api.initialize_block(parent_block_hash, &header) + .map_err(|e| internal_err(format!("Runtime api access error: {:?}", e)))?; + + if trace_api_version >= 4 { + let _result = api + .trace_transaction(parent_block_hash, exts, &transaction) + .map_err(|e| { + internal_err(format!( + "Runtime api access error (version {:?}): {:?}", + trace_api_version, e + )) + })? + .map_err(|e| internal_err(format!("DispatchError: {:?}", e)))?; + } else { + // Pre-london update, legacy transactions. + let _result = match transaction { + ethereum::TransactionV2::Legacy(tx) => + { + #[allow(deprecated)] + api.trace_transaction_before_version_4(parent_block_hash, exts, &tx) + .map_err(|e| { + internal_err(format!( + "Runtime api access error (legacy): {:?}", + e + )) + })? + .map_err(|e| internal_err(format!("DispatchError: {:?}", e)))? + } + _ => { + return Err(internal_err( + "Bug: pre-london runtime expects legacy transactions" + .to_string(), + )) + } + }; + } + + Ok(fp_rpc_debug::Response::Single) + }; + + return match trace_type { + single::TraceType::Raw { + disable_storage, + disable_memory, + disable_stack, + } => { + let mut proxy = fc_evm_tracing::listeners::Raw::new( + disable_storage, + disable_memory, + disable_stack, + raw_max_memory_usage, + ); + proxy.using(f)?; + Ok(Response::Single( + fc_evm_tracing::formatters::Raw::format(proxy).ok_or(internal_err( + "replayed transaction generated too much data. \ + try disabling memory or storage?", + ))?, + )) + } + single::TraceType::CallList => { + let mut proxy = fc_evm_tracing::listeners::CallList::default(); + proxy.using(f)?; + proxy.finish_transaction(); + let response = match tracer_input { + TracerInput::Blockscout => { + fc_evm_tracing::formatters::Blockscout::format(proxy) + .ok_or("Trace result is empty.") + .map_err(|e| internal_err(format!("{:?}", e))) + } + TracerInput::CallTracer => { + let mut res = fc_evm_tracing::formatters::CallTracer::format(proxy) + .ok_or("Trace result is empty.") + .map_err(|e| internal_err(format!("{:?}", e)))?; + Ok(res.pop().expect("Trace result is empty.")) + } + _ => Err(internal_err( + "Bug: failed to resolve the tracer format.".to_string(), + )), + }?; + Ok(Response::Single(response)) + } + not_supported => Err(internal_err(format!( + "Bug: `handle_transaction_request` does not support {:?}.", + not_supported + ))), + }; + } + } + Err(internal_err("Runtime block call failed".to_string())) + } +} diff --git a/client/bifrost/rpc/trace/Cargo.toml b/client/bifrost/rpc/trace/Cargo.toml new file mode 100644 index 0000000000..21039ff072 --- /dev/null +++ b/client/bifrost/rpc/trace/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "fc-rpc-trace" +authors = ["bifrost-platform"] +edition = "2021" +homepage = "https://www.bifrostnetwork.com" +license = "Apache-2.0" +repository = "https://github.com/bifrost-platform/bifrost-frontier" +version = "0.6.0" + +[dependencies] +ethereum = { workspace = true, features = ["with-codec"] } +ethereum-types = { workspace = true } +futures = { workspace = true } +jsonrpsee = { workspace = true, features = ["macros", "server"] } +log = { workspace = true } +serde = { workspace = true, features = ["derive"] } +sha3 = { workspace = true } +tokio = { workspace = true, features = ["sync", "time"] } +tracing = { workspace = true } + +fc-evm-tracing = { workspace = true } +fc-rpc-core-trace = { workspace = true } +fc-rpc-core-types = { workspace = true } +fp-rpc-debug = { workspace = true } + +# Substrate +prometheus-endpoint = { workspace = true } +sc-client-api = { workspace = true } +sc-network = { workspace = true } +sc-utils = { workspace = true } +sp-api = { workspace = true } +sp-block-builder = { workspace = true } +sp-blockchain = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } +sp-transaction-pool = { workspace = true } + +# Frontier +fc-consensus = { workspace = true } +fc-rpc = { workspace = true, features = ["rpc-binary-search-estimate"] } +fc-rpc-core = { workspace = true } +fc-storage = { workspace = true } +fp-rpc = { workspace = true } diff --git a/client/bifrost/rpc/trace/src/lib.rs b/client/bifrost/rpc/trace/src/lib.rs new file mode 100644 index 0000000000..2bc58a3f7c --- /dev/null +++ b/client/bifrost/rpc/trace/src/lib.rs @@ -0,0 +1,909 @@ +//! `trace_filter` RPC handler and its associated service task. +//! The RPC handler rely on `CacheTask` which provides a future that must be run inside a tokio +//! executor. +//! +//! The implementation is composed of multiple tasks : +//! - Many calls the the RPC handler `Trace::filter`, communicating with the main task. +//! - A main `CacheTask` managing the cache and the communication between tasks. +//! - For each traced block an async task responsible to wait for a permit, spawn a blocking +//! task and waiting for the result, then send it to the main `CacheTask`. + +use futures::{select, stream::FuturesUnordered, FutureExt, StreamExt}; +use std::{collections::BTreeMap, future::Future, marker::PhantomData, sync::Arc, time::Duration}; +use tokio::{ + sync::{mpsc, oneshot, Semaphore}, + time::sleep, +}; +use tracing::{instrument, Instrument}; + +use sc_client_api::backend::{Backend, StateBackend, StorageProvider}; +use sc_utils::mpsc::TracingUnboundedSender; +use sp_api::{ApiExt, Core, HeaderT, ProvideRuntimeApi}; +use sp_block_builder::BlockBuilder; +use sp_blockchain::{ + Backend as BlockchainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata, +}; +use sp_runtime::traits::{BlakeTwo256, Block as BlockT}; +use prometheus_endpoint::{ + register, Counter, PrometheusError, Registry as PrometheusRegistry, U64, +}; + +use ethereum_types::H256; +use fc_rpc::OverrideHandle; +use fp_rpc::EthereumRuntimeRPCApi; + +use fc_evm_tracing::{ + formatters::ResponseFormatter, + types::block::{self, TransactionTrace}, +}; +pub use fc_rpc_core_trace::{FilterRequest, TraceServer}; +use fc_rpc_core_types::{RequestBlockId, RequestBlockTag}; +use fp_rpc_debug::DebugRuntimeApi; + +type TxsTraceRes = Result, String>; + +/// RPC handler. Will communicate with a `CacheTask` through a `CacheRequester`. +pub struct Trace { + _phantom: PhantomData, + client: Arc, + requester: CacheRequester, + max_count: u32, +} + +impl Clone for Trace { + fn clone(&self) -> Self { + Self { + _phantom: PhantomData, + client: Arc::clone(&self.client), + requester: self.requester.clone(), + max_count: self.max_count, + } + } +} + +impl Trace +where + B: BlockT + Send + Sync + 'static, + B::Header: HeaderT, + C: HeaderMetadata + HeaderBackend, + C: Send + Sync + 'static, +{ + /// Create a new RPC handler. + pub fn new(client: Arc, requester: CacheRequester, max_count: u32) -> Self { + Self { + client, + requester, + max_count, + _phantom: PhantomData, + } + } + + /// Convert an optional block ID (number or tag) to a block height. + fn block_id(&self, id: Option) -> Result { + match id { + Some(RequestBlockId::Number(n)) => Ok(n), + None | Some(RequestBlockId::Tag(RequestBlockTag::Latest)) => { + Ok(self.client.info().best_number) + } + Some(RequestBlockId::Tag(RequestBlockTag::Earliest)) => Ok(0), + Some(RequestBlockId::Tag(RequestBlockTag::Pending)) => { + Err("'pending' is not supported") + } + Some(RequestBlockId::Hash(_)) => Err("Block hash not supported"), + } + } + + /// `trace_filter` endpoint (wrapped in the trait implementation with futures compatibilty) + async fn filter(self, req: FilterRequest) -> TxsTraceRes { + let from_block = self.block_id(req.from_block)?; + let to_block = self.block_id(req.to_block)?; + let block_heights = from_block..=to_block; + + let count = req.count.unwrap_or(self.max_count); + if count > self.max_count { + return Err(format!( + "count ({}) can't be greater than maximum ({})", + count, self.max_count + )); + } + + // Build a list of all the Substrate block hashes that need to be traced. + let mut block_hashes = vec![]; + for block_height in block_heights { + if block_height == 0 { + continue; // no traces for genesis block. + } + + let block_hash = self + .client + .hash(block_height) + .map_err(|e| { + format!( + "Error when fetching block {} header : {:?}", + block_height, e + ) + })? + .ok_or_else(|| format!("Block with height {} don't exist", block_height))?; + + block_hashes.push(block_hash); + } + + // Start a batch with these blocks. + let batch_id = self.requester.start_batch(block_hashes.clone()).await?; + // Fetch all the traces. It is done in another function to simplify error handling and allow + // to call the following `stop_batch` regardless of the result. This is important for the + // cache cleanup to work properly. + let res = self.fetch_traces(req, &block_hashes, count as usize).await; + // Stop the batch, allowing the cache task to remove useless non-started block traces and + // start the expiration delay. + self.requester.stop_batch(batch_id).await; + + res + } + + async fn fetch_traces( + &self, + req: FilterRequest, + block_hashes: &[H256], + count: usize, + ) -> TxsTraceRes { + let from_address = req.from_address.unwrap_or_default(); + let to_address = req.to_address.unwrap_or_default(); + + let mut traces_amount: i64 = -(req.after.unwrap_or(0) as i64); + let mut traces = vec![]; + + for &block_hash in block_hashes { + // Request the traces of this block to the cache service. + // This will resolve quickly if the block is already cached, or wait until the block + // has finished tracing. + let block_traces = self.requester.get_traces(block_hash).await?; + + // Filter addresses. + let mut block_traces: Vec<_> = block_traces + .iter() + .filter(|trace| match trace.action { + block::TransactionTraceAction::Call { from, to, .. } => { + (from_address.is_empty() || from_address.contains(&from)) + && (to_address.is_empty() || to_address.contains(&to)) + } + block::TransactionTraceAction::Create { from, .. } => { + (from_address.is_empty() || from_address.contains(&from)) + && to_address.is_empty() + } + block::TransactionTraceAction::Suicide { address, .. } => { + (from_address.is_empty() || from_address.contains(&address)) + && to_address.is_empty() + } + }) + .cloned() + .collect(); + + // Don't insert anything if we're still before "after" + traces_amount += block_traces.len() as i64; + if traces_amount > 0 { + let traces_amount = traces_amount as usize; + // If the current Vec of traces is across the "after" marker, + // we skip some elements of it. + if traces_amount < block_traces.len() { + let skip = block_traces.len() - traces_amount; + block_traces = block_traces.into_iter().skip(skip).collect(); + } + + traces.append(&mut block_traces); + + // If we go over "count" (the limit), we trim and exit the loop, + // unless we used the default maximum, in which case we return an error. + if traces_amount >= count { + if req.count.is_none() { + return Err(format!( + "the amount of traces goes over the maximum ({}), please use 'after' \ + and 'count' in your request", + self.max_count + )); + } + + traces = traces.into_iter().take(count).collect(); + break; + } + } + } + + Ok(traces) + } +} + +#[jsonrpsee::core::async_trait] +impl TraceServer for Trace +where + B: BlockT + Send + Sync + 'static, + B::Header: HeaderT, + C: HeaderMetadata + HeaderBackend, + C: Send + Sync + 'static, +{ + async fn filter( + &self, + filter: FilterRequest, + ) -> jsonrpsee::core::RpcResult> { + self.clone() + .filter(filter) + .await + .map_err(|e| fc_rpc::internal_err(e)) + } +} + +/// An opaque batch ID. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct CacheBatchId(u64); + +/// Requests the cache task can accept. +enum CacheRequest { + /// Request to start caching the provided range of blocks. + /// The task will add to blocks to its pool and immediately return a new batch ID. + StartBatch { + /// Returns the ID of the batch for cancellation. + sender: oneshot::Sender, + /// List of block hash to trace. + blocks: Vec, + }, + /// Fetch the traces for given block hash. + /// The task will answer only when it has processed this block. + GetTraces { + /// Returns the array of traces or an error. + sender: oneshot::Sender, + /// Hash of the block. + block: H256, + }, + /// Notify the cache that it can stop the batch with that ID. Any block contained only in + /// this batch and still not started will be discarded. + StopBatch { batch_id: CacheBatchId }, +} + +/// Allows to interact with the cache task. +#[derive(Clone)] +pub struct CacheRequester(TracingUnboundedSender); + +impl CacheRequester { + /// Request to start caching the provided range of blocks. + /// The task will add to blocks to its pool and immediately return the batch ID. + #[instrument(skip(self))] + pub async fn start_batch(&self, blocks: Vec) -> Result { + let (response_tx, response_rx) = oneshot::channel(); + let sender = self.0.clone(); + + sender + .unbounded_send(CacheRequest::StartBatch { + sender: response_tx, + blocks, + }) + .map_err(|e| { + format!( + "Failed to send request to the trace cache task. Error : {:?}", + e + ) + })?; + + response_rx.await.map_err(|e| { + format!( + "Trace cache task closed the response channel. Error : {:?}", + e + ) + }) + } + + /// Fetch the traces for given block hash. + /// The task will answer only when it has processed this block. + /// The block should be part of a batch first. If no batch has requested the block it will + /// return an error. + #[instrument(skip(self))] + pub async fn get_traces(&self, block: H256) -> TxsTraceRes { + let (response_tx, response_rx) = oneshot::channel(); + let sender = self.0.clone(); + + sender + .unbounded_send(CacheRequest::GetTraces { + sender: response_tx, + block, + }) + .map_err(|e| { + format!( + "Failed to send request to the trace cache task. Error : {:?}", + e + ) + })?; + + response_rx + .await + .map_err(|e| { + format!( + "Trace cache task closed the response channel. Error : {:?}", + e + ) + })? + .map_err(|e| format!("Failed to replay block. Error : {:?}", e)) + } + + /// Notify the cache that it can stop the batch with that ID. Any block contained only in + /// this batch and still in the waiting pool will be discarded. + #[instrument(skip(self))] + pub async fn stop_batch(&self, batch_id: CacheBatchId) { + let sender = self.0.clone(); + + // Here we don't care if the request has been accepted or refused, the caller can't + // do anything with it. + let _ = sender + .unbounded_send(CacheRequest::StopBatch { batch_id }) + .map_err(|e| { + format!( + "Failed to send request to the trace cache task. Error : {:?}", + e + ) + }); + } +} + +/// Data stored for each block in the cache. +/// `active_batch_count` represents the number of batches using this +/// block. It will increase immediatly when a batch is created, but will be +/// decrease only after the batch ends and its expiration delay passes. +/// It allows to keep the data in the cache for following requests that would use +/// this block, which is important to handle pagination efficiently. +struct CacheBlock { + active_batch_count: usize, + state: CacheBlockState, +} + +/// State of a cached block. It can either be polled to be traced or cached. +enum CacheBlockState { + /// Block has been added to the pool blocks to be replayed. + /// It may be currently waiting to be replayed or being replayed. + Pooled { + started: bool, + /// Multiple requests might query the same block while it is pooled to be + /// traced. They response channel is stored here, and the result will be + /// sent in all of them when the tracing is finished. + waiting_requests: Vec>, + /// Channel used to unqueue a tracing that has not yet started. + /// A tracing will be unqueued if it has not yet been started and the last batch + /// needing this block is ended (ignoring the expiration delay). + /// It is not used directly, but dropping will wake up the receiver. + #[allow(dead_code)] + unqueue_sender: oneshot::Sender<()>, + }, + /// Tracing has completed and the result is available. No Runtime API call + /// will be needed until this block cache is removed. + Cached { traces: TxsTraceRes }, +} + +/// Tracing a block is done in a separate tokio blocking task to avoid clogging the async threads. +/// For this reason a channel using this type is used by the blocking task to communicate with the +/// main cache task. +enum BlockingTaskMessage { + /// Notify the tracing for this block has started as the blocking task got a permit from + /// the semaphore. This is used to prevent the deletion of a cache entry for a block that has + /// started being traced. + Started { block_hash: H256 }, + /// The tracing is finished and the result is send to the main task. + Finished { + block_hash: H256, + result: TxsTraceRes, + }, +} + +/// Type wrapper for the cache task, generic over the Client, Block and Backend types. +pub struct CacheTask { + client: Arc, + backend: Arc, + blocking_permits: Arc, + cached_blocks: BTreeMap, + batches: BTreeMap>, + next_batch_id: u64, + metrics: Option, + _phantom: PhantomData, +} + +impl CacheTask +where + BE: Backend + 'static, + BE::State: StateBackend, + C: ProvideRuntimeApi, + C: StorageProvider, + C: HeaderMetadata + HeaderBackend, + C: Send + Sync + 'static, + B: BlockT + Send + Sync + 'static, + B::Header: HeaderT, + C::Api: BlockBuilder, + C::Api: DebugRuntimeApi, + C::Api: EthereumRuntimeRPCApi, + C::Api: ApiExt, +{ + /// Create a new cache task. + /// + /// Returns a Future that needs to be added to a tokio executor, and an handle allowing to + /// send requests to the task. + pub fn create( + client: Arc, + backend: Arc, + cache_duration: Duration, + blocking_permits: Arc, + overrides: Arc>, + prometheus: Option, + ) -> (impl Future, CacheRequester) { + // Communication with the outside world : + let (requester_tx, mut requester_rx) = + sc_utils::mpsc::tracing_unbounded("trace-filter-cache", 100_000); + + // Task running in the service. + let task = async move { + // The following variables are polled by the select! macro, and thus cannot be + // part of Self without introducing borrowing issues. + let mut batch_expirations = FuturesUnordered::new(); + let (blocking_tx, mut blocking_rx) = + mpsc::channel(blocking_permits.available_permits() * 2); + let metrics = if let Some(registry) = prometheus { + match Metrics::register(®istry) { + Ok(metrics) => Some(metrics), + Err(err) => { + log::error!(target: "tracing", "Failed to register metrics {err:?}"); + None + } + } + } else { + None + }; + // Contains the inner state of the cache task, excluding the pooled futures/channels. + // Having this object allow to refactor each event into its own function, simplifying + // the main loop. + let mut inner = Self { + client, + backend, + blocking_permits, + cached_blocks: BTreeMap::new(), + batches: BTreeMap::new(), + next_batch_id: 0, + metrics, + _phantom: Default::default(), + }; + + // Main event loop. This loop must not contain any direct .await, as we want to + // react to events as fast as possible. + loop { + select! { + request = requester_rx.next() => { + match request { + None => break, + Some(CacheRequest::StartBatch {sender, blocks}) + => inner.request_start_batch(&blocking_tx, sender, blocks, overrides.clone()), + Some(CacheRequest::GetTraces {sender, block}) + => inner.request_get_traces(sender, block), + Some(CacheRequest::StopBatch {batch_id}) => { + // Cannot be refactored inside `request_stop_batch` because + // it has an unnamable type :C + batch_expirations.push(async move { + sleep(cache_duration).await; + batch_id + }); + + inner.request_stop_batch(batch_id); + }, + } + }, + message = blocking_rx.recv().fuse() => { + match message { + None => (), + Some(BlockingTaskMessage::Started { block_hash }) + => inner.blocking_started(block_hash), + Some(BlockingTaskMessage::Finished { block_hash, result }) + => inner.blocking_finished(block_hash, result), + } + }, + batch_id = batch_expirations.next() => { + match batch_id { + None => (), + Some(batch_id) => inner.expired_batch(batch_id), + } + } + } + } + } + .instrument(tracing::debug_span!("trace_filter_cache")); + + (task, CacheRequester(requester_tx)) + } + + /// Handle the creation of a batch. + /// Will start the tracing process for blocks that are not already in the cache. + #[instrument(skip(self, blocking_tx, sender, blocks, overrides))] + fn request_start_batch( + &mut self, + blocking_tx: &mpsc::Sender, + sender: oneshot::Sender, + blocks: Vec, + overrides: Arc>, + ) { + tracing::trace!("Starting batch {}", self.next_batch_id); + self.batches.insert(self.next_batch_id, blocks.clone()); + + for block in blocks { + // The block is already in the cache, awesome ! + if let Some(block_cache) = self.cached_blocks.get_mut(&block) { + block_cache.active_batch_count += 1; + tracing::trace!( + "Cache hit for block {}, now used by {} batches.", + block, + block_cache.active_batch_count + ); + } + // Otherwise we need to queue this block for tracing. + else { + tracing::trace!("Cache miss for block {}, pooling it for tracing.", block); + + let blocking_permits = Arc::clone(&self.blocking_permits); + let (unqueue_sender, unqueue_receiver) = oneshot::channel(); + let client = Arc::clone(&self.client); + let backend = Arc::clone(&self.backend); + let blocking_tx = blocking_tx.clone(); + let overrides = overrides.clone(); + + // Spawn all block caching asynchronously. + // It will wait to obtain a permit, then spawn a blocking task. + // When the blocking task returns its result, it is send + // thought a channel to the main task loop. + tokio::spawn( + async move { + tracing::trace!("Waiting for blocking permit or task cancellation"); + let _permit = select!( + _ = unqueue_receiver.fuse() => { + tracing::trace!("Tracing of the block has been cancelled."); + return; + }, + permit = blocking_permits.acquire().fuse() => permit, + ); + + // Warn the main task that block tracing as started, and + // this block cache entry should not be removed. + let _ = blocking_tx + .send(BlockingTaskMessage::Started { block_hash: block }) + .await; + + tracing::trace!("Start block tracing in a blocking task."); + + // Perform block tracing in a tokio blocking task. + let result = async { + tokio::task::spawn_blocking(move || { + Self::cache_block(client, backend, block, overrides.clone()) + }) + .await + .map_err(|e| { + format!("Tracing Substrate block {} panicked : {:?}", block, e) + })? + } + .await + .map_err(|e| e.to_string()); + + tracing::trace!("Block tracing finished, sending result to main task."); + + // Send response to main task. + let _ = blocking_tx + .send(BlockingTaskMessage::Finished { + block_hash: block, + result, + }) + .await; + } + .instrument(tracing::trace_span!("Block tracing", block = %block)), + ); + + // Insert the block in the cache. + self.cached_blocks.insert( + block, + CacheBlock { + active_batch_count: 1, + state: CacheBlockState::Pooled { + started: false, + waiting_requests: vec![], + unqueue_sender, + }, + }, + ); + } + } + + // Respond with the batch ID. + let _ = sender.send(CacheBatchId(self.next_batch_id)); + + // Increase batch ID for next request. + self.next_batch_id = self.next_batch_id.overflowing_add(1).0; + } + + /// Handle a request to get the traces of the provided block. + /// - If the result is stored in the cache, it sends it immediatly. + /// - If the block is currently being pooled, it is added in this block cache waiting list, + /// and all requests concerning this block will be satisfied when the tracing for this block + /// is finished. + /// - If this block is missing from the cache, it means no batch asked for it. All requested + /// blocks should be contained in a batch beforehand, and thus an error is returned. + #[instrument(skip(self))] + fn request_get_traces(&mut self, sender: oneshot::Sender, block: H256) { + if let Some(block_cache) = self.cached_blocks.get_mut(&block) { + match &mut block_cache.state { + CacheBlockState::Pooled { + ref mut waiting_requests, + .. + } => { + tracing::warn!( + "A request asked a pooled block ({}), adding it to the list of \ + waiting requests.", + block + ); + waiting_requests.push(sender); + if let Some(metrics) = &self.metrics { + metrics.tracing_cache_misses.inc(); + } + } + CacheBlockState::Cached { traces, .. } => { + tracing::warn!( + "A request asked a cached block ({}), sending the traces directly.", + block + ); + let _ = sender.send(traces.clone()); + if let Some(metrics) = &self.metrics { + metrics.tracing_cache_hits.inc(); + } + } + } + } else { + tracing::warn!( + "An RPC request asked to get a block ({}) which was not batched.", + block + ); + let _ = sender.send(Err(format!( + "RPC request asked a block ({}) that was not batched", + block + ))); + } + } + + /// Handle a request to stop a batch. + /// For all blocks that needed to be traced, are only in this batch and not yet started, their + /// tracing is cancelled to save CPU-time and avoid attacks requesting large amount of blocks. + /// This batch data is not yet removed however. Instead a expiration delay timer is started + /// after which the data will indeed be cleared. (the code for that is in the main loop code + /// as it involved an unnamable type :C) + #[instrument(skip(self))] + fn request_stop_batch(&mut self, batch_id: CacheBatchId) { + tracing::trace!("Stopping batch {}", batch_id.0); + if let Some(blocks) = self.batches.get(&batch_id.0) { + for block in blocks { + let mut remove = false; + + // We remove early the block cache if this batch is the last + // pooling this block. + if let Some(block_cache) = self.cached_blocks.get_mut(block) { + if block_cache.active_batch_count == 1 + && matches!( + block_cache.state, + CacheBlockState::Pooled { started: false, .. } + ) { + remove = true; + } + } + + if remove { + tracing::trace!("Pooled block {} is no longer requested.", block); + // Remove block from the cache. Drops the value, + // closing all the channels contained in it. + let _ = self.cached_blocks.remove(&block); + } + } + } + } + + /// A tracing blocking task notifies it got a permit and is starting the tracing. + /// This started status is stored to avoid removing this block entry. + #[instrument(skip(self))] + fn blocking_started(&mut self, block_hash: H256) { + if let Some(block_cache) = self.cached_blocks.get_mut(&block_hash) { + if let CacheBlockState::Pooled { + ref mut started, .. + } = block_cache.state + { + *started = true; + } + } + } + + /// A tracing blocking task notifies it has finished the tracing and provide the result. + #[instrument(skip(self, result))] + fn blocking_finished(&mut self, block_hash: H256, result: TxsTraceRes) { + // In some cases it might be possible to receive traces of a block + // that has no entry in the cache because it was removed of the pool + // and received a permit concurrently. We just ignore it. + // + // TODO : Should we add it back ? Should it have an active_batch_count + // of 1 then ? + if let Some(block_cache) = self.cached_blocks.get_mut(&block_hash) { + if let CacheBlockState::Pooled { + ref mut waiting_requests, + .. + } = block_cache.state + { + tracing::trace!( + "A new block ({}) has been traced, adding it to the cache and responding to \ + {} waiting requests.", + block_hash, + waiting_requests.len() + ); + // Send result in waiting channels + while let Some(channel) = waiting_requests.pop() { + let _ = channel.send(result.clone()); + } + + // Update cache entry + block_cache.state = CacheBlockState::Cached { traces: result }; + } + } + } + + /// A batch expiration delay timer has completed. It performs the cache cleaning for blocks + /// not longer used by other batches. + #[instrument(skip(self))] + fn expired_batch(&mut self, batch_id: CacheBatchId) { + if let Some(batch) = self.batches.remove(&batch_id.0) { + for block in batch { + // For each block of the batch, we remove it if it was the + // last batch containing it. + let mut remove = false; + if let Some(block_cache) = self.cached_blocks.get_mut(&block) { + block_cache.active_batch_count -= 1; + + if block_cache.active_batch_count == 0 { + remove = true; + } + } + + if remove { + let _ = self.cached_blocks.remove(&block); + } + } + } + } + + /// (In blocking task) Use the Runtime API to trace the block. + #[instrument(skip(client, backend, overrides))] + fn cache_block( + client: Arc, + backend: Arc, + substrate_hash: H256, + overrides: Arc>, + ) -> TxsTraceRes { + // Get Subtrate block data. + let api = client.runtime_api(); + let block_header = client + .header(substrate_hash) + .map_err(|e| { + format!( + "Error when fetching substrate block {} header : {:?}", + substrate_hash, e + ) + })? + .ok_or_else(|| format!("Subtrate block {} don't exist", substrate_hash))?; + + let height = *block_header.number(); + let substrate_parent_hash = *block_header.parent_hash(); + + let schema = + fc_storage::onchain_storage_schema::(client.as_ref(), substrate_hash); + + // Get Ethereum block data. + let (eth_block, eth_transactions) = match overrides.schemas.get(&schema) { + Some(schema) => match ( + schema.current_block(substrate_hash), + schema.current_transaction_statuses(substrate_hash), + ) { + (Some(a), Some(b)) => (a, b), + _ => { + return Err(format!( + "Failed to get Ethereum block data for Substrate block {}", + substrate_hash + )) + } + }, + _ => return Err(format!("No storage override at {:?}", substrate_hash)), + }; + + let eth_block_hash = eth_block.header.hash(); + let eth_tx_hashes = eth_transactions + .iter() + .map(|t| t.transaction_hash) + .collect(); + + // Get extrinsics (containing Ethereum ones) + let extrinsics = backend + .blockchain() + .body(substrate_hash) + .map_err(|e| { + format!( + "Blockchain error when fetching extrinsics of block {} : {:?}", + height, e + ) + })? + .ok_or_else(|| format!("Could not find block {} when fetching extrinsics.", height))?; + + // Trace the block. + let f = || -> Result<_, String> { + api.initialize_block(substrate_parent_hash, &block_header) + .map_err(|e| format!("Runtime api access error: {:?}", e))?; + + let _result = api + .trace_block(substrate_parent_hash, extrinsics, eth_tx_hashes) + .map_err(|e| format!("Blockchain error when replaying block {} : {:?}", height, e))? + .map_err(|e| { + tracing::warn!( + "Internal runtime error when replaying block {} : {:?}", + height, + e + ); + format!( + "Internal runtime error when replaying block {} : {:?}", + height, e + ) + })?; + Ok(fp_rpc_debug::Response::Block) + }; + + let mut proxy = fc_evm_tracing::listeners::CallList::default(); + proxy.using(f)?; + let mut traces: Vec<_> = + fc_evm_tracing::formatters::TraceFilter::format(proxy).ok_or("Fail to format proxy")?; + // Fill missing data. + for trace in traces.iter_mut() { + trace.block_hash = eth_block_hash; + trace.block_number = height; + trace.transaction_hash = eth_transactions + .get(trace.transaction_position as usize) + .ok_or_else(|| { + tracing::warn!( + "Bug: A transaction has been replayed while it shouldn't (in block {}).", + height + ); + + format!( + "Bug: A transaction has been replayed while it shouldn't (in block {}).", + height + ) + })? + .transaction_hash; + + // Reformat error messages. + if let block::TransactionTraceOutput::Error(ref mut error) = trace.output { + if error.as_slice() == b"execution reverted" { + *error = b"Reverted".to_vec(); + } + } + } + Ok(traces) + } +} + +/// Prometheus metrics for tracing. +#[derive(Clone)] +pub(crate) struct Metrics { + tracing_cache_hits: Counter, + tracing_cache_misses: Counter, +} + +impl Metrics { + pub(crate) fn register(registry: &PrometheusRegistry) -> Result { + Ok(Self { + tracing_cache_hits: register( + Counter::new("tracing_cache_hits", "Number of tracing cache hits.")?, + registry, + )?, + tracing_cache_misses: register( + Counter::new("tracing_cache_misses", "Number of tracing cache misses.")?, + registry, + )?, + }) + } +} diff --git a/client/bifrost/rpc/txpool/Cargo.toml b/client/bifrost/rpc/txpool/Cargo.toml new file mode 100644 index 0000000000..edd56732af --- /dev/null +++ b/client/bifrost/rpc/txpool/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "fc-rpc-txpool" +authors = ["bifrost-platform"] +edition = "2021" +homepage = "https://www.bifrostnetwork.com" +license = "Apache-2.0" +repository = "https://github.com/bifrost-platform/bifrost-frontier" +version = "0.6.0" + +[dependencies] +jsonrpsee = { workspace = true, features = ["macros", "server"] } +rlp = { workspace = true } +serde = { workspace = true, features = ["derive"] } +sha3 = { workspace = true } + +fc-rpc-core-txpool = { workspace = true } +fp-rpc-txpool = { workspace = true, features = ["std"] } + +# Substrate +frame-system = { workspace = true } +sc-transaction-pool = { workspace = true } +sc-transaction-pool-api = { workspace = true } +sp-api = { workspace = true } +sp-blockchain = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +# Frontier +ethereum-types = { workspace = true, features = ["std"] } +fc-rpc = { workspace = true, features = ["rpc-binary-search-estimate"] } diff --git a/client/bifrost/rpc/txpool/src/lib.rs b/client/bifrost/rpc/txpool/src/lib.rs new file mode 100644 index 0000000000..97943ef0e0 --- /dev/null +++ b/client/bifrost/rpc/txpool/src/lib.rs @@ -0,0 +1,170 @@ +use ethereum_types::{H160, H256, U256}; +use fc_rpc::{internal_err, public_key}; +pub use fc_rpc_core_txpool::{ + GetT, Summary, Transaction, TransactionMap, TxPoolResult, TxPoolServer, +}; +use jsonrpsee::core::RpcResult; +use sc_transaction_pool::{ChainApi, Pool}; +use sc_transaction_pool_api::InPoolTransaction; +use serde::Serialize; +use sha3::{Digest, Keccak256}; +use sp_api::{ApiExt, ProvideRuntimeApi}; +use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; +use sp_runtime::traits::Block as BlockT; +use std::{collections::HashMap, marker::PhantomData, sync::Arc}; + +use fp_rpc_txpool::{Transaction as TransactionV2, TxPoolResponse, TxPoolRuntimeApi}; + +pub struct TxPool { + client: Arc, + graph: Arc>, + _marker: PhantomData, +} + +impl TxPool +where + C: ProvideRuntimeApi, + C: HeaderMetadata + HeaderBackend + 'static, + C: Send + Sync + 'static, + B: BlockT + Send + Sync + 'static, + A: ChainApi + 'static, + C::Api: TxPoolRuntimeApi, +{ + /// Use the transaction graph interface to get the extrinsics currently in the ready and future + /// queues. + fn map_build(&self) -> RpcResult>> + where + T: GetT + Serialize, + { + // Collect transactions in the ready validated pool. + let txs_ready = self + .graph + .validated_pool() + .ready() + .map(|in_pool_tx| in_pool_tx.data().clone()) + .collect(); + + // Collect transactions in the future validated pool. + let txs_future = self + .graph + .validated_pool() + .futures() + .iter() + .map(|(_hash, extrinsic)| extrinsic.clone()) + .collect(); + + // Use the runtime to match the (here) opaque extrinsics against ethereum transactions. + let best_block = self.client.info().best_hash; + let api = self.client.runtime_api(); + let api_version = + if let Ok(Some(api_version)) = api.api_version::>(best_block) { + api_version + } else { + return Err(internal_err( + "failed to retrieve Runtime Api version".to_string(), + )); + }; + let ethereum_txns: TxPoolResponse = if api_version == 1 { + #[allow(deprecated)] + let res = api.extrinsic_filter_before_version_2(best_block, txs_ready, txs_future) + .map_err(|err| { + internal_err(format!("fetch runtime extrinsic filter failed: {:?}", err)) + })?; + TxPoolResponse { + ready: res + .ready + .iter() + .map(|t| TransactionV2::Legacy(t.clone())) + .collect(), + future: res + .future + .iter() + .map(|t| TransactionV2::Legacy(t.clone())) + .collect(), + } + } else { + api.extrinsic_filter(best_block, txs_ready, txs_future) + .map_err(|err| { + internal_err(format!("fetch runtime extrinsic filter failed: {:?}", err)) + })? + }; + // Build the T response. + let mut pending = TransactionMap::::new(); + for txn in ethereum_txns.ready.iter() { + let hash = txn.hash(); + let nonce = match txn { + TransactionV2::Legacy(t) => t.nonce, + TransactionV2::EIP2930(t) => t.nonce, + TransactionV2::EIP1559(t) => t.nonce, + }; + let from_address = match public_key(txn) { + Ok(pk) => H160::from(H256::from_slice(Keccak256::digest(&pk).as_slice())), + Err(_e) => H160::default(), + }; + pending + .entry(from_address) + .or_insert_with(HashMap::new) + .insert(nonce, T::get(hash, from_address, txn)); + } + let mut queued = TransactionMap::::new(); + for txn in ethereum_txns.future.iter() { + let hash = txn.hash(); + let nonce = match txn { + TransactionV2::Legacy(t) => t.nonce, + TransactionV2::EIP2930(t) => t.nonce, + TransactionV2::EIP1559(t) => t.nonce, + }; + let from_address = match public_key(txn) { + Ok(pk) => H160::from(H256::from_slice(Keccak256::digest(&pk).as_slice())), + Err(_e) => H160::default(), + }; + queued + .entry(from_address) + .or_insert_with(HashMap::new) + .insert(nonce, T::get(hash, from_address, txn)); + } + Ok(TxPoolResult { pending, queued }) + } +} + +impl TxPool { + pub fn new(client: Arc, graph: Arc>) -> Self { + Self { + client, + graph, + _marker: PhantomData, + } + } +} + +impl TxPoolServer for TxPool +where + C: ProvideRuntimeApi, + C: HeaderMetadata + HeaderBackend, + C: Send + Sync + 'static, + B: BlockT + Send + Sync + 'static, + A: ChainApi + 'static, + C::Api: TxPoolRuntimeApi, +{ + fn content(&self) -> RpcResult>> { + self.map_build::() + } + + fn inspect(&self) -> RpcResult>> { + self.map_build::() + } + + fn status(&self) -> RpcResult> { + let status = self.graph.validated_pool().status(); + Ok(TxPoolResult { + pending: U256::from(status.ready), + queued: U256::from(status.future), + }) + } +} + +impl Clone for TxPool { + fn clone(&self) -> Self { + Self::new(self.client.clone(), self.graph.clone()) + } +} diff --git a/client/cli/Cargo.toml b/client/cli/Cargo.toml index e05c20c63e..8826b16168 100644 --- a/client/cli/Cargo.toml +++ b/client/cli/Cargo.toml @@ -27,7 +27,7 @@ fp-storage = { workspace = true, features = ["default"] } [dev-dependencies] futures = { workspace = true } -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } tempfile = "3.3.0" # Substrate sc-block-builder = { workspace = true } diff --git a/client/db/Cargo.toml b/client/db/Cargo.toml index d107e4847d..0ae6659c40 100644 --- a/client/db/Cargo.toml +++ b/client/db/Cargo.toml @@ -18,7 +18,7 @@ kvdb-rocksdb = { workspace = true, optional = true } log = { workspace = true } parity-db = { workspace = true } parking_lot = { workspace = true } -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } smallvec = { version = "1.11", optional = true } sqlx = { workspace = true, features = ["runtime-tokio-native-tls", "sqlite"], optional = true } tokio = { workspace = true, features = ["macros", "sync"], optional = true } diff --git a/client/mapping-sync/Cargo.toml b/client/mapping-sync/Cargo.toml index 02f503dab1..7ceaf782a7 100644 --- a/client/mapping-sync/Cargo.toml +++ b/client/mapping-sync/Cargo.toml @@ -33,7 +33,7 @@ fp-rpc = { workspace = true, features = ["default"] } [dev-dependencies] ethereum = { workspace = true } ethereum-types = { workspace = true } -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } sqlx = { workspace = true, features = ["runtime-tokio-native-tls", "sqlite"] } tempfile = "3.8.0" tokio = { workspace = true, features = ["sync"] } diff --git a/client/rpc-core/src/types/call_request.rs b/client/rpc-core/src/types/call_request.rs index 96d151a71a..efa493652a 100644 --- a/client/rpc-core/src/types/call_request.rs +++ b/client/rpc-core/src/types/call_request.rs @@ -26,7 +26,6 @@ use crate::types::{deserialize_data_or_input, Bytes}; /// Call request #[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize)] -#[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub struct CallRequest { /// From diff --git a/client/rpc/Cargo.toml b/client/rpc/Cargo.toml index 860e269ecb..5abc750eb2 100644 --- a/client/rpc/Cargo.toml +++ b/client/rpc/Cargo.toml @@ -22,9 +22,9 @@ log = { workspace = true } prometheus = { version = "0.13.1", default-features = false } rand = "0.8" rlp = { workspace = true } -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } schnellru = "0.2.1" -serde = { workspace = true } +serde = { workspace = true, optional = true } thiserror = { workspace = true } tokio = { workspace = true, features = ["sync"] } diff --git a/client/rpc/src/eth/filter.rs b/client/rpc/src/eth/filter.rs index b0a34465ec..7e6a90641d 100644 --- a/client/rpc/src/eth/filter.rs +++ b/client/rpc/src/eth/filter.rs @@ -50,6 +50,7 @@ pub struct EthFilter { filter_pool: FilterPool, max_stored_filters: usize, max_past_logs: u32, + logs_request_timeout: u64, block_data_cache: Arc>, _marker: PhantomData, } @@ -62,6 +63,7 @@ impl EthFilter { filter_pool: FilterPool, max_stored_filters: usize, max_past_logs: u32, + logs_request_timeout: u64, block_data_cache: Arc>, ) -> Self { Self { @@ -71,6 +73,7 @@ impl EthFilter { filter_pool, max_stored_filters, max_past_logs, + logs_request_timeout, block_data_cache, _marker: PhantomData, } @@ -314,6 +317,7 @@ where let backend = Arc::clone(&self.backend); let block_data_cache = Arc::clone(&self.block_data_cache); let max_past_logs = self.max_past_logs; + let logs_request_timeout = self.logs_request_timeout; match path { FuturePath::Error(err) => Err(err), @@ -360,6 +364,7 @@ where &block_data_cache, &mut ret, max_past_logs, + logs_request_timeout, &filter, from_number, current_number, @@ -400,6 +405,7 @@ where let backend = Arc::clone(&self.backend); let block_data_cache = Arc::clone(&self.block_data_cache); let max_past_logs = self.max_past_logs; + let logs_request_timeout = self.logs_request_timeout; let filter = filter_result?; @@ -439,6 +445,7 @@ where &block_data_cache, &mut ret, max_past_logs, + logs_request_timeout, &filter, from_number, current_number, @@ -469,6 +476,7 @@ where let block_data_cache = Arc::clone(&self.block_data_cache); let backend = Arc::clone(&self.backend); let max_past_logs = self.max_past_logs; + let logs_request_timeout = self.logs_request_timeout; let mut ret: Vec = Vec::new(); if let Some(hash) = filter.block_hash { @@ -528,6 +536,7 @@ where &block_data_cache, &mut ret, max_past_logs, + logs_request_timeout, &filter, from_number, current_number, @@ -688,6 +697,7 @@ async fn filter_range_logs( block_data_cache: &EthBlockDataCacheTask, ret: &mut Vec, max_past_logs: u32, + logs_request_timeout: u64, filter: &Filter, from: NumberFor, to: NumberFor, @@ -700,7 +710,7 @@ where BE: Backend + 'static, { // Max request duration of 10 seconds. - let max_duration = Duration::from_secs(10); + let max_duration = Duration::from_secs(logs_request_timeout); let begin_request = Instant::now(); let mut current_number = from; diff --git a/client/rpc/src/eth/format.rs b/client/rpc/src/eth/format.rs index 70b2439b45..3e1cf28ecb 100644 --- a/client/rpc/src/eth/format.rs +++ b/client/rpc/src/eth/format.rs @@ -48,6 +48,7 @@ impl Geth { "max priority fee per gas higher than max fee per gas".into() } VError::InvalidFeeInput => "invalid fee input".into(), + VError::InsufficientRent => "insufficient balance for rent + gas * price + value".into(), _ => "transaction validation error".into(), }, _ => "unknown error".into(), diff --git a/client/storage/Cargo.toml b/client/storage/Cargo.toml index b259cb0c66..4b6149c022 100644 --- a/client/storage/Cargo.toml +++ b/client/storage/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] ethereum = { workspace = true, features = ["with-codec"] } ethereum-types = { workspace = true } -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } # Substrate sc-client-api = { workspace = true } diff --git a/frame/base-fee/Cargo.toml b/frame/base-fee/Cargo.toml index dc89a53b4b..34afc9f091 100644 --- a/frame/base-fee/Cargo.toml +++ b/frame/base-fee/Cargo.toml @@ -11,7 +11,7 @@ repository = { workspace = true } targets = ["x86_64-unknown-linux-gnu"] [dependencies] -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } scale-info = { workspace = true } # Substrate frame-support = { workspace = true } diff --git a/frame/base-fee/src/lib.rs b/frame/base-fee/src/lib.rs index 74a42e3d0a..2250ae784d 100644 --- a/frame/base-fee/src/lib.rs +++ b/frame/base-fee/src/lib.rs @@ -96,6 +96,9 @@ pub mod pallet { #[pallet::storage] pub type BaseFeePerGas = StorageValue<_, U256, ValueQuery, DefaultBaseFeePerGas>; + #[pallet::storage] + pub type InitBaseFeePerGas = StorageValue<_, U256, ValueQuery, DefaultBaseFeePerGas>; + #[pallet::type_value] pub fn DefaultElasticity() -> Permill { T::DefaultElasticity::get() @@ -110,6 +113,7 @@ pub mod pallet { NewBaseFeePerGas { fee: U256 }, BaseFeeOverflow, NewElasticity { elasticity: Permill }, + NewInitBaseFeePerGas { fee: U256 } } #[pallet::hooks] @@ -178,7 +182,7 @@ pub mod pallet { let decrease = scaled_basefee .checked_div(U256::from(1_000_000)) .unwrap_or_else(U256::zero); - let default_base_fee = T::DefaultBaseFeePerGas::get(); + let default_base_fee = InitBaseFeePerGas::::get(); // lowest fee is norm(DefaultBaseFeePerGas * Threshold::ideal()): let lowest_base_fee = default_base_fee .checked_mul(U256::from(T::Threshold::ideal().deconstruct())) @@ -217,6 +221,15 @@ pub mod pallet { Self::deposit_event(Event::NewElasticity { elasticity }); Ok(()) } + + #[pallet::call_index(2)] + #[pallet::weight(10_000 + T::DbWeight::get().writes(1).ref_time())] + pub fn set_init_base_fee_per_gas(origin: OriginFor, fee: U256) -> DispatchResult { + ensure_root(origin)?; + let _ = Self::set_init_base_fee_per_gas_inner(fee); + Self::deposit_event(Event::NewInitBaseFeePerGas { fee }); + Ok(()) + } } } @@ -235,4 +248,8 @@ impl Pallet { >::put(value); T::DbWeight::get().writes(1) } + pub fn set_init_base_fee_per_gas_inner(value: U256) -> Weight { + >::put(value); + T::DbWeight::get().writes(1) + } } diff --git a/frame/dynamic-fee/Cargo.toml b/frame/dynamic-fee/Cargo.toml index b26de76fc0..348b624648 100644 --- a/frame/dynamic-fee/Cargo.toml +++ b/frame/dynamic-fee/Cargo.toml @@ -11,7 +11,7 @@ repository = { workspace = true } targets = ["x86_64-unknown-linux-gnu"] [dependencies] -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } scale-info = { workspace = true } # Substrate frame-support = { workspace = true } diff --git a/frame/ethereum/Cargo.toml b/frame/ethereum/Cargo.toml index 634be1081d..7494f3abf0 100644 --- a/frame/ethereum/Cargo.toml +++ b/frame/ethereum/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] ethereum = { workspace = true, features = ["with-codec"] } ethereum-types = { workspace = true } evm = { workspace = true, features = ["with-codec"] } -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } scale-info = { workspace = true } # Substrate frame-support = { workspace = true } @@ -29,6 +29,7 @@ fp-evm = { workspace = true } fp-rpc = { workspace = true } fp-storage = { workspace = true } pallet-evm = { workspace = true } +fp-rent = { workspace = true } [dev-dependencies] hex = { workspace = true } @@ -64,6 +65,7 @@ std = [ "fp-self-contained/std", "fp-storage/std", "pallet-evm/std", + "fp-rent/std", ] runtime-benchmarks = [ "frame-support/runtime-benchmarks", diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index e9aa870f11..cb91974606 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -64,9 +64,13 @@ use fp_evm::{ CallOrCreateInfo, CheckEvmTransaction, CheckEvmTransactionConfig, TransactionValidationError, }; pub use fp_rpc::TransactionStatus; +pub use fp_rent::EvmRentCalculator; use fp_storage::{EthereumStorageSchema, PALLET_ETHEREUM_SCHEMA}; use pallet_evm::{BlockHashMapping, FeeCalculator, GasWeightMapping, Runner}; +// Unique,BEVM +use pallet_evm::CurrentLogs; + #[derive(Clone, Eq, PartialEq, RuntimeDebug)] #[derive(Encode, Decode, MaxEncodedLen, TypeInfo)] pub enum RawOrigin { @@ -74,8 +78,8 @@ pub enum RawOrigin { } pub fn ensure_ethereum_transaction(o: OuterOrigin) -> Result -where - OuterOrigin: Into>, + where + OuterOrigin: Into>, { match o.into() { Ok(RawOrigin::EthereumTransaction(n)) => Ok(n), @@ -85,7 +89,7 @@ where pub struct EnsureEthereumTransaction; impl> + From> EnsureOrigin - for EnsureEthereumTransaction +for EnsureEthereumTransaction { type Success = H160; fn try_origin(o: O) -> Result { @@ -101,10 +105,10 @@ impl> + From> EnsureOrigin } impl Call -where - OriginFor: Into>>, - T: Send + Sync + Config, - T::RuntimeCall: Dispatchable, + where + OriginFor: Into>>, + T: Send + Sync + Config, + T::RuntimeCall: Dispatchable, { pub fn is_self_contained(&self) -> bool { matches!(self, Call::transact { .. }) @@ -225,6 +229,7 @@ pub mod pallet { )); } Pending::::kill(); + assert_eq!(>::get().len(), 0, "fake transaction finalizer is not initialized, as some logs was left after block is finished"); } fn on_initialize(_: BlockNumberFor) -> Weight { @@ -269,17 +274,17 @@ pub mod pallet { #[pallet::call] impl Pallet - where - OriginFor: Into>>, + where + OriginFor: Into>>, { /// Transact an Ethereum transaction. #[pallet::call_index(0)] #[pallet::weight({ - let without_base_extrinsic_weight = true; - ::GasWeightMapping::gas_to_weight({ - let transaction_data: TransactionData = transaction.into(); - transaction_data.gas_limit.unique_saturated_into() - }, without_base_extrinsic_weight) + let without_base_extrinsic_weight = true; + ::GasWeightMapping::gas_to_weight({ + let transaction_data: TransactionData = transaction.into(); + transaction_data.gas_limit.unique_saturated_into() + }, without_base_extrinsic_weight) })] pub fn transact( origin: OriginFor, @@ -320,7 +325,7 @@ pub mod pallet { /// Current building block's transactions and receipts. #[pallet::storage] pub(super) type Pending = - StorageValue<_, Vec<(Transaction, TransactionStatus, Receipt)>, ValueQuery>; + StorageValue<_, Vec<(Transaction, TransactionStatus, Receipt)>, ValueQuery>; /// The current Ethereum block. #[pallet::storage] @@ -338,6 +343,10 @@ pub mod pallet { #[pallet::storage] pub type BlockHash = StorageMap<_, Twox64Concat, U256, H256, ValueQuery>; + /// Injected transactions should have unique nonce, here we store current + #[pallet::storage] + pub(super) type InjectedNonce = StorageValue<_, U256, ValueQuery>; + #[pallet::genesis_config] #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig { @@ -420,7 +429,7 @@ impl Pallet { } }; cumulative_gas_used = used_gas; - Self::logs_bloom(logs, &mut logs_bloom); + Self::logs_bloom(logs.iter(), &mut logs_bloom); } let ommers = Vec::::new(); @@ -472,15 +481,52 @@ impl Pallet { } } - fn logs_bloom(logs: Vec, bloom: &mut Bloom) { + fn logs_bloom<'a>(logs: impl IntoIterator, bloom: &'a mut Bloom) { for log in logs { bloom.accrue(BloomInput::Raw(&log.address[..])); - for topic in log.topics { + for topic in &log.topics { bloom.accrue(BloomInput::Raw(&topic[..])); } } } + fn calculate_max_transaction_fee( + transaction_data: &TransactionData, + ) -> Result { + match ( + transaction_data.gas_price, + transaction_data.max_fee_per_gas, + transaction_data.max_priority_fee_per_gas, + ) { + // Legacy or EIP-2930 transaction + (Some(gas_price), None, None) => { + Ok(gas_price.saturating_mul(transaction_data.gas_limit)) + }, + + // EIP-1559 transaction without tip + (None, Some(max_fee_per_gas), None) => { + Ok(max_fee_per_gas.saturating_mul(transaction_data.gas_limit)) + }, + + // EIP-1559 with tip + (None, Some(max_fee_per_gas), Some(max_priority_fee_per_gas)) => { + if max_priority_fee_per_gas > max_fee_per_gas { + return Err(TransactionValidityError::Invalid( + InvalidTransaction::Custom(TransactionValidationError::PriorityFeeTooHigh as u8) + )); + } + Ok(max_fee_per_gas.saturating_mul(transaction_data.gas_limit)) + } + + _ => { + // must be transactional tx + Err(TransactionValidityError::Invalid( + InvalidTransaction::Custom(TransactionValidationError::InvalidFeeInput as u8) + )) + } + } + } + // Controls that must be performed by the pool. // The controls common with the State Transition Function (STF) are in // the function `validate_transaction_common`. @@ -506,11 +552,27 @@ impl Pallet { weight_limit, proof_size_base_cost, ) - .validate_in_pool_for(&who) - .and_then(|v| v.with_chain_id()) - .and_then(|v| v.with_base_fee()) - .and_then(|v| v.with_balance_for(&who)) - .map_err(|e| e.0)?; + .validate_in_pool_for(&who) + .and_then(|v| v.with_chain_id()) + .and_then(|v| v.with_base_fee()) + .and_then(|v| v.with_balance_for(&who)) + .map_err(|e| e.0)?; + + let rent_amount = T::EvmRentCalculator::estimate_rent(origin).0; + if rent_amount > 0 { + let fee = Self::calculate_max_transaction_fee(&transaction_data)?; + + let total_payment = transaction_data.value.saturating_add(fee); + let total_with_rent = total_payment.saturating_add(U256::from(rent_amount)); + + if who.balance < total_with_rent { + return Err( + TransactionValidityError::Invalid( + InvalidTransaction::Custom(TransactionValidationError::InsufficientRent as u8) + ) + ); + } + } // EIP-3607: https://eips.ethereum.org/EIPS/eip-3607 // Do not allow transactions for which `tx.sender` has any code deployed. @@ -571,70 +633,76 @@ impl Pallet { let transaction_index = pending.len() as u32; let (reason, status, weight_info, used_gas, dest, extra_data) = match info.clone() { - CallOrCreateInfo::Call(info) => ( - info.exit_reason.clone(), - TransactionStatus { - transaction_hash, - transaction_index, - from: source, - to, - contract_address: None, - logs: info.logs.clone(), - logs_bloom: { - let mut bloom: Bloom = Bloom::default(); - Self::logs_bloom(info.logs, &mut bloom); - bloom + CallOrCreateInfo::Call(info) => { + let logs = >::take(); + ( + info.exit_reason.clone(), + TransactionStatus { + transaction_hash, + transaction_index, + from: source, + to, + contract_address: None, + logs_bloom: { + let mut bloom: Bloom = Bloom::default(); + Self::logs_bloom(logs.iter(), &mut bloom); + bloom + }, + logs, }, - }, - info.weight_info, - info.used_gas, - to, - match info.exit_reason { - ExitReason::Revert(_) => { - const LEN_START: usize = 36; - const MESSAGE_START: usize = 68; - - let data = info.value; - let data_len = data.len(); - if data_len > MESSAGE_START { - let message_len = U256::from(&data[LEN_START..MESSAGE_START]) - .saturated_into::(); - let message_end = MESSAGE_START.saturating_add( - message_len.min(T::ExtraDataLength::get() as usize), - ); - - if data_len >= message_end { - data[MESSAGE_START..message_end].to_vec() + info.weight_info, + info.used_gas, + to, + match info.exit_reason { + ExitReason::Revert(_) => { + const LEN_START: usize = 36; + const MESSAGE_START: usize = 68; + + let data = info.value; + let data_len = data.len(); + if data_len > MESSAGE_START { + let message_len = U256::from(&data[LEN_START..MESSAGE_START]) + .saturated_into::(); + let message_end = MESSAGE_START.saturating_add( + message_len.min(T::ExtraDataLength::get() as usize), + ); + + if data_len >= message_end { + data[MESSAGE_START..message_end].to_vec() + } else { + data + } } else { data } - } else { - data } - } - _ => vec![], - }, - ), - CallOrCreateInfo::Create(info) => ( - info.exit_reason, - TransactionStatus { - transaction_hash, - transaction_index, - from: source, - to, - contract_address: Some(info.value), - logs: info.logs.clone(), - logs_bloom: { - let mut bloom: Bloom = Bloom::default(); - Self::logs_bloom(info.logs, &mut bloom); - bloom + _ => vec![], }, - }, - info.weight_info, - info.used_gas, - Some(info.value), - Vec::new(), - ), + ) + }, + CallOrCreateInfo::Create(info) => { + let logs = >::take(); + ( + info.exit_reason, + TransactionStatus { + transaction_hash, + transaction_index, + from: source, + to, + contract_address: Some(info.value), + logs_bloom: { + let mut bloom: Bloom = Bloom::default(); + Self::logs_bloom(logs.iter(), &mut bloom); + bloom + }, + logs + }, + info.weight_info, + info.used_gas, + Some(info.value), + Vec::new(), + ) + }, }; let receipt = { @@ -708,6 +776,70 @@ impl Pallet { )) } + // Unique,BEVM: + pub fn flush_injected_transaction() { + use ethereum::{ + EIP658ReceiptData, EnvelopedEncodable, TransactionSignature, TransactionV0, + }; + + assert!( + fp_consensus::find_pre_log(&frame_system::Pallet::::digest()).is_err(), + "this method is supposed to be called only from other pallets", + ); + + let logs = >::take(); + if logs.is_empty() { + return; + } + + let nonce = >::get() + .checked_add(1u32.into()) + .expect("u256 should be enough"); + >::set(nonce); + + let transaction = Transaction::Legacy(TransactionV0 { + nonce, + gas_price: 0.into(), + gas_limit: 0.into(), + action: TransactionAction::Call(H160([0; 20])), + value: 0.into(), + // zero selector, this transaction always has same sender, so all data should be acquired from logs + input: Vec::from([0, 0, 0, 0]), + // if v is not 27 - then we need to pass some other validity checks + signature: TransactionSignature::new(27, H256([0x88; 32]), H256([0x88; 32])).unwrap(), + }); + + let transaction_hash = H256::from_slice( + sp_io::hashing::keccak_256(&EnvelopedEncodable::encode(&transaction)).as_slice(), + ); + let transaction_index = >::get().len() as u32; + + let logs_bloom = { + let mut bloom: Bloom = Bloom::default(); + Self::logs_bloom(&logs, &mut bloom); + bloom + }; + + let status = TransactionStatus { + transaction_hash, + transaction_index, + from: H160::default(), + to: None, + contract_address: None, + logs_bloom, + logs: logs.clone(), + }; + + let receipt = Receipt::Legacy(EIP658ReceiptData { + status_code: 1, + used_gas: 0u32.into(), + logs_bloom, + logs, + }); + + >::append((transaction, status, receipt)); + } + /// Get current block hash pub fn current_block_hash() -> Option { >::get().map(|block| block.header.hash()) @@ -870,15 +1002,31 @@ impl Pallet { chain_id: T::ChainId::get(), is_transactional: true, }, - transaction_data.into(), + transaction_data.clone().into(), weight_limit, proof_size_base_cost, ) - .validate_in_block_for(&who) - .and_then(|v| v.with_chain_id()) - .and_then(|v| v.with_base_fee()) - .and_then(|v| v.with_balance_for(&who)) - .map_err(|e| TransactionValidityError::Invalid(e.0))?; + .validate_in_block_for(&who) + .and_then(|v| v.with_chain_id()) + .and_then(|v| v.with_base_fee()) + .and_then(|v| v.with_balance_for(&who)) + .map_err(|e| TransactionValidityError::Invalid(e.0))?; + + let rent_amount = T::EvmRentCalculator::estimate_rent(origin).0; + if rent_amount > 0 { + let fee = Self::calculate_max_transaction_fee(&transaction_data)?; + + let total_payment = transaction_data.value.saturating_add(fee); + let total_with_rent = total_payment.saturating_add(U256::from(rent_amount)); + + if who.balance < total_with_rent { + return Err( + TransactionValidityError::Invalid( + InvalidTransaction::Custom(TransactionValidationError::InsufficientRent as u8) + ) + ); + } + } Ok(()) } @@ -930,7 +1078,7 @@ impl Pallet { let (v0_number, v0_parent_hash, v0_transaction_len): (U256, H256, u64) = Decode::decode( &mut v0_data.as_slice(), ) - .expect("the state parameter should be something that was generated by pre_upgrade"); + .expect("the state parameter should be something that was generated by pre_upgrade"); let item = b"CurrentBlock"; let block_v2 = frame_support::storage::migration::get_storage_value::( Self::name().as_bytes(), @@ -1016,9 +1164,62 @@ impl From for InvalidTransactionWrapper { TransactionValidationError::GasPriceTooLow => InvalidTransactionWrapper( InvalidTransaction::Custom(TransactionValidationError::GasPriceTooLow as u8), ), + TransactionValidationError::InsufficientRent => InvalidTransactionWrapper( + InvalidTransaction::Custom(TransactionValidationError::InsufficientRent as u8), + ), TransactionValidationError::UnknownError => InvalidTransactionWrapper( InvalidTransaction::Custom(TransactionValidationError::UnknownError as u8), ), } } } + +#[derive(TypeInfo, PartialEq, Eq, Clone, Debug, Encode, Decode)] +pub struct FakeTransactionFinalizer(PhantomData); + + +impl FakeTransactionFinalizer { + /// Creates new `SignedExtension` to fake transaction finalizer. + pub fn new() -> Self { + Self(PhantomData) + } +} + +impl sp_runtime::traits::SignedExtension +for FakeTransactionFinalizer +{ + const IDENTIFIER: &'static str = "FakeTransactionFinalizer"; + + type AccountId = T::AccountId; + + type Call = T::RuntimeCall; + + type AdditionalSigned = (); + + type Pre = (); + + fn additional_signed(&self) -> Result { + Ok(()) + } + + fn pre_dispatch( + self, + _who: &Self::AccountId, + _call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> Result { + Ok(()) + } + + fn post_dispatch( + _pre: Option, + _info: &DispatchInfoOf, + _post_info: &sp_runtime::traits::PostDispatchInfoOf, + _len: usize, + _result: &sp_runtime::DispatchResult, + ) -> Result<(), TransactionValidityError> { + >::flush_injected_transaction(); + Ok(()) + } +} diff --git a/frame/ethereum/src/mock.rs b/frame/ethereum/src/mock.rs index c55d497a54..4f541c84f0 100644 --- a/frame/ethereum/src/mock.rs +++ b/frame/ethereum/src/mock.rs @@ -177,6 +177,7 @@ impl pallet_evm::Config for Test { type GasLimitPovSizeRatio = GasLimitPovSizeRatio; type SuicideQuickClearLimit = SuicideQuickClearLimit; type Timestamp = Timestamp; + type EvmRentCalculator = (); type WeightInfo = (); } diff --git a/frame/evm-chain-id/Cargo.toml b/frame/evm-chain-id/Cargo.toml index 9427a48187..61dae6b06a 100644 --- a/frame/evm-chain-id/Cargo.toml +++ b/frame/evm-chain-id/Cargo.toml @@ -11,7 +11,7 @@ repository = { workspace = true } targets = ["x86_64-unknown-linux-gnu"] [dependencies] -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } scale-info = { workspace = true } # Substrate frame-support = { workspace = true } diff --git a/frame/evm-rent/Cargo.toml b/frame/evm-rent/Cargo.toml new file mode 100644 index 0000000000..12141c4a19 --- /dev/null +++ b/frame/evm-rent/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "pallet-evm-rent" +version = "1.0.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.dev" +repository = "https://github.com/paritytech/frontier/" +description = "EVM account rent calculation and processing" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +scale-codec = { package = "parity-scale-codec", workspace = true } +scale-info = { workspace = true } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +pallet-timestamp = { workspace = true } + +fp-rent = { version = "2.0.0-dev", default-features = false, path = "../../primitives/rent" } + +[dev-dependencies] +sp-io = { workspace = true } +sp-std = { workspace = true } + +[features] +default = ["std"] +std = [ + "scale-codec/std", + "scale-info/std", + "sp-core/std", + "sp-runtime/std", + "frame-support/std", + "frame-system/std", + + "pallet-timestamp/std", + + "fp-rent/std", +] diff --git a/frame/evm-rent/src/lib.rs b/frame/evm-rent/src/lib.rs new file mode 100644 index 0000000000..23b08796e8 --- /dev/null +++ b/frame/evm-rent/src/lib.rs @@ -0,0 +1,243 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; + +use fp_rent::EvmRentCalculator; +use frame_support::pallet_prelude::*; +use sp_core::H160; +use sp_runtime::traits::SaturatedConversion; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::{dispatch::DispatchResult, traits::EnsureOrigin}; + use frame_system::{ensure_root, pallet_prelude::OriginFor}; + + // ========================================== + // 1. Configuration & Constants + // ========================================== + + // 2026-01-01 00:00:00 UTC + pub const DEFAULT_RENT_START_TIME: u64 = 1_767_225_600_000; + // 2025-12-10 00:00:00 UTC + pub const MIN_RENT_START_TIME: u64 = 1_765_324_800_000; + // 1 satoshi + pub const SATOSHI: u128 = 10_000_000_000; + // 10 satoshis = 100 Gwei (10 * 10^10) + pub const DEFAULT_DAILY_RENT: u128 = 10 * SATOSHI; + // Milliseconds per day + pub const MILLISECONDS_PER_DAY: u64 = 86_400_000; + + #[pallet::config] + pub trait Config: frame_system::Config + pallet_timestamp::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// A majority of the council can execute some transactions. + type CouncilOrigin: EnsureOrigin; + } + + #[pallet::pallet] + pub struct Pallet(_); + + // ========================================== + // 2. Storage Layer + // ========================================== + + /// System rent activation timestamp (milliseconds) + #[pallet::storage] + #[pallet::getter(fn active_timestamp)] + pub type ActiveTimestamp = StorageValue<_, u64, ValueQuery, DefaultActiveTimestamp>; + + /// Daily rent amount (default 100 Gwei) + #[pallet::storage] + #[pallet::getter(fn daily_rent)] + pub type DailyRent = StorageValue<_, u128, ValueQuery, DefaultDailyRent>; + + /// Account rent status structure + #[derive( + Encode, + Decode, + Clone, + PartialEq, + Eq, + RuntimeDebug, + TypeInfo, + MaxEncodedLen + )] + pub struct RentStatus { + pub last_rent_paid_time: u64, // Last settlement time + pub accumulated_rent: u128, // Total accumulated rent (statistical purpose) + } + + /// Core storage: H160 -> Rent status + #[pallet::storage] + #[pallet::getter(fn account_rent_status)] + pub type AccountRentMap = StorageMap<_, Twox64Concat, H160, RentStatus, OptionQuery>; + + // Default value implementations + pub struct DefaultActiveTimestamp; + impl Get for DefaultActiveTimestamp { + fn get() -> u64 { + DEFAULT_RENT_START_TIME + } + } + + pub struct DefaultDailyRent; + impl Get for DefaultDailyRent { + fn get() -> u128 { + DEFAULT_DAILY_RENT + } + } + + // ========================================== + // 3. Events + // ========================================== + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Rent charged: [Account, DaysPaid, Amount] + RentChargedToBurn(H160, u64, u128), + } + + #[pallet::error] + pub enum Error { + /// Invalid timestamp + InvalidTimestamp, + /// Invalid daily rent + InvalidDailyRent, + } + + // ========================================== + // 4. Dispatchable Functions + // ========================================== + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight({0})] + pub fn set_active_timestamp(origin: OriginFor, timestamp: u64) -> DispatchResult { + ::CouncilOrigin::try_origin(origin) + .map(|_| ()) + .or_else(ensure_root)?; + + ensure!( + timestamp >= MIN_RENT_START_TIME, + Error::::InvalidTimestamp + ); + + >::put(timestamp); + + Ok(()) + } + + #[pallet::call_index(1)] + #[pallet::weight({0})] + pub fn set_daily_rent(origin: OriginFor, rent: u128) -> DispatchResult { + ::CouncilOrigin::try_origin(origin) + .map(|_| ()) + .or_else(ensure_root)?; + + ensure!(rent % SATOSHI == 0, Error::::InvalidDailyRent); + + >::put(rent); + + Ok(()) + } + } + + // ========================================== + // 5. Implementation + // ========================================== + + impl EvmRentCalculator for Pallet { + /// Calculate and update state, return amount to charge + /// This method should be called by EVM Adapter (OnChargeEVMTransaction) + /// return rent value by Wei + fn process_rent(who: H160) -> u128 { + let now = >::now().saturated_into::(); + let start_time: u64 = Self::active_timestamp(); + + // 1. if zero address(used for eth_call), no charge + if who == H160::default() { + return 0; + } + + // 2. If current time is before rent start time, no charge + if now < start_time { + return 0; + } + + // 3. Get user status + let mut status = Self::account_rent_status(who).unwrap_or(RentStatus { + last_rent_paid_time: start_time, // Default to system rent start time + accumulated_rent: 0, + }); + + // Defensive check: Prevent time regression + if now <= status.last_rent_paid_time { + return 0; + } + + // 4. Calculate elapsed time and days (floor) + let elapsed_ms = now - status.last_rent_paid_time; + let days_to_pay = elapsed_ms / MILLISECONDS_PER_DAY; + + // 5. Less than 1 day, no charge, no state update + if days_to_pay == 0 { + return 0; + } + + // 6. Calculate amount + let daily_rent = Self::daily_rent(); + let rent_amount = (days_to_pay as u128).saturating_mul(daily_rent); + + // 7. Update state + // Key: Only advance paid days, keep remainder + let time_paid_for = days_to_pay * MILLISECONDS_PER_DAY; + status.last_rent_paid_time += time_paid_for; + status.accumulated_rent = status.accumulated_rent.saturating_add(rent_amount); + + // 8. Write to storage + >::insert(who, status); + + // 9. Emit event + Self::deposit_event(Event::RentChargedToBurn(who, days_to_pay, rent_amount)); + + rent_amount + } + + /// Corresponds to Solidity: estimateRent(address account)(uint256,uint64) + /// return rent value by Wei + fn estimate_rent(who: H160) -> (u128, u64) { + let now = >::now().saturated_into::(); + let start_time: u64 = Self::active_timestamp(); + + if now < start_time { + return (0, 0); + } + + let status = Self::account_rent_status(who).unwrap_or(RentStatus { + last_rent_paid_time: start_time, + accumulated_rent: 0, + }); + + if now <= status.last_rent_paid_time { + return (0, 0); + } + + let elapsed_ms = now - status.last_rent_paid_time; + let days = elapsed_ms / MILLISECONDS_PER_DAY; + + if days == 0 { + return (0, 0); + } + + let rent_amount = (days as u128).saturating_mul(Self::daily_rent()); + + (rent_amount, days) + } + } +} + +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; diff --git a/frame/evm-rent/src/mock.rs b/frame/evm-rent/src/mock.rs new file mode 100644 index 0000000000..2fa9a637eb --- /dev/null +++ b/frame/evm-rent/src/mock.rs @@ -0,0 +1,75 @@ +use crate::pallet as pallet_rent; +use frame_support::{ + pallet_prelude::{ConstU32, Weight}, + parameter_types, + traits::ConstU64, +}; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1024, 0)); +} +impl frame_system::Config for Test { + type RuntimeEvent = RuntimeEvent; + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Block = frame_system::mocking::MockBlock; + type BlockHashCount = BlockHashCount; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<1>; + type WeightInfo = (); +} + +impl pallet_rent::Config for Test { + type RuntimeEvent = RuntimeEvent; + type CouncilOrigin = frame_system::EnsureRoot; +} + +frame_support::construct_runtime!( + pub enum Test { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage}, + Rent: pallet_rent::{Pallet, Call, Storage, Event}, + } +); + +pub fn new_test_ext() -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +pub fn set_time(ms: u64) { + Timestamp::set_timestamp(ms); +} diff --git a/frame/evm-rent/src/tests.rs b/frame/evm-rent/src/tests.rs new file mode 100644 index 0000000000..ba2be8c5c5 --- /dev/null +++ b/frame/evm-rent/src/tests.rs @@ -0,0 +1,117 @@ +use super::mock::*; +use crate::pallet::*; +use fp_rent::EvmRentCalculator; +use frame_support::{assert_noop, assert_ok}; +use sp_core::H160; + +#[test] +fn set_active_timestamp_works() { + new_test_ext().execute_with(|| { + let new_time = MIN_RENT_START_TIME + 1000; + assert_noop!( + Rent::set_active_timestamp(RuntimeOrigin::signed(1), new_time), + sp_runtime::DispatchError::BadOrigin + ); + assert_ok!(Rent::set_active_timestamp(RuntimeOrigin::root(), new_time)); + assert_eq!(Rent::active_timestamp(), new_time); + assert_noop!( + Rent::set_active_timestamp(RuntimeOrigin::root(), MIN_RENT_START_TIME - 1), + Error::::InvalidTimestamp + ); + }); +} + +#[test] +fn set_daily_rent_works() { + new_test_ext().execute_with(|| { + let valid_rent = 2 * SATOSHI; + let invalid_rent = SATOSHI - 1; + + assert_ok!(Rent::set_daily_rent(RuntimeOrigin::root(), valid_rent)); + assert_eq!(Rent::daily_rent(), valid_rent); + + assert_noop!( + Rent::set_daily_rent(RuntimeOrigin::root(), invalid_rent), + Error::::InvalidDailyRent + ); + }); +} + +#[test] +fn process_rent_logic_flow() { + new_test_ext().execute_with(|| { + let alice = H160::from_low_u64_be(1); + let start_time = DEFAULT_RENT_START_TIME; // 1_767_225_600_000 + + // 1. Before start time, rent should be 0 + set_time(start_time - 1000); + assert_eq!(Rent::process_rent(alice), 0); + + // 2. Exactly at start time, but less than 1 day + set_time(start_time); + assert_eq!(Rent::process_rent(alice), 0); + + // 3. After 1.5 days, charge 1 day rent, update last settlement to start_time + 1 day + set_time(start_time + (MILLISECONDS_PER_DAY * 3 / 2)); + let expected_rent = DEFAULT_DAILY_RENT; + assert_eq!(Rent::process_rent(alice), expected_rent); + + // Verify state update + let status = Rent::account_rent_status(alice).unwrap(); + assert_eq!( + status.last_rent_paid_time, + start_time + MILLISECONDS_PER_DAY + ); + assert_eq!(status.accumulated_rent, expected_rent); + + // 4. Another 0.8 days (total 2.3 days), 1 day since last settlement, charge and update to start_time + 2 days + set_time(start_time + (MILLISECONDS_PER_DAY * 23 / 10)); + assert_eq!(Rent::process_rent(alice), DEFAULT_DAILY_RENT); + + let status = Rent::account_rent_status(alice).unwrap(); + assert_eq!( + status.last_rent_paid_time, + start_time + 2 * MILLISECONDS_PER_DAY + ); + assert_eq!(status.accumulated_rent, expected_rent * 2); + + // 5. Another 1.2 days (total 3.5 days), 1.5 days since last settlement + // Charge 1 day, total accumulated should be 3 days + set_time(start_time + (MILLISECONDS_PER_DAY * 35 / 10)); + assert_eq!(Rent::process_rent(alice), DEFAULT_DAILY_RENT); + + let status = Rent::account_rent_status(alice).unwrap(); + assert_eq!( + status.last_rent_paid_time, + start_time + 3 * MILLISECONDS_PER_DAY + ); + assert_eq!(status.accumulated_rent, DEFAULT_DAILY_RENT * 3); + }); +} + +#[test] +fn estimate_rent_matches_process() { + new_test_ext().execute_with(|| { + let bob = H160::from_low_u64_be(2); + let start_time = DEFAULT_RENT_START_TIME; + + set_time(start_time + (MILLISECONDS_PER_DAY * 5)); + + let (est_amount, est_days) = Rent::estimate_rent(bob); + assert_eq!(est_days, 5); + assert_eq!(est_amount, DEFAULT_DAILY_RENT * 5); + + // Execute actual rent charging + let actual_amount = Rent::process_rent(bob); + assert_eq!(actual_amount, est_amount); + }); +} + +#[test] +fn zero_address_is_exempt() { + new_test_ext().execute_with(|| { + let zero_addr = H160::default(); + set_time(DEFAULT_RENT_START_TIME + (MILLISECONDS_PER_DAY * 10)); + assert_eq!(Rent::process_rent(zero_addr), 0); + }); +} diff --git a/frame/evm/Cargo.toml b/frame/evm/Cargo.toml index 39d07150a5..5999b08cc1 100644 --- a/frame/evm/Cargo.toml +++ b/frame/evm/Cargo.toml @@ -20,7 +20,7 @@ hex-literal = { workspace = true } impl-trait-for-tuples = "0.2.2" log = { workspace = true } rlp = { workspace = true, optional = true } -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } scale-info = { workspace = true } # Substrate frame-benchmarking = { workspace = true, optional = true } @@ -33,6 +33,7 @@ sp-std = { workspace = true } # Frontier fp-account = { workspace = true } fp-evm = { workspace = true, features = ["serde"] } +fp-rent = { workspace = true } [dev-dependencies] hex = { workspace = true } @@ -63,6 +64,7 @@ std = [ # Frontier "fp-account/std", "fp-evm/std", + "fp-rent/std", ] runtime-benchmarks = [ "hex", diff --git a/frame/evm/precompile/dispatch/Cargo.toml b/frame/evm/precompile/dispatch/Cargo.toml index 5b621ee385..216d9c5459 100644 --- a/frame/evm/precompile/dispatch/Cargo.toml +++ b/frame/evm/precompile/dispatch/Cargo.toml @@ -8,7 +8,7 @@ edition = { workspace = true } repository = { workspace = true } [dependencies] -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } # Substrate frame-support = { workspace = true } sp-runtime = { workspace = true } diff --git a/frame/evm/src/lib.rs b/frame/evm/src/lib.rs index 59945a4fd1..98f02d9be4 100644 --- a/frame/evm/src/lib.rs +++ b/frame/evm/src/lib.rs @@ -103,6 +103,8 @@ pub use fp_evm::{ PrecompileOutput, PrecompileResult, PrecompileSet, TransactionValidationError, Vicinity, }; +pub use fp_rent::EvmRentCalculator; + pub use self::{ pallet::*, runner::{Runner, RunnerError}, @@ -175,6 +177,9 @@ pub mod pallet { /// Get the timestamp for the current block. type Timestamp: Time; + /// Charge(Burn) evm rent + type EvmRentCalculator: EvmRentCalculator; + /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; @@ -210,8 +215,8 @@ pub mod pallet { /// Issue an EVM call operation. This is similar to a message call transaction in Ethereum. #[pallet::call_index(1)] #[pallet::weight({ - let without_base_extrinsic_weight = true; - T::GasWeightMapping::gas_to_weight(*gas_limit, without_base_extrinsic_weight) + let without_base_extrinsic_weight = true; + T::GasWeightMapping::gas_to_weight(*gas_limit, without_base_extrinsic_weight) })] pub fn call( origin: OriginFor, @@ -287,8 +292,8 @@ pub mod pallet { /// Ethereum. #[pallet::call_index(2)] #[pallet::weight({ - let without_base_extrinsic_weight = true; - T::GasWeightMapping::gas_to_weight(*gas_limit, without_base_extrinsic_weight) + let without_base_extrinsic_weight = true; + T::GasWeightMapping::gas_to_weight(*gas_limit, without_base_extrinsic_weight) })] pub fn create( origin: OriginFor, @@ -373,8 +378,8 @@ pub mod pallet { /// Issue an EVM create2 operation. #[pallet::call_index(3)] #[pallet::weight({ - let without_base_extrinsic_weight = true; - T::GasWeightMapping::gas_to_weight(*gas_limit, without_base_extrinsic_weight) + let without_base_extrinsic_weight = true; + T::GasWeightMapping::gas_to_weight(*gas_limit, without_base_extrinsic_weight) })] pub fn create2( origin: OriginFor, @@ -500,6 +505,8 @@ pub mod pallet { Reentrancy, /// EIP-3607, TransactionMustComeFromEOA, + /// Insufficient Rent + Fee + Value + InsufficientRent, /// Undefined error. Undefined, } @@ -517,6 +524,7 @@ pub mod pallet { TransactionValidationError::InvalidFeeInput => Error::::GasPriceTooLow, TransactionValidationError::InvalidChainId => Error::::InvalidChainId, TransactionValidationError::InvalidSignature => Error::::InvalidSignature, + TransactionValidationError::InsufficientRent => Error::InsufficientRent, TransactionValidationError::UnknownError => Error::::Undefined, } } @@ -532,8 +540,8 @@ pub mod pallet { #[pallet::genesis_build] impl BuildGenesisConfig for GenesisConfig - where - U256: UniqueSaturatedInto>, + where + U256: UniqueSaturatedInto>, { fn build(&self) { const MAX_ACCOUNT_NONCE: usize = 100; @@ -566,34 +574,40 @@ pub mod pallet { #[pallet::storage] pub type AccountCodesMetadata = - StorageMap<_, Blake2_128Concat, H160, CodeMetadata, OptionQuery>; + StorageMap<_, Blake2_128Concat, H160, CodeMetadata, OptionQuery>; #[pallet::storage] pub type AccountStorages = - StorageDoubleMap<_, Blake2_128Concat, H160, Blake2_128Concat, H256, H256, ValueQuery>; + StorageDoubleMap<_, Blake2_128Concat, H160, Blake2_128Concat, H256, H256, ValueQuery>; #[pallet::storage] pub type Suicided = StorageMap<_, Blake2_128Concat, H160, (), OptionQuery>; + + // Unique,BEVM: + /// Written on log, reset after transaction + /// Should be empty between transactions + #[pallet::storage] + pub type CurrentLogs = StorageValue<_, Vec, ValueQuery>; } /// Type alias for currency balance. pub type BalanceOf = - <::Currency as Currency<::AccountId>>::Balance; +<::Currency as Currency<::AccountId>>::Balance; /// Type alias for negative imbalance during fees type NegativeImbalanceOf = - ::AccountId>>::NegativeImbalance; +::AccountId>>::NegativeImbalance; #[derive( - Debug, - Clone, - Copy, - Eq, - PartialEq, - Encode, - Decode, - TypeInfo, - MaxEncodedLen +Debug, +Clone, +Copy, +Eq, +PartialEq, +Encode, +Decode, +TypeInfo, +MaxEncodedLen )] pub struct CodeMetadata { pub size: u64, @@ -633,8 +647,8 @@ pub trait EnsureAddressOrigin { pub struct EnsureAddressSame; impl EnsureAddressOrigin for EnsureAddressSame -where - OuterOrigin: Into, OuterOrigin>> + From>, + where + OuterOrigin: Into, OuterOrigin>> + From>, { type Success = H160; @@ -650,8 +664,8 @@ where pub struct EnsureAddressRoot(sp_std::marker::PhantomData); impl EnsureAddressOrigin for EnsureAddressRoot -where - OuterOrigin: Into, OuterOrigin>> + From>, + where + OuterOrigin: Into, OuterOrigin>> + From>, { type Success = (); @@ -679,8 +693,8 @@ impl EnsureAddressOrigin for EnsureAddressN pub struct EnsureAddressTruncated; impl EnsureAddressOrigin for EnsureAddressTruncated -where - OuterOrigin: Into, OuterOrigin>> + From>, + where + OuterOrigin: Into, OuterOrigin>> + From>, { type Success = AccountId32; @@ -698,8 +712,8 @@ where pub struct EnsureAccountId20; impl EnsureAddressOrigin for EnsureAccountId20 -where - OuterOrigin: Into, OuterOrigin>> + From>, + where + OuterOrigin: Into, OuterOrigin>> + From>, { type Success = AccountId20; @@ -820,7 +834,7 @@ impl Pallet { if T::SuicideQuickClearLimit::get() > 0 { #[allow(deprecated)] - let res = >::remove_prefix(address, Some(T::SuicideQuickClearLimit::get())); + let res = >::remove_prefix(address, Some(T::SuicideQuickClearLimit::get())); match res { KillStorageResult::AllRemoved(_) => { @@ -860,6 +874,22 @@ impl Pallet { >::insert(address, code); } + // Unique,BEVM: + /// Add log to be injected in either real or fake ethereum transaction + pub fn deposit_log(log: Log) { + log::trace!( + target: "evm", + "Inserting mirrored log for {:?}, topics ({}) {:?}, data ({}): {:?}]", + log.address, + log.topics.len(), + log.topics, + log.data.len(), + log.data + ); + >::append(log); + // Log event is not emitted here, as these logs belong to pallets, which will emit pallet-specific logs on substrate side by themselves + } + /// Get the account metadata (hash and size) from storage if it exists, /// or compute it from code and store it if it doesn't exist. pub fn account_code_metadata(address: H160) -> CodeMetadata { @@ -946,35 +976,71 @@ pub trait OnChargeEVMTransaction { pub struct EVMCurrencyAdapter(sp_std::marker::PhantomData<(C, OU)>); impl OnChargeEVMTransaction for EVMCurrencyAdapter -where - T: Config, - C: Currency<::AccountId>, - C::PositiveImbalance: Imbalance< - ::AccountId>>::Balance, - Opposite = C::NegativeImbalance, - >, - C::NegativeImbalance: Imbalance< - ::AccountId>>::Balance, - Opposite = C::PositiveImbalance, - >, - OU: OnUnbalanced>, - U256: UniqueSaturatedInto<::AccountId>>::Balance>, + where + T: Config, + C: Currency<::AccountId>, + C::PositiveImbalance: Imbalance< + ::AccountId>>::Balance, + Opposite = C::NegativeImbalance, + >, + C::NegativeImbalance: Imbalance< + ::AccountId>>::Balance, + Opposite = C::PositiveImbalance, + >, + OU: OnUnbalanced>, + U256: UniqueSaturatedInto<::AccountId>>::Balance>, { // Kept type as Option to satisfy bound of Default type LiquidityInfo = Option>; fn withdraw_fee(who: &H160, fee: U256) -> Result> { + // ==================================================== + // Handle EVM Rent (burn logic) + + // 1. H160 -> T::AccountId + let account_id = T::AddressMapping::into_account_id(*who); + + // 2. call process_rent get rent to burn and update state + let rent_to_burn_u128 = T::EvmRentCalculator::process_rent(*who); + if rent_to_burn_u128 > 0 { + let rent_balance: C::Balance = rent_to_burn_u128.unique_saturated_into(); + + // Withdraw + // WithdrawReasons::FEE + // ExistenceRequirement::AllowDeath + match C::withdraw( + &account_id, + rent_balance, + WithdrawReasons::FEE, + ExistenceRequirement::AllowDeath + ) { + Ok(rent_imbalance) => { + // !!! KeyPoint: Burn !!! + // rent_imbalance is NegativeImbalance + // We don't call resolve_creating or deposit_into_existing. + // We do nothing and just let it drop here. + // Substrate automatically reduces TotalIssuance when rent_imbalance goes out of scope. + drop(rent_imbalance); + }, + Err(_) => { + // The transaction fails if the balance is insufficient to pay the rent. + return Err(Error::::BalanceLow); + } + } + } + // ==================================================== + if fee.is_zero() { return Ok(None); } - let account_id = T::AddressMapping::into_account_id(*who); + let imbalance = C::withdraw( &account_id, fee.unique_saturated_into(), WithdrawReasons::FEE, ExistenceRequirement::AllowDeath, ) - .map_err(|_| Error::::BalanceLow)?; + .map_err(|_| Error::::BalanceLow)?; Ok(Some(imbalance)) } @@ -1041,12 +1107,12 @@ where /// Implementation for () does not specify what to do with imbalance impl OnChargeEVMTransaction for () where - T: Config, - ::AccountId>>::PositiveImbalance: + T: Config, + ::AccountId>>::PositiveImbalance: Imbalance<::AccountId>>::Balance, Opposite = ::AccountId>>::NegativeImbalance>, - ::AccountId>>::NegativeImbalance: -Imbalance<::AccountId>>::Balance, Opposite = ::AccountId>>::PositiveImbalance>, -U256: UniqueSaturatedInto>, + ::AccountId>>::NegativeImbalance: + Imbalance<::AccountId>>::Balance, Opposite = ::AccountId>>::PositiveImbalance>, + U256: UniqueSaturatedInto>, { // Kept type as Option to satisfy bound of Default diff --git a/frame/evm/src/mock.rs b/frame/evm/src/mock.rs index c6529bd9b1..a2c136e1e5 100644 --- a/frame/evm/src/mock.rs +++ b/frame/evm/src/mock.rs @@ -155,6 +155,7 @@ impl crate::Config for Test { type GasLimitPovSizeRatio = GasLimitPovSizeRatio; type SuicideQuickClearLimit = SuicideQuickClearLimit; type Timestamp = Timestamp; + type EvmRentCalculator = (); type WeightInfo = (); } diff --git a/frame/evm/src/runner/stack.rs b/frame/evm/src/runner/stack.rs index 0850db17b1..de255a15fc 100644 --- a/frame/evm/src/runner/stack.rs +++ b/frame/evm/src/runner/stack.rs @@ -52,6 +52,8 @@ use crate::{ BalanceOf, BlockHashMapping, Config, Error, Event, FeeCalculator, OnChargeEVMTransaction, OnCreate, Pallet, RunnerError, }; +// Unique,BEVM +use crate::CurrentLogs; #[cfg(feature = "forbid-evm-reentrancy")] environmental::thread_local_impl!(static IN_EVM: environmental::RefCell = environmental::RefCell::new(false)); @@ -62,8 +64,8 @@ pub struct Runner { } impl Runner -where - BalanceOf: TryFrom + Into, + where + BalanceOf: TryFrom + Into, { #[allow(clippy::let_and_return)] /// Execute an already validated EVM operation. @@ -80,16 +82,16 @@ where proof_size_base_cost: Option, f: F, ) -> Result, RunnerError>> - where - F: FnOnce( - &mut StackExecutor< - 'config, - 'precompiles, - SubstrateStackState<'_, 'config, T>, - T::PrecompilesType, - >, - ) -> (ExitReason, R), - R: Default, + where + F: FnOnce( + &mut StackExecutor< + 'config, + 'precompiles, + SubstrateStackState<'_, 'config, T>, + T::PrecompilesType, + >, + ) -> (ExitReason, R), + R: Default, { let (base_fee, weight) = T::FeeCalculator::min_gas_price(); @@ -120,7 +122,7 @@ where // Set IN_EVM to false // We should make sure that this line is executed whatever the execution path. #[cfg(feature = "forbid-evm-reentrancy")] - let _ = IN_EVM.with(|in_evm| in_evm.take()); + let _ = IN_EVM.with(|in_evm| in_evm.take()); res } @@ -141,16 +143,16 @@ where weight_limit: Option, proof_size_base_cost: Option, ) -> Result, RunnerError>> - where - F: FnOnce( - &mut StackExecutor< - 'config, - 'precompiles, - SubstrateStackState<'_, 'config, T>, - T::PrecompilesType, - >, - ) -> (ExitReason, R), - R: Default, + where + F: FnOnce( + &mut StackExecutor< + 'config, + 'precompiles, + SubstrateStackState<'_, 'config, T>, + T::PrecompilesType, + >, + ) -> (ExitReason, R), + R: Default, { // Used to record the external costs in the evm through the StackState implementation let maybe_weight_info = @@ -175,7 +177,9 @@ where effective: gas_limit.into(), }, weight_info: maybe_weight_info, + /* Unique,BEVM logs: Default::default(), + */ }) } }; @@ -320,6 +324,7 @@ where Pallet::::remove_account(address) } + /* Unique,BEVM: logs are stored in storage for log in &state.substate.logs { log::trace!( target: "evm", @@ -338,6 +343,7 @@ where }, }); } + */ Ok(ExecutionInfoV2 { value: retv, @@ -347,14 +353,16 @@ where effective: effective_gas, }, weight_info: state.weight_info(), + /* Unique,BEVM: logs: state.substate.logs, + */ }) } } impl RunnerT for Runner -where - BalanceOf: TryFrom + Into, + where + BalanceOf: TryFrom + Into, { type Error = Error; @@ -400,10 +408,10 @@ where weight_limit, proof_size_base_cost, ) - .validate_in_block_for(&source_account) - .and_then(|v| v.with_base_fee()) - .and_then(|v| v.with_balance_for(&source_account)) - .map_err(|error| RunnerError { error, weight })?; + .validate_in_block_for(&source_account) + .and_then(|v| v.with_base_fee()) + .and_then(|v| v.with_balance_for(&source_account)) + .map_err(|error| RunnerError { error, weight })?; Ok(()) } @@ -571,14 +579,14 @@ where } } -struct SubstrateStackSubstate<'config> { +struct SubstrateStackSubstate<'config, T> { metadata: StackSubstateMetadata<'config>, deletes: BTreeSet, - logs: Vec, - parent: Option>>, + parent: Option>>, + _marker: PhantomData, } -impl<'config> SubstrateStackSubstate<'config> { +impl<'config, T: Config> SubstrateStackSubstate<'config, T> { pub fn metadata(&self) -> &StackSubstateMetadata<'config> { &self.metadata } @@ -592,7 +600,7 @@ impl<'config> SubstrateStackSubstate<'config> { metadata: self.metadata.spit_child(gas_limit, is_static), parent: None, deletes: BTreeSet::new(), - logs: Vec::new(), + _marker: PhantomData, }; mem::swap(&mut entering, self); @@ -606,7 +614,9 @@ impl<'config> SubstrateStackSubstate<'config> { mem::swap(&mut exited, self); self.metadata.swallow_commit(exited.metadata)?; + /* Unique,BEVM: self.logs.append(&mut exited.logs); + */ self.deletes.append(&mut exited.deletes); sp_io::storage::commit_transaction(); @@ -648,11 +658,30 @@ impl<'config> SubstrateStackSubstate<'config> { } pub fn log(&mut self, address: H160, topics: Vec, data: Vec) { + /* Unique,BEVM: self.logs.push(Log { address, topics, data, }); + */ + + let log = Log { + address, + topics, + data, + }; + log::trace!( + target: "evm", + "Inserting log for {:?}, topics ({}) {:?}, data ({}): {:?}]", + log.address, + log.topics.len(), + log.topics, + log.data.len(), + log.data + ); + >::append(&log); + >::deposit_event(Event::::Log { log }); } fn recursive_is_cold bool>(&self, f: &F) -> bool { @@ -677,7 +706,7 @@ pub struct Recorded { /// Substrate backend for EVM. pub struct SubstrateStackState<'vicinity, 'config, T> { vicinity: &'vicinity Vicinity, - substate: SubstrateStackSubstate<'config>, + substate: SubstrateStackSubstate<'config, T>, original_storage: BTreeMap<(H160, H256), H256>, recorded: Recorded, weight_info: Option, @@ -696,8 +725,11 @@ impl<'vicinity, 'config, T: Config> SubstrateStackState<'vicinity, 'config, T> { substate: SubstrateStackSubstate { metadata, deletes: BTreeSet::new(), + /* Unique: logs: Vec::new(), + */ parent: None, + _marker: PhantomData, }, _marker: PhantomData, original_storage: BTreeMap::new(), @@ -720,8 +752,8 @@ impl<'vicinity, 'config, T: Config> SubstrateStackState<'vicinity, 'config, T> { } impl<'vicinity, 'config, T: Config> BackendT for SubstrateStackState<'vicinity, 'config, T> -where - BalanceOf: TryFrom + Into, + where + BalanceOf: TryFrom + Into, { fn gas_price(&self) -> U256 { self.vicinity.gas_price @@ -805,9 +837,9 @@ where } impl<'vicinity, 'config, T: Config> StackStateT<'config> - for SubstrateStackState<'vicinity, 'config, T> -where - BalanceOf: TryFrom + Into, +for SubstrateStackState<'vicinity, 'config, T> + where + BalanceOf: TryFrom + Into, { fn metadata(&self) -> &StackSubstateMetadata<'config> { self.substate.metadata() @@ -882,7 +914,7 @@ where fn reset_storage(&mut self, address: H160) { #[allow(deprecated)] - let _ = >::remove_prefix(address, None); + let _ = >::remove_prefix(address, None); } fn log(&mut self, address: H160, topics: Vec, data: Vec) { @@ -915,7 +947,7 @@ where .map_err(|_| ExitError::OutOfFund)?, ExistenceRequirement::AllowDeath, ) - .map_err(|_| ExitError::OutOfFund) + .map_err(|_| ExitError::OutOfFund) } fn reset_balance(&mut self, _address: H160) { diff --git a/frame/hotfix-sufficients/Cargo.toml b/frame/hotfix-sufficients/Cargo.toml index ca6a2a6e7a..ed859fc51a 100644 --- a/frame/hotfix-sufficients/Cargo.toml +++ b/frame/hotfix-sufficients/Cargo.toml @@ -12,7 +12,7 @@ repository = { workspace = true } targets = ["x86_64-unknown-linux-gnu"] [dependencies] -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } scale-info = { workspace = true } # Substrate frame-benchmarking = { workspace = true, optional = true } diff --git a/precompiles/Cargo.toml b/precompiles/Cargo.toml index bb7f4359ef..0ad12e826b 100644 --- a/precompiles/Cargo.toml +++ b/precompiles/Cargo.toml @@ -23,7 +23,7 @@ precompile-utils-macro = { path = "macro" } # Substrate frame-support = { workspace = true } frame-system = { workspace = true } -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } sp-core = { workspace = true } sp-io = { workspace = true } sp-runtime = { workspace = true } diff --git a/precompiles/tests-external/Cargo.toml b/precompiles/tests-external/Cargo.toml index b113dd5145..7e546f4651 100644 --- a/precompiles/tests-external/Cargo.toml +++ b/precompiles/tests-external/Cargo.toml @@ -10,7 +10,7 @@ path = "./lib.rs" [dependencies] evm = { workspace = true, features = ["with-codec"] } hex-literal = { workspace = true } -scale-codec = { package = "parity-scale-codec", workspace = true, features = ["max-encoded-len"] } +scale-codec = { workspace = true, features = ["max-encoded-len"] } scale-info = { workspace = true, features = ["derive"] } # Substrate sp-core = { workspace = true } diff --git a/primitives/account/Cargo.toml b/primitives/account/Cargo.toml index 7fe7c853a4..f645623ad0 100644 --- a/primitives/account/Cargo.toml +++ b/primitives/account/Cargo.toml @@ -12,7 +12,7 @@ hex = { workspace = true } impl-serde = { workspace = true, optional = true } libsecp256k1 = { workspace = true } log = { workspace = true } -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } scale-info = { workspace = true } serde = { workspace = true, optional = true } diff --git a/primitives/bifrost/ext/Cargo.toml b/primitives/bifrost/ext/Cargo.toml new file mode 100644 index 0000000000..c95be2b70c --- /dev/null +++ b/primitives/bifrost/ext/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "fp-ext" +authors = ["bifrost-platform"] +edition = "2021" +homepage = "https://www.bifrostnetwork.com" +license = "Apache-2.0" +repository = "https://github.com/bifrost-platform/bifrost-frontier" +version = "0.1.0" + +[dependencies] +ethereum-types = { workspace = true } + +# Frontier +fp-rpc-evm-tracing-events = { workspace = true } + +# Substrate +scale-codec = { workspace = true } +sp-externalities = { workspace = true } +sp-runtime-interface = { workspace = true } +sp-std = { workspace = true } + +[features] +default = ["std"] +std = [ + "scale-codec/std", + "ethereum-types/std", + "fp-rpc-evm-tracing-events/std", + "sp-externalities/std", + "sp-runtime-interface/std", + "sp-std/std", +] diff --git a/primitives/bifrost/ext/src/lib.rs b/primitives/bifrost/ext/src/lib.rs new file mode 100644 index 0000000000..664f1bf01f --- /dev/null +++ b/primitives/bifrost/ext/src/lib.rs @@ -0,0 +1,66 @@ +//! Environmental-aware externalities for EVM tracing in Wasm runtime. This enables +//! capturing the - potentially large - trace output data in the host and keep +//! a low memory footprint in `--execution=wasm`. +//! +//! - The original trace Runtime Api call is wrapped `using` environmental (thread local). +//! - Arguments are scale-encoded known types in the host. +//! - Host functions will decode the input and emit an event `with` environmental. + +#![cfg_attr(not(feature = "std"), no_std)] +use sp_runtime_interface::runtime_interface; + +use scale_codec::Decode; +use sp_std::vec::Vec; + +use fp_rpc_evm_tracing_events::{Event, EvmEvent, GasometerEvent, RuntimeEvent, StepEventFilter}; + +#[runtime_interface] +pub trait TracingExt { + fn raw_step(&mut self, _data: Vec) {} + + fn raw_gas(&mut self, _data: Vec) {} + + fn raw_return_value(&mut self, _data: Vec) {} + + fn call_list_entry(&mut self, _index: u32, _value: Vec) {} + + fn call_list_new(&mut self) {} + + // New design, proxy events. + /// An `Evm` event proxied by the BIFROST runtime to this host function. + /// evm -> bifrost_runtime -> host. + fn evm_event(&mut self, event: Vec) { + if let Ok(event) = EvmEvent::decode(&mut &event[..]) { + Event::Evm(event).emit(); + } + } + + /// A `Gasometer` event proxied by the BIFROST runtime to this host function. + /// evm_gasometer -> bifrost_runtime -> host. + fn gasometer_event(&mut self, event: Vec) { + if let Ok(event) = GasometerEvent::decode(&mut &event[..]) { + Event::Gasometer(event).emit(); + } + } + + /// A `Runtime` event proxied by the BIFROST runtime to this host function. + /// evm_runtime -> bifrost_runtime -> host. + fn runtime_event(&mut self, event: Vec) { + if let Ok(event) = RuntimeEvent::decode(&mut &event[..]) { + Event::Runtime(event).emit(); + } + } + + /// Allow the tracing module in the runtime to know how to filter Step event + /// content, as cloning the entire data is expensive and most of the time + /// not necessary. + fn step_event_filter(&self) -> StepEventFilter { + fp_rpc_evm_tracing_events::step_event_filter().unwrap_or_default() + } + + /// An event to create a new CallList (currently a new transaction when tracing a block). + #[version(2)] + fn call_list_new(&mut self) { + Event::CallListNew().emit(); + } +} diff --git a/primitives/bifrost/rpc/debug/Cargo.toml b/primitives/bifrost/rpc/debug/Cargo.toml new file mode 100644 index 0000000000..ce00bd3c41 --- /dev/null +++ b/primitives/bifrost/rpc/debug/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "fp-rpc-debug" +authors = ["bifrost-platform"] +edition = "2021" +homepage = "https://www.bifrostnetwork.com" +license = "Apache-2.0" +repository = "https://github.com/bifrost-platform/bifrost-frontier" +version = "0.1.0" + +[dependencies] +environmental = { workspace = true } +ethereum = { workspace = true } +ethereum-types = { workspace = true } +hex = { workspace = true, optional = true, features = ["serde"] } +serde = { workspace = true, optional = true, features = ["derive"] } + +# Substrate +scale-codec = { workspace = true } +sp-api = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +[features] +default = ["std"] +std = [ + "scale-codec/std", + "environmental/std", + "ethereum-types/std", + "ethereum/std", + "hex", + "serde", + "sp-api/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/primitives/bifrost/rpc/debug/src/lib.rs b/primitives/bifrost/rpc/debug/src/lib.rs new file mode 100644 index 0000000000..c4c4bca0c4 --- /dev/null +++ b/primitives/bifrost/rpc/debug/src/lib.rs @@ -0,0 +1,50 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +use ethereum::{TransactionV0 as LegacyTransaction, TransactionV2 as Transaction}; +use ethereum_types::H256; +use scale_codec::{Decode, Encode}; +use sp_std::vec::Vec; + +sp_api::decl_runtime_apis! { + // Api version is virtually 4. + // + // We realized that even using runtime overrides, using the ApiExt interface reads the api + // versions from the state runtime, meaning we cannot just reset the versioning as we see fit. + // + // In order to be able to use ApiExt as part of the RPC handler logic we need to be always + // above the version that exists on chain for this Api, even if this Api is only meant + // to be used overridden. + #[api_version(4)] + pub trait DebugRuntimeApi { + #[changed_in(4)] + fn trace_transaction( + extrinsics: Vec, + transaction: &LegacyTransaction, + ) -> Result<(), sp_runtime::DispatchError>; + + fn trace_transaction( + extrinsics: Vec, + transaction: &Transaction, + ) -> Result<(), sp_runtime::DispatchError>; + + fn trace_block( + extrinsics: Vec, + known_transactions: Vec, + ) -> Result<(), sp_runtime::DispatchError>; + } +} + +#[derive(Clone, Copy, Eq, PartialEq, Debug, Encode, Decode)] +pub enum TracerInput { + None, + Blockscout, + CallTracer, +} + +/// DebugRuntimeApi V2 result. Trace response is stored in client and runtime api call response is +/// empty. +#[derive(Debug)] +pub enum Response { + Single, + Block, +} diff --git a/primitives/bifrost/rpc/evm-tracing-events/Cargo.toml b/primitives/bifrost/rpc/evm-tracing-events/Cargo.toml new file mode 100644 index 0000000000..960d482c9a --- /dev/null +++ b/primitives/bifrost/rpc/evm-tracing-events/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "fp-rpc-evm-tracing-events" +authors = ["bifrost-platform"] +edition = "2021" +homepage = "https://www.bifrostnetwork.com" +license = "Apache-2.0" +repository = "https://github.com/bifrost-platform/bifrost-frontier" +version = "0.1.0" + +[dependencies] +environmental = { workspace = true } + +# Substrate +scale-codec = { workspace = true } +sp-runtime-interface = { workspace = true } + +# Ethereum +ethereum = { workspace = true, features = ["with-codec"] } +ethereum-types = { workspace = true } +evm = { workspace = true, features = ["with-codec"] } +evm-gasometer = { workspace = true } +evm-runtime = { workspace = true } + +[features] +default = ["std"] +std = [ + "scale-codec/std", + "environmental/std", + "ethereum-types/std", + "ethereum/std", + "evm-gasometer/std", + "evm-runtime/std", + "evm/std", + "sp-runtime-interface/std", +] +evm-tracing = ["evm-gasometer/tracing", "evm-runtime/tracing", "evm/tracing"] diff --git a/primitives/bifrost/rpc/evm-tracing-events/src/evm.rs b/primitives/bifrost/rpc/evm-tracing-events/src/evm.rs new file mode 100644 index 0000000000..98f86b0d5f --- /dev/null +++ b/primitives/bifrost/rpc/evm-tracing-events/src/evm.rs @@ -0,0 +1,241 @@ +extern crate alloc; + +use alloc::vec::Vec; +use ethereum_types::{H160, H256, U256}; +use evm::ExitReason; +use scale_codec::{Decode, Encode}; + +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq)] +pub struct Transfer { + /// Source address. + pub source: H160, + /// Target address. + pub target: H160, + /// Transfer value. + pub value: U256, +} + +impl From for Transfer { + fn from(i: evm_runtime::Transfer) -> Self { + Self { + source: i.source, + target: i.target, + value: i.value, + } + } +} + +#[derive(Clone, Copy, Eq, PartialEq, Debug, Encode, Decode)] +pub enum CreateScheme { + /// Legacy create scheme of `CREATE`. + Legacy { + /// Caller of the create. + caller: H160, + }, + /// Create scheme of `CREATE2`. + Create2 { + /// Caller of the create. + caller: H160, + /// Code hash. + code_hash: H256, + /// Salt. + salt: H256, + }, + /// Create at a fixed location. + Fixed(H160), +} + +impl From for CreateScheme { + fn from(i: evm_runtime::CreateScheme) -> Self { + match i { + evm_runtime::CreateScheme::Legacy { caller } => Self::Legacy { caller }, + evm_runtime::CreateScheme::Create2 { + caller, + code_hash, + salt, + } => Self::Create2 { + caller, + code_hash, + salt, + }, + evm_runtime::CreateScheme::Fixed(address) => Self::Fixed(address), + } + } +} + +#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] +pub enum EvmEvent { + Call { + code_address: H160, + transfer: Option, + input: Vec, + target_gas: Option, + is_static: bool, + context: super::Context, + }, + Create { + caller: H160, + address: H160, + scheme: CreateScheme, + value: U256, + init_code: Vec, + target_gas: Option, + }, + Suicide { + address: H160, + target: H160, + balance: U256, + }, + Exit { + reason: ExitReason, + return_value: Vec, + }, + TransactCall { + caller: H160, + address: H160, + value: U256, + data: Vec, + gas_limit: u64, + }, + TransactCreate { + caller: H160, + value: U256, + init_code: Vec, + gas_limit: u64, + address: H160, + }, + TransactCreate2 { + caller: H160, + value: U256, + init_code: Vec, + salt: H256, + gas_limit: u64, + address: H160, + }, + PrecompileSubcall { + code_address: H160, + transfer: Option, + input: Vec, + target_gas: Option, + is_static: bool, + context: super::Context, + }, +} + +#[cfg(feature = "evm-tracing")] +impl<'a> From> for EvmEvent { + fn from(i: evm::tracing::Event<'a>) -> Self { + match i { + evm::tracing::Event::Call { + code_address, + transfer, + input, + target_gas, + is_static, + context, + } => Self::Call { + code_address, + transfer: if let Some(transfer) = transfer { + Some(transfer.clone().into()) + } else { + None + }, + input: input.to_vec(), + target_gas, + is_static, + context: context.clone().into(), + }, + evm::tracing::Event::Create { + caller, + address, + scheme, + value, + init_code, + target_gas, + } => Self::Create { + caller, + address, + scheme: scheme.into(), + value, + init_code: init_code.to_vec(), + target_gas, + }, + evm::tracing::Event::Suicide { + address, + target, + balance, + } => Self::Suicide { + address, + target, + balance, + }, + evm::tracing::Event::Exit { + reason, + return_value, + } => Self::Exit { + reason: reason.clone(), + return_value: return_value.to_vec(), + }, + evm::tracing::Event::TransactCall { + caller, + address, + value, + data, + gas_limit, + } => Self::TransactCall { + caller, + address, + value, + data: data.to_vec(), + gas_limit, + }, + evm::tracing::Event::TransactCreate { + caller, + value, + init_code, + gas_limit, + address, + } => Self::TransactCreate { + caller, + value, + init_code: init_code.to_vec(), + gas_limit, + address, + }, + evm::tracing::Event::TransactCreate2 { + caller, + value, + init_code, + salt, + gas_limit, + address, + } => Self::TransactCreate2 { + caller, + value, + init_code: init_code.to_vec(), + salt, + gas_limit, + address, + }, + evm::tracing::Event::PrecompileSubcall { + code_address, + transfer, + input, + target_gas, + is_static, + context, + } => Self::PrecompileSubcall { + code_address, + transfer: if let Some(transfer) = transfer { + Some(transfer.clone().into()) + } else { + None + }, + input: input.to_vec(), + target_gas, + is_static, + context: context.clone().into(), + }, + } + } +} diff --git a/primitives/bifrost/rpc/evm-tracing-events/src/gasometer.rs b/primitives/bifrost/rpc/evm-tracing-events/src/gasometer.rs new file mode 100644 index 0000000000..ec0ae5dbd3 --- /dev/null +++ b/primitives/bifrost/rpc/evm-tracing-events/src/gasometer.rs @@ -0,0 +1,98 @@ +use scale_codec::{Decode, Encode}; + +#[derive(Debug, Default, Copy, Clone, Encode, Decode, PartialEq, Eq)] +pub struct Snapshot { + pub gas_limit: u64, + pub memory_gas: u64, + pub used_gas: u64, + pub refunded_gas: i64, +} + +impl Snapshot { + pub fn gas(&self) -> u64 { + self.gas_limit - self.used_gas - self.memory_gas + } +} + +#[cfg(feature = "evm-tracing")] +impl From> for Snapshot { + fn from(i: Option) -> Self { + if let Some(i) = i { + Self { + gas_limit: i.gas_limit, + memory_gas: i.memory_gas, + used_gas: i.used_gas, + refunded_gas: i.refunded_gas, + } + } else { + Default::default() + } + } +} + +#[derive(Debug, Copy, Clone, Encode, Decode, PartialEq, Eq)] +pub enum GasometerEvent { + RecordCost { + cost: u64, + snapshot: Snapshot, + }, + RecordRefund { + refund: i64, + snapshot: Snapshot, + }, + RecordStipend { + stipend: u64, + snapshot: Snapshot, + }, + RecordDynamicCost { + gas_cost: u64, + memory_gas: u64, + gas_refund: i64, + snapshot: Snapshot, + }, + RecordTransaction { + cost: u64, + snapshot: Snapshot, + }, +} + +#[cfg(feature = "evm-tracing")] +impl From for GasometerEvent { + fn from(i: evm_gasometer::tracing::Event) -> Self { + match i { + evm_gasometer::tracing::Event::RecordCost { cost, snapshot } => Self::RecordCost { + cost, + snapshot: snapshot.into(), + }, + evm_gasometer::tracing::Event::RecordRefund { refund, snapshot } => { + Self::RecordRefund { + refund, + snapshot: snapshot.into(), + } + } + evm_gasometer::tracing::Event::RecordStipend { stipend, snapshot } => { + Self::RecordStipend { + stipend, + snapshot: snapshot.into(), + } + } + evm_gasometer::tracing::Event::RecordDynamicCost { + gas_cost, + memory_gas, + gas_refund, + snapshot, + } => Self::RecordDynamicCost { + gas_cost, + memory_gas, + gas_refund, + snapshot: snapshot.into(), + }, + evm_gasometer::tracing::Event::RecordTransaction { cost, snapshot } => { + Self::RecordTransaction { + cost, + snapshot: snapshot.into(), + } + } + } + } +} diff --git a/primitives/bifrost/rpc/evm-tracing-events/src/lib.rs b/primitives/bifrost/rpc/evm-tracing-events/src/lib.rs new file mode 100644 index 0000000000..a14e0da871 --- /dev/null +++ b/primitives/bifrost/rpc/evm-tracing-events/src/lib.rs @@ -0,0 +1,110 @@ +//! A Proxy in this context is an environmental trait implementor meant to be used for capturing +//! EVM trace events sent to a Host function from the Runtime. Works like: +//! - Runtime Api call `using` environmental. +//! - Runtime calls a Host function with some scale-encoded Evm event. +//! - Host function emits an additional event to this Listener. +//! - Proxy listens for the event and format the actual trace response. +//! +//! There are two proxy types: `Raw` and `CallList`. +//! - `Raw` - used for opcode-level traces. +//! - `CallList` - used for block tracing (stack of call stacks) and custom tracing outputs. +//! +//! The EVM event types may contain references and not implement Encode/Decode. +//! This module provide mirror types and conversion into them from the original events. + +#![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + +pub mod evm; +pub mod gasometer; +pub mod runtime; + +pub use self::evm::EvmEvent; +pub use gasometer::GasometerEvent; +pub use runtime::RuntimeEvent; + +use ethereum_types::{H160, U256}; +use scale_codec::{Decode, Encode}; +use sp_runtime_interface::pass_by::PassByCodec; + +environmental::environmental!(listener: dyn Listener + 'static); + +pub fn using R>(l: &mut (dyn Listener + 'static), f: F) -> R { + listener::using(l, f) +} + +/// Allow to configure which data of the Step event +/// we want to keep or discard. Not discarding the data requires cloning the data +/// in the runtime which have a significant cost for each step. +#[derive( + Clone, + Copy, + Eq, + PartialEq, + Debug, + Encode, + Decode, + Default, + PassByCodec +)] +pub struct StepEventFilter { + pub enable_stack: bool, + pub enable_memory: bool, +} + +#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode)] +pub enum Event { + Evm(evm::EvmEvent), + Gasometer(gasometer::GasometerEvent), + Runtime(runtime::RuntimeEvent), + CallListNew(), +} + +impl Event { + /// Access the global reference and call it's `event` method, passing the `Event` itself as + /// argument. + /// + /// This only works if we are `using` a global reference to a `Listener` implementor. + pub fn emit(self) { + listener::with(|listener| listener.event(self)); + } +} + +/// Main trait to proxy emitted messages. +/// Used 2 times : +/// - Inside the runtime to proxy the events through the host functions +/// - Inside the client to forward those events to the client listener. +pub trait Listener { + fn event(&mut self, event: Event); + + /// Allow the runtime to know which data should be discarded and not cloned. + /// WARNING: It is only called once when the runtime tracing is instantiated to avoid + /// performing many ext calls. + fn step_event_filter(&self) -> StepEventFilter; +} + +pub fn step_event_filter() -> Option { + let mut filter = None; + listener::with(|listener| filter = Some(listener.step_event_filter())); + filter +} + +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq)] +pub struct Context { + /// Execution address. + pub address: H160, + /// Caller of the EVM. + pub caller: H160, + /// Apparent value of the EVM. + pub apparent_value: U256, +} + +impl From for Context { + fn from(i: evm_runtime::Context) -> Self { + Self { + address: i.address, + caller: i.caller, + apparent_value: i.apparent_value, + } + } +} diff --git a/primitives/bifrost/rpc/evm-tracing-events/src/runtime.rs b/primitives/bifrost/rpc/evm-tracing-events/src/runtime.rs new file mode 100644 index 0000000000..c52301d02e --- /dev/null +++ b/primitives/bifrost/rpc/evm-tracing-events/src/runtime.rs @@ -0,0 +1,310 @@ +extern crate alloc; + +use super::Context; +use alloc::vec::Vec; +use ethereum_types::{H160, H256, U256}; +pub use evm::{ExitError, ExitReason, ExitSucceed, Opcode}; +use scale_codec::{Decode, Encode}; + +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq)] +pub struct Stack { + pub data: Vec, + pub limit: u64, +} + +impl From<&evm::Stack> for Stack { + fn from(i: &evm::Stack) -> Self { + Self { + data: i.data().clone(), + limit: i.limit() as u64, + } + } +} + +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq)] +pub struct Memory { + pub data: Vec, + pub effective_len: U256, + pub limit: u64, +} + +impl From<&evm::Memory> for Memory { + fn from(i: &evm::Memory) -> Self { + Self { + data: i.data().clone(), + effective_len: i.effective_len(), + limit: i.limit() as u64, + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Encode, Decode)] +pub enum Capture { + /// The machine has exited. It cannot be executed again. + Exit(E), + /// The machine has trapped. It is waiting for external information, and can + /// be executed again. + Trap(T), +} + +pub type Trap = Vec; // Should hold the marshalled Opcode. + +#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] +pub enum RuntimeEvent { + Step { + context: Context, + // This needs to be marshalled in the runtime no matter what. + opcode: Vec, + // We can use ExitReason with `with-codec` feature, + position: Result, + stack: Option, + memory: Option, + }, + StepResult { + result: Result<(), Capture>, + return_value: Vec, + }, + SLoad { + address: H160, + index: H256, + value: H256, + }, + SStore { + address: H160, + index: H256, + value: H256, + }, +} + +#[cfg(feature = "evm-tracing")] +impl RuntimeEvent { + pub fn from_evm_event<'a>( + i: evm_runtime::tracing::Event<'a>, + filter: crate::StepEventFilter, + ) -> Self { + match i { + evm_runtime::tracing::Event::Step { + context, + opcode, + position, + stack, + memory, + } => Self::Step { + context: context.clone().into(), + opcode: opcodes_string(opcode), + position: match position { + Ok(position) => Ok(*position as u64), + Err(e) => Err(e.clone()), + }, + stack: if filter.enable_stack { + Some(stack.into()) + } else { + None + }, + memory: if filter.enable_memory { + Some(memory.into()) + } else { + None + }, + }, + evm_runtime::tracing::Event::StepResult { + result, + return_value, + } => Self::StepResult { + result: match result { + Ok(_) => Ok(()), + Err(capture) => match capture { + evm::Capture::Exit(e) => Err(Capture::Exit(e.clone())), + evm::Capture::Trap(t) => Err(Capture::Trap(opcodes_string(*t))), + }, + }, + return_value: return_value.to_vec(), + }, + evm_runtime::tracing::Event::SLoad { + address, + index, + value, + } => Self::SLoad { + address, + index, + value, + }, + evm_runtime::tracing::Event::SStore { + address, + index, + value, + } => Self::SStore { + address, + index, + value, + }, + } + } +} + +#[cfg(feature = "evm-tracing")] +/// Converts an Opcode into its name, stored in a `Vec`. +pub fn opcodes_string(opcode: Opcode) -> Vec { + let tmp; + let out = match opcode { + Opcode(0) => "Stop", + Opcode(1) => "Add", + Opcode(2) => "Mul", + Opcode(3) => "Sub", + Opcode(4) => "Div", + Opcode(5) => "SDiv", + Opcode(6) => "Mod", + Opcode(7) => "SMod", + Opcode(8) => "AddMod", + Opcode(9) => "MulMod", + Opcode(10) => "Exp", + Opcode(11) => "SignExtend", + Opcode(16) => "Lt", + Opcode(17) => "Gt", + Opcode(18) => "Slt", + Opcode(19) => "Sgt", + Opcode(20) => "Eq", + Opcode(21) => "IsZero", + Opcode(22) => "And", + Opcode(23) => "Or", + Opcode(24) => "Xor", + Opcode(25) => "Not", + Opcode(26) => "Byte", + Opcode(27) => "Shl", + Opcode(28) => "Shr", + Opcode(29) => "Sar", + Opcode(32) => "Keccak256", + Opcode(48) => "Address", + Opcode(49) => "Balance", + Opcode(50) => "Origin", + Opcode(51) => "Caller", + Opcode(52) => "CallValue", + Opcode(53) => "CallDataLoad", + Opcode(54) => "CallDataSize", + Opcode(55) => "CallDataCopy", + Opcode(56) => "CodeSize", + Opcode(57) => "CodeCopy", + Opcode(58) => "GasPrice", + Opcode(59) => "ExtCodeSize", + Opcode(60) => "ExtCodeCopy", + Opcode(61) => "ReturnDataSize", + Opcode(62) => "ReturnDataCopy", + Opcode(63) => "ExtCodeHash", + Opcode(64) => "BlockHash", + Opcode(65) => "Coinbase", + Opcode(66) => "Timestamp", + Opcode(67) => "Number", + Opcode(68) => "Difficulty", + Opcode(69) => "GasLimit", + Opcode(70) => "ChainId", + Opcode(80) => "Pop", + Opcode(81) => "MLoad", + Opcode(82) => "MStore", + Opcode(83) => "MStore8", + Opcode(84) => "SLoad", + Opcode(85) => "SStore", + Opcode(86) => "Jump", + Opcode(87) => "JumpI", + Opcode(88) => "GetPc", + Opcode(89) => "MSize", + Opcode(90) => "Gas", + Opcode(91) => "JumpDest", + Opcode(96) => "Push1", + Opcode(97) => "Push2", + Opcode(98) => "Push3", + Opcode(99) => "Push4", + Opcode(100) => "Push5", + Opcode(101) => "Push6", + Opcode(102) => "Push7", + Opcode(103) => "Push8", + Opcode(104) => "Push9", + Opcode(105) => "Push10", + Opcode(106) => "Push11", + Opcode(107) => "Push12", + Opcode(108) => "Push13", + Opcode(109) => "Push14", + Opcode(110) => "Push15", + Opcode(111) => "Push16", + Opcode(112) => "Push17", + Opcode(113) => "Push18", + Opcode(114) => "Push19", + Opcode(115) => "Push20", + Opcode(116) => "Push21", + Opcode(117) => "Push22", + Opcode(118) => "Push23", + Opcode(119) => "Push24", + Opcode(120) => "Push25", + Opcode(121) => "Push26", + Opcode(122) => "Push27", + Opcode(123) => "Push28", + Opcode(124) => "Push29", + Opcode(125) => "Push30", + Opcode(126) => "Push31", + Opcode(127) => "Push32", + Opcode(128) => "Dup1", + Opcode(129) => "Dup2", + Opcode(130) => "Dup3", + Opcode(131) => "Dup4", + Opcode(132) => "Dup5", + Opcode(133) => "Dup6", + Opcode(134) => "Dup7", + Opcode(135) => "Dup8", + Opcode(136) => "Dup9", + Opcode(137) => "Dup10", + Opcode(138) => "Dup11", + Opcode(139) => "Dup12", + Opcode(140) => "Dup13", + Opcode(141) => "Dup14", + Opcode(142) => "Dup15", + Opcode(143) => "Dup16", + Opcode(144) => "Swap1", + Opcode(145) => "Swap2", + Opcode(146) => "Swap3", + Opcode(147) => "Swap4", + Opcode(148) => "Swap5", + Opcode(149) => "Swap6", + Opcode(150) => "Swap7", + Opcode(151) => "Swap8", + Opcode(152) => "Swap9", + Opcode(153) => "Swap10", + Opcode(154) => "Swap11", + Opcode(155) => "Swap12", + Opcode(156) => "Swap13", + Opcode(157) => "Swap14", + Opcode(158) => "Swap15", + Opcode(159) => "Swap16", + Opcode(160) => "Log0", + Opcode(161) => "Log1", + Opcode(162) => "Log2", + Opcode(163) => "Log3", + Opcode(164) => "Log4", + Opcode(176) => "JumpTo", + Opcode(177) => "JumpIf", + Opcode(178) => "JumpSub", + Opcode(180) => "JumpSubv", + Opcode(181) => "BeginSub", + Opcode(182) => "BeginData", + Opcode(184) => "ReturnSub", + Opcode(185) => "PutLocal", + Opcode(186) => "GetLocal", + Opcode(225) => "SLoadBytes", + Opcode(226) => "SStoreBytes", + Opcode(227) => "SSize", + Opcode(240) => "Create", + Opcode(241) => "Call", + Opcode(242) => "CallCode", + Opcode(243) => "Return", + Opcode(244) => "DelegateCall", + Opcode(245) => "Create2", + Opcode(250) => "StaticCall", + Opcode(252) => "TxExecGas", + Opcode(253) => "Revert", + Opcode(254) => "Invalid", + Opcode(255) => "SelfDestruct", + Opcode(n) => { + tmp = alloc::format!("Unknown({})", n); + &tmp + } + }; + out.as_bytes().to_vec() +} diff --git a/primitives/bifrost/rpc/txpool/Cargo.toml b/primitives/bifrost/rpc/txpool/Cargo.toml new file mode 100644 index 0000000000..7dac904db3 --- /dev/null +++ b/primitives/bifrost/rpc/txpool/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "fp-rpc-txpool" +authors = ["bifrost-platform"] +edition = "2021" +homepage = "https://www.bifrostnetwork.com" +license = "Apache-2.0" +repository = "https://github.com/bifrost-platform/bifrost-frontier" +version = "0.6.0" + +[dependencies] +ethereum = { workspace = true, features = ["with-codec"] } + +# Substrate +scale-codec = { workspace = true } +scale-info = { workspace = true, features = ["derive"] } +sp-api = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +[features] +default = ["std"] +std = [ + "ethereum/std", + "sp-api/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/primitives/bifrost/rpc/txpool/src/lib.rs b/primitives/bifrost/rpc/txpool/src/lib.rs new file mode 100644 index 0000000000..5f2596eb9d --- /dev/null +++ b/primitives/bifrost/rpc/txpool/src/lib.rs @@ -0,0 +1,36 @@ +#![cfg_attr(not(feature = "std"), no_std)] +// These clippy lints are disabled because the macro-generated code triggers them. +#![allow(clippy::unnecessary_mut_passed)] +#![allow(clippy::too_many_arguments)] + +pub use ethereum::{TransactionV0 as LegacyTransaction, TransactionV2 as Transaction}; +use scale_codec::{Decode, Encode}; +use sp_runtime::{scale_info::TypeInfo, traits::Block as BlockT, RuntimeDebug}; +use sp_std::vec::Vec; + +#[derive(Eq, PartialEq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct TxPoolResponseLegacy { + pub ready: Vec, + pub future: Vec, +} + +#[derive(Eq, PartialEq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct TxPoolResponse { + pub ready: Vec, + pub future: Vec, +} + +sp_api::decl_runtime_apis! { + #[api_version(2)] + pub trait TxPoolRuntimeApi { + #[changed_in(2)] + fn extrinsic_filter( + xt_ready: Vec<::Extrinsic>, + xt_future: Vec<::Extrinsic>, + ) -> TxPoolResponseLegacy; + fn extrinsic_filter( + xt_ready: Vec<::Extrinsic>, + xt_future: Vec<::Extrinsic>, + ) -> TxPoolResponse; + } +} diff --git a/primitives/consensus/Cargo.toml b/primitives/consensus/Cargo.toml index ec4072102f..4bc4d337e3 100644 --- a/primitives/consensus/Cargo.toml +++ b/primitives/consensus/Cargo.toml @@ -9,7 +9,7 @@ repository = { workspace = true } [dependencies] ethereum = { workspace = true, features = ["with-codec"] } -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } # Substrate sp-core = { workspace = true } sp-runtime = { workspace = true } diff --git a/primitives/ethereum/Cargo.toml b/primitives/ethereum/Cargo.toml index 9761ac232e..9a42523de1 100644 --- a/primitives/ethereum/Cargo.toml +++ b/primitives/ethereum/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] ethereum = { workspace = true, features = ["with-codec"] } ethereum-types = { workspace = true } -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } # Substrate frame-support = { workspace = true } sp-std = { workspace = true } diff --git a/primitives/evm/Cargo.toml b/primitives/evm/Cargo.toml index 9cbbd2c6de..c8fde6da33 100644 --- a/primitives/evm/Cargo.toml +++ b/primitives/evm/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] evm = { workspace = true, features = ["with-codec"] } num_enum = { workspace = true, default-features = false } -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } scale-info = { workspace = true } serde = { workspace = true, optional = true } # Substrate diff --git a/primitives/evm/src/lib.rs b/primitives/evm/src/lib.rs index ca0b59466a..633dc1427a 100644 --- a/primitives/evm/src/lib.rs +++ b/primitives/evm/src/lib.rs @@ -90,15 +90,15 @@ impl WeightInfo { Ok(match (weight_limit, proof_size_base_cost) { (None, _) => None, (Some(weight_limit), Some(proof_size_base_cost)) - if weight_limit.proof_size() >= proof_size_base_cost => - { - Some(WeightInfo { - ref_time_limit: Some(weight_limit.ref_time()), - proof_size_limit: Some(weight_limit.proof_size()), - ref_time_usage: Some(0u64), - proof_size_usage: Some(proof_size_base_cost), - }) - } + if weight_limit.proof_size() >= proof_size_base_cost => + { + Some(WeightInfo { + ref_time_limit: Some(weight_limit.ref_time()), + proof_size_limit: Some(weight_limit.proof_size()), + ref_time_usage: Some(0u64), + proof_size_usage: Some(proof_size_base_cost), + }) + } (Some(weight_limit), None) => Some(WeightInfo { ref_time_limit: Some(weight_limit.ref_time()), proof_size_limit: None, @@ -192,7 +192,9 @@ pub struct ExecutionInfoV2 { pub value: T, pub used_gas: UsedGas, pub weight_info: Option, + /* Unique,BEVM: pub logs: Vec, + */ } pub type CallInfo = ExecutionInfoV2>; @@ -211,7 +213,9 @@ pub struct ExecutionInfo { pub exit_reason: ExitReason, pub value: T, pub used_gas: U256, + /* Unique,BEVM: pub logs: Vec, + */ } /// Account definition used for genesis block construction. diff --git a/primitives/evm/src/validation.rs b/primitives/evm/src/validation.rs index 405f09a4b3..5b8c2f9696 100644 --- a/primitives/evm/src/validation.rs +++ b/primitives/evm/src/validation.rs @@ -78,6 +78,8 @@ pub enum TransactionValidationError { InvalidChainId, /// The transaction signature is invalid InvalidSignature, + /// Insufficient rent for the transaction + InsufficientRent, /// Unknown error #[num_enum(default)] UnknownError, @@ -260,6 +262,7 @@ mod tests { InvalidFeeInput, InvalidChainId, InvalidSignature, + InsufficientRent, UnknownError, } @@ -278,6 +281,7 @@ mod tests { TransactionValidationError::InvalidFeeInput => TestError::InvalidFeeInput, TransactionValidationError::InvalidChainId => TestError::InvalidChainId, TransactionValidationError::InvalidSignature => TestError::InvalidSignature, + TransactionValidationError::InsufficientRent => TestError::InsufficientRent, TransactionValidationError::UnknownError => TestError::UnknownError, } } diff --git a/primitives/rent/Cargo.toml b/primitives/rent/Cargo.toml new file mode 100644 index 0000000000..a614e8a72a --- /dev/null +++ b/primitives/rent/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "fp-rent" +version = "2.0.0-dev" +authors = ["Parity Technologies "] +description = "Primitives for EVM rent" +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/frontier/" + +[dependencies] +sp-core = { workspace = true } + +[features] +default = ["std"] +std = [ + "sp-core/std" +] diff --git a/primitives/rent/src/lib.rs b/primitives/rent/src/lib.rs new file mode 100644 index 0000000000..653d12b5de --- /dev/null +++ b/primitives/rent/src/lib.rs @@ -0,0 +1,25 @@ +//! # EVM Rent +#![cfg_attr(not(feature = "std"), no_std)] + +use sp_core::H160; + +pub trait EvmRentCalculator { + /// Calculate and charge (burn) the outstanding rent for an account, + /// then update its status map. + /// return rent value by Wei + fn process_rent(who: H160) -> u128; + /// Estimate the amount of rent to be burned and the corresponding + /// number of rented days for an account. + /// return rent value by Wei + fn estimate_rent(who: H160) -> (u128, u64); +} + +impl EvmRentCalculator for () { + fn process_rent(_who: H160) -> u128 { + 0u128 + } + + fn estimate_rent(_who: H160) -> (u128, u64) { + (0u128, 0u64) + } +} diff --git a/primitives/rpc/Cargo.toml b/primitives/rpc/Cargo.toml index 5850c5e9e3..08ecf728dc 100644 --- a/primitives/rpc/Cargo.toml +++ b/primitives/rpc/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] ethereum = { workspace = true, features = ["with-codec"] } ethereum-types = { workspace = true } -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } scale-info = { workspace = true } # Substrate sp-api = { workspace = true } diff --git a/primitives/self-contained/Cargo.toml b/primitives/self-contained/Cargo.toml index a41c5f3d97..634e10d98a 100644 --- a/primitives/self-contained/Cargo.toml +++ b/primitives/self-contained/Cargo.toml @@ -11,7 +11,7 @@ repository = { workspace = true } targets = ["x86_64-unknown-linux-gnu"] [dependencies] -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } scale-info = { workspace = true } serde = { workspace = true, optional = true } # Substrate diff --git a/primitives/storage/Cargo.toml b/primitives/storage/Cargo.toml index b5cf89f9a2..70c7bfc1d3 100644 --- a/primitives/storage/Cargo.toml +++ b/primitives/storage/Cargo.toml @@ -11,7 +11,7 @@ repository = { workspace = true } targets = ["x86_64-unknown-linux-gnu"] [dependencies] -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } serde = { workspace = true, optional = true } [features] diff --git a/runtime/evm-tracer/Cargo.toml b/runtime/evm-tracer/Cargo.toml new file mode 100644 index 0000000000..8ce330221b --- /dev/null +++ b/runtime/evm-tracer/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "evm-tracer" +authors = ["bifrost-platform"] +edition = "2021" +homepage = "https://thebifrost.io" +license = "Apache-2.0" +repository = "https://github.com/bifrost-platform/bifrost-frontier" +version = "0.1.0" + +[dependencies] +fp-ext = { workspace = true } +fp-rpc-evm-tracing-events = { workspace = true, features = ["evm-tracing"] } + +# Substrate +codec = { package = "parity-scale-codec", version = "3.6.9", features = ["std"] } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +# Frontier +ethereum-types = { workspace = true } +evm = { workspace = true } +evm-gasometer = { workspace = true } +evm-runtime = { workspace = true } +fp-evm = { workspace = true } +pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] } + +[features] +default = ["std"] +std = [ + "codec/std", + "ethereum-types/std", + "evm-gasometer/std", + "evm-runtime/std", + "evm/std", + "evm/with-serde", + "fp-rpc-evm-tracing-events/std", + "fp-evm/std", + "fp-ext/std", + "pallet-evm/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/runtime/evm-tracer/src/lib.rs b/runtime/evm-tracer/src/lib.rs new file mode 100644 index 0000000000..c559622719 --- /dev/null +++ b/runtime/evm-tracer/src/lib.rs @@ -0,0 +1,101 @@ +//! Substrate EVM tracing. +//! +//! The purpose of this crate is enable tracing the EVM opcode execution and will be used by +//! both Dapp developers - to get a granular view on their transactions - and indexers to access +//! the EVM callstack (internal transactions). +//! +//! Proxies EVM messages to the host functions. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod tracer { + use codec::Encode; + use fp_rpc_evm_tracing_events::{EvmEvent, GasometerEvent, RuntimeEvent, StepEventFilter}; + + use evm::tracing::{using as evm_using, EventListener as EvmListener}; + use evm_gasometer::tracing::{using as gasometer_using, EventListener as GasometerListener}; + use evm_runtime::tracing::{using as runtime_using, EventListener as RuntimeListener}; + use sp_std::{cell::RefCell, rc::Rc}; + + struct ListenerProxy(pub Rc>); + impl GasometerListener for ListenerProxy { + fn event(&mut self, event: evm_gasometer::tracing::Event) { + self.0.borrow_mut().event(event); + } + } + + impl RuntimeListener for ListenerProxy { + fn event(&mut self, event: evm_runtime::tracing::Event) { + self.0.borrow_mut().event(event); + } + } + + impl EvmListener for ListenerProxy { + fn event(&mut self, event: evm::tracing::Event) { + self.0.borrow_mut().event(event); + } + } + + pub struct EvmTracer { + step_event_filter: StepEventFilter, + } + + impl EvmTracer { + pub fn new() -> Self { + Self { + step_event_filter: fp_ext::tracing_ext::step_event_filter(), + } + } + + /// Setup event listeners and execute provided closure. + /// + /// Consume the tracer and return it alongside the return value of + /// the closure. + pub fn trace R>(self, f: F) { + let wrapped = Rc::new(RefCell::new(self)); + + let mut gasometer = ListenerProxy(Rc::clone(&wrapped)); + let mut runtime = ListenerProxy(Rc::clone(&wrapped)); + let mut evm = ListenerProxy(Rc::clone(&wrapped)); + + // Each line wraps the previous `f` into a `using` call. + // Listening to new events results in adding one new line. + // Order is irrelevant when registering listeners. + let f = || runtime_using(&mut runtime, f); + let f = || gasometer_using(&mut gasometer, f); + let f = || evm_using(&mut evm, f); + f(); + } + + pub fn emit_new() { + fp_ext::tracing_ext::call_list_new(); + } + } + + impl EvmListener for EvmTracer { + /// Proxies `evm::tracing::Event` to the host. + fn event(&mut self, event: evm::tracing::Event) { + let event: EvmEvent = event.into(); + let message = event.encode(); + fp_ext::tracing_ext::evm_event(message); + } + } + + impl GasometerListener for EvmTracer { + /// Proxies `evm_gasometer::tracing::Event` to the host. + fn event(&mut self, event: evm_gasometer::tracing::Event) { + let event: GasometerEvent = event.into(); + let message = event.encode(); + fp_ext::tracing_ext::gasometer_event(message); + } + } + + impl RuntimeListener for EvmTracer { + /// Proxies `evm_runtime::tracing::Event` to the host. + fn event(&mut self, event: evm_runtime::tracing::Event) { + let event = RuntimeEvent::from_evm_event(event, self.step_event_filter); + let message = event.encode(); + fp_ext::tracing_ext::runtime_event(message); + } + } +} diff --git a/template/node/Cargo.toml b/template/node/Cargo.toml index 6c3b7036fa..ccfc12190f 100644 --- a/template/node/Cargo.toml +++ b/template/node/Cargo.toml @@ -19,11 +19,12 @@ futures = { workspace = true } hex-literal = { workspace = true } jsonrpsee = { workspace = true, features = ["server", "macros"] } log = { workspace = true } -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } serde = { workspace = true } +tokio = { workspace = true, features = ["macros", "sync"] } # Substrate -prometheus-endpoint = { package = "substrate-prometheus-endpoint", workspace = true } +prometheus-endpoint = { workspace = true } sc-basic-authorship = { workspace = true } sc-chain-spec = { workspace = true } sc-cli = { workspace = true } @@ -64,6 +65,7 @@ substrate-frame-rpc-system = { workspace = true } frame-benchmarking = { workspace = true, optional = true } frame-benchmarking-cli = { workspace = true, optional = true } frame-system = { workspace = true } +pallet-ethereum = { workspace = true } pallet-transaction-payment = { workspace = true } # Frontier @@ -72,7 +74,7 @@ fc-cli = { workspace = true } fc-consensus = { workspace = true } fc-db = { workspace = true } fc-mapping-sync = { workspace = true } -fc-rpc = { workspace = true } +fc-rpc = { workspace = true, features = ["rpc-binary-search-estimate"] } fc-rpc-core = { workspace = true } fc-storage = { workspace = true } fp-account = { workspace = true } @@ -81,6 +83,13 @@ fp-evm = { workspace = true, features = ["default"] } fp-rpc = { workspace = true, features = ["default"] } frontier-template-runtime = { workspace = true, features = ["std"] } +fp-ext = { workspace = true, features = ["std"] } +fp-rpc-debug = { workspace = true, features = ["std"] } +fp-rpc-txpool = { workspace = true, features = ["std"] } +fc-rpc-debug = { workspace = true } +fc-rpc-trace = { workspace = true } +fc-rpc-txpool = { workspace = true } + [build-dependencies] substrate-build-script-utils = { workspace = true } @@ -89,7 +98,6 @@ default = [ "with-rocksdb-weights", "rocksdb", "sql", - "txpool", ] rocksdb = [ "sc-cli/rocksdb", @@ -105,11 +113,12 @@ sql = [ ] with-rocksdb-weights = ["frontier-template-runtime/with-rocksdb-weights"] with-paritydb-weights = ["frontier-template-runtime/with-paritydb-weights"] -txpool = ["fc-rpc/txpool"] rpc-binary-search-estimate = ["fc-rpc/rpc-binary-search-estimate"] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-benchmarking-cli/runtime-benchmarks", "sc-service/runtime-benchmarks", "frontier-template-runtime/runtime-benchmarks", + "pallet-ethereum/runtime-benchmarks", ] +evm-tracing = ["frontier-template-runtime/evm-tracing"] \ No newline at end of file diff --git a/template/node/src/benchmarking.rs b/template/node/src/benchmarking.rs index 58d09ca92e..fa74a24f62 100644 --- a/template/node/src/benchmarking.rs +++ b/template/node/src/benchmarking.rs @@ -151,6 +151,7 @@ pub fn create_benchmark_extrinsic( frame_system::CheckNonce::::from(nonce), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(0), + pallet_ethereum::FakeTransactionFinalizer::::new(), ); let raw_payload = runtime::SignedPayload::from_raw( @@ -165,6 +166,7 @@ pub fn create_benchmark_extrinsic( (), (), (), + (), ), ); let signature = raw_payload.using_encoded(|e| sender.sign(e)); diff --git a/template/node/src/client.rs b/template/node/src/client.rs index e4c9ee54a1..070750c481 100644 --- a/template/node/src/client.rs +++ b/template/node/src/client.rs @@ -19,7 +19,7 @@ pub type Client = FullClient PathBuf { config.base_path.config_dir(config.chain_spec.id()) } +#[derive(Debug, PartialEq, Clone)] +pub enum EthApi { + Txpool, + Debug, + Trace, +} + +impl FromStr for EthApi { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(match s { + "txpool" => Self::Txpool, + "debug" => Self::Debug, + "trace" => Self::Trace, + _ => return Err(format!("`{}` is not recognized as a supported Ethereum Api", s)), + }) + } +} + /// Avalailable frontier backend types. #[derive(Debug, Copy, Clone, Default, clap::ValueEnum)] pub enum BackendType { @@ -89,6 +110,32 @@ pub struct EthConfiguration { /// Default value is 200MB. #[arg(long, default_value = "209715200")] pub frontier_sql_backend_cache_size: u64, + + /// Enable EVM tracing & Txpool module. ex) --ethapi=debug,trace,txpool + #[arg(long, value_delimiter = ',')] + pub ethapi: Vec, + + /// Number of concurrent tracing tasks. + #[arg(long, default_value = "500")] + pub ethapi_max_permits: u32, + + /// Maximum number of trace entries a single request of `trace_filter` is allowed to return. + #[arg(long, default_value = "500")] + pub ethapi_trace_max_count: u32, + + /// Duration (in seconds) after which the cache of `trace_filter` for a given block will be + /// discarded. + #[arg(long, default_value = "300")] + pub ethapi_trace_cache_duration: u64, + + /// Size in bytes of data a raw tracing request is allowed to use. + /// Bound the size of memory, stack and storage data. + #[arg(long, default_value = "20000000")] + pub tracing_raw_max_memory_usage: usize, + + /// Timeout for eth logs query RPCs in seconds. (default 10) + #[arg(long, default_value = "10")] + pub logs_request_timeout: u64, } pub struct FrontierPartialComponents { @@ -112,6 +159,8 @@ pub trait EthCompatRuntimeApiCollection: sp_api::ApiExt + fp_rpc::ConvertTransactionRuntimeApi + fp_rpc::EthereumRuntimeRPCApi + + fp_rpc_debug::DebugRuntimeApi + + fp_rpc_txpool::TxPoolRuntimeApi { } @@ -119,6 +168,8 @@ impl EthCompatRuntimeApiCollection for Api where Api: sp_api::ApiExt + fp_rpc::ConvertTransactionRuntimeApi + fp_rpc::EthereumRuntimeRPCApi + + fp_rpc_debug::DebugRuntimeApi + + fp_rpc_txpool::TxPoolRuntimeApi { } diff --git a/template/node/src/main.rs b/template/node/src/main.rs index 94c4643c1b..803bcff5cc 100644 --- a/template/node/src/main.rs +++ b/template/node/src/main.rs @@ -17,6 +17,7 @@ mod command; mod eth; mod rpc; mod service; +mod tracing; fn main() -> sc_cli::Result<()> { command::run() diff --git a/template/node/src/rpc/eth.rs b/template/node/src/rpc/eth.rs index caf7e478ef..f212162955 100644 --- a/template/node/src/rpc/eth.rs +++ b/template/node/src/rpc/eth.rs @@ -10,9 +10,10 @@ use sc_client_api::{ use sc_network::NetworkService; use sc_network_sync::SyncingService; use sc_rpc::SubscriptionTaskExecutor; +use sc_service::TaskManager; use sc_transaction_pool::{ChainApi, Pool}; use sc_transaction_pool_api::TransactionPool; -use sp_api::{CallApiAt, ProvideRuntimeApi}; +use sp_api::{CallApiAt, HeaderT, ProvideRuntimeApi}; use sp_block_builder::BlockBuilder as BlockBuilderApi; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; use sp_consensus_aura::{sr25519::AuthorityId as AuraId, AuraApi}; @@ -53,6 +54,8 @@ pub struct EthDeps { pub filter_pool: Option, /// Maximum number of logs in a query. pub max_past_logs: u32, + /// Timeout for eth logs query in seconds. (default 10) + pub logs_request_timeout: u64, /// Fee history cache. pub fee_history_cache: FeeHistoryCache, /// Maximum fee history cache size. @@ -64,6 +67,24 @@ pub struct EthDeps { pub forced_parent_hashes: Option>, /// Something that can create the inherent data providers for pending state pub pending_create_inherent_data_providers: CIDP, + + pub tracing_config: TracingConfig, +} + +pub struct TracingSpawnTasksParams<'a, B: BlockT, C, BE> { + pub task_manager: &'a TaskManager, + pub client: Arc, + pub substrate_backend: Arc, + pub frontier_backend: fc_db::Backend, + pub filter_pool: Option, + pub overrides: Arc>, + pub fee_history_limit: u64, + pub fee_history_cache: FeeHistoryCache, +} + +pub struct TracingConfig { + pub tracing_requesters: crate::tracing::RpcRequesters, + pub trace_filter_max_count: u32, } /// Instantiate Ethereum-compatible RPC extensions. @@ -79,11 +100,14 @@ pub fn create_eth( ) -> Result, Box> where B: BlockT, + B::Header: HeaderT, C: CallApiAt + ProvideRuntimeApi, C::Api: AuraApi + BlockBuilderApi + ConvertTransactionRuntimeApi + EthereumRuntimeRPCApi, + C::Api: fp_rpc_debug::DebugRuntimeApi, + C::Api: fp_rpc_txpool::TxPoolRuntimeApi, C: HeaderBackend + HeaderMetadata, C: BlockchainEvents + AuxStore + UsageProvider + StorageProvider + 'static, BE: Backend + 'static, @@ -98,8 +122,9 @@ where EthFilterApiServer, EthPubSub, EthPubSubApiServer, EthSigner, Net, NetApiServer, Web3, Web3ApiServer, }; - #[cfg(feature = "txpool")] - use fc_rpc::{TxPool, TxPoolApiServer}; + use fc_rpc_debug::{Debug, DebugServer}; + use fc_rpc_trace::{Trace, TraceServer}; + use fc_rpc_txpool::{TxPool, TxPoolServer}; let EthDeps { client, @@ -115,11 +140,13 @@ where block_data_cache, filter_pool, max_past_logs, + logs_request_timeout, fee_history_cache, fee_history_cache_limit, execute_gas_limit_multiplier, forced_parent_hashes, pending_create_inherent_data_providers, + tracing_config } = deps; let mut signers = Vec::new(); @@ -159,12 +186,30 @@ where filter_pool, 500_usize, // max stored filters max_past_logs, + logs_request_timeout, block_data_cache, ) .into_rpc(), )?; } + if let Some(trace_filter_requester) = tracing_config.tracing_requesters.trace { + io.merge( + Trace::new(client.clone(), trace_filter_requester, tracing_config.trace_filter_max_count) + .into_rpc(), + ) + .ok(); + } + + if let Some(debug_requester) = tracing_config.tracing_requesters.debug { + io.merge(Debug::new(debug_requester).into_rpc()).ok(); + } + + io.merge( + TxPool::new(Arc::clone(&client), + graph.clone()).into_rpc() + ).ok(); + io.merge( EthPubSub::new( pool, @@ -189,8 +234,5 @@ where io.merge(Web3::new(client.clone()).into_rpc())?; - #[cfg(feature = "txpool")] - io.merge(TxPool::new(client, graph).into_rpc())?; - Ok(io) } diff --git a/template/node/src/rpc/mod.rs b/template/node/src/rpc/mod.rs index b655b1e890..a7747ed98a 100644 --- a/template/node/src/rpc/mod.rs +++ b/template/node/src/rpc/mod.rs @@ -24,7 +24,7 @@ use sp_runtime::traits::Block as BlockT; use frontier_template_runtime::{opaque::Block, AccountId, Balance, Hash, Nonce}; mod eth; -pub use self::eth::{create_eth, overrides_handle, EthDeps}; +pub use self::eth::{create_eth, overrides_handle, EthDeps, TracingConfig, TracingSpawnTasksParams}; /// Full client dependencies. pub struct FullDeps { @@ -70,6 +70,8 @@ where C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, C::Api: fp_rpc::ConvertTransactionRuntimeApi, C::Api: fp_rpc::EthereumRuntimeRPCApi, + C::Api: fp_rpc_debug::DebugRuntimeApi, + C::Api: fp_rpc_txpool::TxPoolRuntimeApi, C: HeaderBackend + HeaderMetadata + 'static, C: BlockchainEvents + AuxStore + UsageProvider + StorageProvider, BE: Backend + 'static, diff --git a/template/node/src/service.rs b/template/node/src/service.rs index 96a20c0d69..4ef8adeccf 100644 --- a/template/node/src/service.rs +++ b/template/node/src/service.rs @@ -24,7 +24,10 @@ use crate::{ eth::{ new_frontier_partial, spawn_frontier_tasks, BackendType, EthCompatRuntimeApiCollection, FrontierBackend, FrontierBlockImport, FrontierPartialComponents, + EthApi as EthApiCmd }, + tracing::{spawn_tracing_tasks, RpcRequesters}, + rpc::{TracingConfig, TracingSpawnTasksParams} }; pub use crate::{ client::{Client, TemplateRuntimeExecutor}, @@ -367,6 +370,28 @@ where // for ethereum-compatibility rpc. config.rpc_id_provider = Some(Box::new(fc_rpc::EthereumSubIdProvider)); + let ethapi_cmd = eth_config.ethapi.clone(); + let tracing_requesters: RpcRequesters = { + if ethapi_cmd.contains(&EthApiCmd::Debug) || ethapi_cmd.contains(&EthApiCmd::Trace) { + spawn_tracing_tasks( + ð_config, + prometheus_registry.clone(), + TracingSpawnTasksParams { + task_manager: &task_manager, + client: client.clone(), + substrate_backend: backend.clone(), + frontier_backend: frontier_backend.clone(), + filter_pool: filter_pool.clone(), + overrides: overrides.clone(), + fee_history_limit: eth_config.fee_history_limit, + fee_history_cache: fee_history_cache.clone(), + }, + ) + } else { + RpcRequesters { debug: None, trace: None } + } + }; + let rpc_builder = { let client = client.clone(); let pool = transaction_pool.clone(); @@ -376,6 +401,7 @@ where 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 logs_request_timeout = eth_config.logs_request_timeout; let execute_gas_limit_multiplier = eth_config.execute_gas_limit_multiplier; let filter_pool = filter_pool.clone(); let frontier_backend = frontier_backend.clone(); @@ -422,11 +448,16 @@ where block_data_cache: block_data_cache.clone(), filter_pool: filter_pool.clone(), max_past_logs, + logs_request_timeout, fee_history_cache: fee_history_cache.clone(), fee_history_cache_limit, execute_gas_limit_multiplier, forced_parent_hashes: None, pending_create_inherent_data_providers, + tracing_config: TracingConfig { + tracing_requesters: tracing_requesters.clone(), + trace_filter_max_count: eth_config.ethapi_trace_max_count, + } }; let deps = crate::rpc::FullDeps { client: client.clone(), diff --git a/template/node/src/tracing.rs b/template/node/src/tracing.rs new file mode 100644 index 0000000000..67b9de53ca --- /dev/null +++ b/template/node/src/tracing.rs @@ -0,0 +1,103 @@ +#![allow(missing_docs)] + +use crate::{ + eth::{EthApi as EthApiCmd, EthConfiguration as RpcConfig}, + rpc::TracingSpawnTasksParams, +}; + +use fc_rpc_debug::{DebugHandler, DebugRequester}; +use fc_rpc_trace::{CacheRequester as TraceFilterCacheRequester, CacheTask}; +use fp_rpc::EthereumRuntimeRPCApi; +use sc_client_api::{backend::Backend, BlockOf, BlockchainEvents, StateBackend, StorageProvider}; +use prometheus_endpoint::Registry as PrometheusRegistry; +use tokio::sync::Semaphore; + +use sp_api::{BlockT, HeaderT, ProvideRuntimeApi}; +use sp_block_builder::BlockBuilder; +use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; +use sp_core::H256; +use sp_runtime::traits::BlakeTwo256; + +use std::{sync::Arc, time::Duration}; + +#[derive(Clone)] +pub struct RpcRequesters { + pub debug: Option, + pub trace: Option, +} + +// Spawn the tasks that are required to run a Bifrost tracing node. +pub fn spawn_tracing_tasks( + rpc_config: &RpcConfig, + prometheus: Option, + params: TracingSpawnTasksParams, +) -> RpcRequesters +where + C: ProvideRuntimeApi + BlockOf, + C: StorageProvider, + C: HeaderBackend + HeaderMetadata + 'static, + C: BlockchainEvents, + C: Send + Sync + 'static, + C::Api: EthereumRuntimeRPCApi + fp_rpc_debug::DebugRuntimeApi + fp_rpc_txpool::TxPoolRuntimeApi, + C::Api: BlockBuilder, + B: BlockT + Send + Sync + 'static, + B::Header: HeaderT, + BE: Backend + 'static, + BE::State: StateBackend, +{ + let permit_pool = Arc::new(Semaphore::new(rpc_config.ethapi_max_permits as usize)); + + let (trace_filter_task, trace_filter_requester) = + if rpc_config.ethapi.contains(&EthApiCmd::Trace) { + let (trace_filter_task, trace_filter_requester) = CacheTask::create( + Arc::clone(¶ms.client), + Arc::clone(¶ms.substrate_backend), + Duration::from_secs(rpc_config.ethapi_trace_cache_duration), + Arc::clone(&permit_pool), + Arc::clone(¶ms.overrides), + prometheus, + ); + (Some(trace_filter_task), Some(trace_filter_requester)) + } else { + (None, None) + }; + + let (debug_task, debug_requester) = if rpc_config.ethapi.contains(&EthApiCmd::Debug) { + let (debug_task, debug_requester) = DebugHandler::task( + Arc::clone(¶ms.client), + Arc::clone(¶ms.substrate_backend), + match params.frontier_backend { + fc_db::Backend::KeyValue(b) => Arc::new(b), + fc_db::Backend::Sql(b) => Arc::new(b), + }, + Arc::clone(&permit_pool), + Arc::clone(¶ms.overrides), + rpc_config.tracing_raw_max_memory_usage, + ); + (Some(debug_task), Some(debug_requester)) + } else { + (None, None) + }; + + // `trace_filter` cache task. Essential. + // Proxies rpc requests to it's handler. + if let Some(trace_filter_task) = trace_filter_task { + params.task_manager.spawn_essential_handle().spawn( + "trace-filter-cache", + Some("eth-tracing"), + trace_filter_task, + ); + } + + // `debug` task if enabled. Essential. + // Proxies rpc requests to it's handler. + if let Some(debug_task) = debug_task { + params.task_manager.spawn_essential_handle().spawn( + "ethapi-debug", + Some("eth-tracing"), + debug_task, + ); + } + + RpcRequesters { debug: debug_requester, trace: trace_filter_requester } +} diff --git a/template/runtime/Cargo.toml b/template/runtime/Cargo.toml index 5547dee3b9..297f410e7d 100644 --- a/template/runtime/Cargo.toml +++ b/template/runtime/Cargo.toml @@ -12,7 +12,7 @@ repository = { workspace = true } targets = ["x86_64-unknown-linux-gnu"] [dependencies] -scale-codec = { package = "parity-scale-codec", workspace = true } +scale-codec = { workspace = true } scale-info = { workspace = true } # Substrate @@ -21,6 +21,7 @@ sp-block-builder = { workspace = true } sp-consensus-aura = { workspace = true } sp-core = { workspace = true } sp-inherents = { workspace = true } +sp-io = { workspace = true } sp-offchain = { workspace = true } sp-runtime = { workspace = true } sp-session = { workspace = true } @@ -57,6 +58,12 @@ pallet-evm-precompile-modexp = { workspace = true } pallet-evm-precompile-sha3fips = { workspace = true } pallet-evm-precompile-simple = { workspace = true } pallet-hotfix-sufficients = { workspace = true } +pallet-evm-rent = { workspace = true } + +fp-rpc-debug = { workspace = true } +fp-rpc-txpool = { workspace = true } +fp-rpc-evm-tracing-events = { workspace = true, optional = true } +evm-tracer = { workspace = true, optional = true } [build-dependencies] substrate-wasm-builder = { workspace = true, optional = true } @@ -100,6 +107,12 @@ std = [ "fp-evm/std", "fp-rpc/std", "fp-self-contained/std", + + "fp-rpc-debug/std", + "fp-rpc-txpool/std", + "evm-tracer/std", + "fp-rpc-evm-tracing-events/std", + # Frontier FRAME "pallet-base-fee/std", "pallet-dynamic-fee/std", @@ -110,6 +123,7 @@ std = [ "pallet-evm-precompile-sha3fips/std", "pallet-evm-precompile-simple/std", "pallet-hotfix-sufficients/std", + "pallet-evm-rent/std", ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", @@ -123,3 +137,10 @@ runtime-benchmarks = [ "pallet-evm/runtime-benchmarks", "pallet-hotfix-sufficients/runtime-benchmarks", ] + +evm-tracing = [ + "evm-tracer", + "sp-io/disable_oom", + "sp-io/disable_panic_handler", + "fp-rpc-evm-tracing-events", +] \ No newline at end of file diff --git a/template/runtime/src/lib.rs b/template/runtime/src/lib.rs index bdd6105c3d..364d7b07e9 100644 --- a/template/runtime/src/lib.rs +++ b/template/runtime/src/lib.rs @@ -38,6 +38,7 @@ use frame_support::{ traits::{ConstBool, ConstU32, ConstU8, FindAuthor, OnFinalize, OnTimestampSet}, weights::{constants::WEIGHT_REF_TIME_PER_MILLIS, IdentityFee, Weight}, }; +use frame_system::EnsureRoot; use pallet_grandpa::{ fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, }; @@ -349,6 +350,7 @@ impl pallet_evm::Config for Runtime { type GasLimitPovSizeRatio = GasLimitPovSizeRatio; type SuicideQuickClearLimit = SuicideQuickClearLimit; type Timestamp = Timestamp; + type EvmRentCalculator = EvmRent; type WeightInfo = pallet_evm::weights::SubstrateWeight; } @@ -401,6 +403,11 @@ impl pallet_hotfix_sufficients::Config for Runtime { type WeightInfo = pallet_hotfix_sufficients::weights::SubstrateWeight; } +impl pallet_evm_rent::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type CouncilOrigin = EnsureRoot; +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime { @@ -417,6 +424,7 @@ construct_runtime!( DynamicFee: pallet_dynamic_fee, BaseFee: pallet_base_fee, HotfixSufficients: pallet_hotfix_sufficients, + EvmRent: pallet_evm_rent, } ); @@ -465,6 +473,7 @@ pub type SignedExtra = ( frame_system::CheckNonce, frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, + pallet_ethereum::FakeTransactionFinalizer, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = @@ -826,6 +835,103 @@ impl_runtime_apis! { } } + impl fp_rpc_debug::DebugRuntimeApi for Runtime { + fn trace_transaction( + extrinsics: Vec<::Extrinsic>, + traced_transaction: &EthereumTransaction, + ) -> Result< + (), + sp_runtime::DispatchError, + > { + #[cfg(feature = "evm-tracing")] + { + use evm_tracer::tracer::EvmTracer; + // Apply the a subset of extrinsics: all the substrate-specific or ethereum + // transactions that preceded the requested transaction. + for ext in extrinsics.into_iter() { + let _ = match &ext.0.function { + RuntimeCall::Ethereum(transact { transaction }) => { + if transaction == traced_transaction { + EvmTracer::new().trace(|| Executive::apply_extrinsic(ext)); + return Ok(()); + } else { + Executive::apply_extrinsic(ext) + } + } + _ => Executive::apply_extrinsic(ext), + }; + } + Err(sp_runtime::DispatchError::Other( + "Failed to find Ethereum transaction among the extrinsics.", + )) + } + #[cfg(not(feature = "evm-tracing"))] + Err(sp_runtime::DispatchError::Other( + "Missing `evm-tracing` compile time feature flag.", + )) + } + fn trace_block( + extrinsics: Vec<::Extrinsic>, + known_transactions: Vec, + ) -> Result< + (), + sp_runtime::DispatchError, + > { + #[cfg(feature = "evm-tracing")] + { + use evm_tracer::tracer::EvmTracer; + let mut config = ::config().clone(); + config.estimate = true; + // Apply all extrinsics. Ethereum extrinsics are traced. + for ext in extrinsics.into_iter() { + match &ext.0.function { + RuntimeCall::Ethereum(transact { transaction }) => { + if known_transactions.contains(&transaction.hash()) { + // Each known extrinsic is a new call stack. + EvmTracer::emit_new(); + EvmTracer::new().trace(|| Executive::apply_extrinsic(ext)); + } else { + let _ = Executive::apply_extrinsic(ext); + } + } + _ => { + let _ = Executive::apply_extrinsic(ext); + } + }; + } + Ok(()) + } + #[cfg(not(feature = "evm-tracing"))] + Err(sp_runtime::DispatchError::Other( + "Missing `evm-tracing` compile time feature flag.", + )) + } + } + + impl fp_rpc_txpool::TxPoolRuntimeApi for Runtime { + fn extrinsic_filter( + xts_ready: Vec<::Extrinsic>, + xts_future: Vec<::Extrinsic>, + ) -> fp_rpc_txpool::TxPoolResponse { + fp_rpc_txpool::TxPoolResponse { + ready: xts_ready + .into_iter() + .filter_map(|xt| match xt.0.function { + RuntimeCall::Ethereum(transact { transaction }) => Some(transaction), + _ => None, + }) + .collect(), + future: xts_future + .into_iter() + .filter_map(|xt| match xt.0.function { + RuntimeCall::Ethereum(transact { transaction }) => Some(transaction), + _ => None, + }) + .collect(), + } + } + } + impl fp_rpc::ConvertTransactionRuntimeApi for Runtime { fn convert_transaction(transaction: EthereumTransaction) -> ::Extrinsic { UncheckedExtrinsic::new_unsigned( diff --git a/ts-tests/package-lock.json b/ts-tests/package-lock.json index 93fce34dd9..2023a7f09a 100644 --- a/ts-tests/package-lock.json +++ b/ts-tests/package-lock.json @@ -9,6 +9,8 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@polkadot/api": "10.4.1", + "@polkadot/util-crypto": "12.2.2", "@types/chai": "^4.3.5", "@types/mocha": "^10.0.1", "chai": "^4.3.7", @@ -1305,6 +1307,28 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@noble/curves": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", + "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", + "dependencies": { + "@noble/hashes": "1.3.1" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@noble/hashes": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz", @@ -1336,6 +1360,848 @@ "node": ">=14" } }, + "node_modules/@polkadot/api": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-10.4.1.tgz", + "integrity": "sha512-MFIhykqvU4l5oyLxEnMhtF07VWLXwBS02gVd9Q+Gh2RxXtskQnXlos00hZFdLaCQ7g8BNggoq0Woc2BGZQEG2A==", + "dependencies": { + "@polkadot/api-augment": "10.4.1", + "@polkadot/api-base": "10.4.1", + "@polkadot/api-derive": "10.4.1", + "@polkadot/keyring": "^12.0.1", + "@polkadot/rpc-augment": "10.4.1", + "@polkadot/rpc-core": "10.4.1", + "@polkadot/rpc-provider": "10.4.1", + "@polkadot/types": "10.4.1", + "@polkadot/types-augment": "10.4.1", + "@polkadot/types-codec": "10.4.1", + "@polkadot/types-create": "10.4.1", + "@polkadot/types-known": "10.4.1", + "@polkadot/util": "^12.0.1", + "@polkadot/util-crypto": "^12.0.1", + "eventemitter3": "^5.0.0", + "rxjs": "^7.8.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/api-augment": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-10.4.1.tgz", + "integrity": "sha512-7TUFHiOsaAKwRCDXEwwYHIBi07Nd0+4vMDgP+/eKVrX5XkYCnEA+PdkE8uEpFY3LMoVVr08mPiYPn59gN0b5jw==", + "dependencies": { + "@polkadot/api-base": "10.4.1", + "@polkadot/rpc-augment": "10.4.1", + "@polkadot/types": "10.4.1", + "@polkadot/types-augment": "10.4.1", + "@polkadot/types-codec": "10.4.1", + "@polkadot/util": "^12.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/api-augment/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/api-base": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-base/-/api-base-10.4.1.tgz", + "integrity": "sha512-4uItv9NeZDu60qN8dhPN8Q7HBOYZ0Ylf7+/lAnEMldoCx0RxNP2h1T2JwdW3C5ElTsztYzYVLKg0Iz5zJpbffQ==", + "dependencies": { + "@polkadot/rpc-core": "10.4.1", + "@polkadot/types": "10.4.1", + "@polkadot/util": "^12.0.1", + "rxjs": "^7.8.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/api-base/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/api-derive": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-10.4.1.tgz", + "integrity": "sha512-SR/L4H8oiYMyLaLzmm9Z3kb4tNLAOAIR2uBZshCo917CqMuvrkNVW+Wfh2dAtVsnoSc1FaSPfDwmKash6pqNUQ==", + "dependencies": { + "@polkadot/api": "10.4.1", + "@polkadot/api-augment": "10.4.1", + "@polkadot/api-base": "10.4.1", + "@polkadot/rpc-core": "10.4.1", + "@polkadot/types": "10.4.1", + "@polkadot/types-codec": "10.4.1", + "@polkadot/util": "^12.0.1", + "@polkadot/util-crypto": "^12.0.1", + "rxjs": "^7.8.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/api-derive/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/api/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, + "node_modules/@polkadot/api/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/keyring": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-12.6.2.tgz", + "integrity": "sha512-O3Q7GVmRYm8q7HuB3S0+Yf/q/EB2egKRRU3fv9b3B7V+A52tKzA+vIwEmNVaD1g5FKW9oB97rmpggs0zaKFqHw==", + "dependencies": { + "@polkadot/util": "12.6.2", + "@polkadot/util-crypto": "12.6.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "12.6.2", + "@polkadot/util-crypto": "12.6.2" + } + }, + "node_modules/@polkadot/keyring/node_modules/@noble/curves": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.0.tgz", + "integrity": "sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@polkadot/keyring/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@polkadot/keyring/node_modules/@polkadot/util-crypto": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-12.6.2.tgz", + "integrity": "sha512-FEWI/dJ7wDMNN1WOzZAjQoIcCP/3vz3wvAp5QQm+lOrzOLj0iDmaIGIcBkz8HVm3ErfSe/uKP0KS4jgV/ib+Mg==", + "dependencies": { + "@noble/curves": "^1.3.0", + "@noble/hashes": "^1.3.3", + "@polkadot/networks": "12.6.2", + "@polkadot/util": "12.6.2", + "@polkadot/wasm-crypto": "^7.3.2", + "@polkadot/wasm-util": "^7.3.2", + "@polkadot/x-bigint": "12.6.2", + "@polkadot/x-randomvalues": "12.6.2", + "@scure/base": "^1.1.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "12.6.2" + } + }, + "node_modules/@polkadot/keyring/node_modules/@scure/base": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.6.tgz", + "integrity": "sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@polkadot/keyring/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/networks": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-12.6.2.tgz", + "integrity": "sha512-1oWtZm1IvPWqvMrldVH6NI2gBoCndl5GEwx7lAuQWGr7eNL+6Bdc5K3Z9T0MzFvDGoi2/CBqjX9dRKo39pDC/w==", + "dependencies": { + "@polkadot/util": "12.6.2", + "@substrate/ss58-registry": "^1.44.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/networks/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/rpc-augment": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-10.4.1.tgz", + "integrity": "sha512-lwwSjyboTTDzrfmBxFgBq4ZcD8mhMKkofmVqS/ntvuJePrCnBPGQG9d+tfntizIO5OpCqFGk0lgYBwae+oQ+vw==", + "dependencies": { + "@polkadot/rpc-core": "10.4.1", + "@polkadot/types": "10.4.1", + "@polkadot/types-codec": "10.4.1", + "@polkadot/util": "^12.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/rpc-augment/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/rpc-core": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-10.4.1.tgz", + "integrity": "sha512-jE3dBzfOFkiROMsiXpge/njvwPDCXF990VPFtlFqVWpr9ypXY7eZT1gduanN6YkXRjzcBVVPHaPfWLejie6hZA==", + "dependencies": { + "@polkadot/rpc-augment": "10.4.1", + "@polkadot/rpc-provider": "10.4.1", + "@polkadot/types": "10.4.1", + "@polkadot/util": "^12.0.1", + "rxjs": "^7.8.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/rpc-core/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/rpc-provider": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-10.4.1.tgz", + "integrity": "sha512-DwXs5dB1hp0P17I/Aqg+BnILQRMWlmMlABfSwwPprOh+16BBDw4P7bEuvaYw3gl/Zrxt4UhDcPl48jD+4j5OrQ==", + "dependencies": { + "@polkadot/keyring": "^12.0.1", + "@polkadot/types": "10.4.1", + "@polkadot/types-support": "10.4.1", + "@polkadot/util": "^12.0.1", + "@polkadot/util-crypto": "^12.0.1", + "@polkadot/x-fetch": "^12.0.1", + "@polkadot/x-global": "^12.0.1", + "@polkadot/x-ws": "^12.0.1", + "eventemitter3": "^5.0.0", + "mock-socket": "^9.2.1", + "nock": "^13.3.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "@substrate/connect": "0.7.24" + } + }, + "node_modules/@polkadot/rpc-provider/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, + "node_modules/@polkadot/rpc-provider/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/types": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-10.4.1.tgz", + "integrity": "sha512-w4spUa4CMLoPuIHOANR8X5F/Hfs4b4OnxsofA9SOl2aaQNRgu77Qu0y60rReu8HxBKD/FUBJJmRpZeY/2WQAAQ==", + "dependencies": { + "@polkadot/keyring": "^12.0.1", + "@polkadot/types-augment": "10.4.1", + "@polkadot/types-codec": "10.4.1", + "@polkadot/types-create": "10.4.1", + "@polkadot/util": "^12.0.1", + "@polkadot/util-crypto": "^12.0.1", + "rxjs": "^7.8.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/types-augment": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-10.4.1.tgz", + "integrity": "sha512-4QWfxf6nA+sBf2X88S454N5Wpc4yTL+9uwdCP5PiqyDuCv96l+G0jQWurolywADJcKMKcnu8zHdQdv4rcgUyxQ==", + "dependencies": { + "@polkadot/types": "10.4.1", + "@polkadot/types-codec": "10.4.1", + "@polkadot/util": "^12.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/types-augment/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/types-codec": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-10.4.1.tgz", + "integrity": "sha512-h6OCSF6i0b/Q4uCOEEiibMkGefhb7daaDGwoxjTEOeSqDE9gMIkhkyXVjqcOGbsHsFaYqYhuJakRQe/LICykTw==", + "dependencies": { + "@polkadot/util": "^12.0.1", + "@polkadot/x-bigint": "^12.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/types-codec/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/types-create": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-10.4.1.tgz", + "integrity": "sha512-E15P4ZQSAIq90Gnc+ZVK7XmEWRIF9qbCAQvJQ6nxikb6u5A9AzePAZ4XwxjJ3JXC5/t0Wxnij+pddtfxOkUi2w==", + "dependencies": { + "@polkadot/types-codec": "10.4.1", + "@polkadot/util": "^12.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/types-create/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/types-known": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-10.4.1.tgz", + "integrity": "sha512-vgxsJeZTdbNJ55CCICRcSeOJD6Qy4D2NaMhR2j24xxHRY1lL8uBfCE/ReIEY2Bxa8cCIjCtw5fXJiCWPjnQQoQ==", + "dependencies": { + "@polkadot/networks": "^12.0.1", + "@polkadot/types": "10.4.1", + "@polkadot/types-codec": "10.4.1", + "@polkadot/types-create": "10.4.1", + "@polkadot/util": "^12.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/types-known/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/types-support": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-support/-/types-support-10.4.1.tgz", + "integrity": "sha512-kt/TGVAGaGD76eM/kye8qdYxQDZtpc6GS58xYvgnsk59KkCvaOzOE+hHFDXx0UDy4pPgXKquk1EHxwiFTKSb4g==", + "dependencies": { + "@polkadot/util": "^12.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/types-support/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/types/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/util": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-12.6.2.tgz", + "integrity": "sha512-l8TubR7CLEY47240uki0TQzFvtnxFIO7uI/0GoWzpYD/O62EIAMRsuY01N4DuwgKq2ZWD59WhzsLYmA5K6ksdw==", + "dependencies": { + "@polkadot/x-bigint": "12.6.2", + "@polkadot/x-global": "12.6.2", + "@polkadot/x-textdecoder": "12.6.2", + "@polkadot/x-textencoder": "12.6.2", + "@types/bn.js": "^5.1.5", + "bn.js": "^5.2.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/util-crypto": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-12.2.2.tgz", + "integrity": "sha512-4JfEd/TJaDArp5Jpr3N/aYHp+QR71XzZRKqU4u7WkGKmnGt28Qfh2IWGB/E2MvIFxa6CjIiQMxN2hnkNr49JAQ==", + "dependencies": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@polkadot/networks": "12.2.2", + "@polkadot/util": "12.2.2", + "@polkadot/wasm-crypto": "^7.2.1", + "@polkadot/wasm-util": "^7.2.1", + "@polkadot/x-bigint": "12.2.2", + "@polkadot/x-randomvalues": "12.2.2", + "@scure/base": "1.1.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@polkadot/util": "12.2.2" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/@polkadot/networks": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-12.2.2.tgz", + "integrity": "sha512-SsZognHwXyD2saJkB35G+28noAZBcNpJAXsTI7QTTDHGiQSDp0mPmrk3Rt7BRAeFn4qdXQuRqQYKYUwBM2i9mQ==", + "dependencies": { + "@polkadot/util": "12.2.2", + "@substrate/ss58-registry": "^1.40.0", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/@polkadot/util": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-12.2.2.tgz", + "integrity": "sha512-u/v5Z2+iUwX/CXEMVZgJmwqqx1kT5Zfxsio3vpuYaPCg49xhTKqAcrakgB+1BUHhhyF3Zkb9uG73JWFR0Lkk9w==", + "dependencies": { + "@polkadot/x-bigint": "12.2.2", + "@polkadot/x-global": "12.2.2", + "@polkadot/x-textdecoder": "12.2.2", + "@polkadot/x-textencoder": "12.2.2", + "@types/bn.js": "^5.1.1", + "bn.js": "^5.2.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/@polkadot/x-bigint": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-12.2.2.tgz", + "integrity": "sha512-KSe7WAqwI1tubi0m5CP4oqf8EIjABZXLGkTHXKwjtAAMa9Q7hqFmVG2sXfvC+XSnhto1UKMe52TjuPrYSJI+jg==", + "dependencies": { + "@polkadot/x-global": "12.2.2", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/@polkadot/x-global": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-12.2.2.tgz", + "integrity": "sha512-hLVoKR9fGhZdy/eK/LHTyh4jJ3V+3VfcxbCey0k2t1Byrwbmsi6wL3NUQk6i3NviswR9OSCic9mhgDQPRBXZEg==", + "dependencies": { + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/@polkadot/x-randomvalues": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-12.2.2.tgz", + "integrity": "sha512-eExiOT/up5ZzwHJkFpGhQ6sCdPSJnn6PJsQnyJMEdgPaUES70u/wWMLGFNiy3U8rRRVSsZi6rc9Unsr02LczzA==", + "dependencies": { + "@polkadot/x-global": "12.2.2", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@polkadot/util": "12.2.2", + "@polkadot/wasm-util": "*" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/@polkadot/x-textdecoder": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-12.2.2.tgz", + "integrity": "sha512-Rsvsc7ZLBKT1rls8gdbvzLLEs2sGUA8cDiTaQUkCHJN3ja/37Bppz1wNPcEIMsJ2pyL6bwq86HB0xmC28QVdqA==", + "dependencies": { + "@polkadot/x-global": "12.2.2", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/@polkadot/x-textencoder": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-12.2.2.tgz", + "integrity": "sha512-g6bX4DTBmkr3QLNeihlrHYvaZCKu1kFiK+BDQXVzBg+oHpzxz5wSVhzsG3GEVoVszXMiugWpSn03wCIvaRFMoQ==", + "dependencies": { + "@polkadot/x-global": "12.2.2", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/@polkadot/util-crypto/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/util/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/@polkadot/util/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/wasm-bridge": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.3.2.tgz", + "integrity": "sha512-AJEXChcf/nKXd5Q/YLEV5dXQMle3UNT7jcXYmIffZAo/KI394a+/24PaISyQjoNC0fkzS1Q8T5pnGGHmXiVz2g==", + "dependencies": { + "@polkadot/wasm-util": "7.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-bridge/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/wasm-crypto": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.3.2.tgz", + "integrity": "sha512-+neIDLSJ6jjVXsjyZ5oLSv16oIpwp+PxFqTUaZdZDoA2EyFRQB8pP7+qLsMNk+WJuhuJ4qXil/7XiOnZYZ+wxw==", + "dependencies": { + "@polkadot/wasm-bridge": "7.3.2", + "@polkadot/wasm-crypto-asmjs": "7.3.2", + "@polkadot/wasm-crypto-init": "7.3.2", + "@polkadot/wasm-crypto-wasm": "7.3.2", + "@polkadot/wasm-util": "7.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-asmjs": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.3.2.tgz", + "integrity": "sha512-QP5eiUqUFur/2UoF2KKKYJcesc71fXhQFLT3D4ZjG28Mfk2ZPI0QNRUfpcxVQmIUpV5USHg4geCBNuCYsMm20Q==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-asmjs/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/wasm-crypto-init": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.3.2.tgz", + "integrity": "sha512-FPq73zGmvZtnuJaFV44brze3Lkrki3b4PebxCy9Fplw8nTmisKo9Xxtfew08r0njyYh+uiJRAxPCXadkC9sc8g==", + "dependencies": { + "@polkadot/wasm-bridge": "7.3.2", + "@polkadot/wasm-crypto-asmjs": "7.3.2", + "@polkadot/wasm-crypto-wasm": "7.3.2", + "@polkadot/wasm-util": "7.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-init/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/wasm-crypto-wasm": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.3.2.tgz", + "integrity": "sha512-15wd0EMv9IXs5Abp1ZKpKKAVyZPhATIAHfKsyoWCEFDLSOA0/K0QGOxzrAlsrdUkiKZOq7uzSIgIDgW8okx2Mw==", + "dependencies": { + "@polkadot/wasm-util": "7.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-wasm/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/wasm-crypto/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/wasm-util": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.3.2.tgz", + "integrity": "sha512-bmD+Dxo1lTZyZNxbyPE380wd82QsX+43mgCm40boyKrRppXEyQmWT98v/Poc7chLuskYb6X8IQ6lvvK2bGR4Tg==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-util/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/x-bigint": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-12.6.2.tgz", + "integrity": "sha512-HSIk60uFPX4GOFZSnIF7VYJz7WZA7tpFJsne7SzxOooRwMTWEtw3fUpFy5cYYOeLh17/kHH1Y7SVcuxzVLc74Q==", + "dependencies": { + "@polkadot/x-global": "12.6.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-bigint/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/x-fetch": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-12.6.2.tgz", + "integrity": "sha512-8wM/Z9JJPWN1pzSpU7XxTI1ldj/AfC8hKioBlUahZ8gUiJaOF7K9XEFCrCDLis/A1BoOu7Ne6WMx/vsJJIbDWw==", + "dependencies": { + "@polkadot/x-global": "12.6.2", + "node-fetch": "^3.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-fetch/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/@polkadot/x-fetch/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/x-global": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-12.6.2.tgz", + "integrity": "sha512-a8d6m+PW98jmsYDtAWp88qS4dl8DyqUBsd0S+WgyfSMtpEXu6v9nXDgPZgwF5xdDvXhm+P0ZfVkVTnIGrScb5g==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-global/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/x-randomvalues": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-12.6.2.tgz", + "integrity": "sha512-Vr8uG7rH2IcNJwtyf5ebdODMcr0XjoCpUbI91Zv6AlKVYOGKZlKLYJHIwpTaKKB+7KPWyQrk4Mlym/rS7v9feg==", + "dependencies": { + "@polkadot/x-global": "12.6.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "12.6.2", + "@polkadot/wasm-util": "*" + } + }, + "node_modules/@polkadot/x-randomvalues/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/x-textdecoder": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-12.6.2.tgz", + "integrity": "sha512-M1Bir7tYvNappfpFWXOJcnxUhBUFWkUFIdJSyH0zs5LmFtFdbKAeiDXxSp2Swp5ddOZdZgPac294/o2TnQKN1w==", + "dependencies": { + "@polkadot/x-global": "12.6.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-textdecoder/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/x-textencoder": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-12.6.2.tgz", + "integrity": "sha512-4N+3UVCpI489tUJ6cv3uf0PjOHvgGp9Dl+SZRLgFGt9mvxnvpW/7+XBADRMtlG4xi5gaRK7bgl5bmY6OMDsNdw==", + "dependencies": { + "@polkadot/x-global": "12.6.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-textencoder/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/x-ws": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-12.6.2.tgz", + "integrity": "sha512-cGZWo7K5eRRQCRl2LrcyCYsrc3lRbTlixZh3AzgU8uX4wASVGRlNWi/Hf4TtHNe1ExCDmxabJzdIsABIfrr7xw==", + "dependencies": { + "@polkadot/x-global": "12.6.2", + "tslib": "^2.6.2", + "ws": "^8.15.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-ws/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/x-ws/node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -1459,6 +2325,17 @@ "resolved": "https://registry.npmjs.org/@redux-saga/types/-/types-1.2.1.tgz", "integrity": "sha512-1dgmkh+3so0+LlBWRhGA33ua4MYr7tUOj+a9Si28vUi0IUFNbff1T3sgpeDJI/LaC75bBYnQ0A3wXjn0OrRNBA==" }, + "node_modules/@scure/base": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", + "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, "node_modules/@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", @@ -1470,6 +2347,34 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, + "node_modules/@substrate/connect": { + "version": "0.7.24", + "resolved": "https://registry.npmjs.org/@substrate/connect/-/connect-0.7.24.tgz", + "integrity": "sha512-vF82taiM0yME+ibiJgEv0xn/NZd9TQ4atXk1AQCe2z82SEKzw0Lwx9ZLFEOvlgnh+Nc2EtQi7y4cXJ+48rOqxw==", + "optional": true, + "dependencies": { + "@substrate/connect-extension-protocol": "^1.0.1", + "eventemitter3": "^4.0.7", + "smoldot": "1.0.2" + } + }, + "node_modules/@substrate/connect-extension-protocol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-1.0.1.tgz", + "integrity": "sha512-161JhCC1csjH3GE5mPLEd7HbWtwNSPJBg3p1Ksz9SFlTzj/bgEwudiRN2y5i0MoLGCIJRYKyKGMxVnd29PzNjg==", + "optional": true + }, + "node_modules/@substrate/connect/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "optional": true + }, + "node_modules/@substrate/ss58-registry": { + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.47.0.tgz", + "integrity": "sha512-6kuIJedRcisUJS2pgksEH2jZf3hfSIVzqtFzs/AyjTW3ETbMg5q1Bb7VWa0WYaT6dTrEXp/6UoXM5B9pSIUmcw==" + }, "node_modules/@szmarczak/http-timer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", @@ -2059,9 +2964,9 @@ } }, "node_modules/@types/bn.js": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", - "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", + "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", "dependencies": { "@types/node": "*" } @@ -3532,6 +4437,14 @@ "node": ">=0.10" } }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, "node_modules/dataloader": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.1.0.tgz", @@ -4254,6 +5167,28 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/fetch-cookie": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.11.0.tgz", @@ -4395,6 +5330,17 @@ "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz", "integrity": "sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg==" }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -4676,7 +5622,6 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.5.tgz", "integrity": "sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A==", - "hasInstallScript": true, "optional": true, "dependencies": { "node-gyp-build": "^4.3.0" @@ -5006,7 +5951,6 @@ "version": "5.0.7", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.7.tgz", "integrity": "sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q==", - "hasInstallScript": true, "optional": true, "dependencies": { "node-gyp-build": "^4.3.0" @@ -6544,6 +7488,14 @@ "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz", "integrity": "sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw==" }, + "node_modules/mock-socket": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.3.1.tgz", + "integrity": "sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==", + "engines": { + "node": ">= 8" + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -6636,6 +7588,40 @@ "lower-case": "^1.1.1" } }, + "node_modules/nock": { + "version": "13.5.4", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.4.tgz", + "integrity": "sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw==", + "dependencies": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">= 10.13" + } + }, + "node_modules/nock/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nock/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", @@ -6647,6 +7633,24 @@ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, "node_modules/node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -6908,6 +7912,12 @@ "node": ">=4" } }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "optional": true + }, "node_modules/param-case": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", @@ -7479,6 +8489,14 @@ "node": ">= 0.6.0" } }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "engines": { + "node": ">= 8" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -8022,6 +9040,14 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -8290,8 +9316,39 @@ "dependencies": { "mimic-response": "^1.0.0" }, - "engines": { - "node": ">=4" + "engines": { + "node": ">=4" + } + }, + "node_modules/smoldot": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-1.0.2.tgz", + "integrity": "sha512-IHhMzvXwyl6I5GA4JvfzM2OOp9wBO06AmjqT4nCoNms5PiLe74f/A+jIZIJKyY6eBhMpmECizyfeTneHO2wMFQ==", + "optional": true, + "dependencies": { + "pako": "^2.0.4", + "ws": "^8.8.1" + } + }, + "node_modules/smoldot/node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "optional": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, "node_modules/snake-case": { @@ -9070,8 +10127,7 @@ "node_modules/tslib": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", - "optional": true + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" }, "node_modules/tunnel-agent": { "version": "0.6.0", @@ -9315,6 +10371,14 @@ "integrity": "sha512-Tm7jR1xTzBbPW+6y1tknKiEhz04Wf/1iZkcTJjSFcpNko43+dFW6+OOeQe9taJIug3NdfUAjFKgUSyQrIKaDvQ==", "optional": true }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "engines": { + "node": ">= 8" + } + }, "node_modules/web3": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/web3/-/web3-1.10.0.tgz", @@ -10758,111 +11822,834 @@ "tslib": "^2.4.0" } }, - "@graphql-typed-document-node/core": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", - "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", - "optional": true, - "requires": {} - }, - "@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "optional": true, + "requires": {} + }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==" + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, + "@josephg/resolvable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@josephg/resolvable/-/resolvable-1.0.1.tgz", + "integrity": "sha512-CtzORUwWTTOTqfVtHaKRJ0I1kNQd1bpn3sUh8I3nJDVY+5/M/Oe1DnEWzPQvqq/xPIIkzzzIP7mfCoAjFRvDhg==", + "optional": true + }, + "@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==" + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@noble/curves": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", + "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", + "requires": { + "@noble/hashes": "1.3.1" + }, + "dependencies": { + "@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==" + } + } + }, + "@noble/hashes": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz", + "integrity": "sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==" + }, + "@noble/secp256k1": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", + "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==" + }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "optional": true + }, + "@polkadot/api": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-10.4.1.tgz", + "integrity": "sha512-MFIhykqvU4l5oyLxEnMhtF07VWLXwBS02gVd9Q+Gh2RxXtskQnXlos00hZFdLaCQ7g8BNggoq0Woc2BGZQEG2A==", + "requires": { + "@polkadot/api-augment": "10.4.1", + "@polkadot/api-base": "10.4.1", + "@polkadot/api-derive": "10.4.1", + "@polkadot/keyring": "^12.0.1", + "@polkadot/rpc-augment": "10.4.1", + "@polkadot/rpc-core": "10.4.1", + "@polkadot/rpc-provider": "10.4.1", + "@polkadot/types": "10.4.1", + "@polkadot/types-augment": "10.4.1", + "@polkadot/types-codec": "10.4.1", + "@polkadot/types-create": "10.4.1", + "@polkadot/types-known": "10.4.1", + "@polkadot/util": "^12.0.1", + "@polkadot/util-crypto": "^12.0.1", + "eventemitter3": "^5.0.0", + "rxjs": "^7.8.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/api-augment": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-10.4.1.tgz", + "integrity": "sha512-7TUFHiOsaAKwRCDXEwwYHIBi07Nd0+4vMDgP+/eKVrX5XkYCnEA+PdkE8uEpFY3LMoVVr08mPiYPn59gN0b5jw==", + "requires": { + "@polkadot/api-base": "10.4.1", + "@polkadot/rpc-augment": "10.4.1", + "@polkadot/types": "10.4.1", + "@polkadot/types-augment": "10.4.1", + "@polkadot/types-codec": "10.4.1", + "@polkadot/util": "^12.0.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/api-base": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-base/-/api-base-10.4.1.tgz", + "integrity": "sha512-4uItv9NeZDu60qN8dhPN8Q7HBOYZ0Ylf7+/lAnEMldoCx0RxNP2h1T2JwdW3C5ElTsztYzYVLKg0Iz5zJpbffQ==", + "requires": { + "@polkadot/rpc-core": "10.4.1", + "@polkadot/types": "10.4.1", + "@polkadot/util": "^12.0.1", + "rxjs": "^7.8.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/api-derive": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-10.4.1.tgz", + "integrity": "sha512-SR/L4H8oiYMyLaLzmm9Z3kb4tNLAOAIR2uBZshCo917CqMuvrkNVW+Wfh2dAtVsnoSc1FaSPfDwmKash6pqNUQ==", + "requires": { + "@polkadot/api": "10.4.1", + "@polkadot/api-augment": "10.4.1", + "@polkadot/api-base": "10.4.1", + "@polkadot/rpc-core": "10.4.1", + "@polkadot/types": "10.4.1", + "@polkadot/types-codec": "10.4.1", + "@polkadot/util": "^12.0.1", + "@polkadot/util-crypto": "^12.0.1", + "rxjs": "^7.8.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/keyring": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-12.6.2.tgz", + "integrity": "sha512-O3Q7GVmRYm8q7HuB3S0+Yf/q/EB2egKRRU3fv9b3B7V+A52tKzA+vIwEmNVaD1g5FKW9oB97rmpggs0zaKFqHw==", + "requires": { + "@polkadot/util": "12.6.2", + "@polkadot/util-crypto": "12.6.2", + "tslib": "^2.6.2" + }, + "dependencies": { + "@noble/curves": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.0.tgz", + "integrity": "sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==", + "requires": { + "@noble/hashes": "1.4.0" + } + }, + "@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==" + }, + "@polkadot/util-crypto": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-12.6.2.tgz", + "integrity": "sha512-FEWI/dJ7wDMNN1WOzZAjQoIcCP/3vz3wvAp5QQm+lOrzOLj0iDmaIGIcBkz8HVm3ErfSe/uKP0KS4jgV/ib+Mg==", + "requires": { + "@noble/curves": "^1.3.0", + "@noble/hashes": "^1.3.3", + "@polkadot/networks": "12.6.2", + "@polkadot/util": "12.6.2", + "@polkadot/wasm-crypto": "^7.3.2", + "@polkadot/wasm-util": "^7.3.2", + "@polkadot/x-bigint": "12.6.2", + "@polkadot/x-randomvalues": "12.6.2", + "@scure/base": "^1.1.5", + "tslib": "^2.6.2" + } + }, + "@scure/base": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.6.tgz", + "integrity": "sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==" + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/networks": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-12.6.2.tgz", + "integrity": "sha512-1oWtZm1IvPWqvMrldVH6NI2gBoCndl5GEwx7lAuQWGr7eNL+6Bdc5K3Z9T0MzFvDGoi2/CBqjX9dRKo39pDC/w==", + "requires": { + "@polkadot/util": "12.6.2", + "@substrate/ss58-registry": "^1.44.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/rpc-augment": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-10.4.1.tgz", + "integrity": "sha512-lwwSjyboTTDzrfmBxFgBq4ZcD8mhMKkofmVqS/ntvuJePrCnBPGQG9d+tfntizIO5OpCqFGk0lgYBwae+oQ+vw==", + "requires": { + "@polkadot/rpc-core": "10.4.1", + "@polkadot/types": "10.4.1", + "@polkadot/types-codec": "10.4.1", + "@polkadot/util": "^12.0.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/rpc-core": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-10.4.1.tgz", + "integrity": "sha512-jE3dBzfOFkiROMsiXpge/njvwPDCXF990VPFtlFqVWpr9ypXY7eZT1gduanN6YkXRjzcBVVPHaPfWLejie6hZA==", + "requires": { + "@polkadot/rpc-augment": "10.4.1", + "@polkadot/rpc-provider": "10.4.1", + "@polkadot/types": "10.4.1", + "@polkadot/util": "^12.0.1", + "rxjs": "^7.8.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/rpc-provider": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-10.4.1.tgz", + "integrity": "sha512-DwXs5dB1hp0P17I/Aqg+BnILQRMWlmMlABfSwwPprOh+16BBDw4P7bEuvaYw3gl/Zrxt4UhDcPl48jD+4j5OrQ==", + "requires": { + "@polkadot/keyring": "^12.0.1", + "@polkadot/types": "10.4.1", + "@polkadot/types-support": "10.4.1", + "@polkadot/util": "^12.0.1", + "@polkadot/util-crypto": "^12.0.1", + "@polkadot/x-fetch": "^12.0.1", + "@polkadot/x-global": "^12.0.1", + "@polkadot/x-ws": "^12.0.1", + "@substrate/connect": "0.7.24", + "eventemitter3": "^5.0.0", + "mock-socket": "^9.2.1", + "nock": "^13.3.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/types": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-10.4.1.tgz", + "integrity": "sha512-w4spUa4CMLoPuIHOANR8X5F/Hfs4b4OnxsofA9SOl2aaQNRgu77Qu0y60rReu8HxBKD/FUBJJmRpZeY/2WQAAQ==", + "requires": { + "@polkadot/keyring": "^12.0.1", + "@polkadot/types-augment": "10.4.1", + "@polkadot/types-codec": "10.4.1", + "@polkadot/types-create": "10.4.1", + "@polkadot/util": "^12.0.1", + "@polkadot/util-crypto": "^12.0.1", + "rxjs": "^7.8.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/types-augment": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-10.4.1.tgz", + "integrity": "sha512-4QWfxf6nA+sBf2X88S454N5Wpc4yTL+9uwdCP5PiqyDuCv96l+G0jQWurolywADJcKMKcnu8zHdQdv4rcgUyxQ==", + "requires": { + "@polkadot/types": "10.4.1", + "@polkadot/types-codec": "10.4.1", + "@polkadot/util": "^12.0.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/types-codec": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-10.4.1.tgz", + "integrity": "sha512-h6OCSF6i0b/Q4uCOEEiibMkGefhb7daaDGwoxjTEOeSqDE9gMIkhkyXVjqcOGbsHsFaYqYhuJakRQe/LICykTw==", + "requires": { + "@polkadot/util": "^12.0.1", + "@polkadot/x-bigint": "^12.0.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/types-create": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-10.4.1.tgz", + "integrity": "sha512-E15P4ZQSAIq90Gnc+ZVK7XmEWRIF9qbCAQvJQ6nxikb6u5A9AzePAZ4XwxjJ3JXC5/t0Wxnij+pddtfxOkUi2w==", + "requires": { + "@polkadot/types-codec": "10.4.1", + "@polkadot/util": "^12.0.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/types-known": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-10.4.1.tgz", + "integrity": "sha512-vgxsJeZTdbNJ55CCICRcSeOJD6Qy4D2NaMhR2j24xxHRY1lL8uBfCE/ReIEY2Bxa8cCIjCtw5fXJiCWPjnQQoQ==", + "requires": { + "@polkadot/networks": "^12.0.1", + "@polkadot/types": "10.4.1", + "@polkadot/types-codec": "10.4.1", + "@polkadot/types-create": "10.4.1", + "@polkadot/util": "^12.0.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/types-support": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-support/-/types-support-10.4.1.tgz", + "integrity": "sha512-kt/TGVAGaGD76eM/kye8qdYxQDZtpc6GS58xYvgnsk59KkCvaOzOE+hHFDXx0UDy4pPgXKquk1EHxwiFTKSb4g==", + "requires": { + "@polkadot/util": "^12.0.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/util": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-12.6.2.tgz", + "integrity": "sha512-l8TubR7CLEY47240uki0TQzFvtnxFIO7uI/0GoWzpYD/O62EIAMRsuY01N4DuwgKq2ZWD59WhzsLYmA5K6ksdw==", + "requires": { + "@polkadot/x-bigint": "12.6.2", + "@polkadot/x-global": "12.6.2", + "@polkadot/x-textdecoder": "12.6.2", + "@polkadot/x-textencoder": "12.6.2", + "@types/bn.js": "^5.1.5", + "bn.js": "^5.2.1", + "tslib": "^2.6.2" + }, + "dependencies": { + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/util-crypto": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-12.2.2.tgz", + "integrity": "sha512-4JfEd/TJaDArp5Jpr3N/aYHp+QR71XzZRKqU4u7WkGKmnGt28Qfh2IWGB/E2MvIFxa6CjIiQMxN2hnkNr49JAQ==", "requires": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@polkadot/networks": "12.2.2", + "@polkadot/util": "12.2.2", + "@polkadot/wasm-crypto": "^7.2.1", + "@polkadot/wasm-util": "^7.2.1", + "@polkadot/x-bigint": "12.2.2", + "@polkadot/x-randomvalues": "12.2.2", + "@scure/base": "1.1.1", + "tslib": "^2.5.3" }, "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + "@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==" }, - "ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==" + "@polkadot/networks": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-12.2.2.tgz", + "integrity": "sha512-SsZognHwXyD2saJkB35G+28noAZBcNpJAXsTI7QTTDHGiQSDp0mPmrk3Rt7BRAeFn4qdXQuRqQYKYUwBM2i9mQ==", + "requires": { + "@polkadot/util": "12.2.2", + "@substrate/ss58-registry": "^1.40.0", + "tslib": "^2.5.3" + } }, - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + "@polkadot/util": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-12.2.2.tgz", + "integrity": "sha512-u/v5Z2+iUwX/CXEMVZgJmwqqx1kT5Zfxsio3vpuYaPCg49xhTKqAcrakgB+1BUHhhyF3Zkb9uG73JWFR0Lkk9w==", + "requires": { + "@polkadot/x-bigint": "12.2.2", + "@polkadot/x-global": "12.2.2", + "@polkadot/x-textdecoder": "12.2.2", + "@polkadot/x-textencoder": "12.2.2", + "@types/bn.js": "^5.1.1", + "bn.js": "^5.2.1", + "tslib": "^2.5.3" + } }, - "string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "@polkadot/x-bigint": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-12.2.2.tgz", + "integrity": "sha512-KSe7WAqwI1tubi0m5CP4oqf8EIjABZXLGkTHXKwjtAAMa9Q7hqFmVG2sXfvC+XSnhto1UKMe52TjuPrYSJI+jg==", "requires": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "@polkadot/x-global": "12.2.2", + "tslib": "^2.5.3" } }, - "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "@polkadot/x-global": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-12.2.2.tgz", + "integrity": "sha512-hLVoKR9fGhZdy/eK/LHTyh4jJ3V+3VfcxbCey0k2t1Byrwbmsi6wL3NUQk6i3NviswR9OSCic9mhgDQPRBXZEg==", "requires": { - "ansi-regex": "^6.0.1" + "tslib": "^2.5.3" } }, - "wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "@polkadot/x-randomvalues": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-12.2.2.tgz", + "integrity": "sha512-eExiOT/up5ZzwHJkFpGhQ6sCdPSJnn6PJsQnyJMEdgPaUES70u/wWMLGFNiy3U8rRRVSsZi6rc9Unsr02LczzA==", "requires": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" + "@polkadot/x-global": "12.2.2", + "tslib": "^2.5.3" + } + }, + "@polkadot/x-textdecoder": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-12.2.2.tgz", + "integrity": "sha512-Rsvsc7ZLBKT1rls8gdbvzLLEs2sGUA8cDiTaQUkCHJN3ja/37Bppz1wNPcEIMsJ2pyL6bwq86HB0xmC28QVdqA==", + "requires": { + "@polkadot/x-global": "12.2.2", + "tslib": "^2.5.3" } + }, + "@polkadot/x-textencoder": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-12.2.2.tgz", + "integrity": "sha512-g6bX4DTBmkr3QLNeihlrHYvaZCKu1kFiK+BDQXVzBg+oHpzxz5wSVhzsG3GEVoVszXMiugWpSn03wCIvaRFMoQ==", + "requires": { + "@polkadot/x-global": "12.2.2", + "tslib": "^2.5.3" + } + }, + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, - "@josephg/resolvable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@josephg/resolvable/-/resolvable-1.0.1.tgz", - "integrity": "sha512-CtzORUwWTTOTqfVtHaKRJ0I1kNQd1bpn3sUh8I3nJDVY+5/M/Oe1DnEWzPQvqq/xPIIkzzzIP7mfCoAjFRvDhg==", - "optional": true + "@polkadot/wasm-bridge": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.3.2.tgz", + "integrity": "sha512-AJEXChcf/nKXd5Q/YLEV5dXQMle3UNT7jcXYmIffZAo/KI394a+/24PaISyQjoNC0fkzS1Q8T5pnGGHmXiVz2g==", + "requires": { + "@polkadot/wasm-util": "7.3.2", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } }, - "@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==" + "@polkadot/wasm-crypto": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.3.2.tgz", + "integrity": "sha512-+neIDLSJ6jjVXsjyZ5oLSv16oIpwp+PxFqTUaZdZDoA2EyFRQB8pP7+qLsMNk+WJuhuJ4qXil/7XiOnZYZ+wxw==", + "requires": { + "@polkadot/wasm-bridge": "7.3.2", + "@polkadot/wasm-crypto-asmjs": "7.3.2", + "@polkadot/wasm-crypto-init": "7.3.2", + "@polkadot/wasm-crypto-wasm": "7.3.2", + "@polkadot/wasm-util": "7.3.2", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "@polkadot/wasm-crypto-asmjs": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.3.2.tgz", + "integrity": "sha512-QP5eiUqUFur/2UoF2KKKYJcesc71fXhQFLT3D4ZjG28Mfk2ZPI0QNRUfpcxVQmIUpV5USHg4geCBNuCYsMm20Q==", + "requires": { + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } }, - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "@polkadot/wasm-crypto-init": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.3.2.tgz", + "integrity": "sha512-FPq73zGmvZtnuJaFV44brze3Lkrki3b4PebxCy9Fplw8nTmisKo9Xxtfew08r0njyYh+uiJRAxPCXadkC9sc8g==", "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@polkadot/wasm-bridge": "7.3.2", + "@polkadot/wasm-crypto-asmjs": "7.3.2", + "@polkadot/wasm-crypto-wasm": "7.3.2", + "@polkadot/wasm-util": "7.3.2", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@noble/hashes": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz", - "integrity": "sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==" + "@polkadot/wasm-crypto-wasm": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.3.2.tgz", + "integrity": "sha512-15wd0EMv9IXs5Abp1ZKpKKAVyZPhATIAHfKsyoWCEFDLSOA0/K0QGOxzrAlsrdUkiKZOq7uzSIgIDgW8okx2Mw==", + "requires": { + "@polkadot/wasm-util": "7.3.2", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } }, - "@noble/secp256k1": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", - "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==" + "@polkadot/wasm-util": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.3.2.tgz", + "integrity": "sha512-bmD+Dxo1lTZyZNxbyPE380wd82QsX+43mgCm40boyKrRppXEyQmWT98v/Poc7chLuskYb6X8IQ6lvvK2bGR4Tg==", + "requires": { + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } }, - "@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "optional": true + "@polkadot/x-bigint": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-12.6.2.tgz", + "integrity": "sha512-HSIk60uFPX4GOFZSnIF7VYJz7WZA7tpFJsne7SzxOooRwMTWEtw3fUpFy5cYYOeLh17/kHH1Y7SVcuxzVLc74Q==", + "requires": { + "@polkadot/x-global": "12.6.2", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/x-fetch": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-12.6.2.tgz", + "integrity": "sha512-8wM/Z9JJPWN1pzSpU7XxTI1ldj/AfC8hKioBlUahZ8gUiJaOF7K9XEFCrCDLis/A1BoOu7Ne6WMx/vsJJIbDWw==", + "requires": { + "@polkadot/x-global": "12.6.2", + "node-fetch": "^3.3.2", + "tslib": "^2.6.2" + }, + "dependencies": { + "node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "requires": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + } + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/x-global": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-12.6.2.tgz", + "integrity": "sha512-a8d6m+PW98jmsYDtAWp88qS4dl8DyqUBsd0S+WgyfSMtpEXu6v9nXDgPZgwF5xdDvXhm+P0ZfVkVTnIGrScb5g==", + "requires": { + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/x-randomvalues": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-12.6.2.tgz", + "integrity": "sha512-Vr8uG7rH2IcNJwtyf5ebdODMcr0XjoCpUbI91Zv6AlKVYOGKZlKLYJHIwpTaKKB+7KPWyQrk4Mlym/rS7v9feg==", + "requires": { + "@polkadot/x-global": "12.6.2", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/x-textdecoder": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-12.6.2.tgz", + "integrity": "sha512-M1Bir7tYvNappfpFWXOJcnxUhBUFWkUFIdJSyH0zs5LmFtFdbKAeiDXxSp2Swp5ddOZdZgPac294/o2TnQKN1w==", + "requires": { + "@polkadot/x-global": "12.6.2", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/x-textencoder": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-12.6.2.tgz", + "integrity": "sha512-4N+3UVCpI489tUJ6cv3uf0PjOHvgGp9Dl+SZRLgFGt9mvxnvpW/7+XBADRMtlG4xi5gaRK7bgl5bmY6OMDsNdw==", + "requires": { + "@polkadot/x-global": "12.6.2", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/x-ws": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-12.6.2.tgz", + "integrity": "sha512-cGZWo7K5eRRQCRl2LrcyCYsrc3lRbTlixZh3AzgU8uX4wASVGRlNWi/Hf4TtHNe1ExCDmxabJzdIsABIfrr7xw==", + "requires": { + "@polkadot/x-global": "12.6.2", + "tslib": "^2.6.2", + "ws": "^8.15.1" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "requires": {} + } + } }, "@protobufjs/aspromise": { "version": "1.1.2", @@ -10985,11 +12772,46 @@ "resolved": "https://registry.npmjs.org/@redux-saga/types/-/types-1.2.1.tgz", "integrity": "sha512-1dgmkh+3so0+LlBWRhGA33ua4MYr7tUOj+a9Si28vUi0IUFNbff1T3sgpeDJI/LaC75bBYnQ0A3wXjn0OrRNBA==" }, + "@scure/base": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", + "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==" + }, "@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==" }, + "@substrate/connect": { + "version": "0.7.24", + "resolved": "https://registry.npmjs.org/@substrate/connect/-/connect-0.7.24.tgz", + "integrity": "sha512-vF82taiM0yME+ibiJgEv0xn/NZd9TQ4atXk1AQCe2z82SEKzw0Lwx9ZLFEOvlgnh+Nc2EtQi7y4cXJ+48rOqxw==", + "optional": true, + "requires": { + "@substrate/connect-extension-protocol": "^1.0.1", + "eventemitter3": "^4.0.7", + "smoldot": "1.0.2" + }, + "dependencies": { + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "optional": true + } + } + }, + "@substrate/connect-extension-protocol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-1.0.1.tgz", + "integrity": "sha512-161JhCC1csjH3GE5mPLEd7HbWtwNSPJBg3p1Ksz9SFlTzj/bgEwudiRN2y5i0MoLGCIJRYKyKGMxVnd29PzNjg==", + "optional": true + }, + "@substrate/ss58-registry": { + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.47.0.tgz", + "integrity": "sha512-6kuIJedRcisUJS2pgksEH2jZf3hfSIVzqtFzs/AyjTW3ETbMg5q1Bb7VWa0WYaT6dTrEXp/6UoXM5B9pSIUmcw==" + }, "@szmarczak/http-timer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", @@ -11511,9 +13333,9 @@ } }, "@types/bn.js": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", - "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", + "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", "requires": { "@types/node": "*" } @@ -12688,6 +14510,11 @@ "assert-plus": "^1.0.0" } }, + "data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==" + }, "dataloader": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.1.0.tgz", @@ -13271,6 +15098,15 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "requires": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + } + }, "fetch-cookie": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.11.0.tgz", @@ -13367,6 +15203,14 @@ "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz", "integrity": "sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg==" }, + "formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "requires": { + "fetch-blob": "^3.1.2" + } + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -14967,6 +16811,11 @@ "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz", "integrity": "sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw==" }, + "mock-socket": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.3.1.tgz", + "integrity": "sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==" + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -15049,6 +16898,31 @@ "lower-case": "^1.1.1" } }, + "nock": { + "version": "13.5.4", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.4.tgz", + "integrity": "sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw==", + "requires": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "propagate": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", @@ -15060,6 +16934,11 @@ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" }, + "node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" + }, "node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -15243,6 +17122,12 @@ "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", "optional": true }, + "pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "optional": true + }, "param-case": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", @@ -15728,6 +17613,11 @@ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" }, + "propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==" + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -16140,6 +18030,14 @@ } } }, + "rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "requires": { + "tslib": "^2.1.0" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -16342,6 +18240,25 @@ } } }, + "smoldot": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-1.0.2.tgz", + "integrity": "sha512-IHhMzvXwyl6I5GA4JvfzM2OOp9wBO06AmjqT4nCoNms5PiLe74f/A+jIZIJKyY6eBhMpmECizyfeTneHO2wMFQ==", + "optional": true, + "requires": { + "pako": "^2.0.4", + "ws": "^8.8.1" + }, + "dependencies": { + "ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "optional": true, + "requires": {} + } + } + }, "snake-case": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz", @@ -16951,8 +18868,7 @@ "tslib": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", - "optional": true + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" }, "tunnel-agent": { "version": "0.6.0", @@ -17158,6 +19074,11 @@ "integrity": "sha512-Tm7jR1xTzBbPW+6y1tknKiEhz04Wf/1iZkcTJjSFcpNko43+dFW6+OOeQe9taJIug3NdfUAjFKgUSyQrIKaDvQ==", "optional": true }, + "web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==" + }, "web3": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/web3/-/web3-1.10.0.tgz", diff --git a/ts-tests/package.json b/ts-tests/package.json index 7ed4d45ac7..e35c291273 100644 --- a/ts-tests/package.json +++ b/ts-tests/package.json @@ -13,6 +13,8 @@ "author": "", "license": "ISC", "dependencies": { + "@polkadot/api": "10.4.1", + "@polkadot/util-crypto": "12.2.2", "@types/chai": "^4.3.5", "@types/mocha": "^10.0.1", "chai": "^4.3.7", diff --git a/ts-tests/tests/test-fake-transaction.ts b/ts-tests/tests/test-fake-transaction.ts new file mode 100644 index 0000000000..1a87cbb147 --- /dev/null +++ b/ts-tests/tests/test-fake-transaction.ts @@ -0,0 +1,51 @@ +import { ApiPromise, WsProvider, Keyring } from "@polkadot/api"; +import { u8aToHex } from "@polkadot/util"; +import { xxhashAsU8a } from "@polkadot/util-crypto"; +import { expect } from "chai"; +import { step } from "mocha-steps"; +import { ALITH_SECRET_KEY, createAndFinalizeBlock, describeWithFrontier, describeWithFrontierWs } from "./util"; + +describeWithFrontierWs("Frontier RPC (Fake transaction)", (context) => { + step("should create ethereum transaction if log is injected in CurrentLogs storage", async function () { + const api = context.polkadotApi; + await api.isReady; + + const keyring = new Keyring({ type: "ethereum" }); + const alith = keyring.addFromUri(ALITH_SECRET_KEY); + + let module = xxhashAsU8a(new TextEncoder().encode("EVM"), 128); + let storage = xxhashAsU8a(new TextEncoder().encode("CurrentLogs"), 128); + let key = new Uint8Array(module.length + storage.length); + key.set(module, 0); + key.set(storage, module.length); + + const value = api.createType("Vec", []); + const log = api.createType("EthereumLog", { + address: api.createType("H160", "0x703b0c16133582Ed347eCDe0b4b8480004766Fac"), + topics: api.createType("Vec", ["0x1111111111111111111111111111111111111111111111111111111111111111"]), + data: api.createType("Bytes", [1, 2, 3]), + }); + value.push(log); + + const transaction = api.tx.sudo.sudo(api.tx.system.setStorage([[u8aToHex(key), u8aToHex(value.toU8a())]])); + await transaction.signAndSend(alith); + + await createAndFinalizeBlock(context.web3); + + { + const lastBlockNumber = await context.web3.eth.getBlockNumber(); + const lastBlock = await context.web3.eth.getBlock(lastBlockNumber, true); + // block contains fake transaction + expect(lastBlock.transactions.some((tx) => tx.gas == 0 && tx.gasPrice == "0" && tx.input == "0x00000000")); + } + + await createAndFinalizeBlock(context.web3); + + { + const lastBlockNumber = await context.web3.eth.getBlockNumber(); + const lastBlock = await context.web3.eth.getBlock(lastBlockNumber, true); + // block doesn't contain fake transaction + expect(!lastBlock.transactions.some((tx) => tx.gas == 0 && tx.gasPrice == "0" && tx.input == "0x00000000")); + } + }); +}); \ No newline at end of file diff --git a/ts-tests/tests/util.ts b/ts-tests/tests/util.ts index d31e3e68c0..a0f5cddc82 100644 --- a/ts-tests/tests/util.ts +++ b/ts-tests/tests/util.ts @@ -4,6 +4,7 @@ import { JsonRpcResponse } from "web3-core-helpers"; import { spawn, ChildProcess } from "child_process"; import { NODE_BINARY_NAME, CHAIN_ID } from "./config"; +import { ApiPromise, WsProvider } from "@polkadot/api"; export const PORT = 19931; export const RPC_PORT = 19932; @@ -16,6 +17,18 @@ export const FRONTIER_BACKEND_TYPE = process.env.FRONTIER_BACKEND_TYPE || "key-v export const BINARY_PATH = `../target/${FRONTIER_BUILD}/${NODE_BINARY_NAME}`; export const SPAWNING_TIME = 60000; +export const ALITH_ADDRESS = "0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"; +export const ALITH_SECRET_KEY = "0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133"; + +export const BALTATHAR_ADDRESS = "0x3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0"; +export const BALTATHAR_SECRET_KEY = "0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b"; + +export const CHARLETH_ADDRESS = "0x798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc"; +export const CHARLETH_SECRET_KEY = "0x0b6e18cafb6ed99687ec547bd28139cafdd2bffe70e6b688025de6b445aa5c5b"; + +export const DOROTHY_ADDRESS = "0x773539d4Ac0e786233D90A233654ccEE26a613D9"; +export const DOROTHY_SECRET_KEY = "0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68"; + export async function customRequest(web3: Web3, method: string, params: any[]) { return new Promise((resolve, reject) => { (web3.currentProvider as any).send( @@ -62,6 +75,7 @@ export async function startFrontierNode(provider?: string): Promise<{ web3: Web3; binary: ChildProcess; ethersjs: ethers.JsonRpcProvider; + polkadotApi: ApiPromise; }> { var web3; if (!provider || provider == "http") { @@ -135,20 +149,36 @@ export async function startFrontierNode(provider?: string): Promise<{ web3 = new Web3(`ws://127.0.0.1:${RPC_PORT}`); } + const polkadotApi = new ApiPromise({ + provider: new WsProvider(`ws://127.0.0.1:${RPC_PORT}`), + rpc: {}, + signedExtensions: { + FakeTransactionFinalizer: { + extrinsic: {}, + payload: {}, + }, + }, + }); + let ethersjs = new ethers.JsonRpcProvider(`http://127.0.0.1:${RPC_PORT}`, { chainId: CHAIN_ID, name: "frontier-dev", }); - return { web3, binary, ethersjs }; + return { web3, binary, ethersjs, polkadotApi }; } -export function describeWithFrontier(title: string, cb: (context: { web3: Web3 }) => void, provider?: string) { +export function describeWithFrontier( + title: string, + cb: (context: { web3: Web3; polkadotApi: ApiPromise }) => void, + provider?: string +) { describe(title, () => { let context: { web3: Web3; ethersjs: ethers.JsonRpcProvider; - } = { web3: null, ethersjs: null }; + polkadotApi: ApiPromise; + } = { web3: null, ethersjs: null, polkadotApi: null }; let binary: ChildProcess; // Making sure the Frontier node has started before("Starting Frontier Test Node", async function () { @@ -156,18 +186,20 @@ export function describeWithFrontier(title: string, cb: (context: { web3: Web3 } const init = await startFrontierNode(provider); context.web3 = init.web3; context.ethersjs = init.ethersjs; + context.polkadotApi = init.polkadotApi; binary = init.binary; }); after(async function () { //console.log(`\x1b[31m Killing RPC\x1b[0m`); binary.kill(); + if (context.polkadotApi) await context.polkadotApi.disconnect(); }); cb(context); }); } -export function describeWithFrontierWs(title: string, cb: (context: { web3: Web3 }) => void) { +export function describeWithFrontierWs(title: string, cb: (context: { web3: Web3; polkadotApi: ApiPromise }) => void) { describeWithFrontier(title, cb, "ws"); }