gitlab.com/flarenetwork/coreth@v0.1.1/plugin/evm/import_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 "math/big" 8 "testing" 9 10 "gitlab.com/flarenetwork/coreth/params" 11 12 "github.com/ava-labs/avalanchego/chains/atomic" 13 "github.com/ava-labs/avalanchego/ids" 14 "github.com/ava-labs/avalanchego/utils/crypto" 15 "github.com/ava-labs/avalanchego/utils/units" 16 "github.com/ava-labs/avalanchego/vms/components/avax" 17 "github.com/ava-labs/avalanchego/vms/secp256k1fx" 18 ) 19 20 func TestImportTxVerifyNil(t *testing.T) { 21 var importTx *UnsignedImportTx 22 if err := importTx.Verify(testXChainID, NewContext(), apricotRulesPhase1); err == nil { 23 t.Fatal("Verify should have failed due to nil transaction") 24 } 25 } 26 27 func TestImportTxVerify(t *testing.T) { 28 var importAmount uint64 = 10000000 29 txID := ids.ID{0xff} 30 importTx := &UnsignedImportTx{ 31 NetworkID: testNetworkID, 32 BlockchainID: testCChainID, 33 SourceChain: testXChainID, 34 ImportedInputs: []*avax.TransferableInput{ 35 { 36 UTXOID: avax.UTXOID{ 37 TxID: txID, 38 OutputIndex: uint32(0), 39 }, 40 Asset: avax.Asset{ID: testAvaxAssetID}, 41 In: &secp256k1fx.TransferInput{ 42 Amt: importAmount, 43 Input: secp256k1fx.Input{ 44 SigIndices: []uint32{0}, 45 }, 46 }, 47 }, 48 { 49 UTXOID: avax.UTXOID{ 50 TxID: txID, 51 OutputIndex: uint32(1), 52 }, 53 Asset: avax.Asset{ID: testAvaxAssetID}, 54 In: &secp256k1fx.TransferInput{ 55 Amt: importAmount, 56 Input: secp256k1fx.Input{ 57 SigIndices: []uint32{0}, 58 }, 59 }, 60 }, 61 }, 62 Outs: []EVMOutput{ 63 { 64 Address: testEthAddrs[0], 65 Amount: importAmount - params.AvalancheAtomicTxFee, 66 AssetID: testAvaxAssetID, 67 }, 68 { 69 Address: testEthAddrs[1], 70 Amount: importAmount, 71 AssetID: testAvaxAssetID, 72 }, 73 }, 74 } 75 76 ctx := NewContext() 77 78 // // Sort the inputs and outputs to ensure the transaction is canonical 79 avax.SortTransferableInputs(importTx.ImportedInputs) 80 SortEVMOutputs(importTx.Outs) 81 82 // Test Valid ImportTx 83 if err := importTx.Verify(testXChainID, ctx, apricotRulesPhase1); err != nil { 84 t.Fatalf("Failed to verify ImportTx: %s", err) 85 } 86 87 importTx.NetworkID = testNetworkID + 1 88 89 // // Test Incorrect Network ID Errors 90 if err := importTx.Verify(testXChainID, ctx, apricotRulesPhase1); err == nil { 91 t.Fatal("ImportTx should have failed verification due to incorrect network ID") 92 } 93 94 importTx.NetworkID = testNetworkID 95 importTx.BlockchainID = nonExistentID 96 // // Test Incorrect Blockchain ID Errors 97 if err := importTx.Verify(testXChainID, ctx, apricotRulesPhase1); err == nil { 98 t.Fatal("ImportTx should have failed verification due to incorrect blockchain ID") 99 } 100 101 importTx.BlockchainID = testCChainID 102 importTx.SourceChain = nonExistentID 103 // // Test Incorrect Destination Chain ID Errors 104 if err := importTx.Verify(testXChainID, ctx, apricotRulesPhase1); err == nil { 105 t.Fatal("ImportTx should have failed verification due to incorrect source chain") 106 } 107 108 importTx.SourceChain = testXChainID 109 importedIns := importTx.ImportedInputs 110 evmOutputs := importTx.Outs 111 importTx.ImportedInputs = nil 112 // // Test No Imported Inputs Errors 113 if err := importTx.Verify(testXChainID, ctx, apricotRulesPhase1); err == nil { 114 t.Fatal("ImportTx should have failed verification due to no imported inputs") 115 } 116 117 importTx.ImportedInputs = []*avax.TransferableInput{importedIns[1], importedIns[0]} 118 // // Test Unsorted Imported Inputs Errors 119 if err := importTx.Verify(testXChainID, ctx, apricotRulesPhase1); err == nil { 120 t.Fatal("ImportTx should have failed verification due to unsorted import inputs") 121 } 122 123 importTx.ImportedInputs = []*avax.TransferableInput{importedIns[0], nil} 124 if err := importTx.Verify(testXChainID, ctx, apricotRulesPhase1); err == nil { 125 t.Fatal("ImportTx should have failed verification due to invalid input") 126 } 127 128 importTx.ImportedInputs = []*avax.TransferableInput{importedIns[0], importedIns[1]} 129 importTx.Outs = []EVMOutput{evmOutputs[1], evmOutputs[0]} 130 // Test unsorted EVM Outputs pass verification prior to AP1 131 if err := importTx.Verify(testXChainID, ctx, apricotRulesPhase0); err != nil { 132 t.Fatalf("ImportTx should have passed verification prior to AP1, but failed due to %s", err) 133 } 134 // Test unsorted EVM Outputs fails verification after AP1 135 if err := importTx.Verify(testXChainID, ctx, apricotRulesPhase1); err == nil { 136 t.Fatal("ImportTx should have failed verification due to unsorted EVM Outputs in AP1") 137 } 138 importTx.Outs = []EVMOutput{ 139 { 140 Address: testEthAddrs[0], 141 Amount: 0, 142 AssetID: testAvaxAssetID, 143 }, 144 } 145 // Test ImportTx with invalid EVM Output Amount 0 fails verification 146 if err := importTx.Verify(testXChainID, ctx, apricotRulesPhase1); err == nil { 147 t.Fatal("ImportTx should have failed verification due to 0 value amount") 148 } 149 importTx.Outs = []EVMOutput{evmOutputs[0], evmOutputs[0]} 150 // Test non-unique EVM Outputs passes verification for AP0 and AP1 151 if err := importTx.Verify(testXChainID, ctx, apricotRulesPhase0); err != nil { 152 t.Fatal(err) 153 } 154 if err := importTx.Verify(testXChainID, ctx, apricotRulesPhase1); err != nil { 155 t.Fatal(err) 156 } 157 // Test non-unique EVM Outputs fails verification for AP2 158 if err := importTx.Verify(testXChainID, ctx, apricotRulesPhase2); err == nil { 159 t.Fatal("ImportTx should have failed verification due to non-unique outputs in AP2") 160 } 161 } 162 163 func TestImportTxSemanticVerifyApricotPhase0(t *testing.T) { 164 _, vm, _, sharedMemory := GenesisVM(t, false, genesisJSONApricotPhase0, "", "") 165 166 defer func() { 167 if err := vm.Shutdown(); err != nil { 168 t.Fatal(err) 169 } 170 }() 171 172 parent := vm.LastAcceptedBlockInternal().(*Block) 173 xChainSharedMemory := sharedMemory.NewSharedMemory(vm.ctx.XChainID) 174 175 importAmount := uint64(5000000) 176 utxoID := avax.UTXOID{ 177 TxID: ids.ID{ 178 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 179 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 180 0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea, 181 0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xe8, 182 }, 183 } 184 185 utxo := &avax.UTXO{ 186 UTXOID: utxoID, 187 Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, 188 Out: &secp256k1fx.TransferOutput{ 189 Amt: importAmount, 190 OutputOwners: secp256k1fx.OutputOwners{ 191 Threshold: 1, 192 Addrs: []ids.ShortID{testKeys[0].PublicKey().Address()}, 193 }, 194 }, 195 } 196 utxoBytes, err := vm.codec.Marshal(codecVersion, utxo) 197 if err != nil { 198 t.Fatal(err) 199 } 200 201 evmOutput := EVMOutput{ 202 Address: testEthAddrs[0], 203 Amount: importAmount, 204 AssetID: vm.ctx.AVAXAssetID, 205 } 206 unsignedImportTx := &UnsignedImportTx{ 207 NetworkID: vm.ctx.NetworkID, 208 BlockchainID: vm.ctx.ChainID, 209 SourceChain: vm.ctx.XChainID, 210 ImportedInputs: []*avax.TransferableInput{{ 211 UTXOID: utxoID, 212 Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, 213 In: &secp256k1fx.TransferInput{ 214 Amt: importAmount, 215 Input: secp256k1fx.Input{SigIndices: []uint32{0}}, 216 }, 217 }}, 218 Outs: []EVMOutput{evmOutput}, 219 } 220 221 state, err := vm.chain.CurrentState() 222 if err != nil { 223 t.Fatalf("Failed to get last accepted stateDB due to: %s", err) 224 } 225 226 if empty := state.Empty(testEthAddrs[0]); !empty { 227 t.Fatal("Expected ethereum address to have empty starting balance.") 228 } 229 230 if err := unsignedImportTx.Verify(vm.ctx.XChainID, vm.ctx, apricotRulesPhase0); err != nil { 231 t.Fatal(err) 232 } 233 234 tx := &Tx{UnsignedAtomicTx: unsignedImportTx} 235 236 // Sign with the correct key 237 if err := tx.Sign(vm.codec, [][]*crypto.PrivateKeySECP256K1R{{testKeys[0]}}); err != nil { 238 t.Fatal(err) 239 } 240 241 // Check that SemanticVerify passes without the UTXO being present during bootstrapping 242 if err := unsignedImportTx.SemanticVerify(vm, tx, parent, nil, apricotRulesPhase0); err != nil { 243 t.Fatal(err) 244 } 245 inputID := utxo.InputID() 246 if err := xChainSharedMemory.Apply(map[ids.ID]*atomic.Requests{vm.ctx.ChainID: {PutRequests: []*atomic.Element{{ 247 Key: inputID[:], 248 Value: utxoBytes, 249 Traits: [][]byte{ 250 testKeys[0].PublicKey().Address().Bytes(), 251 }, 252 }}}}); err != nil { 253 t.Fatal(err) 254 } 255 256 // Check that SemanticVerify passes when the UTXO is present during bootstrapping 257 if err := unsignedImportTx.SemanticVerify(vm, tx, parent, nil, apricotRulesPhase0); err != nil { 258 t.Fatal(err) 259 } 260 261 // Check that SemanticVerify does not pass if an additional output is added in 262 unsignedImportTx.Outs = append(unsignedImportTx.Outs, EVMOutput{ 263 Address: testEthAddrs[1], 264 Amount: importAmount, 265 AssetID: vm.ctx.AVAXAssetID, 266 }) 267 // Sign the updated transaction 268 tx.Creds = nil 269 if err := tx.Sign(vm.codec, [][]*crypto.PrivateKeySECP256K1R{{testKeys[0]}}); err != nil { 270 t.Fatal(err) 271 } 272 if err := unsignedImportTx.SemanticVerify(vm, tx, parent, nil, apricotRulesPhase0); err == nil { 273 t.Fatal("Semantic verification should have failed due to insufficient funds") 274 } 275 276 unsignedImportTx.Outs = []EVMOutput{evmOutput} 277 // Sign the updated transaction 278 tx.Creds = nil 279 if err := tx.Sign(vm.codec, [][]*crypto.PrivateKeySECP256K1R{{testKeys[0]}}); err != nil { 280 t.Fatal(err) 281 } 282 283 if err := vm.Bootstrapping(); err != nil { 284 t.Fatal(err) 285 } 286 287 if err := vm.Bootstrapped(); err != nil { 288 t.Fatal(err) 289 } 290 291 vm.ctx.Bootstrapped() 292 293 // Remove the signature 294 tx.Creds = nil 295 if err := unsignedImportTx.SemanticVerify(vm, tx, parent, nil, apricotRulesPhase0); err == nil { 296 t.Fatal("SemanticVerify should have failed due to no signatures") 297 } 298 299 // Sign with the incorrect key 300 if err := tx.Sign(vm.codec, [][]*crypto.PrivateKeySECP256K1R{{testKeys[1]}}); err != nil { 301 t.Fatal(err) 302 } 303 if err := unsignedImportTx.SemanticVerify(vm, tx, parent, nil, apricotRulesPhase0); err == nil { 304 t.Fatal("SemanticVerify should have failed due to an invalid signature") 305 } 306 307 // Re-sign with the correct key 308 tx.Creds = nil 309 if err := tx.Sign(vm.codec, [][]*crypto.PrivateKeySECP256K1R{{testKeys[0]}}); err != nil { 310 t.Fatal(err) 311 } 312 313 // Check that SemanticVerify passes when the UTXO is present after bootstrapping 314 if err := unsignedImportTx.SemanticVerify(vm, tx, parent, nil, apricotRulesPhase0); err != nil { 315 t.Fatal(err) 316 } 317 318 commitBatch, err := vm.db.CommitBatch() 319 if err != nil { 320 t.Fatalf("Failed to create commit batch for VM due to %s", err) 321 } 322 if err := unsignedImportTx.Accept(vm.ctx, commitBatch); err != nil { 323 t.Fatalf("Accept failed due to: %s", err) 324 } 325 326 if err := unsignedImportTx.EVMStateTransfer(vm.ctx, state); err != nil { 327 t.Fatalf("EVM State Transfer failed due to: %s", err) 328 } 329 330 balance := state.GetBalance(testEthAddrs[0]) 331 if balance == nil { 332 t.Fatal("Found nil balance for address receiving imported funds") 333 } else if balance.Uint64() != importAmount*x2cRate.Uint64() { 334 t.Fatalf("Balance was %d, but expected balance of: %d", balance.Uint64(), importAmount*x2cRate.Uint64()) 335 } 336 337 // Check that SemanticVerify fails when the UTXO is not present after bootstrapping 338 if err := unsignedImportTx.SemanticVerify(vm, tx, parent, nil, apricotRulesPhase0); err == nil { 339 t.Fatal("Semantic verification should have failed after the UTXO removed from shared memory") 340 } 341 } 342 343 func TestImportTxSemanticVerifyApricotPhase2(t *testing.T) { 344 _, vm, _, sharedMemory := GenesisVM(t, false, genesisJSONApricotPhase2, "", "") 345 346 defer func() { 347 if err := vm.Shutdown(); err != nil { 348 t.Fatal(err) 349 } 350 }() 351 352 parent := vm.LastAcceptedBlockInternal().(*Block) 353 xChainSharedMemory := sharedMemory.NewSharedMemory(vm.ctx.XChainID) 354 355 importAmount := uint64(5000000) 356 utxoID := avax.UTXOID{ 357 TxID: ids.ID{ 358 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 359 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 360 0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea, 361 0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xe8, 362 }, 363 } 364 365 utxo := &avax.UTXO{ 366 UTXOID: utxoID, 367 Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, 368 Out: &secp256k1fx.TransferOutput{ 369 Amt: importAmount, 370 OutputOwners: secp256k1fx.OutputOwners{ 371 Threshold: 1, 372 Addrs: []ids.ShortID{testKeys[0].PublicKey().Address()}, 373 }, 374 }, 375 } 376 utxoBytes, err := vm.codec.Marshal(codecVersion, utxo) 377 if err != nil { 378 t.Fatal(err) 379 } 380 381 evmOutput := EVMOutput{ 382 Address: testEthAddrs[0], 383 Amount: importAmount - params.AvalancheAtomicTxFee, 384 AssetID: vm.ctx.AVAXAssetID, 385 } 386 unsignedImportTx := &UnsignedImportTx{ 387 NetworkID: vm.ctx.NetworkID, 388 BlockchainID: vm.ctx.ChainID, 389 SourceChain: vm.ctx.XChainID, 390 ImportedInputs: []*avax.TransferableInput{{ 391 UTXOID: utxoID, 392 Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, 393 In: &secp256k1fx.TransferInput{ 394 Amt: importAmount, 395 Input: secp256k1fx.Input{SigIndices: []uint32{0}}, 396 }, 397 }}, 398 Outs: []EVMOutput{evmOutput}, 399 } 400 401 state, err := vm.chain.CurrentState() 402 if err != nil { 403 t.Fatalf("Failed to get last accepted stateDB due to: %s", err) 404 } 405 406 if empty := state.Empty(testEthAddrs[0]); !empty { 407 t.Fatal("Expected ethereum address to have empty starting balance.") 408 } 409 410 if err := unsignedImportTx.Verify(vm.ctx.XChainID, vm.ctx, apricotRulesPhase2); err != nil { 411 t.Fatal(err) 412 } 413 414 tx := &Tx{UnsignedAtomicTx: unsignedImportTx} 415 416 // Sign with the correct key 417 if err := tx.Sign(vm.codec, [][]*crypto.PrivateKeySECP256K1R{{testKeys[0]}}); err != nil { 418 t.Fatal(err) 419 } 420 421 // Check that SemanticVerify passes without the UTXO being present during bootstrapping 422 if err := unsignedImportTx.SemanticVerify(vm, tx, parent, nil, apricotRulesPhase2); err != nil { 423 t.Fatal(err) 424 } 425 inputID := utxo.InputID() 426 if err := xChainSharedMemory.Apply(map[ids.ID]*atomic.Requests{vm.ctx.ChainID: {PutRequests: []*atomic.Element{{ 427 Key: inputID[:], 428 Value: utxoBytes, 429 Traits: [][]byte{ 430 testKeys[0].PublicKey().Address().Bytes(), 431 }, 432 }}}}); err != nil { 433 t.Fatal(err) 434 } 435 436 // Check that SemanticVerify passes when the UTXO is present during bootstrapping 437 if err := unsignedImportTx.SemanticVerify(vm, tx, parent, nil, apricotRulesPhase2); err != nil { 438 t.Fatal(err) 439 } 440 441 // Check that SemanticVerify does not pass if an additional output is added in 442 unsignedImportTx.Outs = append(unsignedImportTx.Outs, EVMOutput{ 443 Address: testEthAddrs[1], 444 Amount: importAmount, 445 AssetID: vm.ctx.AVAXAssetID, 446 }) 447 // Sign the updated transaction 448 tx.Creds = nil 449 if err := tx.Sign(vm.codec, [][]*crypto.PrivateKeySECP256K1R{{testKeys[0]}}); err != nil { 450 t.Fatal(err) 451 } 452 if err := unsignedImportTx.SemanticVerify(vm, tx, parent, nil, apricotRulesPhase2); err == nil { 453 t.Fatal("Semantic verification should have failed due to insufficient funds") 454 } 455 456 // Check that SemanticVerify errors when the transaction fee is not paid 457 unsignedImportTx.Outs = []EVMOutput{ 458 { 459 Address: testEthAddrs[0], 460 Amount: importAmount, 461 AssetID: vm.ctx.AVAXAssetID, 462 }, 463 } 464 tx.Creds = nil 465 if err := tx.Sign(vm.codec, [][]*crypto.PrivateKeySECP256K1R{{testKeys[0]}}); err != nil { 466 t.Fatal(err) 467 } 468 if err := unsignedImportTx.SemanticVerify(vm, tx, parent, nil, apricotRulesPhase2); err == nil { 469 t.Fatal("Semantic verification should have failed due to not paying the transaction fee") 470 } 471 472 unsignedImportTx.Outs = []EVMOutput{evmOutput} 473 // Sign the updated transaction 474 tx.Creds = nil 475 if err := tx.Sign(vm.codec, [][]*crypto.PrivateKeySECP256K1R{{testKeys[0]}}); err != nil { 476 t.Fatal(err) 477 } 478 479 if err := vm.Bootstrapping(); err != nil { 480 t.Fatal(err) 481 } 482 483 if err := vm.Bootstrapped(); err != nil { 484 t.Fatal(err) 485 } 486 487 vm.ctx.Bootstrapped() 488 489 // Remove the signature 490 tx.Creds = nil 491 if err := unsignedImportTx.SemanticVerify(vm, tx, parent, nil, apricotRulesPhase2); err == nil { 492 t.Fatal("SemanticVerify should have failed due to no signatures") 493 } 494 495 // Sign with the incorrect key 496 if err := tx.Sign(vm.codec, [][]*crypto.PrivateKeySECP256K1R{{testKeys[1]}}); err != nil { 497 t.Fatal(err) 498 } 499 if err := unsignedImportTx.SemanticVerify(vm, tx, parent, nil, apricotRulesPhase2); err == nil { 500 t.Fatal("SemanticVerify should have failed due to an invalid signature") 501 } 502 503 // Re-sign with the correct key 504 tx.Creds = nil 505 if err := tx.Sign(vm.codec, [][]*crypto.PrivateKeySECP256K1R{{testKeys[0]}}); err != nil { 506 t.Fatal(err) 507 } 508 509 // Check that SemanticVerify passes when the UTXO is present after bootstrapping 510 if err := unsignedImportTx.SemanticVerify(vm, tx, parent, nil, apricotRulesPhase2); err != nil { 511 t.Fatal(err) 512 } 513 514 commitBatch, err := vm.db.CommitBatch() 515 if err != nil { 516 t.Fatalf("Failed to create commit batch for VM due to %s", err) 517 } 518 if err := unsignedImportTx.Accept(vm.ctx, commitBatch); err != nil { 519 t.Fatalf("Accept failed due to: %s", err) 520 } 521 522 if err := unsignedImportTx.EVMStateTransfer(vm.ctx, state); err != nil { 523 t.Fatalf("EVM State Transfer failed due to: %s", err) 524 } 525 526 balance := state.GetBalance(testEthAddrs[0]) 527 if balance == nil { 528 t.Fatal("Found nil balance for address receiving imported funds") 529 } else if balance.Uint64() != (importAmount-params.AvalancheAtomicTxFee)*x2cRate.Uint64() { 530 t.Fatalf("Balance was %d, but expected balance of: %d", balance.Uint64(), importAmount*x2cRate.Uint64()) 531 } 532 533 // Check that SemanticVerify fails when the UTXO is not present after bootstrapping 534 if err := unsignedImportTx.SemanticVerify(vm, tx, parent, nil, apricotRulesPhase2); err == nil { 535 t.Fatal("Semantic verification should have failed after the UTXO removed from shared memory") 536 } 537 } 538 539 func TestNewImportTx(t *testing.T) { 540 tests := []struct { 541 name string 542 genesis string 543 rules params.Rules 544 bal uint64 545 }{ 546 { 547 name: "apricot phase 0", 548 genesis: genesisJSONApricotPhase0, 549 rules: apricotRulesPhase0, 550 bal: 5000000, 551 }, 552 { 553 name: "apricot phase 1", 554 genesis: genesisJSONApricotPhase1, 555 rules: apricotRulesPhase1, 556 bal: 5000000, 557 }, 558 { 559 name: "apricot phase 2", 560 genesis: genesisJSONApricotPhase2, 561 rules: apricotRulesPhase2, 562 bal: 4000000, 563 }, 564 { 565 name: "apricot phase 3", 566 genesis: genesisJSONApricotPhase3, 567 rules: apricotRulesPhase3, 568 bal: 4723250, 569 }, 570 } 571 for _, test := range tests { 572 t.Run(test.name, func(t *testing.T) { 573 _, vm, _, sharedMemory := GenesisVM(t, true, test.genesis, "", "") 574 575 defer func() { 576 if err := vm.Shutdown(); err != nil { 577 t.Fatal(err) 578 } 579 }() 580 581 parent := vm.LastAcceptedBlockInternal().(*Block) 582 importAmount := uint64(5000000) 583 utxoID := avax.UTXOID{ 584 TxID: ids.ID{ 585 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 586 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 587 0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea, 588 0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xe8, 589 }, 590 } 591 592 utxo := &avax.UTXO{ 593 UTXOID: utxoID, 594 Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, 595 Out: &secp256k1fx.TransferOutput{ 596 Amt: importAmount, 597 OutputOwners: secp256k1fx.OutputOwners{ 598 Threshold: 1, 599 Addrs: []ids.ShortID{testKeys[0].PublicKey().Address()}, 600 }, 601 }, 602 } 603 utxoBytes, err := vm.codec.Marshal(codecVersion, utxo) 604 if err != nil { 605 t.Fatal(err) 606 } 607 608 xChainSharedMemory := sharedMemory.NewSharedMemory(vm.ctx.XChainID) 609 inputID := utxo.InputID() 610 if err := xChainSharedMemory.Apply(map[ids.ID]*atomic.Requests{vm.ctx.ChainID: {PutRequests: []*atomic.Element{{ 611 Key: inputID[:], 612 Value: utxoBytes, 613 Traits: [][]byte{ 614 testKeys[0].PublicKey().Address().Bytes(), 615 }, 616 }}}}); err != nil { 617 t.Fatal(err) 618 } 619 620 tx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*crypto.PrivateKeySECP256K1R{testKeys[0]}) 621 if err != nil { 622 t.Fatal(err) 623 } 624 625 importTx := tx.UnsignedAtomicTx 626 627 if err := importTx.SemanticVerify(vm, tx, parent, parent.ethBlock.BaseFee(), test.rules); err != nil { 628 t.Fatal("newImportTx created an invalid transaction", err) 629 } 630 631 commitBatch, err := vm.db.CommitBatch() 632 if err != nil { 633 t.Fatalf("Failed to create commit batch for VM due to %s", err) 634 } 635 if err := importTx.Accept(vm.ctx, commitBatch); err != nil { 636 t.Fatalf("Failed to accept import transaction due to: %s", err) 637 } 638 639 stdb, err := vm.chain.CurrentState() 640 if err != nil { 641 t.Fatal(err) 642 } 643 err = importTx.EVMStateTransfer(vm.ctx, stdb) 644 if err != nil { 645 t.Fatal(err) 646 } 647 648 addr := GetEthAddress(testKeys[0]) 649 if stdb.GetBalance(addr).Cmp(new(big.Int).SetUint64(test.bal*units.Avax)) != 0 { 650 t.Fatalf("address balance %s equal %s not %s", addr.String(), stdb.GetBalance(addr), new(big.Int).SetUint64(test.bal*units.Avax)) 651 } 652 }) 653 } 654 } 655 656 // Note: this is a brittle test to ensure that the gas cost of a transaction does 657 // not change 658 func TestImportTxGasCost(t *testing.T) { 659 avaxAssetID := ids.GenerateTestID() 660 antAssetID := ids.GenerateTestID() 661 chainID := ids.GenerateTestID() 662 xChainID := ids.GenerateTestID() 663 networkID := uint32(5) 664 importAmount := uint64(5000000) 665 666 tests := map[string]struct { 667 UnsignedImportTx *UnsignedImportTx 668 Keys [][]*crypto.PrivateKeySECP256K1R 669 670 ExpectedCost uint64 671 ExpectedFee uint64 672 BaseFee *big.Int 673 }{ 674 "simple import": { 675 UnsignedImportTx: &UnsignedImportTx{ 676 NetworkID: networkID, 677 BlockchainID: chainID, 678 SourceChain: xChainID, 679 ImportedInputs: []*avax.TransferableInput{{ 680 UTXOID: avax.UTXOID{ 681 TxID: ids.ID{ 682 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 683 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 684 0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea, 685 0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xe8, 686 }, 687 }, 688 Asset: avax.Asset{ID: avaxAssetID}, 689 In: &secp256k1fx.TransferInput{ 690 Amt: importAmount, 691 Input: secp256k1fx.Input{SigIndices: []uint32{0}}, 692 }, 693 }}, 694 Outs: []EVMOutput{{ 695 Address: testEthAddrs[0], 696 Amount: importAmount, 697 AssetID: avaxAssetID, 698 }}, 699 }, 700 Keys: [][]*crypto.PrivateKeySECP256K1R{{testKeys[0]}}, 701 ExpectedCost: 1230, 702 ExpectedFee: 30750, 703 BaseFee: big.NewInt(25 * params.GWei), 704 }, 705 "simple import 1wei": { 706 UnsignedImportTx: &UnsignedImportTx{ 707 NetworkID: networkID, 708 BlockchainID: chainID, 709 SourceChain: xChainID, 710 ImportedInputs: []*avax.TransferableInput{{ 711 UTXOID: avax.UTXOID{ 712 TxID: ids.ID{ 713 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 714 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 715 0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea, 716 0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xe8, 717 }, 718 }, 719 Asset: avax.Asset{ID: avaxAssetID}, 720 In: &secp256k1fx.TransferInput{ 721 Amt: importAmount, 722 Input: secp256k1fx.Input{SigIndices: []uint32{0}}, 723 }, 724 }}, 725 Outs: []EVMOutput{{ 726 Address: testEthAddrs[0], 727 Amount: importAmount, 728 AssetID: avaxAssetID, 729 }}, 730 }, 731 Keys: [][]*crypto.PrivateKeySECP256K1R{{testKeys[0]}}, 732 ExpectedCost: 1230, 733 ExpectedFee: 1, 734 BaseFee: big.NewInt(1), 735 }, 736 "simple ANT import": { 737 UnsignedImportTx: &UnsignedImportTx{ 738 NetworkID: networkID, 739 BlockchainID: chainID, 740 SourceChain: xChainID, 741 ImportedInputs: []*avax.TransferableInput{ 742 { 743 UTXOID: avax.UTXOID{ 744 TxID: ids.ID{ 745 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 746 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 747 0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea, 748 0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xe8, 749 }, 750 }, 751 Asset: avax.Asset{ID: avaxAssetID}, 752 In: &secp256k1fx.TransferInput{ 753 Amt: importAmount, 754 Input: secp256k1fx.Input{SigIndices: []uint32{0}}, 755 }, 756 }, 757 { 758 UTXOID: avax.UTXOID{ 759 TxID: ids.ID{ 760 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 761 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 762 0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea, 763 0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xe9, 764 }, 765 }, 766 Asset: avax.Asset{ID: antAssetID}, 767 In: &secp256k1fx.TransferInput{ 768 Amt: importAmount, 769 Input: secp256k1fx.Input{SigIndices: []uint32{0}}, 770 }, 771 }, 772 }, 773 Outs: []EVMOutput{ 774 { 775 Address: testEthAddrs[0], 776 Amount: importAmount, 777 AssetID: antAssetID, 778 }, 779 }, 780 }, 781 Keys: [][]*crypto.PrivateKeySECP256K1R{{testKeys[0]}, {testKeys[0]}}, 782 ExpectedCost: 2318, 783 ExpectedFee: 57950, 784 BaseFee: big.NewInt(25 * params.GWei), 785 }, 786 "complex ANT import": { 787 UnsignedImportTx: &UnsignedImportTx{ 788 NetworkID: networkID, 789 BlockchainID: chainID, 790 SourceChain: xChainID, 791 ImportedInputs: []*avax.TransferableInput{ 792 { 793 UTXOID: avax.UTXOID{ 794 TxID: ids.ID{ 795 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 796 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 797 0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea, 798 0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xe8, 799 }, 800 }, 801 Asset: avax.Asset{ID: avaxAssetID}, 802 In: &secp256k1fx.TransferInput{ 803 Amt: importAmount, 804 Input: secp256k1fx.Input{SigIndices: []uint32{0}}, 805 }, 806 }, 807 { 808 UTXOID: avax.UTXOID{ 809 TxID: ids.ID{ 810 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 811 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 812 0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea, 813 0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xe9, 814 }, 815 }, 816 Asset: avax.Asset{ID: antAssetID}, 817 In: &secp256k1fx.TransferInput{ 818 Amt: importAmount, 819 Input: secp256k1fx.Input{SigIndices: []uint32{0}}, 820 }, 821 }, 822 }, 823 Outs: []EVMOutput{ 824 { 825 Address: testEthAddrs[0], 826 Amount: importAmount, 827 AssetID: avaxAssetID, 828 }, 829 { 830 Address: testEthAddrs[0], 831 Amount: importAmount, 832 AssetID: antAssetID, 833 }, 834 }, 835 }, 836 Keys: [][]*crypto.PrivateKeySECP256K1R{{testKeys[0]}, {testKeys[0]}}, 837 ExpectedCost: 2378, 838 ExpectedFee: 59450, 839 BaseFee: big.NewInt(25 * params.GWei), 840 }, 841 "multisig import": { 842 UnsignedImportTx: &UnsignedImportTx{ 843 NetworkID: networkID, 844 BlockchainID: chainID, 845 SourceChain: xChainID, 846 ImportedInputs: []*avax.TransferableInput{{ 847 UTXOID: avax.UTXOID{ 848 TxID: ids.ID{ 849 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 850 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 851 0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea, 852 0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xe8, 853 }, 854 }, 855 Asset: avax.Asset{ID: avaxAssetID}, 856 In: &secp256k1fx.TransferInput{ 857 Amt: importAmount, 858 Input: secp256k1fx.Input{SigIndices: []uint32{0, 1}}, 859 }, 860 }}, 861 Outs: []EVMOutput{{ 862 Address: testEthAddrs[0], 863 Amount: importAmount, 864 AssetID: avaxAssetID, 865 }}, 866 }, 867 Keys: [][]*crypto.PrivateKeySECP256K1R{{testKeys[0], testKeys[1]}}, 868 ExpectedCost: 2234, 869 ExpectedFee: 55850, 870 BaseFee: big.NewInt(25 * params.GWei), 871 }, 872 "large import": { 873 UnsignedImportTx: &UnsignedImportTx{ 874 NetworkID: networkID, 875 BlockchainID: chainID, 876 SourceChain: xChainID, 877 ImportedInputs: []*avax.TransferableInput{ 878 { 879 UTXOID: avax.UTXOID{ 880 TxID: ids.ID{ 881 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 882 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 883 0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea, 884 0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xa0, 885 }, 886 }, 887 Asset: avax.Asset{ID: avaxAssetID}, 888 In: &secp256k1fx.TransferInput{ 889 Amt: importAmount, 890 Input: secp256k1fx.Input{SigIndices: []uint32{0}}, 891 }, 892 }, 893 { 894 UTXOID: avax.UTXOID{ 895 TxID: ids.ID{ 896 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 897 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 898 0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea, 899 0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xa1, 900 }, 901 }, 902 Asset: avax.Asset{ID: avaxAssetID}, 903 In: &secp256k1fx.TransferInput{ 904 Amt: importAmount, 905 Input: secp256k1fx.Input{SigIndices: []uint32{0}}, 906 }, 907 }, 908 { 909 UTXOID: avax.UTXOID{ 910 TxID: ids.ID{ 911 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 912 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 913 0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea, 914 0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xa2, 915 }, 916 }, 917 Asset: avax.Asset{ID: avaxAssetID}, 918 In: &secp256k1fx.TransferInput{ 919 Amt: importAmount, 920 Input: secp256k1fx.Input{SigIndices: []uint32{0}}, 921 }, 922 }, 923 { 924 UTXOID: avax.UTXOID{ 925 TxID: ids.ID{ 926 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 927 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 928 0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea, 929 0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xa3, 930 }, 931 }, 932 Asset: avax.Asset{ID: avaxAssetID}, 933 In: &secp256k1fx.TransferInput{ 934 Amt: importAmount, 935 Input: secp256k1fx.Input{SigIndices: []uint32{0}}, 936 }, 937 }, 938 { 939 UTXOID: avax.UTXOID{ 940 TxID: ids.ID{ 941 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 942 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 943 0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea, 944 0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xa4, 945 }, 946 }, 947 Asset: avax.Asset{ID: avaxAssetID}, 948 In: &secp256k1fx.TransferInput{ 949 Amt: importAmount, 950 Input: secp256k1fx.Input{SigIndices: []uint32{0}}, 951 }, 952 }, 953 { 954 UTXOID: avax.UTXOID{ 955 TxID: ids.ID{ 956 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 957 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 958 0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea, 959 0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xa5, 960 }, 961 }, 962 Asset: avax.Asset{ID: avaxAssetID}, 963 In: &secp256k1fx.TransferInput{ 964 Amt: importAmount, 965 Input: secp256k1fx.Input{SigIndices: []uint32{0}}, 966 }, 967 }, 968 { 969 UTXOID: avax.UTXOID{ 970 TxID: ids.ID{ 971 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 972 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 973 0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea, 974 0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xa6, 975 }, 976 }, 977 Asset: avax.Asset{ID: avaxAssetID}, 978 In: &secp256k1fx.TransferInput{ 979 Amt: importAmount, 980 Input: secp256k1fx.Input{SigIndices: []uint32{0}}, 981 }, 982 }, 983 { 984 UTXOID: avax.UTXOID{ 985 TxID: ids.ID{ 986 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 987 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 988 0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea, 989 0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xa7, 990 }, 991 }, 992 Asset: avax.Asset{ID: avaxAssetID}, 993 In: &secp256k1fx.TransferInput{ 994 Amt: importAmount, 995 Input: secp256k1fx.Input{SigIndices: []uint32{0}}, 996 }, 997 }, 998 { 999 UTXOID: avax.UTXOID{ 1000 TxID: ids.ID{ 1001 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 1002 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 1003 0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea, 1004 0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xa8, 1005 }, 1006 }, 1007 Asset: avax.Asset{ID: avaxAssetID}, 1008 In: &secp256k1fx.TransferInput{ 1009 Amt: importAmount, 1010 Input: secp256k1fx.Input{SigIndices: []uint32{0}}, 1011 }, 1012 }, 1013 { 1014 UTXOID: avax.UTXOID{ 1015 TxID: ids.ID{ 1016 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 1017 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 1018 0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea, 1019 0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xa9, 1020 }, 1021 }, 1022 Asset: avax.Asset{ID: avaxAssetID}, 1023 In: &secp256k1fx.TransferInput{ 1024 Amt: importAmount, 1025 Input: secp256k1fx.Input{SigIndices: []uint32{0}}, 1026 }, 1027 }, 1028 }, 1029 Outs: []EVMOutput{ 1030 { 1031 Address: testEthAddrs[0], 1032 Amount: importAmount * 10, 1033 AssetID: avaxAssetID, 1034 }, 1035 }, 1036 }, 1037 Keys: [][]*crypto.PrivateKeySECP256K1R{ 1038 {testKeys[0]}, 1039 {testKeys[0]}, 1040 {testKeys[0]}, 1041 {testKeys[0]}, 1042 {testKeys[0]}, 1043 {testKeys[0]}, 1044 {testKeys[0]}, 1045 {testKeys[0]}, 1046 {testKeys[0]}, 1047 {testKeys[0]}, 1048 }, 1049 ExpectedCost: 11022, 1050 ExpectedFee: 275550, 1051 BaseFee: big.NewInt(25 * params.GWei), 1052 }, 1053 } 1054 1055 for name, test := range tests { 1056 t.Run(name, func(t *testing.T) { 1057 tx := &Tx{UnsignedAtomicTx: test.UnsignedImportTx} 1058 1059 // Sign with the correct key 1060 if err := tx.Sign(Codec, test.Keys); err != nil { 1061 t.Fatal(err) 1062 } 1063 1064 cost, err := tx.Cost() 1065 if err != nil { 1066 t.Fatal(err) 1067 } 1068 if cost != test.ExpectedCost { 1069 t.Fatalf("Expected cost to be %d, but found %d", test.ExpectedCost, cost) 1070 } 1071 1072 fee, err := calculateDynamicFee(cost, test.BaseFee) 1073 if err != nil { 1074 t.Fatal(err) 1075 } 1076 if fee != test.ExpectedFee { 1077 t.Fatalf("Expected fee to be %d, but found %d", test.ExpectedFee, fee) 1078 } 1079 }) 1080 } 1081 }