github.com/koko1123/flow-go-1@v0.29.6/engine/execution/computation/programs_test.go (about) 1 package computation 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 "time" 8 9 "github.com/ipfs/go-datastore" 10 dssync "github.com/ipfs/go-datastore/sync" 11 blockstore "github.com/ipfs/go-ipfs-blockstore" 12 "github.com/onflow/cadence" 13 jsoncdc "github.com/onflow/cadence/encoding/json" 14 "github.com/rs/zerolog" 15 "github.com/stretchr/testify/assert" 16 "github.com/stretchr/testify/mock" 17 "github.com/stretchr/testify/require" 18 19 "github.com/koko1123/flow-go-1/engine/execution" 20 "github.com/koko1123/flow-go-1/engine/execution/computation/committer" 21 "github.com/koko1123/flow-go-1/engine/execution/computation/computer" 22 "github.com/koko1123/flow-go-1/engine/execution/state/delta" 23 "github.com/koko1123/flow-go-1/engine/execution/testutil" 24 "github.com/koko1123/flow-go-1/fvm" 25 "github.com/koko1123/flow-go-1/fvm/derived" 26 "github.com/koko1123/flow-go-1/fvm/state" 27 "github.com/koko1123/flow-go-1/model/flow" 28 "github.com/koko1123/flow-go-1/module/executiondatasync/execution_data" 29 "github.com/koko1123/flow-go-1/module/executiondatasync/provider" 30 mocktracker "github.com/koko1123/flow-go-1/module/executiondatasync/tracker/mock" 31 "github.com/koko1123/flow-go-1/module/mempool/entity" 32 "github.com/koko1123/flow-go-1/module/metrics" 33 module "github.com/koko1123/flow-go-1/module/mock" 34 requesterunit "github.com/koko1123/flow-go-1/module/state_synchronization/requester/unittest" 35 "github.com/koko1123/flow-go-1/module/trace" 36 "github.com/koko1123/flow-go-1/utils/unittest" 37 ) 38 39 func TestPrograms_TestContractUpdates(t *testing.T) { 40 chain := flow.Mainnet.Chain() 41 vm := fvm.NewVirtualMachine() 42 execCtx := fvm.NewContext(fvm.WithChain(chain)) 43 44 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 45 require.NoError(t, err) 46 ledger := testutil.RootBootstrappedLedger(vm, execCtx) 47 accounts, err := testutil.CreateAccounts( 48 vm, 49 ledger, 50 derived.NewEmptyDerivedBlockData(), 51 privateKeys, 52 chain) 53 require.NoError(t, err) 54 55 // setup transactions 56 account := accounts[0] 57 privKey := privateKeys[0] 58 // tx1 deploys contract version 1 59 tx1 := testutil.DeployEventContractTransaction(account, chain, 1) 60 prepareTx(t, tx1, account, privKey, 0, chain) 61 62 // tx2 calls the method of the contract (version 1) 63 tx2 := testutil.CreateEmitEventTransaction(account, account) 64 prepareTx(t, tx2, account, privKey, 1, chain) 65 66 // tx3 updates the contract to version 2 67 tx3 := testutil.UpdateEventContractTransaction(account, chain, 2) 68 prepareTx(t, tx3, account, privKey, 2, chain) 69 70 // tx4 calls the method of the contract (version 2) 71 tx4 := testutil.CreateEmitEventTransaction(account, account) 72 prepareTx(t, tx4, account, privKey, 3, chain) 73 74 // tx5 updates the contract to version 3 but fails (no env signature of service account) 75 tx5 := testutil.UnauthorizedDeployEventContractTransaction(account, chain, 3) 76 tx5.SetProposalKey(account, 0, 4).SetPayer(account) 77 err = testutil.SignEnvelope(tx5, account, privKey) 78 require.NoError(t, err) 79 80 // tx6 calls the method of the contract (version 2 expected) 81 tx6 := testutil.CreateEmitEventTransaction(account, account) 82 prepareTx(t, tx6, account, privKey, 5, chain) 83 84 transactions := []*flow.TransactionBody{tx1, tx2, tx3, tx4, tx5, tx6} 85 86 col := flow.Collection{Transactions: transactions} 87 88 guarantee := flow.CollectionGuarantee{ 89 CollectionID: col.ID(), 90 Signature: nil, 91 } 92 93 block := flow.Block{ 94 Header: &flow.Header{ 95 View: 26, 96 }, 97 Payload: &flow.Payload{ 98 Guarantees: []*flow.CollectionGuarantee{&guarantee}, 99 }, 100 } 101 102 executableBlock := &entity.ExecutableBlock{ 103 Block: &block, 104 CompleteCollections: map[flow.Identifier]*entity.CompleteCollection{ 105 guarantee.ID(): { 106 Guarantee: &guarantee, 107 Transactions: transactions, 108 }, 109 }, 110 StartState: unittest.StateCommitmentPointerFixture(), 111 } 112 113 me := new(module.Local) 114 me.On("NodeID").Return(flow.ZeroID) 115 me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything). 116 Return(nil, nil) 117 118 bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore()))) 119 trackerStorage := mocktracker.NewMockStorage() 120 121 prov := provider.NewProvider( 122 zerolog.Nop(), 123 metrics.NewNoopCollector(), 124 execution_data.DefaultSerializer, 125 bservice, 126 trackerStorage, 127 ) 128 129 blockComputer, err := computer.NewBlockComputer( 130 vm, 131 execCtx, 132 metrics.NewNoopCollector(), 133 trace.NewNoopTracer(), 134 zerolog.Nop(), 135 committer.NewNoopViewCommitter(), 136 me, 137 prov) 138 require.NoError(t, err) 139 140 derivedChainData, err := derived.NewDerivedChainData(10) 141 require.NoError(t, err) 142 143 engine := &Manager{ 144 blockComputer: blockComputer, 145 tracer: trace.NewNoopTracer(), 146 me: me, 147 derivedChainData: derivedChainData, 148 } 149 150 view := delta.NewView(ledger.Get) 151 blockView := view.NewChild() 152 153 returnedComputationResult, err := engine.ComputeBlock(context.Background(), executableBlock, blockView) 154 require.NoError(t, err) 155 156 require.Len(t, returnedComputationResult.Events, 2) // 1 collection + 1 system chunk 157 158 // first event should be contract deployed 159 assert.EqualValues(t, "flow.AccountContractAdded", returnedComputationResult.Events[0][0].Type) 160 161 // second event should have a value of 1 (since is calling version 1 of contract) 162 hasValidEventValue(t, returnedComputationResult.Events[0][1], 1) 163 164 // third event should be contract updated 165 assert.EqualValues(t, "flow.AccountContractUpdated", returnedComputationResult.Events[0][2].Type) 166 167 // 4th event should have a value of 2 (since is calling version 2 of contract) 168 hasValidEventValue(t, returnedComputationResult.Events[0][3], 2) 169 170 // 5th event should have a value of 2 (since is calling version 2 of contract) 171 hasValidEventValue(t, returnedComputationResult.Events[0][4], 2) 172 } 173 174 type blockProvider struct { 175 blocks map[uint64]*flow.Block 176 } 177 178 func (b blockProvider) ByHeightFrom(height uint64, _ *flow.Header) (*flow.Header, error) { 179 block, has := b.blocks[height] 180 if has { 181 return block.Header, nil 182 } 183 return nil, fmt.Errorf("block for height (%d) is not available", height) 184 } 185 186 // TestPrograms_TestBlockForks tests the functionality of 187 // derivedChainData under contract deployment and contract updates on 188 // different block forks 189 // 190 // block structure and operations 191 // Block1 (empty block) 192 // 193 // -> Block11 (deploy contract v1) 194 // -> Block111 (emit event - version should be 1) and (update contract to v3) 195 // -> Block1111 (emit event - version should be 3) 196 // -> Block112 (emit event - version should be 1) and (update contract to v4) 197 // -> Block1121 (emit event - version should be 4) 198 // -> Block12 (deploy contract v2) 199 // -> Block121 (emit event - version should be 2) 200 // -> Block1211 (emit event - version should be 2) 201 func TestPrograms_TestBlockForks(t *testing.T) { 202 block := unittest.BlockFixture() 203 chain := flow.Emulator.Chain() 204 vm := fvm.NewVirtualMachine() 205 execCtx := fvm.NewContext( 206 fvm.WithBlockHeader(block.Header), 207 fvm.WithBlocks(blockProvider{map[uint64]*flow.Block{0: &block}}), 208 fvm.WithChain(chain)) 209 210 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 211 require.NoError(t, err) 212 ledger := testutil.RootBootstrappedLedger(vm, execCtx) 213 accounts, err := testutil.CreateAccounts( 214 vm, 215 ledger, 216 derived.NewEmptyDerivedBlockData(), 217 privateKeys, 218 chain) 219 require.NoError(t, err) 220 221 account := accounts[0] 222 privKey := privateKeys[0] 223 224 me := new(module.Local) 225 me.On("NodeID").Return(flow.ZeroID) 226 me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything). 227 Return(nil, nil) 228 229 bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore()))) 230 trackerStorage := mocktracker.NewMockStorage() 231 232 prov := provider.NewProvider( 233 zerolog.Nop(), 234 metrics.NewNoopCollector(), 235 execution_data.DefaultSerializer, 236 bservice, 237 trackerStorage, 238 ) 239 240 blockComputer, err := computer.NewBlockComputer( 241 vm, 242 execCtx, 243 metrics.NewNoopCollector(), 244 trace.NewNoopTracer(), 245 zerolog.Nop(), 246 committer.NewNoopViewCommitter(), 247 me, 248 prov) 249 require.NoError(t, err) 250 251 derivedChainData, err := derived.NewDerivedChainData(10) 252 require.NoError(t, err) 253 254 engine := &Manager{ 255 blockComputer: blockComputer, 256 tracer: trace.NewNoopTracer(), 257 me: me, 258 derivedChainData: derivedChainData, 259 } 260 261 view := delta.NewView(ledger.Get) 262 263 var ( 264 res *execution.ComputationResult 265 266 block1, block11, block111, block112, block1121, 267 block1111, block12, block121, block1211 *flow.Block 268 269 block1View, block11View, block111View, block112View, block1121View, 270 block1111View, block12View, block121View, block1211View state.View 271 ) 272 273 t.Run("executing block1 (no collection)", func(t *testing.T) { 274 block1 = &flow.Block{ 275 Header: &flow.Header{ 276 View: 1, 277 }, 278 Payload: &flow.Payload{ 279 Guarantees: []*flow.CollectionGuarantee{}, 280 }, 281 } 282 block1View = view.NewChild() 283 executableBlock := &entity.ExecutableBlock{ 284 Block: block1, 285 StartState: unittest.StateCommitmentPointerFixture(), 286 } 287 _, err := engine.ComputeBlock(context.Background(), executableBlock, block1View) 288 require.NoError(t, err) 289 }) 290 291 t.Run("executing block11 (deploys contract version 1)", func(t *testing.T) { 292 block11tx1 := testutil.DeployEventContractTransaction(account, chain, 1) 293 prepareTx(t, block11tx1, account, privKey, 0, chain) 294 295 txs11 := []*flow.TransactionBody{block11tx1} 296 col11 := flow.Collection{Transactions: txs11} 297 block11View = block1View.NewChild() 298 block11, res = createTestBlockAndRun(t, engine, block1, col11, block11View) 299 // cache should include value for this block 300 require.NotNil(t, derivedChainData.Get(block11.ID())) 301 // 1st event should be contract deployed 302 assert.EqualValues(t, "flow.AccountContractAdded", res.Events[0][0].Type) 303 }) 304 305 t.Run("executing block111 (emit event (expected v1), update contract to v3)", func(t *testing.T) { 306 block111ExpectedValue := 1 307 // emit event 308 block111tx1 := testutil.CreateEmitEventTransaction(account, account) 309 prepareTx(t, block111tx1, account, privKey, 1, chain) 310 311 // update contract version 3 312 block111tx2 := testutil.UpdateEventContractTransaction(account, chain, 3) 313 prepareTx(t, block111tx2, account, privKey, 2, chain) 314 315 col111 := flow.Collection{Transactions: []*flow.TransactionBody{block111tx1, block111tx2}} 316 block111View = block11View.NewChild() 317 block111, res = createTestBlockAndRun(t, engine, block11, col111, block111View) 318 // cache should include a program for this block 319 require.NotNil(t, derivedChainData.Get(block111.ID())) 320 321 require.Len(t, res.Events, 2) 322 323 // 1st event 324 hasValidEventValue(t, res.Events[0][0], block111ExpectedValue) 325 // second event should be contract deployed 326 assert.EqualValues(t, "flow.AccountContractUpdated", res.Events[0][1].Type) 327 }) 328 329 t.Run("executing block1111 (emit event (expected v3))", func(t *testing.T) { 330 block1111ExpectedValue := 3 331 block1111tx1 := testutil.CreateEmitEventTransaction(account, account) 332 prepareTx(t, block1111tx1, account, privKey, 3, chain) 333 334 col1111 := flow.Collection{Transactions: []*flow.TransactionBody{block1111tx1}} 335 block1111View = block111View.NewChild() 336 block1111, res = createTestBlockAndRun(t, engine, block111, col1111, block1111View) 337 // cache should include a program for this block 338 require.NotNil(t, derivedChainData.Get(block1111.ID())) 339 340 require.Len(t, res.Events, 2) 341 342 // 1st event 343 hasValidEventValue(t, res.Events[0][0], block1111ExpectedValue) 344 }) 345 346 t.Run("executing block112 (emit event (expected v1))", func(t *testing.T) { 347 block112ExpectedValue := 1 348 block112tx1 := testutil.CreateEmitEventTransaction(account, account) 349 prepareTx(t, block112tx1, account, privKey, 1, chain) 350 351 // update contract version 4 352 block112tx2 := testutil.UpdateEventContractTransaction(account, chain, 4) 353 prepareTx(t, block112tx2, account, privKey, 2, chain) 354 355 col112 := flow.Collection{Transactions: []*flow.TransactionBody{block112tx1, block112tx2}} 356 block112View = block11View.NewChild() 357 block112, res = createTestBlockAndRun(t, engine, block11, col112, block112View) 358 // cache should include a program for this block 359 require.NotNil(t, derivedChainData.Get(block112.ID())) 360 361 require.Len(t, res.Events, 2) 362 363 // 1st event 364 hasValidEventValue(t, res.Events[0][0], block112ExpectedValue) 365 // second event should be contract deployed 366 assert.EqualValues(t, "flow.AccountContractUpdated", res.Events[0][1].Type) 367 368 }) 369 t.Run("executing block1121 (emit event (expected v4))", func(t *testing.T) { 370 block1121ExpectedValue := 4 371 block1121tx1 := testutil.CreateEmitEventTransaction(account, account) 372 prepareTx(t, block1121tx1, account, privKey, 3, chain) 373 374 col1121 := flow.Collection{Transactions: []*flow.TransactionBody{block1121tx1}} 375 block1121View = block112View.NewChild() 376 block1121, res = createTestBlockAndRun(t, engine, block112, col1121, block1121View) 377 // cache should include a program for this block 378 require.NotNil(t, derivedChainData.Get(block1121.ID())) 379 380 require.Len(t, res.Events, 2) 381 382 // 1st event 383 hasValidEventValue(t, res.Events[0][0], block1121ExpectedValue) 384 385 }) 386 t.Run("executing block12 (deploys contract V2)", func(t *testing.T) { 387 388 block12tx1 := testutil.DeployEventContractTransaction(account, chain, 2) 389 prepareTx(t, block12tx1, account, privKey, 0, chain) 390 391 col12 := flow.Collection{Transactions: []*flow.TransactionBody{block12tx1}} 392 block12View = block1View.NewChild() 393 block12, res = createTestBlockAndRun(t, engine, block1, col12, block12View) 394 // cache should include a program for this block 395 require.NotNil(t, derivedChainData.Get(block12.ID())) 396 397 require.Len(t, res.Events, 2) 398 399 assert.EqualValues(t, "flow.AccountContractAdded", res.Events[0][0].Type) 400 }) 401 t.Run("executing block121 (emit event (expected V2)", func(t *testing.T) { 402 block121ExpectedValue := 2 403 block121tx1 := testutil.CreateEmitEventTransaction(account, account) 404 prepareTx(t, block121tx1, account, privKey, 1, chain) 405 406 col121 := flow.Collection{Transactions: []*flow.TransactionBody{block121tx1}} 407 block121View = block12View.NewChild() 408 block121, res = createTestBlockAndRun(t, engine, block12, col121, block121View) 409 // cache should include a program for this block 410 require.NotNil(t, derivedChainData.Get(block121.ID())) 411 412 require.Len(t, res.Events, 2) 413 414 // 1st event 415 hasValidEventValue(t, res.Events[0][0], block121ExpectedValue) 416 }) 417 t.Run("executing Block1211 (emit event (expected V2)", func(t *testing.T) { 418 block1211ExpectedValue := 2 419 block1211tx1 := testutil.CreateEmitEventTransaction(account, account) 420 prepareTx(t, block1211tx1, account, privKey, 2, chain) 421 422 col1211 := flow.Collection{Transactions: []*flow.TransactionBody{block1211tx1}} 423 block1211View = block121View.NewChild() 424 block1211, res = createTestBlockAndRun(t, engine, block121, col1211, block1211View) 425 // cache should include a program for this block 426 require.NotNil(t, derivedChainData.Get(block1211.ID())) 427 // had no change so cache should be equal to parent 428 require.Equal(t, derivedChainData.Get(block121.ID()), derivedChainData.Get(block1211.ID())) 429 430 require.Len(t, res.Events, 2) 431 432 // 1st event 433 hasValidEventValue(t, res.Events[0][0], block1211ExpectedValue) 434 }) 435 436 } 437 438 func createTestBlockAndRun(t *testing.T, engine *Manager, parentBlock *flow.Block, col flow.Collection, view state.View) (*flow.Block, *execution.ComputationResult) { 439 guarantee := flow.CollectionGuarantee{ 440 CollectionID: col.ID(), 441 Signature: nil, 442 } 443 444 block := &flow.Block{ 445 Header: &flow.Header{ 446 ParentID: parentBlock.ID(), 447 View: parentBlock.Header.Height + 1, 448 Timestamp: time.Now(), 449 }, 450 Payload: &flow.Payload{ 451 Guarantees: []*flow.CollectionGuarantee{&guarantee}, 452 }, 453 } 454 455 executableBlock := &entity.ExecutableBlock{ 456 Block: block, 457 CompleteCollections: map[flow.Identifier]*entity.CompleteCollection{ 458 guarantee.ID(): { 459 Guarantee: &guarantee, 460 Transactions: col.Transactions, 461 }, 462 }, 463 StartState: unittest.StateCommitmentPointerFixture(), 464 } 465 returnedComputationResult, err := engine.ComputeBlock(context.Background(), executableBlock, view) 466 require.NoError(t, err) 467 468 for _, txResult := range returnedComputationResult.TransactionResults { 469 require.Empty(t, txResult.ErrorMessage) 470 } 471 472 return block, returnedComputationResult 473 } 474 475 func prepareTx(t *testing.T, 476 tx *flow.TransactionBody, 477 account flow.Address, 478 privKey flow.AccountPrivateKey, 479 seqNumber uint64, 480 chain flow.Chain) { 481 tx.SetProposalKey(account, 0, seqNumber). 482 SetPayer(chain.ServiceAddress()) 483 err := testutil.SignPayload(tx, account, privKey) 484 require.NoError(t, err) 485 err = testutil.SignEnvelope(tx, chain.ServiceAddress(), unittest.ServiceAccountPrivateKey) 486 require.NoError(t, err) 487 } 488 489 func hasValidEventValue(t *testing.T, event flow.Event, value int) { 490 data, err := jsoncdc.Decode(nil, event.Payload) 491 require.NoError(t, err) 492 assert.Equal(t, int16(value), data.(cadence.Event).Fields[0].ToGoValue()) 493 }