Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Aj/backport pending block l1overhead #96

Merged
merged 3 commits into from
May 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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