Skip to content

Commit 20ae963

Browse files
committed
Merkle-ize X-Chain state
Signed-off-by: Joshua Kim <[email protected]>
1 parent daab96b commit 20ae963

File tree

9 files changed

+564
-40
lines changed

9 files changed

+564
-40
lines changed

vms/avm/block/executor/block.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ func (b *Block) Verify(context.Context) error {
201201
return nil
202202
}
203203

204-
func (b *Block) Accept(context.Context) error {
204+
func (b *Block) Accept(ctx context.Context) error {
205205
blkID := b.ID()
206206
defer b.manager.free(blkID)
207207

@@ -246,7 +246,7 @@ func (b *Block) Accept(context.Context) error {
246246
return err
247247
}
248248

249-
txChecksum, utxoChecksum := b.manager.state.Checksums()
249+
txChecksum, utxoChecksum := b.manager.state.Checksums(ctx)
250250
b.manager.backend.Ctx.Log.Trace(
251251
"accepted block",
252252
zap.Stringer("blkID", blkID),

vms/avm/state.go

Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
package avm
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"iter"
7+
"time"
8+
9+
"github.com/prometheus/client_golang/prometheus"
10+
11+
"github.com/ava-labs/avalanchego/database"
12+
"github.com/ava-labs/avalanchego/database/prefixdb"
13+
"github.com/ava-labs/avalanchego/database/versiondb"
14+
"github.com/ava-labs/avalanchego/ids"
15+
"github.com/ava-labs/avalanchego/utils/logging"
16+
"github.com/ava-labs/avalanchego/vms/avm/block"
17+
"github.com/ava-labs/avalanchego/vms/avm/state"
18+
"github.com/ava-labs/avalanchego/vms/avm/txs"
19+
"github.com/ava-labs/avalanchego/vms/components/avax"
20+
"github.com/ava-labs/avalanchego/x/merkledb"
21+
)
22+
23+
var _ state.State = (*gForkState)(nil)
24+
25+
func newGForkState(
26+
ctx context.Context,
27+
db database.Database,
28+
parser block.Parser,
29+
metrics prometheus.Registerer,
30+
trackChecksums bool,
31+
) (gForkState, error) {
32+
versionDB := versiondb.New(db)
33+
34+
merkleDBConfig := merkledb.NewConfig()
35+
36+
// Holds data required for tx execution
37+
stateDB := prefixdb.New([]byte("state"), versionDB)
38+
stateMerkleDB, err := merkledb.New(
39+
ctx,
40+
stateDB,
41+
merkleDBConfig,
42+
"state",
43+
)
44+
if err != nil {
45+
return gForkState{}, fmt.Errorf("failed to initialize state db: %w", err)
46+
}
47+
48+
utxoDB := prefixdb.New([]byte("utxo"), stateMerkleDB)
49+
utxoMerkleDB, err := merkledb.New(ctx, utxoDB, merkledb.NewConfig(), "utxo")
50+
if err != nil {
51+
return gForkState{}, fmt.Errorf("failed to initialize utxo db: %w", err)
52+
}
53+
54+
// Holds all other data
55+
metadataDB := prefixdb.New([]byte("metadata"), versionDB)
56+
metadataMerkleDB, err := merkledb.New(
57+
ctx,
58+
metadataDB,
59+
merkleDBConfig,
60+
"metadata",
61+
)
62+
if err != nil {
63+
return gForkState{}, fmt.Errorf("failed to initialize metadata db: %w", err)
64+
}
65+
66+
txDB := prefixdb.New([]byte("tx"), metadataMerkleDB)
67+
txMerkleDB, err := merkledb.New(ctx, txDB, merkleDBConfig, "tx")
68+
if err != nil {
69+
return gForkState{}, fmt.Errorf("failed to initialize tx db: %w", err)
70+
}
71+
72+
blockIDDB := prefixdb.New([]byte("block_id"), metadataDB)
73+
blockDB := prefixdb.New([]byte("block"), metadataDB)
74+
singletonDB := prefixdb.New([]byte("singleton"), metadataDB)
75+
76+
s, err := state.NewWithFormat(
77+
versionDB,
78+
utxoMerkleDB,
79+
txMerkleDB,
80+
blockIDDB,
81+
blockDB,
82+
singletonDB,
83+
parser,
84+
metrics,
85+
trackChecksums,
86+
)
87+
if err != nil {
88+
return gForkState{}, fmt.Errorf("failed to initialize state: %w", err)
89+
}
90+
91+
return gForkState{
92+
state: s,
93+
utxoDB: utxoMerkleDB,
94+
txDB: txMerkleDB,
95+
}, nil
96+
}
97+
98+
// TODO decompose this struct into state used/not used during execution
99+
// TODO move this into state/ once the G fork is completed
100+
type gForkState struct {
101+
state state.State
102+
utxoDB merkledb.MerkleDB
103+
txDB merkledb.MerkleDB
104+
}
105+
106+
func (g gForkState) GetUTXO(utxoID ids.ID) (*avax.UTXO, error) {
107+
return g.state.GetUTXO(utxoID)
108+
}
109+
110+
func (g gForkState) GetTx(txID ids.ID) (*txs.Tx, error) {
111+
return g.state.GetTx(txID)
112+
}
113+
114+
func (g gForkState) GetBlockIDAtHeight(height uint64) (ids.ID, error) {
115+
return g.state.GetBlockIDAtHeight(height)
116+
}
117+
118+
func (g gForkState) GetBlock(blkID ids.ID) (block.Block, error) {
119+
return g.state.GetBlock(blkID)
120+
}
121+
122+
func (g gForkState) GetLastAccepted() ids.ID {
123+
return g.state.GetLastAccepted()
124+
}
125+
126+
func (g gForkState) GetTimestamp() time.Time {
127+
return g.state.GetTimestamp()
128+
}
129+
130+
func (g gForkState) AddUTXO(utxo *avax.UTXO) {
131+
g.state.AddUTXO(utxo)
132+
}
133+
134+
func (g gForkState) DeleteUTXO(utxoID ids.ID) {
135+
g.state.DeleteUTXO(utxoID)
136+
}
137+
138+
func (g gForkState) AddTx(tx *txs.Tx) {
139+
g.state.AddTx(tx)
140+
}
141+
142+
func (g gForkState) AddBlock(blk block.Block) {
143+
g.state.AddBlock(blk)
144+
}
145+
146+
func (g gForkState) SetLastAccepted(blkID ids.ID) {
147+
g.state.SetLastAccepted(blkID)
148+
}
149+
150+
func (g gForkState) SetTimestamp(t time.Time) {
151+
g.state.SetTimestamp(t)
152+
}
153+
154+
func (g gForkState) UTXOIDs(addr []byte, previous ids.ID, limit int) ([]ids.ID, error) {
155+
return g.state.UTXOIDs(addr, previous, limit)
156+
}
157+
158+
func (g gForkState) UTXOs() iter.Seq2[*avax.UTXO, error] {
159+
return g.state.UTXOs()
160+
}
161+
162+
func (g gForkState) IsInitialized() (bool, error) {
163+
return g.state.IsInitialized()
164+
}
165+
166+
func (g gForkState) SetInitialized() error {
167+
return g.state.SetInitialized()
168+
}
169+
170+
func (g gForkState) InitializeChainState(stopVertexID ids.ID, genesisTimestamp time.Time) error {
171+
return g.state.InitializeChainState(stopVertexID, genesisTimestamp)
172+
}
173+
174+
func (g gForkState) Abort() {
175+
g.state.Abort()
176+
}
177+
178+
func (g gForkState) Commit() error {
179+
return g.state.Commit()
180+
}
181+
182+
func (g gForkState) CommitBatch() (database.Batch, error) {
183+
return g.state.CommitBatch()
184+
}
185+
186+
func (g gForkState) Checksums(ctx context.Context) (txChecksum ids.ID, utxoChecksum ids.ID, err error) {
187+
txRoot, err := g.txDB.GetMerkleRoot(ctx)
188+
if err != nil {
189+
return ids.ID{}, ids.ID{}, fmt.Errorf("failed to get tx root: %w", err)
190+
}
191+
192+
utxoRoot, err := g.utxoDB.GetMerkleRoot(ctx)
193+
if err != nil {
194+
return ids.ID{}, ids.ID{}, fmt.Errorf("failed to get utxo root: %w", err)
195+
}
196+
197+
return txRoot, utxoRoot, nil
198+
}
199+
200+
func (g gForkState) Txs() iter.Seq2[*txs.Tx, error] {
201+
return g.state.Txs()
202+
}
203+
204+
func (g gForkState) Blocks() iter.Seq2[block.Block, error] {
205+
return g.state.Blocks()
206+
}
207+
208+
func (g gForkState) Close() error {
209+
return g.state.Close()
210+
}
211+
212+
func migrateDB[T any](
213+
seq iter.Seq2[T, error],
214+
accept func(t T),
215+
s *gForkState,
216+
freq int,
217+
) error {
218+
i := 0
219+
for e, err := range seq {
220+
if err != nil {
221+
return err
222+
}
223+
224+
accept(e)
225+
i++
226+
227+
if i%freq != 0 {
228+
continue
229+
}
230+
231+
if err := s.Commit(); err != nil {
232+
return fmt.Errorf("failed to commit db: %w", err)
233+
}
234+
}
235+
236+
if err := s.Commit(); err != nil {
237+
return fmt.Errorf("failed to commit db: %w", err)
238+
}
239+
240+
return nil
241+
}
242+
243+
// TODO move to vm?
244+
func Migrate(log logging.Logger, prev state.State, next gForkState,
245+
commitFrequency int) error {
246+
log.Debug("migrating utxos")
247+
if err := migrateDB[*avax.UTXO](
248+
prev.UTXOs(),
249+
func(utxo *avax.UTXO) { next.AddUTXO(utxo) },
250+
next,
251+
commitFrequency,
252+
); err != nil {
253+
return fmt.Errorf("failed to migrate utxos: %w", err)
254+
}
255+
256+
log.Debug("migrating txs")
257+
if err := migrateDB[*txs.Tx](
258+
prev.Txs(),
259+
func(tx *txs.Tx) { next.AddTx(tx) },
260+
next,
261+
commitFrequency,
262+
); err != nil {
263+
return fmt.Errorf("failed to migrate txs: %w", err)
264+
}
265+
266+
log.Debug("migrating blocks")
267+
if err := migrateDB[block.Block](
268+
prev.Blocks(),
269+
func(blk block.Block) {
270+
next.AddBlock(blk)
271+
},
272+
next,
273+
commitFrequency,
274+
); err != nil {
275+
return fmt.Errorf("failed to migrate blocks: %w", err)
276+
}
277+
278+
log.Debug("migrating singletons")
279+
next.SetTimestamp(prev.GetTimestamp())
280+
ok, err := prev.IsInitialized()
281+
if err != nil {
282+
return fmt.Errorf("failed to get initialized flag: %w", err)
283+
}
284+
if ok {
285+
if err := next.SetInitialized(); err != nil {
286+
return fmt.Errorf("failed to set initialized flag: %w", err)
287+
}
288+
}
289+
290+
next.SetLastAccepted(prev.GetLastAccepted())
291+
292+
if err := next.Commit(); err != nil {
293+
return fmt.Errorf("failed to commit db: %w", err)
294+
}
295+
296+
log.Debug("migration complete")
297+
298+
return nil
299+
}

0 commit comments

Comments
 (0)