diff --git a/core/block_validator.go b/core/block_validator.go index 82460b804b57..1bd7cf7f4798 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -56,6 +56,10 @@ func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engin // header's transaction and uncle roots. The headers are assumed to be already // validated at this point. func (v *BlockValidator) ValidateBody(block *types.Block) error { + // check EIP-7934 RLP-encoded block size cap + if v.config.IsOsaka(block.Number()) && block.Size() > params.MaxBlockSize { + return ErrBlockOversized + } // Check whether the block's known, and if not, that it's linkable if v.bc.HasBlockAndFullState(block.Hash(), block.NumberU64()) { return ErrKnownBlock diff --git a/core/block_validator_test.go b/core/block_validator_test.go index d3f498826dc9..b53f60152059 100644 --- a/core/block_validator_test.go +++ b/core/block_validator_test.go @@ -17,6 +17,7 @@ package core import ( + "math/big" "testing" "time" @@ -25,6 +26,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/params" + "github.com/XinFinOrg/XDPoSChain/trie" ) // Tests that simple header verification works, for both good and bad blocks. @@ -76,3 +78,46 @@ func TestHeaderVerification(t *testing.T) { chain.InsertChain(blocks[i : i+1]) } } + +func TestValidateBodyBlockOversizedOsakaByBlockNumber(t *testing.T) { + testdb := rawdb.NewMemoryDatabase() + cfg := *params.TestChainConfig + cfg.OsakaBlock = big.NewInt(2) + gspec := &Genesis{Config: &cfg} + genesis := gspec.MustCommit(testdb) + + chain, err := NewBlockChain(testdb, nil, gspec, ethash.NewFaker(), vm.Config{}) + if err != nil { + t.Fatal(err) + } + defer chain.Stop() + + validator := NewBlockValidator(&cfg, chain, ethash.NewFaker()) + oversizedData := make([]byte, params.MaxBlockSize+1024) + tx := types.NewTx(&types.LegacyTx{ + Nonce: 0, + Gas: params.TxGas, + GasPrice: big.NewInt(1), + Data: oversizedData, + }) + + newOversizedBlock := func(number uint64, ts uint64) *types.Block { + header := &types.Header{ + ParentHash: genesis.Hash(), + Number: new(big.Int).SetUint64(number), + Time: ts, + GasLimit: 30_000_000, + } + return types.NewBlock(header, &types.Body{Transactions: []*types.Transaction{tx}}, nil, trie.NewStackTrie(nil)) + } + + preOsaka := newOversizedBlock(1, ^uint64(0)) + if err := validator.ValidateBody(preOsaka); err == ErrBlockOversized { + t.Fatalf("pre-Osaka block should not trigger ErrBlockOversized") + } + + postOsaka := newOversizedBlock(2, 0) + if err := validator.ValidateBody(postOsaka); err != ErrBlockOversized { + t.Fatalf("post-Osaka oversized block mismatch: have %v, want %v", err, ErrBlockOversized) + } +} diff --git a/core/error.go b/core/error.go index 227da000072d..87bde0c6bd79 100644 --- a/core/error.go +++ b/core/error.go @@ -31,6 +31,10 @@ var ( // ErrNoGenesis is returned when there is no Genesis Block. ErrNoGenesis = errors.New("genesis not found in chain") + + // ErrBlockOversized is returned if the size of the RLP-encoded block + // exceeds the cap established by EIP-7934 + ErrBlockOversized = errors.New("block RLP-encoded size exceeds maximum") ) // List of evm-call-message pre-checking errors. All state transtion messages will diff --git a/miner/worker.go b/miner/worker.go index a7dfca72f12e..2ed32a7332b2 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -62,6 +62,10 @@ const ( chainSideChanSize = 10 txMatchGasLimit = 40000000 + + // Block size is capped by the protocol at params.MaxBlockSize. During + // production keep a safety margin for auxiliary fields added to the block. + maxBlockSizeBufferZone = 1_000_000 ) var ( @@ -90,6 +94,7 @@ type Work struct { signer types.Signer state *state.StateDB // apply state changes here tcount int // tx count in cycle + size uint64 // size of the block we are building evm *vm.EVM parentState *state.StateDB @@ -108,6 +113,15 @@ type Work struct { createdAt time.Time } +// txFitsSize reports whether the transaction fits into the block size limit. +func (w *Work) txFitsSize(tx *types.Transaction) bool { + // this Osaka-specific cap is not enforced pre-fork + if w.config.IsOsaka(w.header.Number) { + return w.size+tx.Size() < params.MaxBlockSize-maxBlockSizeBufferZone + } + return true +} + type Result struct { Work *Work Block *types.Block @@ -712,6 +726,7 @@ func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error { config: w.chainConfig, signer: types.MakeSigner(w.chainConfig, header.Number), state: state, + size: uint64(header.Size()), parentState: state.Copy(), tradingState: XDCxState, lendingState: lendingState, @@ -1080,6 +1095,10 @@ func (w *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Addr var coalescedLogs []*types.Log // first priority for special Txs for _, tx := range specialTxs { + if !w.txFitsSize(tx) { + log.Debug("Skipping oversized transaction", "hash", tx.Hash(), "size", tx.Size()) + continue + } to := tx.To() if w.header.Number.Uint64() >= common.DenylistHFNumber { from := tx.From() @@ -1193,6 +1212,12 @@ func (w *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Addr break } tx := resolvedTx + // if inclusion of the transaction would put the block size over the + // maximum we allow, don't add any more txs to the payload. + if !w.txFitsSize(tx) { + log.Debug("Skipping oversized transaction", "hash", tx.Hash(), "size", tx.Size()) + break + } to := tx.To() if w.header.Number.Uint64() >= common.DenylistHFNumber { from := tx.From() @@ -1330,6 +1355,7 @@ func (w *Work) commitTransaction(balanceFee map[common.Address]*big.Int, tx *typ } w.txs = append(w.txs, tx) w.receipts = append(w.receipts, receipt) + w.size += tx.Size() return receipt.Logs, tokenFeeUsed, gas, nil } diff --git a/params/protocol_params.go b/params/protocol_params.go index 99f79d58fc26..7e72ae8c470a 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -170,6 +170,8 @@ const ( Bn256PairingPerPointGasIstanbul uint64 = 34000 // Per-point price for an elliptic curve pairing check HistoryServeWindow = 8191 // Number of blocks to serve historical block hashes for, EIP-2935. + + MaxBlockSize uint64 = 8_388_608 // maximum size of an RLP-encoded block ) var (