github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/storage/block_database.go (about)

     1  package storage
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/onflow/flow-go/fvm/storage/derived"
     7  	"github.com/onflow/flow-go/fvm/storage/logical"
     8  	"github.com/onflow/flow-go/fvm/storage/primary"
     9  	"github.com/onflow/flow-go/fvm/storage/snapshot"
    10  	"github.com/onflow/flow-go/fvm/storage/state"
    11  )
    12  
    13  // BlockDatabase packages the primary index (BlockData) and secondary indices
    14  // (DerivedBlockData) into a single database via 2PC.
    15  type BlockDatabase struct {
    16  	*primary.BlockData
    17  	*derived.DerivedBlockData
    18  }
    19  
    20  type transaction struct {
    21  	*primary.TransactionData
    22  	*derived.DerivedTransactionData
    23  }
    24  
    25  // NOTE: storageSnapshot must be thread safe.
    26  func NewBlockDatabase(
    27  	storageSnapshot snapshot.StorageSnapshot,
    28  	snapshotTime logical.Time,
    29  	cachedDerivedBlockData *derived.DerivedBlockData, // optional
    30  ) *BlockDatabase {
    31  	derivedBlockData := cachedDerivedBlockData
    32  	if derivedBlockData == nil {
    33  		derivedBlockData = derived.NewEmptyDerivedBlockData(snapshotTime)
    34  	}
    35  
    36  	return &BlockDatabase{
    37  		BlockData:        primary.NewBlockData(storageSnapshot, snapshotTime),
    38  		DerivedBlockData: derivedBlockData,
    39  	}
    40  }
    41  
    42  func (database *BlockDatabase) NewTransaction(
    43  	executionTime logical.Time,
    44  	parameters state.StateParameters,
    45  ) (
    46  	Transaction,
    47  	error,
    48  ) {
    49  	primaryTxn, err := database.BlockData.NewTransactionData(
    50  		executionTime,
    51  		parameters)
    52  	if err != nil {
    53  		return nil, fmt.Errorf("failed to create primary transaction: %w", err)
    54  	}
    55  
    56  	derivedTxn, err := database.DerivedBlockData.NewDerivedTransactionData(
    57  		primaryTxn.SnapshotTime(),
    58  		executionTime)
    59  	if err != nil {
    60  		return nil, fmt.Errorf("failed to create dervied transaction: %w", err)
    61  	}
    62  
    63  	return &transaction{
    64  		TransactionData:        primaryTxn,
    65  		DerivedTransactionData: derivedTxn,
    66  	}, nil
    67  }
    68  
    69  // NewSnapshotReadTransaction creates a new readonly transaction.
    70  func (database *BlockDatabase) NewSnapshotReadTransaction(
    71  	parameters state.StateParameters,
    72  ) Transaction {
    73  
    74  	return &transaction{
    75  		TransactionData: database.BlockData.
    76  			NewSnapshotReadTransactionData(parameters),
    77  		DerivedTransactionData: database.DerivedBlockData.
    78  			NewSnapshotReadDerivedTransactionData(),
    79  	}
    80  }
    81  
    82  // NewCachingSnapshotReadTransaction creates a new readonly transaction that allows writing to the
    83  // derived transaction data table.
    84  func (database *BlockDatabase) NewCachingSnapshotReadTransaction(
    85  	parameters state.StateParameters,
    86  ) (Transaction, error) {
    87  	return &transaction{
    88  		TransactionData:        database.BlockData.NewCachingSnapshotReadTransactionData(parameters),
    89  		DerivedTransactionData: database.DerivedBlockData.NewCachingSnapshotReadDerivedTransactionData(),
    90  	}, nil
    91  }
    92  
    93  func (txn *transaction) Validate() error {
    94  	err := txn.DerivedTransactionData.Validate()
    95  	if err != nil {
    96  		return fmt.Errorf("derived indices validate failed: %w", err)
    97  	}
    98  
    99  	// NOTE: Since the primary txn's SnapshotTime() is exposed to the user,
   100  	// the primary txn should be validated last to prevent primary txn'
   101  	// snapshot time advancement in case of derived txn validation failure.
   102  	err = txn.TransactionData.Validate()
   103  	if err != nil {
   104  		return fmt.Errorf("primary index validate failed: %w", err)
   105  	}
   106  
   107  	return nil
   108  }
   109  
   110  func (txn *transaction) Finalize() error {
   111  	// NOTE: DerivedTransactionData does not need to be finalized.
   112  	return txn.TransactionData.Finalize()
   113  }
   114  
   115  func (txn *transaction) Commit() (*snapshot.ExecutionSnapshot, error) {
   116  	err := txn.DerivedTransactionData.Commit()
   117  	if err != nil {
   118  		return nil, fmt.Errorf("derived indices commit failed: %w", err)
   119  	}
   120  
   121  	// NOTE: Since the primary txn's SnapshotTime() is exposed to the user,
   122  	// the primary txn should be committed last to prevent primary txn'
   123  	// snapshot time advancement in case of derived txn commit failure.
   124  	executionSnapshot, err := txn.TransactionData.Commit()
   125  	if err != nil {
   126  		return nil, fmt.Errorf("primary index commit failed: %w", err)
   127  	}
   128  
   129  	return executionSnapshot, nil
   130  }