github.com/onflow/flow-go@v0.33.17/fvm/storage/derived/derived_block_data.go (about)

     1  package derived
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/onflow/cadence/runtime/common"
     7  	"github.com/onflow/cadence/runtime/interpreter"
     8  
     9  	"github.com/onflow/flow-go/fvm/storage/logical"
    10  	"github.com/onflow/flow-go/fvm/storage/state"
    11  )
    12  
    13  type DerivedTransactionPreparer interface {
    14  	GetOrComputeProgram(
    15  		txState state.NestedTransactionPreparer,
    16  		addressLocation common.AddressLocation,
    17  		programComputer ValueComputer[common.AddressLocation, *Program],
    18  	) (
    19  		*Program,
    20  		error,
    21  	)
    22  	GetProgram(location common.AddressLocation) (*Program, bool)
    23  
    24  	GetMeterParamOverrides(
    25  		txnState state.NestedTransactionPreparer,
    26  		getMeterParamOverrides ValueComputer[struct{}, MeterParamOverrides],
    27  	) (
    28  		MeterParamOverrides,
    29  		error,
    30  	)
    31  
    32  	AddInvalidator(invalidator TransactionInvalidator)
    33  }
    34  
    35  type Program struct {
    36  	*interpreter.Program
    37  
    38  	Dependencies ProgramDependencies
    39  }
    40  
    41  // DerivedBlockData is a simple fork-aware OCC database for "caching" derived
    42  // data for a particular block.
    43  type DerivedBlockData struct {
    44  	programs *DerivedDataTable[common.AddressLocation, *Program]
    45  
    46  	meterParamOverrides *DerivedDataTable[struct{}, MeterParamOverrides]
    47  }
    48  
    49  // DerivedTransactionData is the derived data scratch space for a single
    50  // transaction.
    51  type DerivedTransactionData struct {
    52  	programs *TableTransaction[
    53  		common.AddressLocation,
    54  		*Program,
    55  	]
    56  
    57  	// There's only a single entry in this table.  For simplicity, we'll use
    58  	// struct{} as the entry's key.
    59  	meterParamOverrides *TableTransaction[struct{}, MeterParamOverrides]
    60  }
    61  
    62  func NewEmptyDerivedBlockData(
    63  	initialSnapshotTime logical.Time,
    64  ) *DerivedBlockData {
    65  	return &DerivedBlockData{
    66  		programs: NewEmptyTable[
    67  			common.AddressLocation,
    68  			*Program,
    69  		](initialSnapshotTime),
    70  		meterParamOverrides: NewEmptyTable[
    71  			struct{},
    72  			MeterParamOverrides,
    73  		](initialSnapshotTime),
    74  	}
    75  }
    76  
    77  func (block *DerivedBlockData) NewChildDerivedBlockData() *DerivedBlockData {
    78  	return &DerivedBlockData{
    79  		programs:            block.programs.NewChildTable(),
    80  		meterParamOverrides: block.meterParamOverrides.NewChildTable(),
    81  	}
    82  }
    83  
    84  func (block *DerivedBlockData) NewSnapshotReadDerivedTransactionData() *DerivedTransactionData {
    85  	txnPrograms := block.programs.NewSnapshotReadTableTransaction()
    86  
    87  	txnMeterParamOverrides := block.meterParamOverrides.NewSnapshotReadTableTransaction()
    88  
    89  	return &DerivedTransactionData{
    90  		programs:            txnPrograms,
    91  		meterParamOverrides: txnMeterParamOverrides,
    92  	}
    93  }
    94  
    95  func (block *DerivedBlockData) NewDerivedTransactionData(
    96  	snapshotTime logical.Time,
    97  	executionTime logical.Time,
    98  ) (
    99  	*DerivedTransactionData,
   100  	error,
   101  ) {
   102  	txnPrograms, err := block.programs.NewTableTransaction(
   103  		snapshotTime,
   104  		executionTime)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  
   109  	txnMeterParamOverrides, err := block.meterParamOverrides.NewTableTransaction(
   110  		snapshotTime,
   111  		executionTime)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	return &DerivedTransactionData{
   117  		programs:            txnPrograms,
   118  		meterParamOverrides: txnMeterParamOverrides,
   119  	}, nil
   120  }
   121  
   122  func (block *DerivedBlockData) NextTxIndexForTestingOnly() uint32 {
   123  	// NOTE: We can use next tx index from any table since they are identical.
   124  	return block.programs.NextTxIndexForTestingOnly()
   125  }
   126  
   127  func (block *DerivedBlockData) GetProgramForTestingOnly(
   128  	addressLocation common.AddressLocation,
   129  ) *invalidatableEntry[*Program] {
   130  	return block.programs.GetForTestingOnly(addressLocation)
   131  }
   132  
   133  // CachedPrograms returns the number of programs cached.
   134  // Note: this should only be called after calling commit, otherwise
   135  // the count will contain invalidated entries.
   136  func (block *DerivedBlockData) CachedPrograms() int {
   137  	return len(block.programs.items)
   138  }
   139  
   140  func (transaction *DerivedTransactionData) GetOrComputeProgram(
   141  	txState state.NestedTransactionPreparer,
   142  	addressLocation common.AddressLocation,
   143  	programComputer ValueComputer[common.AddressLocation, *Program],
   144  ) (
   145  	*Program,
   146  	error,
   147  ) {
   148  	return transaction.programs.GetOrCompute(
   149  		txState,
   150  		addressLocation,
   151  		programComputer)
   152  }
   153  
   154  // GetProgram returns the program for the given address location.
   155  // This does NOT apply reads/metering to any nested transaction.
   156  // Use with caution!
   157  func (transaction *DerivedTransactionData) GetProgram(
   158  	location common.AddressLocation,
   159  ) (
   160  	*Program,
   161  	bool,
   162  ) {
   163  	program, _, ok := transaction.programs.get(location)
   164  	return program, ok
   165  }
   166  
   167  func (transaction *DerivedTransactionData) AddInvalidator(
   168  	invalidator TransactionInvalidator,
   169  ) {
   170  	if invalidator == nil {
   171  		return
   172  	}
   173  
   174  	transaction.programs.AddInvalidator(invalidator.ProgramInvalidator())
   175  	transaction.meterParamOverrides.AddInvalidator(
   176  		invalidator.MeterParamOverridesInvalidator())
   177  }
   178  
   179  func (transaction *DerivedTransactionData) GetMeterParamOverrides(
   180  	txnState state.NestedTransactionPreparer,
   181  	getMeterParamOverrides ValueComputer[struct{}, MeterParamOverrides],
   182  ) (
   183  	MeterParamOverrides,
   184  	error,
   185  ) {
   186  	return transaction.meterParamOverrides.GetOrCompute(
   187  		txnState,
   188  		struct{}{},
   189  		getMeterParamOverrides)
   190  }
   191  
   192  func (transaction *DerivedTransactionData) Validate() error {
   193  	err := transaction.programs.Validate()
   194  	if err != nil {
   195  		return fmt.Errorf("programs validate failed: %w", err)
   196  	}
   197  
   198  	err = transaction.meterParamOverrides.Validate()
   199  	if err != nil {
   200  		return fmt.Errorf("meter param overrides validate failed: %w", err)
   201  	}
   202  
   203  	return nil
   204  }
   205  
   206  func (transaction *DerivedTransactionData) Commit() error {
   207  	err := transaction.programs.Commit()
   208  	if err != nil {
   209  		return fmt.Errorf("programs commit failed: %w", err)
   210  	}
   211  
   212  	err = transaction.meterParamOverrides.Commit()
   213  	if err != nil {
   214  		return fmt.Errorf("meter param overrides commit failed: %w", err)
   215  	}
   216  
   217  	return nil
   218  }