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