Skip to content

Commit 497dad9

Browse files
Add Invalid Transaction Rejection Test
1 parent 28122c7 commit 497dad9

1 file changed

Lines changed: 235 additions & 0 deletions

File tree

test/e2e/evm_sequencer_e2e_test.go

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,3 +548,238 @@ func TestEvmDoubleSpendNonceHandlingE2E(t *testing.T) {
548548
t.Logf(" - EVM correctly rejected duplicate nonce transaction")
549549
t.Logf(" - System maintains transaction integrity and prevents double-spending")
550550
}
551+
552+
// TestEvmInvalidTransactionRejectionE2E tests the system's ability to properly reject
553+
// various types of invalid transactions and ensure they are not included in any blocks.
554+
//
555+
// Test Purpose:
556+
// - Confirm that invalid transactions are rejected and not included in blocks
557+
// - Test various types of invalid transactions (bad signature, insufficient funds, malformed data)
558+
// - Ensure system stability when processing invalid transactions
559+
// - Validate that valid transactions still work after invalid ones are rejected
560+
//
561+
// Test Flow:
562+
// 1. Sets up Local DA layer and EVM sequencer
563+
// 2. Submits various invalid transactions:
564+
// a. Transaction with invalid signature
565+
// b. Transaction with insufficient funds (from empty account)
566+
// c. Transaction with invalid nonce (too high)
567+
// d. Transaction with invalid gas limit (too low)
568+
// 3. Verifies that all invalid transactions are rejected
569+
// 4. Submits a valid transaction to confirm system stability
570+
// 5. Ensures the valid transaction is processed correctly
571+
//
572+
// Expected Behavior:
573+
// - All invalid transactions should be rejected at submission or processing
574+
// - No invalid transactions should appear in any blocks
575+
// - Valid transactions should continue to work normally
576+
// - System should remain stable and responsive after rejecting invalid transactions
577+
//
578+
// This test validates Rollkit's transaction validation and rejection mechanisms.
579+
func TestEvmInvalidTransactionRejectionE2E(t *testing.T) {
580+
flag.Parse()
581+
workDir := t.TempDir()
582+
nodeHome := filepath.Join(workDir, "evm-agg")
583+
sut := NewSystemUnderTest(t)
584+
585+
// 1. Start local DA
586+
localDABinary := "local-da"
587+
if evmSingleBinaryPath != "evm-single" {
588+
localDABinary = filepath.Join(filepath.Dir(evmSingleBinaryPath), "local-da")
589+
}
590+
sut.ExecCmd(localDABinary)
591+
t.Log("Started local DA")
592+
time.Sleep(200 * time.Millisecond)
593+
594+
// 2. Start EVM (Reth) via Docker Compose
595+
jwtSecret := setupTestRethEngineE2E(t)
596+
597+
// 3. Get genesis hash from EVM node
598+
genesisHash := evm.GetGenesisHash(t)
599+
t.Logf("Genesis hash: %s", genesisHash)
600+
601+
// 4. Initialize sequencer node
602+
output, err := sut.RunCmd(evmSingleBinaryPath,
603+
"init",
604+
"--rollkit.node.aggregator=true",
605+
"--rollkit.signer.passphrase", "secret",
606+
"--home", nodeHome,
607+
)
608+
require.NoError(t, err, "failed to init sequencer", output)
609+
t.Log("Initialized sequencer node")
610+
611+
// 5. Start sequencer node
612+
sut.ExecCmd(evmSingleBinaryPath,
613+
"start",
614+
"--evm.jwt-secret", jwtSecret,
615+
"--evm.genesis-hash", genesisHash,
616+
"--rollkit.node.block_time", "1s",
617+
"--rollkit.node.aggregator=true",
618+
"--rollkit.signer.passphrase", "secret",
619+
"--home", nodeHome,
620+
"--rollkit.da.address", "http://localhost:7980",
621+
"--rollkit.da.block_time", "1m",
622+
)
623+
sut.AwaitNodeUp(t, "http://127.0.0.1:7331", 10*time.Second)
624+
t.Log("Sequencer node is up")
625+
626+
// 6. Connect to EVM
627+
client, err := ethclient.Dial("http://localhost:8545")
628+
require.NoError(t, err, "Should be able to connect to EVM")
629+
defer client.Close()
630+
631+
ctx := context.Background()
632+
var invalidTxHashes []common.Hash
633+
var invalidTxErrors []string
634+
635+
t.Log("Testing various invalid transaction types...")
636+
637+
// 7a. Test invalid signature transaction
638+
t.Log("7a. Testing transaction with invalid signature...")
639+
func() {
640+
defer func() {
641+
if r := recover(); r != nil {
642+
invalidTxErrors = append(invalidTxErrors, fmt.Sprintf("Invalid signature tx: %v", r))
643+
t.Logf("✅ Invalid signature transaction rejected as expected: %v", r)
644+
}
645+
}()
646+
647+
// Try to submit with a bad signature by creating a transaction with wrong private key
648+
badPrivKey := "1111111111111111111111111111111111111111111111111111111111111111"
649+
lastNonce := uint64(0)
650+
badTx := evm.GetRandomTransaction(t, badPrivKey, "0x944fDcD1c868E3cC566C78023CcB38A32cDA836E", "1234", 22000, &lastNonce)
651+
652+
err := client.SendTransaction(ctx, badTx)
653+
if err != nil {
654+
invalidTxErrors = append(invalidTxErrors, fmt.Sprintf("Invalid signature tx: %v", err))
655+
t.Logf("✅ Invalid signature transaction rejected as expected: %v", err)
656+
} else {
657+
invalidTxHashes = append(invalidTxHashes, badTx.Hash())
658+
t.Logf("⚠️ Invalid signature transaction was submitted: %s", badTx.Hash().Hex())
659+
}
660+
}()
661+
662+
// 7b. Test insufficient funds transaction
663+
t.Log("7b. Testing transaction with insufficient funds...")
664+
func() {
665+
defer func() {
666+
if r := recover(); r != nil {
667+
invalidTxErrors = append(invalidTxErrors, fmt.Sprintf("Insufficient funds tx: %v", r))
668+
t.Logf("✅ Insufficient funds transaction rejected as expected: %v", r)
669+
}
670+
}()
671+
672+
// Use an empty account that has no funds
673+
emptyAccountPrivKey := "2222222222222222222222222222222222222222222222222222222222222222"
674+
lastNonce := uint64(0)
675+
tx := evm.GetRandomTransaction(t, emptyAccountPrivKey, "0x944fDcD1c868E3cC566C78023CcB38A32cDA836E", "1234", 22000, &lastNonce)
676+
677+
err := client.SendTransaction(ctx, tx)
678+
if err != nil {
679+
invalidTxErrors = append(invalidTxErrors, fmt.Sprintf("Insufficient funds tx: %v", err))
680+
t.Logf("✅ Insufficient funds transaction rejected as expected: %v", err)
681+
} else {
682+
invalidTxHashes = append(invalidTxHashes, tx.Hash())
683+
t.Logf("⚠️ Insufficient funds transaction was submitted: %s", tx.Hash().Hex())
684+
}
685+
}()
686+
687+
// 7c. Test invalid nonce transaction (way too high)
688+
t.Log("7c. Testing transaction with invalid nonce...")
689+
func() {
690+
defer func() {
691+
if r := recover(); r != nil {
692+
invalidTxErrors = append(invalidTxErrors, fmt.Sprintf("Invalid nonce tx: %v", r))
693+
t.Logf("✅ Invalid nonce transaction rejected as expected: %v", r)
694+
}
695+
}()
696+
697+
// Use a very high nonce that's way ahead of the current account nonce
698+
lastNonce := uint64(999999)
699+
tx := evm.GetRandomTransaction(t, "cece4f25ac74deb1468965160c7185e07dff413f23fcadb611b05ca37ab0a52e", "0x944fDcD1c868E3cC566C78023CcB38A32cDA836E", "1234", 22000, &lastNonce)
700+
701+
err := client.SendTransaction(ctx, tx)
702+
if err != nil {
703+
invalidTxErrors = append(invalidTxErrors, fmt.Sprintf("Invalid nonce tx: %v", err))
704+
t.Logf("✅ Invalid nonce transaction rejected as expected: %v", err)
705+
} else {
706+
invalidTxHashes = append(invalidTxHashes, tx.Hash())
707+
t.Logf("⚠️ Invalid nonce transaction was submitted: %s", tx.Hash().Hex())
708+
}
709+
}()
710+
711+
// 7d. Test invalid gas limit transaction (too low)
712+
t.Log("7d. Testing transaction with invalid gas limit...")
713+
func() {
714+
defer func() {
715+
if r := recover(); r != nil {
716+
invalidTxErrors = append(invalidTxErrors, fmt.Sprintf("Invalid gas limit tx: %v", r))
717+
t.Logf("✅ Invalid gas limit transaction rejected as expected: %v", r)
718+
}
719+
}()
720+
721+
// Use an extremely low gas limit that's insufficient for basic transfer
722+
lastNonce := uint64(0)
723+
tx := evm.GetRandomTransaction(t, "cece4f25ac74deb1468965160c7185e07dff413f23fcadb611b05ca37ab0a52e", "0x944fDcD1c868E3cC566C78023CcB38A32cDA836E", "1234", 1000, &lastNonce) // Very low gas
724+
725+
err := client.SendTransaction(ctx, tx)
726+
if err != nil {
727+
invalidTxErrors = append(invalidTxErrors, fmt.Sprintf("Invalid gas limit tx: %v", err))
728+
t.Logf("✅ Invalid gas limit transaction rejected as expected: %v", err)
729+
} else {
730+
invalidTxHashes = append(invalidTxHashes, tx.Hash())
731+
t.Logf("⚠️ Invalid gas limit transaction was submitted: %s", tx.Hash().Hex())
732+
}
733+
}()
734+
735+
// 8. Wait a bit for any transactions to be processed
736+
t.Log("Waiting for transaction processing...")
737+
time.Sleep(5 * time.Second)
738+
739+
// 9. Check that none of the invalid transactions were included in blocks
740+
invalidTxsIncluded := 0
741+
for i, txHash := range invalidTxHashes {
742+
receipt, err := client.TransactionReceipt(ctx, txHash)
743+
if err == nil && receipt != nil {
744+
invalidTxsIncluded++
745+
t.Errorf("❌ Invalid transaction %d was included in block %d: %s", i+1, receipt.BlockNumber.Uint64(), txHash.Hex())
746+
}
747+
}
748+
749+
if invalidTxsIncluded > 0 {
750+
require.Fail(t, fmt.Sprintf("❌ %d invalid transactions were incorrectly included in blocks", invalidTxsIncluded))
751+
} else {
752+
t.Logf("✅ All invalid transactions were properly rejected: %d errors recorded", len(invalidTxErrors))
753+
for i, errMsg := range invalidTxErrors {
754+
t.Logf(" %d. %s", i+1, errMsg)
755+
}
756+
}
757+
758+
// 10. Submit a valid transaction to verify system stability
759+
t.Log("Testing system stability with valid transaction...")
760+
lastNonce := uint64(0)
761+
validTx := evm.GetRandomTransaction(t, "cece4f25ac74deb1468965160c7185e07dff413f23fcadb611b05ca37ab0a52e", "0x944fDcD1c868E3cC566C78023CcB38A32cDA836E", "1234", 22000, &lastNonce)
762+
763+
evm.SubmitTransaction(t, validTx)
764+
t.Logf("Submitted valid transaction: %s", validTx.Hash().Hex())
765+
766+
// 11. Wait for valid transaction to be included
767+
require.Eventually(t, func() bool {
768+
receipt, err := client.TransactionReceipt(ctx, validTx.Hash())
769+
return err == nil && receipt != nil && receipt.Status == 1
770+
}, 20*time.Second, 1*time.Second, "Valid transaction should be included after invalid ones were rejected")
771+
772+
t.Log("✅ Valid transaction included successfully - system stability confirmed")
773+
774+
// 12. Final verification
775+
validReceipt, err := client.TransactionReceipt(ctx, validTx.Hash())
776+
require.NoError(t, err, "Should get receipt for valid transaction")
777+
require.Equal(t, uint64(1), validReceipt.Status, "Valid transaction should be successful")
778+
779+
t.Logf("✅ Test PASSED: Invalid transaction rejection working correctly")
780+
t.Logf(" - %d invalid transactions properly rejected", len(invalidTxErrors))
781+
t.Logf(" - No invalid transactions included in any blocks")
782+
t.Logf(" - Valid transactions continue to work normally")
783+
t.Logf(" - System maintains stability after rejecting invalid transactions")
784+
t.Logf(" - Transaction validation mechanisms functioning properly")
785+
}

0 commit comments

Comments
 (0)