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()                           {}