github.com/MetalBlockchain/metalgo@v1.11.9/vms/avm/block/builder/builder_test.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package builder 5 6 import ( 7 "context" 8 "errors" 9 "testing" 10 "time" 11 12 "github.com/prometheus/client_golang/prometheus" 13 "github.com/stretchr/testify/require" 14 "go.uber.org/mock/gomock" 15 16 "github.com/MetalBlockchain/metalgo/codec" 17 "github.com/MetalBlockchain/metalgo/database/memdb" 18 "github.com/MetalBlockchain/metalgo/database/versiondb" 19 "github.com/MetalBlockchain/metalgo/ids" 20 "github.com/MetalBlockchain/metalgo/snow" 21 "github.com/MetalBlockchain/metalgo/snow/consensus/snowman" 22 "github.com/MetalBlockchain/metalgo/snow/engine/common" 23 "github.com/MetalBlockchain/metalgo/utils/constants" 24 "github.com/MetalBlockchain/metalgo/utils/crypto/secp256k1" 25 "github.com/MetalBlockchain/metalgo/utils/logging" 26 "github.com/MetalBlockchain/metalgo/utils/timer/mockable" 27 "github.com/MetalBlockchain/metalgo/vms/avm/block" 28 "github.com/MetalBlockchain/metalgo/vms/avm/fxs" 29 "github.com/MetalBlockchain/metalgo/vms/avm/metrics" 30 "github.com/MetalBlockchain/metalgo/vms/avm/state" 31 "github.com/MetalBlockchain/metalgo/vms/avm/txs" 32 "github.com/MetalBlockchain/metalgo/vms/avm/txs/mempool" 33 "github.com/MetalBlockchain/metalgo/vms/components/avax" 34 "github.com/MetalBlockchain/metalgo/vms/secp256k1fx" 35 36 blkexecutor "github.com/MetalBlockchain/metalgo/vms/avm/block/executor" 37 txexecutor "github.com/MetalBlockchain/metalgo/vms/avm/txs/executor" 38 ) 39 40 const trackChecksums = false 41 42 var ( 43 errTest = errors.New("test error") 44 chainID = ids.GenerateTestID() 45 keys = secp256k1.TestKeys() 46 ) 47 48 func TestBuilderBuildBlock(t *testing.T) { 49 type test struct { 50 name string 51 builderFunc func(*gomock.Controller) Builder 52 expectedErr error 53 } 54 55 tests := []test{ 56 { 57 name: "can't get stateless block", 58 builderFunc: func(ctrl *gomock.Controller) Builder { 59 preferredID := ids.GenerateTestID() 60 manager := blkexecutor.NewMockManager(ctrl) 61 manager.EXPECT().Preferred().Return(preferredID) 62 manager.EXPECT().GetStatelessBlock(preferredID).Return(nil, errTest) 63 64 mempool := mempool.NewMockMempool(ctrl) 65 mempool.EXPECT().RequestBuildBlock() 66 67 return New( 68 &txexecutor.Backend{ 69 Ctx: &snow.Context{ 70 Log: logging.NoLog{}, 71 }, 72 }, 73 manager, 74 &mockable.Clock{}, 75 mempool, 76 ) 77 }, 78 expectedErr: errTest, 79 }, 80 { 81 name: "can't get preferred diff", 82 builderFunc: func(ctrl *gomock.Controller) Builder { 83 preferredID := ids.GenerateTestID() 84 preferredHeight := uint64(1337) 85 preferredTimestamp := time.Now() 86 preferredBlock := block.NewMockBlock(ctrl) 87 preferredBlock.EXPECT().Height().Return(preferredHeight) 88 preferredBlock.EXPECT().Timestamp().Return(preferredTimestamp) 89 90 manager := blkexecutor.NewMockManager(ctrl) 91 manager.EXPECT().Preferred().Return(preferredID) 92 manager.EXPECT().GetStatelessBlock(preferredID).Return(preferredBlock, nil) 93 manager.EXPECT().GetState(preferredID).Return(nil, false) 94 95 mempool := mempool.NewMockMempool(ctrl) 96 mempool.EXPECT().RequestBuildBlock() 97 98 return New( 99 &txexecutor.Backend{ 100 Ctx: &snow.Context{ 101 Log: logging.NoLog{}, 102 }, 103 }, 104 manager, 105 &mockable.Clock{}, 106 mempool, 107 ) 108 }, 109 expectedErr: state.ErrMissingParentState, 110 }, 111 { 112 name: "tx fails semantic verification", 113 builderFunc: func(ctrl *gomock.Controller) Builder { 114 preferredID := ids.GenerateTestID() 115 preferredHeight := uint64(1337) 116 preferredTimestamp := time.Now() 117 preferredBlock := block.NewMockBlock(ctrl) 118 preferredBlock.EXPECT().Height().Return(preferredHeight) 119 preferredBlock.EXPECT().Timestamp().Return(preferredTimestamp) 120 121 preferredState := state.NewMockChain(ctrl) 122 preferredState.EXPECT().GetLastAccepted().Return(preferredID) 123 preferredState.EXPECT().GetTimestamp().Return(preferredTimestamp) 124 125 manager := blkexecutor.NewMockManager(ctrl) 126 manager.EXPECT().Preferred().Return(preferredID) 127 manager.EXPECT().GetStatelessBlock(preferredID).Return(preferredBlock, nil) 128 manager.EXPECT().GetState(preferredID).Return(preferredState, true) 129 130 unsignedTx := txs.NewMockUnsignedTx(ctrl) 131 unsignedTx.EXPECT().Visit(gomock.Any()).Return(errTest) // Fail semantic verification 132 tx := &txs.Tx{Unsigned: unsignedTx} 133 134 mempool := mempool.NewMockMempool(ctrl) 135 mempool.EXPECT().Peek().Return(tx, true) 136 mempool.EXPECT().Remove([]*txs.Tx{tx}) 137 mempool.EXPECT().MarkDropped(tx.ID(), errTest) 138 // Second loop iteration 139 mempool.EXPECT().Peek().Return(nil, false) 140 mempool.EXPECT().RequestBuildBlock() 141 142 return New( 143 &txexecutor.Backend{ 144 Ctx: &snow.Context{ 145 Log: logging.NoLog{}, 146 }, 147 }, 148 manager, 149 &mockable.Clock{}, 150 mempool, 151 ) 152 }, 153 expectedErr: ErrNoTransactions, // The only tx was invalid 154 }, 155 { 156 name: "tx fails execution", 157 builderFunc: func(ctrl *gomock.Controller) Builder { 158 preferredID := ids.GenerateTestID() 159 preferredHeight := uint64(1337) 160 preferredTimestamp := time.Now() 161 preferredBlock := block.NewMockBlock(ctrl) 162 preferredBlock.EXPECT().Height().Return(preferredHeight) 163 preferredBlock.EXPECT().Timestamp().Return(preferredTimestamp) 164 165 preferredState := state.NewMockChain(ctrl) 166 preferredState.EXPECT().GetLastAccepted().Return(preferredID) 167 preferredState.EXPECT().GetTimestamp().Return(preferredTimestamp) 168 169 manager := blkexecutor.NewMockManager(ctrl) 170 manager.EXPECT().Preferred().Return(preferredID) 171 manager.EXPECT().GetStatelessBlock(preferredID).Return(preferredBlock, nil) 172 manager.EXPECT().GetState(preferredID).Return(preferredState, true) 173 174 unsignedTx := txs.NewMockUnsignedTx(ctrl) 175 unsignedTx.EXPECT().Visit(gomock.Any()).Return(nil) // Pass semantic verification 176 unsignedTx.EXPECT().Visit(gomock.Any()).Return(errTest) // Fail execution 177 tx := &txs.Tx{Unsigned: unsignedTx} 178 179 mempool := mempool.NewMockMempool(ctrl) 180 mempool.EXPECT().Peek().Return(tx, true) 181 mempool.EXPECT().Remove([]*txs.Tx{tx}) 182 mempool.EXPECT().MarkDropped(tx.ID(), errTest) 183 // Second loop iteration 184 mempool.EXPECT().Peek().Return(nil, false) 185 mempool.EXPECT().RequestBuildBlock() 186 187 return New( 188 &txexecutor.Backend{ 189 Ctx: &snow.Context{ 190 Log: logging.NoLog{}, 191 }, 192 }, 193 manager, 194 &mockable.Clock{}, 195 mempool, 196 ) 197 }, 198 expectedErr: ErrNoTransactions, // The only tx was invalid 199 }, 200 { 201 name: "tx has non-unique inputs", 202 builderFunc: func(ctrl *gomock.Controller) Builder { 203 preferredID := ids.GenerateTestID() 204 preferredHeight := uint64(1337) 205 preferredTimestamp := time.Now() 206 preferredBlock := block.NewMockBlock(ctrl) 207 preferredBlock.EXPECT().Height().Return(preferredHeight) 208 preferredBlock.EXPECT().Timestamp().Return(preferredTimestamp) 209 210 preferredState := state.NewMockChain(ctrl) 211 preferredState.EXPECT().GetLastAccepted().Return(preferredID) 212 preferredState.EXPECT().GetTimestamp().Return(preferredTimestamp) 213 214 manager := blkexecutor.NewMockManager(ctrl) 215 manager.EXPECT().Preferred().Return(preferredID) 216 manager.EXPECT().GetStatelessBlock(preferredID).Return(preferredBlock, nil) 217 manager.EXPECT().GetState(preferredID).Return(preferredState, true) 218 manager.EXPECT().VerifyUniqueInputs(preferredID, gomock.Any()).Return(errTest) 219 220 unsignedTx := txs.NewMockUnsignedTx(ctrl) 221 unsignedTx.EXPECT().Visit(gomock.Any()).Return(nil) // Pass semantic verification 222 unsignedTx.EXPECT().Visit(gomock.Any()).Return(nil) // Pass execution 223 tx := &txs.Tx{Unsigned: unsignedTx} 224 225 mempool := mempool.NewMockMempool(ctrl) 226 mempool.EXPECT().Peek().Return(tx, true) 227 mempool.EXPECT().Remove([]*txs.Tx{tx}) 228 mempool.EXPECT().MarkDropped(tx.ID(), errTest) 229 // Second loop iteration 230 mempool.EXPECT().Peek().Return(nil, false) 231 mempool.EXPECT().RequestBuildBlock() 232 233 return New( 234 &txexecutor.Backend{ 235 Ctx: &snow.Context{ 236 Log: logging.NoLog{}, 237 }, 238 }, 239 manager, 240 &mockable.Clock{}, 241 mempool, 242 ) 243 }, 244 expectedErr: ErrNoTransactions, // The only tx was invalid 245 }, 246 { 247 name: "txs consume same input", 248 builderFunc: func(ctrl *gomock.Controller) Builder { 249 preferredID := ids.GenerateTestID() 250 preferredHeight := uint64(1337) 251 preferredTimestamp := time.Now() 252 preferredBlock := block.NewMockBlock(ctrl) 253 preferredBlock.EXPECT().Height().Return(preferredHeight) 254 preferredBlock.EXPECT().Timestamp().Return(preferredTimestamp) 255 256 preferredState := state.NewMockChain(ctrl) 257 preferredState.EXPECT().GetLastAccepted().Return(preferredID) 258 preferredState.EXPECT().GetTimestamp().Return(preferredTimestamp) 259 260 // tx1 and tx2 both consume [inputID]. 261 // tx1 is added to the block first, so tx2 should be dropped. 262 inputID := ids.GenerateTestID() 263 unsignedTx1 := txs.NewMockUnsignedTx(ctrl) 264 unsignedTx1.EXPECT().Visit(gomock.Any()).Return(nil) // Pass semantic verification 265 unsignedTx1.EXPECT().Visit(gomock.Any()).DoAndReturn( // Pass execution 266 func(visitor txs.Visitor) error { 267 require.IsType(t, &txexecutor.Executor{}, visitor) 268 executor := visitor.(*txexecutor.Executor) 269 executor.Inputs.Add(inputID) 270 return nil 271 }, 272 ) 273 unsignedTx1.EXPECT().SetBytes(gomock.Any()).AnyTimes() 274 tx1 := &txs.Tx{Unsigned: unsignedTx1} 275 // Set the bytes of tx1 to something other than nil 276 // so we can check that the remainingSize is updated 277 tx1Bytes := []byte{1, 2, 3} 278 tx1.SetBytes(nil, tx1Bytes) 279 280 unsignedTx2 := txs.NewMockUnsignedTx(ctrl) 281 unsignedTx2.EXPECT().Visit(gomock.Any()).Return(nil) // Pass semantic verification 282 unsignedTx2.EXPECT().Visit(gomock.Any()).DoAndReturn( // Pass execution 283 func(visitor txs.Visitor) error { 284 require.IsType(t, &txexecutor.Executor{}, visitor) 285 executor := visitor.(*txexecutor.Executor) 286 executor.Inputs.Add(inputID) 287 return nil 288 }, 289 ) 290 tx2 := &txs.Tx{Unsigned: unsignedTx2} 291 292 manager := blkexecutor.NewMockManager(ctrl) 293 manager.EXPECT().Preferred().Return(preferredID) 294 manager.EXPECT().GetStatelessBlock(preferredID).Return(preferredBlock, nil) 295 manager.EXPECT().GetState(preferredID).Return(preferredState, true) 296 manager.EXPECT().VerifyUniqueInputs(preferredID, gomock.Any()).Return(nil) 297 // Assert created block has one tx, tx1, 298 // and other fields are set correctly. 299 manager.EXPECT().NewBlock(gomock.Any()).DoAndReturn( 300 func(block *block.StandardBlock) snowman.Block { 301 require.Len(t, block.Transactions, 1) 302 require.Equal(t, tx1, block.Transactions[0]) 303 require.Equal(t, preferredHeight+1, block.Height()) 304 require.Equal(t, preferredID, block.Parent()) 305 return nil 306 }, 307 ) 308 309 mempool := mempool.NewMockMempool(ctrl) 310 mempool.EXPECT().Peek().Return(tx1, true) 311 mempool.EXPECT().Remove([]*txs.Tx{tx1}) 312 // Second loop iteration 313 mempool.EXPECT().Peek().Return(tx2, true) 314 mempool.EXPECT().Remove([]*txs.Tx{tx2}) 315 mempool.EXPECT().MarkDropped(tx2.ID(), blkexecutor.ErrConflictingBlockTxs) 316 // Third loop iteration 317 mempool.EXPECT().Peek().Return(nil, false) 318 mempool.EXPECT().RequestBuildBlock() 319 320 // To marshal the tx/block 321 codec := codec.NewMockManager(ctrl) 322 codec.EXPECT().Marshal(gomock.Any(), gomock.Any()).Return([]byte{1, 2, 3}, nil).AnyTimes() 323 codec.EXPECT().Size(gomock.Any(), gomock.Any()).Return(2, nil).AnyTimes() 324 325 return New( 326 &txexecutor.Backend{ 327 Codec: codec, 328 Ctx: &snow.Context{ 329 Log: logging.NoLog{}, 330 }, 331 }, 332 manager, 333 &mockable.Clock{}, 334 mempool, 335 ) 336 }, 337 expectedErr: nil, 338 }, 339 { 340 name: "preferred timestamp after now", 341 builderFunc: func(ctrl *gomock.Controller) Builder { 342 preferredID := ids.GenerateTestID() 343 preferredHeight := uint64(1337) 344 preferredTimestamp := time.Now() 345 preferredBlock := block.NewMockBlock(ctrl) 346 preferredBlock.EXPECT().Height().Return(preferredHeight) 347 preferredBlock.EXPECT().Timestamp().Return(preferredTimestamp) 348 349 // Clock reads just before the preferred timestamp. 350 // Created block should have the preferred timestamp since it's later. 351 clock := &mockable.Clock{} 352 clock.Set(preferredTimestamp.Add(-2 * time.Second)) 353 354 preferredState := state.NewMockChain(ctrl) 355 preferredState.EXPECT().GetLastAccepted().Return(preferredID) 356 preferredState.EXPECT().GetTimestamp().Return(preferredTimestamp) 357 358 manager := blkexecutor.NewMockManager(ctrl) 359 manager.EXPECT().Preferred().Return(preferredID) 360 manager.EXPECT().GetStatelessBlock(preferredID).Return(preferredBlock, nil) 361 manager.EXPECT().GetState(preferredID).Return(preferredState, true) 362 manager.EXPECT().VerifyUniqueInputs(preferredID, gomock.Any()).Return(nil) 363 // Assert that the created block has the right timestamp 364 manager.EXPECT().NewBlock(gomock.Any()).DoAndReturn( 365 func(block *block.StandardBlock) snowman.Block { 366 require.Equal(t, preferredTimestamp.Unix(), block.Timestamp().Unix()) 367 return nil 368 }, 369 ) 370 371 inputID := ids.GenerateTestID() 372 unsignedTx := txs.NewMockUnsignedTx(ctrl) 373 unsignedTx.EXPECT().Visit(gomock.Any()).Return(nil) // Pass semantic verification 374 unsignedTx.EXPECT().Visit(gomock.Any()).DoAndReturn( // Pass execution 375 func(visitor txs.Visitor) error { 376 require.IsType(t, &txexecutor.Executor{}, visitor) 377 executor := visitor.(*txexecutor.Executor) 378 executor.Inputs.Add(inputID) 379 return nil 380 }, 381 ) 382 unsignedTx.EXPECT().SetBytes(gomock.Any()).AnyTimes() 383 tx := &txs.Tx{Unsigned: unsignedTx} 384 385 mempool := mempool.NewMockMempool(ctrl) 386 mempool.EXPECT().Peek().Return(tx, true) 387 mempool.EXPECT().Remove([]*txs.Tx{tx}) 388 // Second loop iteration 389 mempool.EXPECT().Peek().Return(nil, false) 390 mempool.EXPECT().RequestBuildBlock() 391 392 // To marshal the tx/block 393 codec := codec.NewMockManager(ctrl) 394 codec.EXPECT().Marshal(gomock.Any(), gomock.Any()).Return([]byte{1, 2, 3}, nil).AnyTimes() 395 codec.EXPECT().Size(gomock.Any(), gomock.Any()).Return(2, nil).AnyTimes() 396 397 return New( 398 &txexecutor.Backend{ 399 Codec: codec, 400 Ctx: &snow.Context{ 401 Log: logging.NoLog{}, 402 }, 403 }, 404 manager, 405 clock, 406 mempool, 407 ) 408 }, 409 expectedErr: nil, 410 }, 411 { 412 name: "preferred timestamp before now", 413 builderFunc: func(ctrl *gomock.Controller) Builder { 414 preferredID := ids.GenerateTestID() 415 preferredHeight := uint64(1337) 416 // preferred block's timestamp is after the time reported by clock 417 now := time.Now() 418 preferredTimestamp := now.Add(-2 * time.Second) 419 preferredBlock := block.NewMockBlock(ctrl) 420 preferredBlock.EXPECT().Height().Return(preferredHeight) 421 preferredBlock.EXPECT().Timestamp().Return(preferredTimestamp) 422 423 // Clock reads after the preferred timestamp. 424 // Created block should have [now] timestamp since it's later. 425 clock := &mockable.Clock{} 426 clock.Set(now) 427 428 preferredState := state.NewMockChain(ctrl) 429 preferredState.EXPECT().GetLastAccepted().Return(preferredID) 430 preferredState.EXPECT().GetTimestamp().Return(preferredTimestamp) 431 432 manager := blkexecutor.NewMockManager(ctrl) 433 manager.EXPECT().Preferred().Return(preferredID) 434 manager.EXPECT().GetStatelessBlock(preferredID).Return(preferredBlock, nil) 435 manager.EXPECT().GetState(preferredID).Return(preferredState, true) 436 manager.EXPECT().VerifyUniqueInputs(preferredID, gomock.Any()).Return(nil) 437 // Assert that the created block has the right timestamp 438 manager.EXPECT().NewBlock(gomock.Any()).DoAndReturn( 439 func(block *block.StandardBlock) snowman.Block { 440 require.Equal(t, now.Unix(), block.Timestamp().Unix()) 441 return nil 442 }, 443 ) 444 445 inputID := ids.GenerateTestID() 446 unsignedTx := txs.NewMockUnsignedTx(ctrl) 447 unsignedTx.EXPECT().Visit(gomock.Any()).Return(nil) // Pass semantic verification 448 unsignedTx.EXPECT().Visit(gomock.Any()).DoAndReturn( // Pass execution 449 func(visitor txs.Visitor) error { 450 require.IsType(t, &txexecutor.Executor{}, visitor) 451 executor := visitor.(*txexecutor.Executor) 452 executor.Inputs.Add(inputID) 453 return nil 454 }, 455 ) 456 unsignedTx.EXPECT().SetBytes(gomock.Any()).AnyTimes() 457 tx := &txs.Tx{Unsigned: unsignedTx} 458 459 mempool := mempool.NewMockMempool(ctrl) 460 mempool.EXPECT().Peek().Return(tx, true) 461 mempool.EXPECT().Remove([]*txs.Tx{tx}) 462 // Second loop iteration 463 mempool.EXPECT().Peek().Return(nil, false) 464 mempool.EXPECT().RequestBuildBlock() 465 466 // To marshal the tx/block 467 codec := codec.NewMockManager(ctrl) 468 codec.EXPECT().Marshal(gomock.Any(), gomock.Any()).Return([]byte{1, 2, 3}, nil).AnyTimes() 469 codec.EXPECT().Size(gomock.Any(), gomock.Any()).Return(2, nil).AnyTimes() 470 471 return New( 472 &txexecutor.Backend{ 473 Codec: codec, 474 Ctx: &snow.Context{ 475 Log: logging.NoLog{}, 476 }, 477 }, 478 manager, 479 clock, 480 mempool, 481 ) 482 }, 483 expectedErr: nil, 484 }, 485 } 486 487 for _, tt := range tests { 488 t.Run(tt.name, func(t *testing.T) { 489 ctrl := gomock.NewController(t) 490 491 builder := tt.builderFunc(ctrl) 492 _, err := builder.BuildBlock(context.Background()) 493 require.ErrorIs(t, err, tt.expectedErr) 494 }) 495 } 496 } 497 498 func TestBlockBuilderAddLocalTx(t *testing.T) { 499 transactions := createTxs() 500 501 require := require.New(t) 502 503 registerer := prometheus.NewRegistry() 504 toEngine := make(chan common.Message, 100) 505 mempool, err := mempool.New("mempool", registerer, toEngine) 506 require.NoError(err) 507 // add a tx to the mempool 508 tx := transactions[0] 509 txID := tx.ID() 510 require.NoError(mempool.Add(tx)) 511 512 _, ok := mempool.Get(txID) 513 require.True(ok) 514 515 parser, err := block.NewParser( 516 []fxs.Fx{ 517 &secp256k1fx.Fx{}, 518 }, 519 ) 520 require.NoError(err) 521 522 backend := &txexecutor.Backend{ 523 Ctx: &snow.Context{ 524 Log: logging.NoLog{}, 525 }, 526 Codec: parser.Codec(), 527 } 528 529 baseDB := versiondb.New(memdb.New()) 530 531 state, err := state.New(baseDB, parser, registerer, trackChecksums) 532 require.NoError(err) 533 534 clk := &mockable.Clock{} 535 onAccept := func(*txs.Tx) error { return nil } 536 now := time.Now() 537 parentTimestamp := now.Add(-2 * time.Second) 538 parentID := ids.GenerateTestID() 539 cm := parser.Codec() 540 txs, err := createParentTxs(cm) 541 require.NoError(err) 542 parentBlk, err := block.NewStandardBlock(parentID, 0, parentTimestamp, txs, cm) 543 require.NoError(err) 544 state.AddBlock(parentBlk) 545 state.SetLastAccepted(parentBlk.ID()) 546 547 metrics, err := metrics.New(registerer) 548 require.NoError(err) 549 550 manager := blkexecutor.NewManager(mempool, metrics, state, backend, clk, onAccept) 551 552 manager.SetPreference(parentBlk.ID()) 553 554 builder := New(backend, manager, clk, mempool) 555 556 // show that build block fails if tx is invalid 557 _, err = builder.BuildBlock(context.Background()) 558 require.ErrorIs(err, ErrNoTransactions) 559 } 560 561 func createTxs() []*txs.Tx { 562 return []*txs.Tx{{ 563 Unsigned: &txs.BaseTx{BaseTx: avax.BaseTx{ 564 NetworkID: constants.UnitTestID, 565 BlockchainID: ids.GenerateTestID(), 566 Outs: []*avax.TransferableOutput{{ 567 Asset: avax.Asset{ID: ids.GenerateTestID()}, 568 Out: &secp256k1fx.TransferOutput{ 569 OutputOwners: secp256k1fx.OutputOwners{ 570 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 571 }, 572 }, 573 }}, 574 Ins: []*avax.TransferableInput{{ 575 UTXOID: avax.UTXOID{ 576 TxID: ids.ID{'t', 'x', 'I', 'D'}, 577 OutputIndex: 1, 578 }, 579 Asset: avax.Asset{ID: ids.GenerateTestID()}, 580 In: &secp256k1fx.TransferInput{ 581 Amt: uint64(54321), 582 Input: secp256k1fx.Input{ 583 SigIndices: []uint32{2}, 584 }, 585 }, 586 }}, 587 Memo: []byte{1, 2, 3, 4, 5, 6, 7, 8}, 588 }}, 589 Creds: []*fxs.FxCredential{ 590 { 591 Credential: &secp256k1fx.Credential{}, 592 }, 593 }, 594 }} 595 } 596 597 func createParentTxs(cm codec.Manager) ([]*txs.Tx, error) { 598 countTxs := 1 599 testTxs := make([]*txs.Tx, 0, countTxs) 600 for i := 0; i < countTxs; i++ { 601 // Create the tx 602 tx := &txs.Tx{Unsigned: &txs.BaseTx{BaseTx: avax.BaseTx{ 603 NetworkID: constants.UnitTestID, 604 BlockchainID: chainID, 605 Outs: []*avax.TransferableOutput{{ 606 Asset: avax.Asset{ID: ids.ID{1, 2, 3}}, 607 Out: &secp256k1fx.TransferOutput{ 608 Amt: uint64(12345), 609 OutputOwners: secp256k1fx.OutputOwners{ 610 Threshold: 1, 611 Addrs: []ids.ShortID{keys[0].PublicKey().Address()}, 612 }, 613 }, 614 }}, 615 Ins: []*avax.TransferableInput{{ 616 UTXOID: avax.UTXOID{ 617 TxID: ids.ID{'t', 'x', 'p', 'a', 'r', 'e', 'n', 't'}, 618 OutputIndex: 1, 619 }, 620 Asset: avax.Asset{ID: ids.ID{1, 2, 3}}, 621 In: &secp256k1fx.TransferInput{ 622 Amt: uint64(54321), 623 Input: secp256k1fx.Input{ 624 SigIndices: []uint32{2}, 625 }, 626 }, 627 }}, 628 Memo: []byte{1, 2, 9, 4, 5, 6, 7, 8}, 629 }}} 630 if err := tx.SignSECP256K1Fx(cm, [][]*secp256k1.PrivateKey{{keys[0]}}); err != nil { 631 return nil, err 632 } 633 testTxs = append(testTxs, tx) 634 } 635 return testTxs, nil 636 }