@@ -2,6 +2,7 @@ package headstate
22
33import (
44 "context"
5+ "errors"
56 "fmt"
67 "iter"
78 "time"
@@ -24,15 +25,67 @@ const (
2425)
2526
2627type task struct {
27- batch db.Batch
28- addrCount int
28+ batch db.Batch
29+ completedAddrs int
2930}
3031
3132var (
3233 shouldRerun = []byte {}
3334 shouldNotRerun = []byte (nil )
3435)
3536
37+ var _ migration.Migration = (* Migrator )(nil )
38+
39+ // Migrator consolidates the deprecated per-field contract layout into a
40+ // single Contract record per address, written via state.WriteContract:
41+ //
42+ // ContractClassHash[addr]
43+ // ContractNonce[addr]
44+ // ContractDeploymentHeight[addr]
45+ // │
46+ // ▼
47+ // Contract[addr] = { ClassHash, Nonce, DeployedHeight }
48+ //
49+ // StorageRoot is left zero — the running node lazily backfills it on the
50+ // contract's first storage write.
51+ //
52+ // Each address discovered in the ContractClassHash bucket is processed by one
53+ // of ingestorCount worker goroutines that read the three old fields into a
54+ // shared db.Batch; a single committer drains batches to disk. Once every
55+ // address has been migrated, the three deprecated buckets are wiped via
56+ // DeleteRange.
57+ //
58+ // Re-run safe: an address whose Contract record already exists is skipped
59+ // (via state.HasContract), and the trailing wipe re-issues DeleteRange over
60+ // the (possibly already empty) ranges.
61+ type Migrator struct {}
62+
63+ func (Migrator ) Before ([]byte ) error {
64+ return nil
65+ }
66+
67+ func (Migrator ) Migrate (
68+ ctx context.Context ,
69+ database db.KeyValueStore ,
70+ _ * networks.Network ,
71+ logger log.StructuredLogger ,
72+ ) ([]byte , error ) {
73+ addressesIter , sourceErr := pendingAddresses (database )
74+ res := migrateAddresses (ctx , database , logger , addressesIter )
75+
76+ if err := errors .Join (sourceErr (), res .Err ); err != nil {
77+ return shouldRerun , err
78+ }
79+ if ! res .IsDone {
80+ if ctxErr := ctx .Err (); ctxErr != nil {
81+ return shouldRerun , ctxErr
82+ }
83+ return shouldRerun , errors .New ("headstate migration did not complete" )
84+ }
85+
86+ return shouldNotRerun , wipeDeprecatedBuckets (database )
87+ }
88+
3689func migrateAddresses (
3790 ctx context.Context ,
3891 database db.KeyValueStore ,
@@ -64,51 +117,6 @@ func migrateAddresses(
64117 return wait ()
65118}
66119
67- var _ migration.Migration = (* Migrator )(nil )
68-
69- type Migrator struct {}
70-
71- func (Migrator ) Before ([]byte ) error {
72- return nil
73- }
74-
75- func (Migrator ) Migrate (
76- ctx context.Context ,
77- database db.KeyValueStore ,
78- _ * networks.Network ,
79- logger log.StructuredLogger ,
80- ) ([]byte , error ) {
81- hasPending , err := hasPendingAddresses (database )
82- if err != nil {
83- return shouldRerun , err
84- }
85- if ! hasPending {
86- return shouldNotRerun , wipeDeprecatedBuckets (database )
87- }
88-
89- addressesIter , sourceErr := pendingAddresses (database )
90- res := migrateAddresses (ctx , database , logger , addressesIter )
91-
92- if err := sourceErr (); err != nil {
93- return shouldRerun , err
94- }
95- if res .Err != nil || ! res .IsDone {
96- return shouldRerun , res .Err
97- }
98-
99- return shouldNotRerun , wipeDeprecatedBuckets (database )
100- }
101-
102- func hasPendingAddresses (r db.KeyValueReader ) (bool , error ) {
103- prefix := db .ContractClassHash .Key ()
104- it , err := r .NewIterator (prefix , true )
105- if err != nil {
106- return false , err
107- }
108- defer it .Close ()
109- return it .First (), nil
110- }
111-
112120func pendingAddresses (r db.KeyValueReader ) (iter.Seq [felt.Address ], func () error ) {
113121 var iterErr error
114122 seq := func (yield func (felt.Address ) bool ) {
0 commit comments