gitlab.com/flarenetwork/coreth@v0.1.1/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 "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 TestExportTxVerifyNil(t *testing.T) { 21 var exportTx *UnsignedExportTx 22 if err := exportTx.Verify(testXChainID, NewContext(), apricotRulesPhase0); err == nil { 23 t.Fatal("Verify should have failed due to nil transaction") 24 } 25 } 26 27 func TestExportTxVerify(t *testing.T) { 28 var exportAmount uint64 = 10000000 29 exportTx := &UnsignedExportTx{ 30 NetworkID: testNetworkID, 31 BlockchainID: testCChainID, 32 DestinationChain: testXChainID, 33 Ins: []EVMInput{ 34 { 35 Address: testEthAddrs[0], 36 Amount: exportAmount, 37 AssetID: testAvaxAssetID, 38 Nonce: 0, 39 }, 40 { 41 Address: testEthAddrs[2], 42 Amount: exportAmount, 43 AssetID: testAvaxAssetID, 44 Nonce: 0, 45 }, 46 }, 47 ExportedOutputs: []*avax.TransferableOutput{ 48 { 49 Asset: avax.Asset{ID: testAvaxAssetID}, 50 Out: &secp256k1fx.TransferOutput{ 51 Amt: exportAmount, 52 OutputOwners: secp256k1fx.OutputOwners{ 53 Locktime: 0, 54 Threshold: 1, 55 Addrs: []ids.ShortID{testShortIDAddrs[0]}, 56 }, 57 }, 58 }, 59 { 60 Asset: avax.Asset{ID: testAvaxAssetID}, 61 Out: &secp256k1fx.TransferOutput{ 62 Amt: exportAmount, 63 OutputOwners: secp256k1fx.OutputOwners{ 64 Locktime: 0, 65 Threshold: 1, 66 Addrs: []ids.ShortID{testShortIDAddrs[1]}, 67 }, 68 }, 69 }, 70 }, 71 } 72 73 // Sort the inputs and outputs to ensure the transaction is canonical 74 avax.SortTransferableOutputs(exportTx.ExportedOutputs, Codec) 75 // Pass in a list of signers here with the appropriate length 76 // to avoid causing a nil-pointer error in the helper method 77 emptySigners := make([][]*crypto.PrivateKeySECP256K1R, 2) 78 SortEVMInputsAndSigners(exportTx.Ins, emptySigners) 79 80 ctx := NewContext() 81 82 // Test Valid Export Tx 83 if err := exportTx.Verify(testXChainID, ctx, apricotRulesPhase1); err != nil { 84 t.Fatalf("Failed to verify valid ExportTx: %s", err) 85 } 86 87 exportTx.NetworkID = testNetworkID + 1 88 89 // Test Incorrect Network ID Errors 90 if err := exportTx.Verify(testXChainID, ctx, apricotRulesPhase1); err == nil { 91 t.Fatal("ExportTx should have failed verification due to incorrect network ID") 92 } 93 94 exportTx.NetworkID = testNetworkID 95 exportTx.BlockchainID = nonExistentID 96 // Test Incorrect Blockchain ID Errors 97 if err := exportTx.Verify(testXChainID, ctx, apricotRulesPhase1); err == nil { 98 t.Fatal("ExportTx should have failed verification due to incorrect blockchain ID") 99 } 100 101 exportTx.BlockchainID = testCChainID 102 exportTx.DestinationChain = nonExistentID 103 // Test Incorrect Destination Chain ID Errors 104 if err := exportTx.Verify(testXChainID, ctx, apricotRulesPhase1); err == nil { 105 t.Fatal("ExportTx should have failed verification due to incorrect destination chain") 106 } 107 108 exportTx.DestinationChain = testXChainID 109 exportedOuts := exportTx.ExportedOutputs 110 exportTx.ExportedOutputs = nil 111 evmInputs := exportTx.Ins 112 // Test No Exported Outputs Errors 113 if err := exportTx.Verify(testXChainID, ctx, apricotRulesPhase1); err == nil { 114 t.Fatal("ExportTx should have failed verification due to no exported outputs") 115 } 116 117 exportTx.ExportedOutputs = []*avax.TransferableOutput{exportedOuts[1], exportedOuts[0]} 118 // Test Unsorted outputs Errors 119 if err := exportTx.Verify(testXChainID, ctx, apricotRulesPhase1); err == nil { 120 t.Fatal("ExportTx should have failed verification due to no unsorted exported outputs") 121 } 122 123 exportTx.ExportedOutputs = []*avax.TransferableOutput{exportedOuts[0], nil} 124 // Test invalid exported output 125 if err := exportTx.Verify(testXChainID, ctx, apricotRulesPhase1); err == nil { 126 t.Fatal("ExportTx should have failed verification due to invalid output") 127 } 128 129 exportTx.ExportedOutputs = []*avax.TransferableOutput{exportedOuts[0], exportedOuts[1]} 130 exportTx.Ins = []EVMInput{evmInputs[1], evmInputs[0]} 131 // Test unsorted EVM Inputs passes before AP1 132 if err := exportTx.Verify(testXChainID, ctx, apricotRulesPhase0); err != nil { 133 t.Fatalf("ExportTx should have passed verification before AP1, but failed due to %s", err) 134 } 135 // Test unsorted EVM Inputs fails after AP1 136 if err := exportTx.Verify(testXChainID, ctx, apricotRulesPhase1); err == nil { 137 t.Fatal("ExportTx should have failed verification due to unsorted EVM Inputs") 138 } 139 exportTx.Ins = []EVMInput{ 140 { 141 Address: testEthAddrs[0], 142 Amount: 0, 143 AssetID: testAvaxAssetID, 144 Nonce: 0, 145 }, 146 } 147 // Test ExportTx with invalid EVM Input amount 0 fails verification 148 if err := exportTx.Verify(testXChainID, ctx, apricotRulesPhase1); err == nil { 149 t.Fatal("ExportTx should have failed verification due to 0 value amount") 150 } 151 exportTx.Ins = []EVMInput{evmInputs[0], evmInputs[0]} 152 // Test non-unique EVM Inputs passes verification before AP1 153 if err := exportTx.Verify(testXChainID, ctx, apricotRulesPhase0); err != nil { 154 t.Fatalf("ExportTx with non-unique EVM Inputs should have passed verification prior to AP1, but failed due to %s", err) 155 } 156 exportTx.Ins = []EVMInput{evmInputs[0], evmInputs[0]} 157 // Test non-unique EVM Inputs fails verification after AP1 158 if err := exportTx.Verify(testXChainID, ctx, apricotRulesPhase1); err == nil { 159 t.Fatal("ExportTx should have failed verification due to non-unique inputs") 160 } 161 } 162 163 // Note: this is a brittle test to ensure that the gas cost of a transaction does 164 // not change 165 func TestExportTxGasCost(t *testing.T) { 166 avaxAssetID := ids.GenerateTestID() 167 chainID := ids.GenerateTestID() 168 xChainID := ids.GenerateTestID() 169 networkID := uint32(5) 170 exportAmount := uint64(5000000) 171 172 tests := map[string]struct { 173 UnsignedExportTx *UnsignedExportTx 174 Keys [][]*crypto.PrivateKeySECP256K1R 175 176 BaseFee *big.Int 177 ExpectedCost uint64 178 ExpectedFee uint64 179 }{ 180 "simple export 1wei BaseFee": { 181 UnsignedExportTx: &UnsignedExportTx{ 182 NetworkID: networkID, 183 BlockchainID: chainID, 184 DestinationChain: xChainID, 185 Ins: []EVMInput{ 186 { 187 Address: testEthAddrs[0], 188 Amount: exportAmount, 189 AssetID: avaxAssetID, 190 Nonce: 0, 191 }, 192 }, 193 ExportedOutputs: []*avax.TransferableOutput{ 194 { 195 Asset: avax.Asset{ID: avaxAssetID}, 196 Out: &secp256k1fx.TransferOutput{ 197 Amt: exportAmount, 198 OutputOwners: secp256k1fx.OutputOwners{ 199 Locktime: 0, 200 Threshold: 1, 201 Addrs: []ids.ShortID{testShortIDAddrs[0]}, 202 }, 203 }, 204 }, 205 }, 206 }, 207 Keys: [][]*crypto.PrivateKeySECP256K1R{{testKeys[0]}}, 208 ExpectedCost: 1230, 209 ExpectedFee: 1, 210 BaseFee: big.NewInt(1), 211 }, 212 "simple export 25Gwei BaseFee": { 213 UnsignedExportTx: &UnsignedExportTx{ 214 NetworkID: networkID, 215 BlockchainID: chainID, 216 DestinationChain: xChainID, 217 Ins: []EVMInput{ 218 { 219 Address: testEthAddrs[0], 220 Amount: exportAmount, 221 AssetID: avaxAssetID, 222 Nonce: 0, 223 }, 224 }, 225 ExportedOutputs: []*avax.TransferableOutput{ 226 { 227 Asset: avax.Asset{ID: avaxAssetID}, 228 Out: &secp256k1fx.TransferOutput{ 229 Amt: exportAmount, 230 OutputOwners: secp256k1fx.OutputOwners{ 231 Locktime: 0, 232 Threshold: 1, 233 Addrs: []ids.ShortID{testShortIDAddrs[0]}, 234 }, 235 }, 236 }, 237 }, 238 }, 239 Keys: [][]*crypto.PrivateKeySECP256K1R{{testKeys[0]}}, 240 ExpectedCost: 1230, 241 ExpectedFee: 30750, 242 BaseFee: big.NewInt(25 * params.GWei), 243 }, 244 "simple export 225Gwei BaseFee": { 245 UnsignedExportTx: &UnsignedExportTx{ 246 NetworkID: networkID, 247 BlockchainID: chainID, 248 DestinationChain: xChainID, 249 Ins: []EVMInput{ 250 { 251 Address: testEthAddrs[0], 252 Amount: exportAmount, 253 AssetID: avaxAssetID, 254 Nonce: 0, 255 }, 256 }, 257 ExportedOutputs: []*avax.TransferableOutput{ 258 { 259 Asset: avax.Asset{ID: avaxAssetID}, 260 Out: &secp256k1fx.TransferOutput{ 261 Amt: exportAmount, 262 OutputOwners: secp256k1fx.OutputOwners{ 263 Locktime: 0, 264 Threshold: 1, 265 Addrs: []ids.ShortID{testShortIDAddrs[0]}, 266 }, 267 }, 268 }, 269 }, 270 }, 271 Keys: [][]*crypto.PrivateKeySECP256K1R{{testKeys[0]}}, 272 ExpectedCost: 1230, 273 ExpectedFee: 276750, 274 BaseFee: big.NewInt(225 * params.GWei), 275 }, 276 "complex export 25Gwei BaseFee": { 277 UnsignedExportTx: &UnsignedExportTx{ 278 NetworkID: networkID, 279 BlockchainID: chainID, 280 DestinationChain: xChainID, 281 Ins: []EVMInput{ 282 { 283 Address: testEthAddrs[0], 284 Amount: exportAmount, 285 AssetID: avaxAssetID, 286 Nonce: 0, 287 }, 288 { 289 Address: testEthAddrs[1], 290 Amount: exportAmount, 291 AssetID: avaxAssetID, 292 Nonce: 0, 293 }, 294 { 295 Address: testEthAddrs[2], 296 Amount: exportAmount, 297 AssetID: avaxAssetID, 298 Nonce: 0, 299 }, 300 }, 301 ExportedOutputs: []*avax.TransferableOutput{ 302 { 303 Asset: avax.Asset{ID: avaxAssetID}, 304 Out: &secp256k1fx.TransferOutput{ 305 Amt: exportAmount * 3, 306 OutputOwners: secp256k1fx.OutputOwners{ 307 Locktime: 0, 308 Threshold: 1, 309 Addrs: []ids.ShortID{testShortIDAddrs[0]}, 310 }, 311 }, 312 }, 313 }, 314 }, 315 Keys: [][]*crypto.PrivateKeySECP256K1R{{testKeys[0], testKeys[0], testKeys[0]}}, 316 ExpectedCost: 3366, 317 ExpectedFee: 84150, 318 BaseFee: big.NewInt(25 * params.GWei), 319 }, 320 "complex export 225Gwei BaseFee": { 321 UnsignedExportTx: &UnsignedExportTx{ 322 NetworkID: networkID, 323 BlockchainID: chainID, 324 DestinationChain: xChainID, 325 Ins: []EVMInput{ 326 { 327 Address: testEthAddrs[0], 328 Amount: exportAmount, 329 AssetID: avaxAssetID, 330 Nonce: 0, 331 }, 332 { 333 Address: testEthAddrs[1], 334 Amount: exportAmount, 335 AssetID: avaxAssetID, 336 Nonce: 0, 337 }, 338 { 339 Address: testEthAddrs[2], 340 Amount: exportAmount, 341 AssetID: avaxAssetID, 342 Nonce: 0, 343 }, 344 }, 345 ExportedOutputs: []*avax.TransferableOutput{ 346 { 347 Asset: avax.Asset{ID: avaxAssetID}, 348 Out: &secp256k1fx.TransferOutput{ 349 Amt: exportAmount * 3, 350 OutputOwners: secp256k1fx.OutputOwners{ 351 Locktime: 0, 352 Threshold: 1, 353 Addrs: []ids.ShortID{testShortIDAddrs[0]}, 354 }, 355 }, 356 }, 357 }, 358 }, 359 Keys: [][]*crypto.PrivateKeySECP256K1R{{testKeys[0], testKeys[0], testKeys[0]}}, 360 ExpectedCost: 3366, 361 ExpectedFee: 757350, 362 BaseFee: big.NewInt(225 * params.GWei), 363 }, 364 } 365 366 for name, test := range tests { 367 t.Run(name, func(t *testing.T) { 368 tx := &Tx{UnsignedAtomicTx: test.UnsignedExportTx} 369 370 // Sign with the correct key 371 if err := tx.Sign(Codec, test.Keys); err != nil { 372 t.Fatal(err) 373 } 374 375 cost, err := tx.Cost() 376 if err != nil { 377 t.Fatal(err) 378 } 379 if cost != test.ExpectedCost { 380 t.Fatalf("Expected cost to be %d, but found %d", test.ExpectedCost, cost) 381 } 382 383 fee, err := calculateDynamicFee(cost, test.BaseFee) 384 if err != nil { 385 t.Fatal(err) 386 } 387 if fee != test.ExpectedFee { 388 t.Fatalf("Expected fee to be %d, but found %d", test.ExpectedFee, fee) 389 } 390 }) 391 } 392 } 393 394 func TestNewExportTx(t *testing.T) { 395 tests := []struct { 396 name string 397 genesis string 398 rules params.Rules 399 bal uint64 400 }{ 401 { 402 name: "apricot phase 0", 403 genesis: genesisJSONApricotPhase0, 404 rules: apricotRulesPhase0, 405 bal: 44000000, 406 }, 407 { 408 name: "apricot phase 1", 409 genesis: genesisJSONApricotPhase1, 410 rules: apricotRulesPhase1, 411 bal: 44000000, 412 }, 413 { 414 name: "apricot phase 2", 415 genesis: genesisJSONApricotPhase2, 416 rules: apricotRulesPhase2, 417 bal: 43000000, 418 }, 419 { 420 name: "apricot phase 3", 421 genesis: genesisJSONApricotPhase3, 422 rules: apricotRulesPhase3, 423 bal: 44446500, 424 }, 425 } 426 for _, test := range tests { 427 t.Run(test.name, func(t *testing.T) { 428 issuer, vm, _, sharedMemory := GenesisVM(t, true, test.genesis, "", "") 429 430 defer func() { 431 if err := vm.Shutdown(); err != nil { 432 t.Fatal(err) 433 } 434 }() 435 436 parent := vm.LastAcceptedBlockInternal().(*Block) 437 importAmount := uint64(50000000) 438 utxoID := avax.UTXOID{ 439 TxID: ids.ID{ 440 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 441 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 442 0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea, 443 0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xe8, 444 }, 445 } 446 447 utxo := &avax.UTXO{ 448 UTXOID: utxoID, 449 Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, 450 Out: &secp256k1fx.TransferOutput{ 451 Amt: importAmount, 452 OutputOwners: secp256k1fx.OutputOwners{ 453 Threshold: 1, 454 Addrs: []ids.ShortID{testKeys[0].PublicKey().Address()}, 455 }, 456 }, 457 } 458 utxoBytes, err := vm.codec.Marshal(codecVersion, utxo) 459 if err != nil { 460 t.Fatal(err) 461 } 462 463 xChainSharedMemory := sharedMemory.NewSharedMemory(vm.ctx.XChainID) 464 inputID := utxo.InputID() 465 if err := xChainSharedMemory.Apply(map[ids.ID]*atomic.Requests{vm.ctx.ChainID: {PutRequests: []*atomic.Element{{ 466 Key: inputID[:], 467 Value: utxoBytes, 468 Traits: [][]byte{ 469 testKeys[0].PublicKey().Address().Bytes(), 470 }, 471 }}}}); err != nil { 472 t.Fatal(err) 473 } 474 475 tx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*crypto.PrivateKeySECP256K1R{testKeys[0]}) 476 if err != nil { 477 t.Fatal(err) 478 } 479 480 if err := vm.issueTx(tx); err != nil { 481 t.Fatal(err) 482 } 483 484 <-issuer 485 486 blk, err := vm.BuildBlock() 487 if err != nil { 488 t.Fatal(err) 489 } 490 491 if err := blk.Verify(); err != nil { 492 t.Fatal(err) 493 } 494 495 if err := vm.SetPreference(blk.ID()); err != nil { 496 t.Fatal(err) 497 } 498 499 if err := blk.Accept(); err != nil { 500 t.Fatal(err) 501 } 502 503 parent = vm.LastAcceptedBlockInternal().(*Block) 504 exportAmount := uint64(5000000) 505 506 testKeys0Addr := GetEthAddress(testKeys[0]) 507 exportId, err := ids.ToShortID(testKeys0Addr[:]) 508 if err != nil { 509 t.Fatal(err) 510 } 511 512 tx, err = vm.newExportTx(vm.ctx.AVAXAssetID, exportAmount, vm.ctx.XChainID, exportId, initialBaseFee, []*crypto.PrivateKeySECP256K1R{testKeys[0]}) 513 if err != nil { 514 t.Fatal(err) 515 } 516 517 exportTx := tx.UnsignedAtomicTx 518 519 if err := exportTx.SemanticVerify(vm, tx, parent, parent.ethBlock.BaseFee(), test.rules); err != nil { 520 t.Fatal("newExportTx created an invalid transaction", err) 521 } 522 523 commitBatch, err := vm.db.CommitBatch() 524 if err != nil { 525 t.Fatalf("Failed to create commit batch for VM due to %s", err) 526 } 527 if err := exportTx.Accept(vm.ctx, commitBatch); err != nil { 528 t.Fatalf("Failed to accept export transaction due to: %s", err) 529 } 530 531 stdb, err := vm.chain.CurrentState() 532 if err != nil { 533 t.Fatal(err) 534 } 535 err = exportTx.EVMStateTransfer(vm.ctx, stdb) 536 if err != nil { 537 t.Fatal(err) 538 } 539 540 addr := GetEthAddress(testKeys[0]) 541 if stdb.GetBalance(addr).Cmp(new(big.Int).SetUint64(test.bal*units.Avax)) != 0 { 542 t.Fatalf("address balance %s equal %s not %s", addr.String(), stdb.GetBalance(addr), new(big.Int).SetUint64(test.bal*units.Avax)) 543 } 544 }) 545 } 546 }