mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 06:36:43 +00:00
Merge branch 'ethereum:master' into codespace-legendary-space-disco-q7pwg95px5gx2xpqj
This commit is contained in:
commit
b590b5f0e5
197 changed files with 14168 additions and 2920 deletions
7
.mailmap
7
.mailmap
|
|
@ -50,6 +50,10 @@ Boqin Qin <bobbqqin@bupt.edu.cn> <Bobbqqin@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>
|
||||
|
||||
Chris Ziogas <ziogaschr@gmail.com>
|
||||
|
|
@ -301,9 +305,6 @@ Yohann Léon <sybiload@gmail.com>
|
|||
yzb <335357057@qq.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>
|
||||
|
||||
Zsolt Felföldi <zsfelfoldi@gmail.com>
|
||||
|
|
|
|||
2
AUTHORS
2
AUTHORS
|
|
@ -123,6 +123,7 @@ Ceyhun Onur <ceyhun.onur@avalabs.org>
|
|||
chabashilah <doumodoumo@gmail.com>
|
||||
changhong <changhong.yu@shanbay.com>
|
||||
Charles Cooper <cooper.charles.m@gmail.com>
|
||||
Charlotte <tqpcharlie@proton.me>
|
||||
Chase Wright <mysticryuujin@gmail.com>
|
||||
Chawin Aiemvaravutigul <nick41746@hotmail.com>
|
||||
Chen Quan <terasum@163.com>
|
||||
|
|
@ -839,7 +840,6 @@ ywzqwwt <39263032+ywzqwwt@users.noreply.github.com>
|
|||
yzb <335357057@qq.com>
|
||||
zaccoding <zaccoding725@gmail.com>
|
||||
Zach <zach.ramsay@gmail.com>
|
||||
Zachinquarantine <Zachinquarantine@protonmail.com>
|
||||
zah <zahary@gmail.com>
|
||||
Zahoor Mohamed <zahoor@zahoor.in>
|
||||
Zak Cole <zak@beattiecole.com>
|
||||
|
|
|
|||
|
|
@ -939,6 +939,7 @@ var bindTests = []struct {
|
|||
if _, err := eventer.RaiseSimpleEvent(auth, common.Address{byte(j)}, [32]byte{byte(j)}, true, big.NewInt(int64(10*i+j))); err != nil {
|
||||
t.Fatalf("block %d, event %d: raise failed: %v", i, j, err)
|
||||
}
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
}
|
||||
sim.Commit()
|
||||
}
|
||||
|
|
@ -1495,7 +1496,7 @@ var bindTests = []struct {
|
|||
if n != 3 {
|
||||
t.Fatalf("Invalid bar0 event")
|
||||
}
|
||||
case <-time.NewTimer(3 * time.Second).C:
|
||||
case <-time.NewTimer(10 * time.Second).C:
|
||||
t.Fatalf("Wait bar0 event timeout")
|
||||
}
|
||||
|
||||
|
|
@ -1506,7 +1507,7 @@ var bindTests = []struct {
|
|||
if n != 1 {
|
||||
t.Fatalf("Invalid bar event")
|
||||
}
|
||||
case <-time.NewTimer(3 * time.Second).C:
|
||||
case <-time.NewTimer(10 * time.Second).C:
|
||||
t.Fatalf("Wait bar event timeout")
|
||||
}
|
||||
close(stopCh)
|
||||
|
|
|
|||
116
accounts/abi/abigen/testdata/v2/structs-abi.go.txt
vendored
116
accounts/abi/abigen/testdata/v2/structs-abi.go.txt
vendored
|
|
@ -1,116 +0,0 @@
|
|||
// Code generated via abigen V2 - DO NOT EDIT.
|
||||
// This file is a generated binding and any manual changes will be lost.
|
||||
|
||||
package v1bindtests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/v2"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var (
|
||||
_ = bytes.Equal
|
||||
_ = errors.New
|
||||
_ = big.NewInt
|
||||
_ = common.Big1
|
||||
_ = types.BloomLookup
|
||||
_ = abi.ConvertType
|
||||
)
|
||||
|
||||
// Struct0 is an auto generated low-level Go binding around an user-defined struct.
|
||||
type Struct0 struct {
|
||||
B [32]byte
|
||||
}
|
||||
|
||||
// StructsMetaData contains all meta data concerning the Structs contract.
|
||||
var StructsMetaData = bind.MetaData{
|
||||
ABI: "[{\"inputs\":[],\"name\":\"F\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"B\",\"type\":\"bytes32\"}],\"internalType\":\"structStructs.A[]\",\"name\":\"a\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256[]\",\"name\":\"c\",\"type\":\"uint256[]\"},{\"internalType\":\"bool[]\",\"name\":\"d\",\"type\":\"bool[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"G\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"B\",\"type\":\"bytes32\"}],\"internalType\":\"structStructs.A[]\",\"name\":\"a\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]",
|
||||
ID: "Structs",
|
||||
}
|
||||
|
||||
// Structs is an auto generated Go binding around an Ethereum contract.
|
||||
type Structs struct {
|
||||
abi abi.ABI
|
||||
}
|
||||
|
||||
// NewStructs creates a new instance of Structs.
|
||||
func NewStructs() *Structs {
|
||||
parsed, err := StructsMetaData.ParseABI()
|
||||
if err != nil {
|
||||
panic(errors.New("invalid ABI: " + err.Error()))
|
||||
}
|
||||
return &Structs{abi: *parsed}
|
||||
}
|
||||
|
||||
// Instance creates a wrapper for a deployed contract instance at the given address.
|
||||
<<<<<<< HEAD
|
||||
// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc.
|
||||
func (c *Structs) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract {
|
||||
return bind.NewBoundContract(addr, c.abi, backend, backend, backend)
|
||||
=======
|
||||
// Use this to create the instance object passed to abigen v2 library functions Call,
|
||||
// Transact, etc.
|
||||
func (c *Structs) Instance(backend bind.ContractBackend, addr common.Address) bind.BoundContract {
|
||||
return bind.NewBoundContract(backend, addr, c.abi)
|
||||
>>>>>>> 854c25e086 (accounts/abi/abigen: improve v2 template)
|
||||
}
|
||||
|
||||
// F is the Go binding used to pack the parameters required for calling
|
||||
// the contract method 0x28811f59.
|
||||
//
|
||||
// Solidity: function F() view returns((bytes32)[] a, uint256[] c, bool[] d)
|
||||
func (structs *Structs) PackF() ([]byte, error) {
|
||||
return structs.abi.Pack("F")
|
||||
}
|
||||
|
||||
// FOutput serves as a container for the return parameters of contract
|
||||
// method F.
|
||||
type FOutput struct {
|
||||
A []Struct0
|
||||
C []*big.Int
|
||||
D []bool
|
||||
}
|
||||
|
||||
// UnpackF is the Go binding that unpacks the parameters returned
|
||||
// from invoking the contract method with ID 0x28811f59.
|
||||
//
|
||||
// Solidity: function F() view returns((bytes32)[] a, uint256[] c, bool[] d)
|
||||
func (structs *Structs) UnpackF(data []byte) (*FOutput, error) {
|
||||
out, err := structs.abi.Unpack("F", data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := new(FOutput)
|
||||
ret.A = *abi.ConvertType(out[0], new([]Struct0)).(*[]Struct0)
|
||||
ret.C = *abi.ConvertType(out[1], new([]*big.Int)).(*[]*big.Int)
|
||||
ret.D = *abi.ConvertType(out[2], new([]bool)).(*[]bool)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// G is the Go binding used to pack the parameters required for calling
|
||||
// the contract method 0x6fecb623.
|
||||
//
|
||||
// Solidity: function G() view returns((bytes32)[] a)
|
||||
func (structs *Structs) PackG() ([]byte, error) {
|
||||
return structs.abi.Pack("G")
|
||||
}
|
||||
|
||||
// UnpackG is the Go binding that unpacks the parameters returned
|
||||
// from invoking the contract method with ID 0x6fecb623.
|
||||
//
|
||||
// Solidity: function G() view returns((bytes32)[] a)
|
||||
func (structs *Structs) UnpackG(data []byte) (*[]Struct0, error) {
|
||||
out, err := structs.abi.Unpack("G", data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out0 := *abi.ConvertType(out[0], new([]Struct0)).(*[]Struct0)
|
||||
return &out0, nil
|
||||
}
|
||||
|
|
@ -26,6 +26,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/beacon/params"
|
||||
"github.com/ethereum/go-ethereum/beacon/types"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
ctypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
|
|
@ -104,7 +105,11 @@ func (ec *engineClient) callNewPayload(fork string, event types.ChainHeadEvent)
|
|||
method = "engine_newPayloadV4"
|
||||
parentBeaconRoot := event.BeaconHead.ParentRoot
|
||||
blobHashes := collectBlobHashes(event.Block)
|
||||
params = append(params, blobHashes, parentBeaconRoot, event.ExecRequests)
|
||||
hexRequests := make([]hexutil.Bytes, len(event.ExecRequests))
|
||||
for i := range event.ExecRequests {
|
||||
hexRequests[i] = hexutil.Bytes(event.ExecRequests[i])
|
||||
}
|
||||
params = append(params, blobHashes, parentBeaconRoot, hexRequests)
|
||||
case "deneb":
|
||||
method = "engine_newPayloadV3"
|
||||
parentBeaconRoot := event.BeaconHead.ParentRoot
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ type executionPayloadEnvelopeMarshaling struct {
|
|||
|
||||
type PayloadStatusV1 struct {
|
||||
Status string `json:"status"`
|
||||
Witness *hexutil.Bytes `json:"witness"`
|
||||
Witness *hexutil.Bytes `json:"witness,omitempty"`
|
||||
LatestValidHash *common.Hash `json:"latestValidHash"`
|
||||
ValidationError *string `json:"validationError"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
0xf5606a019f0f1006e9ec2070695045f4334450362a48da4c5965314510e0b4c3
|
||||
0xd60e5310c5d52ced44cfb13be4e9f22a1e6a6dc56964c3cccd429182d26d72d0
|
||||
|
|
@ -1 +1 @@
|
|||
0x3c0cb4aa83beded1803d262664ba4392b1023f334d9cca02dc3a6925987ebe91
|
||||
0x02f0bb348b0d45f95a9b7e2bb5705768ad06548876cee03d880a2c9dabb1ff88
|
||||
|
|
@ -1 +1 @@
|
|||
0xa8d56457aa414523d93659aef1f7409bbfb72ad75e94d917c8c0b1baa38153ef
|
||||
0xa0dad451a230c01be6f2492980ec5bb412d8cf33351a75e8b172b5b84a5fd03a
|
||||
|
|
@ -41,7 +41,8 @@ var (
|
|||
AddFork("ALTAIR", 74240, []byte{1, 0, 0, 0}).
|
||||
AddFork("BELLATRIX", 144896, []byte{2, 0, 0, 0}).
|
||||
AddFork("CAPELLA", 194048, []byte{3, 0, 0, 0}).
|
||||
AddFork("DENEB", 269568, []byte{4, 0, 0, 0})
|
||||
AddFork("DENEB", 269568, []byte{4, 0, 0, 0}).
|
||||
AddFork("ELECTRA", 364032, []byte{5, 0, 0, 0})
|
||||
|
||||
SepoliaLightConfig = (&ChainConfig{
|
||||
GenesisValidatorsRoot: common.HexToHash("0xd8ea171f3c94aea21ebc42a1ed61052acf3f9209c00e4efbaaddac09ed9b8078"),
|
||||
|
|
|
|||
|
|
@ -5,54 +5,54 @@
|
|||
# https://github.com/ethereum/execution-spec-tests/releases/download/pectra-devnet-6%40v1.0.0/
|
||||
b69211752a3029083c020dc635fe12156ca1a6725a08559da540a0337586a77e fixtures_pectra-devnet-6.tar.gz
|
||||
|
||||
# version:golang 1.24.2
|
||||
# version:golang 1.24.3
|
||||
# https://go.dev/dl/
|
||||
9dc77ffadc16d837a1bf32d99c624cb4df0647cee7b119edd9e7b1bcc05f2e00 go1.24.2.src.tar.gz
|
||||
427b373540d8fd51dbcc46bdecd340af109cd41514443c000d3dcde72b2c65a3 go1.24.2.aix-ppc64.tar.gz
|
||||
238d9c065d09ff6af229d2e3b8b5e85e688318d69f4006fb85a96e41c216ea83 go1.24.2.darwin-amd64.tar.gz
|
||||
535ed9ff283fee39575a7fb9b6d8b1901b6dc640d06dc71fd7d3faeefdaf8030 go1.24.2.darwin-amd64.pkg
|
||||
b70f8b3c5b4ccb0ad4ffa5ee91cd38075df20fdbd953a1daedd47f50fbcff47a go1.24.2.darwin-arm64.tar.gz
|
||||
4732f607a47ce4d898c0af01ff68f07e0820a6b50603aef5d5c777d1102505e2 go1.24.2.darwin-arm64.pkg
|
||||
c17686b5fd61a663fbfafccfa177961be59386cf294e935ce35866b9dcb8e78a go1.24.2.dragonfly-amd64.tar.gz
|
||||
026f1dd906189acff714c7625686bbc4ed91042618ba010d45b671461acc9e63 go1.24.2.freebsd-386.tar.gz
|
||||
49399ba759b570a8f87d12179133403da6c2dd296d63a8830dee309161b9c40c go1.24.2.freebsd-amd64.tar.gz
|
||||
1f48f47183794d97c29736004247ab541177cf984ac6322c78bc43828daa1172 go1.24.2.freebsd-arm.tar.gz
|
||||
ef856428b60a8c0bd9a2cba596e83024be6f1c2d5574e89cb1ff2262b08df8b9 go1.24.2.freebsd-arm64.tar.gz
|
||||
ec2088823e16df00600a6d0f72e9a7dc6d2f80c9c140c2043c0cf20e1404d1a9 go1.24.2.freebsd-riscv64.tar.gz
|
||||
e030e7cedbb8688f1d75cb80f3de6ee2e6617a67d34051e794e5992b53462147 go1.24.2.illumos-amd64.tar.gz
|
||||
4c382776d52313266f3026236297a224a6688751256a2dffa3f524d8d6f6c0ba go1.24.2.linux-386.tar.gz
|
||||
68097bd680839cbc9d464a0edce4f7c333975e27a90246890e9f1078c7e702ad go1.24.2.linux-amd64.tar.gz
|
||||
756274ea4b68fa5535eb9fe2559889287d725a8da63c6aae4d5f23778c229f4b go1.24.2.linux-arm64.tar.gz
|
||||
438d5d3d7dcb239b58d893a715672eabe670b9730b1fd1c8fc858a46722a598a go1.24.2.linux-armv6l.tar.gz
|
||||
6aefd3bf59c3c5592eda4fb287322207f119c2210f3795afa9be48d3ccb73e1b go1.24.2.linux-loong64.tar.gz
|
||||
93e49bb4692783b0e9a2deab9558c6e8d2867f35592aeff285adda60924167f3 go1.24.2.linux-mips.tar.gz
|
||||
6e86e703675016f3faf6604b8f68f20dc1bba75849136e6dd4f43f69c8a4a9d9 go1.24.2.linux-mips64.tar.gz
|
||||
f233d237538ca1559a7d7cf519a29f0147923a951377bc4e467af4c059e68851 go1.24.2.linux-mips64le.tar.gz
|
||||
545e1b9a7939f923fd53bde98334b987ef42eb353ee3e0bfede8aa06079d6b24 go1.24.2.linux-mipsle.tar.gz
|
||||
6eab31481f2f46187bc1b6c887662eef06fc9d7271a8390854072cdb387c8d74 go1.24.2.linux-ppc64.tar.gz
|
||||
5fff857791d541c71d8ea0171c73f6f99770d15ff7e2ad979104856d01f36563 go1.24.2.linux-ppc64le.tar.gz
|
||||
91bda1558fcbd1c92769ad86c8f5cf796f8c67b0d9d9c19f76eecfc75ce71527 go1.24.2.linux-riscv64.tar.gz
|
||||
1cb3448166d6abb515a85a3ee5afbdf932081fb58ad7143a8fb666fbc06146d9 go1.24.2.linux-s390x.tar.gz
|
||||
a9a2c0db2e826f20f00b02bee01dfdaeb49591c2f6ffacb78dc64a950894f7ff go1.24.2.netbsd-386.tar.gz
|
||||
cd1a35b76ed9c7b6c0c1616741bd319699a77867ade0be9924f32496c0a87a3f go1.24.2.netbsd-amd64.tar.gz
|
||||
8c666388d066e479155cc5116950eeb435df28087ef277c18f1dc7479f836e60 go1.24.2.netbsd-arm.tar.gz
|
||||
5d42f0be04f58da5be788a1e260f8747c316b8ce182bf0b273c2e4c691feaa1a go1.24.2.netbsd-arm64.tar.gz
|
||||
688effa23ea3973cc8b0fdf4246712cbeef55ff20c45f3a9e28b0c2db04246cf go1.24.2.openbsd-386.tar.gz
|
||||
e5daf95f1048d8026b1366450a3f8044d668b0639db6422ad9a83755c6745cf7 go1.24.2.openbsd-amd64.tar.gz
|
||||
aeadaf74bd544d1a12ba9b14c0e7cdb1964de3ba9a52acb4619e91dbae7def7b go1.24.2.openbsd-arm.tar.gz
|
||||
9e222d9adb0ce836a5b3c8d5aadbd167c8869c030b113f4a81aa88e9a200f279 go1.24.2.openbsd-arm64.tar.gz
|
||||
192fffa34536adc3cd1bb7c1ee785b8bc156ae7afd10bbf5db99ec8f2e93066e go1.24.2.openbsd-ppc64.tar.gz
|
||||
a23e90b451a390549042c2a7efbec6f29ed98b2d5618c8d2a35704e21be96e09 go1.24.2.openbsd-riscv64.tar.gz
|
||||
5cdcafe455d859b02779611a5a1e1d63e498b922e05818fb3debe410a5959e9e go1.24.2.plan9-386.tar.gz
|
||||
81351659804fa505c1b3ec6fdf9599f7f88df08614307eeb96071bf5e2e74beb go1.24.2.plan9-amd64.tar.gz
|
||||
6e337d5def14ed0123423c1c32e2e6d8b19161e5d5ffaa7356dad48ee0fd80b4 go1.24.2.plan9-arm.tar.gz
|
||||
07e6926ebc476c044d7d5b17706abfc52be52bccc2073d1734174efe63c6b35e go1.24.2.solaris-amd64.tar.gz
|
||||
13d86cb818bba331da75fcd18246ab31a1067b44fb4a243b6dfd93097eda7f37 go1.24.2.windows-386.zip
|
||||
8a702d9f7104a15bd935f4191c58c24c0b6389e066b9d5661b93915114a2bef0 go1.24.2.windows-386.msi
|
||||
29c553aabee0743e2ffa3e9fa0cda00ef3b3cc4ff0bc92007f31f80fd69892e1 go1.24.2.windows-amd64.zip
|
||||
acefb191e72fea0bdb1a3f5f8f6f5ab18b42b3bbce0c7183f189f25953aff275 go1.24.2.windows-amd64.msi
|
||||
ab267f7f9a3366d48d7664be9e627ce3e63273231430cce5f7783fb910f14148 go1.24.2.windows-arm64.zip
|
||||
d187bfe539356c39573d2f46766d1d08122b4f33da00fd14d12485fa9e241ff5 go1.24.2.windows-arm64.msi
|
||||
229c08b600b1446798109fae1f569228102c8473caba8104b6418cb5bc032878 go1.24.3.src.tar.gz
|
||||
6f6901497547db3b77c14f7f953fbcef9fa5fb84199ee2ee14a5686e66bed5a6 go1.24.3.aix-ppc64.tar.gz
|
||||
a05fa7e4043a4fec66897135219e3b8ab2202b5ef351c60c2fbb531dfb8f2900 go1.24.3.darwin-amd64.pkg
|
||||
13e6fe3fcf65689d77d40e633de1e31c6febbdbcb846eb05fc2434ed2213e92b go1.24.3.darwin-amd64.tar.gz
|
||||
97055ff4214043b39dc32e043fdd5c565df7c0a4e2fc0174e779a134c347ae0e go1.24.3.darwin-arm64.pkg
|
||||
64a3fa22142f627e78fac3018ce3d4aeace68b743eff0afda8aae0411df5e4fb go1.24.3.darwin-arm64.tar.gz
|
||||
32de3fd44d5055973978436a7f1f0ffbaae85c1b603ec6105e5c38d8a674c721 go1.24.3.dragonfly-amd64.tar.gz
|
||||
9fe6101b3797919bd7337ee5ce591954f85d59db7ae88983904db29fd64c3dd1 go1.24.3.freebsd-386.tar.gz
|
||||
6ccf4cca287e90cc28cd7954b6172f5d177a17e20b072b65f7f39636c325e2fb go1.24.3.freebsd-amd64.tar.gz
|
||||
ce45ebf389066f82a7b056b66dd650efb51fde6f8bf92a2a3ab6990f02788ebf go1.24.3.freebsd-arm.tar.gz
|
||||
8f6494a12a874d0ea57c67987829359e016960ce3ba0673273609d6ac2af589a go1.24.3.freebsd-arm64.tar.gz
|
||||
f9db392560cf0851f0bc8f2190e1978e01b4603038c27fecfc8658a695b71616 go1.24.3.freebsd-riscv64.tar.gz
|
||||
01717fff64c5d98457272002fa825d0a15e307bf6e189f2b0c23817fa033b61c go1.24.3.illumos-amd64.tar.gz
|
||||
41b1051063e68cbd2b919bf12326764fe33937cf1d32b5c529dd1a4f43dce578 go1.24.3.linux-386.tar.gz
|
||||
3333f6ea53afa971e9078895eaa4ac7204a8c6b5c68c10e6bc9a33e8e391bdd8 go1.24.3.linux-amd64.tar.gz
|
||||
a463cb59382bd7ae7d8f4c68846e73c4d589f223c589ac76871b66811ded7836 go1.24.3.linux-arm64.tar.gz
|
||||
17a392d7e826625dd12a32099df0b00b85c32d8132ed86fe917183ee5c3f88ed go1.24.3.linux-armv6l.tar.gz
|
||||
e4b003c04c902edc140153d279b42167f1ad7c229f48f1f729bbef5e65e88d1f go1.24.3.linux-loong64.tar.gz
|
||||
1c79d89edf835edf9d4336ccea7cb89bc5c0ca82b12b36b218d599a5400d60fe go1.24.3.linux-mips.tar.gz
|
||||
0b64fe147d69f4d681d8e8a035c760477531432f83d831f18d37cb9bf3652488 go1.24.3.linux-mips64.tar.gz
|
||||
396b784c255b64512dc00c302c053e43a3cbfc77518664c6ac5569aafad4d1e6 go1.24.3.linux-mips64le.tar.gz
|
||||
93898313887f14e8efbe9d7386d5da4792b2d6c492bee562993fd4c9daa75c6d go1.24.3.linux-mipsle.tar.gz
|
||||
873ae3a6a6655a7b6f820e095d9965507e8dfd3cf76bc92d75c564ecbca385f6 go1.24.3.linux-ppc64.tar.gz
|
||||
341a749d168f47b1d4dad25e32cae70849b7ceed7c290823b853c9e6b0df0856 go1.24.3.linux-ppc64le.tar.gz
|
||||
fa482f53ccb4ba280316b8c5751ea67291507280d9166f2a38fe4d9b5d5fb64b go1.24.3.linux-riscv64.tar.gz
|
||||
a87b0c2a079a0bece1620fb29a00e02b4dba17507850f837e754af7d57cda282 go1.24.3.linux-s390x.tar.gz
|
||||
63155382308db1306200aff7821aa26bf2a2dda23537dd637a9704b485b6ddf0 go1.24.3.netbsd-386.tar.gz
|
||||
fe2c5c79482958b867c08a4fc2a10a998de9c0206b08d5b3ebcb2232e8d2777c go1.24.3.netbsd-amd64.tar.gz
|
||||
e8ff77aef21521b5dd94e44282a3243309b80717414cf12f72835a45886a049f go1.24.3.netbsd-arm.tar.gz
|
||||
b337fbaf82822685940ffaa76fbcf4be5d2f0258bc819cd80bc408b491f45c04 go1.24.3.netbsd-arm64.tar.gz
|
||||
c1bb9dd8418480aa7f65452b08de3759da3bf89702be71b5a9fc084836b24ad5 go1.24.3.openbsd-386.tar.gz
|
||||
531218de748b0caaf6d1ad18921206fc12baaa89bf483a0a5e60a571c206fe6f go1.24.3.openbsd-amd64.tar.gz
|
||||
bcd0dc959986fc346969b5d4111c3c8031882d8bf8d87a2c2ecf1328962a91f2 go1.24.3.openbsd-arm.tar.gz
|
||||
00ee6f8f1c41fd2e28ad386bd7e39acce7cab84af6de835855b29d1c597335c4 go1.24.3.openbsd-arm64.tar.gz
|
||||
9f4ec0a9203ed3c54ce1a2a390ad3d45838cdb7efd85baeff857e37dfde04edd go1.24.3.openbsd-ppc64.tar.gz
|
||||
da4d6f80e2373250d8c31c32dcd1e08775c327c0d610923604660cc0e07e8cba go1.24.3.openbsd-riscv64.tar.gz
|
||||
f5d02149132eedda6c2d46b360d7da462b8a5f9e3f8567db100c2d7bff0ddcd7 go1.24.3.plan9-386.tar.gz
|
||||
175f3d79f4762a3c545d2c6393bf6b8bac24e838026869dafab06b930735c94f go1.24.3.plan9-amd64.tar.gz
|
||||
d1e4ac15095da1611659261c2228c2058756cf87d61d9fad262f76755ef26849 go1.24.3.plan9-arm.tar.gz
|
||||
e644220a6ced3c07a7acc1364193cb709a97737dd8b6792a07a8ec6d9996713e go1.24.3.solaris-amd64.tar.gz
|
||||
0d7e7dc0a31ba0cdd487415709d03b02fc9490ef111e8dfd22788a6d63316f37 go1.24.3.windows-386.msi
|
||||
c27c463a61ab849266baa0c17a6c5c4256a574ab642f609ba25c96ec965dc184 go1.24.3.windows-386.zip
|
||||
d5b7637e7e138be877d96a4501709d480e050d86a8f402bc950e72112b5aedc5 go1.24.3.windows-amd64.msi
|
||||
be9787cb08998b1860fe3513e48a5fe5b96302d358a321b58e651184fa9638b3 go1.24.3.windows-amd64.zip
|
||||
7efde2e5e8468e9caf2c7fc94f4da78a726a5031a1ed63acff7899527cdddff6 go1.24.3.windows-arm64.msi
|
||||
eec9fa736056b54dd88ecb669db2bfad39b0c48f6f9080f036dfa1ca42dc4bb5 go1.24.3.windows-arm64.zip
|
||||
|
||||
# version:golangci 2.0.2
|
||||
# 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/ethereum/go-ethereum/crypto/signify"
|
||||
"github.com/ethereum/go-ethereum/internal/build"
|
||||
"github.com/ethereum/go-ethereum/internal/download"
|
||||
"github.com/ethereum/go-ethereum/internal/version"
|
||||
)
|
||||
|
||||
|
|
@ -190,7 +191,7 @@ func doInstall(cmdline []string) {
|
|||
// Configure the toolchain.
|
||||
tc := build.GoToolchain{GOARCH: *arch, CC: *cc}
|
||||
if *dlgo {
|
||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
||||
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||
tc.Root = build.DownloadGo(csdb)
|
||||
}
|
||||
// Disable CLI markdown doc generation in release builds.
|
||||
|
|
@ -285,7 +286,7 @@ func doTest(cmdline []string) {
|
|||
flag.CommandLine.Parse(cmdline)
|
||||
|
||||
// Get test fixtures.
|
||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
||||
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||
downloadSpecTestFixtures(csdb, *cachedir)
|
||||
|
||||
// Configure the toolchain.
|
||||
|
|
@ -329,16 +330,11 @@ func doTest(cmdline []string) {
|
|||
}
|
||||
|
||||
// downloadSpecTestFixtures downloads and extracts the execution-spec-tests fixtures.
|
||||
func downloadSpecTestFixtures(csdb *build.ChecksumDB, cachedir string) string {
|
||||
executionSpecTestsVersion, err := build.Version(csdb, "spec-tests")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
func downloadSpecTestFixtures(csdb *download.ChecksumDB, cachedir string) string {
|
||||
ext := ".tar.gz"
|
||||
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)
|
||||
if err := csdb.DownloadFile(url, archivePath); err != nil {
|
||||
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := build.ExtractArchive(archivePath, executionSpecTestsDir); err != nil {
|
||||
|
|
@ -444,14 +440,13 @@ func doLint(cmdline []string) {
|
|||
|
||||
// downloadLinter downloads and unpacks golangci-lint.
|
||||
func downloadLinter(cachedir string) string {
|
||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
||||
version, err := build.Version(csdb, "golangci")
|
||||
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||
version, err := csdb.FindVersion("golangci")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
arch := runtime.GOARCH
|
||||
ext := ".tar.gz"
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
ext = ".zip"
|
||||
}
|
||||
|
|
@ -459,9 +454,8 @@ func downloadLinter(cachedir string) string {
|
|||
arch += "v" + os.Getenv("GOARM")
|
||||
}
|
||||
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)
|
||||
if err := csdb.DownloadFile(url, archivePath); err != nil {
|
||||
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
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
|
||||
// containing the 'protoc-gen-go' executable.
|
||||
func downloadProtocGenGo(cachedir string) string {
|
||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
||||
version, err := build.Version(csdb, "protoc-gen-go")
|
||||
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||
version, err := csdb.FindVersion("protoc-gen-go")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
@ -510,10 +504,8 @@ func downloadProtocGenGo(cachedir string) string {
|
|||
archiveName += ".tar.gz"
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("https://github.com/protocolbuffers/protobuf-go/releases/download/v%s/%s", version, archiveName)
|
||||
|
||||
archivePath := path.Join(cachedir, archiveName)
|
||||
if err := csdb.DownloadFile(url, archivePath); err != nil {
|
||||
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
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
|
||||
// the protoc executable.
|
||||
func downloadProtoc(cachedir string) string {
|
||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
||||
version, err := build.Version(csdb, "protoc")
|
||||
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||
version, err := csdb.FindVersion("protoc")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
@ -543,10 +535,8 @@ func downloadProtoc(cachedir string) string {
|
|||
|
||||
fileName := fmt.Sprintf("protoc-%s-%s", version, baseName)
|
||||
archiveFileName := fileName + ".zip"
|
||||
url := fmt.Sprintf("https://github.com/protocolbuffers/protobuf/releases/download/v%s/%s", version, archiveFileName)
|
||||
archivePath := filepath.Join(cachedir, archiveFileName)
|
||||
|
||||
if err := csdb.DownloadFile(url, archivePath); err != nil {
|
||||
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
extractDest := filepath.Join(cachedir, fileName)
|
||||
|
|
@ -826,18 +816,17 @@ func doDebianSource(cmdline []string) {
|
|||
// downloadGoBootstrapSources downloads the Go source tarball(s) that will be used
|
||||
// to bootstrap the builder Go.
|
||||
func downloadGoBootstrapSources(cachedir string) []string {
|
||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
||||
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||
|
||||
var bundles []string
|
||||
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 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
file := fmt.Sprintf("go%s.src.tar.gz", gobootVersion)
|
||||
url := "https://dl.google.com/go/" + file
|
||||
dst := filepath.Join(cachedir, file)
|
||||
if err := csdb.DownloadFile(url, dst); err != nil {
|
||||
if err := csdb.DownloadFileFromKnownURL(dst); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
bundles = append(bundles, dst)
|
||||
|
|
@ -847,15 +836,14 @@ func downloadGoBootstrapSources(cachedir string) []string {
|
|||
|
||||
// downloadGoSources downloads the Go source tarball.
|
||||
func downloadGoSources(cachedir string) string {
|
||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
||||
dlgoVersion, err := build.Version(csdb, "golang")
|
||||
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||
dlgoVersion, err := csdb.FindVersion("golang")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
file := fmt.Sprintf("go%s.src.tar.gz", dlgoVersion)
|
||||
url := "https://dl.google.com/go/" + file
|
||||
dst := filepath.Join(cachedir, file)
|
||||
if err := csdb.DownloadFile(url, dst); err != nil {
|
||||
if err := csdb.DownloadFileFromKnownURL(dst); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return dst
|
||||
|
|
@ -1181,5 +1169,6 @@ func doPurge(cmdline []string) {
|
|||
}
|
||||
|
||||
func doSanityCheck() {
|
||||
build.DownloadAndVerifyChecksums(build.MustLoadChecksums("build/checksums.txt"))
|
||||
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||
csdb.DownloadAndVerifyAll()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,10 +66,9 @@ func (s *Suite) dialAs(key *ecdsa.PrivateKey) (*Conn, error) {
|
|||
return nil, err
|
||||
}
|
||||
conn.caps = []p2p.Cap{
|
||||
{Name: "eth", Version: 67},
|
||||
{Name: "eth", Version: 68},
|
||||
{Name: "eth", Version: 69},
|
||||
}
|
||||
conn.ourHighestProtoVersion = 68
|
||||
conn.ourHighestProtoVersion = 69
|
||||
return &conn, nil
|
||||
}
|
||||
|
||||
|
|
@ -156,7 +155,7 @@ func (c *Conn) ReadEth() (any, error) {
|
|||
var msg any
|
||||
switch int(code) {
|
||||
case eth.StatusMsg:
|
||||
msg = new(eth.StatusPacket)
|
||||
msg = new(eth.StatusPacket69)
|
||||
case eth.GetBlockHeadersMsg:
|
||||
msg = new(eth.GetBlockHeadersPacket)
|
||||
case eth.BlockHeadersMsg:
|
||||
|
|
@ -231,7 +230,7 @@ func (c *Conn) ReadSnap() (any, error) {
|
|||
|
||||
// peer performs both the protocol handshake and the status message
|
||||
// exchange with the node in order to peer with it.
|
||||
func (c *Conn) peer(chain *Chain, status *eth.StatusPacket) error {
|
||||
func (c *Conn) peer(chain *Chain, status *eth.StatusPacket69) error {
|
||||
if err := c.handshake(); err != nil {
|
||||
return fmt.Errorf("handshake failed: %v", err)
|
||||
}
|
||||
|
|
@ -304,7 +303,7 @@ func (c *Conn) negotiateEthProtocol(caps []p2p.Cap) {
|
|||
}
|
||||
|
||||
// statusExchange performs a `Status` message exchange with the given node.
|
||||
func (c *Conn) statusExchange(chain *Chain, status *eth.StatusPacket) error {
|
||||
func (c *Conn) statusExchange(chain *Chain, status *eth.StatusPacket69) error {
|
||||
loop:
|
||||
for {
|
||||
code, data, err := c.Read()
|
||||
|
|
@ -313,12 +312,16 @@ loop:
|
|||
}
|
||||
switch code {
|
||||
case eth.StatusMsg + protoOffset(ethProto):
|
||||
msg := new(eth.StatusPacket)
|
||||
msg := new(eth.StatusPacket69)
|
||||
if err := rlp.DecodeBytes(data, &msg); err != nil {
|
||||
return fmt.Errorf("error decoding status packet: %w", err)
|
||||
}
|
||||
if have, want := msg.Head, chain.blocks[chain.Len()-1].Hash(); have != want {
|
||||
return fmt.Errorf("wrong head block in status, want: %#x (block %d) have %#x",
|
||||
if have, want := msg.LatestBlock, chain.blocks[chain.Len()-1].NumberU64(); have != want {
|
||||
return fmt.Errorf("wrong head block in status, want: %d, have %d",
|
||||
want, have)
|
||||
}
|
||||
if have, want := msg.LatestBlockHash, chain.blocks[chain.Len()-1].Hash(); have != want {
|
||||
return fmt.Errorf("wrong head block in status, want: %#x (block %d) have %#x",
|
||||
want, chain.blocks[chain.Len()-1].NumberU64(), have)
|
||||
}
|
||||
if have, want := msg.ForkID, chain.ForkID(); !reflect.DeepEqual(have, want) {
|
||||
|
|
@ -348,13 +351,14 @@ loop:
|
|||
}
|
||||
if status == nil {
|
||||
// default status message
|
||||
status = ð.StatusPacket{
|
||||
status = ð.StatusPacket69{
|
||||
ProtocolVersion: uint32(c.negotiatedProtoVersion),
|
||||
NetworkID: chain.config.ChainID.Uint64(),
|
||||
TD: chain.TD(),
|
||||
Head: chain.blocks[chain.Len()-1].Hash(),
|
||||
Genesis: chain.blocks[0].Hash(),
|
||||
ForkID: chain.ForkID(),
|
||||
EarliestBlock: 0,
|
||||
LatestBlock: chain.blocks[chain.Len()-1].NumberU64(),
|
||||
LatestBlockHash: chain.blocks[chain.Len()-1].Hash(),
|
||||
}
|
||||
}
|
||||
if err := c.Write(ethProto, eth.StatusMsg, status); err != nil {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ const (
|
|||
// Unexported devp2p protocol lengths from p2p package.
|
||||
const (
|
||||
baseProtoLen = 16
|
||||
ethProtoLen = 17
|
||||
ethProtoLen = 18
|
||||
snapProtoLen = 8
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -70,11 +70,13 @@ func (s *Suite) EthTests() []utesting.Test {
|
|||
{Name: "Status", Fn: s.TestStatus},
|
||||
// get block headers
|
||||
{Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders},
|
||||
{Name: "GetNonexistentBlockHeaders", Fn: s.TestGetNonexistentBlockHeaders},
|
||||
{Name: "SimultaneousRequests", Fn: s.TestSimultaneousRequests},
|
||||
{Name: "SameRequestID", Fn: s.TestSameRequestID},
|
||||
{Name: "ZeroRequestID", Fn: s.TestZeroRequestID},
|
||||
// get block bodies
|
||||
// get history
|
||||
{Name: "GetBlockBodies", Fn: s.TestGetBlockBodies},
|
||||
{Name: "GetReceipts", Fn: s.TestGetReceipts},
|
||||
// // malicious handshakes + status
|
||||
{Name: "MaliciousHandshake", Fn: s.TestMaliciousHandshake},
|
||||
// test transactions
|
||||
|
|
@ -158,6 +160,48 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *Suite) TestGetNonexistentBlockHeaders(t *utesting.T) {
|
||||
t.Log(`This test sends GetBlockHeaders requests for nonexistent blocks (using max uint64 value)
|
||||
to check if the node disconnects after receiving multiple invalid requests.`)
|
||||
|
||||
conn, err := s.dial()
|
||||
if err != nil {
|
||||
t.Fatalf("dial failed: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
if err := conn.peer(s.chain, nil); err != nil {
|
||||
t.Fatalf("peering failed: %v", err)
|
||||
}
|
||||
|
||||
// Create request with max uint64 value for a nonexistent block
|
||||
badReq := ð.GetBlockHeadersPacket{
|
||||
GetBlockHeadersRequest: ð.GetBlockHeadersRequest{
|
||||
Origin: eth.HashOrNumber{Number: ^uint64(0)},
|
||||
Amount: 1,
|
||||
Skip: 0,
|
||||
Reverse: false,
|
||||
},
|
||||
}
|
||||
|
||||
// Send request 10 times. Some clients are lient on the first few invalids.
|
||||
for i := 0; i < 10; i++ {
|
||||
badReq.RequestId = uint64(i)
|
||||
if err := conn.Write(ethProto, eth.GetBlockHeadersMsg, badReq); err != nil {
|
||||
if err == errDisc {
|
||||
t.Fatalf("peer disconnected after %d requests", i+1)
|
||||
}
|
||||
t.Fatalf("write failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if peer disconnects at the end.
|
||||
code, _, err := conn.Read()
|
||||
if err == errDisc || code == discMsg {
|
||||
t.Fatal("peer improperly disconnected")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Suite) TestSimultaneousRequests(t *utesting.T) {
|
||||
t.Log(`This test requests blocks headers from the node, performing two requests
|
||||
concurrently, with different request IDs.`)
|
||||
|
|
@ -375,6 +419,51 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *Suite) TestGetReceipts(t *utesting.T) {
|
||||
t.Log(`This test sends GetReceipts requests to the node for known blocks in the test chain.`)
|
||||
|
||||
conn, err := s.dial()
|
||||
if err != nil {
|
||||
t.Fatalf("dial failed: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
if err := conn.peer(s.chain, nil); err != nil {
|
||||
t.Fatalf("peering failed: %v", err)
|
||||
}
|
||||
|
||||
// Find some blocks containing receipts.
|
||||
var hashes = make([]common.Hash, 0, 3)
|
||||
for i := range s.chain.Len() {
|
||||
block := s.chain.GetBlock(i)
|
||||
if len(block.Transactions()) > 0 {
|
||||
hashes = append(hashes, block.Hash())
|
||||
}
|
||||
if len(hashes) == cap(hashes) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Create block bodies request.
|
||||
req := ð.GetReceiptsPacket{
|
||||
RequestId: 66,
|
||||
GetReceiptsRequest: (eth.GetReceiptsRequest)(hashes),
|
||||
}
|
||||
if err := conn.Write(ethProto, eth.GetReceiptsMsg, req); err != nil {
|
||||
t.Fatalf("could not write to connection: %v", err)
|
||||
}
|
||||
// Wait for response.
|
||||
resp := new(eth.ReceiptsPacket[*eth.ReceiptList69])
|
||||
if err := conn.ReadMsg(ethProto, eth.ReceiptsMsg, &resp); err != nil {
|
||||
t.Fatalf("error reading block bodies msg: %v", err)
|
||||
}
|
||||
if got, want := resp.RequestId, req.RequestId; got != want {
|
||||
t.Fatalf("unexpected request id in respond", got, want)
|
||||
}
|
||||
if len(resp.List) != len(req.GetReceiptsRequest) {
|
||||
t.Fatalf("wrong bodies in response: expected %d bodies, got %d", len(req.GetReceiptsRequest), len(resp.List))
|
||||
}
|
||||
}
|
||||
|
||||
// randBuf makes a random buffer size kilobytes large.
|
||||
func randBuf(size int) []byte {
|
||||
buf := make([]byte, size*1024)
|
||||
|
|
@ -457,6 +546,31 @@ func (s *Suite) TestMaliciousHandshake(t *utesting.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *Suite) TestInvalidBlockRangeUpdate(t *utesting.T) {
|
||||
t.Log(`This test sends an invalid BlockRangeUpdate message to the node and expects to be disconnected.`)
|
||||
|
||||
conn, err := s.dial()
|
||||
if err != nil {
|
||||
t.Fatalf("dial failed: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
if err := conn.peer(s.chain, nil); err != nil {
|
||||
t.Fatalf("peering failed: %v", err)
|
||||
}
|
||||
|
||||
conn.Write(ethProto, eth.BlockRangeUpdateMsg, ð.BlockRangeUpdatePacket{
|
||||
EarliestBlock: 10,
|
||||
LatestBlock: 8,
|
||||
LatestBlockHash: s.chain.GetBlock(8).Hash(),
|
||||
})
|
||||
|
||||
if code, _, err := conn.Read(); err != nil {
|
||||
t.Fatalf("expected disconnect, got err: %v", err)
|
||||
} else if code != discMsg {
|
||||
t.Fatalf("expected disconnect message, got msg code %d", code)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Suite) TestTransaction(t *utesting.T) {
|
||||
t.Log(`This test sends a valid transaction to the node and checks if the
|
||||
transaction gets propagated.`)
|
||||
|
|
|
|||
|
|
@ -363,9 +363,13 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not parse requests logs: %v", err))
|
||||
}
|
||||
// EIP-7002
|
||||
core.ProcessWithdrawalQueue(&requests, evm)
|
||||
if err := core.ProcessWithdrawalQueue(&requests, evm); err != nil {
|
||||
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not process withdrawal requests: %v", err))
|
||||
}
|
||||
// EIP-7251
|
||||
core.ProcessConsolidationQueue(&requests, evm)
|
||||
if err := core.ProcessConsolidationQueue(&requests, evm); err != nil {
|
||||
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not process consolidation requests: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
// Commit block
|
||||
|
|
|
|||
|
|
@ -21,9 +21,12 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
|
|
@ -39,6 +42,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/internal/debug"
|
||||
"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/log"
|
||||
"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,
|
||||
Name: "prune-history",
|
||||
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
|
||||
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
|
||||
|
|
@ -246,10 +286,13 @@ func initGenesis(ctx *cli.Context) error {
|
|||
triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
|
||||
defer triedb.Close()
|
||||
|
||||
_, hash, _, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
|
||||
_, hash, compatErr, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to write genesis block: %v", err)
|
||||
}
|
||||
if compatErr != nil {
|
||||
utils.Fatalf("Failed to write chain config: %v", compatErr)
|
||||
}
|
||||
log.Info("Successfully wrote genesis state", "database", "chaindata", "hash", hash)
|
||||
|
||||
return nil
|
||||
|
|
@ -662,3 +705,83 @@ func pruneHistory(ctx *cli.Context) error {
|
|||
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,9 +39,8 @@ const (
|
|||
// child g gets a temporary data directory.
|
||||
func runMinimalGeth(t *testing.T, args ...string) *testgeth {
|
||||
// --holesky to make the 'writing genesis to disk' faster (no accounts)
|
||||
// --networkid=1337 to avoid cache bump
|
||||
// --syncmode=full to avoid allocating fast sync bloom
|
||||
allArgs := []string{"--holesky", "--networkid", "1337", "--authrpc.port", "0", "--syncmode=full", "--port", "0",
|
||||
allArgs := []string{"--holesky", "--authrpc.port", "0", "--syncmode=full", "--port", "0",
|
||||
"--nat", "none", "--nodiscover", "--maxpeers", "0", "--cache", "64",
|
||||
"--datadir.minfreedisk", "0"}
|
||||
return runGeth(t, append(allArgs, args...)...)
|
||||
|
|
@ -103,17 +102,17 @@ func TestAttachWelcome(t *testing.T) {
|
|||
"--http", "--http.port", httpPort,
|
||||
"--ws", "--ws.port", wsPort)
|
||||
t.Run("ipc", func(t *testing.T) {
|
||||
waitForEndpoint(t, ipc, 4*time.Second)
|
||||
waitForEndpoint(t, ipc, 2*time.Minute)
|
||||
testAttachWelcome(t, geth, "ipc:"+ipc, ipcAPIs)
|
||||
})
|
||||
t.Run("http", func(t *testing.T) {
|
||||
endpoint := "http://127.0.0.1:" + httpPort
|
||||
waitForEndpoint(t, endpoint, 4*time.Second)
|
||||
waitForEndpoint(t, endpoint, 2*time.Minute)
|
||||
testAttachWelcome(t, geth, endpoint, httpAPIs)
|
||||
})
|
||||
t.Run("ws", func(t *testing.T) {
|
||||
endpoint := "ws://127.0.0.1:" + wsPort
|
||||
waitForEndpoint(t, endpoint, 4*time.Second)
|
||||
waitForEndpoint(t, endpoint, 2*time.Minute)
|
||||
testAttachWelcome(t, geth, endpoint, httpAPIs)
|
||||
})
|
||||
geth.Kill()
|
||||
|
|
|
|||
|
|
@ -91,13 +91,7 @@ var (
|
|||
utils.LogNoHistoryFlag,
|
||||
utils.LogExportCheckpointsFlag,
|
||||
utils.StateHistoryFlag,
|
||||
utils.LightServeFlag, // deprecated
|
||||
utils.LightIngressFlag, // deprecated
|
||||
utils.LightEgressFlag, // deprecated
|
||||
utils.LightMaxPeersFlag, // deprecated
|
||||
utils.LightNoPruneFlag, // deprecated
|
||||
utils.LightKDFFlag,
|
||||
utils.LightNoSyncServeFlag, // deprecated
|
||||
utils.EthRequiredBlocksFlag,
|
||||
utils.LegacyWhitelistFlag, // deprecated
|
||||
utils.CacheFlag,
|
||||
|
|
@ -225,7 +219,8 @@ func init() {
|
|||
removedbCommand,
|
||||
dumpCommand,
|
||||
dumpGenesisCommand,
|
||||
pruneCommand,
|
||||
pruneHistoryCommand,
|
||||
downloadEraCommand,
|
||||
// See accountcmd.go:
|
||||
accountCommand,
|
||||
walletCommand,
|
||||
|
|
|
|||
|
|
@ -309,7 +309,8 @@ func ImportHistory(chain *core.BlockChain, dir string, network string) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("error reading receipts %d: %w", it.Number(), err)
|
||||
}
|
||||
if _, err := chain.InsertReceiptChain([]*types.Block{block}, []types.Receipts{receipts}, 2^64-1); err != nil {
|
||||
encReceipts := types.EncodeBlockReceiptLists([]types.Receipts{receipts})
|
||||
if _, err := chain.InsertReceiptChain([]*types.Block{block}, encReceipts, 2^64-1); err != nil {
|
||||
return fmt.Errorf("error inserting body %d: %w", it.Number(), err)
|
||||
}
|
||||
imported += 1
|
||||
|
|
|
|||
|
|
@ -1245,28 +1245,6 @@ func setIPC(ctx *cli.Context, cfg *node.Config) {
|
|||
}
|
||||
}
|
||||
|
||||
// setLes shows the deprecation warnings for LES flags.
|
||||
func setLes(ctx *cli.Context, cfg *ethconfig.Config) {
|
||||
if ctx.IsSet(LightServeFlag.Name) {
|
||||
log.Warn("The light server has been deprecated, please remove this flag", "flag", LightServeFlag.Name)
|
||||
}
|
||||
if ctx.IsSet(LightIngressFlag.Name) {
|
||||
log.Warn("The light server has been deprecated, please remove this flag", "flag", LightIngressFlag.Name)
|
||||
}
|
||||
if ctx.IsSet(LightEgressFlag.Name) {
|
||||
log.Warn("The light server has been deprecated, please remove this flag", "flag", LightEgressFlag.Name)
|
||||
}
|
||||
if ctx.IsSet(LightMaxPeersFlag.Name) {
|
||||
log.Warn("The light server has been deprecated, please remove this flag", "flag", LightMaxPeersFlag.Name)
|
||||
}
|
||||
if ctx.IsSet(LightNoPruneFlag.Name) {
|
||||
log.Warn("The light server has been deprecated, please remove this flag", "flag", LightNoPruneFlag.Name)
|
||||
}
|
||||
if ctx.IsSet(LightNoSyncServeFlag.Name) {
|
||||
log.Warn("The light server has been deprecated, please remove this flag", "flag", LightNoSyncServeFlag.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// MakeDatabaseHandles raises out the number of allowed file handles per process
|
||||
// for Geth and returns half of the allowance to assign to the database.
|
||||
func MakeDatabaseHandles(max int) int {
|
||||
|
|
@ -1571,8 +1549,8 @@ func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) {
|
|||
|
||||
// SetEthConfig applies eth-related command line flags to the config.
|
||||
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||
// Avoid conflicting network flags
|
||||
flags.CheckExclusive(ctx, MainnetFlag, DeveloperFlag, SepoliaFlag, HoleskyFlag, HoodiFlag)
|
||||
// Avoid conflicting network flags, don't allow network id override on preset networks
|
||||
flags.CheckExclusive(ctx, MainnetFlag, DeveloperFlag, SepoliaFlag, HoleskyFlag, HoodiFlag, NetworkIdFlag)
|
||||
flags.CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
|
||||
|
||||
// Set configurations from CLI flags
|
||||
|
|
@ -1582,7 +1560,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
|||
setBlobPool(ctx, &cfg.BlobPool)
|
||||
setMiner(ctx, &cfg.Miner)
|
||||
setRequiredBlocks(ctx, cfg)
|
||||
setLes(ctx, cfg)
|
||||
|
||||
// Cap the cache allowance and tune the garbage collector
|
||||
mem, err := gopsutil.VirtualMemory()
|
||||
|
|
@ -1712,7 +1689,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
|||
}
|
||||
}
|
||||
if ctx.IsSet(VMEnableDebugFlag.Name) {
|
||||
// TODO(fjl): force-enable this in --dev mode
|
||||
cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name)
|
||||
}
|
||||
|
||||
|
|
@ -1743,34 +1719,25 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
|||
// Override any default configs for hard coded networks.
|
||||
switch {
|
||||
case ctx.Bool(MainnetFlag.Name):
|
||||
if !ctx.IsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = 1
|
||||
}
|
||||
cfg.NetworkId = 1
|
||||
cfg.Genesis = core.DefaultGenesisBlock()
|
||||
SetDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash)
|
||||
case ctx.Bool(HoleskyFlag.Name):
|
||||
if !ctx.IsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = 17000
|
||||
}
|
||||
cfg.NetworkId = 17000
|
||||
cfg.Genesis = core.DefaultHoleskyGenesisBlock()
|
||||
SetDNSDiscoveryDefaults(cfg, params.HoleskyGenesisHash)
|
||||
case ctx.Bool(SepoliaFlag.Name):
|
||||
if !ctx.IsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = 11155111
|
||||
}
|
||||
cfg.NetworkId = 11155111
|
||||
cfg.Genesis = core.DefaultSepoliaGenesisBlock()
|
||||
SetDNSDiscoveryDefaults(cfg, params.SepoliaGenesisHash)
|
||||
case ctx.Bool(HoodiFlag.Name):
|
||||
if !ctx.IsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = 560048
|
||||
}
|
||||
cfg.NetworkId = 560048
|
||||
cfg.Genesis = core.DefaultHoodiGenesisBlock()
|
||||
SetDNSDiscoveryDefaults(cfg, params.HoodiGenesisHash)
|
||||
case ctx.Bool(DeveloperFlag.Name):
|
||||
if !ctx.IsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = 1337
|
||||
}
|
||||
cfg.NetworkId = 1337
|
||||
cfg.SyncMode = ethconfig.FullSync
|
||||
cfg.EnablePreimageRecording = true
|
||||
// Create new developer account or reuse existing one
|
||||
var (
|
||||
developer accounts.Account
|
||||
|
|
@ -1854,10 +1821,16 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
|||
if ctx.String(CryptoKZGFlag.Name) != "gokzg" && ctx.String(CryptoKZGFlag.Name) != "ckzg" {
|
||||
Fatalf("--%s flag must be 'gokzg' or 'ckzg'", CryptoKZGFlag.Name)
|
||||
}
|
||||
log.Info("Initializing the KZG library", "backend", ctx.String(CryptoKZGFlag.Name))
|
||||
if err := kzg4844.UseCKZG(ctx.String(CryptoKZGFlag.Name) == "ckzg"); err != nil {
|
||||
Fatalf("Failed to set KZG library implementation to %s: %v", ctx.String(CryptoKZGFlag.Name), err)
|
||||
}
|
||||
// The initialization of the KZG library can take up to 2 seconds
|
||||
// We start this here in parallel, so it should be available
|
||||
// once we start executing blocks. It's threadsafe.
|
||||
go func() {
|
||||
log.Info("Initializing the KZG library", "backend", ctx.String(CryptoKZGFlag.Name))
|
||||
if err := kzg4844.UseCKZG(ctx.String(CryptoKZGFlag.Name) == "ckzg"); err != nil {
|
||||
Fatalf("Failed to set KZG library implementation to %s: %v", ctx.String(CryptoKZGFlag.Name), err)
|
||||
}
|
||||
}()
|
||||
|
||||
// VM tracing config.
|
||||
if ctx.IsSet(VMTraceFlag.Name) {
|
||||
if name := ctx.String(VMTraceFlag.Name); name != "" {
|
||||
|
|
|
|||
|
|
@ -39,12 +39,6 @@ var DeprecatedFlags = []cli.Flag{
|
|||
CacheTrieRejournalFlag,
|
||||
LegacyDiscoveryV5Flag,
|
||||
TxLookupLimitFlag,
|
||||
LightServeFlag,
|
||||
LightIngressFlag,
|
||||
LightEgressFlag,
|
||||
LightMaxPeersFlag,
|
||||
LightNoPruneFlag,
|
||||
LightNoSyncServeFlag,
|
||||
LogBacktraceAtFlag,
|
||||
LogDebugFlag,
|
||||
MinerNewPayloadTimeoutFlag,
|
||||
|
|
@ -88,37 +82,6 @@ var (
|
|||
Value: ethconfig.Defaults.TransactionHistory,
|
||||
Category: flags.DeprecatedCategory,
|
||||
}
|
||||
// Light server and client settings, Deprecated November 2023
|
||||
LightServeFlag = &cli.IntFlag{
|
||||
Name: "light.serve",
|
||||
Usage: "Maximum percentage of time allowed for serving LES requests (deprecated)",
|
||||
Category: flags.DeprecatedCategory,
|
||||
}
|
||||
LightIngressFlag = &cli.IntFlag{
|
||||
Name: "light.ingress",
|
||||
Usage: "Incoming bandwidth limit for serving light clients (deprecated)",
|
||||
Category: flags.DeprecatedCategory,
|
||||
}
|
||||
LightEgressFlag = &cli.IntFlag{
|
||||
Name: "light.egress",
|
||||
Usage: "Outgoing bandwidth limit for serving light clients (deprecated)",
|
||||
Category: flags.DeprecatedCategory,
|
||||
}
|
||||
LightMaxPeersFlag = &cli.IntFlag{
|
||||
Name: "light.maxpeers",
|
||||
Usage: "Maximum number of light clients to serve, or light servers to attach to (deprecated)",
|
||||
Category: flags.DeprecatedCategory,
|
||||
}
|
||||
LightNoPruneFlag = &cli.BoolFlag{
|
||||
Name: "light.nopruning",
|
||||
Usage: "Disable ancient light chain data pruning (deprecated)",
|
||||
Category: flags.DeprecatedCategory,
|
||||
}
|
||||
LightNoSyncServeFlag = &cli.BoolFlag{
|
||||
Name: "light.nosyncserve",
|
||||
Usage: "Enables serving light clients before syncing (deprecated)",
|
||||
Category: flags.DeprecatedCategory,
|
||||
}
|
||||
// Deprecated November 2023
|
||||
LogBacktraceAtFlag = &cli.StringFlag{
|
||||
Name: "log.backtrace",
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
|||
if !disk {
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
} else {
|
||||
pdb, err := pebble.New(b.TempDir(), 128, 128, "", false, true)
|
||||
pdb, err := pebble.New(b.TempDir(), 128, 128, "", false)
|
||||
if err != nil {
|
||||
b.Fatalf("cannot create temporary database: %v", err)
|
||||
}
|
||||
|
|
@ -303,7 +303,7 @@ func makeChainForBench(db ethdb.Database, genesis *Genesis, full bool, count uin
|
|||
func benchWriteChain(b *testing.B, full bool, count uint64) {
|
||||
genesis := &Genesis{Config: params.AllEthashProtocolChanges}
|
||||
for i := 0; i < b.N; i++ {
|
||||
pdb, err := pebble.New(b.TempDir(), 1024, 128, "", false, true)
|
||||
pdb, err := pebble.New(b.TempDir(), 1024, 128, "", false)
|
||||
if err != nil {
|
||||
b.Fatalf("error opening database: %v", err)
|
||||
}
|
||||
|
|
@ -316,7 +316,7 @@ func benchWriteChain(b *testing.B, full bool, count uint64) {
|
|||
func benchReadChain(b *testing.B, full bool, count uint64) {
|
||||
dir := b.TempDir()
|
||||
|
||||
pdb, err := pebble.New(dir, 1024, 128, "", false, true)
|
||||
pdb, err := pebble.New(dir, 1024, 128, "", false)
|
||||
if err != nil {
|
||||
b.Fatalf("error opening database: %v", err)
|
||||
}
|
||||
|
|
@ -332,7 +332,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
|
|||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
pdb, err = pebble.New(dir, 1024, 128, "", false, true)
|
||||
pdb, err = pebble.New(dir, 1024, 128, "", false)
|
||||
if err != nil {
|
||||
b.Fatalf("error opening database: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,8 @@ var (
|
|||
headFinalizedBlockGauge = metrics.NewRegisteredGauge("chain/head/finalized", nil)
|
||||
headSafeBlockGauge = metrics.NewRegisteredGauge("chain/head/safe", nil)
|
||||
|
||||
chainInfoGauge = metrics.NewRegisteredGaugeInfo("chain/info", nil)
|
||||
chainInfoGauge = metrics.NewRegisteredGaugeInfo("chain/info", nil)
|
||||
chainMgaspsGauge = metrics.NewRegisteredGauge("chain/mgasps", nil)
|
||||
|
||||
accountReadTimer = metrics.NewRegisteredResettingTimer("chain/account/reads", nil)
|
||||
accountHashTimer = metrics.NewRegisteredResettingTimer("chain/account/hashes", nil)
|
||||
|
|
@ -92,8 +93,10 @@ var (
|
|||
blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil)
|
||||
blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil)
|
||||
|
||||
blockPrefetchExecuteTimer = metrics.NewRegisteredTimer("chain/prefetch/executes", nil)
|
||||
blockPrefetchInterruptMeter = metrics.NewRegisteredMeter("chain/prefetch/interrupts", nil)
|
||||
blockPrefetchExecuteTimer = metrics.NewRegisteredResettingTimer("chain/prefetch/executes", nil)
|
||||
blockPrefetchInterruptMeter = metrics.NewRegisteredMeter("chain/prefetch/interrupts", nil)
|
||||
blockPrefetchTxsInvalidMeter = metrics.NewRegisteredMeter("chain/prefetch/txs/invalid", nil)
|
||||
blockPrefetchTxsValidMeter = metrics.NewRegisteredMeter("chain/prefetch/txs/valid", nil)
|
||||
|
||||
errInsertionInterrupted = errors.New("insertion is interrupted")
|
||||
errChainStopped = errors.New("blockchain is stopped")
|
||||
|
|
@ -180,8 +183,13 @@ func (c *CacheConfig) triedbConfig(isVerkle bool) *triedb.Config {
|
|||
}
|
||||
if c.StateScheme == rawdb.PathScheme {
|
||||
config.PathDB = &pathdb.Config{
|
||||
StateHistory: c.StateHistory,
|
||||
CleanCacheSize: c.TrieCleanLimit * 1024 * 1024,
|
||||
StateHistory: c.StateHistory,
|
||||
TrieCleanSize: c.TrieCleanLimit * 1024 * 1024,
|
||||
StateCleanSize: c.SnapshotLimit * 1024 * 1024,
|
||||
|
||||
// TODO(rjl493456442): The write buffer represents the memory limit used
|
||||
// for flushing both trie data and state data to disk. The config name
|
||||
// should be updated to eliminate the confusion.
|
||||
WriteBufferSize: c.TrieDirtyLimit * 1024 * 1024,
|
||||
}
|
||||
}
|
||||
|
|
@ -377,11 +385,14 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
|||
// Do nothing here until the state syncer picks it up.
|
||||
log.Info("Genesis state is missing, wait state sync")
|
||||
} else {
|
||||
// Head state is missing, before the state recovery, find out the
|
||||
// disk layer point of snapshot(if it's enabled). Make sure the
|
||||
// rewound point is lower than disk layer.
|
||||
// Head state is missing, before the state recovery, find out the disk
|
||||
// layer point of snapshot(if it's enabled). Make sure the rewound point
|
||||
// is lower than disk layer.
|
||||
//
|
||||
// Note it's unnecessary in path mode which always keep trie data and
|
||||
// state data consistent.
|
||||
var diskRoot common.Hash
|
||||
if bc.cacheConfig.SnapshotLimit > 0 {
|
||||
if bc.cacheConfig.SnapshotLimit > 0 && bc.cacheConfig.StateScheme == rawdb.HashScheme {
|
||||
diskRoot = rawdb.ReadSnapshotRoot(bc.db)
|
||||
}
|
||||
if diskRoot != (common.Hash{}) {
|
||||
|
|
@ -454,7 +465,32 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
|||
bc.logger.OnGenesisBlock(bc.genesisBlock, alloc)
|
||||
}
|
||||
}
|
||||
bc.setupSnapshot()
|
||||
|
||||
// Rewind the chain in case of an incompatible config upgrade.
|
||||
if compatErr != nil {
|
||||
log.Warn("Rewinding chain to upgrade configuration", "err", compatErr)
|
||||
if compatErr.RewindToTime > 0 {
|
||||
bc.SetHeadWithTimestamp(compatErr.RewindToTime)
|
||||
} else {
|
||||
bc.SetHead(compatErr.RewindToBlock)
|
||||
}
|
||||
rawdb.WriteChainConfig(db, genesisHash, chainConfig)
|
||||
}
|
||||
|
||||
// Start tx indexer if it's enabled.
|
||||
if txLookupLimit != nil {
|
||||
bc.txIndexer = newTxIndexer(*txLookupLimit, bc)
|
||||
}
|
||||
return bc, nil
|
||||
}
|
||||
|
||||
func (bc *BlockChain) setupSnapshot() {
|
||||
// Short circuit if the chain is established with path scheme, as the
|
||||
// state snapshot has been integrated into path database natively.
|
||||
if bc.cacheConfig.StateScheme == rawdb.PathScheme {
|
||||
return
|
||||
}
|
||||
// Load any existing snapshot, regenerating it if loading failed
|
||||
if bc.cacheConfig.SnapshotLimit > 0 {
|
||||
// If the chain was rewound past the snapshot persistent layer (causing
|
||||
|
|
@ -462,7 +498,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
|||
// in recovery mode and in that case, don't invalidate the snapshot on a
|
||||
// head mismatch.
|
||||
var recover bool
|
||||
|
||||
head := bc.CurrentBlock()
|
||||
if layer := rawdb.ReadSnapshotRecoveryNumber(bc.db); layer != nil && *layer >= head.Number.Uint64() {
|
||||
log.Warn("Enabling snapshot recovery", "chainhead", head.Number, "diskbase", *layer)
|
||||
|
|
@ -479,22 +514,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
|||
// Re-initialize the state database with snapshot
|
||||
bc.statedb = state.NewDatabase(bc.triedb, bc.snaps)
|
||||
}
|
||||
|
||||
// Rewind the chain in case of an incompatible config upgrade.
|
||||
if compatErr != nil {
|
||||
log.Warn("Rewinding chain to upgrade configuration", "err", compatErr)
|
||||
if compatErr.RewindToTime > 0 {
|
||||
bc.SetHeadWithTimestamp(compatErr.RewindToTime)
|
||||
} else {
|
||||
bc.SetHead(compatErr.RewindToBlock)
|
||||
}
|
||||
rawdb.WriteChainConfig(db, genesisHash, chainConfig)
|
||||
}
|
||||
// Start tx indexer if it's enabled.
|
||||
if txLookupLimit != nil {
|
||||
bc.txIndexer = newTxIndexer(*txLookupLimit, bc)
|
||||
}
|
||||
return bc, nil
|
||||
}
|
||||
|
||||
// empty returns an indicator whether the blockchain is empty.
|
||||
|
|
@ -979,17 +998,16 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
|
|||
// Ignore the error here since light client won't hit this path
|
||||
frozen, _ := bc.db.Ancients()
|
||||
if num+1 <= frozen {
|
||||
// Truncate all relative data(header, total difficulty, body, receipt
|
||||
// and canonical hash) from ancient store.
|
||||
if _, err := bc.db.TruncateHead(num); err != nil {
|
||||
log.Crit("Failed to truncate ancient data", "number", num, "err", err)
|
||||
}
|
||||
// Remove the hash <-> number mapping from the active store.
|
||||
rawdb.DeleteHeaderNumber(db, hash)
|
||||
// The chain segment, such as the block header, canonical hash,
|
||||
// body, and receipt, will be removed from the ancient store
|
||||
// in one go.
|
||||
//
|
||||
// The hash-to-number mapping in the key-value store will be
|
||||
// removed by the hc.SetHead function.
|
||||
} else {
|
||||
// Remove relative body and receipts from the active store.
|
||||
// The header, total difficulty and canonical hash will be
|
||||
// removed in the hc.SetHead function.
|
||||
// Remove the associated body and receipts from the key-value store.
|
||||
// The header, hash-to-number mapping, and canonical hash will be
|
||||
// removed by the hc.SetHead function.
|
||||
rawdb.DeleteBody(db, hash, num)
|
||||
rawdb.DeleteReceipts(db, hash, num)
|
||||
}
|
||||
|
|
@ -1291,12 +1309,11 @@ const (
|
|||
//
|
||||
// The optional ancientLimit can also be specified and chain segment before that
|
||||
// will be directly stored in the ancient, getting rid of the chain migration.
|
||||
func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts, ancientLimit uint64) (int, error) {
|
||||
func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []rlp.RawValue, ancientLimit uint64) (int, error) {
|
||||
// Verify the supplied headers before insertion without lock
|
||||
var headers []*types.Header
|
||||
for _, block := range blockChain {
|
||||
headers = append(headers, block.Header())
|
||||
|
||||
// Here we also validate that blob transactions in the block do not
|
||||
// contain a sidecar. While the sidecar does not affect the block hash
|
||||
// or tx hash, sending blobs within a block is not allowed.
|
||||
|
|
@ -1339,11 +1356,11 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
|||
//
|
||||
// this function only accepts canonical chain data. All side chain will be reverted
|
||||
// eventually.
|
||||
writeAncient := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
|
||||
writeAncient := func(blockChain types.Blocks, receiptChain []rlp.RawValue) (int, error) {
|
||||
// Ensure genesis is in the ancient store
|
||||
if blockChain[0].NumberU64() == 1 {
|
||||
if frozen, _ := bc.db.Ancients(); frozen == 0 {
|
||||
writeSize, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []types.Receipts{nil})
|
||||
writeSize, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []rlp.RawValue{rlp.EmptyList})
|
||||
if err != nil {
|
||||
log.Error("Error writing genesis to ancients", "err", err)
|
||||
return 0, err
|
||||
|
|
@ -1361,7 +1378,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
|||
size += writeSize
|
||||
|
||||
// Sync the ancient store explicitly to ensure all data has been flushed to disk.
|
||||
if err := bc.db.Sync(); err != nil {
|
||||
if err := bc.db.SyncAncient(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Write hash to number mappings
|
||||
|
|
@ -1386,7 +1403,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
|||
// existing local chain segments (reorg around the chain tip). The reorganized part
|
||||
// will be included in the provided chain segment, and stale canonical markers will be
|
||||
// silently rewritten. Therefore, no explicit reorg logic is needed.
|
||||
writeLive := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
|
||||
writeLive := func(blockChain types.Blocks, receiptChain []rlp.RawValue) (int, error) {
|
||||
var (
|
||||
skipPresenceCheck = false
|
||||
batch = bc.db.NewBatch()
|
||||
|
|
@ -1411,7 +1428,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
|||
// Write all the data out into the database
|
||||
rawdb.WriteCanonicalHash(batch, block.Hash(), block.NumberU64())
|
||||
rawdb.WriteBlock(batch, block)
|
||||
rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i])
|
||||
rawdb.WriteRawReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i])
|
||||
|
||||
// Write everything belongs to the blocks into the database. So that
|
||||
// we can ensure all components of body is completed(body, receipts)
|
||||
|
|
@ -1759,18 +1776,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
|||
bc.reportBlock(block, nil, err)
|
||||
return nil, it.index, err
|
||||
}
|
||||
// No validation errors for the first block (or chain prefix skipped)
|
||||
var activeState *state.StateDB
|
||||
defer func() {
|
||||
// The chain importer is starting and stopping trie prefetchers. If a bad
|
||||
// block or other error is hit however, an early return may not properly
|
||||
// terminate the background threads. This defer ensures that we clean up
|
||||
// and dangling prefetcher, without deferring each and holding on live refs.
|
||||
if activeState != nil {
|
||||
activeState.StopPrefetcher()
|
||||
}
|
||||
}()
|
||||
|
||||
// Track the singleton witness from this chain insertion (if any)
|
||||
var witness *stateless.Witness
|
||||
|
||||
|
|
@ -1826,63 +1831,20 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
|||
continue
|
||||
}
|
||||
// Retrieve the parent block and it's state to execute on top
|
||||
start := time.Now()
|
||||
parent := it.previous()
|
||||
if parent == nil {
|
||||
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
|
||||
}
|
||||
statedb, err := state.New(parent.Root, bc.statedb)
|
||||
if err != nil {
|
||||
return nil, it.index, err
|
||||
}
|
||||
|
||||
// If we are past Byzantium, enable prefetching to pull in trie node paths
|
||||
// while processing transactions. Before Byzantium the prefetcher is mostly
|
||||
// useless due to the intermediate root hashing after each transaction.
|
||||
if bc.chainConfig.IsByzantium(block.Number()) {
|
||||
// Generate witnesses either if we're self-testing, or if it's the
|
||||
// only block being inserted. A bit crude, but witnesses are huge,
|
||||
// so we refuse to make an entire chain of them.
|
||||
if bc.vmConfig.StatelessSelfValidation || (makeWitness && len(chain) == 1) {
|
||||
witness, err = stateless.NewWitness(block.Header(), bc)
|
||||
if err != nil {
|
||||
return nil, it.index, err
|
||||
}
|
||||
}
|
||||
statedb.StartPrefetcher("chain", witness)
|
||||
}
|
||||
activeState = statedb
|
||||
|
||||
// If we have a followup block, run that against the current state to pre-cache
|
||||
// transactions and probabilistically some of the account/storage trie nodes.
|
||||
var followupInterrupt atomic.Bool
|
||||
if !bc.cacheConfig.TrieCleanNoPrefetch {
|
||||
if followup, err := it.peek(); followup != nil && err == nil {
|
||||
throwaway, _ := state.New(parent.Root, bc.statedb)
|
||||
|
||||
go func(start time.Time, followup *types.Block, throwaway *state.StateDB) {
|
||||
// Disable tracing for prefetcher executions.
|
||||
vmCfg := bc.vmConfig
|
||||
vmCfg.Tracer = nil
|
||||
bc.prefetcher.Prefetch(followup, throwaway, vmCfg, &followupInterrupt)
|
||||
|
||||
blockPrefetchExecuteTimer.Update(time.Since(start))
|
||||
if followupInterrupt.Load() {
|
||||
blockPrefetchInterruptMeter.Mark(1)
|
||||
}
|
||||
}(time.Now(), followup, throwaway)
|
||||
}
|
||||
}
|
||||
|
||||
// The traced section of block import.
|
||||
res, err := bc.processBlock(block, statedb, start, setHead)
|
||||
followupInterrupt.Store(true)
|
||||
start := time.Now()
|
||||
res, err := bc.processBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1)
|
||||
if err != nil {
|
||||
return nil, it.index, err
|
||||
}
|
||||
// Report the import stats before returning the various results
|
||||
stats.processed++
|
||||
stats.usedGas += res.usedGas
|
||||
witness = res.witness
|
||||
|
||||
var snapDiffItems, snapBufItems common.StorageSize
|
||||
if bc.snaps != nil {
|
||||
|
|
@ -1938,11 +1900,74 @@ type blockProcessingResult struct {
|
|||
usedGas uint64
|
||||
procTime time.Duration
|
||||
status WriteStatus
|
||||
witness *stateless.Witness
|
||||
}
|
||||
|
||||
// processBlock executes and validates the given block. If there was no error
|
||||
// it writes the block and associated state to database.
|
||||
func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, start time.Time, setHead bool) (_ *blockProcessingResult, blockEndErr error) {
|
||||
func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool) (_ *blockProcessingResult, blockEndErr error) {
|
||||
var (
|
||||
err error
|
||||
startTime = time.Now()
|
||||
statedb *state.StateDB
|
||||
interrupt atomic.Bool
|
||||
)
|
||||
defer interrupt.Store(true) // terminate the prefetch at the end
|
||||
|
||||
if bc.cacheConfig.TrieCleanNoPrefetch {
|
||||
statedb, err = state.New(parentRoot, bc.statedb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// If prefetching is enabled, run that against the current state to pre-cache
|
||||
// transactions and probabilistically some of the account/storage trie nodes.
|
||||
//
|
||||
// Note: the main processor and prefetcher share the same reader with a local
|
||||
// cache for mitigating the overhead of state access.
|
||||
reader, err := bc.statedb.ReaderWithCache(parentRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
throwaway, err := state.NewWithReader(parentRoot, bc.statedb, reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
statedb, err = state.NewWithReader(parentRoot, bc.statedb, reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go func(start time.Time, throwaway *state.StateDB, block *types.Block) {
|
||||
// Disable tracing for prefetcher executions.
|
||||
vmCfg := bc.vmConfig
|
||||
vmCfg.Tracer = nil
|
||||
bc.prefetcher.Prefetch(block, throwaway, vmCfg, &interrupt)
|
||||
|
||||
blockPrefetchExecuteTimer.Update(time.Since(start))
|
||||
if interrupt.Load() {
|
||||
blockPrefetchInterruptMeter.Mark(1)
|
||||
}
|
||||
}(time.Now(), throwaway, block)
|
||||
}
|
||||
|
||||
// If we are past Byzantium, enable prefetching to pull in trie node paths
|
||||
// while processing transactions. Before Byzantium the prefetcher is mostly
|
||||
// useless due to the intermediate root hashing after each transaction.
|
||||
var witness *stateless.Witness
|
||||
if bc.chainConfig.IsByzantium(block.Number()) {
|
||||
// Generate witnesses either if we're self-testing, or if it's the
|
||||
// only block being inserted. A bit crude, but witnesses are huge,
|
||||
// so we refuse to make an entire chain of them.
|
||||
if bc.vmConfig.StatelessSelfValidation || makeWitness {
|
||||
witness, err = stateless.NewWitness(block.Header(), bc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
statedb.StartPrefetcher("chain", witness)
|
||||
defer statedb.StopPrefetcher()
|
||||
}
|
||||
|
||||
if bc.logger != nil && bc.logger.OnBlockStart != nil {
|
||||
bc.logger.OnBlockStart(tracing.BlockEvent{
|
||||
Block: block,
|
||||
|
|
@ -2001,7 +2026,7 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
|
|||
}
|
||||
}
|
||||
xvtime := time.Since(xvstart)
|
||||
proctime := time.Since(start) // processing + validation + cross validation
|
||||
proctime := time.Since(startTime) // processing + validation + cross validation
|
||||
|
||||
// Update the metrics touched during block processing and validation
|
||||
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing)
|
||||
|
|
@ -2042,9 +2067,14 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
|
|||
triedbCommitTimer.Update(statedb.TrieDBCommits) // Trie database commits are complete, we can mark them
|
||||
|
||||
blockWriteTimer.Update(time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits)
|
||||
blockInsertTimer.UpdateSince(start)
|
||||
blockInsertTimer.UpdateSince(startTime)
|
||||
|
||||
return &blockProcessingResult{usedGas: res.GasUsed, procTime: proctime, status: status}, nil
|
||||
return &blockProcessingResult{
|
||||
usedGas: res.GasUsed,
|
||||
procTime: proctime,
|
||||
status: status,
|
||||
witness: witness,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// insertSideChain is called when an import batch hits upon a pruned ancestor
|
||||
|
|
@ -2528,14 +2558,22 @@ func (bc *BlockChain) reportBlock(block *types.Block, res *ProcessResult, err er
|
|||
// logForkReadiness will write a log when a future fork is scheduled, but not
|
||||
// active. This is useful so operators know their client is ready for the fork.
|
||||
func (bc *BlockChain) logForkReadiness(block *types.Block) {
|
||||
c := bc.Config()
|
||||
current, last := c.LatestFork(block.Time()), c.LatestFork(math.MaxUint64)
|
||||
t := c.Timestamp(last)
|
||||
if t == nil {
|
||||
config := bc.Config()
|
||||
current, last := config.LatestFork(block.Time()), config.LatestFork(math.MaxUint64)
|
||||
|
||||
// Short circuit if the timestamp of the last fork is undefined,
|
||||
// or if the network has already passed the last configured fork.
|
||||
t := config.Timestamp(last)
|
||||
if t == nil || current >= last {
|
||||
return
|
||||
}
|
||||
at := time.Unix(int64(*t), 0)
|
||||
if current < last && time.Now().After(bc.lastForkReadyAlert.Add(forkReadyInterval)) {
|
||||
|
||||
// Only log if:
|
||||
// - Current time is before the fork activation time
|
||||
// - Enough time has passed since last alert
|
||||
now := time.Now()
|
||||
if now.Before(at) && now.After(bc.lastForkReadyAlert.Add(forkReadyInterval)) {
|
||||
log.Info("Ready for fork activation", "fork", last, "date", at.Format(time.RFC822),
|
||||
"remaining", time.Until(at).Round(time.Second), "timestamp", at.Unix())
|
||||
bc.lastForkReadyAlert = time.Now()
|
||||
|
|
@ -2611,7 +2649,7 @@ func (bc *BlockChain) InsertHeadersBeforeCutoff(headers []*types.Header) (int, e
|
|||
first = headers[0].Number.Uint64()
|
||||
)
|
||||
if first == 1 && frozen == 0 {
|
||||
_, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []types.Receipts{nil})
|
||||
_, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []rlp.RawValue{rlp.EmptyList})
|
||||
if err != nil {
|
||||
log.Error("Error writing genesis to ancients", "err", err)
|
||||
return 0, err
|
||||
|
|
@ -2627,7 +2665,8 @@ func (bc *BlockChain) InsertHeadersBeforeCutoff(headers []*types.Header) (int, e
|
|||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := bc.db.Sync(); err != nil {
|
||||
// Sync the ancient store explicitly to ensure all data has been flushed to disk.
|
||||
if err := bc.db.SyncAncient(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Write hash to number mappings
|
||||
|
|
|
|||
|
|
@ -43,8 +43,12 @@ func (st *insertStats) report(chain []*types.Block, index int, snapDiffItems, sn
|
|||
// Fetch the timings for the batch
|
||||
var (
|
||||
now = mclock.Now()
|
||||
elapsed = now.Sub(st.startTime)
|
||||
elapsed = now.Sub(st.startTime) + 1 // prevent zero division
|
||||
mgasps = float64(st.usedGas) * 1000 / float64(elapsed)
|
||||
)
|
||||
// Update the Mgas per second gauge
|
||||
chainMgaspsGauge.Update(int64(mgasps))
|
||||
|
||||
// If we're at the last block of the batch or report period reached, log
|
||||
if index == len(chain)-1 || elapsed >= statsReportLimit {
|
||||
// Count the number of transactions in this segment
|
||||
|
|
@ -58,7 +62,7 @@ func (st *insertStats) report(chain []*types.Block, index int, snapDiffItems, sn
|
|||
context := []interface{}{
|
||||
"number", end.Number(), "hash", end.Hash(),
|
||||
"blocks", st.processed, "txs", txs, "mgas", float64(st.usedGas) / 1000000,
|
||||
"elapsed", common.PrettyDuration(elapsed), "mgasps", float64(st.usedGas) * 1000 / float64(elapsed),
|
||||
"elapsed", common.PrettyDuration(elapsed), "mgasps", mgasps,
|
||||
}
|
||||
if timestamp := time.Unix(int64(end.Time()), 0); time.Since(timestamp) > time.Minute {
|
||||
context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...)
|
||||
|
|
@ -138,6 +142,7 @@ func (it *insertIterator) next() (*types.Block, error) {
|
|||
//
|
||||
// Both header and body validation errors (nil too) is cached into the iterator
|
||||
// to avoid duplicating work on the following next() call.
|
||||
// nolint:unused
|
||||
func (it *insertIterator) peek() (*types.Block, error) {
|
||||
// If we reached the end of the chain, abort
|
||||
if it.index+1 >= len(it.chain) {
|
||||
|
|
|
|||
|
|
@ -234,6 +234,24 @@ func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
|||
return receipts
|
||||
}
|
||||
|
||||
// GetRawReceipts retrieves the receipts for all transactions in a given block
|
||||
// without deriving the internal fields and the Bloom.
|
||||
func (bc *BlockChain) GetRawReceipts(hash common.Hash, number uint64) types.Receipts {
|
||||
if receipts, ok := bc.receiptsCache.Get(hash); ok {
|
||||
return receipts
|
||||
}
|
||||
return rawdb.ReadRawReceipts(bc.db, hash, number)
|
||||
}
|
||||
|
||||
// GetReceiptsRLP retrieves the receipts of a block.
|
||||
func (bc *BlockChain) GetReceiptsRLP(hash common.Hash) rlp.RawValue {
|
||||
number := rawdb.ReadHeaderNumber(bc.db, hash)
|
||||
if number == nil {
|
||||
return nil
|
||||
}
|
||||
return rawdb.ReadReceiptsRLP(bc.db, hash, *number)
|
||||
}
|
||||
|
||||
// GetUnclesInChain retrieves all the uncles from a given block backwards until
|
||||
// a specific distance is reached.
|
||||
func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.Header {
|
||||
|
|
@ -262,42 +280,20 @@ func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, max
|
|||
// GetTransactionLookup retrieves the lookup along with the transaction
|
||||
// itself associate with the given transaction hash.
|
||||
//
|
||||
// An error will be returned if the transaction is not found, and background
|
||||
// indexing for transactions is still in progress. The transaction might be
|
||||
// reachable shortly once it's indexed.
|
||||
//
|
||||
// A null will be returned in the transaction is not found and background
|
||||
// transaction indexing is already finished. The transaction is not existent
|
||||
// from the node's perspective.
|
||||
func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction, error) {
|
||||
// A null will be returned if the transaction is not found. This can be due to
|
||||
// the transaction indexer not being finished. The caller must explicitly check
|
||||
// the indexer progress.
|
||||
func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction) {
|
||||
bc.txLookupLock.RLock()
|
||||
defer bc.txLookupLock.RUnlock()
|
||||
|
||||
// Short circuit if the txlookup already in the cache, retrieve otherwise
|
||||
if item, exist := bc.txLookupCache.Get(hash); exist {
|
||||
return item.lookup, item.transaction, nil
|
||||
return item.lookup, item.transaction
|
||||
}
|
||||
tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash)
|
||||
if tx == nil {
|
||||
progress, err := bc.TxIndexProgress()
|
||||
if err != nil {
|
||||
// No error is returned if the transaction indexing progress is unreachable
|
||||
// due to unexpected internal errors. In such cases, it is impossible to
|
||||
// determine whether the transaction does not exist or has simply not been
|
||||
// indexed yet without a progress marker.
|
||||
//
|
||||
// In such scenarios, the transaction is treated as unreachable, though
|
||||
// this is clearly an unintended and unexpected situation.
|
||||
return nil, nil, nil
|
||||
}
|
||||
// The transaction indexing is not finished yet, returning an
|
||||
// error to explicitly indicate it.
|
||||
if !progress.Done() {
|
||||
return nil, nil, errors.New("transaction indexing still in progress")
|
||||
}
|
||||
// The transaction is already indexed, the transaction is either
|
||||
// not existent or not in the range of index, returning null.
|
||||
return nil, nil, nil
|
||||
return nil, nil
|
||||
}
|
||||
lookup := &rawdb.LegacyTxLookupEntry{
|
||||
BlockHash: blockHash,
|
||||
|
|
@ -308,7 +304,23 @@ func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLoo
|
|||
lookup: lookup,
|
||||
transaction: tx,
|
||||
})
|
||||
return lookup, tx, nil
|
||||
return lookup, tx
|
||||
}
|
||||
|
||||
// TxIndexDone returns true if the transaction indexer has finished indexing.
|
||||
func (bc *BlockChain) TxIndexDone() bool {
|
||||
progress, err := bc.TxIndexProgress()
|
||||
if err != nil {
|
||||
// No error is returned if the transaction indexing progress is unreachable
|
||||
// due to unexpected internal errors. In such cases, it is impossible to
|
||||
// determine whether the transaction does not exist or has simply not been
|
||||
// indexed yet without a progress marker.
|
||||
//
|
||||
// In such scenarios, the transaction is treated as unreachable, though
|
||||
// this is clearly an unintended and unexpected situation.
|
||||
return true
|
||||
}
|
||||
return progress.Done()
|
||||
}
|
||||
|
||||
// HasState checks if state trie is fully present in the database or not.
|
||||
|
|
@ -404,7 +416,7 @@ func (bc *BlockChain) TxIndexProgress() (TxIndexProgress, error) {
|
|||
if bc.txIndexer == nil {
|
||||
return TxIndexProgress{}, errors.New("tx indexer is not enabled")
|
||||
}
|
||||
return bc.txIndexer.txIndexProgress()
|
||||
return bc.txIndexer.txIndexProgress(), nil
|
||||
}
|
||||
|
||||
// HistoryPruningCutoff returns the configured history pruning point.
|
||||
|
|
|
|||
|
|
@ -1765,7 +1765,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
|||
datadir := t.TempDir()
|
||||
ancient := filepath.Join(datadir, "ancient")
|
||||
|
||||
pdb, err := pebble.New(datadir, 0, 0, "", false, true)
|
||||
pdb, err := pebble.New(datadir, 0, 0, "", false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||
}
|
||||
|
|
@ -1791,7 +1791,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
|||
}
|
||||
)
|
||||
defer engine.Close()
|
||||
if snapshots {
|
||||
if snapshots && scheme == rawdb.HashScheme {
|
||||
config.SnapshotLimit = 256
|
||||
config.SnapshotWait = true
|
||||
}
|
||||
|
|
@ -1820,7 +1820,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
|||
if err := chain.triedb.Commit(canonblocks[tt.commitBlock-1].Root(), false); err != nil {
|
||||
t.Fatalf("Failed to flush trie state: %v", err)
|
||||
}
|
||||
if snapshots {
|
||||
if snapshots && scheme == rawdb.HashScheme {
|
||||
if err := chain.snaps.Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil {
|
||||
t.Fatalf("Failed to flatten snapshots: %v", err)
|
||||
}
|
||||
|
|
@ -1850,7 +1850,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
|||
chain.stopWithoutSaving()
|
||||
|
||||
// Start a new blockchain back up and see where the repair leads us
|
||||
pdb, err = pebble.New(datadir, 0, 0, "", false, true)
|
||||
pdb, err = pebble.New(datadir, 0, 0, "", false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to reopen persistent key-value database: %v", err)
|
||||
}
|
||||
|
|
@ -1915,7 +1915,7 @@ func testIssue23496(t *testing.T, scheme string) {
|
|||
datadir := t.TempDir()
|
||||
ancient := filepath.Join(datadir, "ancient")
|
||||
|
||||
pdb, err := pebble.New(datadir, 0, 0, "", false, true)
|
||||
pdb, err := pebble.New(datadir, 0, 0, "", false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||
}
|
||||
|
|
@ -1952,8 +1952,10 @@ func testIssue23496(t *testing.T, scheme string) {
|
|||
if _, err := chain.InsertChain(blocks[1:2]); err != nil {
|
||||
t.Fatalf("Failed to import canonical chain start: %v", err)
|
||||
}
|
||||
if err := chain.snaps.Cap(blocks[1].Root(), 0); err != nil {
|
||||
t.Fatalf("Failed to flatten snapshots: %v", err)
|
||||
if scheme == rawdb.HashScheme {
|
||||
if err := chain.snaps.Cap(blocks[1].Root(), 0); err != nil {
|
||||
t.Fatalf("Failed to flatten snapshots: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Insert block B3 and commit the state into disk
|
||||
|
|
@ -1973,7 +1975,7 @@ func testIssue23496(t *testing.T, scheme string) {
|
|||
chain.stopWithoutSaving()
|
||||
|
||||
// Start a new blockchain back up and see where the repair leads us
|
||||
pdb, err = pebble.New(datadir, 0, 0, "", false, true)
|
||||
pdb, err = pebble.New(datadir, 0, 0, "", false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to reopen persistent key-value database: %v", err)
|
||||
}
|
||||
|
|
@ -1997,15 +1999,23 @@ func testIssue23496(t *testing.T, scheme string) {
|
|||
}
|
||||
expHead := uint64(1)
|
||||
if scheme == rawdb.PathScheme {
|
||||
expHead = uint64(2)
|
||||
// The pathdb database makes sure that snapshot and trie are consistent,
|
||||
// so only the last block is reverted in case of a crash.
|
||||
expHead = uint64(3)
|
||||
}
|
||||
if head := chain.CurrentBlock(); head.Number.Uint64() != expHead {
|
||||
t.Errorf("Head block mismatch: have %d, want %d", head.Number, expHead)
|
||||
}
|
||||
|
||||
// Reinsert B2-B4
|
||||
if _, err := chain.InsertChain(blocks[1:]); err != nil {
|
||||
t.Fatalf("Failed to import canonical chain tail: %v", err)
|
||||
if scheme == rawdb.PathScheme {
|
||||
// Reinsert B4
|
||||
if _, err := chain.InsertChain(blocks[3:]); err != nil {
|
||||
t.Fatalf("Failed to import canonical chain tail: %v", err)
|
||||
}
|
||||
} else {
|
||||
// Reinsert B2-B4
|
||||
if _, err := chain.InsertChain(blocks[1:]); err != nil {
|
||||
t.Fatalf("Failed to import canonical chain tail: %v", err)
|
||||
}
|
||||
}
|
||||
if head := chain.CurrentHeader(); head.Number.Uint64() != uint64(4) {
|
||||
t.Errorf("Head header mismatch: have %d, want %d", head.Number, 4)
|
||||
|
|
@ -2016,7 +2026,9 @@ func testIssue23496(t *testing.T, scheme string) {
|
|||
if head := chain.CurrentBlock(); head.Number.Uint64() != uint64(4) {
|
||||
t.Errorf("Head block mismatch: have %d, want %d", head.Number, uint64(4))
|
||||
}
|
||||
if layer := chain.Snapshots().Snapshot(blocks[2].Root()); layer == nil {
|
||||
t.Error("Failed to regenerate the snapshot of known state")
|
||||
if scheme == rawdb.HashScheme {
|
||||
if layer := chain.Snapshots().Snapshot(blocks[2].Root()); layer == nil {
|
||||
t.Error("Failed to regenerate the snapshot of known state")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1969,7 +1969,7 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
|||
datadir := t.TempDir()
|
||||
ancient := filepath.Join(datadir, "ancient")
|
||||
|
||||
pdb, err := pebble.New(datadir, 0, 0, "", false, true)
|
||||
pdb, err := pebble.New(datadir, 0, 0, "", false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||
}
|
||||
|
|
@ -2023,7 +2023,7 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
|||
}
|
||||
if tt.commitBlock > 0 {
|
||||
chain.triedb.Commit(canonblocks[tt.commitBlock-1].Root(), false)
|
||||
if snapshots {
|
||||
if snapshots && scheme == rawdb.HashScheme {
|
||||
if err := chain.snaps.Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil {
|
||||
t.Fatalf("Failed to flatten snapshots: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo
|
|||
datadir := t.TempDir()
|
||||
ancient := filepath.Join(datadir, "ancient")
|
||||
|
||||
pdb, err := pebble.New(datadir, 0, 0, "", false, true)
|
||||
pdb, err := pebble.New(datadir, 0, 0, "", false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||
}
|
||||
|
|
@ -105,7 +105,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo
|
|||
if basic.commitBlock > 0 && basic.commitBlock == point {
|
||||
chain.TrieDB().Commit(blocks[point-1].Root(), false)
|
||||
}
|
||||
if basic.snapshotBlock > 0 && basic.snapshotBlock == point {
|
||||
if basic.snapshotBlock > 0 && basic.snapshotBlock == point && basic.scheme == rawdb.HashScheme {
|
||||
// Flushing the entire snap tree into the disk, the
|
||||
// relevant (a) snapshot root and (b) snapshot generator
|
||||
// will be persisted atomically.
|
||||
|
|
@ -149,13 +149,17 @@ func (basic *snapshotTestBasic) verify(t *testing.T, chain *BlockChain, blocks [
|
|||
block := chain.GetBlockByNumber(basic.expSnapshotBottom)
|
||||
if block == nil {
|
||||
t.Errorf("The corresponding block[%d] of snapshot disk layer is missing", basic.expSnapshotBottom)
|
||||
} else if !bytes.Equal(chain.snaps.DiskRoot().Bytes(), block.Root().Bytes()) {
|
||||
t.Errorf("The snapshot disk layer root is incorrect, want %x, get %x", block.Root(), chain.snaps.DiskRoot())
|
||||
} else if basic.scheme == rawdb.HashScheme {
|
||||
if !bytes.Equal(chain.snaps.DiskRoot().Bytes(), block.Root().Bytes()) {
|
||||
t.Errorf("The snapshot disk layer root is incorrect, want %x, get %x", block.Root(), chain.snaps.DiskRoot())
|
||||
}
|
||||
}
|
||||
|
||||
// Check the snapshot, ensure it's integrated
|
||||
if err := chain.snaps.Verify(block.Root()); err != nil {
|
||||
t.Errorf("The disk layer is not integrated %v", err)
|
||||
if basic.scheme == rawdb.HashScheme {
|
||||
if err := chain.snaps.Verify(block.Root()); err != nil {
|
||||
t.Errorf("The disk layer is not integrated %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -257,7 +261,7 @@ func (snaptest *crashSnapshotTest) test(t *testing.T) {
|
|||
chain.triedb.Close()
|
||||
|
||||
// Start a new blockchain back up and see where the repair leads us
|
||||
pdb, err := pebble.New(snaptest.datadir, 0, 0, "", false, true)
|
||||
pdb, err := pebble.New(snaptest.datadir, 0, 0, "", false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||
}
|
||||
|
|
@ -565,12 +569,14 @@ func TestHighCommitCrashWithNewSnapshot(t *testing.T) {
|
|||
//
|
||||
// Expected head header : C8
|
||||
// Expected head fast block: C8
|
||||
// Expected head block : G
|
||||
// Expected snapshot disk : C4
|
||||
// Expected head block : G (Hash mode), C6 (Hash mode)
|
||||
// Expected snapshot disk : C4 (Hash mode)
|
||||
for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} {
|
||||
expHead := uint64(0)
|
||||
if scheme == rawdb.PathScheme {
|
||||
expHead = uint64(4)
|
||||
// The pathdb database makes sure that snapshot and trie are consistent,
|
||||
// so only the last two blocks are reverted in case of a crash.
|
||||
expHead = uint64(6)
|
||||
}
|
||||
test := &crashSnapshotTest{
|
||||
snapshotTestBasic{
|
||||
|
|
|
|||
|
|
@ -734,7 +734,7 @@ func testFastVsFullChains(t *testing.T, scheme string) {
|
|||
fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
defer fast.Stop()
|
||||
|
||||
if n, err := fast.InsertReceiptChain(blocks, receipts, 0); err != nil {
|
||||
if n, err := fast.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), 0); err != nil {
|
||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||
}
|
||||
// Freezer style fast import the chain.
|
||||
|
|
@ -747,7 +747,7 @@ func testFastVsFullChains(t *testing.T, scheme string) {
|
|||
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
defer ancient.Stop()
|
||||
|
||||
if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(len(blocks)/2)); err != nil {
|
||||
if n, err := ancient.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), uint64(len(blocks)/2)); err != nil {
|
||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||
}
|
||||
|
||||
|
|
@ -871,7 +871,7 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) {
|
|||
fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
defer fast.Stop()
|
||||
|
||||
if n, err := fast.InsertReceiptChain(blocks, receipts, 0); err != nil {
|
||||
if n, err := fast.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), 0); err != nil {
|
||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||
}
|
||||
assert(t, "fast", fast, height, height, 0)
|
||||
|
|
@ -884,7 +884,7 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) {
|
|||
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
defer ancient.Stop()
|
||||
|
||||
if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil {
|
||||
if n, err := ancient.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), uint64(3*len(blocks)/4)); err != nil {
|
||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||
}
|
||||
assert(t, "ancient", ancient, height, height, 0)
|
||||
|
|
@ -1696,7 +1696,7 @@ func testBlockchainRecovery(t *testing.T, scheme string) {
|
|||
defer ancientDb.Close()
|
||||
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||
|
||||
if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil {
|
||||
if n, err := ancient.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), uint64(3*len(blocks)/4)); err != nil {
|
||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||
}
|
||||
rawdb.WriteLastPivotNumber(ancientDb, blocks[len(blocks)-1].NumberU64()) // Force fast sync behavior
|
||||
|
|
@ -1991,7 +1991,7 @@ func testInsertKnownChainData(t *testing.T, typ string, scheme string) {
|
|||
}
|
||||
} else if typ == "receipts" {
|
||||
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
|
||||
_, err = chain.InsertReceiptChain(blocks, receipts, 0)
|
||||
_, err = chain.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), 0)
|
||||
return err
|
||||
}
|
||||
asserter = func(t *testing.T, block *types.Block) {
|
||||
|
|
@ -2157,7 +2157,7 @@ func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight i
|
|||
}
|
||||
} else if typ == "receipts" {
|
||||
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
|
||||
_, err = chain.InsertReceiptChain(blocks, receipts, 0)
|
||||
_, err = chain.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), 0)
|
||||
return err
|
||||
}
|
||||
asserter = func(t *testing.T, block *types.Block) {
|
||||
|
|
@ -2492,7 +2492,7 @@ func testSideImportPrunedBlocks(t *testing.T, scheme string) {
|
|||
datadir := t.TempDir()
|
||||
ancient := path.Join(datadir, "ancient")
|
||||
|
||||
pdb, err := pebble.New(datadir, 0, 0, "", false, true)
|
||||
pdb, err := pebble.New(datadir, 0, 0, "", false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||
}
|
||||
|
|
@ -4099,7 +4099,7 @@ func TestEIP7702(t *testing.T) {
|
|||
// The way the auths are combined, it becomes
|
||||
// 1. tx -> addr1 which is delegated to 0xaaaa
|
||||
// 2. addr1:0xaaaa calls into addr2:0xbbbb
|
||||
// 3. addr2:0xbbbb writes to storage
|
||||
// 3. addr2:0xbbbb writes to storage
|
||||
auth1, _ := types.SignSetCode(key1, types.SetCodeAuthorization{
|
||||
ChainID: *uint256.MustFromBig(gspec.Config.ChainID),
|
||||
Address: aa,
|
||||
|
|
@ -4205,10 +4205,10 @@ func testChainReorgSnapSync(t *testing.T, ancientLimit uint64) {
|
|||
chain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.PathScheme), gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil)
|
||||
defer chain.Stop()
|
||||
|
||||
if n, err := chain.InsertReceiptChain(blocks, receipts, ancientLimit); err != nil {
|
||||
if n, err := chain.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), ancientLimit); err != nil {
|
||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||
}
|
||||
if n, err := chain.InsertReceiptChain(chainA, receiptsA, ancientLimit); err != nil {
|
||||
if n, err := chain.InsertReceiptChain(chainA, types.EncodeBlockReceiptLists(receiptsA), ancientLimit); err != nil {
|
||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||
}
|
||||
// If the common ancestor is below the ancient limit, rewind the chain head.
|
||||
|
|
@ -4218,7 +4218,7 @@ func testChainReorgSnapSync(t *testing.T, ancientLimit uint64) {
|
|||
rawdb.WriteLastPivotNumber(db, ancestor)
|
||||
chain.SetHead(ancestor)
|
||||
}
|
||||
if n, err := chain.InsertReceiptChain(chainB, receiptsB, ancientLimit); err != nil {
|
||||
if n, err := chain.InsertReceiptChain(chainB, types.EncodeBlockReceiptLists(receiptsB), ancientLimit); err != nil {
|
||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||
}
|
||||
head := chain.CurrentSnapBlock()
|
||||
|
|
@ -4336,7 +4336,7 @@ func testInsertChainWithCutoff(t *testing.T, cutoff uint64, ancientLimit uint64,
|
|||
if n, err := chain.InsertHeadersBeforeCutoff(headersBefore); err != nil {
|
||||
t.Fatalf("failed to insert headers before cutoff %d: %v", n, err)
|
||||
}
|
||||
if n, err := chain.InsertReceiptChain(blocksAfter, receiptsAfter, ancientLimit); err != nil {
|
||||
if n, err := chain.InsertReceiptChain(blocksAfter, types.EncodeBlockReceiptLists(receiptsAfter), ancientLimit); err != nil {
|
||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||
}
|
||||
headSnap := chain.CurrentSnapBlock()
|
||||
|
|
|
|||
|
|
@ -328,9 +328,13 @@ func (b *BlockGen) collectRequests(readonly bool) (requests [][]byte) {
|
|||
blockContext := NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase)
|
||||
evm := vm.NewEVM(blockContext, statedb, b.cm.config, vm.Config{})
|
||||
// EIP-7002
|
||||
ProcessWithdrawalQueue(&requests, evm)
|
||||
if err := ProcessWithdrawalQueue(&requests, evm); err != nil {
|
||||
panic(fmt.Sprintf("could not process withdrawal requests: %v", err))
|
||||
}
|
||||
// EIP-7251
|
||||
ProcessConsolidationQueue(&requests, evm)
|
||||
if err := ProcessConsolidationQueue(&requests, evm); err != nil {
|
||||
panic(fmt.Sprintf("could not process consolidation requests: %v", err))
|
||||
}
|
||||
}
|
||||
return requests
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ type blockchain interface {
|
|||
GetHeader(hash common.Hash, number uint64) *types.Header
|
||||
GetCanonicalHash(number uint64) common.Hash
|
||||
GetReceiptsByHash(hash common.Hash) types.Receipts
|
||||
GetRawReceipts(hash common.Hash, number uint64) types.Receipts
|
||||
}
|
||||
|
||||
// ChainView represents an immutable view of a chain with a block id and a set
|
||||
|
|
@ -58,47 +59,80 @@ func NewChainView(chain blockchain, number uint64, hash common.Hash) *ChainView
|
|||
return cv
|
||||
}
|
||||
|
||||
// getBlockHash returns the block hash belonging to the given block number.
|
||||
// HeadNumber returns the head block number of the chain view.
|
||||
func (cv *ChainView) HeadNumber() uint64 {
|
||||
return cv.headNumber
|
||||
}
|
||||
|
||||
// BlockHash returns the block hash belonging to the given block number.
|
||||
// Note that the hash of the head block is not returned because ChainView might
|
||||
// represent a view where the head block is currently being created.
|
||||
func (cv *ChainView) getBlockHash(number uint64) common.Hash {
|
||||
if number >= cv.headNumber {
|
||||
func (cv *ChainView) BlockHash(number uint64) common.Hash {
|
||||
cv.lock.Lock()
|
||||
defer cv.lock.Unlock()
|
||||
|
||||
if number > cv.headNumber {
|
||||
panic("invalid block number")
|
||||
}
|
||||
return cv.blockHash(number)
|
||||
}
|
||||
|
||||
// getBlockId returns the unique block id belonging to the given block number.
|
||||
// BlockId returns the unique block id belonging to the given block number.
|
||||
// Note that it is currently equal to the block hash. In the future it might
|
||||
// be a different id for future blocks if the log index root becomes part of
|
||||
// consensus and therefore rendering the index with the new head will happen
|
||||
// before the hash of that new head is available.
|
||||
func (cv *ChainView) getBlockId(number uint64) common.Hash {
|
||||
func (cv *ChainView) BlockId(number uint64) common.Hash {
|
||||
cv.lock.Lock()
|
||||
defer cv.lock.Unlock()
|
||||
|
||||
if number > cv.headNumber {
|
||||
panic("invalid block number")
|
||||
}
|
||||
return cv.blockHash(number)
|
||||
}
|
||||
|
||||
// getReceipts returns the set of receipts belonging to the block at the given
|
||||
// Header returns the block header at the given block number.
|
||||
func (cv *ChainView) Header(number uint64) *types.Header {
|
||||
return cv.chain.GetHeader(cv.BlockHash(number), number)
|
||||
}
|
||||
|
||||
// Receipts returns the set of receipts belonging to the block at the given
|
||||
// block number.
|
||||
func (cv *ChainView) getReceipts(number uint64) types.Receipts {
|
||||
if number > cv.headNumber {
|
||||
panic("invalid block number")
|
||||
}
|
||||
blockHash := cv.blockHash(number)
|
||||
func (cv *ChainView) Receipts(number uint64) types.Receipts {
|
||||
blockHash := cv.BlockHash(number)
|
||||
if blockHash == (common.Hash{}) {
|
||||
log.Error("Chain view: block hash unavailable", "number", number, "head", cv.headNumber)
|
||||
return nil
|
||||
}
|
||||
return cv.chain.GetReceiptsByHash(blockHash)
|
||||
}
|
||||
|
||||
// limitedView returns a new chain view that is a truncated version of the parent view.
|
||||
func (cv *ChainView) limitedView(newHead uint64) *ChainView {
|
||||
if newHead >= cv.headNumber {
|
||||
return cv
|
||||
// RawReceipts returns the set of receipts belonging to the block at the given
|
||||
// block number. Does not derive the fields of the receipts, should only be
|
||||
// used during creation of the filter maps, please use cv.Receipts during querying.
|
||||
func (cv *ChainView) RawReceipts(number uint64) types.Receipts {
|
||||
blockHash := cv.BlockHash(number)
|
||||
if blockHash == (common.Hash{}) {
|
||||
log.Error("Chain view: block hash unavailable", "number", number, "head", cv.headNumber)
|
||||
return nil
|
||||
}
|
||||
return NewChainView(cv.chain, newHead, cv.blockHash(newHead))
|
||||
return cv.chain.GetRawReceipts(blockHash, number)
|
||||
}
|
||||
|
||||
// SharedRange returns the block range shared by two chain views.
|
||||
func (cv *ChainView) SharedRange(cv2 *ChainView) common.Range[uint64] {
|
||||
cv.lock.Lock()
|
||||
defer cv.lock.Unlock()
|
||||
|
||||
if cv == nil || cv2 == nil || !cv.extendNonCanonical() || !cv2.extendNonCanonical() {
|
||||
return common.Range[uint64]{}
|
||||
}
|
||||
var sharedLen uint64
|
||||
for n := min(cv.headNumber+1-uint64(len(cv.hashes)), cv2.headNumber+1-uint64(len(cv2.hashes))); n <= cv.headNumber && n <= cv2.headNumber && cv.blockHash(n) == cv2.blockHash(n); n++ {
|
||||
sharedLen = n + 1
|
||||
}
|
||||
return common.NewRange(0, sharedLen)
|
||||
}
|
||||
|
||||
// equalViews returns true if the two chain views are equivalent.
|
||||
|
|
@ -106,7 +140,7 @@ func equalViews(cv1, cv2 *ChainView) bool {
|
|||
if cv1 == nil || cv2 == nil {
|
||||
return false
|
||||
}
|
||||
return cv1.headNumber == cv2.headNumber && cv1.getBlockId(cv1.headNumber) == cv2.getBlockId(cv2.headNumber)
|
||||
return cv1.headNumber == cv2.headNumber && cv1.BlockId(cv1.headNumber) == cv2.BlockId(cv2.headNumber)
|
||||
}
|
||||
|
||||
// matchViews returns true if the two chain views are equivalent up until the
|
||||
|
|
@ -120,9 +154,9 @@ func matchViews(cv1, cv2 *ChainView, number uint64) bool {
|
|||
return false
|
||||
}
|
||||
if number == cv1.headNumber || number == cv2.headNumber {
|
||||
return cv1.getBlockId(number) == cv2.getBlockId(number)
|
||||
return cv1.BlockId(number) == cv2.BlockId(number)
|
||||
}
|
||||
return cv1.getBlockHash(number) == cv2.getBlockHash(number)
|
||||
return cv1.BlockHash(number) == cv2.BlockHash(number)
|
||||
}
|
||||
|
||||
// extendNonCanonical checks whether the previously known reverse list of head
|
||||
|
|
@ -150,9 +184,6 @@ func (cv *ChainView) extendNonCanonical() bool {
|
|||
|
||||
// blockHash returns the given block hash without doing the head number check.
|
||||
func (cv *ChainView) blockHash(number uint64) common.Hash {
|
||||
cv.lock.Lock()
|
||||
defer cv.lock.Unlock()
|
||||
|
||||
if number+uint64(len(cv.hashes)) <= cv.headNumber {
|
||||
hash := cv.chain.GetCanonicalHash(number)
|
||||
if !cv.extendNonCanonical() {
|
||||
|
|
|
|||
|
|
@ -17,5 +17,6 @@
|
|||
{"blockNumber": 3101669, "blockId": "0xa6e66a18fed64d33178c7088f273c404be597662c34f6e6884cf2e24f3ea4ace", "firstIndex": 1073741353},
|
||||
{"blockNumber": 3221725, "blockId": "0xe771f897dece48b1583cc1d1d10de8015da57407eb1fdf239fdbe46eaab85143", "firstIndex": 1140850137},
|
||||
{"blockNumber": 3357164, "blockId": "0x6252d0aa54c79623b0680069c88d7b5c47983f0d5c4845b6c811b8d9b5e8ff3c", "firstIndex": 1207959453},
|
||||
{"blockNumber": 3447019, "blockId": "0xeb7d585e1e063f3cc05ed399fbf6c2df63c271f62f030acb804e9fb1e74b6dc1", "firstIndex": 1275067542}
|
||||
{"blockNumber": 3447019, "blockId": "0xeb7d585e1e063f3cc05ed399fbf6c2df63c271f62f030acb804e9fb1e74b6dc1", "firstIndex": 1275067542},
|
||||
{"blockNumber": 3546397, "blockId": "0xdabdef7defa4281180a57c5af121877b82274f15ccf074ea0096146f4c246df2", "firstIndex": 1342176778}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -260,5 +260,12 @@
|
|||
{"blockNumber": 21959188, "blockId": "0x6b9c482cc4822af2ad62a359b923062556ab6a046d1a39a8243b764848690601", "firstIndex": 17381193886},
|
||||
{"blockNumber": 21994911, "blockId": "0x0d99ef9dd42dd1c62fc5249eb4f618e7672ab93fa0ba7545c77360371cf972e5", "firstIndex": 17448302795},
|
||||
{"blockNumber": 22026007, "blockId": "0xf7cdc7f6694f2c2ed31fa8a607f65cfa59d0dd7d7424ab5c017f959ae2982c71", "firstIndex": 17515413036},
|
||||
{"blockNumber": 22059890, "blockId": "0x82b768a0dddefda2eefd3a33596ea2be075312f1dd4b01f6b0d377faca2af98b", "firstIndex": 17582521768}
|
||||
{"blockNumber": 22059890, "blockId": "0x82b768a0dddefda2eefd3a33596ea2be075312f1dd4b01f6b0d377faca2af98b", "firstIndex": 17582521768},
|
||||
{"blockNumber": 22090784, "blockId": "0xf97c2eaf9a550360ac24000c0ff17ffa388a2bdd6f73f2f36718e332edfa107a", "firstIndex": 17649630983},
|
||||
{"blockNumber": 22121157, "blockId": "0xa790025235db782e899f23d8b09663ec2d74ec149e4125d62989f98829b08e2d", "firstIndex": 17716724973},
|
||||
{"blockNumber": 22148056, "blockId": "0xbe25ac4f1bdd89a7db5782bf1157e6c4378d80220d67a029397853ef16cd1a4c", "firstIndex": 17783838366},
|
||||
{"blockNumber": 22168652, "blockId": "0x6ae43618c915e636794e2cc2d75dde9992766881c7405fe6479c045ed4bee57e", "firstIndex": 17850956277},
|
||||
{"blockNumber": 22190975, "blockId": "0x9437121647899a4b7b84d67fbea7cc6ff967481c2eab4328ccd86e2cefe19420", "firstIndex": 17918066140},
|
||||
{"blockNumber": 22234357, "blockId": "0x036030830134f9224160d5a0b62da35ec7813dc8855d554bd22e9d38545243ed", "firstIndex": 17985175075},
|
||||
{"blockNumber": 22276736, "blockId": "0x5ceb96d98aa1b4c1c2f2fa253ae9cdb1b04e0420c11bf795400e8762c0a1635c", "firstIndex": 18052284344}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -64,5 +64,9 @@
|
|||
{"blockNumber": 7730079, "blockId": "0x08e0d511a93e2a9c004b3456d77d687ae86247417ada1cdd1f85332a62dfed1d", "firstIndex": 4227846795},
|
||||
{"blockNumber": 7777515, "blockId": "0xeba8faed2e12bb9ed5e7fad19dd82662f15f6f4f512a0d232eedf1e676712428", "firstIndex": 4294966082},
|
||||
{"blockNumber": 7828860, "blockId": "0x62c054bd24be351dc4777460ee922a75fdcea5c9830455cbac6fa29552719c85", "firstIndex": 4362076087},
|
||||
{"blockNumber": 7869890, "blockId": "0x5664bcfa6151f798781e22bea4f9a2c796d097402350cb131e4e3c78e13e461a", "firstIndex": 4429183267}
|
||||
{"blockNumber": 7869890, "blockId": "0x5664bcfa6151f798781e22bea4f9a2c796d097402350cb131e4e3c78e13e461a", "firstIndex": 4429183267},
|
||||
{"blockNumber": 7911722, "blockId": "0x9a85e48e3135c97c51fc1786f2af0596c802e021b6c53cfca65a129cafcd23ed", "firstIndex": 4496287265},
|
||||
{"blockNumber": 7960147, "blockId": "0xc9359cc76d7090e1c8a031108f0ab7a8935d971efd4325fe53612a1d99562f6f", "firstIndex": 4563402388},
|
||||
{"blockNumber": 8030418, "blockId": "0x21867e68cd8327aed2da2601399d60f7f9e41dca4a4f2f9be982e5a2b9304a88", "firstIndex": 4630511616},
|
||||
{"blockNumber": 8087701, "blockId": "0x0fa8c8d7549cc9a8d308262706fe248efe759f8b63511efb1e7f3926e9af2dcb", "firstIndex": 4697614758}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ var (
|
|||
)
|
||||
|
||||
const (
|
||||
databaseVersion = 1 // reindexed if database version does not match
|
||||
databaseVersion = 2 // reindexed if database version does not match
|
||||
cachedLastBlocks = 1000 // last block of map pointers
|
||||
cachedLvPointers = 1000 // first log value pointer of block pointers
|
||||
cachedBaseRows = 100 // groups of base layer filter row data
|
||||
|
|
@ -85,11 +85,17 @@ type FilterMaps struct {
|
|||
// fields written by the indexer and read by matcher backend. Indexer can
|
||||
// read them without a lock and write them under indexLock write lock.
|
||||
// Matcher backend can read them under indexLock read lock.
|
||||
indexLock sync.RWMutex
|
||||
indexedRange filterMapsRange
|
||||
cleanedEpochsBefore uint32 // all unindexed data cleaned before this point
|
||||
indexedView *ChainView // always consistent with the log index
|
||||
hasTempRange bool
|
||||
indexLock sync.RWMutex
|
||||
indexedRange filterMapsRange
|
||||
indexedView *ChainView // always consistent with the log index
|
||||
hasTempRange bool
|
||||
|
||||
// cleanedEpochsBefore indicates that all unindexed data before this point
|
||||
// has been cleaned.
|
||||
//
|
||||
// This field is only accessed and modified within tryUnindexTail, so no
|
||||
// explicit locking is required.
|
||||
cleanedEpochsBefore uint32
|
||||
|
||||
// also accessed by indexer and matcher backend but no locking needed.
|
||||
filterMapCache *lru.Cache[uint32, filterMap]
|
||||
|
|
@ -128,6 +134,7 @@ type FilterMaps struct {
|
|||
|
||||
// test hooks
|
||||
testDisableSnapshots, testSnapshotUsed bool
|
||||
testProcessEventsHook func()
|
||||
}
|
||||
|
||||
// filterMap is a full or partial in-memory representation of a filter map where
|
||||
|
|
@ -237,6 +244,8 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f
|
|||
disabledCh: make(chan struct{}),
|
||||
exportFileName: config.ExportFileName,
|
||||
Params: params,
|
||||
targetView: initView,
|
||||
indexedView: initView,
|
||||
indexedRange: filterMapsRange{
|
||||
initialized: initialized,
|
||||
headIndexed: rs.HeadIndexed,
|
||||
|
|
@ -247,26 +256,19 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f
|
|||
},
|
||||
// deleting last unindexed epoch might have been interrupted by shutdown
|
||||
cleanedEpochsBefore: max(rs.MapsFirst>>params.logMapsPerEpoch, 1) - 1,
|
||||
historyCutoff: historyCutoff,
|
||||
finalBlock: finalBlock,
|
||||
matcherSyncCh: make(chan *FilterMapsMatcherBackend),
|
||||
matchers: make(map[*FilterMapsMatcherBackend]struct{}),
|
||||
filterMapCache: lru.NewCache[uint32, filterMap](cachedFilterMaps),
|
||||
lastBlockCache: lru.NewCache[uint32, lastBlockOfMap](cachedLastBlocks),
|
||||
lvPointerCache: lru.NewCache[uint64, uint64](cachedLvPointers),
|
||||
baseRowsCache: lru.NewCache[uint64, [][]uint32](cachedBaseRows),
|
||||
renderSnapshots: lru.NewCache[uint64, *renderedMap](cachedRenderSnapshots),
|
||||
}
|
||||
|
||||
// Set initial indexer target.
|
||||
f.targetView = initView
|
||||
if f.indexedRange.initialized {
|
||||
f.indexedView = f.initChainView(f.targetView)
|
||||
f.indexedRange.headIndexed = f.indexedRange.blocks.AfterLast() == f.indexedView.headNumber+1
|
||||
if !f.indexedRange.headIndexed {
|
||||
f.indexedRange.headDelimiter = 0
|
||||
}
|
||||
historyCutoff: historyCutoff,
|
||||
finalBlock: finalBlock,
|
||||
matcherSyncCh: make(chan *FilterMapsMatcherBackend),
|
||||
matchers: make(map[*FilterMapsMatcherBackend]struct{}),
|
||||
filterMapCache: lru.NewCache[uint32, filterMap](cachedFilterMaps),
|
||||
lastBlockCache: lru.NewCache[uint32, lastBlockOfMap](cachedLastBlocks),
|
||||
lvPointerCache: lru.NewCache[uint64, uint64](cachedLvPointers),
|
||||
baseRowsCache: lru.NewCache[uint64, [][]uint32](cachedBaseRows),
|
||||
renderSnapshots: lru.NewCache[uint64, *renderedMap](cachedRenderSnapshots),
|
||||
}
|
||||
f.checkRevertRange() // revert maps that are inconsistent with the current chain view
|
||||
|
||||
if f.indexedRange.hasIndexedBlocks() {
|
||||
log.Info("Initialized log indexer",
|
||||
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
|
||||
|
|
@ -295,29 +297,40 @@ func (f *FilterMaps) Stop() {
|
|||
f.closeWg.Wait()
|
||||
}
|
||||
|
||||
// initChainView returns a chain view consistent with both the current target
|
||||
// view and the current state of the log index as found in the database, based
|
||||
// on the last block of stored maps.
|
||||
// Note that the returned view might be shorter than the existing index if
|
||||
// the latest maps are not consistent with targetView.
|
||||
func (f *FilterMaps) initChainView(chainView *ChainView) *ChainView {
|
||||
mapIndex := f.indexedRange.maps.AfterLast()
|
||||
for {
|
||||
var ok bool
|
||||
mapIndex, ok = f.lastMapBoundaryBefore(mapIndex)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
lastBlockNumber, lastBlockId, err := f.getLastBlockOfMap(mapIndex)
|
||||
if err != nil {
|
||||
log.Error("Could not initialize indexed chain view", "error", err)
|
||||
break
|
||||
}
|
||||
if lastBlockNumber <= chainView.headNumber && chainView.getBlockId(lastBlockNumber) == lastBlockId {
|
||||
return chainView.limitedView(lastBlockNumber)
|
||||
}
|
||||
// checkRevertRange checks whether the existing index is consistent with the
|
||||
// current indexed view and reverts inconsistent maps if necessary.
|
||||
func (f *FilterMaps) checkRevertRange() {
|
||||
if f.indexedRange.maps.Count() == 0 {
|
||||
return
|
||||
}
|
||||
lastMap := f.indexedRange.maps.Last()
|
||||
lastBlockNumber, lastBlockId, err := f.getLastBlockOfMap(lastMap)
|
||||
if err != nil {
|
||||
log.Error("Error initializing log index database; resetting log index", "error", err)
|
||||
f.reset()
|
||||
return
|
||||
}
|
||||
for lastBlockNumber > f.indexedView.HeadNumber() || f.indexedView.BlockId(lastBlockNumber) != lastBlockId {
|
||||
// revert last map
|
||||
if f.indexedRange.maps.Count() == 1 {
|
||||
f.reset() // reset database if no rendered maps remained
|
||||
return
|
||||
}
|
||||
lastMap--
|
||||
newRange := f.indexedRange
|
||||
newRange.maps.SetLast(lastMap)
|
||||
lastBlockNumber, lastBlockId, err = f.getLastBlockOfMap(lastMap)
|
||||
if err != nil {
|
||||
log.Error("Error initializing log index database; resetting log index", "error", err)
|
||||
f.reset()
|
||||
return
|
||||
}
|
||||
newRange.blocks.SetAfterLast(lastBlockNumber) // lastBlockNumber is probably partially indexed
|
||||
newRange.headIndexed = false
|
||||
newRange.headDelimiter = 0
|
||||
// only shorten range and leave map data; next head render will overwrite it
|
||||
f.setRange(f.db, f.indexedView, newRange, false)
|
||||
}
|
||||
return chainView.limitedView(0)
|
||||
}
|
||||
|
||||
// reset un-initializes the FilterMaps structure and removes all related data from
|
||||
|
|
@ -370,7 +383,7 @@ func (f *FilterMaps) init() error {
|
|||
for min < max {
|
||||
mid := (min + max + 1) / 2
|
||||
cp := checkpointList[mid-1]
|
||||
if cp.BlockNumber <= f.targetView.headNumber && f.targetView.getBlockId(cp.BlockNumber) == cp.BlockId {
|
||||
if cp.BlockNumber <= f.targetView.HeadNumber() && f.targetView.BlockId(cp.BlockNumber) == cp.BlockId {
|
||||
min = mid
|
||||
} else {
|
||||
max = mid - 1
|
||||
|
|
@ -443,6 +456,7 @@ func (f *FilterMaps) safeDeleteWithLogs(deleteFn func(db ethdb.KeyValueStore, ha
|
|||
|
||||
// setRange updates the indexed chain view and covered range and also adds the
|
||||
// changes to the given batch.
|
||||
//
|
||||
// Note that this function assumes that the index write lock is being held.
|
||||
func (f *FilterMaps) setRange(batch ethdb.KeyValueWriter, newView *ChainView, newRange filterMapsRange, isTempRange bool) {
|
||||
f.indexedView = newView
|
||||
|
|
@ -476,6 +490,7 @@ func (f *FilterMaps) setRange(batch ethdb.KeyValueWriter, newView *ChainView, ne
|
|||
// Note that this function assumes that the log index structure is consistent
|
||||
// with the canonical chain at the point where the given log value index points.
|
||||
// If this is not the case then an invalid result or an error may be returned.
|
||||
//
|
||||
// Note that this function assumes that the indexer read lock is being held when
|
||||
// called from outside the indexerLoop goroutine.
|
||||
func (f *FilterMaps) getLogByLvIndex(lvIndex uint64) (*types.Log, error) {
|
||||
|
|
@ -512,7 +527,7 @@ func (f *FilterMaps) getLogByLvIndex(lvIndex uint64) (*types.Log, error) {
|
|||
}
|
||||
}
|
||||
// get block receipts
|
||||
receipts := f.indexedView.getReceipts(firstBlockNumber)
|
||||
receipts := f.indexedView.Receipts(firstBlockNumber)
|
||||
if receipts == nil {
|
||||
return nil, fmt.Errorf("failed to retrieve receipts for block %d containing searched log value index %d: %v", firstBlockNumber, lvIndex, err)
|
||||
}
|
||||
|
|
@ -573,7 +588,7 @@ func (f *FilterMaps) getFilterMapRow(mapIndex, rowIndex uint32, baseLayerOnly bo
|
|||
}
|
||||
f.baseRowsCache.Add(baseMapRowIndex, baseRows)
|
||||
}
|
||||
baseRow := baseRows[mapIndex&(f.baseRowGroupLength-1)]
|
||||
baseRow := slices.Clone(baseRows[mapIndex&(f.baseRowGroupLength-1)])
|
||||
if baseLayerOnly {
|
||||
return baseRow, nil
|
||||
}
|
||||
|
|
@ -610,7 +625,9 @@ func (f *FilterMaps) storeFilterMapRowsOfGroup(batch ethdb.Batch, mapIndices []u
|
|||
if uint32(len(mapIndices)) != f.baseRowGroupLength { // skip base rows read if all rows are replaced
|
||||
var ok bool
|
||||
baseRows, ok = f.baseRowsCache.Get(baseMapRowIndex)
|
||||
if !ok {
|
||||
if ok {
|
||||
baseRows = slices.Clone(baseRows)
|
||||
} else {
|
||||
var err error
|
||||
baseRows, err = rawdb.ReadFilterMapBaseRows(f.db, baseMapRowIndex, f.baseRowGroupLength, f.logMapWidth)
|
||||
if err != nil {
|
||||
|
|
@ -650,14 +667,11 @@ func (f *FilterMaps) mapRowIndex(mapIndex, rowIndex uint32) uint64 {
|
|||
}
|
||||
|
||||
// getBlockLvPointer returns the starting log value index where the log values
|
||||
// generated by the given block are located. If blockNumber is beyond the current
|
||||
// head then the first unoccupied log value index is returned.
|
||||
// generated by the given block are located.
|
||||
//
|
||||
// Note that this function assumes that the indexer read lock is being held when
|
||||
// called from outside the indexerLoop goroutine.
|
||||
func (f *FilterMaps) getBlockLvPointer(blockNumber uint64) (uint64, error) {
|
||||
if blockNumber >= f.indexedRange.blocks.AfterLast() && f.indexedRange.headIndexed {
|
||||
return f.indexedRange.headDelimiter, nil
|
||||
}
|
||||
if lvPointer, ok := f.lvPointerCache.Get(blockNumber); ok {
|
||||
return lvPointer, nil
|
||||
}
|
||||
|
|
@ -759,7 +773,7 @@ func (f *FilterMaps) deleteTailEpoch(epoch uint32) (bool, error) {
|
|||
return false, errors.New("invalid tail epoch number")
|
||||
}
|
||||
// remove index data
|
||||
if err := f.safeDeleteWithLogs(func(db ethdb.KeyValueStore, hashScheme bool, stopCb func(bool) bool) error {
|
||||
deleteFn := func(db ethdb.KeyValueStore, hashScheme bool, stopCb func(bool) bool) error {
|
||||
first := f.mapRowIndex(firstMap, 0)
|
||||
count := f.mapRowIndex(firstMap+f.mapsPerEpoch, 0) - first
|
||||
if err := rawdb.DeleteFilterMapRows(f.db, common.NewRange(first, count), hashScheme, stopCb); err != nil {
|
||||
|
|
@ -783,10 +797,13 @@ func (f *FilterMaps) deleteTailEpoch(epoch uint32) (bool, error) {
|
|||
f.lvPointerCache.Remove(blockNumber)
|
||||
}
|
||||
return nil
|
||||
}, fmt.Sprintf("Deleting tail epoch #%d", epoch), func() bool {
|
||||
}
|
||||
action := fmt.Sprintf("Deleting tail epoch #%d", epoch)
|
||||
stopFn := func() bool {
|
||||
f.processEvents()
|
||||
return f.stop || !f.targetHeadIndexed()
|
||||
}); err == nil {
|
||||
}
|
||||
if err := f.safeDeleteWithLogs(deleteFn, action, stopFn); err == nil {
|
||||
// everything removed; mark as cleaned and report success
|
||||
if f.cleanedEpochsBefore == epoch {
|
||||
f.cleanedEpochsBefore = epoch + 1
|
||||
|
|
@ -805,6 +822,9 @@ func (f *FilterMaps) deleteTailEpoch(epoch uint32) (bool, error) {
|
|||
}
|
||||
|
||||
// exportCheckpoints exports epoch checkpoints in the format used by checkpoints.go.
|
||||
//
|
||||
// Note: acquiring the indexLock read lock is unnecessary here, as this function
|
||||
// is always called within the indexLoop.
|
||||
func (f *FilterMaps) exportCheckpoints() {
|
||||
finalLvPtr, err := f.getBlockLvPointer(f.finalBlock + 1)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -43,8 +43,10 @@ func (f *FilterMaps) indexerLoop() {
|
|||
log.Info("Started log indexer")
|
||||
|
||||
for !f.stop {
|
||||
// Note: acquiring the indexLock read lock is unnecessary here,
|
||||
// as the `indexedRange` is accessed within the indexerLoop.
|
||||
if !f.indexedRange.initialized {
|
||||
if f.targetView.headNumber == 0 {
|
||||
if f.targetView.HeadNumber() == 0 {
|
||||
// initialize when chain head is available
|
||||
f.processSingleEvent(true)
|
||||
continue
|
||||
|
|
@ -105,7 +107,7 @@ type targetUpdate struct {
|
|||
historyCutoff, finalBlock uint64
|
||||
}
|
||||
|
||||
// SetTargetView sets a new target chain view for the indexer to render.
|
||||
// SetTarget sets a new target chain view for the indexer to render.
|
||||
// Note that SetTargetView never blocks.
|
||||
func (f *FilterMaps) SetTarget(targetView *ChainView, historyCutoff, finalBlock uint64) {
|
||||
if targetView == nil {
|
||||
|
|
@ -165,6 +167,9 @@ func (f *FilterMaps) waitForNewHead() {
|
|||
// processEvents processes all events, blocking only if a block processing is
|
||||
// happening and indexing should be suspended.
|
||||
func (f *FilterMaps) processEvents() {
|
||||
if f.testProcessEventsHook != nil {
|
||||
f.testProcessEventsHook()
|
||||
}
|
||||
for f.processSingleEvent(f.blockProcessing) {
|
||||
}
|
||||
}
|
||||
|
|
@ -175,6 +180,8 @@ func (f *FilterMaps) processSingleEvent(blocking bool) bool {
|
|||
if f.stop {
|
||||
return false
|
||||
}
|
||||
// Note: acquiring the indexLock read lock is unnecessary here,
|
||||
// as this function is always called within the indexLoop.
|
||||
if !f.hasTempRange {
|
||||
for _, mb := range f.matcherSyncRequests {
|
||||
mb.synced()
|
||||
|
|
@ -247,9 +254,9 @@ func (f *FilterMaps) tryIndexHead() error {
|
|||
((!f.loggedHeadIndex && time.Since(f.startedHeadIndexAt) > headLogDelay) ||
|
||||
time.Since(f.lastLogHeadIndex) > logFrequency) {
|
||||
log.Info("Log index head rendering in progress",
|
||||
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
|
||||
"firstblock", f.indexedRange.blocks.First(), "lastblock", f.indexedRange.blocks.Last(),
|
||||
"processed", f.indexedRange.blocks.AfterLast()-f.ptrHeadIndex,
|
||||
"remaining", f.indexedView.headNumber-f.indexedRange.blocks.Last(),
|
||||
"remaining", f.indexedView.HeadNumber()-f.indexedRange.blocks.Last(),
|
||||
"elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt)))
|
||||
f.loggedHeadIndex = true
|
||||
f.lastLogHeadIndex = time.Now()
|
||||
|
|
@ -259,7 +266,7 @@ func (f *FilterMaps) tryIndexHead() error {
|
|||
}
|
||||
if f.loggedHeadIndex && f.indexedRange.hasIndexedBlocks() {
|
||||
log.Info("Log index head rendering finished",
|
||||
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
|
||||
"firstblock", f.indexedRange.blocks.First(), "lastblock", f.indexedRange.blocks.Last(),
|
||||
"processed", f.indexedRange.blocks.AfterLast()-f.ptrHeadIndex,
|
||||
"elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt)))
|
||||
}
|
||||
|
|
@ -316,7 +323,7 @@ func (f *FilterMaps) tryIndexTail() (bool, error) {
|
|||
if f.indexedRange.hasIndexedBlocks() && f.ptrTailIndex >= f.indexedRange.blocks.First() &&
|
||||
(!f.loggedTailIndex || time.Since(f.lastLogTailIndex) > logFrequency) {
|
||||
log.Info("Log index tail rendering in progress",
|
||||
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
|
||||
"firstblock", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
|
||||
"processed", f.ptrTailIndex-f.indexedRange.blocks.First()+tpb,
|
||||
"remaining", remaining,
|
||||
"next tail epoch percentage", f.indexedRange.tailPartialEpoch*100/f.mapsPerEpoch,
|
||||
|
|
@ -339,7 +346,7 @@ func (f *FilterMaps) tryIndexTail() (bool, error) {
|
|||
}
|
||||
if f.loggedTailIndex && f.indexedRange.hasIndexedBlocks() {
|
||||
log.Info("Log index tail rendering finished",
|
||||
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
|
||||
"firstblock", f.indexedRange.blocks.First(), "lastblock", f.indexedRange.blocks.Last(),
|
||||
"processed", f.ptrTailIndex-f.indexedRange.blocks.First(),
|
||||
"elapsed", common.PrettyDuration(time.Since(f.startedTailIndexAt)))
|
||||
f.loggedTailIndex = false
|
||||
|
|
@ -418,10 +425,10 @@ func (f *FilterMaps) needTailEpoch(epoch uint32) bool {
|
|||
// tailTargetBlock returns the target value for the tail block number according
|
||||
// to the log history parameter and the current index head.
|
||||
func (f *FilterMaps) tailTargetBlock() uint64 {
|
||||
if f.history == 0 || f.indexedView.headNumber < f.history {
|
||||
if f.history == 0 || f.indexedView.HeadNumber() < f.history {
|
||||
return 0
|
||||
}
|
||||
return f.indexedView.headNumber + 1 - f.history
|
||||
return f.indexedView.HeadNumber() + 1 - f.history
|
||||
}
|
||||
|
||||
// tailPartialBlocks returns the number of rendered blocks in the partially
|
||||
|
|
|
|||
|
|
@ -219,6 +219,58 @@ func testIndexerMatcherView(t *testing.T, concurrentRead bool) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestLogsByIndex(t *testing.T) {
|
||||
ts := newTestSetup(t)
|
||||
defer func() {
|
||||
ts.fm.testProcessEventsHook = nil
|
||||
ts.close()
|
||||
}()
|
||||
|
||||
ts.chain.addBlocks(1000, 10, 3, 4, true)
|
||||
ts.setHistory(0, false)
|
||||
ts.fm.WaitIdle()
|
||||
firstLog := make([]uint64, 1001) // first valid log position per block
|
||||
lastLog := make([]uint64, 1001) // last valid log position per block
|
||||
for i := uint64(0); i <= ts.fm.indexedRange.headDelimiter; i++ {
|
||||
log, err := ts.fm.getLogByLvIndex(i)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting log by index %d: %v", i, err)
|
||||
}
|
||||
if log != nil {
|
||||
if firstLog[log.BlockNumber] == 0 {
|
||||
firstLog[log.BlockNumber] = i
|
||||
}
|
||||
lastLog[log.BlockNumber] = i
|
||||
}
|
||||
}
|
||||
var failed bool
|
||||
ts.fm.testProcessEventsHook = func() {
|
||||
if ts.fm.indexedRange.blocks.IsEmpty() {
|
||||
return
|
||||
}
|
||||
if lvi := firstLog[ts.fm.indexedRange.blocks.First()]; lvi != 0 {
|
||||
log, err := ts.fm.getLogByLvIndex(lvi)
|
||||
if log == nil || err != nil {
|
||||
t.Errorf("Error getting first log of indexed block range: %v", err)
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
if lvi := lastLog[ts.fm.indexedRange.blocks.Last()]; lvi != 0 {
|
||||
log, err := ts.fm.getLogByLvIndex(lvi)
|
||||
if log == nil || err != nil {
|
||||
t.Errorf("Error getting last log of indexed block range: %v", err)
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
chain := ts.chain.getCanonicalChain()
|
||||
for i := 0; i < 1000 && !failed; i++ {
|
||||
head := rand.Intn(len(chain))
|
||||
ts.chain.setCanonicalChain(chain[:head+1])
|
||||
ts.fm.WaitIdle()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexerCompareDb(t *testing.T) {
|
||||
ts := newTestSetup(t)
|
||||
defer ts.close()
|
||||
|
|
@ -463,6 +515,13 @@ func (tc *testChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
|||
return tc.receipts[hash]
|
||||
}
|
||||
|
||||
func (tc *testChain) GetRawReceipts(hash common.Hash, number uint64) types.Receipts {
|
||||
tc.lock.RLock()
|
||||
defer tc.lock.RUnlock()
|
||||
|
||||
return tc.receipts[hash]
|
||||
}
|
||||
|
||||
func (tc *testChain) addBlocks(count, maxTxPerBlock, maxLogsPerReceipt, maxTopicsPerLog int, random bool) {
|
||||
tc.lock.Lock()
|
||||
blockGen := func(i int, gen *core.BlockGen) {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"slices"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
|
|
@ -107,7 +108,7 @@ func (f *FilterMaps) renderMapsFromSnapshot(cp *renderedMap) (*mapRenderer, erro
|
|||
filterMap: cp.filterMap.fullCopy(),
|
||||
mapIndex: cp.mapIndex,
|
||||
lastBlock: cp.lastBlock,
|
||||
blockLvPtrs: cp.blockLvPtrs,
|
||||
blockLvPtrs: slices.Clone(cp.blockLvPtrs),
|
||||
},
|
||||
finishedMaps: make(map[uint32]*renderedMap),
|
||||
finished: common.NewRange(cp.mapIndex, 0),
|
||||
|
|
@ -143,8 +144,8 @@ func (f *FilterMaps) lastCanonicalSnapshotOfMap(mapIndex uint32) *renderedMap {
|
|||
var best *renderedMap
|
||||
for _, blockNumber := range f.renderSnapshots.Keys() {
|
||||
if cp, _ := f.renderSnapshots.Get(blockNumber); cp != nil && blockNumber < f.indexedRange.blocks.AfterLast() &&
|
||||
blockNumber <= f.indexedView.headNumber && f.indexedView.getBlockId(blockNumber) == cp.lastBlockId &&
|
||||
blockNumber <= f.targetView.headNumber && f.targetView.getBlockId(blockNumber) == cp.lastBlockId &&
|
||||
blockNumber <= f.indexedView.HeadNumber() && f.indexedView.BlockId(blockNumber) == cp.lastBlockId &&
|
||||
blockNumber <= f.targetView.HeadNumber() && f.targetView.BlockId(blockNumber) == cp.lastBlockId &&
|
||||
cp.mapIndex == mapIndex && (best == nil || blockNumber > best.lastBlock) {
|
||||
best = cp
|
||||
}
|
||||
|
|
@ -173,7 +174,7 @@ func (f *FilterMaps) lastCanonicalMapBoundaryBefore(renderBefore uint32) (nextMa
|
|||
return 0, 0, 0, fmt.Errorf("failed to retrieve last block of reverse iterated map %d: %v", mapIndex, err)
|
||||
}
|
||||
if (f.indexedRange.headIndexed && mapIndex >= f.indexedRange.maps.Last()) ||
|
||||
lastBlock >= f.targetView.headNumber || lastBlockId != f.targetView.getBlockId(lastBlock) {
|
||||
lastBlock >= f.targetView.HeadNumber() || lastBlockId != f.targetView.BlockId(lastBlock) {
|
||||
continue // map is not full or inconsistent with targetView; roll back
|
||||
}
|
||||
lvPtr, err := f.getBlockLvPointer(lastBlock)
|
||||
|
|
@ -244,10 +245,10 @@ func (f *FilterMaps) loadHeadSnapshot() error {
|
|||
}
|
||||
}
|
||||
f.renderSnapshots.Add(f.indexedRange.blocks.Last(), &renderedMap{
|
||||
filterMap: fm,
|
||||
filterMap: fm.fullCopy(),
|
||||
mapIndex: f.indexedRange.maps.Last(),
|
||||
lastBlock: f.indexedRange.blocks.Last(),
|
||||
lastBlockId: f.indexedView.getBlockId(f.indexedRange.blocks.Last()),
|
||||
lastBlockId: f.indexedView.BlockId(f.indexedRange.blocks.Last()),
|
||||
blockLvPtrs: lvPtrs,
|
||||
finished: true,
|
||||
headDelimiter: f.indexedRange.headDelimiter,
|
||||
|
|
@ -264,7 +265,7 @@ func (r *mapRenderer) makeSnapshot() {
|
|||
filterMap: r.currentMap.filterMap.fastCopy(),
|
||||
mapIndex: r.currentMap.mapIndex,
|
||||
lastBlock: r.currentMap.lastBlock,
|
||||
lastBlockId: r.iterator.chainView.getBlockId(r.currentMap.lastBlock),
|
||||
lastBlockId: r.iterator.chainView.BlockId(r.currentMap.lastBlock),
|
||||
blockLvPtrs: r.currentMap.blockLvPtrs,
|
||||
finished: true,
|
||||
headDelimiter: r.iterator.lvIndex,
|
||||
|
|
@ -370,7 +371,7 @@ func (r *mapRenderer) renderCurrentMap(stopCb func() bool) (bool, error) {
|
|||
r.currentMap.finished = true
|
||||
r.currentMap.headDelimiter = r.iterator.lvIndex
|
||||
}
|
||||
r.currentMap.lastBlockId = r.f.targetView.getBlockId(r.currentMap.lastBlock)
|
||||
r.currentMap.lastBlockId = r.f.targetView.BlockId(r.currentMap.lastBlock)
|
||||
totalTime += time.Since(start)
|
||||
mapRenderTimer.Update(totalTime)
|
||||
mapLogValueMeter.Mark(logValuesProcessed)
|
||||
|
|
@ -467,15 +468,25 @@ func (r *mapRenderer) writeFinishedMaps(pauseCb func() bool) error {
|
|||
r.f.filterMapCache.Remove(mapIndex)
|
||||
}
|
||||
}
|
||||
var blockNumber uint64
|
||||
if r.finished.First() > 0 {
|
||||
// in order to always ensure continuous block pointers, initialize
|
||||
// blockNumber based on the last block of the previous map, then verify
|
||||
// against the first block associated with each rendered map
|
||||
lastBlock, _, err := r.f.getLastBlockOfMap(r.finished.First() - 1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get last block of previous map %d: %v", r.finished.First()-1, err)
|
||||
}
|
||||
blockNumber = lastBlock + 1
|
||||
}
|
||||
// add or update block pointers
|
||||
blockNumber := r.finishedMaps[r.finished.First()].firstBlock()
|
||||
for mapIndex := range r.finished.Iter() {
|
||||
renderedMap := r.finishedMaps[mapIndex]
|
||||
if blockNumber != renderedMap.firstBlock() {
|
||||
return fmt.Errorf("non-continuous block numbers in rendered map %d (next expected: %d first rendered: %d)", mapIndex, blockNumber, renderedMap.firstBlock())
|
||||
}
|
||||
r.f.storeLastBlockOfMap(batch, mapIndex, renderedMap.lastBlock, renderedMap.lastBlockId)
|
||||
checkWriteCnt()
|
||||
if blockNumber != renderedMap.firstBlock() {
|
||||
panic("non-continuous block numbers")
|
||||
}
|
||||
for _, lvPtr := range renderedMap.blockLvPtrs {
|
||||
r.f.storeBlockLvPointer(batch, blockNumber, lvPtr)
|
||||
checkWriteCnt()
|
||||
|
|
@ -536,6 +547,7 @@ func (r *mapRenderer) getTempRange() (filterMapsRange, error) {
|
|||
} else {
|
||||
tempRange.blocks.SetAfterLast(0)
|
||||
}
|
||||
tempRange.headIndexed = false
|
||||
tempRange.headDelimiter = 0
|
||||
}
|
||||
return tempRange, nil
|
||||
|
|
@ -566,8 +578,8 @@ func (r *mapRenderer) getUpdatedRange() (filterMapsRange, error) {
|
|||
lm := r.finishedMaps[r.finished.Last()]
|
||||
newRange.headIndexed = lm.finished
|
||||
if lm.finished {
|
||||
newRange.blocks.SetLast(r.f.targetView.headNumber)
|
||||
if lm.lastBlock != r.f.targetView.headNumber {
|
||||
newRange.blocks.SetLast(r.f.targetView.HeadNumber())
|
||||
if lm.lastBlock != r.f.targetView.HeadNumber() {
|
||||
panic("map rendering finished but last block != head block")
|
||||
}
|
||||
newRange.headDelimiter = lm.headDelimiter
|
||||
|
|
@ -665,13 +677,13 @@ var errUnindexedRange = errors.New("unindexed range")
|
|||
// given block's first log value entry (the block delimiter), according to the
|
||||
// current targetView.
|
||||
func (f *FilterMaps) newLogIteratorFromBlockDelimiter(blockNumber, lvIndex uint64) (*logIterator, error) {
|
||||
if blockNumber > f.targetView.headNumber {
|
||||
return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", blockNumber, f.targetView.headNumber)
|
||||
if blockNumber > f.targetView.HeadNumber() {
|
||||
return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", blockNumber, f.targetView.HeadNumber())
|
||||
}
|
||||
if !f.indexedRange.blocks.Includes(blockNumber) {
|
||||
return nil, errUnindexedRange
|
||||
}
|
||||
finished := blockNumber == f.targetView.headNumber
|
||||
finished := blockNumber == f.targetView.HeadNumber()
|
||||
l := &logIterator{
|
||||
chainView: f.targetView,
|
||||
params: &f.Params,
|
||||
|
|
@ -687,11 +699,11 @@ func (f *FilterMaps) newLogIteratorFromBlockDelimiter(blockNumber, lvIndex uint6
|
|||
// newLogIteratorFromMapBoundary creates a logIterator starting at the given
|
||||
// map boundary, according to the current targetView.
|
||||
func (f *FilterMaps) newLogIteratorFromMapBoundary(mapIndex uint32, startBlock, startLvPtr uint64) (*logIterator, error) {
|
||||
if startBlock > f.targetView.headNumber {
|
||||
return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", startBlock, f.targetView.headNumber)
|
||||
if startBlock > f.targetView.HeadNumber() {
|
||||
return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", startBlock, f.targetView.HeadNumber())
|
||||
}
|
||||
// get block receipts
|
||||
receipts := f.targetView.getReceipts(startBlock)
|
||||
receipts := f.targetView.RawReceipts(startBlock)
|
||||
if receipts == nil {
|
||||
return nil, fmt.Errorf("receipts not found for start block %d", startBlock)
|
||||
}
|
||||
|
|
@ -758,7 +770,7 @@ func (l *logIterator) next() error {
|
|||
if l.delimiter {
|
||||
l.delimiter = false
|
||||
l.blockNumber++
|
||||
l.receipts = l.chainView.getReceipts(l.blockNumber)
|
||||
l.receipts = l.chainView.RawReceipts(l.blockNumber)
|
||||
if l.receipts == nil {
|
||||
return fmt.Errorf("receipts not found for block %d", l.blockNumber)
|
||||
}
|
||||
|
|
@ -795,7 +807,7 @@ func (l *logIterator) enforceValidState() {
|
|||
}
|
||||
l.logIndex = 0
|
||||
}
|
||||
if l.blockNumber == l.chainView.headNumber {
|
||||
if l.blockNumber == l.chainView.HeadNumber() {
|
||||
l.finished = true
|
||||
} else {
|
||||
l.delimiter = true
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ type MatcherBackend interface {
|
|||
// all states of the chain since the previous SyncLogIndex or the creation of
|
||||
// the matcher backend.
|
||||
type SyncRange struct {
|
||||
HeadNumber uint64
|
||||
IndexedView *ChainView
|
||||
// block range where the index has not changed since the last matcher sync
|
||||
// and therefore the set of matches found in this region is guaranteed to
|
||||
// be valid and complete.
|
||||
|
|
|
|||
|
|
@ -82,13 +82,26 @@ func (fm *FilterMapsMatcherBackend) GetFilterMapRow(ctx context.Context, mapInde
|
|||
}
|
||||
|
||||
// GetBlockLvPointer returns the starting log value index where the log values
|
||||
// generated by the given block are located. If blockNumber is beyond the current
|
||||
// head then the first unoccupied log value index is returned.
|
||||
// generated by the given block are located. If blockNumber is beyond the last
|
||||
// indexed block then the pointer will point right after this block, ensuring
|
||||
// that the matcher does not fail and can return a set of results where the
|
||||
// valid range is correct.
|
||||
// GetBlockLvPointer implements MatcherBackend.
|
||||
func (fm *FilterMapsMatcherBackend) GetBlockLvPointer(ctx context.Context, blockNumber uint64) (uint64, error) {
|
||||
fm.f.indexLock.RLock()
|
||||
defer fm.f.indexLock.RUnlock()
|
||||
|
||||
if blockNumber >= fm.f.indexedRange.blocks.AfterLast() {
|
||||
if fm.f.indexedRange.headIndexed {
|
||||
// return index after head block
|
||||
return fm.f.indexedRange.headDelimiter + 1, nil
|
||||
}
|
||||
if fm.f.indexedRange.blocks.Count() > 0 {
|
||||
// return index at the beginning of the last, partially indexed
|
||||
// block (after the last fully indexed one)
|
||||
blockNumber = fm.f.indexedRange.blocks.Last()
|
||||
}
|
||||
}
|
||||
return fm.f.getBlockLvPointer(blockNumber)
|
||||
}
|
||||
|
||||
|
|
@ -111,24 +124,24 @@ func (fm *FilterMapsMatcherBackend) GetLogByLvIndex(ctx context.Context, lvIndex
|
|||
// synced signals to the matcher that has triggered a synchronisation that it
|
||||
// has been finished and the log index is consistent with the chain head passed
|
||||
// as a parameter.
|
||||
//
|
||||
// Note that if the log index head was far behind the chain head then it might not
|
||||
// be synced up to the given head in a single step. Still, the latest chain head
|
||||
// should be passed as a parameter and the existing log index should be consistent
|
||||
// with that chain.
|
||||
//
|
||||
// Note: acquiring the indexLock read lock is unnecessary here, as this function
|
||||
// is always called within the indexLoop.
|
||||
func (fm *FilterMapsMatcherBackend) synced() {
|
||||
fm.f.indexLock.RLock()
|
||||
fm.f.matchersLock.Lock()
|
||||
defer func() {
|
||||
fm.f.matchersLock.Unlock()
|
||||
fm.f.indexLock.RUnlock()
|
||||
}()
|
||||
defer fm.f.matchersLock.Unlock()
|
||||
|
||||
indexedBlocks := fm.f.indexedRange.blocks
|
||||
if !fm.f.indexedRange.headIndexed && !indexedBlocks.IsEmpty() {
|
||||
indexedBlocks.SetAfterLast(indexedBlocks.Last()) // remove partially indexed last block
|
||||
}
|
||||
fm.syncCh <- SyncRange{
|
||||
HeadNumber: fm.f.targetView.headNumber,
|
||||
IndexedView: fm.f.indexedView,
|
||||
ValidBlocks: fm.validBlocks,
|
||||
IndexedBlocks: indexedBlocks,
|
||||
}
|
||||
|
|
@ -154,7 +167,9 @@ func (fm *FilterMapsMatcherBackend) SyncLogIndex(ctx context.Context) (SyncRange
|
|||
case <-ctx.Done():
|
||||
return SyncRange{}, ctx.Err()
|
||||
case <-fm.f.disabledCh:
|
||||
return SyncRange{HeadNumber: fm.f.targetView.headNumber}, nil
|
||||
// Note: acquiring the indexLock read lock is unnecessary here,
|
||||
// as the indexer has already been terminated.
|
||||
return SyncRange{IndexedView: fm.f.indexedView}, nil
|
||||
}
|
||||
select {
|
||||
case vr := <-syncCh:
|
||||
|
|
@ -162,7 +177,9 @@ func (fm *FilterMapsMatcherBackend) SyncLogIndex(ctx context.Context) (SyncRange
|
|||
case <-ctx.Done():
|
||||
return SyncRange{}, ctx.Err()
|
||||
case <-fm.f.disabledCh:
|
||||
return SyncRange{HeadNumber: fm.f.targetView.headNumber}, nil
|
||||
// Note: acquiring the indexLock read lock is unnecessary here,
|
||||
// as the indexer has already been terminated.
|
||||
return SyncRange{IndexedView: fm.f.indexedView}, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -170,7 +187,9 @@ func (fm *FilterMapsMatcherBackend) SyncLogIndex(ctx context.Context) (SyncRange
|
|||
// valid range with the current indexed range. This function should be called
|
||||
// whenever a part of the log index has been removed, before adding new blocks
|
||||
// to it.
|
||||
// Note that this function assumes that the index read lock is being held.
|
||||
//
|
||||
// Note: acquiring the indexLock read lock is unnecessary here, as this function
|
||||
// is always called within the indexLoop.
|
||||
func (f *FilterMaps) updateMatchersValidRange() {
|
||||
f.matchersLock.Lock()
|
||||
defer f.matchersLock.Unlock()
|
||||
|
|
|
|||
|
|
@ -365,6 +365,9 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g
|
|||
return nil, common.Hash{}, nil, errors.New("missing head header")
|
||||
}
|
||||
newCfg := genesis.chainConfigOrDefault(ghash, storedCfg)
|
||||
if err := overrides.apply(newCfg); err != nil {
|
||||
return nil, common.Hash{}, nil, err
|
||||
}
|
||||
|
||||
// Sanity-check the new configuration.
|
||||
if err := newCfg.CheckConfigForkOrder(); err != nil {
|
||||
|
|
|
|||
|
|
@ -591,17 +591,50 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat
|
|||
hashes = append(hashes, hdr.Hash())
|
||||
}
|
||||
for _, hash := range hashes {
|
||||
// Remove the associated block body and receipts if required.
|
||||
//
|
||||
// If the block is in the chain freezer, then this delete operation
|
||||
// is actually ineffective.
|
||||
if delFn != nil {
|
||||
delFn(batch, hash, num)
|
||||
}
|
||||
// Remove the hash->number mapping along with the header itself
|
||||
rawdb.DeleteHeader(batch, hash, num)
|
||||
}
|
||||
// Remove the number->hash mapping
|
||||
rawdb.DeleteCanonicalHash(batch, num)
|
||||
}
|
||||
}
|
||||
// Flush all accumulated deletions.
|
||||
if err := batch.Write(); err != nil {
|
||||
log.Crit("Failed to rewind block", "error", err)
|
||||
log.Crit("Failed to commit batch in setHead", "err", err)
|
||||
}
|
||||
// Explicitly flush the pending writes in the key-value store to disk, ensuring
|
||||
// data durability of the previous deletions.
|
||||
if err := hc.chainDb.SyncKeyValue(); err != nil {
|
||||
log.Crit("Failed to sync the key-value store in setHead", "err", err)
|
||||
}
|
||||
// Truncate the excessive chain segments in the ancient store.
|
||||
// These are actually deferred deletions from the loop above.
|
||||
//
|
||||
// This step must be performed after synchronizing the key-value store;
|
||||
// otherwise, in the event of a panic, it's theoretically possible to
|
||||
// lose recent key-value store writes while the ancient store deletions
|
||||
// remain, leading to data inconsistency, e.g., the gap between the key
|
||||
// value store and ancient can be created due to unclean shutdown.
|
||||
if delFn != nil {
|
||||
// Ignore the error here since light client won't hit this path
|
||||
frozen, _ := hc.chainDb.Ancients()
|
||||
header := hc.CurrentHeader()
|
||||
|
||||
// Truncate the excessive chain segment above the current chain head
|
||||
// in the ancient store.
|
||||
if header.Number.Uint64()+1 < frozen {
|
||||
_, err := hc.chainDb.TruncateHead(header.Number.Uint64() + 1)
|
||||
if err != nil {
|
||||
log.Crit("Failed to truncate head block", "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Clear out any stale content from the caches
|
||||
hc.headerCache.Purge()
|
||||
|
|
|
|||
|
|
@ -545,8 +545,8 @@ func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawVa
|
|||
}
|
||||
|
||||
// ReadRawReceipts retrieves all the transaction receipts belonging to a block.
|
||||
// The receipt metadata fields are not guaranteed to be populated, so they
|
||||
// should not be used. Use ReadReceipts instead if the metadata is needed.
|
||||
// The receipt metadata fields and the Bloom are not guaranteed to be populated,
|
||||
// so they should not be used. Use ReadReceipts instead if the metadata is needed.
|
||||
func ReadRawReceipts(db ethdb.Reader, hash common.Hash, number uint64) types.Receipts {
|
||||
// Retrieve the flattened receipt slice
|
||||
data := ReadReceiptsRLP(db, hash, number)
|
||||
|
|
@ -621,6 +621,14 @@ func WriteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64, rec
|
|||
}
|
||||
}
|
||||
|
||||
// WriteRawReceipts stores all the transaction receipts belonging to a block.
|
||||
func WriteRawReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64, receipts rlp.RawValue) {
|
||||
// Store the flattened receipt slice
|
||||
if err := db.Put(blockReceiptsKey(number, hash), receipts); err != nil {
|
||||
log.Crit("Failed to store block receipts", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteReceipts removes all receipt data associated with a block hash.
|
||||
func DeleteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
||||
if err := db.Delete(blockReceiptsKey(number, hash)); err != nil {
|
||||
|
|
@ -701,18 +709,11 @@ func WriteBlock(db ethdb.KeyValueWriter, block *types.Block) {
|
|||
}
|
||||
|
||||
// WriteAncientBlocks writes entire block data into ancient store and returns the total written size.
|
||||
func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts []types.Receipts) (int64, error) {
|
||||
var stReceipts []*types.ReceiptForStorage
|
||||
|
||||
func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts []rlp.RawValue) (int64, error) {
|
||||
return db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
for i, block := range blocks {
|
||||
// Convert receipts to storage format and sum up total difficulty.
|
||||
stReceipts = stReceipts[:0]
|
||||
for _, receipt := range receipts[i] {
|
||||
stReceipts = append(stReceipts, (*types.ReceiptForStorage)(receipt))
|
||||
}
|
||||
header := block.Header()
|
||||
if err := writeAncientBlock(op, block, header, stReceipts); err != nil {
|
||||
if err := writeAncientBlock(op, block, header, receipts[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
@ -720,7 +721,7 @@ func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts
|
|||
})
|
||||
}
|
||||
|
||||
func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *types.Header, receipts []*types.ReceiptForStorage) error {
|
||||
func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *types.Header, receipts rlp.RawValue) error {
|
||||
num := block.NumberU64()
|
||||
if err := op.AppendRaw(ChainFreezerHashTable, num, block.Hash().Bytes()); err != nil {
|
||||
return fmt.Errorf("can't add block %d hash: %v", num, err)
|
||||
|
|
|
|||
|
|
@ -377,7 +377,11 @@ func TestBlockReceiptStorage(t *testing.T) {
|
|||
t.Fatalf("receipts returned when body was deleted: %v", rs)
|
||||
}
|
||||
// Ensure that receipts without metadata can be returned without the block body too
|
||||
if err := checkReceiptsRLP(ReadRawReceipts(db, hash, 0), receipts); err != nil {
|
||||
raw := ReadRawReceipts(db, hash, 0)
|
||||
for _, r := range raw {
|
||||
r.Bloom = types.CreateBloom(r)
|
||||
}
|
||||
if err := checkReceiptsRLP(raw, receipts); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Sanity check that body alone without the receipt is a full purge
|
||||
|
|
@ -439,7 +443,7 @@ func TestAncientStorage(t *testing.T) {
|
|||
}
|
||||
|
||||
// Write and verify the header in the database
|
||||
WriteAncientBlocks(db, []*types.Block{block}, []types.Receipts{nil})
|
||||
WriteAncientBlocks(db, []*types.Block{block}, types.EncodeBlockReceiptLists([]types.Receipts{nil}))
|
||||
|
||||
if blob := ReadHeaderRLP(db, hash, number); len(blob) == 0 {
|
||||
t.Fatalf("no header returned")
|
||||
|
|
@ -609,7 +613,7 @@ func BenchmarkWriteAncientBlocks(b *testing.B) {
|
|||
|
||||
blocks := allBlocks[i : i+length]
|
||||
receipts := batchReceipts[:length]
|
||||
writeSize, err := WriteAncientBlocks(db, blocks, receipts)
|
||||
writeSize, err := WriteAncientBlocks(db, blocks, types.EncodeBlockReceiptLists(receipts))
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
|
@ -909,7 +913,7 @@ func TestHeadersRLPStorage(t *testing.T) {
|
|||
}
|
||||
receipts := make([]types.Receipts, 100)
|
||||
// Write first half to ancients
|
||||
WriteAncientBlocks(db, chain[:50], receipts[:50])
|
||||
WriteAncientBlocks(db, chain[:50], types.EncodeBlockReceiptLists(receipts[:50]))
|
||||
// Write second half to db
|
||||
for i := 50; i < 100; i++ {
|
||||
WriteCanonicalHash(db, chain[i].Hash(), chain[i].NumberU64())
|
||||
|
|
|
|||
|
|
@ -258,13 +258,21 @@ func ReadStateHistory(db ethdb.AncientReaderOp, id uint64) ([]byte, []byte, []by
|
|||
// WriteStateHistory writes the provided state history to database. Compute the
|
||||
// position of state history in freezer by minus one since the id of first state
|
||||
// history starts from one(zero for initial state).
|
||||
func WriteStateHistory(db ethdb.AncientWriter, id uint64, meta []byte, accountIndex []byte, storageIndex []byte, accounts []byte, storages []byte) {
|
||||
db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
op.AppendRaw(stateHistoryMeta, id-1, meta)
|
||||
op.AppendRaw(stateHistoryAccountIndex, id-1, accountIndex)
|
||||
op.AppendRaw(stateHistoryStorageIndex, id-1, storageIndex)
|
||||
op.AppendRaw(stateHistoryAccountData, id-1, accounts)
|
||||
op.AppendRaw(stateHistoryStorageData, id-1, storages)
|
||||
return nil
|
||||
func WriteStateHistory(db ethdb.AncientWriter, id uint64, meta []byte, accountIndex []byte, storageIndex []byte, accounts []byte, storages []byte) error {
|
||||
_, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
if err := op.AppendRaw(stateHistoryMeta, id-1, meta); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := op.AppendRaw(stateHistoryAccountIndex, id-1, accountIndex); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := op.AppendRaw(stateHistoryStorageIndex, id-1, storageIndex); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := op.AppendRaw(stateHistoryAccountData, id-1, accounts); err != nil {
|
||||
return err
|
||||
}
|
||||
return op.AppendRaw(stateHistoryStorageData, id-1, storages)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ package rawdb
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
|
|
@ -45,25 +44,6 @@ const HashScheme = "hash"
|
|||
// on extra state diffs to survive deep reorg.
|
||||
const PathScheme = "path"
|
||||
|
||||
// hasher is used to compute the sha256 hash of the provided data.
|
||||
type hasher struct{ sha crypto.KeccakState }
|
||||
|
||||
var hasherPool = sync.Pool{
|
||||
New: func() interface{} { return &hasher{sha: crypto.NewKeccakState()} },
|
||||
}
|
||||
|
||||
func newHasher() *hasher {
|
||||
return hasherPool.Get().(*hasher)
|
||||
}
|
||||
|
||||
func (h *hasher) hash(data []byte) common.Hash {
|
||||
return crypto.HashData(h.sha, data)
|
||||
}
|
||||
|
||||
func (h *hasher) release() {
|
||||
hasherPool.Put(h)
|
||||
}
|
||||
|
||||
// ReadAccountTrieNode retrieves the account trie node with the specified node path.
|
||||
func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) []byte {
|
||||
data, _ := db.Get(accountTrieNodeKey(path))
|
||||
|
|
@ -170,9 +150,7 @@ func HasTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash c
|
|||
if len(blob) == 0 {
|
||||
return false
|
||||
}
|
||||
h := newHasher()
|
||||
defer h.release()
|
||||
return h.hash(blob) == hash // exists but not match
|
||||
return crypto.Keccak256Hash(blob) == hash // exists but not match
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown scheme %v", scheme))
|
||||
}
|
||||
|
|
@ -194,9 +172,7 @@ func ReadTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash
|
|||
if len(blob) == 0 {
|
||||
return nil
|
||||
}
|
||||
h := newHasher()
|
||||
defer h.release()
|
||||
if h.hash(blob) != hash {
|
||||
if crypto.Keccak256Hash(blob) != hash {
|
||||
return nil // exists but not match
|
||||
}
|
||||
return blob
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) {
|
|||
continue
|
||||
}
|
||||
// Batch of blocks have been frozen, flush them before wiping from key-value store
|
||||
if err := f.Sync(); err != nil {
|
||||
if err := f.SyncAncient(); err != nil {
|
||||
log.Crit("Failed to flush frozen tables", "err", err)
|
||||
}
|
||||
// Wipe out all data from the active database
|
||||
|
|
|
|||
|
|
@ -131,8 +131,8 @@ func (db *nofreezedb) TruncateTail(items uint64) (uint64, error) {
|
|||
return 0, errNotSupported
|
||||
}
|
||||
|
||||
// Sync returns an error as we don't have a backing chain freezer.
|
||||
func (db *nofreezedb) Sync() error {
|
||||
// SyncAncient returns an error as we don't have a backing chain freezer.
|
||||
func (db *nofreezedb) SyncAncient() error {
|
||||
return errNotSupported
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -325,8 +325,8 @@ func (f *Freezer) TruncateTail(tail uint64) (uint64, error) {
|
|||
return old, nil
|
||||
}
|
||||
|
||||
// Sync flushes all data tables to disk.
|
||||
func (f *Freezer) Sync() error {
|
||||
// SyncAncient flushes all data tables to disk.
|
||||
func (f *Freezer) SyncAncient() error {
|
||||
var errs []error
|
||||
for _, table := range f.tables {
|
||||
if err := table.Sync(); err != nil {
|
||||
|
|
|
|||
|
|
@ -395,8 +395,8 @@ func (f *MemoryFreezer) TruncateTail(tail uint64) (uint64, error) {
|
|||
return old, nil
|
||||
}
|
||||
|
||||
// Sync flushes all data tables to disk.
|
||||
func (f *MemoryFreezer) Sync() error {
|
||||
// SyncAncient flushes all data tables to disk.
|
||||
func (f *MemoryFreezer) SyncAncient() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -194,12 +194,12 @@ func (f *resettableFreezer) TruncateTail(tail uint64) (uint64, error) {
|
|||
return f.freezer.TruncateTail(tail)
|
||||
}
|
||||
|
||||
// Sync flushes all data tables to disk.
|
||||
func (f *resettableFreezer) Sync() error {
|
||||
// SyncAncient flushes all data tables to disk.
|
||||
func (f *resettableFreezer) SyncAncient() error {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
|
||||
return f.freezer.Sync()
|
||||
return f.freezer.SyncAncient()
|
||||
}
|
||||
|
||||
// AncientDatadir returns the path of the ancient store.
|
||||
|
|
|
|||
|
|
@ -392,7 +392,7 @@ func TestFreezerCloseSync(t *testing.T) {
|
|||
if err := f.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := f.Sync(); err == nil {
|
||||
if err := f.SyncAncient(); err == nil {
|
||||
t.Fatalf("want error, have nil")
|
||||
} else if have, want := err.Error(), "[closed closed]"; have != want {
|
||||
t.Fatalf("want %v, have %v", have, want)
|
||||
|
|
|
|||
|
|
@ -107,10 +107,10 @@ func (t *table) TruncateTail(items uint64) (uint64, error) {
|
|||
return t.db.TruncateTail(items)
|
||||
}
|
||||
|
||||
// Sync is a noop passthrough that just forwards the request to the underlying
|
||||
// SyncAncient is a noop passthrough that just forwards the request to the underlying
|
||||
// database.
|
||||
func (t *table) Sync() error {
|
||||
return t.db.Sync()
|
||||
func (t *table) SyncAncient() error {
|
||||
return t.db.SyncAncient()
|
||||
}
|
||||
|
||||
// AncientDatadir returns the ancient datadir of the underlying database.
|
||||
|
|
@ -188,6 +188,12 @@ func (t *table) Compact(start []byte, limit []byte) error {
|
|||
return t.db.Compact(start, limit)
|
||||
}
|
||||
|
||||
// SyncKeyValue ensures that all pending writes are flushed to disk,
|
||||
// guaranteeing data durability up to the point.
|
||||
func (t *table) SyncKeyValue() error {
|
||||
return t.db.SyncKeyValue()
|
||||
}
|
||||
|
||||
// NewBatch creates a write-only database that buffers changes to its host db
|
||||
// until a final write is called, each operation prefixing all keys with the
|
||||
// pre-configured string.
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package state
|
|||
|
||||
import (
|
||||
"maps"
|
||||
gomath "math"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"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
|
||||
// member fields of an account.
|
||||
func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool) uint64 {
|
||||
var gas uint64
|
||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite)
|
||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite)
|
||||
func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool, availableGas uint64) uint64 {
|
||||
var gas uint64 // accumulate the consumed gas
|
||||
consumed, expected := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite, availableGas)
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
// call to that account.
|
||||
func (ae *AccessEvents) MessageCallGas(destination common.Address) uint64 {
|
||||
var gas uint64
|
||||
gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.BasicDataLeafKey, false)
|
||||
return gas
|
||||
func (ae *AccessEvents) MessageCallGas(destination common.Address, availableGas uint64) uint64 {
|
||||
_, expected := ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.BasicDataLeafKey, false, availableGas)
|
||||
if expected == 0 {
|
||||
expected = params.WarmStorageReadCostEIP2929
|
||||
}
|
||||
return expected
|
||||
}
|
||||
|
||||
// ValueTransferGas returns the gas to be charged for each of the currently
|
||||
// cold balance member fields of the caller and the callee accounts.
|
||||
func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address) uint64 {
|
||||
var gas uint64
|
||||
gas += ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
||||
gas += ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
||||
return gas
|
||||
func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address, availableGas uint64) uint64 {
|
||||
_, expected1 := ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, availableGas)
|
||||
if expected1 > availableGas {
|
||||
return expected1
|
||||
}
|
||||
_, 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
|
||||
// a contract creation is initiated. It is just reads, because the
|
||||
// address collision is done before the transfer, and so no write
|
||||
// are guaranteed to happen at this point.
|
||||
func (ae *AccessEvents) ContractCreatePreCheckGas(addr common.Address) uint64 {
|
||||
var gas uint64
|
||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false)
|
||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false)
|
||||
return gas
|
||||
func (ae *AccessEvents) ContractCreatePreCheckGas(addr common.Address, availableGas uint64) uint64 {
|
||||
consumed, expected1 := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false, availableGas)
|
||||
_, expected2 := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false, availableGas-consumed)
|
||||
return expected1 + expected2
|
||||
}
|
||||
|
||||
// ContractCreateInitGas returns the access gas costs for the initialization of
|
||||
// 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
|
||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, true)
|
||||
return gas
|
||||
consumed, expected1 := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true, availableGas)
|
||||
gas += consumed
|
||||
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,
|
||||
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
||||
func (ae *AccessEvents) AddTxOrigin(originAddr common.Address) {
|
||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeHashLeafKey, false)
|
||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, gomath.MaxUint64)
|
||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeHashLeafKey, false, gomath.MaxUint64)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (ae *AccessEvents) AddTxDestination(addr common.Address, sendsValue bool) {
|
||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue)
|
||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false)
|
||||
func (ae *AccessEvents) AddTxDestination(addr common.Address, sendsValue, doesntExist bool) {
|
||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue, gomath.MaxUint64)
|
||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, doesntExist, gomath.MaxUint64)
|
||||
}
|
||||
|
||||
// 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())
|
||||
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
|
||||
// access cost to be charged, if need be.
|
||||
func (ae *AccessEvents) touchAddressAndChargeGas(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool) 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) {
|
||||
// touchAddressAndChargeGas adds any missing access event to the access event list, and returns the
|
||||
// consumed and required gas.
|
||||
func (ae *AccessEvents) touchAddressAndChargeGas(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool, availableGas uint64) (uint64, uint64) {
|
||||
branchKey := newBranchAccessKey(addr, treeIndex)
|
||||
chunkKey := newChunkAccessKey(branchKey, subIndex)
|
||||
|
||||
|
|
@ -190,11 +188,9 @@ func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int,
|
|||
var branchRead, chunkRead bool
|
||||
if _, hasStem := ae.branches[branchKey]; !hasStem {
|
||||
branchRead = true
|
||||
ae.branches[branchKey] = AccessWitnessReadFlag
|
||||
}
|
||||
if _, hasSelector := ae.chunks[chunkKey]; !hasSelector {
|
||||
chunkRead = true
|
||||
ae.chunks[chunkKey] = AccessWitnessReadFlag
|
||||
}
|
||||
|
||||
// Write access.
|
||||
|
|
@ -202,17 +198,51 @@ func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int,
|
|||
if isWrite {
|
||||
if (ae.branches[branchKey] & AccessWitnessWriteFlag) == 0 {
|
||||
branchWrite = true
|
||||
ae.branches[branchKey] |= AccessWitnessWriteFlag
|
||||
}
|
||||
|
||||
chunkValue := ae.chunks[chunkKey]
|
||||
if (chunkValue & AccessWitnessWriteFlag) == 0 {
|
||||
chunkWrite = true
|
||||
ae.chunks[chunkKey] |= AccessWitnessWriteFlag
|
||||
}
|
||||
// TODO: charge chunk filling costs if the leaf was previously empty in the state
|
||||
}
|
||||
return branchRead, chunkRead, branchWrite, chunkWrite, chunkFill
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// consumed == expected
|
||||
return gas, gas
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
// 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
|
||||
|
|
@ -248,7 +278,7 @@ func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC,
|
|||
// is already in the AccessWitness so a stateless verifier can see that
|
||||
// the code from the last leaf is not needed.
|
||||
if (codeLen == 0 && size == 0) || startPC > codeLen {
|
||||
return 0
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
endPC := startPC + size
|
||||
|
|
@ -263,22 +293,34 @@ func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC,
|
|||
for chunkNumber := startPC / 31; chunkNumber <= endPC/31; chunkNumber++ {
|
||||
treeIndex := *uint256.NewInt((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
|
||||
statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, gas)
|
||||
statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, consumed)
|
||||
if overflow {
|
||||
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
|
||||
// amount of gas that it costs.
|
||||
// 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.
|
||||
func (ae *AccessEvents) BasicDataGas(addr common.Address, isWrite bool) uint64 {
|
||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite)
|
||||
func (ae *AccessEvents) BasicDataGas(addr common.Address, isWrite bool, availableGas uint64, chargeWarmCosts bool) uint64 {
|
||||
_, 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
|
||||
|
|
@ -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.
|
||||
// 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.
|
||||
func (ae *AccessEvents) CodeHashGas(addr common.Address, isWrite bool) uint64 {
|
||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite)
|
||||
func (ae *AccessEvents) CodeHashGas(addr common.Address, isWrite bool, availableGas uint64, chargeWarmCosts bool) uint64 {
|
||||
_, 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
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -40,50 +41,50 @@ func TestAccountHeaderGas(t *testing.T) {
|
|||
ae := NewAccessEvents(utils.NewPointCache(1024))
|
||||
|
||||
// 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 {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||
}
|
||||
|
||||
// Check warm read cost
|
||||
gas = ae.BasicDataGas(testAddr, false)
|
||||
gas = ae.BasicDataGas(testAddr, false, math.MaxUint64, false)
|
||||
if 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
|
||||
gas = ae.CodeHashGas(testAddr, false)
|
||||
gas = ae.CodeHashGas(testAddr, false, math.MaxUint64, false)
|
||||
if gas != params.WitnessChunkReadCost {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||
}
|
||||
|
||||
// Check warm write cost
|
||||
gas = ae.BasicDataGas(testAddr, true)
|
||||
gas = ae.BasicDataGas(testAddr, true, math.MaxUint64, false)
|
||||
if 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
|
||||
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 {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||
}
|
||||
|
||||
// Check that reading a slot from the account header only charges the
|
||||
// chunk read cost.
|
||||
gas = ae.SlotGas(testAddr, common.Hash{}, false)
|
||||
gas = ae.SlotGas(testAddr, common.Hash{}, false, math.MaxUint64, false)
|
||||
if 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
|
||||
gas := ae.ContractCreateInitGas(testAddr)
|
||||
gas, _ := ae.ContractCreateInitGas(testAddr, math.MaxUint64)
|
||||
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)
|
||||
}
|
||||
|
||||
// Check warm read cost
|
||||
gas = ae.ContractCreateInitGas(testAddr)
|
||||
gas, _ = ae.ContractCreateInitGas(testAddr, math.MaxUint64)
|
||||
if 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))
|
||||
|
||||
// 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 {
|
||||
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
|
||||
gas = ae.BasicDataGas(testAddr, false)
|
||||
gas = ae.BasicDataGas(testAddr, false, math.MaxUint64, false)
|
||||
if 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 {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||
}
|
||||
|
||||
// Check warm read cost
|
||||
gas = ae.MessageCallGas(testAddr)
|
||||
if gas != 0 {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||
gas = ae.MessageCallGas(testAddr, math.MaxUint64)
|
||||
if gas != params.WarmStorageReadCostEIP2929 {
|
||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WarmStorageReadCostEIP2929)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,10 +34,10 @@ import (
|
|||
|
||||
const (
|
||||
// Number of codehash->size associations to keep.
|
||||
codeSizeCacheSize = 100000
|
||||
codeSizeCacheSize = 1_000_000 // 4 megabytes in total
|
||||
|
||||
// Cache size granted for caching clean code.
|
||||
codeCacheSize = 64 * 1024 * 1024
|
||||
codeCacheSize = 256 * 1024 * 1024
|
||||
|
||||
// Number of address->curve point associations to keep.
|
||||
pointCacheSize = 4096
|
||||
|
|
@ -175,26 +175,27 @@ func NewDatabaseForTesting() *CachingDB {
|
|||
func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
|
||||
var readers []StateReader
|
||||
|
||||
// Set up the state snapshot reader if available. This feature
|
||||
// is optional and may be partially useful if it's not fully
|
||||
// generated.
|
||||
if db.snap != nil {
|
||||
// If standalone state snapshot is available (hash scheme),
|
||||
// then construct the legacy snap reader.
|
||||
// Configure the state reader using the standalone snapshot in hash mode.
|
||||
// This reader offers improved performance but is optional and only
|
||||
// partially useful if the snapshot is not fully generated.
|
||||
if db.TrieDB().Scheme() == rawdb.HashScheme && db.snap != nil {
|
||||
snap := db.snap.Snapshot(stateRoot)
|
||||
if snap != nil {
|
||||
readers = append(readers, newFlatReader(snap))
|
||||
}
|
||||
} else {
|
||||
// If standalone state snapshot is not available, try to construct
|
||||
// the state reader with database.
|
||||
}
|
||||
// Configure the state reader using the path database in path mode.
|
||||
// This reader offers improved performance but is optional and only
|
||||
// partially useful if the snapshot data in path database is not
|
||||
// fully generated.
|
||||
if db.TrieDB().Scheme() == rawdb.PathScheme {
|
||||
reader, err := db.triedb.StateReader(stateRoot)
|
||||
if err == nil {
|
||||
readers = append(readers, newFlatReader(reader)) // state reader is optional
|
||||
readers = append(readers, newFlatReader(reader))
|
||||
}
|
||||
}
|
||||
// Set up the trie reader, which is expected to always be available
|
||||
// as the gatekeeper unless the state is corrupted.
|
||||
// Configure the trie reader, which is expected to be available as the
|
||||
// gatekeeper unless the state is corrupted.
|
||||
tr, err := newTrieReader(stateRoot, db.triedb, db.pointCache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -208,6 +209,15 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
|
|||
return newReader(newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache), combined), nil
|
||||
}
|
||||
|
||||
// ReaderWithCache creates a state reader with internal local cache.
|
||||
func (db *CachingDB) ReaderWithCache(stateRoot common.Hash) (Reader, error) {
|
||||
reader, err := db.Reader(stateRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newReaderWithCache(reader), nil
|
||||
}
|
||||
|
||||
// OpenTrie opens the main account trie at a specific root hash.
|
||||
func (db *CachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||
if db.triedb.IsVerkle() {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package state
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/lru"
|
||||
|
|
@ -51,6 +52,9 @@ type ContractCodeReader interface {
|
|||
|
||||
// StateReader defines the interface for accessing accounts and storage slots
|
||||
// associated with a specific state.
|
||||
//
|
||||
// StateReader is assumed to be thread-safe and implementation must take care
|
||||
// of the concurrency issue by themselves.
|
||||
type StateReader interface {
|
||||
// Account retrieves the account associated with a particular address.
|
||||
//
|
||||
|
|
@ -70,6 +74,9 @@ type StateReader interface {
|
|||
|
||||
// Reader defines the interface for accessing accounts, storage slots and contract
|
||||
// code associated with a specific state.
|
||||
//
|
||||
// Reader is assumed to be thread-safe and implementation must take care of the
|
||||
// concurrency issue by themselves.
|
||||
type Reader interface {
|
||||
ContractCodeReader
|
||||
StateReader
|
||||
|
|
@ -77,6 +84,8 @@ type Reader interface {
|
|||
|
||||
// cachingCodeReader implements ContractCodeReader, accessing contract code either in
|
||||
// local key-value store or the shared code cache.
|
||||
//
|
||||
// cachingCodeReader is safe for concurrent access.
|
||||
type cachingCodeReader struct {
|
||||
db ethdb.KeyValueReader
|
||||
|
||||
|
|
@ -123,18 +132,14 @@ func (r *cachingCodeReader) CodeSize(addr common.Address, codeHash common.Hash)
|
|||
return len(code), nil
|
||||
}
|
||||
|
||||
// flatReader wraps a database state reader.
|
||||
// flatReader wraps a database state reader and is safe for concurrent access.
|
||||
type flatReader struct {
|
||||
reader database.StateReader
|
||||
buff crypto.KeccakState
|
||||
}
|
||||
|
||||
// newFlatReader constructs a state reader with on the given state root.
|
||||
func newFlatReader(reader database.StateReader) *flatReader {
|
||||
return &flatReader{
|
||||
reader: reader,
|
||||
buff: crypto.NewKeccakState(),
|
||||
}
|
||||
return &flatReader{reader: reader}
|
||||
}
|
||||
|
||||
// Account implements StateReader, retrieving the account specified by the address.
|
||||
|
|
@ -144,7 +149,7 @@ func newFlatReader(reader database.StateReader) *flatReader {
|
|||
//
|
||||
// The returned account might be nil if it's not existent.
|
||||
func (r *flatReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||
account, err := r.reader.Account(crypto.HashData(r.buff, addr.Bytes()))
|
||||
account, err := r.reader.Account(crypto.Keccak256Hash(addr.Bytes()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -174,8 +179,8 @@ func (r *flatReader) Account(addr common.Address) (*types.StateAccount, error) {
|
|||
//
|
||||
// The returned storage slot might be empty if it's not existent.
|
||||
func (r *flatReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
|
||||
addrHash := crypto.HashData(r.buff, addr.Bytes())
|
||||
slotHash := crypto.HashData(r.buff, key.Bytes())
|
||||
addrHash := crypto.Keccak256Hash(addr.Bytes())
|
||||
slotHash := crypto.Keccak256Hash(key.Bytes())
|
||||
ret, err := r.reader.Storage(addrHash, slotHash)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
|
|
@ -196,13 +201,20 @@ func (r *flatReader) Storage(addr common.Address, key common.Hash) (common.Hash,
|
|||
|
||||
// trieReader implements the StateReader interface, providing functions to access
|
||||
// state from the referenced trie.
|
||||
//
|
||||
// trieReader is safe for concurrent read.
|
||||
type trieReader struct {
|
||||
root common.Hash // State root which uniquely represent a state
|
||||
db *triedb.Database // Database for loading trie
|
||||
buff crypto.KeccakState // Buffer for keccak256 hashing
|
||||
mainTrie Trie // Main trie, resolved in constructor
|
||||
root common.Hash // State root which uniquely represent a state
|
||||
db *triedb.Database // Database for loading trie
|
||||
buff crypto.KeccakState // Buffer for keccak256 hashing
|
||||
|
||||
// Main trie, resolved in constructor. Note either the Merkle-Patricia-tree
|
||||
// or Verkle-tree is not safe for concurrent read.
|
||||
mainTrie Trie
|
||||
|
||||
subRoots map[common.Address]common.Hash // Set of storage roots, cached when the account is resolved
|
||||
subTries map[common.Address]Trie // Group of storage tries, cached when it's resolved
|
||||
lock sync.Mutex // Lock for protecting concurrent read
|
||||
}
|
||||
|
||||
// trieReader constructs a trie reader of the specific state. An error will be
|
||||
|
|
@ -230,11 +242,8 @@ func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCach
|
|||
}, nil
|
||||
}
|
||||
|
||||
// Account implements StateReader, retrieving the account specified by the address.
|
||||
//
|
||||
// An error will be returned if the trie state is corrupted. An nil account
|
||||
// will be returned if it's not existent in the trie.
|
||||
func (r *trieReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||
// account is the inner version of Account and assumes the r.lock is already held.
|
||||
func (r *trieReader) account(addr common.Address) (*types.StateAccount, error) {
|
||||
account, err := r.mainTrie.GetAccount(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -247,12 +256,26 @@ func (r *trieReader) Account(addr common.Address) (*types.StateAccount, error) {
|
|||
return account, nil
|
||||
}
|
||||
|
||||
// Account implements StateReader, retrieving the account specified by the address.
|
||||
//
|
||||
// An error will be returned if the trie state is corrupted. An nil account
|
||||
// will be returned if it's not existent in the trie.
|
||||
func (r *trieReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
return r.account(addr)
|
||||
}
|
||||
|
||||
// Storage implements StateReader, retrieving the storage slot specified by the
|
||||
// address and slot key.
|
||||
//
|
||||
// An error will be returned if the trie state is corrupted. An empty storage
|
||||
// slot will be returned if it's not existent in the trie.
|
||||
func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
var (
|
||||
tr Trie
|
||||
found bool
|
||||
|
|
@ -268,7 +291,7 @@ func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash,
|
|||
// The storage slot is accessed without account caching. It's unexpected
|
||||
// behavior but try to resolve the account first anyway.
|
||||
if !ok {
|
||||
_, err := r.Account(addr)
|
||||
_, err := r.account(addr)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
|
|
@ -293,6 +316,9 @@ func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash,
|
|||
// multiStateReader is the aggregation of a list of StateReader interface,
|
||||
// providing state access by leveraging all readers. The checking priority
|
||||
// is determined by the position in the reader list.
|
||||
//
|
||||
// multiStateReader is safe for concurrent read and assumes all underlying
|
||||
// readers are thread-safe as well.
|
||||
type multiStateReader struct {
|
||||
readers []StateReader // List of state readers, sorted by checking priority
|
||||
}
|
||||
|
|
@ -358,3 +384,95 @@ func newReader(codeReader ContractCodeReader, stateReader StateReader) *reader {
|
|||
StateReader: stateReader,
|
||||
}
|
||||
}
|
||||
|
||||
// readerWithCache is a wrapper around Reader that maintains additional state caches
|
||||
// to support concurrent state access.
|
||||
type readerWithCache struct {
|
||||
Reader // safe for concurrent read
|
||||
|
||||
// Previously resolved state entries.
|
||||
accounts map[common.Address]*types.StateAccount
|
||||
accountLock sync.RWMutex
|
||||
|
||||
// List of storage buckets, each of which is thread-safe.
|
||||
// This reader is typically used in scenarios requiring concurrent
|
||||
// access to storage. Using multiple buckets helps mitigate
|
||||
// the overhead caused by locking.
|
||||
storageBuckets [16]struct {
|
||||
lock sync.RWMutex
|
||||
storages map[common.Address]map[common.Hash]common.Hash
|
||||
}
|
||||
}
|
||||
|
||||
// newReaderWithCache constructs the reader with local cache.
|
||||
func newReaderWithCache(reader Reader) *readerWithCache {
|
||||
r := &readerWithCache{
|
||||
Reader: reader,
|
||||
accounts: make(map[common.Address]*types.StateAccount),
|
||||
}
|
||||
for i := range r.storageBuckets {
|
||||
r.storageBuckets[i].storages = make(map[common.Address]map[common.Hash]common.Hash)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Account implements StateReader, retrieving the account specified by the address.
|
||||
// The returned account might be nil if it's not existent.
|
||||
//
|
||||
// An error will be returned if the state is corrupted in the underlying reader.
|
||||
func (r *readerWithCache) Account(addr common.Address) (*types.StateAccount, error) {
|
||||
// Try to resolve the requested account in the local cache
|
||||
r.accountLock.RLock()
|
||||
acct, ok := r.accounts[addr]
|
||||
r.accountLock.RUnlock()
|
||||
if ok {
|
||||
return acct, nil
|
||||
}
|
||||
// Try to resolve the requested account from the underlying reader
|
||||
acct, err := r.Reader.Account(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.accountLock.Lock()
|
||||
r.accounts[addr] = acct
|
||||
r.accountLock.Unlock()
|
||||
return acct, nil
|
||||
}
|
||||
|
||||
// Storage implements StateReader, retrieving the storage slot specified by the
|
||||
// address and slot key. The returned storage slot might be empty if it's not
|
||||
// existent.
|
||||
//
|
||||
// An error will be returned if the state is corrupted in the underlying reader.
|
||||
func (r *readerWithCache) Storage(addr common.Address, slot common.Hash) (common.Hash, error) {
|
||||
var (
|
||||
value common.Hash
|
||||
ok bool
|
||||
bucket = &r.storageBuckets[addr[0]&0x0f]
|
||||
)
|
||||
// Try to resolve the requested storage slot in the local cache
|
||||
bucket.lock.RLock()
|
||||
slots, ok := bucket.storages[addr]
|
||||
if ok {
|
||||
value, ok = slots[slot]
|
||||
}
|
||||
bucket.lock.RUnlock()
|
||||
if ok {
|
||||
return value, nil
|
||||
}
|
||||
// Try to resolve the requested storage slot from the underlying reader
|
||||
value, err := r.Reader.Storage(addr, slot)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
bucket.lock.Lock()
|
||||
slots, ok = bucket.storages[addr]
|
||||
if !ok {
|
||||
slots = make(map[common.Hash]common.Hash)
|
||||
bucket.storages[addr] = slots
|
||||
}
|
||||
slots[slot] = value
|
||||
bucket.lock.Unlock()
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -166,7 +166,9 @@ func newHelper(scheme string) *testHelper {
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
config := &triedb.Config{}
|
||||
if scheme == rawdb.PathScheme {
|
||||
config.PathDB = &pathdb.Config{} // disable caching
|
||||
config.PathDB = &pathdb.Config{
|
||||
SnapshotNoBuild: true,
|
||||
} // disable caching
|
||||
} else {
|
||||
config.HashDB = &hashdb.Config{} // disable caching
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,11 +159,17 @@ type StateDB struct {
|
|||
|
||||
// New creates a new state from a given trie.
|
||||
func New(root common.Hash, db Database) (*StateDB, error) {
|
||||
tr, err := db.OpenTrie(root)
|
||||
reader, err := db.Reader(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reader, err := db.Reader(root)
|
||||
return NewWithReader(root, db, reader)
|
||||
}
|
||||
|
||||
// NewWithReader creates a new state for the specified state root. Unlike New,
|
||||
// this function accepts an additional Reader which is bound to the given root.
|
||||
func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, error) {
|
||||
tr, err := db.OpenTrie(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -292,7 +298,7 @@ func (s *StateDB) SubRefund(gas uint64) {
|
|||
}
|
||||
|
||||
// Exist reports whether the given account address exists in the state.
|
||||
// Notably this also returns true for self-destructed accounts.
|
||||
// Notably this also returns true for self-destructed accounts within the current transaction.
|
||||
func (s *StateDB) Exist(addr common.Address) bool {
|
||||
return s.getStateObject(addr) != nil
|
||||
}
|
||||
|
|
@ -392,6 +398,12 @@ func (s *StateDB) Database() Database {
|
|||
return s.db
|
||||
}
|
||||
|
||||
// Reader retrieves the low level database reader supporting the
|
||||
// lower level operations.
|
||||
func (s *StateDB) Reader() Reader {
|
||||
return s.reader
|
||||
}
|
||||
|
||||
func (s *StateDB) HasSelfDestructed(addr common.Address) bool {
|
||||
stateObject := s.getStateObject(addr)
|
||||
if stateObject != nil {
|
||||
|
|
@ -650,11 +662,10 @@ func (s *StateDB) CreateContract(addr common.Address) {
|
|||
// Snapshots of the copied state cannot be applied to the copy.
|
||||
func (s *StateDB) Copy() *StateDB {
|
||||
// Copy all the basic fields, initialize the memory ones
|
||||
reader, _ := s.db.Reader(s.originalRoot) // impossible to fail
|
||||
state := &StateDB{
|
||||
db: s.db,
|
||||
trie: mustCopyTrie(s.trie),
|
||||
reader: reader,
|
||||
reader: s.reader,
|
||||
originalRoot: s.originalRoot,
|
||||
stateObjects: make(map[common.Address]*stateObject, len(s.stateObjects)),
|
||||
stateObjectsDestruct: make(map[common.Address]*stateObject, len(s.stateObjectsDestruct)),
|
||||
|
|
|
|||
|
|
@ -979,7 +979,8 @@ func testMissingTrieNodes(t *testing.T, scheme string) {
|
|||
)
|
||||
if scheme == rawdb.PathScheme {
|
||||
tdb = triedb.NewDatabase(memDb, &triedb.Config{PathDB: &pathdb.Config{
|
||||
CleanCacheSize: 0,
|
||||
TrieCleanSize: 0,
|
||||
StateCleanSize: 0,
|
||||
WriteBufferSize: 0,
|
||||
}}) // disable caching
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -17,17 +17,22 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
// statePrefetcher is a basic Prefetcher, which blindly executes a block on top
|
||||
// of an arbitrary state with the goal of prefetching potentially useful state
|
||||
// data from disk before the main block processor start executing.
|
||||
// statePrefetcher is a basic Prefetcher that executes transactions from a block
|
||||
// on top of the parent state, aiming to prefetch potentially useful state data
|
||||
// from disk. Transactions are executed in parallel to fully leverage the
|
||||
// SSD's read performance.
|
||||
type statePrefetcher struct {
|
||||
config *params.ChainConfig // Chain configuration options
|
||||
chain *HeaderChain // Canonical block chain
|
||||
|
|
@ -43,41 +48,81 @@ func newStatePrefetcher(config *params.ChainConfig, chain *HeaderChain) *statePr
|
|||
|
||||
// Prefetch processes the state changes according to the Ethereum rules by running
|
||||
// the transaction messages using the statedb, but any changes are discarded. The
|
||||
// only goal is to pre-cache transaction signatures and state trie nodes.
|
||||
// only goal is to warm the state caches.
|
||||
func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, cfg vm.Config, interrupt *atomic.Bool) {
|
||||
var (
|
||||
header = block.Header()
|
||||
gaspool = new(GasPool).AddGas(block.GasLimit())
|
||||
blockContext = NewEVMBlockContext(header, p.chain, nil)
|
||||
evm = vm.NewEVM(blockContext, statedb, p.config, cfg)
|
||||
signer = types.MakeSigner(p.config, header.Number, header.Time)
|
||||
fails atomic.Int64
|
||||
header = block.Header()
|
||||
signer = types.MakeSigner(p.config, header.Number, header.Time)
|
||||
workers errgroup.Group
|
||||
reader = statedb.Reader()
|
||||
)
|
||||
// Iterate over and process the individual transactions
|
||||
byzantium := p.config.IsByzantium(block.Number())
|
||||
for i, tx := range block.Transactions() {
|
||||
// If block precaching was interrupted, abort
|
||||
if interrupt != nil && interrupt.Load() {
|
||||
return
|
||||
}
|
||||
// Convert the transaction into an executable message and pre-cache its sender
|
||||
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
|
||||
if err != nil {
|
||||
return // Also invalid block, bail out
|
||||
}
|
||||
statedb.SetTxContext(tx.Hash(), i)
|
||||
workers.SetLimit(runtime.NumCPU() / 2)
|
||||
|
||||
// We attempt to apply a transaction. The goal is not to execute
|
||||
// the transaction successfully, rather to warm up touched data slots.
|
||||
if _, err := ApplyMessage(evm, msg, gaspool); err != nil {
|
||||
return // Ugh, something went horribly wrong, bail out
|
||||
}
|
||||
// If we're pre-byzantium, pre-load trie nodes for the intermediate root
|
||||
if !byzantium {
|
||||
statedb.IntermediateRoot(true)
|
||||
}
|
||||
}
|
||||
// If were post-byzantium, pre-load trie nodes for the final root hash
|
||||
if byzantium {
|
||||
statedb.IntermediateRoot(true)
|
||||
// Iterate over and process the individual transactions
|
||||
for i, tx := range block.Transactions() {
|
||||
stateCpy := statedb.Copy() // closure
|
||||
workers.Go(func() error {
|
||||
// If block precaching was interrupted, abort
|
||||
if interrupt != nil && interrupt.Load() {
|
||||
return nil
|
||||
}
|
||||
// Preload the touched accounts and storage slots in advance
|
||||
sender, err := types.Sender(signer, tx)
|
||||
if err != nil {
|
||||
fails.Add(1)
|
||||
return nil
|
||||
}
|
||||
reader.Account(sender)
|
||||
|
||||
if tx.To() != nil {
|
||||
account, _ := reader.Account(*tx.To())
|
||||
|
||||
// Preload the contract code if the destination has non-empty code
|
||||
if account != nil && !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) {
|
||||
reader.Code(*tx.To(), common.BytesToHash(account.CodeHash))
|
||||
}
|
||||
}
|
||||
for _, list := range tx.AccessList() {
|
||||
reader.Account(list.Address)
|
||||
if len(list.StorageKeys) > 0 {
|
||||
for _, slot := range list.StorageKeys {
|
||||
reader.Storage(list.Address, slot)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Execute the message to preload the implicit touched states
|
||||
evm := vm.NewEVM(NewEVMBlockContext(header, p.chain, nil), stateCpy, p.config, cfg)
|
||||
|
||||
// Convert the transaction into an executable message and pre-cache its sender
|
||||
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
|
||||
if err != nil {
|
||||
fails.Add(1)
|
||||
return nil // Also invalid block, bail out
|
||||
}
|
||||
// Disable the nonce check
|
||||
msg.SkipNonceChecks = true
|
||||
|
||||
stateCpy.SetTxContext(tx.Hash(), i)
|
||||
|
||||
// We attempt to apply a transaction. The goal is not to execute
|
||||
// the transaction successfully, rather to warm up touched data slots.
|
||||
if _, err := ApplyMessage(evm, msg, new(GasPool).AddGas(block.GasLimit())); err != nil {
|
||||
fails.Add(1)
|
||||
return nil // Ugh, something went horribly wrong, bail out
|
||||
}
|
||||
// Pre-load trie nodes for the intermediate root.
|
||||
//
|
||||
// This operation incurs significant memory allocations due to
|
||||
// trie hashing and node decoding. TODO(rjl493456442): investigate
|
||||
// ways to mitigate this overhead.
|
||||
stateCpy.IntermediateRoot(true)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
workers.Wait()
|
||||
|
||||
blockPrefetchTxsValidMeter.Mark(int64(len(block.Transactions())) - fails.Load())
|
||||
blockPrefetchTxsInvalidMeter.Mark(fails.Load())
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,9 +113,13 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
|||
return nil, err
|
||||
}
|
||||
// EIP-7002
|
||||
ProcessWithdrawalQueue(&requests, evm)
|
||||
if err := ProcessWithdrawalQueue(&requests, evm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// EIP-7251
|
||||
ProcessConsolidationQueue(&requests, evm)
|
||||
if err := ProcessConsolidationQueue(&requests, evm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
||||
|
|
@ -265,17 +269,17 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) {
|
|||
|
||||
// ProcessWithdrawalQueue calls the EIP-7002 withdrawal queue contract.
|
||||
// It returns the opaque request data returned by the contract.
|
||||
func ProcessWithdrawalQueue(requests *[][]byte, evm *vm.EVM) {
|
||||
processRequestsSystemCall(requests, evm, 0x01, params.WithdrawalQueueAddress)
|
||||
func ProcessWithdrawalQueue(requests *[][]byte, evm *vm.EVM) error {
|
||||
return processRequestsSystemCall(requests, evm, 0x01, params.WithdrawalQueueAddress)
|
||||
}
|
||||
|
||||
// ProcessConsolidationQueue calls the EIP-7251 consolidation queue contract.
|
||||
// It returns the opaque request data returned by the contract.
|
||||
func ProcessConsolidationQueue(requests *[][]byte, evm *vm.EVM) {
|
||||
processRequestsSystemCall(requests, evm, 0x02, params.ConsolidationQueueAddress)
|
||||
func ProcessConsolidationQueue(requests *[][]byte, evm *vm.EVM) error {
|
||||
return processRequestsSystemCall(requests, evm, 0x02, params.ConsolidationQueueAddress)
|
||||
}
|
||||
|
||||
func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte, addr common.Address) {
|
||||
func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte, addr common.Address) error {
|
||||
if tracer := evm.Config.Tracer; tracer != nil {
|
||||
onSystemCallStart(tracer, evm.GetVMContext())
|
||||
if tracer.OnSystemCallEnd != nil {
|
||||
|
|
@ -292,17 +296,20 @@ func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte
|
|||
}
|
||||
evm.SetTxContext(NewEVMTxContext(msg))
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
ret, _, _ := evm.Call(msg.From, *msg.To, msg.Data, 30_000_000, common.U2560)
|
||||
ret, _, err := evm.Call(msg.From, *msg.To, msg.Data, 30_000_000, common.U2560)
|
||||
evm.StateDB.Finalise(true)
|
||||
if len(ret) == 0 {
|
||||
return // skip empty output
|
||||
if err != nil {
|
||||
return fmt.Errorf("system call failed to execute: %v", err)
|
||||
}
|
||||
if len(ret) == 0 {
|
||||
return nil // skip empty output
|
||||
}
|
||||
|
||||
// Append prefixed requestsData to the requests list.
|
||||
requestsData := make([]byte, len(ret)+1)
|
||||
requestsData[0] = requestType
|
||||
copy(requestsData[1:], ret)
|
||||
*requests = append(*requests, requestsData)
|
||||
return nil
|
||||
}
|
||||
|
||||
var depositTopic = common.HexToHash("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5")
|
||||
|
|
|
|||
|
|
@ -255,7 +255,8 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||
},
|
||||
want: "could not apply tx 0 [0xc18d10f4c809dbdfa1a074c3300de9bc4b7f16a20f0ec667f6f67312b71b956a]: EIP-7702 transaction with empty auth list (sender 0x71562b71999873DB5b286dF957af199Ec94617F7)",
|
||||
},
|
||||
// ErrSetCodeTxCreate cannot be tested: it is impossible to create a SetCode-tx with nil `to`.
|
||||
// ErrSetCodeTxCreate cannot be tested here: it is impossible to create a SetCode-tx with nil `to`.
|
||||
// The EstimateGas API tests test this case.
|
||||
} {
|
||||
block := GenerateBadBlock(gspec.ToBlock(), beacon.New(ethash.NewFaker()), tt.txs, gspec.Config, false)
|
||||
_, err := blockchain.InsertChain(types.Blocks{block})
|
||||
|
|
|
|||
|
|
@ -34,10 +34,10 @@ import (
|
|||
// ExecutionResult includes all output after executing given evm
|
||||
// message no matter the execution itself is successful or not.
|
||||
type ExecutionResult struct {
|
||||
UsedGas uint64 // Total used gas, not including the refunded gas
|
||||
RefundedGas uint64 // Total gas refunded after execution
|
||||
Err error // Any error encountered during the execution(listed in core/vm/errors.go)
|
||||
ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode)
|
||||
UsedGas uint64 // Total used gas, not including the refunded gas
|
||||
MaxUsedGas uint64 // Maximum gas consumed during execution, excluding gas refunds.
|
||||
Err error // Any error encountered during the execution(listed in core/vm/errors.go)
|
||||
ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode)
|
||||
}
|
||||
|
||||
// Unwrap returns the internal evm error which allows us for further
|
||||
|
|
@ -159,7 +159,9 @@ type Message struct {
|
|||
|
||||
// When SkipNonceChecks is true, the message nonce is not checked against the
|
||||
// account nonce in state.
|
||||
// This field will be set to true for operations like RPC eth_call.
|
||||
//
|
||||
// This field will be set to true for operations like RPC eth_call
|
||||
// or the state prefetching.
|
||||
SkipNonceChecks bool
|
||||
|
||||
// When SkipFromEOACheck is true, the message sender is not checked to be an EOA.
|
||||
|
|
@ -455,7 +457,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
st.evm.AccessEvents.AddTxOrigin(msg.From)
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -509,9 +511,12 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
ret, st.gasRemaining, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining, value)
|
||||
}
|
||||
|
||||
// Record the gas used excluding gas refunds. This value represents the actual
|
||||
// gas allowance required to complete execution.
|
||||
peakGasUsed := st.gasUsed()
|
||||
|
||||
// Compute refund counter, capped to a refund quotient.
|
||||
gasRefund := st.calcRefund()
|
||||
st.gasRemaining += gasRefund
|
||||
st.gasRemaining += st.calcRefund()
|
||||
if rules.IsPrague {
|
||||
// After EIP-7623: Data-heavy transactions pay the floor gas.
|
||||
if st.gasUsed() < floorDataGas {
|
||||
|
|
@ -521,6 +526,9 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
t.OnGasChange(prev, st.gasRemaining, tracing.GasChangeTxDataFloor)
|
||||
}
|
||||
}
|
||||
if peakGasUsed < floorDataGas {
|
||||
peakGasUsed = floorDataGas
|
||||
}
|
||||
}
|
||||
st.returnGas()
|
||||
|
||||
|
|
@ -544,15 +552,15 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
|
||||
// add the coinbase to the witness iff the fee is greater than 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)
|
||||
}
|
||||
}
|
||||
|
||||
return &ExecutionResult{
|
||||
UsedGas: st.gasUsed(),
|
||||
RefundedGas: gasRefund,
|
||||
Err: vmerr,
|
||||
ReturnData: ret,
|
||||
UsedGas: st.gasUsed(),
|
||||
MaxUsedGas: peakGasUsed,
|
||||
Err: vmerr,
|
||||
ReturnData: ret,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
|
|
@ -47,26 +47,38 @@ type txIndexer struct {
|
|||
// and all others shouldn't.
|
||||
limit uint64
|
||||
|
||||
// The current head of blockchain for transaction indexing. This field
|
||||
// is accessed by both the indexer and the indexing progress queries.
|
||||
head atomic.Uint64
|
||||
|
||||
// The current tail of the indexed transactions, null indicates
|
||||
// that no transactions have been indexed yet.
|
||||
//
|
||||
// This field is accessed by both the indexer and the indexing
|
||||
// progress queries.
|
||||
tail atomic.Pointer[uint64]
|
||||
|
||||
// cutoff denotes the block number before which the chain segment should
|
||||
// be pruned and not available locally.
|
||||
cutoff uint64
|
||||
db ethdb.Database
|
||||
progress chan chan TxIndexProgress
|
||||
term chan chan struct{}
|
||||
closed chan struct{}
|
||||
cutoff uint64
|
||||
db ethdb.Database
|
||||
term chan chan struct{}
|
||||
closed chan struct{}
|
||||
}
|
||||
|
||||
// newTxIndexer initializes the transaction indexer.
|
||||
func newTxIndexer(limit uint64, chain *BlockChain) *txIndexer {
|
||||
cutoff, _ := chain.HistoryPruningCutoff()
|
||||
indexer := &txIndexer{
|
||||
limit: limit,
|
||||
cutoff: cutoff,
|
||||
db: chain.db,
|
||||
progress: make(chan chan TxIndexProgress),
|
||||
term: make(chan chan struct{}),
|
||||
closed: make(chan struct{}),
|
||||
limit: limit,
|
||||
cutoff: cutoff,
|
||||
db: chain.db,
|
||||
term: make(chan chan struct{}),
|
||||
closed: make(chan struct{}),
|
||||
}
|
||||
indexer.head.Store(indexer.resolveHead())
|
||||
indexer.tail.Store(rawdb.ReadTxIndexTail(chain.db))
|
||||
|
||||
go indexer.loop(chain)
|
||||
|
||||
var msg string
|
||||
|
|
@ -154,6 +166,7 @@ func (indexer *txIndexer) repair(head uint64) {
|
|||
// A crash may occur between the two delete operations,
|
||||
// potentially leaving dangling indexes in the database.
|
||||
// However, this is considered acceptable.
|
||||
indexer.tail.Store(nil)
|
||||
rawdb.DeleteTxIndexTail(indexer.db)
|
||||
rawdb.DeleteAllTxLookupEntries(indexer.db, nil)
|
||||
log.Warn("Purge transaction indexes", "head", head, "tail", *tail)
|
||||
|
|
@ -174,6 +187,7 @@ func (indexer *txIndexer) repair(head uint64) {
|
|||
// Traversing the database directly within the transaction
|
||||
// index namespace might be slow and expensive, but we
|
||||
// have no choice.
|
||||
indexer.tail.Store(nil)
|
||||
rawdb.DeleteTxIndexTail(indexer.db)
|
||||
rawdb.DeleteAllTxLookupEntries(indexer.db, nil)
|
||||
log.Warn("Purge transaction indexes", "head", head, "cutoff", indexer.cutoff)
|
||||
|
|
@ -187,6 +201,7 @@ func (indexer *txIndexer) repair(head uint64) {
|
|||
// A crash may occur between the two delete operations,
|
||||
// potentially leaving dangling indexes in the database.
|
||||
// However, this is considered acceptable.
|
||||
indexer.tail.Store(&indexer.cutoff)
|
||||
rawdb.WriteTxIndexTail(indexer.db, indexer.cutoff)
|
||||
rawdb.DeleteAllTxLookupEntries(indexer.db, func(txhash common.Hash, blob []byte) bool {
|
||||
n := rawdb.DecodeTxLookupEntry(blob, indexer.db)
|
||||
|
|
@ -216,16 +231,15 @@ func (indexer *txIndexer) loop(chain *BlockChain) {
|
|||
|
||||
// Listening to chain events and manipulate the transaction indexes.
|
||||
var (
|
||||
stop chan struct{} // Non-nil if background routine is active
|
||||
done chan struct{} // Non-nil if background routine is active
|
||||
head = indexer.resolveHead() // The latest announced chain head
|
||||
|
||||
stop chan struct{} // Non-nil if background routine is active
|
||||
done chan struct{} // Non-nil if background routine is active
|
||||
headCh = make(chan ChainHeadEvent)
|
||||
sub = chain.SubscribeChainHeadEvent(headCh)
|
||||
)
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
// Validate the transaction indexes and repair if necessary
|
||||
head := indexer.head.Load()
|
||||
indexer.repair(head)
|
||||
|
||||
// Launch the initial processing if chain is not empty (head != genesis).
|
||||
|
|
@ -238,17 +252,18 @@ func (indexer *txIndexer) loop(chain *BlockChain) {
|
|||
for {
|
||||
select {
|
||||
case h := <-headCh:
|
||||
indexer.head.Store(h.Header.Number.Uint64())
|
||||
if done == nil {
|
||||
stop = make(chan struct{})
|
||||
done = make(chan struct{})
|
||||
go indexer.run(h.Header.Number.Uint64(), stop, done)
|
||||
}
|
||||
head = h.Header.Number.Uint64()
|
||||
|
||||
case <-done:
|
||||
stop = nil
|
||||
done = nil
|
||||
case ch := <-indexer.progress:
|
||||
ch <- indexer.report(head)
|
||||
indexer.tail.Store(rawdb.ReadTxIndexTail(indexer.db))
|
||||
|
||||
case ch := <-indexer.term:
|
||||
if stop != nil {
|
||||
close(stop)
|
||||
|
|
@ -264,7 +279,7 @@ func (indexer *txIndexer) loop(chain *BlockChain) {
|
|||
}
|
||||
|
||||
// report returns the tx indexing progress.
|
||||
func (indexer *txIndexer) report(head uint64) TxIndexProgress {
|
||||
func (indexer *txIndexer) report(head uint64, tail *uint64) TxIndexProgress {
|
||||
// Special case if the head is even below the cutoff,
|
||||
// nothing to index.
|
||||
if head < indexer.cutoff {
|
||||
|
|
@ -284,7 +299,6 @@ func (indexer *txIndexer) report(head uint64) TxIndexProgress {
|
|||
}
|
||||
// Compute how many blocks have been indexed
|
||||
var indexed uint64
|
||||
tail := rawdb.ReadTxIndexTail(indexer.db)
|
||||
if tail != nil {
|
||||
indexed = head - *tail + 1
|
||||
}
|
||||
|
|
@ -300,16 +314,12 @@ func (indexer *txIndexer) report(head uint64) TxIndexProgress {
|
|||
}
|
||||
}
|
||||
|
||||
// txIndexProgress retrieves the tx indexing progress, or an error if the
|
||||
// background tx indexer is already stopped.
|
||||
func (indexer *txIndexer) txIndexProgress() (TxIndexProgress, error) {
|
||||
ch := make(chan TxIndexProgress, 1)
|
||||
select {
|
||||
case indexer.progress <- ch:
|
||||
return <-ch, nil
|
||||
case <-indexer.closed:
|
||||
return TxIndexProgress{}, errors.New("indexer is closed")
|
||||
}
|
||||
// txIndexProgress retrieves the transaction indexing progress. The reported
|
||||
// progress may slightly lag behind the actual indexing state, as the tail is
|
||||
// only updated at the end of each indexing operation. However, this delay is
|
||||
// considered acceptable.
|
||||
func (indexer *txIndexer) txIndexProgress() TxIndexProgress {
|
||||
return indexer.report(indexer.head.Load(), indexer.tail.Load())
|
||||
}
|
||||
|
||||
// close shutdown the indexer. Safe to be called for multiple times.
|
||||
|
|
|
|||
|
|
@ -117,13 +117,12 @@ func TestTxIndexer(t *testing.T) {
|
|||
}
|
||||
for _, c := range cases {
|
||||
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
||||
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...))
|
||||
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), types.EncodeBlockReceiptLists(append([]types.Receipts{{}}, receipts...)))
|
||||
|
||||
// Index the initial blocks from ancient store
|
||||
indexer := &txIndexer{
|
||||
limit: 0,
|
||||
db: db,
|
||||
progress: make(chan chan TxIndexProgress),
|
||||
limit: 0,
|
||||
db: db,
|
||||
}
|
||||
for i, limit := range c.limits {
|
||||
indexer.limit = limit
|
||||
|
|
@ -237,13 +236,13 @@ func TestTxIndexerRepair(t *testing.T) {
|
|||
}
|
||||
for _, c := range cases {
|
||||
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
||||
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...))
|
||||
encReceipts := types.EncodeBlockReceiptLists(append([]types.Receipts{{}}, receipts...))
|
||||
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), encReceipts)
|
||||
|
||||
// Index the initial blocks from ancient store
|
||||
indexer := &txIndexer{
|
||||
limit: c.limit,
|
||||
db: db,
|
||||
progress: make(chan chan TxIndexProgress),
|
||||
limit: c.limit,
|
||||
db: db,
|
||||
}
|
||||
indexer.run(chainHead, make(chan struct{}), make(chan struct{}))
|
||||
|
||||
|
|
@ -428,19 +427,16 @@ func TestTxIndexerReport(t *testing.T) {
|
|||
}
|
||||
for _, c := range cases {
|
||||
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
||||
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...))
|
||||
encReceipts := types.EncodeBlockReceiptLists(append([]types.Receipts{{}}, receipts...))
|
||||
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), encReceipts)
|
||||
|
||||
// Index the initial blocks from ancient store
|
||||
indexer := &txIndexer{
|
||||
limit: c.limit,
|
||||
cutoff: c.cutoff,
|
||||
db: db,
|
||||
progress: make(chan chan TxIndexProgress),
|
||||
limit: c.limit,
|
||||
cutoff: c.cutoff,
|
||||
db: db,
|
||||
}
|
||||
if c.tail != nil {
|
||||
rawdb.WriteTxIndexTail(db, *c.tail)
|
||||
}
|
||||
p := indexer.report(c.head)
|
||||
p := indexer.report(c.head, c.tail)
|
||||
if p.Indexed != c.expIndexed {
|
||||
t.Fatalf("Unexpected indexed: %d, expected: %d", p.Indexed, c.expIndexed)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1494,22 +1494,22 @@ func (pool *LegacyPool) promoteExecutables(accounts []common.Address) []*types.T
|
|||
// equal number for all for accounts with many pending transactions.
|
||||
func (pool *LegacyPool) truncatePending() {
|
||||
pending := uint64(0)
|
||||
for _, list := range pool.pending {
|
||||
pending += uint64(list.Len())
|
||||
|
||||
// Assemble a spam order to penalize large transactors first
|
||||
spammers := prque.New[uint64, common.Address](nil)
|
||||
for addr, list := range pool.pending {
|
||||
// Only evict transactions from high rollers
|
||||
length := uint64(list.Len())
|
||||
pending += length
|
||||
if length > pool.config.AccountSlots {
|
||||
spammers.Push(addr, length)
|
||||
}
|
||||
}
|
||||
if pending <= pool.config.GlobalSlots {
|
||||
return
|
||||
}
|
||||
|
||||
pendingBeforeCap := pending
|
||||
// Assemble a spam order to penalize large transactors first
|
||||
spammers := prque.New[int64, common.Address](nil)
|
||||
for addr, list := range pool.pending {
|
||||
// Only evict transactions from high rollers
|
||||
if uint64(list.Len()) > pool.config.AccountSlots {
|
||||
spammers.Push(addr, int64(list.Len()))
|
||||
}
|
||||
}
|
||||
|
||||
// Gradually drop transactions from offenders
|
||||
offenders := []common.Address{}
|
||||
for pending > pool.config.GlobalSlots && !spammers.Empty() {
|
||||
|
|
@ -1934,7 +1934,7 @@ func (pool *LegacyPool) Clear() {
|
|||
pool.reserver.Release(addr)
|
||||
}
|
||||
pool.all.Clear()
|
||||
pool.priced = newPricedList(pool.all)
|
||||
pool.priced.Reheap()
|
||||
pool.pending = make(map[common.Address]*list)
|
||||
pool.queue = make(map[common.Address]*list)
|
||||
pool.pendingNonces = newNoncer(pool.currentState)
|
||||
|
|
|
|||
|
|
@ -38,17 +38,13 @@ type Account struct {
|
|||
Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
|
||||
Balance *big.Int `json:"balance" gencodec:"required"`
|
||||
Nonce uint64 `json:"nonce,omitempty"`
|
||||
|
||||
// used in tests
|
||||
PrivateKey []byte `json:"secretKey,omitempty"`
|
||||
}
|
||||
|
||||
type accountMarshaling struct {
|
||||
Code hexutil.Bytes
|
||||
Balance *math.HexOrDecimal256
|
||||
Nonce math.HexOrDecimal64
|
||||
Storage map[storageJSON]storageJSON
|
||||
PrivateKey hexutil.Bytes
|
||||
Code hexutil.Bytes
|
||||
Balance *math.HexOrDecimal256
|
||||
Nonce math.HexOrDecimal64
|
||||
Storage map[storageJSON]storageJSON
|
||||
}
|
||||
|
||||
// storageJSON represents a 256 bit byte array, but allows less than 256 bits when
|
||||
|
|
|
|||
|
|
@ -59,11 +59,12 @@ func (b *Bloom) SetBytes(d []byte) {
|
|||
|
||||
// Add adds d to the filter. Future calls of Test(d) will return true.
|
||||
func (b *Bloom) Add(d []byte) {
|
||||
b.add(d, make([]byte, 6))
|
||||
var buf [6]byte
|
||||
b.AddWithBuffer(d, &buf)
|
||||
}
|
||||
|
||||
// add is internal version of Add, which takes a scratch buffer for reuse (needs to be at least 6 bytes)
|
||||
func (b *Bloom) add(d []byte, buf []byte) {
|
||||
func (b *Bloom) AddWithBuffer(d []byte, buf *[6]byte) {
|
||||
i1, v1, i2, v2, i3, v3 := bloomValues(d, buf)
|
||||
b[i1] |= v1
|
||||
b[i2] |= v2
|
||||
|
|
@ -84,7 +85,8 @@ func (b Bloom) Bytes() []byte {
|
|||
|
||||
// Test checks if the given topic is present in the bloom filter
|
||||
func (b Bloom) Test(topic []byte) bool {
|
||||
i1, v1, i2, v2, i3, v3 := bloomValues(topic, make([]byte, 6))
|
||||
var buf [6]byte
|
||||
i1, v1, i2, v2, i3, v3 := bloomValues(topic, &buf)
|
||||
return v1 == v1&b[i1] &&
|
||||
v2 == v2&b[i2] &&
|
||||
v3 == v3&b[i3]
|
||||
|
|
@ -104,12 +106,12 @@ func (b *Bloom) UnmarshalText(input []byte) error {
|
|||
func CreateBloom(receipt *Receipt) Bloom {
|
||||
var (
|
||||
bin Bloom
|
||||
buf = make([]byte, 6)
|
||||
buf [6]byte
|
||||
)
|
||||
for _, log := range receipt.Logs {
|
||||
bin.add(log.Address.Bytes(), buf)
|
||||
bin.AddWithBuffer(log.Address.Bytes(), &buf)
|
||||
for _, b := range log.Topics {
|
||||
bin.add(b[:], buf)
|
||||
bin.AddWithBuffer(b[:], &buf)
|
||||
}
|
||||
}
|
||||
return bin
|
||||
|
|
@ -139,21 +141,20 @@ func Bloom9(data []byte) []byte {
|
|||
}
|
||||
|
||||
// bloomValues returns the bytes (index-value pairs) to set for the given data
|
||||
func bloomValues(data []byte, hashbuf []byte) (uint, byte, uint, byte, uint, byte) {
|
||||
func bloomValues(data []byte, hashbuf *[6]byte) (uint, byte, uint, byte, uint, byte) {
|
||||
sha := hasherPool.Get().(crypto.KeccakState)
|
||||
sha.Reset()
|
||||
sha.Write(data)
|
||||
sha.Read(hashbuf)
|
||||
sha.Read(hashbuf[:])
|
||||
hasherPool.Put(sha)
|
||||
// The actual bits to flip
|
||||
v1 := byte(1 << (hashbuf[1] & 0x7))
|
||||
v2 := byte(1 << (hashbuf[3] & 0x7))
|
||||
v3 := byte(1 << (hashbuf[5] & 0x7))
|
||||
// The indices for the bytes to OR in
|
||||
i1 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf)&0x7ff)>>3) - 1
|
||||
i1 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[0:])&0x7ff)>>3) - 1
|
||||
i2 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[2:])&0x7ff)>>3) - 1
|
||||
i3 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[4:])&0x7ff)>>3) - 1
|
||||
|
||||
return i1, v1, i2, v2, i3, v3
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,11 +17,10 @@ var _ = (*accountMarshaling)(nil)
|
|||
// MarshalJSON marshals as JSON.
|
||||
func (a Account) MarshalJSON() ([]byte, error) {
|
||||
type Account struct {
|
||||
Code hexutil.Bytes `json:"code,omitempty"`
|
||||
Storage map[storageJSON]storageJSON `json:"storage,omitempty"`
|
||||
Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"`
|
||||
Nonce math.HexOrDecimal64 `json:"nonce,omitempty"`
|
||||
PrivateKey hexutil.Bytes `json:"secretKey,omitempty"`
|
||||
Code hexutil.Bytes `json:"code,omitempty"`
|
||||
Storage map[storageJSON]storageJSON `json:"storage,omitempty"`
|
||||
Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"`
|
||||
Nonce math.HexOrDecimal64 `json:"nonce,omitempty"`
|
||||
}
|
||||
var enc Account
|
||||
enc.Code = a.Code
|
||||
|
|
@ -33,18 +32,16 @@ func (a Account) MarshalJSON() ([]byte, error) {
|
|||
}
|
||||
enc.Balance = (*math.HexOrDecimal256)(a.Balance)
|
||||
enc.Nonce = math.HexOrDecimal64(a.Nonce)
|
||||
enc.PrivateKey = a.PrivateKey
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (a *Account) UnmarshalJSON(input []byte) error {
|
||||
type Account struct {
|
||||
Code *hexutil.Bytes `json:"code,omitempty"`
|
||||
Storage map[storageJSON]storageJSON `json:"storage,omitempty"`
|
||||
Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"`
|
||||
Nonce *math.HexOrDecimal64 `json:"nonce,omitempty"`
|
||||
PrivateKey *hexutil.Bytes `json:"secretKey,omitempty"`
|
||||
Code *hexutil.Bytes `json:"code,omitempty"`
|
||||
Storage map[storageJSON]storageJSON `json:"storage,omitempty"`
|
||||
Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"`
|
||||
Nonce *math.HexOrDecimal64 `json:"nonce,omitempty"`
|
||||
}
|
||||
var dec Account
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
|
|
@ -66,8 +63,5 @@ func (a *Account) UnmarshalJSON(input []byte) error {
|
|||
if dec.Nonce != nil {
|
||||
a.Nonce = uint64(*dec.Nonce)
|
||||
}
|
||||
if dec.PrivateKey != nil {
|
||||
a.PrivateKey = *dec.PrivateKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,12 +25,11 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// hasherPool holds LegacyKeccak256 hashers for rlpHash.
|
||||
var hasherPool = sync.Pool{
|
||||
New: func() interface{} { return sha3.NewLegacyKeccak256() },
|
||||
New: func() interface{} { return crypto.NewKeccakState() },
|
||||
}
|
||||
|
||||
// encodeBufferPool holds temporary encoder buffers for DeriveSha and TX encoding.
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
|
@ -258,7 +259,7 @@ func (r *Receipt) Size() common.StorageSize {
|
|||
}
|
||||
|
||||
// ReceiptForStorage is a wrapper around a Receipt with RLP serialization
|
||||
// that omits the Bloom field and deserialization that re-computes it.
|
||||
// that omits the Bloom field. The Bloom field is recomputed by DeriveFields.
|
||||
type ReceiptForStorage Receipt
|
||||
|
||||
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
|
||||
|
|
@ -291,7 +292,6 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
|
|||
}
|
||||
r.CumulativeGasUsed = stored.CumulativeGasUsed
|
||||
r.Logs = stored.Logs
|
||||
r.Bloom = CreateBloom((*Receipt)(r))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -372,6 +372,26 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu
|
|||
rs[i].Logs[j].Index = logIndex
|
||||
logIndex++
|
||||
}
|
||||
// also derive the Bloom if not derived yet
|
||||
rs[i].Bloom = CreateBloom(rs[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncodeBlockReceiptLists encodes a list of block receipt lists into RLP.
|
||||
func EncodeBlockReceiptLists(receipts []Receipts) []rlp.RawValue {
|
||||
var storageReceipts []*ReceiptForStorage
|
||||
result := make([]rlp.RawValue, len(receipts))
|
||||
for i, receipt := range receipts {
|
||||
storageReceipts = storageReceipts[:0]
|
||||
for _, r := range receipt {
|
||||
storageReceipts = append(storageReceipts, (*ReceiptForStorage)(r))
|
||||
}
|
||||
bytes, err := rlp.EncodeToBytes(storageReceipts)
|
||||
if err != nil {
|
||||
log.Crit("Failed to encode block receipts", "err", err)
|
||||
}
|
||||
result[i] = bytes
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"math"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -154,148 +155,161 @@ var (
|
|||
blockNumber = big.NewInt(1)
|
||||
blockTime = uint64(2)
|
||||
blockHash = common.BytesToHash([]byte{0x03, 0x14})
|
||||
|
||||
// Create the corresponding receipts
|
||||
receipts = Receipts{
|
||||
&Receipt{
|
||||
Status: ReceiptStatusFailed,
|
||||
CumulativeGasUsed: 1,
|
||||
Logs: []*Log{
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
// derived fields:
|
||||
BlockNumber: blockNumber.Uint64(),
|
||||
TxHash: txs[0].Hash(),
|
||||
TxIndex: 0,
|
||||
BlockHash: blockHash,
|
||||
Index: 0,
|
||||
},
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x01, 0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
// derived fields:
|
||||
BlockNumber: blockNumber.Uint64(),
|
||||
TxHash: txs[0].Hash(),
|
||||
TxIndex: 0,
|
||||
BlockHash: blockHash,
|
||||
Index: 1,
|
||||
},
|
||||
},
|
||||
// derived fields:
|
||||
TxHash: txs[0].Hash(),
|
||||
ContractAddress: common.HexToAddress("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"),
|
||||
GasUsed: 1,
|
||||
EffectiveGasPrice: big.NewInt(11),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 0,
|
||||
},
|
||||
&Receipt{
|
||||
PostState: common.Hash{2}.Bytes(),
|
||||
CumulativeGasUsed: 3,
|
||||
Logs: []*Log{
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x22}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
// derived fields:
|
||||
BlockNumber: blockNumber.Uint64(),
|
||||
TxHash: txs[1].Hash(),
|
||||
TxIndex: 1,
|
||||
BlockHash: blockHash,
|
||||
Index: 2,
|
||||
},
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x02, 0x22}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
// derived fields:
|
||||
BlockNumber: blockNumber.Uint64(),
|
||||
TxHash: txs[1].Hash(),
|
||||
TxIndex: 1,
|
||||
BlockHash: blockHash,
|
||||
Index: 3,
|
||||
},
|
||||
},
|
||||
// derived fields:
|
||||
TxHash: txs[1].Hash(),
|
||||
GasUsed: 2,
|
||||
EffectiveGasPrice: big.NewInt(22),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 1,
|
||||
},
|
||||
&Receipt{
|
||||
Type: AccessListTxType,
|
||||
PostState: common.Hash{3}.Bytes(),
|
||||
CumulativeGasUsed: 6,
|
||||
Logs: []*Log{},
|
||||
// derived fields:
|
||||
TxHash: txs[2].Hash(),
|
||||
GasUsed: 3,
|
||||
EffectiveGasPrice: big.NewInt(33),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 2,
|
||||
},
|
||||
&Receipt{
|
||||
Type: DynamicFeeTxType,
|
||||
PostState: common.Hash{4}.Bytes(),
|
||||
CumulativeGasUsed: 10,
|
||||
Logs: []*Log{},
|
||||
// derived fields:
|
||||
TxHash: txs[3].Hash(),
|
||||
GasUsed: 4,
|
||||
EffectiveGasPrice: big.NewInt(1044),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 3,
|
||||
},
|
||||
&Receipt{
|
||||
Type: DynamicFeeTxType,
|
||||
PostState: common.Hash{5}.Bytes(),
|
||||
CumulativeGasUsed: 15,
|
||||
Logs: []*Log{},
|
||||
// derived fields:
|
||||
TxHash: txs[4].Hash(),
|
||||
GasUsed: 5,
|
||||
EffectiveGasPrice: big.NewInt(1055),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 4,
|
||||
},
|
||||
&Receipt{
|
||||
Type: BlobTxType,
|
||||
PostState: common.Hash{6}.Bytes(),
|
||||
CumulativeGasUsed: 21,
|
||||
Logs: []*Log{},
|
||||
// derived fields:
|
||||
TxHash: txs[5].Hash(),
|
||||
GasUsed: 6,
|
||||
EffectiveGasPrice: big.NewInt(1066),
|
||||
BlobGasUsed: params.BlobTxBlobGasPerBlob,
|
||||
BlobGasPrice: big.NewInt(920),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 5,
|
||||
},
|
||||
&Receipt{
|
||||
Type: BlobTxType,
|
||||
PostState: common.Hash{7}.Bytes(),
|
||||
CumulativeGasUsed: 28,
|
||||
Logs: []*Log{},
|
||||
// derived fields:
|
||||
TxHash: txs[6].Hash(),
|
||||
GasUsed: 7,
|
||||
EffectiveGasPrice: big.NewInt(1077),
|
||||
BlobGasUsed: 3 * params.BlobTxBlobGasPerBlob,
|
||||
BlobGasPrice: big.NewInt(920),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 6,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
var receiptsOnce sync.Once
|
||||
var testReceipts Receipts
|
||||
|
||||
func getTestReceipts() Receipts {
|
||||
// Compute the blooms only once
|
||||
receiptsOnce.Do(func() {
|
||||
// Create the corresponding receipts
|
||||
r := Receipts{
|
||||
&Receipt{
|
||||
Status: ReceiptStatusFailed,
|
||||
CumulativeGasUsed: 1,
|
||||
Logs: []*Log{
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
// derived fields:
|
||||
BlockNumber: blockNumber.Uint64(),
|
||||
TxHash: txs[0].Hash(),
|
||||
TxIndex: 0,
|
||||
BlockHash: blockHash,
|
||||
Index: 0,
|
||||
},
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x01, 0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
// derived fields:
|
||||
BlockNumber: blockNumber.Uint64(),
|
||||
TxHash: txs[0].Hash(),
|
||||
TxIndex: 0,
|
||||
BlockHash: blockHash,
|
||||
Index: 1,
|
||||
},
|
||||
},
|
||||
// derived fields:
|
||||
TxHash: txs[0].Hash(),
|
||||
ContractAddress: common.HexToAddress("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"),
|
||||
GasUsed: 1,
|
||||
EffectiveGasPrice: big.NewInt(11),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 0,
|
||||
},
|
||||
&Receipt{
|
||||
PostState: common.Hash{2}.Bytes(),
|
||||
CumulativeGasUsed: 3,
|
||||
Logs: []*Log{
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x22}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
// derived fields:
|
||||
BlockNumber: blockNumber.Uint64(),
|
||||
TxHash: txs[1].Hash(),
|
||||
TxIndex: 1,
|
||||
BlockHash: blockHash,
|
||||
Index: 2,
|
||||
},
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x02, 0x22}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
// derived fields:
|
||||
BlockNumber: blockNumber.Uint64(),
|
||||
TxHash: txs[1].Hash(),
|
||||
TxIndex: 1,
|
||||
BlockHash: blockHash,
|
||||
Index: 3,
|
||||
},
|
||||
},
|
||||
// derived fields:
|
||||
TxHash: txs[1].Hash(),
|
||||
GasUsed: 2,
|
||||
EffectiveGasPrice: big.NewInt(22),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 1,
|
||||
},
|
||||
&Receipt{
|
||||
Type: AccessListTxType,
|
||||
PostState: common.Hash{3}.Bytes(),
|
||||
CumulativeGasUsed: 6,
|
||||
Logs: []*Log{},
|
||||
// derived fields:
|
||||
TxHash: txs[2].Hash(),
|
||||
GasUsed: 3,
|
||||
EffectiveGasPrice: big.NewInt(33),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 2,
|
||||
},
|
||||
&Receipt{
|
||||
Type: DynamicFeeTxType,
|
||||
PostState: common.Hash{4}.Bytes(),
|
||||
CumulativeGasUsed: 10,
|
||||
Logs: []*Log{},
|
||||
// derived fields:
|
||||
TxHash: txs[3].Hash(),
|
||||
GasUsed: 4,
|
||||
EffectiveGasPrice: big.NewInt(1044),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 3,
|
||||
},
|
||||
&Receipt{
|
||||
Type: DynamicFeeTxType,
|
||||
PostState: common.Hash{5}.Bytes(),
|
||||
CumulativeGasUsed: 15,
|
||||
Logs: []*Log{},
|
||||
// derived fields:
|
||||
TxHash: txs[4].Hash(),
|
||||
GasUsed: 5,
|
||||
EffectiveGasPrice: big.NewInt(1055),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 4,
|
||||
},
|
||||
&Receipt{
|
||||
Type: BlobTxType,
|
||||
PostState: common.Hash{6}.Bytes(),
|
||||
CumulativeGasUsed: 21,
|
||||
Logs: []*Log{},
|
||||
// derived fields:
|
||||
TxHash: txs[5].Hash(),
|
||||
GasUsed: 6,
|
||||
EffectiveGasPrice: big.NewInt(1066),
|
||||
BlobGasUsed: params.BlobTxBlobGasPerBlob,
|
||||
BlobGasPrice: big.NewInt(920),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 5,
|
||||
},
|
||||
&Receipt{
|
||||
Type: BlobTxType,
|
||||
PostState: common.Hash{7}.Bytes(),
|
||||
CumulativeGasUsed: 28,
|
||||
Logs: []*Log{},
|
||||
// derived fields:
|
||||
TxHash: txs[6].Hash(),
|
||||
GasUsed: 7,
|
||||
EffectiveGasPrice: big.NewInt(1077),
|
||||
BlobGasUsed: 3 * params.BlobTxBlobGasPerBlob,
|
||||
BlobGasPrice: big.NewInt(920),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 6,
|
||||
},
|
||||
}
|
||||
for _, receipt := range r {
|
||||
receipt.Bloom = CreateBloom(receipt)
|
||||
}
|
||||
testReceipts = r
|
||||
})
|
||||
return testReceipts
|
||||
}
|
||||
|
||||
func TestDecodeEmptyTypedReceipt(t *testing.T) {
|
||||
input := []byte{0x80}
|
||||
var r Receipt
|
||||
|
|
@ -310,6 +324,7 @@ func TestDeriveFields(t *testing.T) {
|
|||
// Re-derive receipts.
|
||||
basefee := big.NewInt(1000)
|
||||
blobGasPrice := big.NewInt(920)
|
||||
receipts := getTestReceipts()
|
||||
derivedReceipts := clearComputedFieldsOnReceipts(receipts)
|
||||
err := Receipts(derivedReceipts).DeriveFields(params.TestChainConfig, blockHash, blockNumber.Uint64(), blockTime, basefee, blobGasPrice, txs)
|
||||
if err != nil {
|
||||
|
|
@ -335,6 +350,7 @@ func TestDeriveFields(t *testing.T) {
|
|||
// Test that we can marshal/unmarshal receipts to/from json without errors.
|
||||
// This also confirms that our test receipts contain all the required fields.
|
||||
func TestReceiptJSON(t *testing.T) {
|
||||
receipts := getTestReceipts()
|
||||
for i := range receipts {
|
||||
b, err := receipts[i].MarshalJSON()
|
||||
if err != nil {
|
||||
|
|
@ -351,6 +367,7 @@ func TestReceiptJSON(t *testing.T) {
|
|||
// Test we can still parse receipt without EffectiveGasPrice for backwards compatibility, even
|
||||
// though it is required per the spec.
|
||||
func TestEffectiveGasPriceNotRequired(t *testing.T) {
|
||||
receipts := getTestReceipts()
|
||||
r := *receipts[0]
|
||||
r.EffectiveGasPrice = nil
|
||||
b, err := r.MarshalJSON()
|
||||
|
|
@ -511,6 +528,7 @@ func clearComputedFieldsOnReceipt(receipt *Receipt) *Receipt {
|
|||
cpy.EffectiveGasPrice = big.NewInt(0)
|
||||
cpy.BlobGasUsed = 0
|
||||
cpy.BlobGasPrice = nil
|
||||
cpy.Bloom = CreateBloom(&cpy)
|
||||
return &cpy
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -355,28 +355,31 @@ func (tx *Transaction) GasTipCapIntCmp(other *big.Int) int {
|
|||
// Note: if the effective gasTipCap is negative, this method returns both error
|
||||
// the actual negative value, _and_ ErrGasFeeCapTooLow
|
||||
func (tx *Transaction) EffectiveGasTip(baseFee *big.Int) (*big.Int, error) {
|
||||
dst := new(big.Int)
|
||||
err := tx.calcEffectiveGasTip(dst, baseFee)
|
||||
return dst, err
|
||||
}
|
||||
|
||||
// calcEffectiveGasTip calculates the effective gas tip of the transaction and
|
||||
// saves the result to dst.
|
||||
func (tx *Transaction) calcEffectiveGasTip(dst *big.Int, baseFee *big.Int) error {
|
||||
if baseFee == nil {
|
||||
return tx.GasTipCap(), nil
|
||||
dst.Set(tx.inner.gasTipCap())
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
gasFeeCap := tx.GasFeeCap()
|
||||
gasFeeCap := tx.inner.gasFeeCap()
|
||||
if gasFeeCap.Cmp(baseFee) < 0 {
|
||||
err = ErrGasFeeCapTooLow
|
||||
}
|
||||
gasFeeCap = gasFeeCap.Sub(gasFeeCap, baseFee)
|
||||
|
||||
gasTipCap := tx.GasTipCap()
|
||||
if gasTipCap.Cmp(gasFeeCap) < 0 {
|
||||
return gasTipCap, err
|
||||
dst.Sub(gasFeeCap, baseFee)
|
||||
gasTipCap := tx.inner.gasTipCap()
|
||||
if gasTipCap.Cmp(dst) < 0 {
|
||||
dst.Set(gasTipCap)
|
||||
}
|
||||
return gasFeeCap, err
|
||||
}
|
||||
|
||||
// EffectiveGasTipValue is identical to EffectiveGasTip, but does not return an
|
||||
// error in case the effective gasTipCap is negative
|
||||
func (tx *Transaction) EffectiveGasTipValue(baseFee *big.Int) *big.Int {
|
||||
effectiveTip, _ := tx.EffectiveGasTip(baseFee)
|
||||
return effectiveTip
|
||||
return err
|
||||
}
|
||||
|
||||
// EffectiveGasTipCmp compares the effective gasTipCap of two transactions assuming the given base fee.
|
||||
|
|
@ -384,7 +387,11 @@ func (tx *Transaction) EffectiveGasTipCmp(other *Transaction, baseFee *big.Int)
|
|||
if baseFee == nil {
|
||||
return tx.GasTipCapCmp(other)
|
||||
}
|
||||
return tx.EffectiveGasTipValue(baseFee).Cmp(other.EffectiveGasTipValue(baseFee))
|
||||
// Use more efficient internal method.
|
||||
txTip, otherTip := new(big.Int), new(big.Int)
|
||||
tx.calcEffectiveGasTip(txTip, baseFee)
|
||||
other.calcEffectiveGasTip(otherTip, baseFee)
|
||||
return txTip.Cmp(otherTip)
|
||||
}
|
||||
|
||||
// EffectiveGasTipIntCmp compares the effective gasTipCap of a transaction to the given gasTipCap.
|
||||
|
|
@ -392,7 +399,9 @@ func (tx *Transaction) EffectiveGasTipIntCmp(other *big.Int, baseFee *big.Int) i
|
|||
if baseFee == nil {
|
||||
return tx.GasTipCapIntCmp(other)
|
||||
}
|
||||
return tx.EffectiveGasTipValue(baseFee).Cmp(other)
|
||||
txTip := new(big.Int)
|
||||
tx.calcEffectiveGasTip(txTip, baseFee)
|
||||
return txTip.Cmp(other)
|
||||
}
|
||||
|
||||
// BlobGas returns the blob gas limit of the transaction for blob transactions, 0 otherwise.
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
|
|
@ -593,3 +594,101 @@ func BenchmarkHash(b *testing.B) {
|
|||
signer.Hash(tx)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEffectiveGasTip(b *testing.B) {
|
||||
signer := LatestSigner(params.TestChainConfig)
|
||||
key, _ := crypto.GenerateKey()
|
||||
txdata := &DynamicFeeTx{
|
||||
ChainID: big.NewInt(1),
|
||||
Nonce: 0,
|
||||
GasTipCap: big.NewInt(2000000000),
|
||||
GasFeeCap: big.NewInt(3000000000),
|
||||
Gas: 21000,
|
||||
To: &common.Address{},
|
||||
Value: big.NewInt(0),
|
||||
Data: nil,
|
||||
}
|
||||
tx, _ := SignNewTx(key, signer, txdata)
|
||||
baseFee := big.NewInt(1000000000) // 1 gwei
|
||||
|
||||
b.Run("Original", func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := tx.EffectiveGasTip(baseFee)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("IntoMethod", func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
dst := new(big.Int)
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := tx.calcEffectiveGasTip(dst, baseFee)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestEffectiveGasTipInto(t *testing.T) {
|
||||
signer := LatestSigner(params.TestChainConfig)
|
||||
key, _ := crypto.GenerateKey()
|
||||
|
||||
testCases := []struct {
|
||||
tipCap int64
|
||||
feeCap int64
|
||||
baseFee *int64
|
||||
}{
|
||||
{tipCap: 1, feeCap: 100, baseFee: intPtr(50)},
|
||||
{tipCap: 10, feeCap: 100, baseFee: intPtr(50)},
|
||||
{tipCap: 50, feeCap: 100, baseFee: intPtr(50)},
|
||||
{tipCap: 100, feeCap: 100, baseFee: intPtr(50)},
|
||||
{tipCap: 1, feeCap: 50, baseFee: intPtr(50)},
|
||||
{tipCap: 1, feeCap: 20, baseFee: intPtr(50)}, // Base fee higher than fee cap
|
||||
{tipCap: 50, feeCap: 100, baseFee: intPtr(0)},
|
||||
{tipCap: 50, feeCap: 100, baseFee: nil}, // nil base fee
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
txdata := &DynamicFeeTx{
|
||||
ChainID: big.NewInt(1),
|
||||
Nonce: 0,
|
||||
GasTipCap: big.NewInt(tc.tipCap),
|
||||
GasFeeCap: big.NewInt(tc.feeCap),
|
||||
Gas: 21000,
|
||||
To: &common.Address{},
|
||||
Value: big.NewInt(0),
|
||||
Data: nil,
|
||||
}
|
||||
tx, _ := SignNewTx(key, signer, txdata)
|
||||
|
||||
var baseFee *big.Int
|
||||
if tc.baseFee != nil {
|
||||
baseFee = big.NewInt(*tc.baseFee)
|
||||
}
|
||||
|
||||
// Get result from original method
|
||||
orig, origErr := tx.EffectiveGasTip(baseFee)
|
||||
|
||||
// Get result from new method
|
||||
dst := new(big.Int)
|
||||
newErr := tx.calcEffectiveGasTip(dst, baseFee)
|
||||
|
||||
// Compare results
|
||||
if (origErr != nil) != (newErr != nil) {
|
||||
t.Fatalf("case %d: error mismatch: orig %v, new %v", i, origErr, newErr)
|
||||
}
|
||||
|
||||
if orig.Cmp(dst) != 0 {
|
||||
t.Fatalf("case %d: result mismatch: orig %v, new %v", i, orig, dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to create integer pointer
|
||||
func intPtr(i int64) *int64 {
|
||||
return &i
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ var PrecompiledContractsPrague = PrecompiledContracts{
|
|||
|
||||
var PrecompiledContractsBLS = PrecompiledContractsPrague
|
||||
|
||||
var PrecompiledContractsVerkle = PrecompiledContractsPrague
|
||||
var PrecompiledContractsVerkle = PrecompiledContractsBerlin
|
||||
|
||||
var (
|
||||
PrecompiledAddressesPrague []common.Address
|
||||
|
|
|
|||
|
|
@ -339,12 +339,10 @@ func opExtCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeC
|
|||
addr := common.Address(a.Bytes20())
|
||||
code := interpreter.evm.StateDB.GetCode(addr)
|
||||
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
|
||||
if !scope.Contract.IsSystemCall {
|
||||
statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false)
|
||||
if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) {
|
||||
scope.Contract.Gas = 0
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
consumed, wanted := interpreter.evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false, scope.Contract.Gas)
|
||||
scope.Contract.UseGas(consumed, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||
if consumed < wanted {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), paddedCodeCopy)
|
||||
|
||||
|
|
@ -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
|
||||
// advanced past this boundary.
|
||||
contractAddr := scope.Contract.Address()
|
||||
statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false)
|
||||
if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) {
|
||||
scope.Contract.Gas = 0
|
||||
consumed, wanted := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas)
|
||||
scope.Contract.UseGas(wanted, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||
if consumed < wanted {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
|
|
@ -395,9 +393,9 @@ func makePushEIP4762(size uint64, pushByteSize int) executionFunc {
|
|||
|
||||
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall {
|
||||
contractAddr := scope.Contract.Address()
|
||||
statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false)
|
||||
if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) {
|
||||
scope.Contract.Gas = 0
|
||||
consumed, wanted := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas)
|
||||
scope.Contract.UseGas(consumed, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||
if consumed < wanted {
|
||||
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 !isPrecompile && evm.chainRules.IsEIP4762 && !isSystemCall(caller) {
|
||||
// add proof of absence to witness
|
||||
wgas := evm.AccessEvents.AddAccount(addr, false)
|
||||
// Add proof of absence to witness
|
||||
// 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 {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
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
|
||||
if evm.chainRules.IsEIP4762 {
|
||||
statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address)
|
||||
statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address, gas)
|
||||
if statelessGas > gas {
|
||||
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
|
||||
if evm.chainRules.IsEIP4762 {
|
||||
statelessGas := evm.AccessEvents.ContractCreateInitGas(address)
|
||||
if statelessGas > gas {
|
||||
consumed, wanted := evm.AccessEvents.ContractCreateInitGas(address, gas)
|
||||
if consumed < wanted {
|
||||
return nil, common.Address{}, 0, ErrOutOfGas
|
||||
}
|
||||
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)
|
||||
|
||||
|
|
@ -535,7 +541,9 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
|
|||
return ret, ErrCodeStoreOutOfGas
|
||||
}
|
||||
} 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
|
||||
}
|
||||
}
|
||||
|
|
@ -555,7 +563,8 @@ func (evm *EVM) Create(caller common.Address, code []byte, gas uint64, value *ui
|
|||
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
|
||||
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
|
||||
func (evm *EVM) Create2(caller common.Address, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
||||
contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), crypto.Keccak256(code))
|
||||
inithash := crypto.HashData(evm.interpreter.hasher, code)
|
||||
contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), inithash[:])
|
||||
return evm.create(caller, code, gas, endowment, contractAddr, CREATE2)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -394,14 +394,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
|
|||
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
||||
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))
|
||||
if err != nil {
|
||||
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 {
|
||||
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))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
|
@ -234,11 +233,7 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
|
|||
offset, size := scope.Stack.pop(), scope.Stack.peek()
|
||||
data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
|
||||
|
||||
if interpreter.hasher == nil {
|
||||
interpreter.hasher = crypto.NewKeccakState()
|
||||
} else {
|
||||
interpreter.hasher.Reset()
|
||||
}
|
||||
interpreter.hasher.Reset()
|
||||
interpreter.hasher.Write(data)
|
||||
interpreter.hasher.Read(interpreter.hasherBuf[:])
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ type StateDB interface {
|
|||
SelfDestruct6780(common.Address) (uint256.Int, bool)
|
||||
|
||||
// Exist reports whether the given account exists in state.
|
||||
// Notably this should also return true for self-destructed accounts.
|
||||
// Notably this also returns true for self-destructed accounts within the current transaction.
|
||||
Exist(common.Address) bool
|
||||
// Empty returns whether the given account is empty. Empty
|
||||
// is defined according to EIP161 (balance = nonce = code = 0).
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter {
|
|||
}
|
||||
}
|
||||
evm.Config.ExtraEips = extraEips
|
||||
return &EVMInterpreter{evm: evm, table: table}
|
||||
return &EVMInterpreter{evm: evm, table: table, hasher: crypto.NewKeccakState()}
|
||||
}
|
||||
|
||||
// Run loops and evaluates the contract's code with the given input data and returns
|
||||
|
|
@ -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
|
||||
// associated costs.
|
||||
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
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ func validate(jt JumpTable) JumpTable {
|
|||
}
|
||||
|
||||
func newVerkleInstructionSet() JumpTable {
|
||||
instructionSet := newCancunInstructionSet()
|
||||
instructionSet := newShanghaiInstructionSet()
|
||||
enable4762(&instructionSet)
|
||||
return validate(instructionSet)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,31 +25,16 @@ import (
|
|||
)
|
||||
|
||||
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
gas := evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true)
|
||||
if gas == 0 {
|
||||
gas = params.WarmStorageReadCostEIP2929
|
||||
}
|
||||
return gas, nil
|
||||
return evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true, contract.Gas, true), nil
|
||||
}
|
||||
|
||||
func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
gas := evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false)
|
||||
if gas == 0 {
|
||||
gas = params.WarmStorageReadCostEIP2929
|
||||
}
|
||||
return gas, nil
|
||||
return evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false, contract.Gas, true), nil
|
||||
}
|
||||
|
||||
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
if contract.IsSystemCall {
|
||||
return 0, nil
|
||||
}
|
||||
address := stack.peek().Bytes20()
|
||||
gas := evm.AccessEvents.BasicDataGas(address, false)
|
||||
if gas == 0 {
|
||||
gas = params.WarmStorageReadCostEIP2929
|
||||
}
|
||||
return gas, nil
|
||||
return evm.AccessEvents.BasicDataGas(address, false, contract.Gas, true), nil
|
||||
}
|
||||
|
||||
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 {
|
||||
return 0, nil
|
||||
}
|
||||
if contract.IsSystemCall {
|
||||
return 0, nil
|
||||
}
|
||||
gas := evm.AccessEvents.BasicDataGas(address, false)
|
||||
if gas == 0 {
|
||||
gas = params.WarmStorageReadCostEIP2929
|
||||
}
|
||||
return gas, nil
|
||||
return evm.AccessEvents.BasicDataGas(address, false, contract.Gas, true), nil
|
||||
}
|
||||
|
||||
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
if contract.IsSystemCall {
|
||||
return 0, nil
|
||||
}
|
||||
address := stack.peek().Bytes20()
|
||||
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
||||
return 0, nil
|
||||
}
|
||||
gas := evm.AccessEvents.CodeHashGas(address, false)
|
||||
if gas == 0 {
|
||||
gas = params.WarmStorageReadCostEIP2929
|
||||
}
|
||||
return gas, nil
|
||||
return evm.AccessEvents.CodeHashGas(address, false, contract.Gas, true), 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) {
|
||||
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if contract.IsSystemCall {
|
||||
return gas, nil
|
||||
}
|
||||
if _, isPrecompile := evm.precompile(contract.Address()); isPrecompile {
|
||||
return gas, nil
|
||||
}
|
||||
witnessGas := evm.AccessEvents.MessageCallGas(contract.Address())
|
||||
if witnessGas == 0 {
|
||||
var (
|
||||
target = common.Address(stack.Back(1).Bytes20())
|
||||
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
|
||||
}
|
||||
witnessGas = wantedValueTransferWitnessGas
|
||||
} else if isPrecompile || isSystemContract {
|
||||
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
|
||||
}
|
||||
if witnessGas > contract.Gas {
|
||||
return witnessGas, nil
|
||||
}
|
||||
}
|
||||
return witnessGas + gas, 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 (
|
||||
gasCallEIP4762 = makeCallVariantGasEIP4762(gasCall)
|
||||
gasCallCodeEIP4762 = makeCallVariantGasEIP4762(gasCallCode)
|
||||
gasStaticCallEIP4762 = makeCallVariantGasEIP4762(gasStaticCall)
|
||||
gasDelegateCallEIP4762 = makeCallVariantGasEIP4762(gasDelegateCall)
|
||||
gasCallEIP4762 = makeCallVariantGasEIP4762(gasCall, true)
|
||||
gasCallCodeEIP4762 = makeCallVariantGasEIP4762(gasCallCode, false)
|
||||
gasStaticCallEIP4762 = makeCallVariantGasEIP4762(gasStaticCall, false)
|
||||
gasDelegateCallEIP4762 = makeCallVariantGasEIP4762(gasDelegateCall, false)
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
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 {
|
||||
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
|
||||
if evm.StateDB.GetBalance(contractAddr).Sign() != 0 {
|
||||
statelessGas += evm.AccessEvents.BasicDataGas(contractAddr, true)
|
||||
if !balanceIsZero {
|
||||
wanted := evm.AccessEvents.BasicDataGas(contractAddr, true, contract.Gas-statelessGas, false)
|
||||
if wanted > contract.Gas-statelessGas {
|
||||
return statelessGas + wanted, nil
|
||||
}
|
||||
statelessGas += wanted
|
||||
|
||||
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
|
||||
|
|
@ -137,17 +164,19 @@ func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
|||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var (
|
||||
codeOffset = stack.Back(1)
|
||||
length = stack.Back(2)
|
||||
)
|
||||
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
|
||||
if overflow {
|
||||
uint64CodeOffset = gomath.MaxUint64
|
||||
}
|
||||
_, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64())
|
||||
if !contract.IsDeployment && !contract.IsSystemCall {
|
||||
gas += evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false)
|
||||
var (
|
||||
codeOffset = stack.Back(1)
|
||||
length = stack.Back(2)
|
||||
)
|
||||
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
|
||||
if overflow {
|
||||
uint64CodeOffset = gomath.MaxUint64
|
||||
}
|
||||
|
||||
_, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64())
|
||||
_, wanted := evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false, contract.Gas-gas)
|
||||
gas += wanted
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
|
@ -158,16 +187,17 @@ func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memo
|
|||
if err != nil {
|
||||
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
|
||||
}
|
||||
addr := common.Address(stack.peek().Bytes20())
|
||||
wgas := evm.AccessEvents.BasicDataGas(addr, false)
|
||||
if wgas == 0 {
|
||||
wgas = params.WarmStorageReadCostEIP2929
|
||||
}
|
||||
wgas := evm.AccessEvents.BasicDataGas(addr, false, contract.Gas-gas, true)
|
||||
var overflow bool
|
||||
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
||||
if gas, overflow = math.SafeAdd(gas, wgas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import (
|
|||
"io"
|
||||
"math/big"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
|
|
@ -73,6 +74,12 @@ func NewKeccakState() KeccakState {
|
|||
return sha3.NewLegacyKeccak256().(KeccakState)
|
||||
}
|
||||
|
||||
var hasherPool = sync.Pool{
|
||||
New: func() any {
|
||||
return sha3.NewLegacyKeccak256().(KeccakState)
|
||||
},
|
||||
}
|
||||
|
||||
// HashData hashes the provided data using the KeccakState and returns a 32 byte hash
|
||||
func HashData(kh KeccakState, data []byte) (h common.Hash) {
|
||||
kh.Reset()
|
||||
|
|
@ -84,22 +91,26 @@ func HashData(kh KeccakState, data []byte) (h common.Hash) {
|
|||
// Keccak256 calculates and returns the Keccak256 hash of the input data.
|
||||
func Keccak256(data ...[]byte) []byte {
|
||||
b := make([]byte, 32)
|
||||
d := NewKeccakState()
|
||||
d := hasherPool.Get().(KeccakState)
|
||||
d.Reset()
|
||||
for _, b := range data {
|
||||
d.Write(b)
|
||||
}
|
||||
d.Read(b)
|
||||
hasherPool.Put(d)
|
||||
return b
|
||||
}
|
||||
|
||||
// Keccak256Hash calculates and returns the Keccak256 hash of the input data,
|
||||
// converting it to an internal Hash data structure.
|
||||
func Keccak256Hash(data ...[]byte) (h common.Hash) {
|
||||
d := NewKeccakState()
|
||||
d := hasherPool.Get().(KeccakState)
|
||||
d.Reset()
|
||||
for _, b := range data {
|
||||
d.Write(b)
|
||||
}
|
||||
d.Read(h[:])
|
||||
hasherPool.Put(d)
|
||||
return h
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -149,6 +149,17 @@ func VerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error {
|
|||
return gokzgVerifyBlobProof(blob, commitment, proof)
|
||||
}
|
||||
|
||||
// ComputeCellProofs returns the KZG cell proofs that are used to verify the blob against
|
||||
// the commitment.
|
||||
//
|
||||
// This method does not verify that the commitment is correct with respect to blob.
|
||||
func ComputeCellProofs(blob *Blob) ([]Proof, error) {
|
||||
if useCKZG.Load() {
|
||||
return ckzgComputeCellProofs(blob)
|
||||
}
|
||||
return gokzgComputeCellProofs(blob)
|
||||
}
|
||||
|
||||
// CalcBlobHashV1 calculates the 'versioned blob hash' of a commitment.
|
||||
// The given hasher must be a sha256 hash instance, otherwise the result will be invalid!
|
||||
func CalcBlobHashV1(hasher hash.Hash, commit *Commitment) (vh [32]byte) {
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ import (
|
|||
"errors"
|
||||
"sync"
|
||||
|
||||
gokzg4844 "github.com/crate-crypto/go-kzg-4844"
|
||||
ckzg4844 "github.com/ethereum/c-kzg-4844/bindings/go"
|
||||
gokzg4844 "github.com/crate-crypto/go-eth-kzg"
|
||||
ckzg4844 "github.com/ethereum/c-kzg-4844/v2/bindings/go"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
|
|
@ -47,15 +47,21 @@ func ckzgInit() {
|
|||
if err = gokzg4844.CheckTrustedSetupIsWellFormed(params); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
g1s := make([]byte, len(params.SetupG1Lagrange)*(len(params.SetupG1Lagrange[0])-2)/2)
|
||||
g1Lag := make([]byte, len(params.SetupG1Lagrange)*(len(params.SetupG1Lagrange[0])-2)/2)
|
||||
for i, g1 := range params.SetupG1Lagrange {
|
||||
copy(g1Lag[i*(len(g1)-2)/2:], hexutil.MustDecode(g1))
|
||||
}
|
||||
g1s := make([]byte, len(params.SetupG1Monomial)*(len(params.SetupG1Monomial[0])-2)/2)
|
||||
for i, g1 := range params.SetupG1Monomial {
|
||||
copy(g1s[i*(len(g1)-2)/2:], hexutil.MustDecode(g1))
|
||||
}
|
||||
g2s := make([]byte, len(params.SetupG2)*(len(params.SetupG2[0])-2)/2)
|
||||
for i, g2 := range params.SetupG2 {
|
||||
copy(g2s[i*(len(g2)-2)/2:], hexutil.MustDecode(g2))
|
||||
}
|
||||
if err = ckzg4844.LoadTrustedSetup(g1s, g2s); err != nil {
|
||||
// The last parameter determines the multiplication table, see https://notes.ethereum.org/@jtraglia/windowed_multiplications
|
||||
// I think 6 is an decent compromise between size and speed
|
||||
if err = ckzg4844.LoadTrustedSetup(g1s, g1Lag, g2s, 6); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -125,3 +131,21 @@ func ckzgVerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ckzgComputeCellProofs returns the KZG cell proofs that are used to verify the blob against
|
||||
// the commitment.
|
||||
//
|
||||
// This method does not verify that the commitment is correct with respect to blob.
|
||||
func ckzgComputeCellProofs(blob *Blob) ([]Proof, error) {
|
||||
ckzgIniter.Do(ckzgInit)
|
||||
|
||||
_, proofs, err := ckzg4844.ComputeCellsAndKZGProofs((*ckzg4844.Blob)(blob))
|
||||
if err != nil {
|
||||
return []Proof{}, err
|
||||
}
|
||||
var p []Proof
|
||||
for _, proof := range proofs {
|
||||
p = append(p, (Proof)(proof))
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,3 +60,11 @@ func ckzgComputeBlobProof(blob *Blob, commitment Commitment) (Proof, error) {
|
|||
func ckzgVerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error {
|
||||
panic("unsupported platform")
|
||||
}
|
||||
|
||||
// ckzgComputeCellProofs returns the KZG cell proofs that are used to verify the blob against
|
||||
// the commitment.
|
||||
//
|
||||
// This method does not verify that the commitment is correct with respect to blob.
|
||||
func ckzgComputeCellProofs(blob *Blob) ([]Proof, error) {
|
||||
panic("unsupported platform")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import (
|
|||
"encoding/json"
|
||||
"sync"
|
||||
|
||||
gokzg4844 "github.com/crate-crypto/go-kzg-4844"
|
||||
gokzg4844 "github.com/crate-crypto/go-eth-kzg"
|
||||
)
|
||||
|
||||
// context is the crypto primitive pre-seeded with the trusted setup parameters.
|
||||
|
|
@ -96,3 +96,21 @@ func gokzgVerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error
|
|||
|
||||
return context.VerifyBlobKZGProof((*gokzg4844.Blob)(blob), (gokzg4844.KZGCommitment)(commitment), (gokzg4844.KZGProof)(proof))
|
||||
}
|
||||
|
||||
// gokzgComputeCellProofs returns the KZG cell proofs that are used to verify the blob against
|
||||
// the commitment.
|
||||
//
|
||||
// This method does not verify that the commitment is correct with respect to blob.
|
||||
func gokzgComputeCellProofs(blob *Blob) ([]Proof, error) {
|
||||
gokzgIniter.Do(gokzgInit)
|
||||
|
||||
_, proofs, err := context.ComputeCellsAndKZGProofs((*gokzg4844.Blob)(blob), 0)
|
||||
if err != nil {
|
||||
return []Proof{}, err
|
||||
}
|
||||
var p []Proof
|
||||
for _, proof := range proofs {
|
||||
p = append(p, (Proof)(proof))
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
|
||||
gokzg4844 "github.com/crate-crypto/go-kzg-4844"
|
||||
gokzg4844 "github.com/crate-crypto/go-eth-kzg"
|
||||
)
|
||||
|
||||
func randFieldElement() [32]byte {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -349,22 +349,20 @@ func (b *EthAPIBackend) GetPoolTransaction(hash common.Hash) *types.Transaction
|
|||
// GetTransaction retrieves the lookup along with the transaction itself associate
|
||||
// with the given transaction hash.
|
||||
//
|
||||
// An error will be returned if the transaction is not found, and background
|
||||
// indexing for transactions is still in progress. The error is used to indicate the
|
||||
// scenario explicitly that the transaction might be reachable shortly.
|
||||
//
|
||||
// A null will be returned in the transaction is not found and background transaction
|
||||
// indexing is already finished. The transaction is not existent from the perspective
|
||||
// of node.
|
||||
func (b *EthAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) {
|
||||
lookup, tx, err := b.eth.blockchain.GetTransactionLookup(txHash)
|
||||
if err != nil {
|
||||
return false, nil, common.Hash{}, 0, 0, err
|
||||
}
|
||||
// A null will be returned if the transaction is not found. The transaction is not
|
||||
// existent from the node's perspective. This can be due to the transaction indexer
|
||||
// not being finished. The caller must explicitly check the indexer progress.
|
||||
func (b *EthAPIBackend) GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) {
|
||||
lookup, tx := b.eth.blockchain.GetTransactionLookup(txHash)
|
||||
if lookup == nil || tx == nil {
|
||||
return false, nil, common.Hash{}, 0, 0, nil
|
||||
return false, nil, common.Hash{}, 0, 0
|
||||
}
|
||||
return true, tx, lookup.BlockHash, lookup.BlockIndex, lookup.Index, nil
|
||||
return true, tx, lookup.BlockHash, lookup.BlockIndex, lookup.Index
|
||||
}
|
||||
|
||||
// TxIndexDone returns true if the transaction indexer has finished indexing.
|
||||
func (b *EthAPIBackend) TxIndexDone() bool {
|
||||
return b.eth.blockchain.TxIndexDone()
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
|
||||
|
|
@ -391,7 +389,7 @@ func (b *EthAPIBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.S
|
|||
return b.eth.txPool.SubscribeTransactions(ch, true)
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) SyncProgress() ethereum.SyncProgress {
|
||||
func (b *EthAPIBackend) SyncProgress(ctx context.Context) ethereum.SyncProgress {
|
||||
prog := b.eth.Downloader().Progress()
|
||||
if txProg, err := b.eth.blockchain.TxIndexProgress(); err == nil {
|
||||
prog.TxIndexFinishedBlocks = txProg.Indexed
|
||||
|
|
@ -443,6 +441,14 @@ func (b *EthAPIBackend) RPCTxFeeCap() float64 {
|
|||
return b.eth.config.RPCTxFeeCap
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) CurrentView() *filtermaps.ChainView {
|
||||
head := b.eth.blockchain.CurrentBlock()
|
||||
if head == nil {
|
||||
return nil
|
||||
}
|
||||
return filtermaps.NewChainView(b.eth.blockchain, head.Number.Uint64(), head.Hash())
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) NewMatcherBackend() filtermaps.MatcherBackend {
|
||||
return b.eth.filterMaps.NewMatcherBackend()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,13 +134,17 @@ func TestSendTx(t *testing.T) {
|
|||
func testSendTx(t *testing.T, withLocal bool) {
|
||||
b := initBackend(withLocal)
|
||||
|
||||
txA := pricedSetCodeTx(0, 250000, uint256.NewInt(params.GWei), uint256.NewInt(params.GWei), key, []unsignedAuth{
|
||||
{
|
||||
nonce: 0,
|
||||
key: key,
|
||||
},
|
||||
})
|
||||
b.SendTx(context.Background(), txA)
|
||||
txA := pricedSetCodeTx(0, 250000, uint256.NewInt(params.GWei), uint256.NewInt(params.GWei), key, []unsignedAuth{{nonce: 0, key: key}})
|
||||
if err := b.SendTx(context.Background(), txA); err != nil {
|
||||
t.Fatalf("Failed to submit tx: %v", err)
|
||||
}
|
||||
for {
|
||||
pending, _ := b.TxPool().ContentFrom(address)
|
||||
if len(pending) == 1 {
|
||||
break
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
txB := makeTx(1, nil, nil, key)
|
||||
err := b.SendTx(context.Background(), txB)
|
||||
|
|
|
|||
|
|
@ -271,26 +271,26 @@ func storageRangeAt(statedb *state.StateDB, root common.Hash, address common.Add
|
|||
//
|
||||
// With one parameter, returns the list of accounts modified in the specified block.
|
||||
func (api *DebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum *uint64) ([]common.Address, error) {
|
||||
var startBlock, endBlock *types.Block
|
||||
var startHeader, endHeader *types.Header
|
||||
|
||||
startBlock = api.eth.blockchain.GetBlockByNumber(startNum)
|
||||
if startBlock == nil {
|
||||
startHeader = api.eth.blockchain.GetHeaderByNumber(startNum)
|
||||
if startHeader == nil {
|
||||
return nil, fmt.Errorf("start block %x not found", startNum)
|
||||
}
|
||||
|
||||
if endNum == nil {
|
||||
endBlock = startBlock
|
||||
startBlock = api.eth.blockchain.GetBlockByHash(startBlock.ParentHash())
|
||||
if startBlock == nil {
|
||||
return nil, fmt.Errorf("block %x has no parent", endBlock.Number())
|
||||
endHeader = startHeader
|
||||
startHeader = api.eth.blockchain.GetHeaderByHash(startHeader.ParentHash)
|
||||
if startHeader == nil {
|
||||
return nil, fmt.Errorf("block %x has no parent", endHeader.Number)
|
||||
}
|
||||
} else {
|
||||
endBlock = api.eth.blockchain.GetBlockByNumber(*endNum)
|
||||
if endBlock == nil {
|
||||
endHeader = api.eth.blockchain.GetHeaderByNumber(*endNum)
|
||||
if endHeader == nil {
|
||||
return nil, fmt.Errorf("end block %d not found", *endNum)
|
||||
}
|
||||
}
|
||||
return api.getModifiedAccounts(startBlock, endBlock)
|
||||
return api.getModifiedAccounts(startHeader, endHeader)
|
||||
}
|
||||
|
||||
// GetModifiedAccountsByHash returns all accounts that have changed between the
|
||||
|
|
@ -299,38 +299,38 @@ func (api *DebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum *uint64
|
|||
//
|
||||
// With one parameter, returns the list of accounts modified in the specified block.
|
||||
func (api *DebugAPI) GetModifiedAccountsByHash(startHash common.Hash, endHash *common.Hash) ([]common.Address, error) {
|
||||
var startBlock, endBlock *types.Block
|
||||
startBlock = api.eth.blockchain.GetBlockByHash(startHash)
|
||||
if startBlock == nil {
|
||||
var startHeader, endHeader *types.Header
|
||||
startHeader = api.eth.blockchain.GetHeaderByHash(startHash)
|
||||
if startHeader == nil {
|
||||
return nil, fmt.Errorf("start block %x not found", startHash)
|
||||
}
|
||||
|
||||
if endHash == nil {
|
||||
endBlock = startBlock
|
||||
startBlock = api.eth.blockchain.GetBlockByHash(startBlock.ParentHash())
|
||||
if startBlock == nil {
|
||||
return nil, fmt.Errorf("block %x has no parent", endBlock.Number())
|
||||
endHeader = startHeader
|
||||
startHeader = api.eth.blockchain.GetHeaderByHash(startHeader.ParentHash)
|
||||
if startHeader == nil {
|
||||
return nil, fmt.Errorf("block %x has no parent", endHeader.Number)
|
||||
}
|
||||
} else {
|
||||
endBlock = api.eth.blockchain.GetBlockByHash(*endHash)
|
||||
if endBlock == nil {
|
||||
endHeader = api.eth.blockchain.GetHeaderByHash(*endHash)
|
||||
if endHeader == nil {
|
||||
return nil, fmt.Errorf("end block %x not found", *endHash)
|
||||
}
|
||||
}
|
||||
return api.getModifiedAccounts(startBlock, endBlock)
|
||||
return api.getModifiedAccounts(startHeader, endHeader)
|
||||
}
|
||||
|
||||
func (api *DebugAPI) getModifiedAccounts(startBlock, endBlock *types.Block) ([]common.Address, error) {
|
||||
if startBlock.Number().Uint64() >= endBlock.Number().Uint64() {
|
||||
return nil, fmt.Errorf("start block height (%d) must be less than end block height (%d)", startBlock.Number().Uint64(), endBlock.Number().Uint64())
|
||||
func (api *DebugAPI) getModifiedAccounts(startHeader, endHeader *types.Header) ([]common.Address, error) {
|
||||
if startHeader.Number.Uint64() >= endHeader.Number.Uint64() {
|
||||
return nil, fmt.Errorf("start block height (%d) must be less than end block height (%d)", startHeader.Number.Uint64(), endHeader.Number.Uint64())
|
||||
}
|
||||
triedb := api.eth.BlockChain().TrieDB()
|
||||
|
||||
oldTrie, err := trie.NewStateTrie(trie.StateTrieID(startBlock.Root()), triedb)
|
||||
oldTrie, err := trie.NewStateTrie(trie.StateTrieID(startHeader.Root), triedb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newTrie, err := trie.NewStateTrie(trie.StateTrieID(endBlock.Root()), triedb)
|
||||
newTrie, err := trie.NewStateTrie(trie.StateTrieID(endHeader.Root), triedb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,25 +18,74 @@ package eth
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
"github.com/holiman/uint256"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var dumper = spew.ConfigState{Indent: " "}
|
||||
|
||||
type Account struct {
|
||||
key *ecdsa.PrivateKey
|
||||
addr common.Address
|
||||
}
|
||||
|
||||
func newAccounts(n int) (accounts []Account) {
|
||||
for i := 0; i < n; i++ {
|
||||
key, _ := crypto.GenerateKey()
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
accounts = append(accounts, Account{key: key, addr: addr})
|
||||
}
|
||||
slices.SortFunc(accounts, func(a, b Account) int { return a.addr.Cmp(b.addr) })
|
||||
return accounts
|
||||
}
|
||||
|
||||
// newTestBlockChain creates a new test blockchain. OBS: After test is done, teardown must be
|
||||
// invoked in order to release associated resources.
|
||||
func newTestBlockChain(t *testing.T, n int, gspec *core.Genesis, generator func(i int, b *core.BlockGen)) *core.BlockChain {
|
||||
engine := ethash.NewFaker()
|
||||
// Generate blocks for testing
|
||||
_, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, n, generator)
|
||||
|
||||
// Import the canonical chain
|
||||
cacheConfig := &core.CacheConfig{
|
||||
TrieCleanLimit: 256,
|
||||
TrieDirtyLimit: 256,
|
||||
TrieTimeLimit: 5 * time.Minute,
|
||||
SnapshotLimit: 0,
|
||||
Preimages: true,
|
||||
TrieDirtyDisabled: true, // Archive mode
|
||||
}
|
||||
chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), cacheConfig, gspec, nil, engine, vm.Config{}, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
if n, err := chain.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||
}
|
||||
return chain
|
||||
}
|
||||
|
||||
func accountRangeTest(t *testing.T, trie *state.Trie, statedb *state.StateDB, start common.Hash, requestedNum int, expectedNum int) state.Dump {
|
||||
result := statedb.RawDump(&state.DumpConfig{
|
||||
SkipCode: true,
|
||||
|
|
@ -224,3 +273,66 @@ func TestStorageRangeAt(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetModifiedAccounts(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Initialize test accounts
|
||||
accounts := newAccounts(4)
|
||||
genesis := &core.Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: types.GenesisAlloc{
|
||||
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
|
||||
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
|
||||
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
|
||||
accounts[3].addr: {Balance: big.NewInt(params.Ether)},
|
||||
},
|
||||
}
|
||||
genBlocks := 1
|
||||
signer := types.HomesteadSigner{}
|
||||
blockChain := newTestBlockChain(t, genBlocks, genesis, func(_ int, b *core.BlockGen) {
|
||||
// Transfer from account[0] to account[1]
|
||||
// value: 1000 wei
|
||||
// fee: 0 wei
|
||||
for _, account := range accounts[:3] {
|
||||
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
|
||||
Nonce: 0,
|
||||
To: &accounts[3].addr,
|
||||
Value: big.NewInt(1000),
|
||||
Gas: params.TxGas,
|
||||
GasPrice: b.BaseFee(),
|
||||
Data: nil}),
|
||||
signer, account.key)
|
||||
b.AddTx(tx)
|
||||
}
|
||||
})
|
||||
defer blockChain.Stop()
|
||||
|
||||
// Create a debug API instance.
|
||||
api := NewDebugAPI(&Ethereum{blockchain: blockChain})
|
||||
|
||||
// Test GetModifiedAccountsByNumber
|
||||
t.Run("GetModifiedAccountsByNumber", func(t *testing.T) {
|
||||
addrs, err := api.GetModifiedAccountsByNumber(uint64(genBlocks), nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, addrs, len(accounts)+1) // +1 for the coinbase
|
||||
for _, account := range accounts {
|
||||
if !slices.Contains(addrs, account.addr) {
|
||||
t.Fatalf("account %s not found in modified accounts", account.addr.Hex())
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Test GetModifiedAccountsByHash
|
||||
t.Run("GetModifiedAccountsByHash", func(t *testing.T) {
|
||||
header := blockChain.GetHeaderByNumber(uint64(genBlocks))
|
||||
addrs, err := api.GetModifiedAccountsByHash(header.Hash(), nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, addrs, len(accounts)+1) // +1 for the coinbase
|
||||
for _, account := range accounts {
|
||||
if !slices.Contains(addrs, account.addr) {
|
||||
t.Fatalf("account %s not found in modified accounts", account.addr.Hex())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/miner"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/params/forks"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
|
|
@ -196,11 +197,11 @@ func newConsensusAPIWithoutHeartbeat(eth *eth.Ethereum) *ConsensusAPI {
|
|||
// and return its payloadID.
|
||||
func (api *ConsensusAPI) ForkchoiceUpdatedV1(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||
if payloadAttributes != nil {
|
||||
if payloadAttributes.Withdrawals != nil || payloadAttributes.BeaconRoot != nil {
|
||||
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("withdrawals and beacon root not supported in V1"))
|
||||
}
|
||||
if api.eth.BlockChain().Config().IsShanghai(api.eth.BlockChain().Config().LondonBlock, payloadAttributes.Timestamp) {
|
||||
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("forkChoiceUpdateV1 called post-shanghai"))
|
||||
switch {
|
||||
case payloadAttributes.Withdrawals != nil || payloadAttributes.BeaconRoot != nil:
|
||||
return engine.STATUS_INVALID, paramsErr("withdrawals and beacon root not supported in V1")
|
||||
case !api.checkFork(payloadAttributes.Timestamp, forks.Paris, forks.Shanghai):
|
||||
return engine.STATUS_INVALID, paramsErr("fcuV1 called post-shanghai")
|
||||
}
|
||||
}
|
||||
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.
|
||||
func (api *ConsensusAPI) ForkchoiceUpdatedV2(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||
if params != nil {
|
||||
if params.BeaconRoot != nil {
|
||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("unexpected beacon root"))
|
||||
}
|
||||
switch api.eth.BlockChain().Config().LatestFork(params.Timestamp) {
|
||||
case forks.Paris:
|
||||
if params.Withdrawals != nil {
|
||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("withdrawals before shanghai"))
|
||||
}
|
||||
case forks.Shanghai:
|
||||
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"))
|
||||
switch {
|
||||
case params.BeaconRoot != nil:
|
||||
return engine.STATUS_INVALID, attributesErr("unexpected beacon root")
|
||||
case api.checkFork(params.Timestamp, forks.Paris) && params.Withdrawals != nil:
|
||||
return engine.STATUS_INVALID, attributesErr("withdrawals before shanghai")
|
||||
case api.checkFork(params.Timestamp, forks.Shanghai) && params.Withdrawals == nil:
|
||||
return engine.STATUS_INVALID, attributesErr("missing withdrawals")
|
||||
case !api.checkFork(params.Timestamp, forks.Paris, forks.Shanghai):
|
||||
return engine.STATUS_INVALID, unsupportedForkErr("fcuV2 must only be called with paris or shanghai payloads")
|
||||
}
|
||||
}
|
||||
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.
|
||||
func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||
if params != nil {
|
||||
if params.Withdrawals == nil {
|
||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing withdrawals"))
|
||||
}
|
||||
if params.BeaconRoot == nil {
|
||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing beacon root"))
|
||||
}
|
||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun && api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague {
|
||||
return engine.STATUS_INVALID, engine.UnsupportedFork.With(errors.New("forkchoiceUpdatedV3 must only be called for cancun payloads"))
|
||||
switch {
|
||||
case params.Withdrawals == nil:
|
||||
return engine.STATUS_INVALID, attributesErr("missing withdrawals")
|
||||
case params.BeaconRoot == nil:
|
||||
return engine.STATUS_INVALID, attributesErr("missing beacon root")
|
||||
case !api.checkFork(params.Timestamp, forks.Cancun, forks.Prague):
|
||||
return engine.STATUS_INVALID, unsupportedForkErr("fcuV3 must only be called for cancun or prague payloads")
|
||||
}
|
||||
}
|
||||
// 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.
|
||||
func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV1(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||
if payloadAttributes != nil {
|
||||
if payloadAttributes.Withdrawals != nil || payloadAttributes.BeaconRoot != nil {
|
||||
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("withdrawals and beacon root not supported in V1"))
|
||||
}
|
||||
if api.eth.BlockChain().Config().IsShanghai(api.eth.BlockChain().Config().LondonBlock, payloadAttributes.Timestamp) {
|
||||
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("forkChoiceUpdateV1 called post-shanghai"))
|
||||
switch {
|
||||
case payloadAttributes.Withdrawals != nil || payloadAttributes.BeaconRoot != nil:
|
||||
return engine.STATUS_INVALID, paramsErr("withdrawals and beacon root not supported in V1")
|
||||
case !api.checkFork(payloadAttributes.Timestamp, forks.Paris, forks.Shanghai):
|
||||
return engine.STATUS_INVALID, paramsErr("fcuV1 called post-shanghai")
|
||||
}
|
||||
}
|
||||
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.
|
||||
func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV2(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||
if params != nil {
|
||||
if params.BeaconRoot != nil {
|
||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("unexpected beacon root"))
|
||||
}
|
||||
switch api.eth.BlockChain().Config().LatestFork(params.Timestamp) {
|
||||
case forks.Paris:
|
||||
if params.Withdrawals != nil {
|
||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("withdrawals before shanghai"))
|
||||
}
|
||||
case forks.Shanghai:
|
||||
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"))
|
||||
switch {
|
||||
case params.BeaconRoot != nil:
|
||||
return engine.STATUS_INVALID, attributesErr("unexpected beacon root")
|
||||
case api.checkFork(params.Timestamp, forks.Paris) && params.Withdrawals != nil:
|
||||
return engine.STATUS_INVALID, attributesErr("withdrawals before shanghai")
|
||||
case api.checkFork(params.Timestamp, forks.Shanghai) && params.Withdrawals == nil:
|
||||
return engine.STATUS_INVALID, attributesErr("missing withdrawals")
|
||||
case !api.checkFork(params.Timestamp, forks.Paris, forks.Shanghai):
|
||||
return engine.STATUS_INVALID, unsupportedForkErr("fcuV2 must only be called with paris or shanghai payloads")
|
||||
}
|
||||
}
|
||||
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.
|
||||
func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV3(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||
if params != nil {
|
||||
if params.Withdrawals == nil {
|
||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing withdrawals"))
|
||||
}
|
||||
if params.BeaconRoot == nil {
|
||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing beacon root"))
|
||||
}
|
||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun && api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague {
|
||||
return engine.STATUS_INVALID, engine.UnsupportedFork.With(errors.New("forkchoiceUpdatedV3 must only be called for cancun payloads"))
|
||||
switch {
|
||||
case params.Withdrawals == nil:
|
||||
return engine.STATUS_INVALID, attributesErr("missing withdrawals")
|
||||
case params.BeaconRoot == nil:
|
||||
return engine.STATUS_INVALID, attributesErr("missing beacon root")
|
||||
case !api.checkFork(params.Timestamp, forks.Cancun, forks.Prague):
|
||||
return engine.STATUS_INVALID, unsupportedForkErr("fcuV3 must only be called for cancun or prague payloads")
|
||||
}
|
||||
}
|
||||
// TODO(matt): the spec requires that fcu is applied when called on a valid
|
||||
|
|
@ -373,8 +362,11 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
|
|||
}
|
||||
valid := func(id *engine.PayloadID) engine.ForkChoiceResponse {
|
||||
return engine.ForkChoiceResponse{
|
||||
PayloadStatus: engine.PayloadStatusV1{Status: engine.VALID, LatestValidHash: &update.HeadBlockHash},
|
||||
PayloadID: id,
|
||||
PayloadStatus: engine.PayloadStatusV1{
|
||||
Status: engine.VALID,
|
||||
LatestValidHash: &update.HeadBlockHash,
|
||||
},
|
||||
PayloadID: id,
|
||||
}
|
||||
}
|
||||
if rawdb.ReadCanonicalHash(api.eth.ChainDb(), block.NumberU64()) != update.HeadBlockHash {
|
||||
|
|
@ -465,7 +457,7 @@ func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config engine.Transit
|
|||
api.lastTransitionUpdate = time.Now()
|
||||
api.lastTransitionLock.Unlock()
|
||||
|
||||
ttd := api.eth.BlockChain().Config().TerminalTotalDifficulty
|
||||
ttd := api.config().TerminalTotalDifficulty
|
||||
if ttd == nil || ttd.Cmp(config.TerminalTotalDifficulty.ToInt()) != 0 {
|
||||
log.Warn("Invalid TTD configured", "geth", ttd, "beacon", config.TerminalTotalDifficulty)
|
||||
return nil, fmt.Errorf("invalid ttd: execution %v consensus %v", ttd, config.TerminalTotalDifficulty)
|
||||
|
|
@ -547,86 +539,74 @@ func (api *ConsensusAPI) GetBlobsV1(hashes []common.Hash) ([]*engine.BlobAndProo
|
|||
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.
|
||||
func (api *ConsensusAPI) NewPayloadV1(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
|
||||
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)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if api.eth.BlockChain().Config().IsCancun(api.eth.BlockChain().Config().LondonBlock, params.Timestamp) {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("can't use newPayloadV2 post-cancun"))
|
||||
}
|
||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) == forks.Shanghai {
|
||||
if params.Withdrawals == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
||||
}
|
||||
} else {
|
||||
if params.Withdrawals != nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil withdrawals pre-shanghai"))
|
||||
}
|
||||
}
|
||||
if params.ExcessBlobGas != nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil excessBlobGas pre-cancun"))
|
||||
}
|
||||
if params.BlobGasUsed != nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil blobGasUsed pre-cancun"))
|
||||
var (
|
||||
cancun = api.config().IsCancun(api.config().LondonBlock, params.Timestamp)
|
||||
shanghai = api.config().IsShanghai(api.config().LondonBlock, params.Timestamp)
|
||||
)
|
||||
switch {
|
||||
case cancun:
|
||||
return invalidStatus, paramsErr("can't use newPayloadV2 post-cancun")
|
||||
case shanghai && params.Withdrawals == nil:
|
||||
return invalidStatus, paramsErr("nil withdrawals post-shanghai")
|
||||
case !shanghai && params.Withdrawals != nil:
|
||||
return invalidStatus, paramsErr("non-nil withdrawals pre-shanghai")
|
||||
case params.ExcessBlobGas != nil:
|
||||
return invalidStatus, paramsErr("non-nil excessBlobGas pre-cancun")
|
||||
case params.BlobGasUsed != nil:
|
||||
return invalidStatus, paramsErr("non-nil blobGasUsed pre-cancun")
|
||||
}
|
||||
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.
|
||||
func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) {
|
||||
if params.Withdrawals == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
||||
}
|
||||
if params.ExcessBlobGas == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
||||
}
|
||||
if params.BlobGasUsed == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun"))
|
||||
}
|
||||
|
||||
if versionedHashes == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
||||
}
|
||||
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"))
|
||||
switch {
|
||||
case params.Withdrawals == nil:
|
||||
return invalidStatus, paramsErr("nil withdrawals post-shanghai")
|
||||
case params.ExcessBlobGas == nil:
|
||||
return invalidStatus, paramsErr("nil excessBlobGas post-cancun")
|
||||
case params.BlobGasUsed == nil:
|
||||
return invalidStatus, paramsErr("nil blobGasUsed post-cancun")
|
||||
case versionedHashes == nil:
|
||||
return invalidStatus, paramsErr("nil versionedHashes post-cancun")
|
||||
case beaconRoot == nil:
|
||||
return invalidStatus, paramsErr("nil beaconRoot post-cancun")
|
||||
case !api.checkFork(params.Timestamp, forks.Cancun):
|
||||
return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
|
||||
}
|
||||
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.
|
||||
func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) {
|
||||
if params.Withdrawals == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
||||
}
|
||||
if params.ExcessBlobGas == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
||||
}
|
||||
if params.BlobGasUsed == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun"))
|
||||
}
|
||||
|
||||
if versionedHashes == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
||||
}
|
||||
if beaconRoot == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun"))
|
||||
}
|
||||
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"))
|
||||
switch {
|
||||
case params.Withdrawals == nil:
|
||||
return invalidStatus, paramsErr("nil withdrawals post-shanghai")
|
||||
case params.ExcessBlobGas == nil:
|
||||
return invalidStatus, paramsErr("nil excessBlobGas post-cancun")
|
||||
case params.BlobGasUsed == nil:
|
||||
return invalidStatus, paramsErr("nil blobGasUsed post-cancun")
|
||||
case versionedHashes == nil:
|
||||
return invalidStatus, paramsErr("nil versionedHashes post-cancun")
|
||||
case beaconRoot == nil:
|
||||
return invalidStatus, paramsErr("nil beaconRoot post-cancun")
|
||||
case executionRequests == nil:
|
||||
return invalidStatus, paramsErr("nil executionRequests post-prague")
|
||||
case !api.checkFork(params.Timestamp, forks.Prague):
|
||||
return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
|
||||
}
|
||||
requests := convertRequests(executionRequests)
|
||||
if err := validateRequests(requests); err != nil {
|
||||
|
|
@ -647,23 +627,21 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV1(params engine.ExecutableData) (
|
|||
// NewPayloadWithWitnessV2 is analogous to NewPayloadV2, only it also generates
|
||||
// and returns a stateless witness after running the payload.
|
||||
func (api *ConsensusAPI) NewPayloadWithWitnessV2(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
|
||||
if api.eth.BlockChain().Config().IsCancun(api.eth.BlockChain().Config().LondonBlock, params.Timestamp) {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("can't use newPayloadV2 post-cancun"))
|
||||
}
|
||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) == forks.Shanghai {
|
||||
if params.Withdrawals == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
||||
}
|
||||
} else {
|
||||
if params.Withdrawals != nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil withdrawals pre-shanghai"))
|
||||
}
|
||||
}
|
||||
if params.ExcessBlobGas != nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil excessBlobGas pre-cancun"))
|
||||
}
|
||||
if params.BlobGasUsed != nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil blobGasUsed pre-cancun"))
|
||||
var (
|
||||
cancun = api.config().IsCancun(api.config().LondonBlock, params.Timestamp)
|
||||
shanghai = api.config().IsShanghai(api.config().LondonBlock, params.Timestamp)
|
||||
)
|
||||
switch {
|
||||
case cancun:
|
||||
return invalidStatus, paramsErr("can't use newPayloadV2 post-cancun")
|
||||
case shanghai && params.Withdrawals == nil:
|
||||
return invalidStatus, paramsErr("nil withdrawals post-shanghai")
|
||||
case !shanghai && params.Withdrawals != nil:
|
||||
return invalidStatus, paramsErr("non-nil withdrawals pre-shanghai")
|
||||
case params.ExcessBlobGas != nil:
|
||||
return invalidStatus, paramsErr("non-nil excessBlobGas pre-cancun")
|
||||
case params.BlobGasUsed != nil:
|
||||
return invalidStatus, paramsErr("non-nil blobGasUsed pre-cancun")
|
||||
}
|
||||
return api.newPayload(params, nil, nil, nil, true)
|
||||
}
|
||||
|
|
@ -671,25 +649,19 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV2(params engine.ExecutableData) (
|
|||
// NewPayloadWithWitnessV3 is analogous to NewPayloadV3, only it also generates
|
||||
// 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) {
|
||||
if params.Withdrawals == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
||||
}
|
||||
if params.ExcessBlobGas == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
||||
}
|
||||
if params.BlobGasUsed == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun"))
|
||||
}
|
||||
|
||||
if versionedHashes == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
||||
}
|
||||
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"))
|
||||
switch {
|
||||
case params.Withdrawals == nil:
|
||||
return invalidStatus, paramsErr("nil withdrawals post-shanghai")
|
||||
case params.ExcessBlobGas == nil:
|
||||
return invalidStatus, paramsErr("nil excessBlobGas post-cancun")
|
||||
case params.BlobGasUsed == nil:
|
||||
return invalidStatus, paramsErr("nil blobGasUsed post-cancun")
|
||||
case versionedHashes == nil:
|
||||
return invalidStatus, paramsErr("nil versionedHashes post-cancun")
|
||||
case beaconRoot == nil:
|
||||
return invalidStatus, paramsErr("nil beaconRoot post-cancun")
|
||||
case !api.checkFork(params.Timestamp, forks.Cancun):
|
||||
return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
|
||||
}
|
||||
return api.newPayload(params, versionedHashes, beaconRoot, nil, true)
|
||||
}
|
||||
|
|
@ -697,28 +669,21 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV3(params engine.ExecutableData, v
|
|||
// NewPayloadWithWitnessV4 is analogous to NewPayloadV4, only it also generates
|
||||
// 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) {
|
||||
if params.Withdrawals == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
||||
}
|
||||
if params.ExcessBlobGas == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
||||
}
|
||||
if params.BlobGasUsed == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun"))
|
||||
}
|
||||
|
||||
if versionedHashes == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
||||
}
|
||||
if beaconRoot == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun"))
|
||||
}
|
||||
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"))
|
||||
switch {
|
||||
case params.Withdrawals == nil:
|
||||
return invalidStatus, paramsErr("nil withdrawals post-shanghai")
|
||||
case params.ExcessBlobGas == nil:
|
||||
return invalidStatus, paramsErr("nil excessBlobGas post-cancun")
|
||||
case params.BlobGasUsed == nil:
|
||||
return invalidStatus, paramsErr("nil blobGasUsed post-cancun")
|
||||
case versionedHashes == nil:
|
||||
return invalidStatus, paramsErr("nil versionedHashes post-cancun")
|
||||
case beaconRoot == nil:
|
||||
return invalidStatus, paramsErr("nil beaconRoot post-cancun")
|
||||
case executionRequests == nil:
|
||||
return invalidStatus, paramsErr("nil executionRequests post-prague")
|
||||
case !api.checkFork(params.Timestamp, forks.Prague):
|
||||
return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
|
||||
}
|
||||
requests := convertRequests(executionRequests)
|
||||
if err := validateRequests(requests); err != nil {
|
||||
|
|
@ -739,23 +704,21 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV1(params engine.ExecutableData,
|
|||
// ExecuteStatelessPayloadV2 is analogous to NewPayloadV2, only it operates in
|
||||
// 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) {
|
||||
if api.eth.BlockChain().Config().IsCancun(api.eth.BlockChain().Config().LondonBlock, params.Timestamp) {
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("can't use newPayloadV2 post-cancun"))
|
||||
}
|
||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) == forks.Shanghai {
|
||||
if params.Withdrawals == nil {
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
||||
}
|
||||
} else {
|
||||
if params.Withdrawals != nil {
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil withdrawals pre-shanghai"))
|
||||
}
|
||||
}
|
||||
if params.ExcessBlobGas != nil {
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil excessBlobGas pre-cancun"))
|
||||
}
|
||||
if params.BlobGasUsed != nil {
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil blobGasUsed pre-cancun"))
|
||||
var (
|
||||
cancun = api.config().IsCancun(api.config().LondonBlock, params.Timestamp)
|
||||
shanghai = api.config().IsShanghai(api.config().LondonBlock, params.Timestamp)
|
||||
)
|
||||
switch {
|
||||
case cancun:
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("can't use newPayloadV2 post-cancun")
|
||||
case shanghai && params.Withdrawals == nil:
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil withdrawals post-shanghai")
|
||||
case !shanghai && params.Withdrawals != nil:
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("non-nil withdrawals pre-shanghai")
|
||||
case params.ExcessBlobGas != nil:
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("non-nil excessBlobGas pre-cancun")
|
||||
case params.BlobGasUsed != nil:
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("non-nil blobGasUsed pre-cancun")
|
||||
}
|
||||
return api.executeStatelessPayload(params, nil, nil, nil, opaqueWitness)
|
||||
}
|
||||
|
|
@ -763,25 +726,19 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV2(params engine.ExecutableData,
|
|||
// ExecuteStatelessPayloadV3 is analogous to NewPayloadV3, only it operates in
|
||||
// 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) {
|
||||
if params.Withdrawals == nil {
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
||||
}
|
||||
if params.ExcessBlobGas == nil {
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
||||
}
|
||||
if params.BlobGasUsed == nil {
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun"))
|
||||
}
|
||||
|
||||
if versionedHashes == nil {
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
||||
}
|
||||
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"))
|
||||
switch {
|
||||
case params.Withdrawals == nil:
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil withdrawals post-shanghai")
|
||||
case params.ExcessBlobGas == nil:
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil excessBlobGas post-cancun")
|
||||
case params.BlobGasUsed == nil:
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil blobGasUsed post-cancun")
|
||||
case versionedHashes == nil:
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil versionedHashes post-cancun")
|
||||
case beaconRoot == nil:
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil beaconRoot post-cancun")
|
||||
case !api.checkFork(params.Timestamp, forks.Cancun):
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
|
||||
}
|
||||
return api.executeStatelessPayload(params, versionedHashes, beaconRoot, nil, opaqueWitness)
|
||||
}
|
||||
|
|
@ -789,30 +746,26 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV3(params engine.ExecutableData,
|
|||
// ExecuteStatelessPayloadV4 is analogous to NewPayloadV4, only it operates in
|
||||
// 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) {
|
||||
if params.Withdrawals == nil {
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
||||
}
|
||||
if params.ExcessBlobGas == nil {
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
||||
}
|
||||
if params.BlobGasUsed == nil {
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun"))
|
||||
}
|
||||
|
||||
if versionedHashes == nil {
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
||||
}
|
||||
if beaconRoot == nil {
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun"))
|
||||
}
|
||||
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"))
|
||||
switch {
|
||||
case params.Withdrawals == nil:
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil withdrawals post-shanghai")
|
||||
case params.ExcessBlobGas == nil:
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil excessBlobGas post-cancun")
|
||||
case params.BlobGasUsed == nil:
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil blobGasUsed post-cancun")
|
||||
case versionedHashes == nil:
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil versionedHashes post-cancun")
|
||||
case beaconRoot == nil:
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil beaconRoot post-cancun")
|
||||
case executionRequests == nil:
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil executionRequests post-prague")
|
||||
case !api.checkFork(params.Timestamp, forks.Prague):
|
||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
@ -978,7 +931,7 @@ func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, v
|
|||
api.lastNewPayloadLock.Unlock()
|
||||
|
||||
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 {
|
||||
log.Warn("ExecuteStatelessPayload: execution failed", "err", err)
|
||||
errorMsg := err.Error()
|
||||
|
|
@ -1116,7 +1069,7 @@ func (api *ConsensusAPI) heartbeat() {
|
|||
time.Sleep(beaconUpdateStartupTimeout)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
@ -1160,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.
|
||||
func (api *ConsensusAPI) ExchangeCapabilities([]string) []string {
|
||||
return caps
|
||||
|
|
@ -1285,3 +1255,21 @@ func validateRequests(requests [][]byte) error {
|
|||
}
|
||||
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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
)
|
||||
|
||||
|
|
@ -202,7 +203,7 @@ type BlockChain interface {
|
|||
// into the local chain. Blocks older than the specified `ancientLimit`
|
||||
// are stored directly in the ancient store, while newer blocks are stored
|
||||
// in the live key-value store.
|
||||
InsertReceiptChain(types.Blocks, []types.Receipts, uint64) (int, error)
|
||||
InsertReceiptChain(types.Blocks, []rlp.RawValue, uint64) (int, error)
|
||||
|
||||
// Snapshots returns the blockchain snapshot tree to paused it during sync.
|
||||
Snapshots() *snapshot.Tree
|
||||
|
|
@ -1034,7 +1035,7 @@ func (d *Downloader) commitSnapSyncData(results []*fetchResult, stateSync *state
|
|||
"lastnumn", last.Number, "lasthash", last.Hash(),
|
||||
)
|
||||
blocks := make([]*types.Block, len(results))
|
||||
receipts := make([]types.Receipts, len(results))
|
||||
receipts := make([]rlp.RawValue, len(results))
|
||||
for i, result := range results {
|
||||
blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.body())
|
||||
receipts[i] = result.Receipts
|
||||
|
|
@ -1051,7 +1052,7 @@ func (d *Downloader) commitPivotBlock(result *fetchResult) error {
|
|||
log.Debug("Committing snap sync pivot as new head", "number", block.Number(), "hash", block.Hash())
|
||||
|
||||
// Commit the pivot block as the new head, will require full sync from here on
|
||||
if _, err := d.blockchain.InsertReceiptChain([]*types.Block{block}, []types.Receipts{result.Receipts}, d.ancientLimit); err != nil {
|
||||
if _, err := d.blockchain.InsertReceiptChain([]*types.Block{block}, []rlp.RawValue{result.Receipts}, d.ancientLimit); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.blockchain.SnapSyncCommitHead(block.Hash()); err != nil {
|
||||
|
|
|
|||
|
|
@ -255,23 +255,24 @@ func (dlp *downloadTesterPeer) RequestBodies(hashes []common.Hash, sink chan *et
|
|||
// peer in the download tester. The returned function can be used to retrieve
|
||||
// batches of block receipts from the particularly requested peer.
|
||||
func (dlp *downloadTesterPeer) RequestReceipts(hashes []common.Hash, sink chan *eth.Response) (*eth.Request, error) {
|
||||
blobs := eth.ServiceGetReceiptsQuery(dlp.chain, hashes)
|
||||
blobs := eth.ServiceGetReceiptsQuery68(dlp.chain, hashes)
|
||||
|
||||
receipts := make([][]*types.Receipt, len(blobs))
|
||||
receipts := make([]types.Receipts, len(blobs))
|
||||
for i, blob := range blobs {
|
||||
rlp.DecodeBytes(blob, &receipts[i])
|
||||
}
|
||||
hasher := trie.NewStackTrie(nil)
|
||||
hashes = make([]common.Hash, len(receipts))
|
||||
for i, receipt := range receipts {
|
||||
hashes[i] = types.DeriveSha(types.Receipts(receipt), hasher)
|
||||
hashes[i] = types.DeriveSha(receipt, hasher)
|
||||
}
|
||||
req := ð.Request{
|
||||
Peer: dlp.id,
|
||||
}
|
||||
resp := eth.ReceiptsRLPResponse(types.EncodeBlockReceiptLists(receipts))
|
||||
res := ð.Response{
|
||||
Req: req,
|
||||
Res: (*eth.ReceiptsResponse)(&receipts),
|
||||
Res: &resp,
|
||||
Meta: hashes,
|
||||
Time: 1,
|
||||
Done: make(chan error, 1), // Ignore the returned status
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ func (q *receiptQueue) request(peer *peerConnection, req *fetchRequest, resCh ch
|
|||
// deliver is responsible for taking a generic response packet from the concurrent
|
||||
// fetcher, unpacking the receipt data and delivering it to the downloader's queue.
|
||||
func (q *receiptQueue) deliver(peer *peerConnection, packet *eth.Response) (int, error) {
|
||||
receipts := *packet.Res.(*eth.ReceiptsResponse)
|
||||
receipts := *packet.Res.(*eth.ReceiptsRLPResponse)
|
||||
hashes := packet.Meta.([]common.Hash) // {receipt hashes}
|
||||
|
||||
accepted, err := q.queue.DeliverReceipts(peer.id, receipts, hashes)
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue