Skip to content

Commit

Permalink
Merge pull request #5755 from onflow/fxamacker/fix-broken-reference-m…
Browse files Browse the repository at this point in the history
…igration

Add migration to fix refs to non-existent registers
  • Loading branch information
fxamacker authored Apr 24, 2024
2 parents 3c85102 + 923895e commit e659bfa
Show file tree
Hide file tree
Showing 6 changed files with 432 additions and 28 deletions.
5 changes: 5 additions & 0 deletions cmd/util/cmd/execution-state-extract/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ var (
flagOutputPayloadFileName string
flagOutputPayloadByAddresses string
flagMaxAccountSize uint64
flagFixSlabsWithBrokenReferences bool
flagFilterUnreferencedSlabs bool
)

Expand Down Expand Up @@ -153,6 +154,9 @@ func init() {
Cmd.Flags().Uint64Var(&flagMaxAccountSize, "max-account-size", 0,
"max account size")

Cmd.Flags().BoolVar(&flagFixSlabsWithBrokenReferences, "fix-testnet-slabs-with-broken-references", false,
"fix slabs with broken references in testnet")

Cmd.Flags().BoolVar(&flagFilterUnreferencedSlabs, "filter-unreferenced-slabs", false,
"filter unreferenced slabs")
}
Expand Down Expand Up @@ -375,6 +379,7 @@ func run(*cobra.Command, []string) {
Prune: flagPrune,
MaxAccountSize: flagMaxAccountSize,
VerboseErrorOutput: flagVerboseErrorOutput,
FixSlabsWithBrokenReferences: chainID == flow.Testnet && flagFixSlabsWithBrokenReferences,
FilterUnreferencedSlabs: flagFilterUnreferencedSlabs,
}

Expand Down
47 changes: 30 additions & 17 deletions cmd/util/ledger/migrations/account_based_migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"github.com/onflow/flow-go/cmd/util/ledger/util"
"github.com/onflow/flow-go/ledger"
"github.com/onflow/flow-go/model/flow"
moduleUtil "github.com/onflow/flow-go/module/util"
)

Expand Down Expand Up @@ -186,22 +187,6 @@ func MigrateGroupConcurrently(
continue
}

if _, ok := knownProblematicAccounts[job.Address]; ok {
log.Info().
Hex("address", job.Address[:]).
Int("payload_count", len(job.Payloads)).
Msg("skipping problematic account")
resultCh <- &migrationResult{
migrationDuration: migrationDuration{
Address: job.Address,
Duration: time.Since(start),
PayloadCount: len(job.Payloads),
},
Migrated: job.Payloads,
}
continue
}

var err error
accountMigrated := job.Payloads
for m, migrator := range migrations {
Expand Down Expand Up @@ -315,7 +300,35 @@ func MigrateGroupConcurrently(
return migrated, nil
}

var knownProblematicAccounts = map[common.Address]string{}
var testnetAccountsWithBrokenSlabReferences = func() map[common.Address]struct{} {
testnetAddresses := map[common.Address]struct{}{
mustHexToAddress("434a1f199a7ae3ba"): {},
mustHexToAddress("454c9991c2b8d947"): {},
mustHexToAddress("48602d8056ff9d93"): {},
mustHexToAddress("5d63c34d7f05e5a4"): {},
mustHexToAddress("5e3448b3cffb97f2"): {},
mustHexToAddress("7d8c7e050c694eaa"): {},
mustHexToAddress("ba53f16ede01972d"): {},
mustHexToAddress("c843c1f5a4805c3a"): {},
mustHexToAddress("48d3be92e6e4a973"): {},
}

for address := range testnetAddresses {
if !flow.Testnet.Chain().IsValid(flow.Address(address)) {
panic(fmt.Sprintf("invalid testnet address: %s", address.Hex()))
}
}

return testnetAddresses
}()

func mustHexToAddress(hex string) common.Address {
address, err := common.HexToAddress(hex)
if err != nil {
panic(err)
}
return address
}

type jobMigrateAccountGroup struct {
Address common.Address
Expand Down
28 changes: 23 additions & 5 deletions cmd/util/ledger/migrations/cadence.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ type Options struct {
StagedContracts []StagedContract
Prune bool
MaxAccountSize uint64
FixSlabsWithBrokenReferences bool
FilterUnreferencedSlabs bool
}

Expand Down Expand Up @@ -414,15 +415,32 @@ func NewCadence1Migrations(
)
}

if opts.FilterUnreferencedSlabs {
if opts.FixSlabsWithBrokenReferences || opts.FilterUnreferencedSlabs {

var accountBasedMigrations []AccountBasedMigration

if opts.FixSlabsWithBrokenReferences {
accountBasedMigrations = append(
accountBasedMigrations,
NewFixBrokenReferencesInSlabsMigration(rwf, testnetAccountsWithBrokenSlabReferences),
)
}

if opts.FilterUnreferencedSlabs {
accountBasedMigrations = append(
accountBasedMigrations,
// NOTE: migration to filter unreferenced slabs should happen
// after migration to fix slabs with references to nonexistent slabs.
NewFilterUnreferencedSlabsMigration(outputDir, rwf),
)
}

migrations = append(migrations, NamedMigration{
Name: "filter-unreferenced-slabs-migration",
Name: "fix-slabs-migration",
Migrate: NewAccountBasedMigration(
log,
opts.NWorker,
[]AccountBasedMigration{
NewFilterUnreferencedSlabsMigration(outputDir, rwf),
},
accountBasedMigrations,
),
})
}
Expand Down
172 changes: 172 additions & 0 deletions cmd/util/ledger/migrations/fix_broken_data_migration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package migrations

import (
"context"
"fmt"

"github.com/rs/zerolog"

"github.com/onflow/atree"

"github.com/onflow/cadence/runtime/common"

"github.com/onflow/flow-go/cmd/util/ledger/reporters"
"github.com/onflow/flow-go/ledger"
"github.com/onflow/flow-go/ledger/common/convert"
"github.com/onflow/flow-go/model/flow"
)

type FixSlabsWithBrokenReferencesMigration struct {
log zerolog.Logger
rw reporters.ReportWriter
accountsToFix map[common.Address]struct{}
nWorkers int
}

var _ AccountBasedMigration = &FixSlabsWithBrokenReferencesMigration{}

const fixSlabsWithBrokenReferencesName = "fix-slabs-with-broken-references"

func NewFixBrokenReferencesInSlabsMigration(
rwf reporters.ReportWriterFactory,
accountsToFix map[common.Address]struct{},
) *FixSlabsWithBrokenReferencesMigration {
return &FixSlabsWithBrokenReferencesMigration{
rw: rwf.ReportWriter(fixSlabsWithBrokenReferencesName),
accountsToFix: accountsToFix,
}
}

func (m *FixSlabsWithBrokenReferencesMigration) InitMigration(
log zerolog.Logger,
_ []*ledger.Payload,
nWorkers int,
) error {
m.log = log.
With().
Str("migration", fixSlabsWithBrokenReferencesName).
Logger()
m.nWorkers = nWorkers

return nil
}

func (m *FixSlabsWithBrokenReferencesMigration) MigrateAccount(
_ context.Context,
address common.Address,
oldPayloads []*ledger.Payload,
) (
newPayloads []*ledger.Payload,
err error,
) {

if _, exist := m.accountsToFix[address]; !exist {
return oldPayloads, nil
}

migrationRuntime, err := NewAtreeRegisterMigratorRuntime(address, oldPayloads)
if err != nil {
return nil, fmt.Errorf("failed to create cadence runtime: %w", err)
}

storage := migrationRuntime.Storage

// Load all atree registers in storage
err = loadAtreeSlabsInStorge(storage, oldPayloads)
if err != nil {
return nil, err
}

// Fix broken references
fixedStorageIDs, skippedStorageIDs, err := storage.FixLoadedBrokenReferences(func(old atree.Value) bool {
// TODO: Cadence may need to export functions to check type info, etc.
return true
})
if err != nil {
return nil, err
}

if len(skippedStorageIDs) > 0 {
m.log.Warn().
Str("account", address.Hex()).
Msgf("skipped slabs with broken references: %v", skippedStorageIDs)
}

if len(fixedStorageIDs) == 0 {
m.log.Warn().
Str("account", address.Hex()).
Msgf("did not fix any slabs with broken references")

return oldPayloads, nil
}

m.log.Log().
Str("account", address.Hex()).
Msgf("fixed slabs with broken references: %v", fixedStorageIDs)

err = storage.FastCommit(m.nWorkers)
if err != nil {
return nil, err
}

// Finalize the transaction
result, err := migrationRuntime.TransactionState.FinalizeMainTransaction()
if err != nil {
return nil, fmt.Errorf("failed to finalize main transaction: %w", err)
}

// Merge the changes to the original payloads.
expectedAddresses := map[flow.Address]struct{}{
flow.Address(address): {},
}

newPayloads, err = migrationRuntime.Snapshot.ApplyChangesAndGetNewPayloads(
result.WriteSet,
expectedAddresses,
m.log,
)
if err != nil {
return nil, err
}

// Log fixed payloads
fixedPayloads := make([]*ledger.Payload, 0, len(fixedStorageIDs))
for _, payload := range newPayloads {
registerID, _, err := convert.PayloadToRegister(payload)
if err != nil {
return nil, fmt.Errorf("failed to convert payload to register: %w", err)
}

if !registerID.IsSlabIndex() {
continue
}

storageID := atree.NewStorageID(
atree.Address([]byte(registerID.Owner)),
atree.StorageIndex([]byte(registerID.Key[1:])),
)

if _, ok := fixedStorageIDs[storageID]; ok {
fixedPayloads = append(fixedPayloads, payload)
}
}

m.rw.Write(fixedSlabsWithBrokenReferences{
Account: address,
Payloads: fixedPayloads,
})

return newPayloads, nil
}

func (m *FixSlabsWithBrokenReferencesMigration) Close() error {
// close the report writer so it flushes to file
m.rw.Close()

return nil
}

type fixedSlabsWithBrokenReferences struct {
Account common.Address `json:"account"`
Payloads []*ledger.Payload `json:"payloads"`
}
Loading

0 comments on commit e659bfa

Please sign in to comment.