github.com/dim4egster/coreth@v0.10.2/plugin/evm/export_tx_test.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package evm 5 6 import ( 7 "bytes" 8 "math/big" 9 "testing" 10 11 "github.com/dim4egster/qmallgo/chains/atomic" 12 "github.com/dim4egster/qmallgo/ids" 13 engCommon "github.com/dim4egster/qmallgo/snow/engine/common" 14 "github.com/dim4egster/qmallgo/utils/constants" 15 "github.com/dim4egster/qmallgo/utils/crypto" 16 "github.com/dim4egster/qmallgo/utils/units" 17 "github.com/dim4egster/qmallgo/vms/components/avax" 18 "github.com/dim4egster/qmallgo/vms/secp256k1fx" 19 "github.com/dim4egster/coreth/params" 20 "github.com/ethereum/go-ethereum/common" 21 ) 22 23 // createExportTxOptions adds funds to shared memory, imports them, and returns a list of export transactions 24 // that attempt to send the funds to each of the test keys (list of length 3). 25 func createExportTxOptions(t *testing.T, vm *VM, issuer chan engCommon.Message, sharedMemory *atomic.Memory) []*Tx { 26 // Add a UTXO to shared memory 27 utxo := &avax.UTXO{ 28 UTXOID: avax.UTXOID{TxID: ids.GenerateTestID()}, 29 Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, 30 Out: &secp256k1fx.TransferOutput{ 31 Amt: uint64(50000000), 32 OutputOwners: secp256k1fx.OutputOwners{ 33 Threshold: 1, 34 Addrs: []ids.ShortID{testKeys[0].PublicKey().Address()}, 35 }, 36 }, 37 } 38 utxoBytes, err := vm.codec.Marshal(codecVersion, utxo) 39 if err != nil { 40 t.Fatal(err) 41 } 42 43 xChainSharedMemory := sharedMemory.NewSharedMemory(vm.ctx.XChainID) 44 inputID := utxo.InputID() 45 if err := xChainSharedMemory.Apply(map[ids.ID]*atomic.Requests{vm.ctx.ChainID: {PutRequests: []*atomic.Element{{ 46 Key: inputID[:], 47 Value: utxoBytes, 48 Traits: [][]byte{ 49 testKeys[0].PublicKey().Address().Bytes(), 50 }, 51 }}}}); err != nil { 52 t.Fatal(err) 53 } 54 55 // Import the funds 56 importTx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*crypto.PrivateKeySECP256K1R{testKeys[0]}) 57 if err != nil { 58 t.Fatal(err) 59 } 60 61 if err := vm.issueTx(importTx, true /*=local*/); err != nil { 62 t.Fatal(err) 63 } 64 65 <-issuer 66 67 blk, err := vm.BuildBlock() 68 if err != nil { 69 t.Fatal(err) 70 } 71 72 if err := blk.Verify(); err != nil { 73 t.Fatal(err) 74 } 75 76 if err := vm.SetPreference(blk.ID()); err != nil { 77 t.Fatal(err) 78 } 79 80 if err := blk.Accept(); err != nil { 81 t.Fatal(err) 82 } 83 84 // Use the funds to create 3 conflicting export transactions sending the funds to each of the test addresses 85 exportTxs := make([]*Tx, 0, 3) 86 for _, addr := range testShortIDAddrs { 87 exportTx, err := vm.newExportTx(vm.ctx.AVAXAssetID, uint64(5000000), vm.ctx.XChainID, addr, initialBaseFee, []*crypto.PrivateKeySECP256K1R{testKeys[0]}) 88 if err != nil { 89 t.Fatal(err) 90 } 91 exportTxs = append(exportTxs, exportTx) 92 } 93 94 return exportTxs 95 } 96 97 func TestExportTxEVMStateTransfer(t *testing.T) { 98 key := testKeys[0] 99 addr := key.PublicKey().Address() 100 ethAddr := GetEthAddress(key) 101 102 avaxAmount := 50 * units.MilliAvax 103 avaxUTXOID := avax.UTXOID{ 104 OutputIndex: 0, 105 } 106 avaxInputID := avaxUTXOID.InputID() 107 108 customAmount := uint64(100) 109 customAssetID := ids.ID{1, 2, 3, 4, 5, 7} 110 customUTXOID := avax.UTXOID{ 111 OutputIndex: 1, 112 } 113 customInputID := customUTXOID.InputID() 114 115 customUTXO := &avax.UTXO{ 116 UTXOID: customUTXOID, 117 Asset: avax.Asset{ID: customAssetID}, 118 Out: &secp256k1fx.TransferOutput{ 119 Amt: customAmount, 120 OutputOwners: secp256k1fx.OutputOwners{ 121 Threshold: 1, 122 Addrs: []ids.ShortID{addr}, 123 }, 124 }, 125 } 126 127 tests := []struct { 128 name string 129 tx []EVMInput 130 avaxBalance *big.Int 131 balances map[ids.ID]*big.Int 132 expectedNonce uint64 133 shouldErr bool 134 }{ 135 { 136 name: "no transfers", 137 tx: nil, 138 avaxBalance: big.NewInt(int64(avaxAmount) * x2cRateInt64), 139 balances: map[ids.ID]*big.Int{ 140 customAssetID: big.NewInt(int64(customAmount)), 141 }, 142 expectedNonce: 0, 143 shouldErr: false, 144 }, 145 { 146 name: "spend half AVAX", 147 tx: []EVMInput{ 148 { 149 Address: ethAddr, 150 Amount: avaxAmount / 2, 151 AssetID: testAvaxAssetID, 152 Nonce: 0, 153 }, 154 }, 155 avaxBalance: big.NewInt(int64(avaxAmount/2) * x2cRateInt64), 156 balances: map[ids.ID]*big.Int{ 157 customAssetID: big.NewInt(int64(customAmount)), 158 }, 159 expectedNonce: 1, 160 shouldErr: false, 161 }, 162 { 163 name: "spend all AVAX", 164 tx: []EVMInput{ 165 { 166 Address: ethAddr, 167 Amount: avaxAmount, 168 AssetID: testAvaxAssetID, 169 Nonce: 0, 170 }, 171 }, 172 avaxBalance: big.NewInt(0), 173 balances: map[ids.ID]*big.Int{ 174 customAssetID: big.NewInt(int64(customAmount)), 175 }, 176 expectedNonce: 1, 177 shouldErr: false, 178 }, 179 { 180 name: "spend too much AVAX", 181 tx: []EVMInput{ 182 { 183 Address: ethAddr, 184 Amount: avaxAmount + 1, 185 AssetID: testAvaxAssetID, 186 Nonce: 0, 187 }, 188 }, 189 avaxBalance: big.NewInt(0), 190 balances: map[ids.ID]*big.Int{ 191 customAssetID: big.NewInt(int64(customAmount)), 192 }, 193 expectedNonce: 1, 194 shouldErr: true, 195 }, 196 { 197 name: "spend half custom", 198 tx: []EVMInput{ 199 { 200 Address: ethAddr, 201 Amount: customAmount / 2, 202 AssetID: customAssetID, 203 Nonce: 0, 204 }, 205 }, 206 avaxBalance: big.NewInt(int64(avaxAmount) * x2cRateInt64), 207 balances: map[ids.ID]*big.Int{ 208 customAssetID: big.NewInt(int64(customAmount / 2)), 209 }, 210 expectedNonce: 1, 211 shouldErr: false, 212 }, 213 { 214 name: "spend all custom", 215 tx: []EVMInput{ 216 { 217 Address: ethAddr, 218 Amount: customAmount, 219 AssetID: customAssetID, 220 Nonce: 0, 221 }, 222 }, 223 avaxBalance: big.NewInt(int64(avaxAmount) * x2cRateInt64), 224 balances: map[ids.ID]*big.Int{ 225 customAssetID: big.NewInt(0), 226 }, 227 expectedNonce: 1, 228 shouldErr: false, 229 }, 230 { 231 name: "spend too much custom", 232 tx: []EVMInput{ 233 { 234 Address: ethAddr, 235 Amount: customAmount + 1, 236 AssetID: customAssetID, 237 Nonce: 0, 238 }, 239 }, 240 avaxBalance: big.NewInt(int64(avaxAmount) * x2cRateInt64), 241 balances: map[ids.ID]*big.Int{ 242 customAssetID: big.NewInt(0), 243 }, 244 expectedNonce: 1, 245 shouldErr: true, 246 }, 247 { 248 name: "spend everything", 249 tx: []EVMInput{ 250 { 251 Address: ethAddr, 252 Amount: customAmount, 253 AssetID: customAssetID, 254 Nonce: 0, 255 }, 256 { 257 Address: ethAddr, 258 Amount: avaxAmount, 259 AssetID: testAvaxAssetID, 260 Nonce: 0, 261 }, 262 }, 263 avaxBalance: big.NewInt(0), 264 balances: map[ids.ID]*big.Int{ 265 customAssetID: big.NewInt(0), 266 }, 267 expectedNonce: 1, 268 shouldErr: false, 269 }, 270 { 271 name: "spend everything wrong nonce", 272 tx: []EVMInput{ 273 { 274 Address: ethAddr, 275 Amount: customAmount, 276 AssetID: customAssetID, 277 Nonce: 1, 278 }, 279 { 280 Address: ethAddr, 281 Amount: avaxAmount, 282 AssetID: testAvaxAssetID, 283 Nonce: 1, 284 }, 285 }, 286 avaxBalance: big.NewInt(0), 287 balances: map[ids.ID]*big.Int{ 288 customAssetID: big.NewInt(0), 289 }, 290 expectedNonce: 1, 291 shouldErr: true, 292 }, 293 { 294 name: "spend everything changing nonces", 295 tx: []EVMInput{ 296 { 297 Address: ethAddr, 298 Amount: customAmount, 299 AssetID: customAssetID, 300 Nonce: 0, 301 }, 302 { 303 Address: ethAddr, 304 Amount: avaxAmount, 305 AssetID: testAvaxAssetID, 306 Nonce: 1, 307 }, 308 }, 309 avaxBalance: big.NewInt(0), 310 balances: map[ids.ID]*big.Int{ 311 customAssetID: big.NewInt(0), 312 }, 313 expectedNonce: 1, 314 shouldErr: true, 315 }, 316 } 317 for _, test := range tests { 318 t.Run(test.name, func(t *testing.T) { 319 issuer, vm, _, sharedMemory, _ := GenesisVM(t, true, genesisJSONApricotPhase0, "", "") 320 defer func() { 321 if err := vm.Shutdown(); err != nil { 322 t.Fatal(err) 323 } 324 }() 325 326 avaxUTXO := &avax.UTXO{ 327 UTXOID: avaxUTXOID, 328 Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, 329 Out: &secp256k1fx.TransferOutput{ 330 Amt: avaxAmount, 331 OutputOwners: secp256k1fx.OutputOwners{ 332 Threshold: 1, 333 Addrs: []ids.ShortID{addr}, 334 }, 335 }, 336 } 337 338 avaxUTXOBytes, err := vm.codec.Marshal(codecVersion, avaxUTXO) 339 if err != nil { 340 t.Fatal(err) 341 } 342 343 customUTXOBytes, err := vm.codec.Marshal(codecVersion, customUTXO) 344 if err != nil { 345 t.Fatal(err) 346 } 347 348 xChainSharedMemory := sharedMemory.NewSharedMemory(vm.ctx.XChainID) 349 if err := xChainSharedMemory.Apply(map[ids.ID]*atomic.Requests{vm.ctx.ChainID: {PutRequests: []*atomic.Element{ 350 { 351 Key: avaxInputID[:], 352 Value: avaxUTXOBytes, 353 Traits: [][]byte{ 354 addr.Bytes(), 355 }, 356 }, 357 { 358 Key: customInputID[:], 359 Value: customUTXOBytes, 360 Traits: [][]byte{ 361 addr.Bytes(), 362 }, 363 }, 364 }}}); err != nil { 365 t.Fatal(err) 366 } 367 368 tx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*crypto.PrivateKeySECP256K1R{testKeys[0]}) 369 if err != nil { 370 t.Fatal(err) 371 } 372 373 if err := vm.issueTx(tx, true /*=local*/); err != nil { 374 t.Fatal(err) 375 } 376 377 <-issuer 378 379 blk, err := vm.BuildBlock() 380 if err != nil { 381 t.Fatal(err) 382 } 383 384 if err := blk.Verify(); err != nil { 385 t.Fatal(err) 386 } 387 388 if err := vm.SetPreference(blk.ID()); err != nil { 389 t.Fatal(err) 390 } 391 392 if err := blk.Accept(); err != nil { 393 t.Fatal(err) 394 } 395 396 newTx := UnsignedExportTx{ 397 Ins: test.tx, 398 } 399 400 stateDB, err := vm.blockChain.State() 401 if err != nil { 402 t.Fatal(err) 403 } 404 405 err = newTx.EVMStateTransfer(vm.ctx, stateDB) 406 if test.shouldErr { 407 if err == nil { 408 t.Fatal("expected EVMStateTransfer to fail") 409 } 410 return 411 } 412 if err != nil { 413 t.Fatal(err) 414 } 415 416 avaxBalance := stateDB.GetBalance(ethAddr) 417 if avaxBalance.Cmp(test.avaxBalance) != 0 { 418 t.Fatalf("address balance %s equal %s not %s", addr.String(), avaxBalance, test.avaxBalance) 419 } 420 421 for assetID, expectedBalance := range test.balances { 422 balance := stateDB.GetBalanceMultiCoin(ethAddr, common.Hash(assetID)) 423 if avaxBalance.Cmp(test.avaxBalance) != 0 { 424 t.Fatalf("%s address balance %s equal %s not %s", assetID, addr.String(), balance, expectedBalance) 425 } 426 } 427 428 if stateDB.GetNonce(ethAddr) != test.expectedNonce { 429 t.Fatalf("failed to set nonce to %d", test.expectedNonce) 430 } 431 }) 432 } 433 } 434 435 func TestExportTxSemanticVerify(t *testing.T) { 436 _, vm, _, _, _ := GenesisVM(t, true, genesisJSONApricotPhase0, "", "") 437 438 defer func() { 439 if err := vm.Shutdown(); err != nil { 440 t.Fatal(err) 441 } 442 }() 443 444 parent := vm.LastAcceptedBlockInternal().(*Block) 445 446 key := testKeys[0] 447 addr := key.PublicKey().Address() 448 ethAddr := testEthAddrs[0] 449 450 var ( 451 avaxBalance = 10 * units.Avax 452 custom0Balance uint64 = 100 453 custom0AssetID = ids.ID{1, 2, 3, 4, 5} 454 custom1Balance uint64 = 1000 455 custom1AssetID = ids.ID{1, 2, 3, 4, 5, 6} 456 ) 457 458 validExportTx := &UnsignedExportTx{ 459 NetworkID: vm.ctx.NetworkID, 460 BlockchainID: vm.ctx.ChainID, 461 DestinationChain: vm.ctx.XChainID, 462 Ins: []EVMInput{ 463 { 464 Address: ethAddr, 465 Amount: avaxBalance, 466 AssetID: vm.ctx.AVAXAssetID, 467 Nonce: 0, 468 }, 469 { 470 Address: ethAddr, 471 Amount: custom0Balance, 472 AssetID: custom0AssetID, 473 Nonce: 0, 474 }, 475 { 476 Address: ethAddr, 477 Amount: custom1Balance, 478 AssetID: custom1AssetID, 479 Nonce: 0, 480 }, 481 }, 482 ExportedOutputs: []*avax.TransferableOutput{ 483 { 484 Asset: avax.Asset{ID: custom0AssetID}, 485 Out: &secp256k1fx.TransferOutput{ 486 Amt: custom0Balance, 487 OutputOwners: secp256k1fx.OutputOwners{ 488 Threshold: 1, 489 Addrs: []ids.ShortID{addr}, 490 }, 491 }, 492 }, 493 }, 494 } 495 496 validAVAXExportTx := &UnsignedExportTx{ 497 NetworkID: vm.ctx.NetworkID, 498 BlockchainID: vm.ctx.ChainID, 499 DestinationChain: vm.ctx.XChainID, 500 Ins: []EVMInput{ 501 { 502 Address: ethAddr, 503 Amount: avaxBalance, 504 AssetID: vm.ctx.AVAXAssetID, 505 Nonce: 0, 506 }, 507 }, 508 ExportedOutputs: []*avax.TransferableOutput{ 509 { 510 Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, 511 Out: &secp256k1fx.TransferOutput{ 512 Amt: avaxBalance / 2, 513 OutputOwners: secp256k1fx.OutputOwners{ 514 Threshold: 1, 515 Addrs: []ids.ShortID{addr}, 516 }, 517 }, 518 }, 519 }, 520 } 521 522 tests := []struct { 523 name string 524 tx *Tx 525 signers [][]*crypto.PrivateKeySECP256K1R 526 baseFee *big.Int 527 rules params.Rules 528 shouldErr bool 529 }{ 530 { 531 name: "valid", 532 tx: &Tx{UnsignedAtomicTx: validExportTx}, 533 signers: [][]*crypto.PrivateKeySECP256K1R{ 534 {key}, 535 {key}, 536 {key}, 537 }, 538 baseFee: initialBaseFee, 539 rules: apricotRulesPhase3, 540 shouldErr: false, 541 }, 542 { 543 name: "P-chain before AP5", 544 tx: func() *Tx { 545 validExportTx := *validAVAXExportTx 546 validExportTx.DestinationChain = constants.PlatformChainID 547 return &Tx{UnsignedAtomicTx: &validExportTx} 548 }(), 549 signers: [][]*crypto.PrivateKeySECP256K1R{ 550 {key}, 551 }, 552 baseFee: initialBaseFee, 553 rules: apricotRulesPhase3, 554 shouldErr: true, 555 }, 556 { 557 name: "P-chain after AP5", 558 tx: func() *Tx { 559 validExportTx := *validAVAXExportTx 560 validExportTx.DestinationChain = constants.PlatformChainID 561 return &Tx{UnsignedAtomicTx: &validExportTx} 562 }(), 563 signers: [][]*crypto.PrivateKeySECP256K1R{ 564 {key}, 565 }, 566 baseFee: initialBaseFee, 567 rules: apricotRulesPhase5, 568 shouldErr: false, 569 }, 570 { 571 name: "random chain after AP5", 572 tx: func() *Tx { 573 validExportTx := *validAVAXExportTx 574 validExportTx.DestinationChain = ids.GenerateTestID() 575 return &Tx{UnsignedAtomicTx: &validExportTx} 576 }(), 577 signers: [][]*crypto.PrivateKeySECP256K1R{ 578 {key}, 579 }, 580 baseFee: initialBaseFee, 581 rules: apricotRulesPhase5, 582 shouldErr: true, 583 }, 584 { 585 name: "P-chain multi-coin before AP5", 586 tx: func() *Tx { 587 validExportTx := *validExportTx 588 validExportTx.DestinationChain = constants.PlatformChainID 589 return &Tx{UnsignedAtomicTx: &validExportTx} 590 }(), 591 signers: [][]*crypto.PrivateKeySECP256K1R{ 592 {key}, 593 {key}, 594 {key}, 595 }, 596 baseFee: initialBaseFee, 597 rules: apricotRulesPhase3, 598 shouldErr: true, 599 }, 600 { 601 name: "P-chain multi-coin after AP5", 602 tx: func() *Tx { 603 validExportTx := *validExportTx 604 validExportTx.DestinationChain = constants.PlatformChainID 605 return &Tx{UnsignedAtomicTx: &validExportTx} 606 }(), 607 signers: [][]*crypto.PrivateKeySECP256K1R{ 608 {key}, 609 {key}, 610 {key}, 611 }, 612 baseFee: initialBaseFee, 613 rules: apricotRulesPhase5, 614 shouldErr: true, 615 }, 616 { 617 name: "random chain multi-coin after AP5", 618 tx: func() *Tx { 619 validExportTx := *validExportTx 620 validExportTx.DestinationChain = ids.GenerateTestID() 621 return &Tx{UnsignedAtomicTx: &validExportTx} 622 }(), 623 signers: [][]*crypto.PrivateKeySECP256K1R{ 624 {key}, 625 {key}, 626 {key}, 627 }, 628 baseFee: initialBaseFee, 629 rules: apricotRulesPhase5, 630 shouldErr: true, 631 }, 632 { 633 name: "no outputs", 634 tx: func() *Tx { 635 validExportTx := *validExportTx 636 validExportTx.ExportedOutputs = nil 637 return &Tx{UnsignedAtomicTx: &validExportTx} 638 }(), 639 signers: [][]*crypto.PrivateKeySECP256K1R{ 640 {key}, 641 {key}, 642 {key}, 643 }, 644 baseFee: initialBaseFee, 645 rules: apricotRulesPhase3, 646 shouldErr: true, 647 }, 648 { 649 name: "wrong networkID", 650 tx: func() *Tx { 651 validExportTx := *validExportTx 652 validExportTx.NetworkID++ 653 return &Tx{UnsignedAtomicTx: &validExportTx} 654 }(), 655 signers: [][]*crypto.PrivateKeySECP256K1R{ 656 {key}, 657 {key}, 658 {key}, 659 }, 660 baseFee: initialBaseFee, 661 rules: apricotRulesPhase3, 662 shouldErr: true, 663 }, 664 { 665 name: "wrong chainID", 666 tx: func() *Tx { 667 validExportTx := *validExportTx 668 validExportTx.BlockchainID = ids.GenerateTestID() 669 return &Tx{UnsignedAtomicTx: &validExportTx} 670 }(), 671 signers: [][]*crypto.PrivateKeySECP256K1R{ 672 {key}, 673 {key}, 674 {key}, 675 }, 676 baseFee: initialBaseFee, 677 rules: apricotRulesPhase3, 678 shouldErr: true, 679 }, 680 { 681 name: "invalid input", 682 tx: func() *Tx { 683 validExportTx := *validExportTx 684 validExportTx.Ins = append([]EVMInput{}, validExportTx.Ins...) 685 validExportTx.Ins[2].Amount = 0 686 return &Tx{UnsignedAtomicTx: &validExportTx} 687 }(), 688 signers: [][]*crypto.PrivateKeySECP256K1R{ 689 {key}, 690 {key}, 691 {key}, 692 }, 693 baseFee: initialBaseFee, 694 rules: apricotRulesPhase3, 695 shouldErr: true, 696 }, 697 { 698 name: "invalid output", 699 tx: func() *Tx { 700 validExportTx := *validExportTx 701 validExportTx.ExportedOutputs = []*avax.TransferableOutput{{ 702 Asset: avax.Asset{ID: custom0AssetID}, 703 Out: &secp256k1fx.TransferOutput{ 704 Amt: custom0Balance, 705 OutputOwners: secp256k1fx.OutputOwners{ 706 Threshold: 0, 707 Addrs: []ids.ShortID{addr}, 708 }, 709 }, 710 }} 711 return &Tx{UnsignedAtomicTx: &validExportTx} 712 }(), 713 signers: [][]*crypto.PrivateKeySECP256K1R{ 714 {key}, 715 {key}, 716 {key}, 717 }, 718 baseFee: initialBaseFee, 719 rules: apricotRulesPhase3, 720 shouldErr: true, 721 }, 722 { 723 name: "unsorted outputs", 724 tx: func() *Tx { 725 validExportTx := *validExportTx 726 exportOutputs := []*avax.TransferableOutput{ 727 { 728 Asset: avax.Asset{ID: custom0AssetID}, 729 Out: &secp256k1fx.TransferOutput{ 730 Amt: custom0Balance/2 + 1, 731 OutputOwners: secp256k1fx.OutputOwners{ 732 Threshold: 1, 733 Addrs: []ids.ShortID{addr}, 734 }, 735 }, 736 }, 737 { 738 Asset: avax.Asset{ID: custom0AssetID}, 739 Out: &secp256k1fx.TransferOutput{ 740 Amt: custom0Balance/2 - 1, 741 OutputOwners: secp256k1fx.OutputOwners{ 742 Threshold: 1, 743 Addrs: []ids.ShortID{addr}, 744 }, 745 }, 746 }, 747 } 748 // Sort the outputs and then swap the ordering to ensure that they are ordered incorrectly 749 avax.SortTransferableOutputs(exportOutputs, Codec) 750 exportOutputs[0], exportOutputs[1] = exportOutputs[1], exportOutputs[0] 751 validExportTx.ExportedOutputs = exportOutputs 752 return &Tx{UnsignedAtomicTx: &validExportTx} 753 }(), 754 signers: [][]*crypto.PrivateKeySECP256K1R{ 755 {key}, 756 {key}, 757 {key}, 758 }, 759 baseFee: initialBaseFee, 760 rules: apricotRulesPhase3, 761 shouldErr: true, 762 }, 763 { 764 name: "not unique inputs", 765 tx: func() *Tx { 766 validExportTx := *validExportTx 767 validExportTx.Ins = append([]EVMInput{}, validExportTx.Ins...) 768 validExportTx.Ins[2] = validExportTx.Ins[1] 769 return &Tx{UnsignedAtomicTx: &validExportTx} 770 }(), 771 signers: [][]*crypto.PrivateKeySECP256K1R{ 772 {key}, 773 {key}, 774 {key}, 775 }, 776 baseFee: initialBaseFee, 777 rules: apricotRulesPhase3, 778 shouldErr: true, 779 }, 780 { 781 name: "custom asset insufficient funds", 782 tx: func() *Tx { 783 validExportTx := *validExportTx 784 validExportTx.ExportedOutputs = []*avax.TransferableOutput{ 785 { 786 Asset: avax.Asset{ID: custom0AssetID}, 787 Out: &secp256k1fx.TransferOutput{ 788 Amt: custom0Balance + 1, 789 OutputOwners: secp256k1fx.OutputOwners{ 790 Threshold: 1, 791 Addrs: []ids.ShortID{addr}, 792 }, 793 }, 794 }, 795 } 796 return &Tx{UnsignedAtomicTx: &validExportTx} 797 }(), 798 signers: [][]*crypto.PrivateKeySECP256K1R{ 799 {key}, 800 {key}, 801 {key}, 802 }, 803 baseFee: initialBaseFee, 804 rules: apricotRulesPhase3, 805 shouldErr: true, 806 }, 807 { 808 name: "avax insufficient funds", 809 tx: func() *Tx { 810 validExportTx := *validExportTx 811 validExportTx.ExportedOutputs = []*avax.TransferableOutput{ 812 { 813 Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, 814 Out: &secp256k1fx.TransferOutput{ 815 Amt: avaxBalance, // after fees this should be too much 816 OutputOwners: secp256k1fx.OutputOwners{ 817 Threshold: 1, 818 Addrs: []ids.ShortID{addr}, 819 }, 820 }, 821 }, 822 } 823 return &Tx{UnsignedAtomicTx: &validExportTx} 824 }(), 825 signers: [][]*crypto.PrivateKeySECP256K1R{ 826 {key}, 827 {key}, 828 {key}, 829 }, 830 baseFee: initialBaseFee, 831 rules: apricotRulesPhase3, 832 shouldErr: true, 833 }, 834 { 835 name: "too many signatures", 836 tx: &Tx{UnsignedAtomicTx: validExportTx}, 837 signers: [][]*crypto.PrivateKeySECP256K1R{ 838 {key}, 839 {key}, 840 {key}, 841 {key}, 842 }, 843 baseFee: initialBaseFee, 844 rules: apricotRulesPhase3, 845 shouldErr: true, 846 }, 847 { 848 name: "too few signatures", 849 tx: &Tx{UnsignedAtomicTx: validExportTx}, 850 signers: [][]*crypto.PrivateKeySECP256K1R{ 851 {key}, 852 {key}, 853 }, 854 baseFee: initialBaseFee, 855 rules: apricotRulesPhase3, 856 shouldErr: true, 857 }, 858 { 859 name: "too many signatures on credential", 860 tx: &Tx{UnsignedAtomicTx: validExportTx}, 861 signers: [][]*crypto.PrivateKeySECP256K1R{ 862 {key, testKeys[1]}, 863 {key}, 864 {key}, 865 }, 866 baseFee: initialBaseFee, 867 rules: apricotRulesPhase3, 868 shouldErr: true, 869 }, 870 { 871 name: "too few signatures on credential", 872 tx: &Tx{UnsignedAtomicTx: validExportTx}, 873 signers: [][]*crypto.PrivateKeySECP256K1R{ 874 {}, 875 {key}, 876 {key}, 877 }, 878 baseFee: initialBaseFee, 879 rules: apricotRulesPhase3, 880 shouldErr: true, 881 }, 882 { 883 name: "wrong signature on credential", 884 tx: &Tx{UnsignedAtomicTx: validExportTx}, 885 signers: [][]*crypto.PrivateKeySECP256K1R{ 886 {testKeys[1]}, 887 {key}, 888 {key}, 889 }, 890 baseFee: initialBaseFee, 891 rules: apricotRulesPhase3, 892 shouldErr: true, 893 }, 894 { 895 name: "no signatures", 896 tx: &Tx{UnsignedAtomicTx: validExportTx}, 897 signers: [][]*crypto.PrivateKeySECP256K1R{}, 898 baseFee: initialBaseFee, 899 rules: apricotRulesPhase3, 900 shouldErr: true, 901 }, 902 } 903 for _, test := range tests { 904 if err := test.tx.Sign(vm.codec, test.signers); err != nil { 905 t.Fatal(err) 906 } 907 908 t.Run(test.name, func(t *testing.T) { 909 tx := test.tx 910 exportTx := tx.UnsignedAtomicTx 911 912 err := exportTx.SemanticVerify(vm, tx, parent, test.baseFee, test.rules) 913 if test.shouldErr && err == nil { 914 t.Fatalf("should have errored but returned valid") 915 } 916 if !test.shouldErr && err != nil { 917 t.Fatalf("shouldn't have errored but returned %s", err) 918 } 919 }) 920 } 921 } 922 923 func TestExportTxAccept(t *testing.T) { 924 _, vm, _, sharedMemory, _ := GenesisVM(t, true, genesisJSONApricotPhase0, "", "") 925 926 xChainSharedMemory := sharedMemory.NewSharedMemory(vm.ctx.XChainID) 927 928 defer func() { 929 if err := vm.Shutdown(); err != nil { 930 t.Fatal(err) 931 } 932 }() 933 934 key := testKeys[0] 935 addr := key.PublicKey().Address() 936 ethAddr := testEthAddrs[0] 937 938 var ( 939 avaxBalance = 10 * units.Avax 940 custom0Balance uint64 = 100 941 custom0AssetID = ids.ID{1, 2, 3, 4, 5} 942 ) 943 944 exportTx := &UnsignedExportTx{ 945 NetworkID: vm.ctx.NetworkID, 946 BlockchainID: vm.ctx.ChainID, 947 DestinationChain: vm.ctx.XChainID, 948 Ins: []EVMInput{ 949 { 950 Address: ethAddr, 951 Amount: avaxBalance, 952 AssetID: vm.ctx.AVAXAssetID, 953 Nonce: 0, 954 }, 955 { 956 Address: ethAddr, 957 Amount: custom0Balance, 958 AssetID: custom0AssetID, 959 Nonce: 0, 960 }, 961 }, 962 ExportedOutputs: []*avax.TransferableOutput{ 963 { 964 Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, 965 Out: &secp256k1fx.TransferOutput{ 966 Amt: avaxBalance, 967 OutputOwners: secp256k1fx.OutputOwners{ 968 Threshold: 1, 969 Addrs: []ids.ShortID{addr}, 970 }, 971 }, 972 }, 973 { 974 Asset: avax.Asset{ID: custom0AssetID}, 975 Out: &secp256k1fx.TransferOutput{ 976 Amt: custom0Balance, 977 OutputOwners: secp256k1fx.OutputOwners{ 978 Threshold: 1, 979 Addrs: []ids.ShortID{addr}, 980 }, 981 }, 982 }, 983 }, 984 } 985 986 tx := &Tx{UnsignedAtomicTx: exportTx} 987 988 signers := [][]*crypto.PrivateKeySECP256K1R{ 989 {key}, 990 {key}, 991 {key}, 992 } 993 994 if err := tx.Sign(vm.codec, signers); err != nil { 995 t.Fatal(err) 996 } 997 998 commitBatch, err := vm.db.CommitBatch() 999 if err != nil { 1000 t.Fatalf("Failed to create commit batch for VM due to %s", err) 1001 } 1002 chainID, atomicRequests, err := tx.AtomicOps() 1003 if err != nil { 1004 t.Fatalf("Failed to accept export transaction due to: %s", err) 1005 } 1006 1007 if err := vm.ctx.SharedMemory.Apply(map[ids.ID]*atomic.Requests{chainID: {PutRequests: atomicRequests.PutRequests}}, commitBatch); err != nil { 1008 t.Fatal(err) 1009 } 1010 indexedValues, _, _, err := xChainSharedMemory.Indexed(vm.ctx.ChainID, [][]byte{addr.Bytes()}, nil, nil, 3) 1011 if err != nil { 1012 t.Fatal(err) 1013 } 1014 1015 if len(indexedValues) != 2 { 1016 t.Fatalf("expected 2 values but got %d", len(indexedValues)) 1017 } 1018 1019 avaxUTXOID := avax.UTXOID{ 1020 TxID: tx.ID(), 1021 OutputIndex: 0, 1022 } 1023 avaxInputID := avaxUTXOID.InputID() 1024 1025 customUTXOID := avax.UTXOID{ 1026 TxID: tx.ID(), 1027 OutputIndex: 1, 1028 } 1029 customInputID := customUTXOID.InputID() 1030 1031 fetchedValues, err := xChainSharedMemory.Get(vm.ctx.ChainID, [][]byte{ 1032 customInputID[:], 1033 avaxInputID[:], 1034 }) 1035 if err != nil { 1036 t.Fatal(err) 1037 } 1038 1039 if !bytes.Equal(fetchedValues[0], indexedValues[0]) { 1040 t.Fatalf("inconsistent values returned fetched %x indexed %x", fetchedValues[0], indexedValues[0]) 1041 } 1042 if !bytes.Equal(fetchedValues[1], indexedValues[1]) { 1043 t.Fatalf("inconsistent values returned fetched %x indexed %x", fetchedValues[1], indexedValues[1]) 1044 } 1045 1046 customUTXOBytes, err := Codec.Marshal(codecVersion, &avax.UTXO{ 1047 UTXOID: customUTXOID, 1048 Asset: avax.Asset{ID: custom0AssetID}, 1049 Out: exportTx.ExportedOutputs[1].Out, 1050 }) 1051 if err != nil { 1052 t.Fatal(err) 1053 } 1054 1055 avaxUTXOBytes, err := Codec.Marshal(codecVersion, &avax.UTXO{ 1056 UTXOID: avaxUTXOID, 1057 Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, 1058 Out: exportTx.ExportedOutputs[0].Out, 1059 }) 1060 if err != nil { 1061 t.Fatal(err) 1062 } 1063 1064 if !bytes.Equal(fetchedValues[0], customUTXOBytes) { 1065 t.Fatalf("incorrect values returned expected %x got %x", customUTXOBytes, fetchedValues[0]) 1066 } 1067 if !bytes.Equal(fetchedValues[1], avaxUTXOBytes) { 1068 t.Fatalf("incorrect values returned expected %x got %x", avaxUTXOBytes, fetchedValues[1]) 1069 } 1070 } 1071 1072 func TestExportTxVerify(t *testing.T) { 1073 var exportAmount uint64 = 10000000 1074 exportTx := &UnsignedExportTx{ 1075 NetworkID: testNetworkID, 1076 BlockchainID: testCChainID, 1077 DestinationChain: testXChainID, 1078 Ins: []EVMInput{ 1079 { 1080 Address: testEthAddrs[0], 1081 Amount: exportAmount, 1082 AssetID: testAvaxAssetID, 1083 Nonce: 0, 1084 }, 1085 { 1086 Address: testEthAddrs[2], 1087 Amount: exportAmount, 1088 AssetID: testAvaxAssetID, 1089 Nonce: 0, 1090 }, 1091 }, 1092 ExportedOutputs: []*avax.TransferableOutput{ 1093 { 1094 Asset: avax.Asset{ID: testAvaxAssetID}, 1095 Out: &secp256k1fx.TransferOutput{ 1096 Amt: exportAmount, 1097 OutputOwners: secp256k1fx.OutputOwners{ 1098 Locktime: 0, 1099 Threshold: 1, 1100 Addrs: []ids.ShortID{testShortIDAddrs[0]}, 1101 }, 1102 }, 1103 }, 1104 { 1105 Asset: avax.Asset{ID: testAvaxAssetID}, 1106 Out: &secp256k1fx.TransferOutput{ 1107 Amt: exportAmount, 1108 OutputOwners: secp256k1fx.OutputOwners{ 1109 Locktime: 0, 1110 Threshold: 1, 1111 Addrs: []ids.ShortID{testShortIDAddrs[1]}, 1112 }, 1113 }, 1114 }, 1115 }, 1116 } 1117 1118 // Sort the inputs and outputs to ensure the transaction is canonical 1119 avax.SortTransferableOutputs(exportTx.ExportedOutputs, Codec) 1120 // Pass in a list of signers here with the appropriate length 1121 // to avoid causing a nil-pointer error in the helper method 1122 emptySigners := make([][]*crypto.PrivateKeySECP256K1R, 2) 1123 SortEVMInputsAndSigners(exportTx.Ins, emptySigners) 1124 1125 ctx := NewContext() 1126 1127 tests := map[string]atomicTxVerifyTest{ 1128 "nil tx": { 1129 generate: func(t *testing.T) UnsignedAtomicTx { 1130 return (*UnsignedExportTx)(nil) 1131 }, 1132 ctx: ctx, 1133 rules: apricotRulesPhase0, 1134 expectedErr: errNilTx.Error(), 1135 }, 1136 "valid export tx": { 1137 generate: func(t *testing.T) UnsignedAtomicTx { 1138 return exportTx 1139 }, 1140 ctx: ctx, 1141 rules: apricotRulesPhase0, 1142 expectedErr: "", 1143 }, 1144 "valid export tx banff": { 1145 generate: func(t *testing.T) UnsignedAtomicTx { 1146 return exportTx 1147 }, 1148 ctx: ctx, 1149 rules: banffRules, 1150 expectedErr: "", 1151 }, 1152 "incorrect networkID": { 1153 generate: func(t *testing.T) UnsignedAtomicTx { 1154 tx := *exportTx 1155 tx.NetworkID++ 1156 return &tx 1157 }, 1158 ctx: ctx, 1159 rules: apricotRulesPhase0, 1160 expectedErr: errWrongNetworkID.Error(), 1161 }, 1162 "incorrect blockchainID": { 1163 generate: func(t *testing.T) UnsignedAtomicTx { 1164 tx := *exportTx 1165 tx.BlockchainID = nonExistentID 1166 return &tx 1167 }, 1168 ctx: ctx, 1169 rules: apricotRulesPhase0, 1170 expectedErr: errWrongBlockchainID.Error(), 1171 }, 1172 "incorrect destination chain": { 1173 generate: func(t *testing.T) UnsignedAtomicTx { 1174 tx := *exportTx 1175 tx.DestinationChain = nonExistentID 1176 return &tx 1177 }, 1178 ctx: ctx, 1179 rules: apricotRulesPhase0, 1180 expectedErr: errWrongChainID.Error(), // TODO make this error more specific to destination not just chainID 1181 }, 1182 "no exported outputs": { 1183 generate: func(t *testing.T) UnsignedAtomicTx { 1184 tx := *exportTx 1185 tx.ExportedOutputs = nil 1186 return &tx 1187 }, 1188 ctx: ctx, 1189 rules: apricotRulesPhase0, 1190 expectedErr: errNoExportOutputs.Error(), 1191 }, 1192 "unsorted outputs": { 1193 generate: func(t *testing.T) UnsignedAtomicTx { 1194 tx := *exportTx 1195 tx.ExportedOutputs = []*avax.TransferableOutput{ 1196 tx.ExportedOutputs[1], 1197 tx.ExportedOutputs[0], 1198 } 1199 return &tx 1200 }, 1201 ctx: ctx, 1202 rules: apricotRulesPhase0, 1203 expectedErr: errOutputsNotSorted.Error(), 1204 }, 1205 "invalid exported output": { 1206 generate: func(t *testing.T) UnsignedAtomicTx { 1207 tx := *exportTx 1208 tx.ExportedOutputs = []*avax.TransferableOutput{tx.ExportedOutputs[0], nil} 1209 return &tx 1210 }, 1211 ctx: ctx, 1212 rules: apricotRulesPhase0, 1213 expectedErr: "nil transferable output is not valid", 1214 }, 1215 "unsorted EVM inputs before AP1": { 1216 generate: func(t *testing.T) UnsignedAtomicTx { 1217 tx := *exportTx 1218 tx.Ins = []EVMInput{ 1219 tx.Ins[1], 1220 tx.Ins[0], 1221 } 1222 return &tx 1223 }, 1224 ctx: ctx, 1225 rules: apricotRulesPhase0, 1226 expectedErr: "", 1227 }, 1228 "unsorted EVM inputs after AP1": { 1229 generate: func(t *testing.T) UnsignedAtomicTx { 1230 tx := *exportTx 1231 tx.Ins = []EVMInput{ 1232 tx.Ins[1], 1233 tx.Ins[0], 1234 } 1235 return &tx 1236 }, 1237 ctx: ctx, 1238 rules: apricotRulesPhase1, 1239 expectedErr: errInputsNotSortedUnique.Error(), 1240 }, 1241 "EVM input with amount 0": { 1242 generate: func(t *testing.T) UnsignedAtomicTx { 1243 tx := *exportTx 1244 tx.Ins = []EVMInput{ 1245 { 1246 Address: testEthAddrs[0], 1247 Amount: 0, 1248 AssetID: testAvaxAssetID, 1249 Nonce: 0, 1250 }, 1251 } 1252 return &tx 1253 }, 1254 ctx: ctx, 1255 rules: apricotRulesPhase0, 1256 expectedErr: errNoValueInput.Error(), 1257 }, 1258 "non-unique EVM input before AP1": { 1259 generate: func(t *testing.T) UnsignedAtomicTx { 1260 tx := *exportTx 1261 tx.Ins = []EVMInput{tx.Ins[0], tx.Ins[0]} 1262 return &tx 1263 }, 1264 ctx: ctx, 1265 rules: apricotRulesPhase0, 1266 expectedErr: "", 1267 }, 1268 "non-unique EVM input after AP1": { 1269 generate: func(t *testing.T) UnsignedAtomicTx { 1270 tx := *exportTx 1271 tx.Ins = []EVMInput{tx.Ins[0], tx.Ins[0]} 1272 return &tx 1273 }, 1274 ctx: ctx, 1275 rules: apricotRulesPhase1, 1276 expectedErr: errInputsNotSortedUnique.Error(), 1277 }, 1278 "non-AVAX input Apricot Phase 6": { 1279 generate: func(t *testing.T) UnsignedAtomicTx { 1280 tx := *exportTx 1281 tx.Ins = []EVMInput{ 1282 { 1283 Address: testEthAddrs[0], 1284 Amount: 1, 1285 AssetID: nonExistentID, 1286 Nonce: 0, 1287 }, 1288 } 1289 return &tx 1290 }, 1291 ctx: ctx, 1292 rules: apricotRulesPhase6, 1293 expectedErr: "", 1294 }, 1295 "non-AVAX output Apricot Phase 6": { 1296 generate: func(t *testing.T) UnsignedAtomicTx { 1297 tx := *exportTx 1298 tx.ExportedOutputs = []*avax.TransferableOutput{ 1299 { 1300 Asset: avax.Asset{ID: nonExistentID}, 1301 Out: &secp256k1fx.TransferOutput{ 1302 Amt: exportAmount, 1303 OutputOwners: secp256k1fx.OutputOwners{ 1304 Locktime: 0, 1305 Threshold: 1, 1306 Addrs: []ids.ShortID{testShortIDAddrs[0]}, 1307 }, 1308 }, 1309 }, 1310 } 1311 return &tx 1312 }, 1313 ctx: ctx, 1314 rules: apricotRulesPhase6, 1315 expectedErr: "", 1316 }, 1317 "non-AVAX input Banff": { 1318 generate: func(t *testing.T) UnsignedAtomicTx { 1319 tx := *exportTx 1320 tx.Ins = []EVMInput{ 1321 { 1322 Address: testEthAddrs[0], 1323 Amount: 1, 1324 AssetID: nonExistentID, 1325 Nonce: 0, 1326 }, 1327 } 1328 return &tx 1329 }, 1330 ctx: ctx, 1331 rules: banffRules, 1332 expectedErr: errExportNonAVAXInputBanff.Error(), 1333 }, 1334 "non-AVAX output Banff": { 1335 generate: func(t *testing.T) UnsignedAtomicTx { 1336 tx := *exportTx 1337 tx.ExportedOutputs = []*avax.TransferableOutput{ 1338 { 1339 Asset: avax.Asset{ID: nonExistentID}, 1340 Out: &secp256k1fx.TransferOutput{ 1341 Amt: exportAmount, 1342 OutputOwners: secp256k1fx.OutputOwners{ 1343 Locktime: 0, 1344 Threshold: 1, 1345 Addrs: []ids.ShortID{testShortIDAddrs[0]}, 1346 }, 1347 }, 1348 }, 1349 } 1350 return &tx 1351 }, 1352 ctx: ctx, 1353 rules: banffRules, 1354 expectedErr: errExportNonAVAXOutputBanff.Error(), 1355 }, 1356 } 1357 1358 for name, test := range tests { 1359 t.Run(name, func(t *testing.T) { 1360 executeTxVerifyTest(t, test) 1361 }) 1362 } 1363 } 1364 1365 // Note: this is a brittle test to ensure that the gas cost of a transaction does 1366 // not change 1367 func TestExportTxGasCost(t *testing.T) { 1368 avaxAssetID := ids.GenerateTestID() 1369 chainID := ids.GenerateTestID() 1370 xChainID := ids.GenerateTestID() 1371 networkID := uint32(5) 1372 exportAmount := uint64(5000000) 1373 1374 tests := map[string]struct { 1375 UnsignedExportTx *UnsignedExportTx 1376 Keys [][]*crypto.PrivateKeySECP256K1R 1377 1378 BaseFee *big.Int 1379 ExpectedGasUsed uint64 1380 ExpectedFee uint64 1381 FixedFee bool 1382 }{ 1383 "simple export 1wei BaseFee": { 1384 UnsignedExportTx: &UnsignedExportTx{ 1385 NetworkID: networkID, 1386 BlockchainID: chainID, 1387 DestinationChain: xChainID, 1388 Ins: []EVMInput{ 1389 { 1390 Address: testEthAddrs[0], 1391 Amount: exportAmount, 1392 AssetID: avaxAssetID, 1393 Nonce: 0, 1394 }, 1395 }, 1396 ExportedOutputs: []*avax.TransferableOutput{ 1397 { 1398 Asset: avax.Asset{ID: avaxAssetID}, 1399 Out: &secp256k1fx.TransferOutput{ 1400 Amt: exportAmount, 1401 OutputOwners: secp256k1fx.OutputOwners{ 1402 Locktime: 0, 1403 Threshold: 1, 1404 Addrs: []ids.ShortID{testShortIDAddrs[0]}, 1405 }, 1406 }, 1407 }, 1408 }, 1409 }, 1410 Keys: [][]*crypto.PrivateKeySECP256K1R{{testKeys[0]}}, 1411 ExpectedGasUsed: 1230, 1412 ExpectedFee: 1, 1413 BaseFee: big.NewInt(1), 1414 }, 1415 "simple export 1wei BaseFee + fixed fee": { 1416 UnsignedExportTx: &UnsignedExportTx{ 1417 NetworkID: networkID, 1418 BlockchainID: chainID, 1419 DestinationChain: xChainID, 1420 Ins: []EVMInput{ 1421 { 1422 Address: testEthAddrs[0], 1423 Amount: exportAmount, 1424 AssetID: avaxAssetID, 1425 Nonce: 0, 1426 }, 1427 }, 1428 ExportedOutputs: []*avax.TransferableOutput{ 1429 { 1430 Asset: avax.Asset{ID: avaxAssetID}, 1431 Out: &secp256k1fx.TransferOutput{ 1432 Amt: exportAmount, 1433 OutputOwners: secp256k1fx.OutputOwners{ 1434 Locktime: 0, 1435 Threshold: 1, 1436 Addrs: []ids.ShortID{testShortIDAddrs[0]}, 1437 }, 1438 }, 1439 }, 1440 }, 1441 }, 1442 Keys: [][]*crypto.PrivateKeySECP256K1R{{testKeys[0]}}, 1443 ExpectedGasUsed: 11230, 1444 ExpectedFee: 1, 1445 BaseFee: big.NewInt(1), 1446 FixedFee: true, 1447 }, 1448 "simple export 25Gwei BaseFee": { 1449 UnsignedExportTx: &UnsignedExportTx{ 1450 NetworkID: networkID, 1451 BlockchainID: chainID, 1452 DestinationChain: xChainID, 1453 Ins: []EVMInput{ 1454 { 1455 Address: testEthAddrs[0], 1456 Amount: exportAmount, 1457 AssetID: avaxAssetID, 1458 Nonce: 0, 1459 }, 1460 }, 1461 ExportedOutputs: []*avax.TransferableOutput{ 1462 { 1463 Asset: avax.Asset{ID: avaxAssetID}, 1464 Out: &secp256k1fx.TransferOutput{ 1465 Amt: exportAmount, 1466 OutputOwners: secp256k1fx.OutputOwners{ 1467 Locktime: 0, 1468 Threshold: 1, 1469 Addrs: []ids.ShortID{testShortIDAddrs[0]}, 1470 }, 1471 }, 1472 }, 1473 }, 1474 }, 1475 Keys: [][]*crypto.PrivateKeySECP256K1R{{testKeys[0]}}, 1476 ExpectedGasUsed: 1230, 1477 ExpectedFee: 30750, 1478 BaseFee: big.NewInt(25 * params.GWei), 1479 }, 1480 "simple export 225Gwei BaseFee": { 1481 UnsignedExportTx: &UnsignedExportTx{ 1482 NetworkID: networkID, 1483 BlockchainID: chainID, 1484 DestinationChain: xChainID, 1485 Ins: []EVMInput{ 1486 { 1487 Address: testEthAddrs[0], 1488 Amount: exportAmount, 1489 AssetID: avaxAssetID, 1490 Nonce: 0, 1491 }, 1492 }, 1493 ExportedOutputs: []*avax.TransferableOutput{ 1494 { 1495 Asset: avax.Asset{ID: avaxAssetID}, 1496 Out: &secp256k1fx.TransferOutput{ 1497 Amt: exportAmount, 1498 OutputOwners: secp256k1fx.OutputOwners{ 1499 Locktime: 0, 1500 Threshold: 1, 1501 Addrs: []ids.ShortID{testShortIDAddrs[0]}, 1502 }, 1503 }, 1504 }, 1505 }, 1506 }, 1507 Keys: [][]*crypto.PrivateKeySECP256K1R{{testKeys[0]}}, 1508 ExpectedGasUsed: 1230, 1509 ExpectedFee: 276750, 1510 BaseFee: big.NewInt(225 * params.GWei), 1511 }, 1512 "complex export 25Gwei BaseFee": { 1513 UnsignedExportTx: &UnsignedExportTx{ 1514 NetworkID: networkID, 1515 BlockchainID: chainID, 1516 DestinationChain: xChainID, 1517 Ins: []EVMInput{ 1518 { 1519 Address: testEthAddrs[0], 1520 Amount: exportAmount, 1521 AssetID: avaxAssetID, 1522 Nonce: 0, 1523 }, 1524 { 1525 Address: testEthAddrs[1], 1526 Amount: exportAmount, 1527 AssetID: avaxAssetID, 1528 Nonce: 0, 1529 }, 1530 { 1531 Address: testEthAddrs[2], 1532 Amount: exportAmount, 1533 AssetID: avaxAssetID, 1534 Nonce: 0, 1535 }, 1536 }, 1537 ExportedOutputs: []*avax.TransferableOutput{ 1538 { 1539 Asset: avax.Asset{ID: avaxAssetID}, 1540 Out: &secp256k1fx.TransferOutput{ 1541 Amt: exportAmount * 3, 1542 OutputOwners: secp256k1fx.OutputOwners{ 1543 Locktime: 0, 1544 Threshold: 1, 1545 Addrs: []ids.ShortID{testShortIDAddrs[0]}, 1546 }, 1547 }, 1548 }, 1549 }, 1550 }, 1551 Keys: [][]*crypto.PrivateKeySECP256K1R{{testKeys[0], testKeys[0], testKeys[0]}}, 1552 ExpectedGasUsed: 3366, 1553 ExpectedFee: 84150, 1554 BaseFee: big.NewInt(25 * params.GWei), 1555 }, 1556 "complex export 225Gwei BaseFee": { 1557 UnsignedExportTx: &UnsignedExportTx{ 1558 NetworkID: networkID, 1559 BlockchainID: chainID, 1560 DestinationChain: xChainID, 1561 Ins: []EVMInput{ 1562 { 1563 Address: testEthAddrs[0], 1564 Amount: exportAmount, 1565 AssetID: avaxAssetID, 1566 Nonce: 0, 1567 }, 1568 { 1569 Address: testEthAddrs[1], 1570 Amount: exportAmount, 1571 AssetID: avaxAssetID, 1572 Nonce: 0, 1573 }, 1574 { 1575 Address: testEthAddrs[2], 1576 Amount: exportAmount, 1577 AssetID: avaxAssetID, 1578 Nonce: 0, 1579 }, 1580 }, 1581 ExportedOutputs: []*avax.TransferableOutput{ 1582 { 1583 Asset: avax.Asset{ID: avaxAssetID}, 1584 Out: &secp256k1fx.TransferOutput{ 1585 Amt: exportAmount * 3, 1586 OutputOwners: secp256k1fx.OutputOwners{ 1587 Locktime: 0, 1588 Threshold: 1, 1589 Addrs: []ids.ShortID{testShortIDAddrs[0]}, 1590 }, 1591 }, 1592 }, 1593 }, 1594 }, 1595 Keys: [][]*crypto.PrivateKeySECP256K1R{{testKeys[0], testKeys[0], testKeys[0]}}, 1596 ExpectedGasUsed: 3366, 1597 ExpectedFee: 757350, 1598 BaseFee: big.NewInt(225 * params.GWei), 1599 }, 1600 } 1601 1602 for name, test := range tests { 1603 t.Run(name, func(t *testing.T) { 1604 tx := &Tx{UnsignedAtomicTx: test.UnsignedExportTx} 1605 1606 // Sign with the correct key 1607 if err := tx.Sign(Codec, test.Keys); err != nil { 1608 t.Fatal(err) 1609 } 1610 1611 gasUsed, err := tx.GasUsed(test.FixedFee) 1612 if err != nil { 1613 t.Fatal(err) 1614 } 1615 if gasUsed != test.ExpectedGasUsed { 1616 t.Fatalf("Expected gasUsed to be %d, but found %d", test.ExpectedGasUsed, gasUsed) 1617 } 1618 1619 fee, err := calculateDynamicFee(gasUsed, test.BaseFee) 1620 if err != nil { 1621 t.Fatal(err) 1622 } 1623 if fee != test.ExpectedFee { 1624 t.Fatalf("Expected fee to be %d, but found %d", test.ExpectedFee, fee) 1625 } 1626 }) 1627 } 1628 } 1629 1630 func TestNewExportTx(t *testing.T) { 1631 tests := []struct { 1632 name string 1633 genesis string 1634 rules params.Rules 1635 bal uint64 1636 expectedBurnedAVAX uint64 1637 }{ 1638 { 1639 name: "apricot phase 0", 1640 genesis: genesisJSONApricotPhase0, 1641 rules: apricotRulesPhase0, 1642 bal: 44000000, 1643 expectedBurnedAVAX: 1000000, 1644 }, 1645 { 1646 name: "apricot phase 1", 1647 genesis: genesisJSONApricotPhase1, 1648 rules: apricotRulesPhase1, 1649 bal: 44000000, 1650 expectedBurnedAVAX: 1000000, 1651 }, 1652 { 1653 name: "apricot phase 2", 1654 genesis: genesisJSONApricotPhase2, 1655 rules: apricotRulesPhase2, 1656 bal: 43000000, 1657 expectedBurnedAVAX: 1000000, 1658 }, 1659 { 1660 name: "apricot phase 3", 1661 genesis: genesisJSONApricotPhase3, 1662 rules: apricotRulesPhase3, 1663 bal: 44446500, 1664 expectedBurnedAVAX: 276750, 1665 }, 1666 { 1667 name: "apricot phase 4", 1668 genesis: genesisJSONApricotPhase4, 1669 rules: apricotRulesPhase4, 1670 bal: 44446500, 1671 expectedBurnedAVAX: 276750, 1672 }, 1673 { 1674 name: "apricot phase 5", 1675 genesis: genesisJSONApricotPhase5, 1676 rules: apricotRulesPhase5, 1677 bal: 39946500, 1678 expectedBurnedAVAX: 2526750, 1679 }, 1680 } 1681 for _, test := range tests { 1682 t.Run(test.name, func(t *testing.T) { 1683 issuer, vm, _, sharedMemory, _ := GenesisVM(t, true, test.genesis, "", "") 1684 1685 defer func() { 1686 if err := vm.Shutdown(); err != nil { 1687 t.Fatal(err) 1688 } 1689 }() 1690 1691 parent := vm.LastAcceptedBlockInternal().(*Block) 1692 importAmount := uint64(50000000) 1693 utxoID := avax.UTXOID{TxID: ids.GenerateTestID()} 1694 1695 utxo := &avax.UTXO{ 1696 UTXOID: utxoID, 1697 Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, 1698 Out: &secp256k1fx.TransferOutput{ 1699 Amt: importAmount, 1700 OutputOwners: secp256k1fx.OutputOwners{ 1701 Threshold: 1, 1702 Addrs: []ids.ShortID{testKeys[0].PublicKey().Address()}, 1703 }, 1704 }, 1705 } 1706 utxoBytes, err := vm.codec.Marshal(codecVersion, utxo) 1707 if err != nil { 1708 t.Fatal(err) 1709 } 1710 1711 xChainSharedMemory := sharedMemory.NewSharedMemory(vm.ctx.XChainID) 1712 inputID := utxo.InputID() 1713 if err := xChainSharedMemory.Apply(map[ids.ID]*atomic.Requests{vm.ctx.ChainID: {PutRequests: []*atomic.Element{{ 1714 Key: inputID[:], 1715 Value: utxoBytes, 1716 Traits: [][]byte{ 1717 testKeys[0].PublicKey().Address().Bytes(), 1718 }, 1719 }}}}); err != nil { 1720 t.Fatal(err) 1721 } 1722 1723 tx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*crypto.PrivateKeySECP256K1R{testKeys[0]}) 1724 if err != nil { 1725 t.Fatal(err) 1726 } 1727 1728 if err := vm.issueTx(tx, true /*=local*/); err != nil { 1729 t.Fatal(err) 1730 } 1731 1732 <-issuer 1733 1734 blk, err := vm.BuildBlock() 1735 if err != nil { 1736 t.Fatal(err) 1737 } 1738 1739 if err := blk.Verify(); err != nil { 1740 t.Fatal(err) 1741 } 1742 1743 if err := vm.SetPreference(blk.ID()); err != nil { 1744 t.Fatal(err) 1745 } 1746 1747 if err := blk.Accept(); err != nil { 1748 t.Fatal(err) 1749 } 1750 1751 parent = vm.LastAcceptedBlockInternal().(*Block) 1752 exportAmount := uint64(5000000) 1753 1754 tx, err = vm.newExportTx(vm.ctx.AVAXAssetID, exportAmount, vm.ctx.XChainID, testShortIDAddrs[0], initialBaseFee, []*crypto.PrivateKeySECP256K1R{testKeys[0]}) 1755 if err != nil { 1756 t.Fatal(err) 1757 } 1758 1759 exportTx := tx.UnsignedAtomicTx 1760 1761 if err := exportTx.SemanticVerify(vm, tx, parent, parent.ethBlock.BaseFee(), test.rules); err != nil { 1762 t.Fatal("newExportTx created an invalid transaction", err) 1763 } 1764 1765 burnedAVAX, err := exportTx.Burned(vm.ctx.AVAXAssetID) 1766 if err != nil { 1767 t.Fatal(err) 1768 } 1769 if burnedAVAX != test.expectedBurnedAVAX { 1770 t.Fatalf("burned wrong amount of AVAX - expected %d burned %d", test.expectedBurnedAVAX, burnedAVAX) 1771 } 1772 1773 commitBatch, err := vm.db.CommitBatch() 1774 if err != nil { 1775 t.Fatalf("Failed to create commit batch for VM due to %s", err) 1776 } 1777 chainID, atomicRequests, err := exportTx.AtomicOps() 1778 1779 if err != nil { 1780 t.Fatalf("Failed to accept export transaction due to: %s", err) 1781 } 1782 1783 if err := vm.ctx.SharedMemory.Apply(map[ids.ID]*atomic.Requests{chainID: {PutRequests: atomicRequests.PutRequests}}, commitBatch); err != nil { 1784 t.Fatal(err) 1785 } 1786 1787 sdb, err := vm.blockChain.State() 1788 if err != nil { 1789 t.Fatal(err) 1790 } 1791 err = exportTx.EVMStateTransfer(vm.ctx, sdb) 1792 if err != nil { 1793 t.Fatal(err) 1794 } 1795 1796 addr := GetEthAddress(testKeys[0]) 1797 if sdb.GetBalance(addr).Cmp(new(big.Int).SetUint64(test.bal*units.Avax)) != 0 { 1798 t.Fatalf("address balance %s equal %s not %s", addr.String(), sdb.GetBalance(addr), new(big.Int).SetUint64(test.bal*units.Avax)) 1799 } 1800 }) 1801 } 1802 } 1803 1804 func TestNewExportTxMulticoin(t *testing.T) { 1805 tests := []struct { 1806 name string 1807 genesis string 1808 rules params.Rules 1809 bal uint64 1810 balmc uint64 1811 }{ 1812 { 1813 name: "apricot phase 0", 1814 genesis: genesisJSONApricotPhase0, 1815 rules: apricotRulesPhase0, 1816 bal: 49000000, 1817 balmc: 25000000, 1818 }, 1819 { 1820 name: "apricot phase 1", 1821 genesis: genesisJSONApricotPhase1, 1822 rules: apricotRulesPhase1, 1823 bal: 49000000, 1824 balmc: 25000000, 1825 }, 1826 { 1827 name: "apricot phase 2", 1828 genesis: genesisJSONApricotPhase2, 1829 rules: apricotRulesPhase2, 1830 bal: 48000000, 1831 balmc: 25000000, 1832 }, 1833 { 1834 name: "apricot phase 3", 1835 genesis: genesisJSONApricotPhase3, 1836 rules: apricotRulesPhase3, 1837 bal: 48947900, 1838 balmc: 25000000, 1839 }, 1840 } 1841 for _, test := range tests { 1842 t.Run(test.name, func(t *testing.T) { 1843 issuer, vm, _, sharedMemory, _ := GenesisVM(t, true, test.genesis, "", "") 1844 1845 defer func() { 1846 if err := vm.Shutdown(); err != nil { 1847 t.Fatal(err) 1848 } 1849 }() 1850 1851 parent := vm.LastAcceptedBlockInternal().(*Block) 1852 importAmount := uint64(50000000) 1853 utxoID := avax.UTXOID{TxID: ids.GenerateTestID()} 1854 1855 utxo := &avax.UTXO{ 1856 UTXOID: utxoID, 1857 Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, 1858 Out: &secp256k1fx.TransferOutput{ 1859 Amt: importAmount, 1860 OutputOwners: secp256k1fx.OutputOwners{ 1861 Threshold: 1, 1862 Addrs: []ids.ShortID{testKeys[0].PublicKey().Address()}, 1863 }, 1864 }, 1865 } 1866 utxoBytes, err := vm.codec.Marshal(codecVersion, utxo) 1867 if err != nil { 1868 t.Fatal(err) 1869 } 1870 1871 inputID := utxo.InputID() 1872 1873 tid := ids.GenerateTestID() 1874 importAmount2 := uint64(30000000) 1875 utxoID2 := avax.UTXOID{TxID: ids.GenerateTestID()} 1876 utxo2 := &avax.UTXO{ 1877 UTXOID: utxoID2, 1878 Asset: avax.Asset{ID: tid}, 1879 Out: &secp256k1fx.TransferOutput{ 1880 Amt: importAmount2, 1881 OutputOwners: secp256k1fx.OutputOwners{ 1882 Threshold: 1, 1883 Addrs: []ids.ShortID{testKeys[0].PublicKey().Address()}, 1884 }, 1885 }, 1886 } 1887 utxoBytes2, err := vm.codec.Marshal(codecVersion, utxo2) 1888 if err != nil { 1889 t.Fatal(err) 1890 } 1891 1892 xChainSharedMemory := sharedMemory.NewSharedMemory(vm.ctx.XChainID) 1893 inputID2 := utxo2.InputID() 1894 if err := xChainSharedMemory.Apply(map[ids.ID]*atomic.Requests{vm.ctx.ChainID: {PutRequests: []*atomic.Element{ 1895 { 1896 Key: inputID[:], 1897 Value: utxoBytes, 1898 Traits: [][]byte{ 1899 testKeys[0].PublicKey().Address().Bytes(), 1900 }, 1901 }, 1902 { 1903 Key: inputID2[:], 1904 Value: utxoBytes2, 1905 Traits: [][]byte{ 1906 testKeys[0].PublicKey().Address().Bytes(), 1907 }, 1908 }, 1909 }}}); err != nil { 1910 t.Fatal(err) 1911 } 1912 1913 tx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*crypto.PrivateKeySECP256K1R{testKeys[0]}) 1914 if err != nil { 1915 t.Fatal(err) 1916 } 1917 1918 if err := vm.issueTx(tx, false); err != nil { 1919 t.Fatal(err) 1920 } 1921 1922 <-issuer 1923 1924 blk, err := vm.BuildBlock() 1925 if err != nil { 1926 t.Fatal(err) 1927 } 1928 1929 if err := blk.Verify(); err != nil { 1930 t.Fatal(err) 1931 } 1932 1933 if err := vm.SetPreference(blk.ID()); err != nil { 1934 t.Fatal(err) 1935 } 1936 1937 if err := blk.Accept(); err != nil { 1938 t.Fatal(err) 1939 } 1940 1941 parent = vm.LastAcceptedBlockInternal().(*Block) 1942 exportAmount := uint64(5000000) 1943 1944 testKeys0Addr := GetEthAddress(testKeys[0]) 1945 exportId, err := ids.ToShortID(testKeys0Addr[:]) 1946 if err != nil { 1947 t.Fatal(err) 1948 } 1949 1950 tx, err = vm.newExportTx(tid, exportAmount, vm.ctx.XChainID, exportId, initialBaseFee, []*crypto.PrivateKeySECP256K1R{testKeys[0]}) 1951 if err != nil { 1952 t.Fatal(err) 1953 } 1954 1955 exportTx := tx.UnsignedAtomicTx 1956 1957 if err := exportTx.SemanticVerify(vm, tx, parent, parent.ethBlock.BaseFee(), test.rules); err != nil { 1958 t.Fatal("newExportTx created an invalid transaction", err) 1959 } 1960 1961 commitBatch, err := vm.db.CommitBatch() 1962 if err != nil { 1963 t.Fatalf("Failed to create commit batch for VM due to %s", err) 1964 } 1965 chainID, atomicRequests, err := exportTx.AtomicOps() 1966 1967 if err != nil { 1968 t.Fatalf("Failed to accept export transaction due to: %s", err) 1969 } 1970 1971 if err := vm.ctx.SharedMemory.Apply(map[ids.ID]*atomic.Requests{chainID: {PutRequests: atomicRequests.PutRequests}}, commitBatch); err != nil { 1972 t.Fatal(err) 1973 } 1974 1975 stdb, err := vm.blockChain.State() 1976 if err != nil { 1977 t.Fatal(err) 1978 } 1979 err = exportTx.EVMStateTransfer(vm.ctx, stdb) 1980 if err != nil { 1981 t.Fatal(err) 1982 } 1983 1984 addr := GetEthAddress(testKeys[0]) 1985 if stdb.GetBalance(addr).Cmp(new(big.Int).SetUint64(test.bal*units.Avax)) != 0 { 1986 t.Fatalf("address balance %s equal %s not %s", addr.String(), stdb.GetBalance(addr), new(big.Int).SetUint64(test.bal*units.Avax)) 1987 } 1988 if stdb.GetBalanceMultiCoin(addr, common.BytesToHash(tid[:])).Cmp(new(big.Int).SetUint64(test.balmc)) != 0 { 1989 t.Fatalf("address balance multicoin %s equal %s not %s", addr.String(), stdb.GetBalanceMultiCoin(addr, common.BytesToHash(tid[:])), new(big.Int).SetUint64(test.balmc)) 1990 } 1991 }) 1992 } 1993 }