Skip to content

Commit f673a93

Browse files
committed
Enforce MAX_BLOBS_PER_BLOCK consensus rules
1 parent a1385c9 commit f673a93

File tree

2 files changed

+133
-0
lines changed

2 files changed

+133
-0
lines changed

miner/worker.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ const (
8080
var (
8181
errBlockInterruptedByNewHead = errors.New("new head arrived while building block")
8282
errBlockInterruptedByRecommit = errors.New("recommit interrupt while building block")
83+
errMaxBlobsReached = errors.New("reached max number of blobs per block")
8384
)
8485

8586
// environment is the worker's current environment and holds all
@@ -98,6 +99,7 @@ type environment struct {
9899
txs []*types.Transaction
99100
receipts []*types.Receipt
100101
uncles map[common.Hash]*types.Header
102+
numBlobs int
101103
}
102104

103105
// copy creates a deep copy of environment.
@@ -825,13 +827,19 @@ func (w *worker) updateSnapshot(env *environment) {
825827
func (w *worker) commitTransaction(env *environment, tx *types.Transaction) ([]*types.Log, error) {
826828
snap := env.state.Snapshot()
827829

830+
txBlobCount := len(tx.DataHashes())
831+
if env.numBlobs+txBlobCount > params.MaxBlobsPerBlock {
832+
return nil, errMaxBlobsReached
833+
}
834+
828835
receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig())
829836
if err != nil {
830837
env.state.RevertToSnapshot(snap)
831838
return nil, err
832839
}
833840
env.txs = append(env.txs, tx)
834841
env.receipts = append(env.receipts, receipt)
842+
env.numBlobs += txBlobCount
835843

836844
return receipt.Logs, nil
837845
}
@@ -919,6 +927,11 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP
919927
log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type())
920928
txs.Pop()
921929

930+
case errors.Is(err, errMaxBlobsReached):
931+
// Shift, as the next tx from the account may not contain blobs
932+
log.Trace("Skipping blob transaction. Reached max number of blobs in current context", "sender", from, "numBlobs", len(tx.DataHashes()))
933+
txs.Shift()
934+
922935
default:
923936
// Strange error, discard the transaction and get the next in line (note, the
924937
// nonce-too-high clause will prevent us from executing in vain).

miner/worker_test.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ import (
3838
"github.com/ethereum/go-ethereum/ethdb"
3939
"github.com/ethereum/go-ethereum/event"
4040
"github.com/ethereum/go-ethereum/params"
41+
"github.com/holiman/uint256"
42+
"github.com/protolambda/ztyp/view"
4143
)
4244

4345
const (
@@ -198,6 +200,42 @@ func (b *testWorkerBackend) newRandomTx(creation bool) *types.Transaction {
198200
return tx
199201
}
200202

203+
func (b *testWorkerBackend) newRandomBlobTx(chainID *big.Int, nonce uint64) *types.Transaction {
204+
var blobs types.Blobs
205+
blobs = append(blobs, types.Blob{})
206+
207+
commitments, versionedHashes, aggregatedProof, err := blobs.ComputeCommitmentsAndAggregatedProof()
208+
if err != nil {
209+
panic(err)
210+
}
211+
212+
gasPrice := big.NewInt(10 * params.InitialBaseFee).Uint64()
213+
txData := &types.SignedBlobTx{
214+
Message: types.BlobTxMessage{
215+
ChainID: view.Uint256View(*uint256.NewInt(chainID.Uint64())),
216+
Nonce: view.Uint64View(nonce),
217+
Gas: view.Uint64View(testGas),
218+
GasFeeCap: view.Uint256View(*uint256.NewInt(gasPrice)),
219+
GasTipCap: view.Uint256View(*uint256.NewInt(gasPrice)),
220+
Value: view.Uint256View(*uint256.NewInt(1000)),
221+
To: types.AddressOptionalSSZ{Address: (*types.AddressSSZ)(&testUserAddress)},
222+
BlobVersionedHashes: versionedHashes,
223+
},
224+
}
225+
wrapData := &types.BlobTxWrapData{
226+
BlobKzgs: commitments,
227+
Blobs: blobs,
228+
KzgAggregatedProof: aggregatedProof,
229+
}
230+
tx := types.NewTx(txData, types.WithTxWrapData(wrapData))
231+
signer := types.NewDankSigner(chainID)
232+
tx, err = types.SignTx(tx, signer, testBankKey)
233+
if err != nil {
234+
panic(err)
235+
}
236+
return tx
237+
}
238+
201239
func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, blocks int) (*worker, *testWorkerBackend) {
202240
backend := newTestWorkerBackend(t, chainConfig, engine, db, blocks)
203241
backend.txPool.AddLocals(pendingTxs)
@@ -670,3 +708,85 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
670708
}
671709
}
672710
}
711+
712+
func testGenerateBlockWithBlobsAndImport(t *testing.T, isClique bool) {
713+
var (
714+
engine consensus.Engine
715+
chainConfig *params.ChainConfig
716+
db = rawdb.NewMemoryDatabase()
717+
)
718+
if isClique {
719+
chainConfig = params.AllCliqueProtocolChanges
720+
chainConfig.Clique = &params.CliqueConfig{Period: 1, Epoch: 30000}
721+
engine = clique.New(chainConfig.Clique, db)
722+
} else {
723+
chainConfig = params.AllEthashProtocolChanges
724+
engine = ethash.NewFaker()
725+
}
726+
727+
chainConfig.LondonBlock = big.NewInt(0)
728+
chainConfig.ShardingForkBlock = big.NewInt(0)
729+
w, b := newTestWorker(t, chainConfig, engine, db, 0)
730+
defer w.close()
731+
732+
// This test chain imports the mined blocks.
733+
db2 := rawdb.NewMemoryDatabase()
734+
b.genesis.MustCommit(db2)
735+
chain, _ := core.NewBlockChain(db2, nil, b.chain.Config(), engine, vm.Config{}, nil, nil)
736+
defer chain.Stop()
737+
738+
// Ignore empty commit here for less noise.
739+
w.skipSealHook = func(task *task) bool {
740+
return len(task.receipts) == 0
741+
}
742+
743+
// Wait for mined blocks.
744+
sub := w.mux.Subscribe(core.NewMinedBlockEvent{})
745+
defer sub.Unsubscribe()
746+
747+
// Start mining!
748+
w.start()
749+
750+
nonce := b.txPool.Nonce(testBankAddress)
751+
752+
var txs []*types.Transaction
753+
for i := 0; i < params.MaxBlobsPerBlock+5; i++ {
754+
txs = append(txs, b.newRandomBlobTx(chainConfig.ChainID, nonce))
755+
nonce += 1
756+
}
757+
// Batch add blob txs to guarantee single block inclusion
758+
errs := b.txPool.AddLocals(txs)
759+
for i := range errs {
760+
if errs[i] != nil {
761+
panic(errs[i])
762+
}
763+
}
764+
765+
select {
766+
case ev := <-sub.Chan():
767+
block := ev.Data.(core.NewMinedBlockEvent).Block
768+
if _, err := chain.InsertChain([]*types.Block{block}); err != nil {
769+
t.Fatalf("failed to insert new mined block %d: %v", block.NumberU64(), err)
770+
}
771+
txs := block.Transactions()
772+
var numBlobs int
773+
for i := range txs {
774+
numBlobs += len(txs[i].DataHashes())
775+
}
776+
// Assert that there are no more than params.MaxBlobsPerBlock blobs in the block
777+
if numBlobs != params.MaxBlobsPerBlock {
778+
t.Fatalf("unexpected number of blobs in block: %d", numBlobs)
779+
}
780+
781+
case <-time.After(3 * time.Second): // Worker needs 1s to include new changes.
782+
t.Fatalf("timeout")
783+
}
784+
}
785+
786+
func TestGenerateBlockWithBlobsAndImportEthash(t *testing.T) {
787+
testGenerateBlockWithBlobsAndImport(t, false)
788+
}
789+
790+
func TestGenerateBlockWithBlobsAndImportClique(t *testing.T) {
791+
testGenerateBlockWithBlobsAndImport(t, true)
792+
}

0 commit comments

Comments
 (0)