mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-10 22:13:47 +00:00
feat: add block transaction count limit (#216)
* feat: add block transaction count limit * add tests * nit
This commit is contained in:
parent
e3e0078d1b
commit
30f4f57f07
5 changed files with 83 additions and 3 deletions
|
|
@ -34,4 +34,7 @@ var (
|
||||||
// ErrInvalidNumber is returned if a block's number doesn't equal its parent's
|
// ErrInvalidNumber is returned if a block's number doesn't equal its parent's
|
||||||
// plus one.
|
// plus one.
|
||||||
ErrInvalidNumber = errors.New("invalid block number")
|
ErrInvalidNumber = errors.New("invalid block number")
|
||||||
|
|
||||||
|
// ErrInvalidTxCount is returned if a block contains too many transactions.
|
||||||
|
ErrInvalidTxCount = errors.New("invalid transaction count")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,9 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
|
||||||
if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) {
|
if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) {
|
||||||
return ErrKnownBlock
|
return ErrKnownBlock
|
||||||
}
|
}
|
||||||
|
if !v.config.IsValidTxCount(len(block.Transactions())) {
|
||||||
|
return consensus.ErrInvalidTxCount
|
||||||
|
}
|
||||||
// Header validity is known at this point, check the uncles and transactions
|
// Header validity is known at this point, check the uncles and transactions
|
||||||
header := block.Header()
|
header := block.Header()
|
||||||
if err := v.engine.VerifyUncles(v.bc, block); err != nil {
|
if err := v.engine.VerifyUncles(v.bc, block); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -3177,3 +3177,64 @@ func TestFeeVault(t *testing.T) {
|
||||||
t.Fatalf("fee vault balance incorrect: expected %d, got %d", expected, actual)
|
t.Fatalf("fee vault balance incorrect: expected %d, got %d", expected, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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, config, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -818,6 +818,11 @@ func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coin
|
||||||
}
|
}
|
||||||
return atomic.LoadInt32(interrupt) == commitInterruptNewHead
|
return atomic.LoadInt32(interrupt) == commitInterruptNewHead
|
||||||
}
|
}
|
||||||
|
// If we have collected enough transactions then we're done
|
||||||
|
if !w.chainConfig.IsValidTxCount(w.current.tcount + 1) {
|
||||||
|
log.Trace("Transaction count limit reached", "have", w.current.tcount, "want", w.chainConfig.MaxTxPerBlock)
|
||||||
|
break
|
||||||
|
}
|
||||||
// If we don't have enough gas for any further transactions then we're done
|
// If we don't have enough gas for any further transactions then we're done
|
||||||
if w.current.gasPool.Gas() < params.TxGas {
|
if w.current.gasPool.Gas() < params.TxGas {
|
||||||
log.Trace("Not enough gas for further transactions", "have", w.current.gasPool, "want", params.TxGas)
|
log.Trace("Not enough gas for further transactions", "have", w.current.gasPool, "want", params.TxGas)
|
||||||
|
|
|
||||||
|
|
@ -258,16 +258,16 @@ var (
|
||||||
//
|
//
|
||||||
// This configuration is intentionally not using keyed fields to force anyone
|
// This configuration is intentionally not using keyed fields to force anyone
|
||||||
// adding flags to the config to also have to set these fields.
|
// adding flags to the config to also have to set these fields.
|
||||||
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, false, nil, true, true}
|
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, false, nil, true, true, nil}
|
||||||
|
|
||||||
// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
|
// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
|
||||||
// and accepted by the Ethereum core developers into the Clique consensus.
|
// and accepted by the Ethereum core developers into the Clique consensus.
|
||||||
//
|
//
|
||||||
// This configuration is intentionally not using keyed fields to force anyone
|
// This configuration is intentionally not using keyed fields to force anyone
|
||||||
// adding flags to the config to also have to set these fields.
|
// adding flags to the config to also have to set these fields.
|
||||||
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, false, nil, true, true}
|
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, false, nil, true, true, nil}
|
||||||
|
|
||||||
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, false, &common.Address{123}, true, true}
|
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, false, &common.Address{123}, true, true, nil}
|
||||||
TestRules = TestChainConfig.Rules(new(big.Int))
|
TestRules = TestChainConfig.Rules(new(big.Int))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -367,6 +367,9 @@ type ChainConfig struct {
|
||||||
|
|
||||||
// Scroll genesis extension: enable EIP-1559 in tx pool, EnableEIP2718 should be true too.
|
// Scroll genesis extension: enable EIP-1559 in tx pool, EnableEIP2718 should be true too.
|
||||||
EnableEIP1559 bool `json:"enableEIP1559,omitempty"`
|
EnableEIP1559 bool `json:"enableEIP1559,omitempty"`
|
||||||
|
|
||||||
|
// Scroll genesis extension: Maximum number of transactions per block [optional]
|
||||||
|
MaxTxPerBlock *int `json:"maxTxPerBlock,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// EthashConfig is the consensus engine configs for proof-of-work based sealing.
|
// EthashConfig is the consensus engine configs for proof-of-work based sealing.
|
||||||
|
|
@ -494,6 +497,11 @@ func (c *ChainConfig) IsTerminalPoWBlock(parentTotalDiff *big.Int, totalDiff *bi
|
||||||
return parentTotalDiff.Cmp(c.TerminalTotalDifficulty) < 0 && totalDiff.Cmp(c.TerminalTotalDifficulty) >= 0
|
return parentTotalDiff.Cmp(c.TerminalTotalDifficulty) < 0 && totalDiff.Cmp(c.TerminalTotalDifficulty) >= 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
// CheckCompatible checks whether scheduled fork transitions have been imported
|
// CheckCompatible checks whether scheduled fork transitions have been imported
|
||||||
// with a mismatching chain configuration.
|
// with a mismatching chain configuration.
|
||||||
func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64) *ConfigCompatError {
|
func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64) *ConfigCompatError {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue