github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/cmd/util/ledger/migrations/migrator_runtime.go (about) 1 package migrations 2 3 import ( 4 "fmt" 5 6 "github.com/onflow/cadence/runtime" 7 "github.com/onflow/cadence/runtime/common" 8 "github.com/onflow/cadence/runtime/interpreter" 9 "github.com/onflow/cadence/runtime/stdlib" 10 "github.com/onflow/crypto/hash" 11 12 "github.com/onflow/flow-go/cmd/util/ledger/util" 13 "github.com/onflow/flow-go/cmd/util/ledger/util/registers" 14 "github.com/onflow/flow-go/fvm/environment" 15 "github.com/onflow/flow-go/fvm/evm" 16 evmStdlib "github.com/onflow/flow-go/fvm/evm/stdlib" 17 "github.com/onflow/flow-go/fvm/storage/state" 18 "github.com/onflow/flow-go/model/flow" 19 ) 20 21 type BasicMigrationRuntime struct { 22 TransactionState state.NestedTransactionPreparer 23 Storage *runtime.Storage 24 AccountsLedger *util.AccountsAtreeLedger 25 Accounts environment.Accounts 26 } 27 28 type InterpreterMigrationRuntime struct { 29 *BasicMigrationRuntime 30 Interpreter *interpreter.Interpreter 31 ContractAdditionHandler stdlib.AccountContractAdditionHandler 32 ContractNamesProvider stdlib.AccountContractNamesProvider 33 } 34 35 // InterpreterMigrationRuntimeConfig is used to configure the InterpreterMigrationRuntime. 36 // The code, contract names, and program loading functions can be nil, 37 // in which case program loading will be configured to use the default behavior, 38 // loading contracts from the given payloads. 39 // The listener function is optional and can be used to listen for program loading events. 40 type InterpreterMigrationRuntimeConfig struct { 41 GetCode util.GetContractCodeFunc 42 GetContractNames util.GetContractNamesFunc 43 GetOrLoadProgram util.GetOrLoadProgramFunc 44 GetOrLoadProgramListener util.GerOrLoadProgramListenerFunc 45 } 46 47 func (c InterpreterMigrationRuntimeConfig) NewRuntimeInterface( 48 transactionState state.NestedTransactionPreparer, 49 accounts environment.Accounts, 50 ) ( 51 runtime.Interface, 52 error, 53 ) { 54 55 getCodeFunc := func(location common.AddressLocation) ([]byte, error) { 56 // First, try to get the code from the provided function. 57 // If it is not provided, fall back to the default behavior, 58 // getting the code from the accounts. 59 60 getCodeFunc := c.GetCode 61 if getCodeFunc != nil { 62 code, err := getCodeFunc(location) 63 if err != nil || code != nil { 64 // If the code was found, or if an error occurred, then return. 65 return code, err 66 } 67 } 68 69 return accounts.GetContract( 70 location.Name, 71 flow.Address(location.Address), 72 ) 73 } 74 75 getContractNames := c.GetContractNames 76 if getContractNames == nil { 77 getContractNames = accounts.GetContractNames 78 } 79 80 getOrLoadProgram := c.GetOrLoadProgram 81 if getOrLoadProgram == nil { 82 var err error 83 getOrLoadProgram, err = util.NewProgramsGetOrLoadProgramFunc( 84 transactionState, 85 accounts, 86 ) 87 if err != nil { 88 return nil, err 89 } 90 } 91 92 return util.NewMigrationRuntimeInterface( 93 getCodeFunc, 94 getContractNames, 95 getOrLoadProgram, 96 c.GetOrLoadProgramListener, 97 ), nil 98 } 99 100 // NewBasicMigrationRuntime returns a basic runtime for migrations. 101 func NewBasicMigrationRuntime(regs registers.Registers) *BasicMigrationRuntime { 102 // Create a new transaction state with a dummy hasher 103 // because we do not need spock proofs for migrations. 104 transactionState := state.NewTransactionStateFromExecutionState( 105 state.NewExecutionStateWithSpockStateHasher( 106 registers.StorageSnapshot{ 107 Registers: regs, 108 }, 109 state.DefaultParameters(), 110 func() hash.Hasher { 111 return dummyHasher{} 112 }, 113 ), 114 ) 115 accounts := environment.NewAccounts(transactionState) 116 117 accountsAtreeLedger := util.NewAccountsAtreeLedger(accounts) 118 runtimeStorage := runtime.NewStorage(accountsAtreeLedger, nil) 119 120 return &BasicMigrationRuntime{ 121 TransactionState: transactionState, 122 Storage: runtimeStorage, 123 AccountsLedger: accountsAtreeLedger, 124 Accounts: accounts, 125 } 126 } 127 128 // NewInterpreterMigrationRuntime returns a runtime for migrations that need an interpreter. 129 func NewInterpreterMigrationRuntime( 130 regs registers.Registers, 131 chainID flow.ChainID, 132 config InterpreterMigrationRuntimeConfig, 133 ) ( 134 *InterpreterMigrationRuntime, 135 error, 136 ) { 137 basicMigrationRuntime := NewBasicMigrationRuntime(regs) 138 139 env := runtime.NewBaseInterpreterEnvironment(runtime.Config{ 140 AttachmentsEnabled: true, 141 }) 142 143 runtimeInterface, err := config.NewRuntimeInterface( 144 basicMigrationRuntime.TransactionState, 145 basicMigrationRuntime.Accounts, 146 ) 147 if err != nil { 148 return nil, fmt.Errorf("failed to create runtime interface: %w", err) 149 } 150 151 evmContractAccountAddress, err := evm.ContractAccountAddress(chainID) 152 if err != nil { 153 return nil, fmt.Errorf("failed to get EVM contract account address for chain %s: %w", chainID, err) 154 } 155 156 evmStdlib.SetupEnvironment(env, nil, evmContractAccountAddress) 157 158 env.Configure( 159 runtimeInterface, 160 runtime.NewCodesAndPrograms(), 161 basicMigrationRuntime.Storage, 162 nil, 163 ) 164 165 inter, err := interpreter.NewInterpreter( 166 nil, 167 nil, 168 env.InterpreterConfig, 169 ) 170 if err != nil { 171 return nil, err 172 } 173 174 return &InterpreterMigrationRuntime{ 175 BasicMigrationRuntime: basicMigrationRuntime, 176 Interpreter: inter, 177 ContractAdditionHandler: env, 178 ContractNamesProvider: env, 179 }, nil 180 } 181 182 type dummyHasher struct{} 183 184 func (d dummyHasher) Algorithm() hash.HashingAlgorithm { return hash.UnknownHashingAlgorithm } 185 func (d dummyHasher) Size() int { return 0 } 186 func (d dummyHasher) ComputeHash([]byte) hash.Hash { return nil } 187 func (d dummyHasher) Write([]byte) (int, error) { return 0, nil } 188 func (d dummyHasher) SumHash() hash.Hash { return nil } 189 func (d dummyHasher) Reset() {}