mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 15:16:43 +00:00
Merge branch 'master' into enhance-simulation-api
This commit is contained in:
commit
cde1056e24
55 changed files with 2135 additions and 269 deletions
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
|
|
@ -29,7 +29,7 @@ jobs:
|
|||
- name: Run linters
|
||||
run: |
|
||||
go run build/ci.go lint
|
||||
go run build/ci.go check_tidy
|
||||
go run build/ci.go check_generate
|
||||
go run build/ci.go check_baddeps
|
||||
|
||||
build:
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ for:
|
|||
- image: Ubuntu
|
||||
build_script:
|
||||
- go run build/ci.go lint
|
||||
- go run build/ci.go check_tidy
|
||||
- go run build/ci.go check_generate
|
||||
- go run build/ci.go check_baddeps
|
||||
- go run build/ci.go install -dlgo
|
||||
|
|
|
|||
|
|
@ -2,126 +2,130 @@
|
|||
|
||||
# version:spec-tests pectra-devnet-6@v1.0.0
|
||||
# https://github.com/ethereum/execution-spec-tests/releases
|
||||
# https://github.com/ethereum/execution-spec-tests/releases/download/pectra-devnet-6%40v1.0.0/fixtures_pectra-devnet-6.tar.gz
|
||||
# https://github.com/ethereum/execution-spec-tests/releases/download/pectra-devnet-6%40v1.0.0/
|
||||
b69211752a3029083c020dc635fe12156ca1a6725a08559da540a0337586a77e fixtures_pectra-devnet-6.tar.gz
|
||||
|
||||
# version:golang 1.24.0
|
||||
# version:golang 1.24.1
|
||||
# https://go.dev/dl/
|
||||
d14120614acb29d12bcab72bd689f257eb4be9e0b6f88a8fb7e41ac65f8556e5 go1.24.0.src.tar.gz
|
||||
5d04588154d5923bd8e26b76111806340ec55c41af1b05623ea744fcb3d6bc22 go1.24.0.aix-ppc64.tar.gz
|
||||
7af054e5088b68c24b3d6e135e5ca8d91bbd5a05cb7f7f0187367b3e6e9e05ee go1.24.0.darwin-amd64.tar.gz
|
||||
dee0ea64411a00b47ded586d5a8e30cfe3acf51564aa1bb24e039a6dca807a29 go1.24.0.darwin-amd64.pkg
|
||||
fd9cfb5dd6c75a347cfc641a253f0db1cebaca16b0dd37965351c6184ba595e4 go1.24.0.darwin-arm64.tar.gz
|
||||
b19eb6b7ae87f8371c3e7a84d129db67779a2883d2fffa6bb90412b0167df133 go1.24.0.darwin-arm64.pkg
|
||||
d0dc34ad86aea746abe245994c68a9e1ad8f46ba8c4af901cd5861a4dd4c21df go1.24.0.dragonfly-amd64.tar.gz
|
||||
4ee02b1f3812aff4da79c79464ee4038ca61ad74b3a9619850f30435f81c2536 go1.24.0.freebsd-386.tar.gz
|
||||
838191001f9324da904dece35a586a3156d548687db87ac9461aa3d38fc88b09 go1.24.0.freebsd-amd64.tar.gz
|
||||
ce6ad4e84a40a8a1d848b7e31b0cddfd1cee8f7959e7dc358a8fa8b5566ea718 go1.24.0.freebsd-arm.tar.gz
|
||||
511f7b0cac4c4ed1066d324072ce223b906ad6b2a85f2e1c5d260eb7d08b5901 go1.24.0.freebsd-arm64.tar.gz
|
||||
a1e4072630dc589a2975ef51317b52c7d8599bf6f389fc59033c01e0a0fa705a go1.24.0.freebsd-riscv64.tar.gz
|
||||
7593e9dcee9f07c3df6d099f7d259f5734a6c0dccc5f28962f18e7f501c9bb21 go1.24.0.illumos-amd64.tar.gz
|
||||
90521453a59c6ce20364d2dc7c38532949b033b602ba12d782caeb90af1b0624 go1.24.0.linux-386.tar.gz
|
||||
dea9ca38a0b852a74e81c26134671af7c0fbe65d81b0dc1c5bfe22cf7d4c8858 go1.24.0.linux-amd64.tar.gz
|
||||
c3fa6d16ffa261091a5617145553c71d21435ce547e44cc6dfb7470865527cc7 go1.24.0.linux-arm64.tar.gz
|
||||
695dc54fa14cd3124fa6900d7b5ae39eeac23f7a4ecea81656070160fac2c54a go1.24.0.linux-armv6l.tar.gz
|
||||
a201e4c9b7e6d29ed64c43296ed88e81a66f82f2093ce45b766d2c526941396f go1.24.0.linux-loong64.tar.gz
|
||||
f3ac039aae78ad0bfb08106406c2e62eaf763dd82ebaf0ecd539adadd1d729a6 go1.24.0.linux-mips.tar.gz
|
||||
f2e6456d45e024831b1da8d88b1bb6392cca9500c1b00841f525d76c9e9553e0 go1.24.0.linux-mips64.tar.gz
|
||||
b847893ff119389c939adc2b8516b6500204b7cb49d5e19b25e1c2091d2c74c6 go1.24.0.linux-mips64le.tar.gz
|
||||
bd4aed27d02746c237c3921e97029ac6b6fe687a67436b8f52ff1f698d330bd9 go1.24.0.linux-mipsle.tar.gz
|
||||
007123c9b06c41729a4bb3f166f4df7196adf4e33c2d2ab0e7e990175f0ce1d4 go1.24.0.linux-ppc64.tar.gz
|
||||
a871a43de7d26c91dd90cb6e0adacb214c9e35ee2188c617c91c08c017efe81a go1.24.0.linux-ppc64le.tar.gz
|
||||
620dcf48c6297519aad6c81f8e344926dc0ab09a2a79f1e306964aece95a553d go1.24.0.linux-riscv64.tar.gz
|
||||
544d78b077c6b54bf78958c4a8285abec2d21f668fb007261c77418cd2edbb46 go1.24.0.linux-s390x.tar.gz
|
||||
8b143a7edefbaa2a0b0246c9df2df1bac9fbed909d8615a375c08da7744e697d go1.24.0.netbsd-386.tar.gz
|
||||
67150a6dd7bdb9c4e88d77f46ee8c4dc99d5e71deca4912d8c2c85f7a16d0262 go1.24.0.netbsd-amd64.tar.gz
|
||||
446b2539f11218fd6f6f6e3dd90b20ae55a06afe129885eeb3df51eb344eb0f6 go1.24.0.netbsd-arm.tar.gz
|
||||
370115b6ff7d30b29431223de348eb11ab65e3c92627532d97fd55f63f94e7a8 go1.24.0.netbsd-arm64.tar.gz
|
||||
cbda5f15f06ed9630f122a53542d9de13d149643633c74f1dcb45e79649b788a go1.24.0.openbsd-386.tar.gz
|
||||
926f601d0e655ab1e8d7f357fd82542e5cf206c38c4e2f9fccf0706987d38836 go1.24.0.openbsd-amd64.tar.gz
|
||||
8a54892f8c933c541fff144a825d0fdc41bae14b0832aab703cb75eb4cb64f2c go1.24.0.openbsd-arm.tar.gz
|
||||
ef7fddcef0a22c7900c178b7687cf5aa25c2a9d46a3cc330b77a6de6e6c2396b go1.24.0.openbsd-arm64.tar.gz
|
||||
b3b5e2e2b53489ded2c2c21900ddcbbdb7991632bb5b42f05f125d71675e0b76 go1.24.0.openbsd-ppc64.tar.gz
|
||||
fbcb1dbf1269b4079dc4fd0b15f3274b9d635f1a7e319c3fc1a907b03280348e go1.24.0.openbsd-riscv64.tar.gz
|
||||
33b4221e1c174a16e3f661deab6c60838ac4ae6cb869a4da1d1115773ceed88b go1.24.0.plan9-386.tar.gz
|
||||
111a89014019cdbd69c2978de9b3e201f77e35183c8ab3606fba339d38f28549 go1.24.0.plan9-amd64.tar.gz
|
||||
8da3d3997049f40ebe0cd336a9bb9e4bfa4832df3c90a32f07383371d6d74849 go1.24.0.plan9-arm.tar.gz
|
||||
b6069da21dc95ccdbd047675b584e5480ffc3eba35f9e7c8b0e7b317aaf01e2c go1.24.0.solaris-amd64.tar.gz
|
||||
b53c28a4c2863ec50ab4a1dbebe818ef6177f86773b6f43475d40a5d9aa4ec9e go1.24.0.windows-386.zip
|
||||
f07677013cd7861c5e16067b0a82144c23c4bf72c139c762e142440f4c926f61 go1.24.0.windows-386.msi
|
||||
96b7280979205813759ee6947be7e3bb497da85c482711116c00522e3bb41ff1 go1.24.0.windows-amd64.zip
|
||||
4e78016d889431eb16aa0f87868cf52479b90059791c94a4ff45872d0573089e go1.24.0.windows-amd64.msi
|
||||
53f73450fb66075d16be9f206e9177bd972b528168271918c4747903b5596c3d go1.24.0.windows-arm64.zip
|
||||
4f9780158fb7996dcbcbc7c7ef208f880b5e8c1f2792ba3ede0c75050c1bc23a go1.24.0.windows-arm64.msi
|
||||
8244ebf46c65607db10222b5806aeb31c1fcf8979c1b6b12f60c677e9a3c0656 go1.24.1.src.tar.gz
|
||||
8d627dc163a4bffa2b1887112ad6194af175dce108d606ed1714a089fb806033 go1.24.1.aix-ppc64.tar.gz
|
||||
addbfce2056744962e2d7436313ab93486660cf7a2e066d171b9d6f2da7c7abe go1.24.1.darwin-amd64.tar.gz
|
||||
58d529334561cff11087cd4ab18fe0b46d8d5aad88f45c02b9645f847e014512 go1.24.1.darwin-amd64.pkg
|
||||
295581b5619acc92f5106e5bcb05c51869337eb19742fdfa6c8346c18e78ff88 go1.24.1.darwin-arm64.tar.gz
|
||||
78b0fc8ddc344eb499f1a952c687cb84cbd28ba2b739cfa0d4eb042f07e44e82 go1.24.1.darwin-arm64.pkg
|
||||
e70053f56f7eb93806d80cbd5726f78509a0a467602f7bea0e2c4ee8ed7c3968 go1.24.1.dragonfly-amd64.tar.gz
|
||||
3595e2674ed8fe72e604ca59c964d3e5277aafb08475c2b1aaca2d2fd69c24fc go1.24.1.freebsd-386.tar.gz
|
||||
47d7de8bb64d5c3ee7b6723aa62d5ecb11e3568ef2249bbe1d4bbd432d37c00c go1.24.1.freebsd-amd64.tar.gz
|
||||
04eec3bcfaa14c1370cdf98e8307fac7e4853496c3045afb9c3124a29cbca205 go1.24.1.freebsd-arm.tar.gz
|
||||
51aa70146e40cfdc20927424083dc86e6223f85dc08089913a1651973b55665b go1.24.1.freebsd-arm64.tar.gz
|
||||
3c131d8e3fc285a1340f87813153e24226d3ddbd6e54f3facbd6e4c46a84655e go1.24.1.freebsd-riscv64.tar.gz
|
||||
201d09da737ba39d5367f87d4e8b31edaeeb3dc9b9c407cb8cfb40f90c5a727a go1.24.1.illumos-amd64.tar.gz
|
||||
8c530ecedbc17e42ce10177bea07ccc96a3e77c792ea1ea72173a9675d16ffa5 go1.24.1.linux-386.tar.gz
|
||||
cb2396bae64183cdccf81a9a6df0aea3bce9511fc21469fb89a0c00470088073 go1.24.1.linux-amd64.tar.gz
|
||||
8df5750ffc0281017fb6070fba450f5d22b600a02081dceef47966ffaf36a3af go1.24.1.linux-arm64.tar.gz
|
||||
6d95f8d7884bfe2364644c837f080f2b585903d0b771eb5b06044e226a4f120a go1.24.1.linux-armv6l.tar.gz
|
||||
19304a4a56e46d04604547d2d83235dc4f9b192c79832560ce337d26cc7b835a go1.24.1.linux-loong64.tar.gz
|
||||
6347be77fa5359c12a5308c8ab87147c1fc4717b0c216493d1706c3b9fcde22d go1.24.1.linux-mips.tar.gz
|
||||
1647df415f7030b82d4105670192aa7e8910e18563bb0d505192d72800cc2d21 go1.24.1.linux-mips64.tar.gz
|
||||
762da594e4ec0f9cf6defae6ef971f5f7901203ee6a2d979e317adec96657317 go1.24.1.linux-mips64le.tar.gz
|
||||
9d8133c7b23a557399fab870b5cf464079c2b623a43b214a7567cf11c254a444 go1.24.1.linux-mipsle.tar.gz
|
||||
132f10999abbaccbada47fa85462db30c423955913b14d6c692de25f4636c766 go1.24.1.linux-ppc64.tar.gz
|
||||
0fb522efcefabae6e37e69bdc444094e75bfe824ea6d4cc3cbc70c7ae1b16858 go1.24.1.linux-ppc64le.tar.gz
|
||||
eaef4323d5467ff97fb1979c8491764060dade19f02f3275a9313f9a0da3b9c0 go1.24.1.linux-riscv64.tar.gz
|
||||
6c05e14d8f11094cb56a1c50f390b6b658bed8a7cbd8d1a57e926581b7eabfce go1.24.1.linux-s390x.tar.gz
|
||||
5dbb287d343ea00d58a70b11629f32ee716dc50a6875c459ea2018df0f294cd8 go1.24.1.netbsd-386.tar.gz
|
||||
617aa3faee50ce84c343db0888e9a210c310a7203666b4ed620f31030c9fb32f go1.24.1.netbsd-amd64.tar.gz
|
||||
59a928b7080c4a6ac985946274b7c65ce1cecc0b468ecd992d17b7c12fab9296 go1.24.1.netbsd-arm.tar.gz
|
||||
28daa8d0feb4aef2af60cefa3305bb9314de7e8a05cbca41ac548964cdfe89b7 go1.24.1.netbsd-arm64.tar.gz
|
||||
b7382b2f5d99813aeac14db482faa3bfbd47a68880b607fa2a7e669e26bab9cd go1.24.1.openbsd-386.tar.gz
|
||||
2513b6537c45deead5e641c7ce7502913e7d5e6f0b21c52542fb11f81578690f go1.24.1.openbsd-amd64.tar.gz
|
||||
853c1917d4fc7b144c55a02842aa48542d5cc798dde8db96dc0fdbc263200e04 go1.24.1.openbsd-arm.tar.gz
|
||||
6bc207b91e6f6ae3347fb54616a8fb2f5c11983713846a4cef111ff3f4f94d14 go1.24.1.openbsd-arm64.tar.gz
|
||||
4279260e2f2b94ee94e81470d13db7367f4393b061fee60985528fa0fa430df4 go1.24.1.openbsd-ppc64.tar.gz
|
||||
6fc4023a0a339ee0778522364a127d94c78e62122288d47d820dba703f81dc07 go1.24.1.openbsd-riscv64.tar.gz
|
||||
b5eb9fafd77146e7e1f748acfd95559580ecc8d2f15abf432a20f58c929c7cd2 go1.24.1.plan9-386.tar.gz
|
||||
24dcad6361b141fc8cced15b092351e12a99d2e58d7013204a3013c50daf9fdd go1.24.1.plan9-amd64.tar.gz
|
||||
a026ac3b55aa1e6fdc2aaab30207a117eafbe965ed81d3aa0676409f280ddc37 go1.24.1.plan9-arm.tar.gz
|
||||
8e4f6a77388dc6e5aa481efd5abdb3b9f5c9463bb82f4db074494e04e5c84992 go1.24.1.solaris-amd64.tar.gz
|
||||
b799f4ab264eef12a014c759383ed934056608c483e0f73e34ea6caf9f1df5f9 go1.24.1.windows-386.zip
|
||||
db128981033ac82a64688a123f631e61297b6b8f52ca913145e57caa8ce94cc3 go1.24.1.windows-386.msi
|
||||
95666b551453209a2b8869d29d177285ff9573af10f085d961d7ae5440f645ce go1.24.1.windows-amd64.zip
|
||||
5968e7adcf26e68a54f1cd41ad561275a670a8e2ca5263bc375b524638557dfb go1.24.1.windows-amd64.msi
|
||||
e28c4e6d0b913955765b46157ab88ae59bb636acaa12d7bec959aa6900f1cebd go1.24.1.windows-arm64.zip
|
||||
6d352c1f154a102a5b90c480cc64bab205ccf2681e34e78a3a4d3f1ddfbc81e4 go1.24.1.windows-arm64.msi
|
||||
|
||||
# version:golangci 1.64.4
|
||||
# version:golangci 1.64.6
|
||||
# https://github.com/golangci/golangci-lint/releases/
|
||||
# https://github.com/golangci/golangci-lint/releases/download/v1.64.4/
|
||||
f31ef6e20c62acc31eb7b851d611b4bd10256d1b1d8be38f0cceec300b5b60db golangci-lint-1.64.4-darwin-amd64.tar.gz
|
||||
bb4a9ece7ef66ef49bd6de873f5f5ec801cf93dacf58efb360cc939b4fd37c47 golangci-lint-1.64.4-darwin-arm64.tar.gz
|
||||
fd8f9046150356e36ad8af31f99219515d5e0ff6e8e9f4204182312447018fcf golangci-lint-1.64.4-freebsd-386.tar.gz
|
||||
bbae940caa31cb1d2d8d26abd867666cd92746d2ddc63d6917d128c456d715ef golangci-lint-1.64.4-freebsd-amd64.tar.gz
|
||||
86eb00cbdc5e277857a380d556d78a42b3edf8aae5991de8767dce7da337d73b golangci-lint-1.64.4-freebsd-armv6.tar.gz
|
||||
c0f7d50f4fcd39ae8bda181c127b078a79d87c885ac472c4cce5b01134dde5bc golangci-lint-1.64.4-freebsd-armv7.tar.gz
|
||||
31d264fa26a73a583425bc9b39c18de70a330e346fe800e80c277ddb5f8477b6 golangci-lint-1.64.4-illumos-amd64.tar.gz
|
||||
3dccc18a6b1a0f2518196190ed56277b1480b381231605936eca6b862426b83c golangci-lint-1.64.4-linux-386.deb
|
||||
b95f3e757c59ada75144bd086478ab63bcda83c2b2a935e60d8f39aff20fbcd2 golangci-lint-1.64.4-linux-386.rpm
|
||||
8fa58af0022ae0487162c02d08075e2d869c9b5f8613a64b85fd769a2a2d2b93 golangci-lint-1.64.4-linux-386.tar.gz
|
||||
fa80d2ad920fc74b85a0b93ad1671fd27c9c4d6f22e167cc4e1da758d72dd893 golangci-lint-1.64.4-linux-amd64.deb
|
||||
8bf2e3c82504a893a12e84ecbc8071f787075d2fdc1ef946deda3d29aae1afad golangci-lint-1.64.4-linux-amd64.rpm
|
||||
7de8f80a12cd3052610e6be21f08f2220805b1f3e43b3b6ec843f14a12140de2 golangci-lint-1.64.4-linux-amd64.tar.gz
|
||||
ade222b9ae997a2875e44ed53ca5a129f24944b232132d0a5898f101381d3bfa golangci-lint-1.64.4-linux-arm64.deb
|
||||
8f9f8617ed0d8b8f62c8dfe69877932c0d85f0d83a9ec3250b04777c7133ada1 golangci-lint-1.64.4-linux-arm64.rpm
|
||||
debf1f73e71f1dd288957574eab4e7515b1b04c4ac2f127192181da8a42f0e8e golangci-lint-1.64.4-linux-arm64.tar.gz
|
||||
3f616caa4a16738114ad29076a333c62bb0650fe4846b438c341d235f78ee5aa golangci-lint-1.64.4-linux-armv6.deb
|
||||
6a7c9d7b7c57d7d440e1e3f133e18e4b5b703604d4ae7f7862fb9ea461887a8b golangci-lint-1.64.4-linux-armv6.rpm
|
||||
d7b9e3c89af93d98c6b5a555ef48e25bee24f84d7825a29e343e9966404d5b4d golangci-lint-1.64.4-linux-armv6.tar.gz
|
||||
18179ecf04ace3051450852424b31a4d26644a3912e759cd595e624eee0174bd golangci-lint-1.64.4-linux-armv7.deb
|
||||
f669fce85d3edc3d557d1e64e2241d895f1ee676f79d139874bf76693cc447a2 golangci-lint-1.64.4-linux-armv7.rpm
|
||||
9bbaa13b474ecb1560fb05aee0a8cab3e9cc327d41632bee8b553b766f2bf756 golangci-lint-1.64.4-linux-armv7.tar.gz
|
||||
b5a2725adba2a1b04a5160ffa0730ba30b88c1ee99ecbc65dd7075a8d70f3730 golangci-lint-1.64.4-linux-loong64.deb
|
||||
28ac00afb3f6407808fe7c15a6002e5fe6b3b461df3fe0223603542e6f9b974a golangci-lint-1.64.4-linux-loong64.rpm
|
||||
121170309fed82cb64d6fae5020ef2b194e591a53156370c221a80121cc4aa08 golangci-lint-1.64.4-linux-loong64.tar.gz
|
||||
aadfaab41380f53ada8f7ad4fa9af784bb882eaaa1e3939e048b3a5ea8f2c13f golangci-lint-1.64.4-linux-mips64.deb
|
||||
6fecbff69086e316dd028050d3ebf82fc466c7f4dc0f6084307054621101c202 golangci-lint-1.64.4-linux-mips64.rpm
|
||||
430c48034527af028dc608c115166f47b6b67c8cb83a317d9d44876b9753292a golangci-lint-1.64.4-linux-mips64.tar.gz
|
||||
432126af2bbbd639dbbcc2c265337f104cc613a7b4c5a8fb5e49f182b428f077 golangci-lint-1.64.4-linux-mips64le.deb
|
||||
cfa663eee0fae721510d6dc31e61db4be51b4e4244eb7de2b6a9bc6d475551e5 golangci-lint-1.64.4-linux-mips64le.rpm
|
||||
2bacfac65182893c5ceb67b61d5b9d2b24da47726d84cb87d734a6e32f040148 golangci-lint-1.64.4-linux-mips64le.tar.gz
|
||||
66d86e47d68420c03045eff0698e9acf9f82d97cd1f07d7e52837a6e544eceed golangci-lint-1.64.4-linux-ppc64le.deb
|
||||
05466bd2d8e82f7245b838e29d0c90cdbaf67375b2b5bc980d8510d5edb206ad golangci-lint-1.64.4-linux-ppc64le.rpm
|
||||
e013b6980f7140712af0aedc9a661265e1d3c6adc7e9d045734e3686a3601acc golangci-lint-1.64.4-linux-ppc64le.tar.gz
|
||||
753980d2fb421d35292b71b51f9bb0552f18de2eb181ccf692ef79e086ad2304 golangci-lint-1.64.4-linux-riscv64.deb
|
||||
73a33a883020298f8c652a4ace4f6888103d80d411fe3e9b77ce0a3b36ff7d3a golangci-lint-1.64.4-linux-riscv64.rpm
|
||||
020efd09b894dc75012f43b1d4c58e69e5446ca3f79845b5e10a02588def3e1b golangci-lint-1.64.4-linux-riscv64.tar.gz
|
||||
4d99a16bc9ff5cea5e4ae3568c6254390fbdad8f1d370c287744409112bceff9 golangci-lint-1.64.4-linux-s390x.deb
|
||||
6e63374a9f95fc09f2bb9501059eb18b8107e08416722ba7b561ae385d80b33b golangci-lint-1.64.4-linux-s390x.rpm
|
||||
62a6febb892b26eee84a32cc99d04afe90bf62d0970774b1bab75e36b66f53b4 golangci-lint-1.64.4-linux-s390x.tar.gz
|
||||
5ba4d235f9abd60d2d280f9fe4228c6ddab11ae9fe48d253b7e477046675f1f6 golangci-lint-1.64.4-netbsd-386.tar.gz
|
||||
061643bb4370a52010e5e76f88f5b5220e51058df50af86b45286263af92b321 golangci-lint-1.64.4-netbsd-amd64.tar.gz
|
||||
12b2be6c35bcf7a36374e952c7efec7905a17fa3ebe6214924ba8721b766c4af golangci-lint-1.64.4-netbsd-arm64.tar.gz
|
||||
553a71a677751dccee082ed8afe24cf58bef725f72aa02aedcc6758dd8f44125 golangci-lint-1.64.4-netbsd-armv6.tar.gz
|
||||
74a5a5a7b8c8cd11dccdec303ee6592d904b09836782e6d658be359737f300ef golangci-lint-1.64.4-netbsd-armv7.tar.gz
|
||||
b3ceea2187843f9ba6510303141a72465621e3f190a262c0b48707b9e983ea9a golangci-lint-1.64.4-source.tar.gz
|
||||
fda4de312fcb0b4853b03bcdfb31ac631a68fa0b966f6aa010eb30d14427180b golangci-lint-1.64.4-windows-386.zip
|
||||
8fc60bd809f86c6431ed8f3d1f25917538c0ebc93e670cfb199f25bbaa56e602 golangci-lint-1.64.4-windows-amd64.zip
|
||||
b9058c9d2ae8d75048c6ad8885d11e5ac0a6c40be2af85bfa6605e48d69d3eb6 golangci-lint-1.64.4-windows-arm64.zip
|
||||
809b0131adb1aa8b359becd13222df367b505bc2955bb3b1d2d0dd6f07347b13 golangci-lint-1.64.4-windows-armv6.zip
|
||||
fc43f8c95d6bec8ba9a3557a0bb63cf9f504137ce6d24cfdc4c330d46f7779d2 golangci-lint-1.64.4-windows-armv7.zip
|
||||
# https://github.com/golangci/golangci-lint/releases/download/v1.64.6/
|
||||
08f9459e7125fed2612abd71596e04d172695921aff82120d1c1e5e9b6fff2a3 golangci-lint-1.64.6-darwin-amd64.tar.gz
|
||||
8c10d0c7c3935b8c2269d628b6a06a8f48d8fb4cc31af02fe4ce0cd18b0704c1 golangci-lint-1.64.6-darwin-arm64.tar.gz
|
||||
c07fcabb55deb8d2c4d390025568e76162d7f91b1d14bd2311be45d8d440a624 golangci-lint-1.64.6-freebsd-386.tar.gz
|
||||
8ed1ef1102e1a42983ffcaae8e06de6a540334c3a69e205c610b8a7c92d469cd golangci-lint-1.64.6-freebsd-amd64.tar.gz
|
||||
8f8dda66d1b4a85cc8a1daf1b69af6559c3eb0a41dd8033148a9ad85cfc0e1d9 golangci-lint-1.64.6-freebsd-armv6.tar.gz
|
||||
59e8ca1fa254661b2a55e96515b14a10cd02221d443054deac5b28c3c3e12d6b golangci-lint-1.64.6-freebsd-armv7.tar.gz
|
||||
e3d323d5f132e9b7593141bfe1d19c8460e65a55cea1008ec96945fed563f981 golangci-lint-1.64.6-illumos-amd64.tar.gz
|
||||
5ce910f2a864c5fbeb32a30cbd506e1b2ef215f7a0f422cd5be6370b13db6f03 golangci-lint-1.64.6-linux-386.deb
|
||||
2caab682a26b9a5fb94ba24e3a7e1404fa9eab2c12e36ae1c5548d80a1be190c golangci-lint-1.64.6-linux-386.rpm
|
||||
2d82d0a4716e6d9b70c95103054855cb4b5f20f7bbdee42297f0189955bd14b6 golangci-lint-1.64.6-linux-386.tar.gz
|
||||
9cd82503e9841abcaa57663fc899587fe90363c26d86a793a98d3080fd25e907 golangci-lint-1.64.6-linux-amd64.deb
|
||||
981aaca5e5202d4fbb162ec7080490eb67ef5d32add5fb62fb02210597acc9da golangci-lint-1.64.6-linux-amd64.rpm
|
||||
71e290acbacb7b3ba4f68f0540fb74dc180c4336ac8a6f3bdcd7fcc48b15148d golangci-lint-1.64.6-linux-amd64.tar.gz
|
||||
718016bb06c1f598a8d23c7964e2643de621dbe5808688cb38857eb0bb773c84 golangci-lint-1.64.6-linux-arm64.deb
|
||||
ddc0e7b4b10b47cf894aea157e89e3674bbb60f8f5c480110c21c49cc2c1634d golangci-lint-1.64.6-linux-arm64.rpm
|
||||
99a7ff13dec7a8781a68408b6ecb8a1c5e82786cba3189eaa91d5cdcc24362ce golangci-lint-1.64.6-linux-arm64.tar.gz
|
||||
90e360f89c244394912b8709fb83a900b6d56cf19141df2afaf9e902ef3057b1 golangci-lint-1.64.6-linux-armv6.deb
|
||||
46546ff7c98d873ffcdbee06b39dc1024fc08db4fbf1f6309360a44cf976b5c2 golangci-lint-1.64.6-linux-armv6.rpm
|
||||
e45c1a42868aca0b0ee54d14fb89da216f3b4409367cd7bce3b5f36788b4fc92 golangci-lint-1.64.6-linux-armv6.tar.gz
|
||||
3cf6ddbbbf358db3de4b64a24f9683bbe2da3f003cfdee86cb610124b57abafb golangci-lint-1.64.6-linux-armv7.deb
|
||||
508b6712710da10f11aab9ea5e63af415c932dfe7fff718e649d3100b838f797 golangci-lint-1.64.6-linux-armv7.rpm
|
||||
da9a8bbee86b4dfee73fbc17e0856ec84c5b04919eb09bf3dd5904c39ce41753 golangci-lint-1.64.6-linux-armv7.tar.gz
|
||||
ad496a58284e1e9c8ac6f993fec429dcd96c85a8c4715dbb6530a5af0dae7fbd golangci-lint-1.64.6-linux-loong64.deb
|
||||
3bd70fa737b224750254dce616d9a188570e49b894b0cdb2fd04155e2c061350 golangci-lint-1.64.6-linux-loong64.rpm
|
||||
a535af973499729f2215a84303eb0de61399057aad6901ddea1b4f73f68d5f2c golangci-lint-1.64.6-linux-loong64.tar.gz
|
||||
6ad2a1cd37ca30303a488abfca2de52aff57519901c6d8d2608656fe9fb05292 golangci-lint-1.64.6-linux-mips64.deb
|
||||
5f18369f0ca010a02c267352ebe6e3e0514c6debff49899c9e5520906c0da287 golangci-lint-1.64.6-linux-mips64.rpm
|
||||
3449d6c13307b91b0e8817f8911c07c3412cdb6931b8d101e42db3e9762e91ad golangci-lint-1.64.6-linux-mips64.tar.gz
|
||||
d4712a348f65dcaf9f6c58f1cfd6d0984d54a902873dca5e76f0d686f5c59499 golangci-lint-1.64.6-linux-mips64le.deb
|
||||
57cea4538894558cb8c956d6b69c5509e4304546abe4a467478fc9ada0c736d6 golangci-lint-1.64.6-linux-mips64le.rpm
|
||||
bc030977d44535b2152fddb2732f056d193c043992fe638ddecea21a40ef09fe golangci-lint-1.64.6-linux-mips64le.tar.gz
|
||||
1ceb4e492efa527d246c61798c581f49113756fab8c39bb3eefdb1fa97041f92 golangci-lint-1.64.6-linux-ppc64le.deb
|
||||
454e1c2b3583a8644e0c33a970fb4ce35b8f11848e1a770d9095d99d159ad0ab golangci-lint-1.64.6-linux-ppc64le.rpm
|
||||
fddf30d35923eb6c7bb57520d8645768f802bf86c4cbf5a3a13049efb9e71b82 golangci-lint-1.64.6-linux-ppc64le.tar.gz
|
||||
bd75f0dd6f65bee5821c433803b28f3c427ef3582764c3d0e7f7fae1c9d468b6 golangci-lint-1.64.6-linux-riscv64.deb
|
||||
58457207c225cbd5340c8dcb75d9a44aa890d604e28464115a3a9762febaed04 golangci-lint-1.64.6-linux-riscv64.rpm
|
||||
82639518a613a6705b7e2de5b28c278e875d782a5c97e9c1b2ac10b4ecd7852f golangci-lint-1.64.6-linux-riscv64.tar.gz
|
||||
131d69204f8ced200b1437731e987cc886edac30dc87e6e1dcbd4f833d351713 golangci-lint-1.64.6-linux-s390x.deb
|
||||
ca6caf28ca7a1df7cff720f8fd6ac4b8f2eac10c8cbfe7d2eade70876aded570 golangci-lint-1.64.6-linux-s390x.rpm
|
||||
ed966d28a6512bc2b1120029a9f78ed77f2015e330b589a731d67d59be30b0b1 golangci-lint-1.64.6-linux-s390x.tar.gz
|
||||
b181132b41943fc77ace7f9f5523085d12d3613f27774a0e35e17dd5e65ba589 golangci-lint-1.64.6-netbsd-386.tar.gz
|
||||
f7b81c426006e3c699dc8665797a462aecba8c2cd23ac4971472e55184d95bc9 golangci-lint-1.64.6-netbsd-amd64.tar.gz
|
||||
663d6fb4c3bef57b8aacdb1b1a4634075e55d294ed170724b443374860897ca6 golangci-lint-1.64.6-netbsd-arm64.tar.gz
|
||||
8c7a76ee822568cc19352bbb9b2b0863dc5e185eb07782adbbaf648afd02ebcd golangci-lint-1.64.6-netbsd-armv6.tar.gz
|
||||
0ce26d56ce78e516529e087118ba3f1bcd71d311b4c5b2bde6ec24a6e8966d85 golangci-lint-1.64.6-netbsd-armv7.tar.gz
|
||||
135d5d998168683f46e4fab308cef5aa3c282025b7de6b258f976aadfb820db7 golangci-lint-1.64.6-source.tar.gz
|
||||
ccdb0cc249531a205304a76308cfa7cda830083d083d557884416a2461148263 golangci-lint-1.64.6-windows-386.zip
|
||||
2d88f282e08e1853c70bc7c914b5f58beaa6509903d56098aeb9bc3df30ea9d5 golangci-lint-1.64.6-windows-amd64.zip
|
||||
6a3c6e7afc6916392679d7d6b95ac239827644e3e50ec4e8ca6ab1e4e6db65fe golangci-lint-1.64.6-windows-arm64.zip
|
||||
9c9c1ef9687651566987f93e76252f7526c386901d7d8aeee044ca88115da4b1 golangci-lint-1.64.6-windows-armv6.zip
|
||||
4f0df114155791799dfde8cd8cb6307fecfb17fed70b44205486ec925e2f39cf golangci-lint-1.64.6-windows-armv7.zip
|
||||
|
||||
# This is the builder on PPA that will build Go itself (inception-y), don't modify!
|
||||
#
|
||||
# This version is fine to be old and full of security holes, we just use it
|
||||
# to build the latest Go. Don't change it.
|
||||
#
|
||||
# version:ppa-builder-1 1.19.6
|
||||
# version:ppa-builder-1.19 1.19.6
|
||||
# https://go.dev/dl/
|
||||
d7f0013f82e6d7f862cc6cb5c8cdb48eef5f2e239b35baa97e2f1a7466043767 go1.19.6.src.tar.gz
|
||||
|
||||
# version:ppa-builder-2 1.21.9
|
||||
# version:ppa-builder-1.21 1.21.9
|
||||
# https://go.dev/dl/
|
||||
58f0c5ced45a0012bce2ff7a9df03e128abcc8818ebabe5027bb92bafe20e421 go1.21.9.src.tar.gz
|
||||
|
||||
# version:ppa-builder-1.23 1.23.6
|
||||
# https://go.dev/dl/
|
||||
039c5b04e65279daceee8a6f71e70bd05cf5b801782b6f77c6e19e2ed0511222 go1.23.6.src.tar.gz
|
||||
|
||||
# version:protoc 27.1
|
||||
# https://github.com/protocolbuffers/protobuf/releases/
|
||||
# https://github.com/protocolbuffers/protobuf/releases/download/v27.1/
|
||||
|
|
|
|||
34
build/ci.go
34
build/ci.go
|
|
@ -25,8 +25,7 @@ Usage: go run build/ci.go <command> <command flags/arguments>
|
|||
Available commands are:
|
||||
|
||||
lint -- runs certain pre-selected linters
|
||||
check_tidy -- verifies that everything is 'go mod tidy'-ed
|
||||
check_generate -- verifies that everything is 'go generate'-ed
|
||||
check_generate -- verifies that 'go generate' and 'go mod tidy' do not produce changes
|
||||
check_baddeps -- verifies that certain dependencies are avoided
|
||||
|
||||
install [ -arch architecture ] [ -cc compiler ] [ packages... ] -- builds packages and executables
|
||||
|
|
@ -155,8 +154,6 @@ func main() {
|
|||
doTest(os.Args[2:])
|
||||
case "lint":
|
||||
doLint(os.Args[2:])
|
||||
case "check_tidy":
|
||||
doCheckTidy()
|
||||
case "check_generate":
|
||||
doCheckGenerate()
|
||||
case "check_baddeps":
|
||||
|
|
@ -352,22 +349,6 @@ func downloadSpecTestFixtures(csdb *build.ChecksumDB, cachedir string) string {
|
|||
|
||||
// doCheckTidy assets that the Go modules files are tidied already.
|
||||
func doCheckTidy() {
|
||||
targets := []string{"go.mod", "go.sum"}
|
||||
|
||||
hashes, err := build.HashFiles(targets)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to hash go.mod/go.sum: %v", err)
|
||||
}
|
||||
build.MustRun(new(build.GoToolchain).Go("mod", "tidy"))
|
||||
|
||||
tidied, err := build.HashFiles(targets)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to rehash go.mod/go.sum: %v", err)
|
||||
}
|
||||
if updates := build.DiffHashes(hashes, tidied); len(updates) > 0 {
|
||||
log.Fatalf("files changed on running 'go mod tidy': %v", updates)
|
||||
}
|
||||
fmt.Println("No untidy module files detected.")
|
||||
}
|
||||
|
||||
// doCheckGenerate ensures that re-generating generated files does not cause
|
||||
|
|
@ -375,12 +356,13 @@ func doCheckTidy() {
|
|||
func doCheckGenerate() {
|
||||
var (
|
||||
cachedir = flag.String("cachedir", "./build/cache", "directory for caching binaries.")
|
||||
tc = new(build.GoToolchain)
|
||||
)
|
||||
// Compute the origin hashes of all the files
|
||||
var hashes map[string][32]byte
|
||||
|
||||
var err error
|
||||
hashes, err = build.HashFolder(".", []string{"tests/testdata", "build/cache"})
|
||||
hashes, err = build.HashFolder(".", []string{"tests/testdata", "build/cache", ".git"})
|
||||
if err != nil {
|
||||
log.Fatal("Error computing hashes", "err", err)
|
||||
}
|
||||
|
|
@ -389,13 +371,13 @@ func doCheckGenerate() {
|
|||
protocPath = downloadProtoc(*cachedir)
|
||||
protocGenGoPath = downloadProtocGenGo(*cachedir)
|
||||
)
|
||||
c := new(build.GoToolchain).Go("generate", "./...")
|
||||
c := tc.Go("generate", "./...")
|
||||
pathList := []string{filepath.Join(protocPath, "bin"), protocGenGoPath, os.Getenv("PATH")}
|
||||
c.Env = append(c.Env, "PATH="+strings.Join(pathList, string(os.PathListSeparator)))
|
||||
build.MustRun(c)
|
||||
|
||||
// Check if generate file hashes have changed
|
||||
generated, err := build.HashFolder(".", []string{"tests/testdata", "build/cache"})
|
||||
generated, err := build.HashFolder(".", []string{"tests/testdata", "build/cache", ".git"})
|
||||
if err != nil {
|
||||
log.Fatalf("Error re-computing hashes: %v", err)
|
||||
}
|
||||
|
|
@ -407,6 +389,10 @@ func doCheckGenerate() {
|
|||
log.Fatal("One or more generated files were updated by running 'go generate ./...'")
|
||||
}
|
||||
fmt.Println("No stale files detected.")
|
||||
|
||||
// Run go mod tidy check.
|
||||
build.MustRun(tc.Go("mod", "tidy", "-diff"))
|
||||
fmt.Println("No untidy module files detected.")
|
||||
}
|
||||
|
||||
// doCheckBadDeps verifies whether certain unintended dependencies between some
|
||||
|
|
@ -843,7 +829,7 @@ func downloadGoBootstrapSources(cachedir string) []string {
|
|||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
||||
|
||||
var bundles []string
|
||||
for _, booter := range []string{"ppa-builder-1", "ppa-builder-2"} {
|
||||
for _, booter := range []string{"ppa-builder-1.19", "ppa-builder-1.21", "ppa-builder-1.23"} {
|
||||
gobootVersion, err := build.Version(csdb, booter)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ override_dh_auto_build:
|
|||
# requirements opposed to older versions of Go.
|
||||
(mv .goboot-1 ../ && cd ../.goboot-1/src && ./make.bash)
|
||||
(mv .goboot-2 ../ && cd ../.goboot-2/src && GOROOT_BOOTSTRAP=`pwd`/../../.goboot-1 ./make.bash)
|
||||
(mv .go ../ && cd ../.go/src && GOROOT_BOOTSTRAP=`pwd`/../../.goboot-2 ./make.bash)
|
||||
(mv .goboot-3 ../ && cd ../.goboot-3/src && GOROOT_BOOTSTRAP=`pwd`/../../.goboot-2 ./make.bash)
|
||||
(mv .go ../ && cd ../.go/src && GOROOT_BOOTSTRAP=`pwd`/../../.goboot-3 ./make.bash)
|
||||
|
||||
# We can't download external go modules within Launchpad, so we're shipping the
|
||||
# entire dependency source cache with go-ethereum.
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ func init() {
|
|||
}
|
||||
|
||||
func abigen(c *cli.Context) error {
|
||||
utils.CheckExclusive(c, abiFlag, jsonFlag) // Only one source can be selected.
|
||||
flags.CheckExclusive(c, abiFlag, jsonFlag) // Only one source can be selected.
|
||||
|
||||
if c.String(pkgFlag.Name) == "" {
|
||||
utils.Fatalf("No destination package specified (--pkg)")
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
|
@ -40,7 +41,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/eth/protocols/eth"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
// Chain is a lightweight blockchain-like store which can read a hivechain
|
||||
|
|
@ -162,8 +162,8 @@ func (c *Chain) RootAt(height int) common.Hash {
|
|||
// GetSender returns the address associated with account at the index in the
|
||||
// pre-funded accounts list.
|
||||
func (c *Chain) GetSender(idx int) (common.Address, uint64) {
|
||||
accounts := maps.Keys(c.senders)
|
||||
slices.SortFunc(accounts, common.Address.Cmp)
|
||||
accounts := slices.SortedFunc(maps.Keys(c.senders), common.Address.Cmp)
|
||||
|
||||
addr := accounts[idx]
|
||||
return addr, c.senders[addr].Nonce
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,11 +130,16 @@ func (c *Conn) Write(proto Proto, code uint64, msg any) error {
|
|||
return err
|
||||
}
|
||||
|
||||
var errDisc error = fmt.Errorf("disconnect")
|
||||
|
||||
// ReadEth reads an Eth sub-protocol wire message.
|
||||
func (c *Conn) ReadEth() (any, error) {
|
||||
c.SetReadDeadline(time.Now().Add(timeout))
|
||||
for {
|
||||
code, data, _, err := c.Conn.Read()
|
||||
if code == discMsg {
|
||||
return nil, errDisc
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,12 @@
|
|||
package ethtest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||
|
|
@ -79,6 +83,8 @@ func (s *Suite) EthTests() []utesting.Test {
|
|||
{Name: "InvalidTxs", Fn: s.TestInvalidTxs},
|
||||
{Name: "NewPooledTxs", Fn: s.TestNewPooledTxs},
|
||||
{Name: "BlobViolations", Fn: s.TestBlobViolations},
|
||||
{Name: "TestBlobTxWithoutSidecar", Fn: s.TestBlobTxWithoutSidecar},
|
||||
{Name: "TestBlobTxWithMismatchedSidecar", Fn: s.TestBlobTxWithMismatchedSidecar},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -825,3 +831,194 @@ func (s *Suite) TestBlobViolations(t *utesting.T) {
|
|||
conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// mangleSidecar returns a copy of the given blob transaction where the sidecar
|
||||
// data has been modified to produce a different commitment hash.
|
||||
func mangleSidecar(tx *types.Transaction) *types.Transaction {
|
||||
sidecar := tx.BlobTxSidecar()
|
||||
copy := types.BlobTxSidecar{
|
||||
Blobs: append([]kzg4844.Blob{}, sidecar.Blobs...),
|
||||
Commitments: append([]kzg4844.Commitment{}, sidecar.Commitments...),
|
||||
Proofs: append([]kzg4844.Proof{}, sidecar.Proofs...),
|
||||
}
|
||||
// zero the first commitment to alter the sidecar hash
|
||||
copy.Commitments[0] = kzg4844.Commitment{}
|
||||
return tx.WithBlobTxSidecar(©)
|
||||
}
|
||||
|
||||
func (s *Suite) TestBlobTxWithoutSidecar(t *utesting.T) {
|
||||
t.Log(`This test checks that a blob transaction first advertised/transmitted without blobs will result in the sending peer being disconnected, and the full transaction should be successfully retrieved from another peer.`)
|
||||
tx := s.makeBlobTxs(1, 2, 42)[0]
|
||||
badTx := tx.WithoutBlobTxSidecar()
|
||||
s.testBadBlobTx(t, tx, badTx)
|
||||
}
|
||||
|
||||
func (s *Suite) TestBlobTxWithMismatchedSidecar(t *utesting.T) {
|
||||
t.Log(`This test checks that a blob transaction first advertised/transmitted without blobs, whose commitment don't correspond to the blob_versioned_hashes in the transaction, will result in the sending peer being disconnected, and the full transaction should be successfully retrieved from another peer.`)
|
||||
tx := s.makeBlobTxs(1, 2, 43)[0]
|
||||
badTx := mangleSidecar(tx)
|
||||
s.testBadBlobTx(t, tx, badTx)
|
||||
}
|
||||
|
||||
// readUntil reads eth protocol messages until a message of the target type is
|
||||
// received. It returns an error if there is a disconnect, or if the context
|
||||
// is cancelled before a message of the desired type can be read.
|
||||
func readUntil[T any](ctx context.Context, conn *Conn) (*T, error) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, context.Canceled
|
||||
default:
|
||||
}
|
||||
received, err := conn.ReadEth()
|
||||
if err != nil {
|
||||
if err == errDisc {
|
||||
return nil, errDisc
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
switch res := received.(type) {
|
||||
case *T:
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// readUntilDisconnect reads eth protocol messages until the peer disconnects.
|
||||
// It returns whether the peer disconnects in the next 100ms.
|
||||
func readUntilDisconnect(conn *Conn) (disconnected bool) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||
defer cancel()
|
||||
_, err := readUntil[struct{}](ctx, conn)
|
||||
return err == errDisc
|
||||
}
|
||||
|
||||
func (s *Suite) testBadBlobTx(t *utesting.T, tx *types.Transaction, badTx *types.Transaction) {
|
||||
stage1, stage2, stage3 := new(sync.WaitGroup), new(sync.WaitGroup), new(sync.WaitGroup)
|
||||
stage1.Add(1)
|
||||
stage2.Add(1)
|
||||
stage3.Add(1)
|
||||
|
||||
errc := make(chan error)
|
||||
|
||||
badPeer := func() {
|
||||
// announce the correct hash from the bad peer.
|
||||
// when the transaction is first requested before transmitting it from the bad peer,
|
||||
// trigger step 2: connection and announcement by good peers
|
||||
|
||||
conn, err := s.dial()
|
||||
if err != nil {
|
||||
errc <- fmt.Errorf("dial fail: %v", err)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
if err := conn.peer(s.chain, nil); err != nil {
|
||||
errc <- fmt.Errorf("bad peer: peering failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
ann := eth.NewPooledTransactionHashesPacket{
|
||||
Types: []byte{types.BlobTxType},
|
||||
Sizes: []uint32{uint32(badTx.Size())},
|
||||
Hashes: []common.Hash{badTx.Hash()},
|
||||
}
|
||||
|
||||
if err := conn.Write(ethProto, eth.NewPooledTransactionHashesMsg, ann); err != nil {
|
||||
errc <- fmt.Errorf("sending announcement failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
req, err := readUntil[eth.GetPooledTransactionsPacket](context.Background(), conn)
|
||||
if err != nil {
|
||||
errc <- fmt.Errorf("failed to read GetPooledTransactions message: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
stage1.Done()
|
||||
stage2.Wait()
|
||||
|
||||
// the good peer is connected, and has announced the tx.
|
||||
// proceed to send the incorrect one from the bad peer.
|
||||
|
||||
resp := eth.PooledTransactionsPacket{RequestId: req.RequestId, PooledTransactionsResponse: eth.PooledTransactionsResponse(types.Transactions{badTx})}
|
||||
if err := conn.Write(ethProto, eth.PooledTransactionsMsg, resp); err != nil {
|
||||
errc <- fmt.Errorf("writing pooled tx response failed: %v", err)
|
||||
return
|
||||
}
|
||||
if !readUntilDisconnect(conn) {
|
||||
errc <- fmt.Errorf("expected bad peer to be disconnected")
|
||||
return
|
||||
}
|
||||
stage3.Done()
|
||||
}
|
||||
|
||||
goodPeer := func() {
|
||||
stage1.Wait()
|
||||
|
||||
conn, err := s.dial()
|
||||
if err != nil {
|
||||
errc <- fmt.Errorf("dial fail: %v", err)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
if err := conn.peer(s.chain, nil); err != nil {
|
||||
errc <- fmt.Errorf("peering failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
ann := eth.NewPooledTransactionHashesPacket{
|
||||
Types: []byte{types.BlobTxType},
|
||||
Sizes: []uint32{uint32(tx.Size())},
|
||||
Hashes: []common.Hash{tx.Hash()},
|
||||
}
|
||||
|
||||
if err := conn.Write(ethProto, eth.NewPooledTransactionHashesMsg, ann); err != nil {
|
||||
errc <- fmt.Errorf("sending announcement failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// wait until the bad peer has transmitted the incorrect transaction
|
||||
stage2.Done()
|
||||
stage3.Wait()
|
||||
|
||||
// the bad peer has transmitted the bad tx, and been disconnected.
|
||||
// transmit the same tx but with correct sidecar from the good peer.
|
||||
|
||||
var req *eth.GetPooledTransactionsPacket
|
||||
req, err = readUntil[eth.GetPooledTransactionsPacket](context.Background(), conn)
|
||||
if err != nil {
|
||||
errc <- fmt.Errorf("reading pooled tx request failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if req.GetPooledTransactionsRequest[0] != tx.Hash() {
|
||||
errc <- fmt.Errorf("requested unknown tx hash")
|
||||
return
|
||||
}
|
||||
|
||||
resp := eth.PooledTransactionsPacket{RequestId: req.RequestId, PooledTransactionsResponse: eth.PooledTransactionsResponse(types.Transactions{tx})}
|
||||
if err := conn.Write(ethProto, eth.PooledTransactionsMsg, resp); err != nil {
|
||||
errc <- fmt.Errorf("writing pooled tx response failed: %v", err)
|
||||
return
|
||||
}
|
||||
if readUntilDisconnect(conn) {
|
||||
errc <- fmt.Errorf("unexpected disconnect")
|
||||
return
|
||||
}
|
||||
close(errc)
|
||||
}
|
||||
|
||||
if err := s.engine.sendForkchoiceUpdated(); err != nil {
|
||||
t.Fatalf("send fcu failed: %v", err)
|
||||
}
|
||||
|
||||
go goodPeer()
|
||||
go badPeer()
|
||||
err := <-errc
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"os"
|
||||
"regexp"
|
||||
"slices"
|
||||
|
|
@ -28,7 +29,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/tests"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
var blockTestCommand = &cli.Command{
|
||||
|
|
@ -80,8 +80,7 @@ func runBlockTest(ctx *cli.Context, fname string) ([]testResult, error) {
|
|||
tracer := tracerFromFlags(ctx)
|
||||
|
||||
// Pull out keys to sort and ensure tests are run in order.
|
||||
keys := maps.Keys(tests)
|
||||
slices.Sort(keys)
|
||||
keys := slices.Sorted(maps.Keys(tests))
|
||||
|
||||
// Run all the tests.
|
||||
var results []testResult
|
||||
|
|
|
|||
|
|
@ -1197,7 +1197,7 @@ func setWS(ctx *cli.Context, cfg *node.Config) {
|
|||
// setIPC creates an IPC path configuration from the set command line flags,
|
||||
// returning an empty string if IPC was explicitly disabled, or the set path.
|
||||
func setIPC(ctx *cli.Context, cfg *node.Config) {
|
||||
CheckExclusive(ctx, IPCDisabledFlag, IPCPathFlag)
|
||||
flags.CheckExclusive(ctx, IPCDisabledFlag, IPCPathFlag)
|
||||
switch {
|
||||
case ctx.Bool(IPCDisabledFlag.Name):
|
||||
cfg.IPCPath = ""
|
||||
|
|
@ -1295,8 +1295,8 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
|
|||
cfg.NoDiscovery = true
|
||||
}
|
||||
|
||||
CheckExclusive(ctx, DiscoveryV4Flag, NoDiscoverFlag)
|
||||
CheckExclusive(ctx, DiscoveryV5Flag, NoDiscoverFlag)
|
||||
flags.CheckExclusive(ctx, DiscoveryV4Flag, NoDiscoverFlag)
|
||||
flags.CheckExclusive(ctx, DiscoveryV5Flag, NoDiscoverFlag)
|
||||
cfg.DiscoveryV4 = ctx.Bool(DiscoveryV4Flag.Name)
|
||||
cfg.DiscoveryV5 = ctx.Bool(DiscoveryV5Flag.Name)
|
||||
|
||||
|
|
@ -1528,52 +1528,11 @@ func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) {
|
|||
}
|
||||
}
|
||||
|
||||
// CheckExclusive verifies that only a single instance of the provided flags was
|
||||
// set by the user. Each flag might optionally be followed by a string type to
|
||||
// specialize it further.
|
||||
func CheckExclusive(ctx *cli.Context, args ...interface{}) {
|
||||
set := make([]string, 0, 1)
|
||||
for i := 0; i < len(args); i++ {
|
||||
// Make sure the next argument is a flag and skip if not set
|
||||
flag, ok := args[i].(cli.Flag)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("invalid argument, not cli.Flag type: %T", args[i]))
|
||||
}
|
||||
// Check if next arg extends current and expand its name if so
|
||||
name := flag.Names()[0]
|
||||
|
||||
if i+1 < len(args) {
|
||||
switch option := args[i+1].(type) {
|
||||
case string:
|
||||
// Extended flag check, make sure value set doesn't conflict with passed in option
|
||||
if ctx.String(flag.Names()[0]) == option {
|
||||
name += "=" + option
|
||||
set = append(set, "--"+name)
|
||||
}
|
||||
// shift arguments and continue
|
||||
i++
|
||||
continue
|
||||
|
||||
case cli.Flag:
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid argument, not cli.Flag or string extension: %T", args[i+1]))
|
||||
}
|
||||
}
|
||||
// Mark the flag if it's set
|
||||
if ctx.IsSet(flag.Names()[0]) {
|
||||
set = append(set, "--"+name)
|
||||
}
|
||||
}
|
||||
if len(set) > 1 {
|
||||
Fatalf("Flags %v can't be used at the same time", strings.Join(set, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
// SetEthConfig applies eth-related command line flags to the config.
|
||||
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||
// Avoid conflicting network flags
|
||||
CheckExclusive(ctx, MainnetFlag, DeveloperFlag, SepoliaFlag, HoleskyFlag)
|
||||
CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
|
||||
flags.CheckExclusive(ctx, MainnetFlag, DeveloperFlag, SepoliaFlag, HoleskyFlag)
|
||||
flags.CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
|
||||
|
||||
// Set configurations from CLI flags
|
||||
setEtherbase(ctx, cfg)
|
||||
|
|
@ -1841,7 +1800,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
|||
func MakeBeaconLightConfig(ctx *cli.Context) bparams.ClientConfig {
|
||||
var config bparams.ClientConfig
|
||||
customConfig := ctx.IsSet(BeaconConfigFlag.Name)
|
||||
CheckExclusive(ctx, MainnetFlag, SepoliaFlag, HoleskyFlag, BeaconConfigFlag)
|
||||
flags.CheckExclusive(ctx, MainnetFlag, SepoliaFlag, HoleskyFlag, BeaconConfigFlag)
|
||||
switch {
|
||||
case ctx.Bool(MainnetFlag.Name):
|
||||
config.ChainConfig = *bparams.MainnetLightConfig
|
||||
|
|
|
|||
29
cmd/workload/README.md
Normal file
29
cmd/workload/README.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
## Workload Testing Tool
|
||||
|
||||
This tool performs RPC calls against a live node. It has tests for the Sepolia testnet and
|
||||
Mainnet. Note the tests require a fully synced node.
|
||||
|
||||
To run the tests against a Sepolia node, use:
|
||||
|
||||
```shell
|
||||
> ./workload test --sepolia http://host:8545
|
||||
```
|
||||
|
||||
To run a specific test, use the `--run` flag to filter the test cases. Filtering works
|
||||
similar to the `go test` command. For example, to run only tests for `eth_getBlockByHash`
|
||||
and `eth_getBlockByNumber`, use this command:
|
||||
|
||||
```
|
||||
> ./workload test --sepolia --run History/getBlockBy http://host:8545
|
||||
```
|
||||
|
||||
### Regenerating tests
|
||||
|
||||
There is a facility for updating the tests from the chain. This can also be used to
|
||||
generate the tests for a new network. As an example, to recreate tests for mainnet, run
|
||||
the following commands (in this directory) against a synced mainnet node:
|
||||
|
||||
```shell
|
||||
> go run . filtergen --queries queries/filter_queries_mainnet.json http://host:8545
|
||||
> go run . historygen --history-tests queries/history_mainnet.json http://host:8545
|
||||
```
|
||||
216
cmd/workload/filtertest.go
Normal file
216
cmd/workload/filtertest.go
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
// Copyright 2025 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/internal/utesting"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
type filterTestSuite struct {
|
||||
cfg testConfig
|
||||
queries [][]*filterQuery
|
||||
}
|
||||
|
||||
func newFilterTestSuite(cfg testConfig) *filterTestSuite {
|
||||
s := &filterTestSuite{cfg: cfg}
|
||||
if err := s.loadQueries(); err != nil {
|
||||
exit(err)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *filterTestSuite) allTests() []utesting.Test {
|
||||
return []utesting.Test{
|
||||
{Name: "Filter/ShortRange", Fn: s.filterShortRange},
|
||||
{Name: "Filter/LongRange", Fn: s.filterLongRange, Slow: true},
|
||||
{Name: "Filter/FullRange", Fn: s.filterFullRange, Slow: true},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *filterTestSuite) filterRange(t *utesting.T, test func(query *filterQuery) bool, do func(t *utesting.T, query *filterQuery)) {
|
||||
var count, total int
|
||||
for _, bucket := range s.queries {
|
||||
for _, query := range bucket {
|
||||
if test(query) {
|
||||
total++
|
||||
}
|
||||
}
|
||||
}
|
||||
if total == 0 {
|
||||
t.Fatalf("No suitable queries available")
|
||||
}
|
||||
start := time.Now()
|
||||
last := start
|
||||
for _, bucket := range s.queries {
|
||||
for _, query := range bucket {
|
||||
if test(query) {
|
||||
do(t, query)
|
||||
count++
|
||||
if time.Since(last) > time.Second*5 {
|
||||
t.Logf("Making filter query %d/%d (elapsed: %v)", count, total, time.Since(start))
|
||||
last = time.Now()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
t.Logf("Made %d filter queries (elapsed: %v)", count, time.Since(start))
|
||||
}
|
||||
|
||||
const filterRangeThreshold = 10000
|
||||
|
||||
// filterShortRange runs all short-range filter tests.
|
||||
func (s *filterTestSuite) filterShortRange(t *utesting.T) {
|
||||
s.filterRange(t, func(query *filterQuery) bool {
|
||||
return query.ToBlock+1-query.FromBlock <= filterRangeThreshold
|
||||
}, s.queryAndCheck)
|
||||
}
|
||||
|
||||
// filterShortRange runs all long-range filter tests.
|
||||
func (s *filterTestSuite) filterLongRange(t *utesting.T) {
|
||||
s.filterRange(t, func(query *filterQuery) bool {
|
||||
return query.ToBlock+1-query.FromBlock > filterRangeThreshold
|
||||
}, s.queryAndCheck)
|
||||
}
|
||||
|
||||
// filterFullRange runs all filter tests, extending their range from genesis up
|
||||
// to the latest block. Note that results are only partially verified in this mode.
|
||||
func (s *filterTestSuite) filterFullRange(t *utesting.T) {
|
||||
finalized := mustGetFinalizedBlock(s.cfg.client)
|
||||
s.filterRange(t, func(query *filterQuery) bool {
|
||||
return query.ToBlock+1-query.FromBlock > finalized/2
|
||||
}, s.fullRangeQueryAndCheck)
|
||||
}
|
||||
|
||||
func (s *filterTestSuite) queryAndCheck(t *utesting.T, query *filterQuery) {
|
||||
query.run(s.cfg.client)
|
||||
if query.Err != nil {
|
||||
t.Errorf("Filter query failed (fromBlock: %d toBlock: %d addresses: %v topics: %v error: %v)", query.FromBlock, query.ToBlock, query.Address, query.Topics, query.Err)
|
||||
return
|
||||
}
|
||||
if *query.ResultHash != query.calculateHash() {
|
||||
t.Fatalf("Filter query result mismatch (fromBlock: %d toBlock: %d addresses: %v topics: %v)", query.FromBlock, query.ToBlock, query.Address, query.Topics)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *filterTestSuite) fullRangeQueryAndCheck(t *utesting.T, query *filterQuery) {
|
||||
frQuery := &filterQuery{ // create full range query
|
||||
FromBlock: 0,
|
||||
ToBlock: int64(rpc.LatestBlockNumber),
|
||||
Address: query.Address,
|
||||
Topics: query.Topics,
|
||||
}
|
||||
frQuery.run(s.cfg.client)
|
||||
if frQuery.Err != nil {
|
||||
t.Errorf("Full range filter query failed (addresses: %v topics: %v error: %v)", frQuery.Address, frQuery.Topics, frQuery.Err)
|
||||
return
|
||||
}
|
||||
// filter out results outside the original query range
|
||||
j := 0
|
||||
for _, log := range frQuery.results {
|
||||
if int64(log.BlockNumber) >= query.FromBlock && int64(log.BlockNumber) <= query.ToBlock {
|
||||
frQuery.results[j] = log
|
||||
j++
|
||||
}
|
||||
}
|
||||
frQuery.results = frQuery.results[:j]
|
||||
if *query.ResultHash != frQuery.calculateHash() {
|
||||
t.Fatalf("Full range filter query result mismatch (fromBlock: %d toBlock: %d addresses: %v topics: %v)", query.FromBlock, query.ToBlock, query.Address, query.Topics)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *filterTestSuite) loadQueries() error {
|
||||
file, err := s.cfg.fsys.Open(s.cfg.filterQueryFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't open filterQueryFile: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var queries [][]*filterQuery
|
||||
if err := json.NewDecoder(file).Decode(&queries); err != nil {
|
||||
return fmt.Errorf("invalid JSON in %s: %v", s.cfg.filterQueryFile, err)
|
||||
}
|
||||
var count int
|
||||
for _, bucket := range queries {
|
||||
count += len(bucket)
|
||||
}
|
||||
if count == 0 {
|
||||
return fmt.Errorf("filterQueryFile %s is empty", s.cfg.filterQueryFile)
|
||||
}
|
||||
s.queries = queries
|
||||
return nil
|
||||
}
|
||||
|
||||
// filterQuery is a single query for testing.
|
||||
type filterQuery struct {
|
||||
FromBlock int64 `json:"fromBlock"`
|
||||
ToBlock int64 `json:"toBlock"`
|
||||
Address []common.Address `json:"address"`
|
||||
Topics [][]common.Hash `json:"topics"`
|
||||
ResultHash *common.Hash `json:"resultHash,omitempty"`
|
||||
results []types.Log
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (fq *filterQuery) isWildcard() bool {
|
||||
if len(fq.Address) != 0 {
|
||||
return false
|
||||
}
|
||||
for _, topics := range fq.Topics {
|
||||
if len(topics) != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (fq *filterQuery) calculateHash() common.Hash {
|
||||
enc, err := rlp.EncodeToBytes(&fq.results)
|
||||
if err != nil {
|
||||
exit(fmt.Errorf("Error encoding logs: %v", err))
|
||||
}
|
||||
return crypto.Keccak256Hash(enc)
|
||||
}
|
||||
|
||||
func (fq *filterQuery) run(client *client) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
||||
defer cancel()
|
||||
logs, err := client.Eth.FilterLogs(ctx, ethereum.FilterQuery{
|
||||
FromBlock: big.NewInt(fq.FromBlock),
|
||||
ToBlock: big.NewInt(fq.ToBlock),
|
||||
Addresses: fq.Address,
|
||||
Topics: fq.Topics,
|
||||
})
|
||||
if err != nil {
|
||||
fq.Err = err
|
||||
fmt.Printf("Filter query failed: fromBlock: %d toBlock: %d addresses: %v topics: %v error: %v\n",
|
||||
fq.FromBlock, fq.ToBlock, fq.Address, fq.Topics, err)
|
||||
return
|
||||
}
|
||||
fq.results = logs
|
||||
}
|
||||
382
cmd/workload/filtertestgen.go
Normal file
382
cmd/workload/filtertestgen.go
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
// Copyright 2025 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/internal/flags"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
filterGenerateCommand = &cli.Command{
|
||||
Name: "filtergen",
|
||||
Usage: "Generates query set for log filter workload test",
|
||||
ArgsUsage: "<RPC endpoint URL>",
|
||||
Action: filterGenCmd,
|
||||
Flags: []cli.Flag{
|
||||
filterQueryFileFlag,
|
||||
filterErrorFileFlag,
|
||||
},
|
||||
}
|
||||
filterQueryFileFlag = &cli.StringFlag{
|
||||
Name: "queries",
|
||||
Usage: "JSON file containing filter test queries",
|
||||
Value: "filter_queries.json",
|
||||
Category: flags.TestingCategory,
|
||||
}
|
||||
filterErrorFileFlag = &cli.StringFlag{
|
||||
Name: "errors",
|
||||
Usage: "JSON file containing failed filter queries",
|
||||
Value: "filter_errors.json",
|
||||
Category: flags.TestingCategory,
|
||||
}
|
||||
)
|
||||
|
||||
// filterGenCmd is the main function of the filter tests generator.
|
||||
func filterGenCmd(ctx *cli.Context) error {
|
||||
f := newFilterTestGen(ctx)
|
||||
lastWrite := time.Now()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
||||
f.updateFinalizedBlock()
|
||||
query := f.newQuery()
|
||||
query.run(f.client)
|
||||
if query.Err != nil {
|
||||
f.errors = append(f.errors, query)
|
||||
continue
|
||||
}
|
||||
if len(query.results) > 0 && len(query.results) <= maxFilterResultSize {
|
||||
for {
|
||||
extQuery := f.extendRange(query)
|
||||
if extQuery == nil {
|
||||
break
|
||||
}
|
||||
extQuery.run(f.client)
|
||||
if extQuery.Err == nil && len(extQuery.results) < len(query.results) {
|
||||
extQuery.Err = fmt.Errorf("invalid result length; old range %d %d; old length %d; new range %d %d; new length %d; address %v; Topics %v",
|
||||
query.FromBlock, query.ToBlock, len(query.results),
|
||||
extQuery.FromBlock, extQuery.ToBlock, len(extQuery.results),
|
||||
extQuery.Address, extQuery.Topics,
|
||||
)
|
||||
}
|
||||
if extQuery.Err != nil {
|
||||
f.errors = append(f.errors, extQuery)
|
||||
break
|
||||
}
|
||||
if len(extQuery.results) > maxFilterResultSize {
|
||||
break
|
||||
}
|
||||
query = extQuery
|
||||
}
|
||||
f.storeQuery(query)
|
||||
if time.Since(lastWrite) > time.Second*10 {
|
||||
f.writeQueries()
|
||||
f.writeErrors()
|
||||
lastWrite = time.Now()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// filterTestGen is the filter query test generator.
|
||||
type filterTestGen struct {
|
||||
client *client
|
||||
queryFile string
|
||||
errorFile string
|
||||
|
||||
finalizedBlock int64
|
||||
queries [filterBuckets][]*filterQuery
|
||||
errors []*filterQuery
|
||||
}
|
||||
|
||||
func newFilterTestGen(ctx *cli.Context) *filterTestGen {
|
||||
return &filterTestGen{
|
||||
client: makeClient(ctx),
|
||||
queryFile: ctx.String(filterQueryFileFlag.Name),
|
||||
errorFile: ctx.String(filterErrorFileFlag.Name),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *filterTestGen) updateFinalizedBlock() {
|
||||
s.finalizedBlock = mustGetFinalizedBlock(s.client)
|
||||
}
|
||||
|
||||
const (
|
||||
// Parameter of the random filter query generator.
|
||||
maxFilterRange = 10000000
|
||||
maxFilterResultSize = 300
|
||||
filterBuckets = 10
|
||||
maxFilterBucketSize = 100
|
||||
filterSeedChance = 10
|
||||
filterMergeChance = 45
|
||||
)
|
||||
|
||||
// storeQuery adds a filter query to the output file.
|
||||
func (s *filterTestGen) storeQuery(query *filterQuery) {
|
||||
query.ResultHash = new(common.Hash)
|
||||
*query.ResultHash = query.calculateHash()
|
||||
logRatio := math.Log(float64(len(query.results))*float64(s.finalizedBlock)/float64(query.ToBlock+1-query.FromBlock)) / math.Log(float64(s.finalizedBlock)*maxFilterResultSize)
|
||||
bucket := int(math.Floor(logRatio * filterBuckets))
|
||||
if bucket >= filterBuckets {
|
||||
bucket = filterBuckets - 1
|
||||
}
|
||||
if len(s.queries[bucket]) < maxFilterBucketSize {
|
||||
s.queries[bucket] = append(s.queries[bucket], query)
|
||||
} else {
|
||||
s.queries[bucket][rand.Intn(len(s.queries[bucket]))] = query
|
||||
}
|
||||
fmt.Print("Generated queries per bucket:")
|
||||
for _, list := range s.queries {
|
||||
fmt.Print(" ", len(list))
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func (s *filterTestGen) extendRange(q *filterQuery) *filterQuery {
|
||||
rangeLen := q.ToBlock + 1 - q.FromBlock
|
||||
extLen := rand.Int63n(rangeLen) + 1
|
||||
if rangeLen+extLen > s.finalizedBlock {
|
||||
return nil
|
||||
}
|
||||
extBefore := min(rand.Int63n(extLen+1), q.FromBlock)
|
||||
extAfter := extLen - extBefore
|
||||
if q.ToBlock+extAfter > s.finalizedBlock {
|
||||
d := q.ToBlock + extAfter - s.finalizedBlock
|
||||
extAfter -= d
|
||||
if extBefore+d <= q.FromBlock {
|
||||
extBefore += d
|
||||
} else {
|
||||
extBefore = q.FromBlock
|
||||
}
|
||||
}
|
||||
return &filterQuery{
|
||||
FromBlock: q.FromBlock - extBefore,
|
||||
ToBlock: q.ToBlock + extAfter,
|
||||
Address: q.Address,
|
||||
Topics: q.Topics,
|
||||
}
|
||||
}
|
||||
|
||||
// newQuery generates a new filter query.
|
||||
func (s *filterTestGen) newQuery() *filterQuery {
|
||||
for {
|
||||
t := rand.Intn(100)
|
||||
if t < filterSeedChance {
|
||||
return s.newSeedQuery()
|
||||
}
|
||||
if t < filterSeedChance+filterMergeChance {
|
||||
if query := s.newMergedQuery(); query != nil {
|
||||
return query
|
||||
}
|
||||
continue
|
||||
}
|
||||
if query := s.newNarrowedQuery(); query != nil {
|
||||
return query
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// newSeedQuery creates a query that gets all logs in a random non-finalized block.
|
||||
func (s *filterTestGen) newSeedQuery() *filterQuery {
|
||||
block := rand.Int63n(s.finalizedBlock + 1)
|
||||
return &filterQuery{
|
||||
FromBlock: block,
|
||||
ToBlock: block,
|
||||
}
|
||||
}
|
||||
|
||||
// newMergedQuery creates a new query by combining (with OR) the filter criteria
|
||||
// of two existing queries (chosen at random).
|
||||
func (s *filterTestGen) newMergedQuery() *filterQuery {
|
||||
q1 := s.randomQuery()
|
||||
q2 := s.randomQuery()
|
||||
if q1 == nil || q2 == nil || q1 == q2 {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
block int64
|
||||
topicCount int
|
||||
)
|
||||
if rand.Intn(2) == 0 {
|
||||
block = q1.FromBlock + rand.Int63n(q1.ToBlock+1-q1.FromBlock)
|
||||
topicCount = len(q1.Topics)
|
||||
} else {
|
||||
block = q2.FromBlock + rand.Int63n(q2.ToBlock+1-q2.FromBlock)
|
||||
topicCount = len(q2.Topics)
|
||||
}
|
||||
m := &filterQuery{
|
||||
FromBlock: block,
|
||||
ToBlock: block,
|
||||
Topics: make([][]common.Hash, topicCount),
|
||||
}
|
||||
for _, addr := range q1.Address {
|
||||
if rand.Intn(2) == 0 {
|
||||
m.Address = append(m.Address, addr)
|
||||
}
|
||||
}
|
||||
for _, addr := range q2.Address {
|
||||
if rand.Intn(2) == 0 {
|
||||
m.Address = append(m.Address, addr)
|
||||
}
|
||||
}
|
||||
for i := range m.Topics {
|
||||
if len(q1.Topics) > i {
|
||||
for _, topic := range q1.Topics[i] {
|
||||
if rand.Intn(2) == 0 {
|
||||
m.Topics[i] = append(m.Topics[i], topic)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(q2.Topics) > i {
|
||||
for _, topic := range q2.Topics[i] {
|
||||
if rand.Intn(2) == 0 {
|
||||
m.Topics[i] = append(m.Topics[i], topic)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// newNarrowedQuery creates a new query by 'narrowing' an existing (randomly chosen)
|
||||
// query. The new query is made more specific by analyzing the filter criteria and adding
|
||||
// topics/addresses from the known result set.
|
||||
func (s *filterTestGen) newNarrowedQuery() *filterQuery {
|
||||
q := s.randomQuery()
|
||||
if q == nil {
|
||||
return nil
|
||||
}
|
||||
log := q.results[rand.Intn(len(q.results))]
|
||||
var emptyCount int
|
||||
if len(q.Address) == 0 {
|
||||
emptyCount++
|
||||
}
|
||||
for i := range log.Topics {
|
||||
if len(q.Topics) <= i || len(q.Topics[i]) == 0 {
|
||||
emptyCount++
|
||||
}
|
||||
}
|
||||
if emptyCount == 0 {
|
||||
return nil
|
||||
}
|
||||
query := &filterQuery{
|
||||
FromBlock: q.FromBlock,
|
||||
ToBlock: q.ToBlock,
|
||||
Address: make([]common.Address, len(q.Address)),
|
||||
Topics: make([][]common.Hash, len(q.Topics)),
|
||||
}
|
||||
copy(query.Address, q.Address)
|
||||
for i, topics := range q.Topics {
|
||||
if len(topics) > 0 {
|
||||
query.Topics[i] = make([]common.Hash, len(topics))
|
||||
copy(query.Topics[i], topics)
|
||||
}
|
||||
}
|
||||
pick := rand.Intn(emptyCount)
|
||||
if len(query.Address) == 0 {
|
||||
if pick == 0 {
|
||||
query.Address = []common.Address{log.Address}
|
||||
return query
|
||||
}
|
||||
pick--
|
||||
}
|
||||
for i := range log.Topics {
|
||||
if len(query.Topics) <= i || len(query.Topics[i]) == 0 {
|
||||
if pick == 0 {
|
||||
if len(query.Topics) <= i {
|
||||
query.Topics = append(query.Topics, make([][]common.Hash, i+1-len(query.Topics))...)
|
||||
}
|
||||
query.Topics[i] = []common.Hash{log.Topics[i]}
|
||||
return query
|
||||
}
|
||||
pick--
|
||||
}
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// randomQuery returns a random query from the ones that were already generated.
|
||||
func (s *filterTestGen) randomQuery() *filterQuery {
|
||||
var bucket, bucketCount int
|
||||
for _, list := range s.queries {
|
||||
if len(list) > 0 {
|
||||
bucketCount++
|
||||
}
|
||||
}
|
||||
if bucketCount == 0 {
|
||||
return nil
|
||||
}
|
||||
pick := rand.Intn(bucketCount)
|
||||
for i, list := range s.queries {
|
||||
if len(list) > 0 {
|
||||
if pick == 0 {
|
||||
bucket = i
|
||||
break
|
||||
}
|
||||
pick--
|
||||
}
|
||||
}
|
||||
return s.queries[bucket][rand.Intn(len(s.queries[bucket]))]
|
||||
}
|
||||
|
||||
// writeQueries serializes the generated queries to the output file.
|
||||
func (s *filterTestGen) writeQueries() {
|
||||
file, err := os.Create(s.queryFile)
|
||||
if err != nil {
|
||||
exit(fmt.Errorf("Error creating filter test query file %s: %v", s.queryFile, err))
|
||||
return
|
||||
}
|
||||
json.NewEncoder(file).Encode(&s.queries)
|
||||
file.Close()
|
||||
}
|
||||
|
||||
// writeQueries serializes the generated errors to the error file.
|
||||
func (s *filterTestGen) writeErrors() {
|
||||
file, err := os.Create(s.errorFile)
|
||||
if err != nil {
|
||||
exit(fmt.Errorf("Error creating filter error file %s: %v", s.errorFile, err))
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
json.NewEncoder(file).Encode(s.errors)
|
||||
}
|
||||
|
||||
func mustGetFinalizedBlock(client *client) int64 {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
defer cancel()
|
||||
header, err := client.Eth.HeaderByNumber(ctx, big.NewInt(int64(rpc.FinalizedBlockNumber)))
|
||||
if err != nil {
|
||||
exit(fmt.Errorf("could not fetch finalized header (error: %v)", err))
|
||||
}
|
||||
return header.Number.Int64()
|
||||
}
|
||||
137
cmd/workload/filtertestperf.go
Normal file
137
cmd/workload/filtertestperf.go
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
// Copyright 2025 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"slices"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
filterPerfCommand = &cli.Command{
|
||||
Name: "filterperf",
|
||||
Usage: "Runs log filter performance test against an RPC endpoint",
|
||||
ArgsUsage: "<RPC endpoint URL>",
|
||||
Action: filterPerfCmd,
|
||||
Flags: []cli.Flag{
|
||||
testSepoliaFlag,
|
||||
testMainnetFlag,
|
||||
filterQueryFileFlag,
|
||||
filterErrorFileFlag,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const passCount = 1
|
||||
|
||||
func filterPerfCmd(ctx *cli.Context) error {
|
||||
cfg := testConfigFromCLI(ctx)
|
||||
f := newFilterTestSuite(cfg)
|
||||
|
||||
type queryTest struct {
|
||||
query *filterQuery
|
||||
bucket, index int
|
||||
runtime []time.Duration
|
||||
medianTime time.Duration
|
||||
}
|
||||
var queries, processed []queryTest
|
||||
for i, bucket := range f.queries[:] {
|
||||
for j, query := range bucket {
|
||||
queries = append(queries, queryTest{query: query, bucket: i, index: j})
|
||||
}
|
||||
}
|
||||
|
||||
// Run test queries.
|
||||
var failed, mismatch int
|
||||
for i := 1; i <= passCount; i++ {
|
||||
fmt.Println("Performance test pass", i, "/", passCount)
|
||||
for len(queries) > 0 {
|
||||
pick := rand.Intn(len(queries))
|
||||
qt := queries[pick]
|
||||
queries[pick] = queries[len(queries)-1]
|
||||
queries = queries[:len(queries)-1]
|
||||
start := time.Now()
|
||||
qt.query.run(cfg.client)
|
||||
qt.runtime = append(qt.runtime, time.Since(start))
|
||||
slices.Sort(qt.runtime)
|
||||
qt.medianTime = qt.runtime[len(qt.runtime)/2]
|
||||
if qt.query.Err != nil {
|
||||
failed++
|
||||
continue
|
||||
}
|
||||
if rhash := qt.query.calculateHash(); *qt.query.ResultHash != rhash {
|
||||
fmt.Printf("Filter query result mismatch: fromBlock: %d toBlock: %d addresses: %v topics: %v expected hash: %064x calculated hash: %064x\n", qt.query.FromBlock, qt.query.ToBlock, qt.query.Address, qt.query.Topics, *qt.query.ResultHash, rhash)
|
||||
continue
|
||||
}
|
||||
processed = append(processed, qt)
|
||||
if len(processed)%50 == 0 {
|
||||
fmt.Println(" processed:", len(processed), "remaining", len(queries), "failed:", failed, "result mismatch:", mismatch)
|
||||
}
|
||||
}
|
||||
queries, processed = processed, nil
|
||||
}
|
||||
|
||||
// Show results and stats.
|
||||
fmt.Println("Performance test finished; processed:", len(queries), "failed:", failed, "result mismatch:", mismatch)
|
||||
stats := make([]bucketStats, len(f.queries))
|
||||
var wildcardStats bucketStats
|
||||
for _, qt := range queries {
|
||||
bs := &stats[qt.bucket]
|
||||
if qt.query.isWildcard() {
|
||||
bs = &wildcardStats
|
||||
}
|
||||
bs.blocks += qt.query.ToBlock + 1 - qt.query.FromBlock
|
||||
bs.count++
|
||||
bs.logs += len(qt.query.results)
|
||||
bs.runtime += qt.medianTime
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
for i := range stats {
|
||||
stats[i].print(fmt.Sprintf("bucket #%d", i+1))
|
||||
}
|
||||
wildcardStats.print("wild card queries")
|
||||
fmt.Println()
|
||||
sort.Slice(queries, func(i, j int) bool {
|
||||
return queries[i].medianTime > queries[j].medianTime
|
||||
})
|
||||
for i := 0; i < 10; i++ {
|
||||
q := queries[i]
|
||||
fmt.Printf("Most expensive query #%-2d median runtime: %13v max runtime: %13v result count: %4d fromBlock: %9d toBlock: %9d addresses: %v topics: %v\n",
|
||||
i+1, q.medianTime, q.runtime[len(q.runtime)-1], len(q.query.results), q.query.FromBlock, q.query.ToBlock, q.query.Address, q.query.Topics)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type bucketStats struct {
|
||||
blocks int64
|
||||
count, logs int
|
||||
runtime time.Duration
|
||||
}
|
||||
|
||||
func (st *bucketStats) print(name string) {
|
||||
if st.count == 0 {
|
||||
return
|
||||
}
|
||||
fmt.Printf("%-20s queries: %4d average block length: %12.2f average log count: %7.2f average runtime: %13v\n",
|
||||
name, st.count, float64(st.blocks)/float64(st.count), float64(st.logs)/float64(st.count), st.runtime/time.Duration(st.count))
|
||||
}
|
||||
309
cmd/workload/historytest.go
Normal file
309
cmd/workload/historytest.go
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
// Copyright 2025 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/internal/utesting"
|
||||
)
|
||||
|
||||
// historyTest is the content of a history test.
|
||||
type historyTest struct {
|
||||
BlockNumbers []uint64 `json:"blockNumbers"`
|
||||
BlockHashes []common.Hash `json:"blockHashes"`
|
||||
TxCounts []int `json:"txCounts"`
|
||||
TxHashIndex []int `json:"txHashIndex"`
|
||||
TxHashes []*common.Hash `json:"txHashes"`
|
||||
ReceiptsHashes []common.Hash `json:"blockReceiptsHashes"`
|
||||
}
|
||||
|
||||
type historyTestSuite struct {
|
||||
cfg testConfig
|
||||
tests historyTest
|
||||
}
|
||||
|
||||
func newHistoryTestSuite(cfg testConfig) *historyTestSuite {
|
||||
s := &historyTestSuite{cfg: cfg}
|
||||
if err := s.loadTests(); err != nil {
|
||||
exit(err)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *historyTestSuite) loadTests() error {
|
||||
file, err := s.cfg.fsys.Open(s.cfg.historyTestFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't open historyTestFile: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
if err := json.NewDecoder(file).Decode(&s.tests); err != nil {
|
||||
return fmt.Errorf("invalid JSON in %s: %v", s.cfg.historyTestFile, err)
|
||||
}
|
||||
if len(s.tests.BlockNumbers) == 0 {
|
||||
return fmt.Errorf("historyTestFile %s has no test data", s.cfg.historyTestFile)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *historyTestSuite) allTests() []utesting.Test {
|
||||
return []utesting.Test{
|
||||
{
|
||||
Name: "History/getBlockByHash",
|
||||
Fn: s.testGetBlockByHash,
|
||||
},
|
||||
{
|
||||
Name: "History/getBlockByNumber",
|
||||
Fn: s.testGetBlockByNumber,
|
||||
},
|
||||
{
|
||||
Name: "History/getBlockReceiptsByHash",
|
||||
Fn: s.testGetBlockReceiptsByHash,
|
||||
},
|
||||
{
|
||||
Name: "History/getBlockReceiptsByNumber",
|
||||
Fn: s.testGetBlockReceiptsByNumber,
|
||||
},
|
||||
{
|
||||
Name: "History/getBlockTransactionCountByHash",
|
||||
Fn: s.testGetBlockTransactionCountByHash,
|
||||
},
|
||||
{
|
||||
Name: "History/getBlockTransactionCountByNumber",
|
||||
Fn: s.testGetBlockTransactionCountByNumber,
|
||||
},
|
||||
{
|
||||
Name: "History/getTransactionByBlockHashAndIndex",
|
||||
Fn: s.testGetTransactionByBlockHashAndIndex,
|
||||
},
|
||||
{
|
||||
Name: "History/getTransactionByBlockNumberAndIndex",
|
||||
Fn: s.testGetTransactionByBlockNumberAndIndex,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *historyTestSuite) testGetBlockByHash(t *utesting.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for i, num := range s.tests.BlockNumbers {
|
||||
bhash := s.tests.BlockHashes[i]
|
||||
b, err := s.cfg.client.getBlockByHash(ctx, bhash, false)
|
||||
if err != nil {
|
||||
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
|
||||
}
|
||||
if b == nil {
|
||||
t.Errorf("block %d (hash %v): not found", num, bhash)
|
||||
continue
|
||||
}
|
||||
if b.Hash != bhash || uint64(b.Number) != num {
|
||||
t.Errorf("block %d (hash %v): invalid number/hash", num, bhash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *historyTestSuite) testGetBlockByNumber(t *utesting.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for i, num := range s.tests.BlockNumbers {
|
||||
bhash := s.tests.BlockHashes[i]
|
||||
b, err := s.cfg.client.getBlockByNumber(ctx, num, false)
|
||||
if err != nil {
|
||||
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
|
||||
}
|
||||
if b == nil {
|
||||
t.Errorf("block %d (hash %v): not found", num, bhash)
|
||||
continue
|
||||
}
|
||||
if b.Hash != bhash || uint64(b.Number) != num {
|
||||
t.Errorf("block %d (hash %v): invalid number/hash", num, bhash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *historyTestSuite) testGetBlockTransactionCountByHash(t *utesting.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for i, num := range s.tests.BlockNumbers {
|
||||
bhash := s.tests.BlockHashes[i]
|
||||
count, err := s.cfg.client.getBlockTransactionCountByHash(ctx, bhash)
|
||||
if err != nil {
|
||||
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
|
||||
}
|
||||
expectedCount := uint64(s.tests.TxCounts[i])
|
||||
if count != expectedCount {
|
||||
t.Errorf("block %d (hash %v): wrong txcount %d, want %d", count, expectedCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *historyTestSuite) testGetBlockTransactionCountByNumber(t *utesting.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for i, num := range s.tests.BlockNumbers {
|
||||
bhash := s.tests.BlockHashes[i]
|
||||
count, err := s.cfg.client.getBlockTransactionCountByNumber(ctx, num)
|
||||
if err != nil {
|
||||
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
|
||||
}
|
||||
expectedCount := uint64(s.tests.TxCounts[i])
|
||||
if count != expectedCount {
|
||||
t.Errorf("block %d (hash %v): wrong txcount %d, want %d", count, expectedCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *historyTestSuite) testGetBlockReceiptsByHash(t *utesting.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for i, num := range s.tests.BlockNumbers {
|
||||
bhash := s.tests.BlockHashes[i]
|
||||
receipts, err := s.cfg.client.getBlockReceipts(ctx, bhash)
|
||||
if err != nil {
|
||||
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
|
||||
}
|
||||
hash := calcReceiptsHash(receipts)
|
||||
expectedHash := s.tests.ReceiptsHashes[i]
|
||||
if hash != expectedHash {
|
||||
t.Errorf("block %d (hash %v): wrong receipts hash %v, want %v", num, bhash, hash, expectedHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *historyTestSuite) testGetBlockReceiptsByNumber(t *utesting.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for i, num := range s.tests.BlockNumbers {
|
||||
bhash := s.tests.BlockHashes[i]
|
||||
receipts, err := s.cfg.client.getBlockReceipts(ctx, hexutil.Uint64(num))
|
||||
if err != nil {
|
||||
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
|
||||
}
|
||||
hash := calcReceiptsHash(receipts)
|
||||
expectedHash := s.tests.ReceiptsHashes[i]
|
||||
if hash != expectedHash {
|
||||
t.Errorf("block %d (hash %v): wrong receipts hash %v, want %v", num, bhash, hash, expectedHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *historyTestSuite) testGetTransactionByBlockHashAndIndex(t *utesting.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for i, num := range s.tests.BlockNumbers {
|
||||
bhash := s.tests.BlockHashes[i]
|
||||
txIndex := s.tests.TxHashIndex[i]
|
||||
expectedHash := s.tests.TxHashes[i]
|
||||
if expectedHash == nil {
|
||||
continue // no txs in block
|
||||
}
|
||||
|
||||
tx, err := s.cfg.client.getTransactionByBlockHashAndIndex(ctx, bhash, uint64(txIndex))
|
||||
if err != nil {
|
||||
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
|
||||
}
|
||||
if tx == nil {
|
||||
t.Errorf("block %d (hash %v): txIndex %d not found", num, bhash, txIndex)
|
||||
continue
|
||||
}
|
||||
if tx.Hash != *expectedHash || uint64(tx.TransactionIndex) != uint64(txIndex) {
|
||||
t.Errorf("block %d (hash %v): txIndex %d has wrong txHash/Index", num, bhash, txIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *historyTestSuite) testGetTransactionByBlockNumberAndIndex(t *utesting.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for i, num := range s.tests.BlockNumbers {
|
||||
bhash := s.tests.BlockHashes[i]
|
||||
txIndex := s.tests.TxHashIndex[i]
|
||||
expectedHash := s.tests.TxHashes[i]
|
||||
if expectedHash == nil {
|
||||
continue // no txs in block
|
||||
}
|
||||
|
||||
tx, err := s.cfg.client.getTransactionByBlockNumberAndIndex(ctx, num, uint64(txIndex))
|
||||
if err != nil {
|
||||
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
|
||||
}
|
||||
if tx == nil {
|
||||
t.Errorf("block %d (hash %v): txIndex %d not found", num, bhash, txIndex)
|
||||
continue
|
||||
}
|
||||
if tx.Hash != *expectedHash || uint64(tx.TransactionIndex) != uint64(txIndex) {
|
||||
t.Errorf("block %d (hash %v): txIndex %d has wrong txHash/Index", num, bhash, txIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type simpleBlock struct {
|
||||
Number hexutil.Uint64 `json:"number"`
|
||||
Hash common.Hash `json:"hash"`
|
||||
}
|
||||
|
||||
type simpleTransaction struct {
|
||||
Hash common.Hash `json:"hash"`
|
||||
TransactionIndex hexutil.Uint64 `json:"transactionIndex"`
|
||||
}
|
||||
|
||||
func (c *client) getBlockByHash(ctx context.Context, arg common.Hash, fullTx bool) (*simpleBlock, error) {
|
||||
var r *simpleBlock
|
||||
err := c.RPC.CallContext(ctx, &r, "eth_getBlockByHash", arg, fullTx)
|
||||
return r, err
|
||||
}
|
||||
|
||||
func (c *client) getBlockByNumber(ctx context.Context, arg uint64, fullTx bool) (*simpleBlock, error) {
|
||||
var r *simpleBlock
|
||||
err := c.RPC.CallContext(ctx, &r, "eth_getBlockByNumber", hexutil.Uint64(arg), fullTx)
|
||||
return r, err
|
||||
}
|
||||
|
||||
func (c *client) getTransactionByBlockHashAndIndex(ctx context.Context, block common.Hash, index uint64) (*simpleTransaction, error) {
|
||||
var r *simpleTransaction
|
||||
err := c.RPC.CallContext(ctx, &r, "eth_getTransactionByBlockHashAndIndex", block, hexutil.Uint64(index))
|
||||
return r, err
|
||||
}
|
||||
|
||||
func (c *client) getTransactionByBlockNumberAndIndex(ctx context.Context, block uint64, index uint64) (*simpleTransaction, error) {
|
||||
var r *simpleTransaction
|
||||
err := c.RPC.CallContext(ctx, &r, "eth_getTransactionByBlockNumberAndIndex", hexutil.Uint64(block), hexutil.Uint64(index))
|
||||
return r, err
|
||||
}
|
||||
|
||||
func (c *client) getBlockTransactionCountByHash(ctx context.Context, block common.Hash) (uint64, error) {
|
||||
var r hexutil.Uint64
|
||||
err := c.RPC.CallContext(ctx, &r, "eth_getBlockTransactionCountByHash", block)
|
||||
return uint64(r), err
|
||||
}
|
||||
|
||||
func (c *client) getBlockTransactionCountByNumber(ctx context.Context, block uint64) (uint64, error) {
|
||||
var r hexutil.Uint64
|
||||
err := c.RPC.CallContext(ctx, &r, "eth_getBlockTransactionCountByNumber", hexutil.Uint64(block))
|
||||
return uint64(r), err
|
||||
}
|
||||
|
||||
func (c *client) getBlockReceipts(ctx context.Context, arg any) ([]*types.Receipt, error) {
|
||||
var result []*types.Receipt
|
||||
err := c.RPC.CallContext(ctx, &result, "eth_getBlockReceipts", arg)
|
||||
return result, err
|
||||
}
|
||||
147
cmd/workload/historytestgen.go
Normal file
147
cmd/workload/historytestgen.go
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
// Copyright 2025 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/internal/flags"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
historyGenerateCommand = &cli.Command{
|
||||
Name: "historygen",
|
||||
Usage: "Generates history retrieval tests",
|
||||
ArgsUsage: "<RPC endpoint URL>",
|
||||
Action: generateHistoryTests,
|
||||
Flags: []cli.Flag{
|
||||
historyTestFileFlag,
|
||||
historyTestEarliestFlag,
|
||||
},
|
||||
}
|
||||
|
||||
historyTestFileFlag = &cli.StringFlag{
|
||||
Name: "history-tests",
|
||||
Usage: "JSON file containing filter test queries",
|
||||
Value: "history_tests.json",
|
||||
Category: flags.TestingCategory,
|
||||
}
|
||||
historyTestEarliestFlag = &cli.IntFlag{
|
||||
Name: "earliest",
|
||||
Usage: "JSON file containing filter test queries",
|
||||
Value: 0,
|
||||
Category: flags.TestingCategory,
|
||||
}
|
||||
)
|
||||
|
||||
const historyTestBlockCount = 2000
|
||||
|
||||
func generateHistoryTests(clictx *cli.Context) error {
|
||||
var (
|
||||
client = makeClient(clictx)
|
||||
earliest = uint64(clictx.Int(historyTestEarliestFlag.Name))
|
||||
outputFile = clictx.String(historyTestFileFlag.Name)
|
||||
ctx = context.Background()
|
||||
)
|
||||
|
||||
test := new(historyTest)
|
||||
|
||||
// Create the block numbers. Here we choose 1k blocks between earliest and head.
|
||||
latest, err := client.Eth.BlockNumber(ctx)
|
||||
if err != nil {
|
||||
exit(err)
|
||||
}
|
||||
if latest < historyTestBlockCount {
|
||||
exit(fmt.Errorf("node seems not synced, latest block is %d", latest))
|
||||
}
|
||||
test.BlockNumbers = make([]uint64, 0, historyTestBlockCount)
|
||||
stride := (latest - earliest) / historyTestBlockCount
|
||||
for b := earliest; b < latest; b += stride {
|
||||
test.BlockNumbers = append(test.BlockNumbers, b)
|
||||
}
|
||||
|
||||
// Get blocks and assign block info into the test
|
||||
fmt.Println("Fetching blocks")
|
||||
blocks := make([]*types.Block, len(test.BlockNumbers))
|
||||
for i, blocknum := range test.BlockNumbers {
|
||||
b, err := client.Eth.BlockByNumber(ctx, new(big.Int).SetUint64(blocknum))
|
||||
if err != nil {
|
||||
exit(fmt.Errorf("error fetching block %d: %v", blocknum, err))
|
||||
}
|
||||
blocks[i] = b
|
||||
}
|
||||
test.BlockHashes = make([]common.Hash, len(blocks))
|
||||
test.TxCounts = make([]int, len(blocks))
|
||||
for i, block := range blocks {
|
||||
test.BlockHashes[i] = block.Hash()
|
||||
test.TxCounts[i] = len(block.Transactions())
|
||||
}
|
||||
|
||||
// Fill tx index.
|
||||
test.TxHashIndex = make([]int, len(blocks))
|
||||
test.TxHashes = make([]*common.Hash, len(blocks))
|
||||
for i, block := range blocks {
|
||||
txs := block.Transactions()
|
||||
if len(txs) == 0 {
|
||||
continue
|
||||
}
|
||||
index := len(txs) / 2
|
||||
txhash := txs[index].Hash()
|
||||
test.TxHashIndex[i] = index
|
||||
test.TxHashes[i] = &txhash
|
||||
}
|
||||
|
||||
// Get receipts.
|
||||
fmt.Println("Fetching receipts")
|
||||
test.ReceiptsHashes = make([]common.Hash, len(blocks))
|
||||
for i, blockHash := range test.BlockHashes {
|
||||
receipts, err := client.getBlockReceipts(ctx, blockHash)
|
||||
if err != nil {
|
||||
exit(fmt.Errorf("error fetching block %v receipts: %v", blockHash, err))
|
||||
}
|
||||
test.ReceiptsHashes[i] = calcReceiptsHash(receipts)
|
||||
}
|
||||
|
||||
// Write output file.
|
||||
writeJSON(outputFile, test)
|
||||
return nil
|
||||
}
|
||||
|
||||
func calcReceiptsHash(rcpt []*types.Receipt) common.Hash {
|
||||
h := crypto.NewKeccakState()
|
||||
rlp.Encode(h, rcpt)
|
||||
return common.Hash(h.Sum(nil))
|
||||
}
|
||||
|
||||
func writeJSON(fileName string, value any) {
|
||||
file, err := os.Create(fileName)
|
||||
if err != nil {
|
||||
exit(fmt.Errorf("Error creating %s: %v", fileName, err))
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
json.NewEncoder(file).Encode(value)
|
||||
}
|
||||
86
cmd/workload/main.go
Normal file
86
cmd/workload/main.go
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
// Copyright 2025 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/internal/debug"
|
||||
"github.com/ethereum/go-ethereum/internal/flags"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var app = flags.NewApp("go-ethereum workload test tool")
|
||||
|
||||
func init() {
|
||||
app.Flags = append(app.Flags, debug.Flags...)
|
||||
app.Before = func(ctx *cli.Context) error {
|
||||
flags.MigrateGlobalFlags(ctx)
|
||||
return debug.Setup(ctx)
|
||||
}
|
||||
app.After = func(ctx *cli.Context) error {
|
||||
debug.Exit()
|
||||
return nil
|
||||
}
|
||||
app.CommandNotFound = func(ctx *cli.Context, cmd string) {
|
||||
fmt.Fprintf(os.Stderr, "No such command: %s\n", cmd)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Add subcommands.
|
||||
app.Commands = []*cli.Command{
|
||||
runTestCommand,
|
||||
historyGenerateCommand,
|
||||
filterGenerateCommand,
|
||||
filterPerfCommand,
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
exit(app.Run(os.Args))
|
||||
}
|
||||
|
||||
type client struct {
|
||||
Eth *ethclient.Client
|
||||
RPC *rpc.Client
|
||||
}
|
||||
|
||||
func makeClient(ctx *cli.Context) *client {
|
||||
if ctx.NArg() < 1 {
|
||||
exit("missing RPC endpoint URL as command-line argument")
|
||||
}
|
||||
url := ctx.Args().First()
|
||||
cl, err := rpc.Dial(url)
|
||||
if err != nil {
|
||||
exit(fmt.Errorf("Could not create RPC client at %s: %v", url, err))
|
||||
}
|
||||
return &client{
|
||||
RPC: cl,
|
||||
Eth: ethclient.NewClient(cl),
|
||||
}
|
||||
}
|
||||
|
||||
func exit(err any) {
|
||||
if err == nil {
|
||||
os.Exit(0)
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
1
cmd/workload/queries/filter_queries_mainnet.json
Normal file
1
cmd/workload/queries/filter_queries_mainnet.json
Normal file
File diff suppressed because one or more lines are too long
1
cmd/workload/queries/filter_queries_sepolia.json
Normal file
1
cmd/workload/queries/filter_queries_sepolia.json
Normal file
File diff suppressed because one or more lines are too long
1
cmd/workload/queries/history_mainnet.json
Normal file
1
cmd/workload/queries/history_mainnet.json
Normal file
File diff suppressed because one or more lines are too long
1
cmd/workload/queries/history_sepolia.json
Normal file
1
cmd/workload/queries/history_sepolia.json
Normal file
File diff suppressed because one or more lines are too long
145
cmd/workload/testsuite.go
Normal file
145
cmd/workload/testsuite.go
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
// Copyright 2020 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io/fs"
|
||||
"os"
|
||||
"slices"
|
||||
|
||||
"github.com/ethereum/go-ethereum/internal/flags"
|
||||
"github.com/ethereum/go-ethereum/internal/utesting"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
//go:embed queries
|
||||
var builtinTestFiles embed.FS
|
||||
|
||||
var (
|
||||
runTestCommand = &cli.Command{
|
||||
Name: "test",
|
||||
Usage: "Runs workload tests against an RPC endpoint",
|
||||
ArgsUsage: "<RPC endpoint URL>",
|
||||
Action: runTestCmd,
|
||||
Flags: []cli.Flag{
|
||||
testPatternFlag,
|
||||
testTAPFlag,
|
||||
testSlowFlag,
|
||||
testSepoliaFlag,
|
||||
testMainnetFlag,
|
||||
filterQueryFileFlag,
|
||||
historyTestFileFlag,
|
||||
},
|
||||
}
|
||||
testPatternFlag = &cli.StringFlag{
|
||||
Name: "run",
|
||||
Usage: "Pattern of test suite(s) to run",
|
||||
Category: flags.TestingCategory,
|
||||
}
|
||||
testTAPFlag = &cli.BoolFlag{
|
||||
Name: "tap",
|
||||
Usage: "Output test results in TAP format",
|
||||
Category: flags.TestingCategory,
|
||||
}
|
||||
testSlowFlag = &cli.BoolFlag{
|
||||
Name: "slow",
|
||||
Usage: "Enable slow tests",
|
||||
Value: false,
|
||||
Category: flags.TestingCategory,
|
||||
}
|
||||
testSepoliaFlag = &cli.BoolFlag{
|
||||
Name: "sepolia",
|
||||
Usage: "Use test cases for sepolia network",
|
||||
Category: flags.TestingCategory,
|
||||
}
|
||||
testMainnetFlag = &cli.BoolFlag{
|
||||
Name: "mainnet",
|
||||
Usage: "Use test cases for mainnet network",
|
||||
Category: flags.TestingCategory,
|
||||
}
|
||||
)
|
||||
|
||||
// testConfig holds the parameters for testing.
|
||||
type testConfig struct {
|
||||
client *client
|
||||
fsys fs.FS
|
||||
filterQueryFile string
|
||||
historyTestFile string
|
||||
}
|
||||
|
||||
func testConfigFromCLI(ctx *cli.Context) (cfg testConfig) {
|
||||
flags.CheckExclusive(ctx, testMainnetFlag, testSepoliaFlag)
|
||||
if (ctx.IsSet(testMainnetFlag.Name) || ctx.IsSet(testSepoliaFlag.Name)) && ctx.IsSet(filterQueryFileFlag.Name) {
|
||||
exit(filterQueryFileFlag.Name + " cannot be used with " + testMainnetFlag.Name + " or " + testSepoliaFlag.Name)
|
||||
}
|
||||
|
||||
// configure ethclient
|
||||
cfg.client = makeClient(ctx)
|
||||
|
||||
// configure test files
|
||||
switch {
|
||||
case ctx.Bool(testMainnetFlag.Name):
|
||||
cfg.fsys = builtinTestFiles
|
||||
cfg.filterQueryFile = "queries/filter_queries_mainnet.json"
|
||||
cfg.historyTestFile = "queries/history_mainnet.json"
|
||||
case ctx.Bool(testSepoliaFlag.Name):
|
||||
cfg.fsys = builtinTestFiles
|
||||
cfg.filterQueryFile = "queries/filter_queries_sepolia.json"
|
||||
cfg.historyTestFile = "queries/history_sepolia.json"
|
||||
default:
|
||||
cfg.fsys = os.DirFS(".")
|
||||
cfg.filterQueryFile = ctx.String(filterQueryFileFlag.Name)
|
||||
cfg.historyTestFile = ctx.String(historyTestFileFlag.Name)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
func runTestCmd(ctx *cli.Context) error {
|
||||
cfg := testConfigFromCLI(ctx)
|
||||
filterSuite := newFilterTestSuite(cfg)
|
||||
historySuite := newHistoryTestSuite(cfg)
|
||||
|
||||
// Filter test cases.
|
||||
tests := filterSuite.allTests()
|
||||
tests = append(tests, historySuite.allTests()...)
|
||||
if ctx.IsSet(testPatternFlag.Name) {
|
||||
tests = utesting.MatchTests(tests, ctx.String(testPatternFlag.Name))
|
||||
}
|
||||
if !ctx.Bool(testSlowFlag.Name) {
|
||||
tests = slices.DeleteFunc(tests, func(test utesting.Test) bool {
|
||||
return test.Slow
|
||||
})
|
||||
}
|
||||
|
||||
// Disable logging unless explicitly enabled.
|
||||
if !ctx.IsSet("verbosity") && !ctx.IsSet("vmodule") {
|
||||
log.SetDefault(log.NewLogger(log.DiscardHandler()))
|
||||
}
|
||||
|
||||
// Run the tests.
|
||||
var run = utesting.RunTests
|
||||
if ctx.Bool(testTAPFlag.Name) {
|
||||
run = utesting.RunTAP
|
||||
}
|
||||
results := run(tests, os.Stdout)
|
||||
if utesting.CountFailures(results) > 0 {
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ package snapshot
|
|||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"maps"
|
||||
"math"
|
||||
"math/rand"
|
||||
"slices"
|
||||
|
|
@ -30,7 +31,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
bloomfilter "github.com/holiman/bloomfilter/v2"
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -431,8 +431,7 @@ func (dl *diffLayer) AccountList() []common.Hash {
|
|||
dl.lock.Lock()
|
||||
defer dl.lock.Unlock()
|
||||
|
||||
dl.accountList = maps.Keys(dl.accountData)
|
||||
slices.SortFunc(dl.accountList, common.Hash.Cmp)
|
||||
dl.accountList = slices.SortedFunc(maps.Keys(dl.accountData), common.Hash.Cmp)
|
||||
dl.memory += uint64(len(dl.accountList) * common.HashLength)
|
||||
return dl.accountList
|
||||
}
|
||||
|
|
@ -464,8 +463,7 @@ func (dl *diffLayer) StorageList(accountHash common.Hash) []common.Hash {
|
|||
dl.lock.Lock()
|
||||
defer dl.lock.Unlock()
|
||||
|
||||
storageList := maps.Keys(dl.storageData[accountHash])
|
||||
slices.SortFunc(storageList, common.Hash.Cmp)
|
||||
storageList := slices.SortedFunc(maps.Keys(dl.storageData[accountHash]), common.Hash.Cmp)
|
||||
dl.storageList[accountHash] = storageList
|
||||
dl.memory += uint64(len(dl.storageList)*common.HashLength + common.HashLength)
|
||||
return storageList
|
||||
|
|
|
|||
|
|
@ -83,8 +83,8 @@ func TestHooks(t *testing.T) {
|
|||
inner.SetTxContext(common.Hash{0x11}, 100) // For the log
|
||||
var result []string
|
||||
var wants = []string{
|
||||
"0xaa00000000000000000000000000000000000000.balance: 0->100 (BalanceChangeUnspecified)",
|
||||
"0xaa00000000000000000000000000000000000000.balance: 100->50 (BalanceChangeTransfer)",
|
||||
"0xaa00000000000000000000000000000000000000.balance: 0->100 (Unspecified)",
|
||||
"0xaa00000000000000000000000000000000000000.balance: 100->50 (Transfer)",
|
||||
"0xaa00000000000000000000000000000000000000.nonce: 0->1337",
|
||||
"0xaa00000000000000000000000000000000000000.code: (0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470) ->0x1325 (0xa12ae05590de0c93a00bc7ac773c2fdb621e44f814985e72194f921c0050f728)",
|
||||
"0xaa00000000000000000000000000000000000000.storage slot 0x0000000000000000000000000000000000000000000000000000000000000001: 0x0000000000000000000000000000000000000000000000000000000000000000 ->0x0000000000000000000000000000000000000000000000000000000000000011",
|
||||
|
|
|
|||
|
|
@ -305,12 +305,14 @@ func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte
|
|||
*requests = append(*requests, requestsData)
|
||||
}
|
||||
|
||||
var depositTopic = common.HexToHash("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5")
|
||||
|
||||
// ParseDepositLogs extracts the EIP-6110 deposit values from logs emitted by
|
||||
// BeaconDepositContract.
|
||||
func ParseDepositLogs(requests *[][]byte, logs []*types.Log, config *params.ChainConfig) error {
|
||||
deposits := make([]byte, 1) // note: first byte is 0x00 (== deposit request type)
|
||||
for _, log := range logs {
|
||||
if log.Address == config.DepositContractAddress {
|
||||
if log.Address == config.DepositContractAddress && len(log.Topics) > 0 && log.Topics[0] == depositTopic {
|
||||
request, err := types.DepositLogToRequest(log.Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse deposit data: %v", err)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by "stringer -type=BalanceChangeReason -output gen_balance_change_reason_stringer.go"; DO NOT EDIT.
|
||||
// Code generated by "stringer -type=BalanceChangeReason -trimprefix=BalanceChange -output gen_balance_change_reason_stringer.go"; DO NOT EDIT.
|
||||
|
||||
package tracing
|
||||
|
||||
|
|
@ -26,9 +26,9 @@ func _() {
|
|||
_ = x[BalanceChangeRevert-15]
|
||||
}
|
||||
|
||||
const _BalanceChangeReason_name = "BalanceChangeUnspecifiedBalanceIncreaseRewardMineUncleBalanceIncreaseRewardMineBlockBalanceIncreaseWithdrawalBalanceIncreaseGenesisBalanceBalanceIncreaseRewardTransactionFeeBalanceDecreaseGasBuyBalanceIncreaseGasReturnBalanceIncreaseDaoContractBalanceDecreaseDaoAccountBalanceChangeTransferBalanceChangeTouchAccountBalanceIncreaseSelfdestructBalanceDecreaseSelfdestructBalanceDecreaseSelfdestructBurnBalanceChangeRevert"
|
||||
const _BalanceChangeReason_name = "UnspecifiedBalanceIncreaseRewardMineUncleBalanceIncreaseRewardMineBlockBalanceIncreaseWithdrawalBalanceIncreaseGenesisBalanceBalanceIncreaseRewardTransactionFeeBalanceDecreaseGasBuyBalanceIncreaseGasReturnBalanceIncreaseDaoContractBalanceDecreaseDaoAccountTransferTouchAccountBalanceIncreaseSelfdestructBalanceDecreaseSelfdestructBalanceDecreaseSelfdestructBurnRevert"
|
||||
|
||||
var _BalanceChangeReason_index = [...]uint16{0, 24, 54, 84, 109, 138, 173, 194, 218, 244, 269, 290, 315, 342, 369, 400, 419}
|
||||
var _BalanceChangeReason_index = [...]uint16{0, 11, 41, 71, 96, 125, 160, 181, 205, 231, 256, 264, 276, 303, 330, 361, 367}
|
||||
|
||||
func (i BalanceChangeReason) String() string {
|
||||
if i >= BalanceChangeReason(len(_BalanceChangeReason_index)-1) {
|
||||
|
|
|
|||
52
core/tracing/gen_gas_change_reason_stringer.go
Normal file
52
core/tracing/gen_gas_change_reason_stringer.go
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// Code generated by "stringer -type=GasChangeReason -trimprefix=GasChange -output gen_gas_change_reason_stringer.go"; DO NOT EDIT.
|
||||
|
||||
package tracing
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[GasChangeUnspecified-0]
|
||||
_ = x[GasChangeTxInitialBalance-1]
|
||||
_ = x[GasChangeTxIntrinsicGas-2]
|
||||
_ = x[GasChangeTxRefunds-3]
|
||||
_ = x[GasChangeTxLeftOverReturned-4]
|
||||
_ = x[GasChangeCallInitialBalance-5]
|
||||
_ = x[GasChangeCallLeftOverReturned-6]
|
||||
_ = x[GasChangeCallLeftOverRefunded-7]
|
||||
_ = x[GasChangeCallContractCreation-8]
|
||||
_ = x[GasChangeCallContractCreation2-9]
|
||||
_ = x[GasChangeCallCodeStorage-10]
|
||||
_ = x[GasChangeCallOpCode-11]
|
||||
_ = x[GasChangeCallPrecompiledContract-12]
|
||||
_ = x[GasChangeCallStorageColdAccess-13]
|
||||
_ = x[GasChangeCallFailedExecution-14]
|
||||
_ = x[GasChangeWitnessContractInit-15]
|
||||
_ = x[GasChangeWitnessContractCreation-16]
|
||||
_ = x[GasChangeWitnessCodeChunk-17]
|
||||
_ = x[GasChangeWitnessContractCollisionCheck-18]
|
||||
_ = x[GasChangeTxDataFloor-19]
|
||||
_ = x[GasChangeIgnored-255]
|
||||
}
|
||||
|
||||
const (
|
||||
_GasChangeReason_name_0 = "UnspecifiedTxInitialBalanceTxIntrinsicGasTxRefundsTxLeftOverReturnedCallInitialBalanceCallLeftOverReturnedCallLeftOverRefundedCallContractCreationCallContractCreation2CallCodeStorageCallOpCodeCallPrecompiledContractCallStorageColdAccessCallFailedExecutionWitnessContractInitWitnessContractCreationWitnessCodeChunkWitnessContractCollisionCheckTxDataFloor"
|
||||
_GasChangeReason_name_1 = "Ignored"
|
||||
)
|
||||
|
||||
var (
|
||||
_GasChangeReason_index_0 = [...]uint16{0, 11, 27, 41, 50, 68, 86, 106, 126, 146, 167, 182, 192, 215, 236, 255, 274, 297, 313, 342, 353}
|
||||
)
|
||||
|
||||
func (i GasChangeReason) String() string {
|
||||
switch {
|
||||
case i <= 19:
|
||||
return _GasChangeReason_name_0[_GasChangeReason_index_0[i]:_GasChangeReason_index_0[i+1]]
|
||||
case i == 255:
|
||||
return _GasChangeReason_name_1
|
||||
default:
|
||||
return "GasChangeReason(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
}
|
||||
29
core/tracing/gen_nonce_change_reason_stringer.go
Normal file
29
core/tracing/gen_nonce_change_reason_stringer.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// Code generated by "stringer -type=NonceChangeReason -trimprefix NonceChange -output gen_nonce_change_reason_stringer.go"; DO NOT EDIT.
|
||||
|
||||
package tracing
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[NonceChangeUnspecified-0]
|
||||
_ = x[NonceChangeGenesis-1]
|
||||
_ = x[NonceChangeEoACall-2]
|
||||
_ = x[NonceChangeContractCreator-3]
|
||||
_ = x[NonceChangeNewContract-4]
|
||||
_ = x[NonceChangeAuthorization-5]
|
||||
_ = x[NonceChangeRevert-6]
|
||||
}
|
||||
|
||||
const _NonceChangeReason_name = "UnspecifiedGenesisEoACallContractCreatorNewContractAuthorizationRevert"
|
||||
|
||||
var _NonceChangeReason_index = [...]uint8{0, 11, 18, 25, 40, 51, 64, 70}
|
||||
|
||||
func (i NonceChangeReason) String() string {
|
||||
if i >= NonceChangeReason(len(_NonceChangeReason_index)-1) {
|
||||
return "NonceChangeReason(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _NonceChangeReason_name[_NonceChangeReason_index[i]:_NonceChangeReason_index[i+1]]
|
||||
}
|
||||
|
|
@ -221,7 +221,7 @@ type Hooks struct {
|
|||
// for tracing and reporting.
|
||||
type BalanceChangeReason byte
|
||||
|
||||
//go:generate go run golang.org/x/tools/cmd/stringer -type=BalanceChangeReason -output gen_balance_change_reason_stringer.go
|
||||
//go:generate go run golang.org/x/tools/cmd/stringer -type=BalanceChangeReason -trimprefix=BalanceChange -output gen_balance_change_reason_stringer.go
|
||||
|
||||
const (
|
||||
BalanceChangeUnspecified BalanceChangeReason = 0
|
||||
|
|
@ -282,6 +282,8 @@ const (
|
|||
// once per transaction, while those that start with `GasChangeCall` are emitted on a call basis.
|
||||
type GasChangeReason byte
|
||||
|
||||
//go:generate go run golang.org/x/tools/cmd/stringer -type=GasChangeReason -trimprefix=GasChange -output gen_gas_change_reason_stringer.go
|
||||
|
||||
const (
|
||||
GasChangeUnspecified GasChangeReason = 0
|
||||
|
||||
|
|
@ -346,6 +348,8 @@ const (
|
|||
// NonceChangeReason is used to indicate the reason for a nonce change.
|
||||
type NonceChangeReason byte
|
||||
|
||||
//go:generate go run golang.org/x/tools/cmd/stringer -type=NonceChangeReason -trimprefix NonceChange -output gen_nonce_change_reason_stringer.go
|
||||
|
||||
const (
|
||||
NonceChangeUnspecified NonceChangeReason = 0
|
||||
|
||||
|
|
|
|||
|
|
@ -1085,19 +1085,24 @@ func (p *BlobPool) SetGasTip(tip *big.Int) {
|
|||
p.updateStorageMetrics()
|
||||
}
|
||||
|
||||
// validateTx checks whether a transaction is valid according to the consensus
|
||||
// rules and adheres to some heuristic limits of the local node (price and size).
|
||||
func (p *BlobPool) validateTx(tx *types.Transaction) error {
|
||||
// Ensure the transaction adheres to basic pool filters (type, size, tip) and
|
||||
// consensus rules
|
||||
baseOpts := &txpool.ValidationOptions{
|
||||
// ValidateTxBasics checks whether a transaction is valid according to the consensus
|
||||
// rules, but does not check state-dependent validation such as sufficient balance.
|
||||
// This check is meant as an early check which only needs to be performed once,
|
||||
// and does not require the pool mutex to be held.
|
||||
func (p *BlobPool) ValidateTxBasics(tx *types.Transaction) error {
|
||||
opts := &txpool.ValidationOptions{
|
||||
Config: p.chain.Config(),
|
||||
Accept: 1 << types.BlobTxType,
|
||||
MaxSize: txMaxSize,
|
||||
MinTip: p.gasTip.ToBig(),
|
||||
}
|
||||
return txpool.ValidateTransaction(tx, p.head, p.signer, opts)
|
||||
}
|
||||
|
||||
if err := p.txValidationFn(tx, p.head, p.signer, baseOpts); err != nil {
|
||||
// validateTx checks whether a transaction is valid according to the consensus
|
||||
// rules and adheres to some heuristic limits of the local node (price and size).
|
||||
func (p *BlobPool) validateTx(tx *types.Transaction) error {
|
||||
if err := p.ValidateTxBasics(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
// Ensure the transaction adheres to the stateful pool filters (nonce, balance)
|
||||
|
|
|
|||
|
|
@ -18,12 +18,12 @@ package blobpool
|
|||
|
||||
import (
|
||||
"container/heap"
|
||||
"maps"
|
||||
"math"
|
||||
"slices"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/holiman/uint256"
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
// evictHeap is a helper data structure to keep track of the cheapest bottleneck
|
||||
|
|
@ -54,8 +54,7 @@ func newPriceHeap(basefee *uint256.Int, blobfee *uint256.Int, index map[common.A
|
|||
// Populate the heap in account sort order. Not really needed in practice,
|
||||
// but it makes the heap initialization deterministic and less annoying to
|
||||
// test in unit tests.
|
||||
heap.addrs = maps.Keys(index)
|
||||
slices.SortFunc(heap.addrs, common.Address.Cmp)
|
||||
heap.addrs = slices.SortedFunc(maps.Keys(index), common.Address.Cmp)
|
||||
for i, addr := range heap.addrs {
|
||||
heap.index[addr] = i
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package legacypool
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"maps"
|
||||
"math"
|
||||
"math/big"
|
||||
"slices"
|
||||
|
|
@ -40,7 +41,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -558,11 +558,11 @@ func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address]
|
|||
return pending
|
||||
}
|
||||
|
||||
// validateTxBasics checks whether a transaction is valid according to the consensus
|
||||
// ValidateTxBasics checks whether a transaction is valid according to the consensus
|
||||
// rules, but does not check state-dependent validation such as sufficient balance.
|
||||
// This check is meant as an early check which only needs to be performed once,
|
||||
// and does not require the pool mutex to be held.
|
||||
func (pool *LegacyPool) validateTxBasics(tx *types.Transaction) error {
|
||||
func (pool *LegacyPool) ValidateTxBasics(tx *types.Transaction) error {
|
||||
opts := &txpool.ValidationOptions{
|
||||
Config: pool.chainconfig,
|
||||
Accept: 0 |
|
||||
|
|
@ -573,10 +573,7 @@ func (pool *LegacyPool) validateTxBasics(tx *types.Transaction) error {
|
|||
MaxSize: txMaxSize,
|
||||
MinTip: pool.gasTip.Load().ToBig(),
|
||||
}
|
||||
if err := txpool.ValidateTransaction(tx, pool.currentHead.Load(), pool.signer, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return txpool.ValidateTransaction(tx, pool.currentHead.Load(), pool.signer, opts)
|
||||
}
|
||||
|
||||
// validateTx checks whether a transaction is valid according to the consensus
|
||||
|
|
@ -929,7 +926,7 @@ func (pool *LegacyPool) Add(txs []*types.Transaction, sync bool) []error {
|
|||
// Exclude transactions with basic errors, e.g invalid signatures and
|
||||
// insufficient intrinsic gas as soon as possible and cache senders
|
||||
// in transactions before obtaining lock
|
||||
if err := pool.validateTxBasics(tx); err != nil {
|
||||
if err := pool.ValidateTxBasics(tx); err != nil {
|
||||
errs[i] = err
|
||||
log.Trace("Discarding invalid transaction", "hash", tx.Hash(), "err", err)
|
||||
invalidTxMeter.Mark(1)
|
||||
|
|
@ -1674,7 +1671,7 @@ func (as *accountSet) addTx(tx *types.Transaction) {
|
|||
// reuse. The returned slice should not be changed!
|
||||
func (as *accountSet) flatten() []common.Address {
|
||||
if as.cache == nil {
|
||||
as.cache = maps.Keys(as.accounts)
|
||||
as.cache = slices.Collect(maps.Keys(as.accounts))
|
||||
}
|
||||
return as.cache
|
||||
}
|
||||
|
|
@ -1765,12 +1762,12 @@ func (t *lookup) Remove(hash common.Hash) {
|
|||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
t.removeAuthorities(hash)
|
||||
tx, ok := t.txs[hash]
|
||||
if !ok {
|
||||
log.Error("No transaction found to be deleted", "hash", hash)
|
||||
return
|
||||
}
|
||||
t.removeAuthorities(tx)
|
||||
t.slots -= numSlots(tx)
|
||||
slotsGauge.Update(int64(t.slots))
|
||||
|
||||
|
|
@ -1808,8 +1805,9 @@ func (t *lookup) addAuthorities(tx *types.Transaction) {
|
|||
|
||||
// removeAuthorities stops tracking the supplied tx in relation to its
|
||||
// authorities.
|
||||
func (t *lookup) removeAuthorities(hash common.Hash) {
|
||||
for addr := range t.auths {
|
||||
func (t *lookup) removeAuthorities(tx *types.Transaction) {
|
||||
hash := tx.Hash()
|
||||
for _, addr := range tx.SetCodeAuthorities() {
|
||||
list := t.auths[addr]
|
||||
// Remove tx from tracker.
|
||||
if i := slices.Index(list, hash); i >= 0 {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"slices"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
|
@ -238,6 +239,23 @@ func validatePoolInternals(pool *LegacyPool) error {
|
|||
return fmt.Errorf("pending nonce mismatch: have %v, want %v", nonce, last+1)
|
||||
}
|
||||
}
|
||||
// Ensure all auths in pool are tracked
|
||||
for _, tx := range pool.all.txs {
|
||||
for _, addr := range tx.SetCodeAuthorities() {
|
||||
list := pool.all.auths[addr]
|
||||
if i := slices.Index(list, tx.Hash()); i < 0 {
|
||||
return fmt.Errorf("authority not tracked: addr %s, tx %s", addr, tx.Hash())
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ensure all auths in pool have an associated tx.
|
||||
for addr, hashes := range pool.all.auths {
|
||||
for _, hash := range hashes {
|
||||
if _, ok := pool.all.txs[hash]; !ok {
|
||||
return fmt.Errorf("dangling authority, missing originating tx: addr %s, hash %s", addr, hash.Hex())
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -2381,6 +2399,32 @@ func TestSetCodeTransactions(t *testing.T) {
|
|||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remove-hash-from-authority-tracker",
|
||||
pending: 10,
|
||||
run: func(name string) {
|
||||
var keys []*ecdsa.PrivateKey
|
||||
for i := 0; i < 30; i++ {
|
||||
key, _ := crypto.GenerateKey()
|
||||
keys = append(keys, key)
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
testAddBalance(pool, addr, big.NewInt(params.Ether))
|
||||
}
|
||||
// Create a transactions with 3 unique auths so the lookup's auth map is
|
||||
// filled with addresses.
|
||||
for i := 0; i < 30; i += 3 {
|
||||
if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(10), uint256.NewInt(3), keys[i], []unsignedAuth{{0, keys[i]}, {0, keys[i+1]}, {0, keys[i+2]}})); err != nil {
|
||||
t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err)
|
||||
}
|
||||
}
|
||||
// Replace one of the transactions with a normal transaction so that the
|
||||
// original hash is removed from the tracker. The hash should be
|
||||
// associated with 3 different authorities.
|
||||
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keys[0])); err != nil {
|
||||
t.Fatalf("%s: failed to replace with remote transaction: %v", name, err)
|
||||
}
|
||||
},
|
||||
},
|
||||
} {
|
||||
tt.run(tt.name)
|
||||
pending, queued := pool.Stats()
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
package locals
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
|
@ -28,7 +29,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -74,28 +74,45 @@ func New(journalPath string, journalTime time.Duration, chainConfig *params.Chai
|
|||
|
||||
// Track adds a transaction to the tracked set.
|
||||
// Note: blob-type transactions are ignored.
|
||||
func (tracker *TxTracker) Track(tx *types.Transaction) {
|
||||
tracker.TrackAll([]*types.Transaction{tx})
|
||||
func (tracker *TxTracker) Track(tx *types.Transaction) error {
|
||||
return tracker.TrackAll([]*types.Transaction{tx})[0]
|
||||
}
|
||||
|
||||
// TrackAll adds a list of transactions to the tracked set.
|
||||
// Note: blob-type transactions are ignored.
|
||||
func (tracker *TxTracker) TrackAll(txs []*types.Transaction) {
|
||||
func (tracker *TxTracker) TrackAll(txs []*types.Transaction) []error {
|
||||
tracker.mu.Lock()
|
||||
defer tracker.mu.Unlock()
|
||||
|
||||
var errors []error
|
||||
for _, tx := range txs {
|
||||
if tx.Type() == types.BlobTxType {
|
||||
errors = append(errors, nil)
|
||||
continue
|
||||
}
|
||||
// Ignore the transactions which are failed for fundamental
|
||||
// validation such as invalid parameters.
|
||||
if err := tracker.pool.ValidateTxBasics(tx); err != nil {
|
||||
log.Debug("Invalid transaction submitted", "hash", tx.Hash(), "err", err)
|
||||
errors = append(errors, err)
|
||||
continue
|
||||
}
|
||||
// If we're already tracking it, it's a no-op
|
||||
if _, ok := tracker.all[tx.Hash()]; ok {
|
||||
errors = append(errors, nil)
|
||||
continue
|
||||
}
|
||||
// Theoretically, checking the error here is unnecessary since sender recovery
|
||||
// is already part of basic validation. However, retrieving the sender address
|
||||
// from the transaction cache is effectively a no-op if it was previously verified.
|
||||
// Therefore, the error is still checked just in case.
|
||||
addr, err := types.Sender(tracker.signer, tx)
|
||||
if err != nil { // Ignore this tx
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
continue
|
||||
}
|
||||
errors = append(errors, nil)
|
||||
|
||||
tracker.all[tx.Hash()] = tx
|
||||
if tracker.byAddr[addr] == nil {
|
||||
tracker.byAddr[addr] = legacypool.NewSortedMap()
|
||||
|
|
@ -107,6 +124,7 @@ func (tracker *TxTracker) TrackAll(txs []*types.Transaction) {
|
|||
}
|
||||
}
|
||||
localGauge.Update(int64(len(tracker.all)))
|
||||
return errors
|
||||
}
|
||||
|
||||
// recheck checks and returns any transactions that needs to be resubmitted.
|
||||
|
|
|
|||
|
|
@ -129,6 +129,12 @@ type SubPool interface {
|
|||
// retrieve blobs from the pools directly instead of the network.
|
||||
GetBlobs(vhashes []common.Hash) ([]*kzg4844.Blob, []*kzg4844.Proof)
|
||||
|
||||
// ValidateTxBasics checks whether a transaction is valid according to the consensus
|
||||
// rules, but does not check state-dependent validation such as sufficient balance.
|
||||
// This check is meant as a static check which can be performed without holding the
|
||||
// pool mutex.
|
||||
ValidateTxBasics(tx *types.Transaction) error
|
||||
|
||||
// Add enqueues a batch of transactions into the pool if they are valid. Due
|
||||
// to the large transaction churn, add may postpone fully integrating the tx
|
||||
// to a later point to batch multiple ones together.
|
||||
|
|
|
|||
|
|
@ -325,6 +325,17 @@ func (p *TxPool) GetBlobs(vhashes []common.Hash) ([]*kzg4844.Blob, []*kzg4844.Pr
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
// ValidateTxBasics checks whether a transaction is valid according to the consensus
|
||||
// rules, but does not check state-dependent validation such as sufficient balance.
|
||||
func (p *TxPool) ValidateTxBasics(tx *types.Transaction) error {
|
||||
for _, subpool := range p.subpools {
|
||||
if subpool.Filter(tx) {
|
||||
return subpool.ValidateTxBasics(tx)
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("%w: received type %d", core.ErrTxTypeNotSupported, tx.Type())
|
||||
}
|
||||
|
||||
// Add enqueues a batch of transactions into the pool if they are valid. Due
|
||||
// to the large transaction churn, add may postpone fully integrating the tx
|
||||
// to a later point to batch multiple ones together.
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
package txpool
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
|
@ -170,20 +169,11 @@ func validateBlobSidecar(hashes []common.Hash, sidecar *types.BlobTxSidecar) err
|
|||
if len(sidecar.Blobs) != len(hashes) {
|
||||
return fmt.Errorf("invalid number of %d blobs compared to %d blob hashes", len(sidecar.Blobs), len(hashes))
|
||||
}
|
||||
if len(sidecar.Commitments) != len(hashes) {
|
||||
return fmt.Errorf("invalid number of %d blob commitments compared to %d blob hashes", len(sidecar.Commitments), len(hashes))
|
||||
}
|
||||
if len(sidecar.Proofs) != len(hashes) {
|
||||
return fmt.Errorf("invalid number of %d blob proofs compared to %d blob hashes", len(sidecar.Proofs), len(hashes))
|
||||
}
|
||||
// Blob quantities match up, validate that the provers match with the
|
||||
// transaction hash before getting to the cryptography
|
||||
hasher := sha256.New()
|
||||
for i, vhash := range hashes {
|
||||
computed := kzg4844.CalcBlobHashV1(hasher, &sidecar.Commitments[i])
|
||||
if vhash != computed {
|
||||
return fmt.Errorf("blob %d: computed hash %#x mismatches transaction one %#x", i, computed, vhash)
|
||||
}
|
||||
if err := sidecar.ValidateBlobCommitmentHashes(hashes); err != nil {
|
||||
return err
|
||||
}
|
||||
// Blob commitments match with the hashes in the transaction, verify the
|
||||
// blobs themselves via KZG
|
||||
|
|
|
|||
|
|
@ -483,15 +483,23 @@ func (tx *Transaction) SetCodeAuthorizations() []SetCodeAuthorization {
|
|||
return setcodetx.AuthList
|
||||
}
|
||||
|
||||
// SetCodeAuthorities returns a list of each authorization's corresponding authority.
|
||||
// SetCodeAuthorities returns a list of unique authorities from the
|
||||
// authorization list.
|
||||
func (tx *Transaction) SetCodeAuthorities() []common.Address {
|
||||
setcodetx, ok := tx.inner.(*SetCodeTx)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
auths := make([]common.Address, 0, len(setcodetx.AuthList))
|
||||
var (
|
||||
marks = make(map[common.Address]bool)
|
||||
auths = make([]common.Address, 0, len(setcodetx.AuthList))
|
||||
)
|
||||
for _, auth := range setcodetx.AuthList {
|
||||
if addr, err := auth.Authority(); err == nil {
|
||||
if marks[addr] {
|
||||
continue
|
||||
}
|
||||
marks[addr] = true
|
||||
auths = append(auths, addr)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package types
|
|||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -85,6 +86,22 @@ func (sc *BlobTxSidecar) encodedSize() uint64 {
|
|||
return rlp.ListSize(blobs) + rlp.ListSize(commitments) + rlp.ListSize(proofs)
|
||||
}
|
||||
|
||||
// ValidateBlobCommitmentHashes checks whether the given hashes correspond to the
|
||||
// commitments in the sidecar
|
||||
func (sc *BlobTxSidecar) ValidateBlobCommitmentHashes(hashes []common.Hash) error {
|
||||
if len(sc.Commitments) != len(hashes) {
|
||||
return fmt.Errorf("invalid number of %d blob commitments compared to %d blob hashes", len(sc.Commitments), len(hashes))
|
||||
}
|
||||
hasher := sha256.New()
|
||||
for i, vhash := range hashes {
|
||||
computed := kzg4844.CalcBlobHashV1(hasher, &sc.Commitments[i])
|
||||
if vhash != computed {
|
||||
return fmt.Errorf("blob %d: computed hash %#x mismatches transaction one %#x", i, computed, vhash)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// blobTxWithBlobs is used for encoding of transactions when blobs are present.
|
||||
type blobTxWithBlobs struct {
|
||||
BlobTx *BlobTx
|
||||
|
|
|
|||
|
|
@ -272,10 +272,20 @@ func (b *EthAPIBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscri
|
|||
}
|
||||
|
||||
func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
||||
if locals := b.eth.localTxTracker; locals != nil {
|
||||
locals.Track(signedTx)
|
||||
locals := b.eth.localTxTracker
|
||||
if locals != nil {
|
||||
if err := locals.Track(signedTx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return b.eth.txPool.Add([]*types.Transaction{signedTx}, false)[0]
|
||||
// No error will be returned to user if the transaction fails stateful
|
||||
// validation (e.g., no available slot), as the locally submitted transactions
|
||||
// may be resubmitted later via the local tracker.
|
||||
err := b.eth.txPool.Add([]*types.Transaction{signedTx}, false)[0]
|
||||
if err != nil && locals == nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) {
|
||||
|
|
@ -368,10 +378,6 @@ func (b *EthAPIBackend) ChainDb() ethdb.Database {
|
|||
return b.eth.ChainDb()
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) EventMux() *event.TypeMux {
|
||||
return b.eth.EventMux()
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) AccountManager() *accounts.Manager {
|
||||
return b.eth.AccountManager()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -345,7 +345,6 @@ func (s *Ethereum) Miner() *miner.Miner { return s.miner }
|
|||
func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
|
||||
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
|
||||
func (s *Ethereum) TxPool() *txpool.TxPool { return s.txPool }
|
||||
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
|
||||
func (s *Ethereum) Engine() consensus.Engine { return s.engine }
|
||||
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
|
||||
func (s *Ethereum) IsListening() bool { return true } // Always listening
|
||||
|
|
|
|||
|
|
@ -107,8 +107,8 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) {
|
|||
// Compute gas used ratio for normal and blob gas.
|
||||
bf.results.gasUsedRatio = float64(bf.header.GasUsed) / float64(bf.header.GasLimit)
|
||||
if blobGasUsed := bf.header.BlobGasUsed; blobGasUsed != nil {
|
||||
maxBlobs := eip4844.MaxBlobsPerBlock(config, bf.header.Time)
|
||||
bf.results.blobGasUsedRatio = float64(*blobGasUsed) / float64(maxBlobs)
|
||||
maxBlobGas := eip4844.MaxBlobGasPerBlock(config, bf.header.Time)
|
||||
bf.results.blobGasUsedRatio = float64(*blobGasUsed) / float64(maxBlobGas)
|
||||
}
|
||||
|
||||
if len(percentiles) == 0 {
|
||||
|
|
|
|||
|
|
@ -84,9 +84,19 @@ func TestFeeHistory(t *testing.T) {
|
|||
if len(ratio) != c.expCount {
|
||||
t.Fatalf("Test case %d: gasUsedRatio array length mismatch, want %d, got %d", i, c.expCount, len(ratio))
|
||||
}
|
||||
for _, ratio := range ratio {
|
||||
if ratio > 1 {
|
||||
t.Fatalf("Test case %d: gasUsedRatio greater than 1, got %f", i, ratio)
|
||||
}
|
||||
}
|
||||
if len(blobRatio) != c.expCount {
|
||||
t.Fatalf("Test case %d: blobGasUsedRatio array length mismatch, want %d, got %d", i, c.expCount, len(blobRatio))
|
||||
}
|
||||
for _, ratio := range blobRatio {
|
||||
if ratio > 1 {
|
||||
t.Fatalf("Test case %d: blobGasUsedRatio greater than 1, got %f", i, ratio)
|
||||
}
|
||||
}
|
||||
if len(blobBaseFee) != len(baseFee) {
|
||||
t.Fatalf("Test case %d: blobBaseFee array length mismatch, want %d, got %d", i, len(baseFee), len(blobBaseFee))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,6 +69,19 @@ func (h *ethHandler) Handle(peer *eth.Peer, packet eth.Packet) error {
|
|||
return h.txFetcher.Enqueue(peer.ID(), *packet, false)
|
||||
|
||||
case *eth.PooledTransactionsResponse:
|
||||
// If we receive any blob transactions missing sidecars, or with
|
||||
// sidecars that don't correspond to the versioned hashes reported
|
||||
// in the header, disconnect from the sending peer.
|
||||
for _, tx := range *packet {
|
||||
if tx.Type() == types.BlobTxType {
|
||||
if tx.BlobTxSidecar() == nil {
|
||||
return errors.New("received sidecar-less blob transaction")
|
||||
}
|
||||
if err := tx.BlobTxSidecar().ValidateBlobCommitmentHashes(tx.BlobHashes()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return h.txFetcher.Enqueue(peer.ID(), *packet, true)
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -177,14 +177,14 @@ func (s *StructLog) toLegacyJSON() json.RawMessage {
|
|||
if len(s.ReturnData) > 0 {
|
||||
msg.ReturnData = hexutil.Bytes(s.ReturnData).String()
|
||||
}
|
||||
if s.Memory != nil {
|
||||
if len(s.Memory) > 0 {
|
||||
memory := make([]string, 0, (len(s.Memory)+31)/32)
|
||||
for i := 0; i+32 <= len(s.Memory); i += 32 {
|
||||
memory = append(memory, fmt.Sprintf("%x", s.Memory[i:i+32]))
|
||||
}
|
||||
msg.Memory = &memory
|
||||
}
|
||||
if s.Storage != nil {
|
||||
if len(s.Storage) > 0 {
|
||||
storage := make(map[string]string)
|
||||
for i, storageValue := range s.Storage {
|
||||
storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue)
|
||||
|
|
|
|||
|
|
@ -598,6 +598,15 @@ func (ec *Client) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
|||
return (*big.Int)(&hex), nil
|
||||
}
|
||||
|
||||
// BlobBaseFee retrieves the current blob base fee.
|
||||
func (ec *Client) BlobBaseFee(ctx context.Context) (*big.Int, error) {
|
||||
var hex hexutil.Big
|
||||
if err := ec.c.CallContext(ctx, &hex, "eth_blobBaseFee"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (*big.Int)(&hex), nil
|
||||
}
|
||||
|
||||
type feeHistoryResultMarshaling struct {
|
||||
OldestBlock *hexutil.Big `json:"oldestBlock"`
|
||||
Reward [][]*hexutil.Big `json:"reward,omitempty"`
|
||||
|
|
|
|||
|
|
@ -404,6 +404,15 @@ func testStatusFunctions(t *testing.T, client *rpc.Client) {
|
|||
t.Fatalf("unexpected gas tip cap: %v", gasTipCap)
|
||||
}
|
||||
|
||||
// BlobBaseFee
|
||||
blobBaseFee, err := ec.BlobBaseFee(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if blobBaseFee.Cmp(big.NewInt(1)) != 0 {
|
||||
t.Fatalf("unexpected blob base fee: %v", blobBaseFee)
|
||||
}
|
||||
|
||||
// FeeHistory
|
||||
history, err := ec.FeeHistory(context.Background(), 1, big.NewInt(2), []float64{95, 99})
|
||||
if err != nil {
|
||||
|
|
|
|||
2
go.mod
2
go.mod
|
|
@ -65,7 +65,6 @@ require (
|
|||
go.uber.org/automaxprocs v1.5.2
|
||||
go.uber.org/goleak v1.3.0
|
||||
golang.org/x/crypto v0.32.0
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
|
||||
golang.org/x/sync v0.10.0
|
||||
golang.org/x/sys v0.29.0
|
||||
golang.org/x/text v0.21.0
|
||||
|
|
@ -142,6 +141,7 @@ require (
|
|||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/net v0.34.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
|
|
|
|||
4
go.sum
4
go.sum
|
|
@ -553,8 +553,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
|||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
|
||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
|
||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
|
|
|
|||
|
|
@ -295,3 +295,45 @@ func CheckEnvVars(ctx *cli.Context, flags []cli.Flag, prefix string) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CheckExclusive verifies that only a single instance of the provided flags was
|
||||
// set by the user. Each flag might optionally be followed by a string type to
|
||||
// specialize it further.
|
||||
func CheckExclusive(ctx *cli.Context, args ...any) {
|
||||
set := make([]string, 0, 1)
|
||||
for i := 0; i < len(args); i++ {
|
||||
// Make sure the next argument is a flag and skip if not set
|
||||
flag, ok := args[i].(cli.Flag)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("invalid argument, not cli.Flag type: %T", args[i]))
|
||||
}
|
||||
// Check if next arg extends current and expand its name if so
|
||||
name := flag.Names()[0]
|
||||
|
||||
if i+1 < len(args) {
|
||||
switch option := args[i+1].(type) {
|
||||
case string:
|
||||
// Extended flag check, make sure value set doesn't conflict with passed in option
|
||||
if ctx.String(flag.Names()[0]) == option {
|
||||
name += "=" + option
|
||||
set = append(set, "--"+name)
|
||||
}
|
||||
// shift arguments and continue
|
||||
i++
|
||||
continue
|
||||
|
||||
case cli.Flag:
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid argument, not cli.Flag or string extension: %T", args[i+1]))
|
||||
}
|
||||
}
|
||||
// Mark the flag if it's set
|
||||
if ctx.IsSet(flag.Names()[0]) {
|
||||
set = append(set, "--"+name)
|
||||
}
|
||||
}
|
||||
if len(set) > 1 {
|
||||
fmt.Fprintf(os.Stderr, "Flags %v can't be used at the same time", strings.Join(set, ", "))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,12 +21,11 @@ import (
|
|||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"net"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
var special4, special6 Netlist
|
||||
|
|
@ -324,8 +323,7 @@ func (s *DistinctNetSet) key(ip netip.Addr) netip.Prefix {
|
|||
|
||||
// String implements fmt.Stringer
|
||||
func (s DistinctNetSet) String() string {
|
||||
keys := maps.Keys(s.members)
|
||||
slices.SortFunc(keys, func(a, b netip.Prefix) int {
|
||||
keys := slices.SortedFunc(maps.Keys(s.members), func(a, b netip.Prefix) int {
|
||||
return strings.Compare(a.String(), b.String())
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
|
|
@ -29,7 +30,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
// State history records the state changes involved in executing a block. The
|
||||
|
|
@ -250,15 +250,11 @@ type history struct {
|
|||
// newHistory constructs the state history object with provided state change set.
|
||||
func newHistory(root common.Hash, parent common.Hash, block uint64, accounts map[common.Address][]byte, storages map[common.Address]map[common.Hash][]byte, rawStorageKey bool) *history {
|
||||
var (
|
||||
accountList = maps.Keys(accounts)
|
||||
accountList = slices.SortedFunc(maps.Keys(accounts), common.Address.Cmp)
|
||||
storageList = make(map[common.Address][]common.Hash)
|
||||
)
|
||||
slices.SortFunc(accountList, common.Address.Cmp)
|
||||
|
||||
for addr, slots := range storages {
|
||||
slist := maps.Keys(slots)
|
||||
slices.SortFunc(slist, common.Hash.Cmp)
|
||||
storageList[addr] = slist
|
||||
storageList[addr] = slices.SortedFunc(maps.Keys(slots), common.Hash.Cmp)
|
||||
}
|
||||
version := historyVersion
|
||||
if !rawStorageKey {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package pathdb
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
|
|
@ -27,7 +28,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
// counter helps in tracking items and their corresponding sizes.
|
||||
|
|
@ -174,8 +174,7 @@ func (s *stateSet) accountList() []common.Hash {
|
|||
s.listLock.Lock()
|
||||
defer s.listLock.Unlock()
|
||||
|
||||
list = maps.Keys(s.accountData)
|
||||
slices.SortFunc(list, common.Hash.Cmp)
|
||||
list = slices.SortedFunc(maps.Keys(s.accountData), common.Hash.Cmp)
|
||||
s.accountListSorted = list
|
||||
return list
|
||||
}
|
||||
|
|
@ -205,8 +204,7 @@ func (s *stateSet) storageList(accountHash common.Hash) []common.Hash {
|
|||
s.listLock.Lock()
|
||||
defer s.listLock.Unlock()
|
||||
|
||||
list := maps.Keys(s.storageData[accountHash])
|
||||
slices.SortFunc(list, common.Hash.Cmp)
|
||||
list := slices.SortedFunc(maps.Keys(s.storageData[accountHash]), common.Hash.Cmp)
|
||||
s.storageListSorted[accountHash] = list
|
||||
return list
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,6 @@ package version
|
|||
const (
|
||||
Major = 1 // Major version component of the current release
|
||||
Minor = 15 // Minor version component of the current release
|
||||
Patch = 4 // Patch version component of the current release
|
||||
Patch = 6 // Patch version component of the current release
|
||||
Meta = "unstable" // Version metadata to append to the version string
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue