diff --git a/trie/verkle_iterator.go b/trie/verkle_iterator.go
new file mode 100644
index 000000000000..47fa9c2d0f77
--- /dev/null
+++ b/trie/verkle_iterator.go
@@ -0,0 +1,218 @@
+// Copyright 2021 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 .
+
+package trie
+
+import (
+ "github.com/ethereum/go-ethereum/common"
+
+ "github.com/ethereum/go-verkle"
+)
+
+type verkleNodeIteratorState struct {
+ Node verkle.VerkleNode
+ Index int // points to _next_ value
+}
+
+type verkleNodeIterator struct {
+ trie *VerkleTrie
+ current verkle.VerkleNode
+ lastErr error
+
+ stack []verkleNodeIteratorState
+}
+
+func newVerkleNodeIterator(trie *VerkleTrie, _ []byte) (NodeIterator, error) {
+ if trie.Hash() == (common.Hash{}) {
+ return new(nodeIterator), nil
+ }
+ it := &verkleNodeIterator{trie: trie, current: trie.root}
+ // it.err = it.seek(start)
+ return it, nil
+}
+
+// Next moves the iterator to the next node. If the parameter is false, any child
+// nodes will be skipped.
+func (it *verkleNodeIterator) Next(descend bool) bool {
+ if it.lastErr == errIteratorEnd {
+ it.lastErr = errIteratorEnd
+ return false
+ }
+
+ if len(it.stack) == 0 {
+ it.stack = append(it.stack, verkleNodeIteratorState{Node: it.trie.root, Index: 0})
+ it.current = it.trie.root
+
+ return true
+ }
+
+ switch node := it.current.(type) {
+ case *verkle.InternalNode:
+ context := &it.stack[len(it.stack)-1]
+
+ // Look for the next non-empty child
+ children := node.Children()
+ for ; context.Index < len(children); context.Index++ {
+ if _, ok := children[context.Index].(verkle.Empty); !ok {
+ it.stack = append(it.stack, verkleNodeIteratorState{Node: children[context.Index], Index: 0})
+ it.current = children[context.Index]
+ return it.Next(descend)
+ }
+ }
+
+ // Reached the end of this node, go back to the parent, if
+ // this isn't root.
+ if len(it.stack) == 1 {
+ it.lastErr = errIteratorEnd
+ return false
+ }
+ it.stack = it.stack[:len(it.stack)-1]
+ it.current = it.stack[len(it.stack)-1].Node
+ it.stack[len(it.stack)-1].Index++
+ return it.Next(descend)
+ case *verkle.LeafNode:
+ // Look for the next non-empty value
+ for i := it.stack[len(it.stack)-1].Index; i < 256; i++ {
+ if node.Value(i) != nil {
+ it.stack[len(it.stack)-1].Index = i + 1
+ return true
+ }
+ }
+
+ // go back to parent to get the next leaf
+ it.stack = it.stack[:len(it.stack)-1]
+ it.current = it.stack[len(it.stack)-1].Node
+ it.stack[len(it.stack)-1].Index++
+ return it.Next(descend)
+ case verkle.HashedNode:
+ // resolve the node
+ data, err := it.trie.reader.node(it.Path(), common.Hash{})
+ if err != nil {
+ panic(err)
+ }
+ it.current, err = verkle.ParseNode(data, byte(len(it.stack)-1))
+ if err != nil {
+ panic(err)
+ }
+
+ // update the stack and parent with the resolved node
+ it.stack[len(it.stack)-1].Node = it.current
+ parent := &it.stack[len(it.stack)-2]
+ parent.Node.(*verkle.InternalNode).SetChild(parent.Index, it.current)
+ return it.Next(true)
+ default:
+ panic("invalid node type")
+ }
+}
+
+// Error returns the error status of the iterator.
+func (it *verkleNodeIterator) Error() error {
+ if it.lastErr == errIteratorEnd {
+ return nil
+ }
+ return it.lastErr
+}
+
+// Hash returns the hash of the current node.
+func (it *verkleNodeIterator) Hash() common.Hash {
+ return it.current.Commit().Bytes()
+}
+
+// Parent returns the hash of the parent of the current node. The hash may be the one
+// grandparent if the immediate parent is an internal node with no hash.
+func (it *verkleNodeIterator) Parent() common.Hash {
+ return it.stack[len(it.stack)-1].Node.Commit().Bytes()
+}
+
+// Path returns the hex-encoded path to the current node.
+// Callers must not retain references to the return value after calling Next.
+// For leaf nodes, the last element of the path is the 'terminator symbol' 0x10.
+func (it *verkleNodeIterator) Path() []byte {
+ if it.Leaf() {
+ return it.LeafKey()
+ }
+ var path []byte
+ for i, state := range it.stack {
+ // skip the last byte
+ if i >= len(it.stack)-1 {
+ break
+ }
+ path = append(path, byte(state.Index))
+ }
+ return path
+}
+
+func (it *verkleNodeIterator) NodeBlob() []byte {
+ panic("not completely implemented")
+}
+
+// Leaf returns true iff the current node is a leaf node.
+func (it *verkleNodeIterator) Leaf() bool {
+ _, ok := it.current.(*verkle.LeafNode)
+ return ok
+}
+
+// LeafKey returns the key of the leaf. The method panics if the iterator is not
+// positioned at a leaf. Callers must not retain references to the value after
+// calling Next.
+func (it *verkleNodeIterator) LeafKey() []byte {
+ leaf, ok := it.current.(*verkle.LeafNode)
+ if !ok {
+ panic("Leaf() called on an verkle node iterator not at a leaf location")
+ }
+
+ return leaf.Key(it.stack[len(it.stack)-1].Index - 1)
+}
+
+// LeafBlob returns the content of the leaf. The method panics if the iterator
+// is not positioned at a leaf. Callers must not retain references to the value
+// after calling Next.
+func (it *verkleNodeIterator) LeafBlob() []byte {
+ leaf, ok := it.current.(*verkle.LeafNode)
+ if !ok {
+ panic("LeafBlob() called on an verkle node iterator not at a leaf location")
+ }
+
+ return leaf.Value(it.stack[len(it.stack)-1].Index - 1)
+}
+
+// LeafProof returns the Merkle proof of the leaf. The method panics if the
+// iterator is not positioned at a leaf. Callers must not retain references
+// to the value after calling Next.
+func (it *verkleNodeIterator) LeafProof() [][]byte {
+ _, ok := it.current.(*verkle.LeafNode)
+ if !ok {
+ panic("LeafProof() called on an verkle node iterator not at a leaf location")
+ }
+
+ // return it.trie.Prove(leaf.Key())
+ panic("not completely implemented")
+}
+
+// AddResolver sets an intermediate database to use for looking up trie nodes
+// before reaching into the real persistent layer.
+//
+// This is not required for normal operation, rather is an optimization for
+// cases where trie nodes can be recovered from some external mechanism without
+// reading from disk. In those cases, this resolver allows short circuiting
+// accesses and returning them from memory.
+//
+// Before adding a similar mechanism to any other place in Geth, consider
+// making trie.Database an interface and wrapping at that level. It's a huge
+// refactor, but it could be worth it if another occurrence arises.
+func (it *verkleNodeIterator) AddResolver(NodeResolver) {
+ // Not implemented, but should not panic
+}
diff --git a/trie/verkle_iterator_test.go b/trie/verkle_iterator_test.go
new file mode 100644
index 000000000000..e1ec6701078f
--- /dev/null
+++ b/trie/verkle_iterator_test.go
@@ -0,0 +1,70 @@
+// Copyright 2023 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 .
+
+package trie
+
+import (
+ "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/trie/utils"
+ "github.com/holiman/uint256"
+)
+
+func TestVerkleIterator(t *testing.T) {
+ trie, err := NewVerkleTrie(types.EmptyVerkleHash, newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.PathScheme), utils.NewPointCache(1024))
+ if err != nil {
+ panic(err)
+ }
+ account0 := &types.StateAccount{
+ Nonce: 1,
+ Balance: new(uint256.Int).SetUint64(2),
+ Root: types.EmptyRootHash,
+ CodeHash: nil,
+ }
+ // NOTE: the code size isn't written to the trie via TryUpdateAccount
+ // so it will be missing from the test nodes.
+ trie.UpdateAccount(common.Address{}, account0, 0)
+ account1 := &types.StateAccount{
+ Nonce: 1337,
+ Balance: new(uint256.Int).SetUint64(2000),
+ Root: types.EmptyRootHash,
+ CodeHash: nil,
+ }
+ // This address is meant to hash to a value that has the same first byte as 0xbf
+ var clash = common.HexToAddress("69fd8034cdb20934dedffa7dccb4fb3b8062a8be")
+ trie.UpdateAccount(clash, account1, 0)
+
+ // Manually go over every node to check that we get all
+ // the correct nodes.
+ it, err := trie.NodeIterator(nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ var leafcount int
+ for it.Next(true) {
+ t.Logf("Node: %x", it.Path())
+ if it.Leaf() {
+ leafcount++
+ t.Logf("\tLeaf: %x", it.LeafKey())
+ }
+ }
+ if leafcount != 2 {
+ t.Fatalf("invalid leaf count: %d != 6", leafcount)
+ }
+}