Skip to content

Commit

Permalink
feat: add block transaction count limit
Browse files Browse the repository at this point in the history
  • Loading branch information
chokobole committed Mar 19, 2023
1 parent df79df9 commit 63ec97f
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 0 deletions.
3 changes: 3 additions & 0 deletions consensus/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,7 @@ var (
// ErrInvalidTerminalBlock is returned if a block is invalid wrt. the terminal
// total difficulty.
ErrInvalidTerminalBlock = errors.New("invalid terminal block")

// ErrInvalidTxCount is returned if a block contains too many transactions.
ErrInvalidTxCount = errors.New("invalid transaction count")
)
5 changes: 5 additions & 0 deletions core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) {
return ErrKnownBlock
}
// [Scroll: START]
if !v.config.IsValidTxCount(len(block.Transactions())) {
return consensus.ErrInvalidTxCount
}
// [Scroll: END]

// Header validity is known at this point. Here we verify that uncles, transactions
// and withdrawals given in the block body match the header.
Expand Down
61 changes: 61 additions & 0 deletions core/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4503,4 +4503,65 @@ func TestPoseidonCodeHash(t *testing.T) {
assert.Equal(t, common.HexToHash("0x2fa5836118b70a257defd2e54064ab63cc9bb2e91823eaacbdef32370050b5b2"), codeHash2, "code hash mismatch")
}

// TestTransactionCountLimit tests that the chain reject blocks with too many transactions.
func TestTransactionCountLimit(t *testing.T) {
// Create config that allows at most 1 transaction per block
config := params.TestChainConfig
config.MaxTxPerBlock = new(int)
*config.MaxTxPerBlock = 1

var (
engine = ethash.NewFaker()
db = rawdb.NewMemoryDatabase()
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
address = crypto.PubkeyToAddress(key.PublicKey)
funds = big.NewInt(1000000000000000)
gspec = &Genesis{Config: config, Alloc: GenesisAlloc{address: {Balance: funds}}}
genesis = gspec.MustCommit(db)
)

addTx := func(b *BlockGen) {
tx := types.NewTransaction(b.TxNonce(address), address, big.NewInt(0), 50000, b.header.BaseFee, nil)
signed, _ := types.SignTx(tx, types.HomesteadSigner{}, key)
b.AddTx(signed)
}

// Initialize blockchain
blockchain, err := NewBlockChain(db, nil, gspec, nil, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("failed to create new chain manager: %v", err)
}
defer blockchain.Stop()

// Insert empty block
block1, _ := GenerateChain(config, genesis, ethash.NewFaker(), db, 1, func(i int, b *BlockGen) {
// empty
})

if _, err := blockchain.InsertChain(block1); err != nil {
t.Fatalf("failed to insert chain: %v", err)
}

// Insert block with 1 transaction
block2, _ := GenerateChain(config, genesis, ethash.NewFaker(), db, 1, func(i int, b *BlockGen) {
addTx(b)
})

if _, err := blockchain.InsertChain(block2); err != nil {
t.Fatalf("failed to insert chain: %v", err)
}

// Insert block with 2 transactions
block3, _ := GenerateChain(config, genesis, ethash.NewFaker(), db, 1, func(i int, b *BlockGen) {
addTx(b)
addTx(b)
})

_, err = blockchain.InsertChain(block3)

if !errors.Is(err, consensus.ErrInvalidTxCount) {
t.Fatalf("error mismatch: have: %v, want: %v", err, consensus.ErrInvalidTxCount)
}
}

// [Scroll: END]
7 changes: 7 additions & 0 deletions miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,13 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP
return signalToErr(signal)
}
}
// [Scroll: START]
// If we have collected enough transactions then we're done
if !w.chainConfig.IsValidTxCount(env.tcount + 1) {
log.Trace("Transaction count limit reached", "have", env.tcount, "want", w.chainConfig.MaxTxPerBlock)
break
}
// [Scroll: END]
// If we don't have enough gas for any further transactions then we're done.
if env.gasPool.Gas() < params.TxGas {
log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas)
Expand Down
11 changes: 11 additions & 0 deletions params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,9 @@ type ChainConfig struct {
// [Scroll: START]
// Use zktrie
Zktrie bool `json:"zktrie,omitempty"`

// Maximum number of transactions per block [optional]
MaxTxPerBlock *int `json:"maxTxPerBlock,omitempty"`
// [Scroll: END]
}

Expand Down Expand Up @@ -688,6 +691,14 @@ func (c *ChainConfig) IsKanvas() bool {
return c.Kanvas != nil
}

// [Scroll: START]
// IsValidTxCount returns whether the given block's transaction count is below the limit.
func (c *ChainConfig) IsValidTxCount(count int) bool {
return c.MaxTxPerBlock == nil || count <= *c.MaxTxPerBlock
}

// [Scroll: END]

// CheckCompatible checks whether scheduled fork transitions have been imported
// with a mismatching chain configuration.
func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64, time uint64) *ConfigCompatError {
Expand Down

0 comments on commit 63ec97f

Please sign in to comment.