Skip to content

Commit

Permalink
worker: add commit interrupt tests for non-validator
Browse files Browse the repository at this point in the history
  • Loading branch information
manav2401 committed Feb 10, 2025
1 parent 466ff66 commit 95037d6
Showing 1 changed file with 105 additions and 3 deletions.
108 changes: 105 additions & 3 deletions miner/worker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import (
"github.com/ethereum/go-ethereum/triedb"
"github.com/golang/mock/gomock"
"github.com/holiman/uint256"
"github.com/stretchr/testify/require"
"gotest.tools/assert"
)

Expand Down Expand Up @@ -707,9 +708,10 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
}
}

// nolint : paralleltest
// TestCommitInterruptExperimentBor tests the commit interrupt experiment for bor consensus by inducing an artificial delay at transaction level.
func TestCommitInterruptExperimentBor(t *testing.T) {
// nolint: paralleltest
// TestCommitInterruptExperimentBor_NormalFlow tests the commit interrupt experiment for bor consensus by inducing
// an artificial delay at transaction level. It runs the normal mining flow triggered via new head.
func TestCommitInterruptExperimentBor_NormalFlow(t *testing.T) {
// with 1 sec block time and 200 millisec tx delay we should get 5 txs per block
testCommitInterruptExperimentBor(t, 200, 5, 0)

Expand Down Expand Up @@ -834,6 +836,106 @@ func testCommitInterruptExperimentBor(t *testing.T, delay uint, txCount int, opc
assert.Check(t, 0 < w.chain.GetBlockByNumber(currentBlockNumber-1).Transactions().Len())
}

// TestCommitInterruptExperimentBor_NewTxFlow tests the commit interrupt experiment for bor consensus by inducing
// an artificial delay at transaction level. It runs the mining flow triggered via new transactions channel. The tests
// are a bit unconventional compared to normal flow as the situations are only possible in non-validator mode.
func TestCommitInterruptExperimentBor_NewTxFlow(t *testing.T) {
var (
engine consensus.Engine
chainConfig *params.ChainConfig
db = rawdb.NewMemoryDatabase()
ctrl *gomock.Controller
txs = make([]*types.Transaction, 0, 1)
)

chainConfig = params.BorUnittestChainConfig

log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))

engine, ctrl = getFakeBorFromConfig(t, chainConfig)

w, b, _ := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), true, uint(0), uint(0))
defer func() {
w.close()
engine.Close()
db.Close()
ctrl.Finish()
}()

// Create random transactions (contract interaction)
tx1, addr := b.newStorageCreateContractTx()
tx2 := b.newStorageContractCallTx(addr, 1)
tx3 := b.newStorageContractCallTx(addr, 2)
txs = append(txs, tx1)

// Create a chain head subscription for tests
chainHeadCh := make(chan core.ChainHeadEvent, 10)
w.chain.SubscribeChainHeadEvent(chainHeadCh)

// Start mining!
w.start()
go func() {
for {

Check failure on line 878 in miner/worker_test.go

View workflow job for this annotation

GitHub Actions / lint (ubuntu-20.04)

S1000: should use for range instead of for { select {} } (gosimple)
select {
case head := <-chainHeadCh:
// We skip the initial 2 blocks as the mining timings are a bit skewed up
if head.Block.NumberU64() == 2 {
// Stop the miner so that worker assumes it's a sentry and not a validator
w.stop()

// Add the first transaction to be mined normally via `txsCh`
b.TxPool().Add(txs, false, false)

// Set it to syncing mode so that it doesn't mine via the `commitWork` flow
w.syncing.Store(true)

// Wait until the mining window (2s) is almost about to reach leaving
// a very small time (~10ms) to try to commit transaction before timing out.
delay := time.Until(time.Unix(int64(w.current.header.Time), 0))
delay -= 10 * time.Millisecond

// Case 1: This transaction should not be included due to commit interrupt
// at opcode level. It will start the EVM execution but will end in between.
select {

Check failure on line 899 in miner/worker_test.go

View workflow job for this annotation

GitHub Actions / lint (ubuntu-20.04)

S1000: should use a simple channel send/receive instead of `select` with a single case (gosimple)
case <-time.After(delay):
// Set an artificial delay at opcode level
w.setInterruptCtx(vm.InterruptCtxOpcodeDelayKey, uint(500))

// Send the second transaction
txs = make([]*types.Transaction, 0, 1)
txs = append(txs, tx2)
b.TxPool().Add(txs, false, false)
}

// Reset the delay again. By this time, we're sure that it has timed out.
delay = time.Until(time.Unix(int64(w.current.header.Time), 0))

// Case 2: This transaction should not be included because the miner loop
// won't accept any transactions post the deadline (i.e. header.Timestamp).
select {

Check failure on line 915 in miner/worker_test.go

View workflow job for this annotation

GitHub Actions / lint (ubuntu-20.04)

S1000: should use a simple channel send/receive instead of `select` with a single case (gosimple)
case <-time.After(delay):
// Reset the artificial opcode delay just to be sure of the exclusion of tx
w.setInterruptCtx(vm.InterruptCtxOpcodeDelayKey, uint(0))

// Send the third transaction
txs = make([]*types.Transaction, 0, 1)
txs = append(txs, tx3)
b.TxPool().Add(txs, false, false)
}
}
}
}
}()

// Wait for enough time to mine 3 blocks
time.Sleep(6 * time.Second)

// Ensure that the last block was 3 and only 1 transactions out of 3 were included
assert.Equal(t, w.current.header.Number.Uint64(), uint64(3))
require.Equal(t, w.current.tcount, 1)
require.Equal(t, len(w.current.txs), 1)
}

func BenchmarkBorMining(b *testing.B) {
chainConfig := params.BorUnittestChainConfig

Expand Down

0 comments on commit 95037d6

Please sign in to comment.