github.com/MetalBlockchain/metalgo@v1.11.9/vms/avm/txs/executor/syntactic_verifier_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 executor 5 6 import ( 7 "math" 8 "strings" 9 "testing" 10 11 "github.com/stretchr/testify/require" 12 13 "github.com/MetalBlockchain/metalgo/ids" 14 "github.com/MetalBlockchain/metalgo/snow/snowtest" 15 "github.com/MetalBlockchain/metalgo/utils/constants" 16 "github.com/MetalBlockchain/metalgo/utils/crypto/secp256k1" 17 "github.com/MetalBlockchain/metalgo/utils/timer/mockable" 18 "github.com/MetalBlockchain/metalgo/vms/avm/config" 19 "github.com/MetalBlockchain/metalgo/vms/avm/fxs" 20 "github.com/MetalBlockchain/metalgo/vms/avm/txs" 21 "github.com/MetalBlockchain/metalgo/vms/components/avax" 22 "github.com/MetalBlockchain/metalgo/vms/components/verify" 23 "github.com/MetalBlockchain/metalgo/vms/secp256k1fx" 24 25 safemath "github.com/MetalBlockchain/metalgo/utils/math" 26 ) 27 28 var ( 29 keys = secp256k1.TestKeys() 30 feeConfig = config.Config{ 31 TxFee: 2, 32 CreateAssetTxFee: 3, 33 EUpgradeTime: mockable.MaxTime, 34 } 35 ) 36 37 func TestSyntacticVerifierBaseTx(t *testing.T) { 38 ctx := snowtest.Context(t, snowtest.XChainID) 39 40 fx := &secp256k1fx.Fx{} 41 parser, err := txs.NewParser( 42 []fxs.Fx{ 43 fx, 44 }, 45 ) 46 require.NoError(t, err) 47 48 feeAssetID := ids.GenerateTestID() 49 asset := avax.Asset{ 50 ID: feeAssetID, 51 } 52 outputOwners := secp256k1fx.OutputOwners{ 53 Threshold: 1, 54 Addrs: []ids.ShortID{keys[0].PublicKey().Address()}, 55 } 56 fxOutput := secp256k1fx.TransferOutput{ 57 Amt: 12345, 58 OutputOwners: outputOwners, 59 } 60 output := avax.TransferableOutput{ 61 Asset: asset, 62 Out: &fxOutput, 63 } 64 inputTxID := ids.GenerateTestID() 65 utxoID := avax.UTXOID{ 66 TxID: inputTxID, 67 OutputIndex: 0, 68 } 69 inputSigners := secp256k1fx.Input{ 70 SigIndices: []uint32{2}, 71 } 72 fxInput := secp256k1fx.TransferInput{ 73 Amt: 54321, 74 Input: inputSigners, 75 } 76 input := avax.TransferableInput{ 77 UTXOID: utxoID, 78 Asset: asset, 79 In: &fxInput, 80 } 81 baseTx := avax.BaseTx{ 82 NetworkID: constants.UnitTestID, 83 BlockchainID: ctx.ChainID, 84 Outs: []*avax.TransferableOutput{ 85 &output, 86 }, 87 Ins: []*avax.TransferableInput{ 88 &input, 89 }, 90 } 91 cred := fxs.FxCredential{ 92 Credential: &secp256k1fx.Credential{}, 93 } 94 creds := []*fxs.FxCredential{ 95 &cred, 96 } 97 98 codec := parser.Codec() 99 backend := &Backend{ 100 Ctx: ctx, 101 Config: &feeConfig, 102 Fxs: []*fxs.ParsedFx{ 103 { 104 ID: secp256k1fx.ID, 105 Fx: fx, 106 }, 107 }, 108 Codec: codec, 109 FeeAssetID: feeAssetID, 110 } 111 112 tests := []struct { 113 name string 114 txFunc func() *txs.Tx 115 err error 116 }{ 117 { 118 name: "valid", 119 txFunc: func() *txs.Tx { 120 return &txs.Tx{ 121 Unsigned: &txs.BaseTx{BaseTx: baseTx}, 122 Creds: creds, 123 } 124 }, 125 err: nil, 126 }, 127 { 128 name: "wrong networkID", 129 txFunc: func() *txs.Tx { 130 baseTx := baseTx 131 baseTx.NetworkID++ 132 return &txs.Tx{ 133 Unsigned: &txs.BaseTx{BaseTx: baseTx}, 134 Creds: creds, 135 } 136 }, 137 err: avax.ErrWrongNetworkID, 138 }, 139 { 140 name: "wrong chainID", 141 txFunc: func() *txs.Tx { 142 baseTx := baseTx 143 baseTx.BlockchainID = ids.GenerateTestID() 144 return &txs.Tx{ 145 Unsigned: &txs.BaseTx{BaseTx: baseTx}, 146 Creds: creds, 147 } 148 }, 149 err: avax.ErrWrongChainID, 150 }, 151 { 152 name: "memo too large", 153 txFunc: func() *txs.Tx { 154 baseTx := baseTx 155 baseTx.Memo = make([]byte, avax.MaxMemoSize+1) 156 return &txs.Tx{ 157 Unsigned: &txs.BaseTx{BaseTx: baseTx}, 158 Creds: creds, 159 } 160 }, 161 err: avax.ErrMemoTooLarge, 162 }, 163 { 164 name: "invalid output", 165 txFunc: func() *txs.Tx { 166 output := output 167 output.Out = &secp256k1fx.TransferOutput{ 168 Amt: 0, 169 OutputOwners: outputOwners, 170 } 171 172 baseTx := baseTx 173 baseTx.Outs = []*avax.TransferableOutput{ 174 &output, 175 } 176 return &txs.Tx{ 177 Unsigned: &txs.BaseTx{BaseTx: baseTx}, 178 Creds: creds, 179 } 180 }, 181 err: secp256k1fx.ErrNoValueOutput, 182 }, 183 { 184 name: "unsorted outputs", 185 txFunc: func() *txs.Tx { 186 output0 := output 187 output0.Out = &secp256k1fx.TransferOutput{ 188 Amt: 1, 189 OutputOwners: outputOwners, 190 } 191 192 output1 := output 193 output1.Out = &secp256k1fx.TransferOutput{ 194 Amt: 2, 195 OutputOwners: outputOwners, 196 } 197 198 outputs := []*avax.TransferableOutput{ 199 &output0, 200 &output1, 201 } 202 avax.SortTransferableOutputs(outputs, codec) 203 outputs[0], outputs[1] = outputs[1], outputs[0] 204 205 baseTx := baseTx 206 baseTx.Outs = outputs 207 return &txs.Tx{ 208 Unsigned: &txs.BaseTx{BaseTx: baseTx}, 209 Creds: creds, 210 } 211 }, 212 err: avax.ErrOutputsNotSorted, 213 }, 214 { 215 name: "invalid input", 216 txFunc: func() *txs.Tx { 217 input := input 218 input.In = &secp256k1fx.TransferInput{ 219 Amt: 0, 220 Input: inputSigners, 221 } 222 223 baseTx := baseTx 224 baseTx.Ins = []*avax.TransferableInput{ 225 &input, 226 } 227 return &txs.Tx{ 228 Unsigned: &txs.BaseTx{BaseTx: baseTx}, 229 Creds: creds, 230 } 231 }, 232 err: secp256k1fx.ErrNoValueInput, 233 }, 234 { 235 name: "duplicate inputs", 236 txFunc: func() *txs.Tx { 237 baseTx := baseTx 238 baseTx.Ins = []*avax.TransferableInput{ 239 &input, 240 &input, 241 } 242 return &txs.Tx{ 243 Unsigned: &txs.BaseTx{BaseTx: baseTx}, 244 Creds: []*fxs.FxCredential{ 245 &cred, 246 &cred, 247 }, 248 } 249 }, 250 err: avax.ErrInputsNotSortedUnique, 251 }, 252 { 253 name: "input overflow", 254 txFunc: func() *txs.Tx { 255 input0 := input 256 input0.In = &secp256k1fx.TransferInput{ 257 Amt: 1, 258 Input: inputSigners, 259 } 260 261 input1 := input 262 input1.UTXOID.OutputIndex++ 263 input1.In = &secp256k1fx.TransferInput{ 264 Amt: math.MaxUint64, 265 Input: inputSigners, 266 } 267 268 baseTx := baseTx 269 baseTx.Ins = []*avax.TransferableInput{ 270 &input0, 271 &input1, 272 } 273 avax.SortTransferableInputsWithSigners(baseTx.Ins, make([][]*secp256k1.PrivateKey, 2)) 274 return &txs.Tx{ 275 Unsigned: &txs.BaseTx{BaseTx: baseTx}, 276 Creds: []*fxs.FxCredential{ 277 &cred, 278 &cred, 279 }, 280 } 281 }, 282 err: safemath.ErrOverflow, 283 }, 284 { 285 name: "output overflow", 286 txFunc: func() *txs.Tx { 287 output0 := output 288 output0.Out = &secp256k1fx.TransferOutput{ 289 Amt: 1, 290 OutputOwners: outputOwners, 291 } 292 293 output1 := output 294 output1.Out = &secp256k1fx.TransferOutput{ 295 Amt: math.MaxUint64, 296 OutputOwners: outputOwners, 297 } 298 299 outputs := []*avax.TransferableOutput{ 300 &output0, 301 &output1, 302 } 303 avax.SortTransferableOutputs(outputs, codec) 304 305 baseTx := baseTx 306 baseTx.Outs = outputs 307 return &txs.Tx{ 308 Unsigned: &txs.BaseTx{BaseTx: baseTx}, 309 Creds: creds, 310 } 311 }, 312 err: safemath.ErrOverflow, 313 }, 314 { 315 name: "insufficient funds", 316 txFunc: func() *txs.Tx { 317 input := input 318 input.In = &secp256k1fx.TransferInput{ 319 Amt: 1, 320 Input: inputSigners, 321 } 322 323 baseTx := baseTx 324 baseTx.Ins = []*avax.TransferableInput{ 325 &input, 326 } 327 return &txs.Tx{ 328 Unsigned: &txs.BaseTx{BaseTx: baseTx}, 329 Creds: creds, 330 } 331 }, 332 err: avax.ErrInsufficientFunds, 333 }, 334 { 335 name: "invalid credential", 336 txFunc: func() *txs.Tx { 337 return &txs.Tx{ 338 Unsigned: &txs.BaseTx{BaseTx: baseTx}, 339 Creds: []*fxs.FxCredential{{ 340 Credential: (*secp256k1fx.Credential)(nil), 341 }}, 342 } 343 }, 344 err: secp256k1fx.ErrNilCredential, 345 }, 346 { 347 name: "wrong number of credentials", 348 txFunc: func() *txs.Tx { 349 return &txs.Tx{ 350 Unsigned: &txs.BaseTx{BaseTx: baseTx}, 351 } 352 }, 353 err: errWrongNumberOfCredentials, 354 }, 355 { 356 name: "barely sufficient funds", 357 txFunc: func() *txs.Tx { 358 input := input 359 input.In = &secp256k1fx.TransferInput{ 360 Amt: fxOutput.Amt + feeConfig.TxFee, 361 Input: inputSigners, 362 } 363 364 baseTx := baseTx 365 baseTx.Ins = []*avax.TransferableInput{ 366 &input, 367 } 368 return &txs.Tx{ 369 Unsigned: &txs.BaseTx{BaseTx: baseTx}, 370 Creds: creds, 371 } 372 }, 373 err: nil, 374 }, 375 { 376 name: "barely insufficient funds", 377 txFunc: func() *txs.Tx { 378 input := input 379 input.In = &secp256k1fx.TransferInput{ 380 Amt: fxOutput.Amt + feeConfig.TxFee - 1, 381 Input: inputSigners, 382 } 383 384 baseTx := baseTx 385 baseTx.Ins = []*avax.TransferableInput{ 386 &input, 387 } 388 return &txs.Tx{ 389 Unsigned: &txs.BaseTx{BaseTx: baseTx}, 390 Creds: creds, 391 } 392 }, 393 err: avax.ErrInsufficientFunds, 394 }, 395 } 396 for _, test := range tests { 397 t.Run(test.name, func(t *testing.T) { 398 tx := test.txFunc() 399 verifier := &SyntacticVerifier{ 400 Backend: backend, 401 Tx: tx, 402 } 403 err := tx.Unsigned.Visit(verifier) 404 require.ErrorIs(t, err, test.err) 405 }) 406 } 407 } 408 409 func TestSyntacticVerifierCreateAssetTx(t *testing.T) { 410 ctx := snowtest.Context(t, snowtest.XChainID) 411 412 fx := &secp256k1fx.Fx{} 413 parser, err := txs.NewParser( 414 []fxs.Fx{ 415 fx, 416 }, 417 ) 418 require.NoError(t, err) 419 420 feeAssetID := ids.GenerateTestID() 421 asset := avax.Asset{ 422 ID: feeAssetID, 423 } 424 outputOwners := secp256k1fx.OutputOwners{ 425 Threshold: 1, 426 Addrs: []ids.ShortID{keys[0].PublicKey().Address()}, 427 } 428 fxOutput := secp256k1fx.TransferOutput{ 429 Amt: 12345, 430 OutputOwners: outputOwners, 431 } 432 output := avax.TransferableOutput{ 433 Asset: asset, 434 Out: &fxOutput, 435 } 436 inputTxID := ids.GenerateTestID() 437 utxoID := avax.UTXOID{ 438 TxID: inputTxID, 439 OutputIndex: 0, 440 } 441 inputSigners := secp256k1fx.Input{ 442 SigIndices: []uint32{2}, 443 } 444 fxInput := secp256k1fx.TransferInput{ 445 Amt: 54321, 446 Input: inputSigners, 447 } 448 input := avax.TransferableInput{ 449 UTXOID: utxoID, 450 Asset: asset, 451 In: &fxInput, 452 } 453 baseTx := avax.BaseTx{ 454 NetworkID: constants.UnitTestID, 455 BlockchainID: ctx.ChainID, 456 Outs: []*avax.TransferableOutput{ 457 &output, 458 }, 459 Ins: []*avax.TransferableInput{ 460 &input, 461 }, 462 } 463 initialState := txs.InitialState{ 464 FxIndex: 0, 465 Outs: []verify.State{ 466 &fxOutput, 467 }, 468 } 469 tx := txs.CreateAssetTx{ 470 BaseTx: txs.BaseTx{BaseTx: baseTx}, 471 Name: "NormalName", 472 Symbol: "TICK", 473 Denomination: byte(2), 474 States: []*txs.InitialState{ 475 &initialState, 476 }, 477 } 478 cred := fxs.FxCredential{ 479 Credential: &secp256k1fx.Credential{}, 480 } 481 creds := []*fxs.FxCredential{ 482 &cred, 483 } 484 485 codec := parser.Codec() 486 backend := &Backend{ 487 Ctx: ctx, 488 Config: &feeConfig, 489 Fxs: []*fxs.ParsedFx{ 490 { 491 ID: secp256k1fx.ID, 492 Fx: fx, 493 }, 494 }, 495 Codec: codec, 496 FeeAssetID: feeAssetID, 497 } 498 499 tests := []struct { 500 name string 501 txFunc func() *txs.Tx 502 err error 503 }{ 504 { 505 name: "valid", 506 txFunc: func() *txs.Tx { 507 return &txs.Tx{ 508 Unsigned: &tx, 509 Creds: creds, 510 } 511 }, 512 err: nil, 513 }, 514 { 515 name: "name too short", 516 txFunc: func() *txs.Tx { 517 tx := tx 518 tx.Name = "" 519 return &txs.Tx{ 520 Unsigned: &tx, 521 Creds: creds, 522 } 523 }, 524 err: errNameTooShort, 525 }, 526 { 527 name: "name too long", 528 txFunc: func() *txs.Tx { 529 tx := tx 530 tx.Name = strings.Repeat("X", maxNameLen+1) 531 return &txs.Tx{ 532 Unsigned: &tx, 533 Creds: creds, 534 } 535 }, 536 err: errNameTooLong, 537 }, 538 { 539 name: "symbol too short", 540 txFunc: func() *txs.Tx { 541 tx := tx 542 tx.Symbol = "" 543 return &txs.Tx{ 544 Unsigned: &tx, 545 Creds: creds, 546 } 547 }, 548 err: errSymbolTooShort, 549 }, 550 { 551 name: "symbol too long", 552 txFunc: func() *txs.Tx { 553 tx := tx 554 tx.Symbol = strings.Repeat("X", maxSymbolLen+1) 555 return &txs.Tx{ 556 Unsigned: &tx, 557 Creds: creds, 558 } 559 }, 560 err: errSymbolTooLong, 561 }, 562 { 563 name: "no feature extensions", 564 txFunc: func() *txs.Tx { 565 tx := tx 566 tx.States = nil 567 return &txs.Tx{ 568 Unsigned: &tx, 569 Creds: creds, 570 } 571 }, 572 err: errNoFxs, 573 }, 574 { 575 name: "denomination too large", 576 txFunc: func() *txs.Tx { 577 tx := tx 578 tx.Denomination = maxDenomination + 1 579 return &txs.Tx{ 580 Unsigned: &tx, 581 Creds: creds, 582 } 583 }, 584 err: errDenominationTooLarge, 585 }, 586 { 587 name: "bounding whitespace in name", 588 txFunc: func() *txs.Tx { 589 tx := tx 590 tx.Name = " AVAX" 591 return &txs.Tx{ 592 Unsigned: &tx, 593 Creds: creds, 594 } 595 }, 596 err: errUnexpectedWhitespace, 597 }, 598 { 599 name: "illegal character in name", 600 txFunc: func() *txs.Tx { 601 tx := tx 602 tx.Name = "h8*32" 603 return &txs.Tx{ 604 Unsigned: &tx, 605 Creds: creds, 606 } 607 }, 608 err: errIllegalNameCharacter, 609 }, 610 { 611 name: "illegal character in ticker", 612 txFunc: func() *txs.Tx { 613 tx := tx 614 tx.Symbol = "H I" 615 return &txs.Tx{ 616 Unsigned: &tx, 617 Creds: creds, 618 } 619 }, 620 err: errIllegalSymbolCharacter, 621 }, 622 { 623 name: "wrong networkID", 624 txFunc: func() *txs.Tx { 625 tx := tx 626 tx.NetworkID++ 627 return &txs.Tx{ 628 Unsigned: &tx, 629 Creds: creds, 630 } 631 }, 632 err: avax.ErrWrongNetworkID, 633 }, 634 { 635 name: "wrong chainID", 636 txFunc: func() *txs.Tx { 637 tx := tx 638 tx.BlockchainID = ids.GenerateTestID() 639 return &txs.Tx{ 640 Unsigned: &tx, 641 Creds: creds, 642 } 643 }, 644 err: avax.ErrWrongChainID, 645 }, 646 { 647 name: "memo too large", 648 txFunc: func() *txs.Tx { 649 tx := tx 650 tx.Memo = make([]byte, avax.MaxMemoSize+1) 651 return &txs.Tx{ 652 Unsigned: &tx, 653 Creds: creds, 654 } 655 }, 656 err: avax.ErrMemoTooLarge, 657 }, 658 { 659 name: "invalid output", 660 txFunc: func() *txs.Tx { 661 output := output 662 output.Out = &secp256k1fx.TransferOutput{ 663 Amt: 0, 664 OutputOwners: outputOwners, 665 } 666 667 tx := tx 668 tx.Outs = []*avax.TransferableOutput{ 669 &output, 670 } 671 return &txs.Tx{ 672 Unsigned: &tx, 673 Creds: creds, 674 } 675 }, 676 err: secp256k1fx.ErrNoValueOutput, 677 }, 678 { 679 name: "unsorted outputs", 680 txFunc: func() *txs.Tx { 681 output0 := output 682 output0.Out = &secp256k1fx.TransferOutput{ 683 Amt: 1, 684 OutputOwners: outputOwners, 685 } 686 687 output1 := output 688 output1.Out = &secp256k1fx.TransferOutput{ 689 Amt: 2, 690 OutputOwners: outputOwners, 691 } 692 693 outputs := []*avax.TransferableOutput{ 694 &output0, 695 &output1, 696 } 697 avax.SortTransferableOutputs(outputs, codec) 698 outputs[0], outputs[1] = outputs[1], outputs[0] 699 700 tx := tx 701 tx.Outs = outputs 702 return &txs.Tx{ 703 Unsigned: &tx, 704 Creds: creds, 705 } 706 }, 707 err: avax.ErrOutputsNotSorted, 708 }, 709 { 710 name: "invalid input", 711 txFunc: func() *txs.Tx { 712 input := input 713 input.In = &secp256k1fx.TransferInput{ 714 Amt: 0, 715 Input: inputSigners, 716 } 717 718 tx := tx 719 tx.Ins = []*avax.TransferableInput{ 720 &input, 721 } 722 return &txs.Tx{ 723 Unsigned: &tx, 724 Creds: creds, 725 } 726 }, 727 err: secp256k1fx.ErrNoValueInput, 728 }, 729 { 730 name: "duplicate inputs", 731 txFunc: func() *txs.Tx { 732 tx := tx 733 tx.Ins = []*avax.TransferableInput{ 734 &input, 735 &input, 736 } 737 return &txs.Tx{ 738 Unsigned: &tx, 739 Creds: []*fxs.FxCredential{ 740 &cred, 741 &cred, 742 }, 743 } 744 }, 745 err: avax.ErrInputsNotSortedUnique, 746 }, 747 { 748 name: "input overflow", 749 txFunc: func() *txs.Tx { 750 input0 := input 751 input0.In = &secp256k1fx.TransferInput{ 752 Amt: 1, 753 Input: inputSigners, 754 } 755 756 input1 := input 757 input1.UTXOID.OutputIndex++ 758 input1.In = &secp256k1fx.TransferInput{ 759 Amt: math.MaxUint64, 760 Input: inputSigners, 761 } 762 763 tx := tx 764 tx.Ins = []*avax.TransferableInput{ 765 &input0, 766 &input1, 767 } 768 avax.SortTransferableInputsWithSigners(baseTx.Ins, make([][]*secp256k1.PrivateKey, 2)) 769 return &txs.Tx{ 770 Unsigned: &tx, 771 Creds: []*fxs.FxCredential{ 772 &cred, 773 &cred, 774 }, 775 } 776 }, 777 err: safemath.ErrOverflow, 778 }, 779 { 780 name: "output overflow", 781 txFunc: func() *txs.Tx { 782 output0 := output 783 output0.Out = &secp256k1fx.TransferOutput{ 784 Amt: 1, 785 OutputOwners: outputOwners, 786 } 787 788 output1 := output 789 output1.Out = &secp256k1fx.TransferOutput{ 790 Amt: math.MaxUint64, 791 OutputOwners: outputOwners, 792 } 793 794 outputs := []*avax.TransferableOutput{ 795 &output0, 796 &output1, 797 } 798 avax.SortTransferableOutputs(outputs, codec) 799 800 tx := tx 801 tx.Outs = outputs 802 return &txs.Tx{ 803 Unsigned: &tx, 804 Creds: creds, 805 } 806 }, 807 err: safemath.ErrOverflow, 808 }, 809 { 810 name: "insufficient funds", 811 txFunc: func() *txs.Tx { 812 input := input 813 input.In = &secp256k1fx.TransferInput{ 814 Amt: 1, 815 Input: inputSigners, 816 } 817 818 tx := tx 819 tx.Ins = []*avax.TransferableInput{ 820 &input, 821 } 822 return &txs.Tx{ 823 Unsigned: &tx, 824 Creds: creds, 825 } 826 }, 827 err: avax.ErrInsufficientFunds, 828 }, 829 { 830 name: "invalid nil state", 831 txFunc: func() *txs.Tx { 832 tx := tx 833 tx.States = []*txs.InitialState{ 834 nil, 835 } 836 return &txs.Tx{ 837 Unsigned: &tx, 838 Creds: creds, 839 } 840 }, 841 err: txs.ErrNilInitialState, 842 }, 843 { 844 name: "invalid fx", 845 txFunc: func() *txs.Tx { 846 initialState := initialState 847 initialState.FxIndex = 1 848 849 tx := tx 850 tx.States = []*txs.InitialState{ 851 &initialState, 852 } 853 return &txs.Tx{ 854 Unsigned: &tx, 855 Creds: creds, 856 } 857 }, 858 err: txs.ErrUnknownFx, 859 }, 860 { 861 name: "invalid nil state output", 862 txFunc: func() *txs.Tx { 863 initialState := initialState 864 initialState.Outs = []verify.State{ 865 nil, 866 } 867 868 tx := tx 869 tx.States = []*txs.InitialState{ 870 &initialState, 871 } 872 return &txs.Tx{ 873 Unsigned: &tx, 874 Creds: creds, 875 } 876 }, 877 err: txs.ErrNilFxOutput, 878 }, 879 { 880 name: "invalid state output", 881 txFunc: func() *txs.Tx { 882 fxOutput := fxOutput 883 fxOutput.Amt = 0 884 885 initialState := initialState 886 initialState.Outs = []verify.State{ 887 &fxOutput, 888 } 889 890 tx := tx 891 tx.States = []*txs.InitialState{ 892 &initialState, 893 } 894 return &txs.Tx{ 895 Unsigned: &tx, 896 Creds: creds, 897 } 898 }, 899 err: secp256k1fx.ErrNoValueOutput, 900 }, 901 { 902 name: "unsorted initial state", 903 txFunc: func() *txs.Tx { 904 fxOutput0 := fxOutput 905 906 fxOutput1 := fxOutput 907 fxOutput1.Amt++ 908 909 initialState := initialState 910 initialState.Outs = []verify.State{ 911 &fxOutput0, 912 &fxOutput1, 913 } 914 initialState.Sort(codec) 915 initialState.Outs[0], initialState.Outs[1] = initialState.Outs[1], initialState.Outs[0] 916 917 tx := tx 918 tx.States = []*txs.InitialState{ 919 &initialState, 920 } 921 return &txs.Tx{ 922 Unsigned: &tx, 923 Creds: creds, 924 } 925 }, 926 err: txs.ErrOutputsNotSorted, 927 }, 928 { 929 name: "non-unique initial states", 930 txFunc: func() *txs.Tx { 931 tx := tx 932 tx.States = []*txs.InitialState{ 933 &initialState, 934 &initialState, 935 } 936 return &txs.Tx{ 937 Unsigned: &tx, 938 Creds: creds, 939 } 940 }, 941 err: errInitialStatesNotSortedUnique, 942 }, 943 { 944 name: "invalid credential", 945 txFunc: func() *txs.Tx { 946 return &txs.Tx{ 947 Unsigned: &tx, 948 Creds: []*fxs.FxCredential{{ 949 Credential: (*secp256k1fx.Credential)(nil), 950 }}, 951 } 952 }, 953 err: secp256k1fx.ErrNilCredential, 954 }, 955 { 956 name: "wrong number of credentials", 957 txFunc: func() *txs.Tx { 958 return &txs.Tx{ 959 Unsigned: &tx, 960 } 961 }, 962 err: errWrongNumberOfCredentials, 963 }, 964 { 965 name: "barely sufficient funds", 966 txFunc: func() *txs.Tx { 967 input := input 968 input.In = &secp256k1fx.TransferInput{ 969 Amt: fxOutput.Amt + feeConfig.CreateAssetTxFee, 970 Input: inputSigners, 971 } 972 973 tx := tx 974 tx.Ins = []*avax.TransferableInput{ 975 &input, 976 } 977 return &txs.Tx{ 978 Unsigned: &tx, 979 Creds: creds, 980 } 981 }, 982 err: nil, 983 }, 984 { 985 name: "barely insufficient funds", 986 txFunc: func() *txs.Tx { 987 input := input 988 input.In = &secp256k1fx.TransferInput{ 989 Amt: fxOutput.Amt + feeConfig.CreateAssetTxFee - 1, 990 Input: inputSigners, 991 } 992 993 tx := tx 994 tx.Ins = []*avax.TransferableInput{ 995 &input, 996 } 997 return &txs.Tx{ 998 Unsigned: &tx, 999 Creds: creds, 1000 } 1001 }, 1002 err: avax.ErrInsufficientFunds, 1003 }, 1004 } 1005 for _, test := range tests { 1006 t.Run(test.name, func(t *testing.T) { 1007 tx := test.txFunc() 1008 verifier := &SyntacticVerifier{ 1009 Backend: backend, 1010 Tx: tx, 1011 } 1012 err := tx.Unsigned.Visit(verifier) 1013 require.ErrorIs(t, err, test.err) 1014 }) 1015 } 1016 } 1017 1018 func TestSyntacticVerifierOperationTx(t *testing.T) { 1019 ctx := snowtest.Context(t, snowtest.XChainID) 1020 1021 fx := &secp256k1fx.Fx{} 1022 parser, err := txs.NewParser( 1023 []fxs.Fx{ 1024 fx, 1025 }, 1026 ) 1027 require.NoError(t, err) 1028 1029 feeAssetID := ids.GenerateTestID() 1030 asset := avax.Asset{ 1031 ID: feeAssetID, 1032 } 1033 outputOwners := secp256k1fx.OutputOwners{ 1034 Threshold: 1, 1035 Addrs: []ids.ShortID{keys[0].PublicKey().Address()}, 1036 } 1037 fxOutput := secp256k1fx.TransferOutput{ 1038 Amt: 12345, 1039 OutputOwners: outputOwners, 1040 } 1041 output := avax.TransferableOutput{ 1042 Asset: asset, 1043 Out: &fxOutput, 1044 } 1045 inputTxID := ids.GenerateTestID() 1046 utxoID := avax.UTXOID{ 1047 TxID: inputTxID, 1048 OutputIndex: 0, 1049 } 1050 inputSigners := secp256k1fx.Input{ 1051 SigIndices: []uint32{2}, 1052 } 1053 fxInput := secp256k1fx.TransferInput{ 1054 Amt: 54321, 1055 Input: inputSigners, 1056 } 1057 input := avax.TransferableInput{ 1058 UTXOID: utxoID, 1059 Asset: asset, 1060 In: &fxInput, 1061 } 1062 baseTx := avax.BaseTx{ 1063 NetworkID: constants.UnitTestID, 1064 BlockchainID: ctx.ChainID, 1065 Ins: []*avax.TransferableInput{ 1066 &input, 1067 }, 1068 Outs: []*avax.TransferableOutput{ 1069 &output, 1070 }, 1071 } 1072 opUTXOID := utxoID 1073 opUTXOID.OutputIndex++ 1074 fxOp := secp256k1fx.MintOperation{ 1075 MintInput: inputSigners, 1076 MintOutput: secp256k1fx.MintOutput{ 1077 OutputOwners: outputOwners, 1078 }, 1079 TransferOutput: fxOutput, 1080 } 1081 op := txs.Operation{ 1082 Asset: asset, 1083 UTXOIDs: []*avax.UTXOID{ 1084 &opUTXOID, 1085 }, 1086 Op: &fxOp, 1087 } 1088 tx := txs.OperationTx{ 1089 BaseTx: txs.BaseTx{BaseTx: baseTx}, 1090 Ops: []*txs.Operation{ 1091 &op, 1092 }, 1093 } 1094 cred := fxs.FxCredential{ 1095 Credential: &secp256k1fx.Credential{}, 1096 } 1097 creds := []*fxs.FxCredential{ 1098 &cred, 1099 &cred, 1100 } 1101 1102 codec := parser.Codec() 1103 backend := &Backend{ 1104 Ctx: ctx, 1105 Config: &feeConfig, 1106 Fxs: []*fxs.ParsedFx{ 1107 { 1108 ID: secp256k1fx.ID, 1109 Fx: fx, 1110 }, 1111 }, 1112 Codec: codec, 1113 FeeAssetID: feeAssetID, 1114 } 1115 1116 tests := []struct { 1117 name string 1118 txFunc func() *txs.Tx 1119 err error 1120 }{ 1121 { 1122 name: "valid", 1123 txFunc: func() *txs.Tx { 1124 return &txs.Tx{ 1125 Unsigned: &tx, 1126 Creds: creds, 1127 } 1128 }, 1129 err: nil, 1130 }, 1131 { 1132 name: "no operation", 1133 txFunc: func() *txs.Tx { 1134 tx := tx 1135 tx.Ops = nil 1136 return &txs.Tx{ 1137 Unsigned: &tx, 1138 Creds: creds, 1139 } 1140 }, 1141 err: errNoOperations, 1142 }, 1143 { 1144 name: "wrong networkID", 1145 txFunc: func() *txs.Tx { 1146 tx := tx 1147 tx.NetworkID++ 1148 return &txs.Tx{ 1149 Unsigned: &tx, 1150 Creds: creds, 1151 } 1152 }, 1153 err: avax.ErrWrongNetworkID, 1154 }, 1155 { 1156 name: "wrong chainID", 1157 txFunc: func() *txs.Tx { 1158 tx := tx 1159 tx.BlockchainID = ids.GenerateTestID() 1160 return &txs.Tx{ 1161 Unsigned: &tx, 1162 Creds: creds, 1163 } 1164 }, 1165 err: avax.ErrWrongChainID, 1166 }, 1167 { 1168 name: "memo too large", 1169 txFunc: func() *txs.Tx { 1170 tx := tx 1171 tx.Memo = make([]byte, avax.MaxMemoSize+1) 1172 return &txs.Tx{ 1173 Unsigned: &tx, 1174 Creds: creds, 1175 } 1176 }, 1177 err: avax.ErrMemoTooLarge, 1178 }, 1179 { 1180 name: "invalid output", 1181 txFunc: func() *txs.Tx { 1182 output := output 1183 output.Out = &secp256k1fx.TransferOutput{ 1184 Amt: 0, 1185 OutputOwners: outputOwners, 1186 } 1187 1188 tx := tx 1189 tx.Outs = []*avax.TransferableOutput{ 1190 &output, 1191 } 1192 return &txs.Tx{ 1193 Unsigned: &tx, 1194 Creds: creds, 1195 } 1196 }, 1197 err: secp256k1fx.ErrNoValueOutput, 1198 }, 1199 { 1200 name: "unsorted outputs", 1201 txFunc: func() *txs.Tx { 1202 output0 := output 1203 output0.Out = &secp256k1fx.TransferOutput{ 1204 Amt: 1, 1205 OutputOwners: outputOwners, 1206 } 1207 1208 output1 := output 1209 output1.Out = &secp256k1fx.TransferOutput{ 1210 Amt: 2, 1211 OutputOwners: outputOwners, 1212 } 1213 1214 outputs := []*avax.TransferableOutput{ 1215 &output0, 1216 &output1, 1217 } 1218 avax.SortTransferableOutputs(outputs, codec) 1219 outputs[0], outputs[1] = outputs[1], outputs[0] 1220 1221 tx := tx 1222 tx.Outs = outputs 1223 return &txs.Tx{ 1224 Unsigned: &tx, 1225 Creds: creds, 1226 } 1227 }, 1228 err: avax.ErrOutputsNotSorted, 1229 }, 1230 { 1231 name: "invalid input", 1232 txFunc: func() *txs.Tx { 1233 input := input 1234 input.In = &secp256k1fx.TransferInput{ 1235 Amt: 0, 1236 Input: inputSigners, 1237 } 1238 1239 tx := tx 1240 tx.Ins = []*avax.TransferableInput{ 1241 &input, 1242 } 1243 return &txs.Tx{ 1244 Unsigned: &tx, 1245 Creds: creds, 1246 } 1247 }, 1248 err: secp256k1fx.ErrNoValueInput, 1249 }, 1250 { 1251 name: "duplicate inputs", 1252 txFunc: func() *txs.Tx { 1253 tx := tx 1254 tx.Ins = []*avax.TransferableInput{ 1255 &input, 1256 &input, 1257 } 1258 return &txs.Tx{ 1259 Unsigned: &tx, 1260 Creds: []*fxs.FxCredential{ 1261 &cred, 1262 &cred, 1263 }, 1264 } 1265 }, 1266 err: avax.ErrInputsNotSortedUnique, 1267 }, 1268 { 1269 name: "input overflow", 1270 txFunc: func() *txs.Tx { 1271 input0 := input 1272 input0.In = &secp256k1fx.TransferInput{ 1273 Amt: 1, 1274 Input: inputSigners, 1275 } 1276 1277 input1 := input 1278 input1.UTXOID.OutputIndex++ 1279 input1.In = &secp256k1fx.TransferInput{ 1280 Amt: math.MaxUint64, 1281 Input: inputSigners, 1282 } 1283 1284 tx := tx 1285 tx.Ins = []*avax.TransferableInput{ 1286 &input0, 1287 &input1, 1288 } 1289 avax.SortTransferableInputsWithSigners(tx.Ins, make([][]*secp256k1.PrivateKey, 2)) 1290 return &txs.Tx{ 1291 Unsigned: &tx, 1292 Creds: []*fxs.FxCredential{ 1293 &cred, 1294 &cred, 1295 }, 1296 } 1297 }, 1298 err: safemath.ErrOverflow, 1299 }, 1300 { 1301 name: "output overflow", 1302 txFunc: func() *txs.Tx { 1303 output := output 1304 output.Out = &secp256k1fx.TransferOutput{ 1305 Amt: math.MaxUint64, 1306 OutputOwners: outputOwners, 1307 } 1308 1309 outputs := []*avax.TransferableOutput{ 1310 &output, 1311 } 1312 avax.SortTransferableOutputs(outputs, codec) 1313 1314 tx := tx 1315 tx.Outs = outputs 1316 return &txs.Tx{ 1317 Unsigned: &tx, 1318 Creds: creds, 1319 } 1320 }, 1321 err: safemath.ErrOverflow, 1322 }, 1323 { 1324 name: "insufficient funds", 1325 txFunc: func() *txs.Tx { 1326 input := input 1327 input.In = &secp256k1fx.TransferInput{ 1328 Amt: 1, 1329 Input: inputSigners, 1330 } 1331 1332 tx := tx 1333 tx.Ins = []*avax.TransferableInput{ 1334 &input, 1335 } 1336 return &txs.Tx{ 1337 Unsigned: &tx, 1338 Creds: creds, 1339 } 1340 }, 1341 err: avax.ErrInsufficientFunds, 1342 }, 1343 { 1344 name: "invalid nil op", 1345 txFunc: func() *txs.Tx { 1346 tx := tx 1347 tx.Ops = []*txs.Operation{ 1348 nil, 1349 } 1350 return &txs.Tx{ 1351 Unsigned: &tx, 1352 Creds: creds, 1353 } 1354 }, 1355 err: txs.ErrNilOperation, 1356 }, 1357 { 1358 name: "invalid nil fx op", 1359 txFunc: func() *txs.Tx { 1360 op := op 1361 op.Op = nil 1362 1363 tx := tx 1364 tx.Ops = []*txs.Operation{ 1365 &op, 1366 } 1367 return &txs.Tx{ 1368 Unsigned: &tx, 1369 Creds: creds, 1370 } 1371 }, 1372 err: txs.ErrNilFxOperation, 1373 }, 1374 { 1375 name: "invalid duplicated op UTXOs", 1376 txFunc: func() *txs.Tx { 1377 op := op 1378 op.UTXOIDs = []*avax.UTXOID{ 1379 &opUTXOID, 1380 &opUTXOID, 1381 } 1382 1383 tx := tx 1384 tx.Ops = []*txs.Operation{ 1385 &op, 1386 } 1387 return &txs.Tx{ 1388 Unsigned: &tx, 1389 Creds: creds, 1390 } 1391 }, 1392 err: txs.ErrNotSortedAndUniqueUTXOIDs, 1393 }, 1394 { 1395 name: "invalid duplicated UTXOs across ops", 1396 txFunc: func() *txs.Tx { 1397 newOp := op 1398 op.Asset.ID = ids.GenerateTestID() 1399 1400 tx := tx 1401 tx.Ops = []*txs.Operation{ 1402 &op, 1403 &newOp, 1404 } 1405 txs.SortOperations(tx.Ops, codec) 1406 return &txs.Tx{ 1407 Unsigned: &tx, 1408 Creds: creds, 1409 } 1410 }, 1411 err: errDoubleSpend, 1412 }, 1413 { 1414 name: "invalid duplicated op", 1415 txFunc: func() *txs.Tx { 1416 op := op 1417 op.UTXOIDs = nil 1418 1419 tx := tx 1420 tx.Ops = []*txs.Operation{ 1421 &op, 1422 &op, 1423 } 1424 txs.SortOperations(tx.Ops, codec) 1425 return &txs.Tx{ 1426 Unsigned: &tx, 1427 Creds: creds, 1428 } 1429 }, 1430 err: errOperationsNotSortedUnique, 1431 }, 1432 { 1433 name: "invalid credential", 1434 txFunc: func() *txs.Tx { 1435 return &txs.Tx{ 1436 Unsigned: &tx, 1437 Creds: []*fxs.FxCredential{{ 1438 Credential: (*secp256k1fx.Credential)(nil), 1439 }}, 1440 } 1441 }, 1442 err: secp256k1fx.ErrNilCredential, 1443 }, 1444 { 1445 name: "wrong number of credentials", 1446 txFunc: func() *txs.Tx { 1447 return &txs.Tx{ 1448 Unsigned: &tx, 1449 } 1450 }, 1451 err: errWrongNumberOfCredentials, 1452 }, 1453 { 1454 name: "barely sufficient funds", 1455 txFunc: func() *txs.Tx { 1456 input := input 1457 input.In = &secp256k1fx.TransferInput{ 1458 Amt: fxOutput.Amt + feeConfig.TxFee, 1459 Input: inputSigners, 1460 } 1461 1462 tx := tx 1463 tx.Ins = []*avax.TransferableInput{ 1464 &input, 1465 } 1466 return &txs.Tx{ 1467 Unsigned: &tx, 1468 Creds: creds, 1469 } 1470 }, 1471 err: nil, 1472 }, 1473 { 1474 name: "barely insufficient funds", 1475 txFunc: func() *txs.Tx { 1476 input := input 1477 input.In = &secp256k1fx.TransferInput{ 1478 Amt: fxOutput.Amt + feeConfig.TxFee - 1, 1479 Input: inputSigners, 1480 } 1481 1482 tx := tx 1483 tx.Ins = []*avax.TransferableInput{ 1484 &input, 1485 } 1486 return &txs.Tx{ 1487 Unsigned: &tx, 1488 Creds: creds, 1489 } 1490 }, 1491 err: avax.ErrInsufficientFunds, 1492 }, 1493 } 1494 for _, test := range tests { 1495 t.Run(test.name, func(t *testing.T) { 1496 tx := test.txFunc() 1497 verifier := &SyntacticVerifier{ 1498 Backend: backend, 1499 Tx: tx, 1500 } 1501 err := tx.Unsigned.Visit(verifier) 1502 require.ErrorIs(t, err, test.err) 1503 }) 1504 } 1505 } 1506 1507 func TestSyntacticVerifierImportTx(t *testing.T) { 1508 ctx := snowtest.Context(t, snowtest.XChainID) 1509 1510 fx := &secp256k1fx.Fx{} 1511 parser, err := txs.NewParser( 1512 []fxs.Fx{ 1513 fx, 1514 }, 1515 ) 1516 require.NoError(t, err) 1517 1518 feeAssetID := ids.GenerateTestID() 1519 asset := avax.Asset{ 1520 ID: feeAssetID, 1521 } 1522 outputOwners := secp256k1fx.OutputOwners{ 1523 Threshold: 1, 1524 Addrs: []ids.ShortID{keys[0].PublicKey().Address()}, 1525 } 1526 fxOutput := secp256k1fx.TransferOutput{ 1527 Amt: 12345, 1528 OutputOwners: outputOwners, 1529 } 1530 output := avax.TransferableOutput{ 1531 Asset: asset, 1532 Out: &fxOutput, 1533 } 1534 inputTxID := ids.GenerateTestID() 1535 utxoID := avax.UTXOID{ 1536 TxID: inputTxID, 1537 OutputIndex: 0, 1538 } 1539 inputSigners := secp256k1fx.Input{ 1540 SigIndices: []uint32{2}, 1541 } 1542 fxInput := secp256k1fx.TransferInput{ 1543 Amt: 54321, 1544 Input: inputSigners, 1545 } 1546 input := avax.TransferableInput{ 1547 UTXOID: utxoID, 1548 Asset: asset, 1549 In: &fxInput, 1550 } 1551 baseTx := avax.BaseTx{ 1552 NetworkID: constants.UnitTestID, 1553 BlockchainID: ctx.ChainID, 1554 Outs: []*avax.TransferableOutput{ 1555 &output, 1556 }, 1557 } 1558 tx := txs.ImportTx{ 1559 BaseTx: txs.BaseTx{BaseTx: baseTx}, 1560 SourceChain: ctx.CChainID, 1561 ImportedIns: []*avax.TransferableInput{ 1562 &input, 1563 }, 1564 } 1565 cred := fxs.FxCredential{ 1566 Credential: &secp256k1fx.Credential{}, 1567 } 1568 creds := []*fxs.FxCredential{ 1569 &cred, 1570 } 1571 1572 codec := parser.Codec() 1573 backend := &Backend{ 1574 Ctx: ctx, 1575 Config: &feeConfig, 1576 Fxs: []*fxs.ParsedFx{ 1577 { 1578 ID: secp256k1fx.ID, 1579 Fx: fx, 1580 }, 1581 }, 1582 Codec: codec, 1583 FeeAssetID: feeAssetID, 1584 } 1585 1586 tests := []struct { 1587 name string 1588 txFunc func() *txs.Tx 1589 err error 1590 }{ 1591 { 1592 name: "valid", 1593 txFunc: func() *txs.Tx { 1594 return &txs.Tx{ 1595 Unsigned: &tx, 1596 Creds: creds, 1597 } 1598 }, 1599 err: nil, 1600 }, 1601 { 1602 name: "no imported inputs", 1603 txFunc: func() *txs.Tx { 1604 tx := tx 1605 tx.ImportedIns = nil 1606 return &txs.Tx{ 1607 Unsigned: &tx, 1608 Creds: creds, 1609 } 1610 }, 1611 err: errNoImportInputs, 1612 }, 1613 { 1614 name: "wrong networkID", 1615 txFunc: func() *txs.Tx { 1616 tx := tx 1617 tx.NetworkID++ 1618 return &txs.Tx{ 1619 Unsigned: &tx, 1620 Creds: creds, 1621 } 1622 }, 1623 err: avax.ErrWrongNetworkID, 1624 }, 1625 { 1626 name: "wrong chainID", 1627 txFunc: func() *txs.Tx { 1628 tx := tx 1629 tx.BlockchainID = ids.GenerateTestID() 1630 return &txs.Tx{ 1631 Unsigned: &tx, 1632 Creds: creds, 1633 } 1634 }, 1635 err: avax.ErrWrongChainID, 1636 }, 1637 { 1638 name: "memo too large", 1639 txFunc: func() *txs.Tx { 1640 tx := tx 1641 tx.Memo = make([]byte, avax.MaxMemoSize+1) 1642 return &txs.Tx{ 1643 Unsigned: &tx, 1644 Creds: creds, 1645 } 1646 }, 1647 err: avax.ErrMemoTooLarge, 1648 }, 1649 { 1650 name: "invalid output", 1651 txFunc: func() *txs.Tx { 1652 output := output 1653 output.Out = &secp256k1fx.TransferOutput{ 1654 Amt: 0, 1655 OutputOwners: outputOwners, 1656 } 1657 1658 tx := tx 1659 tx.Outs = []*avax.TransferableOutput{ 1660 &output, 1661 } 1662 return &txs.Tx{ 1663 Unsigned: &tx, 1664 Creds: creds, 1665 } 1666 }, 1667 err: secp256k1fx.ErrNoValueOutput, 1668 }, 1669 { 1670 name: "unsorted outputs", 1671 txFunc: func() *txs.Tx { 1672 output0 := output 1673 output0.Out = &secp256k1fx.TransferOutput{ 1674 Amt: 1, 1675 OutputOwners: outputOwners, 1676 } 1677 1678 output1 := output 1679 output1.Out = &secp256k1fx.TransferOutput{ 1680 Amt: 2, 1681 OutputOwners: outputOwners, 1682 } 1683 1684 outputs := []*avax.TransferableOutput{ 1685 &output0, 1686 &output1, 1687 } 1688 avax.SortTransferableOutputs(outputs, codec) 1689 outputs[0], outputs[1] = outputs[1], outputs[0] 1690 1691 tx := tx 1692 tx.Outs = outputs 1693 return &txs.Tx{ 1694 Unsigned: &tx, 1695 Creds: creds, 1696 } 1697 }, 1698 err: avax.ErrOutputsNotSorted, 1699 }, 1700 { 1701 name: "invalid input", 1702 txFunc: func() *txs.Tx { 1703 input := input 1704 input.In = &secp256k1fx.TransferInput{ 1705 Amt: 0, 1706 Input: inputSigners, 1707 } 1708 1709 tx := tx 1710 tx.Ins = []*avax.TransferableInput{ 1711 &input, 1712 } 1713 return &txs.Tx{ 1714 Unsigned: &tx, 1715 Creds: creds, 1716 } 1717 }, 1718 err: secp256k1fx.ErrNoValueInput, 1719 }, 1720 { 1721 name: "duplicate inputs", 1722 txFunc: func() *txs.Tx { 1723 tx := tx 1724 tx.Ins = []*avax.TransferableInput{ 1725 &input, 1726 &input, 1727 } 1728 return &txs.Tx{ 1729 Unsigned: &tx, 1730 Creds: []*fxs.FxCredential{ 1731 &cred, 1732 &cred, 1733 &cred, 1734 }, 1735 } 1736 }, 1737 err: avax.ErrInputsNotSortedUnique, 1738 }, 1739 { 1740 name: "duplicate imported inputs", 1741 txFunc: func() *txs.Tx { 1742 tx := tx 1743 tx.ImportedIns = []*avax.TransferableInput{ 1744 &input, 1745 &input, 1746 } 1747 return &txs.Tx{ 1748 Unsigned: &tx, 1749 Creds: []*fxs.FxCredential{ 1750 &cred, 1751 &cred, 1752 }, 1753 } 1754 }, 1755 err: avax.ErrInputsNotSortedUnique, 1756 }, 1757 { 1758 name: "input overflow", 1759 txFunc: func() *txs.Tx { 1760 input0 := input 1761 input0.In = &secp256k1fx.TransferInput{ 1762 Amt: 1, 1763 Input: inputSigners, 1764 } 1765 1766 input1 := input 1767 input1.UTXOID.OutputIndex++ 1768 input1.In = &secp256k1fx.TransferInput{ 1769 Amt: math.MaxUint64, 1770 Input: inputSigners, 1771 } 1772 1773 tx := tx 1774 tx.Ins = []*avax.TransferableInput{ 1775 &input0, 1776 &input1, 1777 } 1778 avax.SortTransferableInputsWithSigners(tx.Ins, make([][]*secp256k1.PrivateKey, 2)) 1779 return &txs.Tx{ 1780 Unsigned: &tx, 1781 Creds: []*fxs.FxCredential{ 1782 &cred, 1783 &cred, 1784 }, 1785 } 1786 }, 1787 err: safemath.ErrOverflow, 1788 }, 1789 { 1790 name: "output overflow", 1791 txFunc: func() *txs.Tx { 1792 output := output 1793 output.Out = &secp256k1fx.TransferOutput{ 1794 Amt: math.MaxUint64, 1795 OutputOwners: outputOwners, 1796 } 1797 1798 outputs := []*avax.TransferableOutput{ 1799 &output, 1800 } 1801 avax.SortTransferableOutputs(outputs, codec) 1802 1803 tx := tx 1804 tx.Outs = outputs 1805 return &txs.Tx{ 1806 Unsigned: &tx, 1807 Creds: creds, 1808 } 1809 }, 1810 err: safemath.ErrOverflow, 1811 }, 1812 { 1813 name: "insufficient funds", 1814 txFunc: func() *txs.Tx { 1815 input := input 1816 input.In = &secp256k1fx.TransferInput{ 1817 Amt: 1, 1818 Input: inputSigners, 1819 } 1820 1821 tx := tx 1822 tx.ImportedIns = []*avax.TransferableInput{ 1823 &input, 1824 } 1825 return &txs.Tx{ 1826 Unsigned: &tx, 1827 Creds: creds, 1828 } 1829 }, 1830 err: avax.ErrInsufficientFunds, 1831 }, 1832 { 1833 name: "invalid credential", 1834 txFunc: func() *txs.Tx { 1835 return &txs.Tx{ 1836 Unsigned: &tx, 1837 Creds: []*fxs.FxCredential{{ 1838 Credential: (*secp256k1fx.Credential)(nil), 1839 }}, 1840 } 1841 }, 1842 err: secp256k1fx.ErrNilCredential, 1843 }, 1844 { 1845 name: "wrong number of credentials", 1846 txFunc: func() *txs.Tx { 1847 return &txs.Tx{ 1848 Unsigned: &tx, 1849 } 1850 }, 1851 err: errWrongNumberOfCredentials, 1852 }, 1853 { 1854 name: "barely sufficient funds", 1855 txFunc: func() *txs.Tx { 1856 input := input 1857 input.In = &secp256k1fx.TransferInput{ 1858 Amt: fxOutput.Amt + feeConfig.TxFee, 1859 Input: inputSigners, 1860 } 1861 1862 tx := tx 1863 tx.ImportedIns = []*avax.TransferableInput{ 1864 &input, 1865 } 1866 return &txs.Tx{ 1867 Unsigned: &tx, 1868 Creds: creds, 1869 } 1870 }, 1871 err: nil, 1872 }, 1873 { 1874 name: "barely insufficient funds", 1875 txFunc: func() *txs.Tx { 1876 input := input 1877 input.In = &secp256k1fx.TransferInput{ 1878 Amt: fxOutput.Amt + feeConfig.TxFee - 1, 1879 Input: inputSigners, 1880 } 1881 1882 tx := tx 1883 tx.ImportedIns = []*avax.TransferableInput{ 1884 &input, 1885 } 1886 return &txs.Tx{ 1887 Unsigned: &tx, 1888 Creds: creds, 1889 } 1890 }, 1891 err: avax.ErrInsufficientFunds, 1892 }, 1893 } 1894 for _, test := range tests { 1895 t.Run(test.name, func(t *testing.T) { 1896 tx := test.txFunc() 1897 verifier := &SyntacticVerifier{ 1898 Backend: backend, 1899 Tx: tx, 1900 } 1901 err := tx.Unsigned.Visit(verifier) 1902 require.ErrorIs(t, err, test.err) 1903 }) 1904 } 1905 } 1906 1907 func TestSyntacticVerifierExportTx(t *testing.T) { 1908 ctx := snowtest.Context(t, snowtest.XChainID) 1909 1910 fx := &secp256k1fx.Fx{} 1911 parser, err := txs.NewParser( 1912 []fxs.Fx{ 1913 fx, 1914 }, 1915 ) 1916 require.NoError(t, err) 1917 1918 feeAssetID := ids.GenerateTestID() 1919 asset := avax.Asset{ 1920 ID: feeAssetID, 1921 } 1922 outputOwners := secp256k1fx.OutputOwners{ 1923 Threshold: 1, 1924 Addrs: []ids.ShortID{keys[0].PublicKey().Address()}, 1925 } 1926 fxOutput := secp256k1fx.TransferOutput{ 1927 Amt: 12345, 1928 OutputOwners: outputOwners, 1929 } 1930 output := avax.TransferableOutput{ 1931 Asset: asset, 1932 Out: &fxOutput, 1933 } 1934 inputTxID := ids.GenerateTestID() 1935 utxoID := avax.UTXOID{ 1936 TxID: inputTxID, 1937 OutputIndex: 0, 1938 } 1939 inputSigners := secp256k1fx.Input{ 1940 SigIndices: []uint32{2}, 1941 } 1942 fxInput := secp256k1fx.TransferInput{ 1943 Amt: 54321, 1944 Input: inputSigners, 1945 } 1946 input := avax.TransferableInput{ 1947 UTXOID: utxoID, 1948 Asset: asset, 1949 In: &fxInput, 1950 } 1951 baseTx := avax.BaseTx{ 1952 NetworkID: constants.UnitTestID, 1953 BlockchainID: ctx.ChainID, 1954 Ins: []*avax.TransferableInput{ 1955 &input, 1956 }, 1957 } 1958 tx := txs.ExportTx{ 1959 BaseTx: txs.BaseTx{BaseTx: baseTx}, 1960 DestinationChain: ctx.CChainID, 1961 ExportedOuts: []*avax.TransferableOutput{ 1962 &output, 1963 }, 1964 } 1965 cred := fxs.FxCredential{ 1966 Credential: &secp256k1fx.Credential{}, 1967 } 1968 creds := []*fxs.FxCredential{ 1969 &cred, 1970 } 1971 1972 codec := parser.Codec() 1973 backend := &Backend{ 1974 Ctx: ctx, 1975 Config: &feeConfig, 1976 Fxs: []*fxs.ParsedFx{ 1977 { 1978 ID: secp256k1fx.ID, 1979 Fx: fx, 1980 }, 1981 }, 1982 Codec: codec, 1983 FeeAssetID: feeAssetID, 1984 } 1985 1986 tests := []struct { 1987 name string 1988 txFunc func() *txs.Tx 1989 err error 1990 }{ 1991 { 1992 name: "valid", 1993 txFunc: func() *txs.Tx { 1994 return &txs.Tx{ 1995 Unsigned: &tx, 1996 Creds: creds, 1997 } 1998 }, 1999 err: nil, 2000 }, 2001 { 2002 name: "no exported outputs", 2003 txFunc: func() *txs.Tx { 2004 tx := tx 2005 tx.ExportedOuts = nil 2006 return &txs.Tx{ 2007 Unsigned: &tx, 2008 Creds: creds, 2009 } 2010 }, 2011 err: errNoExportOutputs, 2012 }, 2013 { 2014 name: "wrong networkID", 2015 txFunc: func() *txs.Tx { 2016 tx := tx 2017 tx.NetworkID++ 2018 return &txs.Tx{ 2019 Unsigned: &tx, 2020 Creds: creds, 2021 } 2022 }, 2023 err: avax.ErrWrongNetworkID, 2024 }, 2025 { 2026 name: "wrong chainID", 2027 txFunc: func() *txs.Tx { 2028 tx := tx 2029 tx.BlockchainID = ids.GenerateTestID() 2030 return &txs.Tx{ 2031 Unsigned: &tx, 2032 Creds: creds, 2033 } 2034 }, 2035 err: avax.ErrWrongChainID, 2036 }, 2037 { 2038 name: "memo too large", 2039 txFunc: func() *txs.Tx { 2040 tx := tx 2041 tx.Memo = make([]byte, avax.MaxMemoSize+1) 2042 return &txs.Tx{ 2043 Unsigned: &tx, 2044 Creds: creds, 2045 } 2046 }, 2047 err: avax.ErrMemoTooLarge, 2048 }, 2049 { 2050 name: "invalid output", 2051 txFunc: func() *txs.Tx { 2052 output := output 2053 output.Out = &secp256k1fx.TransferOutput{ 2054 Amt: 0, 2055 OutputOwners: outputOwners, 2056 } 2057 2058 tx := tx 2059 tx.Outs = []*avax.TransferableOutput{ 2060 &output, 2061 } 2062 return &txs.Tx{ 2063 Unsigned: &tx, 2064 Creds: creds, 2065 } 2066 }, 2067 err: secp256k1fx.ErrNoValueOutput, 2068 }, 2069 { 2070 name: "unsorted outputs", 2071 txFunc: func() *txs.Tx { 2072 output0 := output 2073 output0.Out = &secp256k1fx.TransferOutput{ 2074 Amt: 1, 2075 OutputOwners: outputOwners, 2076 } 2077 2078 output1 := output 2079 output1.Out = &secp256k1fx.TransferOutput{ 2080 Amt: 2, 2081 OutputOwners: outputOwners, 2082 } 2083 2084 outputs := []*avax.TransferableOutput{ 2085 &output0, 2086 &output1, 2087 } 2088 avax.SortTransferableOutputs(outputs, codec) 2089 outputs[0], outputs[1] = outputs[1], outputs[0] 2090 2091 tx := tx 2092 tx.Outs = outputs 2093 return &txs.Tx{ 2094 Unsigned: &tx, 2095 Creds: creds, 2096 } 2097 }, 2098 err: avax.ErrOutputsNotSorted, 2099 }, 2100 { 2101 name: "unsorted exported outputs", 2102 txFunc: func() *txs.Tx { 2103 output0 := output 2104 output0.Out = &secp256k1fx.TransferOutput{ 2105 Amt: 1, 2106 OutputOwners: outputOwners, 2107 } 2108 2109 output1 := output 2110 output1.Out = &secp256k1fx.TransferOutput{ 2111 Amt: 2, 2112 OutputOwners: outputOwners, 2113 } 2114 2115 outputs := []*avax.TransferableOutput{ 2116 &output0, 2117 &output1, 2118 } 2119 avax.SortTransferableOutputs(outputs, codec) 2120 outputs[0], outputs[1] = outputs[1], outputs[0] 2121 2122 tx := tx 2123 tx.ExportedOuts = outputs 2124 return &txs.Tx{ 2125 Unsigned: &tx, 2126 Creds: creds, 2127 } 2128 }, 2129 err: avax.ErrOutputsNotSorted, 2130 }, 2131 { 2132 name: "invalid input", 2133 txFunc: func() *txs.Tx { 2134 input := input 2135 input.In = &secp256k1fx.TransferInput{ 2136 Amt: 0, 2137 Input: inputSigners, 2138 } 2139 2140 tx := tx 2141 tx.Ins = []*avax.TransferableInput{ 2142 &input, 2143 } 2144 return &txs.Tx{ 2145 Unsigned: &tx, 2146 Creds: creds, 2147 } 2148 }, 2149 err: secp256k1fx.ErrNoValueInput, 2150 }, 2151 { 2152 name: "duplicate inputs", 2153 txFunc: func() *txs.Tx { 2154 tx := tx 2155 tx.Ins = []*avax.TransferableInput{ 2156 &input, 2157 &input, 2158 } 2159 return &txs.Tx{ 2160 Unsigned: &tx, 2161 Creds: []*fxs.FxCredential{ 2162 &cred, 2163 &cred, 2164 }, 2165 } 2166 }, 2167 err: avax.ErrInputsNotSortedUnique, 2168 }, 2169 { 2170 name: "input overflow", 2171 txFunc: func() *txs.Tx { 2172 input0 := input 2173 input0.In = &secp256k1fx.TransferInput{ 2174 Amt: 1, 2175 Input: inputSigners, 2176 } 2177 2178 input1 := input 2179 input1.UTXOID.OutputIndex++ 2180 input1.In = &secp256k1fx.TransferInput{ 2181 Amt: math.MaxUint64, 2182 Input: inputSigners, 2183 } 2184 2185 tx := tx 2186 tx.Ins = []*avax.TransferableInput{ 2187 &input0, 2188 &input1, 2189 } 2190 avax.SortTransferableInputsWithSigners(tx.Ins, make([][]*secp256k1.PrivateKey, 2)) 2191 return &txs.Tx{ 2192 Unsigned: &tx, 2193 Creds: []*fxs.FxCredential{ 2194 &cred, 2195 &cred, 2196 }, 2197 } 2198 }, 2199 err: safemath.ErrOverflow, 2200 }, 2201 { 2202 name: "output overflow", 2203 txFunc: func() *txs.Tx { 2204 output := output 2205 output.Out = &secp256k1fx.TransferOutput{ 2206 Amt: math.MaxUint64, 2207 OutputOwners: outputOwners, 2208 } 2209 2210 outputs := []*avax.TransferableOutput{ 2211 &output, 2212 } 2213 avax.SortTransferableOutputs(outputs, codec) 2214 2215 tx := tx 2216 tx.Outs = outputs 2217 return &txs.Tx{ 2218 Unsigned: &tx, 2219 Creds: creds, 2220 } 2221 }, 2222 err: safemath.ErrOverflow, 2223 }, 2224 { 2225 name: "insufficient funds", 2226 txFunc: func() *txs.Tx { 2227 input := input 2228 input.In = &secp256k1fx.TransferInput{ 2229 Amt: 1, 2230 Input: inputSigners, 2231 } 2232 2233 tx := tx 2234 tx.Ins = []*avax.TransferableInput{ 2235 &input, 2236 } 2237 return &txs.Tx{ 2238 Unsigned: &tx, 2239 Creds: creds, 2240 } 2241 }, 2242 err: avax.ErrInsufficientFunds, 2243 }, 2244 { 2245 name: "invalid credential", 2246 txFunc: func() *txs.Tx { 2247 return &txs.Tx{ 2248 Unsigned: &tx, 2249 Creds: []*fxs.FxCredential{{ 2250 Credential: (*secp256k1fx.Credential)(nil), 2251 }}, 2252 } 2253 }, 2254 err: secp256k1fx.ErrNilCredential, 2255 }, 2256 { 2257 name: "wrong number of credentials", 2258 txFunc: func() *txs.Tx { 2259 return &txs.Tx{ 2260 Unsigned: &tx, 2261 } 2262 }, 2263 err: errWrongNumberOfCredentials, 2264 }, 2265 { 2266 name: "barely sufficient funds", 2267 txFunc: func() *txs.Tx { 2268 input := input 2269 input.In = &secp256k1fx.TransferInput{ 2270 Amt: fxOutput.Amt + feeConfig.TxFee, 2271 Input: inputSigners, 2272 } 2273 2274 tx := tx 2275 tx.Ins = []*avax.TransferableInput{ 2276 &input, 2277 } 2278 return &txs.Tx{ 2279 Unsigned: &tx, 2280 Creds: creds, 2281 } 2282 }, 2283 err: nil, 2284 }, 2285 { 2286 name: "barely insufficient funds", 2287 txFunc: func() *txs.Tx { 2288 input := input 2289 input.In = &secp256k1fx.TransferInput{ 2290 Amt: fxOutput.Amt + feeConfig.TxFee - 1, 2291 Input: inputSigners, 2292 } 2293 2294 tx := tx 2295 tx.Ins = []*avax.TransferableInput{ 2296 &input, 2297 } 2298 return &txs.Tx{ 2299 Unsigned: &tx, 2300 Creds: creds, 2301 } 2302 }, 2303 err: avax.ErrInsufficientFunds, 2304 }, 2305 } 2306 for _, test := range tests { 2307 t.Run(test.name, func(t *testing.T) { 2308 tx := test.txFunc() 2309 verifier := &SyntacticVerifier{ 2310 Backend: backend, 2311 Tx: tx, 2312 } 2313 err := tx.Unsigned.Visit(verifier) 2314 require.ErrorIs(t, err, test.err) 2315 }) 2316 } 2317 }