From 75449f4db0d582facab219cc6179db4956253db4 Mon Sep 17 00:00:00 2001 From: Francisco Valentim Castilho Date: Tue, 2 Sep 2025 23:39:48 -0300 Subject: [PATCH 01/12] Feat: Implement `ReceiptService` with state pruning - Introduce new ReceiptService for managing transaction receipts - Implement ReceiptGcPolicy derived from Configuration for state pruning - Add pruning system for both finalized and untrusted receipts - Implement backfill system for finalized receipts with GC policy awareness - Add reorg safety by pruning untrusted receipts before writing - Support configurable retention policies through keep_blocks and gc_step parameters --- Cargo.lock | 1552 +++++++++++++++++++++++++-- Cargo.toml | 3 + pallet-epico/runtime-api/src/lib.rs | 6 +- pallet-epico/src/types.rs | 2 +- receipt-service/Cargo.toml | 31 + receipt-service/src/lib.rs | 549 ++++++++++ rpc/src/eth.rs | 2 +- 7 files changed, 2029 insertions(+), 116 deletions(-) create mode 100644 receipt-service/Cargo.toml create mode 100644 receipt-service/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 66af009..34b2b96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,7 +43,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ "crypto-common", - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -53,7 +53,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", - "cipher", + "cipher 0.4.4", "cpufeatures", ] @@ -65,10 +65,10 @@ checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", "aes", - "cipher", + "cipher 0.4.4", "ctr", "ghash", - "subtle", + "subtle 2.6.1", ] [[package]] @@ -99,6 +99,21 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstyle" version = "1.0.10" @@ -517,6 +532,27 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.99", +] + [[package]] name = "bitcoin-internals" version = "0.2.0" @@ -557,6 +593,18 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake2" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330" +dependencies = [ + "byte-tools", + "crypto-mac 0.7.0", + "digest 0.8.1", + "opaque-debug 0.2.3", +] + [[package]] name = "blake2" version = "0.10.6" @@ -607,7 +655,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -616,7 +664,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -652,6 +700,12 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + [[package]] name = "bytemuck" version = "1.22.0" @@ -670,6 +724,26 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +[[package]] +name = "bzip2-sys" +version = "0.1.13+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" +dependencies = [ + "cc", + "pkg-config", +] + +[[package]] +name = "c2-chacha" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27dae93fe7b1e0424dc57179ac396908c26b035a87234809f5c4dfd1b47dc80" +dependencies = [ + "cipher 0.2.5", + "ppv-lite86", +] + [[package]] name = "cc" version = "1.2.16" @@ -687,6 +761,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-expr" version = "0.15.8" @@ -702,6 +785,28 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddf3c081b5fba1e5615640aae998e0fbd10c24cbd897ee39ed754a77601a4862" +dependencies = [ + "byteorder", + "keystream", +] + [[package]] name = "chacha20" version = "0.9.1" @@ -709,7 +814,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", - "cipher", + "cipher 0.4.4", "cpufeatures", ] @@ -721,11 +826,25 @@ checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ "aead", "chacha20", - "cipher", + "cipher 0.4.4", "poly1305", "zeroize", ] +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + [[package]] name = "cid" version = "0.9.0" @@ -752,6 +871,15 @@ dependencies = [ "unsigned-varint 0.7.2", ] +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array 0.14.7", +] + [[package]] name = "cipher" version = "0.4.4" @@ -763,6 +891,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "combine" version = "4.6.7" @@ -788,6 +927,19 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "console" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width", + "windows-sys 0.59.0", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -1049,9 +1201,9 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ - "generic-array", + "generic-array 0.14.7", "rand_core 0.6.4", - "subtle", + "subtle 2.6.1", "zeroize", ] @@ -1061,19 +1213,29 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array", + "generic-array 0.14.7", "rand_core 0.6.4", "typenum", ] +[[package]] +name = "crypto-mac" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" +dependencies = [ + "generic-array 0.12.4", + "subtle 1.0.0", +] + [[package]] name = "crypto-mac" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ - "generic-array", - "subtle", + "generic-array 0.14.7", + "subtle 2.6.1", ] [[package]] @@ -1082,7 +1244,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher", + "cipher 0.4.4", ] [[package]] @@ -1097,7 +1259,7 @@ dependencies = [ "digest 0.10.7", "fiat-crypto", "rustc_version", - "subtle", + "subtle 2.6.1", "zeroize", ] @@ -1112,6 +1274,19 @@ dependencies = [ "syn 2.0.99", ] +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core 0.9.10", +] + [[package]] name = "data-encoding" version = "2.8.0" @@ -1246,13 +1421,22 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + [[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -1264,7 +1448,16 @@ dependencies = [ "block-buffer 0.10.4", "const-oid", "crypto-common", - "subtle", + "subtle 2.6.1", +] + +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", ] [[package]] @@ -1277,6 +1470,18 @@ dependencies = [ "dirs-sys-next", ] +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -1401,7 +1606,7 @@ dependencies = [ "rand_core 0.6.4", "serde", "sha2 0.10.8", - "subtle", + "subtle 2.6.1", "zeroize", ] @@ -1436,16 +1641,22 @@ dependencies = [ "crypto-bigint", "digest 0.10.7", "ff", - "generic-array", + "generic-array 0.14.7", "group", "pkcs8", "rand_core 0.6.4", "sec1", "serdect", - "subtle", + "subtle 2.6.1", "zeroize", ] +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "enum-as-inner" version = "0.5.1" @@ -1577,6 +1788,30 @@ dependencies = [ "staging-xcm-executor", ] +[[package]] +name = "epico-receipt-service" +version = "0.1.0" +dependencies = [ + "ep-account", + "frame-system", + "futures", + "pallet-balances", + "pallet-epico", + "pallet-epico-runtime-api", + "parity-scale-codec", + "sc-client-api", + "sc-network", + "sc-offchain", + "sc-service", + "serde", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-io", + "sp-runtime", + "tracing", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -1668,13 +1903,22 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "exit-future" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e43f2f1833d64e33f15592464d6fdd70f349dda7b1a53088eb83cd94014008c5" +dependencies = [ + "futures", +] + [[package]] name = "expander" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2c470c71d91ecbd179935b24170459e926382eaaa86b590b78814e180d8a8e2" dependencies = [ - "blake2", + "blake2 0.10.6", "file-guard", "fs-err", "prettyplease", @@ -1702,7 +1946,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ "rand_core 0.6.4", - "subtle", + "subtle 2.6.1", ] [[package]] @@ -1803,6 +2047,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "forwarded-header-value" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9" +dependencies = [ + "nonempty", + "thiserror 1.0.69", +] + [[package]] name = "fragile" version = "2.0.0" @@ -1959,6 +2213,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "funty" version = "2.0.0" @@ -2074,7 +2338,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" dependencies = [ "gloo-timers", - "send_wrapper", + "send_wrapper 0.4.0", ] [[package]] @@ -2104,6 +2368,15 @@ dependencies = [ "byteorder", ] +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -2154,7 +2427,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" dependencies = [ - "opaque-debug", + "opaque-debug 0.3.1", "polyval", ] @@ -2175,6 +2448,12 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + [[package]] name = "gloo-net" version = "0.6.0" @@ -2221,6 +2500,26 @@ dependencies = [ "web-sys", ] +[[package]] +name = "governor" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a7f542ee6b35af73b06abc0dad1c1bae89964e4e253bc4b587b91c9637867b" +dependencies = [ + "cfg-if", + "dashmap", + "futures", + "futures-timer", + "no-std-compat", + "nonzero_ext", + "parking_lot 0.12.3", + "portable-atomic", + "quanta", + "rand 0.8.5", + "smallvec", + "spinning_top", +] + [[package]] name = "group" version = "0.13.0" @@ -2229,7 +2528,7 @@ checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", "rand_core 0.6.4", - "subtle", + "subtle 2.6.1", ] [[package]] @@ -2321,6 +2620,15 @@ dependencies = [ "foldhash", ] +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown 0.14.5", +] + [[package]] name = "heck" version = "0.4.1" @@ -2429,7 +2737,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" dependencies = [ - "crypto-mac", + "crypto-mac 0.8.0", "digest 0.9.0", ] @@ -2449,7 +2757,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ "digest 0.9.0", - "generic-array", + "generic-array 0.14.7", "hmac 0.8.1", ] @@ -2621,6 +2929,30 @@ dependencies = [ "tracing", ] +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core 0.61.2", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "icu_collections" version = "1.5.0" @@ -2937,7 +3269,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -3279,6 +3611,12 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "keystream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33070833c9ee02266356de0c43f723152bd38bd96ddf52c82b3af10c9138b28" + [[package]] name = "kvdb" version = "0.13.0" @@ -3289,22 +3627,68 @@ dependencies = [ ] [[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "libc" -version = "0.2.170" +name = "kvdb-memorydb" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" +checksum = "bf7a85fe66f9ff9cd74e169fdd2c94c6e1e74c412c99a73b4df3200b5d3760b2" +dependencies = [ + "kvdb", + "parking_lot 0.12.3", +] [[package]] -name = "libp2p" -version = "0.52.4" +name = "kvdb-rocksdb" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94495eb319a85b70a68b85e2389a95bb3555c71c49025b78c691a854a7e6464" +checksum = "b644c70b92285f66bfc2032922a79000ea30af7bc2ab31902992a5dcb9b434f6" +dependencies = [ + "kvdb", + "num_cpus", + "parking_lot 0.12.3", + "regex", + "rocksdb", + "smallvec", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.170" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" + +[[package]] +name = "libloading" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "libp2p" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94495eb319a85b70a68b85e2389a95bb3555c71c49025b78c691a854a7e6464" dependencies = [ "bytes", "either", @@ -3328,6 +3712,7 @@ dependencies = [ "libp2p-swarm", "libp2p-tcp", "libp2p-upnp", + "libp2p-wasm-ext", "libp2p-websocket", "libp2p-yamux", "multiaddr 0.18.2", @@ -3685,6 +4070,20 @@ dependencies = [ "void", ] +[[package]] +name = "libp2p-wasm-ext" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e5d8e3a9e07da0ef5b55a9f26c009c8fb3c725d492d8bb4b431715786eea79c" +dependencies = [ + "futures", + "js-sys", + "libp2p-core", + "send_wrapper 0.6.0", + "wasm-bindgen", + "wasm-bindgen-futures", +] + [[package]] name = "libp2p-websocket" version = "0.42.2" @@ -3729,6 +4128,21 @@ dependencies = [ "libc", ] +[[package]] +name = "librocksdb-sys" +version = "0.11.0+8.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3386f101bcb4bd252d8e9d2fb41ec3b0862a15a62b478c355b2982efa469e3e" +dependencies = [ + "bindgen", + "bzip2-sys", + "cc", + "glob", + "libc", + "libz-sys", + "tikv-jemalloc-sys", +] + [[package]] name = "libsecp256k1" version = "0.7.1" @@ -3756,7 +4170,7 @@ checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" dependencies = [ "crunchy", "digest 0.9.0", - "subtle", + "subtle 2.6.1", ] [[package]] @@ -3777,6 +4191,17 @@ dependencies = [ "libsecp256k1-core", ] +[[package]] +name = "libz-sys" +version = "1.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -3819,6 +4244,18 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9" +[[package]] +name = "lioness" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae926706ba42c425c9457121178330d75e273df2e82e28b758faf3de3a9acb9" +dependencies = [ + "arrayref", + "blake2 0.8.1", + "chacha", + "keystream", +] + [[package]] name = "litemap" version = "0.7.5" @@ -3910,6 +4347,25 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "lz4" +version = "1.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a20b523e860d03443e98350ceaac5e71c6ba89aea7d960769ec3ce37f4de5af4" +dependencies = [ + "lz4-sys", +] + +[[package]] +name = "lz4-sys" +version = "1.11.1+lz4-1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bd8c0d6c6ed0cd30b3652886bb8711dc4bb01d637a68105a3d5158039b418e6" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "mach" version = "0.3.2" @@ -4013,6 +4469,24 @@ dependencies = [ "rustix 0.38.44", ] +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.8.0" @@ -4069,6 +4543,31 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "mixnet" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daa3eb39495d8e2e2947a1d862852c90cc6a4a8845f8b41c8829cb9fcc047f4a" +dependencies = [ + "arrayref", + "arrayvec", + "bitflags 1.3.2", + "blake2 0.10.6", + "c2-chacha", + "curve25519-dalek", + "either", + "hashlink", + "lioness", + "log", + "parking_lot 0.12.3", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_distr", + "subtle 2.6.1", + "thiserror 1.0.69", + "zeroize", +] + [[package]] name = "mockall" version = "0.11.4" @@ -4351,6 +4850,12 @@ dependencies = [ "libc", ] +[[package]] +name = "no-std-compat" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" + [[package]] name = "nohash-hasher" version = "0.2.0" @@ -4367,6 +4872,18 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nonempty" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" + +[[package]] +name = "nonzero_ext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" + [[package]] name = "normalize-line-endings" version = "0.3.0" @@ -4445,6 +4962,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -4502,6 +5020,12 @@ version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + [[package]] name = "opaque-debug" version = "0.3.1" @@ -4514,6 +5038,12 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "overload" version = "0.1.1" @@ -4615,6 +5145,27 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "parity-db" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "592a28a24b09c9dc20ac8afaa6839abc417c720afe42c12e1e4a9d6aa2508d2e" +dependencies = [ + "blake2 0.10.6", + "crc32fast", + "fs2", + "hex", + "libc", + "log", + "lz4", + "memmap2 0.5.10", + "parking_lot 0.12.3", + "rand 0.8.5", + "siphasher", + "snap", + "winapi", +] + [[package]] name = "parity-scale-codec" version = "3.7.4" @@ -4718,7 +5269,7 @@ checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" dependencies = [ "base64ct", "rand_core 0.6.4", - "subtle", + "subtle 2.6.1", ] [[package]] @@ -4737,6 +5288,12 @@ dependencies = [ "password-hash", ] +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "pem" version = "1.1.1" @@ -4927,7 +5484,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ "cpufeatures", - "opaque-debug", + "opaque-debug 0.3.1", "universal-hash", ] @@ -4939,10 +5496,16 @@ checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", "cpufeatures", - "opaque-debug", + "opaque-debug 0.3.1", "universal-hash", ] +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + [[package]] name = "powerfmt" version = "0.2.0" @@ -5218,6 +5781,21 @@ dependencies = [ "cc", ] +[[package]] +name = "quanta" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" +dependencies = [ + "crossbeam-utils", + "libc", + "once_cell", + "raw-cpuid", + "wasi 0.11.0+wasi-snapshot-preview1", + "web-sys", + "winapi", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -5369,6 +5947,34 @@ dependencies = [ "getrandom 0.3.1", ] +[[package]] +name = "rand_distr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "rand_pcg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "raw-cpuid" +version = "11.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" +dependencies = [ + "bitflags 2.9.0", +] + [[package]] name = "rawpointer" version = "0.2.1" @@ -5529,7 +6135,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ "hmac 0.12.1", - "subtle", + "subtle 2.6.1", ] [[package]] @@ -5583,6 +6189,16 @@ dependencies = [ "syn 2.0.99", ] +[[package]] +name = "rocksdb" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6f170a4041d50a0ce04b0d2e14916d6ca863ea2e422689a5b694395d299ffe" +dependencies = [ + "libc", + "librocksdb-sys", +] + [[package]] name = "route-recognizer" version = "0.3.1" @@ -5723,7 +6339,7 @@ dependencies = [ "ring 0.17.13", "rustls-pki-types", "rustls-webpki 0.102.8", - "subtle", + "subtle 2.6.1", "zeroize", ] @@ -5867,6 +6483,59 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "sc-block-builder" +version = "0.43.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "parity-scale-codec", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-trie", +] + +[[package]] +name = "sc-chain-spec" +version = "41.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "array-bytes", + "docify", + "log", + "memmap2 0.9.8", + "parity-scale-codec", + "sc-chain-spec-derive", + "sc-client-api", + "sc-executor", + "sc-network", + "sc-telemetry", + "serde", + "serde_json", + "sp-blockchain", + "sp-core", + "sp-crypto-hashing", + "sp-genesis-builder", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-tracing", +] + +[[package]] +name = "sc-chain-spec-derive" +version = "12.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "proc-macro-crate 3.3.0", + "proc-macro2", + "quote", + "syn 2.0.99", +] + [[package]] name = "sc-client-api" version = "38.0.0" @@ -5895,22 +6564,48 @@ dependencies = [ ] [[package]] -name = "sc-consensus" -version = "0.47.0" +name = "sc-client-db" +version = "0.45.0" source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" dependencies = [ - "async-trait", - "futures", + "hash-db", + "kvdb", + "kvdb-memorydb", + "kvdb-rocksdb", + "linked-hash-map", "log", - "mockall 0.11.4", + "parity-db", + "parity-scale-codec", "parking_lot 0.12.3", "sc-client-api", - "sc-network-types", - "sc-utils", - "serde", - "sp-api", + "sc-state-db", + "schnellru", + "sp-arithmetic", "sp-blockchain", - "sp-consensus", + "sp-core", + "sp-database", + "sp-runtime", + "sp-state-machine", + "sp-trie", +] + +[[package]] +name = "sc-consensus" +version = "0.47.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "async-trait", + "futures", + "log", + "mockall 0.11.4", + "parking_lot 0.12.3", + "sc-client-api", + "sc-network-types", + "sc-utils", + "serde", + "sp-api", + "sp-blockchain", + "sp-consensus", "sp-core", "sp-runtime", "sp-state-machine", @@ -5983,6 +6678,66 @@ dependencies = [ "wasmtime", ] +[[package]] +name = "sc-informant" +version = "0.47.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "console", + "futures", + "futures-timer", + "log", + "sc-client-api", + "sc-network", + "sc-network-common", + "sc-network-sync", + "sp-blockchain", + "sp-runtime", +] + +[[package]] +name = "sc-keystore" +version = "34.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "array-bytes", + "parking_lot 0.12.3", + "serde_json", + "sp-application-crypto", + "sp-core", + "sp-keystore", + "thiserror 1.0.69", +] + +[[package]] +name = "sc-mixnet" +version = "0.18.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "array-bytes", + "arrayvec", + "blake2 0.10.6", + "bytes", + "futures", + "futures-timer", + "log", + "mixnet", + "multiaddr 0.18.2", + "parity-scale-codec", + "parking_lot 0.12.3", + "sc-client-api", + "sc-network", + "sc-network-types", + "sc-transaction-pool-api", + "sp-api", + "sp-consensus", + "sp-core", + "sp-keystore", + "sp-mixnet", + "sp-runtime", + "thiserror 1.0.69", +] + [[package]] name = "sc-network" version = "0.48.2" @@ -6052,6 +6807,27 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "sc-network-light" +version = "0.47.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "array-bytes", + "async-channel", + "futures", + "log", + "parity-scale-codec", + "prost 0.12.6", + "prost-build", + "sc-client-api", + "sc-network", + "sc-network-types", + "sp-blockchain", + "sp-core", + "sp-runtime", + "thiserror 1.0.69", +] + [[package]] name = "sc-network-sync" version = "0.47.0" @@ -6069,77 +6845,390 @@ dependencies = [ "prost 0.12.6", "prost-build", "sc-client-api", - "sc-consensus", - "sc-network", - "sc-network-common", - "sc-network-types", - "sc-utils", - "schnellru", - "smallvec", - "sp-arithmetic", + "sc-consensus", + "sc-network", + "sc-network-common", + "sc-network-types", + "sc-utils", + "schnellru", + "smallvec", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-consensus-grandpa", + "sp-core", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror 1.0.69", + "tokio", + "tokio-stream", +] + +[[package]] +name = "sc-network-transactions" +version = "0.47.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "array-bytes", + "futures", + "log", + "parity-scale-codec", + "sc-network", + "sc-network-common", + "sc-network-sync", + "sc-network-types", + "sc-utils", + "sp-consensus", + "sp-runtime", + "substrate-prometheus-endpoint", +] + +[[package]] +name = "sc-network-types" +version = "0.15.1" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "bs58", + "ed25519-dalek", + "libp2p-identity", + "litep2p", + "log", + "multiaddr 0.18.2", + "multihash 0.19.3", + "rand 0.8.5", + "thiserror 1.0.69", + "zeroize", +] + +[[package]] +name = "sc-offchain" +version = "43.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "array-bytes", + "bytes", + "fnv", + "futures", + "futures-timer", + "http-body-util", + "hyper 1.6.0", + "hyper-rustls", + "hyper-util", + "log", + "num_cpus", + "once_cell", + "parity-scale-codec", + "parking_lot 0.12.3", + "rand 0.8.5", + "rustls 0.23.23", + "sc-client-api", + "sc-network", + "sc-network-common", + "sc-network-types", + "sc-transaction-pool-api", + "sc-utils", + "sp-api", + "sp-core", + "sp-externalities", + "sp-keystore", + "sp-offchain", + "sp-runtime", + "threadpool", + "tracing", +] + +[[package]] +name = "sc-rpc" +version = "43.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "futures", + "jsonrpsee", + "log", + "parity-scale-codec", + "parking_lot 0.12.3", + "sc-block-builder", + "sc-chain-spec", + "sc-client-api", + "sc-mixnet", + "sc-rpc-api", + "sc-tracing", + "sc-transaction-pool-api", + "sc-utils", + "serde_json", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-keystore", + "sp-offchain", + "sp-rpc", + "sp-runtime", + "sp-session", + "sp-statement-store", + "sp-version", + "tokio", +] + +[[package]] +name = "sc-rpc-api" +version = "0.47.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "jsonrpsee", + "parity-scale-codec", + "sc-chain-spec", + "sc-mixnet", + "sc-transaction-pool-api", + "scale-info", + "serde", + "serde_json", + "sp-core", + "sp-rpc", + "sp-runtime", + "sp-version", + "thiserror 1.0.69", +] + +[[package]] +name = "sc-rpc-server" +version = "20.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "dyn-clone", + "forwarded-header-value", + "futures", + "governor", + "http 1.2.0", + "http-body-util", + "hyper 1.6.0", + "ip_network", + "jsonrpsee", + "log", + "sc-rpc-api", + "serde", + "serde_json", + "substrate-prometheus-endpoint", + "tokio", + "tower", + "tower-http", +] + +[[package]] +name = "sc-rpc-spec-v2" +version = "0.48.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "array-bytes", + "futures", + "futures-util", + "hex", + "itertools 0.11.0", + "jsonrpsee", + "log", + "parity-scale-codec", + "parking_lot 0.12.3", + "rand 0.8.5", + "sc-chain-spec", + "sc-client-api", + "sc-rpc", + "sc-transaction-pool-api", + "schnellru", + "serde", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-rpc", + "sp-runtime", + "sp-version", + "thiserror 1.0.69", + "tokio", + "tokio-stream", +] + +[[package]] +name = "sc-service" +version = "0.49.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "async-trait", + "directories", + "exit-future", + "futures", + "futures-timer", + "jsonrpsee", + "log", + "parity-scale-codec", + "parking_lot 0.12.3", + "pin-project", + "rand 0.8.5", + "sc-chain-spec", + "sc-client-api", + "sc-client-db", + "sc-consensus", + "sc-executor", + "sc-informant", + "sc-keystore", + "sc-network", + "sc-network-common", + "sc-network-light", + "sc-network-sync", + "sc-network-transactions", + "sc-network-types", + "sc-rpc", + "sc-rpc-server", + "sc-rpc-spec-v2", + "sc-sysinfo", + "sc-telemetry", + "sc-tracing", + "sc-transaction-pool", + "sc-transaction-pool-api", + "sc-utils", + "schnellru", + "serde", + "serde_json", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-externalities", + "sp-keystore", + "sp-runtime", + "sp-session", + "sp-state-machine", + "sp-storage", + "sp-transaction-pool", + "sp-transaction-storage-proof", + "sp-trie", + "sp-version", + "static_init", + "substrate-prometheus-endpoint", + "tempfile", + "thiserror 1.0.69", + "tokio", + "tracing", + "tracing-futures", +] + +[[package]] +name = "sc-state-db" +version = "0.37.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "log", + "parity-scale-codec", + "parking_lot 0.12.3", + "sp-core", +] + +[[package]] +name = "sc-sysinfo" +version = "41.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "derive_more 0.99.19", + "futures", + "libc", + "log", + "rand 0.8.5", + "rand_pcg", + "regex", + "sc-telemetry", + "serde", + "serde_json", + "sp-core", + "sp-crypto-hashing", + "sp-io", + "sp-std", +] + +[[package]] +name = "sc-telemetry" +version = "28.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "chrono", + "futures", + "libp2p", + "log", + "parking_lot 0.12.3", + "pin-project", + "rand 0.8.5", + "sc-network", + "sc-utils", + "serde", + "serde_json", + "thiserror 1.0.69", + "wasm-timer", +] + +[[package]] +name = "sc-tracing" +version = "38.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "chrono", + "console", + "is-terminal", + "libc", + "log", + "parity-scale-codec", + "parking_lot 0.12.3", + "rustc-hash 1.1.0", + "sc-client-api", + "sc-tracing-proc-macro", + "serde", + "sp-api", "sp-blockchain", - "sp-consensus", - "sp-consensus-grandpa", "sp-core", + "sp-rpc", "sp-runtime", - "substrate-prometheus-endpoint", + "sp-tracing", "thiserror 1.0.69", - "tokio", - "tokio-stream", + "tracing", + "tracing-log", + "tracing-subscriber", ] [[package]] -name = "sc-network-types" -version = "0.15.1" +name = "sc-tracing-proc-macro" +version = "11.0.0" source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" dependencies = [ - "bs58", - "ed25519-dalek", - "libp2p-identity", - "litep2p", - "log", - "multiaddr 0.18.2", - "multihash 0.19.3", - "rand 0.8.5", - "thiserror 1.0.69", - "zeroize", + "proc-macro-crate 3.3.0", + "proc-macro2", + "quote", + "syn 2.0.99", ] [[package]] -name = "sc-offchain" -version = "43.0.0" +name = "sc-transaction-pool" +version = "38.1.0" source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" dependencies = [ - "array-bytes", - "bytes", - "fnv", + "async-trait", "futures", "futures-timer", - "http-body-util", - "hyper 1.6.0", - "hyper-rustls", - "hyper-util", + "indexmap 2.7.1", + "itertools 0.11.0", + "linked-hash-map", "log", - "num_cpus", - "once_cell", "parity-scale-codec", "parking_lot 0.12.3", - "rand 0.8.5", - "rustls 0.23.23", "sc-client-api", - "sc-network", - "sc-network-common", - "sc-network-types", "sc-transaction-pool-api", "sc-utils", + "serde", "sp-api", + "sp-blockchain", "sp-core", - "sp-externalities", - "sp-keystore", - "sp-offchain", + "sp-crypto-hashing", "sp-runtime", - "threadpool", - "tracing", + "sp-tracing", + "sp-transaction-pool", + "substrate-prometheus-endpoint", + "thiserror 1.0.69", + "tokio", + "tokio-stream", ] [[package]] @@ -6233,7 +7322,7 @@ dependencies = [ "rand_core 0.6.4", "serde_bytes", "sha2 0.10.8", - "subtle", + "subtle 2.6.1", "zeroize", ] @@ -6261,10 +7350,10 @@ checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", "der", - "generic-array", + "generic-array 0.14.7", "pkcs8", "serdect", - "subtle", + "subtle 2.6.1", "zeroize", ] @@ -6344,6 +7433,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" + [[package]] name = "serde" version = "1.0.218" @@ -6425,7 +7520,7 @@ dependencies = [ "cfg-if", "cpufeatures", "digest 0.9.0", - "opaque-debug", + "opaque-debug 0.3.1", ] [[package]] @@ -6502,6 +7597,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "620a1d43d70e142b1d46a929af51d44f383db9c7a2ec122de2cd992ccfcf3c18" +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "slab" version = "0.4.9" @@ -6523,6 +7624,12 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +[[package]] +name = "snap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" + [[package]] name = "snow" version = "0.9.6" @@ -6530,14 +7637,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "850948bee068e713b8ab860fe1adc4d109676ab4c3b621fd8147f06b261f2f85" dependencies = [ "aes-gcm", - "blake2", + "blake2 0.10.6", "chacha20poly1305", "curve25519-dalek", "rand_core 0.6.4", "ring 0.17.13", "rustc_version", "sha2 0.10.8", - "subtle", + "subtle 2.6.1", ] [[package]] @@ -6604,7 +7711,7 @@ version = "21.0.0" source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" dependencies = [ "Inflector", - "blake2", + "blake2 0.10.6", "expander", "proc-macro-crate 3.3.0", "proc-macro2", @@ -6638,6 +7745,16 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "sp-block-builder" +version = "35.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "sp-api", + "sp-inherents", + "sp-runtime", +] + [[package]] name = "sp-blockchain" version = "38.0.0" @@ -6696,7 +7813,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412 dependencies = [ "array-bytes", "bitflags 1.3.2", - "blake2", + "blake2 0.10.6", "bounded-collections", "bs58", "dyn-clonable", @@ -6868,6 +7985,17 @@ dependencies = [ "scale-info", ] +[[package]] +name = "sp-mixnet" +version = "0.13.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto", +] + [[package]] name = "sp-offchain" version = "35.0.0" @@ -6887,6 +8015,16 @@ dependencies = [ "regex", ] +[[package]] +name = "sp-rpc" +version = "33.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "rustc-hash 1.1.0", + "serde", + "sp-core", +] + [[package]] name = "sp-runtime" version = "40.1.0" @@ -6948,6 +8086,20 @@ dependencies = [ "syn 2.0.99", ] +[[package]] +name = "sp-session" +version = "37.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-keystore", + "sp-runtime", + "sp-staking", +] + [[package]] name = "sp-staking" version = "37.0.0" @@ -7033,6 +8185,29 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "sp-transaction-pool" +version = "35.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "sp-api", + "sp-runtime", +] + +[[package]] +name = "sp-transaction-storage-proof" +version = "35.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-2#863f353d20749caccfd1ccd548cc0cd85291f3a8" +dependencies = [ + "async-trait", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-trie", +] + [[package]] name = "sp-trie" version = "38.0.0" @@ -7116,6 +8291,15 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spinning_top" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" +dependencies = [ + "lock_api", +] + [[package]] name = "spki" version = "0.7.3" @@ -7216,6 +8400,34 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "static_init" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bae1df58c5fea7502e8e352ec26b5579f6178e1fdb311e088580c980dee25ed" +dependencies = [ + "bitflags 1.3.2", + "cfg_aliases 0.2.1", + "libc", + "parking_lot 0.12.3", + "parking_lot_core 0.9.10", + "static_init_macro", + "winapi", +] + +[[package]] +name = "static_init_macro" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1389c88ddd739ec6d3f8f83343764a0e944cd23cfbf126a9796a714b0b6edd6f" +dependencies = [ + "cfg_aliases 0.1.1", + "memchr", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "substrate-bip39" version = "0.6.0" @@ -7242,6 +8454,12 @@ dependencies = [ "tokio", ] +[[package]] +name = "subtle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" + [[package]] name = "subtle" version = "2.6.1" @@ -7414,6 +8632,16 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "tikv-jemalloc-sys" +version = "0.5.4+5.3.0-patched" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9402443cb8fd499b6f327e40565234ff34dbda27460c5b47db0db77443dd85d1" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "time" version = "0.3.39" @@ -7617,6 +8845,22 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower-http" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +dependencies = [ + "bitflags 2.9.0", + "bytes", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "pin-project-lite", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -7662,6 +8906,16 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + [[package]] name = "tracing-log" version = "0.2.0" @@ -7682,6 +8936,7 @@ dependencies = [ "matchers", "nu-ansi-term", "once_cell", + "parking_lot 0.12.3", "regex", "sharded-slab", "smallvec", @@ -7886,6 +9141,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-width" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -7899,7 +9160,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ "crypto-common", - "subtle", + "subtle 2.6.1", ] [[package]] @@ -7971,6 +9232,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" @@ -8439,7 +9706,7 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efc5cf48f83140dcaab716eeaea345f9e93d0018fb81162753a3f76c3397b538" dependencies = [ - "windows-core", + "windows-core 0.53.0", "windows-targets 0.52.6", ] @@ -8449,10 +9716,51 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dcc5b895a6377f1ab9fa55acedab1fd5ac0db66ad1e6c7f47e28a22e446a5dd" dependencies = [ - "windows-result", + "windows-result 0.1.2", "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result 0.3.4", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.99", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.99", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-result" version = "0.1.2" @@ -8462,6 +9770,24 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.45.0" diff --git a/Cargo.toml b/Cargo.toml index 34bce8c..d113f1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ members = [ "rpc", "pallet-epico", "primitives/*", + "receipt-service" ] resolver = "2" @@ -22,6 +23,7 @@ frame-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk", tag = futures = { version = "0.3.30" } jsonrpsee = { version = "0.24.3" } log = { version = "0.4.21", default-features = false } +tracing = { version = "0.1.41", default-features = false } sc-network = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-stable2412-2", default-features = false } sc-network-sync = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-stable2412-2", default-features = false } sc-transaction-pool = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-stable2412-2", default-features = false } @@ -34,6 +36,7 @@ libsecp256k1 = { version = "0.7.1", default-features = false } sc-client-api = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-stable2412-2", default-features = false } sp-consensus = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-stable2412-2", default-features = false } sc-offchain = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-stable2412-2", default-features = false } +sc-service = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-stable2412-2", default-features = false } sp-api = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-stable2412-2", default-features = false } sp-blockchain = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-stable2412-2", default-features = false } sp-core = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-stable2412-2", default-features = false } diff --git a/pallet-epico/runtime-api/src/lib.rs b/pallet-epico/runtime-api/src/lib.rs index 4c7c8f8..46b3064 100644 --- a/pallet-epico/runtime-api/src/lib.rs +++ b/pallet-epico/runtime-api/src/lib.rs @@ -5,7 +5,7 @@ use ep_ethereum::{ common::TransactionV2, ethereum_types::{Address, H160, U256}, }; -use pallet_epico::types::TransactionCall; +use pallet_epico::types::{TransactionCall, TransactionReceipt}; use parity_scale_codec::{Decode, Encode}; use sp_runtime::traits::Block as BlockT; use sp_runtime::Vec; @@ -45,6 +45,10 @@ sp_api::decl_runtime_apis! { /// Retrieves the timestamp for the current block. fn block_timestamp() -> U256; + /// Runtime api for returning transaction receipts of all `pallet_epico::ExecutedTransaction` + /// in a given block. + fn build_receipt_at(block_number: u32) -> Vec>; + /// Performs a dry-run of the given Ethereum-style transaction without modifying state. /// /// Returns `Ok(())` if the transaction would succeed, or an `Err` containing the diff --git a/pallet-epico/src/types.rs b/pallet-epico/src/types.rs index e0818d3..c90ad3f 100644 --- a/pallet-epico/src/types.rs +++ b/pallet-epico/src/types.rs @@ -163,7 +163,7 @@ pub fn default_transaction_from_tx_call(call: TransactionCall) -> Transaction { Transaction::Legacy(transaction) } -#[derive(Clone, Eq, PartialEq)] +#[derive(Clone, Eq, PartialEq, Debug)] pub enum OffchainStatus { Untrusted, Finalized, diff --git a/receipt-service/Cargo.toml b/receipt-service/Cargo.toml new file mode 100644 index 0000000..59d9dd4 --- /dev/null +++ b/receipt-service/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "epico-receipt-service" +version = "0.1.0" +description = "Ethereum RPC" +authors = { workspace = true } +edition = { workspace = true } +repository = { workspace = true } + +[dependencies] +serde = { workspace = true, default-features = true } +parity-scale-codec = { workspace = true, default-features = true } +tracing = { workspace = true, default-features = true } +futures = { workspace = true, default-features = true } +frame-system = { workspace = true, default-features = true } +sc-service = { workspace = true, default-features = true } +sc-client-api = { workspace = true, default-features = true } +sc-offchain = { workspace = true, default-features = true } +sp-api = { workspace = true, default-features = true } +sp-blockchain = { workspace = true, default-features = true } +sp-core = { workspace = true, default-features = true } +sp-runtime = { workspace = true, default-features = true } +sp-io = { workspace = true, default-features = true } +sc-network = { workspace = true, default-features = true } +pallet-balances = { workspace = true, default-features = true } +pallet-epico-runtime-api = { workspace = true, default-features = true } +pallet-epico = { workspace = true, default-features = true } +ep-account = { workspace = true, default-features = true } + +[features] +default = ["std"] +std = [] diff --git a/receipt-service/src/lib.rs b/receipt-service/src/lib.rs new file mode 100644 index 0000000..d49b520 --- /dev/null +++ b/receipt-service/src/lib.rs @@ -0,0 +1,549 @@ +use std::sync::Arc; + +use frame_system::pallet_prelude::BlockNumberFor; +use futures::StreamExt; +use parity_scale_codec::{Decode, Encode}; +use sc_client_api::AuxStore; +use sc_network::peer_store::LOG_TARGET; +use sc_offchain::OffchainDb; +use sc_service::Configuration; +use sp_api::ApiExt; +use sp_core::{ + offchain, + offchain::{DbExternalities, StorageKind}, + traits::SpawnNamed, + H160, H256, +}; +use sp_runtime::{ + traits::{Block as BlockT, Header, Header as HeaderT, PhantomData, UniqueSaturatedInto}, + SaturatedConversion, +}; + +use ep_account::AccountId20; +use pallet_epico::types::{ + get_block_transaction_offchain_key, get_tx_offchain_key, OffchainStatus, TransactionReceipt, +}; +use pallet_epico_runtime_api::EpicoRuntimeApi; + +const AUX_WM_KEY: &[u8] = b"receipt_service.finalized_watermark"; +const AUX_FIN_KEPT_FROM: &[u8] = b"receipt_service.finalized_kept_from"; +const AUX_UNT_KEPT_FROM: &[u8] = b"receipt_service.untrusted_kept_from"; + +#[derive(Encode, Decode, Clone, Default, Debug)] +struct FinalizedWatermark { + up_to: u64, +} + +#[derive(Encode, Decode, Clone, Default, Debug)] +struct ReceiptsKeptFrom { + block_number: u64, +} + +impl From for ReceiptsKeptFrom { + fn from(block_number: u64) -> Self { + ReceiptsKeptFrom { block_number } + } +} + +#[derive(Clone, Debug, Encode, Decode)] +pub struct ReceiptGcPolicy { + /// None means keep all finalized receipts. + pub keep_blocks: Option, + /// how many blocks to prune per execution cycle. + pub gc_step: u64, +} + +impl From<&Configuration> for ReceiptGcPolicy { + fn from(cfg: &Configuration) -> Self { + let keep_finalized_blocks = { + match cfg.state_pruning.clone() { + Some(sc_service::PruningMode::Constrained(blocks)) + if blocks.max_blocks.is_some() => + { + // max_blocks should default to 0 when unspecified + // as per `sc_state_db::Constraints` documentation. + Some(blocks.max_blocks.unwrap_or(0).into()) + } + _ => None, + } + }; + ReceiptGcPolicy { + keep_blocks: keep_finalized_blocks, + gc_step: 1_000, + } + } +} + +#[derive(Encode, Decode, Debug)] +pub struct ReceiptService { + pub client: Arc, + pub storage: OffchainDb, + pub gc_policy: ReceiptGcPolicy, + pub _phantom: PhantomData<(Client, Block, Runtime)>, +} + +impl ReceiptService +where + Block: BlockT, + Client: sp_api::ProvideRuntimeApi + + sc_client_api::BlockBackend + + sc_client_api::blockchain::HeaderBackend + + sp_runtime::traits::BlockIdTo + + sc_client_api::ExecutorProvider + + sc_client_api::UsageProvider + + sp_blockchain::HeaderMetadata + + sc_client_api::BlockchainEvents + + AuxStore + + Send + + Sync + + 'static, + Client::Api: + pallet_epico_runtime_api::EpicoRuntimeApi, + ::Hash: std::marker::Unpin, + Runtime: pallet_epico::Config + Send + Sync, + ::AccountId: From + From, + Storage: sp_runtime::offchain::OffchainStorage + 'static, +{ + pub fn new(cfg: &Configuration, client: Arc, storage: Storage) -> Self { + Self { + client, + storage: OffchainDb::new(storage), + gc_policy: cfg.into(), + _phantom: Default::default(), + } + } + + fn load_untrusted_kept_from(&self) -> ReceiptsKeptFrom { + self.client + .get_aux(AUX_UNT_KEPT_FROM) + .ok() + .flatten() + .and_then(|v| ReceiptsKeptFrom::decode(&mut &v[..]).ok()) + .unwrap_or_default() + } + + fn persist_untrusted_kept_from(&self, kept_from: &ReceiptsKeptFrom) { + let _ = self + .client + .insert_aux(&[(AUX_UNT_KEPT_FROM, kept_from.encode().as_slice())], &[]); + } + + fn load_finalized_kept_from(&self) -> ReceiptsKeptFrom { + self.client + .get_aux(AUX_FIN_KEPT_FROM) + .ok() + .flatten() + .and_then(|v| ReceiptsKeptFrom::decode(&mut &v[..]).ok()) + .unwrap_or_default() + } + + fn persist_finalized_kept_from(&self, kept_from: &ReceiptsKeptFrom) { + let _ = self + .client + .insert_aux(&[(AUX_FIN_KEPT_FROM, kept_from.encode().as_slice())], &[]); + } + + fn do_prune_block( + storage: &mut OffchainDb, + offchain_status: OffchainStatus, + runtime_block: BlockNumberFor, + ) { + let block_transactions_key = + get_block_transaction_offchain_key(offchain_status.clone(), runtime_block.encode()); + let block_transactions: Vec = storage + .local_storage_get(StorageKind::PERSISTENT, &block_transactions_key) + .and_then(|v| Vec::::decode(&mut &v[..]).ok()) + .unwrap_or_default(); + + for tx_hash in block_transactions { + let tx_key = get_tx_offchain_key(offchain_status.clone(), tx_hash); + storage.local_storage_clear(StorageKind::PERSISTENT, &tx_key); + } + + storage.local_storage_clear(StorageKind::PERSISTENT, &block_transactions_key); + tracing::debug!( + target: LOG_TARGET, + "Pruned {:?} receipts from block_number: {}", offchain_status, runtime_block, + ); + } + + fn prune_receipts( + &self, + mut storage: OffchainDb, + offchain_status: OffchainStatus, + kept_from: u64, + last_to_keep: u64, + ) { + for block in kept_from..last_to_keep { + Self::do_prune_block( + &mut storage, + offchain_status.clone(), + block.unique_saturated_into(), + ); + tracing::info!( + target: LOG_TARGET, + "Pruned {:?} receipts from block_number: {}", offchain_status, block, + ); + } + } + + fn maybe_prune( + &self, + storage: OffchainDb, + offchain_status: OffchainStatus, + current_block: u64, + ) { + let blocks_to_keep = { + if let Some(keep_blocks) = self.gc_policy.keep_blocks { + keep_blocks + } else { + return; + } + }; + + let kept_from = { + match offchain_status { + OffchainStatus::Finalized => self.load_finalized_kept_from(), + OffchainStatus::Untrusted => self.load_untrusted_kept_from(), + } + }; + + let last_block_to_keep = { + let last = current_block.saturating_sub(blocks_to_keep); + if last.saturating_sub(kept_from.block_number) <= self.gc_policy.gc_step { + last + } else { + kept_from + .block_number + .saturating_add(self.gc_policy.gc_step) + } + }; + + if kept_from.block_number >= last_block_to_keep { + return; + } + + self.prune_receipts( + storage, + offchain_status.clone(), + kept_from.block_number, + last_block_to_keep, + ); + + tracing::debug!( + target: LOG_TARGET, + "Pruning receipts completed, pruned from block: {}, to block: {}", + kept_from.block_number, + last_block_to_keep, + ); + + match offchain_status { + OffchainStatus::Finalized => { + self.persist_finalized_kept_from(&last_block_to_keep.into()); + } + OffchainStatus::Untrusted => { + self.persist_untrusted_kept_from(&last_block_to_keep.into()); + } + } + } + + fn load_finalized_watermark(&self) -> FinalizedWatermark { + self.client + .get_aux(AUX_WM_KEY) + .ok() + .flatten() + .and_then(|v| FinalizedWatermark::decode(&mut &v[..]).ok()) + .unwrap_or_default() + } + + fn persist_finalized_watermark(&self, wm: &FinalizedWatermark) { + let _ = self + .client + .insert_aux(&[(AUX_WM_KEY, wm.encode().as_slice())], &[]); + } + + fn backfill_finalized_gap(&self, target_finalized: u64) { + let mut wm = self.load_finalized_watermark(); + + let mut n = { + if let Some(keep_blocks) = self.gc_policy.keep_blocks { + target_finalized.saturating_sub(keep_blocks) + } else { + wm.up_to + } + }; + let finalized_from_block_n = n; + while n < target_finalized { + let hash = { + if let Ok(Some(hash)) = self.client.block_hash(n.unique_saturated_into()) { + hash + } else { + tracing::error!( + target: LOG_TARGET, + "Failed to backfill finalized gap for block: {}. from {} to block: {}", + n, finalized_from_block_n, target_finalized + ); + // In case we don't have access to the state of older blocks + // wipe any existing receipts for this block. + Self::do_prune_block( + &mut self.storage.clone(), + OffchainStatus::Untrusted, + n.unique_saturated_into(), + ); + n += 1; + // Ignore any possible errors because we can't be certain that we have access + // to the state of older blocks. + continue; + } + }; + let receipts = self + .client + .runtime_api() + .build_receipt_at(hash, n.unique_saturated_into()) + .unwrap_or_default(); + Self::store_finalized_data_offchain(self.storage.clone(), n.saturated_into(), receipts); + wm.up_to = n; + self.persist_finalized_watermark(&wm); + self.persist_finalized_kept_from(&finalized_from_block_n.into()); + n += 1; + } + tracing::info!( + target: LOG_TARGET, + "ReceiptService worker `backfill_finalized_gap`, finalized_from_block_n: {:?} to target_finalized: {:?}", + finalized_from_block_n, target_finalized + ); + } + + /// Stores all epico executed transactions for the current block in offchain + /// persistent storage under the `Untrusted` prefix. + /// + /// This data is considered "untrusted" because the block may still be + /// reorged out of the canonical chain. + pub fn store_newest_data_offchain( + mut storage: OffchainDb, + current_block: BlockNumberFor, + receipts: Vec>, + ) { + // To avoid potential data leaks in reorgs, we ensure that no data is stored for this block + // prior to writing new data. + Self::do_prune_block(&mut storage, OffchainStatus::Untrusted, current_block); + let mut block_transactions: Vec = Vec::new(); + + // Iterate over all transactions executed in the block. + for r in receipts { + let transaction_hash = r.transaction_hash; + block_transactions.push(transaction_hash); + + let storage_key = get_tx_offchain_key(OffchainStatus::Untrusted, transaction_hash); + // Persist the transaction receipt under the "Untrusted" offchain key. + storage.local_storage_set(StorageKind::PERSISTENT, &storage_key, &r.encode()); + tracing::debug!( + target: LOG_TARGET, + "Untrusted offchain storage key {:?}", + &storage_key, + ); + } + + if block_transactions.is_empty() { + return; + } + + // Store a mapping of transaction hashes for this block. + storage.local_storage_set( + StorageKind::PERSISTENT, + &pallet_epico::types::get_block_transaction_offchain_key( + OffchainStatus::Untrusted, + current_block.encode(), + ), + &block_transactions.encode(), + ); + } + + /// Promotes transactions from a sufficiently old block to "Finalized" status + /// in the offchain DB. + /// + /// This assumes that blocks older than `SafeFinalityDepth` are finalized + /// and no longer subject to forks. + pub fn store_finalized_data_offchain( + mut storage: OffchainDb, + safe_finalized_block: BlockNumberFor, + receipts: Vec>, + ) { + let mut block_transactions: Vec = Vec::new(); + + // Iterate over all transactions in the finalized block. + for r in receipts { + let transaction_hash = r.transaction_hash; + block_transactions.push(transaction_hash); + + let storage_key = get_tx_offchain_key(OffchainStatus::Finalized, transaction_hash); + + // Move receipts to "Finalized" namespace. + storage.local_storage_set(StorageKind::PERSISTENT, &storage_key, &r.encode()); + + tracing::debug!( + target: LOG_TARGET, + "Finalized offchain storage key {:?}", + &storage_key, + ); + + // Remove the untrusted version of this receipt. + storage.local_storage_clear( + StorageKind::PERSISTENT, + &get_tx_offchain_key(OffchainStatus::Untrusted, transaction_hash), + ); + } + + if block_transactions.is_empty() { + return; + } + + // Store finalized transaction hash list and remove the untrusted copy. + storage.local_storage_set( + StorageKind::PERSISTENT, + &pallet_epico::types::get_block_transaction_offchain_key( + OffchainStatus::Finalized, + safe_finalized_block.encode(), + ), + &block_transactions.encode(), + ); + + storage.local_storage_clear( + StorageKind::PERSISTENT, + &pallet_epico::types::get_block_transaction_offchain_key( + OffchainStatus::Untrusted, + safe_finalized_block.encode(), + ), + ) + } + + pub async fn run(self, spawner: impl SpawnNamed) + where + <<::Block as BlockT>::Header as HeaderT>::Number: + From<<::Header as HeaderT>::Number>, + { + // live import stream + let import_stream = self.client.every_import_notification_stream(); + + self.client + .runtime_api() + .register_extension(offchain::OffchainDbExt::new( + offchain::LimitedExternalities::new( + offchain::Capabilities::all(), + self.storage.clone(), + ), + )); + + let cloned_self = self.clone_spawned(); + + spawner.spawn( + "receipt-service-offchain-on-block", + Some("ReceiptService offchain-worker"), + Box::pin(async move { + futures::pin_mut!(import_stream); + + while let Some(n) = import_stream.next().await { + let block_num = *n.header.number(); + let mut runtime = cloned_self.client.runtime_api(); + + runtime.register_extension(offchain::OffchainDbExt::new( + offchain::LimitedExternalities::new( + offchain::Capabilities::all(), + cloned_self.storage.clone(), + ), + )); + + tracing::debug!( + target: LOG_TARGET, + "ReceiptService worker pre `store_offchain_at`, block_num: {:?}", + block_num + ); + + let receipts = &runtime + .build_receipt_at(n.header.hash(), block_num.unique_saturated_into()) + .unwrap_or_default(); + + Self::store_newest_data_offchain( + cloned_self.storage.clone(), + block_num.unique_saturated_into(), + receipts.clone(), + ); + + cloned_self.maybe_prune( + cloned_self.storage.clone(), + OffchainStatus::Untrusted, + block_num.unique_saturated_into(), + ); + + tracing::info!( + target: LOG_TARGET, + "ReceiptService worker result `store_offchain_at`, Block {:?}\n Recipts: {:?}", + block_num, + receipts, + ); + } + }), + ); + + let cloned_self = self.clone_spawned(); + let finalized_stream = self.client.finality_notification_stream(); + + spawner.spawn( + "receipt-service-offchain-on-block-finalized", + Some("FinalizedReceiptService offchain-worker"), + Box::pin(async move { + + futures::pin_mut!(finalized_stream); + let mut wm = cloned_self.load_finalized_watermark(); + while let Some(notification) = finalized_stream.next().await { + let block_num = *notification.header.number(); + let mut runtime = cloned_self.client.runtime_api(); + + runtime.register_extension(offchain::OffchainDbExt::new( + offchain::LimitedExternalities::new(offchain::Capabilities::all(), cloned_self.storage.clone()), + )); + + // fill the gap strictly below the event, if any + if block_num > wm.up_to.saturating_add(1).unique_saturated_into() { + cloned_self.backfill_finalized_gap(block_num.unique_saturated_into()); + wm = cloned_self.load_finalized_watermark(); + } + + tracing::debug!( + target: LOG_TARGET, + "ReceiptService worker pre `store_finalized_data_offchain`, finalized_block: {:?}", + notification.header.number() + ); + let receipts = runtime + .build_receipt_at(notification.header.hash(), block_num.unique_saturated_into()) + .unwrap_or_default(); + + + Self::store_finalized_data_offchain(cloned_self.storage.clone(), block_num.unique_saturated_into(), receipts.clone()); + + if block_num >= wm.up_to.unique_saturated_into() { + wm.up_to = block_num.saturated_into(); + cloned_self.persist_finalized_watermark(&wm); + } + + cloned_self.maybe_prune(cloned_self.storage.clone(), OffchainStatus::Finalized, block_num.unique_saturated_into()); + + tracing::info!( + target: LOG_TARGET, + "ReceiptService worker result `store_finalized_data_offchain`, Block: {:?}\n Receipts: {:?}", + block_num, + receipts, + ); + } + })); + } + + fn clone_spawned(&self) -> Self { + Self { + client: self.client.clone(), + storage: self.storage.clone(), + gc_policy: self.gc_policy.clone(), + _phantom: Default::default(), + } + } +} diff --git a/rpc/src/eth.rs b/rpc/src/eth.rs index 56c6bf9..666e02e 100644 --- a/rpc/src/eth.rs +++ b/rpc/src/eth.rs @@ -573,7 +573,7 @@ where }; let transaction_receipt = - TransactionReceipt::::decode(&mut &raw_receipt[2..]) + TransactionReceipt::::decode(&mut &raw_receipt[..]) .map_err(eth_rpc_err)?; if offchain_status == OffchainStatus::Untrusted { From 82ae82612346e2b3630e7723c4ebd51b4215d927 Mon Sep 17 00:00:00 2001 From: Francisco Valentim Castilho Date: Tue, 2 Sep 2025 23:41:42 -0300 Subject: [PATCH 02/12] Refactor: Remove `offchain_worker` hook and remove offchain data logic from the pallet runtime --- pallet-epico/src/lib.rs | 138 ++++------------------------------------ 1 file changed, 11 insertions(+), 127 deletions(-) diff --git a/pallet-epico/src/lib.rs b/pallet-epico/src/lib.rs index 0ccdc55..cd3ab5c 100644 --- a/pallet-epico/src/lib.rs +++ b/pallet-epico/src/lib.rs @@ -34,7 +34,6 @@ pub mod pallet { use scale_info::prelude::boxed::Box; use sp_runtime::{ - offchain::storage::StorageValueRef, traits::{Dispatchable, Saturating, StaticLookup, UniqueSaturatedInto}, DispatchError, FixedPointNumber, FixedU128, Perbill, SaturatedConversion, Vec, Weight, }; @@ -42,8 +41,8 @@ pub mod pallet { use weights::WeightInfo; use crate::types::{ - default_transaction_from_tx_call, get_block_transaction_offchain_key, get_tx_offchain_key, - BalanceOf, ExecutedTransactionInfo, ExecutedTransactionInfoOf, OffchainStatus, + default_transaction_from_tx_call, BalanceOf, ExecutedTransactionInfo, + ExecutedTransactionInfoOf, }; use frame_system::WeightInfo as SystemWeightInfo; @@ -186,24 +185,6 @@ pub mod pallet { NextTransactionIndex::::set(0); } - /// The offchain worker stores transaction data for the current block and - /// handles promoting previously untrusted data to finalized status - /// once it’s deemed safe based on `SafeFinalityDepth`. - fn offchain_worker(block_number: BlockNumberFor) { - // Store transactions from the latest block in the offchain DB. - // - // Since the block is not finalized, it is subject to reorgs, - // so the data is stored under the "Untrusted" prefix. - Self::store_newest_data_offchain(block_number); - - // Promote older transactions to "Finalized" status if they are - // sufficiently deep in the chain (based on SafeFinalityDepth). - // - // This involves moving the data to the "Finalized" prefix - // and cleaning up its untrusted counterpart. - Self::store_finalized_data_offchain(block_number); - } - fn on_initialize(n: BlockNumberFor) -> Weight { let safe_to_clear_block = Self::safe_finalized_block(n).saturating_sub(1u8.into()); @@ -441,20 +422,20 @@ pub mod pallet { /// Substrate weight units into gas units, taking into account various transaction factors. /// /// ### Process: - /// 1. **Extract Runtime Call**: - /// - If transaction `data` exists, decode it into a runtime call. + /// 1. **Extract Runtime Call**: + /// - If transaction `data` exists, decode it into a runtime call. /// - If no data is provided, fallback to a default `transfer_keep_alive` call. /// - /// 2. **Weight Calculation**: - /// - Retrieve the call weight via `get_dispatch_info()`. - /// - Add the base extrinsic weight for a normal transaction class. - /// - Add length-derived weight based on the encoded transaction length. - /// - Add weight for storing executed transactions. + /// 2. **Weight Calculation**: + /// - Retrieve the call weight via `get_dispatch_info()`. + /// - Add the base extrinsic weight for a normal transaction class. + /// - Add length-derived weight based on the encoded transaction length. + /// - Add weight for storing executed transactions. /// - /// 3. **Gas Adjustment**: + /// 3. **Gas Adjustment**: /// - Apply a configurable `GasEstimationCoefficient` multiplier to adjust the weight for conservative gas estimates. /// - /// 4. **Weight-to-Gas Conversion**: + /// 4. **Weight-to-Gas Conversion**: /// - Convert the final weight into an approximate gas value using a predefined reference ratio between Substrate weight and EVM gas. /// - The conversion factors in both `ref_time` and `proof_size` components of the weight. pub fn extract_gas_from_transaction_call(request: TransactionCall) -> Option { @@ -593,103 +574,6 @@ pub mod pallet { .len() as u64 } - /// Stores all epico executed transactions for the current block in offchain - /// persistent storage under the `Untrusted` prefix. - /// - /// This data is considered "untrusted" because the block may still be - /// reorged out of the canonical chain. - fn store_newest_data_offchain(current_block: BlockNumberFor) { - let mut block_transactions: Vec = Vec::new(); - - // Iterate over all transactions executed in the block. - for (transaction_hash, transaction_info) in - ExecutedTransaction::::iter_prefix(current_block) - { - block_transactions.push(transaction_hash); - - // Persist the transaction receipt under the "Untrusted" offchain key. - StorageValueRef::persistent(&get_tx_offchain_key( - OffchainStatus::Untrusted, - transaction_hash, - )) - .set( - &Self::get_transaction_receipt( - transaction_hash, - transaction_info, - current_block, - ) - .encode(), - ); - } - - if block_transactions.is_empty() { - return; - } - - // Store a mapping of transaction hashes for this block. - StorageValueRef::persistent(&get_block_transaction_offchain_key( - OffchainStatus::Untrusted, - current_block.encode(), - )) - .set(&block_transactions.encode()); - } - - /// Promotes transactions from a sufficiently old block to "Finalized" status - /// in the offchain DB. - /// - /// This assumes that blocks older than `SafeFinalityDepth` are finalized - /// and no longer subject to forks. - pub fn store_finalized_data_offchain(current_block: BlockNumberFor) { - let safe_finalized_block = Self::safe_finalized_block(current_block); - - let mut block_transactions: Vec = Vec::new(); - - // Iterate over all transactions in the finalized block. - for (transaction_hash, transaction_info) in - ExecutedTransaction::::iter_prefix(safe_finalized_block) - { - block_transactions.push(transaction_hash); - - // Move receipts to "Finalized" namespace. - StorageValueRef::persistent(&get_tx_offchain_key( - OffchainStatus::Finalized, - transaction_hash, - )) - .set( - &Self::get_transaction_receipt( - transaction_hash, - transaction_info, - safe_finalized_block, - ) - .encode(), - ); - - // Remove the untrusted version of this receipt. - StorageValueRef::persistent(&get_tx_offchain_key( - OffchainStatus::Untrusted, - transaction_hash, - )) - .clear(); - } - - if block_transactions.is_empty() { - return; - } - - // Store finalized transaction hash list and remove the untrusted copy. - StorageValueRef::persistent(&get_block_transaction_offchain_key( - OffchainStatus::Finalized, - safe_finalized_block.encode(), - )) - .set(&block_transactions.encode()); - - StorageValueRef::persistent(&get_block_transaction_offchain_key( - OffchainStatus::Untrusted, - safe_finalized_block.encode(), - )) - .clear(); - } - /// Calculates the oldest block that can be assumed finalized based on /// the provided `block_number` and the configured `SafeFinalityDepth`. /// From ac1193e382802914127af8043a13f370e89caa15 Mon Sep 17 00:00:00 2001 From: Francisco Valentim Castilho Date: Mon, 8 Sep 2025 01:33:27 -0300 Subject: [PATCH 03/12] Feat: Docs related to the receipt-service --- README.md | 24 +++++++++++++---------- docs/INTEGRATION.md | 47 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 0b391c1..276e8d3 100644 --- a/README.md +++ b/README.md @@ -58,22 +58,23 @@ epico/ │ ├── self-contained/ # Self-contained logic and primitives │ ├── xcm/ # Cross-Consensus Messaging (XCM) primitives ├── rpc/ # Remote Procedure Call implementations +├── receipt-service/ # Offchain worker for managing receipts ``` --- ## Advantages of Native Execution -1. **Improved Performance and Throughput** +1. **Improved Performance and Throughput** By bypassing EVM emulation, Ethereum transactions are processed directly by the Substrate runtime, leading to faster and more efficient operations. -2. **Customizability** +2. **Customizability** Substrate runtimes are known for their flexibility. With native execution, developers can tailor execution logic to their specific projects, something that’s not easily achievable in a strict EVM environment. -3. **Lower Gas Costs** +3. **Lower Gas Costs** Native execution leverages Substrate’s optimized gas metering. This often results in cheaper fees compared to Ethereum’s traditional gas model. -4. **Enhanced Security** +4. **Enhanced Security** By avoiding known EVM vulnerabilities (like certain reentrancy attacks or gas-limit exploits), Epico creates a more secure environment for transaction processing. --- @@ -92,18 +93,21 @@ As a result, tools like **MetaMask**, **Hardhat**, and other Ethereum-compatible ## How Epico Works: Architecture Overview -Epico’s compatibility layer is built on three main components: +Epico's compatibility layer is built on four main components: -1. **Epico RPC** +1. **Epico RPC** This module, embedded within the client, exposes Ethereum JSON-RPC methods. It acts as a “mask,” presenting a Substrate node as a standard Ethereum node to external clients and tools. ![Substrate Node](./docs/substrate-node.png) -2. **Epico Pallet** +2. **Epico Pallet** The core logic that validates, decodes, and executes Ethereum transactions within the Substrate environment. It handles storage of execution results so that transaction receipts and execution outcomes can be queried later. -3. **Epico Pallet Runtime APIs** +3. **Epico Pallet Runtime APIs** These APIs provide an interface between the runtime and the Epico RPC client, retrieving the necessary data (balance, nonce, block info, etc.) for Ethereum-compatible RPC responses. +4. **Epico Receipt Service** + An offchain worker that manages transaction receipts in persistent storage. It runs two workers: one stores receipts as "untrusted" on block import, another promotes them to "finalized" on finality events. Features automatic garbage collection that follows the node's state pruning configuration and gap-filling for missed events. Essential for Ethereum RPC compatibility. + In addition, Epico leverages a custom `UncheckedExtrinsic` and `CheckedExtrinsic` flow (inspired by Frontier) to handle validation, pre-dispatch, dispatch, and post-dispatch logic for transactions submitted through the Epico RPC. --- @@ -112,10 +116,10 @@ In addition, Epico leverages a custom `UncheckedExtrinsic` and `CheckedExtrinsic Because Ethereum addresses are **20 bytes** long, any Substrate runtime that leverages Epico must accommodate Ethereum-style accounts. There are two main ways to do this: -1. **Native Ethereum Accounts** +1. **Native Ethereum Accounts** Adopt a **unified** 20-byte account model as your chain’s default (similar to Moonbeam). This simplifies interoperability because Ethereum and Substrate addresses are the same format. -2. **Account Conversion Mechanism** +2. **Account Conversion Mechanism** Implement a way to **map** Substrate’s default 32-byte addresses to a 20-byte address. This keeps your node’s default account structure intact while still allowing Ethereum-based tools to connect and interact. --- diff --git a/docs/INTEGRATION.md b/docs/INTEGRATION.md index fad8526..60bb105 100644 --- a/docs/INTEGRATION.md +++ b/docs/INTEGRATION.md @@ -15,6 +15,7 @@ From your project setup, "Epico" consists of various components located under th - [`ep-rpc`](./rpc) - [`pallet-epico-runtime-api`](./pallet-epico/runtime-api) - [`pallet-epico`](./pallet-epico) + - [`epico-receipt-service`](./receipt-service) Each of these serves a specific purpose (e.g., primitives for fundamental types or logic, RPC components for Ethereum compatibility). @@ -34,6 +35,7 @@ ep-ethereum = { path = "../epico/primitives/ethereum", default-features = false ep-rpc = { path = "../epico/rpc" } pallet-epico-runtime-api = { path = "../epico/pallet-epico/runtime-api", default-features = false } pallet-epico = { path = "../epico/pallet-epico", default-features = false } +epico-receipt-service = { path = "../epico/receipt-service" } ``` --- @@ -221,6 +223,12 @@ The `pallet-epico` module enables Ethereum compatibility features. Timestamp::get().into() } + fn build_receipt_at(block_number: u32) -> Vec> { + pallet_epico::ExecutedTransaction::::iter_prefix(block_number).map(|(hash, transaction_info)| + pallet_epico::Pallet::::get_transaction_receipt(hash, transaction_info, block_number) + ).collect() + } + fn dry_run_eth_transaction(call: TransactionCall) -> Result<(), Vec> { Epico::dry_run(call).map_err(|e| format!("{:?}", e).encode())?; Ok(()) @@ -518,10 +526,47 @@ The RPC module provides Ethereum JSON-RPC compatibility for interacting with Eth --- +## Step 7: Add Receipt Service for Transaction Receipt Storage + +The `epico-receipt-service` is an offchain worker that manages transaction receipts in persistent storage. It runs two workers: one stores receipts as "untrusted" on block import, another promotes them to "finalized" on finality events. + +1. Add the `epico-receipt-service` dependency in your **node's** `Cargo.toml` file: + + ```toml + [dependencies] + epico-receipt-service = { workspace = true } + ``` + +2. Initialize and spawn the receipt service in your node's service setup (e.g., `node/src/service.rs`): + + ```rust + use epico_receipt_service::ReceiptService; + + // In your service setup function + let receipt_service = ReceiptService::<_, Block, solochain_template_runtime::Runtime, _>::new( + &config, + client.clone(), + backend + .offchain_storage() + .ok_or_else(|| "Offchain storage not available")?, + ); + + task_manager.spawn_handle().spawn( + "receipt-service-offchain-workers-runner", + "receipt-service-offchain-worker", + receipt_service.run(task_manager.spawn_handle()).boxed(), + ); + ``` + +**Note**: The `build_receipt_at` runtime API method required by this service has already been added to the runtime API implementation in Step 4. + +--- + ## Summary checklist 1. Add `Epico` dependencies to `Cargo.toml`. 2. Integrate AccountId20 into your runtime. 3. Add the `pallet-epico` implementation to the runtime. 4. Integrate the `ep-self-contained` package into your runtime. -5. Set up the RPC for Ethereum compatibility +5. Set up the RPC for Ethereum compatibility. +6. Add Receipt Service for transaction receipt storage. From 546829d61a7f60d1fbed9289a6d8dced5a8c7260 Mon Sep 17 00:00:00 2001 From: Francisco Valentim Castilho Date: Mon, 8 Sep 2025 10:52:22 -0300 Subject: [PATCH 04/12] Refactor: Remove `SafeFinalityDepth` --- pallet-epico/src/lib.rs | 21 +-------------------- pallet-epico/src/mock.rs | 1 - pallet-epico/src/tests.rs | 6 ++---- 3 files changed, 3 insertions(+), 25 deletions(-) diff --git a/pallet-epico/src/lib.rs b/pallet-epico/src/lib.rs index cd3ab5c..ef8d9c9 100644 --- a/pallet-epico/src/lib.rs +++ b/pallet-epico/src/lib.rs @@ -64,13 +64,6 @@ pub mod pallet { /// Coefficient to overestimate gas estimation /// by a factor of N. type GasEstimationCoefficient: Get; - - /// The number of block ancestors to consider as safely finalized. - /// - /// This represents a depth from the current best block, below which - /// blocks are presumed to be finalized, even if finality has not - /// been explicitly confirmed via the consensus mechanism. - type SafeFinalityDepth: Get; } /// Reference Gas consumtion for a set of operations in the EVM. @@ -186,7 +179,7 @@ pub mod pallet { } fn on_initialize(n: BlockNumberFor) -> Weight { - let safe_to_clear_block = Self::safe_finalized_block(n).saturating_sub(1u8.into()); + let safe_to_clear_block = n.saturating_sub(1u8.into()); // Wipe ExecutedTransactions Map and return number of removed transactions let keys_removed = @@ -574,18 +567,6 @@ pub mod pallet { .len() as u64 } - /// Calculates the oldest block that can be assumed finalized based on - /// the provided `block_number` and the configured `SafeFinalityDepth`. - /// - /// This is used to compensate for the lack of finalized - /// block information within the runtime. - fn safe_finalized_block(block_number: BlockNumberFor) -> BlockNumberFor { - let safe_finality_depth: BlockNumberFor = - BlockNumberFor::::saturated_from(T::SafeFinalityDepth::get()); - - block_number.saturating_sub(safe_finality_depth) - } - /// Dry-runs a given transaction. /// /// This function is intended **only** for use within a runtime API context, diff --git a/pallet-epico/src/mock.rs b/pallet-epico/src/mock.rs index ca6b87a..ccca89f 100644 --- a/pallet-epico/src/mock.rs +++ b/pallet-epico/src/mock.rs @@ -83,7 +83,6 @@ impl crate::Config for Test { type WeightInfo = (); type Call = RuntimeCall; type GasEstimationCoefficient = GasEstimationCoefficient; - type SafeFinalityDepth = ConstU8<10>; } parameter_types! { diff --git a/pallet-epico/src/tests.rs b/pallet-epico/src/tests.rs index 5d933d3..bf82ebd 100644 --- a/pallet-epico/src/tests.rs +++ b/pallet-epico/src/tests.rs @@ -1,7 +1,7 @@ use crate::{ mock::*, types::{ExecutedTransactionInfoOf, TransactionCall}, - Config, Error, ExecutedTransaction, NextTransactionIndex, ReentrancyGuard, + Error, ExecutedTransaction, NextTransactionIndex, ReentrancyGuard, }; use ep_ethereum::common::{ LegacyTransaction, TransactionAction, TransactionSignature, TransactionV2 as Transaction, @@ -10,7 +10,6 @@ use frame_support::traits::OnFinalize; use frame_support::{assert_noop, assert_ok, traits::fungible::Mutate}; use hex_literal::hex; use parity_scale_codec::Encode; -use sp_core::Get; use sp_core::U256; use sp_runtime::{ArithmeticError, Vec, Weight}; @@ -434,8 +433,7 @@ fn clean_transactions_after_safe_finalization_depth() { ExecutedTransaction::::get(initial_block_number, transaction.hash()).is_some() ); - let safe_finality_depth: u8 = ::SafeFinalityDepth::get(); - initialize_to_block(initial_block_number.saturating_add((safe_finality_depth + 1).into())); + initialize_to_block(initial_block_number.saturating_add((2_u64).into())); assert!( ExecutedTransaction::::get(initial_block_number, transaction.hash()).is_none() From 26c9ae77d24dad7c8a18efbab177b20aaeeddd4e Mon Sep 17 00:00:00 2001 From: Francisco Valentim Castilho Date: Mon, 8 Sep 2025 14:02:34 -0300 Subject: [PATCH 05/12] Chore: Update runtime_api implementation guide to reflect `SafeFinalityDepth` removal --- docs/INTEGRATION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/INTEGRATION.md b/docs/INTEGRATION.md index 60bb105..33b61d3 100644 --- a/docs/INTEGRATION.md +++ b/docs/INTEGRATION.md @@ -216,7 +216,7 @@ The `pallet-epico` module enables Ethereum compatibility features. } fn safe_finality_depth() -> u8 { - ::SafeFinalityDepth::get() + 1 } fn block_timestamp() -> U256 { From c6940273d1c30114ddb0e9d9c682524eac92a4d9 Mon Sep 17 00:00:00 2001 From: Francisco Valentim Castilho Date: Tue, 9 Sep 2025 10:36:01 -0300 Subject: [PATCH 06/12] Chore: Remove `safe_finality_depth` runtime_api --- pallet-epico/runtime-api/src/lib.rs | 3 --- rpc/src/eth.rs | 14 ++------------ 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/pallet-epico/runtime-api/src/lib.rs b/pallet-epico/runtime-api/src/lib.rs index 46b3064..9b35b92 100644 --- a/pallet-epico/runtime-api/src/lib.rs +++ b/pallet-epico/runtime-api/src/lib.rs @@ -39,9 +39,6 @@ sp_api::decl_runtime_apis! { /// Get best effort estimation for a gas price. fn gas_price() -> U256; - /// Returns pallet-epico's configured safe finality depth. - fn safe_finality_depth() -> u8; - /// Retrieves the timestamp for the current block. fn block_timestamp() -> U256; diff --git a/rpc/src/eth.rs b/rpc/src/eth.rs index 666e02e..98d5d79 100644 --- a/rpc/src/eth.rs +++ b/rpc/src/eth.rs @@ -578,13 +578,8 @@ where if offchain_status == OffchainStatus::Untrusted { let block_info = self.client.info(); - let safe_finality_depth = self - .client - .runtime_api() - .safe_finality_depth(block_info.best_hash) - .map_err(runtime_error_into_rpc_err)?; - if (block_info.best_number - safe_finality_depth.into()) + if (block_info.best_number - 1_u8.into()) > transaction_receipt.block_number.saturated_into() { return Ok(None); @@ -608,11 +603,6 @@ where block_number: <::Header as sp_runtime::traits::Header>::Number, ) -> RpcResult> { let block_info = self.client.info(); - let safe_finality_depth = self - .client - .runtime_api() - .safe_finality_depth(block_info.best_hash) - .map_err(runtime_error_into_rpc_err)?; let raw_block_transactions = self .offchain_db @@ -625,7 +615,7 @@ where ), ) .or_else(|| { - if (block_info.best_number - safe_finality_depth.into()) <= block_number { + if (block_info.best_number - 1_u8.into()) <= block_number { self.offchain_db.clone().local_storage_get( StorageKind::PERSISTENT, &get_block_transaction_offchain_key( From 5c7372e6ed8a55b73c5f9c994ed10f31143ceafd Mon Sep 17 00:00:00 2001 From: Francisco Valentim Castilho Date: Tue, 9 Sep 2025 10:37:34 -0300 Subject: [PATCH 07/12] Chore: Remove `safe_finality_depth` doc mentions --- docs/INTEGRATION.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/INTEGRATION.md b/docs/INTEGRATION.md index 33b61d3..239613c 100644 --- a/docs/INTEGRATION.md +++ b/docs/INTEGRATION.md @@ -215,10 +215,6 @@ The `pallet-epico` module enables Ethereum compatibility features. U256::from(adjusted_fee_per_gas) } - fn safe_finality_depth() -> u8 { - 1 - } - fn block_timestamp() -> U256 { Timestamp::get().into() } From 7a3e60658a29f05c1dfd09047129e6b884e0c141 Mon Sep 17 00:00:00 2001 From: Francisco Valentim Castilho Date: Tue, 9 Sep 2025 10:41:02 -0300 Subject: [PATCH 08/12] Refactor: Clarify `epico-receipt-service` as an 'offchain service' in the docs --- README.md | 2 +- docs/INTEGRATION.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 276e8d3..45d4ceb 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ Epico's compatibility layer is built on four main components: These APIs provide an interface between the runtime and the Epico RPC client, retrieving the necessary data (balance, nonce, block info, etc.) for Ethereum-compatible RPC responses. 4. **Epico Receipt Service** - An offchain worker that manages transaction receipts in persistent storage. It runs two workers: one stores receipts as "untrusted" on block import, another promotes them to "finalized" on finality events. Features automatic garbage collection that follows the node's state pruning configuration and gap-filling for missed events. Essential for Ethereum RPC compatibility. + An offchain service that manages transaction receipts in persistent storage. It runs two workers: one stores receipts as "untrusted" on block import, another promotes them to "finalized" on finality events. Features automatic garbage collection that follows the node's state pruning configuration and gap-filling for missed events. Essential for Ethereum RPC compatibility. In addition, Epico leverages a custom `UncheckedExtrinsic` and `CheckedExtrinsic` flow (inspired by Frontier) to handle validation, pre-dispatch, dispatch, and post-dispatch logic for transactions submitted through the Epico RPC. diff --git a/docs/INTEGRATION.md b/docs/INTEGRATION.md index 239613c..d03f591 100644 --- a/docs/INTEGRATION.md +++ b/docs/INTEGRATION.md @@ -524,7 +524,7 @@ The RPC module provides Ethereum JSON-RPC compatibility for interacting with Eth ## Step 7: Add Receipt Service for Transaction Receipt Storage -The `epico-receipt-service` is an offchain worker that manages transaction receipts in persistent storage. It runs two workers: one stores receipts as "untrusted" on block import, another promotes them to "finalized" on finality events. +The `epico-receipt-service` is an offchain service that manages transaction receipts in persistent storage. It runs two workers: one stores receipts as "untrusted" on block import, another promotes them to "finalized" on finality events. 1. Add the `epico-receipt-service` dependency in your **node's** `Cargo.toml` file: From 6524dac7847ef48803b86a395788cf88d01d93f5 Mon Sep 17 00:00:00 2001 From: Francisco Valentim Castilho Date: Tue, 9 Sep 2025 11:39:57 -0300 Subject: [PATCH 09/12] Refactor: Improve code clarity by renaming non descriptive variables --- receipt-service/src/lib.rs | 65 +++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/receipt-service/src/lib.rs b/receipt-service/src/lib.rs index d49b520..f017831 100644 --- a/receipt-service/src/lib.rs +++ b/receipt-service/src/lib.rs @@ -49,7 +49,7 @@ impl From for ReceiptsKeptFrom { pub struct ReceiptGcPolicy { /// None means keep all finalized receipts. pub keep_blocks: Option, - /// how many blocks to prune per execution cycle. + /// Amount of blocks to prune per execution cycle. pub gc_step: u64, } @@ -265,32 +265,32 @@ where fn backfill_finalized_gap(&self, target_finalized: u64) { let mut wm = self.load_finalized_watermark(); - let mut n = { + let mut block_num = { if let Some(keep_blocks) = self.gc_policy.keep_blocks { target_finalized.saturating_sub(keep_blocks) } else { wm.up_to } }; - let finalized_from_block_n = n; - while n < target_finalized { + let finalized_from_block_n = block_num; + while block_num < target_finalized { let hash = { - if let Ok(Some(hash)) = self.client.block_hash(n.unique_saturated_into()) { + if let Ok(Some(hash)) = self.client.block_hash(block_num.unique_saturated_into()) { hash } else { tracing::error!( target: LOG_TARGET, "Failed to backfill finalized gap for block: {}. from {} to block: {}", - n, finalized_from_block_n, target_finalized + block_num, finalized_from_block_n, target_finalized ); // In case we don't have access to the state of older blocks // wipe any existing receipts for this block. Self::do_prune_block( &mut self.storage.clone(), OffchainStatus::Untrusted, - n.unique_saturated_into(), + block_num.unique_saturated_into(), ); - n += 1; + block_num += 1; // Ignore any possible errors because we can't be certain that we have access // to the state of older blocks. continue; @@ -299,13 +299,17 @@ where let receipts = self .client .runtime_api() - .build_receipt_at(hash, n.unique_saturated_into()) + .build_receipt_at(hash, block_num.unique_saturated_into()) .unwrap_or_default(); - Self::store_finalized_data_offchain(self.storage.clone(), n.saturated_into(), receipts); - wm.up_to = n; + Self::store_finalized_data_offchain( + self.storage.clone(), + block_num.saturated_into(), + receipts, + ); + wm.up_to = block_num; self.persist_finalized_watermark(&wm); self.persist_finalized_kept_from(&finalized_from_block_n.into()); - n += 1; + block_num += 1; } tracing::info!( target: LOG_TARGET, @@ -442,8 +446,8 @@ where Box::pin(async move { futures::pin_mut!(import_stream); - while let Some(n) = import_stream.next().await { - let block_num = *n.header.number(); + while let Some(imported_block) = import_stream.next().await { + let block_num = *imported_block.header.number(); let mut runtime = cloned_self.client.runtime_api(); runtime.register_extension(offchain::OffchainDbExt::new( @@ -460,7 +464,10 @@ where ); let receipts = &runtime - .build_receipt_at(n.header.hash(), block_num.unique_saturated_into()) + .build_receipt_at( + imported_block.header.hash(), + block_num.unique_saturated_into(), + ) .unwrap_or_default(); Self::store_newest_data_offchain( @@ -495,12 +502,15 @@ where futures::pin_mut!(finalized_stream); let mut wm = cloned_self.load_finalized_watermark(); - while let Some(notification) = finalized_stream.next().await { - let block_num = *notification.header.number(); + while let Some(finalized_block) = finalized_stream.next().await { + let block_num = *finalized_block.header.number(); let mut runtime = cloned_self.client.runtime_api(); runtime.register_extension(offchain::OffchainDbExt::new( - offchain::LimitedExternalities::new(offchain::Capabilities::all(), cloned_self.storage.clone()), + offchain::LimitedExternalities::new( + offchain::Capabilities::all(), + cloned_self.storage.clone(), + ), )); // fill the gap strictly below the event, if any @@ -512,21 +522,32 @@ where tracing::debug!( target: LOG_TARGET, "ReceiptService worker pre `store_finalized_data_offchain`, finalized_block: {:?}", - notification.header.number() + finalized_block.header.number() ); let receipts = runtime - .build_receipt_at(notification.header.hash(), block_num.unique_saturated_into()) + .build_receipt_at( + finalized_block.header.hash(), + block_num.unique_saturated_into() + ) .unwrap_or_default(); - Self::store_finalized_data_offchain(cloned_self.storage.clone(), block_num.unique_saturated_into(), receipts.clone()); + Self::store_finalized_data_offchain( + cloned_self.storage.clone(), + block_num.unique_saturated_into(), + receipts.clone() + ); if block_num >= wm.up_to.unique_saturated_into() { wm.up_to = block_num.saturated_into(); cloned_self.persist_finalized_watermark(&wm); } - cloned_self.maybe_prune(cloned_self.storage.clone(), OffchainStatus::Finalized, block_num.unique_saturated_into()); + cloned_self.maybe_prune( + cloned_self.storage.clone(), + OffchainStatus::Finalized, + block_num.unique_saturated_into() + ); tracing::info!( target: LOG_TARGET, From d35011b56c9b8a544a802a49c3f6e871efb78cf2 Mon Sep 17 00:00:00 2001 From: Francisco Valentim Castilho Date: Tue, 9 Sep 2025 11:56:07 -0300 Subject: [PATCH 10/12] Feat: Improve doc comments --- receipt-service/src/lib.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/receipt-service/src/lib.rs b/receipt-service/src/lib.rs index f017831..17e8967 100644 --- a/receipt-service/src/lib.rs +++ b/receipt-service/src/lib.rs @@ -29,11 +29,13 @@ const AUX_WM_KEY: &[u8] = b"receipt_service.finalized_watermark"; const AUX_FIN_KEPT_FROM: &[u8] = b"receipt_service.finalized_kept_from"; const AUX_UNT_KEPT_FROM: &[u8] = b"receipt_service.untrusted_kept_from"; +/// Last block number that we processed receipts for. #[derive(Encode, Decode, Clone, Default, Debug)] struct FinalizedWatermark { up_to: u64, } +/// Oldest block number that we have kept receipts for. #[derive(Encode, Decode, Clone, Default, Debug)] struct ReceiptsKeptFrom { block_number: u64, @@ -45,6 +47,8 @@ impl From for ReceiptsKeptFrom { } } +/// Pruning policy for receipts, the configuration is derived from the node's +/// state pruning settings via [`Configuration`]. #[derive(Clone, Debug, Encode, Decode)] pub struct ReceiptGcPolicy { /// None means keep all finalized receipts. @@ -74,6 +78,7 @@ impl From<&Configuration> for ReceiptGcPolicy { } } +/// Epico offchain receipt service. #[derive(Encode, Decode, Debug)] pub struct ReceiptService { pub client: Arc, @@ -113,6 +118,7 @@ where } } + /// Loads the oldest block number from which we have unfinalized receipts. fn load_untrusted_kept_from(&self) -> ReceiptsKeptFrom { self.client .get_aux(AUX_UNT_KEPT_FROM) @@ -122,12 +128,14 @@ where .unwrap_or_default() } + /// Stores the new oldest block number from which we have unfinalized receipts. fn persist_untrusted_kept_from(&self, kept_from: &ReceiptsKeptFrom) { let _ = self .client .insert_aux(&[(AUX_UNT_KEPT_FROM, kept_from.encode().as_slice())], &[]); } + /// Loads the oldest block number from which we have finalized receipts. fn load_finalized_kept_from(&self) -> ReceiptsKeptFrom { self.client .get_aux(AUX_FIN_KEPT_FROM) @@ -137,6 +145,7 @@ where .unwrap_or_default() } + /// Stores the new oldest block number from which we have finalized receipts. fn persist_finalized_kept_from(&self, kept_from: &ReceiptsKeptFrom) { let _ = self .client @@ -247,6 +256,7 @@ where } } + /// Loads the information of the last finalized block that we have processed. fn load_finalized_watermark(&self) -> FinalizedWatermark { self.client .get_aux(AUX_WM_KEY) @@ -256,12 +266,17 @@ where .unwrap_or_default() } + /// Stores the new finalized block number that was processed. fn persist_finalized_watermark(&self, wm: &FinalizedWatermark) { let _ = self .client .insert_aux(&[(AUX_WM_KEY, wm.encode().as_slice())], &[]); } + /// Backfills the receipts based on the newest finalized block notification + /// and the last block number that we have in the database. + /// + /// This allows us to catch up with the finalized blocks that we have missed. fn backfill_finalized_gap(&self, target_finalized: u64) { let mut wm = self.load_finalized_watermark(); @@ -365,9 +380,6 @@ where /// Promotes transactions from a sufficiently old block to "Finalized" status /// in the offchain DB. - /// - /// This assumes that blocks older than `SafeFinalityDepth` are finalized - /// and no longer subject to forks. pub fn store_finalized_data_offchain( mut storage: OffchainDb, safe_finalized_block: BlockNumberFor, From 631c075f52a4f13be85331b3458cd010a9fc0a93 Mon Sep 17 00:00:00 2001 From: Francisco Valentim Castilho Date: Tue, 9 Sep 2025 12:31:48 -0300 Subject: [PATCH 11/12] Refactor: Remove unnecessary receipt check --- rpc/src/eth.rs | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/rpc/src/eth.rs b/rpc/src/eth.rs index 98d5d79..09f943c 100644 --- a/rpc/src/eth.rs +++ b/rpc/src/eth.rs @@ -557,7 +557,7 @@ where &get_tx_offchain_key(OffchainStatus::Finalized, transaction_hash), ); - let (raw_receipt, offchain_status) = if let Some(raw_receipt) = maybe_trusted_raw_receipt { + let (raw_receipt, _) = if let Some(raw_receipt) = maybe_trusted_raw_receipt { (raw_receipt, OffchainStatus::Finalized) } else { let maybe_untrusted_raw_receipt = self.offchain_db.clone().local_storage_get( @@ -576,16 +576,6 @@ where TransactionReceipt::::decode(&mut &raw_receipt[..]) .map_err(eth_rpc_err)?; - if offchain_status == OffchainStatus::Untrusted { - let block_info = self.client.info(); - - if (block_info.best_number - 1_u8.into()) - > transaction_receipt.block_number.saturated_into() - { - return Ok(None); - } - }; - let mut receipt: Receipt = transaction_receipt.into(); if receipt.block_hash == H256::zero() { receipt.block_hash = self @@ -602,8 +592,6 @@ where &self, block_number: <::Header as sp_runtime::traits::Header>::Number, ) -> RpcResult> { - let block_info = self.client.info(); - let raw_block_transactions = self .offchain_db .clone() @@ -615,17 +603,13 @@ where ), ) .or_else(|| { - if (block_info.best_number - 1_u8.into()) <= block_number { - self.offchain_db.clone().local_storage_get( - StorageKind::PERSISTENT, - &get_block_transaction_offchain_key( - OffchainStatus::Untrusted, - block_number.encode(), - ), - ); - } - - None + self.offchain_db.clone().local_storage_get( + StorageKind::PERSISTENT, + &get_block_transaction_offchain_key( + OffchainStatus::Untrusted, + block_number.encode(), + ), + ) }); let block_transactions = match raw_block_transactions { From 9faadbe36fd94641401191b2d7d382c5027bcf6d Mon Sep 17 00:00:00 2001 From: Francisco Valentim Castilho Date: Tue, 9 Sep 2025 12:32:47 -0300 Subject: [PATCH 12/12] Feat: Preemptively wipe `Untrusted` receipts when storing the new `Finalized` ones --- receipt-service/src/lib.rs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/receipt-service/src/lib.rs b/receipt-service/src/lib.rs index 17e8967..e23a591 100644 --- a/receipt-service/src/lib.rs +++ b/receipt-service/src/lib.rs @@ -385,6 +385,12 @@ where safe_finalized_block: BlockNumberFor, receipts: Vec>, ) { + Self::do_prune_block( + &mut storage, + OffchainStatus::Untrusted, + safe_finalized_block, + ); + let mut block_transactions: Vec = Vec::new(); // Iterate over all transactions in the finalized block. @@ -402,12 +408,6 @@ where "Finalized offchain storage key {:?}", &storage_key, ); - - // Remove the untrusted version of this receipt. - storage.local_storage_clear( - StorageKind::PERSISTENT, - &get_tx_offchain_key(OffchainStatus::Untrusted, transaction_hash), - ); } if block_transactions.is_empty() { @@ -423,14 +423,6 @@ where ), &block_transactions.encode(), ); - - storage.local_storage_clear( - StorageKind::PERSISTENT, - &pallet_epico::types::get_block_transaction_offchain_key( - OffchainStatus::Untrusted, - safe_finalized_block.encode(), - ), - ) } pub async fn run(self, spawner: impl SpawnNamed)