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 }