mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 06:36:43 +00:00
Merge branch 'ethereum:master' into gethintegration
This commit is contained in:
commit
001565b087
26 changed files with 3297 additions and 741 deletions
7
.mailmap
7
.mailmap
|
|
@ -50,6 +50,10 @@ Boqin Qin <bobbqqin@bupt.edu.cn> <Bobbqqin@gmail.com>
|
||||||
|
|
||||||
Casey Detrio <cdetrio@gmail.com>
|
Casey Detrio <cdetrio@gmail.com>
|
||||||
|
|
||||||
|
Charlotte <tqpcharlie@proton.me>
|
||||||
|
Charlotte <tqpcharlie@proton.me> <Zachinquarantine@protonmail.com>
|
||||||
|
Charlotte <tqpcharlie@proton.me> <zachinquarantine@yahoo.com>
|
||||||
|
|
||||||
Cheng Li <lob4tt@gmail.com>
|
Cheng Li <lob4tt@gmail.com>
|
||||||
|
|
||||||
Chris Ziogas <ziogaschr@gmail.com>
|
Chris Ziogas <ziogaschr@gmail.com>
|
||||||
|
|
@ -301,9 +305,6 @@ Yohann Léon <sybiload@gmail.com>
|
||||||
yzb <335357057@qq.com>
|
yzb <335357057@qq.com>
|
||||||
yzb <335357057@qq.com> <flyingyzb@gmail.com>
|
yzb <335357057@qq.com> <flyingyzb@gmail.com>
|
||||||
|
|
||||||
Zachinquarantine <Zachinquarantine@protonmail.com>
|
|
||||||
Zachinquarantine <Zachinquarantine@protonmail.com> <zachinquarantine@yahoo.com>
|
|
||||||
|
|
||||||
Ziyuan Zhong <zzy.albert@163.com>
|
Ziyuan Zhong <zzy.albert@163.com>
|
||||||
|
|
||||||
Zsolt Felföldi <zsfelfoldi@gmail.com>
|
Zsolt Felföldi <zsfelfoldi@gmail.com>
|
||||||
|
|
|
||||||
2
AUTHORS
2
AUTHORS
|
|
@ -123,6 +123,7 @@ Ceyhun Onur <ceyhun.onur@avalabs.org>
|
||||||
chabashilah <doumodoumo@gmail.com>
|
chabashilah <doumodoumo@gmail.com>
|
||||||
changhong <changhong.yu@shanbay.com>
|
changhong <changhong.yu@shanbay.com>
|
||||||
Charles Cooper <cooper.charles.m@gmail.com>
|
Charles Cooper <cooper.charles.m@gmail.com>
|
||||||
|
Charlotte <tqpcharlie@proton.me>
|
||||||
Chase Wright <mysticryuujin@gmail.com>
|
Chase Wright <mysticryuujin@gmail.com>
|
||||||
Chawin Aiemvaravutigul <nick41746@hotmail.com>
|
Chawin Aiemvaravutigul <nick41746@hotmail.com>
|
||||||
Chen Quan <terasum@163.com>
|
Chen Quan <terasum@163.com>
|
||||||
|
|
@ -839,7 +840,6 @@ ywzqwwt <39263032+ywzqwwt@users.noreply.github.com>
|
||||||
yzb <335357057@qq.com>
|
yzb <335357057@qq.com>
|
||||||
zaccoding <zaccoding725@gmail.com>
|
zaccoding <zaccoding725@gmail.com>
|
||||||
Zach <zach.ramsay@gmail.com>
|
Zach <zach.ramsay@gmail.com>
|
||||||
Zachinquarantine <Zachinquarantine@protonmail.com>
|
|
||||||
zah <zahary@gmail.com>
|
zah <zahary@gmail.com>
|
||||||
Zahoor Mohamed <zahoor@zahoor.in>
|
Zahoor Mohamed <zahoor@zahoor.in>
|
||||||
Zak Cole <zak@beattiecole.com>
|
Zak Cole <zak@beattiecole.com>
|
||||||
|
|
|
||||||
|
|
@ -5,54 +5,54 @@
|
||||||
# https://github.com/ethereum/execution-spec-tests/releases/download/pectra-devnet-6%40v1.0.0/
|
# https://github.com/ethereum/execution-spec-tests/releases/download/pectra-devnet-6%40v1.0.0/
|
||||||
b69211752a3029083c020dc635fe12156ca1a6725a08559da540a0337586a77e fixtures_pectra-devnet-6.tar.gz
|
b69211752a3029083c020dc635fe12156ca1a6725a08559da540a0337586a77e fixtures_pectra-devnet-6.tar.gz
|
||||||
|
|
||||||
# version:golang 1.24.2
|
# version:golang 1.24.3
|
||||||
# https://go.dev/dl/
|
# https://go.dev/dl/
|
||||||
9dc77ffadc16d837a1bf32d99c624cb4df0647cee7b119edd9e7b1bcc05f2e00 go1.24.2.src.tar.gz
|
229c08b600b1446798109fae1f569228102c8473caba8104b6418cb5bc032878 go1.24.3.src.tar.gz
|
||||||
427b373540d8fd51dbcc46bdecd340af109cd41514443c000d3dcde72b2c65a3 go1.24.2.aix-ppc64.tar.gz
|
6f6901497547db3b77c14f7f953fbcef9fa5fb84199ee2ee14a5686e66bed5a6 go1.24.3.aix-ppc64.tar.gz
|
||||||
238d9c065d09ff6af229d2e3b8b5e85e688318d69f4006fb85a96e41c216ea83 go1.24.2.darwin-amd64.tar.gz
|
a05fa7e4043a4fec66897135219e3b8ab2202b5ef351c60c2fbb531dfb8f2900 go1.24.3.darwin-amd64.pkg
|
||||||
535ed9ff283fee39575a7fb9b6d8b1901b6dc640d06dc71fd7d3faeefdaf8030 go1.24.2.darwin-amd64.pkg
|
13e6fe3fcf65689d77d40e633de1e31c6febbdbcb846eb05fc2434ed2213e92b go1.24.3.darwin-amd64.tar.gz
|
||||||
b70f8b3c5b4ccb0ad4ffa5ee91cd38075df20fdbd953a1daedd47f50fbcff47a go1.24.2.darwin-arm64.tar.gz
|
97055ff4214043b39dc32e043fdd5c565df7c0a4e2fc0174e779a134c347ae0e go1.24.3.darwin-arm64.pkg
|
||||||
4732f607a47ce4d898c0af01ff68f07e0820a6b50603aef5d5c777d1102505e2 go1.24.2.darwin-arm64.pkg
|
64a3fa22142f627e78fac3018ce3d4aeace68b743eff0afda8aae0411df5e4fb go1.24.3.darwin-arm64.tar.gz
|
||||||
c17686b5fd61a663fbfafccfa177961be59386cf294e935ce35866b9dcb8e78a go1.24.2.dragonfly-amd64.tar.gz
|
32de3fd44d5055973978436a7f1f0ffbaae85c1b603ec6105e5c38d8a674c721 go1.24.3.dragonfly-amd64.tar.gz
|
||||||
026f1dd906189acff714c7625686bbc4ed91042618ba010d45b671461acc9e63 go1.24.2.freebsd-386.tar.gz
|
9fe6101b3797919bd7337ee5ce591954f85d59db7ae88983904db29fd64c3dd1 go1.24.3.freebsd-386.tar.gz
|
||||||
49399ba759b570a8f87d12179133403da6c2dd296d63a8830dee309161b9c40c go1.24.2.freebsd-amd64.tar.gz
|
6ccf4cca287e90cc28cd7954b6172f5d177a17e20b072b65f7f39636c325e2fb go1.24.3.freebsd-amd64.tar.gz
|
||||||
1f48f47183794d97c29736004247ab541177cf984ac6322c78bc43828daa1172 go1.24.2.freebsd-arm.tar.gz
|
ce45ebf389066f82a7b056b66dd650efb51fde6f8bf92a2a3ab6990f02788ebf go1.24.3.freebsd-arm.tar.gz
|
||||||
ef856428b60a8c0bd9a2cba596e83024be6f1c2d5574e89cb1ff2262b08df8b9 go1.24.2.freebsd-arm64.tar.gz
|
8f6494a12a874d0ea57c67987829359e016960ce3ba0673273609d6ac2af589a go1.24.3.freebsd-arm64.tar.gz
|
||||||
ec2088823e16df00600a6d0f72e9a7dc6d2f80c9c140c2043c0cf20e1404d1a9 go1.24.2.freebsd-riscv64.tar.gz
|
f9db392560cf0851f0bc8f2190e1978e01b4603038c27fecfc8658a695b71616 go1.24.3.freebsd-riscv64.tar.gz
|
||||||
e030e7cedbb8688f1d75cb80f3de6ee2e6617a67d34051e794e5992b53462147 go1.24.2.illumos-amd64.tar.gz
|
01717fff64c5d98457272002fa825d0a15e307bf6e189f2b0c23817fa033b61c go1.24.3.illumos-amd64.tar.gz
|
||||||
4c382776d52313266f3026236297a224a6688751256a2dffa3f524d8d6f6c0ba go1.24.2.linux-386.tar.gz
|
41b1051063e68cbd2b919bf12326764fe33937cf1d32b5c529dd1a4f43dce578 go1.24.3.linux-386.tar.gz
|
||||||
68097bd680839cbc9d464a0edce4f7c333975e27a90246890e9f1078c7e702ad go1.24.2.linux-amd64.tar.gz
|
3333f6ea53afa971e9078895eaa4ac7204a8c6b5c68c10e6bc9a33e8e391bdd8 go1.24.3.linux-amd64.tar.gz
|
||||||
756274ea4b68fa5535eb9fe2559889287d725a8da63c6aae4d5f23778c229f4b go1.24.2.linux-arm64.tar.gz
|
a463cb59382bd7ae7d8f4c68846e73c4d589f223c589ac76871b66811ded7836 go1.24.3.linux-arm64.tar.gz
|
||||||
438d5d3d7dcb239b58d893a715672eabe670b9730b1fd1c8fc858a46722a598a go1.24.2.linux-armv6l.tar.gz
|
17a392d7e826625dd12a32099df0b00b85c32d8132ed86fe917183ee5c3f88ed go1.24.3.linux-armv6l.tar.gz
|
||||||
6aefd3bf59c3c5592eda4fb287322207f119c2210f3795afa9be48d3ccb73e1b go1.24.2.linux-loong64.tar.gz
|
e4b003c04c902edc140153d279b42167f1ad7c229f48f1f729bbef5e65e88d1f go1.24.3.linux-loong64.tar.gz
|
||||||
93e49bb4692783b0e9a2deab9558c6e8d2867f35592aeff285adda60924167f3 go1.24.2.linux-mips.tar.gz
|
1c79d89edf835edf9d4336ccea7cb89bc5c0ca82b12b36b218d599a5400d60fe go1.24.3.linux-mips.tar.gz
|
||||||
6e86e703675016f3faf6604b8f68f20dc1bba75849136e6dd4f43f69c8a4a9d9 go1.24.2.linux-mips64.tar.gz
|
0b64fe147d69f4d681d8e8a035c760477531432f83d831f18d37cb9bf3652488 go1.24.3.linux-mips64.tar.gz
|
||||||
f233d237538ca1559a7d7cf519a29f0147923a951377bc4e467af4c059e68851 go1.24.2.linux-mips64le.tar.gz
|
396b784c255b64512dc00c302c053e43a3cbfc77518664c6ac5569aafad4d1e6 go1.24.3.linux-mips64le.tar.gz
|
||||||
545e1b9a7939f923fd53bde98334b987ef42eb353ee3e0bfede8aa06079d6b24 go1.24.2.linux-mipsle.tar.gz
|
93898313887f14e8efbe9d7386d5da4792b2d6c492bee562993fd4c9daa75c6d go1.24.3.linux-mipsle.tar.gz
|
||||||
6eab31481f2f46187bc1b6c887662eef06fc9d7271a8390854072cdb387c8d74 go1.24.2.linux-ppc64.tar.gz
|
873ae3a6a6655a7b6f820e095d9965507e8dfd3cf76bc92d75c564ecbca385f6 go1.24.3.linux-ppc64.tar.gz
|
||||||
5fff857791d541c71d8ea0171c73f6f99770d15ff7e2ad979104856d01f36563 go1.24.2.linux-ppc64le.tar.gz
|
341a749d168f47b1d4dad25e32cae70849b7ceed7c290823b853c9e6b0df0856 go1.24.3.linux-ppc64le.tar.gz
|
||||||
91bda1558fcbd1c92769ad86c8f5cf796f8c67b0d9d9c19f76eecfc75ce71527 go1.24.2.linux-riscv64.tar.gz
|
fa482f53ccb4ba280316b8c5751ea67291507280d9166f2a38fe4d9b5d5fb64b go1.24.3.linux-riscv64.tar.gz
|
||||||
1cb3448166d6abb515a85a3ee5afbdf932081fb58ad7143a8fb666fbc06146d9 go1.24.2.linux-s390x.tar.gz
|
a87b0c2a079a0bece1620fb29a00e02b4dba17507850f837e754af7d57cda282 go1.24.3.linux-s390x.tar.gz
|
||||||
a9a2c0db2e826f20f00b02bee01dfdaeb49591c2f6ffacb78dc64a950894f7ff go1.24.2.netbsd-386.tar.gz
|
63155382308db1306200aff7821aa26bf2a2dda23537dd637a9704b485b6ddf0 go1.24.3.netbsd-386.tar.gz
|
||||||
cd1a35b76ed9c7b6c0c1616741bd319699a77867ade0be9924f32496c0a87a3f go1.24.2.netbsd-amd64.tar.gz
|
fe2c5c79482958b867c08a4fc2a10a998de9c0206b08d5b3ebcb2232e8d2777c go1.24.3.netbsd-amd64.tar.gz
|
||||||
8c666388d066e479155cc5116950eeb435df28087ef277c18f1dc7479f836e60 go1.24.2.netbsd-arm.tar.gz
|
e8ff77aef21521b5dd94e44282a3243309b80717414cf12f72835a45886a049f go1.24.3.netbsd-arm.tar.gz
|
||||||
5d42f0be04f58da5be788a1e260f8747c316b8ce182bf0b273c2e4c691feaa1a go1.24.2.netbsd-arm64.tar.gz
|
b337fbaf82822685940ffaa76fbcf4be5d2f0258bc819cd80bc408b491f45c04 go1.24.3.netbsd-arm64.tar.gz
|
||||||
688effa23ea3973cc8b0fdf4246712cbeef55ff20c45f3a9e28b0c2db04246cf go1.24.2.openbsd-386.tar.gz
|
c1bb9dd8418480aa7f65452b08de3759da3bf89702be71b5a9fc084836b24ad5 go1.24.3.openbsd-386.tar.gz
|
||||||
e5daf95f1048d8026b1366450a3f8044d668b0639db6422ad9a83755c6745cf7 go1.24.2.openbsd-amd64.tar.gz
|
531218de748b0caaf6d1ad18921206fc12baaa89bf483a0a5e60a571c206fe6f go1.24.3.openbsd-amd64.tar.gz
|
||||||
aeadaf74bd544d1a12ba9b14c0e7cdb1964de3ba9a52acb4619e91dbae7def7b go1.24.2.openbsd-arm.tar.gz
|
bcd0dc959986fc346969b5d4111c3c8031882d8bf8d87a2c2ecf1328962a91f2 go1.24.3.openbsd-arm.tar.gz
|
||||||
9e222d9adb0ce836a5b3c8d5aadbd167c8869c030b113f4a81aa88e9a200f279 go1.24.2.openbsd-arm64.tar.gz
|
00ee6f8f1c41fd2e28ad386bd7e39acce7cab84af6de835855b29d1c597335c4 go1.24.3.openbsd-arm64.tar.gz
|
||||||
192fffa34536adc3cd1bb7c1ee785b8bc156ae7afd10bbf5db99ec8f2e93066e go1.24.2.openbsd-ppc64.tar.gz
|
9f4ec0a9203ed3c54ce1a2a390ad3d45838cdb7efd85baeff857e37dfde04edd go1.24.3.openbsd-ppc64.tar.gz
|
||||||
a23e90b451a390549042c2a7efbec6f29ed98b2d5618c8d2a35704e21be96e09 go1.24.2.openbsd-riscv64.tar.gz
|
da4d6f80e2373250d8c31c32dcd1e08775c327c0d610923604660cc0e07e8cba go1.24.3.openbsd-riscv64.tar.gz
|
||||||
5cdcafe455d859b02779611a5a1e1d63e498b922e05818fb3debe410a5959e9e go1.24.2.plan9-386.tar.gz
|
f5d02149132eedda6c2d46b360d7da462b8a5f9e3f8567db100c2d7bff0ddcd7 go1.24.3.plan9-386.tar.gz
|
||||||
81351659804fa505c1b3ec6fdf9599f7f88df08614307eeb96071bf5e2e74beb go1.24.2.plan9-amd64.tar.gz
|
175f3d79f4762a3c545d2c6393bf6b8bac24e838026869dafab06b930735c94f go1.24.3.plan9-amd64.tar.gz
|
||||||
6e337d5def14ed0123423c1c32e2e6d8b19161e5d5ffaa7356dad48ee0fd80b4 go1.24.2.plan9-arm.tar.gz
|
d1e4ac15095da1611659261c2228c2058756cf87d61d9fad262f76755ef26849 go1.24.3.plan9-arm.tar.gz
|
||||||
07e6926ebc476c044d7d5b17706abfc52be52bccc2073d1734174efe63c6b35e go1.24.2.solaris-amd64.tar.gz
|
e644220a6ced3c07a7acc1364193cb709a97737dd8b6792a07a8ec6d9996713e go1.24.3.solaris-amd64.tar.gz
|
||||||
13d86cb818bba331da75fcd18246ab31a1067b44fb4a243b6dfd93097eda7f37 go1.24.2.windows-386.zip
|
0d7e7dc0a31ba0cdd487415709d03b02fc9490ef111e8dfd22788a6d63316f37 go1.24.3.windows-386.msi
|
||||||
8a702d9f7104a15bd935f4191c58c24c0b6389e066b9d5661b93915114a2bef0 go1.24.2.windows-386.msi
|
c27c463a61ab849266baa0c17a6c5c4256a574ab642f609ba25c96ec965dc184 go1.24.3.windows-386.zip
|
||||||
29c553aabee0743e2ffa3e9fa0cda00ef3b3cc4ff0bc92007f31f80fd69892e1 go1.24.2.windows-amd64.zip
|
d5b7637e7e138be877d96a4501709d480e050d86a8f402bc950e72112b5aedc5 go1.24.3.windows-amd64.msi
|
||||||
acefb191e72fea0bdb1a3f5f8f6f5ab18b42b3bbce0c7183f189f25953aff275 go1.24.2.windows-amd64.msi
|
be9787cb08998b1860fe3513e48a5fe5b96302d358a321b58e651184fa9638b3 go1.24.3.windows-amd64.zip
|
||||||
ab267f7f9a3366d48d7664be9e627ce3e63273231430cce5f7783fb910f14148 go1.24.2.windows-arm64.zip
|
7efde2e5e8468e9caf2c7fc94f4da78a726a5031a1ed63acff7899527cdddff6 go1.24.3.windows-arm64.msi
|
||||||
d187bfe539356c39573d2f46766d1d08122b4f33da00fd14d12485fa9e241ff5 go1.24.2.windows-arm64.msi
|
eec9fa736056b54dd88ecb669db2bfad39b0c48f6f9080f036dfa1ca42dc4bb5 go1.24.3.windows-arm64.zip
|
||||||
|
|
||||||
# version:golangci 2.0.2
|
# version:golangci 2.0.2
|
||||||
# https://github.com/golangci/golangci-lint/releases/
|
# https://github.com/golangci/golangci-lint/releases/
|
||||||
|
|
|
||||||
55
build/ci.go
55
build/ci.go
|
|
@ -59,6 +59,7 @@ import (
|
||||||
"github.com/cespare/cp"
|
"github.com/cespare/cp"
|
||||||
"github.com/ethereum/go-ethereum/crypto/signify"
|
"github.com/ethereum/go-ethereum/crypto/signify"
|
||||||
"github.com/ethereum/go-ethereum/internal/build"
|
"github.com/ethereum/go-ethereum/internal/build"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/download"
|
||||||
"github.com/ethereum/go-ethereum/internal/version"
|
"github.com/ethereum/go-ethereum/internal/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -190,7 +191,7 @@ func doInstall(cmdline []string) {
|
||||||
// Configure the toolchain.
|
// Configure the toolchain.
|
||||||
tc := build.GoToolchain{GOARCH: *arch, CC: *cc}
|
tc := build.GoToolchain{GOARCH: *arch, CC: *cc}
|
||||||
if *dlgo {
|
if *dlgo {
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
tc.Root = build.DownloadGo(csdb)
|
tc.Root = build.DownloadGo(csdb)
|
||||||
}
|
}
|
||||||
// Disable CLI markdown doc generation in release builds.
|
// Disable CLI markdown doc generation in release builds.
|
||||||
|
|
@ -285,7 +286,7 @@ func doTest(cmdline []string) {
|
||||||
flag.CommandLine.Parse(cmdline)
|
flag.CommandLine.Parse(cmdline)
|
||||||
|
|
||||||
// Get test fixtures.
|
// Get test fixtures.
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
downloadSpecTestFixtures(csdb, *cachedir)
|
downloadSpecTestFixtures(csdb, *cachedir)
|
||||||
|
|
||||||
// Configure the toolchain.
|
// Configure the toolchain.
|
||||||
|
|
@ -329,16 +330,11 @@ func doTest(cmdline []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// downloadSpecTestFixtures downloads and extracts the execution-spec-tests fixtures.
|
// downloadSpecTestFixtures downloads and extracts the execution-spec-tests fixtures.
|
||||||
func downloadSpecTestFixtures(csdb *build.ChecksumDB, cachedir string) string {
|
func downloadSpecTestFixtures(csdb *download.ChecksumDB, cachedir string) string {
|
||||||
executionSpecTestsVersion, err := build.Version(csdb, "spec-tests")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
ext := ".tar.gz"
|
ext := ".tar.gz"
|
||||||
base := "fixtures_pectra-devnet-6" // TODO(s1na) rename once the version becomes part of the filename
|
base := "fixtures_pectra-devnet-6" // TODO(s1na) rename once the version becomes part of the filename
|
||||||
url := fmt.Sprintf("https://github.com/ethereum/execution-spec-tests/releases/download/%s/%s%s", executionSpecTestsVersion, base, ext)
|
|
||||||
archivePath := filepath.Join(cachedir, base+ext)
|
archivePath := filepath.Join(cachedir, base+ext)
|
||||||
if err := csdb.DownloadFile(url, archivePath); err != nil {
|
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := build.ExtractArchive(archivePath, executionSpecTestsDir); err != nil {
|
if err := build.ExtractArchive(archivePath, executionSpecTestsDir); err != nil {
|
||||||
|
|
@ -444,14 +440,13 @@ func doLint(cmdline []string) {
|
||||||
|
|
||||||
// downloadLinter downloads and unpacks golangci-lint.
|
// downloadLinter downloads and unpacks golangci-lint.
|
||||||
func downloadLinter(cachedir string) string {
|
func downloadLinter(cachedir string) string {
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
version, err := build.Version(csdb, "golangci")
|
version, err := csdb.FindVersion("golangci")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
arch := runtime.GOARCH
|
arch := runtime.GOARCH
|
||||||
ext := ".tar.gz"
|
ext := ".tar.gz"
|
||||||
|
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
ext = ".zip"
|
ext = ".zip"
|
||||||
}
|
}
|
||||||
|
|
@ -459,9 +454,8 @@ func downloadLinter(cachedir string) string {
|
||||||
arch += "v" + os.Getenv("GOARM")
|
arch += "v" + os.Getenv("GOARM")
|
||||||
}
|
}
|
||||||
base := fmt.Sprintf("golangci-lint-%s-%s-%s", version, runtime.GOOS, arch)
|
base := fmt.Sprintf("golangci-lint-%s-%s-%s", version, runtime.GOOS, arch)
|
||||||
url := fmt.Sprintf("https://github.com/golangci/golangci-lint/releases/download/v%s/%s%s", version, base, ext)
|
|
||||||
archivePath := filepath.Join(cachedir, base+ext)
|
archivePath := filepath.Join(cachedir, base+ext)
|
||||||
if err := csdb.DownloadFile(url, archivePath); err != nil {
|
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := build.ExtractArchive(archivePath, cachedir); err != nil {
|
if err := build.ExtractArchive(archivePath, cachedir); err != nil {
|
||||||
|
|
@ -497,8 +491,8 @@ func protocArchiveBaseName() (string, error) {
|
||||||
// in the generate command. It returns the full path of the directory
|
// in the generate command. It returns the full path of the directory
|
||||||
// containing the 'protoc-gen-go' executable.
|
// containing the 'protoc-gen-go' executable.
|
||||||
func downloadProtocGenGo(cachedir string) string {
|
func downloadProtocGenGo(cachedir string) string {
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
version, err := build.Version(csdb, "protoc-gen-go")
|
version, err := csdb.FindVersion("protoc-gen-go")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -510,10 +504,8 @@ func downloadProtocGenGo(cachedir string) string {
|
||||||
archiveName += ".tar.gz"
|
archiveName += ".tar.gz"
|
||||||
}
|
}
|
||||||
|
|
||||||
url := fmt.Sprintf("https://github.com/protocolbuffers/protobuf-go/releases/download/v%s/%s", version, archiveName)
|
|
||||||
|
|
||||||
archivePath := path.Join(cachedir, archiveName)
|
archivePath := path.Join(cachedir, archiveName)
|
||||||
if err := csdb.DownloadFile(url, archivePath); err != nil {
|
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
extractDest := filepath.Join(cachedir, baseName)
|
extractDest := filepath.Join(cachedir, baseName)
|
||||||
|
|
@ -531,8 +523,8 @@ func downloadProtocGenGo(cachedir string) string {
|
||||||
// files as a CI step. It returns the full path to the directory containing
|
// files as a CI step. It returns the full path to the directory containing
|
||||||
// the protoc executable.
|
// the protoc executable.
|
||||||
func downloadProtoc(cachedir string) string {
|
func downloadProtoc(cachedir string) string {
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
version, err := build.Version(csdb, "protoc")
|
version, err := csdb.FindVersion("protoc")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -543,10 +535,8 @@ func downloadProtoc(cachedir string) string {
|
||||||
|
|
||||||
fileName := fmt.Sprintf("protoc-%s-%s", version, baseName)
|
fileName := fmt.Sprintf("protoc-%s-%s", version, baseName)
|
||||||
archiveFileName := fileName + ".zip"
|
archiveFileName := fileName + ".zip"
|
||||||
url := fmt.Sprintf("https://github.com/protocolbuffers/protobuf/releases/download/v%s/%s", version, archiveFileName)
|
|
||||||
archivePath := filepath.Join(cachedir, archiveFileName)
|
archivePath := filepath.Join(cachedir, archiveFileName)
|
||||||
|
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
|
||||||
if err := csdb.DownloadFile(url, archivePath); err != nil {
|
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
extractDest := filepath.Join(cachedir, fileName)
|
extractDest := filepath.Join(cachedir, fileName)
|
||||||
|
|
@ -826,18 +816,17 @@ func doDebianSource(cmdline []string) {
|
||||||
// downloadGoBootstrapSources downloads the Go source tarball(s) that will be used
|
// downloadGoBootstrapSources downloads the Go source tarball(s) that will be used
|
||||||
// to bootstrap the builder Go.
|
// to bootstrap the builder Go.
|
||||||
func downloadGoBootstrapSources(cachedir string) []string {
|
func downloadGoBootstrapSources(cachedir string) []string {
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
|
|
||||||
var bundles []string
|
var bundles []string
|
||||||
for _, booter := range []string{"ppa-builder-1.19", "ppa-builder-1.21", "ppa-builder-1.23"} {
|
for _, booter := range []string{"ppa-builder-1.19", "ppa-builder-1.21", "ppa-builder-1.23"} {
|
||||||
gobootVersion, err := build.Version(csdb, booter)
|
gobootVersion, err := csdb.FindVersion(booter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
file := fmt.Sprintf("go%s.src.tar.gz", gobootVersion)
|
file := fmt.Sprintf("go%s.src.tar.gz", gobootVersion)
|
||||||
url := "https://dl.google.com/go/" + file
|
|
||||||
dst := filepath.Join(cachedir, file)
|
dst := filepath.Join(cachedir, file)
|
||||||
if err := csdb.DownloadFile(url, dst); err != nil {
|
if err := csdb.DownloadFileFromKnownURL(dst); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
bundles = append(bundles, dst)
|
bundles = append(bundles, dst)
|
||||||
|
|
@ -847,15 +836,14 @@ func downloadGoBootstrapSources(cachedir string) []string {
|
||||||
|
|
||||||
// downloadGoSources downloads the Go source tarball.
|
// downloadGoSources downloads the Go source tarball.
|
||||||
func downloadGoSources(cachedir string) string {
|
func downloadGoSources(cachedir string) string {
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
dlgoVersion, err := build.Version(csdb, "golang")
|
dlgoVersion, err := csdb.FindVersion("golang")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
file := fmt.Sprintf("go%s.src.tar.gz", dlgoVersion)
|
file := fmt.Sprintf("go%s.src.tar.gz", dlgoVersion)
|
||||||
url := "https://dl.google.com/go/" + file
|
|
||||||
dst := filepath.Join(cachedir, file)
|
dst := filepath.Join(cachedir, file)
|
||||||
if err := csdb.DownloadFile(url, dst); err != nil {
|
if err := csdb.DownloadFileFromKnownURL(dst); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
return dst
|
return dst
|
||||||
|
|
@ -1181,5 +1169,6 @@ func doPurge(cmdline []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func doSanityCheck() {
|
func doSanityCheck() {
|
||||||
build.DownloadAndVerifyChecksums(build.MustLoadChecksums("build/checksums.txt"))
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
|
csdb.DownloadAndVerifyAll()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,12 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -39,6 +42,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/internal/debug"
|
"github.com/ethereum/go-ethereum/internal/debug"
|
||||||
"github.com/ethereum/go-ethereum/internal/era"
|
"github.com/ethereum/go-ethereum/internal/era"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/era/eradl"
|
||||||
"github.com/ethereum/go-ethereum/internal/flags"
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
@ -190,7 +194,7 @@ This command dumps out the state for a given block (or latest, if none provided)
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
pruneCommand = &cli.Command{
|
pruneHistoryCommand = &cli.Command{
|
||||||
Action: pruneHistory,
|
Action: pruneHistory,
|
||||||
Name: "prune-history",
|
Name: "prune-history",
|
||||||
Usage: "Prune blockchain history (block bodies and receipts) up to the merge block",
|
Usage: "Prune blockchain history (block bodies and receipts) up to the merge block",
|
||||||
|
|
@ -201,6 +205,42 @@ The prune-history command removes historical block bodies and receipts from the
|
||||||
blockchain database up to the merge block, while preserving block headers. This
|
blockchain database up to the merge block, while preserving block headers. This
|
||||||
helps reduce storage requirements for nodes that don't need full historical data.`,
|
helps reduce storage requirements for nodes that don't need full historical data.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
downloadEraCommand = &cli.Command{
|
||||||
|
Action: downloadEra,
|
||||||
|
Name: "download-era",
|
||||||
|
Usage: "Fetches era1 files (pre-merge history) from an HTTP endpoint",
|
||||||
|
ArgsUsage: "",
|
||||||
|
Flags: slices.Concat(
|
||||||
|
utils.DatabaseFlags,
|
||||||
|
utils.NetworkFlags,
|
||||||
|
[]cli.Flag{
|
||||||
|
eraBlockFlag,
|
||||||
|
eraEpochFlag,
|
||||||
|
eraAllFlag,
|
||||||
|
eraServerFlag,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
eraBlockFlag = &cli.StringFlag{
|
||||||
|
Name: "block",
|
||||||
|
Usage: "Block number to fetch. (can also be a range <start>-<end>)",
|
||||||
|
}
|
||||||
|
eraEpochFlag = &cli.StringFlag{
|
||||||
|
Name: "epoch",
|
||||||
|
Usage: "Epoch number to fetch (can also be a range <start>-<end>)",
|
||||||
|
}
|
||||||
|
eraAllFlag = &cli.BoolFlag{
|
||||||
|
Name: "all",
|
||||||
|
Usage: "Download all available era1 files",
|
||||||
|
}
|
||||||
|
eraServerFlag = &cli.StringFlag{
|
||||||
|
Name: "server",
|
||||||
|
Usage: "era1 server URL",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// initGenesis will initialise the given JSON format genesis file and writes it as
|
// initGenesis will initialise the given JSON format genesis file and writes it as
|
||||||
|
|
@ -665,3 +705,83 @@ func pruneHistory(ctx *cli.Context) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// downladEra is the era1 file downloader tool.
|
||||||
|
func downloadEra(ctx *cli.Context) error {
|
||||||
|
flags.CheckExclusive(ctx, eraBlockFlag, eraEpochFlag, eraAllFlag)
|
||||||
|
|
||||||
|
// Resolve the network.
|
||||||
|
var network = "mainnet"
|
||||||
|
if utils.IsNetworkPreset(ctx) {
|
||||||
|
switch {
|
||||||
|
case ctx.IsSet(utils.MainnetFlag.Name):
|
||||||
|
case ctx.IsSet(utils.SepoliaFlag.Name):
|
||||||
|
network = "sepolia"
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported network, no known era1 checksums")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the destination directory.
|
||||||
|
stack, _ := makeConfigNode(ctx)
|
||||||
|
defer stack.Close()
|
||||||
|
ancients := stack.ResolveAncient("chaindata", "")
|
||||||
|
dir := filepath.Join(ancients, "era")
|
||||||
|
|
||||||
|
baseURL := ctx.String(eraServerFlag.Name)
|
||||||
|
if baseURL == "" {
|
||||||
|
return fmt.Errorf("need --%s flag to download", eraServerFlag.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
l, err := eradl.New(baseURL, network)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case ctx.IsSet(eraAllFlag.Name):
|
||||||
|
return l.DownloadAll(dir)
|
||||||
|
|
||||||
|
case ctx.IsSet(eraBlockFlag.Name):
|
||||||
|
s := ctx.String(eraBlockFlag.Name)
|
||||||
|
start, end, ok := parseRange(s)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid block range: %q", s)
|
||||||
|
}
|
||||||
|
return l.DownloadBlockRange(start, end, dir)
|
||||||
|
|
||||||
|
case ctx.IsSet(eraEpochFlag.Name):
|
||||||
|
s := ctx.String(eraEpochFlag.Name)
|
||||||
|
start, end, ok := parseRange(s)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid epoch range: %q", s)
|
||||||
|
}
|
||||||
|
return l.DownloadEpochRange(start, end, dir)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("specify one of --%s, --%s, or --%s to download", eraAllFlag.Name, eraBlockFlag.Name, eraEpochFlag.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRange(s string) (start uint64, end uint64, ok bool) {
|
||||||
|
if m, _ := regexp.MatchString("[0-9]+", s); m {
|
||||||
|
start, err := strconv.ParseUint(s, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
end = start
|
||||||
|
return start, end, true
|
||||||
|
}
|
||||||
|
if m, _ := regexp.MatchString("[0-9]+-[0-9]+", s); m {
|
||||||
|
s1, s2, _ := strings.Cut(s, "-")
|
||||||
|
start, err := strconv.ParseUint(s1, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
end, err = strconv.ParseUint(s2, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
return start, end, true
|
||||||
|
}
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -225,7 +225,8 @@ func init() {
|
||||||
removedbCommand,
|
removedbCommand,
|
||||||
dumpCommand,
|
dumpCommand,
|
||||||
dumpGenesisCommand,
|
dumpGenesisCommand,
|
||||||
pruneCommand,
|
pruneHistoryCommand,
|
||||||
|
downloadEraCommand,
|
||||||
// See accountcmd.go:
|
// See accountcmd.go:
|
||||||
accountCommand,
|
accountCommand,
|
||||||
walletCommand,
|
walletCommand,
|
||||||
|
|
|
||||||
|
|
@ -1712,7 +1712,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ctx.IsSet(VMEnableDebugFlag.Name) {
|
if ctx.IsSet(VMEnableDebugFlag.Name) {
|
||||||
// TODO(fjl): force-enable this in --dev mode
|
|
||||||
cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name)
|
cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1761,6 +1760,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
case ctx.Bool(DeveloperFlag.Name):
|
case ctx.Bool(DeveloperFlag.Name):
|
||||||
cfg.NetworkId = 1337
|
cfg.NetworkId = 1337
|
||||||
cfg.SyncMode = ethconfig.FullSync
|
cfg.SyncMode = ethconfig.FullSync
|
||||||
|
cfg.EnablePreimageRecording = true
|
||||||
// Create new developer account or reuse existing one
|
// Create new developer account or reuse existing one
|
||||||
var (
|
var (
|
||||||
developer accounts.Account
|
developer accounts.Account
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"maps"
|
"maps"
|
||||||
|
gomath "math"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
|
@ -92,97 +93,94 @@ func (ae *AccessEvents) Copy() *AccessEvents {
|
||||||
|
|
||||||
// AddAccount returns the gas to be charged for each of the currently cold
|
// AddAccount returns the gas to be charged for each of the currently cold
|
||||||
// member fields of an account.
|
// member fields of an account.
|
||||||
func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool) uint64 {
|
func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool, availableGas uint64) uint64 {
|
||||||
var gas uint64
|
var gas uint64 // accumulate the consumed gas
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite)
|
consumed, expected := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite, availableGas)
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite)
|
if consumed < expected {
|
||||||
|
return expected
|
||||||
|
}
|
||||||
|
gas += consumed
|
||||||
|
consumed, expected = ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite, availableGas-consumed)
|
||||||
|
if consumed < expected {
|
||||||
|
return expected + gas
|
||||||
|
}
|
||||||
|
gas += expected
|
||||||
return gas
|
return gas
|
||||||
}
|
}
|
||||||
|
|
||||||
// MessageCallGas returns the gas to be charged for each of the currently
|
// MessageCallGas returns the gas to be charged for each of the currently
|
||||||
// cold member fields of an account, that need to be touched when making a message
|
// cold member fields of an account, that need to be touched when making a message
|
||||||
// call to that account.
|
// call to that account.
|
||||||
func (ae *AccessEvents) MessageCallGas(destination common.Address) uint64 {
|
func (ae *AccessEvents) MessageCallGas(destination common.Address, availableGas uint64) uint64 {
|
||||||
var gas uint64
|
_, expected := ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.BasicDataLeafKey, false, availableGas)
|
||||||
gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.BasicDataLeafKey, false)
|
if expected == 0 {
|
||||||
return gas
|
expected = params.WarmStorageReadCostEIP2929
|
||||||
|
}
|
||||||
|
return expected
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValueTransferGas returns the gas to be charged for each of the currently
|
// ValueTransferGas returns the gas to be charged for each of the currently
|
||||||
// cold balance member fields of the caller and the callee accounts.
|
// cold balance member fields of the caller and the callee accounts.
|
||||||
func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address) uint64 {
|
func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address, availableGas uint64) uint64 {
|
||||||
var gas uint64
|
_, expected1 := ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, availableGas)
|
||||||
gas += ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
if expected1 > availableGas {
|
||||||
gas += ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
return expected1
|
||||||
return gas
|
}
|
||||||
|
_, expected2 := ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, availableGas-expected1)
|
||||||
|
if expected1+expected2 == 0 {
|
||||||
|
return params.WarmStorageReadCostEIP2929
|
||||||
|
}
|
||||||
|
return expected1 + expected2
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContractCreatePreCheckGas charges access costs before
|
// ContractCreatePreCheckGas charges access costs before
|
||||||
// a contract creation is initiated. It is just reads, because the
|
// a contract creation is initiated. It is just reads, because the
|
||||||
// address collision is done before the transfer, and so no write
|
// address collision is done before the transfer, and so no write
|
||||||
// are guaranteed to happen at this point.
|
// are guaranteed to happen at this point.
|
||||||
func (ae *AccessEvents) ContractCreatePreCheckGas(addr common.Address) uint64 {
|
func (ae *AccessEvents) ContractCreatePreCheckGas(addr common.Address, availableGas uint64) uint64 {
|
||||||
var gas uint64
|
consumed, expected1 := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false, availableGas)
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false)
|
_, expected2 := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false, availableGas-consumed)
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false)
|
return expected1 + expected2
|
||||||
return gas
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContractCreateInitGas returns the access gas costs for the initialization of
|
// ContractCreateInitGas returns the access gas costs for the initialization of
|
||||||
// a contract creation.
|
// a contract creation.
|
||||||
func (ae *AccessEvents) ContractCreateInitGas(addr common.Address) uint64 {
|
func (ae *AccessEvents) ContractCreateInitGas(addr common.Address, availableGas uint64) (uint64, uint64) {
|
||||||
var gas uint64
|
var gas uint64
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
consumed, expected1 := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true, availableGas)
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, true)
|
gas += consumed
|
||||||
return gas
|
consumed, expected2 := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, true, availableGas-consumed)
|
||||||
|
gas += consumed
|
||||||
|
return gas, expected1 + expected2
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTxOrigin adds the member fields of the sender account to the access event list,
|
// AddTxOrigin adds the member fields of the sender account to the access event list,
|
||||||
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
||||||
func (ae *AccessEvents) AddTxOrigin(originAddr common.Address) {
|
func (ae *AccessEvents) AddTxOrigin(originAddr common.Address) {
|
||||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, gomath.MaxUint64)
|
||||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeHashLeafKey, false)
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeHashLeafKey, false, gomath.MaxUint64)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTxDestination adds the member fields of the sender account to the access event list,
|
// AddTxDestination adds the member fields of the sender account to the access event list,
|
||||||
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
||||||
func (ae *AccessEvents) AddTxDestination(addr common.Address, sendsValue bool) {
|
func (ae *AccessEvents) AddTxDestination(addr common.Address, sendsValue, doesntExist bool) {
|
||||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue)
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue, gomath.MaxUint64)
|
||||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false)
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, doesntExist, gomath.MaxUint64)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SlotGas returns the amount of gas to be charged for a cold storage access.
|
// SlotGas returns the amount of gas to be charged for a cold storage access.
|
||||||
func (ae *AccessEvents) SlotGas(addr common.Address, slot common.Hash, isWrite bool) uint64 {
|
func (ae *AccessEvents) SlotGas(addr common.Address, slot common.Hash, isWrite bool, availableGas uint64, chargeWarmCosts bool) uint64 {
|
||||||
treeIndex, subIndex := utils.StorageIndex(slot.Bytes())
|
treeIndex, subIndex := utils.StorageIndex(slot.Bytes())
|
||||||
return ae.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite)
|
_, expected := ae.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite, availableGas)
|
||||||
|
if expected == 0 && chargeWarmCosts {
|
||||||
|
expected = params.WarmStorageReadCostEIP2929
|
||||||
|
}
|
||||||
|
return expected
|
||||||
}
|
}
|
||||||
|
|
||||||
// touchAddressAndChargeGas adds any missing access event to the access event list, and returns the cold
|
// touchAddressAndChargeGas adds any missing access event to the access event list, and returns the
|
||||||
// access cost to be charged, if need be.
|
// consumed and required gas.
|
||||||
func (ae *AccessEvents) touchAddressAndChargeGas(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool) uint64 {
|
func (ae *AccessEvents) touchAddressAndChargeGas(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool, availableGas uint64) (uint64, uint64) {
|
||||||
stemRead, selectorRead, stemWrite, selectorWrite, selectorFill := ae.touchAddress(addr, treeIndex, subIndex, isWrite)
|
|
||||||
|
|
||||||
var gas uint64
|
|
||||||
if stemRead {
|
|
||||||
gas += params.WitnessBranchReadCost
|
|
||||||
}
|
|
||||||
if selectorRead {
|
|
||||||
gas += params.WitnessChunkReadCost
|
|
||||||
}
|
|
||||||
if stemWrite {
|
|
||||||
gas += params.WitnessBranchWriteCost
|
|
||||||
}
|
|
||||||
if selectorWrite {
|
|
||||||
gas += params.WitnessChunkWriteCost
|
|
||||||
}
|
|
||||||
if selectorFill {
|
|
||||||
gas += params.WitnessChunkFillCost
|
|
||||||
}
|
|
||||||
return gas
|
|
||||||
}
|
|
||||||
|
|
||||||
// touchAddress adds any missing access event to the access event list.
|
|
||||||
func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool) (bool, bool, bool, bool, bool) {
|
|
||||||
branchKey := newBranchAccessKey(addr, treeIndex)
|
branchKey := newBranchAccessKey(addr, treeIndex)
|
||||||
chunkKey := newChunkAccessKey(branchKey, subIndex)
|
chunkKey := newChunkAccessKey(branchKey, subIndex)
|
||||||
|
|
||||||
|
|
@ -190,11 +188,9 @@ func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int,
|
||||||
var branchRead, chunkRead bool
|
var branchRead, chunkRead bool
|
||||||
if _, hasStem := ae.branches[branchKey]; !hasStem {
|
if _, hasStem := ae.branches[branchKey]; !hasStem {
|
||||||
branchRead = true
|
branchRead = true
|
||||||
ae.branches[branchKey] = AccessWitnessReadFlag
|
|
||||||
}
|
}
|
||||||
if _, hasSelector := ae.chunks[chunkKey]; !hasSelector {
|
if _, hasSelector := ae.chunks[chunkKey]; !hasSelector {
|
||||||
chunkRead = true
|
chunkRead = true
|
||||||
ae.chunks[chunkKey] = AccessWitnessReadFlag
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write access.
|
// Write access.
|
||||||
|
|
@ -202,17 +198,51 @@ func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int,
|
||||||
if isWrite {
|
if isWrite {
|
||||||
if (ae.branches[branchKey] & AccessWitnessWriteFlag) == 0 {
|
if (ae.branches[branchKey] & AccessWitnessWriteFlag) == 0 {
|
||||||
branchWrite = true
|
branchWrite = true
|
||||||
ae.branches[branchKey] |= AccessWitnessWriteFlag
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chunkValue := ae.chunks[chunkKey]
|
chunkValue := ae.chunks[chunkKey]
|
||||||
if (chunkValue & AccessWitnessWriteFlag) == 0 {
|
if (chunkValue & AccessWitnessWriteFlag) == 0 {
|
||||||
chunkWrite = true
|
chunkWrite = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var gas uint64
|
||||||
|
if branchRead {
|
||||||
|
gas += params.WitnessBranchReadCost
|
||||||
|
}
|
||||||
|
if chunkRead {
|
||||||
|
gas += params.WitnessChunkReadCost
|
||||||
|
}
|
||||||
|
if branchWrite {
|
||||||
|
gas += params.WitnessBranchWriteCost
|
||||||
|
}
|
||||||
|
if chunkWrite {
|
||||||
|
gas += params.WitnessChunkWriteCost
|
||||||
|
}
|
||||||
|
if chunkFill {
|
||||||
|
gas += params.WitnessChunkFillCost
|
||||||
|
}
|
||||||
|
|
||||||
|
if availableGas < gas {
|
||||||
|
// consumed != expected
|
||||||
|
return availableGas, gas
|
||||||
|
}
|
||||||
|
|
||||||
|
if branchRead {
|
||||||
|
ae.branches[branchKey] = AccessWitnessReadFlag
|
||||||
|
}
|
||||||
|
if branchWrite {
|
||||||
|
ae.branches[branchKey] |= AccessWitnessWriteFlag
|
||||||
|
}
|
||||||
|
if chunkRead {
|
||||||
|
ae.chunks[chunkKey] = AccessWitnessReadFlag
|
||||||
|
}
|
||||||
|
if chunkWrite {
|
||||||
ae.chunks[chunkKey] |= AccessWitnessWriteFlag
|
ae.chunks[chunkKey] |= AccessWitnessWriteFlag
|
||||||
}
|
}
|
||||||
// TODO: charge chunk filling costs if the leaf was previously empty in the state
|
|
||||||
}
|
// consumed == expected
|
||||||
return branchRead, chunkRead, branchWrite, chunkWrite, chunkFill
|
return gas, gas
|
||||||
}
|
}
|
||||||
|
|
||||||
type branchAccessKey struct {
|
type branchAccessKey struct {
|
||||||
|
|
@ -240,7 +270,7 @@ func newChunkAccessKey(branchKey branchAccessKey, leafKey byte) chunkAccessKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CodeChunksRangeGas is a helper function to touch every chunk in a code range and charge witness gas costs
|
// CodeChunksRangeGas is a helper function to touch every chunk in a code range and charge witness gas costs
|
||||||
func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC, size uint64, codeLen uint64, isWrite bool) uint64 {
|
func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC, size uint64, codeLen uint64, isWrite bool, availableGas uint64) (uint64, uint64) {
|
||||||
// note that in the case where the copied code is outside the range of the
|
// note that in the case where the copied code is outside the range of the
|
||||||
// contract code but touches the last leaf with contract code in it,
|
// contract code but touches the last leaf with contract code in it,
|
||||||
// we don't include the last leaf of code in the AccessWitness. The
|
// we don't include the last leaf of code in the AccessWitness. The
|
||||||
|
|
@ -248,7 +278,7 @@ func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC,
|
||||||
// is already in the AccessWitness so a stateless verifier can see that
|
// is already in the AccessWitness so a stateless verifier can see that
|
||||||
// the code from the last leaf is not needed.
|
// the code from the last leaf is not needed.
|
||||||
if (codeLen == 0 && size == 0) || startPC > codeLen {
|
if (codeLen == 0 && size == 0) || startPC > codeLen {
|
||||||
return 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
endPC := startPC + size
|
endPC := startPC + size
|
||||||
|
|
@ -263,22 +293,34 @@ func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC,
|
||||||
for chunkNumber := startPC / 31; chunkNumber <= endPC/31; chunkNumber++ {
|
for chunkNumber := startPC / 31; chunkNumber <= endPC/31; chunkNumber++ {
|
||||||
treeIndex := *uint256.NewInt((chunkNumber + 128) / 256)
|
treeIndex := *uint256.NewInt((chunkNumber + 128) / 256)
|
||||||
subIndex := byte((chunkNumber + 128) % 256)
|
subIndex := byte((chunkNumber + 128) % 256)
|
||||||
gas := ae.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite)
|
consumed, expected := ae.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite, availableGas)
|
||||||
|
// did we OOG ?
|
||||||
|
if expected > consumed {
|
||||||
|
return statelessGasCharged + consumed, statelessGasCharged + expected
|
||||||
|
}
|
||||||
var overflow bool
|
var overflow bool
|
||||||
statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, gas)
|
statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, consumed)
|
||||||
if overflow {
|
if overflow {
|
||||||
panic("overflow when adding gas")
|
panic("overflow when adding gas")
|
||||||
}
|
}
|
||||||
|
availableGas -= consumed
|
||||||
}
|
}
|
||||||
return statelessGasCharged
|
return statelessGasCharged, statelessGasCharged
|
||||||
}
|
}
|
||||||
|
|
||||||
// BasicDataGas adds the account's basic data to the accessed data, and returns the
|
// BasicDataGas adds the account's basic data to the accessed data, and returns the
|
||||||
// amount of gas that it costs.
|
// amount of gas that it costs.
|
||||||
// Note that an access in write mode implies an access in read mode, whereas an
|
// Note that an access in write mode implies an access in read mode, whereas an
|
||||||
// access in read mode does not imply an access in write mode.
|
// access in read mode does not imply an access in write mode.
|
||||||
func (ae *AccessEvents) BasicDataGas(addr common.Address, isWrite bool) uint64 {
|
func (ae *AccessEvents) BasicDataGas(addr common.Address, isWrite bool, availableGas uint64, chargeWarmCosts bool) uint64 {
|
||||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite)
|
_, expected := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite, availableGas)
|
||||||
|
if expected == 0 && chargeWarmCosts {
|
||||||
|
if availableGas < params.WarmStorageReadCostEIP2929 {
|
||||||
|
return availableGas
|
||||||
|
}
|
||||||
|
expected = params.WarmStorageReadCostEIP2929
|
||||||
|
}
|
||||||
|
return expected
|
||||||
}
|
}
|
||||||
|
|
||||||
// CodeHashGas adds the account's code hash to the accessed data, and returns the
|
// CodeHashGas adds the account's code hash to the accessed data, and returns the
|
||||||
|
|
@ -286,6 +328,13 @@ func (ae *AccessEvents) BasicDataGas(addr common.Address, isWrite bool) uint64 {
|
||||||
// in write mode. If false, the charged gas corresponds to an access in read mode.
|
// in write mode. If false, the charged gas corresponds to an access in read mode.
|
||||||
// Note that an access in write mode implies an access in read mode, whereas an access in
|
// Note that an access in write mode implies an access in read mode, whereas an access in
|
||||||
// read mode does not imply an access in write mode.
|
// read mode does not imply an access in write mode.
|
||||||
func (ae *AccessEvents) CodeHashGas(addr common.Address, isWrite bool) uint64 {
|
func (ae *AccessEvents) CodeHashGas(addr common.Address, isWrite bool, availableGas uint64, chargeWarmCosts bool) uint64 {
|
||||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite)
|
_, expected := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite, availableGas)
|
||||||
|
if expected == 0 && chargeWarmCosts {
|
||||||
|
if availableGas < params.WarmStorageReadCostEIP2929 {
|
||||||
|
return availableGas
|
||||||
|
}
|
||||||
|
expected = params.WarmStorageReadCostEIP2929
|
||||||
|
}
|
||||||
|
return expected
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -40,50 +41,50 @@ func TestAccountHeaderGas(t *testing.T) {
|
||||||
ae := NewAccessEvents(utils.NewPointCache(1024))
|
ae := NewAccessEvents(utils.NewPointCache(1024))
|
||||||
|
|
||||||
// Check cold read cost
|
// Check cold read cost
|
||||||
gas := ae.BasicDataGas(testAddr, false)
|
gas := ae.BasicDataGas(testAddr, false, math.MaxUint64, false)
|
||||||
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want {
|
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check warm read cost
|
// Check warm read cost
|
||||||
gas = ae.BasicDataGas(testAddr, false)
|
gas = ae.BasicDataGas(testAddr, false, math.MaxUint64, false)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cold read costs in the same group no longer incur the branch read cost
|
// Check cold read costs in the same group no longer incur the branch read cost
|
||||||
gas = ae.CodeHashGas(testAddr, false)
|
gas = ae.CodeHashGas(testAddr, false, math.MaxUint64, false)
|
||||||
if gas != params.WitnessChunkReadCost {
|
if gas != params.WitnessChunkReadCost {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cold write cost
|
// Check cold write cost
|
||||||
gas = ae.BasicDataGas(testAddr, true)
|
gas = ae.BasicDataGas(testAddr, true, math.MaxUint64, false)
|
||||||
if want := params.WitnessBranchWriteCost + params.WitnessChunkWriteCost; gas != want {
|
if want := params.WitnessBranchWriteCost + params.WitnessChunkWriteCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check warm write cost
|
// Check warm write cost
|
||||||
gas = ae.BasicDataGas(testAddr, true)
|
gas = ae.BasicDataGas(testAddr, true, math.MaxUint64, false)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check a write without a read charges both read and write costs
|
// Check a write without a read charges both read and write costs
|
||||||
gas = ae.BasicDataGas(testAddr2, true)
|
gas = ae.BasicDataGas(testAddr2, true, math.MaxUint64, false)
|
||||||
if want := params.WitnessBranchReadCost + params.WitnessBranchWriteCost + params.WitnessChunkWriteCost + params.WitnessChunkReadCost; gas != want {
|
if want := params.WitnessBranchReadCost + params.WitnessBranchWriteCost + params.WitnessChunkWriteCost + params.WitnessChunkReadCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that a write followed by a read charges nothing
|
// Check that a write followed by a read charges nothing
|
||||||
gas = ae.BasicDataGas(testAddr2, false)
|
gas = ae.BasicDataGas(testAddr2, false, math.MaxUint64, false)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that reading a slot from the account header only charges the
|
// Check that reading a slot from the account header only charges the
|
||||||
// chunk read cost.
|
// chunk read cost.
|
||||||
gas = ae.SlotGas(testAddr, common.Hash{}, false)
|
gas = ae.SlotGas(testAddr, common.Hash{}, false, math.MaxUint64, false)
|
||||||
if gas != params.WitnessChunkReadCost {
|
if gas != params.WitnessChunkReadCost {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
||||||
}
|
}
|
||||||
|
|
@ -100,13 +101,13 @@ func TestContractCreateInitGas(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cold read cost, without a value
|
// Check cold read cost, without a value
|
||||||
gas := ae.ContractCreateInitGas(testAddr)
|
gas, _ := ae.ContractCreateInitGas(testAddr, math.MaxUint64)
|
||||||
if want := params.WitnessBranchWriteCost + params.WitnessBranchReadCost + 2*params.WitnessChunkWriteCost + 2*params.WitnessChunkReadCost; gas != want {
|
if want := params.WitnessBranchWriteCost + params.WitnessBranchReadCost + 2*params.WitnessChunkWriteCost + 2*params.WitnessChunkReadCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check warm read cost
|
// Check warm read cost
|
||||||
gas = ae.ContractCreateInitGas(testAddr)
|
gas, _ = ae.ContractCreateInitGas(testAddr, math.MaxUint64)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
@ -118,24 +119,24 @@ func TestMessageCallGas(t *testing.T) {
|
||||||
ae := NewAccessEvents(utils.NewPointCache(1024))
|
ae := NewAccessEvents(utils.NewPointCache(1024))
|
||||||
|
|
||||||
// Check cold read cost, without a value
|
// Check cold read cost, without a value
|
||||||
gas := ae.MessageCallGas(testAddr)
|
gas := ae.MessageCallGas(testAddr, math.MaxUint64)
|
||||||
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want {
|
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that reading the basic data and code hash of the same account does not incur the branch read cost
|
// Check that reading the basic data and code hash of the same account does not incur the branch read cost
|
||||||
gas = ae.BasicDataGas(testAddr, false)
|
gas = ae.BasicDataGas(testAddr, false, math.MaxUint64, false)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
gas = ae.CodeHashGas(testAddr, false)
|
gas = ae.CodeHashGas(testAddr, false, math.MaxUint64, false)
|
||||||
if gas != params.WitnessChunkReadCost {
|
if gas != params.WitnessChunkReadCost {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check warm read cost
|
// Check warm read cost
|
||||||
gas = ae.MessageCallGas(testAddr)
|
gas = ae.MessageCallGas(testAddr, math.MaxUint64)
|
||||||
if gas != 0 {
|
if gas != params.WarmStorageReadCostEIP2929 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WarmStorageReadCostEIP2929)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -457,7 +457,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
st.evm.AccessEvents.AddTxOrigin(msg.From)
|
st.evm.AccessEvents.AddTxOrigin(msg.From)
|
||||||
|
|
||||||
if targetAddr := msg.To; targetAddr != nil {
|
if targetAddr := msg.To; targetAddr != nil {
|
||||||
st.evm.AccessEvents.AddTxDestination(*targetAddr, msg.Value.Sign() != 0)
|
st.evm.AccessEvents.AddTxDestination(*targetAddr, msg.Value.Sign() != 0, !st.state.Exist(*targetAddr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -552,7 +552,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
|
|
||||||
// add the coinbase to the witness iff the fee is greater than 0
|
// add the coinbase to the witness iff the fee is greater than 0
|
||||||
if rules.IsEIP4762 && fee.Sign() != 0 {
|
if rules.IsEIP4762 && fee.Sign() != 0 {
|
||||||
st.evm.AccessEvents.AddAccount(st.evm.Context.Coinbase, true)
|
st.evm.AccessEvents.AddAccount(st.evm.Context.Coinbase, true, math.MaxUint64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ var PrecompiledContractsPrague = PrecompiledContracts{
|
||||||
|
|
||||||
var PrecompiledContractsBLS = PrecompiledContractsPrague
|
var PrecompiledContractsBLS = PrecompiledContractsPrague
|
||||||
|
|
||||||
var PrecompiledContractsVerkle = PrecompiledContractsPrague
|
var PrecompiledContractsVerkle = PrecompiledContractsBerlin
|
||||||
|
|
||||||
var (
|
var (
|
||||||
PrecompiledAddressesPrague []common.Address
|
PrecompiledAddressesPrague []common.Address
|
||||||
|
|
|
||||||
|
|
@ -339,13 +339,11 @@ func opExtCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeC
|
||||||
addr := common.Address(a.Bytes20())
|
addr := common.Address(a.Bytes20())
|
||||||
code := interpreter.evm.StateDB.GetCode(addr)
|
code := interpreter.evm.StateDB.GetCode(addr)
|
||||||
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
|
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
|
||||||
if !scope.Contract.IsSystemCall {
|
consumed, wanted := interpreter.evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false, scope.Contract.Gas)
|
||||||
statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false)
|
scope.Contract.UseGas(consumed, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||||
if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) {
|
if consumed < wanted {
|
||||||
scope.Contract.Gas = 0
|
|
||||||
return nil, ErrOutOfGas
|
return nil, ErrOutOfGas
|
||||||
}
|
}
|
||||||
}
|
|
||||||
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), paddedCodeCopy)
|
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), paddedCodeCopy)
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
@ -367,9 +365,9 @@ func opPush1EIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
|
||||||
// touch next chunk if PUSH1 is at the boundary. if so, *pc has
|
// touch next chunk if PUSH1 is at the boundary. if so, *pc has
|
||||||
// advanced past this boundary.
|
// advanced past this boundary.
|
||||||
contractAddr := scope.Contract.Address()
|
contractAddr := scope.Contract.Address()
|
||||||
statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false)
|
consumed, wanted := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas)
|
||||||
if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) {
|
scope.Contract.UseGas(wanted, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||||
scope.Contract.Gas = 0
|
if consumed < wanted {
|
||||||
return nil, ErrOutOfGas
|
return nil, ErrOutOfGas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -395,9 +393,9 @@ func makePushEIP4762(size uint64, pushByteSize int) executionFunc {
|
||||||
|
|
||||||
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall {
|
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall {
|
||||||
contractAddr := scope.Contract.Address()
|
contractAddr := scope.Contract.Address()
|
||||||
statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false)
|
consumed, wanted := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas)
|
||||||
if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) {
|
scope.Contract.UseGas(consumed, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||||
scope.Contract.Gas = 0
|
if consumed < wanted {
|
||||||
return nil, ErrOutOfGas
|
return nil, ErrOutOfGas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -206,8 +206,14 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
||||||
|
|
||||||
if !evm.StateDB.Exist(addr) {
|
if !evm.StateDB.Exist(addr) {
|
||||||
if !isPrecompile && evm.chainRules.IsEIP4762 && !isSystemCall(caller) {
|
if !isPrecompile && evm.chainRules.IsEIP4762 && !isSystemCall(caller) {
|
||||||
// add proof of absence to witness
|
// Add proof of absence to witness
|
||||||
wgas := evm.AccessEvents.AddAccount(addr, false)
|
// At this point, the read costs have already been charged, either because this
|
||||||
|
// is a direct tx call, in which case it's covered by the intrinsic gas, or because
|
||||||
|
// of a CALL instruction, in which case BASIC_DATA has been added to the access
|
||||||
|
// list in write mode. If there is enough gas paying for the addition of the code
|
||||||
|
// hash leaf to the access list, then account creation will proceed unimpaired.
|
||||||
|
// Thus, only pay for the creation of the code hash leaf here.
|
||||||
|
wgas := evm.AccessEvents.CodeHashGas(addr, true, gas, false)
|
||||||
if gas < wgas {
|
if gas < wgas {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
return nil, 0, ErrOutOfGas
|
return nil, 0, ErrOutOfGas
|
||||||
|
|
@ -433,7 +439,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
|
||||||
|
|
||||||
// Charge the contract creation init gas in verkle mode
|
// Charge the contract creation init gas in verkle mode
|
||||||
if evm.chainRules.IsEIP4762 {
|
if evm.chainRules.IsEIP4762 {
|
||||||
statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address)
|
statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address, gas)
|
||||||
if statelessGas > gas {
|
if statelessGas > gas {
|
||||||
return nil, common.Address{}, 0, ErrOutOfGas
|
return nil, common.Address{}, 0, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
|
@ -481,14 +487,14 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
|
||||||
}
|
}
|
||||||
// Charge the contract creation init gas in verkle mode
|
// Charge the contract creation init gas in verkle mode
|
||||||
if evm.chainRules.IsEIP4762 {
|
if evm.chainRules.IsEIP4762 {
|
||||||
statelessGas := evm.AccessEvents.ContractCreateInitGas(address)
|
consumed, wanted := evm.AccessEvents.ContractCreateInitGas(address, gas)
|
||||||
if statelessGas > gas {
|
if consumed < wanted {
|
||||||
return nil, common.Address{}, 0, ErrOutOfGas
|
return nil, common.Address{}, 0, ErrOutOfGas
|
||||||
}
|
}
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||||
evm.Config.Tracer.OnGasChange(gas, gas-statelessGas, tracing.GasChangeWitnessContractInit)
|
evm.Config.Tracer.OnGasChange(gas, gas-consumed, tracing.GasChangeWitnessContractInit)
|
||||||
}
|
}
|
||||||
gas = gas - statelessGas
|
gas = gas - consumed
|
||||||
}
|
}
|
||||||
evm.Context.Transfer(evm.StateDB, caller, address, value)
|
evm.Context.Transfer(evm.StateDB, caller, address, value)
|
||||||
|
|
||||||
|
|
@ -535,7 +541,9 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
|
||||||
return ret, ErrCodeStoreOutOfGas
|
return ret, ErrCodeStoreOutOfGas
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if len(ret) > 0 && !contract.UseGas(evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true), evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) {
|
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true, contract.Gas)
|
||||||
|
contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
|
||||||
|
if len(ret) > 0 && (consumed < wanted) {
|
||||||
return ret, ErrCodeStoreOutOfGas
|
return ret, ErrCodeStoreOutOfGas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -394,14 +394,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
|
||||||
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
if evm.chainRules.IsEIP4762 && !contract.IsSystemCall {
|
|
||||||
if transfersValue {
|
|
||||||
gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address))
|
|
||||||
if overflow {
|
|
||||||
return 0, ErrGasUintOverflow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
@ -428,16 +421,6 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory
|
||||||
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
if evm.chainRules.IsEIP4762 && !contract.IsSystemCall {
|
|
||||||
address := common.Address(stack.Back(1).Bytes20())
|
|
||||||
transfersValue := !stack.Back(2).IsZero()
|
|
||||||
if transfersValue {
|
|
||||||
gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address))
|
|
||||||
if overflow {
|
|
||||||
return 0, ErrGasUintOverflow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
|
||||||
|
|
@ -237,7 +237,11 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||||
// if the PC ends up in a new "chunk" of verkleized code, charge the
|
// if the PC ends up in a new "chunk" of verkleized code, charge the
|
||||||
// associated costs.
|
// associated costs.
|
||||||
contractAddr := contract.Address()
|
contractAddr := contract.Address()
|
||||||
contract.Gas -= in.evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false)
|
consumed, wanted := in.evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false, contract.Gas)
|
||||||
|
contract.UseGas(consumed, in.evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
|
||||||
|
if consumed < wanted {
|
||||||
|
return nil, ErrOutOfGas
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the operation from the jump table and validate the stack to ensure there are
|
// Get the operation from the jump table and validate the stack to ensure there are
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ func validate(jt JumpTable) JumpTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newVerkleInstructionSet() JumpTable {
|
func newVerkleInstructionSet() JumpTable {
|
||||||
instructionSet := newCancunInstructionSet()
|
instructionSet := newShanghaiInstructionSet()
|
||||||
enable4762(&instructionSet)
|
enable4762(&instructionSet)
|
||||||
return validate(instructionSet)
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,31 +25,16 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
gas := evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true)
|
return evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true, contract.Gas, true), nil
|
||||||
if gas == 0 {
|
|
||||||
gas = params.WarmStorageReadCostEIP2929
|
|
||||||
}
|
|
||||||
return gas, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
gas := evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false)
|
return evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false, contract.Gas, true), nil
|
||||||
if gas == 0 {
|
|
||||||
gas = params.WarmStorageReadCostEIP2929
|
|
||||||
}
|
|
||||||
return gas, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
if contract.IsSystemCall {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
address := stack.peek().Bytes20()
|
address := stack.peek().Bytes20()
|
||||||
gas := evm.AccessEvents.BasicDataGas(address, false)
|
return evm.AccessEvents.BasicDataGas(address, false, contract.Gas, true), nil
|
||||||
if gas == 0 {
|
|
||||||
gas = params.WarmStorageReadCostEIP2929
|
|
||||||
}
|
|
||||||
return gas, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
|
|
@ -57,56 +42,69 @@ func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
||||||
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
if contract.IsSystemCall {
|
return evm.AccessEvents.BasicDataGas(address, false, contract.Gas, true), nil
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
gas := evm.AccessEvents.BasicDataGas(address, false)
|
|
||||||
if gas == 0 {
|
|
||||||
gas = params.WarmStorageReadCostEIP2929
|
|
||||||
}
|
|
||||||
return gas, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
if contract.IsSystemCall {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
address := stack.peek().Bytes20()
|
address := stack.peek().Bytes20()
|
||||||
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
gas := evm.AccessEvents.CodeHashGas(address, false)
|
return evm.AccessEvents.CodeHashGas(address, false, contract.Gas, true), nil
|
||||||
if gas == 0 {
|
|
||||||
gas = params.WarmStorageReadCostEIP2929
|
|
||||||
}
|
|
||||||
return gas, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeCallVariantGasEIP4762(oldCalculator gasFunc) gasFunc {
|
func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) gasFunc {
|
||||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
var (
|
||||||
if err != nil {
|
target = common.Address(stack.Back(1).Bytes20())
|
||||||
return 0, err
|
witnessGas uint64
|
||||||
|
_, isPrecompile = evm.precompile(target)
|
||||||
|
isSystemContract = target == params.HistoryStorageAddress
|
||||||
|
)
|
||||||
|
|
||||||
|
// If value is transferred, it is charged before 1/64th
|
||||||
|
// is subtracted from the available gas pool.
|
||||||
|
if withTransferCosts && !stack.Back(2).IsZero() {
|
||||||
|
wantedValueTransferWitnessGas := evm.AccessEvents.ValueTransferGas(contract.Address(), target, contract.Gas)
|
||||||
|
if wantedValueTransferWitnessGas > contract.Gas {
|
||||||
|
return wantedValueTransferWitnessGas, nil
|
||||||
}
|
}
|
||||||
if contract.IsSystemCall {
|
witnessGas = wantedValueTransferWitnessGas
|
||||||
return gas, nil
|
} else if isPrecompile || isSystemContract {
|
||||||
}
|
|
||||||
if _, isPrecompile := evm.precompile(contract.Address()); isPrecompile {
|
|
||||||
return gas, nil
|
|
||||||
}
|
|
||||||
witnessGas := evm.AccessEvents.MessageCallGas(contract.Address())
|
|
||||||
if witnessGas == 0 {
|
|
||||||
witnessGas = params.WarmStorageReadCostEIP2929
|
witnessGas = params.WarmStorageReadCostEIP2929
|
||||||
|
} else {
|
||||||
|
// The charging for the value transfer is done BEFORE subtracting
|
||||||
|
// the 1/64th gas, as this is considered part of the CALL instruction.
|
||||||
|
// (so before we get to this point)
|
||||||
|
// But the message call is part of the subcall, for which only 63/64th
|
||||||
|
// of the gas should be available.
|
||||||
|
wantedMessageCallWitnessGas := evm.AccessEvents.MessageCallGas(target, contract.Gas-witnessGas)
|
||||||
|
var overflow bool
|
||||||
|
if witnessGas, overflow = math.SafeAdd(witnessGas, wantedMessageCallWitnessGas); overflow {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
return witnessGas + gas, nil
|
if witnessGas > contract.Gas {
|
||||||
|
return witnessGas, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract.Gas -= witnessGas
|
||||||
|
// if the operation fails, adds witness gas to the gas before returning the error
|
||||||
|
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
||||||
|
contract.Gas += witnessGas // restore witness gas so that it can be charged at the callsite
|
||||||
|
var overflow bool
|
||||||
|
if gas, overflow = math.SafeAdd(gas, witnessGas); overflow {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
return gas, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
gasCallEIP4762 = makeCallVariantGasEIP4762(gasCall)
|
gasCallEIP4762 = makeCallVariantGasEIP4762(gasCall, true)
|
||||||
gasCallCodeEIP4762 = makeCallVariantGasEIP4762(gasCallCode)
|
gasCallCodeEIP4762 = makeCallVariantGasEIP4762(gasCallCode, false)
|
||||||
gasStaticCallEIP4762 = makeCallVariantGasEIP4762(gasStaticCall)
|
gasStaticCallEIP4762 = makeCallVariantGasEIP4762(gasStaticCall, false)
|
||||||
gasDelegateCallEIP4762 = makeCallVariantGasEIP4762(gasDelegateCall)
|
gasDelegateCallEIP4762 = makeCallVariantGasEIP4762(gasDelegateCall, false)
|
||||||
)
|
)
|
||||||
|
|
||||||
func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
|
|
@ -118,15 +116,44 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
contractAddr := contract.Address()
|
contractAddr := contract.Address()
|
||||||
statelessGas := evm.AccessEvents.BasicDataGas(contractAddr, false)
|
wanted := evm.AccessEvents.BasicDataGas(contractAddr, false, contract.Gas, false)
|
||||||
|
if wanted > contract.Gas {
|
||||||
|
return wanted, nil
|
||||||
|
}
|
||||||
|
statelessGas := wanted
|
||||||
|
balanceIsZero := evm.StateDB.GetBalance(contractAddr).Sign() == 0
|
||||||
|
_, isPrecompile := evm.precompile(beneficiaryAddr)
|
||||||
|
isSystemContract := beneficiaryAddr == params.HistoryStorageAddress
|
||||||
|
|
||||||
|
if (isPrecompile || isSystemContract) && balanceIsZero {
|
||||||
|
return statelessGas, nil
|
||||||
|
}
|
||||||
|
|
||||||
if contractAddr != beneficiaryAddr {
|
if contractAddr != beneficiaryAddr {
|
||||||
statelessGas += evm.AccessEvents.BasicDataGas(beneficiaryAddr, false)
|
wanted := evm.AccessEvents.BasicDataGas(beneficiaryAddr, false, contract.Gas-statelessGas, false)
|
||||||
|
if wanted > contract.Gas-statelessGas {
|
||||||
|
return statelessGas + wanted, nil
|
||||||
|
}
|
||||||
|
statelessGas += wanted
|
||||||
}
|
}
|
||||||
// Charge write costs if it transfers value
|
// Charge write costs if it transfers value
|
||||||
if evm.StateDB.GetBalance(contractAddr).Sign() != 0 {
|
if !balanceIsZero {
|
||||||
statelessGas += evm.AccessEvents.BasicDataGas(contractAddr, true)
|
wanted := evm.AccessEvents.BasicDataGas(contractAddr, true, contract.Gas-statelessGas, false)
|
||||||
|
if wanted > contract.Gas-statelessGas {
|
||||||
|
return statelessGas + wanted, nil
|
||||||
|
}
|
||||||
|
statelessGas += wanted
|
||||||
|
|
||||||
if contractAddr != beneficiaryAddr {
|
if contractAddr != beneficiaryAddr {
|
||||||
statelessGas += evm.AccessEvents.BasicDataGas(beneficiaryAddr, true)
|
if evm.StateDB.Exist(beneficiaryAddr) {
|
||||||
|
wanted = evm.AccessEvents.BasicDataGas(beneficiaryAddr, true, contract.Gas-statelessGas, false)
|
||||||
|
} else {
|
||||||
|
wanted = evm.AccessEvents.AddAccount(beneficiaryAddr, true, contract.Gas-statelessGas)
|
||||||
|
}
|
||||||
|
if wanted > contract.Gas-statelessGas {
|
||||||
|
return statelessGas + wanted, nil
|
||||||
|
}
|
||||||
|
statelessGas += wanted
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return statelessGas, nil
|
return statelessGas, nil
|
||||||
|
|
@ -137,6 +164,7 @@ func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
if !contract.IsDeployment && !contract.IsSystemCall {
|
||||||
var (
|
var (
|
||||||
codeOffset = stack.Back(1)
|
codeOffset = stack.Back(1)
|
||||||
length = stack.Back(2)
|
length = stack.Back(2)
|
||||||
|
|
@ -145,9 +173,10 @@ func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
||||||
if overflow {
|
if overflow {
|
||||||
uint64CodeOffset = gomath.MaxUint64
|
uint64CodeOffset = gomath.MaxUint64
|
||||||
}
|
}
|
||||||
|
|
||||||
_, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64())
|
_, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64())
|
||||||
if !contract.IsDeployment && !contract.IsSystemCall {
|
_, wanted := evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false, contract.Gas-gas)
|
||||||
gas += evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false)
|
gas += wanted
|
||||||
}
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
@ -158,16 +187,17 @@ func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memo
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if contract.IsSystemCall {
|
addr := common.Address(stack.peek().Bytes20())
|
||||||
|
_, isPrecompile := evm.precompile(addr)
|
||||||
|
if isPrecompile || addr == params.HistoryStorageAddress {
|
||||||
|
var overflow bool
|
||||||
|
if gas, overflow = math.SafeAdd(gas, params.WarmStorageReadCostEIP2929); overflow {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
addr := common.Address(stack.peek().Bytes20())
|
wgas := evm.AccessEvents.BasicDataGas(addr, false, contract.Gas-gas, true)
|
||||||
wgas := evm.AccessEvents.BasicDataGas(addr, false)
|
|
||||||
if wgas == 0 {
|
|
||||||
wgas = params.WarmStorageReadCostEIP2929
|
|
||||||
}
|
|
||||||
var overflow bool
|
var overflow bool
|
||||||
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
|
||||||
if gas, overflow = math.SafeAdd(gas, wgas); overflow {
|
if gas, overflow = math.SafeAdd(gas, wgas); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/miner"
|
"github.com/ethereum/go-ethereum/miner"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/params/forks"
|
"github.com/ethereum/go-ethereum/params/forks"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
|
@ -196,11 +197,11 @@ func newConsensusAPIWithoutHeartbeat(eth *eth.Ethereum) *ConsensusAPI {
|
||||||
// and return its payloadID.
|
// and return its payloadID.
|
||||||
func (api *ConsensusAPI) ForkchoiceUpdatedV1(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
func (api *ConsensusAPI) ForkchoiceUpdatedV1(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||||
if payloadAttributes != nil {
|
if payloadAttributes != nil {
|
||||||
if payloadAttributes.Withdrawals != nil || payloadAttributes.BeaconRoot != nil {
|
switch {
|
||||||
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("withdrawals and beacon root not supported in V1"))
|
case payloadAttributes.Withdrawals != nil || payloadAttributes.BeaconRoot != nil:
|
||||||
}
|
return engine.STATUS_INVALID, paramsErr("withdrawals and beacon root not supported in V1")
|
||||||
if api.eth.BlockChain().Config().IsShanghai(api.eth.BlockChain().Config().LondonBlock, payloadAttributes.Timestamp) {
|
case !api.checkFork(payloadAttributes.Timestamp, forks.Paris, forks.Shanghai):
|
||||||
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("forkChoiceUpdateV1 called post-shanghai"))
|
return engine.STATUS_INVALID, paramsErr("fcuV1 called post-shanghai")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return api.forkchoiceUpdated(update, payloadAttributes, engine.PayloadV1, false)
|
return api.forkchoiceUpdated(update, payloadAttributes, engine.PayloadV1, false)
|
||||||
|
|
@ -210,20 +211,15 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update engine.ForkchoiceStateV1, pa
|
||||||
// attributes. It supports both PayloadAttributesV1 and PayloadAttributesV2.
|
// attributes. It supports both PayloadAttributesV1 and PayloadAttributesV2.
|
||||||
func (api *ConsensusAPI) ForkchoiceUpdatedV2(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
func (api *ConsensusAPI) ForkchoiceUpdatedV2(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||||
if params != nil {
|
if params != nil {
|
||||||
if params.BeaconRoot != nil {
|
switch {
|
||||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("unexpected beacon root"))
|
case params.BeaconRoot != nil:
|
||||||
}
|
return engine.STATUS_INVALID, attributesErr("unexpected beacon root")
|
||||||
switch api.eth.BlockChain().Config().LatestFork(params.Timestamp) {
|
case api.checkFork(params.Timestamp, forks.Paris) && params.Withdrawals != nil:
|
||||||
case forks.Paris:
|
return engine.STATUS_INVALID, attributesErr("withdrawals before shanghai")
|
||||||
if params.Withdrawals != nil {
|
case api.checkFork(params.Timestamp, forks.Shanghai) && params.Withdrawals == nil:
|
||||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("withdrawals before shanghai"))
|
return engine.STATUS_INVALID, attributesErr("missing withdrawals")
|
||||||
}
|
case !api.checkFork(params.Timestamp, forks.Paris, forks.Shanghai):
|
||||||
case forks.Shanghai:
|
return engine.STATUS_INVALID, unsupportedForkErr("fcuV2 must only be called with paris or shanghai payloads")
|
||||||
if params.Withdrawals == nil {
|
|
||||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing withdrawals"))
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return engine.STATUS_INVALID, engine.UnsupportedFork.With(errors.New("forkchoiceUpdatedV2 must only be called with paris and shanghai payloads"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return api.forkchoiceUpdated(update, params, engine.PayloadV2, false)
|
return api.forkchoiceUpdated(update, params, engine.PayloadV2, false)
|
||||||
|
|
@ -233,14 +229,13 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV2(update engine.ForkchoiceStateV1, pa
|
||||||
// in the payload attributes. It supports only PayloadAttributesV3.
|
// in the payload attributes. It supports only PayloadAttributesV3.
|
||||||
func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||||
if params != nil {
|
if params != nil {
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing withdrawals"))
|
case params.Withdrawals == nil:
|
||||||
}
|
return engine.STATUS_INVALID, attributesErr("missing withdrawals")
|
||||||
if params.BeaconRoot == nil {
|
case params.BeaconRoot == nil:
|
||||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing beacon root"))
|
return engine.STATUS_INVALID, attributesErr("missing beacon root")
|
||||||
}
|
case !api.checkFork(params.Timestamp, forks.Cancun, forks.Prague):
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun && api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague {
|
return engine.STATUS_INVALID, unsupportedForkErr("fcuV3 must only be called for cancun or prague payloads")
|
||||||
return engine.STATUS_INVALID, engine.UnsupportedFork.With(errors.New("forkchoiceUpdatedV3 must only be called for cancun payloads"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO(matt): the spec requires that fcu is applied when called on a valid
|
// TODO(matt): the spec requires that fcu is applied when called on a valid
|
||||||
|
|
@ -254,11 +249,11 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, pa
|
||||||
// generates an execution witness too if block building was requested.
|
// generates an execution witness too if block building was requested.
|
||||||
func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV1(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV1(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||||
if payloadAttributes != nil {
|
if payloadAttributes != nil {
|
||||||
if payloadAttributes.Withdrawals != nil || payloadAttributes.BeaconRoot != nil {
|
switch {
|
||||||
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("withdrawals and beacon root not supported in V1"))
|
case payloadAttributes.Withdrawals != nil || payloadAttributes.BeaconRoot != nil:
|
||||||
}
|
return engine.STATUS_INVALID, paramsErr("withdrawals and beacon root not supported in V1")
|
||||||
if api.eth.BlockChain().Config().IsShanghai(api.eth.BlockChain().Config().LondonBlock, payloadAttributes.Timestamp) {
|
case !api.checkFork(payloadAttributes.Timestamp, forks.Paris, forks.Shanghai):
|
||||||
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("forkChoiceUpdateV1 called post-shanghai"))
|
return engine.STATUS_INVALID, paramsErr("fcuV1 called post-shanghai")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return api.forkchoiceUpdated(update, payloadAttributes, engine.PayloadV1, true)
|
return api.forkchoiceUpdated(update, payloadAttributes, engine.PayloadV1, true)
|
||||||
|
|
@ -268,20 +263,15 @@ func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV1(update engine.Forkchoice
|
||||||
// generates an execution witness too if block building was requested.
|
// generates an execution witness too if block building was requested.
|
||||||
func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV2(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV2(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||||
if params != nil {
|
if params != nil {
|
||||||
if params.BeaconRoot != nil {
|
switch {
|
||||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("unexpected beacon root"))
|
case params.BeaconRoot != nil:
|
||||||
}
|
return engine.STATUS_INVALID, attributesErr("unexpected beacon root")
|
||||||
switch api.eth.BlockChain().Config().LatestFork(params.Timestamp) {
|
case api.checkFork(params.Timestamp, forks.Paris) && params.Withdrawals != nil:
|
||||||
case forks.Paris:
|
return engine.STATUS_INVALID, attributesErr("withdrawals before shanghai")
|
||||||
if params.Withdrawals != nil {
|
case api.checkFork(params.Timestamp, forks.Shanghai) && params.Withdrawals == nil:
|
||||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("withdrawals before shanghai"))
|
return engine.STATUS_INVALID, attributesErr("missing withdrawals")
|
||||||
}
|
case !api.checkFork(params.Timestamp, forks.Paris, forks.Shanghai):
|
||||||
case forks.Shanghai:
|
return engine.STATUS_INVALID, unsupportedForkErr("fcuV2 must only be called with paris or shanghai payloads")
|
||||||
if params.Withdrawals == nil {
|
|
||||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing withdrawals"))
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return engine.STATUS_INVALID, engine.UnsupportedFork.With(errors.New("forkchoiceUpdatedV2 must only be called with paris and shanghai payloads"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return api.forkchoiceUpdated(update, params, engine.PayloadV2, true)
|
return api.forkchoiceUpdated(update, params, engine.PayloadV2, true)
|
||||||
|
|
@ -291,14 +281,13 @@ func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV2(update engine.Forkchoice
|
||||||
// generates an execution witness too if block building was requested.
|
// generates an execution witness too if block building was requested.
|
||||||
func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV3(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV3(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||||
if params != nil {
|
if params != nil {
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing withdrawals"))
|
case params.Withdrawals == nil:
|
||||||
}
|
return engine.STATUS_INVALID, attributesErr("missing withdrawals")
|
||||||
if params.BeaconRoot == nil {
|
case params.BeaconRoot == nil:
|
||||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing beacon root"))
|
return engine.STATUS_INVALID, attributesErr("missing beacon root")
|
||||||
}
|
case !api.checkFork(params.Timestamp, forks.Cancun, forks.Prague):
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun && api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague {
|
return engine.STATUS_INVALID, unsupportedForkErr("fcuV3 must only be called for cancun or prague payloads")
|
||||||
return engine.STATUS_INVALID, engine.UnsupportedFork.With(errors.New("forkchoiceUpdatedV3 must only be called for cancun payloads"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO(matt): the spec requires that fcu is applied when called on a valid
|
// TODO(matt): the spec requires that fcu is applied when called on a valid
|
||||||
|
|
@ -468,7 +457,7 @@ func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config engine.Transit
|
||||||
api.lastTransitionUpdate = time.Now()
|
api.lastTransitionUpdate = time.Now()
|
||||||
api.lastTransitionLock.Unlock()
|
api.lastTransitionLock.Unlock()
|
||||||
|
|
||||||
ttd := api.eth.BlockChain().Config().TerminalTotalDifficulty
|
ttd := api.config().TerminalTotalDifficulty
|
||||||
if ttd == nil || ttd.Cmp(config.TerminalTotalDifficulty.ToInt()) != 0 {
|
if ttd == nil || ttd.Cmp(config.TerminalTotalDifficulty.ToInt()) != 0 {
|
||||||
log.Warn("Invalid TTD configured", "geth", ttd, "beacon", config.TerminalTotalDifficulty)
|
log.Warn("Invalid TTD configured", "geth", ttd, "beacon", config.TerminalTotalDifficulty)
|
||||||
return nil, fmt.Errorf("invalid ttd: execution %v consensus %v", ttd, config.TerminalTotalDifficulty)
|
return nil, fmt.Errorf("invalid ttd: execution %v consensus %v", ttd, config.TerminalTotalDifficulty)
|
||||||
|
|
@ -550,86 +539,74 @@ func (api *ConsensusAPI) GetBlobsV1(hashes []common.Hash) ([]*engine.BlobAndProo
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper for NewPayload* methods.
|
||||||
|
var invalidStatus = engine.PayloadStatusV1{Status: engine.INVALID}
|
||||||
|
|
||||||
// NewPayloadV1 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
// NewPayloadV1 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
||||||
func (api *ConsensusAPI) NewPayloadV1(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
|
func (api *ConsensusAPI) NewPayloadV1(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
|
||||||
if params.Withdrawals != nil {
|
if params.Withdrawals != nil {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("withdrawals not supported in V1"))
|
return invalidStatus, paramsErr("withdrawals not supported in V1")
|
||||||
}
|
}
|
||||||
return api.newPayload(params, nil, nil, nil, false)
|
return api.newPayload(params, nil, nil, nil, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPayloadV2 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
// NewPayloadV2 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
||||||
func (api *ConsensusAPI) NewPayloadV2(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
|
func (api *ConsensusAPI) NewPayloadV2(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
|
||||||
if api.eth.BlockChain().Config().IsCancun(api.eth.BlockChain().Config().LondonBlock, params.Timestamp) {
|
var (
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("can't use newPayloadV2 post-cancun"))
|
cancun = api.config().IsCancun(api.config().LondonBlock, params.Timestamp)
|
||||||
}
|
shanghai = api.config().IsShanghai(api.config().LondonBlock, params.Timestamp)
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) == forks.Shanghai {
|
)
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
case cancun:
|
||||||
}
|
return invalidStatus, paramsErr("can't use newPayloadV2 post-cancun")
|
||||||
} else {
|
case shanghai && params.Withdrawals == nil:
|
||||||
if params.Withdrawals != nil {
|
return invalidStatus, paramsErr("nil withdrawals post-shanghai")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil withdrawals pre-shanghai"))
|
case !shanghai && params.Withdrawals != nil:
|
||||||
}
|
return invalidStatus, paramsErr("non-nil withdrawals pre-shanghai")
|
||||||
}
|
case params.ExcessBlobGas != nil:
|
||||||
if params.ExcessBlobGas != nil {
|
return invalidStatus, paramsErr("non-nil excessBlobGas pre-cancun")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil excessBlobGas pre-cancun"))
|
case params.BlobGasUsed != nil:
|
||||||
}
|
return invalidStatus, paramsErr("non-nil blobGasUsed pre-cancun")
|
||||||
if params.BlobGasUsed != nil {
|
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil blobGasUsed pre-cancun"))
|
|
||||||
}
|
}
|
||||||
return api.newPayload(params, nil, nil, nil, false)
|
return api.newPayload(params, nil, nil, nil, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPayloadV3 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
// NewPayloadV3 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
||||||
func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) {
|
func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) {
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
case params.Withdrawals == nil:
|
||||||
}
|
return invalidStatus, paramsErr("nil withdrawals post-shanghai")
|
||||||
if params.ExcessBlobGas == nil {
|
case params.ExcessBlobGas == nil:
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
return invalidStatus, paramsErr("nil excessBlobGas post-cancun")
|
||||||
}
|
case params.BlobGasUsed == nil:
|
||||||
if params.BlobGasUsed == nil {
|
return invalidStatus, paramsErr("nil blobGasUsed post-cancun")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun"))
|
case versionedHashes == nil:
|
||||||
}
|
return invalidStatus, paramsErr("nil versionedHashes post-cancun")
|
||||||
|
case beaconRoot == nil:
|
||||||
if versionedHashes == nil {
|
return invalidStatus, paramsErr("nil beaconRoot post-cancun")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
case !api.checkFork(params.Timestamp, forks.Cancun):
|
||||||
}
|
return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
|
||||||
if beaconRoot == nil {
|
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun {
|
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadV3 must only be called for cancun payloads"))
|
|
||||||
}
|
}
|
||||||
return api.newPayload(params, versionedHashes, beaconRoot, nil, false)
|
return api.newPayload(params, versionedHashes, beaconRoot, nil, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPayloadV4 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
// NewPayloadV4 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
||||||
func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) {
|
func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) {
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
case params.Withdrawals == nil:
|
||||||
}
|
return invalidStatus, paramsErr("nil withdrawals post-shanghai")
|
||||||
if params.ExcessBlobGas == nil {
|
case params.ExcessBlobGas == nil:
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
return invalidStatus, paramsErr("nil excessBlobGas post-cancun")
|
||||||
}
|
case params.BlobGasUsed == nil:
|
||||||
if params.BlobGasUsed == nil {
|
return invalidStatus, paramsErr("nil blobGasUsed post-cancun")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun"))
|
case versionedHashes == nil:
|
||||||
}
|
return invalidStatus, paramsErr("nil versionedHashes post-cancun")
|
||||||
|
case beaconRoot == nil:
|
||||||
if versionedHashes == nil {
|
return invalidStatus, paramsErr("nil beaconRoot post-cancun")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
case executionRequests == nil:
|
||||||
}
|
return invalidStatus, paramsErr("nil executionRequests post-prague")
|
||||||
if beaconRoot == nil {
|
case !api.checkFork(params.Timestamp, forks.Prague):
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun"))
|
return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
|
||||||
}
|
|
||||||
if executionRequests == nil {
|
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-prague"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague {
|
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadV4 must only be called for prague payloads"))
|
|
||||||
}
|
}
|
||||||
requests := convertRequests(executionRequests)
|
requests := convertRequests(executionRequests)
|
||||||
if err := validateRequests(requests); err != nil {
|
if err := validateRequests(requests); err != nil {
|
||||||
|
|
@ -650,23 +627,21 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV1(params engine.ExecutableData) (
|
||||||
// NewPayloadWithWitnessV2 is analogous to NewPayloadV2, only it also generates
|
// NewPayloadWithWitnessV2 is analogous to NewPayloadV2, only it also generates
|
||||||
// and returns a stateless witness after running the payload.
|
// and returns a stateless witness after running the payload.
|
||||||
func (api *ConsensusAPI) NewPayloadWithWitnessV2(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
|
func (api *ConsensusAPI) NewPayloadWithWitnessV2(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
|
||||||
if api.eth.BlockChain().Config().IsCancun(api.eth.BlockChain().Config().LondonBlock, params.Timestamp) {
|
var (
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("can't use newPayloadV2 post-cancun"))
|
cancun = api.config().IsCancun(api.config().LondonBlock, params.Timestamp)
|
||||||
}
|
shanghai = api.config().IsShanghai(api.config().LondonBlock, params.Timestamp)
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) == forks.Shanghai {
|
)
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
case cancun:
|
||||||
}
|
return invalidStatus, paramsErr("can't use newPayloadV2 post-cancun")
|
||||||
} else {
|
case shanghai && params.Withdrawals == nil:
|
||||||
if params.Withdrawals != nil {
|
return invalidStatus, paramsErr("nil withdrawals post-shanghai")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil withdrawals pre-shanghai"))
|
case !shanghai && params.Withdrawals != nil:
|
||||||
}
|
return invalidStatus, paramsErr("non-nil withdrawals pre-shanghai")
|
||||||
}
|
case params.ExcessBlobGas != nil:
|
||||||
if params.ExcessBlobGas != nil {
|
return invalidStatus, paramsErr("non-nil excessBlobGas pre-cancun")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil excessBlobGas pre-cancun"))
|
case params.BlobGasUsed != nil:
|
||||||
}
|
return invalidStatus, paramsErr("non-nil blobGasUsed pre-cancun")
|
||||||
if params.BlobGasUsed != nil {
|
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil blobGasUsed pre-cancun"))
|
|
||||||
}
|
}
|
||||||
return api.newPayload(params, nil, nil, nil, true)
|
return api.newPayload(params, nil, nil, nil, true)
|
||||||
}
|
}
|
||||||
|
|
@ -674,25 +649,19 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV2(params engine.ExecutableData) (
|
||||||
// NewPayloadWithWitnessV3 is analogous to NewPayloadV3, only it also generates
|
// NewPayloadWithWitnessV3 is analogous to NewPayloadV3, only it also generates
|
||||||
// and returns a stateless witness after running the payload.
|
// and returns a stateless witness after running the payload.
|
||||||
func (api *ConsensusAPI) NewPayloadWithWitnessV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) {
|
func (api *ConsensusAPI) NewPayloadWithWitnessV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) {
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
case params.Withdrawals == nil:
|
||||||
}
|
return invalidStatus, paramsErr("nil withdrawals post-shanghai")
|
||||||
if params.ExcessBlobGas == nil {
|
case params.ExcessBlobGas == nil:
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
return invalidStatus, paramsErr("nil excessBlobGas post-cancun")
|
||||||
}
|
case params.BlobGasUsed == nil:
|
||||||
if params.BlobGasUsed == nil {
|
return invalidStatus, paramsErr("nil blobGasUsed post-cancun")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun"))
|
case versionedHashes == nil:
|
||||||
}
|
return invalidStatus, paramsErr("nil versionedHashes post-cancun")
|
||||||
|
case beaconRoot == nil:
|
||||||
if versionedHashes == nil {
|
return invalidStatus, paramsErr("nil beaconRoot post-cancun")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
case !api.checkFork(params.Timestamp, forks.Cancun):
|
||||||
}
|
return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
|
||||||
if beaconRoot == nil {
|
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun {
|
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadWithWitnessV3 must only be called for cancun payloads"))
|
|
||||||
}
|
}
|
||||||
return api.newPayload(params, versionedHashes, beaconRoot, nil, true)
|
return api.newPayload(params, versionedHashes, beaconRoot, nil, true)
|
||||||
}
|
}
|
||||||
|
|
@ -700,28 +669,21 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV3(params engine.ExecutableData, v
|
||||||
// NewPayloadWithWitnessV4 is analogous to NewPayloadV4, only it also generates
|
// NewPayloadWithWitnessV4 is analogous to NewPayloadV4, only it also generates
|
||||||
// and returns a stateless witness after running the payload.
|
// and returns a stateless witness after running the payload.
|
||||||
func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) {
|
func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) {
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
case params.Withdrawals == nil:
|
||||||
}
|
return invalidStatus, paramsErr("nil withdrawals post-shanghai")
|
||||||
if params.ExcessBlobGas == nil {
|
case params.ExcessBlobGas == nil:
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
return invalidStatus, paramsErr("nil excessBlobGas post-cancun")
|
||||||
}
|
case params.BlobGasUsed == nil:
|
||||||
if params.BlobGasUsed == nil {
|
return invalidStatus, paramsErr("nil blobGasUsed post-cancun")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun"))
|
case versionedHashes == nil:
|
||||||
}
|
return invalidStatus, paramsErr("nil versionedHashes post-cancun")
|
||||||
|
case beaconRoot == nil:
|
||||||
if versionedHashes == nil {
|
return invalidStatus, paramsErr("nil beaconRoot post-cancun")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
case executionRequests == nil:
|
||||||
}
|
return invalidStatus, paramsErr("nil executionRequests post-prague")
|
||||||
if beaconRoot == nil {
|
case !api.checkFork(params.Timestamp, forks.Prague):
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun"))
|
return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
|
||||||
}
|
|
||||||
if executionRequests == nil {
|
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-prague"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague {
|
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadWithWitnessV4 must only be called for prague payloads"))
|
|
||||||
}
|
}
|
||||||
requests := convertRequests(executionRequests)
|
requests := convertRequests(executionRequests)
|
||||||
if err := validateRequests(requests); err != nil {
|
if err := validateRequests(requests); err != nil {
|
||||||
|
|
@ -742,23 +704,21 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV1(params engine.ExecutableData,
|
||||||
// ExecuteStatelessPayloadV2 is analogous to NewPayloadV2, only it operates in
|
// ExecuteStatelessPayloadV2 is analogous to NewPayloadV2, only it operates in
|
||||||
// a stateless mode on top of a provided witness instead of the local database.
|
// a stateless mode on top of a provided witness instead of the local database.
|
||||||
func (api *ConsensusAPI) ExecuteStatelessPayloadV2(params engine.ExecutableData, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) {
|
func (api *ConsensusAPI) ExecuteStatelessPayloadV2(params engine.ExecutableData, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) {
|
||||||
if api.eth.BlockChain().Config().IsCancun(api.eth.BlockChain().Config().LondonBlock, params.Timestamp) {
|
var (
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("can't use newPayloadV2 post-cancun"))
|
cancun = api.config().IsCancun(api.config().LondonBlock, params.Timestamp)
|
||||||
}
|
shanghai = api.config().IsShanghai(api.config().LondonBlock, params.Timestamp)
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) == forks.Shanghai {
|
)
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
case cancun:
|
||||||
}
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("can't use newPayloadV2 post-cancun")
|
||||||
} else {
|
case shanghai && params.Withdrawals == nil:
|
||||||
if params.Withdrawals != nil {
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil withdrawals post-shanghai")
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil withdrawals pre-shanghai"))
|
case !shanghai && params.Withdrawals != nil:
|
||||||
}
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("non-nil withdrawals pre-shanghai")
|
||||||
}
|
case params.ExcessBlobGas != nil:
|
||||||
if params.ExcessBlobGas != nil {
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("non-nil excessBlobGas pre-cancun")
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil excessBlobGas pre-cancun"))
|
case params.BlobGasUsed != nil:
|
||||||
}
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("non-nil blobGasUsed pre-cancun")
|
||||||
if params.BlobGasUsed != nil {
|
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil blobGasUsed pre-cancun"))
|
|
||||||
}
|
}
|
||||||
return api.executeStatelessPayload(params, nil, nil, nil, opaqueWitness)
|
return api.executeStatelessPayload(params, nil, nil, nil, opaqueWitness)
|
||||||
}
|
}
|
||||||
|
|
@ -766,25 +726,19 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV2(params engine.ExecutableData,
|
||||||
// ExecuteStatelessPayloadV3 is analogous to NewPayloadV3, only it operates in
|
// ExecuteStatelessPayloadV3 is analogous to NewPayloadV3, only it operates in
|
||||||
// a stateless mode on top of a provided witness instead of the local database.
|
// a stateless mode on top of a provided witness instead of the local database.
|
||||||
func (api *ConsensusAPI) ExecuteStatelessPayloadV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) {
|
func (api *ConsensusAPI) ExecuteStatelessPayloadV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) {
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
case params.Withdrawals == nil:
|
||||||
}
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil withdrawals post-shanghai")
|
||||||
if params.ExcessBlobGas == nil {
|
case params.ExcessBlobGas == nil:
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil excessBlobGas post-cancun")
|
||||||
}
|
case params.BlobGasUsed == nil:
|
||||||
if params.BlobGasUsed == nil {
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil blobGasUsed post-cancun")
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun"))
|
case versionedHashes == nil:
|
||||||
}
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil versionedHashes post-cancun")
|
||||||
|
case beaconRoot == nil:
|
||||||
if versionedHashes == nil {
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil beaconRoot post-cancun")
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
case !api.checkFork(params.Timestamp, forks.Cancun):
|
||||||
}
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
|
||||||
if beaconRoot == nil {
|
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun {
|
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("executeStatelessPayloadV3 must only be called for cancun payloads"))
|
|
||||||
}
|
}
|
||||||
return api.executeStatelessPayload(params, versionedHashes, beaconRoot, nil, opaqueWitness)
|
return api.executeStatelessPayload(params, versionedHashes, beaconRoot, nil, opaqueWitness)
|
||||||
}
|
}
|
||||||
|
|
@ -792,30 +746,26 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV3(params engine.ExecutableData,
|
||||||
// ExecuteStatelessPayloadV4 is analogous to NewPayloadV4, only it operates in
|
// ExecuteStatelessPayloadV4 is analogous to NewPayloadV4, only it operates in
|
||||||
// a stateless mode on top of a provided witness instead of the local database.
|
// a stateless mode on top of a provided witness instead of the local database.
|
||||||
func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) {
|
func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) {
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
case params.Withdrawals == nil:
|
||||||
}
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil withdrawals post-shanghai")
|
||||||
if params.ExcessBlobGas == nil {
|
case params.ExcessBlobGas == nil:
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil excessBlobGas post-cancun")
|
||||||
}
|
case params.BlobGasUsed == nil:
|
||||||
if params.BlobGasUsed == nil {
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil blobGasUsed post-cancun")
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun"))
|
case versionedHashes == nil:
|
||||||
}
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil versionedHashes post-cancun")
|
||||||
|
case beaconRoot == nil:
|
||||||
if versionedHashes == nil {
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil beaconRoot post-cancun")
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
case executionRequests == nil:
|
||||||
}
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil executionRequests post-prague")
|
||||||
if beaconRoot == nil {
|
case !api.checkFork(params.Timestamp, forks.Prague):
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun"))
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
|
||||||
}
|
|
||||||
if executionRequests == nil {
|
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-prague"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague {
|
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("executeStatelessPayloadV4 must only be called for prague payloads"))
|
|
||||||
}
|
}
|
||||||
requests := convertRequests(executionRequests)
|
requests := convertRequests(executionRequests)
|
||||||
|
if err := validateRequests(requests); err != nil {
|
||||||
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(err)
|
||||||
|
}
|
||||||
return api.executeStatelessPayload(params, versionedHashes, beaconRoot, requests, opaqueWitness)
|
return api.executeStatelessPayload(params, versionedHashes, beaconRoot, requests, opaqueWitness)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -981,7 +931,7 @@ func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, v
|
||||||
api.lastNewPayloadLock.Unlock()
|
api.lastNewPayloadLock.Unlock()
|
||||||
|
|
||||||
log.Trace("Executing block statelessly", "number", block.Number(), "hash", params.BlockHash)
|
log.Trace("Executing block statelessly", "number", block.Number(), "hash", params.BlockHash)
|
||||||
stateRoot, receiptRoot, err := core.ExecuteStateless(api.eth.BlockChain().Config(), vm.Config{}, block, witness)
|
stateRoot, receiptRoot, err := core.ExecuteStateless(api.config(), vm.Config{}, block, witness)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("ExecuteStatelessPayload: execution failed", "err", err)
|
log.Warn("ExecuteStatelessPayload: execution failed", "err", err)
|
||||||
errorMsg := err.Error()
|
errorMsg := err.Error()
|
||||||
|
|
@ -1119,7 +1069,7 @@ func (api *ConsensusAPI) heartbeat() {
|
||||||
time.Sleep(beaconUpdateStartupTimeout)
|
time.Sleep(beaconUpdateStartupTimeout)
|
||||||
|
|
||||||
// If the network is not yet merged/merging, don't bother continuing.
|
// If the network is not yet merged/merging, don't bother continuing.
|
||||||
if api.eth.BlockChain().Config().TerminalTotalDifficulty == nil {
|
if api.config().TerminalTotalDifficulty == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1163,6 +1113,23 @@ func (api *ConsensusAPI) heartbeat() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// config retrieves the chain's fork configuration.
|
||||||
|
func (api *ConsensusAPI) config() *params.ChainConfig {
|
||||||
|
return api.eth.BlockChain().Config()
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkFork returns true if the latest fork at the given timestamp
|
||||||
|
// is one of the forks provided.
|
||||||
|
func (api *ConsensusAPI) checkFork(timestamp uint64, forks ...forks.Fork) bool {
|
||||||
|
latest := api.config().LatestFork(timestamp)
|
||||||
|
for _, fork := range forks {
|
||||||
|
if latest == fork {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// ExchangeCapabilities returns the current methods provided by this node.
|
// ExchangeCapabilities returns the current methods provided by this node.
|
||||||
func (api *ConsensusAPI) ExchangeCapabilities([]string) []string {
|
func (api *ConsensusAPI) ExchangeCapabilities([]string) []string {
|
||||||
return caps
|
return caps
|
||||||
|
|
@ -1288,3 +1255,21 @@ func validateRequests(requests [][]byte) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// paramsErr is a helper function for creating an InvalidPayloadAttributes
|
||||||
|
// Engine API error.
|
||||||
|
func paramsErr(msg string) error {
|
||||||
|
return engine.InvalidParams.With(errors.New(msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
// attributesErr is a helper function for creating an InvalidPayloadAttributes
|
||||||
|
// Engine API error.
|
||||||
|
func attributesErr(msg string) error {
|
||||||
|
return engine.InvalidPayloadAttributes.With(errors.New(msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsupportedForkErr is a helper function for creating an UnsupportedFork
|
||||||
|
// Engine API error.
|
||||||
|
func unsupportedForkErr(msg string) error {
|
||||||
|
return engine.UnsupportedFork.With(errors.New(msg))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,151 +0,0 @@
|
||||||
// Copyright 2019 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library 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 Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package build
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ChecksumDB keeps file checksums.
|
|
||||||
type ChecksumDB struct {
|
|
||||||
allChecksums []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustLoadChecksums loads a file containing checksums.
|
|
||||||
func MustLoadChecksums(file string) *ChecksumDB {
|
|
||||||
content, err := os.ReadFile(file)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("can't load checksum file: " + err.Error())
|
|
||||||
}
|
|
||||||
return &ChecksumDB{strings.Split(strings.ReplaceAll(string(content), "\r\n", "\n"), "\n")}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify checks whether the given file is valid according to the checksum database.
|
|
||||||
func (db *ChecksumDB) Verify(path string) error {
|
|
||||||
fd, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer fd.Close()
|
|
||||||
|
|
||||||
h := sha256.New()
|
|
||||||
if _, err := io.Copy(h, bufio.NewReader(fd)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fileHash := hex.EncodeToString(h.Sum(nil))
|
|
||||||
if !db.findHash(filepath.Base(path), fileHash) {
|
|
||||||
return fmt.Errorf("invalid file hash: %s %s", fileHash, filepath.Base(path))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *ChecksumDB) findHash(basename, hash string) bool {
|
|
||||||
want := hash + " " + basename
|
|
||||||
for _, line := range db.allChecksums {
|
|
||||||
if strings.TrimSpace(line) == want {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// DownloadFile downloads a file and verifies its checksum.
|
|
||||||
func (db *ChecksumDB) DownloadFile(url, dstPath string) error {
|
|
||||||
if err := db.Verify(dstPath); err == nil {
|
|
||||||
fmt.Printf("%s is up-to-date\n", dstPath)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
fmt.Printf("%s is stale\n", dstPath)
|
|
||||||
fmt.Printf("downloading from %s\n", url)
|
|
||||||
|
|
||||||
resp, err := http.Get(url)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("download error: %v", err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return fmt.Errorf("download error: status %d", resp.StatusCode)
|
|
||||||
}
|
|
||||||
if err := os.MkdirAll(filepath.Dir(dstPath), 0755); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fd, err := os.OpenFile(dstPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
dst := newDownloadWriter(fd, resp.ContentLength)
|
|
||||||
_, err = io.Copy(dst, resp.Body)
|
|
||||||
dst.Close()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return db.Verify(dstPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
type downloadWriter struct {
|
|
||||||
file *os.File
|
|
||||||
dstBuf *bufio.Writer
|
|
||||||
size int64
|
|
||||||
written int64
|
|
||||||
lastpct int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDownloadWriter(dst *os.File, size int64) *downloadWriter {
|
|
||||||
return &downloadWriter{
|
|
||||||
file: dst,
|
|
||||||
dstBuf: bufio.NewWriter(dst),
|
|
||||||
size: size,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *downloadWriter) Write(buf []byte) (int, error) {
|
|
||||||
n, err := w.dstBuf.Write(buf)
|
|
||||||
|
|
||||||
// Report progress.
|
|
||||||
w.written += int64(n)
|
|
||||||
pct := w.written * 10 / w.size * 10
|
|
||||||
if pct != w.lastpct {
|
|
||||||
if w.lastpct != 0 {
|
|
||||||
fmt.Print("...")
|
|
||||||
}
|
|
||||||
fmt.Print(pct, "%")
|
|
||||||
w.lastpct = pct
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *downloadWriter) Close() error {
|
|
||||||
if w.lastpct > 0 {
|
|
||||||
fmt.Println() // Finish the progress line.
|
|
||||||
}
|
|
||||||
flushErr := w.dstBuf.Flush()
|
|
||||||
closeErr := w.file.Close()
|
|
||||||
if flushErr != nil {
|
|
||||||
return flushErr
|
|
||||||
}
|
|
||||||
return closeErr
|
|
||||||
}
|
|
||||||
|
|
@ -24,6 +24,8 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/internal/download"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GoToolchain struct {
|
type GoToolchain struct {
|
||||||
|
|
@ -84,8 +86,8 @@ func (g *GoToolchain) goTool(command string, args ...string) *exec.Cmd {
|
||||||
|
|
||||||
// DownloadGo downloads the Go binary distribution and unpacks it into a temporary
|
// DownloadGo downloads the Go binary distribution and unpacks it into a temporary
|
||||||
// directory. It returns the GOROOT of the unpacked toolchain.
|
// directory. It returns the GOROOT of the unpacked toolchain.
|
||||||
func DownloadGo(csdb *ChecksumDB) string {
|
func DownloadGo(csdb *download.ChecksumDB) string {
|
||||||
version, err := Version(csdb, "golang")
|
version, err := csdb.FindVersion("golang")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -130,51 +132,3 @@ func DownloadGo(csdb *ChecksumDB) string {
|
||||||
}
|
}
|
||||||
return goroot
|
return goroot
|
||||||
}
|
}
|
||||||
|
|
||||||
// Version returns the versions defined in the checksumdb.
|
|
||||||
func Version(csdb *ChecksumDB, version string) (string, error) {
|
|
||||||
for _, l := range csdb.allChecksums {
|
|
||||||
if !strings.HasPrefix(l, "# version:") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
v := strings.Split(l, ":")[1]
|
|
||||||
parts := strings.Split(v, " ")
|
|
||||||
if len(parts) != 2 {
|
|
||||||
log.Print("Erroneous version-string", "v", l)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if parts[0] == version {
|
|
||||||
return parts[1], nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("no version found for '%v'", version)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DownloadAndVerifyChecksums downloads all files and checks that they match
|
|
||||||
// the checksum given in checksums.txt.
|
|
||||||
// This task can be used to sanity-check new checksums.
|
|
||||||
func DownloadAndVerifyChecksums(csdb *ChecksumDB) {
|
|
||||||
var (
|
|
||||||
base = ""
|
|
||||||
ucache = os.TempDir()
|
|
||||||
)
|
|
||||||
for _, l := range csdb.allChecksums {
|
|
||||||
if strings.HasPrefix(l, "# https://") {
|
|
||||||
base = l[2:]
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(l, "#") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
hashFile := strings.Split(l, " ")
|
|
||||||
if len(hashFile) != 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
file := hashFile[1]
|
|
||||||
url := base + file
|
|
||||||
dst := filepath.Join(ucache, file)
|
|
||||||
if err := csdb.DownloadFile(url, dst); err != nil {
|
|
||||||
log.Print(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
298
internal/download/download.go
Normal file
298
internal/download/download.go
Normal file
|
|
@ -0,0 +1,298 @@
|
||||||
|
// Copyright 2019 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library 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 Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Package download implements checksum-verified file downloads.
|
||||||
|
package download
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"iter"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChecksumDB keeps file checksums and tool versions.
|
||||||
|
type ChecksumDB struct {
|
||||||
|
hashes []hashEntry
|
||||||
|
versions []versionEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
type versionEntry struct {
|
||||||
|
name string
|
||||||
|
version string
|
||||||
|
}
|
||||||
|
|
||||||
|
type hashEntry struct {
|
||||||
|
hash string
|
||||||
|
file string
|
||||||
|
url *url.URL
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustLoadChecksums loads a file containing checksums.
|
||||||
|
func MustLoadChecksums(file string) *ChecksumDB {
|
||||||
|
content, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
panic("can't load checksum file: " + err.Error())
|
||||||
|
}
|
||||||
|
db, err := ParseChecksums(content)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("invalid checksums in %s: %v", file, err))
|
||||||
|
}
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseChecksums parses a checksum database.
|
||||||
|
func ParseChecksums(input []byte) (*ChecksumDB, error) {
|
||||||
|
var (
|
||||||
|
csdb = new(ChecksumDB)
|
||||||
|
rd = bytes.NewBuffer(input)
|
||||||
|
lastURL *url.URL
|
||||||
|
)
|
||||||
|
for lineNum := 1; ; lineNum++ {
|
||||||
|
line, err := rd.ReadString('\n')
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
switch {
|
||||||
|
case line == "":
|
||||||
|
// Blank lines are allowed, and they reset the current urlEntry.
|
||||||
|
lastURL = nil
|
||||||
|
|
||||||
|
case strings.HasPrefix(line, "#"):
|
||||||
|
// It's a comment. Some comments have special meaning.
|
||||||
|
content := strings.TrimLeft(line, "# ")
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(content, "version:"):
|
||||||
|
// Version comments define the version of a tool.
|
||||||
|
v := strings.Split(content, ":")[1]
|
||||||
|
parts := strings.Split(v, " ")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return nil, fmt.Errorf("line %d: invalid version string: %q", lineNum, v)
|
||||||
|
}
|
||||||
|
csdb.versions = append(csdb.versions, versionEntry{parts[0], parts[1]})
|
||||||
|
|
||||||
|
case strings.HasPrefix(content, "https://") || strings.HasPrefix(content, "http://"):
|
||||||
|
// URL comments define the URL where the following files are found. Here
|
||||||
|
// we keep track of the last found urlEntry and attach it to each file later.
|
||||||
|
u, err := url.Parse(content)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("line %d: invalid URL: %v", lineNum, err)
|
||||||
|
}
|
||||||
|
lastURL = u
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
// It's a file hash entry.
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) != 2 {
|
||||||
|
return nil, fmt.Errorf("line %d: invalid number of space-separated fields (%d)", lineNum, len(fields))
|
||||||
|
}
|
||||||
|
csdb.hashes = append(csdb.hashes, hashEntry{fields[0], fields[1], lastURL})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return csdb, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Files returns an iterator over all file names.
|
||||||
|
func (db *ChecksumDB) Files() iter.Seq[string] {
|
||||||
|
return func(yield func(string) bool) {
|
||||||
|
for _, e := range db.hashes {
|
||||||
|
if !yield(e.file) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DownloadAndVerifyAll downloads all files and checks that they match the checksum given in
|
||||||
|
// the database. This task can be used to sanity-check new checksums.
|
||||||
|
func (db *ChecksumDB) DownloadAndVerifyAll() {
|
||||||
|
var tmp = os.TempDir()
|
||||||
|
for _, e := range db.hashes {
|
||||||
|
if e.url == nil {
|
||||||
|
fmt.Printf("Skipping verification of %s: no URL defined in checksum database", e.file)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
url := e.url.JoinPath(e.file).String()
|
||||||
|
dst := filepath.Join(tmp, e.file)
|
||||||
|
if err := db.DownloadFile(url, dst); err != nil {
|
||||||
|
fmt.Println("error:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// verifyHash checks that the file at 'path' has the expected hash.
|
||||||
|
func verifyHash(path, expectedHash string) error {
|
||||||
|
fd, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fd.Close()
|
||||||
|
|
||||||
|
h := sha256.New()
|
||||||
|
if _, err := io.Copy(h, bufio.NewReader(fd)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fileHash := hex.EncodeToString(h.Sum(nil))
|
||||||
|
if fileHash != expectedHash {
|
||||||
|
return fmt.Errorf("invalid file hash: %s %s", fileHash, filepath.Base(path))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DownloadFileFromKnownURL downloads a file from the URL defined in the checksum database.
|
||||||
|
func (db *ChecksumDB) DownloadFileFromKnownURL(dstPath string) error {
|
||||||
|
base := filepath.Base(dstPath)
|
||||||
|
url, err := db.FindURL(base)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return db.DownloadFile(url, dstPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DownloadFile downloads a file and verifies its checksum.
|
||||||
|
func (db *ChecksumDB) DownloadFile(url, dstPath string) error {
|
||||||
|
basename := filepath.Base(dstPath)
|
||||||
|
hash := db.findHash(basename)
|
||||||
|
if hash == "" {
|
||||||
|
return fmt.Errorf("no known hash for file %q", basename)
|
||||||
|
}
|
||||||
|
// Shortcut if already downloaded.
|
||||||
|
if verifyHash(dstPath, hash) == nil {
|
||||||
|
fmt.Printf("%s is up-to-date\n", dstPath)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s is stale\n", dstPath)
|
||||||
|
fmt.Printf("downloading from %s\n", url)
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("download error: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return fmt.Errorf("download error: status %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(filepath.Dir(dstPath), 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download to a temporary file.
|
||||||
|
tmpfile := dstPath + ".tmp"
|
||||||
|
fd, err := os.OpenFile(tmpfile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dst := newDownloadWriter(fd, resp.ContentLength)
|
||||||
|
_, err = io.Copy(dst, resp.Body)
|
||||||
|
dst.Close()
|
||||||
|
if err != nil {
|
||||||
|
os.Remove(tmpfile)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := verifyHash(tmpfile, hash); err != nil {
|
||||||
|
os.Remove(tmpfile)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// It's valid, rename to dstPath to complete the download.
|
||||||
|
return os.Rename(tmpfile, dstPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// findHash returns the known hash of a file.
|
||||||
|
func (db *ChecksumDB) findHash(basename string) string {
|
||||||
|
for _, e := range db.hashes {
|
||||||
|
if e.file == basename {
|
||||||
|
return e.hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindVersion returns the current known version of a tool, if it is defined in the file.
|
||||||
|
func (db *ChecksumDB) FindVersion(tool string) (string, error) {
|
||||||
|
for _, e := range db.versions {
|
||||||
|
if e.name == tool {
|
||||||
|
return e.version, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("tool version %q not defined in checksum database", tool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindURL gets the URL for a file.
|
||||||
|
func (db *ChecksumDB) FindURL(basename string) (string, error) {
|
||||||
|
for _, e := range db.hashes {
|
||||||
|
if e.file == basename {
|
||||||
|
if e.url == nil {
|
||||||
|
return "", fmt.Errorf("file %q has no URL defined", e.file)
|
||||||
|
}
|
||||||
|
return e.url.JoinPath(e.file).String(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("file %q does not exist in checksum database", basename)
|
||||||
|
}
|
||||||
|
|
||||||
|
type downloadWriter struct {
|
||||||
|
file *os.File
|
||||||
|
dstBuf *bufio.Writer
|
||||||
|
size int64
|
||||||
|
written int64
|
||||||
|
lastpct int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDownloadWriter(dst *os.File, size int64) *downloadWriter {
|
||||||
|
return &downloadWriter{
|
||||||
|
file: dst,
|
||||||
|
dstBuf: bufio.NewWriter(dst),
|
||||||
|
size: size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *downloadWriter) Write(buf []byte) (int, error) {
|
||||||
|
n, err := w.dstBuf.Write(buf)
|
||||||
|
|
||||||
|
// Report progress.
|
||||||
|
w.written += int64(n)
|
||||||
|
pct := w.written * 10 / w.size * 10
|
||||||
|
if pct != w.lastpct {
|
||||||
|
if w.lastpct != 0 {
|
||||||
|
fmt.Print("...")
|
||||||
|
}
|
||||||
|
fmt.Print(pct, "%")
|
||||||
|
w.lastpct = pct
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *downloadWriter) Close() error {
|
||||||
|
if w.lastpct > 0 {
|
||||||
|
fmt.Println() // Finish the progress line.
|
||||||
|
}
|
||||||
|
flushErr := w.dstBuf.Flush()
|
||||||
|
closeErr := w.file.Close()
|
||||||
|
if flushErr != nil {
|
||||||
|
return flushErr
|
||||||
|
}
|
||||||
|
return closeErr
|
||||||
|
}
|
||||||
1897
internal/era/eradl/checksums_mainnet.txt
Normal file
1897
internal/era/eradl/checksums_mainnet.txt
Normal file
File diff suppressed because it is too large
Load diff
183
internal/era/eradl/checksums_sepolia.txt
Normal file
183
internal/era/eradl/checksums_sepolia.txt
Normal file
|
|
@ -0,0 +1,183 @@
|
||||||
|
ab7f6d4f4eba0267406f783b75accc7a93dece520242d04fed27b0af51d79242 sepolia-00000-643a00f7.era1
|
||||||
|
9cae627459d13ed3d05f6812eefa93242555fbefd27aa0927b40b62d44feb2e9 sepolia-00001-f3ea493e.era1
|
||||||
|
a6f691585bc74fae6c445a8985f0df342e983ba1ef569018befb4b21b30891e2 sepolia-00002-dcb01f4d.era1
|
||||||
|
1add5a98a9e6c15a667d6a7bbdaea115893019f3033664c54c2dcab70829268d sepolia-00003-18b32584.era1
|
||||||
|
5e5ce2ca04b0f1aef6f026214cb64d16f76606fb5b5baf8a462f4a851dda0513 sepolia-00004-e93b3f11.era1
|
||||||
|
db6f7687e9826a4e4dbc1361d666a8f2aa735eae309f63d9cadf6b27a899277b sepolia-00005-90918472.era1
|
||||||
|
f61118a4e1cb718bdb71fde1daf84e59c3524b1e241ea1a5e2112d8c53acc625 sepolia-00006-a4a583b1.era1
|
||||||
|
fd0c6fc443a4c30617ce88a48e7438fecaf4cf4772034511fa51433c7aae181a sepolia-00007-ef1e0a86.era1
|
||||||
|
95bc416c6393e0a579f9e4af42421cf861e43354d49273d369bcf9b38e070246 sepolia-00008-dfe1e6dd.era1
|
||||||
|
8262e7182f9a8abb9f5fc05d745a3cd2061eacda5c87cc2533af2407bd20672c sepolia-00009-dba60c04.era1
|
||||||
|
d7c123c8e06713df23db0046df7c45d873e153ff076357b6d0c5bc9b0081a246 sepolia-00010-c99c706d.era1
|
||||||
|
7fd359358ebf710c5c61a6a65551a4cb91c2160dbd7b59ed355a4d7e10f29328 sepolia-00011-04513853.era1
|
||||||
|
9d6ea741d1b91e1c2c37ac1659ef3516d4968b6e7c164d347e534c4bebe644fd sepolia-00012-6e07302d.era1
|
||||||
|
ce3e580fd3e5cb9e0ba9ebffae948406fdbe59f96fa3a05a30a381bc7d1b583e sepolia-00013-25709be2.era1
|
||||||
|
15df46dc8c2cfd402f302f2925d1c50ac12538d394e8687920f53346429d6743 sepolia-00014-7c90e5a5.era1
|
||||||
|
1985aaeb96c81279b978beab1145b3baf460b12bff86b8ca05c5067e1b0ca25b sepolia-00015-fadc08ab.era1
|
||||||
|
2a53126cd443e9310fa15180c5bbbdb76f257d7fee67159500d8efa0ed13bef4 sepolia-00016-d44b2499.era1
|
||||||
|
e783cff42ba7a53461c7fcdaf59a5387e7091a1230aa6761af05e7daf59b4805 sepolia-00017-02451eb3.era1
|
||||||
|
92848afbeea81397fc9790e7fba7c627c01c17cbc5b8961a48ca60329bbfa3f3 sepolia-00018-cdb53b11.era1
|
||||||
|
d15ac188a4dfac75818dfeb04e72c2f269d9f985100a457ebdc17661e36f0d83 sepolia-00019-fa770019.era1
|
||||||
|
e8ef40780f55a0b640d1b68e1a89d1d438e8105bb1388bffc8a743b88e357671 sepolia-00020-15fc80e7.era1
|
||||||
|
8041d790a0c50044e331a385eb98b17f5050ab25637b6a4f2c3e8e99f3f81e6e sepolia-00021-b8814b14.era1
|
||||||
|
f17adda073b66cc97cb58acc7b776296e135943718dab9fc7cfecbac748419b7 sepolia-00022-87944c43.era1
|
||||||
|
159a5ec4fa1e79be6d13fe4753eaa8994f547e31783069082a39d51e3e00b178 sepolia-00023-516a30f5.era1
|
||||||
|
5b75090c9f26899aa6fe504963b7f1a43ab13a8fd7547da42f9ca9cc980511de sepolia-00024-b922dde6.era1
|
||||||
|
d5c39668b309b43eec720b2185ac23ef819cc7fa92e35f2e6c9a4ce9ee1c249d sepolia-00025-a081e175.era1
|
||||||
|
188b5119e110feec067dc06aefde7decba2a87bde642cc1735391a1782a99b03 sepolia-00026-d3931a12.era1
|
||||||
|
3114068f4eb9cf8277d4417da371e820f0bf1a654600f8dea149f9854523d1f2 sepolia-00027-43f5e0ac.era1
|
||||||
|
b18597daf66f27976638a84a4cac5622f7d53fca8264dac3665f84f28435cc26 sepolia-00028-dae08170.era1
|
||||||
|
d0672ee04c5a84e881c4acc3757e16f963c57b5b69da81a674498c1b06159f3e sepolia-00029-17818143.era1
|
||||||
|
122dff18aaac0bae0a006da088794f638ee95ddaa1e0a347ca9a8eca816471c1 sepolia-00030-cc7fc4eb.era1
|
||||||
|
42c2206fda093c7aa6b31fad1addf2dd14e862b4686b0e17050bd284dc479cfd sepolia-00031-3171eede.era1
|
||||||
|
42b023d07a02ad739b61ec8297c43501ddac200d1866fa3203df466d2442c4b0 sepolia-00032-9e2ff5ca.era1
|
||||||
|
2461fb005a9a62cf6ab52a0b3505abde9828c1f36def4fa12a547ebb5e8c89c0 sepolia-00033-bc921023.era1
|
||||||
|
2ca97708fba029693d7754a85424d29f57ee296a48fcbde02597603e7e90525d sepolia-00034-b2496634.era1
|
||||||
|
d2944e1cbd7a3276ca5ef9d4b4473557a65e58bd512029b2fb00d41f618ac551 sepolia-00035-f3add4f1.era1
|
||||||
|
c7f63d7230dd7ae9c57c33014d56bd4c921f5db95f3872bf6745d03bc64b5cc0 sepolia-00036-0db12924.era1
|
||||||
|
dc027011c543418fcfdd7897cea3356de16122f676da2459d78da5369cdc2b8f sepolia-00037-ae8bdd13.era1
|
||||||
|
9be2262ef4e1e1b93cee3316ad473895bd7a7f3bff15ac8c18c1a157292ab7f6 sepolia-00038-81f80b03.era1
|
||||||
|
56c03c86028c95d431030fa2728493ca8001cc86eec3f5d7a558224e550b42ef sepolia-00039-03e5d6f1.era1
|
||||||
|
d64e65ecfcf480c2edbdebac350ac899b25874a522c226695a46fab17de5b15c sepolia-00040-296fe287.era1
|
||||||
|
18226ba389f150581b36a234d877fad9c08c0fa691a366ba11f808ec9110cf67 sepolia-00041-5cd87470.era1
|
||||||
|
0d2960f23933deadcdfe545fc8aba626e79c0c010fad0ac01b577ca5b99840e0 sepolia-00042-de86936c.era1
|
||||||
|
b14f66c2fc74d0f772eb75c548a86da4bf160cf1067091c65c05a90812e7af1f sepolia-00043-ede4e682.era1
|
||||||
|
15d704883fadfcbe4ce3520bc9ed5fcff98a8a419b6fa9f832394008ae1941aa sepolia-00044-4d49a81a.era1
|
||||||
|
5343d8015eb0a6b8728b571fd0e7a2bb92728ec593469b0a9d8d2082a5afc36d sepolia-00045-7071c19b.era1
|
||||||
|
63efe606b8c687aeeff4d2ff5596a72da862d024f9dbb1ff5bf67ca4f2921f77 sepolia-00046-4f4fe79f.era1
|
||||||
|
9eb6dd3772ebfe63c37a22423310a5cb36c0d09defd66d7aa02a5b4c1283ca80 sepolia-00047-76b58fb3.era1
|
||||||
|
82afedad4e56f86d836446f0c71616f8d88fdec11b93a6360dacc111cdb12db2 sepolia-00048-0fa9d93c.era1
|
||||||
|
8a86584323e83a11499265f33ddabe0de1517b51c8e73985fa6eed15cfd63d0b sepolia-00049-d193ff47.era1
|
||||||
|
b66621a148fb7acce05efe6aab89be5a965463a4c4c95a3350e64f3e8a684417 sepolia-00050-736b969f.era1
|
||||||
|
49d47547284339de6f92e6495347dce0c45c5294f07c1b96fcfa510c43cfc206 sepolia-00051-466eb482.era1
|
||||||
|
b075750d6efdde0cd755c90cd893b2917c56ab8674022fb36bb7a1305dc9bfed sepolia-00052-9752212c.era1
|
||||||
|
36eb486d7dab70e759c72198b9f2505ac3fcb7f6a0ae2cca82fd9ade3d65e86d sepolia-00053-b2897233.era1
|
||||||
|
9f024ea4e135fe6939f2fedb6a6821d61202139fa35378616fcf0274bdeac172 sepolia-00054-aedad6a8.era1
|
||||||
|
2bc46d10add9c9903d554523f6ed0c8f5a632521c659d9f3f4af4c444221d08c sepolia-00055-19af5091.era1
|
||||||
|
21900526ff5f0f31257bdabaae3fffa700eccd3cf3a6ba346b4bdf4347c97324 sepolia-00056-9ac921a1.era1
|
||||||
|
c9a47d7db6af1b984e6ad8de2ea0eb0e73e442281d4fa497ba64c02caaf0b7d0 sepolia-00057-240f011a.era1
|
||||||
|
10d38d3b6b66367a1c05922040073d6a62a2cbb843ea821dbd02f4d9f037c740 sepolia-00058-06d606d6.era1
|
||||||
|
d391f44dfa9bab3ced35676cd964daeffe0f4e4028c3bd44fe41010b761c5229 sepolia-00059-8ea69a55.era1
|
||||||
|
39a1bdf4d7c1dd8365f635c8da568b34d1c2e59aa5d7fe8745d6b7db2cf2c6ed sepolia-00060-d22079b0.era1
|
||||||
|
cf41ae65b6391d8c44bb0128e66591a96d42fea0a7d35c30c8eca5bbe0790578 sepolia-00061-834d00e3.era1
|
||||||
|
9bcff9fa47c2addc140857b23d6453583ca526c6cdf11144583db24acf33b1b2 sepolia-00062-d1230cc2.era1
|
||||||
|
984c5662f6f36ece4e0ca9efc47dcb3e50e0aa581a874a680736f2f558cdd889 sepolia-00063-e1e9fa40.era1
|
||||||
|
7582532b67144ea9499c28d935e7ac4bbf8f465c382a8a97ce04e56539b8c9af sepolia-00064-7f827781.era1
|
||||||
|
945e3da724660df934484d68868d54fbfaa5feaacdd635f6185b56e88858ad8d sepolia-00065-9d8993d6.era1
|
||||||
|
77fdff04d6d6233c4bab9811ad559a328fdb0447070b96e2f38ea7450639a00e sepolia-00066-8d516260.era1
|
||||||
|
3486301cf48b1f727b46c0223c4a018e03d0857efedc2f616ea850b539139f93 sepolia-00067-7466141b.era1
|
||||||
|
2954560d2b1c6ff48242312b7f5fe902c53dc93a3d1c7d9abf41633926ef72ac sepolia-00068-1bd11e8a.era1
|
||||||
|
8f27de663991d7e4e8a7005dee7b94f991cfefad743507362eca5ad83f53e449 sepolia-00069-536ecd2c.era1
|
||||||
|
2d0fa324482aab3db61718abfb2a5c084a55db5f39c4bce125255eec9a56a01a sepolia-00070-b27f7c5c.era1
|
||||||
|
472b1c9ec83d9b6564a45d4363c856604d21b53d8c89a7b5810f2596c14f710a sepolia-00071-d2ef5349.era1
|
||||||
|
3a0e6051e4c5f1a263b51f7a827d2276c7b25cb9d426f24f39fca33a7e76d623 sepolia-00072-b23578a9.era1
|
||||||
|
734b46f0bb7150e1d705bff53a0cdedd3ed57ca1461f06deb517c8b74c4b25c9 sepolia-00073-8bacb416.era1
|
||||||
|
28091f1a78dc398e2d47212087f652c8d35f07cda5457ee691b4191c3a6698c8 sepolia-00074-0e81003c.era1
|
||||||
|
b743e3e1721e593562d9a144f49a927bbe2db70be9e4884521cae9500b0004cb sepolia-00075-09177034.era1
|
||||||
|
6850f49599b95dcf1db54a3c5d126654ca21de4df27b7a24bc35c4d10fe6cdb7 sepolia-00076-0f2898a3.era1
|
||||||
|
da57f20ff5a3944bd1fef22e7c914f2eda3e79dd7346b0e7c0a01e69cf538d7e sepolia-00077-61de3538.era1
|
||||||
|
fc20551b69bacce502fa42ba5b0a223c9fb558d5f495948e38c060328f1ceb7e sepolia-00078-be407b86.era1
|
||||||
|
ab72961502e1d8055886d744c077dbf60db8acba64c7879109b3ed573ce4cd02 sepolia-00079-d5c957be.era1
|
||||||
|
216b65dba90adf1288d40fc689f6087ff2435bd379df482b130cd464cbd1d490 sepolia-00080-ae6ce310.era1
|
||||||
|
e824e04c1cf15f142bdbb40161b64953767abd3ec7c40a726ce69d4ce646df08 sepolia-00081-daac26d6.era1
|
||||||
|
d70d059609dda26e25af96d32b646c364e2ae44fe7b1cf9e2e8589d731e9f381 sepolia-00082-ecb8f0c2.era1
|
||||||
|
a7dc282ca7fd7aa8d42693363aec655d6cfea330220233c7aad3f7955284d650 sepolia-00083-5b017cd4.era1
|
||||||
|
2f1006528caa6d47729c4ef0a699c441cb5d2c201afb6563ab51184429294020 sepolia-00084-8c1f92cd.era1
|
||||||
|
374494754cfd728b1b4e4a00db810012346ba8c4dc9cf2ad1d80187999ab868b sepolia-00085-9180667c.era1
|
||||||
|
7ff9b360a0656ddbace4032d249f5cf06068ee3f8be526d2a4ae0ed6eaef046f sepolia-00086-1aca7add.era1
|
||||||
|
de592f4bde8de973d8c82a7d89dbe21cf75bb0b28204567ea4f5012b8be1f28c sepolia-00087-0e100944.era1
|
||||||
|
fb6eb1e1e1ce97e03b39fe2d53e3595d759701a65cc861d0568c04ee7e2ec03c sepolia-00088-fcdd7aa5.era1
|
||||||
|
4d178ce50f5436f5ba2347d87dc79e9a2e18c17d3056eb77c7c8dbb9b03bb986 sepolia-00089-b23b2368.era1
|
||||||
|
21e919f0d32137e7924d54ddcf1e697bb5b57bbb7cc12a169475df993c4c1d9f sepolia-00090-b78edeb7.era1
|
||||||
|
cb7ae025ca17c5f52ef7910147a47a0d3680c26f9d2d109d6af330f93f95b735 sepolia-00091-4c81cdca.era1
|
||||||
|
1e0249554f150b63cb7dd57c53c624c197b415401c0a1f3986c2f06e8aaa795c sepolia-00092-ddbf3a4c.era1
|
||||||
|
8d950d8c13a4e002514a47e6d6ebcad4bf7e5808398641a5c04863d9c90f0f87 sepolia-00093-a52a45ec.era1
|
||||||
|
b8720324f04dca4e0046b12ec3b8a2b862158f3156c4d1fc2947340b1fc1a9cb sepolia-00094-839ea4e5.era1
|
||||||
|
8c4134bbb21d36fbd7ba608eb1e485e76cb0fd56c4aa8032be1b6848757856d9 sepolia-00095-bccc0958.era1
|
||||||
|
e3ad213e760ea2320ff52caf5de32ced37d6b7ad0f99384785ca42a7e58d1cd9 sepolia-00096-429b2f39.era1
|
||||||
|
9c324e109f77ebc1750dd2a0941904ff820047def91f8b832e61c01b31297f38 sepolia-00097-6f74184d.era1
|
||||||
|
26efc4469736b283fd80d9105fc85a70dbec209fbb0e1e40a905a9406e1bb3cc sepolia-00098-40286059.era1
|
||||||
|
e14134b3aad0533366f9aeb19cf2561cf1e81b989a0b83f12b05493948dd71ea sepolia-00099-dd1ef4f4.era1
|
||||||
|
7283a437244545e784d51186c0d7d4809808dd472850ca3cd2d7a40f4883b50d sepolia-00100-bd3d6bf4.era1
|
||||||
|
b67270e94377703aac798d8323c0a91b451d36806775013a2b8b12db04797851 sepolia-00101-41676e0d.era1
|
||||||
|
dd6ea805242a67c6255867f64fc5fbc941a98318b5ea1d5e2d42d42c6910493c sepolia-00102-4f3c1fe1.era1
|
||||||
|
d6f6da94b16a201b304936cb5f798b6fedba76ead056cd2c4dc860c21d82b8dd sepolia-00103-e13a95f0.era1
|
||||||
|
e2309561f7a86eddfe182dc86fb38e5454c5a272cc77b3d0d7c6ce987ddc874c sepolia-00104-4e49b068.era1
|
||||||
|
deff905084758be35bf0b8cb640b1b283232abbc6153388a4275c767ea0bdf0e sepolia-00105-4ab7d9ea.era1
|
||||||
|
964f9679a39fe06192a7025f14404cb1f6542071665f1c2f739f62fdb55e9274 sepolia-00106-5ec678af.era1
|
||||||
|
8fd75370992f97e77a2cd3663bc1fb9b2bcec9b64a0439de7846d7c6321e6d93 sepolia-00107-44721c55.era1
|
||||||
|
7e8219aa13d6a7b32c3a69888a2c5f446703eada3fdcde841ba26edfe3cb4453 sepolia-00108-3b4ad768.era1
|
||||||
|
24e8d2f8d01abf63d97365bafa914dd5203f4e807bc6655dc0673b9f87215ebd sepolia-00109-16e54758.era1
|
||||||
|
edadb0ebec6c4b7b54237e5802cc59fd962e23cf690ff10ab52c10258d7b7827 sepolia-00110-b5da103b.era1
|
||||||
|
4faebfbdc009188cc8cf0ffe4aeca9c93ac5b3abe763cfb165d8bed37b12b51c sepolia-00111-15c53aba.era1
|
||||||
|
40210479bd14ca3f8db76b288c2ed4e8f3d5b5ce8116ba8d8fe3133b0cc765ad sepolia-00112-8e2144e3.era1
|
||||||
|
92ed406d564ed1504bc7ff91298a419fe72a710585256b1830040ba3d1adbde8 sepolia-00113-1e1aac0a.era1
|
||||||
|
7e771bba1aa9c87be155a9a7d62d3479240f21934a3d763cafa5f3cd4ce49f8a sepolia-00114-0d7a4b23.era1
|
||||||
|
51d71ed3674f5d70f719b3251e29a0825f2573b1bfd1a92940b013806aaf4fd3 sepolia-00115-8e983a59.era1
|
||||||
|
6abea02d82f7a30b67d6de4516bbc711855eea3d891ea42fa0124c101f981f86 sepolia-00116-1708ba6e.era1
|
||||||
|
88e7222e35b1275483b6eec11bf8d4417602fdb16b611d6c154abd8d0ec0b49a sepolia-00117-86962046.era1
|
||||||
|
6ddeae324cc17337fa23c71b242ab1675b72f43fb393af7e971edec20451f864 sepolia-00118-934e9f5f.era1
|
||||||
|
914c35142f7244d07b670b1f990c8bb5997119db451e8dcd8a67c8d71fcc6af2 sepolia-00119-4d88db3e.era1
|
||||||
|
eedec437c182f330f2f5f3c530cce3bc59f0c99c9e6cd36288a588adba7679bb sepolia-00120-1a3274d5.era1
|
||||||
|
4914f6a8cf63cb806c5736431f6402d9a99c2de953da05a17069fa15cf8c695b sepolia-00121-62b79757.era1
|
||||||
|
b9a0138c77dcbb2b4784813204ec8bc30407cd8daf3501707719985acff4a1cb sepolia-00122-41d14652.era1
|
||||||
|
57453e0eaa2bf46a13c94d9e96ee98943a46012c2bdd9da5d7c77064e803fbfd sepolia-00123-67f86708.era1
|
||||||
|
1e927fdb13cebda45a963c8cec921b16c795fb3020cad9054fa1fb7f4cb528cd sepolia-00124-488cb226.era1
|
||||||
|
8015846c8c3663005a8a6d59347fb672a602e6bbff028989355a01f5f6d2b183 sepolia-00125-8a21f7da.era1
|
||||||
|
8807f6abe63b76f31d99cf14404fee8bb5eba3a814e2d08b9d85540ef9742550 sepolia-00126-6569478a.era1
|
||||||
|
28dc46c8120426b395cf9b5ff781962a3212766b24511b2dc3d2172568b10021 sepolia-00127-1312ab23.era1
|
||||||
|
ed66119ec9dae6c61952cbf40b9dc5770cd80e659a83dd18e1a5f2ffa947a04b sepolia-00128-1c95bdc6.era1
|
||||||
|
a4a68dd54b77f21939ddb1ec9ef9ae0a3551d6d53b8f9578b01ad8e32e5c8d8e sepolia-00129-f32fdefa.era1
|
||||||
|
e54d6d4b95a985f37391750b3bf3bc0f9f4e2f790805797ee198d77287433ca2 sepolia-00130-8f31c590.era1
|
||||||
|
577fe68c5c33e47af8823ac573709ed9d63df0d7f9aec02a7907e3e6e5922d67 sepolia-00131-5aaad354.era1
|
||||||
|
c3beada9620c55b6d0dbdab8f66b735da0b3d62d5c5eb2c3b5369619549fb3e0 sepolia-00132-2147d970.era1
|
||||||
|
12843e128e76d5636f857c8c6801e433757fc4f8aba159bc2d4e6c41f2c94abd sepolia-00133-690cc16f.era1
|
||||||
|
e5e4904ec83ebcebccc1aae1de89622a50bf6f735d985234da88fa1ced1787c0 sepolia-00134-fe276e6b.era1
|
||||||
|
9c618458f6d76a09f16c86baf526dc7f2310ce92d862f73392144987c04b39ed sepolia-00135-ce40efd0.era1
|
||||||
|
1d032d1279c3841c65385f85463226df04a98a3ccc5d7bf461f042492230615c sepolia-00136-adc6e1bb.era1
|
||||||
|
a3aa401ca2bc8cb8b104667c6f68e28d00e37813a1a993d8abd1ec5c3555f71d sepolia-00137-805a333a.era1
|
||||||
|
105c1e50e73715bf58d42f6bbe49bff36dc0051bfb477efc77ebf14b5ba98974 sepolia-00138-65ff170f.era1
|
||||||
|
ad53fc0e4d2718ce105fa3a7ce5574dbb945f49b2fa7470a406179c45738fc46 sepolia-00139-1c86d86a.era1
|
||||||
|
c27ae427ca0052686a3ac8a3e568d1dae30449f9edba465f218c147074d61ccc sepolia-00140-e7f36280.era1
|
||||||
|
143d09ffb26cd2a1bb334958d92b77a92a8f9ac99c01059d22bee4e4d8bc3212 sepolia-00141-5d3d0d5a.era1
|
||||||
|
378a77f0b0c4072297de4c382cece46be521a6c6935db8eb499dd99857d566aa sepolia-00142-e45aa418.era1
|
||||||
|
35855d5bd75dff01248dd5b2080babf6127d87c157a987124f7a258cfb9a5ecb sepolia-00143-70379f59.era1
|
||||||
|
58f07f98d8e9486acd01ec212518b1c0d7764e68263f9b1e9c51cf5e597a0b6b sepolia-00144-c345e75c.era1
|
||||||
|
17caa603e2d1f9c01c9117754f0bb4c8ce42c58da806f758f99468d9f8c1c47b sepolia-00145-9f4bb4c5.era1
|
||||||
|
d05a92b8fe1306397b98a6202b18f66c9bc259bd41c25b3d8211c09813e3a9ac sepolia-00146-3336d26b.era1
|
||||||
|
b7120a656d06d4010a06a2abadd809ee7f26707cf313e9de5cb1bd172745ad23 sepolia-00147-6b007873.era1
|
||||||
|
aa45cc5747e9c21ccfc44c87f98717702551a0511f423b2624a34af58ba798fc sepolia-00148-35f8a5b4.era1
|
||||||
|
952843d531ea4d9ee9c456e50e77c50f75a559bff13326dd593271f90cd55129 sepolia-00149-01b6e9ca.era1
|
||||||
|
2d93d1d84e0f4013d6daab61d66907488223a2f1b50e4a22d3ba1da24a9c6a84 sepolia-00150-0c372e80.era1
|
||||||
|
6b516e3f31720c54453c5ce9bbac68300615f455a7cf4b1dc6592a5d5432ee89 sepolia-00151-c15a5955.era1
|
||||||
|
82023cab017453e8fcf49ace9befbaef4af167e0e6c6976456bf251f803b5d51 sepolia-00152-ef7ac893.era1
|
||||||
|
6ace677a17380127349ba0710fd02ff38ba185526cc2d9bbb6f05375d6f163f6 sepolia-00153-0e4073c2.era1
|
||||||
|
66db5e0786cb9963ed66a0e31773b30d3405621a7636c62ffbb0b84ff41705bd sepolia-00154-298c3549.era1
|
||||||
|
cf797283087a079773770c1bebfecb113a4155e0d77d5713c702478e2c24f64a sepolia-00155-a99309bb.era1
|
||||||
|
b143cc994d0b544cd08b2a1f66308718ea49f3f68adef8e62300e91f8048130a sepolia-00156-8c33148f.era1
|
||||||
|
cbc20f9f767324438a237c3114a1c4e65dda74d4a655d55a7cc3e63e62f373f8 sepolia-00157-ab02fc10.era1
|
||||||
|
c8368d1752e66bcd7cabc653477f9aec0071027f91112a8cf71a945db23404e7 sepolia-00158-17992856.era1
|
||||||
|
03a1a66eab3a7cbc63b29cfbe2e70285940e64db4af6d23ab70592c820f8d3ea sepolia-00159-b3448cf6.era1
|
||||||
|
bf9dcd8400e56c58cd3280f0a3d7aab23a7eaf594e67db5387f40101ad8e8c92 sepolia-00160-acad7054.era1
|
||||||
|
03a3b975b2a78a4b28afd9ee089440465ceefd32bd1d82b74ae69caa80b6fb6c sepolia-00161-36e611bd.era1
|
||||||
|
ddf201b59ebc77eccdec97b0a5a7d7a89dda02928ff7c5cca8868b5aba84f533 sepolia-00162-3557cc58.era1
|
||||||
|
041be469daf6f7abaa0cfe66c4d68b7d9d223383553c16f078edb3a1fa3d0d92 sepolia-00163-638b9afe.era1
|
||||||
|
68d151048412318cbd0af98efa27d7ee16e2fc58830d93b4d76b66cb632ebfc3 sepolia-00164-fbc67b64.era1
|
||||||
|
3025771380736a5de790ead991c0512a4c73106fd1d2f169863d395964238ac1 sepolia-00165-442b83a1.era1
|
||||||
|
6d6b1273a6fc353c6999b4c9be301660a404b78cced911e230991305da990e3c sepolia-00166-018915fc.era1
|
||||||
|
01052c4f779e4ec276498d4192b8ad11ad6d2d3992224bb62ea882a80deda935 sepolia-00167-0df2b4c5.era1
|
||||||
|
2f8f3034eb7e41c5add0e639e21b52c0794f36bb5918274849402c5fe0d247e6 sepolia-00168-7cab66f5.era1
|
||||||
|
504b0203d995e5ef6aee679b048c276f45b05dc68a6fd319a0f4f0133b230a77 sepolia-00169-75850131.era1
|
||||||
|
b277df79fe5a83189f9619b275a9aaab63db8506539c56997be61fd5a3552bb9 sepolia-00170-c2044b78.era1
|
||||||
|
1af0a1778fc5d2a53aaca06dbda6671b91ae3f56da73a6c6708bc729e5fb9efd sepolia-00171-af44ff2e.era1
|
||||||
|
a977654c0ed243ccce7b5514914ec94d70a3a742ff014ac52b0654e21940f2da sepolia-00172-bd76a8d9.era1
|
||||||
|
146672b2e63c320c9474ecf41d7f32f31f5298512cc8f911074c88789849d935 sepolia-00173-b6924da5.era1
|
||||||
|
82af1c5bc51f9e9ae1be5d0e0827ea755d104ed373b374c08ef401115491671f sepolia-00174-2a61fac5.era1
|
||||||
|
627ee6cf9282191ff32d291fc0437b812bad01f158b5ca42ae717b7bbb125f8c sepolia-00175-1cc76405.era1
|
||||||
|
6ee38f117646a47f6ab2f666fa6460bda770c194ae4a29744534e9bfd9e7ba3b sepolia-00176-20daa2c6.era1
|
||||||
|
28e7e49e71dd8581cc484d8be89fb083a79577b06f26c1e3bfd4a57bdf24c5e7 sepolia-00177-379df7e4.era1
|
||||||
|
055c365cfeda3495a5e2b334803dfffe7af2a584745acabc42b5ad146b250534 sepolia-00178-88c9d2aa.era1
|
||||||
|
bca9a920232983636b7cf20fc8319f891df94ba0860f51b5a052290b58d9e3ae sepolia-00179-8329b80c.era1
|
||||||
|
e894a6f3ce0ede2825aaafa64122a5f458cd3b2ecaeae20a44a59fd668971bb0 sepolia-00180-22de0418.era1
|
||||||
|
83bfe0d7c49c3a108a4ed044119a3549f2f4b0f8e74c971150b6977b7d46a250 sepolia-00181-b3fbe6f2.era1
|
||||||
|
3b806177536ac3615f376440c0589696ea746eea0215bb3b175df8f4f1e73894 sepolia-00182-a4f0a8a1.era1
|
||||||
115
internal/era/eradl/eradl.go
Normal file
115
internal/era/eradl/eradl.go
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
// Copyright 2025 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library 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 Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Package eradl implements downloading of era1 files.
|
||||||
|
package eradl
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/internal/download"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/era"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed checksums_mainnet.txt
|
||||||
|
var mainnetDB []byte
|
||||||
|
|
||||||
|
//go:embed checksums_sepolia.txt
|
||||||
|
var sepoliaDB []byte
|
||||||
|
|
||||||
|
type Loader struct {
|
||||||
|
csdb *download.ChecksumDB
|
||||||
|
network string
|
||||||
|
baseURL *url.URL
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates an era1 loader for the given server URL and network name.
|
||||||
|
func New(baseURL string, network string) (*Loader, error) {
|
||||||
|
var checksums []byte
|
||||||
|
switch network {
|
||||||
|
case "mainnet":
|
||||||
|
checksums = mainnetDB
|
||||||
|
case "sepolia":
|
||||||
|
checksums = sepoliaDB
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("missing era1 checksum definitions for network %q", network)
|
||||||
|
}
|
||||||
|
|
||||||
|
csdb, err := download.ParseChecksums(checksums)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid checksums: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
base, err := url.Parse(baseURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid base URL %q: %v", baseURL, err)
|
||||||
|
}
|
||||||
|
if base.Scheme != "http" && base.Scheme != "https" {
|
||||||
|
return nil, fmt.Errorf("invalid base URL scheme, expected http(s): %q", baseURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
l := &Loader{
|
||||||
|
network: network,
|
||||||
|
csdb: csdb,
|
||||||
|
baseURL: base,
|
||||||
|
}
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DownloadAll downloads all known era1 files to the given directory.
|
||||||
|
func (l *Loader) DownloadAll(destDir string) error {
|
||||||
|
for file := range l.csdb.Files() {
|
||||||
|
if err := l.download(file, destDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DownloadBlockRange fetches the era1 files for the given block range.
|
||||||
|
func (l *Loader) DownloadBlockRange(start, end uint64, destDir string) error {
|
||||||
|
startEpoch := start / uint64(era.MaxEra1Size)
|
||||||
|
endEpoch := end / uint64(era.MaxEra1Size)
|
||||||
|
return l.DownloadEpochRange(startEpoch, endEpoch, destDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DownloadEpochRange fetches the era1 files in the given epoch range.
|
||||||
|
func (l *Loader) DownloadEpochRange(start, end uint64, destDir string) error {
|
||||||
|
pat := regexp.MustCompile(regexp.QuoteMeta(l.network) + "-([0-9]+)-[0-9a-f]+\\.era1")
|
||||||
|
for file := range l.csdb.Files() {
|
||||||
|
m := pat.FindStringSubmatch(file)
|
||||||
|
if len(m) == 2 {
|
||||||
|
fileEpoch, _ := strconv.Atoi(m[1])
|
||||||
|
if uint64(fileEpoch) >= start && uint64(fileEpoch) <= end {
|
||||||
|
if err := l.download(file, destDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Loader) download(file, destDir string) error {
|
||||||
|
url := l.baseURL.JoinPath(file).String()
|
||||||
|
dest := filepath.Join(destDir, file)
|
||||||
|
return l.csdb.DownloadFile(url, dest)
|
||||||
|
}
|
||||||
|
|
@ -30,6 +30,35 @@ type Iterator interface {
|
||||||
Close() // ends the iterator
|
Close() // ends the iterator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SourceIterator represents a sequence of nodes like [Iterator]
|
||||||
|
// Each node also has a named 'source'.
|
||||||
|
type SourceIterator interface {
|
||||||
|
Iterator
|
||||||
|
NodeSource() string // source of current node
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSource attaches a 'source name' to an iterator.
|
||||||
|
func WithSourceName(name string, it Iterator) SourceIterator {
|
||||||
|
return sourceIter{it, name}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureSourceIter(it Iterator) SourceIterator {
|
||||||
|
if si, ok := it.(SourceIterator); ok {
|
||||||
|
return si
|
||||||
|
}
|
||||||
|
return WithSourceName("", it)
|
||||||
|
}
|
||||||
|
|
||||||
|
type sourceIter struct {
|
||||||
|
Iterator
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeSource implements IteratorSource.
|
||||||
|
func (it sourceIter) NodeSource() string {
|
||||||
|
return it.name
|
||||||
|
}
|
||||||
|
|
||||||
// ReadNodes reads at most n nodes from the given iterator. The return value contains no
|
// ReadNodes reads at most n nodes from the given iterator. The return value contains no
|
||||||
// duplicates and no nil values. To prevent looping indefinitely for small repeating node
|
// duplicates and no nil values. To prevent looping indefinitely for small repeating node
|
||||||
// sequences, this function calls Next at most n times.
|
// sequences, this function calls Next at most n times.
|
||||||
|
|
@ -106,16 +135,16 @@ func (it *sliceIter) Close() {
|
||||||
// Filter wraps an iterator such that Next only returns nodes for which
|
// Filter wraps an iterator such that Next only returns nodes for which
|
||||||
// the 'check' function returns true.
|
// the 'check' function returns true.
|
||||||
func Filter(it Iterator, check func(*Node) bool) Iterator {
|
func Filter(it Iterator, check func(*Node) bool) Iterator {
|
||||||
return &filterIter{it, check}
|
return &filterIter{ensureSourceIter(it), check}
|
||||||
}
|
}
|
||||||
|
|
||||||
type filterIter struct {
|
type filterIter struct {
|
||||||
Iterator
|
SourceIterator
|
||||||
check func(*Node) bool
|
check func(*Node) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *filterIter) Next() bool {
|
func (f *filterIter) Next() bool {
|
||||||
for f.Iterator.Next() {
|
for f.SourceIterator.Next() {
|
||||||
if f.check(f.Node()) {
|
if f.check(f.Node()) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -135,9 +164,9 @@ func (f *filterIter) Next() bool {
|
||||||
// It's safe to call AddSource and Close concurrently with Next.
|
// It's safe to call AddSource and Close concurrently with Next.
|
||||||
type FairMix struct {
|
type FairMix struct {
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
fromAny chan *Node
|
fromAny chan mixItem
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
cur *Node
|
cur mixItem
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
closed chan struct{}
|
closed chan struct{}
|
||||||
|
|
@ -146,11 +175,16 @@ type FairMix struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type mixSource struct {
|
type mixSource struct {
|
||||||
it Iterator
|
it SourceIterator
|
||||||
next chan *Node
|
next chan mixItem
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type mixItem struct {
|
||||||
|
n *Node
|
||||||
|
source string
|
||||||
|
}
|
||||||
|
|
||||||
// NewFairMix creates a mixer.
|
// NewFairMix creates a mixer.
|
||||||
//
|
//
|
||||||
// The timeout specifies how long the mixer will wait for the next fairly-chosen source
|
// The timeout specifies how long the mixer will wait for the next fairly-chosen source
|
||||||
|
|
@ -159,7 +193,7 @@ type mixSource struct {
|
||||||
// timeout makes the mixer completely fair.
|
// timeout makes the mixer completely fair.
|
||||||
func NewFairMix(timeout time.Duration) *FairMix {
|
func NewFairMix(timeout time.Duration) *FairMix {
|
||||||
m := &FairMix{
|
m := &FairMix{
|
||||||
fromAny: make(chan *Node),
|
fromAny: make(chan mixItem),
|
||||||
closed: make(chan struct{}),
|
closed: make(chan struct{}),
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
}
|
}
|
||||||
|
|
@ -175,7 +209,11 @@ func (m *FairMix) AddSource(it Iterator) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
m.wg.Add(1)
|
m.wg.Add(1)
|
||||||
source := &mixSource{it, make(chan *Node), m.timeout}
|
source := &mixSource{
|
||||||
|
it: ensureSourceIter(it),
|
||||||
|
next: make(chan mixItem),
|
||||||
|
timeout: m.timeout,
|
||||||
|
}
|
||||||
m.sources = append(m.sources, source)
|
m.sources = append(m.sources, source)
|
||||||
go m.runSource(m.closed, source)
|
go m.runSource(m.closed, source)
|
||||||
}
|
}
|
||||||
|
|
@ -201,7 +239,7 @@ func (m *FairMix) Close() {
|
||||||
|
|
||||||
// Next returns a node from a random source.
|
// Next returns a node from a random source.
|
||||||
func (m *FairMix) Next() bool {
|
func (m *FairMix) Next() bool {
|
||||||
m.cur = nil
|
m.cur = mixItem{}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
source := m.pickSource()
|
source := m.pickSource()
|
||||||
|
|
@ -217,12 +255,12 @@ func (m *FairMix) Next() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case n, ok := <-source.next:
|
case item, ok := <-source.next:
|
||||||
if ok {
|
if ok {
|
||||||
// Here, the timeout is reset to the configured value
|
// Here, the timeout is reset to the configured value
|
||||||
// because the source delivered a node.
|
// because the source delivered a node.
|
||||||
source.timeout = m.timeout
|
source.timeout = m.timeout
|
||||||
m.cur = n
|
m.cur = item
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// This source has ended.
|
// This source has ended.
|
||||||
|
|
@ -239,15 +277,20 @@ func (m *FairMix) Next() bool {
|
||||||
|
|
||||||
// Node returns the current node.
|
// Node returns the current node.
|
||||||
func (m *FairMix) Node() *Node {
|
func (m *FairMix) Node() *Node {
|
||||||
return m.cur
|
return m.cur.n
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeSource returns the current node's source name.
|
||||||
|
func (m *FairMix) NodeSource() string {
|
||||||
|
return m.cur.source
|
||||||
}
|
}
|
||||||
|
|
||||||
// nextFromAny is used when there are no sources or when the 'fair' choice
|
// nextFromAny is used when there are no sources or when the 'fair' choice
|
||||||
// doesn't turn up a node quickly enough.
|
// doesn't turn up a node quickly enough.
|
||||||
func (m *FairMix) nextFromAny() bool {
|
func (m *FairMix) nextFromAny() bool {
|
||||||
n, ok := <-m.fromAny
|
item, ok := <-m.fromAny
|
||||||
if ok {
|
if ok {
|
||||||
m.cur = n
|
m.cur = item
|
||||||
}
|
}
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
@ -284,10 +327,10 @@ func (m *FairMix) runSource(closed chan struct{}, s *mixSource) {
|
||||||
defer m.wg.Done()
|
defer m.wg.Done()
|
||||||
defer close(s.next)
|
defer close(s.next)
|
||||||
for s.it.Next() {
|
for s.it.Next() {
|
||||||
n := s.it.Node()
|
item := mixItem{s.it.Node(), s.it.NodeSource()}
|
||||||
select {
|
select {
|
||||||
case s.next <- n:
|
case s.next <- item:
|
||||||
case m.fromAny <- n:
|
case m.fromAny <- item:
|
||||||
case <-closed:
|
case <-closed:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package enode
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"slices"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -183,6 +184,53 @@ func TestFairMixRemoveSource(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This checks that FairMix correctly returns the name of the source that produced the node.
|
||||||
|
func TestFairMixSourceName(t *testing.T) {
|
||||||
|
nodes := make([]*Node, 6)
|
||||||
|
for i := range nodes {
|
||||||
|
nodes[i] = testNode(uint64(i), uint64(i))
|
||||||
|
}
|
||||||
|
mix := NewFairMix(-1)
|
||||||
|
mix.AddSource(WithSourceName("s1", IterNodes(nodes[0:2])))
|
||||||
|
mix.AddSource(WithSourceName("s2", IterNodes(nodes[2:4])))
|
||||||
|
mix.AddSource(WithSourceName("s3", IterNodes(nodes[4:6])))
|
||||||
|
|
||||||
|
var names []string
|
||||||
|
for range nodes {
|
||||||
|
mix.Next()
|
||||||
|
names = append(names, mix.NodeSource())
|
||||||
|
}
|
||||||
|
want := []string{"s2", "s3", "s1", "s2", "s3", "s1"}
|
||||||
|
if !slices.Equal(names, want) {
|
||||||
|
t.Fatalf("wrong names: %v", names)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This checks that FairMix returns the name of the source that produced the node,
|
||||||
|
// even when FairMix instances are nested.
|
||||||
|
func TestFairMixNestedSourceName(t *testing.T) {
|
||||||
|
nodes := make([]*Node, 6)
|
||||||
|
for i := range nodes {
|
||||||
|
nodes[i] = testNode(uint64(i), uint64(i))
|
||||||
|
}
|
||||||
|
mix := NewFairMix(-1)
|
||||||
|
mix.AddSource(WithSourceName("s1", IterNodes(nodes[0:2])))
|
||||||
|
submix := NewFairMix(-1)
|
||||||
|
submix.AddSource(WithSourceName("s2", IterNodes(nodes[2:4])))
|
||||||
|
submix.AddSource(WithSourceName("s3", IterNodes(nodes[4:6])))
|
||||||
|
mix.AddSource(submix)
|
||||||
|
|
||||||
|
var names []string
|
||||||
|
for range nodes {
|
||||||
|
mix.Next()
|
||||||
|
names = append(names, mix.NodeSource())
|
||||||
|
}
|
||||||
|
want := []string{"s3", "s1", "s2", "s1", "s3", "s2"}
|
||||||
|
if !slices.Equal(names, want) {
|
||||||
|
t.Fatalf("wrong names: %v", names)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type blockingIter chan struct{}
|
type blockingIter chan struct{}
|
||||||
|
|
||||||
func (it blockingIter) Next() bool {
|
func (it blockingIter) Next() bool {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue