From ad43d0ea3fca425636d7478d8f6e1a215385058d Mon Sep 17 00:00:00 2001 From: Richord Date: Wed, 24 May 2023 06:23:57 -0700 Subject: [PATCH] feat(block_validator): check payload size during block validation (#322) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * added block size check in validate body * added payload method to block * Update core/types/block.go Co-authored-by: Péter Garamvölgyi * fixed test * fix * bump version * fix test --------- Co-authored-by: Péter Garamvölgyi Co-authored-by: Péter Garamvölgyi --- core/block_validator.go | 4 +++ core/blockchain_test.go | 61 +++++++++++++++++++++++++++++++++++++++++ core/error.go | 3 ++ core/types/block.go | 10 +++++++ params/version.go | 2 +- 5 files changed, 79 insertions(+), 1 deletion(-) diff --git a/core/block_validator.go b/core/block_validator.go index 393abb3f6e..4a0a59d758 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -57,6 +57,10 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { if !v.config.Scroll.IsValidTxCount(len(block.Transactions())) { return consensus.ErrInvalidTxCount } + // Check if block payload size is smaller than the max size + if !v.config.Scroll.IsValidBlockSize(block.PayloadSize()) { + return ErrInvalidBlockPayloadSize + } // Header validity is known at this point, check the uncles and transactions header := block.Header() if err := v.engine.VerifyUncles(v.bc, block); err != nil { diff --git a/core/blockchain_test.go b/core/blockchain_test.go index abf6332207..ab149d15aa 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -3239,6 +3239,67 @@ func TestTransactionCountLimit(t *testing.T) { } } +func TestBlockPayloadSizeLimit(t *testing.T) { + // Create config that allows at most 150 bytes per block payload + config := params.TestChainConfig + config.Scroll.MaxTxPayloadBytesPerBlock = new(int) + *config.Scroll.MaxTxPayloadBytesPerBlock = 150 + config.Scroll.MaxTxPerBlock = nil + + 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, ErrInvalidBlockPayloadSize) { + t.Fatalf("error mismatch: have: %v, want: %v", err, ErrInvalidBlockPayloadSize) + } +} + func TestEIP3651(t *testing.T) { var ( addraa = common.HexToAddress("0x000000000000000000000000000000000000aaaa") diff --git a/core/error.go b/core/error.go index 40ebd9773c..a29f56ebf3 100644 --- a/core/error.go +++ b/core/error.go @@ -26,6 +26,9 @@ var ( // ErrKnownBlock is returned when a block to import is already known locally. ErrKnownBlock = errors.New("block already known") + // ErrInvalidBlockPayloadSize is returned when a block to import has an oversized payload. + ErrInvalidBlockPayloadSize = errors.New("invalid block payload size") + // ErrBannedHash is returned if a block to import is on the banned list. ErrBannedHash = errors.New("banned hash") diff --git a/core/types/block.go b/core/types/block.go index 78f40bf0b4..1c6571f4ea 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -325,6 +325,16 @@ func (b *Block) Size() common.StorageSize { return common.StorageSize(c) } +// PayloadSize returns the sum of all transactions in a block. +func (b *Block) PayloadSize() common.StorageSize { + // add up all txs sizes + var totalSize common.StorageSize + for _, tx := range b.transactions { + totalSize += tx.Size() + } + return totalSize +} + // SanityCheck can be used to prevent that unbounded fields are // stuffed with junk data to add processing overhead func (b *Block) SanityCheck() error { diff --git a/params/version.go b/params/version.go index 9c9e0b65cd..2d6b309590 100644 --- a/params/version.go +++ b/params/version.go @@ -24,7 +24,7 @@ import ( const ( VersionMajor = 4 // Major version component of the current release VersionMinor = 0 // Minor version component of the current release - VersionPatch = 0 // Patch version component of the current release + VersionPatch = 1 // Patch version component of the current release VersionMeta = "sepolia" // Version metadata to append to the version string )