github.com/onflow/flow-go@v0.33.17/cmd/util/ledger/migrations/deduplicate_contract_names_migration.go (about) 1 package migrations 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/fxamacker/cbor/v2" 8 "github.com/rs/zerolog" 9 10 "github.com/onflow/cadence/runtime/common" 11 12 "github.com/onflow/flow-go/ledger" 13 "github.com/onflow/flow-go/ledger/common/convert" 14 "github.com/onflow/flow-go/model/flow" 15 ) 16 17 // DeduplicateContractNamesMigration checks if the contract names have been duplicated and 18 // removes the duplicate ones. 19 // 20 // This migration de-syncs storage used, so it should be run before the StorageUsedMigration. 21 type DeduplicateContractNamesMigration struct { 22 log zerolog.Logger 23 } 24 25 func (d *DeduplicateContractNamesMigration) Close() error { 26 return nil 27 } 28 29 func (d *DeduplicateContractNamesMigration) InitMigration( 30 log zerolog.Logger, 31 _ []*ledger.Payload, 32 _ int, 33 ) error { 34 d.log = log. 35 With(). 36 Str("migration", "DeduplicateContractNamesMigration"). 37 Logger() 38 39 return nil 40 } 41 42 func (d *DeduplicateContractNamesMigration) MigrateAccount( 43 ctx context.Context, 44 address common.Address, 45 payloads []*ledger.Payload, 46 ) ([]*ledger.Payload, error) { 47 flowAddress := flow.ConvertAddress(address) 48 contractNamesID := flow.ContractNamesRegisterID(flowAddress) 49 50 var contractNamesPayload *ledger.Payload 51 contractNamesPayloadIndex := 0 52 for i, payload := range payloads { 53 key, err := payload.Key() 54 if err != nil { 55 return nil, err 56 } 57 id, err := convert.LedgerKeyToRegisterID(key) 58 if err != nil { 59 return nil, err 60 } 61 if id == contractNamesID { 62 contractNamesPayload = payload 63 contractNamesPayloadIndex = i 64 break 65 } 66 } 67 if contractNamesPayload == nil { 68 return payloads, nil 69 } 70 71 value := contractNamesPayload.Value() 72 if len(value) == 0 { 73 // Remove the empty payload 74 copy(payloads[contractNamesPayloadIndex:], payloads[contractNamesPayloadIndex+1:]) 75 payloads = payloads[:len(payloads)-1] 76 77 return payloads, nil 78 } 79 80 var contractNames []string 81 err := cbor.Unmarshal(value, &contractNames) 82 if err != nil { 83 return nil, fmt.Errorf("failed to get contract names: %w", err) 84 } 85 86 var foundDuplicate bool 87 i := 1 88 for i < len(contractNames) { 89 if contractNames[i-1] != contractNames[i] { 90 91 if contractNames[i-1] > contractNames[i] { 92 // this is not a valid state and we should fail. 93 // Contract names must be sorted by definition. 94 return nil, fmt.Errorf( 95 "contract names for account %s are not sorted: %s", 96 address.Hex(), 97 contractNames, 98 ) 99 } 100 101 i++ 102 continue 103 } 104 // Found duplicate (contactNames[i-1] == contactNames[i]) 105 // Remove contractNames[i] 106 copy(contractNames[i:], contractNames[i+1:]) 107 contractNames = contractNames[:len(contractNames)-1] 108 foundDuplicate = true 109 } 110 111 if !foundDuplicate { 112 return payloads, nil 113 } 114 115 d.log.Info(). 116 Str("address", address.Hex()). 117 Strs("contract_names", contractNames). 118 Msg("removing duplicate contract names") 119 120 newContractNames, err := cbor.Marshal(contractNames) 121 if err != nil { 122 return nil, fmt.Errorf( 123 "cannot encode contract names: %s", 124 contractNames, 125 ) 126 } 127 128 payloads[contractNamesPayloadIndex] = ledger.NewPayload(convert.RegisterIDToLedgerKey(contractNamesID), newContractNames) 129 return payloads, nil 130 131 } 132 133 var _ AccountBasedMigration = &DeduplicateContractNamesMigration{}