Skip to content

Commit

Permalink
Merge pull request #96 from ethereum-optimism/aj/backport-pending-blo…
Browse files Browse the repository at this point in the history
…ck-l1overhead

Aj/backport pending block l1overhead
  • Loading branch information
protolambda authored May 26, 2023
2 parents 24ae687 + 5a4d510 commit db68332
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 4 deletions.
1 change: 1 addition & 0 deletions cmd/geth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ var (
utils.RollupHistoricalRPCFlag,
utils.RollupHistoricalRPCTimeoutFlag,
utils.RollupDisableTxPoolGossipFlag,
utils.RollupComputePendingBlock,
configFileFlag,
}, utils.NetworkFlags, utils.DatabasePathFlags)

Expand Down
8 changes: 8 additions & 0 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,11 @@ var (
Usage: "Disable transaction pool gossip.",
Category: flags.RollupCategory,
}
RollupComputePendingBlock = &cli.BoolFlag{
Name: "rollup.computependingblock",
Usage: "By default the pending block equals the latest block to save resources and not leak txs from the tx-pool, this flag enables computing of the pending block from the tx-pool instead.",
Category: flags.RollupCategory,
}

// Metrics flags
MetricsEnabledFlag = &cli.BoolFlag{
Expand Down Expand Up @@ -1696,6 +1701,9 @@ func setMiner(ctx *cli.Context, cfg *miner.Config) {
if ctx.IsSet(MinerNewPayloadTimeout.Name) {
cfg.NewPayloadTimeout = ctx.Duration(MinerNewPayloadTimeout.Name)
}
if ctx.IsSet(RollupComputePendingBlock.Name) {
cfg.RollupComputePendingBlock = ctx.Bool(RollupComputePendingBlock.Name)
}
}

func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) {
Expand Down
3 changes: 2 additions & 1 deletion core/types/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,8 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu
if !txs[i].IsDepositTx() {
gas := txs[i].RollupDataGas().DataGas(time, config)
rs[i].L1GasPrice = l1Basefee
rs[i].L1GasUsed = new(big.Int).SetUint64(gas)
// GasUsed reported in receipt should include the overhead
rs[i].L1GasUsed = new(big.Int).Add(new(big.Int).SetUint64(gas), overhead)
rs[i].L1Fee = L1Cost(gas, l1Basefee, overhead, scalar)
rs[i].FeeScalar = feeScalar
}
Expand Down
109 changes: 109 additions & 0 deletions core/types/receipt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,115 @@ func TestDeriveFields(t *testing.T) {
}
}

func TestDeriveOptimismTxReceipt(t *testing.T) {
to4 := common.HexToAddress("0x4")
// Create a few transactions to have receipts for
txs := Transactions{
NewTx(&DepositTx{
To: nil, // contract creation
Value: big.NewInt(6),
Gas: 50,
// System config with L1Scalar=2_000_000 (becomes 2 after division), L1Overhead=2500, L1BaseFee=5000
Data: common.Hex2Bytes("015d8eb900000000000000000000000000000000000000000000000026b39534042076f70000000000000000000000000000000000000000000000007e33b7c4995967580000000000000000000000000000000000000000000000000000000000001388547dea8ff339566349ed0ef6384876655d1b9b955e36ac165c6b8ab69b9af5cd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123400000000000000000000000000000000000000000000000000000000000009c400000000000000000000000000000000000000000000000000000000001e8480"),
}),
NewTx(&DynamicFeeTx{
To: &to4,
Nonce: 4,
Value: big.NewInt(4),
Gas: 4,
GasTipCap: big.NewInt(44),
GasFeeCap: big.NewInt(1045),
Data: []byte{0, 1, 255, 0},
}),
}
depNonce := uint64(7)
blockNumber := big.NewInt(1)
blockHash := common.BytesToHash([]byte{0x03, 0x14})

// Create the corresponding receipts
receipts := Receipts{
&Receipt{
Type: DepositTxType,
PostState: common.Hash{5}.Bytes(),
CumulativeGasUsed: 50 + 15,
Logs: []*Log{
{
Address: common.BytesToAddress([]byte{0x33}),
// derived fields:
BlockNumber: blockNumber.Uint64(),
TxHash: txs[0].Hash(),
TxIndex: 0,
BlockHash: blockHash,
Index: 0,
},
{
Address: common.BytesToAddress([]byte{0x03, 0x33}),
// derived fields:
BlockNumber: blockNumber.Uint64(),
TxHash: txs[0].Hash(),
TxIndex: 0,
BlockHash: blockHash,
Index: 1,
},
},
TxHash: txs[0].Hash(),
ContractAddress: common.HexToAddress("0x3bb898b4bbe24f68a4e9be46cfe72d1787fd74f4"),
GasUsed: 65,
EffectiveGasPrice: big.NewInt(0),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 0,
DepositNonce: &depNonce,
},
&Receipt{
Type: DynamicFeeTxType,
PostState: common.Hash{4}.Bytes(),
CumulativeGasUsed: 10,
Logs: []*Log{},
// derived fields:
TxHash: txs[1].Hash(),
GasUsed: 18446744073709551561,
EffectiveGasPrice: big.NewInt(1044),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 1,
L1GasPrice: big.NewInt(5000),
L1GasUsed: big.NewInt(3976),
L1Fee: big.NewInt(39760000),
FeeScalar: big.NewFloat(2),
},
}

// Re-derive receipts.
basefee := big.NewInt(1000)
derivedReceipts := clearComputedFieldsOnReceipts(receipts)
err := Receipts(derivedReceipts).DeriveFields(params.OptimismTestConfig, blockHash, blockNumber.Uint64(), 0, basefee, txs)
if err != nil {
t.Fatalf("DeriveFields(...) = %v, want <nil>", err)
}

// Check diff of receipts against derivedReceipts.
r1, err := json.MarshalIndent(receipts, "", " ")
if err != nil {
t.Fatal("error marshaling input receipts:", err)
}
r2, err := json.MarshalIndent(derivedReceipts, "", " ")
if err != nil {
t.Fatal("error marshaling derived receipts:", err)
}
d := diff.Diff(string(r1), string(r2))
if d != "" {
t.Fatal("receipts differ:", d)
}

// Check that we preserved the invariant: l1Fee = l1GasPrice * l1GasUsed * l1FeeScalar
// but with more difficult int math...
l2Rcpt := derivedReceipts[1]
l1GasCost := new(big.Int).Mul(l2Rcpt.L1GasPrice, l2Rcpt.L1GasUsed)
l1Fee := new(big.Float).Mul(new(big.Float).SetInt(l1GasCost), l2Rcpt.FeeScalar)
require.Equal(t, new(big.Float).SetInt(l2Rcpt.L1Fee), l1Fee)
}

// TestTypedReceiptEncodingDecoding reproduces a flaw that existed in the receipt
// rlp decoder, which failed due to a shadowing error.
func TestTypedReceiptEncodingDecoding(t *testing.T) {
Expand Down
6 changes: 6 additions & 0 deletions eth/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,9 @@ func (api *DebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error) {
// both the pending block as well as the pending state from
// the miner and operate on those
_, stateDb := api.eth.miner.Pending()
if stateDb == nil {
return state.Dump{}, errors.New("no pending state")
}
return stateDb.RawDump(opts), nil
}
var header *types.Header
Expand Down Expand Up @@ -350,6 +353,9 @@ func (api *DebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start hex
// both the pending block as well as the pending state from
// the miner and operate on those
_, stateDb = api.eth.miner.Pending()
if stateDb == nil {
return state.IteratorDump{}, errors.New("no pending state")
}
} else {
var header *types.Header
if number == rpc.LatestBlockNumber {
Expand Down
6 changes: 5 additions & 1 deletion eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,11 @@ func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.B
// Pending state is only known by the miner
if number == rpc.PendingBlockNumber {
block, state := b.eth.miner.Pending()
return state, block.Header(), nil
if block != nil {
return state, block.Header(), nil
} else {
number = rpc.LatestBlockNumber
}
}
// Otherwise resolve the block number and return its state
header, err := b.HeaderByNumber(ctx, number)
Expand Down
4 changes: 2 additions & 2 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,7 @@ func (s *BlockChainAPI) GetHeaderByNumber(ctx context.Context, number rpc.BlockN
header, err := s.b.HeaderByNumber(ctx, number)
if header != nil && err == nil {
response := s.rpcMarshalHeader(ctx, header)
if number == rpc.PendingBlockNumber {
if number == rpc.PendingBlockNumber && s.b.ChainConfig().Optimism == nil { // don't remove info if optimism
// Pending header need to nil out a few fields
for _, field := range []string{"hash", "nonce", "miner"} {
response[field] = nil
Expand Down Expand Up @@ -803,7 +803,7 @@ func (s *BlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNu
block, err := s.b.BlockByNumber(ctx, number)
if block != nil && err == nil {
response, err := s.rpcMarshalBlock(ctx, block, true, fullTx)
if err == nil && number == rpc.PendingBlockNumber {
if err == nil && number == rpc.PendingBlockNumber && s.b.ChainConfig().Optimism == nil { // don't remove info if optimism
// Pending blocks need to nil out a few fields
for _, field := range []string{"hash", "nonce", "miner"} {
response[field] = nil
Expand Down
2 changes: 2 additions & 0 deletions miner/miner.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ type Config struct {
Noverify bool // Disable remote mining solution verification(only useful in ethash).

NewPayloadTimeout time.Duration // The maximum time allowance for creating a new payload

RollupComputePendingBlock bool // Compute the pending block from tx-pool, instead of copying the latest-block
}

// DefaultConfig contains default settings for miner.
Expand Down
28 changes: 28 additions & 0 deletions miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,9 @@ func (w *worker) enablePreseal() {

// pending returns the pending state and corresponding block.
func (w *worker) pending() (*types.Block, *state.StateDB) {
if w.chainConfig.Optimism != nil && !w.config.RollupComputePendingBlock {
return nil, nil // when not computing the pending block, there is never a pending state
}
// return a snapshot to avoid contention on currentMu mutex
w.snapshotMu.RLock()
defer w.snapshotMu.RUnlock()
Expand All @@ -395,6 +398,12 @@ func (w *worker) pending() (*types.Block, *state.StateDB) {

// pendingBlock returns pending block.
func (w *worker) pendingBlock() *types.Block {
if w.chainConfig.Optimism != nil && !w.config.RollupComputePendingBlock {
// For compatibility when not computing a pending block, we serve the latest block as "pending"
headHeader := w.eth.BlockChain().CurrentHeader()
headBlock := w.eth.BlockChain().GetBlock(headHeader.Hash(), headHeader.Number.Uint64())
return headBlock
}
// return a snapshot to avoid contention on currentMu mutex
w.snapshotMu.RLock()
defer w.snapshotMu.RUnlock()
Expand All @@ -403,6 +412,9 @@ func (w *worker) pendingBlock() *types.Block {

// pendingBlockAndReceipts returns pending block and corresponding receipts.
func (w *worker) pendingBlockAndReceipts() (*types.Block, types.Receipts) {
if w.chainConfig.Optimism != nil && !w.config.RollupComputePendingBlock {
return nil, nil // when not computing the pending block, there are no pending receipts, and thus no pending logs
}
// return a snapshot to avoid contention on currentMu mutex
w.snapshotMu.RLock()
defer w.snapshotMu.RUnlock()
Expand Down Expand Up @@ -458,6 +470,19 @@ func recalcRecommit(minRecommit, prev time.Duration, target float64, inc bool) t
// newWorkLoop is a standalone goroutine to submit new sealing work upon received events.
func (w *worker) newWorkLoop(recommit time.Duration) {
defer w.wg.Done()
if w.chainConfig.Optimism != nil && !w.config.RollupComputePendingBlock {
for { // do not update the pending-block, instead drain work without doing it, to keep producers from blocking.
select {
case <-w.startCh:
case <-w.chainHeadCh:
case <-w.resubmitIntervalCh:
case <-w.resubmitAdjustCh:
case <-w.exitCh:
return
}
}
}

var (
interrupt *int32
minRecommit = recommit // minimal resubmit interval specified by user.
Expand Down Expand Up @@ -620,6 +645,9 @@ func (w *worker) mainLoop() {
}

case ev := <-w.txsCh:
if w.chainConfig.Optimism != nil && !w.config.RollupComputePendingBlock {
continue // don't update the pending-block snapshot if we are not computing the pending block
}
// Apply transactions to the pending state if we're not sealing
//
// Note all transactions received may not be continuous with transactions
Expand Down

0 comments on commit db68332

Please sign in to comment.