mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-25 17:29:27 +00:00
Merge branch 'master' of https://github.com/0xjvn/go-ethereum into f/prune-txn-enable
This commit is contained in:
commit
26afa16777
187 changed files with 17344 additions and 10903 deletions
|
|
@ -438,14 +438,11 @@ func (s *serverWithLimits) fail(desc string) {
|
|||
// failLocked calculates the dynamic failure delay and applies it.
|
||||
func (s *serverWithLimits) failLocked(desc string) {
|
||||
log.Debug("Server error", "description", desc)
|
||||
s.failureDelay *= 2
|
||||
now := s.clock.Now()
|
||||
if now > s.failureDelayEnd {
|
||||
s.failureDelay *= math.Pow(2, -float64(now-s.failureDelayEnd)/float64(maxFailureDelay))
|
||||
}
|
||||
if s.failureDelay < float64(minFailureDelay) {
|
||||
s.failureDelay = float64(minFailureDelay)
|
||||
}
|
||||
s.failureDelay = max(min(s.failureDelay*2, float64(maxFailureDelay)), float64(minFailureDelay))
|
||||
s.failureDelayEnd = now + mclock.AbsTime(s.failureDelay)
|
||||
s.delay(time.Duration(s.failureDelay))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@ const (
|
|||
ssNeedParent // cp header slot %32 != 0, need parent to check epoch boundary
|
||||
ssParentRequested // cp parent header requested
|
||||
ssPrintStatus // has all necessary info, print log message if init still not successful
|
||||
ssDone // log message printed, no more action required
|
||||
)
|
||||
|
||||
type serverState struct {
|
||||
|
|
@ -180,7 +179,8 @@ func (s *CheckpointInit) Process(requester request.Requester, events []request.E
|
|||
default:
|
||||
log.Error("blsync: checkpoint not available, but reported as finalized; specified checkpoint hash might be too old", "server", server.Name())
|
||||
}
|
||||
s.serverState[server] = serverState{state: ssDone}
|
||||
s.serverState[server] = serverState{state: ssDefault}
|
||||
requester.Fail(server, "checkpoint init failed")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
20
build/ci.go
20
build/ci.go
|
|
@ -107,17 +107,21 @@ var (
|
|||
Tags: "ziren",
|
||||
Env: map[string]string{"GOMIPS": "softfloat", "CGO_ENABLED": "0"},
|
||||
},
|
||||
{
|
||||
Name: "womir",
|
||||
GOOS: "wasip1",
|
||||
GOARCH: "wasm",
|
||||
Tags: "womir",
|
||||
},
|
||||
{
|
||||
Name: "wasm-js",
|
||||
GOOS: "js",
|
||||
GOARCH: "wasm",
|
||||
Tags: "example",
|
||||
},
|
||||
{
|
||||
Name: "wasm-wasi",
|
||||
GOOS: "wasip1",
|
||||
GOARCH: "wasm",
|
||||
Tags: "example",
|
||||
},
|
||||
{
|
||||
Name: "example",
|
||||
|
|
@ -163,11 +167,11 @@ var (
|
|||
|
||||
// Distros for which packages are created
|
||||
debDistros = []string{
|
||||
"xenial", // 16.04, EOL: 04/2026
|
||||
"bionic", // 18.04, EOL: 04/2028
|
||||
"focal", // 20.04, EOL: 04/2030
|
||||
"jammy", // 22.04, EOL: 04/2032
|
||||
"noble", // 24.04, EOL: 04/2034
|
||||
"xenial", // 16.04, EOL: 04/2026
|
||||
"bionic", // 18.04, EOL: 04/2028
|
||||
"focal", // 20.04, EOL: 04/2030
|
||||
"jammy", // 22.04, EOL: 04/2032
|
||||
"noble", // 24.04, EOL: 04/2034
|
||||
}
|
||||
|
||||
// This is where the tests should be unpacked.
|
||||
|
|
@ -305,7 +309,7 @@ func doInstallKeeper(cmdline []string) {
|
|||
args := slices.Clone(gobuild.Args)
|
||||
args = append(args, "-o", executablePath(outputName))
|
||||
args = append(args, ".")
|
||||
build.MustRun(&exec.Cmd{Path: gobuild.Path, Args: args, Env: gobuild.Env})
|
||||
build.MustRun(&exec.Cmd{Path: gobuild.Path, Args: args, Env: gobuild.Env, Dir: gobuild.Dir})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,12 @@ type Chain struct {
|
|||
state map[common.Address]state.DumpAccount // state of head block
|
||||
senders map[common.Address]*senderInfo
|
||||
config *params.ChainConfig
|
||||
|
||||
txInfo txInfo
|
||||
}
|
||||
|
||||
type txInfo struct {
|
||||
LargeReceiptBlock *uint64 `json:"tx-largereceipt"`
|
||||
}
|
||||
|
||||
// NewChain takes the given chain.rlp file, and decodes and returns
|
||||
|
|
@ -74,12 +80,20 @@ func NewChain(dir string) (*Chain, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var txInfo txInfo
|
||||
err = common.LoadJSON(filepath.Join(dir, "txinfo.json"), &txInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Chain{
|
||||
genesis: gen,
|
||||
blocks: blocks,
|
||||
state: state,
|
||||
senders: accounts,
|
||||
config: gen.Config,
|
||||
txInfo: txInfo,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,9 +66,10 @@ func (s *Suite) dialAs(key *ecdsa.PrivateKey) (*Conn, error) {
|
|||
return nil, err
|
||||
}
|
||||
conn.caps = []p2p.Cap{
|
||||
{Name: "eth", Version: 70},
|
||||
{Name: "eth", Version: 69},
|
||||
}
|
||||
conn.ourHighestProtoVersion = 69
|
||||
conn.ourHighestProtoVersion = 70
|
||||
return &conn, nil
|
||||
}
|
||||
|
||||
|
|
@ -335,10 +336,12 @@ loop:
|
|||
if have, want := msg.ForkID, chain.ForkID(); !reflect.DeepEqual(have, want) {
|
||||
return fmt.Errorf("wrong fork ID in status: have %v, want %v", have, want)
|
||||
}
|
||||
if have, want := msg.ProtocolVersion, c.ourHighestProtoVersion; have != uint32(want) {
|
||||
return fmt.Errorf("wrong protocol version: have %v, want %v", have, want)
|
||||
for _, cap := range c.caps {
|
||||
if cap.Name == "eth" && cap.Version == uint(msg.ProtocolVersion) {
|
||||
break loop
|
||||
}
|
||||
}
|
||||
break loop
|
||||
return fmt.Errorf("wrong protocol version: have %v, want %v", msg.ProtocolVersion, c.caps)
|
||||
case discMsg:
|
||||
var msg []p2p.DiscReason
|
||||
if rlp.DecodeBytes(data, &msg); len(msg) == 0 {
|
||||
|
|
|
|||
|
|
@ -87,9 +87,9 @@ func (s *Suite) TestSnapGetAccountRange(t *utesting.T) {
|
|||
root: root,
|
||||
startingHash: zero,
|
||||
limitHash: ffHash,
|
||||
expAccounts: 67,
|
||||
expAccounts: 68,
|
||||
expFirst: firstKey,
|
||||
expLast: common.HexToHash("0x622e662246601dd04f996289ce8b85e86db7bb15bb17f86487ec9d543ddb6f9a"),
|
||||
expLast: common.HexToHash("0x59312f89c13e9e24c1cb8b103aa39a9b2800348d97a92c2c9e2a78fa02b70025"),
|
||||
desc: "In this test, we request the entire state range, but limit the response to 4000 bytes.",
|
||||
},
|
||||
{
|
||||
|
|
@ -97,9 +97,9 @@ func (s *Suite) TestSnapGetAccountRange(t *utesting.T) {
|
|||
root: root,
|
||||
startingHash: zero,
|
||||
limitHash: ffHash,
|
||||
expAccounts: 49,
|
||||
expAccounts: 50,
|
||||
expFirst: firstKey,
|
||||
expLast: common.HexToHash("0x445cb5c1278fdce2f9cbdb681bdd76c52f8e50e41dbd9e220242a69ba99ac099"),
|
||||
expLast: common.HexToHash("0x4615e5f5df5b25349a00ad313c6cd0436b6c08ee5826e33a018661997f85ebaa"),
|
||||
desc: "In this test, we request the entire state range, but limit the response to 3000 bytes.",
|
||||
},
|
||||
{
|
||||
|
|
@ -107,9 +107,9 @@ func (s *Suite) TestSnapGetAccountRange(t *utesting.T) {
|
|||
root: root,
|
||||
startingHash: zero,
|
||||
limitHash: ffHash,
|
||||
expAccounts: 34,
|
||||
expAccounts: 35,
|
||||
expFirst: firstKey,
|
||||
expLast: common.HexToHash("0x2ef46ebd2073cecde499c2e8df028ad79a26d57bfaa812c4c6f7eb4c9617b913"),
|
||||
expLast: common.HexToHash("0x2de4bdbddcfbb9c3e195dae6b45f9c38daff897e926764bf34887fb0db5c3284"),
|
||||
desc: "In this test, we request the entire state range, but limit the response to 2000 bytes.",
|
||||
},
|
||||
{
|
||||
|
|
@ -178,9 +178,9 @@ The server should return the first available account.`,
|
|||
root: root,
|
||||
startingHash: firstKey,
|
||||
limitHash: ffHash,
|
||||
expAccounts: 67,
|
||||
expAccounts: 68,
|
||||
expFirst: firstKey,
|
||||
expLast: common.HexToHash("0x622e662246601dd04f996289ce8b85e86db7bb15bb17f86487ec9d543ddb6f9a"),
|
||||
expLast: common.HexToHash("0x59312f89c13e9e24c1cb8b103aa39a9b2800348d97a92c2c9e2a78fa02b70025"),
|
||||
desc: `In this test, startingHash is exactly the first available account key.
|
||||
The server should return the first available account of the state as the first item.`,
|
||||
},
|
||||
|
|
@ -189,9 +189,9 @@ The server should return the first available account of the state as the first i
|
|||
root: root,
|
||||
startingHash: hashAdd(firstKey, 1),
|
||||
limitHash: ffHash,
|
||||
expAccounts: 67,
|
||||
expAccounts: 68,
|
||||
expFirst: secondKey,
|
||||
expLast: common.HexToHash("0x66192e4c757fba1cdc776e6737008f42d50370d3cd801db3624274283bf7cd63"),
|
||||
expLast: common.HexToHash("0x59a7c8818f1c16b298a054020dc7c3f403a970d1d1db33f9478b1c36e3a2e509"),
|
||||
desc: `In this test, startingHash is after the first available key.
|
||||
The server should return the second account of the state as the first item.`,
|
||||
},
|
||||
|
|
@ -227,9 +227,9 @@ server to return no data because genesis is older than 127 blocks.`,
|
|||
root: s.chain.RootAt(int(s.chain.Head().Number().Uint64()) - 127),
|
||||
startingHash: zero,
|
||||
limitHash: ffHash,
|
||||
expAccounts: 66,
|
||||
expAccounts: 68,
|
||||
expFirst: firstKey,
|
||||
expLast: common.HexToHash("0x729953a43ed6c913df957172680a17e5735143ad767bda8f58ac84ec62fbec5e"),
|
||||
expLast: common.HexToHash("0x683b6c03cc32afe5db8cb96050f711fdaff8f8ff44c7587a9a848f921d02815e"),
|
||||
desc: `This test requests data at a state root that is 127 blocks old.
|
||||
We expect the server to have this state available.`,
|
||||
},
|
||||
|
|
@ -658,8 +658,8 @@ The server should reject the request.`,
|
|||
// It's a bit unfortunate these are hard-coded, but the result depends on
|
||||
// a lot of aspects of the state trie and can't be guessed in a simple
|
||||
// way. So you'll have to update this when the test chain is changed.
|
||||
common.HexToHash("0x5bdc0d6057b35642a16d27223ea5454e5a17a400e28f7328971a5f2a87773b76"),
|
||||
common.HexToHash("0x0a76c9812ca90ffed8ee4d191e683f93386b6e50cfe3679c0760d27510aa7fc5"),
|
||||
common.HexToHash("0x4bdecec09691ad38113eebee2df94fadefdff5841c0f182bae1be3c8a6d60bf3"),
|
||||
common.HexToHash("0x4178696465d4514ff5924ef8c28ce64d41a669634b63184c2c093e252d6b4bc4"),
|
||||
empty, empty, empty, empty, empty, empty, empty, empty, empty, empty, empty, empty,
|
||||
empty, empty, empty, empty, empty, empty, empty, empty, empty, empty, empty, empty,
|
||||
empty, empty, empty, empty, empty, empty, empty, empty, empty, empty, empty, empty,
|
||||
|
|
@ -679,8 +679,8 @@ The server should reject the request.`,
|
|||
// be updated when the test chain is changed.
|
||||
expHashes: []common.Hash{
|
||||
empty,
|
||||
common.HexToHash("0x0a76c9812ca90ffed8ee4d191e683f93386b6e50cfe3679c0760d27510aa7fc5"),
|
||||
common.HexToHash("0x5bdc0d6057b35642a16d27223ea5454e5a17a400e28f7328971a5f2a87773b76"),
|
||||
common.HexToHash("0x4178696465d4514ff5924ef8c28ce64d41a669634b63184c2c093e252d6b4bc4"),
|
||||
common.HexToHash("0x4bdecec09691ad38113eebee2df94fadefdff5841c0f182bae1be3c8a6d60bf3"),
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
|
|
@ -83,6 +84,7 @@ func (s *Suite) EthTests() []utesting.Test {
|
|||
// get history
|
||||
{Name: "GetBlockBodies", Fn: s.TestGetBlockBodies},
|
||||
{Name: "GetReceipts", Fn: s.TestGetReceipts},
|
||||
{Name: "GetLargeReceipts", Fn: s.TestGetLargeReceipts},
|
||||
// test transactions
|
||||
{Name: "LargeTxRequest", Fn: s.TestLargeTxRequest, Slow: true},
|
||||
{Name: "Transaction", Fn: s.TestTransaction},
|
||||
|
|
@ -429,6 +431,9 @@ func (s *Suite) TestGetReceipts(t *utesting.T) {
|
|||
// Find some blocks containing receipts.
|
||||
var hashes = make([]common.Hash, 0, 3)
|
||||
for i := range s.chain.Len() {
|
||||
if s.chain.txInfo.LargeReceiptBlock != nil && uint64(i) == *s.chain.txInfo.LargeReceiptBlock {
|
||||
continue
|
||||
}
|
||||
block := s.chain.GetBlock(i)
|
||||
if len(block.Transactions()) > 0 {
|
||||
hashes = append(hashes, block.Hash())
|
||||
|
|
@ -437,25 +442,121 @@ func (s *Suite) TestGetReceipts(t *utesting.T) {
|
|||
break
|
||||
}
|
||||
}
|
||||
if conn.negotiatedProtoVersion < eth.ETH70 {
|
||||
// Create block bodies request.
|
||||
req := ð.GetReceiptsPacket69{
|
||||
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.ReceiptsPacket69)
|
||||
if err := conn.ReadMsg(ethProto, eth.ReceiptsMsg, &resp); err != nil {
|
||||
t.Fatalf("error reading block receipts msg: %v", err)
|
||||
}
|
||||
if got, want := resp.RequestId, req.RequestId; got != want {
|
||||
t.Fatalf("unexpected request id in respond", got, want)
|
||||
}
|
||||
if resp.List.Len() != len(req.GetReceiptsRequest) {
|
||||
t.Fatalf("wrong receipts in response: expected %d receipts, got %d", len(req.GetReceiptsRequest), resp.List.Len())
|
||||
}
|
||||
} else {
|
||||
// Create block bodies request.
|
||||
req := ð.GetReceiptsPacket70{
|
||||
RequestId: 66,
|
||||
FirstBlockReceiptIndex: 0,
|
||||
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.ReceiptsPacket70)
|
||||
if err := conn.ReadMsg(ethProto, eth.ReceiptsMsg, &resp); err != nil {
|
||||
t.Fatalf("error reading block receipts msg: %v", err)
|
||||
}
|
||||
if got, want := resp.RequestId, req.RequestId; got != want {
|
||||
t.Fatalf("unexpected request id in respond", got, want)
|
||||
}
|
||||
if resp.List.Len() != len(req.GetReceiptsRequest) {
|
||||
t.Fatalf("wrong receipts in response: expected %d receipts, got %d", len(req.GetReceiptsRequest), resp.List.Len())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create receipts request.
|
||||
req := ð.GetReceiptsPacket{
|
||||
RequestId: 66,
|
||||
GetReceiptsRequest: (eth.GetReceiptsRequest)(hashes),
|
||||
func (s *Suite) TestGetLargeReceipts(t *utesting.T) {
|
||||
t.Log(`This test sends GetReceipts requests to the node for large receipt (>10MiB) in the test chain.
|
||||
This test is meaningful only if the client supports protocol version ETH70 or higher
|
||||
and LargeReceiptBlock is configured in txInfo.json.`)
|
||||
conn, err := s.dialAndPeer(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("peering failed: %v", err)
|
||||
}
|
||||
if err := conn.Write(ethProto, eth.GetReceiptsMsg, req); err != nil {
|
||||
t.Fatalf("could not write to connection: %v", err)
|
||||
defer conn.Close()
|
||||
|
||||
if conn.negotiatedProtoVersion < eth.ETH70 || s.chain.txInfo.LargeReceiptBlock == nil {
|
||||
return
|
||||
}
|
||||
// Wait for response.
|
||||
resp := new(eth.ReceiptsPacket)
|
||||
if err := conn.ReadMsg(ethProto, eth.ReceiptsMsg, &resp); err != nil {
|
||||
t.Fatalf("error reading block bodies msg: %v", err)
|
||||
|
||||
// Find block with large receipt.
|
||||
// Place the large receipt block hash in the middle of the query
|
||||
start := max(int(*s.chain.txInfo.LargeReceiptBlock)-2, 0)
|
||||
end := min(*s.chain.txInfo.LargeReceiptBlock+2, uint64(len(s.chain.blocks)))
|
||||
|
||||
var blocks []common.Hash
|
||||
var receiptHashes []common.Hash
|
||||
var receipts []*eth.ReceiptList
|
||||
|
||||
for i := uint64(start); i < end; i++ {
|
||||
block := s.chain.GetBlock(int(i))
|
||||
blocks = append(blocks, block.Hash())
|
||||
receiptHashes = append(receiptHashes, block.Header().ReceiptHash)
|
||||
receipts = append(receipts, ð.ReceiptList{})
|
||||
}
|
||||
if got, want := resp.RequestId, req.RequestId; got != want {
|
||||
t.Fatalf("unexpected request id in respond", got, want)
|
||||
|
||||
incomplete := false
|
||||
lastBlock := 0
|
||||
|
||||
for incomplete || lastBlock != len(blocks)-1 {
|
||||
// Create get receipt request.
|
||||
req := ð.GetReceiptsPacket70{
|
||||
RequestId: 66,
|
||||
FirstBlockReceiptIndex: uint64(receipts[lastBlock].Derivable().Len()),
|
||||
GetReceiptsRequest: blocks[lastBlock:],
|
||||
}
|
||||
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.ReceiptsPacket70)
|
||||
if err := conn.ReadMsg(ethProto, eth.ReceiptsMsg, &resp); err != nil {
|
||||
t.Fatalf("error reading block receipts msg: %v", err)
|
||||
}
|
||||
if got, want := resp.RequestId, req.RequestId; got != want {
|
||||
t.Fatalf("unexpected request id in respond, want: %d, got: %d", got, want)
|
||||
}
|
||||
|
||||
receiptLists, _ := resp.List.Items()
|
||||
for i, rc := range receiptLists {
|
||||
receipts[lastBlock+i].Append(rc)
|
||||
}
|
||||
lastBlock += len(receiptLists) - 1
|
||||
|
||||
incomplete = resp.LastBlockIncomplete
|
||||
}
|
||||
if resp.List.Len() != len(req.GetReceiptsRequest) {
|
||||
t.Fatalf("wrong receipts in response: expected %d receipts, got %d", len(req.GetReceiptsRequest), resp.List.Len())
|
||||
|
||||
hasher := trie.NewStackTrie(nil)
|
||||
hashes := make([]common.Hash, len(receipts))
|
||||
for i := range receipts {
|
||||
hashes[i] = types.DeriveSha(receipts[i].Derivable(), hasher)
|
||||
}
|
||||
|
||||
for i, hash := range hashes {
|
||||
if receiptHashes[i] != hash {
|
||||
t.Fatalf("wrong receipt root: want %x, got %x", receiptHashes[i], hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
BIN
cmd/devp2p/internal/ethtest/testdata/chain.rlp
vendored
BIN
cmd/devp2p/internal/ethtest/testdata/chain.rlp
vendored
Binary file not shown.
|
|
@ -37,7 +37,7 @@
|
|||
"nonce": "0x0",
|
||||
"timestamp": "0x0",
|
||||
"extraData": "0x68697665636861696e",
|
||||
"gasLimit": "0x23f3e20",
|
||||
"gasLimit": "0x11e1a300",
|
||||
"difficulty": "0x20000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
|
|
@ -119,6 +119,10 @@
|
|||
"balance": "0x1",
|
||||
"nonce": "0x1"
|
||||
},
|
||||
"8dcd17433742f4c0ca53122ab541d0ba67fc27ff": {
|
||||
"code": "0x6202e6306000a0",
|
||||
"balance": "0x0"
|
||||
},
|
||||
"c7b99a164efd027a93f147376cc7da7c67c6bbe0": {
|
||||
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
{
|
||||
"parentHash": "0x65151b101682b54cd08ba226f640c14c86176865ff9bfc57e0147dadaeac34bb",
|
||||
"parentHash": "0x7e80093a491eba0e5b2c1895837902f64f514100221801318fe391e1e09c96a6",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"stateRoot": "0xce423ebc60fc7764a43f09f1fe3ae61eef25e3eb8d09b1108f7e7eb77dfff5e6",
|
||||
"transactionsRoot": "0x7ec1ae3989efa75d7bcc766e5e2443afa8a89a5fda42ebba90050e7e702980f7",
|
||||
"receiptsRoot": "0xfe160832b1ca85f38c6674cb0aae3a24693bc49be56e2ecdf3698b71a794de86",
|
||||
"stateRoot": "0x8fcfb02cfca007773bd55bc1c3e50a3c8612a59c87ce057e5957e8bf17c1728b",
|
||||
"transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"difficulty": "0x0",
|
||||
"number": "0x258",
|
||||
"gasLimit": "0x23f3e20",
|
||||
"gasUsed": "0x19d36",
|
||||
"gasLimit": "0x11e1a300",
|
||||
"gasUsed": "0x0",
|
||||
"timestamp": "0x1770",
|
||||
"extraData": "0x",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"baseFeePerGas": "0x7",
|
||||
"withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"withdrawalsRoot": "0x92abfda39de7df7d705c5a8f30386802ad59d31e782a06d5c5b0f9a260056cf0",
|
||||
"blobGasUsed": "0x0",
|
||||
"excessBlobGas": "0x0",
|
||||
"parentBeaconBlockRoot": "0xf5003fc8f92358e790a114bce93ce1d9c283c85e1787f8d7d56714d3489b49e6",
|
||||
"requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"hash": "0xce8d86ba17a2ec303155f0e264c58a4b8f94ce3436274cf1924f91acdb7502d0"
|
||||
"hash": "0x44e3809c9a3cda717f00aea3a9da336d149612c8d5657fbc0028176ef8d94d2a"
|
||||
}
|
||||
|
|
@ -4,9 +4,9 @@
|
|||
"method": "engine_forkchoiceUpdatedV3",
|
||||
"params": [
|
||||
{
|
||||
"headBlockHash": "0xce8d86ba17a2ec303155f0e264c58a4b8f94ce3436274cf1924f91acdb7502d0",
|
||||
"safeBlockHash": "0xce8d86ba17a2ec303155f0e264c58a4b8f94ce3436274cf1924f91acdb7502d0",
|
||||
"finalizedBlockHash": "0xce8d86ba17a2ec303155f0e264c58a4b8f94ce3436274cf1924f91acdb7502d0"
|
||||
"headBlockHash": "0x44e3809c9a3cda717f00aea3a9da336d149612c8d5657fbc0028176ef8d94d2a",
|
||||
"safeBlockHash": "0x44e3809c9a3cda717f00aea3a9da336d149612c8d5657fbc0028176ef8d94d2a",
|
||||
"finalizedBlockHash": "0x44e3809c9a3cda717f00aea3a9da336d149612c8d5657fbc0028176ef8d94d2a"
|
||||
},
|
||||
null
|
||||
]
|
||||
|
|
|
|||
4210
cmd/devp2p/internal/ethtest/testdata/headstate.json
vendored
4210
cmd/devp2p/internal/ethtest/testdata/headstate.json
vendored
File diff suppressed because it is too large
Load diff
10313
cmd/devp2p/internal/ethtest/testdata/newpayload.json
vendored
10313
cmd/devp2p/internal/ethtest/testdata/newpayload.json
vendored
File diff suppressed because it is too large
Load diff
2943
cmd/devp2p/internal/ethtest/testdata/txinfo.json
vendored
2943
cmd/devp2p/internal/ethtest/testdata/txinfo.json
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -218,11 +218,15 @@ func (tc *conn) read(c net.PacketConn) v5wire.Packet {
|
|||
if err := c.SetReadDeadline(time.Now().Add(waitTime)); err != nil {
|
||||
return &readError{err}
|
||||
}
|
||||
n, fromAddr, err := c.ReadFrom(buf)
|
||||
n, _, err := c.ReadFrom(buf)
|
||||
if err != nil {
|
||||
return &readError{err}
|
||||
}
|
||||
_, _, p, err := tc.codec.Decode(buf[:n], fromAddr.String())
|
||||
// Always use tc.remoteAddr for session lookup. The actual source address of
|
||||
// the packet may differ from tc.remoteAddr when the remote node is reachable
|
||||
// via multiple networks (e.g. Docker bridge vs. overlay), but the codec's
|
||||
// session cache is keyed by the address used during Encode.
|
||||
_, _, p, err := tc.codec.Decode(buf[:n], tc.remoteAddr.String())
|
||||
if err != nil {
|
||||
return &readError{err}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -365,10 +365,6 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||
// Set requestsHash on block.
|
||||
h := types.CalcRequestsHash(requests)
|
||||
execRs.RequestsHash = &h
|
||||
for i := range requests {
|
||||
// remove prefix
|
||||
requests[i] = requests[i][1:]
|
||||
}
|
||||
execRs.Requests = requests
|
||||
}
|
||||
|
||||
|
|
|
|||
408
cmd/geth/bintrie_convert.go
Normal file
408
cmd/geth/bintrie_convert.go
Normal file
|
|
@ -0,0 +1,408 @@
|
|||
// Copyright 2026 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/bintrie"
|
||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
deleteSourceFlag = &cli.BoolFlag{
|
||||
Name: "delete-source",
|
||||
Usage: "Delete MPT trie nodes after conversion",
|
||||
}
|
||||
memoryLimitFlag = &cli.Uint64Flag{
|
||||
Name: "memory-limit",
|
||||
Usage: "Max heap allocation in MB before forcing a commit cycle",
|
||||
Value: 16384,
|
||||
}
|
||||
|
||||
bintrieCommand = &cli.Command{
|
||||
Name: "bintrie",
|
||||
Usage: "A set of commands for binary trie operations",
|
||||
Description: "",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "convert",
|
||||
Usage: "Convert MPT state to binary trie",
|
||||
ArgsUsage: "[state-root]",
|
||||
Action: convertToBinaryTrie,
|
||||
Flags: slices.Concat([]cli.Flag{
|
||||
deleteSourceFlag,
|
||||
memoryLimitFlag,
|
||||
}, utils.NetworkFlags, utils.DatabaseFlags),
|
||||
Description: `
|
||||
geth bintrie convert [--delete-source] [--memory-limit MB] [state-root]
|
||||
|
||||
Reads all state from the Merkle Patricia Trie and writes it into a Binary Trie,
|
||||
operating offline. Memory-safe via periodic commit-and-reload cycles.
|
||||
|
||||
The optional state-root argument specifies which state root to convert.
|
||||
If omitted, the head block's state root is used.
|
||||
|
||||
Flags:
|
||||
--delete-source Delete MPT trie nodes after successful conversion
|
||||
--memory-limit Max heap allocation in MB before forcing a commit (default: 16384)
|
||||
`,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type conversionStats struct {
|
||||
accounts uint64
|
||||
slots uint64
|
||||
codes uint64
|
||||
commits uint64
|
||||
start time.Time
|
||||
lastReport time.Time
|
||||
lastMemChk time.Time
|
||||
}
|
||||
|
||||
func (s *conversionStats) report(force bool) {
|
||||
if !force && time.Since(s.lastReport) < 8*time.Second {
|
||||
return
|
||||
}
|
||||
elapsed := time.Since(s.start).Seconds()
|
||||
acctRate := float64(0)
|
||||
if elapsed > 0 {
|
||||
acctRate = float64(s.accounts) / elapsed
|
||||
}
|
||||
log.Info("Conversion progress",
|
||||
"accounts", s.accounts,
|
||||
"slots", s.slots,
|
||||
"codes", s.codes,
|
||||
"commits", s.commits,
|
||||
"accounts/sec", fmt.Sprintf("%.0f", acctRate),
|
||||
"elapsed", common.PrettyDuration(time.Since(s.start)),
|
||||
)
|
||||
s.lastReport = time.Now()
|
||||
}
|
||||
|
||||
func convertToBinaryTrie(ctx *cli.Context) error {
|
||||
if ctx.NArg() > 1 {
|
||||
return errors.New("too many arguments")
|
||||
}
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
defer stack.Close()
|
||||
|
||||
chaindb := utils.MakeChainDatabase(ctx, stack, false)
|
||||
defer chaindb.Close()
|
||||
|
||||
headBlock := rawdb.ReadHeadBlock(chaindb)
|
||||
if headBlock == nil {
|
||||
return errors.New("no head block found")
|
||||
}
|
||||
var (
|
||||
root common.Hash
|
||||
err error
|
||||
)
|
||||
if ctx.NArg() == 1 {
|
||||
root, err = parseRoot(ctx.Args().First())
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid state root: %w", err)
|
||||
}
|
||||
} else {
|
||||
root = headBlock.Root()
|
||||
}
|
||||
log.Info("Starting MPT to binary trie conversion", "root", root, "block", headBlock.NumberU64())
|
||||
|
||||
srcTriedb := utils.MakeTrieDatabase(ctx, stack, chaindb, true, true, false)
|
||||
defer srcTriedb.Close()
|
||||
|
||||
destTriedb := triedb.NewDatabase(chaindb, &triedb.Config{
|
||||
IsVerkle: true,
|
||||
PathDB: &pathdb.Config{
|
||||
JournalDirectory: stack.ResolvePath("triedb-bintrie"),
|
||||
},
|
||||
})
|
||||
defer destTriedb.Close()
|
||||
|
||||
binTrie, err := bintrie.NewBinaryTrie(types.EmptyBinaryHash, destTriedb)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create binary trie: %w", err)
|
||||
}
|
||||
memLimit := ctx.Uint64(memoryLimitFlag.Name) * 1024 * 1024
|
||||
|
||||
currentRoot, err := runConversionLoop(chaindb, srcTriedb, destTriedb, binTrie, root, memLimit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("Conversion complete", "binaryRoot", currentRoot)
|
||||
|
||||
if ctx.Bool(deleteSourceFlag.Name) {
|
||||
log.Info("Deleting source MPT data")
|
||||
if err := deleteMPTData(chaindb, srcTriedb, root); err != nil {
|
||||
return fmt.Errorf("MPT deletion failed: %w", err)
|
||||
}
|
||||
log.Info("Source MPT data deleted")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runConversionLoop(chaindb ethdb.Database, srcTriedb *triedb.Database, destTriedb *triedb.Database, binTrie *bintrie.BinaryTrie, root common.Hash, memLimit uint64) (common.Hash, error) {
|
||||
currentRoot := types.EmptyBinaryHash
|
||||
stats := &conversionStats{
|
||||
start: time.Now(),
|
||||
lastReport: time.Now(),
|
||||
lastMemChk: time.Now(),
|
||||
}
|
||||
|
||||
srcTrie, err := trie.NewStateTrie(trie.StateTrieID(root), srcTriedb)
|
||||
if err != nil {
|
||||
return common.Hash{}, fmt.Errorf("failed to open source trie: %w", err)
|
||||
}
|
||||
acctIt, err := srcTrie.NodeIterator(nil)
|
||||
if err != nil {
|
||||
return common.Hash{}, fmt.Errorf("failed to create account iterator: %w", err)
|
||||
}
|
||||
accIter := trie.NewIterator(acctIt)
|
||||
|
||||
for accIter.Next() {
|
||||
var acc types.StateAccount
|
||||
if err := rlp.DecodeBytes(accIter.Value, &acc); err != nil {
|
||||
return common.Hash{}, fmt.Errorf("invalid account RLP: %w", err)
|
||||
}
|
||||
addrBytes := srcTrie.GetKey(accIter.Key)
|
||||
if addrBytes == nil {
|
||||
return common.Hash{}, fmt.Errorf("missing preimage for account hash %x (run with --cache.preimages)", accIter.Key)
|
||||
}
|
||||
addr := common.BytesToAddress(addrBytes)
|
||||
|
||||
var code []byte
|
||||
codeHash := common.BytesToHash(acc.CodeHash)
|
||||
if codeHash != types.EmptyCodeHash {
|
||||
code = rawdb.ReadCode(chaindb, codeHash)
|
||||
if code == nil {
|
||||
return common.Hash{}, fmt.Errorf("missing code for hash %x (account %x)", codeHash, addr)
|
||||
}
|
||||
stats.codes++
|
||||
}
|
||||
|
||||
if err := binTrie.UpdateAccount(addr, &acc, len(code)); err != nil {
|
||||
return common.Hash{}, fmt.Errorf("failed to update account %x: %w", addr, err)
|
||||
}
|
||||
if len(code) > 0 {
|
||||
if err := binTrie.UpdateContractCode(addr, codeHash, code); err != nil {
|
||||
return common.Hash{}, fmt.Errorf("failed to update code for %x: %w", addr, err)
|
||||
}
|
||||
}
|
||||
|
||||
if acc.Root != types.EmptyRootHash {
|
||||
addrHash := common.BytesToHash(accIter.Key)
|
||||
storageTrie, err := trie.NewStateTrie(trie.StorageTrieID(root, addrHash, acc.Root), srcTriedb)
|
||||
if err != nil {
|
||||
return common.Hash{}, fmt.Errorf("failed to open storage trie for %x: %w", addr, err)
|
||||
}
|
||||
storageNodeIt, err := storageTrie.NodeIterator(nil)
|
||||
if err != nil {
|
||||
return common.Hash{}, fmt.Errorf("failed to create storage iterator for %x: %w", addr, err)
|
||||
}
|
||||
storageIter := trie.NewIterator(storageNodeIt)
|
||||
|
||||
slotCount := uint64(0)
|
||||
for storageIter.Next() {
|
||||
slotKey := storageTrie.GetKey(storageIter.Key)
|
||||
if slotKey == nil {
|
||||
return common.Hash{}, fmt.Errorf("missing preimage for storage key %x (account %x)", storageIter.Key, addr)
|
||||
}
|
||||
_, content, _, err := rlp.Split(storageIter.Value)
|
||||
if err != nil {
|
||||
return common.Hash{}, fmt.Errorf("invalid storage RLP for key %x (account %x): %w", slotKey, addr, err)
|
||||
}
|
||||
if err := binTrie.UpdateStorage(addr, slotKey, content); err != nil {
|
||||
return common.Hash{}, fmt.Errorf("failed to update storage %x/%x: %w", addr, slotKey, err)
|
||||
}
|
||||
stats.slots++
|
||||
slotCount++
|
||||
|
||||
if slotCount%10000 == 0 {
|
||||
binTrie, currentRoot, err = maybeCommit(binTrie, currentRoot, destTriedb, memLimit, stats)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if storageIter.Err != nil {
|
||||
return common.Hash{}, fmt.Errorf("storage iteration error for %x: %w", addr, storageIter.Err)
|
||||
}
|
||||
}
|
||||
stats.accounts++
|
||||
stats.report(false)
|
||||
|
||||
if stats.accounts%1000 == 0 {
|
||||
binTrie, currentRoot, err = maybeCommit(binTrie, currentRoot, destTriedb, memLimit, stats)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if accIter.Err != nil {
|
||||
return common.Hash{}, fmt.Errorf("account iteration error: %w", accIter.Err)
|
||||
}
|
||||
|
||||
_, currentRoot, err = commitBinaryTrie(binTrie, currentRoot, destTriedb)
|
||||
if err != nil {
|
||||
return common.Hash{}, fmt.Errorf("final commit failed: %w", err)
|
||||
}
|
||||
stats.commits++
|
||||
stats.report(true)
|
||||
return currentRoot, nil
|
||||
}
|
||||
|
||||
func maybeCommit(bt *bintrie.BinaryTrie, currentRoot common.Hash, destDB *triedb.Database, memLimit uint64, stats *conversionStats) (*bintrie.BinaryTrie, common.Hash, error) {
|
||||
if time.Since(stats.lastMemChk) < 5*time.Second {
|
||||
return bt, currentRoot, nil
|
||||
}
|
||||
stats.lastMemChk = time.Now()
|
||||
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
if m.Alloc < memLimit {
|
||||
return bt, currentRoot, nil
|
||||
}
|
||||
log.Info("Memory limit reached, committing", "alloc", common.StorageSize(m.Alloc), "limit", common.StorageSize(memLimit))
|
||||
|
||||
bt, currentRoot, err := commitBinaryTrie(bt, currentRoot, destDB)
|
||||
if err != nil {
|
||||
return nil, common.Hash{}, err
|
||||
}
|
||||
stats.commits++
|
||||
stats.report(true)
|
||||
return bt, currentRoot, nil
|
||||
}
|
||||
|
||||
func commitBinaryTrie(bt *bintrie.BinaryTrie, currentRoot common.Hash, destDB *triedb.Database) (*bintrie.BinaryTrie, common.Hash, error) {
|
||||
newRoot, nodeSet := bt.Commit(false)
|
||||
if nodeSet != nil {
|
||||
merged := trienode.NewWithNodeSet(nodeSet)
|
||||
if err := destDB.Update(newRoot, currentRoot, 0, merged, triedb.NewStateSet()); err != nil {
|
||||
return nil, common.Hash{}, fmt.Errorf("triedb update failed: %w", err)
|
||||
}
|
||||
if err := destDB.Commit(newRoot, false); err != nil {
|
||||
return nil, common.Hash{}, fmt.Errorf("triedb commit failed: %w", err)
|
||||
}
|
||||
}
|
||||
runtime.GC()
|
||||
debug.FreeOSMemory()
|
||||
|
||||
bt, err := bintrie.NewBinaryTrie(newRoot, destDB)
|
||||
if err != nil {
|
||||
return nil, common.Hash{}, fmt.Errorf("failed to reload binary trie: %w", err)
|
||||
}
|
||||
return bt, newRoot, nil
|
||||
}
|
||||
|
||||
func deleteMPTData(chaindb ethdb.Database, srcTriedb *triedb.Database, root common.Hash) error {
|
||||
isPathDB := srcTriedb.Scheme() == rawdb.PathScheme
|
||||
|
||||
srcTrie, err := trie.NewStateTrie(trie.StateTrieID(root), srcTriedb)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open source trie for deletion: %w", err)
|
||||
}
|
||||
acctIt, err := srcTrie.NodeIterator(nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create account iterator for deletion: %w", err)
|
||||
}
|
||||
batch := chaindb.NewBatch()
|
||||
deleted := 0
|
||||
|
||||
for acctIt.Next(true) {
|
||||
if isPathDB {
|
||||
rawdb.DeleteAccountTrieNode(batch, acctIt.Path())
|
||||
} else {
|
||||
node := acctIt.Hash()
|
||||
if node != (common.Hash{}) {
|
||||
rawdb.DeleteLegacyTrieNode(batch, node)
|
||||
}
|
||||
}
|
||||
deleted++
|
||||
|
||||
if acctIt.Leaf() {
|
||||
var acc types.StateAccount
|
||||
if err := rlp.DecodeBytes(acctIt.LeafBlob(), &acc); err != nil {
|
||||
return fmt.Errorf("invalid account during deletion: %w", err)
|
||||
}
|
||||
if acc.Root != types.EmptyRootHash {
|
||||
addrHash := common.BytesToHash(acctIt.LeafKey())
|
||||
storageTrie, err := trie.NewStateTrie(trie.StorageTrieID(root, addrHash, acc.Root), srcTriedb)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open storage trie for deletion: %w", err)
|
||||
}
|
||||
storageIt, err := storageTrie.NodeIterator(nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create storage iterator for deletion: %w", err)
|
||||
}
|
||||
for storageIt.Next(true) {
|
||||
if isPathDB {
|
||||
rawdb.DeleteStorageTrieNode(batch, addrHash, storageIt.Path())
|
||||
} else {
|
||||
node := storageIt.Hash()
|
||||
if node != (common.Hash{}) {
|
||||
rawdb.DeleteLegacyTrieNode(batch, node)
|
||||
}
|
||||
}
|
||||
deleted++
|
||||
if batch.ValueSize() >= ethdb.IdealBatchSize {
|
||||
if err := batch.Write(); err != nil {
|
||||
return fmt.Errorf("batch write failed: %w", err)
|
||||
}
|
||||
batch.Reset()
|
||||
}
|
||||
}
|
||||
if storageIt.Error() != nil {
|
||||
return fmt.Errorf("storage deletion iterator error: %w", storageIt.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
if batch.ValueSize() >= ethdb.IdealBatchSize {
|
||||
if err := batch.Write(); err != nil {
|
||||
return fmt.Errorf("batch write failed: %w", err)
|
||||
}
|
||||
batch.Reset()
|
||||
}
|
||||
}
|
||||
if acctIt.Error() != nil {
|
||||
return fmt.Errorf("account deletion iterator error: %w", acctIt.Error())
|
||||
}
|
||||
if batch.ValueSize() > 0 {
|
||||
if err := batch.Write(); err != nil {
|
||||
return fmt.Errorf("final batch write failed: %w", err)
|
||||
}
|
||||
}
|
||||
log.Info("MPT deletion complete", "nodesDeleted", deleted)
|
||||
return nil
|
||||
}
|
||||
229
cmd/geth/bintrie_convert_test.go
Normal file
229
cmd/geth/bintrie_convert_test.go
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
// Copyright 2026 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie/bintrie"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
func TestBintrieConvert(t *testing.T) {
|
||||
var (
|
||||
addr1 = common.HexToAddress("0x1111111111111111111111111111111111111111")
|
||||
addr2 = common.HexToAddress("0x2222222222222222222222222222222222222222")
|
||||
slotKey1 = common.HexToHash("0x01")
|
||||
slotKey2 = common.HexToHash("0x02")
|
||||
slotVal1 = common.HexToHash("0xdeadbeef")
|
||||
slotVal2 = common.HexToHash("0xcafebabe")
|
||||
code = []byte{0x60, 0x42, 0x60, 0x00, 0x52, 0x60, 0x20, 0x60, 0x00, 0xf3}
|
||||
)
|
||||
|
||||
chaindb := rawdb.NewMemoryDatabase()
|
||||
|
||||
srcTriedb := triedb.NewDatabase(chaindb, &triedb.Config{
|
||||
Preimages: true,
|
||||
PathDB: pathdb.Defaults,
|
||||
})
|
||||
|
||||
gspec := &core.Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
Alloc: types.GenesisAlloc{
|
||||
addr1: {
|
||||
Balance: big.NewInt(1000000),
|
||||
Nonce: 5,
|
||||
},
|
||||
addr2: {
|
||||
Balance: big.NewInt(2000000),
|
||||
Nonce: 10,
|
||||
Code: code,
|
||||
Storage: map[common.Hash]common.Hash{
|
||||
slotKey1: slotVal1,
|
||||
slotKey2: slotVal2,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
genesisBlock := gspec.MustCommit(chaindb, srcTriedb)
|
||||
root := genesisBlock.Root()
|
||||
t.Logf("Genesis root: %x", root)
|
||||
srcTriedb.Close()
|
||||
|
||||
srcTriedb2 := triedb.NewDatabase(chaindb, &triedb.Config{
|
||||
Preimages: true,
|
||||
PathDB: &pathdb.Config{ReadOnly: true},
|
||||
})
|
||||
defer srcTriedb2.Close()
|
||||
|
||||
destTriedb := triedb.NewDatabase(chaindb, &triedb.Config{
|
||||
IsVerkle: true,
|
||||
PathDB: pathdb.Defaults,
|
||||
})
|
||||
defer destTriedb.Close()
|
||||
|
||||
bt, err := bintrie.NewBinaryTrie(types.EmptyBinaryHash, destTriedb)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create binary trie: %v", err)
|
||||
}
|
||||
|
||||
currentRoot, err := runConversionLoop(chaindb, srcTriedb2, destTriedb, bt, root, math.MaxUint64)
|
||||
if err != nil {
|
||||
t.Fatalf("conversion failed: %v", err)
|
||||
}
|
||||
t.Logf("Binary trie root: %x", currentRoot)
|
||||
|
||||
bt2, err := bintrie.NewBinaryTrie(currentRoot, destTriedb)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to reload binary trie: %v", err)
|
||||
}
|
||||
|
||||
acc1, err := bt2.GetAccount(addr1)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get account1: %v", err)
|
||||
}
|
||||
if acc1 == nil {
|
||||
t.Fatal("account1 not found in binary trie")
|
||||
}
|
||||
if acc1.Nonce != 5 {
|
||||
t.Errorf("account1 nonce: got %d, want 5", acc1.Nonce)
|
||||
}
|
||||
wantBal1 := uint256.NewInt(1000000)
|
||||
if acc1.Balance.Cmp(wantBal1) != 0 {
|
||||
t.Errorf("account1 balance: got %s, want %s", acc1.Balance, wantBal1)
|
||||
}
|
||||
|
||||
acc2, err := bt2.GetAccount(addr2)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get account2: %v", err)
|
||||
}
|
||||
if acc2 == nil {
|
||||
t.Fatal("account2 not found in binary trie")
|
||||
}
|
||||
if acc2.Nonce != 10 {
|
||||
t.Errorf("account2 nonce: got %d, want 10", acc2.Nonce)
|
||||
}
|
||||
wantBal2 := uint256.NewInt(2000000)
|
||||
if acc2.Balance.Cmp(wantBal2) != 0 {
|
||||
t.Errorf("account2 balance: got %s, want %s", acc2.Balance, wantBal2)
|
||||
}
|
||||
|
||||
treeKey1 := bintrie.GetBinaryTreeKeyStorageSlot(addr2, slotKey1[:])
|
||||
val1, err := bt2.GetWithHashedKey(treeKey1)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get storage slot1: %v", err)
|
||||
}
|
||||
if len(val1) == 0 {
|
||||
t.Fatal("storage slot1 not found")
|
||||
}
|
||||
got1 := common.BytesToHash(val1)
|
||||
if got1 != slotVal1 {
|
||||
t.Errorf("storage slot1: got %x, want %x", got1, slotVal1)
|
||||
}
|
||||
|
||||
treeKey2 := bintrie.GetBinaryTreeKeyStorageSlot(addr2, slotKey2[:])
|
||||
val2, err := bt2.GetWithHashedKey(treeKey2)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get storage slot2: %v", err)
|
||||
}
|
||||
if len(val2) == 0 {
|
||||
t.Fatal("storage slot2 not found")
|
||||
}
|
||||
got2 := common.BytesToHash(val2)
|
||||
if got2 != slotVal2 {
|
||||
t.Errorf("storage slot2: got %x, want %x", got2, slotVal2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBintrieConvertDeleteSource(t *testing.T) {
|
||||
addr1 := common.HexToAddress("0x3333333333333333333333333333333333333333")
|
||||
|
||||
chaindb := rawdb.NewMemoryDatabase()
|
||||
|
||||
srcTriedb := triedb.NewDatabase(chaindb, &triedb.Config{
|
||||
Preimages: true,
|
||||
PathDB: pathdb.Defaults,
|
||||
})
|
||||
|
||||
gspec := &core.Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
Alloc: types.GenesisAlloc{
|
||||
addr1: {
|
||||
Balance: big.NewInt(1000000),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
genesisBlock := gspec.MustCommit(chaindb, srcTriedb)
|
||||
root := genesisBlock.Root()
|
||||
srcTriedb.Close()
|
||||
|
||||
srcTriedb2 := triedb.NewDatabase(chaindb, &triedb.Config{
|
||||
Preimages: true,
|
||||
PathDB: &pathdb.Config{ReadOnly: true},
|
||||
})
|
||||
|
||||
destTriedb := triedb.NewDatabase(chaindb, &triedb.Config{
|
||||
IsVerkle: true,
|
||||
PathDB: pathdb.Defaults,
|
||||
})
|
||||
|
||||
bt, err := bintrie.NewBinaryTrie(types.EmptyBinaryHash, destTriedb)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create binary trie: %v", err)
|
||||
}
|
||||
|
||||
newRoot, err := runConversionLoop(chaindb, srcTriedb2, destTriedb, bt, root, math.MaxUint64)
|
||||
if err != nil {
|
||||
t.Fatalf("conversion failed: %v", err)
|
||||
}
|
||||
|
||||
if err := deleteMPTData(chaindb, srcTriedb2, root); err != nil {
|
||||
t.Fatalf("deletion failed: %v", err)
|
||||
}
|
||||
srcTriedb2.Close()
|
||||
|
||||
bt2, err := bintrie.NewBinaryTrie(newRoot, destTriedb)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to reload binary trie after deletion: %v", err)
|
||||
}
|
||||
|
||||
acc, err := bt2.GetAccount(addr1)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get account after deletion: %v", err)
|
||||
}
|
||||
if acc == nil {
|
||||
t.Fatal("account not found after MPT deletion")
|
||||
}
|
||||
wantBal := uint256.NewInt(1000000)
|
||||
if acc.Balance.Cmp(wantBal) != 0 {
|
||||
t.Errorf("balance after deletion: got %s, want %s", acc.Balance, wantBal)
|
||||
}
|
||||
destTriedb.Close()
|
||||
}
|
||||
|
|
@ -816,13 +816,16 @@ func pruneHistory(ctx *cli.Context) error {
|
|||
|
||||
// Determine the prune point based on the history mode.
|
||||
genesisHash := chain.Genesis().Hash()
|
||||
prunePoint := history.GetPrunePoint(genesisHash, mode)
|
||||
if prunePoint == nil {
|
||||
policy, err := history.NewPolicy(mode, genesisHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if policy.Target == nil {
|
||||
return fmt.Errorf("prune point for %q not found for this network", mode.String())
|
||||
}
|
||||
var (
|
||||
targetBlock = prunePoint.BlockNumber
|
||||
targetBlockHash = prunePoint.BlockHash
|
||||
targetBlock = policy.Target.BlockNumber
|
||||
targetBlockHash = policy.Target.BlockHash
|
||||
)
|
||||
|
||||
// Check the current freezer tail to see if pruning is needed/possible.
|
||||
|
|
|
|||
|
|
@ -261,6 +261,8 @@ func init() {
|
|||
utils.ShowDeprecated,
|
||||
// See snapshot.go
|
||||
snapshotCommand,
|
||||
// See bintrie_convert.go
|
||||
bintrieCommand,
|
||||
}
|
||||
if logTestCommand != nil {
|
||||
app.Commands = append(app.Commands, logTestCommand)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
|
|
@ -105,7 +106,9 @@ information about the specified address.
|
|||
Usage: "Traverse the state with given root hash and perform quick verification",
|
||||
ArgsUsage: "<root>",
|
||||
Action: traverseState,
|
||||
Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags),
|
||||
Flags: slices.Concat([]cli.Flag{
|
||||
utils.AccountFlag,
|
||||
}, utils.NetworkFlags, utils.DatabaseFlags),
|
||||
Description: `
|
||||
geth snapshot traverse-state <state-root>
|
||||
will traverse the whole state from the given state root and will abort if any
|
||||
|
|
@ -113,6 +116,8 @@ referenced trie node or contract code is missing. This command can be used for
|
|||
state integrity verification. The default checking target is the HEAD state.
|
||||
|
||||
It's also usable without snapshot enabled.
|
||||
|
||||
If --account is specified, only the storage trie of that account is traversed.
|
||||
`,
|
||||
},
|
||||
{
|
||||
|
|
@ -120,7 +125,9 @@ It's also usable without snapshot enabled.
|
|||
Usage: "Traverse the state with given root hash and perform detailed verification",
|
||||
ArgsUsage: "<root>",
|
||||
Action: traverseRawState,
|
||||
Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags),
|
||||
Flags: slices.Concat([]cli.Flag{
|
||||
utils.AccountFlag,
|
||||
}, utils.NetworkFlags, utils.DatabaseFlags),
|
||||
Description: `
|
||||
geth snapshot traverse-rawstate <state-root>
|
||||
will traverse the whole state from the given root and will abort if any referenced
|
||||
|
|
@ -129,6 +136,8 @@ verification. The default checking target is the HEAD state. It's basically iden
|
|||
to traverse-state, but the check granularity is smaller.
|
||||
|
||||
It's also usable without snapshot enabled.
|
||||
|
||||
If --account is specified, only the storage trie of that account is traversed.
|
||||
`,
|
||||
},
|
||||
{
|
||||
|
|
@ -272,6 +281,120 @@ func checkDanglingStorage(ctx *cli.Context) error {
|
|||
return snapshot.CheckDanglingStorage(db)
|
||||
}
|
||||
|
||||
// parseAccount parses the account flag value as either an address (20 bytes)
|
||||
// or an account hash (32 bytes) and returns the hashed account key.
|
||||
func parseAccount(input string) (common.Hash, error) {
|
||||
switch len(input) {
|
||||
case 40, 42: // address
|
||||
return crypto.Keccak256Hash(common.HexToAddress(input).Bytes()), nil
|
||||
case 64, 66: // hash
|
||||
return common.HexToHash(input), nil
|
||||
default:
|
||||
return common.Hash{}, errors.New("malformed account address or hash")
|
||||
}
|
||||
}
|
||||
|
||||
// lookupAccount resolves the account from the state trie using the given
|
||||
// account hash.
|
||||
func lookupAccount(accountHash common.Hash, tr *trie.Trie) (*types.StateAccount, error) {
|
||||
accData, err := tr.Get(accountHash.Bytes())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get account %s: %w", accountHash, err)
|
||||
}
|
||||
if accData == nil {
|
||||
return nil, fmt.Errorf("account not found: %s", accountHash)
|
||||
}
|
||||
var acc types.StateAccount
|
||||
if err := rlp.DecodeBytes(accData, &acc); err != nil {
|
||||
return nil, fmt.Errorf("invalid account data %s: %w", accountHash, err)
|
||||
}
|
||||
return &acc, nil
|
||||
}
|
||||
|
||||
func traverseStorage(id *trie.ID, db *triedb.Database, report bool, detail bool) error {
|
||||
tr, err := trie.NewStateTrie(id, db)
|
||||
if err != nil {
|
||||
log.Error("Failed to open storage trie", "account", id.Owner, "root", id.Root, "err", err)
|
||||
return err
|
||||
}
|
||||
var (
|
||||
slots int
|
||||
nodes int
|
||||
lastReport time.Time
|
||||
start = time.Now()
|
||||
)
|
||||
it, err := tr.NodeIterator(nil)
|
||||
if err != nil {
|
||||
log.Error("Failed to open storage iterator", "account", id.Owner, "root", id.Root, "err", err)
|
||||
return err
|
||||
}
|
||||
logger := log.Debug
|
||||
if report {
|
||||
logger = log.Info
|
||||
}
|
||||
logger("Start traversing storage trie", "account", id.Owner, "storageRoot", id.Root)
|
||||
|
||||
if !detail {
|
||||
iter := trie.NewIterator(it)
|
||||
for iter.Next() {
|
||||
slots += 1
|
||||
if time.Since(lastReport) > time.Second*8 {
|
||||
logger("Traversing storage", "account", id.Owner, "slots", slots, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
lastReport = time.Now()
|
||||
}
|
||||
}
|
||||
if iter.Err != nil {
|
||||
log.Error("Failed to traverse storage trie", "root", id.Root, "err", iter.Err)
|
||||
return iter.Err
|
||||
}
|
||||
logger("Storage is complete", "account", id.Owner, "slots", slots, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
} else {
|
||||
reader, err := db.NodeReader(id.StateRoot)
|
||||
if err != nil {
|
||||
log.Error("Failed to open state reader", "err", err)
|
||||
return err
|
||||
}
|
||||
var (
|
||||
buffer = make([]byte, 32)
|
||||
hasher = crypto.NewKeccakState()
|
||||
)
|
||||
for it.Next(true) {
|
||||
nodes += 1
|
||||
node := it.Hash()
|
||||
|
||||
// Check the presence for non-empty hash node(embedded node doesn't
|
||||
// have their own hash).
|
||||
if node != (common.Hash{}) {
|
||||
blob, _ := reader.Node(id.Owner, it.Path(), node)
|
||||
if len(blob) == 0 {
|
||||
log.Error("Missing trie node(storage)", "hash", node)
|
||||
return errors.New("missing storage")
|
||||
}
|
||||
hasher.Reset()
|
||||
hasher.Write(blob)
|
||||
hasher.Read(buffer)
|
||||
if !bytes.Equal(buffer, node.Bytes()) {
|
||||
log.Error("Invalid trie node(storage)", "hash", node.Hex(), "value", blob)
|
||||
return errors.New("invalid storage node")
|
||||
}
|
||||
}
|
||||
if it.Leaf() {
|
||||
slots += 1
|
||||
}
|
||||
if time.Since(lastReport) > time.Second*8 {
|
||||
logger("Traversing storage", "account", id.Owner, "nodes", nodes, "slots", slots, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
lastReport = time.Now()
|
||||
}
|
||||
}
|
||||
if err := it.Error(); err != nil {
|
||||
log.Error("Failed to traverse storage trie", "root", id.Root, "err", err)
|
||||
return err
|
||||
}
|
||||
logger("Storage is complete", "account", id.Owner, "nodes", nodes, "slots", slots, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// traverseState is a helper function used for pruning verification.
|
||||
// Basically it just iterates the trie, ensure all nodes and associated
|
||||
// contract codes are present.
|
||||
|
|
@ -309,6 +432,30 @@ func traverseState(ctx *cli.Context) error {
|
|||
root = headBlock.Root()
|
||||
log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64())
|
||||
}
|
||||
// If --account is specified, only traverse the storage trie of that account.
|
||||
if accountStr := ctx.String(utils.AccountFlag.Name); accountStr != "" {
|
||||
accountHash, err := parseAccount(accountStr)
|
||||
if err != nil {
|
||||
log.Error("Failed to parse account", "err", err)
|
||||
return err
|
||||
}
|
||||
// Use raw trie since the account key is already hashed.
|
||||
t, err := trie.New(trie.StateTrieID(root), triedb)
|
||||
if err != nil {
|
||||
log.Error("Failed to open state trie", "root", root, "err", err)
|
||||
return err
|
||||
}
|
||||
acc, err := lookupAccount(accountHash, t)
|
||||
if err != nil {
|
||||
log.Error("Failed to look up account", "hash", accountHash, "err", err)
|
||||
return err
|
||||
}
|
||||
if acc.Root == types.EmptyRootHash {
|
||||
log.Info("Account has no storage", "hash", accountHash)
|
||||
return nil
|
||||
}
|
||||
return traverseStorage(trie.StorageTrieID(root, accountHash, acc.Root), triedb, true, false)
|
||||
}
|
||||
t, err := trie.NewStateTrie(trie.StateTrieID(root), triedb)
|
||||
if err != nil {
|
||||
log.Error("Failed to open trie", "root", root, "err", err)
|
||||
|
|
@ -335,30 +482,10 @@ func traverseState(ctx *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
if acc.Root != types.EmptyRootHash {
|
||||
id := trie.StorageTrieID(root, common.BytesToHash(accIter.Key), acc.Root)
|
||||
storageTrie, err := trie.NewStateTrie(id, triedb)
|
||||
err := traverseStorage(trie.StorageTrieID(root, common.BytesToHash(accIter.Key), acc.Root), triedb, false, false)
|
||||
if err != nil {
|
||||
log.Error("Failed to open storage trie", "root", acc.Root, "err", err)
|
||||
return err
|
||||
}
|
||||
storageIt, err := storageTrie.NodeIterator(nil)
|
||||
if err != nil {
|
||||
log.Error("Failed to open storage iterator", "root", acc.Root, "err", err)
|
||||
return err
|
||||
}
|
||||
storageIter := trie.NewIterator(storageIt)
|
||||
for storageIter.Next() {
|
||||
slots += 1
|
||||
|
||||
if time.Since(lastReport) > time.Second*8 {
|
||||
log.Info("Traversing state", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
lastReport = time.Now()
|
||||
}
|
||||
}
|
||||
if storageIter.Err != nil {
|
||||
log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Err)
|
||||
return storageIter.Err
|
||||
}
|
||||
}
|
||||
if !bytes.Equal(acc.CodeHash, types.EmptyCodeHash.Bytes()) {
|
||||
if !rawdb.HasCode(chaindb, common.BytesToHash(acc.CodeHash)) {
|
||||
|
|
@ -418,6 +545,30 @@ func traverseRawState(ctx *cli.Context) error {
|
|||
root = headBlock.Root()
|
||||
log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64())
|
||||
}
|
||||
// If --account is specified, only traverse the storage trie of that account.
|
||||
if accountStr := ctx.String(utils.AccountFlag.Name); accountStr != "" {
|
||||
accountHash, err := parseAccount(accountStr)
|
||||
if err != nil {
|
||||
log.Error("Failed to parse account", "err", err)
|
||||
return err
|
||||
}
|
||||
// Use raw trie since the account key is already hashed.
|
||||
t, err := trie.New(trie.StateTrieID(root), triedb)
|
||||
if err != nil {
|
||||
log.Error("Failed to open state trie", "root", root, "err", err)
|
||||
return err
|
||||
}
|
||||
acc, err := lookupAccount(accountHash, t)
|
||||
if err != nil {
|
||||
log.Error("Failed to look up account", "hash", accountHash, "err", err)
|
||||
return err
|
||||
}
|
||||
if acc.Root == types.EmptyRootHash {
|
||||
log.Info("Account has no storage", "hash", accountHash)
|
||||
return nil
|
||||
}
|
||||
return traverseStorage(trie.StorageTrieID(root, accountHash, acc.Root), triedb, true, true)
|
||||
}
|
||||
t, err := trie.NewStateTrie(trie.StateTrieID(root), triedb)
|
||||
if err != nil {
|
||||
log.Error("Failed to open trie", "root", root, "err", err)
|
||||
|
|
@ -473,50 +624,10 @@ func traverseRawState(ctx *cli.Context) error {
|
|||
return errors.New("invalid account")
|
||||
}
|
||||
if acc.Root != types.EmptyRootHash {
|
||||
id := trie.StorageTrieID(root, common.BytesToHash(accIter.LeafKey()), acc.Root)
|
||||
storageTrie, err := trie.NewStateTrie(id, triedb)
|
||||
err := traverseStorage(trie.StorageTrieID(root, common.BytesToHash(accIter.LeafKey()), acc.Root), triedb, false, true)
|
||||
if err != nil {
|
||||
log.Error("Failed to open storage trie", "root", acc.Root, "err", err)
|
||||
return errors.New("missing storage trie")
|
||||
}
|
||||
storageIter, err := storageTrie.NodeIterator(nil)
|
||||
if err != nil {
|
||||
log.Error("Failed to open storage iterator", "root", acc.Root, "err", err)
|
||||
return err
|
||||
}
|
||||
for storageIter.Next(true) {
|
||||
nodes += 1
|
||||
node := storageIter.Hash()
|
||||
|
||||
// Check the presence for non-empty hash node(embedded node doesn't
|
||||
// have their own hash).
|
||||
if node != (common.Hash{}) {
|
||||
blob, _ := reader.Node(common.BytesToHash(accIter.LeafKey()), storageIter.Path(), node)
|
||||
if len(blob) == 0 {
|
||||
log.Error("Missing trie node(storage)", "hash", node)
|
||||
return errors.New("missing storage")
|
||||
}
|
||||
hasher.Reset()
|
||||
hasher.Write(blob)
|
||||
hasher.Read(got)
|
||||
if !bytes.Equal(got, node.Bytes()) {
|
||||
log.Error("Invalid trie node(storage)", "hash", node.Hex(), "value", blob)
|
||||
return errors.New("invalid storage node")
|
||||
}
|
||||
}
|
||||
// Bump the counter if it's leaf node.
|
||||
if storageIter.Leaf() {
|
||||
slots += 1
|
||||
}
|
||||
if time.Since(lastReport) > time.Second*8 {
|
||||
log.Info("Traversing state", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
lastReport = time.Now()
|
||||
}
|
||||
}
|
||||
if storageIter.Error() != nil {
|
||||
log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Error())
|
||||
return storageIter.Error()
|
||||
}
|
||||
}
|
||||
if !bytes.Equal(acc.CodeHash, types.EmptyCodeHash.Bytes()) {
|
||||
if !rawdb.HasCode(chaindb, common.BytesToHash(acc.CodeHash)) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build example
|
||||
// +build example
|
||||
|
||||
package main
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build wasm
|
||||
// +build wasm
|
||||
//go:build wasm && !womir
|
||||
// +build wasm,!womir
|
||||
|
||||
package main
|
||||
|
||||
|
|
|
|||
49
cmd/keeper/getpayload_womir.go
Normal file
49
cmd/keeper/getpayload_womir.go
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2026 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build womir
|
||||
|
||||
package main
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// These match the WOMIR guest-io imports (env module).
|
||||
// Protocol: __hint_input prepares next item, __hint_buffer reads words.
|
||||
// Each item has format: [byte_len_u32_le, ...data_words_padded_to_4bytes]
|
||||
//
|
||||
//go:wasmimport env __hint_input
|
||||
func hintInput()
|
||||
|
||||
//go:wasmimport env __hint_buffer
|
||||
func hintBuffer(ptr unsafe.Pointer, numWords uint32)
|
||||
func readWord() uint32 {
|
||||
var buf [4]byte
|
||||
hintBuffer(unsafe.Pointer(&buf[0]), 1)
|
||||
return uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
|
||||
}
|
||||
func readBytes() []byte {
|
||||
hintInput()
|
||||
byteLen := readWord()
|
||||
numWords := (byteLen + 3) / 4
|
||||
data := make([]byte, numWords*4)
|
||||
hintBuffer(unsafe.Pointer(&data[0]), numWords)
|
||||
return data[:byteLen]
|
||||
}
|
||||
|
||||
// getInput reads the RLP-encoded Payload from the WOMIR hint stream.
|
||||
func getInput() []byte {
|
||||
return readBytes()
|
||||
}
|
||||
|
|
@ -14,8 +14,8 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build !example && !ziren && !wasm
|
||||
// +build !example,!ziren,!wasm
|
||||
//go:build !example && !ziren && !wasm && !womir
|
||||
// +build !example,!ziren,!wasm,!womir
|
||||
|
||||
package main
|
||||
|
||||
|
|
|
|||
|
|
@ -274,40 +274,66 @@ func ImportHistory(chain *core.BlockChain, dir string, network string, from func
|
|||
reported = time.Now()
|
||||
imported = 0
|
||||
h = sha256.New()
|
||||
scratch = bytes.NewBuffer(nil)
|
||||
buf = bytes.NewBuffer(nil)
|
||||
)
|
||||
|
||||
for i, file := range entries {
|
||||
err := func() error {
|
||||
path := filepath.Join(dir, file)
|
||||
|
||||
// validate against checksum file in directory
|
||||
// Validate against checksum file in directory.
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open %s: %w", path, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
return fmt.Errorf("checksum %s: %w", path, err)
|
||||
}
|
||||
got := common.BytesToHash(h.Sum(scratch.Bytes()[:])).Hex()
|
||||
want := checksums[i]
|
||||
got := common.BytesToHash(h.Sum(buf.Bytes()[:])).Hex()
|
||||
h.Reset()
|
||||
scratch.Reset()
|
||||
|
||||
if got != want {
|
||||
return fmt.Errorf("%s checksum mismatch: have %s want %s", file, got, want)
|
||||
buf.Reset()
|
||||
if got != checksums[i] {
|
||||
return fmt.Errorf("%s checksum mismatch: have %s want %s", file, got, checksums[i])
|
||||
}
|
||||
// Import all block data from Era1.
|
||||
e, err := from(f)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening era: %w", err)
|
||||
}
|
||||
defer e.Close()
|
||||
|
||||
it, err := e.Iterator()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating iterator: %w", err)
|
||||
}
|
||||
|
||||
var (
|
||||
blocks = make([]*types.Block, 0, importBatchSize)
|
||||
receiptsList = make([]types.Receipts, 0, importBatchSize)
|
||||
flush = func() error {
|
||||
if len(blocks) == 0 {
|
||||
return nil
|
||||
}
|
||||
enc := types.EncodeBlockReceiptLists(receiptsList)
|
||||
if _, err := chain.InsertReceiptChain(blocks, enc, math.MaxUint64); err != nil {
|
||||
return fmt.Errorf("error inserting blocks %d-%d: %w",
|
||||
blocks[0].NumberU64(), blocks[len(blocks)-1].NumberU64(), err)
|
||||
}
|
||||
imported += len(blocks)
|
||||
if time.Since(reported) >= 8*time.Second {
|
||||
head := blocks[len(blocks)-1].NumberU64()
|
||||
log.Info("Importing Era files", "head", head, "imported", imported,
|
||||
"elapsed", common.PrettyDuration(time.Since(start)))
|
||||
imported = 0
|
||||
reported = time.Now()
|
||||
}
|
||||
blocks = blocks[:0]
|
||||
receiptsList = receiptsList[:0]
|
||||
return nil
|
||||
}
|
||||
)
|
||||
for it.Next() {
|
||||
block, err := it.Block()
|
||||
if err != nil {
|
||||
|
|
@ -320,23 +346,18 @@ func ImportHistory(chain *core.BlockChain, dir string, network string, from func
|
|||
if err != nil {
|
||||
return fmt.Errorf("error reading receipts %d: %w", it.Number(), err)
|
||||
}
|
||||
enc := types.EncodeBlockReceiptLists([]types.Receipts{receipts})
|
||||
if _, err := chain.InsertReceiptChain([]*types.Block{block}, enc, math.MaxUint64); err != nil {
|
||||
return fmt.Errorf("error inserting body %d: %w", it.Number(), err)
|
||||
}
|
||||
imported++
|
||||
|
||||
if time.Since(reported) >= 8*time.Second {
|
||||
log.Info("Importing Era files", "head", it.Number(), "imported", imported,
|
||||
"elapsed", common.PrettyDuration(time.Since(start)))
|
||||
imported = 0
|
||||
reported = time.Now()
|
||||
blocks = append(blocks, block)
|
||||
receiptsList = append(receiptsList, receipts)
|
||||
if len(blocks) == importBatchSize {
|
||||
if err := flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := it.Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return flush()
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -218,6 +218,10 @@ var (
|
|||
Usage: "Max number of elements (0 = no limit)",
|
||||
Value: 0,
|
||||
}
|
||||
AccountFlag = &cli.StringFlag{
|
||||
Name: "account",
|
||||
Usage: "Specifies the account address or hash to traverse a single storage trie",
|
||||
}
|
||||
OutputFileFlag = &cli.StringFlag{
|
||||
Name: "output",
|
||||
Usage: "Writes the result in json to the output",
|
||||
|
|
@ -1580,7 +1584,9 @@ func setOpenTelemetry(ctx *cli.Context, cfg *node.Config) {
|
|||
if ctx.IsSet(RPCTelemetryTagsFlag.Name) {
|
||||
tcfg.Tags = ctx.String(RPCTelemetryTagsFlag.Name)
|
||||
}
|
||||
tcfg.SampleRatio = ctx.Float64(RPCTelemetrySampleRatioFlag.Name)
|
||||
if ctx.IsSet(RPCTelemetrySampleRatioFlag.Name) {
|
||||
tcfg.SampleRatio = ctx.Float64(RPCTelemetrySampleRatioFlag.Name)
|
||||
}
|
||||
|
||||
if tcfg.Endpoint != "" && !tcfg.Enabled {
|
||||
log.Warn(fmt.Sprintf("OpenTelemetry endpoint configured but telemetry is not enabled, use --%s to enable.", RPCTelemetryFlag.Name))
|
||||
|
|
|
|||
|
|
@ -155,7 +155,9 @@ func testConfigFromCLI(ctx *cli.Context) (cfg testConfig) {
|
|||
}
|
||||
|
||||
cfg.historyPruneBlock = new(uint64)
|
||||
*cfg.historyPruneBlock = history.PrunePoints[params.MainnetGenesisHash].BlockNumber
|
||||
if p, err := history.NewPolicy(history.KeepPostMerge, params.MainnetGenesisHash); err == nil {
|
||||
*cfg.historyPruneBlock = p.Target.BlockNumber
|
||||
}
|
||||
case ctx.Bool(testSepoliaFlag.Name):
|
||||
cfg.fsys = builtinTestFiles
|
||||
if ctx.IsSet(filterQueryFileFlag.Name) {
|
||||
|
|
@ -180,7 +182,9 @@ func testConfigFromCLI(ctx *cli.Context) (cfg testConfig) {
|
|||
}
|
||||
|
||||
cfg.historyPruneBlock = new(uint64)
|
||||
*cfg.historyPruneBlock = history.PrunePoints[params.SepoliaGenesisHash].BlockNumber
|
||||
if p, err := history.NewPolicy(history.KeepPostMerge, params.SepoliaGenesisHash); err == nil {
|
||||
*cfg.historyPruneBlock = p.Target.BlockNumber
|
||||
}
|
||||
default:
|
||||
cfg.fsys = os.DirFS(".")
|
||||
cfg.filterQueryFile = ctx.String(filterQueryFileFlag.Name)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package beacon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
|
@ -29,6 +30,7 @@ import (
|
|||
"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/internal/telemetry"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/holiman/uint256"
|
||||
|
|
@ -273,12 +275,22 @@ func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, pa
|
|||
}
|
||||
}
|
||||
|
||||
// Verify the existence / non-existence of Amsterdam-specific header fields
|
||||
amsterdam := chain.Config().IsAmsterdam(header.Number, header.Time)
|
||||
if amsterdam && header.SlotNumber == nil {
|
||||
return errors.New("header is missing slotNumber")
|
||||
}
|
||||
if !amsterdam && header.SlotNumber != nil {
|
||||
return fmt.Errorf("invalid slotNumber: have %d, expected nil", *header.SlotNumber)
|
||||
if amsterdam {
|
||||
if header.BlockAccessListHash == nil {
|
||||
return errors.New("header is missing block access list hash")
|
||||
}
|
||||
if header.SlotNumber == nil {
|
||||
return errors.New("header is missing slotNumber")
|
||||
}
|
||||
} else {
|
||||
if header.BlockAccessListHash != nil {
|
||||
return fmt.Errorf("invalid block access list hash: have %x, expected nil", *header.BlockAccessListHash)
|
||||
}
|
||||
if header.SlotNumber != nil {
|
||||
return fmt.Errorf("invalid slotNumber: have %d, expected nil", *header.SlotNumber)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -351,9 +363,17 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
|
|||
|
||||
// FinalizeAndAssemble implements consensus.Engine, setting the final state and
|
||||
// assembling the block.
|
||||
func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
|
||||
func (beacon *Beacon) FinalizeAndAssemble(ctx context.Context, chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (result *types.Block, err error) {
|
||||
ctx, _, spanEnd := telemetry.StartSpan(ctx, "consensus.beacon.FinalizeAndAssemble",
|
||||
telemetry.Int64Attribute("block.number", int64(header.Number.Uint64())),
|
||||
telemetry.Int64Attribute("txs.count", int64(len(body.Transactions))),
|
||||
telemetry.Int64Attribute("withdrawals.count", int64(len(body.Withdrawals))),
|
||||
)
|
||||
defer spanEnd(&err)
|
||||
|
||||
if !beacon.IsPoSHeader(header) {
|
||||
return beacon.ethone.FinalizeAndAssemble(chain, header, state, body, receipts)
|
||||
block, delegateErr := beacon.ethone.FinalizeAndAssemble(ctx, chain, header, state, body, receipts)
|
||||
return block, delegateErr
|
||||
}
|
||||
shanghai := chain.Config().IsShanghai(header.Number, header.Time)
|
||||
if shanghai {
|
||||
|
|
@ -367,13 +387,20 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
|
|||
}
|
||||
}
|
||||
// Finalize and assemble the block.
|
||||
_, _, finalizeSpanEnd := telemetry.StartSpan(ctx, "consensus.beacon.Finalize")
|
||||
beacon.Finalize(chain, header, state, body)
|
||||
finalizeSpanEnd(nil)
|
||||
|
||||
// Assign the final state root to header.
|
||||
_, _, rootSpanEnd := telemetry.StartSpan(ctx, "consensus.beacon.IntermediateRoot")
|
||||
header.Root = state.IntermediateRoot(true)
|
||||
rootSpanEnd(nil)
|
||||
|
||||
// Assemble the final block.
|
||||
return types.NewBlock(header, body, receipts, trie.NewStackTrie(nil)), nil
|
||||
_, _, blockSpanEnd := telemetry.StartSpan(ctx, "consensus.beacon.NewBlock")
|
||||
block := types.NewBlock(header, body, receipts, trie.NewStackTrie(nil))
|
||||
blockSpanEnd(nil)
|
||||
return block, nil
|
||||
}
|
||||
|
||||
// Seal generates a new sealing request for the given input block and pushes
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package clique
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
|
@ -581,7 +582,7 @@ func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Heade
|
|||
|
||||
// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
|
||||
// nor block rewards given, and returns the final block.
|
||||
func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
|
||||
func (c *Clique) FinalizeAndAssemble(ctx context.Context, chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
|
||||
if len(body.Withdrawals) > 0 {
|
||||
return nil, errors.New("clique does not support withdrawals")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
package consensus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -92,7 +93,7 @@ type Engine interface {
|
|||
//
|
||||
// Note: The block header and state database might be updated to reflect any
|
||||
// consensus rules that happen at finalization (e.g. block rewards).
|
||||
FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error)
|
||||
FinalizeAndAssemble(ctx context.Context, chain ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error)
|
||||
|
||||
// Seal generates a new sealing request for the given input block and pushes
|
||||
// the result into the given channel.
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package ethash
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
|
@ -513,7 +514,7 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.
|
|||
|
||||
// FinalizeAndAssemble implements consensus.Engine, accumulating the block and
|
||||
// uncle rewards, setting the final state and assembling the block.
|
||||
func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
|
||||
func (ethash *Ethash) FinalizeAndAssemble(ctx context.Context, chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
|
||||
if len(body.Withdrawals) > 0 {
|
||||
return nil, errors.New("ethash does not support withdrawals")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -194,9 +194,8 @@ type BlockChainConfig struct {
|
|||
SnapshotNoBuild bool // Whether the background generation is allowed
|
||||
SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it
|
||||
|
||||
// This defines the cutoff block for history expiry.
|
||||
// Blocks before this number may be unavailable in the chain database.
|
||||
ChainHistoryMode history.HistoryMode
|
||||
// HistoryPolicy defines the chain history pruning intent.
|
||||
HistoryPolicy history.HistoryPolicy
|
||||
|
||||
// Misc options
|
||||
NoPrefetch bool // Whether to disable heuristic state prefetching when processing blocks
|
||||
|
|
@ -227,13 +226,13 @@ type BlockChainConfig struct {
|
|||
// Note the returned object is safe to modify!
|
||||
func DefaultConfig() *BlockChainConfig {
|
||||
return &BlockChainConfig{
|
||||
TrieCleanLimit: 256,
|
||||
TrieDirtyLimit: 256,
|
||||
TrieTimeLimit: 5 * time.Minute,
|
||||
StateScheme: rawdb.HashScheme,
|
||||
SnapshotLimit: 256,
|
||||
SnapshotWait: true,
|
||||
ChainHistoryMode: history.KeepAll,
|
||||
TrieCleanLimit: 256,
|
||||
TrieDirtyLimit: 256,
|
||||
TrieTimeLimit: 5 * time.Minute,
|
||||
StateScheme: rawdb.HashScheme,
|
||||
SnapshotLimit: 256,
|
||||
SnapshotWait: true,
|
||||
HistoryPolicy: history.HistoryPolicy{Mode: history.KeepAll},
|
||||
// Transaction indexing is disabled by default.
|
||||
// This is appropriate for most unit tests.
|
||||
TxLookupLimit: -1,
|
||||
|
|
@ -715,82 +714,44 @@ func (bc *BlockChain) loadLastState() error {
|
|||
|
||||
// initializeHistoryPruning sets bc.historyPrunePoint.
|
||||
func (bc *BlockChain) initializeHistoryPruning(latest uint64) error {
|
||||
var (
|
||||
freezerTail, _ = bc.db.Tail()
|
||||
genesisHash = bc.genesisBlock.Hash()
|
||||
mergePoint = history.MergePrunePoints[genesisHash]
|
||||
praguePoint = history.PraguePrunePoints[genesisHash]
|
||||
)
|
||||
switch bc.cfg.ChainHistoryMode {
|
||||
case history.KeepAll:
|
||||
if freezerTail == 0 {
|
||||
return nil
|
||||
}
|
||||
// The database was pruned somehow, so we need to figure out if it's a known
|
||||
// configuration or an error.
|
||||
if mergePoint != nil && freezerTail == mergePoint.BlockNumber {
|
||||
bc.historyPrunePoint.Store(mergePoint)
|
||||
return nil
|
||||
}
|
||||
if praguePoint != nil && freezerTail == praguePoint.BlockNumber {
|
||||
bc.historyPrunePoint.Store(praguePoint)
|
||||
return nil
|
||||
}
|
||||
log.Error("Chain history database is pruned with unknown configuration", "tail", freezerTail)
|
||||
return errors.New("unexpected database tail")
|
||||
freezerTail, _ := bc.db.Tail()
|
||||
policy := bc.cfg.HistoryPolicy
|
||||
|
||||
case history.KeepPostMerge:
|
||||
if mergePoint == nil {
|
||||
return errors.New("history pruning requested for unknown network")
|
||||
switch policy.Mode {
|
||||
case history.KeepAll:
|
||||
if freezerTail > 0 {
|
||||
// Database was pruned externally. Record the actual state.
|
||||
log.Warn("Chain history database is pruned", "tail", freezerTail, "mode", policy.Mode)
|
||||
bc.historyPrunePoint.Store(&history.PrunePoint{
|
||||
BlockNumber: freezerTail,
|
||||
BlockHash: bc.GetCanonicalHash(freezerTail),
|
||||
})
|
||||
}
|
||||
if freezerTail == 0 && latest != 0 {
|
||||
log.Error(fmt.Sprintf("Chain history mode is configured as %q, but database is not pruned.", bc.cfg.ChainHistoryMode.String()))
|
||||
log.Error("Run 'geth prune-history --history.chain postmerge' to prune pre-merge history.")
|
||||
return errors.New("history pruning requested via configuration")
|
||||
}
|
||||
// Check if DB is pruned further than requested (to Prague).
|
||||
if praguePoint != nil && freezerTail == praguePoint.BlockNumber {
|
||||
log.Error("Chain history database is pruned to Prague block, but postmerge mode was requested.")
|
||||
log.Error("History cannot be unpruned. To restore history, use 'geth import-history'.")
|
||||
log.Error("If you intended to keep post-Prague history, use '--history.chain postprague' instead.")
|
||||
return errors.New("database pruned beyond requested history mode")
|
||||
}
|
||||
if freezerTail > 0 && freezerTail != mergePoint.BlockNumber {
|
||||
return errors.New("chain history database pruned to unknown block")
|
||||
}
|
||||
bc.historyPrunePoint.Store(mergePoint)
|
||||
return nil
|
||||
|
||||
case history.KeepPostPrague:
|
||||
if praguePoint == nil {
|
||||
return errors.New("history pruning requested for unknown network")
|
||||
}
|
||||
// Check if already at the prague prune point.
|
||||
if freezerTail == praguePoint.BlockNumber {
|
||||
bc.historyPrunePoint.Store(praguePoint)
|
||||
case history.KeepPostMerge, history.KeepPostPrague:
|
||||
target := policy.Target
|
||||
// Already at the target.
|
||||
if freezerTail == target.BlockNumber {
|
||||
bc.historyPrunePoint.Store(target)
|
||||
return nil
|
||||
}
|
||||
// Check if database needs pruning.
|
||||
if latest != 0 {
|
||||
if freezerTail == 0 {
|
||||
log.Error(fmt.Sprintf("Chain history mode is configured as %q, but database is not pruned.", bc.cfg.ChainHistoryMode.String()))
|
||||
log.Error("Run 'geth prune-history --history.chain postprague' to prune pre-Prague history.")
|
||||
return errors.New("history pruning requested via configuration")
|
||||
}
|
||||
if mergePoint != nil && freezerTail == mergePoint.BlockNumber {
|
||||
log.Error(fmt.Sprintf("Chain history mode is configured as %q, but database is only pruned to merge block.", bc.cfg.ChainHistoryMode.String()))
|
||||
log.Error("Run 'geth prune-history --history.chain postprague' to prune pre-Prague history.")
|
||||
return errors.New("history pruning requested via configuration")
|
||||
}
|
||||
log.Error("Chain history database is pruned to unknown block", "tail", freezerTail)
|
||||
return errors.New("unexpected database tail")
|
||||
// Database is pruned beyond the target.
|
||||
if freezerTail > target.BlockNumber {
|
||||
return fmt.Errorf("database pruned beyond requested history (tail=%d, target=%d)", freezerTail, target.BlockNumber)
|
||||
}
|
||||
// Fresh database (latest == 0), will sync from prague point.
|
||||
bc.historyPrunePoint.Store(praguePoint)
|
||||
// Database needs pruning (freezerTail < target).
|
||||
if latest != 0 {
|
||||
log.Error(fmt.Sprintf("Chain history mode is configured as %q, but database is not pruned to the target block.", policy.Mode.String()))
|
||||
log.Error(fmt.Sprintf("Run 'geth prune-history --history.chain %s' to prune history.", policy.Mode.String()))
|
||||
return errors.New("history pruning required")
|
||||
}
|
||||
// Fresh database (latest == 0), will sync from target point.
|
||||
bc.historyPrunePoint.Store(target)
|
||||
return nil
|
||||
|
||||
default:
|
||||
return fmt.Errorf("invalid history mode: %d", bc.cfg.ChainHistoryMode)
|
||||
return fmt.Errorf("invalid history mode: %d", policy.Mode)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2209,24 +2170,18 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash,
|
|||
// 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
|
||||
witnessStats *stateless.WitnessStats
|
||||
)
|
||||
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 config.StatelessSelfValidation || config.MakeWitness {
|
||||
witness, err = stateless.NewWitness(block.Header(), bc)
|
||||
witness, err = stateless.NewWitness(block.Header(), bc, config.EnableWitnessStats)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if config.EnableWitnessStats {
|
||||
witnessStats = stateless.NewWitnessStats()
|
||||
}
|
||||
}
|
||||
statedb.StartPrefetcher("chain", witness, witnessStats)
|
||||
statedb.StartPrefetcher("chain", witness)
|
||||
defer statedb.StopPrefetcher()
|
||||
}
|
||||
|
||||
|
|
@ -2345,8 +2300,8 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash,
|
|||
stats.BlockWrite = time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.DatabaseCommits
|
||||
}
|
||||
// Report the collected witness statistics
|
||||
if witnessStats != nil {
|
||||
witnessStats.ReportMetrics(block.NumberU64())
|
||||
if witness != nil {
|
||||
witness.ReportMetrics(block.NumberU64())
|
||||
}
|
||||
elapsed := time.Since(startTime) + 1 // prevent zero division
|
||||
stats.TotalTime = elapsed
|
||||
|
|
@ -2628,6 +2583,7 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Header) error
|
|||
// as the txlookups should be changed atomically, and all subsequent
|
||||
// reads should be blocked until the mutation is complete.
|
||||
bc.txLookupLock.Lock()
|
||||
defer bc.txLookupLock.Unlock()
|
||||
|
||||
// Reorg can be executed, start reducing the chain's old blocks and appending
|
||||
// the new blocks
|
||||
|
|
@ -2730,9 +2686,6 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Header) error
|
|||
// Reset the tx lookup cache to clear stale txlookup cache.
|
||||
bc.txLookupCache.Purge()
|
||||
|
||||
// Release the tx-lookup lock after mutation.
|
||||
bc.txLookupLock.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -296,6 +296,14 @@ func (bc *BlockChain) GetReceiptsRLP(hash common.Hash) rlp.RawValue {
|
|||
return rawdb.ReadReceiptsRLP(bc.db, hash, number)
|
||||
}
|
||||
|
||||
func (bc *BlockChain) GetAccessListRLP(hash common.Hash) rlp.RawValue {
|
||||
number, ok := rawdb.ReadHeaderNumber(bc.db, hash)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return rawdb.ReadAccessListRLP(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 {
|
||||
|
|
@ -468,7 +476,7 @@ func (bc *BlockChain) TxIndexProgress() (TxIndexProgress, error) {
|
|||
}
|
||||
|
||||
// StateIndexProgress returns the historical state indexing progress.
|
||||
func (bc *BlockChain) StateIndexProgress() (uint64, error) {
|
||||
func (bc *BlockChain) StateIndexProgress() (uint64, uint64, error) {
|
||||
return bc.triedb.IndexProgress()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/beacon"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core/history"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
|
|
@ -4337,26 +4336,13 @@ func TestInsertChainWithCutoff(t *testing.T) {
|
|||
func testInsertChainWithCutoff(t *testing.T, cutoff uint64, ancientLimit uint64, genesis *Genesis, blocks []*types.Block, receipts []types.Receipts) {
|
||||
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true)))
|
||||
|
||||
// Add a known pruning point for the duration of the test.
|
||||
ghash := genesis.ToBlock().Hash()
|
||||
cutoffBlock := blocks[cutoff-1]
|
||||
history.PrunePoints[ghash] = &history.PrunePoint{
|
||||
BlockNumber: cutoffBlock.NumberU64(),
|
||||
BlockHash: cutoffBlock.Hash(),
|
||||
}
|
||||
defer func() {
|
||||
delete(history.PrunePoints, ghash)
|
||||
}()
|
||||
|
||||
// Enable pruning in cache config.
|
||||
config := DefaultConfig().WithStateScheme(rawdb.PathScheme)
|
||||
config.ChainHistoryMode = history.KeepPostMerge
|
||||
|
||||
db, _ := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
|
||||
defer db.Close()
|
||||
|
||||
options := DefaultConfig().WithStateScheme(rawdb.PathScheme)
|
||||
chain, _ := NewBlockChain(db, genesis, beacon.New(ethash.NewFaker()), options)
|
||||
chain, _ := NewBlockChain(db, genesis, beacon.New(ethash.NewFaker()), DefaultConfig().WithStateScheme(rawdb.PathScheme))
|
||||
defer chain.Stop()
|
||||
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
|
|
@ -411,7 +412,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
|||
}
|
||||
|
||||
body := types.Body{Transactions: b.txs, Uncles: b.uncles, Withdrawals: b.withdrawals}
|
||||
block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, &body, b.receipts)
|
||||
block, err := b.engine.FinalizeAndAssemble(context.Background(), cm, b.header, statedb, &body, b.receipts)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,10 +66,6 @@ var (
|
|||
// have enough funds for transfer(topmost call only).
|
||||
ErrInsufficientFundsForTransfer = errors.New("insufficient funds for transfer")
|
||||
|
||||
// ErrMaxInitCodeSizeExceeded is returned if creation transaction provides the init code bigger
|
||||
// than init code size limit.
|
||||
ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded")
|
||||
|
||||
// ErrInsufficientBalanceWitness is returned if the transaction sender has enough
|
||||
// funds to cover the transfer, but not enough to pay for witness access/modification
|
||||
// costs for the transaction
|
||||
|
|
|
|||
169
core/eth_transfer_logs_test.go
Normal file
169
core/eth_transfer_logs_test.go
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
// Copyright 2026 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/beacon"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var ethTransferTestCode = common.FromHex("6080604052600436106100345760003560e01c8063574ffc311461003957806366e41cb714610090578063f8a8fd6d1461009a575b600080fd5b34801561004557600080fd5b5061004e6100a4565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100986100ac565b005b6100a26100f5565b005b63deadbeef81565b7f38e80b5c85ba49b7280ccc8f22548faa62ae30d5a008a1b168fba5f47f5d1ee560405160405180910390a1631234567873ffffffffffffffffffffffffffffffffffffffff16ff5b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405160405180910390a163deadbeef73ffffffffffffffffffffffffffffffffffffffff166002348161014657fe5b046040516024016040516020818303038152906040527f66e41cb7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040518082805190602001908083835b602083106101fd57805182526020820191506020810190506020830392506101da565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d806000811461025f576040519150601f19603f3d011682016040523d82523d6000602084013e610264565b606091505b50505056fea265627a7a723158202cce817a434785d8560c200762f972d453ccd30694481be7545f9035a512826364736f6c63430005100032")
|
||||
|
||||
/*
|
||||
pragma solidity >=0.4.22 <0.6.0;
|
||||
|
||||
contract TestLogs {
|
||||
|
||||
address public constant target_contract = 0x00000000000000000000000000000000DeaDBeef;
|
||||
address payable constant selfdestruct_addr = 0x0000000000000000000000000000000012345678;
|
||||
|
||||
event Response(bool success, bytes data);
|
||||
event TestEvent();
|
||||
event TestEvent2();
|
||||
|
||||
function test() public payable {
|
||||
emit TestEvent();
|
||||
target_contract.call.value(msg.value/2)(abi.encodeWithSignature("test2()"));
|
||||
}
|
||||
function test2() public payable {
|
||||
emit TestEvent2();
|
||||
selfdestruct(selfdestruct_addr);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// TestEthTransferLogs tests EIP-7708 ETH transfer log output by simulating a
|
||||
// scenario including transaction, CALL and SELFDESTRUCT value transfers, and
|
||||
// also "ordinary" logs emitted. The same scenario is also tested with no value
|
||||
// transferred.
|
||||
func TestEthTransferLogs(t *testing.T) {
|
||||
testEthTransferLogs(t, 1_000_000_000)
|
||||
testEthTransferLogs(t, 0)
|
||||
}
|
||||
|
||||
func testEthTransferLogs(t *testing.T, value uint64) {
|
||||
var (
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
addr2 = common.HexToAddress("cafebabe") // caller
|
||||
addr3 = common.HexToAddress("deadbeef") // callee
|
||||
addr4 = common.HexToAddress("12345678") // selfdestruct target
|
||||
testEvent = crypto.Keccak256Hash([]byte("TestEvent()"))
|
||||
testEvent2 = crypto.Keccak256Hash([]byte("TestEvent2()"))
|
||||
config = *params.MergedTestChainConfig
|
||||
signer = types.LatestSigner(&config)
|
||||
engine = beacon.New(ethash.NewFaker())
|
||||
)
|
||||
|
||||
//TODO remove this hacky config initialization when final Amsterdam config is available
|
||||
config.AmsterdamTime = new(uint64)
|
||||
blobConfig := *config.BlobScheduleConfig
|
||||
blobConfig.Amsterdam = blobConfig.Osaka
|
||||
config.BlobScheduleConfig = &blobConfig
|
||||
|
||||
gspec := &Genesis{
|
||||
Config: &config,
|
||||
Alloc: types.GenesisAlloc{
|
||||
addr1: {Balance: newGwei(1000000000)},
|
||||
addr2: {Code: ethTransferTestCode},
|
||||
addr3: {Code: ethTransferTestCode},
|
||||
},
|
||||
}
|
||||
_, blocks, receipts := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
|
||||
tx := types.MustSignNewTx(key1, signer, &types.DynamicFeeTx{
|
||||
ChainID: gspec.Config.ChainID,
|
||||
Nonce: 0,
|
||||
To: &addr2,
|
||||
Gas: 500_000,
|
||||
GasFeeCap: newGwei(5),
|
||||
GasTipCap: newGwei(5),
|
||||
Value: big.NewInt(int64(value)),
|
||||
Data: common.FromHex("f8a8fd6d"),
|
||||
})
|
||||
b.AddTx(tx)
|
||||
})
|
||||
|
||||
blockHash := blocks[0].Hash()
|
||||
txHash := blocks[0].Transactions()[0].Hash()
|
||||
addr2hash := func(addr common.Address) (hash common.Hash) {
|
||||
copy(hash[12:], addr[:])
|
||||
return
|
||||
}
|
||||
u256 := func(amount uint64) []byte {
|
||||
data := make([]byte, 32)
|
||||
binary.BigEndian.PutUint64(data[24:], amount)
|
||||
return data
|
||||
}
|
||||
|
||||
var expLogs = []*types.Log{
|
||||
{
|
||||
Address: params.SystemAddress,
|
||||
Topics: []common.Hash{params.EthTransferLogEvent, addr2hash(addr1), addr2hash(addr2)},
|
||||
Data: u256(value),
|
||||
},
|
||||
{
|
||||
Address: addr2,
|
||||
Topics: []common.Hash{testEvent},
|
||||
Data: nil,
|
||||
},
|
||||
{
|
||||
Address: params.SystemAddress,
|
||||
Topics: []common.Hash{params.EthTransferLogEvent, addr2hash(addr2), addr2hash(addr3)},
|
||||
Data: u256(value / 2),
|
||||
},
|
||||
{
|
||||
Address: addr3,
|
||||
Topics: []common.Hash{testEvent2},
|
||||
Data: nil,
|
||||
},
|
||||
{
|
||||
Address: params.SystemAddress,
|
||||
Topics: []common.Hash{params.EthTransferLogEvent, addr2hash(addr3), addr2hash(addr4)},
|
||||
Data: u256(value / 2),
|
||||
},
|
||||
}
|
||||
if value == 0 {
|
||||
// no ETH transfer logs expected with zero value
|
||||
expLogs = []*types.Log{expLogs[1], expLogs[3]}
|
||||
}
|
||||
for i, log := range expLogs {
|
||||
log.BlockNumber = 1
|
||||
log.BlockHash = blockHash
|
||||
log.BlockTimestamp = 10
|
||||
log.TxIndex = 0
|
||||
log.TxHash = txHash
|
||||
log.Index = uint(i)
|
||||
}
|
||||
|
||||
if len(expLogs) != len(receipts[0][0].Logs) {
|
||||
t.Fatalf("Incorrect number of logs (expected: %d, got: %d)", len(expLogs), len(receipts[0][0].Logs))
|
||||
}
|
||||
for i, log := range receipts[0][0].Logs {
|
||||
if !reflect.DeepEqual(expLogs[i], log) {
|
||||
t.Fatalf("Incorrect log at index %d (expected: %v, got: %v)", i, expLogs[i], log)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"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/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
|
|
@ -138,7 +139,10 @@ func CanTransfer(db vm.StateDB, addr common.Address, amount *uint256.Int) bool {
|
|||
}
|
||||
|
||||
// Transfer subtracts amount from sender and adds amount to recipient using the given Db
|
||||
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *uint256.Int) {
|
||||
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *uint256.Int, rules *params.Rules) {
|
||||
db.SubBalance(sender, amount, tracing.BalanceChangeTransfer)
|
||||
db.AddBalance(recipient, amount, tracing.BalanceChangeTransfer)
|
||||
if rules.IsAmsterdam && !amount.IsZero() && sender != recipient {
|
||||
db.AddLog(types.EthTransferLog(sender, recipient, amount))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,9 +41,7 @@ func TestSingleMatch(t *testing.T) {
|
|||
t.Fatalf("Invalid length of matches (got %d, expected 1)", len(matches))
|
||||
}
|
||||
if matches[0] != lvIndex {
|
||||
if len(matches) != 1 {
|
||||
t.Fatalf("Incorrect match returned (got %d, expected %d)", matches[0], lvIndex)
|
||||
}
|
||||
t.Fatalf("Incorrect match returned (got %d, expected %d)", matches[0], lvIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,57 +77,62 @@ func (m *HistoryMode) UnmarshalText(text []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// PrunePoint identifies a specific block for history pruning.
|
||||
type PrunePoint struct {
|
||||
BlockNumber uint64
|
||||
BlockHash common.Hash
|
||||
}
|
||||
|
||||
// MergePrunePoints contains the pre-defined history pruning cutoff blocks for known networks.
|
||||
// They point to the first post-merge block. Any pruning should truncate *up to* but excluding
|
||||
// the given block.
|
||||
var MergePrunePoints = map[common.Hash]*PrunePoint{
|
||||
// mainnet
|
||||
params.MainnetGenesisHash: {
|
||||
BlockNumber: 15537393,
|
||||
BlockHash: common.HexToHash("0x55b11b918355b1ef9c5db810302ebad0bf2544255b530cdce90674d5887bb286"),
|
||||
// staticPrunePoints contains the pre-defined history pruning cutoff blocks for
|
||||
// known networks, keyed by history mode and genesis hash. They point to the first
|
||||
// block after the respective fork. Any pruning should truncate *up to* but
|
||||
// excluding the given block.
|
||||
var staticPrunePoints = map[HistoryMode]map[common.Hash]*PrunePoint{
|
||||
KeepPostMerge: {
|
||||
params.MainnetGenesisHash: {
|
||||
BlockNumber: 15537393,
|
||||
BlockHash: common.HexToHash("0x55b11b918355b1ef9c5db810302ebad0bf2544255b530cdce90674d5887bb286"),
|
||||
},
|
||||
params.SepoliaGenesisHash: {
|
||||
BlockNumber: 1450409,
|
||||
BlockHash: common.HexToHash("0x229f6b18ca1552f1d5146deceb5387333f40dc6275aebee3f2c5c4ece07d02db"),
|
||||
},
|
||||
},
|
||||
// sepolia
|
||||
params.SepoliaGenesisHash: {
|
||||
BlockNumber: 1450409,
|
||||
BlockHash: common.HexToHash("0x229f6b18ca1552f1d5146deceb5387333f40dc6275aebee3f2c5c4ece07d02db"),
|
||||
KeepPostPrague: {
|
||||
params.MainnetGenesisHash: {
|
||||
BlockNumber: 22431084,
|
||||
BlockHash: common.HexToHash("0x50c8cab760b2948349c590461b166773c45d8f4858cccf5a43025ab2960152e8"),
|
||||
},
|
||||
params.SepoliaGenesisHash: {
|
||||
BlockNumber: 7836331,
|
||||
BlockHash: common.HexToHash("0xe6571beb68bf24dbd8a6ba354518996920c55a3f8d8fdca423e391b8ad071f22"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// PraguePrunePoints contains the pre-defined history pruning cutoff blocks for the Prague
|
||||
// (Pectra) upgrade. They point to the first post-Prague block. Any pruning should truncate
|
||||
// *up to* but excluding the given block.
|
||||
var PraguePrunePoints = map[common.Hash]*PrunePoint{
|
||||
// mainnet - first Prague block (May 7, 2025)
|
||||
params.MainnetGenesisHash: {
|
||||
BlockNumber: 22431084,
|
||||
BlockHash: common.HexToHash("0x50c8cab760b2948349c590461b166773c45d8f4858cccf5a43025ab2960152e8"),
|
||||
},
|
||||
// sepolia - first Prague block (March 5, 2025)
|
||||
params.SepoliaGenesisHash: {
|
||||
BlockNumber: 7836331,
|
||||
BlockHash: common.HexToHash("0xe6571beb68bf24dbd8a6ba354518996920c55a3f8d8fdca423e391b8ad071f22"),
|
||||
},
|
||||
// HistoryPolicy describes the configured history pruning strategy. It captures
|
||||
// user intent as opposed to the actual DB state.
|
||||
type HistoryPolicy struct {
|
||||
Mode HistoryMode
|
||||
// Static prune point for PostMerge/PostPrague, nil otherwise.
|
||||
Target *PrunePoint
|
||||
}
|
||||
|
||||
// PrunePoints is an alias for MergePrunePoints for backward compatibility.
|
||||
// Deprecated: Use GetPrunePoint or MergePrunePoints directly.
|
||||
var PrunePoints = MergePrunePoints
|
||||
|
||||
// GetPrunePoint returns the prune point for the given genesis hash and history mode.
|
||||
// Returns nil if no prune point is defined for the given combination.
|
||||
func GetPrunePoint(genesisHash common.Hash, mode HistoryMode) *PrunePoint {
|
||||
// NewPolicy constructs a HistoryPolicy from the given mode and genesis hash.
|
||||
func NewPolicy(mode HistoryMode, genesisHash common.Hash) (HistoryPolicy, error) {
|
||||
switch mode {
|
||||
case KeepPostMerge:
|
||||
return MergePrunePoints[genesisHash]
|
||||
case KeepPostPrague:
|
||||
return PraguePrunePoints[genesisHash]
|
||||
case KeepAll:
|
||||
return HistoryPolicy{Mode: KeepAll}, nil
|
||||
|
||||
case KeepPostMerge, KeepPostPrague:
|
||||
point := staticPrunePoints[mode][genesisHash]
|
||||
if point == nil {
|
||||
return HistoryPolicy{}, fmt.Errorf("%s history pruning not available for network %s", mode, genesisHash.Hex())
|
||||
}
|
||||
return HistoryPolicy{Mode: mode, Target: point}, nil
|
||||
|
||||
default:
|
||||
return nil
|
||||
return HistoryPolicy{}, fmt.Errorf("invalid history mode: %d", mode)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
58
core/history/historymode_test.go
Normal file
58
core/history/historymode_test.go
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright 2026 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package history
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func TestNewPolicy(t *testing.T) {
|
||||
// KeepAll: no target.
|
||||
p, err := NewPolicy(KeepAll, params.MainnetGenesisHash)
|
||||
if err != nil {
|
||||
t.Fatalf("KeepAll: %v", err)
|
||||
}
|
||||
if p.Mode != KeepAll || p.Target != nil {
|
||||
t.Errorf("KeepAll: unexpected policy %+v", p)
|
||||
}
|
||||
|
||||
// PostMerge: resolves known mainnet prune point.
|
||||
p, err = NewPolicy(KeepPostMerge, params.MainnetGenesisHash)
|
||||
if err != nil {
|
||||
t.Fatalf("PostMerge: %v", err)
|
||||
}
|
||||
if p.Target == nil || p.Target.BlockNumber != 15537393 {
|
||||
t.Errorf("PostMerge: unexpected target %+v", p.Target)
|
||||
}
|
||||
|
||||
// PostPrague: resolves known mainnet prune point.
|
||||
p, err = NewPolicy(KeepPostPrague, params.MainnetGenesisHash)
|
||||
if err != nil {
|
||||
t.Fatalf("PostPrague: %v", err)
|
||||
}
|
||||
if p.Target == nil || p.Target.BlockNumber != 22431084 {
|
||||
t.Errorf("PostPrague: unexpected target %+v", p.Target)
|
||||
}
|
||||
|
||||
// PostMerge on unknown network: error.
|
||||
if _, err = NewPolicy(KeepPostMerge, common.HexToHash("0xdeadbeef")); err == nil {
|
||||
t.Fatal("PostMerge unknown network: expected error")
|
||||
}
|
||||
}
|
||||
|
|
@ -26,6 +26,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
|
|
@ -605,6 +606,55 @@ func DeleteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
|||
}
|
||||
}
|
||||
|
||||
// HasAccessList verifies the existence of a block access list for a block.
|
||||
func HasAccessList(db ethdb.Reader, hash common.Hash, number uint64) bool {
|
||||
has, _ := db.Has(accessListKey(number, hash))
|
||||
return has
|
||||
}
|
||||
|
||||
// ReadAccessListRLP retrieves the RLP-encoded block access list for a block from KV.
|
||||
func ReadAccessListRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
data, _ := db.Get(accessListKey(number, hash))
|
||||
return data
|
||||
}
|
||||
|
||||
// ReadAccessList retrieves and decodes the block access list for a block.
|
||||
func ReadAccessList(db ethdb.Reader, hash common.Hash, number uint64) *bal.BlockAccessList {
|
||||
data := ReadAccessListRLP(db, hash, number)
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
b := new(bal.BlockAccessList)
|
||||
if err := rlp.DecodeBytes(data, b); err != nil {
|
||||
log.Error("Invalid BAL RLP", "hash", hash, "err", err)
|
||||
return nil
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// WriteAccessList RLP-encodes and stores a block access list in the active KV store.
|
||||
func WriteAccessList(db ethdb.KeyValueWriter, hash common.Hash, number uint64, b *bal.BlockAccessList) {
|
||||
bytes, err := rlp.EncodeToBytes(b)
|
||||
if err != nil {
|
||||
log.Crit("Failed to encode BAL", "err", err)
|
||||
}
|
||||
WriteAccessListRLP(db, hash, number, bytes)
|
||||
}
|
||||
|
||||
// WriteAccessListRLP stores a pre-encoded block access list in the active KV store.
|
||||
func WriteAccessListRLP(db ethdb.KeyValueWriter, hash common.Hash, number uint64, encoded rlp.RawValue) {
|
||||
if err := db.Put(accessListKey(number, hash), encoded); err != nil {
|
||||
log.Crit("Failed to store BAL", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteAccessList removes a block access list from the active KV store.
|
||||
func DeleteAccessList(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
||||
if err := db.Delete(accessListKey(number, hash)); err != nil {
|
||||
log.Crit("Failed to delete BAL", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReceiptLogs is a barebone version of ReceiptForStorage which only keeps
|
||||
// the list of logs. When decoding a stored receipt into this object we
|
||||
// avoid creating the bloom filter.
|
||||
|
|
@ -659,13 +709,25 @@ func ReadBlock(db ethdb.Reader, hash common.Hash, number uint64) *types.Block {
|
|||
if body == nil {
|
||||
return nil
|
||||
}
|
||||
return types.NewBlockWithHeader(header).WithBody(*body)
|
||||
block := types.NewBlockWithHeader(header).WithBody(*body)
|
||||
|
||||
// Best-effort assembly of the block access list from the database.
|
||||
if header.BlockAccessListHash != nil {
|
||||
al := ReadAccessList(db, hash, number)
|
||||
block = block.WithAccessListUnsafe(al)
|
||||
}
|
||||
return block
|
||||
}
|
||||
|
||||
// WriteBlock serializes a block into the database, header and body separately.
|
||||
func WriteBlock(db ethdb.KeyValueWriter, block *types.Block) {
|
||||
WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
|
||||
hash, number := block.Hash(), block.NumberU64()
|
||||
WriteBody(db, hash, number, block.Body())
|
||||
WriteHeader(db, block.Header())
|
||||
|
||||
if accessList := block.AccessList(); accessList != nil {
|
||||
WriteAccessList(db, hash, number, accessList)
|
||||
}
|
||||
}
|
||||
|
||||
// WriteAncientBlocks writes entire block data into ancient store and returns the total written size.
|
||||
|
|
|
|||
|
|
@ -27,10 +27,12 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/keccak"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// Tests block header storage and retrieval operations.
|
||||
|
|
@ -899,3 +901,78 @@ func TestHeadersRLPStorage(t *testing.T) {
|
|||
checkSequence(1, 1) // Only block 1
|
||||
checkSequence(1, 2) // Genesis + block 1
|
||||
}
|
||||
|
||||
func makeTestBAL(t *testing.T) (rlp.RawValue, *bal.BlockAccessList) {
|
||||
t.Helper()
|
||||
|
||||
cb := bal.NewConstructionBlockAccessList()
|
||||
addr := common.HexToAddress("0x1111111111111111111111111111111111111111")
|
||||
cb.AccountRead(addr)
|
||||
cb.StorageRead(addr, common.BytesToHash([]byte{0x01}))
|
||||
cb.StorageWrite(0, addr, common.BytesToHash([]byte{0x02}), common.BytesToHash([]byte{0xaa}))
|
||||
cb.BalanceChange(0, addr, uint256.NewInt(100))
|
||||
cb.NonceChange(addr, 0, 1)
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := cb.EncodeRLP(&buf); err != nil {
|
||||
t.Fatalf("failed to encode BAL: %v", err)
|
||||
}
|
||||
encoded := buf.Bytes()
|
||||
|
||||
var decoded bal.BlockAccessList
|
||||
if err := rlp.DecodeBytes(encoded, &decoded); err != nil {
|
||||
t.Fatalf("failed to decode BAL: %v", err)
|
||||
}
|
||||
return encoded, &decoded
|
||||
}
|
||||
|
||||
// TestBALStorage tests write/read/delete of BALs in the KV store.
|
||||
func TestBALStorage(t *testing.T) {
|
||||
db := NewMemoryDatabase()
|
||||
|
||||
hash := common.BytesToHash([]byte{0x03, 0x14})
|
||||
number := uint64(42)
|
||||
|
||||
// Check that no BAL exists in a new database.
|
||||
if HasAccessList(db, hash, number) {
|
||||
t.Fatal("BAL found in new database")
|
||||
}
|
||||
if b := ReadAccessList(db, hash, number); b != nil {
|
||||
t.Fatalf("non existent BAL returned: %v", b)
|
||||
}
|
||||
|
||||
// Write a BAL and verify it can be read back.
|
||||
encoded, testBAL := makeTestBAL(t)
|
||||
WriteAccessList(db, hash, number, testBAL)
|
||||
|
||||
if !HasAccessList(db, hash, number) {
|
||||
t.Fatal("HasAccessList returned false after write")
|
||||
}
|
||||
if blob := ReadAccessListRLP(db, hash, number); len(blob) == 0 {
|
||||
t.Fatal("ReadAccessListRLP returned empty after write")
|
||||
}
|
||||
if b := ReadAccessList(db, hash, number); b == nil {
|
||||
t.Fatal("ReadAccessList returned nil after write")
|
||||
} else if b.Hash() != testBAL.Hash() {
|
||||
t.Fatalf("BAL hash mismatch: got %x, want %x", b.Hash(), testBAL.Hash())
|
||||
}
|
||||
|
||||
// Also test WriteAccessListRLP with pre-encoded data.
|
||||
hash2 := common.BytesToHash([]byte{0x03, 0x15})
|
||||
WriteAccessListRLP(db, hash2, number, encoded)
|
||||
if b := ReadAccessList(db, hash2, number); b == nil {
|
||||
t.Fatal("ReadAccessList returned nil after WriteAccessListRLP")
|
||||
} else if b.Hash() != testBAL.Hash() {
|
||||
t.Fatalf("BAL hash mismatch after WriteAccessListRLP: got %x, want %x", b.Hash(), testBAL.Hash())
|
||||
}
|
||||
|
||||
// Delete the BAL and verify it's gone.
|
||||
DeleteAccessList(db, hash, number)
|
||||
|
||||
if HasAccessList(db, hash, number) {
|
||||
t.Fatal("HasAccessList returned true after delete")
|
||||
}
|
||||
if b := ReadAccessList(db, hash, number); b != nil {
|
||||
t.Fatalf("deleted BAL returned: %v", b)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -413,6 +413,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
|||
tds stat
|
||||
numHashPairings stat
|
||||
hashNumPairings stat
|
||||
blockAccessList stat
|
||||
legacyTries stat
|
||||
stateLookups stat
|
||||
accountTries stat
|
||||
|
|
@ -480,10 +481,13 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
|||
receipts.add(size)
|
||||
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix) && len(key) == (len(headerPrefix)+8+common.HashLength+len(headerTDSuffix)):
|
||||
tds.add(size)
|
||||
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix) && len(key) == (len(headerPrefix)+8+common.HashLength+len(headerHashSuffix)):
|
||||
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix) && len(key) == (len(headerPrefix)+8+len(headerHashSuffix)):
|
||||
numHashPairings.add(size)
|
||||
case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
|
||||
hashNumPairings.add(size)
|
||||
case bytes.HasPrefix(key, accessListPrefix) && len(key) == len(accessListPrefix)+8+common.HashLength:
|
||||
blockAccessList.add(size)
|
||||
|
||||
case IsLegacyTrieNode(key, it.Value()):
|
||||
legacyTries.add(size)
|
||||
case bytes.HasPrefix(key, stateIDPrefix) && len(key) == len(stateIDPrefix)+common.HashLength:
|
||||
|
|
@ -625,6 +629,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
|||
{"Key-Value store", "Difficulties (deprecated)", tds.sizeString(), tds.countString()},
|
||||
{"Key-Value store", "Block number->hash", numHashPairings.sizeString(), numHashPairings.countString()},
|
||||
{"Key-Value store", "Block hash->number", hashNumPairings.sizeString(), hashNumPairings.countString()},
|
||||
{"Key-Value store", "Block accessList", blockAccessList.sizeString(), blockAccessList.countString()},
|
||||
{"Key-Value store", "Transaction index", txLookups.sizeString(), txLookups.countString()},
|
||||
{"Key-Value store", "Log index filter-map rows", filterMapRows.sizeString(), filterMapRows.countString()},
|
||||
{"Key-Value store", "Log index last-block-of-map", filterMapLastBlock.sizeString(), filterMapLastBlock.countString()},
|
||||
|
|
|
|||
|
|
@ -26,13 +26,7 @@ func atomicRename(src, dest string) error {
|
|||
if err := os.Rename(src, dest); err != nil {
|
||||
return err
|
||||
}
|
||||
dir, err := os.Open(filepath.Dir(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
return dir.Sync()
|
||||
return syncDir(filepath.Dir(src))
|
||||
}
|
||||
|
||||
// copyFrom copies data from 'srcPath' at offset 'offset' into 'destPath'.
|
||||
|
|
|
|||
49
core/rawdb/freezer_utils_unix.go
Normal file
49
core/rawdb/freezer_utils_unix.go
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2022 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package rawdb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// syncDir ensures that the directory metadata (e.g. newly renamed files)
|
||||
// is flushed to durable storage.
|
||||
func syncDir(name string) error {
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Some file systems do not support fsyncing directories (e.g. some FUSE
|
||||
// mounts). Ignore EINVAL in those cases.
|
||||
if err := f.Sync(); err != nil {
|
||||
if errors.Is(err, os.ErrInvalid) {
|
||||
return nil
|
||||
}
|
||||
if patherr, ok := err.(*os.PathError); ok && patherr.Err == syscall.EINVAL {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
26
core/rawdb/freezer_utils_windows.go
Normal file
26
core/rawdb/freezer_utils_windows.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright 2022 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package rawdb
|
||||
|
||||
// syncDir is a no-op on Windows. Fsyncing a directory handle is not
|
||||
// supported and returns "Access is denied".
|
||||
func syncDir(name string) error {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -112,6 +112,7 @@ var (
|
|||
|
||||
blockBodyPrefix = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body
|
||||
blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts
|
||||
accessListPrefix = []byte("j") // accessListPrefix + num (uint64 big endian) + hash -> block access list
|
||||
|
||||
txLookupPrefix = []byte("l") // txLookupPrefix + hash -> transaction/receipt lookup metadata
|
||||
bloomBitsPrefix = []byte("B") // bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash -> bloom bits
|
||||
|
|
@ -217,6 +218,11 @@ func blockReceiptsKey(number uint64, hash common.Hash) []byte {
|
|||
return append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
|
||||
}
|
||||
|
||||
// accessListKey = accessListPrefix + num (uint64 big endian) + hash
|
||||
func accessListKey(number uint64, hash common.Hash) []byte {
|
||||
return append(append(accessListPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
|
||||
}
|
||||
|
||||
// txLookupKey = txLookupPrefix + hash
|
||||
func txLookupKey(hash common.Hash) []byte {
|
||||
return append(txLookupPrefix, hash.Bytes()...)
|
||||
|
|
|
|||
|
|
@ -39,6 +39,10 @@ type Database interface {
|
|||
// Reader returns a state reader associated with the specified state root.
|
||||
Reader(root common.Hash) (Reader, error)
|
||||
|
||||
// Iteratee returns a state iteratee associated with the specified state root,
|
||||
// through which the account iterator and storage iterator can be created.
|
||||
Iteratee(root common.Hash) (Iteratee, error)
|
||||
|
||||
// OpenTrie opens the main account trie.
|
||||
OpenTrie(root common.Hash) (Trie, error)
|
||||
|
||||
|
|
@ -48,9 +52,6 @@ type Database interface {
|
|||
// TrieDB returns the underlying trie database for managing trie nodes.
|
||||
TrieDB() *triedb.Database
|
||||
|
||||
// Snapshot returns the underlying state snapshot.
|
||||
Snapshot() *snapshot.Tree
|
||||
|
||||
// Commit flushes all pending writes and finalizes the state transition,
|
||||
// committing the changes to the underlying storage. It returns an error
|
||||
// if the commit fails.
|
||||
|
|
@ -310,6 +311,12 @@ func (db *CachingDB) Commit(update *stateUpdate) error {
|
|||
return db.triedb.Update(update.root, update.originRoot, update.blockNumber, update.nodes, update.stateSet())
|
||||
}
|
||||
|
||||
// Iteratee returns a state iteratee associated with the specified state root,
|
||||
// through which the account iterator and storage iterator can be created.
|
||||
func (db *CachingDB) Iteratee(root common.Hash) (Iteratee, error) {
|
||||
return newStateIteratee(!db.triedb.IsVerkle(), root, db.triedb, db.snap)
|
||||
}
|
||||
|
||||
// mustCopyTrie returns a deep-copied trie.
|
||||
func mustCopyTrie(t Trie) Trie {
|
||||
switch t := t.(type) {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
|
|
@ -289,14 +288,15 @@ func (db *HistoricDB) TrieDB() *triedb.Database {
|
|||
return db.triedb
|
||||
}
|
||||
|
||||
// Snapshot returns the underlying state snapshot.
|
||||
func (db *HistoricDB) Snapshot() *snapshot.Tree {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Commit flushes all pending writes and finalizes the state transition,
|
||||
// committing the changes to the underlying storage. It returns an error
|
||||
// if the commit fails.
|
||||
func (db *HistoricDB) Commit(update *stateUpdate) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
// Iteratee returns a state iteratee associated with the specified state root,
|
||||
// through which the account iterator and storage iterator can be created.
|
||||
func (db *HistoricDB) Iteratee(root common.Hash) (Iteratee, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
|
|
|||
435
core/state/database_iterator.go
Normal file
435
core/state/database_iterator.go
Normal file
|
|
@ -0,0 +1,435 @@
|
|||
// Copyright 2025 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
)
|
||||
|
||||
// Iterator is an iterator to step over all the accounts or the specific
|
||||
// storage in the specific state.
|
||||
type Iterator interface {
|
||||
// Next steps the iterator forward one element. It returns false if the iterator
|
||||
// is exhausted or if an error occurs. Any error encountered is retained and
|
||||
// can be retrieved via Error().
|
||||
Next() bool
|
||||
|
||||
// Error returns any failure that occurred during iteration, which might have
|
||||
// caused a premature iteration exit.
|
||||
Error() error
|
||||
|
||||
// Hash returns the hash of the account or storage slot the iterator is
|
||||
// currently at.
|
||||
Hash() common.Hash
|
||||
|
||||
// Release releases associated resources. Release should always succeed and
|
||||
// can be called multiple times without causing error.
|
||||
Release()
|
||||
}
|
||||
|
||||
// AccountIterator is an iterator to step over all the accounts in the
|
||||
// specific state.
|
||||
type AccountIterator interface {
|
||||
Iterator
|
||||
|
||||
// Address returns the raw account address the iterator is currently at.
|
||||
// An error will be returned if the preimage is not available.
|
||||
Address() (common.Address, error)
|
||||
|
||||
// Account returns the RLP encoded account the iterator is currently at.
|
||||
// An error will be retained if the iterator becomes invalid.
|
||||
Account() []byte
|
||||
}
|
||||
|
||||
// StorageIterator is an iterator to step over the specific storage in the
|
||||
// specific state.
|
||||
type StorageIterator interface {
|
||||
Iterator
|
||||
|
||||
// Key returns the raw storage slot key the iterator is currently at.
|
||||
// An error will be returned if the preimage is not available.
|
||||
Key() (common.Hash, error)
|
||||
|
||||
// Slot returns the storage slot the iterator is currently at. An error will
|
||||
// be retained if the iterator becomes invalid.
|
||||
Slot() []byte
|
||||
}
|
||||
|
||||
// Iteratee wraps the NewIterator methods for traversing the accounts and
|
||||
// storages of the specific state.
|
||||
type Iteratee interface {
|
||||
// NewAccountIterator creates an account iterator for the state specified by
|
||||
// the given root. It begins at a specified starting position, corresponding
|
||||
// to a particular initial key (or the next key if the specified one does
|
||||
// not exist).
|
||||
//
|
||||
// The starting position here refers to the hash of the account address.
|
||||
NewAccountIterator(start common.Hash) (AccountIterator, error)
|
||||
|
||||
// NewStorageIterator creates a storage iterator for the state specified by
|
||||
// the address hash. It begins at a specified starting position, corresponding
|
||||
// to a particular initial key (or the next key if the specified one does
|
||||
// not exist).
|
||||
//
|
||||
// The starting position here refers to the hash of the slot key.
|
||||
NewStorageIterator(addressHash common.Hash, start common.Hash) (StorageIterator, error)
|
||||
}
|
||||
|
||||
// PreimageReader wraps the function Preimage for accessing the preimage of
|
||||
// a given hash.
|
||||
type PreimageReader interface {
|
||||
// Preimage returns the preimage of associated hash.
|
||||
Preimage(hash common.Hash) []byte
|
||||
}
|
||||
|
||||
// flatAccountIterator is a wrapper around the underlying flat state iterator.
|
||||
// Before returning data from the iterator, it performs an additional conversion
|
||||
// to bridge the slim encoding with the full encoding format.
|
||||
type flatAccountIterator struct {
|
||||
err error
|
||||
it snapshot.AccountIterator
|
||||
preimage PreimageReader
|
||||
}
|
||||
|
||||
// newFlatAccountIterator constructs the account iterator with the provided
|
||||
// flat state iterator.
|
||||
func newFlatAccountIterator(it snapshot.AccountIterator, preimage PreimageReader) *flatAccountIterator {
|
||||
return &flatAccountIterator{it: it, preimage: preimage}
|
||||
}
|
||||
|
||||
// Next steps the iterator forward one element. It returns false if the iterator
|
||||
// is exhausted or if an error occurs. Any error encountered is retained and
|
||||
// can be retrieved via Error().
|
||||
func (ai *flatAccountIterator) Next() bool {
|
||||
if ai.err != nil {
|
||||
return false
|
||||
}
|
||||
return ai.it.Next()
|
||||
}
|
||||
|
||||
// Error returns any failure that occurred during iteration, which might have
|
||||
// caused a premature iteration exit.
|
||||
func (ai *flatAccountIterator) Error() error {
|
||||
if ai.err != nil {
|
||||
return ai.err
|
||||
}
|
||||
return ai.it.Error()
|
||||
}
|
||||
|
||||
// Hash returns the hash of the account or storage slot the iterator is
|
||||
// currently at.
|
||||
func (ai *flatAccountIterator) Hash() common.Hash {
|
||||
return ai.it.Hash()
|
||||
}
|
||||
|
||||
// Release releases associated resources. Release should always succeed and
|
||||
// can be called multiple times without causing error.
|
||||
func (ai *flatAccountIterator) Release() {
|
||||
ai.it.Release()
|
||||
}
|
||||
|
||||
// Address returns the raw account address the iterator is currently at.
|
||||
// An error will be returned if the preimage is not available.
|
||||
func (ai *flatAccountIterator) Address() (common.Address, error) {
|
||||
if ai.preimage == nil {
|
||||
return common.Address{}, errors.New("account address is not available")
|
||||
}
|
||||
preimage := ai.preimage.Preimage(ai.Hash())
|
||||
if preimage == nil {
|
||||
return common.Address{}, errors.New("account address is not available")
|
||||
}
|
||||
return common.BytesToAddress(preimage), nil
|
||||
}
|
||||
|
||||
// Account returns the account data the iterator is currently at. The account
|
||||
// data is encoded as slim format from the underlying iterator, the conversion
|
||||
// is required.
|
||||
func (ai *flatAccountIterator) Account() []byte {
|
||||
data, err := types.FullAccountRLP(ai.it.Account())
|
||||
if err != nil {
|
||||
ai.err = err
|
||||
return nil
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// flatStorageIterator is a wrapper around the underlying flat state iterator.
|
||||
type flatStorageIterator struct {
|
||||
it snapshot.StorageIterator
|
||||
preimage PreimageReader
|
||||
}
|
||||
|
||||
// newFlatStorageIterator constructs the storage iterator with the provided
|
||||
// flat state iterator.
|
||||
func newFlatStorageIterator(it snapshot.StorageIterator, preimage PreimageReader) *flatStorageIterator {
|
||||
return &flatStorageIterator{it: it, preimage: preimage}
|
||||
}
|
||||
|
||||
// Next steps the iterator forward one element. It returns false if the iterator
|
||||
// is exhausted or if an error occurs. Any error encountered is retained and
|
||||
// can be retrieved via Error().
|
||||
func (si *flatStorageIterator) Next() bool {
|
||||
return si.it.Next()
|
||||
}
|
||||
|
||||
// Error returns any failure that occurred during iteration, which might have
|
||||
// caused a premature iteration exit.
|
||||
func (si *flatStorageIterator) Error() error {
|
||||
return si.it.Error()
|
||||
}
|
||||
|
||||
// Hash returns the hash of the account or storage slot the iterator is
|
||||
// currently at.
|
||||
func (si *flatStorageIterator) Hash() common.Hash {
|
||||
return si.it.Hash()
|
||||
}
|
||||
|
||||
// Release releases associated resources. Release should always succeed and
|
||||
// can be called multiple times without causing error.
|
||||
func (si *flatStorageIterator) Release() {
|
||||
si.it.Release()
|
||||
}
|
||||
|
||||
// Key returns the raw storage slot key the iterator is currently at.
|
||||
// An error will be returned if the preimage is not available.
|
||||
func (si *flatStorageIterator) Key() (common.Hash, error) {
|
||||
if si.preimage == nil {
|
||||
return common.Hash{}, errors.New("slot key is not available")
|
||||
}
|
||||
preimage := si.preimage.Preimage(si.Hash())
|
||||
if preimage == nil {
|
||||
return common.Hash{}, errors.New("slot key is not available")
|
||||
}
|
||||
return common.BytesToHash(preimage), nil
|
||||
}
|
||||
|
||||
// Slot returns the storage slot data the iterator is currently at.
|
||||
func (si *flatStorageIterator) Slot() []byte {
|
||||
return si.it.Slot()
|
||||
}
|
||||
|
||||
// merkleIterator implements the Iterator interface, providing functions to traverse
|
||||
// the accounts or storages with the manner of Merkle-Patricia-Trie.
|
||||
type merkleIterator struct {
|
||||
tr Trie
|
||||
it *trie.Iterator
|
||||
account bool
|
||||
}
|
||||
|
||||
// newMerkleTrieIterator constructs the iterator with the given trie and starting position.
|
||||
func newMerkleTrieIterator(tr Trie, start common.Hash, account bool) (*merkleIterator, error) {
|
||||
it, err := tr.NodeIterator(start.Bytes())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &merkleIterator{
|
||||
tr: tr,
|
||||
it: trie.NewIterator(it),
|
||||
account: account,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Next steps the iterator forward one element. It returns false if the iterator
|
||||
// is exhausted or if an error occurs. Any error encountered is retained and
|
||||
// can be retrieved via Error().
|
||||
func (ti *merkleIterator) Next() bool {
|
||||
return ti.it.Next()
|
||||
}
|
||||
|
||||
// Error returns any failure that occurred during iteration, which might have
|
||||
// caused a premature iteration exit.
|
||||
func (ti *merkleIterator) Error() error {
|
||||
return ti.it.Err
|
||||
}
|
||||
|
||||
// Hash returns the hash of the account or storage slot the iterator is
|
||||
// currently at.
|
||||
func (ti *merkleIterator) Hash() common.Hash {
|
||||
return common.BytesToHash(ti.it.Key)
|
||||
}
|
||||
|
||||
// Release releases associated resources. Release should always succeed and
|
||||
// can be called multiple times without causing error.
|
||||
func (ti *merkleIterator) Release() {}
|
||||
|
||||
// Address returns the raw account address the iterator is currently at.
|
||||
// An error will be returned if the preimage is not available.
|
||||
func (ti *merkleIterator) Address() (common.Address, error) {
|
||||
if !ti.account {
|
||||
return common.Address{}, errors.New("account address is not available")
|
||||
}
|
||||
preimage := ti.tr.GetKey(ti.it.Key)
|
||||
if preimage == nil {
|
||||
return common.Address{}, errors.New("account address is not available")
|
||||
}
|
||||
return common.BytesToAddress(preimage), nil
|
||||
}
|
||||
|
||||
// Account returns the account data the iterator is currently at.
|
||||
func (ti *merkleIterator) Account() []byte {
|
||||
if !ti.account {
|
||||
return nil
|
||||
}
|
||||
return ti.it.Value
|
||||
}
|
||||
|
||||
// Key returns the raw storage slot key the iterator is currently at.
|
||||
// An error will be returned if the preimage is not available.
|
||||
func (ti *merkleIterator) Key() (common.Hash, error) {
|
||||
if ti.account {
|
||||
return common.Hash{}, errors.New("slot key is not available")
|
||||
}
|
||||
preimage := ti.tr.GetKey(ti.it.Key)
|
||||
if preimage == nil {
|
||||
return common.Hash{}, errors.New("slot key is not available")
|
||||
}
|
||||
return common.BytesToHash(preimage), nil
|
||||
}
|
||||
|
||||
// Slot returns the storage slot the iterator is currently at.
|
||||
func (ti *merkleIterator) Slot() []byte {
|
||||
if ti.account {
|
||||
return nil
|
||||
}
|
||||
return ti.it.Value
|
||||
}
|
||||
|
||||
// stateIteratee implements Iteratee interface, providing the state traversal
|
||||
// functionalities of a specific state.
|
||||
type stateIteratee struct {
|
||||
merkle bool
|
||||
root common.Hash
|
||||
triedb *triedb.Database
|
||||
snap *snapshot.Tree
|
||||
}
|
||||
|
||||
func newStateIteratee(merkle bool, root common.Hash, triedb *triedb.Database, snap *snapshot.Tree) (*stateIteratee, error) {
|
||||
return &stateIteratee{
|
||||
merkle: merkle,
|
||||
root: root,
|
||||
triedb: triedb,
|
||||
snap: snap,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewAccountIterator creates an account iterator for the state specified by
|
||||
// the given root. It begins at a specified starting position, corresponding
|
||||
// to a particular initial key (or the next key if the specified one does
|
||||
// not exist).
|
||||
//
|
||||
// The starting position here refers to the hash of the account address.
|
||||
func (si *stateIteratee) NewAccountIterator(start common.Hash) (AccountIterator, error) {
|
||||
// If the external snapshot is available (hash scheme), try to initialize
|
||||
// the account iterator from there first.
|
||||
if si.snap != nil {
|
||||
it, err := si.snap.AccountIterator(si.root, start)
|
||||
if err == nil {
|
||||
return newFlatAccountIterator(it, si.triedb), nil
|
||||
}
|
||||
}
|
||||
// If the external snapshot is not available, try to initialize the
|
||||
// account iterator from the trie database (path scheme)
|
||||
it, err := si.triedb.AccountIterator(si.root, start)
|
||||
if err == nil {
|
||||
return newFlatAccountIterator(it, si.triedb), nil
|
||||
}
|
||||
if !si.merkle {
|
||||
return nil, fmt.Errorf("state %x is not available for account traversal", si.root)
|
||||
}
|
||||
// The snapshot is not usable so far, construct the account iterator from
|
||||
// the trie as the fallback. It's not as efficient as the flat state iterator.
|
||||
tr, err := trie.NewStateTrie(trie.StateTrieID(si.root), si.triedb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newMerkleTrieIterator(tr, start, true)
|
||||
}
|
||||
|
||||
// NewStorageIterator creates a storage iterator for the state specified by
|
||||
// the address hash. It begins at a specified starting position, corresponding
|
||||
// to a particular initial key (or the next key if the specified one does not exist).
|
||||
//
|
||||
// The starting position here refers to the hash of the slot key.
|
||||
func (si *stateIteratee) NewStorageIterator(addressHash common.Hash, start common.Hash) (StorageIterator, error) {
|
||||
// If the external snapshot is available (hash scheme), try to initialize
|
||||
// the storage iterator from there first.
|
||||
if si.snap != nil {
|
||||
it, err := si.snap.StorageIterator(si.root, addressHash, start)
|
||||
if err == nil {
|
||||
return newFlatStorageIterator(it, si.triedb), nil
|
||||
}
|
||||
}
|
||||
// If the external snapshot is not available, try to initialize the
|
||||
// storage iterator from the trie database (path scheme)
|
||||
it, err := si.triedb.StorageIterator(si.root, addressHash, start)
|
||||
if err == nil {
|
||||
return newFlatStorageIterator(it, si.triedb), nil
|
||||
}
|
||||
if !si.merkle {
|
||||
return nil, fmt.Errorf("state %x is not available for storage traversal", si.root)
|
||||
}
|
||||
// The snapshot is not usable so far, construct the storage iterator from
|
||||
// the trie as the fallback. It's not as efficient as the flat state iterator.
|
||||
tr, err := trie.NewStateTrie(trie.StateTrieID(si.root), si.triedb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
acct, err := tr.GetAccountByHash(addressHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if acct == nil || acct.Root == types.EmptyRootHash {
|
||||
return &exhaustedIterator{}, nil
|
||||
}
|
||||
storageTr, err := trie.NewStateTrie(trie.StorageTrieID(si.root, addressHash, acct.Root), si.triedb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newMerkleTrieIterator(storageTr, start, false)
|
||||
}
|
||||
|
||||
type exhaustedIterator struct{}
|
||||
|
||||
func (e exhaustedIterator) Next() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (e exhaustedIterator) Error() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e exhaustedIterator) Hash() common.Hash {
|
||||
return common.Hash{}
|
||||
}
|
||||
|
||||
func (e exhaustedIterator) Release() {
|
||||
}
|
||||
|
||||
func (e exhaustedIterator) Key() (common.Hash, error) {
|
||||
return common.Hash{}, nil
|
||||
}
|
||||
|
||||
func (e exhaustedIterator) Slot() []byte {
|
||||
return nil
|
||||
}
|
||||
262
core/state/database_iterator_test.go
Normal file
262
core/state/database_iterator_test.go
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
// Copyright 2026 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
// TestExhaustedIterator verifies the exhaustedIterator sentinel: Next is false,
|
||||
// Error is nil, Hash/Key are zero, Slot is nil, and double Release is safe.
|
||||
func TestExhaustedIterator(t *testing.T) {
|
||||
var it exhaustedIterator
|
||||
|
||||
if it.Next() {
|
||||
t.Fatal("Next() returned true")
|
||||
}
|
||||
if err := it.Error(); err != nil {
|
||||
t.Fatalf("Error() = %v, want nil", err)
|
||||
}
|
||||
if hash := it.Hash(); hash != (common.Hash{}) {
|
||||
t.Fatalf("Hash() = %x, want zero", hash)
|
||||
}
|
||||
if key, err := it.Key(); key != (common.Hash{}) || err != nil {
|
||||
t.Fatalf("Key() = %x, %v; want zero, nil", key, err)
|
||||
}
|
||||
if slot := it.Slot(); slot != nil {
|
||||
t.Fatalf("Slot() = %x, want nil", slot)
|
||||
}
|
||||
it.Release()
|
||||
it.Release()
|
||||
}
|
||||
|
||||
// TestAccountIterator tests the account iterator: correct count, ascending
|
||||
// hash order, valid full-format RLP, data integrity, address preimage
|
||||
// resolution, and seek behavior.
|
||||
func TestAccountIterator(t *testing.T) {
|
||||
testAccountIterator(t, rawdb.HashScheme)
|
||||
testAccountIterator(t, rawdb.PathScheme)
|
||||
}
|
||||
|
||||
func testAccountIterator(t *testing.T, scheme string) {
|
||||
_, sdb, ndb, root, accounts := makeTestState(scheme)
|
||||
ndb.Commit(root, false)
|
||||
|
||||
iteratee, err := sdb.Iteratee(root)
|
||||
if err != nil {
|
||||
t.Fatalf("(%s) failed to create iteratee: %v", scheme, err)
|
||||
}
|
||||
// Build lookups from address hash.
|
||||
addrByHash := make(map[common.Hash]*testAccount)
|
||||
for _, acc := range accounts {
|
||||
addrByHash[crypto.Keccak256Hash(acc.address.Bytes())] = acc
|
||||
}
|
||||
|
||||
// --- Full iteration: count, ordering, RLP validity, data integrity, address resolution ---
|
||||
acctIt, err := iteratee.NewAccountIterator(common.Hash{})
|
||||
if err != nil {
|
||||
t.Fatalf("(%s) failed to create account iterator: %v", scheme, err)
|
||||
}
|
||||
var (
|
||||
hashes []common.Hash
|
||||
prevHash common.Hash
|
||||
)
|
||||
for acctIt.Next() {
|
||||
hash := acctIt.Hash()
|
||||
if hash == (common.Hash{}) {
|
||||
t.Fatalf("(%s) zero hash at position %d", scheme, len(hashes))
|
||||
}
|
||||
if len(hashes) > 0 && bytes.Compare(prevHash.Bytes(), hash.Bytes()) >= 0 {
|
||||
t.Fatalf("(%s) hashes not ascending: %x >= %x", scheme, prevHash, hash)
|
||||
}
|
||||
prevHash = hash
|
||||
hashes = append(hashes, hash)
|
||||
|
||||
// Decode and verify account data.
|
||||
blob := acctIt.Account()
|
||||
if blob == nil {
|
||||
t.Fatalf("(%s) nil account at %x", scheme, hash)
|
||||
}
|
||||
var decoded types.StateAccount
|
||||
if err := rlp.DecodeBytes(blob, &decoded); err != nil {
|
||||
t.Fatalf("(%s) bad RLP at %x: %v", scheme, hash, err)
|
||||
}
|
||||
acc := addrByHash[hash]
|
||||
if decoded.Nonce != acc.nonce {
|
||||
t.Fatalf("(%s) nonce %x: got %d, want %d", scheme, hash, decoded.Nonce, acc.nonce)
|
||||
}
|
||||
if decoded.Balance.Cmp(acc.balance) != 0 {
|
||||
t.Fatalf("(%s) balance %x: got %v, want %v", scheme, hash, decoded.Balance, acc.balance)
|
||||
}
|
||||
// Verify address preimage resolution.
|
||||
addr, err := acctIt.Address()
|
||||
if err != nil {
|
||||
t.Fatalf("(%s) failed to address: %v", scheme, err)
|
||||
}
|
||||
if addr != acc.address {
|
||||
t.Fatalf("(%s) Address() = %x, want %x", scheme, addr, acc.address)
|
||||
}
|
||||
}
|
||||
acctIt.Release()
|
||||
|
||||
if err := acctIt.Error(); err != nil {
|
||||
t.Fatalf("(%s) iteration error: %v", scheme, err)
|
||||
}
|
||||
if len(hashes) != len(accounts) {
|
||||
t.Fatalf("(%s) iterated %d accounts, want %d", scheme, len(hashes), len(accounts))
|
||||
}
|
||||
|
||||
// --- Seek: starting from midpoint should skip earlier entries ---
|
||||
mid := hashes[len(hashes)/2]
|
||||
seekIt, err := iteratee.NewAccountIterator(mid)
|
||||
if err != nil {
|
||||
t.Fatalf("(%s) failed to create seeked iterator: %v", scheme, err)
|
||||
}
|
||||
seekCount := 0
|
||||
for seekIt.Next() {
|
||||
if bytes.Compare(seekIt.Hash().Bytes(), mid.Bytes()) < 0 {
|
||||
t.Fatalf("(%s) seeked iterator returned hash before start", scheme)
|
||||
}
|
||||
seekCount++
|
||||
}
|
||||
seekIt.Release()
|
||||
|
||||
if seekCount != len(hashes)/2 {
|
||||
t.Fatalf("(%s) unexpected seeked count, %d != %d", scheme, seekCount, len(hashes)/2)
|
||||
}
|
||||
}
|
||||
|
||||
// TestStorageIterator tests the storage iterator: correct slot counts against
|
||||
// the trie, ascending hash order, non-nil slot data, key preimage resolution,
|
||||
// seek behavior, and empty-storage accounts.
|
||||
func TestStorageIterator(t *testing.T) {
|
||||
testStorageIterator(t, rawdb.HashScheme)
|
||||
testStorageIterator(t, rawdb.PathScheme)
|
||||
}
|
||||
|
||||
func testStorageIterator(t *testing.T, scheme string) {
|
||||
_, sdb, ndb, root, accounts := makeTestState(scheme)
|
||||
ndb.Commit(root, false)
|
||||
|
||||
iteratee, err := sdb.Iteratee(root)
|
||||
if err != nil {
|
||||
t.Fatalf("(%s) failed to create iteratee: %v", scheme, err)
|
||||
}
|
||||
|
||||
// --- Slot count and ordering for every account ---
|
||||
var withStorage common.Hash // remember an account that has storage for seek test
|
||||
for _, acc := range accounts {
|
||||
addrHash := crypto.Keccak256Hash(acc.address.Bytes())
|
||||
expected := countStorageSlots(t, scheme, sdb, root, addrHash)
|
||||
|
||||
storageIt, err := iteratee.NewStorageIterator(addrHash, common.Hash{})
|
||||
if err != nil {
|
||||
t.Fatalf("(%s) failed to create storage iterator for %x: %v", scheme, acc.address, err)
|
||||
}
|
||||
count := 0
|
||||
var prevHash common.Hash
|
||||
for storageIt.Next() {
|
||||
hash := storageIt.Hash()
|
||||
if count > 0 && bytes.Compare(prevHash.Bytes(), hash.Bytes()) >= 0 {
|
||||
t.Fatalf("(%s) storage hashes not ascending for %x", scheme, acc.address)
|
||||
}
|
||||
prevHash = hash
|
||||
if storageIt.Slot() == nil {
|
||||
t.Fatalf("(%s) nil slot at %x", scheme, hash)
|
||||
}
|
||||
// Check key preimage resolution on first slot.
|
||||
if _, err := storageIt.Key(); err != nil {
|
||||
t.Fatalf("(%s) Key() failed to resolve", scheme)
|
||||
}
|
||||
count++
|
||||
}
|
||||
if err := storageIt.Error(); err != nil {
|
||||
t.Fatalf("(%s) storage iteration error for %x: %v", scheme, acc.address, err)
|
||||
}
|
||||
storageIt.Release()
|
||||
|
||||
if count != expected {
|
||||
t.Fatalf("(%s) account %x: %d slots, want %d", scheme, acc.address, count, expected)
|
||||
}
|
||||
if count > 0 {
|
||||
withStorage = addrHash
|
||||
}
|
||||
}
|
||||
|
||||
// --- Seek: starting from second slot should skip the first ---
|
||||
if withStorage == (common.Hash{}) {
|
||||
t.Fatalf("(%s) no account with storage found", scheme)
|
||||
}
|
||||
fullIt, err := iteratee.NewStorageIterator(withStorage, common.Hash{})
|
||||
if err != nil {
|
||||
t.Fatalf("(%s) failed to create full storage iterator: %v", scheme, err)
|
||||
}
|
||||
var slotHashes []common.Hash
|
||||
for fullIt.Next() {
|
||||
slotHashes = append(slotHashes, fullIt.Hash())
|
||||
}
|
||||
fullIt.Release()
|
||||
|
||||
seekIt, err := iteratee.NewStorageIterator(withStorage, slotHashes[1])
|
||||
if err != nil {
|
||||
t.Fatalf("(%s) failed to create seeked storage iterator: %v", scheme, err)
|
||||
}
|
||||
seekCount := 0
|
||||
for seekIt.Next() {
|
||||
if bytes.Compare(seekIt.Hash().Bytes(), slotHashes[1].Bytes()) < 0 {
|
||||
t.Fatalf("(%s) seeked storage iterator returned hash before start", scheme)
|
||||
}
|
||||
seekCount++
|
||||
}
|
||||
seekIt.Release()
|
||||
|
||||
if seekCount != len(slotHashes)-1 {
|
||||
t.Fatalf("(%s) unexpected seeked storage count %d != %d", scheme, seekCount, len(slotHashes)-1)
|
||||
}
|
||||
}
|
||||
|
||||
// countStorageSlots counts storage slots for an account by opening the
|
||||
// storage trie directly.
|
||||
func countStorageSlots(t *testing.T, scheme string, sdb Database, root common.Hash, addrHash common.Hash) int {
|
||||
t.Helper()
|
||||
accTrie, err := trie.NewStateTrie(trie.StateTrieID(root), sdb.TrieDB())
|
||||
if err != nil {
|
||||
t.Fatalf("(%s) failed to open account trie: %v", scheme, err)
|
||||
}
|
||||
acct, err := accTrie.GetAccountByHash(addrHash)
|
||||
if err != nil || acct == nil || acct.Root == types.EmptyRootHash {
|
||||
return 0
|
||||
}
|
||||
storageTrie, err := trie.NewStateTrie(trie.StorageTrieID(root, addrHash, acct.Root), sdb.TrieDB())
|
||||
if err != nil {
|
||||
t.Fatalf("(%s) failed to open storage trie for %x: %v", scheme, addrHash, err)
|
||||
}
|
||||
it := trie.NewIterator(storageTrie.MustNodeIterator(nil))
|
||||
count := 0
|
||||
for it.Next() {
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
|
@ -27,7 +27,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/bintrie"
|
||||
)
|
||||
|
||||
|
|
@ -45,6 +44,7 @@ type DumpConfig struct {
|
|||
type DumpCollector interface {
|
||||
// OnRoot is called with the state root
|
||||
OnRoot(common.Hash)
|
||||
|
||||
// OnAccount is called once for each account in the trie
|
||||
OnAccount(*common.Address, DumpAccount)
|
||||
}
|
||||
|
|
@ -65,9 +65,10 @@ type DumpAccount struct {
|
|||
type Dump struct {
|
||||
Root string `json:"root"`
|
||||
Accounts map[string]DumpAccount `json:"accounts"`
|
||||
|
||||
// Next can be set to represent that this dump is only partial, and Next
|
||||
// is where an iterator should be positioned in order to continue the dump.
|
||||
Next []byte `json:"next,omitempty"` // nil if no more accounts
|
||||
Next hexutil.Bytes `json:"next,omitempty"` // nil if no more accounts
|
||||
}
|
||||
|
||||
// OnRoot implements DumpCollector interface
|
||||
|
|
@ -114,9 +115,6 @@ func (d iterativeDump) OnRoot(root common.Hash) {
|
|||
|
||||
// DumpToCollector iterates the state according to the given options and inserts
|
||||
// the items into a collector for aggregation or serialization.
|
||||
//
|
||||
// The state iterator is still trie-based and can be converted to snapshot-based
|
||||
// once the state snapshot is fully integrated into database. TODO(rjl493456442).
|
||||
func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []byte) {
|
||||
// Sanitize the input to allow nil configs
|
||||
if conf == nil {
|
||||
|
|
@ -131,20 +129,23 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
|
|||
log.Info("Trie dumping started", "root", s.originalRoot)
|
||||
c.OnRoot(s.originalRoot)
|
||||
|
||||
tr, err := s.db.OpenTrie(s.originalRoot)
|
||||
iteratee, err := s.db.Iteratee(s.originalRoot)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
trieIt, err := tr.NodeIterator(conf.Start)
|
||||
var startHash common.Hash
|
||||
if conf.Start != nil {
|
||||
startHash = common.BytesToHash(conf.Start)
|
||||
}
|
||||
acctIt, err := iteratee.NewAccountIterator(startHash)
|
||||
if err != nil {
|
||||
log.Error("Trie dumping error", "err", err)
|
||||
return nil
|
||||
}
|
||||
it := trie.NewIterator(trieIt)
|
||||
defer acctIt.Release()
|
||||
|
||||
for it.Next() {
|
||||
for acctIt.Next() {
|
||||
var data types.StateAccount
|
||||
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
|
||||
if err := rlp.DecodeBytes(acctIt.Account(), &data); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var (
|
||||
|
|
@ -153,63 +154,55 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
|
|||
Nonce: data.Nonce,
|
||||
Root: data.Root[:],
|
||||
CodeHash: data.CodeHash,
|
||||
AddressHash: it.Key,
|
||||
AddressHash: acctIt.Hash().Bytes(),
|
||||
}
|
||||
address *common.Address
|
||||
addr common.Address
|
||||
addrBytes = tr.GetKey(it.Key)
|
||||
address *common.Address
|
||||
)
|
||||
if addrBytes == nil {
|
||||
addrBytes, err := acctIt.Address()
|
||||
if err != nil {
|
||||
missingPreimages++
|
||||
if conf.OnlyWithAddresses {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
addr = common.BytesToAddress(addrBytes)
|
||||
address = &addr
|
||||
address = &addrBytes
|
||||
account.Address = address
|
||||
}
|
||||
obj := newObject(s, addr, &data)
|
||||
obj := newObject(s, addrBytes, &data)
|
||||
if !conf.SkipCode {
|
||||
account.Code = obj.Code()
|
||||
}
|
||||
if !conf.SkipStorage {
|
||||
account.Storage = make(map[common.Hash]string)
|
||||
|
||||
storageTr, err := s.db.OpenStorageTrie(s.originalRoot, addr, obj.Root(), tr)
|
||||
storageIt, err := iteratee.NewStorageIterator(acctIt.Hash(), common.Hash{})
|
||||
if err != nil {
|
||||
log.Error("Failed to load storage trie", "err", err)
|
||||
continue
|
||||
}
|
||||
trieIt, err := storageTr.NodeIterator(nil)
|
||||
if err != nil {
|
||||
log.Error("Failed to create trie iterator", "err", err)
|
||||
continue
|
||||
}
|
||||
storageIt := trie.NewIterator(trieIt)
|
||||
for storageIt.Next() {
|
||||
_, content, _, err := rlp.Split(storageIt.Value)
|
||||
_, content, _, err := rlp.Split(storageIt.Slot())
|
||||
if err != nil {
|
||||
log.Error("Failed to decode the value returned by iterator", "error", err)
|
||||
continue
|
||||
}
|
||||
key := storageTr.GetKey(storageIt.Key)
|
||||
if key == nil {
|
||||
key, err := storageIt.Key()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
account.Storage[common.BytesToHash(key)] = common.Bytes2Hex(content)
|
||||
account.Storage[key] = common.Bytes2Hex(content)
|
||||
}
|
||||
storageIt.Release()
|
||||
}
|
||||
c.OnAccount(address, account)
|
||||
accounts++
|
||||
if time.Since(logged) > 8*time.Second {
|
||||
log.Info("Trie dumping in progress", "at", common.Bytes2Hex(it.Key), "accounts", accounts,
|
||||
"elapsed", common.PrettyDuration(time.Since(start)))
|
||||
log.Info("Trie dumping in progress", "at", acctIt.Hash().Hex(), "accounts", accounts, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
logged = time.Now()
|
||||
}
|
||||
if conf.Max > 0 && accounts >= conf.Max {
|
||||
if it.Next() {
|
||||
nextKey = it.Key
|
||||
if acctIt.Next() {
|
||||
nextKey = acctIt.Hash().Bytes()
|
||||
}
|
||||
break
|
||||
}
|
||||
|
|
@ -217,9 +210,7 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
|
|||
if missingPreimages > 0 {
|
||||
log.Warn("Dump incomplete due to missing preimages", "missing", missingPreimages)
|
||||
}
|
||||
log.Info("Trie dumping complete", "accounts", accounts,
|
||||
"elapsed", common.PrettyDuration(time.Since(start)))
|
||||
|
||||
log.Info("Trie dumping complete", "accounts", accounts, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
return nextKey
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -98,7 +98,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.Keccak256Hash(addr.Bytes()))
|
||||
account, err := r.reader.Account(crypto.Keccak256Hash(addr[:]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -128,8 +128,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.Keccak256Hash(addr.Bytes())
|
||||
slotHash := crypto.Keccak256Hash(key.Bytes())
|
||||
addrHash := crypto.Keccak256Hash(addr[:])
|
||||
slotHash := crypto.Keccak256Hash(key[:])
|
||||
ret, err := r.reader.Storage(addrHash, slotHash)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
|
|
|
|||
|
|
@ -474,6 +474,14 @@ func (s *stateObject) commit() (*accountUpdate, *trienode.NodeSet, error) {
|
|||
s.origin = s.data.Copy()
|
||||
return op, nil, nil
|
||||
}
|
||||
// In Verkle/binary trie mode, all state objects share one unified trie.
|
||||
// The main account trie commit in stateDB.commit() already calls
|
||||
// CollectNodes on this trie, so calling Commit here again would
|
||||
// redundantly traverse and serialize the entire tree per dirty account.
|
||||
if s.db.GetTrie().IsVerkle() {
|
||||
s.origin = s.data.Copy()
|
||||
return op, nil, nil
|
||||
}
|
||||
root, nodes := s.trie.Commit(false)
|
||||
s.data.Root = root
|
||||
s.origin = s.data.Copy()
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||
"github.com/ethereum/go-ethereum/core/stateless"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
|
|
@ -135,8 +134,7 @@ type StateDB struct {
|
|||
journal *journal
|
||||
|
||||
// State witness if cross validation is needed
|
||||
witness *stateless.Witness
|
||||
witnessStats *stateless.WitnessStats
|
||||
witness *stateless.Witness
|
||||
|
||||
// Measurements gathered during execution for debugging purposes
|
||||
AccountReads time.Duration
|
||||
|
|
@ -201,13 +199,12 @@ func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, erro
|
|||
// StartPrefetcher initializes a new trie prefetcher to pull in nodes from the
|
||||
// state trie concurrently while the state is mutated so that when we reach the
|
||||
// commit phase, most of the needed data is already hot.
|
||||
func (s *StateDB) StartPrefetcher(namespace string, witness *stateless.Witness, witnessStats *stateless.WitnessStats) {
|
||||
func (s *StateDB) StartPrefetcher(namespace string, witness *stateless.Witness) {
|
||||
// Terminate any previously running prefetcher
|
||||
s.StopPrefetcher()
|
||||
|
||||
// Enable witness collection if requested
|
||||
s.witness = witness
|
||||
s.witnessStats = witnessStats
|
||||
|
||||
// With the switch to the Proof-of-Stake consensus algorithm, block production
|
||||
// rewards are now handled at the consensus layer. Consequently, a block may
|
||||
|
|
@ -743,6 +740,44 @@ func (s *StateDB) GetRefund() uint64 {
|
|||
return s.refund
|
||||
}
|
||||
|
||||
type removedAccountWithBalance struct {
|
||||
address common.Address
|
||||
balance *uint256.Int
|
||||
}
|
||||
|
||||
// LogsForBurnAccounts returns the eth burn logs for accounts scheduled for
|
||||
// removal which still have positive balance. The purpose of this function is
|
||||
// to handle a corner case of EIP-7708 where a self-destructed account might
|
||||
// still receive funds between sending/burning its previous balance and actual
|
||||
// removal. In this case the burning of these remaining balances still need to
|
||||
// be logged.
|
||||
// Specification EIP-7708: https://eips.ethereum.org/EIPS/eip-7708
|
||||
//
|
||||
// This function should only be invoked at the transaction boundary, specifically
|
||||
// before the Finalise.
|
||||
func (s *StateDB) LogsForBurnAccounts() []*types.Log {
|
||||
var list []removedAccountWithBalance
|
||||
for addr := range s.journal.dirties {
|
||||
if obj, exist := s.stateObjects[addr]; exist && obj.selfDestructed && !obj.Balance().IsZero() {
|
||||
list = append(list, removedAccountWithBalance{
|
||||
address: obj.address,
|
||||
balance: obj.Balance(),
|
||||
})
|
||||
}
|
||||
}
|
||||
if list == nil {
|
||||
return nil
|
||||
}
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
return list[i].address.Cmp(list[j].address) < 0
|
||||
})
|
||||
logs := make([]*types.Log, len(list))
|
||||
for i, acct := range list {
|
||||
logs[i] = types.EthBurnLog(acct.address, acct.balance)
|
||||
}
|
||||
return logs
|
||||
}
|
||||
|
||||
// Finalise finalises the state by removing the destructed objects and clears
|
||||
// the journal as well as the refunds. Finalise, however, will not push any updates
|
||||
// into the tries just yet. Only IntermediateRoot or Commit will do that.
|
||||
|
|
@ -824,32 +859,67 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
|||
workers errgroup.Group
|
||||
)
|
||||
if s.db.TrieDB().IsVerkle() {
|
||||
// Whilst MPT storage tries are independent, Verkle has one single trie
|
||||
// for all the accounts and all the storage slots merged together. The
|
||||
// former can thus be simply parallelized, but updating the latter will
|
||||
// need concurrency support within the trie itself. That's a TODO for a
|
||||
// later time.
|
||||
workers.SetLimit(1)
|
||||
}
|
||||
for addr, op := range s.mutations {
|
||||
if op.applied || op.isDelete() {
|
||||
continue
|
||||
// Bypass per-account updateTrie() for binary trie. In binary trie mode
|
||||
// there is only one unified trie (OpenStorageTrie returns self), so the
|
||||
// per-account trie setup in updateTrie() (getPrefetchedTrie, getTrie,
|
||||
// prefetcher.used) is redundant overhead. Apply all storage updates
|
||||
// directly in a single pass.
|
||||
for addr, op := range s.mutations {
|
||||
if op.applied || op.isDelete() {
|
||||
continue
|
||||
}
|
||||
obj := s.stateObjects[addr]
|
||||
if len(obj.uncommittedStorage) == 0 {
|
||||
continue
|
||||
}
|
||||
for key, origin := range obj.uncommittedStorage {
|
||||
value, exist := obj.pendingStorage[key]
|
||||
if value == origin || !exist {
|
||||
continue
|
||||
}
|
||||
if (value != common.Hash{}) {
|
||||
if err := s.trie.UpdateStorage(addr, key[:], common.TrimLeftZeroes(value[:])); err != nil {
|
||||
s.setError(err)
|
||||
}
|
||||
s.StorageUpdated.Add(1)
|
||||
} else {
|
||||
if err := s.trie.DeleteStorage(addr, key[:]); err != nil {
|
||||
s.setError(err)
|
||||
}
|
||||
s.StorageDeleted.Add(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
obj := s.stateObjects[addr] // closure for the task runner below
|
||||
workers.Go(func() error {
|
||||
if s.db.TrieDB().IsVerkle() {
|
||||
obj.updateTrie()
|
||||
} else {
|
||||
// Clear uncommittedStorage and assign trie on each touched object.
|
||||
// obj.trie must be set because this path bypasses updateTrie(), which
|
||||
// is where obj.trie normally gets lazily loaded via getTrie().
|
||||
for addr, op := range s.mutations {
|
||||
if op.applied || op.isDelete() {
|
||||
continue
|
||||
}
|
||||
obj := s.stateObjects[addr]
|
||||
if len(obj.uncommittedStorage) > 0 {
|
||||
obj.uncommittedStorage = make(Storage)
|
||||
}
|
||||
obj.trie = s.trie
|
||||
}
|
||||
} else {
|
||||
for addr, op := range s.mutations {
|
||||
if op.applied || op.isDelete() {
|
||||
continue
|
||||
}
|
||||
obj := s.stateObjects[addr] // closure for the task runner below
|
||||
workers.Go(func() error {
|
||||
obj.updateRoot()
|
||||
|
||||
// If witness building is enabled and the state object has a trie,
|
||||
// gather the witnesses for its specific storage trie
|
||||
if s.witness != nil && obj.trie != nil {
|
||||
s.witness.AddState(obj.trie.Witness())
|
||||
s.witness.AddState(obj.trie.Witness(), obj.addrHash())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
// If witness building is enabled, gather all the read-only accesses.
|
||||
// Skip witness collection in Verkle mode, they will be gathered
|
||||
|
|
@ -862,17 +932,9 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
|||
continue
|
||||
}
|
||||
if trie := obj.getPrefetchedTrie(); trie != nil {
|
||||
witness := trie.Witness()
|
||||
s.witness.AddState(witness)
|
||||
if s.witnessStats != nil {
|
||||
s.witnessStats.Add(witness, obj.addrHash())
|
||||
}
|
||||
s.witness.AddState(trie.Witness(), obj.addrHash())
|
||||
} else if obj.trie != nil {
|
||||
witness := obj.trie.Witness()
|
||||
s.witness.AddState(witness)
|
||||
if s.witnessStats != nil {
|
||||
s.witnessStats.Add(witness, obj.addrHash())
|
||||
}
|
||||
s.witness.AddState(obj.trie.Witness(), obj.addrHash())
|
||||
}
|
||||
}
|
||||
// Pull in only-read and non-destructed trie witnesses
|
||||
|
|
@ -886,17 +948,9 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
|||
continue
|
||||
}
|
||||
if trie := obj.getPrefetchedTrie(); trie != nil {
|
||||
witness := trie.Witness()
|
||||
s.witness.AddState(witness)
|
||||
if s.witnessStats != nil {
|
||||
s.witnessStats.Add(witness, obj.addrHash())
|
||||
}
|
||||
s.witness.AddState(trie.Witness(), obj.addrHash())
|
||||
} else if obj.trie != nil {
|
||||
witness := obj.trie.Witness()
|
||||
s.witness.AddState(witness)
|
||||
if s.witnessStats != nil {
|
||||
s.witnessStats.Add(witness, obj.addrHash())
|
||||
}
|
||||
s.witness.AddState(obj.trie.Witness(), obj.addrHash())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -911,7 +965,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
|||
// only a single trie is used for state hashing. Replacing a non-nil verkle tree
|
||||
// here could result in losing uncommitted changes from storage.
|
||||
start = time.Now()
|
||||
if s.prefetcher != nil {
|
||||
if s.prefetcher != nil && !s.db.TrieDB().IsVerkle() {
|
||||
if trie := s.prefetcher.trie(common.Hash{}, s.originalRoot); trie == nil {
|
||||
log.Error("Failed to retrieve account pre-fetcher trie")
|
||||
} else {
|
||||
|
|
@ -969,11 +1023,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
|||
|
||||
// If witness building is enabled, gather the account trie witness
|
||||
if s.witness != nil {
|
||||
witness := s.trie.Witness()
|
||||
s.witness.AddState(witness)
|
||||
if s.witnessStats != nil {
|
||||
s.witnessStats.Add(witness, common.Hash{})
|
||||
}
|
||||
s.witness.AddState(s.trie.Witness(), common.Hash{})
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
|
@ -991,31 +1041,32 @@ func (s *StateDB) clearJournalAndRefund() {
|
|||
s.refund = 0
|
||||
}
|
||||
|
||||
// fastDeleteStorage is the function that efficiently deletes the storage trie
|
||||
// of a specific account. It leverages the associated state snapshot for fast
|
||||
// storage iteration and constructs trie node deletion markers by creating
|
||||
// stack trie with iterated slots.
|
||||
func (s *StateDB) fastDeleteStorage(snaps *snapshot.Tree, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, map[common.Hash][]byte, *trienode.NodeSet, error) {
|
||||
iter, err := snaps.StorageIterator(s.originalRoot, addrHash, common.Hash{})
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
defer iter.Release()
|
||||
|
||||
// deleteStorage is designed to delete the storage trie of a designated account.
|
||||
func (s *StateDB) deleteStorage(addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, map[common.Hash][]byte, *trienode.NodeSet, error) {
|
||||
var (
|
||||
nodes = trienode.NewNodeSet(addrHash) // the set for trie node mutations (value is nil)
|
||||
storages = make(map[common.Hash][]byte) // the set for storage mutations (value is nil)
|
||||
storageOrigins = make(map[common.Hash][]byte) // the set for tracking the original value of slot
|
||||
)
|
||||
iteratee, err := s.db.Iteratee(s.originalRoot)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
it, err := iteratee.NewStorageIterator(addrHash, common.Hash{})
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
defer it.Release()
|
||||
|
||||
stack := trie.NewStackTrie(func(path []byte, hash common.Hash, blob []byte) {
|
||||
nodes.AddNode(path, trienode.NewDeletedWithPrev(blob))
|
||||
})
|
||||
for iter.Next() {
|
||||
slot := common.CopyBytes(iter.Slot())
|
||||
if err := iter.Error(); err != nil { // error might occur after Slot function
|
||||
for it.Next() {
|
||||
slot := common.CopyBytes(it.Slot())
|
||||
if err := it.Error(); err != nil { // error might occur after Slot function
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
key := iter.Hash()
|
||||
key := it.Hash()
|
||||
storages[key] = nil
|
||||
storageOrigins[key] = slot
|
||||
|
||||
|
|
@ -1023,7 +1074,7 @@ func (s *StateDB) fastDeleteStorage(snaps *snapshot.Tree, addrHash common.Hash,
|
|||
return nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
if err := iter.Error(); err != nil { // error might occur during iteration
|
||||
if err := it.Error(); err != nil { // error might occur during iteration
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if stack.Hash() != root {
|
||||
|
|
@ -1032,68 +1083,6 @@ func (s *StateDB) fastDeleteStorage(snaps *snapshot.Tree, addrHash common.Hash,
|
|||
return storages, storageOrigins, nodes, nil
|
||||
}
|
||||
|
||||
// slowDeleteStorage serves as a less-efficient alternative to "fastDeleteStorage,"
|
||||
// employed when the associated state snapshot is not available. It iterates the
|
||||
// storage slots along with all internal trie nodes via trie directly.
|
||||
func (s *StateDB) slowDeleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, map[common.Hash][]byte, *trienode.NodeSet, error) {
|
||||
tr, err := s.db.OpenStorageTrie(s.originalRoot, addr, root, s.trie)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("failed to open storage trie, err: %w", err)
|
||||
}
|
||||
it, err := tr.NodeIterator(nil)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("failed to open storage iterator, err: %w", err)
|
||||
}
|
||||
var (
|
||||
nodes = trienode.NewNodeSet(addrHash) // the set for trie node mutations (value is nil)
|
||||
storages = make(map[common.Hash][]byte) // the set for storage mutations (value is nil)
|
||||
storageOrigins = make(map[common.Hash][]byte) // the set for tracking the original value of slot
|
||||
)
|
||||
for it.Next(true) {
|
||||
if it.Leaf() {
|
||||
key := common.BytesToHash(it.LeafKey())
|
||||
storages[key] = nil
|
||||
storageOrigins[key] = common.CopyBytes(it.LeafBlob())
|
||||
continue
|
||||
}
|
||||
if it.Hash() == (common.Hash{}) {
|
||||
continue
|
||||
}
|
||||
nodes.AddNode(it.Path(), trienode.NewDeletedWithPrev(it.NodeBlob()))
|
||||
}
|
||||
if err := it.Error(); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
return storages, storageOrigins, nodes, nil
|
||||
}
|
||||
|
||||
// deleteStorage is designed to delete the storage trie of a designated account.
|
||||
// The function will make an attempt to utilize an efficient strategy if the
|
||||
// associated state snapshot is reachable; otherwise, it will resort to a less
|
||||
// efficient approach.
|
||||
func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, map[common.Hash][]byte, *trienode.NodeSet, error) {
|
||||
var (
|
||||
err error
|
||||
nodes *trienode.NodeSet // the set for trie node mutations (value is nil)
|
||||
storages map[common.Hash][]byte // the set for storage mutations (value is nil)
|
||||
storageOrigins map[common.Hash][]byte // the set for tracking the original value of slot
|
||||
)
|
||||
// The fast approach can be failed if the snapshot is not fully
|
||||
// generated, or it's internally corrupted. Fallback to the slow
|
||||
// one just in case.
|
||||
snaps := s.db.Snapshot()
|
||||
if snaps != nil {
|
||||
storages, storageOrigins, nodes, err = s.fastDeleteStorage(snaps, addrHash, root)
|
||||
}
|
||||
if snaps == nil || err != nil {
|
||||
storages, storageOrigins, nodes, err = s.slowDeleteStorage(addr, addrHash, root)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
return storages, storageOrigins, nodes, nil
|
||||
}
|
||||
|
||||
// handleDestruction processes all destruction markers and deletes the account
|
||||
// and associated storage slots if necessary. There are four potential scenarios
|
||||
// as following:
|
||||
|
|
@ -1144,7 +1133,7 @@ func (s *StateDB) handleDestruction(noStorageWiping bool) (map[common.Hash]*acco
|
|||
return nil, nil, fmt.Errorf("unexpected storage wiping, %x", addr)
|
||||
}
|
||||
// Remove storage slots belonging to the account.
|
||||
storages, storagesOrigin, set, err := s.deleteStorage(addr, addrHash, prev.Root)
|
||||
storages, storagesOrigin, set, err := s.deleteStorage(addrHash, prev.Root)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to delete storage, err: %w", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -229,6 +229,10 @@ func (s *hookedStateDB) AddLog(log *types.Log) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *hookedStateDB) LogsForBurnAccounts() []*types.Log {
|
||||
return s.inner.LogsForBurnAccounts()
|
||||
}
|
||||
|
||||
func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) {
|
||||
if s.hooks.OnBalanceChange == nil && s.hooks.OnNonceChangeV2 == nil && s.hooks.OnNonceChange == nil && s.hooks.OnCodeChangeV2 == nil && s.hooks.OnCodeChange == nil {
|
||||
// Short circuit if no relevant hooks are set.
|
||||
|
|
|
|||
|
|
@ -1296,12 +1296,12 @@ func TestDeleteStorage(t *testing.T) {
|
|||
obj := fastState.getOrNewStateObject(addr)
|
||||
storageRoot := obj.data.Root
|
||||
|
||||
_, _, fastNodes, err := fastState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot)
|
||||
_, _, fastNodes, err := fastState.deleteStorage(crypto.Keccak256Hash(addr[:]), storageRoot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, _, slowNodes, err := slowState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot)
|
||||
_, _, slowNodes, err := slowState.deleteStorage(crypto.Keccak256Hash(addr[:]), storageRoot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -260,6 +260,9 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) {
|
|||
evm.SetTxContext(NewEVMTxContext(msg))
|
||||
evm.StateDB.AddAddressToAccessList(params.BeaconRootsAddress)
|
||||
_, _, _ = evm.Call(msg.From, *msg.To, msg.Data, 30_000_000, common.U2560)
|
||||
if evm.StateDB.AccessEvents() != nil {
|
||||
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
||||
}
|
||||
evm.StateDB.Finalise(true)
|
||||
}
|
||||
|
||||
|
|
@ -323,6 +326,9 @@ func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte
|
|||
evm.SetTxContext(NewEVMTxContext(msg))
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
ret, _, err := evm.Call(msg.From, *msg.To, msg.Data, 30_000_000, common.U2560)
|
||||
if evm.StateDB.AccessEvents() != nil {
|
||||
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
||||
}
|
||||
evm.StateDB.Finalise(true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("system call failed to execute: %v", err)
|
||||
|
|
|
|||
|
|
@ -583,6 +583,11 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
st.evm.AccessEvents.AddAccount(st.evm.Context.Coinbase, true, math.MaxUint64)
|
||||
}
|
||||
}
|
||||
if rules.IsAmsterdam {
|
||||
for _, log := range st.evm.StateDB.LogsForBurnAccounts() {
|
||||
st.evm.StateDB.AddLog(log)
|
||||
}
|
||||
}
|
||||
return &ExecutionResult{
|
||||
UsedGas: st.gasUsed(),
|
||||
MaxUsedGas: peakGasUsed,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package stateless
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
|
|
@ -42,6 +43,9 @@ func (w *Witness) ToExtWitness() *ExtWitness {
|
|||
|
||||
// FromExtWitness converts the consensus witness format into our internal one.
|
||||
func (w *Witness) FromExtWitness(ext *ExtWitness) error {
|
||||
if len(ext.Headers) == 0 {
|
||||
return errors.New("witness must contain at least one header")
|
||||
}
|
||||
w.Headers = ext.Headers
|
||||
|
||||
w.Codes = make(map[string]struct{}, len(ext.Codes))
|
||||
|
|
|
|||
|
|
@ -54,6 +54,13 @@ func NewWitnessStats() *WitnessStats {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *WitnessStats) copy() *WitnessStats {
|
||||
return &WitnessStats{
|
||||
accountTrie: s.accountTrie.Copy(),
|
||||
storageTrie: s.storageTrie.Copy(),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *WitnessStats) init() {
|
||||
if s.accountTrie == nil {
|
||||
s.accountTrie = trie.NewLevelStats()
|
||||
|
|
|
|||
|
|
@ -42,12 +42,13 @@ type Witness struct {
|
|||
Codes map[string]struct{} // Set of bytecodes ran or accessed
|
||||
State map[string]struct{} // Set of MPT state trie nodes (account and storage together)
|
||||
|
||||
chain HeaderReader // Chain reader to convert block hash ops to header proofs
|
||||
lock sync.Mutex // Lock to allow concurrent state insertions
|
||||
chain HeaderReader // Chain reader to convert block hash ops to header proofs
|
||||
stats *WitnessStats // Optional statistics collector
|
||||
lock sync.Mutex // Lock to allow concurrent state insertions
|
||||
}
|
||||
|
||||
// NewWitness creates an empty witness ready for population.
|
||||
func NewWitness(context *types.Header, chain HeaderReader) (*Witness, error) {
|
||||
func NewWitness(context *types.Header, chain HeaderReader, enableStats bool) (*Witness, error) {
|
||||
// When building witnesses, retrieve the parent header, which will *always*
|
||||
// be included to act as a trustless pre-root hash container
|
||||
var headers []*types.Header
|
||||
|
|
@ -59,13 +60,17 @@ func NewWitness(context *types.Header, chain HeaderReader) (*Witness, error) {
|
|||
headers = append(headers, parent)
|
||||
}
|
||||
// Create the witness with a reconstructed gutted out block
|
||||
return &Witness{
|
||||
w := &Witness{
|
||||
context: context,
|
||||
Headers: headers,
|
||||
Codes: make(map[string]struct{}),
|
||||
State: make(map[string]struct{}),
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
if enableStats {
|
||||
w.stats = NewWitnessStats()
|
||||
}
|
||||
return w, nil
|
||||
}
|
||||
|
||||
// AddBlockHash adds a "blockhash" to the witness with the designated offset from
|
||||
|
|
@ -87,8 +92,11 @@ func (w *Witness) AddCode(code []byte) {
|
|||
w.Codes[string(code)] = struct{}{}
|
||||
}
|
||||
|
||||
// AddState inserts a batch of MPT trie nodes into the witness.
|
||||
func (w *Witness) AddState(nodes map[string][]byte) {
|
||||
// AddState inserts a batch of MPT trie nodes into the witness. The owner
|
||||
// identifies which trie the nodes belong to: the zero hash for the account
|
||||
// trie, or the hashed address for a storage trie. This is used for optional
|
||||
// statistics collection.
|
||||
func (w *Witness) AddState(nodes map[string][]byte, owner common.Hash) {
|
||||
if len(nodes) == 0 {
|
||||
return
|
||||
}
|
||||
|
|
@ -98,6 +106,17 @@ func (w *Witness) AddState(nodes map[string][]byte) {
|
|||
for _, value := range nodes {
|
||||
w.State[string(value)] = struct{}{}
|
||||
}
|
||||
if w.stats != nil {
|
||||
w.stats.Add(nodes, owner)
|
||||
}
|
||||
}
|
||||
|
||||
// ReportMetrics reports the collected statistics to the global metrics registry.
|
||||
func (w *Witness) ReportMetrics(blockNumber uint64) {
|
||||
if w.stats == nil {
|
||||
return
|
||||
}
|
||||
w.stats.ReportMetrics(blockNumber)
|
||||
}
|
||||
|
||||
func (w *Witness) AddKey() {
|
||||
|
|
@ -113,6 +132,9 @@ func (w *Witness) Copy() *Witness {
|
|||
State: maps.Clone(w.State),
|
||||
chain: w.chain,
|
||||
}
|
||||
if w.stats != nil {
|
||||
cpy.stats = w.stats.copy()
|
||||
}
|
||||
if w.context != nil {
|
||||
cpy.context = types.CopyHeader(w.context)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -426,7 +426,7 @@ const (
|
|||
// NonceChangeNewContract is the nonce change of a newly created contract.
|
||||
NonceChangeNewContract NonceChangeReason = 4
|
||||
|
||||
// NonceChangeTransaction is the nonce change due to a EIP-7702 authorization.
|
||||
// NonceChangeAuthorization is the nonce change due to a EIP-7702 authorization.
|
||||
NonceChangeAuthorization NonceChangeReason = 5
|
||||
|
||||
// NonceChangeRevert is emitted when the nonce is reverted back to a previous value due to call failure.
|
||||
|
|
|
|||
|
|
@ -1865,11 +1865,11 @@ func (p *BlobPool) drop() {
|
|||
//
|
||||
// The transactions can also be pre-filtered by the dynamic fee components to
|
||||
// reduce allocations and load on downstream subsystems.
|
||||
func (p *BlobPool) Pending(filter txpool.PendingFilter) map[common.Address][]*txpool.LazyTransaction {
|
||||
func (p *BlobPool) Pending(filter txpool.PendingFilter) (map[common.Address][]*txpool.LazyTransaction, int) {
|
||||
// If only plain transactions are requested, this pool is unsuitable as it
|
||||
// contains none, don't even bother.
|
||||
if !filter.BlobTxs {
|
||||
return nil
|
||||
return nil, 0
|
||||
}
|
||||
// Track the amount of time waiting to retrieve the list of pending blob txs
|
||||
// from the pool and the amount of time actually spent on assembling the data.
|
||||
|
|
@ -1885,6 +1885,7 @@ func (p *BlobPool) Pending(filter txpool.PendingFilter) map[common.Address][]*tx
|
|||
pendtimeHist.Update(time.Since(execStart).Nanoseconds())
|
||||
}()
|
||||
|
||||
var count int
|
||||
pending := make(map[common.Address][]*txpool.LazyTransaction, len(p.index))
|
||||
for addr, txs := range p.index {
|
||||
lazies := make([]*txpool.LazyTransaction, 0, len(txs))
|
||||
|
|
@ -1930,9 +1931,10 @@ func (p *BlobPool) Pending(filter txpool.PendingFilter) map[common.Address][]*tx
|
|||
}
|
||||
if len(lazies) > 0 {
|
||||
pending[addr] = lazies
|
||||
count += len(lazies)
|
||||
}
|
||||
}
|
||||
return pending
|
||||
return pending, count
|
||||
}
|
||||
|
||||
// updateStorageMetrics retrieves a bunch of stats from the data store and pushes
|
||||
|
|
|
|||
|
|
@ -2122,7 +2122,7 @@ func benchmarkPoolPending(b *testing.B, datacap uint64) {
|
|||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
p := pool.Pending(txpool.PendingFilter{
|
||||
p, _ := pool.Pending(txpool.PendingFilter{
|
||||
MinTip: uint256.NewInt(1),
|
||||
BaseFee: chain.basefee,
|
||||
BlobFee: chain.blobfee,
|
||||
|
|
|
|||
|
|
@ -494,15 +494,16 @@ func (pool *LegacyPool) ContentFrom(addr common.Address) ([]*types.Transaction,
|
|||
//
|
||||
// The transactions can also be pre-filtered by the dynamic fee components to
|
||||
// reduce allocations and load on downstream subsystems.
|
||||
func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address][]*txpool.LazyTransaction {
|
||||
func (pool *LegacyPool) Pending(filter txpool.PendingFilter) (map[common.Address][]*txpool.LazyTransaction, int) {
|
||||
// If only blob transactions are requested, this pool is unsuitable as it
|
||||
// contains none, don't even bother.
|
||||
if filter.BlobTxs {
|
||||
return nil
|
||||
return nil, 0
|
||||
}
|
||||
pool.mu.Lock()
|
||||
defer pool.mu.Unlock()
|
||||
|
||||
var count int
|
||||
pending := make(map[common.Address][]*txpool.LazyTransaction, len(pool.pending))
|
||||
for addr, list := range pool.pending {
|
||||
txs := list.Flatten()
|
||||
|
|
@ -539,9 +540,10 @@ func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address]
|
|||
}
|
||||
}
|
||||
pending[addr] = lazies
|
||||
count += len(lazies)
|
||||
}
|
||||
}
|
||||
return pending
|
||||
return pending, count
|
||||
}
|
||||
|
||||
// ValidateTxBasics checks whether a transaction is valid according to the consensus
|
||||
|
|
@ -996,11 +998,7 @@ func (pool *LegacyPool) Status(hash common.Hash) txpool.TxStatus {
|
|||
|
||||
// Get returns a transaction if it is contained in the pool and nil otherwise.
|
||||
func (pool *LegacyPool) Get(hash common.Hash) *types.Transaction {
|
||||
tx := pool.get(hash)
|
||||
if tx == nil {
|
||||
return nil
|
||||
}
|
||||
return tx
|
||||
return pool.get(hash)
|
||||
}
|
||||
|
||||
// get returns a transaction if it is contained in the pool and nil otherwise.
|
||||
|
|
@ -1406,7 +1404,7 @@ func (pool *LegacyPool) promoteExecutables(accounts []common.Address) []*types.T
|
|||
// promote all promotable transactions
|
||||
promoted := make([]*types.Transaction, 0, len(promotable))
|
||||
for _, tx := range promotable {
|
||||
from, _ := pool.signer.Sender(tx)
|
||||
from, _ := types.Sender(pool.signer, tx) // already validated
|
||||
if pool.promoteTx(from, tx.Hash(), tx) {
|
||||
promoted = append(promoted, tx)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ type SubPool interface {
|
|||
//
|
||||
// The transactions can also be pre-filtered by the dynamic fee components to
|
||||
// reduce allocations and load on downstream subsystems.
|
||||
Pending(filter PendingFilter) map[common.Address][]*LazyTransaction
|
||||
Pending(filter PendingFilter) (map[common.Address][]*LazyTransaction, int)
|
||||
|
||||
// SubscribeTransactions subscribes to new transaction events. The subscriber
|
||||
// can decide whether to receive notifications only for newly seen transactions
|
||||
|
|
|
|||
|
|
@ -359,14 +359,17 @@ func (p *TxPool) Add(txs []*types.Transaction, sync bool) []error {
|
|||
//
|
||||
// The transactions can also be pre-filtered by the dynamic fee components to
|
||||
// reduce allocations and load on downstream subsystems.
|
||||
func (p *TxPool) Pending(filter PendingFilter) map[common.Address][]*LazyTransaction {
|
||||
func (p *TxPool) Pending(filter PendingFilter) (map[common.Address][]*LazyTransaction, int) {
|
||||
var count int
|
||||
txs := make(map[common.Address][]*LazyTransaction)
|
||||
for _, subpool := range p.subpools {
|
||||
for addr, set := range subpool.Pending(filter) {
|
||||
txs[addr] = set
|
||||
set, n := subpool.Pending(filter)
|
||||
for addr, list := range set {
|
||||
txs[addr] = list
|
||||
}
|
||||
count += n
|
||||
}
|
||||
return txs
|
||||
return txs, count
|
||||
}
|
||||
|
||||
// SubscribeTransactions registers a subscription for new transaction events,
|
||||
|
|
|
|||
|
|
@ -350,9 +350,12 @@ func (e *BlockAccessList) PrettyPrint() string {
|
|||
}
|
||||
|
||||
// Copy returns a deep copy of the access list
|
||||
func (e *BlockAccessList) Copy() (res BlockAccessList) {
|
||||
for _, accountAccess := range e.Accesses {
|
||||
res.Accesses = append(res.Accesses, accountAccess.Copy())
|
||||
func (e *BlockAccessList) Copy() *BlockAccessList {
|
||||
cpy := &BlockAccessList{
|
||||
Accesses: make([]AccountAccess, 0, len(e.Accesses)),
|
||||
}
|
||||
return
|
||||
for _, accountAccess := range e.Accesses {
|
||||
cpy.Accesses = append(cpy.Accesses, accountAccess.Copy())
|
||||
}
|
||||
return cpy
|
||||
}
|
||||
|
|
|
|||
|
|
@ -190,8 +190,8 @@ func makeTestAccountAccess(sort bool) AccountAccess {
|
|||
}
|
||||
}
|
||||
|
||||
func makeTestBAL(sort bool) BlockAccessList {
|
||||
list := BlockAccessList{}
|
||||
func makeTestBAL(sort bool) *BlockAccessList {
|
||||
list := &BlockAccessList{}
|
||||
for i := 0; i < 5; i++ {
|
||||
list.Accesses = append(list.Accesses, makeTestAccountAccess(sort))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
|
|
@ -99,6 +100,9 @@ type Header struct {
|
|||
// RequestsHash was added by EIP-7685 and is ignored in legacy headers.
|
||||
RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"`
|
||||
|
||||
// BlockAccessListHash was added by EIP-7928 and is ignored in legacy headers.
|
||||
BlockAccessListHash *common.Hash `json:"balHash" rlp:"optional"`
|
||||
|
||||
// SlotNumber was added by EIP-7843 and is ignored in legacy headers.
|
||||
SlotNumber *uint64 `json:"slotNumber" rlp:"optional"`
|
||||
}
|
||||
|
|
@ -204,6 +208,7 @@ type Block struct {
|
|||
uncles []*Header
|
||||
transactions Transactions
|
||||
withdrawals Withdrawals
|
||||
accessList *bal.BlockAccessList
|
||||
|
||||
// caches
|
||||
hash atomic.Pointer[common.Hash]
|
||||
|
|
@ -320,6 +325,10 @@ func CopyHeader(h *Header) *Header {
|
|||
cpy.RequestsHash = new(common.Hash)
|
||||
*cpy.RequestsHash = *h.RequestsHash
|
||||
}
|
||||
if h.BlockAccessListHash != nil {
|
||||
cpy.BlockAccessListHash = new(common.Hash)
|
||||
*cpy.BlockAccessListHash = *h.BlockAccessListHash
|
||||
}
|
||||
if h.SlotNumber != nil {
|
||||
cpy.SlotNumber = new(uint64)
|
||||
*cpy.SlotNumber = *h.SlotNumber
|
||||
|
|
@ -358,9 +367,10 @@ func (b *Block) Body() *Body {
|
|||
// Accessors for body data. These do not return a copy because the content
|
||||
// of the body slices does not affect the cached hash/size in block.
|
||||
|
||||
func (b *Block) Uncles() []*Header { return b.uncles }
|
||||
func (b *Block) Transactions() Transactions { return b.transactions }
|
||||
func (b *Block) Withdrawals() Withdrawals { return b.withdrawals }
|
||||
func (b *Block) Uncles() []*Header { return b.uncles }
|
||||
func (b *Block) Transactions() Transactions { return b.transactions }
|
||||
func (b *Block) Withdrawals() Withdrawals { return b.withdrawals }
|
||||
func (b *Block) AccessList() *bal.BlockAccessList { return b.accessList }
|
||||
|
||||
func (b *Block) Transaction(hash common.Hash) *Transaction {
|
||||
for _, transaction := range b.transactions {
|
||||
|
|
@ -495,6 +505,7 @@ func (b *Block) WithSeal(header *Header) *Block {
|
|||
transactions: b.transactions,
|
||||
uncles: b.uncles,
|
||||
withdrawals: b.withdrawals,
|
||||
accessList: b.accessList,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -506,6 +517,7 @@ func (b *Block) WithBody(body Body) *Block {
|
|||
transactions: slices.Clone(body.Transactions),
|
||||
uncles: make([]*Header, len(body.Uncles)),
|
||||
withdrawals: slices.Clone(body.Withdrawals),
|
||||
accessList: b.accessList,
|
||||
}
|
||||
for i := range body.Uncles {
|
||||
block.uncles[i] = CopyHeader(body.Uncles[i])
|
||||
|
|
@ -513,6 +525,24 @@ func (b *Block) WithBody(body Body) *Block {
|
|||
return block
|
||||
}
|
||||
|
||||
// WithAccessList returns a copy of the block with the given access list embedded.
|
||||
func (b *Block) WithAccessList(accessList *bal.BlockAccessList) *Block {
|
||||
return b.WithAccessListUnsafe(accessList.Copy())
|
||||
}
|
||||
|
||||
// WithAccessListUnsafe returns a copy of the block with the given access list
|
||||
// embedded. Note that the access list is not deep-copied; use WithAccessList
|
||||
// if the provided list may be modified by other actors.
|
||||
func (b *Block) WithAccessListUnsafe(accessList *bal.BlockAccessList) *Block {
|
||||
return &Block{
|
||||
header: b.header,
|
||||
transactions: b.transactions,
|
||||
uncles: b.uncles,
|
||||
withdrawals: b.withdrawals,
|
||||
accessList: accessList,
|
||||
}
|
||||
}
|
||||
|
||||
// Hash returns the keccak256 hash of b's header.
|
||||
// The hash is computed on the first call and cached thereafter.
|
||||
func (b *Block) Hash() common.Hash {
|
||||
|
|
|
|||
|
|
@ -16,29 +16,30 @@ var _ = (*headerMarshaling)(nil)
|
|||
// MarshalJSON marshals as JSON.
|
||||
func (h Header) MarshalJSON() ([]byte, error) {
|
||||
type Header struct {
|
||||
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
|
||||
Coinbase common.Address `json:"miner"`
|
||||
Root common.Hash `json:"stateRoot" gencodec:"required"`
|
||||
TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
|
||||
ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||
Bloom Bloom `json:"logsBloom" gencodec:"required"`
|
||||
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
|
||||
Number *hexutil.Big `json:"number" gencodec:"required"`
|
||||
GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
|
||||
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||
Time hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
||||
Extra hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||
MixDigest common.Hash `json:"mixHash"`
|
||||
Nonce BlockNonce `json:"nonce"`
|
||||
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
|
||||
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"`
|
||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
|
||||
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
|
||||
RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"`
|
||||
SlotNumber *hexutil.Uint64 `json:"slotNumber" rlp:"optional"`
|
||||
Hash common.Hash `json:"hash"`
|
||||
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
|
||||
Coinbase common.Address `json:"miner"`
|
||||
Root common.Hash `json:"stateRoot" gencodec:"required"`
|
||||
TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
|
||||
ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||
Bloom Bloom `json:"logsBloom" gencodec:"required"`
|
||||
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
|
||||
Number *hexutil.Big `json:"number" gencodec:"required"`
|
||||
GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
|
||||
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||
Time hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
||||
Extra hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||
MixDigest common.Hash `json:"mixHash"`
|
||||
Nonce BlockNonce `json:"nonce"`
|
||||
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
|
||||
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"`
|
||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
|
||||
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
|
||||
RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"`
|
||||
BlockAccessListHash *common.Hash `json:"balHash" rlp:"optional"`
|
||||
SlotNumber *hexutil.Uint64 `json:"slotNumber" rlp:"optional"`
|
||||
Hash common.Hash `json:"hash"`
|
||||
}
|
||||
var enc Header
|
||||
enc.ParentHash = h.ParentHash
|
||||
|
|
@ -62,6 +63,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
|
|||
enc.ExcessBlobGas = (*hexutil.Uint64)(h.ExcessBlobGas)
|
||||
enc.ParentBeaconRoot = h.ParentBeaconRoot
|
||||
enc.RequestsHash = h.RequestsHash
|
||||
enc.BlockAccessListHash = h.BlockAccessListHash
|
||||
enc.SlotNumber = (*hexutil.Uint64)(h.SlotNumber)
|
||||
enc.Hash = h.Hash()
|
||||
return json.Marshal(&enc)
|
||||
|
|
@ -70,28 +72,29 @@ func (h Header) MarshalJSON() ([]byte, error) {
|
|||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (h *Header) UnmarshalJSON(input []byte) error {
|
||||
type Header struct {
|
||||
ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
|
||||
UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"`
|
||||
Coinbase *common.Address `json:"miner"`
|
||||
Root *common.Hash `json:"stateRoot" gencodec:"required"`
|
||||
TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"`
|
||||
ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||
Bloom *Bloom `json:"logsBloom" gencodec:"required"`
|
||||
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
|
||||
Number *hexutil.Big `json:"number" gencodec:"required"`
|
||||
GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
|
||||
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||
Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
||||
Extra *hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||
MixDigest *common.Hash `json:"mixHash"`
|
||||
Nonce *BlockNonce `json:"nonce"`
|
||||
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
|
||||
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"`
|
||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
|
||||
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
|
||||
RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"`
|
||||
SlotNumber *hexutil.Uint64 `json:"slotNumber" rlp:"optional"`
|
||||
ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
|
||||
UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"`
|
||||
Coinbase *common.Address `json:"miner"`
|
||||
Root *common.Hash `json:"stateRoot" gencodec:"required"`
|
||||
TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"`
|
||||
ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||
Bloom *Bloom `json:"logsBloom" gencodec:"required"`
|
||||
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
|
||||
Number *hexutil.Big `json:"number" gencodec:"required"`
|
||||
GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
|
||||
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||
Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
||||
Extra *hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||
MixDigest *common.Hash `json:"mixHash"`
|
||||
Nonce *BlockNonce `json:"nonce"`
|
||||
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
|
||||
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"`
|
||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
|
||||
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
|
||||
RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"`
|
||||
BlockAccessListHash *common.Hash `json:"balHash" rlp:"optional"`
|
||||
SlotNumber *hexutil.Uint64 `json:"slotNumber" rlp:"optional"`
|
||||
}
|
||||
var dec Header
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
|
|
@ -172,6 +175,9 @@ func (h *Header) UnmarshalJSON(input []byte) error {
|
|||
if dec.RequestsHash != nil {
|
||||
h.RequestsHash = dec.RequestsHash
|
||||
}
|
||||
if dec.BlockAccessListHash != nil {
|
||||
h.BlockAccessListHash = dec.BlockAccessListHash
|
||||
}
|
||||
if dec.SlotNumber != nil {
|
||||
h.SlotNumber = (*uint64)(dec.SlotNumber)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,8 +43,9 @@ func (obj *Header) EncodeRLP(_w io.Writer) error {
|
|||
_tmp4 := obj.ExcessBlobGas != nil
|
||||
_tmp5 := obj.ParentBeaconRoot != nil
|
||||
_tmp6 := obj.RequestsHash != nil
|
||||
_tmp7 := obj.SlotNumber != nil
|
||||
if _tmp1 || _tmp2 || _tmp3 || _tmp4 || _tmp5 || _tmp6 || _tmp7 {
|
||||
_tmp7 := obj.BlockAccessListHash != nil
|
||||
_tmp8 := obj.SlotNumber != nil
|
||||
if _tmp1 || _tmp2 || _tmp3 || _tmp4 || _tmp5 || _tmp6 || _tmp7 || _tmp8 {
|
||||
if obj.BaseFee == nil {
|
||||
w.Write(rlp.EmptyString)
|
||||
} else {
|
||||
|
|
@ -54,42 +55,49 @@ func (obj *Header) EncodeRLP(_w io.Writer) error {
|
|||
w.WriteBigInt(obj.BaseFee)
|
||||
}
|
||||
}
|
||||
if _tmp2 || _tmp3 || _tmp4 || _tmp5 || _tmp6 || _tmp7 {
|
||||
if _tmp2 || _tmp3 || _tmp4 || _tmp5 || _tmp6 || _tmp7 || _tmp8 {
|
||||
if obj.WithdrawalsHash == nil {
|
||||
w.Write([]byte{0x80})
|
||||
} else {
|
||||
w.WriteBytes(obj.WithdrawalsHash[:])
|
||||
}
|
||||
}
|
||||
if _tmp3 || _tmp4 || _tmp5 || _tmp6 || _tmp7 {
|
||||
if _tmp3 || _tmp4 || _tmp5 || _tmp6 || _tmp7 || _tmp8 {
|
||||
if obj.BlobGasUsed == nil {
|
||||
w.Write([]byte{0x80})
|
||||
} else {
|
||||
w.WriteUint64((*obj.BlobGasUsed))
|
||||
}
|
||||
}
|
||||
if _tmp4 || _tmp5 || _tmp6 || _tmp7 {
|
||||
if _tmp4 || _tmp5 || _tmp6 || _tmp7 || _tmp8 {
|
||||
if obj.ExcessBlobGas == nil {
|
||||
w.Write([]byte{0x80})
|
||||
} else {
|
||||
w.WriteUint64((*obj.ExcessBlobGas))
|
||||
}
|
||||
}
|
||||
if _tmp5 || _tmp6 || _tmp7 {
|
||||
if _tmp5 || _tmp6 || _tmp7 || _tmp8 {
|
||||
if obj.ParentBeaconRoot == nil {
|
||||
w.Write([]byte{0x80})
|
||||
} else {
|
||||
w.WriteBytes(obj.ParentBeaconRoot[:])
|
||||
}
|
||||
}
|
||||
if _tmp6 || _tmp7 {
|
||||
if _tmp6 || _tmp7 || _tmp8 {
|
||||
if obj.RequestsHash == nil {
|
||||
w.Write([]byte{0x80})
|
||||
} else {
|
||||
w.WriteBytes(obj.RequestsHash[:])
|
||||
}
|
||||
}
|
||||
if _tmp7 {
|
||||
if _tmp7 || _tmp8 {
|
||||
if obj.BlockAccessListHash == nil {
|
||||
w.Write([]byte{0x80})
|
||||
} else {
|
||||
w.WriteBytes(obj.BlockAccessListHash[:])
|
||||
}
|
||||
}
|
||||
if _tmp8 {
|
||||
if obj.SlotNumber == nil {
|
||||
w.Write([]byte{0x80})
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ package types
|
|||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
//go:generate go run ../../rlp/rlpgen -type Log -out gen_log_rlp.go
|
||||
|
|
@ -62,3 +64,32 @@ type logMarshaling struct {
|
|||
BlockTimestamp hexutil.Uint64
|
||||
Index hexutil.Uint
|
||||
}
|
||||
|
||||
// EthTransferLog creates and ETH transfer log according to EIP-7708.
|
||||
// Specification: https://eips.ethereum.org/EIPS/eip-7708
|
||||
func EthTransferLog(from, to common.Address, amount *uint256.Int) *Log {
|
||||
amount32 := amount.Bytes32()
|
||||
return &Log{
|
||||
Address: params.SystemAddress,
|
||||
Topics: []common.Hash{
|
||||
params.EthTransferLogEvent,
|
||||
common.BytesToHash(from.Bytes()),
|
||||
common.BytesToHash(to.Bytes()),
|
||||
},
|
||||
Data: amount32[:],
|
||||
}
|
||||
}
|
||||
|
||||
// EthBurnLog creates an ETH burn log according to EIP-7708.
|
||||
// Specification: https://eips.ethereum.org/EIPS/eip-7708
|
||||
func EthBurnLog(from common.Address, amount *uint256.Int) *Log {
|
||||
amount32 := amount.Bytes32()
|
||||
return &Log{
|
||||
Address: params.SystemAddress,
|
||||
Topics: []common.Hash{
|
||||
params.EthBurnLogEvent,
|
||||
common.BytesToHash(from.Bytes()),
|
||||
},
|
||||
Data: amount32[:],
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ type Contract struct {
|
|||
IsDeployment bool
|
||||
IsSystemCall bool
|
||||
|
||||
Gas uint64
|
||||
Gas GasCosts
|
||||
value *uint256.Int
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ func NewContract(caller common.Address, address common.Address, value *uint256.I
|
|||
caller: caller,
|
||||
address: address,
|
||||
jumpDests: jumpDests,
|
||||
Gas: gas,
|
||||
Gas: GasCosts{RegularGas: gas},
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
|
@ -127,13 +127,13 @@ func (c *Contract) Caller() common.Address {
|
|||
|
||||
// UseGas attempts the use gas and subtracts it and returns true on success
|
||||
func (c *Contract) UseGas(gas uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) (ok bool) {
|
||||
if c.Gas < gas {
|
||||
if c.Gas.RegularGas < gas {
|
||||
return false
|
||||
}
|
||||
if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored {
|
||||
logger.OnGasChange(c.Gas, c.Gas-gas, reason)
|
||||
logger.OnGasChange(c.Gas.RegularGas, c.Gas.RegularGas-gas, reason)
|
||||
}
|
||||
c.Gas -= gas
|
||||
c.Gas.RegularGas -= gas
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -143,9 +143,9 @@ func (c *Contract) RefundGas(gas uint64, logger *tracing.Hooks, reason tracing.G
|
|||
return
|
||||
}
|
||||
if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored {
|
||||
logger.OnGasChange(c.Gas, c.Gas+gas, reason)
|
||||
logger.OnGasChange(c.Gas.RegularGas, c.Gas.RegularGas+gas, reason)
|
||||
}
|
||||
c.Gas += gas
|
||||
c.Gas.RegularGas += gas
|
||||
}
|
||||
|
||||
// Address returns the contracts address
|
||||
|
|
|
|||
|
|
@ -381,7 +381,7 @@ func opExtCodeCopyEIP4762(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, er
|
|||
addr := common.Address(a.Bytes20())
|
||||
code := evm.StateDB.GetCode(addr)
|
||||
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
|
||||
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false, scope.Contract.Gas)
|
||||
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false, scope.Contract.Gas.RegularGas)
|
||||
scope.Contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||
if consumed < wanted {
|
||||
return nil, ErrOutOfGas
|
||||
|
|
@ -407,7 +407,7 @@ func opPush1EIP4762(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
// touch next chunk if PUSH1 is at the boundary. if so, *pc has
|
||||
// advanced past this boundary.
|
||||
contractAddr := scope.Contract.Address()
|
||||
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas)
|
||||
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas.RegularGas)
|
||||
scope.Contract.UseGas(wanted, evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||
if consumed < wanted {
|
||||
return nil, ErrOutOfGas
|
||||
|
|
@ -435,7 +435,7 @@ func makePushEIP4762(size uint64, pushByteSize int) executionFunc {
|
|||
|
||||
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall {
|
||||
contractAddr := scope.Contract.Address()
|
||||
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas)
|
||||
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas.RegularGas)
|
||||
scope.Contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||
if consumed < wanted {
|
||||
return nil, ErrOutOfGas
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ type (
|
|||
// CanTransferFunc is the signature of a transfer guard function
|
||||
CanTransferFunc func(StateDB, common.Address, *uint256.Int) bool
|
||||
// TransferFunc is the signature of a transfer function
|
||||
TransferFunc func(StateDB, common.Address, common.Address, *uint256.Int)
|
||||
TransferFunc func(StateDB, common.Address, common.Address, *uint256.Int, *params.Rules)
|
||||
// GetHashFunc returns the n'th block hash in the blockchain
|
||||
// and is used by the BLOCKHASH EVM op code.
|
||||
GetHashFunc func(uint64) common.Hash
|
||||
|
|
@ -283,8 +283,9 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
|||
// Calling this is required even for zero-value transfers,
|
||||
// to ensure the state clearing mechanism is applied.
|
||||
if !syscall {
|
||||
evm.Context.Transfer(evm.StateDB, caller, addr, value)
|
||||
evm.Context.Transfer(evm.StateDB, caller, addr, value, &evm.chainRules)
|
||||
}
|
||||
|
||||
if isPrecompile {
|
||||
var stateDB StateDB
|
||||
if evm.chainRules.IsAmsterdam {
|
||||
|
|
@ -302,7 +303,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
|||
contract.IsSystemCall = isSystemCall(caller)
|
||||
contract.SetCallCode(evm.resolveCodeHash(addr), code)
|
||||
ret, err = evm.Run(contract, input, false)
|
||||
gas = contract.Gas
|
||||
gas = contract.Gas.RegularGas
|
||||
}
|
||||
}
|
||||
// When an error was returned by the EVM or when setting the creation code
|
||||
|
|
@ -364,7 +365,7 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
|
|||
contract := NewContract(caller, caller, value, gas, evm.jumpDests)
|
||||
contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr))
|
||||
ret, err = evm.Run(contract, input, false)
|
||||
gas = contract.Gas
|
||||
gas = contract.Gas.RegularGas
|
||||
}
|
||||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
|
|
@ -412,7 +413,7 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
|
|||
contract := NewContract(originCaller, caller, value, gas, evm.jumpDests)
|
||||
contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr))
|
||||
ret, err = evm.Run(contract, input, false)
|
||||
gas = contract.Gas
|
||||
gas = contract.Gas.RegularGas
|
||||
}
|
||||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
|
|
@ -471,7 +472,7 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
|
|||
// above we revert to the snapshot and consume any gas remaining. Additionally
|
||||
// when we're in Homestead this also counts for code storage gas errors.
|
||||
ret, err = evm.Run(contract, input, true)
|
||||
gas = contract.Gas
|
||||
gas = contract.Gas.RegularGas
|
||||
}
|
||||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
|
|
@ -567,7 +568,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
|
|||
}
|
||||
gas = gas - consumed
|
||||
}
|
||||
evm.Context.Transfer(evm.StateDB, caller, address, value)
|
||||
evm.Context.Transfer(evm.StateDB, caller, address, value, &evm.chainRules)
|
||||
|
||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||
// The contract is a scoped environment for this execution context only.
|
||||
|
|
@ -582,10 +583,10 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
|
|||
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
if err != ErrExecutionReverted {
|
||||
contract.UseGas(contract.Gas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution)
|
||||
contract.UseGas(contract.Gas.RegularGas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution)
|
||||
}
|
||||
}
|
||||
return ret, address, contract.Gas, err
|
||||
return ret, address, contract.Gas.RegularGas, err
|
||||
}
|
||||
|
||||
// initNewContract runs a new contract's creation code, performs checks on the
|
||||
|
|
@ -612,7 +613,7 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
|
|||
return ret, ErrCodeStoreOutOfGas
|
||||
}
|
||||
} else {
|
||||
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true, contract.Gas)
|
||||
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true, contract.Gas.RegularGas)
|
||||
contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
|
||||
if len(ret) > 0 && (consumed < wanted) {
|
||||
return ret, ErrCodeStoreOutOfGas
|
||||
|
|
|
|||
|
|
@ -49,6 +49,5 @@ func callGas(isEip150 bool, availableGas, base uint64, callCost *uint256.Int) (u
|
|||
if !callCost.IsUint64() {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
|
||||
return callCost.Uint64(), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,26 +64,26 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
|
|||
// EXTCODECOPY (stack position 3)
|
||||
// RETURNDATACOPY (stack position 2)
|
||||
func memoryCopierGas(stackpos int) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
// Gas for expanding the memory
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
// And gas for copying data, charged per word at param.CopyGas
|
||||
words, overflow := stack.Back(stackpos).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
|
||||
if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
|
||||
if gas, overflow = math.SafeAdd(gas, words); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,9 +95,9 @@ var (
|
|||
gasReturnDataCopy = memoryCopierGas(2)
|
||||
)
|
||||
|
||||
func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
if evm.readOnly {
|
||||
return 0, ErrWriteProtection
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
}
|
||||
var (
|
||||
y, x = stack.Back(1), stack.Back(0)
|
||||
|
|
@ -114,12 +114,12 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
|
|||
// 3. From a non-zero to a non-zero (CHANGE)
|
||||
switch {
|
||||
case current == (common.Hash{}) && y.Sign() != 0: // 0 => non 0
|
||||
return params.SstoreSetGas, nil
|
||||
return GasCosts{RegularGas: params.SstoreSetGas}, nil
|
||||
case current != (common.Hash{}) && y.Sign() == 0: // non 0 => 0
|
||||
evm.StateDB.AddRefund(params.SstoreRefundGas)
|
||||
return params.SstoreClearGas, nil
|
||||
return GasCosts{RegularGas: params.SstoreClearGas}, nil
|
||||
default: // non 0 => non 0 (or 0 => 0)
|
||||
return params.SstoreResetGas, nil
|
||||
return GasCosts{RegularGas: params.SstoreResetGas}, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -139,16 +139,16 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
|
|||
// (2.2.2.2.) Otherwise, add 4800 gas to refund counter.
|
||||
value := common.Hash(y.Bytes32())
|
||||
if current == value { // noop (1)
|
||||
return params.NetSstoreNoopGas, nil
|
||||
return GasCosts{RegularGas: params.NetSstoreNoopGas}, nil
|
||||
}
|
||||
if original == current {
|
||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||
return params.NetSstoreInitGas, nil
|
||||
return GasCosts{RegularGas: params.NetSstoreInitGas}, nil
|
||||
}
|
||||
if value == (common.Hash{}) { // delete slot (2.1.2b)
|
||||
evm.StateDB.AddRefund(params.NetSstoreClearRefund)
|
||||
}
|
||||
return params.NetSstoreCleanGas, nil // write existing slot (2.1.2)
|
||||
return GasCosts{RegularGas: params.NetSstoreCleanGas}, nil // write existing slot (2.1.2)
|
||||
}
|
||||
if original != (common.Hash{}) {
|
||||
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
|
||||
|
|
@ -164,7 +164,7 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
|
|||
evm.StateDB.AddRefund(params.NetSstoreResetRefund)
|
||||
}
|
||||
}
|
||||
return params.NetSstoreDirtyGas, nil
|
||||
return GasCosts{RegularGas: params.NetSstoreDirtyGas}, nil
|
||||
}
|
||||
|
||||
// Here come the EIP2200 rules:
|
||||
|
|
@ -182,13 +182,13 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
|
|||
// (2.2.2.) If original value equals new value (this storage slot is reset):
|
||||
// (2.2.2.1.) If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter.
|
||||
// (2.2.2.2.) Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter.
|
||||
func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
if evm.readOnly {
|
||||
return 0, ErrWriteProtection
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
}
|
||||
// If we fail the minimum gas availability invariant, fail (0)
|
||||
if contract.Gas <= params.SstoreSentryGasEIP2200 {
|
||||
return 0, errors.New("not enough gas for reentrancy sentry")
|
||||
if contract.Gas.RegularGas <= params.SstoreSentryGasEIP2200 {
|
||||
return GasCosts{}, errors.New("not enough gas for reentrancy sentry")
|
||||
}
|
||||
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||
var (
|
||||
|
|
@ -198,16 +198,16 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
|||
value := common.Hash(y.Bytes32())
|
||||
|
||||
if current == value { // noop (1)
|
||||
return params.SloadGasEIP2200, nil
|
||||
return GasCosts{RegularGas: params.SloadGasEIP2200}, nil
|
||||
}
|
||||
if original == current {
|
||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||
return params.SstoreSetGasEIP2200, nil
|
||||
return GasCosts{RegularGas: params.SstoreSetGasEIP2200}, nil
|
||||
}
|
||||
if value == (common.Hash{}) { // delete slot (2.1.2b)
|
||||
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
|
||||
}
|
||||
return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
|
||||
return GasCosts{RegularGas: params.SstoreResetGasEIP2200}, nil // write existing slot (2.1.2)
|
||||
}
|
||||
if original != (common.Hash{}) {
|
||||
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
|
||||
|
|
@ -223,62 +223,66 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
|||
evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
|
||||
}
|
||||
}
|
||||
return params.SloadGasEIP2200, nil // dirty update (2.2)
|
||||
return GasCosts{RegularGas: params.SloadGasEIP2200}, nil // dirty update (2.2)
|
||||
}
|
||||
|
||||
func makeGasLog(n uint64) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
requestedSize, overflow := stack.Back(1).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
|
||||
if gas, overflow = math.SafeAdd(gas, params.LogGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, n*params.LogTopicGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
|
||||
var memorySizeGas uint64
|
||||
if memorySizeGas, overflow = math.SafeMul(requestedSize, params.LogDataGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, memorySizeGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
wordGas, overflow := stack.Back(1).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
||||
// pureMemoryGascost is used by several operations, which aside from their
|
||||
// static cost have a dynamic cost which is solely based on the memory
|
||||
// expansion
|
||||
func pureMemoryGascost(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return memoryGasCost(mem, memorySize)
|
||||
func pureMemoryGascost(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
}
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
@ -290,64 +294,64 @@ var (
|
|||
gasCreate = pureMemoryGascost
|
||||
)
|
||||
|
||||
func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
wordGas, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
||||
func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
size, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
// Since size <= the protocol-defined maximum initcode size limit, these multiplication cannot overflow
|
||||
moreGas := params.InitCodeWordGas * ((size + 31) / 32)
|
||||
if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
size, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
// Since size <= the protocol-defined maximum initcode size limit, these multiplication cannot overflow
|
||||
moreGas := (params.InitCodeWordGas + params.Keccak256WordGas) * ((size + 31) / 32)
|
||||
if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
||||
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
||||
|
||||
var (
|
||||
|
|
@ -355,12 +359,12 @@ func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
|
|||
overflow bool
|
||||
)
|
||||
if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
||||
func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
||||
|
||||
var (
|
||||
|
|
@ -368,55 +372,82 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
|
|||
overflow bool
|
||||
)
|
||||
if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
||||
func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
var (
|
||||
gasCall = makeCallVariantGasCost(gasCallIntrinsic)
|
||||
gasCallCode = makeCallVariantGasCost(gasCallCodeIntrinsic)
|
||||
gasDelegateCall = makeCallVariantGasCost(gasDelegateCallIntrinsic)
|
||||
gasStaticCall = makeCallVariantGasCost(gasStaticCallIntrinsic)
|
||||
)
|
||||
|
||||
func makeCallVariantGasCost(intrinsicFunc gasFunc) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
intrinsic, err := intrinsicFunc(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
}
|
||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, intrinsic.RegularGas, stack.Back(0))
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
}
|
||||
gas, overflow := math.SafeAdd(intrinsic.RegularGas, evm.callGasTemp)
|
||||
if overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func gasCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
var (
|
||||
gas uint64
|
||||
transfersValue = !stack.Back(2).IsZero()
|
||||
address = common.Address(stack.Back(1).Bytes20())
|
||||
)
|
||||
if evm.readOnly && transfersValue {
|
||||
return 0, ErrWriteProtection
|
||||
}
|
||||
|
||||
if evm.chainRules.IsEIP158 {
|
||||
if transfersValue && evm.StateDB.Empty(address) {
|
||||
gas += params.CallNewAccountGas
|
||||
}
|
||||
} else if !evm.StateDB.Exist(address) {
|
||||
gas += params.CallNewAccountGas
|
||||
}
|
||||
if transfersValue && !evm.chainRules.IsEIP4762 {
|
||||
gas += params.CallValueTransferGas
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
}
|
||||
// Stateless check
|
||||
memoryGas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
var transferGas uint64
|
||||
if transfersValue && !evm.chainRules.IsEIP4762 {
|
||||
transferGas = params.CallValueTransferGas
|
||||
}
|
||||
var overflow bool
|
||||
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
if gas, overflow = math.SafeAdd(memoryGas, transferGas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
|
||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
// Terminate the gas measurement if the leftover gas is not sufficient,
|
||||
// it can effectively prevent accessing the states in the following steps.
|
||||
if contract.Gas.RegularGas < gas {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
// Stateful check
|
||||
var stateGas uint64
|
||||
if evm.chainRules.IsEIP158 {
|
||||
if transfersValue && evm.StateDB.Empty(address) {
|
||||
stateGas += params.CallNewAccountGas
|
||||
}
|
||||
} else if !evm.StateDB.Exist(address) {
|
||||
stateGas += params.CallNewAccountGas
|
||||
}
|
||||
|
||||
return gas, nil
|
||||
if gas, overflow = math.SafeAdd(gas, stateGas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
||||
func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasCallCodeIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
memoryGas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
var (
|
||||
gas uint64
|
||||
|
|
@ -426,53 +457,30 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory
|
|||
gas += params.CallValueTransferGas
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
||||
func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasDelegateCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var overflow bool
|
||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
||||
func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasStaticCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var overflow bool
|
||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
||||
func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
if evm.readOnly {
|
||||
return 0, ErrWriteProtection
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
}
|
||||
|
||||
var gas uint64
|
||||
|
|
@ -494,5 +502,5 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
|||
if !evm.StateDB.HasSelfDestructed(contract.Address()) {
|
||||
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ func TestEIP2200(t *testing.T) {
|
|||
|
||||
vmctx := BlockContext{
|
||||
CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true },
|
||||
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
|
||||
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int, *params.Rules) {},
|
||||
}
|
||||
evm := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}})
|
||||
|
||||
|
|
@ -144,7 +144,7 @@ func TestCreateGas(t *testing.T) {
|
|||
statedb.Finalise(true)
|
||||
vmctx := BlockContext{
|
||||
CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true },
|
||||
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
|
||||
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int, *params.Rules) {},
|
||||
BlockNumber: big.NewInt(0),
|
||||
}
|
||||
config := Config{}
|
||||
|
|
|
|||
36
core/vm/gascosts.go
Normal file
36
core/vm/gascosts.go
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2026 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
import "fmt"
|
||||
|
||||
// GasCosts denotes a vector of gas costs in the
|
||||
// multidimensional metering paradigm.
|
||||
type GasCosts struct {
|
||||
RegularGas uint64
|
||||
StateGas uint64
|
||||
}
|
||||
|
||||
// Sum returns the total gas (regular + state).
|
||||
func (g GasCosts) Sum() uint64 {
|
||||
return g.RegularGas + g.StateGas
|
||||
}
|
||||
|
||||
// String returns a visual representation of the gas vector.
|
||||
func (g GasCosts) String() string {
|
||||
return fmt.Sprintf("<%v,%v>", g.RegularGas, g.StateGas)
|
||||
}
|
||||
|
|
@ -566,7 +566,7 @@ func opMsize(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
}
|
||||
|
||||
func opGas(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetUint64(scope.Contract.Gas))
|
||||
scope.Stack.push(new(uint256.Int).SetUint64(scope.Contract.Gas.RegularGas))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
|
@ -658,7 +658,7 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
value = scope.Stack.pop()
|
||||
offset, size = scope.Stack.pop(), scope.Stack.pop()
|
||||
input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
|
||||
gas = scope.Contract.Gas
|
||||
gas = scope.Contract.Gas.RegularGas
|
||||
)
|
||||
if evm.chainRules.IsEIP150 {
|
||||
gas -= gas / 64
|
||||
|
|
@ -702,7 +702,7 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
offset, size = scope.Stack.pop(), scope.Stack.pop()
|
||||
salt = scope.Stack.pop()
|
||||
input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
|
||||
gas = scope.Contract.Gas
|
||||
gas = scope.Contract.Gas.RegularGas
|
||||
)
|
||||
|
||||
// Apply EIP150
|
||||
|
|
@ -934,6 +934,13 @@ func opSelfdestruct6780(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, erro
|
|||
evm.StateDB.SubBalance(this, balance, tracing.BalanceDecreaseSelfdestruct)
|
||||
evm.StateDB.AddBalance(beneficiary, balance, tracing.BalanceIncreaseSelfdestruct)
|
||||
}
|
||||
if evm.chainRules.IsAmsterdam && !balance.IsZero() {
|
||||
if this != beneficiary {
|
||||
evm.StateDB.AddLog(types.EthTransferLog(this, beneficiary, balance))
|
||||
} else if newContract {
|
||||
evm.StateDB.AddLog(types.EthBurnLog(this, balance))
|
||||
}
|
||||
}
|
||||
|
||||
if tracer := evm.Config.Tracer; tracer != nil {
|
||||
if tracer.OnEnter != nil {
|
||||
|
|
@ -1086,9 +1093,6 @@ func makeLog(size int) executionFunc {
|
|||
Address: scope.Contract.Address(),
|
||||
Topics: topics,
|
||||
Data: d,
|
||||
// This is a non-consensus field, but assigned here because
|
||||
// core/state doesn't know the current block number.
|
||||
BlockNumber: evm.Context.BlockNumber.Uint64(),
|
||||
})
|
||||
|
||||
return nil, nil
|
||||
|
|
|
|||
|
|
@ -879,7 +879,7 @@ func TestOpMCopy(t *testing.T) {
|
|||
if dynamicCost, err := gasMcopy(evm, nil, stack, mem, memorySize); err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
haveGas = GasFastestStep + dynamicCost
|
||||
haveGas = GasFastestStep + dynamicCost.RegularGas
|
||||
}
|
||||
// Expand mem
|
||||
if memorySize > 0 {
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ type StateDB interface {
|
|||
Snapshot() int
|
||||
|
||||
AddLog(*types.Log)
|
||||
LogsForBurnAccounts() []*types.Log
|
||||
AddPreimage(common.Hash, []byte)
|
||||
|
||||
Witness() *stateless.Witness
|
||||
|
|
|
|||
|
|
@ -166,14 +166,14 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
|||
for {
|
||||
if debug {
|
||||
// Capture pre-execution values for tracing.
|
||||
logged, pcCopy, gasCopy = false, pc, contract.Gas
|
||||
logged, pcCopy, gasCopy = false, pc, contract.Gas.RegularGas
|
||||
}
|
||||
|
||||
if isEIP4762 && !contract.IsDeployment && !contract.IsSystemCall {
|
||||
// if the PC ends up in a new "chunk" of verkleized code, charge the
|
||||
// associated costs.
|
||||
contractAddr := contract.Address()
|
||||
consumed, wanted := evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false, contract.Gas)
|
||||
consumed, wanted := evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false, contract.Gas.RegularGas)
|
||||
contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
|
||||
if consumed < wanted {
|
||||
return nil, ErrOutOfGas
|
||||
|
|
@ -192,10 +192,10 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
|||
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
|
||||
}
|
||||
// for tracing: this gas consumption event is emitted below in the debug section.
|
||||
if contract.Gas < cost {
|
||||
if contract.Gas.RegularGas < cost {
|
||||
return nil, ErrOutOfGas
|
||||
} else {
|
||||
contract.Gas -= cost
|
||||
contract.Gas.RegularGas -= cost
|
||||
}
|
||||
|
||||
// All ops with a dynamic memory usage also has a dynamic gas cost.
|
||||
|
|
@ -218,17 +218,17 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
|||
}
|
||||
// Consume the gas and return an error if not enough gas is available.
|
||||
// cost is explicitly set so that the capture state defer method can get the proper cost
|
||||
var dynamicCost uint64
|
||||
var dynamicCost GasCosts
|
||||
dynamicCost, err = operation.dynamicGas(evm, contract, stack, mem, memorySize)
|
||||
cost += dynamicCost // for tracing
|
||||
cost += dynamicCost.RegularGas // for tracing
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err)
|
||||
}
|
||||
// for tracing: this gas consumption event is emitted below in the debug section.
|
||||
if contract.Gas < dynamicCost {
|
||||
if contract.Gas.RegularGas < dynamicCost.RegularGas {
|
||||
return nil, ErrOutOfGas
|
||||
} else {
|
||||
contract.Gas -= dynamicCost
|
||||
contract.Gas.RegularGas -= dynamicCost.RegularGas
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ var loopInterruptTests = []string{
|
|||
func TestLoopInterrupt(t *testing.T) {
|
||||
address := common.BytesToAddress([]byte("contract"))
|
||||
vmctx := BlockContext{
|
||||
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
|
||||
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int, *params.Rules) {},
|
||||
}
|
||||
|
||||
for i, tt := range loopInterruptTests {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import (
|
|||
|
||||
type (
|
||||
executionFunc func(pc *uint64, evm *EVM, callContext *ScopeContext) ([]byte, error)
|
||||
gasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
|
||||
gasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (GasCosts, error) // last parameter is the requested memory size as a uint64
|
||||
// memorySizeFunc returns the required size, and whether the operation overflowed a uint64
|
||||
memorySizeFunc func(*Stack) (size uint64, overflow bool)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -27,13 +27,13 @@ import (
|
|||
)
|
||||
|
||||
func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
if evm.readOnly {
|
||||
return 0, ErrWriteProtection
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
}
|
||||
// If we fail the minimum gas availability invariant, fail (0)
|
||||
if contract.Gas <= params.SstoreSentryGasEIP2200 {
|
||||
return 0, errors.New("not enough gas for reentrancy sentry")
|
||||
if contract.Gas.RegularGas <= params.SstoreSentryGasEIP2200 {
|
||||
return GasCosts{}, errors.New("not enough gas for reentrancy sentry")
|
||||
}
|
||||
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||
var (
|
||||
|
|
@ -53,18 +53,18 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
|||
if current == value { // noop (1)
|
||||
// EIP 2200 original clause:
|
||||
// return params.SloadGasEIP2200, nil
|
||||
return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS
|
||||
return GasCosts{RegularGas: cost + params.WarmStorageReadCostEIP2929}, nil // SLOAD_GAS
|
||||
}
|
||||
if original == current {
|
||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||
return cost + params.SstoreSetGasEIP2200, nil
|
||||
return GasCosts{RegularGas: cost + params.SstoreSetGasEIP2200}, nil
|
||||
}
|
||||
if value == (common.Hash{}) { // delete slot (2.1.2b)
|
||||
evm.StateDB.AddRefund(clearingRefund)
|
||||
}
|
||||
// EIP-2200 original clause:
|
||||
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
|
||||
return cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929), nil // write existing slot (2.1.2)
|
||||
return GasCosts{RegularGas: cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929)}, nil // write existing slot (2.1.2)
|
||||
}
|
||||
if original != (common.Hash{}) {
|
||||
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
|
||||
|
|
@ -89,7 +89,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
|||
}
|
||||
// EIP-2200 original clause:
|
||||
//return params.SloadGasEIP2200, nil // dirty update (2.2)
|
||||
return cost + params.WarmStorageReadCostEIP2929, nil // dirty update (2.2)
|
||||
return GasCosts{RegularGas: cost + params.WarmStorageReadCostEIP2929}, nil // dirty update (2.2)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -98,7 +98,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
|||
// whose storage is being read) is not yet in accessed_storage_keys,
|
||||
// charge 2100 gas and add the pair to accessed_storage_keys.
|
||||
// If the pair is already in accessed_storage_keys, charge 100 gas.
|
||||
func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
loc := stack.peek()
|
||||
slot := common.Hash(loc.Bytes32())
|
||||
// Check slot presence in the access list
|
||||
|
|
@ -106,9 +106,9 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
|||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
// If he does afford it, we can skip checking the same thing later on, during execution
|
||||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
||||
return params.ColdSloadCostEIP2929, nil
|
||||
return GasCosts{RegularGas: params.ColdSloadCostEIP2929}, nil
|
||||
}
|
||||
return params.WarmStorageReadCostEIP2929, nil
|
||||
return GasCosts{RegularGas: params.WarmStorageReadCostEIP2929}, nil
|
||||
}
|
||||
|
||||
// gasExtCodeCopyEIP2929 implements extcodecopy according to EIP-2929
|
||||
|
|
@ -116,12 +116,13 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
|||
// > If the target is not in accessed_addresses,
|
||||
// > charge COLD_ACCOUNT_ACCESS_COST gas, and add the address to accessed_addresses.
|
||||
// > Otherwise, charge WARM_STORAGE_READ_COST gas.
|
||||
func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
// memory expansion first (dynamic part of pre-2929 implementation)
|
||||
gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
|
||||
gasCost, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
gas := gasCost.RegularGas
|
||||
addr := common.Address(stack.peek().Bytes20())
|
||||
// Check slot presence in the access list
|
||||
if !evm.StateDB.AddressInAccessList(addr) {
|
||||
|
|
@ -129,11 +130,11 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
|
|||
var overflow bool
|
||||
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
||||
if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
||||
// gasEip2929AccountCheck checks whether the first stack item (as address) is present in the access list.
|
||||
|
|
@ -143,20 +144,20 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
|
|||
// - extcodehash,
|
||||
// - extcodesize,
|
||||
// - (ext) balance
|
||||
func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
addr := common.Address(stack.peek().Bytes20())
|
||||
// Check slot presence in the access list
|
||||
if !evm.StateDB.AddressInAccessList(addr) {
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
// The warm storage read cost is already charged as constantGas
|
||||
return params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929, nil
|
||||
return GasCosts{RegularGas: params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929}, nil
|
||||
}
|
||||
return 0, nil
|
||||
return GasCosts{}, nil
|
||||
}
|
||||
|
||||
func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
addr := common.Address(stack.Back(addressPosition).Bytes20())
|
||||
// Check slot presence in the access list
|
||||
warmAccess := evm.StateDB.AddressInAccessList(addr)
|
||||
|
|
@ -168,7 +169,7 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) g
|
|||
// Charge the remaining difference here already, to correctly calculate available
|
||||
// gas for call
|
||||
if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return 0, ErrOutOfGas
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
// Now call the old calculator, which takes into account
|
||||
|
|
@ -176,21 +177,22 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) g
|
|||
// - transfer value
|
||||
// - memory expansion
|
||||
// - 63/64ths rule
|
||||
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
||||
gasCost, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
||||
if warmAccess || err != nil {
|
||||
return gas, err
|
||||
return gasCost, err
|
||||
}
|
||||
// In case of a cold access, we temporarily add the cold charge back, and also
|
||||
// add it to the returned gas. By adding it to the return, it will be charged
|
||||
// outside of this function, as part of the dynamic gas, and that will make it
|
||||
// also become correctly reported to tracers.
|
||||
contract.Gas += coldCost
|
||||
contract.Gas.RegularGas += coldCost
|
||||
|
||||
gas := gasCost.RegularGas
|
||||
var overflow bool
|
||||
if gas, overflow = math.SafeAdd(gas, coldCost); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -224,13 +226,13 @@ var (
|
|||
|
||||
// makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-3529
|
||||
func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
||||
gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
var (
|
||||
gas uint64
|
||||
address = common.Address(stack.peek().Bytes20())
|
||||
)
|
||||
if evm.readOnly {
|
||||
return 0, ErrWriteProtection
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
}
|
||||
if !evm.StateDB.AddressInAccessList(address) {
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
|
|
@ -239,8 +241,8 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
|||
|
||||
// Terminate the gas measurement if the leftover gas is not sufficient,
|
||||
// it can effectively prevent accessing the states in the following steps
|
||||
if contract.Gas < gas {
|
||||
return 0, ErrOutOfGas
|
||||
if contract.Gas.RegularGas < gas {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
// if empty and transfers value
|
||||
|
|
@ -250,86 +252,109 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
|||
if refundsEnabled && !evm.StateDB.HasSelfDestructed(contract.Address()) {
|
||||
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
return gasFunc
|
||||
}
|
||||
|
||||
var (
|
||||
innerGasCallEIP7702 = makeCallVariantGasCallEIP7702(gasCall)
|
||||
gasDelegateCallEIP7702 = makeCallVariantGasCallEIP7702(gasDelegateCall)
|
||||
gasStaticCallEIP7702 = makeCallVariantGasCallEIP7702(gasStaticCall)
|
||||
gasCallCodeEIP7702 = makeCallVariantGasCallEIP7702(gasCallCode)
|
||||
innerGasCallEIP7702 = makeCallVariantGasCallEIP7702(gasCallIntrinsic)
|
||||
gasDelegateCallEIP7702 = makeCallVariantGasCallEIP7702(gasDelegateCallIntrinsic)
|
||||
gasStaticCallEIP7702 = makeCallVariantGasCallEIP7702(gasStaticCallIntrinsic)
|
||||
gasCallCodeEIP7702 = makeCallVariantGasCallEIP7702(gasCallCodeIntrinsic)
|
||||
)
|
||||
|
||||
func gasCallEIP7702(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasCallEIP7702(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
// Return early if this call attempts to transfer value in a static context.
|
||||
// Although it's checked in `gasCall`, EIP-7702 loads the target's code before
|
||||
// to determine if it is resolving a delegation. This could incorrectly record
|
||||
// the target in the block access list (BAL) if the call later fails.
|
||||
transfersValue := !stack.Back(2).IsZero()
|
||||
if evm.readOnly && transfersValue {
|
||||
return 0, ErrWriteProtection
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
}
|
||||
return innerGasCallEIP7702(evm, contract, stack, mem, memorySize)
|
||||
}
|
||||
|
||||
func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func makeCallVariantGasCallEIP7702(intrinsicFunc gasFunc) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
var (
|
||||
total uint64 // total dynamic gas used
|
||||
addr = common.Address(stack.Back(1).Bytes20())
|
||||
eip2929Cost uint64
|
||||
eip7702Cost uint64
|
||||
addr = common.Address(stack.Back(1).Bytes20())
|
||||
)
|
||||
|
||||
// Check slot presence in the access list
|
||||
// Perform EIP-2929 checks (stateless), checking address presence
|
||||
// in the accessList and charge the cold access accordingly.
|
||||
if !evm.StateDB.AddressInAccessList(addr) {
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
|
||||
// the cost to charge for cold access, if any, is Cold - Warm
|
||||
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
||||
// Charge the remaining difference here already, to correctly calculate available
|
||||
// gas for call
|
||||
if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return 0, ErrOutOfGas
|
||||
|
||||
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form
|
||||
// of a constant cost, so the cost to charge for cold access, if any,
|
||||
// is Cold - Warm
|
||||
eip2929Cost = params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
||||
|
||||
// Charge the remaining difference here already, to correctly calculate
|
||||
// available gas for call
|
||||
if !contract.UseGas(eip2929Cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
}
|
||||
total += coldCost
|
||||
}
|
||||
|
||||
// Perform the intrinsic cost calculation including:
|
||||
//
|
||||
// - transfer value
|
||||
// - memory expansion
|
||||
// - create new account
|
||||
intrinsicCost, err := intrinsicFunc(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
}
|
||||
// Terminate the gas measurement if the leftover gas is not sufficient,
|
||||
// it can effectively prevent accessing the states in the following steps.
|
||||
// It's an essential safeguard before any stateful check.
|
||||
if contract.Gas.RegularGas < intrinsicCost.RegularGas {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
}
|
||||
|
||||
// Check if code is a delegation and if so, charge for resolution.
|
||||
if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
|
||||
var cost uint64
|
||||
if evm.StateDB.AddressInAccessList(target) {
|
||||
cost = params.WarmStorageReadCostEIP2929
|
||||
eip7702Cost = params.WarmStorageReadCostEIP2929
|
||||
} else {
|
||||
evm.StateDB.AddAddressToAccessList(target)
|
||||
cost = params.ColdAccountAccessCostEIP2929
|
||||
eip7702Cost = params.ColdAccountAccessCostEIP2929
|
||||
}
|
||||
if !contract.UseGas(cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return 0, ErrOutOfGas
|
||||
if !contract.UseGas(eip7702Cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
}
|
||||
total += cost
|
||||
}
|
||||
|
||||
// Now call the old calculator, which takes into account
|
||||
// - create new account
|
||||
// - transfer value
|
||||
// - memory expansion
|
||||
// - 63/64ths rule
|
||||
old, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
||||
// Calculate the gas budget for the nested call. The costs defined by
|
||||
// EIP-2929 and EIP-7702 have already been applied.
|
||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, intrinsicCost.RegularGas, stack.Back(0))
|
||||
if err != nil {
|
||||
return old, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
|
||||
// Temporarily add the gas charge back to the contract and return value. By
|
||||
// adding it to the return, it will be charged outside of this function, as
|
||||
// part of the dynamic gas. This will ensure it is correctly reported to
|
||||
// tracers.
|
||||
contract.Gas += total
|
||||
contract.Gas.RegularGas += eip2929Cost + eip7702Cost
|
||||
|
||||
var overflow bool
|
||||
if total, overflow = math.SafeAdd(old, total); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
// Aggregate the gas costs from all components, including EIP-2929, EIP-7702,
|
||||
// the CALL opcode itself, and the cost incurred by nested calls.
|
||||
var (
|
||||
overflow bool
|
||||
totalCost uint64
|
||||
)
|
||||
if totalCost, overflow = math.SafeAdd(eip2929Cost, eip7702Cost); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return total, nil
|
||||
if totalCost, overflow = math.SafeAdd(totalCost, intrinsicCost.RegularGas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if totalCost, overflow = math.SafeAdd(totalCost, evm.callGasTemp); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return GasCosts{RegularGas: totalCost}, nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,37 +24,37 @@ import (
|
|||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true, contract.Gas, true), nil
|
||||
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
return GasCosts{RegularGas: evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true, contract.Gas.RegularGas, true)}, nil
|
||||
}
|
||||
|
||||
func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false, contract.Gas, true), nil
|
||||
func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
return GasCosts{RegularGas: evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false, contract.Gas.RegularGas, true)}, nil
|
||||
}
|
||||
|
||||
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
address := stack.peek().Bytes20()
|
||||
return evm.AccessEvents.BasicDataGas(address, false, contract.Gas, true), nil
|
||||
return GasCosts{RegularGas: evm.AccessEvents.BasicDataGas(address, false, contract.Gas.RegularGas, true)}, nil
|
||||
}
|
||||
|
||||
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
address := stack.peek().Bytes20()
|
||||
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
||||
return 0, nil
|
||||
return GasCosts{}, nil
|
||||
}
|
||||
return evm.AccessEvents.BasicDataGas(address, false, contract.Gas, true), nil
|
||||
return GasCosts{RegularGas: evm.AccessEvents.BasicDataGas(address, false, contract.Gas.RegularGas, true)}, nil
|
||||
}
|
||||
|
||||
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
address := stack.peek().Bytes20()
|
||||
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
||||
return 0, nil
|
||||
return GasCosts{}, nil
|
||||
}
|
||||
return evm.AccessEvents.CodeHashGas(address, false, contract.Gas, true), nil
|
||||
return GasCosts{RegularGas: evm.AccessEvents.CodeHashGas(address, false, contract.Gas.RegularGas, true)}, nil
|
||||
}
|
||||
|
||||
func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
var (
|
||||
target = common.Address(stack.Back(1).Bytes20())
|
||||
witnessGas uint64
|
||||
|
|
@ -65,9 +65,9 @@ func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) ga
|
|||
// 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
|
||||
wantedValueTransferWitnessGas := evm.AccessEvents.ValueTransferGas(contract.Address(), target, contract.Gas.RegularGas)
|
||||
if wantedValueTransferWitnessGas > contract.Gas.RegularGas {
|
||||
return GasCosts{RegularGas: wantedValueTransferWitnessGas}, nil
|
||||
}
|
||||
witnessGas = wantedValueTransferWitnessGas
|
||||
} else if isPrecompile || isSystemContract {
|
||||
|
|
@ -78,25 +78,26 @@ func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) ga
|
|||
// (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)
|
||||
wantedMessageCallWitnessGas := evm.AccessEvents.MessageCallGas(target, contract.Gas.RegularGas-witnessGas)
|
||||
var overflow bool
|
||||
if witnessGas, overflow = math.SafeAdd(witnessGas, wantedMessageCallWitnessGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if witnessGas > contract.Gas {
|
||||
return witnessGas, nil
|
||||
if witnessGas > contract.Gas.RegularGas {
|
||||
return GasCosts{RegularGas: witnessGas}, nil
|
||||
}
|
||||
}
|
||||
|
||||
contract.Gas -= witnessGas
|
||||
contract.Gas.RegularGas -= 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
|
||||
gasCost, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
||||
contract.Gas.RegularGas += witnessGas // restore witness gas so that it can be charged at the callsite
|
||||
gas := gasCost.RegularGas
|
||||
var overflow bool
|
||||
if gas, overflow = math.SafeAdd(gas, witnessGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, err
|
||||
return GasCosts{RegularGas: gas}, err
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -107,18 +108,18 @@ var (
|
|||
gasDelegateCallEIP4762 = makeCallVariantGasEIP4762(gasDelegateCall, false)
|
||||
)
|
||||
|
||||
func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
beneficiaryAddr := common.Address(stack.peek().Bytes20())
|
||||
if _, isPrecompile := evm.precompile(beneficiaryAddr); isPrecompile {
|
||||
return 0, nil
|
||||
return GasCosts{}, nil
|
||||
}
|
||||
if contract.IsSystemCall {
|
||||
return 0, nil
|
||||
return GasCosts{}, nil
|
||||
}
|
||||
contractAddr := contract.Address()
|
||||
wanted := evm.AccessEvents.BasicDataGas(contractAddr, false, contract.Gas, false)
|
||||
if wanted > contract.Gas {
|
||||
return wanted, nil
|
||||
wanted := evm.AccessEvents.BasicDataGas(contractAddr, false, contract.Gas.RegularGas, false)
|
||||
if wanted > contract.Gas.RegularGas {
|
||||
return GasCosts{RegularGas: wanted}, nil
|
||||
}
|
||||
statelessGas := wanted
|
||||
balanceIsZero := evm.StateDB.GetBalance(contractAddr).Sign() == 0
|
||||
|
|
@ -126,44 +127,45 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem
|
|||
isSystemContract := beneficiaryAddr == params.HistoryStorageAddress
|
||||
|
||||
if (isPrecompile || isSystemContract) && balanceIsZero {
|
||||
return statelessGas, nil
|
||||
return GasCosts{RegularGas: statelessGas}, nil
|
||||
}
|
||||
|
||||
if contractAddr != beneficiaryAddr {
|
||||
wanted := evm.AccessEvents.BasicDataGas(beneficiaryAddr, false, contract.Gas-statelessGas, false)
|
||||
if wanted > contract.Gas-statelessGas {
|
||||
return statelessGas + wanted, nil
|
||||
wanted := evm.AccessEvents.BasicDataGas(beneficiaryAddr, false, contract.Gas.RegularGas-statelessGas, false)
|
||||
if wanted > contract.Gas.RegularGas-statelessGas {
|
||||
return GasCosts{RegularGas: statelessGas + wanted}, nil
|
||||
}
|
||||
statelessGas += wanted
|
||||
}
|
||||
// Charge write costs if it transfers value
|
||||
if !balanceIsZero {
|
||||
wanted := evm.AccessEvents.BasicDataGas(contractAddr, true, contract.Gas-statelessGas, false)
|
||||
if wanted > contract.Gas-statelessGas {
|
||||
return statelessGas + wanted, nil
|
||||
wanted := evm.AccessEvents.BasicDataGas(contractAddr, true, contract.Gas.RegularGas-statelessGas, false)
|
||||
if wanted > contract.Gas.RegularGas-statelessGas {
|
||||
return GasCosts{RegularGas: statelessGas + wanted}, nil
|
||||
}
|
||||
statelessGas += wanted
|
||||
|
||||
if contractAddr != beneficiaryAddr {
|
||||
if evm.StateDB.Exist(beneficiaryAddr) {
|
||||
wanted = evm.AccessEvents.BasicDataGas(beneficiaryAddr, true, contract.Gas-statelessGas, false)
|
||||
wanted = evm.AccessEvents.BasicDataGas(beneficiaryAddr, true, contract.Gas.RegularGas-statelessGas, false)
|
||||
} else {
|
||||
wanted = evm.AccessEvents.AddAccount(beneficiaryAddr, true, contract.Gas-statelessGas)
|
||||
wanted = evm.AccessEvents.AddAccount(beneficiaryAddr, true, contract.Gas.RegularGas-statelessGas)
|
||||
}
|
||||
if wanted > contract.Gas-statelessGas {
|
||||
return statelessGas + wanted, nil
|
||||
if wanted > contract.Gas.RegularGas-statelessGas {
|
||||
return GasCosts{RegularGas: statelessGas + wanted}, nil
|
||||
}
|
||||
statelessGas += wanted
|
||||
}
|
||||
}
|
||||
return statelessGas, nil
|
||||
return GasCosts{RegularGas: statelessGas}, nil
|
||||
}
|
||||
|
||||
func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
gas, err := gasCodeCopy(evm, contract, stack, mem, memorySize)
|
||||
func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
gasCost, err := gasCodeCopy(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
gas := gasCost.RegularGas
|
||||
if !contract.IsDeployment && !contract.IsSystemCall {
|
||||
var (
|
||||
codeOffset = stack.Back(1)
|
||||
|
|
@ -175,31 +177,32 @@ func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
|||
}
|
||||
|
||||
_, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64())
|
||||
_, wanted := evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false, contract.Gas-gas)
|
||||
_, wanted := evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false, contract.Gas.RegularGas-gas)
|
||||
gas += wanted
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
||||
func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
// memory expansion first (dynamic part of pre-2929 implementation)
|
||||
gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
|
||||
gasCost, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
gas := gasCost.RegularGas
|
||||
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 GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
wgas := evm.AccessEvents.BasicDataGas(addr, false, contract.Gas-gas, true)
|
||||
wgas := evm.AccessEvents.BasicDataGas(addr, false, contract.Gas.RegularGas-gas, true)
|
||||
var overflow bool
|
||||
if gas, overflow = math.SafeAdd(gas, wgas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -347,7 +347,7 @@ func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction)
|
|||
}
|
||||
|
||||
func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) {
|
||||
pending := b.eth.txPool.Pending(txpool.PendingFilter{})
|
||||
pending, _ := b.eth.txPool.Pending(txpool.PendingFilter{})
|
||||
var txs types.Transactions
|
||||
for _, batch := range pending {
|
||||
for _, lazy := range batch {
|
||||
|
|
@ -414,9 +414,10 @@ func (b *EthAPIBackend) SyncProgress(ctx context.Context) ethereum.SyncProgress
|
|||
prog.TxIndexFinishedBlocks = txProg.Indexed
|
||||
prog.TxIndexRemainingBlocks = txProg.Remaining
|
||||
}
|
||||
remain, err := b.eth.blockchain.StateIndexProgress()
|
||||
stateRemain, trienodeRemain, err := b.eth.blockchain.StateIndexProgress()
|
||||
if err == nil {
|
||||
prog.StateIndexRemaining = remain
|
||||
prog.StateIndexRemaining = stateRemain
|
||||
prog.TrienodeIndexRemaining = trienodeRemain
|
||||
}
|
||||
return prog
|
||||
}
|
||||
|
|
@ -484,12 +485,12 @@ func (b *EthAPIBackend) CurrentHeader() *types.Header {
|
|||
return b.eth.blockchain.CurrentHeader()
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, readOnly bool, preferDisk bool) (*state.StateDB, tracers.StateReleaseFunc, error) {
|
||||
return b.eth.stateAtBlock(ctx, block, reexec, base, readOnly, preferDisk)
|
||||
func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, base *state.StateDB, readOnly bool, preferDisk bool) (*state.StateDB, tracers.StateReleaseFunc, error) {
|
||||
return b.eth.stateAtBlock(ctx, block, base, readOnly, preferDisk)
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
|
||||
return b.eth.stateAtTransaction(ctx, block, txIndex, reexec)
|
||||
func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int) (*types.Transaction, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
|
||||
return b.eth.stateAtTransaction(ctx, block, txIndex)
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) RPCTxSyncDefaultTimeout() time.Duration {
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ func (api *DebugAPI) StorageRangeAt(ctx context.Context, blockNrOrHash rpc.Block
|
|||
if block == nil {
|
||||
return StorageRangeResult{}, fmt.Errorf("block %v not found", blockNrOrHash)
|
||||
}
|
||||
_, _, statedb, release, err := api.eth.stateAtTransaction(ctx, block, txIndex, 0)
|
||||
_, _, statedb, release, err := api.eth.stateAtTransaction(ctx, block, txIndex)
|
||||
if err != nil {
|
||||
return StorageRangeResult{}, err
|
||||
}
|
||||
|
|
@ -236,6 +236,8 @@ func storageRangeAt(statedb *state.StateDB, root common.Hash, address common.Add
|
|||
if storageRoot == types.EmptyRootHash || storageRoot == (common.Hash{}) {
|
||||
return StorageRangeResult{}, nil // empty storage
|
||||
}
|
||||
// TODO(rjl493456442) it's problematic for traversing the state with in-memory
|
||||
// state mutations, specifically txIndex != 0.
|
||||
id := trie.StorageTrieID(root, crypto.Keccak256Hash(address.Bytes()), storageRoot)
|
||||
tr, err := trie.NewStateTrie(id, statedb.Database().TrieDB())
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/filtermaps"
|
||||
"github.com/ethereum/go-ethereum/core/history"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state/pruner"
|
||||
"github.com/ethereum/go-ethereum/core/txpool"
|
||||
|
|
@ -175,7 +176,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
|||
|
||||
// Here we determine genesis hash and active ChainConfig.
|
||||
// We need these to figure out the consensus parameters and to set up history pruning.
|
||||
chainConfig, _, err := core.LoadChainConfig(chainDb, config.Genesis)
|
||||
chainConfig, genesisHash, err := core.LoadChainConfig(chainDb, config.Genesis)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -220,6 +221,10 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
|||
rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion)
|
||||
}
|
||||
}
|
||||
histPolicy, err := history.NewPolicy(config.HistoryMode, genesisHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var (
|
||||
options = &core.BlockChainConfig{
|
||||
TrieCleanLimit: config.TrieCleanCache,
|
||||
|
|
@ -233,7 +238,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
|||
TrienodeHistory: config.TrienodeHistory,
|
||||
NodeFullValueCheckpoint: config.NodeFullValueCheckpoint,
|
||||
StateScheme: scheme,
|
||||
ChainHistoryMode: config.HistoryMode,
|
||||
HistoryPolicy: histPolicy,
|
||||
TxLookupLimit: int64(min(config.TransactionHistory, math.MaxInt64)),
|
||||
VmConfig: vm.Config{
|
||||
EnablePreimageRecording: config.EnablePreimageRecording,
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ func newConsensusAPIWithoutHeartbeat(eth *eth.Ethereum) *ConsensusAPI {
|
|||
//
|
||||
// If there are payloadAttributes: we try to assemble a block with the payloadAttributes
|
||||
// and return its payloadID.
|
||||
func (api *ConsensusAPI) ForkchoiceUpdatedV1(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||
func (api *ConsensusAPI) ForkchoiceUpdatedV1(ctx context.Context, update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||
if payloadAttributes != nil {
|
||||
switch {
|
||||
case payloadAttributes.Withdrawals != nil || payloadAttributes.BeaconRoot != nil:
|
||||
|
|
@ -171,12 +171,12 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update engine.ForkchoiceStateV1, pa
|
|||
return engine.STATUS_INVALID, paramsErr("fcuV1 called post-shanghai")
|
||||
}
|
||||
}
|
||||
return api.forkchoiceUpdated(update, payloadAttributes, engine.PayloadV1, false)
|
||||
return api.forkchoiceUpdated(ctx, update, payloadAttributes, engine.PayloadV1, false)
|
||||
}
|
||||
|
||||
// ForkchoiceUpdatedV2 is equivalent to V1 with the addition of withdrawals in the payload
|
||||
// attributes. It supports both PayloadAttributesV1 and PayloadAttributesV2.
|
||||
func (api *ConsensusAPI) ForkchoiceUpdatedV2(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||
func (api *ConsensusAPI) ForkchoiceUpdatedV2(ctx context.Context, update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||
if params != nil {
|
||||
switch {
|
||||
case params.BeaconRoot != nil:
|
||||
|
|
@ -189,12 +189,12 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV2(update engine.ForkchoiceStateV1, pa
|
|||
return engine.STATUS_INVALID, unsupportedForkErr("fcuV2 must only be called with paris or shanghai payloads")
|
||||
}
|
||||
}
|
||||
return api.forkchoiceUpdated(update, params, engine.PayloadV2, false)
|
||||
return api.forkchoiceUpdated(ctx, update, params, engine.PayloadV2, false)
|
||||
}
|
||||
|
||||
// ForkchoiceUpdatedV3 is equivalent to V2 with the addition of parent beacon block root
|
||||
// in the payload attributes. It supports only PayloadAttributesV3.
|
||||
func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||
func (api *ConsensusAPI) ForkchoiceUpdatedV3(ctx context.Context, update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||
if params != nil {
|
||||
switch {
|
||||
case params.Withdrawals == nil:
|
||||
|
|
@ -209,12 +209,12 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, pa
|
|||
// hash, even if params are wrong. To do this we need to split up
|
||||
// forkchoiceUpdate into a function that only updates the head and then a
|
||||
// function that kicks off block construction.
|
||||
return api.forkchoiceUpdated(update, params, engine.PayloadV3, false)
|
||||
return api.forkchoiceUpdated(ctx, update, params, engine.PayloadV3, false)
|
||||
}
|
||||
|
||||
// ForkchoiceUpdatedV4 is equivalent to V3 with the addition of slot number
|
||||
// in the payload attributes. It supports only PayloadAttributesV4.
|
||||
func (api *ConsensusAPI) ForkchoiceUpdatedV4(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||
func (api *ConsensusAPI) ForkchoiceUpdatedV4(ctx context.Context, update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||
if params != nil {
|
||||
switch {
|
||||
case params.Withdrawals == nil:
|
||||
|
|
@ -231,10 +231,12 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV4(update engine.ForkchoiceStateV1, pa
|
|||
// hash, even if params are wrong. To do this we need to split up
|
||||
// forkchoiceUpdate into a function that only updates the head and then a
|
||||
// function that kicks off block construction.
|
||||
return api.forkchoiceUpdated(update, params, engine.PayloadV4, false)
|
||||
return api.forkchoiceUpdated(ctx, update, params, engine.PayloadV4, false)
|
||||
}
|
||||
|
||||
func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes, payloadVersion engine.PayloadVersion, payloadWitness bool) (engine.ForkChoiceResponse, error) {
|
||||
func (api *ConsensusAPI) forkchoiceUpdated(ctx context.Context, update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes, payloadVersion engine.PayloadVersion, payloadWitness bool) (result engine.ForkChoiceResponse, err error) {
|
||||
ctx, _, spanEnd := telemetry.StartSpan(ctx, "engine.forkchoiceUpdated")
|
||||
defer spanEnd(&err)
|
||||
api.forkchoiceLock.Lock()
|
||||
defer api.forkchoiceLock.Unlock()
|
||||
|
||||
|
|
@ -375,7 +377,7 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
|
|||
if api.localBlocks.has(id) {
|
||||
return valid(&id), nil
|
||||
}
|
||||
payload, err := api.eth.Miner().BuildPayload(args, payloadWitness)
|
||||
payload, err := api.eth.Miner().BuildPayload(ctx, args, payloadWitness)
|
||||
if err != nil {
|
||||
log.Error("Failed to build payload", "err", err)
|
||||
return valid(nil), engine.InvalidPayloadAttributes.With(err)
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ func TestEth2PrepareAndGetPayload(t *testing.T) {
|
|||
SafeBlockHash: common.Hash{},
|
||||
FinalizedBlockHash: common.Hash{},
|
||||
}
|
||||
_, err := api.ForkchoiceUpdatedV1(fcState, &blockParams)
|
||||
_, err := api.ForkchoiceUpdatedV1(context.Background(), fcState, &blockParams)
|
||||
if err != nil {
|
||||
t.Fatalf("error preparing payload, err=%v", err)
|
||||
}
|
||||
|
|
@ -270,7 +270,7 @@ func TestInvalidPayloadTimestamp(t *testing.T) {
|
|||
SafeBlockHash: common.Hash{},
|
||||
FinalizedBlockHash: common.Hash{},
|
||||
}
|
||||
_, err := api.ForkchoiceUpdatedV1(fcState, ¶ms)
|
||||
_, err := api.ForkchoiceUpdatedV1(context.Background(), fcState, ¶ms)
|
||||
if test.shouldErr && err == nil {
|
||||
t.Fatalf("expected error preparing payload with invalid timestamp, err=%v", err)
|
||||
} else if !test.shouldErr && err != nil {
|
||||
|
|
@ -329,7 +329,7 @@ func TestEth2NewBlock(t *testing.T) {
|
|||
SafeBlockHash: block.Hash(),
|
||||
FinalizedBlockHash: block.Hash(),
|
||||
}
|
||||
if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
|
||||
if _, err := api.ForkchoiceUpdatedV1(context.Background(), fcState, nil); err != nil {
|
||||
t.Fatalf("Failed to insert block: %v", err)
|
||||
}
|
||||
if have, want := ethservice.BlockChain().CurrentBlock().Number.Uint64(), block.NumberU64(); have != want {
|
||||
|
|
@ -369,7 +369,7 @@ func TestEth2NewBlock(t *testing.T) {
|
|||
SafeBlockHash: block.Hash(),
|
||||
FinalizedBlockHash: block.Hash(),
|
||||
}
|
||||
if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
|
||||
if _, err := api.ForkchoiceUpdatedV1(context.Background(), fcState, nil); err != nil {
|
||||
t.Fatalf("Failed to insert block: %v", err)
|
||||
}
|
||||
if ethservice.BlockChain().CurrentBlock().Number.Uint64() != block.NumberU64() {
|
||||
|
|
@ -515,7 +515,7 @@ func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.He
|
|||
SafeBlockHash: payload.ParentHash,
|
||||
FinalizedBlockHash: payload.ParentHash,
|
||||
}
|
||||
if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
|
||||
if _, err := api.ForkchoiceUpdatedV1(context.Background(), fcState, nil); err != nil {
|
||||
t.Fatalf("Failed to insert block: %v", err)
|
||||
}
|
||||
if ethservice.BlockChain().CurrentBlock().Number.Uint64() != payload.Number {
|
||||
|
|
@ -629,7 +629,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) {
|
|||
err error
|
||||
)
|
||||
for i := 0; ; i++ {
|
||||
if resp, err = api.ForkchoiceUpdatedV1(fcState, ¶ms); err != nil {
|
||||
if resp, err = api.ForkchoiceUpdatedV1(context.Background(), fcState, ¶ms); err != nil {
|
||||
t.Fatalf("error preparing payload, err=%v", err)
|
||||
}
|
||||
if resp.PayloadStatus.Status != engine.VALID {
|
||||
|
|
@ -660,7 +660,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) {
|
|||
SafeBlockHash: payload.ExecutionPayload.ParentHash,
|
||||
FinalizedBlockHash: payload.ExecutionPayload.ParentHash,
|
||||
}
|
||||
if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
|
||||
if _, err := api.ForkchoiceUpdatedV1(context.Background(), fcState, nil); err != nil {
|
||||
t.Fatalf("Failed to insert block: %v", err)
|
||||
}
|
||||
if ethservice.BlockChain().CurrentBlock().Number.Uint64() != payload.ExecutionPayload.Number {
|
||||
|
|
@ -679,7 +679,7 @@ func assembleEnvelope(api *ConsensusAPI, parentHash common.Hash, params *engine.
|
|||
Withdrawals: params.Withdrawals,
|
||||
BeaconRoot: params.BeaconRoot,
|
||||
}
|
||||
payload, err := api.eth.Miner().BuildPayload(args, false)
|
||||
payload, err := api.eth.Miner().BuildPayload(context.Background(), args, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -867,7 +867,7 @@ func TestTrickRemoteBlockCache(t *testing.T) {
|
|||
t.Error("invalid status: VALID on an invalid chain")
|
||||
}
|
||||
// Now reorg to the head of the invalid chain
|
||||
resp, err := apiB.ForkchoiceUpdatedV1(engine.ForkchoiceStateV1{HeadBlockHash: payload.BlockHash, SafeBlockHash: payload.BlockHash, FinalizedBlockHash: payload.ParentHash}, nil)
|
||||
resp, err := apiB.ForkchoiceUpdatedV1(context.Background(), engine.ForkchoiceStateV1{HeadBlockHash: payload.BlockHash, SafeBlockHash: payload.BlockHash, FinalizedBlockHash: payload.ParentHash}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -970,7 +970,7 @@ func TestSimultaneousNewBlock(t *testing.T) {
|
|||
for ii := 0; ii < 10; ii++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
|
||||
if _, err := api.ForkchoiceUpdatedV1(context.Background(), fcState, nil); err != nil {
|
||||
errMu.Lock()
|
||||
testErr = fmt.Errorf("failed to insert block: %w", err)
|
||||
errMu.Unlock()
|
||||
|
|
@ -1011,7 +1011,7 @@ func TestWithdrawals(t *testing.T) {
|
|||
fcState := engine.ForkchoiceStateV1{
|
||||
HeadBlockHash: parent.Hash(),
|
||||
}
|
||||
resp, err := api.ForkchoiceUpdatedV2(fcState, &blockParams)
|
||||
resp, err := api.ForkchoiceUpdatedV2(context.Background(), fcState, &blockParams)
|
||||
if err != nil {
|
||||
t.Fatalf("error preparing payload, err=%v", err)
|
||||
}
|
||||
|
|
@ -1063,7 +1063,7 @@ func TestWithdrawals(t *testing.T) {
|
|||
},
|
||||
}
|
||||
fcState.HeadBlockHash = execData.ExecutionPayload.BlockHash
|
||||
_, err = api.ForkchoiceUpdatedV2(fcState, &blockParams)
|
||||
_, err = api.ForkchoiceUpdatedV2(context.Background(), fcState, &blockParams)
|
||||
if err != nil {
|
||||
t.Fatalf("error preparing payload, err=%v", err)
|
||||
}
|
||||
|
|
@ -1090,7 +1090,7 @@ func TestWithdrawals(t *testing.T) {
|
|||
|
||||
// 11: set block as head.
|
||||
fcState.HeadBlockHash = execData.ExecutionPayload.BlockHash
|
||||
_, err = api.ForkchoiceUpdatedV2(fcState, nil)
|
||||
_, err = api.ForkchoiceUpdatedV2(context.Background(), fcState, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("error preparing payload, err=%v", err)
|
||||
}
|
||||
|
|
@ -1196,10 +1196,10 @@ func TestNilWithdrawals(t *testing.T) {
|
|||
)
|
||||
if !shanghai {
|
||||
payloadVersion = engine.PayloadV1
|
||||
_, err = api.ForkchoiceUpdatedV1(fcState, &test.blockParams)
|
||||
_, err = api.ForkchoiceUpdatedV1(context.Background(), fcState, &test.blockParams)
|
||||
} else {
|
||||
payloadVersion = engine.PayloadV2
|
||||
_, err = api.ForkchoiceUpdatedV2(fcState, &test.blockParams)
|
||||
_, err = api.ForkchoiceUpdatedV2(context.Background(), fcState, &test.blockParams)
|
||||
}
|
||||
if test.wantErr {
|
||||
if err == nil {
|
||||
|
|
@ -1579,7 +1579,7 @@ func TestParentBeaconBlockRoot(t *testing.T) {
|
|||
fcState := engine.ForkchoiceStateV1{
|
||||
HeadBlockHash: parent.Hash(),
|
||||
}
|
||||
resp, err := api.ForkchoiceUpdatedV3(fcState, &blockParams)
|
||||
resp, err := api.ForkchoiceUpdatedV3(context.Background(), fcState, &blockParams)
|
||||
if err != nil {
|
||||
t.Fatalf("error preparing payload, err=%v", err.(*engine.EngineAPIError).ErrorData())
|
||||
}
|
||||
|
|
@ -1610,7 +1610,7 @@ func TestParentBeaconBlockRoot(t *testing.T) {
|
|||
}
|
||||
|
||||
fcState.HeadBlockHash = execData.ExecutionPayload.BlockHash
|
||||
resp, err = api.ForkchoiceUpdatedV3(fcState, nil)
|
||||
resp, err = api.ForkchoiceUpdatedV3(context.Background(), fcState, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("error preparing payload, err=%v", err.(*engine.EngineAPIError).ErrorData())
|
||||
}
|
||||
|
|
@ -1666,7 +1666,7 @@ func TestWitnessCreationAndConsumption(t *testing.T) {
|
|||
SafeBlockHash: common.Hash{},
|
||||
FinalizedBlockHash: common.Hash{},
|
||||
}
|
||||
_, err := api.ForkchoiceUpdatedWithWitnessV3(fcState, &blockParams)
|
||||
_, err := api.ForkchoiceUpdatedWithWitnessV3(context.Background(), fcState, &blockParams)
|
||||
if err != nil {
|
||||
t.Fatalf("error preparing payload, err=%v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ func NewSimulatedBeacon(period uint64, feeRecipient common.Address, eth *eth.Eth
|
|||
// if genesis block, send forkchoiceUpdated to trigger transition to PoS
|
||||
if block.Number.Sign() == 0 {
|
||||
version := payloadVersion(eth.BlockChain().Config(), block.Time)
|
||||
if _, err := engineAPI.forkchoiceUpdated(current, nil, version, false); err != nil {
|
||||
if _, err := engineAPI.forkchoiceUpdated(context.Background(), current, nil, version, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
|
@ -212,7 +212,16 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal, timestamp u
|
|||
slotNumber := uint64(0)
|
||||
attribute.SlotNumber = &slotNumber
|
||||
}
|
||||
fcResponse, err := c.engineAPI.forkchoiceUpdated(c.curForkchoiceState, attribute, version, false)
|
||||
|
||||
// Create a server span for forkchoiceUpdated with payload attributes,
|
||||
// simulating an incoming engine API request from a real consensus client.
|
||||
fcCtx, fcSpanEnd := telemetry.StartServerSpan(context.Background(), tracer, telemetry.RPCInfo{
|
||||
System: "jsonrpc",
|
||||
Service: "engine",
|
||||
Method: "forkchoiceUpdatedV" + fmt.Sprintf("%d", version),
|
||||
})
|
||||
fcResponse, err := c.engineAPI.forkchoiceUpdated(fcCtx, c.curForkchoiceState, attribute, version, false)
|
||||
fcSpanEnd(&err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -226,7 +235,15 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal, timestamp u
|
|||
return nil
|
||||
}
|
||||
|
||||
// Create a server span for getPayload, simulating the consensus client
|
||||
// coming back to retrieve the built payload.
|
||||
_, gpSpanEnd := telemetry.StartServerSpan(context.Background(), tracer, telemetry.RPCInfo{
|
||||
System: "jsonrpc",
|
||||
Service: "engine",
|
||||
Method: "getPayloadV" + fmt.Sprintf("%d", version),
|
||||
})
|
||||
envelope, err := c.engineAPI.getPayload(*fcResponse.PayloadID, true, nil, nil)
|
||||
gpSpanEnd(&err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -274,6 +291,7 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal, timestamp u
|
|||
Service: "engine",
|
||||
Method: "newPayloadV" + fmt.Sprintf("%d", version),
|
||||
})
|
||||
|
||||
// Mark the payload as canon
|
||||
_, err = c.engineAPI.newPayload(npCtx, *payload, blobHashes, beaconRoot, requests, false)
|
||||
npSpanEnd(&err)
|
||||
|
|
@ -282,8 +300,16 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal, timestamp u
|
|||
}
|
||||
c.setCurrentState(payload.BlockHash, finalizedHash)
|
||||
|
||||
// Mark the block containing the payload as canonical
|
||||
if _, err = c.engineAPI.forkchoiceUpdated(c.curForkchoiceState, nil, version, false); err != nil {
|
||||
// Create a server span for the final forkchoiceUpdated (no payload attributes),
|
||||
// which sets the new block as the canonical chain head.
|
||||
fcuCtx, fcuSpanEnd := telemetry.StartServerSpan(context.Background(), tracer, telemetry.RPCInfo{
|
||||
System: "jsonrpc",
|
||||
Service: "engine",
|
||||
Method: "forkchoiceUpdatedV" + fmt.Sprintf("%d", version),
|
||||
})
|
||||
_, err = c.engineAPI.forkchoiceUpdated(fcuCtx, c.curForkchoiceState, nil, version, false)
|
||||
fcuSpanEnd(&err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.lastBlockTime = payload.Timestamp
|
||||
|
|
@ -349,7 +375,7 @@ func (c *SimulatedBeacon) Rollback() {
|
|||
func (c *SimulatedBeacon) Fork(parentHash common.Hash) error {
|
||||
// Ensure no pending transactions.
|
||||
c.eth.TxPool().Sync()
|
||||
if len(c.eth.TxPool().Pending(txpool.PendingFilter{})) != 0 {
|
||||
if pending, _ := c.eth.TxPool().Pending(txpool.PendingFilter{}); len(pending) != 0 {
|
||||
return errors.New("pending block dirty")
|
||||
}
|
||||
|
||||
|
|
@ -363,7 +389,7 @@ func (c *SimulatedBeacon) Fork(parentHash common.Hash) error {
|
|||
|
||||
// AdjustTime creates a new block with an adjusted timestamp.
|
||||
func (c *SimulatedBeacon) AdjustTime(adjustment time.Duration) error {
|
||||
if len(c.eth.TxPool().Pending(txpool.PendingFilter{})) != 0 {
|
||||
if pending, _ := c.eth.TxPool().Pending(txpool.PendingFilter{}); len(pending) != 0 {
|
||||
return errors.New("could not adjust time on non-empty block")
|
||||
}
|
||||
parent := c.eth.BlockChain().CurrentBlock()
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import (
|
|||
|
||||
// ForkchoiceUpdatedWithWitnessV1 is analogous to ForkchoiceUpdatedV1, only it
|
||||
// generates an execution witness too if block building was requested.
|
||||
func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV1(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||
func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV1(ctx context.Context, update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||
if payloadAttributes != nil {
|
||||
switch {
|
||||
case payloadAttributes.Withdrawals != nil || payloadAttributes.BeaconRoot != nil:
|
||||
|
|
@ -44,12 +44,12 @@ func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV1(update engine.Forkchoice
|
|||
return engine.STATUS_INVALID, paramsErr("fcuV1 called post-shanghai")
|
||||
}
|
||||
}
|
||||
return api.forkchoiceUpdated(update, payloadAttributes, engine.PayloadV1, true)
|
||||
return api.forkchoiceUpdated(ctx, update, payloadAttributes, engine.PayloadV1, true)
|
||||
}
|
||||
|
||||
// ForkchoiceUpdatedWithWitnessV2 is analogous to ForkchoiceUpdatedV2, only it
|
||||
// generates an execution witness too if block building was requested.
|
||||
func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV2(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||
func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV2(ctx context.Context, update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||
if params != nil {
|
||||
switch {
|
||||
case params.BeaconRoot != nil:
|
||||
|
|
@ -62,12 +62,12 @@ func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV2(update engine.Forkchoice
|
|||
return engine.STATUS_INVALID, unsupportedForkErr("fcuV2 must only be called with paris or shanghai payloads")
|
||||
}
|
||||
}
|
||||
return api.forkchoiceUpdated(update, params, engine.PayloadV2, true)
|
||||
return api.forkchoiceUpdated(ctx, update, params, engine.PayloadV2, true)
|
||||
}
|
||||
|
||||
// ForkchoiceUpdatedWithWitnessV3 is analogous to ForkchoiceUpdatedV3, only it
|
||||
// generates an execution witness too if block building was requested.
|
||||
func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV3(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||
func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV3(ctx context.Context, update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||
if params != nil {
|
||||
switch {
|
||||
case params.Withdrawals == nil:
|
||||
|
|
@ -82,7 +82,7 @@ func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV3(update engine.Forkchoice
|
|||
// hash, even if params are wrong. To do this we need to split up
|
||||
// forkchoiceUpdate into a function that only updates the head and then a
|
||||
// function that kicks off block construction.
|
||||
return api.forkchoiceUpdated(update, params, engine.PayloadV3, true)
|
||||
return api.forkchoiceUpdated(ctx, update, params, engine.PayloadV3, true)
|
||||
}
|
||||
|
||||
// NewPayloadWithWitnessV1 is analogous to NewPayloadV1, only it also generates
|
||||
|
|
|
|||
|
|
@ -81,9 +81,10 @@ func (api *DownloaderAPI) eventLoop() {
|
|||
prog.TxIndexFinishedBlocks = txProg.Indexed
|
||||
prog.TxIndexRemainingBlocks = txProg.Remaining
|
||||
}
|
||||
remain, err := api.chain.StateIndexProgress()
|
||||
stateRemain, trienodeRemain, err := api.chain.StateIndexProgress()
|
||||
if err == nil {
|
||||
prog.StateIndexRemaining = remain
|
||||
prog.StateIndexRemaining = stateRemain
|
||||
prog.TrienodeIndexRemaining = trienodeRemain
|
||||
}
|
||||
return prog
|
||||
}
|
||||
|
|
|
|||
|
|
@ -259,8 +259,8 @@ func (dlp *downloadTesterPeer) RequestBodies(hashes []common.Hash, sink chan *et
|
|||
// RequestReceipts constructs a getReceipts method associated with a particular
|
||||
// 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)
|
||||
func (dlp *downloadTesterPeer) RequestReceipts(hashes []common.Hash, gasUsed []uint64, timestamps []uint64, sink chan *eth.Response) (*eth.Request, error) {
|
||||
blobs := eth.ServiceGetReceiptsQuery69(dlp.chain, hashes)
|
||||
receipts := make([]types.Receipts, blobs.Len())
|
||||
|
||||
// compute hashes
|
||||
|
|
@ -370,7 +370,7 @@ func (dlp *downloadTesterPeer) RequestTrieNodes(id uint64, root common.Hash, cou
|
|||
Paths: encPaths,
|
||||
Bytes: uint64(bytes),
|
||||
}
|
||||
nodes, _ := snap.ServiceGetTrieNodesQuery(dlp.chain, req, time.Now())
|
||||
nodes, _ := snap.ServiceGetTrieNodesQuery(dlp.chain, req)
|
||||
go dlp.dl.downloader.SnapSyncer.OnTrieNodes(dlp, id, nodes)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue