feat: add transaction validation task (#3358)

This commit is contained in:
Matthias Seitz
2023-06-25 15:12:00 +02:00
committed by GitHub
parent b787d09993
commit 8dd7a78356
11 changed files with 745 additions and 692 deletions

369
Cargo.lock generated
View File

@ -324,12 +324,6 @@ dependencies = [
"rustc-demangle",
]
[[package]]
name = "base16ct"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce"
[[package]]
name = "base16ct"
version = "0.2.0"
@ -437,9 +431,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.3.1"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6776fc96284a0bb647b615056fc496d1fe1644a7ab01829818a6d91cae888b84"
checksum = "6dbe3c979c178231552ecba20214a8272df4e09f232a87aef4320cf06539aded"
[[package]]
name = "bitvec"
@ -494,9 +488,9 @@ dependencies = [
[[package]]
name = "boa_ast"
version = "0.16.0"
source = "git+https://github.com/boa-dev/boa#da4a3315d75680e63ce2a2dba66000ef2e0e50b9"
source = "git+https://github.com/boa-dev/boa#0e1b32a232109fc0e192c1297a7274091af2ac61"
dependencies = [
"bitflags 2.3.1",
"bitflags 2.3.2",
"boa_interner",
"boa_macros",
"indexmap",
@ -507,9 +501,9 @@ dependencies = [
[[package]]
name = "boa_engine"
version = "0.16.0"
source = "git+https://github.com/boa-dev/boa#da4a3315d75680e63ce2a2dba66000ef2e0e50b9"
source = "git+https://github.com/boa-dev/boa#0e1b32a232109fc0e192c1297a7274091af2ac61"
dependencies = [
"bitflags 2.3.1",
"bitflags 2.3.2",
"boa_ast",
"boa_gc",
"boa_icu_provider",
@ -545,7 +539,7 @@ dependencies = [
[[package]]
name = "boa_gc"
version = "0.16.0"
source = "git+https://github.com/boa-dev/boa#da4a3315d75680e63ce2a2dba66000ef2e0e50b9"
source = "git+https://github.com/boa-dev/boa#0e1b32a232109fc0e192c1297a7274091af2ac61"
dependencies = [
"boa_macros",
"boa_profiler",
@ -555,7 +549,7 @@ dependencies = [
[[package]]
name = "boa_icu_provider"
version = "0.16.0"
source = "git+https://github.com/boa-dev/boa#da4a3315d75680e63ce2a2dba66000ef2e0e50b9"
source = "git+https://github.com/boa-dev/boa#0e1b32a232109fc0e192c1297a7274091af2ac61"
dependencies = [
"icu_collections",
"icu_normalizer",
@ -568,7 +562,7 @@ dependencies = [
[[package]]
name = "boa_interner"
version = "0.16.0"
source = "git+https://github.com/boa-dev/boa#da4a3315d75680e63ce2a2dba66000ef2e0e50b9"
source = "git+https://github.com/boa-dev/boa#0e1b32a232109fc0e192c1297a7274091af2ac61"
dependencies = [
"boa_gc",
"boa_macros",
@ -583,7 +577,7 @@ dependencies = [
[[package]]
name = "boa_macros"
version = "0.16.0"
source = "git+https://github.com/boa-dev/boa#da4a3315d75680e63ce2a2dba66000ef2e0e50b9"
source = "git+https://github.com/boa-dev/boa#0e1b32a232109fc0e192c1297a7274091af2ac61"
dependencies = [
"proc-macro2 1.0.60",
"quote 1.0.28",
@ -594,9 +588,9 @@ dependencies = [
[[package]]
name = "boa_parser"
version = "0.16.0"
source = "git+https://github.com/boa-dev/boa#da4a3315d75680e63ce2a2dba66000ef2e0e50b9"
source = "git+https://github.com/boa-dev/boa#0e1b32a232109fc0e192c1297a7274091af2ac61"
dependencies = [
"bitflags 2.3.1",
"bitflags 2.3.2",
"boa_ast",
"boa_icu_provider",
"boa_interner",
@ -614,7 +608,7 @@ dependencies = [
[[package]]
name = "boa_profiler"
version = "0.16.0"
source = "git+https://github.com/boa-dev/boa#da4a3315d75680e63ce2a2dba66000ef2e0e50b9"
source = "git+https://github.com/boa-dev/boa#0e1b32a232109fc0e192c1297a7274091af2ac61"
[[package]]
name = "brotli"
@ -936,7 +930,7 @@ dependencies = [
"digest 0.10.6",
"getrandom 0.2.9",
"hmac",
"k256 0.13.1",
"k256",
"lazy_static",
"serde",
"sha2 0.10.6",
@ -1212,18 +1206,6 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "crypto-bigint"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef"
dependencies = [
"generic-array",
"rand_core 0.6.4",
"subtle",
"zeroize",
]
[[package]]
name = "crypto-bigint"
version = "0.5.1"
@ -1276,13 +1258,15 @@ dependencies = [
[[package]]
name = "curve25519-dalek"
version = "3.2.0"
version = "4.0.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61"
checksum = "03d928d978dbec61a1167414f5ec534f24bea0d7a0d24dd9b6233d3d8223e585"
dependencies = [
"byteorder",
"digest 0.9.0",
"rand_core 0.5.1",
"cfg-if",
"digest 0.10.6",
"fiat-crypto",
"packed_simd_2",
"platforms",
"subtle",
"zeroize",
]
@ -1471,17 +1455,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4355c25cbf99edcb6b4a0e906f6bdc6956eda149e84455bea49696429b2f8e8"
dependencies = [
"futures",
"tokio-util 0.7.7",
]
[[package]]
name = "der"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de"
dependencies = [
"const-oid",
"zeroize",
"tokio-util",
]
[[package]]
@ -1628,14 +1602,14 @@ dependencies = [
[[package]]
name = "discv5"
version = "0.2.2"
source = "git+https://github.com/sigp/discv5#d86707d79c1183b14b8cf31ef62a8a74ef9cd3e4"
version = "0.3.0"
source = "git+https://github.com/sigp/discv5#47844ca54e8d22f4fd3db4594645e65afb288bb6"
dependencies = [
"aes 0.7.5",
"aes-gcm",
"arrayvec",
"delay_map",
"enr 0.7.0",
"enr",
"fnv",
"futures",
"hashlink",
@ -1650,8 +1624,6 @@ dependencies = [
"smallvec",
"socket2",
"tokio",
"tokio-stream",
"tokio-util 0.6.10",
"tracing",
"tracing-subscriber",
"uint",
@ -1699,51 +1671,40 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30"
[[package]]
name = "ecdsa"
version = "0.14.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c"
dependencies = [
"der 0.6.1",
"elliptic-curve 0.12.3",
"rfc6979 0.3.1",
"signature 1.6.4",
]
[[package]]
name = "ecdsa"
version = "0.16.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a48e5d537b8a30c0b023116d981b16334be1485af7ca68db3a2b7024cbc957fd"
dependencies = [
"der 0.7.3",
"der",
"digest 0.10.6",
"elliptic-curve 0.13.4",
"rfc6979 0.4.0",
"signature 2.1.0",
"elliptic-curve",
"rfc6979",
"signature",
]
[[package]]
name = "ed25519"
version = "1.5.3"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7"
checksum = "5fb04eee5d9d907f29e80ee6b0e78f7e2c82342c63e3580d8c4f69d9d5aad963"
dependencies = [
"signature 1.6.4",
"pkcs8",
"signature",
]
[[package]]
name = "ed25519-dalek"
version = "1.0.1"
version = "2.0.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d"
checksum = "798f704d128510932661a3489b08e3f4c934a01d61c5def59ae7b8e48f19665a"
dependencies = [
"curve25519-dalek",
"ed25519",
"rand 0.7.3",
"rand_core 0.6.4",
"serde",
"sha2 0.9.9",
"sha2 0.10.6",
"zeroize",
]
@ -1784,41 +1745,21 @@ version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "elliptic-curve"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3"
dependencies = [
"base16ct 0.1.1",
"crypto-bigint 0.4.9",
"der 0.6.1",
"digest 0.10.6",
"ff 0.12.1",
"generic-array",
"group 0.12.1",
"pkcs8 0.9.0",
"rand_core 0.6.4",
"sec1 0.3.0",
"subtle",
"zeroize",
]
[[package]]
name = "elliptic-curve"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75c71eaa367f2e5d556414a8eea812bc62985c879748d6403edabd9cb03f16e7"
dependencies = [
"base16ct 0.2.0",
"crypto-bigint 0.5.1",
"base16ct",
"crypto-bigint",
"digest 0.10.6",
"ff 0.13.0",
"ff",
"generic-array",
"group 0.13.0",
"pkcs8 0.10.2",
"group",
"pkcs8",
"rand_core 0.6.4",
"sec1 0.7.2",
"sec1",
"subtle",
"zeroize",
]
@ -1844,26 +1785,6 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
[[package]]
name = "enr"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "492a7e5fc2504d5fdce8e124d3e263b244a68b283cac67a69eda0cd43e0aebad"
dependencies = [
"base64 0.13.1",
"bs58",
"bytes",
"ed25519-dalek",
"hex",
"k256 0.11.6",
"log",
"rand 0.8.5",
"rlp",
"serde",
"sha3",
"zeroize",
]
[[package]]
name = "enr"
version = "0.8.1"
@ -1872,8 +1793,9 @@ checksum = "cf56acd72bb22d2824e66ae8e9e5ada4d0de17a69c7fd35569dde2ada8ec9116"
dependencies = [
"base64 0.13.1",
"bytes",
"ed25519-dalek",
"hex",
"k256 0.13.1",
"k256",
"log",
"rand 0.8.5",
"rlp",
@ -2101,11 +2023,11 @@ dependencies = [
"bytes",
"cargo_metadata",
"chrono",
"elliptic-curve 0.13.4",
"elliptic-curve",
"ethabi",
"generic-array",
"hex",
"k256 0.13.1",
"k256",
"num_enum",
"once_cell",
"open-fastrlp",
@ -2173,7 +2095,7 @@ dependencies = [
"auto_impl",
"base64 0.21.0",
"bytes",
"enr 0.8.1",
"enr",
"ethers-core",
"futures-channel",
"futures-core",
@ -2209,7 +2131,7 @@ dependencies = [
"async-trait",
"coins-bip32",
"coins-bip39",
"elliptic-curve 0.13.4",
"elliptic-curve",
"eth-keystore",
"ethers-core",
"hex",
@ -2265,16 +2187,6 @@ dependencies = [
"libc",
]
[[package]]
name = "ff"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160"
dependencies = [
"rand_core 0.6.4",
"subtle",
]
[[package]]
name = "ff"
version = "0.13.0"
@ -2285,6 +2197,12 @@ dependencies = [
"subtle",
]
[[package]]
name = "fiat-crypto"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77"
[[package]]
name = "findshlibs"
version = "0.10.2"
@ -2588,24 +2506,13 @@ dependencies = [
"web-sys",
]
[[package]]
name = "group"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7"
dependencies = [
"ff 0.12.1",
"rand_core 0.6.4",
"subtle",
]
[[package]]
name = "group"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
dependencies = [
"ff 0.13.0",
"ff",
"rand_core 0.6.4",
"subtle",
]
@ -2625,7 +2532,7 @@ dependencies = [
"indexmap",
"slab",
"tokio",
"tokio-util 0.7.7",
"tokio-util",
"tracing",
]
@ -3322,7 +3229,7 @@ dependencies = [
"thiserror",
"tokio",
"tokio-rustls",
"tokio-util 0.7.7",
"tokio-util",
"tracing",
"webpki-roots",
]
@ -3402,7 +3309,7 @@ dependencies = [
"soketto",
"tokio",
"tokio-stream",
"tokio-util 0.7.7",
"tokio-util",
"tower",
"tracing",
]
@ -3458,18 +3365,6 @@ dependencies = [
"simple_asn1",
]
[[package]]
name = "k256"
version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b"
dependencies = [
"cfg-if",
"ecdsa 0.14.8",
"elliptic-curve 0.12.3",
"sha2 0.10.6",
]
[[package]]
name = "k256"
version = "0.13.1"
@ -3477,11 +3372,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc"
dependencies = [
"cfg-if",
"ecdsa 0.16.6",
"elliptic-curve 0.13.4",
"ecdsa",
"elliptic-curve",
"once_cell",
"sha2 0.10.6",
"signature 2.1.0",
"signature",
]
[[package]]
@ -3524,6 +3419,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "libm"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a"
[[package]]
name = "libm"
version = "0.2.6"
@ -3981,7 +3882,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
"libm",
"libm 0.2.6",
]
[[package]]
@ -4107,6 +4008,16 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "packed_simd_2"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282"
dependencies = [
"cfg-if",
"libm 0.1.4",
]
[[package]]
name = "page_size"
version = "0.4.2"
@ -4336,24 +4247,14 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkcs8"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba"
dependencies = [
"der 0.6.1",
"spki 0.6.0",
]
[[package]]
name = "pkcs8"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
dependencies = [
"der 0.7.3",
"spki 0.7.1",
"der",
"spki",
]
[[package]]
@ -4371,6 +4272,12 @@ dependencies = [
"crunchy",
]
[[package]]
name = "platforms"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630"
[[package]]
name = "plotters"
version = "0.3.4"
@ -5164,7 +5071,7 @@ name = "reth-discv4"
version = "0.1.0-alpha.1"
dependencies = [
"discv5",
"enr 0.8.1",
"enr",
"generic-array",
"hex",
"rand 0.8.5",
@ -5188,7 +5095,7 @@ version = "0.1.0-alpha.1"
dependencies = [
"async-trait",
"data-encoding",
"enr 0.8.1",
"enr",
"linked_hash_set",
"parking_lot 0.12.1",
"reth-net-common",
@ -5227,7 +5134,7 @@ dependencies = [
"thiserror",
"tokio",
"tokio-stream",
"tokio-util 0.7.7",
"tokio-util",
"tracing",
]
@ -5257,7 +5164,7 @@ dependencies = [
"thiserror",
"tokio",
"tokio-stream",
"tokio-util 0.7.7",
"tokio-util",
"tracing",
"typenum",
]
@ -5291,7 +5198,7 @@ dependencies = [
"thiserror",
"tokio",
"tokio-stream",
"tokio-util 0.7.7",
"tokio-util",
"tracing",
]
@ -5336,7 +5243,7 @@ dependencies = [
"thiserror",
"tokio",
"tokio-stream",
"tokio-util 0.7.7",
"tokio-util",
"tower",
"tracing",
"tracing-test",
@ -5425,7 +5332,7 @@ dependencies = [
"aquamarine",
"async-trait",
"auto_impl",
"enr 0.8.1",
"enr",
"ethers-core",
"ethers-middleware",
"ethers-providers",
@ -5464,7 +5371,7 @@ dependencies = [
"thiserror",
"tokio",
"tokio-stream",
"tokio-util 0.7.7",
"tokio-util",
"tracing",
]
@ -5670,7 +5577,7 @@ dependencies = [
"thiserror",
"tokio",
"tokio-stream",
"tokio-util 0.7.7",
"tokio-util",
"tower",
"tracing",
"tracing-futures",
@ -5775,7 +5682,7 @@ dependencies = [
"assert_matches",
"async-trait",
"confy",
"enr 0.8.1",
"enr",
"ethers-core",
"ethers-middleware",
"ethers-providers",
@ -5882,9 +5789,11 @@ dependencies = [
"reth-primitives",
"reth-provider",
"reth-rlp",
"reth-tasks",
"serde",
"thiserror",
"tokio",
"tokio-stream",
"tracing",
]
@ -5934,7 +5843,7 @@ name = "revm-precompile"
version = "2.0.3"
source = "git+https://github.com/bluealloy/revm/?branch=release/v25#88337924f4d16ed1f5e4cde12a03d0cb755cd658"
dependencies = [
"k256 0.13.1",
"k256",
"num",
"once_cell",
"revm-primitives",
@ -5969,17 +5878,6 @@ dependencies = [
"sha3",
]
[[package]]
name = "rfc6979"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb"
dependencies = [
"crypto-bigint 0.4.9",
"hmac",
"zeroize",
]
[[package]]
name = "rfc6979"
version = "0.4.0"
@ -6290,30 +6188,16 @@ dependencies = [
"untrusted",
]
[[package]]
name = "sec1"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928"
dependencies = [
"base16ct 0.1.1",
"der 0.6.1",
"generic-array",
"pkcs8 0.9.0",
"subtle",
"zeroize",
]
[[package]]
name = "sec1"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e"
dependencies = [
"base16ct 0.2.0",
"der 0.7.3",
"base16ct",
"der",
"generic-array",
"pkcs8 0.10.2",
"pkcs8",
"subtle",
"zeroize",
]
@ -6619,16 +6503,6 @@ dependencies = [
"libc",
]
[[package]]
name = "signature"
version = "1.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c"
dependencies = [
"digest 0.10.6",
"rand_core 0.6.4",
]
[[package]]
name = "signature"
version = "2.1.0"
@ -6754,16 +6628,6 @@ dependencies = [
"lock_api",
]
[[package]]
name = "spki"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b"
dependencies = [
"base64ct",
"der 0.6.1",
]
[[package]]
name = "spki"
version = "0.7.1"
@ -6771,7 +6635,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37a5be806ab6f127c3da44b7378837ebf01dadca8510a0e572460216b228bd0e"
dependencies = [
"base64ct",
"der 0.7.3",
"der",
]
[[package]]
@ -7203,7 +7067,7 @@ dependencies = [
"futures-core",
"pin-project-lite",
"tokio",
"tokio-util 0.7.7",
"tokio-util",
]
[[package]]
@ -7218,21 +7082,6 @@ dependencies = [
"tungstenite",
]
[[package]]
name = "tokio-util"
version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"log",
"pin-project-lite",
"slab",
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.7"
@ -7320,7 +7169,7 @@ dependencies = [
"rand 0.8.5",
"slab",
"tokio",
"tokio-util 0.7.7",
"tokio-util",
"tower-layer",
"tower-service",
"tracing",
@ -7348,7 +7197,7 @@ dependencies = [
"percent-encoding",
"pin-project-lite",
"tokio",
"tokio-util 0.7.7",
"tokio-util",
"tower",
"tower-layer",
"tower-service",
@ -7682,9 +7531,9 @@ checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
[[package]]
name = "unicode-ident"
version = "1.0.8"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
[[package]]
name = "unicode-normalization"

View File

@ -114,7 +114,7 @@ tokio = { version = "1.21", default-features = false }
tokio-util = { version = "0.7.4", features = ["codec"] }
## async
async-trait = "0.1.58"
async-trait = "0.1.68"
futures = "0.3.26"
pin-project = "1.0.12"
futures-util = "0.3.25"

View File

@ -206,7 +206,12 @@ impl Command {
let blockchain_db = BlockchainProvider::new(factory, blockchain_tree.clone())?;
let transaction_pool = reth_transaction_pool::Pool::eth_pool(
EthTransactionValidator::new(blockchain_db.clone(), Arc::clone(&self.chain)),
EthTransactionValidator::new(
blockchain_db.clone(),
Arc::clone(&self.chain),
ctx.task_executor.clone(),
1,
),
Default::default(),
);
info!(target: "reth::cli", "Transaction pool initialized");
@ -218,14 +223,11 @@ impl Command {
let client = blockchain_db.clone();
ctx.task_executor.spawn_critical(
"txpool maintenance task",
Box::pin(async move {
reth_transaction_pool::maintain::maintain_transaction_pool(
client,
pool,
chain_events,
)
.await
}),
reth_transaction_pool::maintain::maintain_transaction_pool_future(
client,
pool,
chain_events,
),
);
debug!(target: "reth::cli", "Spawned txpool maintenance task");
}

View File

@ -23,12 +23,14 @@ reth-provider = { workspace = true }
reth-interfaces = { workspace = true }
reth-rlp = { workspace = true }
reth-metrics = { workspace = true }
reth-tasks = { workspace = true }
# async/futures
async-trait = { workspace = true}
futures-util = { workspace = true }
parking_lot = "0.12"
tokio = { workspace = true, default-features = false, features = ["sync"] }
tokio-stream.workspace = true
# misc
aquamarine = "0.3.0"

View File

@ -116,7 +116,7 @@ pub mod metrics;
mod ordering;
pub mod pool;
mod traits;
mod validate;
pub mod validate;
#[cfg(any(test, feature = "test-utils"))]
/// Common test helpers for mocking A pool
@ -222,7 +222,7 @@ where
impl<Client>
Pool<EthTransactionValidator<Client, PooledTransaction>, CostOrdering<PooledTransaction>>
where
Client: StateProviderFactory,
Client: StateProviderFactory + Clone + 'static,
{
/// Returns a new [Pool] that uses the default [EthTransactionValidator] when validating
/// [PooledTransaction]s and ords via [CostOrdering]

View File

@ -4,7 +4,7 @@ use crate::{
traits::{CanonicalStateUpdate, ChangedAccount},
BlockInfo, Pool, TransactionOrdering, TransactionPool, TransactionValidator,
};
use futures_util::{Stream, StreamExt};
use futures_util::{future::BoxFuture, FutureExt, Stream, StreamExt};
use reth_primitives::{Address, BlockHash, BlockNumberOrTag, FromRecoveredTransaction};
use reth_provider::{BlockProviderIdExt, CanonStateNotification, PostState, StateProviderFactory};
use std::{
@ -18,6 +18,24 @@ use tracing::debug;
/// last_seen.number`
const MAX_UPDATE_DEPTH: u64 = 64;
/// Returns a spawnable future for maintaining the state of the transaction pool.
pub fn maintain_transaction_pool_future<Client, V, T, St>(
client: Client,
pool: Pool<V, T>,
events: St,
) -> BoxFuture<'static, ()>
where
Client: StateProviderFactory + BlockProviderIdExt + Send + 'static,
V: TransactionValidator + Send + 'static,
T: TransactionOrdering<Transaction = <V as TransactionValidator>::Transaction> + Send + 'static,
St: Stream<Item = CanonStateNotification> + Send + Unpin + 'static,
{
async move {
maintain_transaction_pool(client, pool, events).await;
}
.boxed()
}
/// Maintains the state of the transaction pool by handling new blocks and reorgs.
///
/// This listens for any new blocks and reorgs and updates the transaction pool's state accordingly
@ -27,10 +45,10 @@ pub async fn maintain_transaction_pool<Client, V, T, St>(
pool: Pool<V, T>,
mut events: St,
) where
Client: StateProviderFactory + BlockProviderIdExt,
V: TransactionValidator,
T: TransactionOrdering<Transaction = <V as TransactionValidator>::Transaction>,
St: Stream<Item = CanonStateNotification> + Unpin,
Client: StateProviderFactory + BlockProviderIdExt + Send + 'static,
V: TransactionValidator + Send + 'static,
T: TransactionOrdering<Transaction = <V as TransactionValidator>::Transaction> + Send + 'static,
St: Stream<Item = CanonStateNotification> + Send + Unpin + 'static,
{
// ensure the pool points to latest state
if let Ok(Some(latest)) = client.block_by_number_or_tag(BlockNumberOrTag::Latest) {

View File

@ -285,10 +285,10 @@ where
listener.discarded(tx.hash());
Err(PoolError::InvalidTransaction(*tx.hash(), err))
}
TransactionValidationOutcome::Error(tx, err) => {
TransactionValidationOutcome::Error(tx_hash, err) => {
let mut listener = self.event_listener.write();
listener.discarded(tx.hash());
Err(PoolError::Other(*tx.hash(), err))
listener.discarded(&tx_hash);
Err(PoolError::Other(tx_hash, err))
}
}
}

View File

@ -1,411 +0,0 @@
//! Transaction validation abstractions.
use crate::{
error::InvalidPoolTransactionError,
identifier::{SenderId, TransactionId},
traits::{PoolTransaction, TransactionOrigin},
MAX_INIT_CODE_SIZE, TX_MAX_SIZE,
};
use reth_primitives::{
constants::ETHEREUM_BLOCK_GAS_LIMIT, Address, ChainSpec, IntoRecoveredTransaction,
InvalidTransactionError, TransactionKind, TransactionSignedEcRecovered, TxHash,
EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, LEGACY_TX_TYPE_ID, U256,
};
use reth_provider::{AccountReader, StateProviderFactory};
use std::{fmt, marker::PhantomData, sync::Arc, time::Instant};
/// A Result type returned after checking a transaction's validity.
#[derive(Debug)]
pub enum TransactionValidationOutcome<T: PoolTransaction> {
/// The transaction is considered _currently_ valid and can be inserted into the pool.
Valid {
/// Balance of the sender at the current point.
balance: U256,
/// Current nonce of the sender.
state_nonce: u64,
/// Validated transaction.
transaction: T,
},
/// The transaction is considered invalid indefinitely: It violates constraints that prevent
/// this transaction from ever becoming valid.
Invalid(T, InvalidPoolTransactionError),
/// An error occurred while trying to validate the transaction
Error(T, Box<dyn std::error::Error + Send + Sync>),
}
impl<T: PoolTransaction> TransactionValidationOutcome<T> {
/// Returns the transaction that was validated.
pub fn transaction(&self) -> &T {
match self {
Self::Valid { transaction, .. } => transaction,
Self::Invalid(transaction, ..) => transaction,
Self::Error(transaction, ..) => transaction,
}
}
/// Returns the hash of the transactions
pub fn tx_hash(&self) -> TxHash {
*self.transaction().hash()
}
}
/// Provides support for validating transaction at any given state of the chain
#[async_trait::async_trait]
pub trait TransactionValidator: Send + Sync {
/// The transaction type to validate.
type Transaction: PoolTransaction;
/// Validates the transaction and returns a [`TransactionValidationOutcome`] describing the
/// validity of the given transaction.
///
/// This will be used by the transaction-pool to check whether the transaction should be
/// inserted into the pool or discarded right away.
///
/// Implementers of this trait must ensure that the transaction is well-formed, i.e. that it
/// complies at least all static constraints, which includes checking for:
///
/// * chain id
/// * gas limit
/// * max cost
/// * nonce >= next nonce of the sender
/// * ...
///
/// See [InvalidTransactionError](InvalidTransactionError) for common errors variants.
///
/// The transaction pool makes no additional assumptions about the validity of the transaction
/// at the time of this call before it inserts it into the pool. However, the validity of
/// this transaction is still subject to future (dynamic) changes enforced by the pool, for
/// example nonce or balance changes. Hence, any validation checks must be applied in this
/// function.
///
/// See [EthTransactionValidator] for a reference implementation.
async fn validate_transaction(
&self,
origin: TransactionOrigin,
transaction: Self::Transaction,
) -> TransactionValidationOutcome<Self::Transaction>;
/// Ensure that the code size is not greater than `max_init_code_size`.
/// `max_init_code_size` should be configurable so this will take it as an argument.
fn ensure_max_init_code_size(
&self,
transaction: &Self::Transaction,
max_init_code_size: usize,
) -> Result<(), InvalidPoolTransactionError> {
if *transaction.kind() == TransactionKind::Create && transaction.size() > max_init_code_size
{
Err(InvalidPoolTransactionError::ExceedsMaxInitCodeSize(
transaction.size(),
max_init_code_size,
))
} else {
Ok(())
}
}
}
/// A [TransactionValidator] implementation that validates ethereum transaction.
#[derive(Debug, Clone)]
pub struct EthTransactionValidator<Client, T> {
/// Spec of the chain
chain_spec: Arc<ChainSpec>,
/// This type fetches account info from the db
client: Client,
/// Fork indicator whether we are in the Shanghai stage.
shanghai: bool,
/// Fork indicator whether we are using EIP-2718 type transactions.
eip2718: bool,
/// Fork indicator whether we are using EIP-1559 type transactions.
eip1559: bool,
/// The current max gas limit
block_gas_limit: u64,
/// Minimum priority fee to enforce for acceptance into the pool.
minimum_priority_fee: Option<u128>,
/// Marker for the transaction type
_marker: PhantomData<T>,
}
// === impl EthTransactionValidator ===
impl<Client, Tx> EthTransactionValidator<Client, Tx> {
/// Creates a new instance for the given [ChainSpec]
pub fn new(client: Client, chain_spec: Arc<ChainSpec>) -> Self {
// TODO(mattsse): improve these settings by checking against hardfork
// See [reth_consensus::validation::validate_transaction_regarding_header]
Self {
chain_spec,
client,
shanghai: true,
eip2718: true,
eip1559: true,
block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT,
minimum_priority_fee: None,
_marker: Default::default(),
}
}
/// Returns the configured chain id
pub fn chain_id(&self) -> u64 {
self.chain_spec.chain().id()
}
}
#[async_trait::async_trait]
impl<Client, Tx> TransactionValidator for EthTransactionValidator<Client, Tx>
where
Client: StateProviderFactory,
Tx: PoolTransaction,
{
type Transaction = Tx;
async fn validate_transaction(
&self,
origin: TransactionOrigin,
transaction: Self::Transaction,
) -> TransactionValidationOutcome<Self::Transaction> {
// Checks for tx_type
match transaction.tx_type() {
LEGACY_TX_TYPE_ID => {
// Accept legacy transactions
}
EIP2930_TX_TYPE_ID => {
// Accept only legacy transactions until EIP-2718/2930 activates
if !self.eip2718 {
return TransactionValidationOutcome::Invalid(
transaction,
InvalidTransactionError::Eip1559Disabled.into(),
)
}
}
EIP1559_TX_TYPE_ID => {
// Reject dynamic fee transactions until EIP-1559 activates.
if !self.eip1559 {
return TransactionValidationOutcome::Invalid(
transaction,
InvalidTransactionError::Eip1559Disabled.into(),
)
}
}
_ => {
return TransactionValidationOutcome::Invalid(
transaction,
InvalidTransactionError::TxTypeNotSupported.into(),
)
}
};
// Reject transactions over defined size to prevent DOS attacks
if transaction.size() > TX_MAX_SIZE {
let size = transaction.size();
return TransactionValidationOutcome::Invalid(
transaction,
InvalidPoolTransactionError::OversizedData(size, TX_MAX_SIZE),
)
}
// Check whether the init code size has been exceeded.
if self.shanghai {
if let Err(err) = self.ensure_max_init_code_size(&transaction, MAX_INIT_CODE_SIZE) {
return TransactionValidationOutcome::Invalid(transaction, err)
}
}
// Checks for gas limit
if transaction.gas_limit() > self.block_gas_limit {
let gas_limit = transaction.gas_limit();
return TransactionValidationOutcome::Invalid(
transaction,
InvalidPoolTransactionError::ExceedsGasLimit(gas_limit, self.block_gas_limit),
)
}
// Ensure max_priority_fee_per_gas (if EIP1559) is less than max_fee_per_gas if any.
if transaction.max_priority_fee_per_gas() > Some(transaction.max_fee_per_gas()) {
return TransactionValidationOutcome::Invalid(
transaction,
InvalidTransactionError::TipAboveFeeCap.into(),
)
}
// Drop non-local transactions with a fee lower than the configured fee for acceptance into
// the pool.
if !origin.is_local() &&
transaction.is_eip1559() &&
transaction.max_priority_fee_per_gas() < self.minimum_priority_fee
{
return TransactionValidationOutcome::Invalid(
transaction,
InvalidPoolTransactionError::Underpriced,
)
}
// Checks for chainid
if let Some(chain_id) = transaction.chain_id() {
if chain_id != self.chain_id() {
return TransactionValidationOutcome::Invalid(
transaction,
InvalidTransactionError::ChainIdMismatch.into(),
)
}
}
let account = match self
.client
.latest()
.and_then(|state| state.basic_account(transaction.sender()))
{
Ok(account) => account.unwrap_or_default(),
Err(err) => return TransactionValidationOutcome::Error(transaction, Box::new(err)),
};
// Signer account shouldn't have bytecode. Presence of bytecode means this is a
// smartcontract.
if account.has_bytecode() {
return TransactionValidationOutcome::Invalid(
transaction,
InvalidTransactionError::SignerAccountHasBytecode.into(),
)
}
// Checks for nonce
if transaction.nonce() < account.nonce {
return TransactionValidationOutcome::Invalid(
transaction,
InvalidTransactionError::NonceNotConsistent.into(),
)
}
// Checks for max cost
if transaction.cost() > account.balance {
let cost = transaction.cost();
return TransactionValidationOutcome::Invalid(
transaction,
InvalidTransactionError::InsufficientFunds {
cost,
available_funds: account.balance,
}
.into(),
)
}
// Return the valid transaction
TransactionValidationOutcome::Valid {
balance: account.balance,
state_nonce: account.nonce,
transaction,
}
}
}
/// A valid transaction in the pool.
pub struct ValidPoolTransaction<T: PoolTransaction> {
/// The transaction
pub transaction: T,
/// The identifier for this transaction.
pub transaction_id: TransactionId,
/// Whether to propagate the transaction.
pub propagate: bool,
/// Total cost of the transaction: `feeCap x gasLimit + transferredValue`.
pub cost: U256,
/// Timestamp when this was added to the pool.
pub timestamp: Instant,
/// Where this transaction originated from.
pub origin: TransactionOrigin,
/// The length of the rlp encoded transaction (cached)
pub encoded_length: usize,
}
// === impl ValidPoolTransaction ===
impl<T: PoolTransaction> ValidPoolTransaction<T> {
/// Returns the hash of the transaction.
pub fn hash(&self) -> &TxHash {
self.transaction.hash()
}
/// Returns the type identifier of the transaction
pub fn tx_type(&self) -> u8 {
self.transaction.tx_type()
}
/// Returns the address of the sender
pub fn sender(&self) -> Address {
self.transaction.sender()
}
/// Returns the internal identifier for the sender of this transaction
pub(crate) fn sender_id(&self) -> SenderId {
self.transaction_id.sender
}
/// Returns the internal identifier for this transaction.
pub(crate) fn id(&self) -> &TransactionId {
&self.transaction_id
}
/// Returns the nonce set for this transaction.
pub fn nonce(&self) -> u64 {
self.transaction.nonce()
}
/// Returns the EIP-1559 Max base fee the caller is willing to pay.
///
/// For legacy transactions this is gas_price.
pub fn max_fee_per_gas(&self) -> u128 {
self.transaction.max_fee_per_gas()
}
/// Returns the EIP-1559 Max base fee the caller is willing to pay.
pub fn effective_gas_price(&self) -> u128 {
self.transaction.effective_gas_price()
}
/// Amount of gas that should be used in executing this transaction. This is paid up-front.
pub fn gas_limit(&self) -> u64 {
self.transaction.gas_limit()
}
/// Whether the transaction originated locally.
pub fn is_local(&self) -> bool {
self.origin.is_local()
}
/// The heap allocated size of this transaction.
pub(crate) fn size(&self) -> usize {
self.transaction.size()
}
}
impl<T: PoolTransaction> IntoRecoveredTransaction for ValidPoolTransaction<T> {
fn to_recovered_transaction(&self) -> TransactionSignedEcRecovered {
self.transaction.to_recovered_transaction()
}
}
#[cfg(test)]
impl<T: PoolTransaction + Clone> Clone for ValidPoolTransaction<T> {
fn clone(&self) -> Self {
Self {
transaction: self.transaction.clone(),
transaction_id: self.transaction_id,
propagate: self.propagate,
cost: self.cost,
timestamp: self.timestamp,
origin: self.origin,
encoded_length: self.encoded_length,
}
}
}
impl<T: PoolTransaction> fmt::Debug for ValidPoolTransaction<T> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "Transaction {{ ")?;
write!(fmt, "hash: {:?}, ", &self.transaction.hash())?;
write!(fmt, "provides: {:?}, ", &self.transaction_id)?;
write!(fmt, "raw tx: {:?}", &self.transaction)?;
write!(fmt, "}}")?;
Ok(())
}
}

View File

@ -0,0 +1,306 @@
//! Ethereum transaction validator.
use crate::{
error::InvalidPoolTransactionError,
traits::{PoolTransaction, TransactionOrigin},
validate::{task::ValidationJobSender, TransactionValidatorError, ValidationTask},
TransactionValidationOutcome, TransactionValidator, MAX_INIT_CODE_SIZE, TX_MAX_SIZE,
};
use reth_primitives::{
constants::ETHEREUM_BLOCK_GAS_LIMIT, ChainSpec, InvalidTransactionError, EIP1559_TX_TYPE_ID,
EIP2930_TX_TYPE_ID, LEGACY_TX_TYPE_ID,
};
use reth_provider::{AccountReader, StateProviderFactory};
use reth_tasks::TaskSpawner;
use std::{marker::PhantomData, sync::Arc};
use tokio::sync::{oneshot, Mutex};
/// A [TransactionValidator] implementation that validates ethereum transaction.
///
/// This validator is non-blocking, all validation work is done in a separate task.
#[derive(Debug, Clone)]
pub struct EthTransactionValidator<Client, T> {
/// The type that performs the actual validation.
inner: Arc<EthTransactionValidatorInner<Client, T>>,
/// The sender half to validation tasks that perform the actual validation.
to_validation_task: Arc<Mutex<ValidationJobSender>>,
}
// === impl EthTransactionValidator ===
impl<Client, Tx> EthTransactionValidator<Client, Tx> {
/// Creates a new instance for the given [ChainSpec]
///
/// This will always spawn a validation tasks that perform the actual validation. A will spawn
/// `num_additional_tasks` additional tasks.
pub fn new<T>(
client: Client,
chain_spec: Arc<ChainSpec>,
tasks: T,
num_additional_tasks: usize,
) -> Self
where
T: TaskSpawner,
{
let inner = EthTransactionValidatorInner {
chain_spec,
client,
shanghai: true,
eip2718: true,
eip1559: true,
block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT,
minimum_priority_fee: None,
_marker: Default::default(),
};
let (tx, task) = ValidationTask::new();
// Spawn validation tasks, they are blocking because they perform db lookups
for _ in 0..num_additional_tasks {
let task = task.clone();
tasks.spawn_blocking(Box::pin(async move {
task.run().await;
}));
}
tasks.spawn_critical_blocking(
"transaction-validation-service",
Box::pin(async move {
task.run().await;
}),
);
let to_validation_task = Arc::new(Mutex::new(tx));
Self { inner: Arc::new(inner), to_validation_task }
}
/// Returns the configured chain id
pub fn chain_id(&self) -> u64 {
self.inner.chain_id()
}
}
#[async_trait::async_trait]
impl<Client, Tx> TransactionValidator for EthTransactionValidator<Client, Tx>
where
Client: StateProviderFactory + Clone + 'static,
Tx: PoolTransaction + 'static,
{
type Transaction = Tx;
async fn validate_transaction(
&self,
origin: TransactionOrigin,
transaction: Self::Transaction,
) -> TransactionValidationOutcome<Self::Transaction> {
let hash = *transaction.hash();
let (tx, rx) = oneshot::channel();
{
let to_validation_task = self.to_validation_task.clone();
let to_validation_task = to_validation_task.lock().await;
let validator = Arc::clone(&self.inner);
let res = to_validation_task
.send(Box::pin(async move {
let res = validator.validate_transaction(origin, transaction).await;
let _ = tx.send(res);
}))
.await;
if res.is_err() {
return TransactionValidationOutcome::Error(
hash,
Box::new(TransactionValidatorError::ValidationServiceUnreachable),
)
}
}
match rx.await {
Ok(res) => res,
Err(_) => TransactionValidationOutcome::Error(
hash,
Box::new(TransactionValidatorError::ValidationServiceUnreachable),
),
}
}
}
/// A [TransactionValidator] implementation that validates ethereum transaction.
#[derive(Debug, Clone)]
struct EthTransactionValidatorInner<Client, T> {
/// Spec of the chain
chain_spec: Arc<ChainSpec>,
/// This type fetches account info from the db
client: Client,
/// Fork indicator whether we are in the Shanghai stage.
shanghai: bool,
/// Fork indicator whether we are using EIP-2718 type transactions.
eip2718: bool,
/// Fork indicator whether we are using EIP-1559 type transactions.
eip1559: bool,
/// The current max gas limit
block_gas_limit: u64,
/// Minimum priority fee to enforce for acceptance into the pool.
minimum_priority_fee: Option<u128>,
/// Marker for the transaction type
_marker: PhantomData<T>,
}
// === impl EthTransactionValidatorInner ===
impl<Client, Tx> EthTransactionValidatorInner<Client, Tx> {
/// Returns the configured chain id
fn chain_id(&self) -> u64 {
self.chain_spec.chain().id()
}
}
#[async_trait::async_trait]
impl<Client, Tx> TransactionValidator for EthTransactionValidatorInner<Client, Tx>
where
Client: StateProviderFactory,
Tx: PoolTransaction,
{
type Transaction = Tx;
async fn validate_transaction(
&self,
origin: TransactionOrigin,
transaction: Self::Transaction,
) -> TransactionValidationOutcome<Self::Transaction> {
// Checks for tx_type
match transaction.tx_type() {
LEGACY_TX_TYPE_ID => {
// Accept legacy transactions
}
EIP2930_TX_TYPE_ID => {
// Accept only legacy transactions until EIP-2718/2930 activates
if !self.eip2718 {
return TransactionValidationOutcome::Invalid(
transaction,
InvalidTransactionError::Eip1559Disabled.into(),
)
}
}
EIP1559_TX_TYPE_ID => {
// Reject dynamic fee transactions until EIP-1559 activates.
if !self.eip1559 {
return TransactionValidationOutcome::Invalid(
transaction,
InvalidTransactionError::Eip1559Disabled.into(),
)
}
}
_ => {
return TransactionValidationOutcome::Invalid(
transaction,
InvalidTransactionError::TxTypeNotSupported.into(),
)
}
};
// Reject transactions over defined size to prevent DOS attacks
if transaction.size() > TX_MAX_SIZE {
let size = transaction.size();
return TransactionValidationOutcome::Invalid(
transaction,
InvalidPoolTransactionError::OversizedData(size, TX_MAX_SIZE),
)
}
// Check whether the init code size has been exceeded.
if self.shanghai {
if let Err(err) = self.ensure_max_init_code_size(&transaction, MAX_INIT_CODE_SIZE) {
return TransactionValidationOutcome::Invalid(transaction, err)
}
}
// Checks for gas limit
if transaction.gas_limit() > self.block_gas_limit {
let gas_limit = transaction.gas_limit();
return TransactionValidationOutcome::Invalid(
transaction,
InvalidPoolTransactionError::ExceedsGasLimit(gas_limit, self.block_gas_limit),
)
}
// Ensure max_priority_fee_per_gas (if EIP1559) is less than max_fee_per_gas if any.
if transaction.max_priority_fee_per_gas() > Some(transaction.max_fee_per_gas()) {
return TransactionValidationOutcome::Invalid(
transaction,
InvalidTransactionError::TipAboveFeeCap.into(),
)
}
// Drop non-local transactions with a fee lower than the configured fee for acceptance into
// the pool.
if !origin.is_local() &&
transaction.is_eip1559() &&
transaction.max_priority_fee_per_gas() < self.minimum_priority_fee
{
return TransactionValidationOutcome::Invalid(
transaction,
InvalidPoolTransactionError::Underpriced,
)
}
// Checks for chainid
if let Some(chain_id) = transaction.chain_id() {
if chain_id != self.chain_id() {
return TransactionValidationOutcome::Invalid(
transaction,
InvalidTransactionError::ChainIdMismatch.into(),
)
}
}
let account = match self
.client
.latest()
.and_then(|state| state.basic_account(transaction.sender()))
{
Ok(account) => account.unwrap_or_default(),
Err(err) => {
return TransactionValidationOutcome::Error(*transaction.hash(), Box::new(err))
}
};
// Signer account shouldn't have bytecode. Presence of bytecode means this is a
// smartcontract.
if account.has_bytecode() {
return TransactionValidationOutcome::Invalid(
transaction,
InvalidTransactionError::SignerAccountHasBytecode.into(),
)
}
// Checks for nonce
if transaction.nonce() < account.nonce {
return TransactionValidationOutcome::Invalid(
transaction,
InvalidTransactionError::NonceNotConsistent.into(),
)
}
// Checks for max cost
if transaction.cost() > account.balance {
let cost = transaction.cost();
return TransactionValidationOutcome::Invalid(
transaction,
InvalidTransactionError::InsufficientFunds {
cost,
available_funds: account.balance,
}
.into(),
)
}
// Return the valid transaction
TransactionValidationOutcome::Valid {
balance: account.balance,
state_nonce: account.nonce,
transaction,
}
}
}

View File

@ -0,0 +1,225 @@
//! Transaction validation abstractions.
use crate::{
error::InvalidPoolTransactionError,
identifier::{SenderId, TransactionId},
traits::{PoolTransaction, TransactionOrigin},
};
use reth_primitives::{
Address, IntoRecoveredTransaction, TransactionKind, TransactionSignedEcRecovered, TxHash, U256,
};
use std::{fmt, time::Instant};
mod eth;
mod task;
/// A [TransactionValidator] implementation that validates ethereum transaction.
pub use eth::EthTransactionValidator;
/// A spawnable task that performs transaction validation.
pub use task::ValidationTask;
/// A Result type returned after checking a transaction's validity.
#[derive(Debug)]
pub enum TransactionValidationOutcome<T: PoolTransaction> {
/// The transaction is considered _currently_ valid and can be inserted into the pool.
Valid {
/// Balance of the sender at the current point.
balance: U256,
/// Current nonce of the sender.
state_nonce: u64,
/// Validated transaction.
transaction: T,
},
/// The transaction is considered invalid indefinitely: It violates constraints that prevent
/// this transaction from ever becoming valid.
Invalid(T, InvalidPoolTransactionError),
/// An error occurred while trying to validate the transaction
Error(TxHash, Box<dyn std::error::Error + Send + Sync>),
}
impl<T: PoolTransaction> TransactionValidationOutcome<T> {
/// Returns the hash of the transactions
pub fn tx_hash(&self) -> TxHash {
match self {
Self::Valid { transaction, .. } => *transaction.hash(),
Self::Invalid(transaction, ..) => *transaction.hash(),
Self::Error(hash, ..) => *hash,
}
}
}
/// Provides support for validating transaction at any given state of the chain
#[async_trait::async_trait]
pub trait TransactionValidator: Send + Sync {
/// The transaction type to validate.
type Transaction: PoolTransaction;
/// Validates the transaction and returns a [`TransactionValidationOutcome`] describing the
/// validity of the given transaction.
///
/// This will be used by the transaction-pool to check whether the transaction should be
/// inserted into the pool or discarded right away.
///
/// Implementers of this trait must ensure that the transaction is well-formed, i.e. that it
/// complies at least all static constraints, which includes checking for:
///
/// * chain id
/// * gas limit
/// * max cost
/// * nonce >= next nonce of the sender
/// * ...
///
/// See [InvalidTransactionError](reth_primitives::InvalidTransactionError) for common errors
/// variants.
///
/// The transaction pool makes no additional assumptions about the validity of the transaction
/// at the time of this call before it inserts it into the pool. However, the validity of
/// this transaction is still subject to future (dynamic) changes enforced by the pool, for
/// example nonce or balance changes. Hence, any validation checks must be applied in this
/// function.
///
/// See [EthTransactionValidator] for a reference implementation.
async fn validate_transaction(
&self,
origin: TransactionOrigin,
transaction: Self::Transaction,
) -> TransactionValidationOutcome<Self::Transaction>;
/// Ensure that the code size is not greater than `max_init_code_size`.
/// `max_init_code_size` should be configurable so this will take it as an argument.
fn ensure_max_init_code_size(
&self,
transaction: &Self::Transaction,
max_init_code_size: usize,
) -> Result<(), InvalidPoolTransactionError> {
if *transaction.kind() == TransactionKind::Create && transaction.size() > max_init_code_size
{
Err(InvalidPoolTransactionError::ExceedsMaxInitCodeSize(
transaction.size(),
max_init_code_size,
))
} else {
Ok(())
}
}
}
/// A valid transaction in the pool.
pub struct ValidPoolTransaction<T: PoolTransaction> {
/// The transaction
pub transaction: T,
/// The identifier for this transaction.
pub transaction_id: TransactionId,
/// Whether to propagate the transaction.
pub propagate: bool,
/// Total cost of the transaction: `feeCap x gasLimit + transferredValue`.
pub cost: U256,
/// Timestamp when this was added to the pool.
pub timestamp: Instant,
/// Where this transaction originated from.
pub origin: TransactionOrigin,
/// The length of the rlp encoded transaction (cached)
pub encoded_length: usize,
}
// === impl ValidPoolTransaction ===
impl<T: PoolTransaction> ValidPoolTransaction<T> {
/// Returns the hash of the transaction.
pub fn hash(&self) -> &TxHash {
self.transaction.hash()
}
/// Returns the type identifier of the transaction
pub fn tx_type(&self) -> u8 {
self.transaction.tx_type()
}
/// Returns the address of the sender
pub fn sender(&self) -> Address {
self.transaction.sender()
}
/// Returns the internal identifier for the sender of this transaction
pub(crate) fn sender_id(&self) -> SenderId {
self.transaction_id.sender
}
/// Returns the internal identifier for this transaction.
pub(crate) fn id(&self) -> &TransactionId {
&self.transaction_id
}
/// Returns the nonce set for this transaction.
pub fn nonce(&self) -> u64 {
self.transaction.nonce()
}
/// Returns the EIP-1559 Max base fee the caller is willing to pay.
///
/// For legacy transactions this is gas_price.
pub fn max_fee_per_gas(&self) -> u128 {
self.transaction.max_fee_per_gas()
}
/// Returns the EIP-1559 Max base fee the caller is willing to pay.
pub fn effective_gas_price(&self) -> u128 {
self.transaction.effective_gas_price()
}
/// Amount of gas that should be used in executing this transaction. This is paid up-front.
pub fn gas_limit(&self) -> u64 {
self.transaction.gas_limit()
}
/// Whether the transaction originated locally.
pub fn is_local(&self) -> bool {
self.origin.is_local()
}
/// The heap allocated size of this transaction.
pub(crate) fn size(&self) -> usize {
self.transaction.size()
}
}
impl<T: PoolTransaction> IntoRecoveredTransaction for ValidPoolTransaction<T> {
fn to_recovered_transaction(&self) -> TransactionSignedEcRecovered {
self.transaction.to_recovered_transaction()
}
}
#[cfg(test)]
impl<T: PoolTransaction + Clone> Clone for ValidPoolTransaction<T> {
fn clone(&self) -> Self {
Self {
transaction: self.transaction.clone(),
transaction_id: self.transaction_id,
propagate: self.propagate,
cost: self.cost,
timestamp: self.timestamp,
origin: self.origin,
encoded_length: self.encoded_length,
}
}
}
impl<T: PoolTransaction> fmt::Debug for ValidPoolTransaction<T> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "Transaction {{ ")?;
write!(fmt, "hash: {:?}, ", &self.transaction.hash())?;
write!(fmt, "provides: {:?}, ", &self.transaction_id)?;
write!(fmt, "raw tx: {:?}", &self.transaction)?;
write!(fmt, "}}")?;
Ok(())
}
}
/// Validation Errors that can occur during transaction validation.
#[derive(thiserror::Error, Debug)]
pub enum TransactionValidatorError {
/// Failed to communicate with the validation service.
#[error("Validation service unreachable")]
ValidationServiceUnreachable,
}

View File

@ -0,0 +1,62 @@
//! A validation service for transactions.
use crate::validate::TransactionValidatorError;
use futures_util::{lock::Mutex, StreamExt};
use std::{future::Future, pin::Pin, sync::Arc};
use tokio::sync::mpsc;
use tokio_stream::wrappers::ReceiverStream;
/// A service that performs validation jobs.
#[derive(Clone)]
pub struct ValidationTask {
#[allow(clippy::type_complexity)]
validation_jobs: Arc<Mutex<ReceiverStream<Pin<Box<dyn Future<Output = ()> + Send>>>>>,
}
impl ValidationTask {
/// Creates a new clonable task pair
pub fn new() -> (ValidationJobSender, Self) {
let (tx, rx) = mpsc::channel(1);
(ValidationJobSender { tx }, Self::with_receiver(rx))
}
/// Creates a new task with the given receiver.
pub fn with_receiver(jobs: mpsc::Receiver<Pin<Box<dyn Future<Output = ()> + Send>>>) -> Self {
ValidationTask { validation_jobs: Arc::new(Mutex::new(ReceiverStream::new(jobs))) }
}
/// Executes all new validation jobs that come in.
///
/// This will run as long as the channel is alive and is expected to be spawned as a task.
pub async fn run(self) {
loop {
let task = self.validation_jobs.lock().await.next().await;
match task {
None => return,
Some(task) => task.await,
}
}
}
}
impl std::fmt::Debug for ValidationTask {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ValidationTask").field("validation_jobs", &"...").finish()
}
}
/// A sender new type for sending validation jobs to [ValidationTask].
#[derive(Debug)]
pub struct ValidationJobSender {
tx: mpsc::Sender<Pin<Box<dyn Future<Output = ()> + Send>>>,
}
impl ValidationJobSender {
/// Sends the given job to the validation task.
pub async fn send(
&self,
job: Pin<Box<dyn Future<Output = ()> + Send>>,
) -> Result<(), TransactionValidatorError> {
self.tx.send(job).await.map_err(|_| TransactionValidatorError::ValidationServiceUnreachable)
}
}