Skip to content

Commit 9f6e30c

Browse files
committed
trie: make value node resolvable via a callback
1 parent 6608a2a commit 9f6e30c

File tree

21 files changed

+205
-78
lines changed

21 files changed

+205
-78
lines changed

cmd/devp2p/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ with our test chain. The chain files are located in `./cmd/devp2p/internal/ethte
121121
--nat=none \
122122
--networkid 3503995874084926 \
123123
--verbosity 5 \
124-
--authrpc.jwtsecret 0x7365637265747365637265747365637265747365637265747365637265747365
124+
--authrpc.jwtsecret jwt.secret
125125

126126
Note that the tests also require access to the engine API.
127127
The test suite can now be executed using the devp2p tool.
@@ -130,7 +130,7 @@ The test suite can now be executed using the devp2p tool.
130130
--chain internal/ethtest/testdata \
131131
--node enode://.... \
132132
--engineapi http://127.0.0.1:8551 \
133-
--jwtsecret 0x7365637265747365637265747365637265747365637265747365637265747365
133+
--jwtsecret $(cat jwt.secret)
134134

135135
Repeat the above process (re-initialising the node) in order to run the Eth Protocol test suite again.
136136

cmd/devp2p/rlpxcmd.go

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,6 @@ type testParams struct {
143143

144144
func cliTestParams(ctx *cli.Context) *testParams {
145145
nodeStr := ctx.String(testNodeFlag.Name)
146-
if nodeStr == "" {
147-
exit(fmt.Errorf("missing -%s", testNodeFlag.Name))
148-
}
149146
node, err := parseNode(nodeStr)
150147
if err != nil {
151148
exit(err)
@@ -156,14 +153,5 @@ func cliTestParams(ctx *cli.Context) *testParams {
156153
jwt: ctx.String(testNodeJWTFlag.Name),
157154
chainDir: ctx.String(testChainDirFlag.Name),
158155
}
159-
if p.engineAPI == "" {
160-
exit(fmt.Errorf("missing -%s", testNodeEngineFlag.Name))
161-
}
162-
if p.jwt == "" {
163-
exit(fmt.Errorf("missing -%s", testNodeJWTFlag.Name))
164-
}
165-
if p.chainDir == "" {
166-
exit(fmt.Errorf("missing -%s", testChainDirFlag.Name))
167-
}
168156
return &p
169157
}

cmd/devp2p/runtest.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,29 @@ var (
3939
}
4040

4141
// for eth/snap tests
42-
testChainDirFlag = &cli.StringFlag{
42+
testChainDirFlag = &cli.PathFlag{
4343
Name: "chain",
4444
Usage: "Test chain directory (required)",
4545
Category: flags.TestingCategory,
46+
Required: true,
4647
}
4748
testNodeFlag = &cli.StringFlag{
4849
Name: "node",
4950
Usage: "Peer-to-Peer endpoint (ENR) of the test node (required)",
5051
Category: flags.TestingCategory,
52+
Required: true,
5153
}
5254
testNodeJWTFlag = &cli.StringFlag{
5355
Name: "jwtsecret",
5456
Usage: "JWT secret for the engine API of the test node (required)",
5557
Category: flags.TestingCategory,
56-
Value: "0x7365637265747365637265747365637265747365637265747365637265747365",
58+
Required: true,
5759
}
5860
testNodeEngineFlag = &cli.StringFlag{
5961
Name: "engineapi",
6062
Usage: "Engine API endpoint of the test node (required)",
6163
Category: flags.TestingCategory,
64+
Required: true,
6265
}
6366

6467
// These two are specific to the discovery tests.

cmd/utils/flags.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ package utils
2020
import (
2121
"context"
2222
"crypto/ecdsa"
23-
"encoding/hex"
2423
"encoding/json"
2524
"errors"
2625
"fmt"
@@ -1341,15 +1340,10 @@ func setEtherbase(ctx *cli.Context, cfg *ethconfig.Config) {
13411340
return
13421341
}
13431342
addr := ctx.String(MinerPendingFeeRecipientFlag.Name)
1344-
if strings.HasPrefix(addr, "0x") || strings.HasPrefix(addr, "0X") {
1345-
addr = addr[2:]
1346-
}
1347-
b, err := hex.DecodeString(addr)
1348-
if err != nil || len(b) != common.AddressLength {
1343+
if !common.IsHexAddress(addr) {
13491344
Fatalf("-%s: invalid pending block producer address %q", MinerPendingFeeRecipientFlag.Name, addr)
1350-
return
13511345
}
1352-
cfg.Miner.PendingFeeRecipient = common.BytesToAddress(b)
1346+
cfg.Miner.PendingFeeRecipient = common.HexToAddress(addr)
13531347
}
13541348

13551349
func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {

core/state/database.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ type Trie interface {
9999
// in the trie with provided address.
100100
UpdateAccount(address common.Address, account *types.StateAccount, codeLen int) error
101101

102+
UpdateAccountAsync(address common.Address, accountResolver func() *types.StateAccount) error
103+
102104
// UpdateStorage associates key with value in the trie. If value has length zero,
103105
// any existing value is deleted from the trie. The value bytes must not be modified
104106
// by the caller while they are stored in the trie. If a node was not found in the

core/state/state_sizer_test.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@ func TestSizeTracker(t *testing.T) {
9494
}
9595
baselineRoot := currentRoot
9696

97+
// Close and reopen the trie database so all async flushes triggered by the
98+
// baseline commits are written before we measure the baseline snapshot.
99+
if err := tdb.Close(); err != nil {
100+
t.Fatalf("Failed to close triedb before baseline measurement: %v", err)
101+
}
102+
tdb = triedb.NewDatabase(db, &triedb.Config{PathDB: pathdb.Defaults})
103+
sdb = NewDatabase(tdb, nil)
104+
97105
// Wait for snapshot completion
98106
for !tdb.SnapshotCompleted() {
99107
time.Sleep(100 * time.Millisecond)
@@ -215,13 +223,12 @@ func TestSizeTracker(t *testing.T) {
215223
if actualStats.ContractCodeBytes != expectedStats.ContractCodeBytes {
216224
t.Errorf("Contract code bytes mismatch: expected %d, got %d", expectedStats.ContractCodeBytes, actualStats.ContractCodeBytes)
217225
}
218-
// TODO: failed on github actions, need to investigate
219-
// if actualStats.AccountTrienodes != expectedStats.AccountTrienodes {
220-
// t.Errorf("Account trie nodes mismatch: expected %d, got %d", expectedStats.AccountTrienodes, actualStats.AccountTrienodes)
221-
// }
222-
// if actualStats.AccountTrienodeBytes != expectedStats.AccountTrienodeBytes {
223-
// t.Errorf("Account trie node bytes mismatch: expected %d, got %d", expectedStats.AccountTrienodeBytes, actualStats.AccountTrienodeBytes)
224-
// }
226+
if actualStats.AccountTrienodes != expectedStats.AccountTrienodes {
227+
t.Errorf("Account trie nodes mismatch: expected %d, got %d", expectedStats.AccountTrienodes, actualStats.AccountTrienodes)
228+
}
229+
if actualStats.AccountTrienodeBytes != expectedStats.AccountTrienodeBytes {
230+
t.Errorf("Account trie node bytes mismatch: expected %d, got %d", expectedStats.AccountTrienodeBytes, actualStats.AccountTrienodeBytes)
231+
}
225232
if actualStats.StorageTrienodes != expectedStats.StorageTrienodes {
226233
t.Errorf("Storage trie nodes mismatch: expected %d, got %d", expectedStats.StorageTrienodes, actualStats.StorageTrienodes)
227234
}

core/state/statedb.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,13 @@ func (s *StateDB) updateStateObject(obj *stateObject) {
577577
}
578578
}
579579

580+
// updateStateObject writes the given object to the trie.
581+
func (s *StateDB) updateStateObjectAsync(addr common.Address, resolver func() *types.StateAccount) {
582+
if err := s.trie.UpdateAccountAsync(addr, resolver); err != nil {
583+
s.setError(fmt.Errorf("updateStateObject (%x) error: %v", addr, err))
584+
}
585+
}
586+
580587
// deleteStateObject removes the given object from the state trie.
581588
func (s *StateDB) deleteStateObject(addr common.Address) {
582589
if err := s.trie.DeleteAccount(addr); err != nil {
@@ -829,11 +836,14 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
829836
// later time.
830837
workers.SetLimit(1)
831838
}
839+
840+
stateObjectsResolve := make(map[common.Address]func() *types.StateAccount)
832841
for addr, op := range s.mutations {
833842
if op.applied || op.isDelete() {
834843
continue
835844
}
836845
obj := s.stateObjects[addr] // closure for the task runner below
846+
complete := make(chan *types.StateAccount)
837847
workers.Go(func() error {
838848
if s.db.TrieDB().IsVerkle() {
839849
obj.updateTrie()
@@ -846,8 +856,13 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
846856
s.witness.AddState(obj.trie.Witness())
847857
}
848858
}
859+
complete <- &obj.data
849860
return nil
850861
})
862+
863+
stateObjectsResolve[addr] = func() *types.StateAccount {
864+
return <-complete
865+
}
851866
}
852867
// If witness building is enabled, gather all the read-only accesses.
853868
// Skip witness collection in Verkle mode, they will be gathered
@@ -898,7 +913,6 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
898913
}
899914
}
900915
}
901-
workers.Wait()
902916
s.StorageUpdates += time.Since(start)
903917

904918
// Now we're about to start to write changes to the trie. The trie is so far
@@ -939,7 +953,11 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
939953
if op.isDelete() {
940954
deletedAddrs = append(deletedAddrs, addr)
941955
} else {
942-
s.updateStateObject(s.stateObjects[addr])
956+
if s.db.TrieDB().IsVerkle() {
957+
s.updateStateObject(s.stateObjects[addr])
958+
} else {
959+
s.updateStateObjectAsync(addr, stateObjectsResolve[addr])
960+
}
943961
s.AccountUpdated += 1
944962
}
945963
usedAddrs = append(usedAddrs, addr) // Copy needed for closure
@@ -966,6 +984,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
966984
s.witnessStats.Add(witness, common.Hash{})
967985
}
968986
}
987+
969988
return hash
970989
}
971990

crypto/keccak_ziren.go

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,72 @@ package crypto
2121
import (
2222
"github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime"
2323
"github.com/ethereum/go-ethereum/common"
24-
"golang.org/x/crypto/sha3"
2524
)
2625

26+
// zirenKeccakState implements the KeccakState interface using the Ziren zkvm_runtime.
27+
// It accumulates data written to it and uses the zkvm's Keccak256 system call for hashing.
28+
type zirenKeccakState struct {
29+
buf []byte // accumulated data
30+
result []byte // cached result
31+
dirty bool // whether new data has been written since last hash
32+
}
33+
34+
func newZirenKeccakState() KeccakState {
35+
return &zirenKeccakState{
36+
buf: make([]byte, 0, 512), // pre-allocate reasonable capacity
37+
}
38+
}
39+
40+
func (s *zirenKeccakState) Write(p []byte) (n int, err error) {
41+
s.buf = append(s.buf, p...)
42+
s.dirty = true
43+
return len(p), nil
44+
}
45+
46+
func (s *zirenKeccakState) Sum(b []byte) []byte {
47+
s.computeHashIfNeeded()
48+
return append(b, s.result...)
49+
}
50+
51+
func (s *zirenKeccakState) Reset() {
52+
s.buf = s.buf[:0]
53+
s.result = nil
54+
s.dirty = false
55+
}
56+
57+
func (s *zirenKeccakState) Size() int {
58+
return 32
59+
}
60+
61+
func (s *zirenKeccakState) BlockSize() int {
62+
return 136 // Keccak256 rate
63+
}
64+
65+
func (s *zirenKeccakState) Read(p []byte) (n int, err error) {
66+
s.computeHashIfNeeded()
67+
68+
if len(p) == 0 {
69+
return 0, nil
70+
}
71+
72+
// After computeHashIfNeeded(), s.result is always a 32-byte slice
73+
n = copy(p, s.result)
74+
return n, nil
75+
}
76+
77+
func (s *zirenKeccakState) computeHashIfNeeded() {
78+
if s.dirty || s.result == nil {
79+
// Use the zkvm_runtime Keccak256 which uses SyscallKeccakSponge
80+
hashArray := zkvm_runtime.Keccak256(s.buf)
81+
s.result = hashArray[:]
82+
s.dirty = false
83+
}
84+
}
85+
2786
// NewKeccakState creates a new KeccakState
28-
// For now, we fallback to the original implementation for the stateful interface.
29-
// TODO: Implement a stateful wrapper around zkvm_runtime.Keccak256 if needed.
87+
// This uses a Ziren-optimized implementation that leverages the zkvm_runtime.Keccak256 system call.
3088
func NewKeccakState() KeccakState {
31-
return sha3.NewLegacyKeccak256().(KeccakState)
89+
return newZirenKeccakState()
3290
}
3391

3492
// Keccak256 calculates and returns the Keccak256 hash using the Ziren zkvm_runtime implementation.

p2p/server.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,9 @@ func (srv *Server) listenLoop() {
813813
time.Sleep(time.Millisecond * 200)
814814
continue
815815
} else if err != nil {
816-
srv.log.Debug("Read error", "err", err)
816+
if !errors.Is(err, net.ErrClosed) {
817+
srv.log.Debug("Read error", "err", err)
818+
}
817819
slots <- struct{}{}
818820
return
819821
}

rpc/client_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -973,7 +973,7 @@ func (l *flakeyListener) Accept() (net.Conn, error) {
973973

974974
c, err := l.Listener.Accept()
975975
if err == nil {
976-
timeout := time.Duration(rand.Int63n(int64(l.maxKillTimeout)))
976+
timeout := max(time.Millisecond*10, time.Duration(rand.Int63n(int64(l.maxKillTimeout))))
977977
time.AfterFunc(timeout, func() {
978978
log.Debug(fmt.Sprintf("killing conn %v after %v", c.LocalAddr(), timeout))
979979
c.Close()

0 commit comments

Comments
 (0)