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

     1  package derived
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/onflow/cadence/runtime/common"
     7  	"github.com/onflow/cadence/runtime/interpreter"
     8  
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/onflow/flow-go/fvm/storage/state"
    12  	"github.com/onflow/flow-go/model/flow"
    13  )
    14  
    15  func TestDerivedChainData(t *testing.T) {
    16  	testBlockId := func(i byte) flow.Identifier {
    17  		id := flow.ZeroID
    18  		id[0] = i
    19  		return id
    20  	}
    21  
    22  	testLocation := func(hex string) common.AddressLocation {
    23  		return common.AddressLocation{
    24  			Address: common.MustBytesToAddress(flow.HexToAddress(hex).Bytes()),
    25  			Name:    hex,
    26  		}
    27  	}
    28  
    29  	programs, err := NewDerivedChainData(2)
    30  	require.NoError(t, err)
    31  
    32  	//
    33  	// Creating a DerivedBlockData from scratch
    34  	//
    35  
    36  	blockId1 := testBlockId(1)
    37  
    38  	block1 := programs.GetOrCreateDerivedBlockData(blockId1, flow.ZeroID)
    39  	require.NotNil(t, block1)
    40  
    41  	loc1 := testLocation("0a")
    42  	prog1 := &Program{
    43  		Program: &interpreter.Program{},
    44  	}
    45  
    46  	txn, err := block1.NewDerivedTransactionData(0, 0)
    47  	require.NoError(t, err)
    48  
    49  	txState := state.NewTransactionState(nil, state.DefaultParameters())
    50  
    51  	_, err = txn.GetOrComputeProgram(txState, loc1, newProgramLoader(
    52  		func(
    53  			txnState state.NestedTransactionPreparer,
    54  			key common.AddressLocation,
    55  		) (*Program, error) {
    56  			return prog1, nil
    57  		}))
    58  	require.NoError(t, err)
    59  	err = txn.Commit()
    60  	require.NoError(t, err)
    61  
    62  	entry := block1.programs.GetForTestingOnly(loc1)
    63  	require.NotNil(t, entry)
    64  	require.Same(t, prog1, entry.Value)
    65  
    66  	//
    67  	// Creating a DerivedBlockData from parent
    68  	//
    69  
    70  	blockId2 := testBlockId(2)
    71  
    72  	block2 := programs.GetOrCreateDerivedBlockData(blockId2, blockId1)
    73  	require.NotNil(t, block2)
    74  	require.NotSame(t, block1, block2)
    75  
    76  	loc2 := testLocation("0b")
    77  	prog2 := &Program{
    78  		Program: &interpreter.Program{},
    79  	}
    80  
    81  	txn, err = block2.NewDerivedTransactionData(0, 0)
    82  	require.NoError(t, err)
    83  
    84  	txState = state.NewTransactionState(nil, state.DefaultParameters())
    85  
    86  	_, err = txn.GetOrComputeProgram(txState, loc2, newProgramLoader(
    87  		func(
    88  			txnState state.NestedTransactionPreparer,
    89  			key common.AddressLocation,
    90  		) (*Program, error) {
    91  			return prog2, nil
    92  		}))
    93  	require.NoError(t, err)
    94  	err = txn.Commit()
    95  	require.NoError(t, err)
    96  
    97  	entry = block2.programs.GetForTestingOnly(loc1)
    98  	require.NotNil(t, entry)
    99  	require.Same(t, prog1, entry.Value)
   100  
   101  	entry = block2.programs.GetForTestingOnly(loc2)
   102  	require.NotNil(t, entry)
   103  	require.Same(t, prog2, entry.Value)
   104  
   105  	//
   106  	// Reuse exising DerivedBlockData in cache
   107  	//
   108  
   109  	foundBlock := programs.GetOrCreateDerivedBlockData(blockId1, flow.ZeroID)
   110  	require.Same(t, block1, foundBlock)
   111  
   112  	foundBlock = programs.Get(blockId1)
   113  	require.Same(t, block1, foundBlock)
   114  
   115  	entry = block1.programs.GetForTestingOnly(loc1)
   116  	require.NotNil(t, entry)
   117  	require.Same(t, prog1, entry.Value)
   118  
   119  	// writes to block2 did't poplute block1.
   120  	entry = block1.programs.GetForTestingOnly(loc2)
   121  	require.Nil(t, entry)
   122  
   123  	//
   124  	// Test eviction
   125  	//
   126  
   127  	blockId3 := testBlockId(3)
   128  
   129  	// block3 forces block1 to evict
   130  	block3 := programs.GetOrCreateDerivedBlockData(blockId3, blockId2)
   131  	require.NotNil(t, block3)
   132  	require.NotSame(t, block1, block3)
   133  	require.NotSame(t, block2, block3)
   134  
   135  	entry = block3.programs.GetForTestingOnly(loc1)
   136  	require.NotNil(t, entry)
   137  	require.Same(t, prog1, entry.Value)
   138  
   139  	entry = block3.programs.GetForTestingOnly(loc2)
   140  	require.NotNil(t, entry)
   141  	require.Same(t, prog2, entry.Value)
   142  
   143  	// block1 forces block2 to evict
   144  	foundBlock = programs.GetOrCreateDerivedBlockData(blockId1, flow.ZeroID)
   145  	require.NotSame(t, block1, foundBlock)
   146  
   147  	entry = foundBlock.programs.GetForTestingOnly(loc1)
   148  	require.Nil(t, entry)
   149  
   150  	entry = foundBlock.programs.GetForTestingOnly(loc2)
   151  	require.Nil(t, entry)
   152  
   153  	block1 = foundBlock
   154  
   155  	//
   156  	// Scripts don't poplute the cache
   157  	//
   158  
   159  	// Create from cached current block
   160  	scriptBlock := programs.NewDerivedBlockDataForScript(blockId3)
   161  	require.NotNil(t, scriptBlock)
   162  	require.NotSame(t, block2, scriptBlock)
   163  	require.NotSame(t, block3, scriptBlock)
   164  
   165  	entry = scriptBlock.programs.GetForTestingOnly(loc1)
   166  	require.NotNil(t, entry)
   167  	require.Same(t, prog1, entry.Value)
   168  
   169  	entry = scriptBlock.programs.GetForTestingOnly(loc2)
   170  	require.NotNil(t, entry)
   171  	require.Same(t, prog2, entry.Value)
   172  
   173  	foundBlock = programs.Get(blockId3)
   174  	require.Same(t, block3, foundBlock)
   175  
   176  	foundBlock = programs.Get(blockId1)
   177  	require.Same(t, block1, foundBlock)
   178  
   179  	foundBlock = programs.Get(blockId2)
   180  	require.Nil(t, foundBlock)
   181  }
   182  
   183  type programLoader struct {
   184  	f func(
   185  		txnState state.NestedTransactionPreparer,
   186  		key common.AddressLocation,
   187  	) (*Program, error)
   188  }
   189  
   190  var _ ValueComputer[common.AddressLocation, *Program] = &programLoader{}
   191  
   192  func newProgramLoader(
   193  	f func(
   194  		txnState state.NestedTransactionPreparer,
   195  		key common.AddressLocation,
   196  	) (*Program, error),
   197  ) *programLoader {
   198  	return &programLoader{
   199  		f: f,
   200  	}
   201  }
   202  
   203  func (p *programLoader) Compute(
   204  	txnState state.NestedTransactionPreparer,
   205  	key common.AddressLocation,
   206  ) (*Program, error) {
   207  	return p.f(txnState, key)
   208  }