-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
op-node/rollup/derive: Implement upgrade gas & upgrade block user tx omission #14797
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -66,7 +66,11 @@ func NewL2FaultProofEnv[c any](t helpers.Testing, testCfg *TestCfg[c], tp *e2eut | |
case Holocene: | ||
dp.DeployConfig.ActivateForkAtGenesis(rollup.Holocene) | ||
case Isthmus: | ||
// Isthmus usually runs on a Prague L1 network | ||
dp.DeployConfig.L1PragueTimeOffset = &genesisBlock | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a helper method we already have that would ensure all the Hardforks before Prague are also enabled? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. L1 Cancun is enabled by default. So that's currently the only L1 fork that needs to be enabled manually. |
||
dp.DeployConfig.ActivateForkAtGenesis(rollup.Isthmus) | ||
case Interop: | ||
dp.DeployConfig.ActivateForkAtGenesis(rollup.Interop) | ||
} | ||
|
||
for _, override := range deployConfigOverrides { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
package proofs_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis" | ||
actionsHelpers "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers" | ||
"github.com/ethereum-optimism/optimism/op-e2e/actions/proofs/helpers" | ||
"github.com/ethereum-optimism/optimism/op-node/rollup" | ||
"github.com/ethereum-optimism/optimism/op-service/eth" | ||
"github.com/ethereum-optimism/optimism/op-service/testlog" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/ethereum/go-ethereum/common/hexutil" | ||
"github.com/ethereum/go-ethereum/core/types" | ||
) | ||
|
||
type upgradeBlockTestCfg struct { | ||
fork rollup.ForkName | ||
numUpgradeTxs int | ||
} | ||
|
||
// TestUpgradeBlockGas tests that the upgrade block correctly adds ugprade gas | ||
func TestUpgradeBlockGas(gt *testing.T) { | ||
matrix := helpers.NewMatrix[upgradeBlockTestCfg]() | ||
defer matrix.Run(gt) | ||
|
||
matrix.AddDefaultTestCases( | ||
upgradeBlockTestCfg{fork: rollup.Isthmus, numUpgradeTxs: 8}, | ||
helpers.NewForkMatrix(helpers.Holocene), | ||
testUpgradeBlockGas, | ||
) //.AddDefaultTestCases( | ||
// upgradeBlockTestCfg{fork: rollup.Interop, numUpgradeTxs: 0}, | ||
// helpers.NewForkMatrix(helpers.Isthmus), | ||
// testUpgradeBlock, | ||
// ) | ||
Comment on lines
+32
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Can probably remove the commented out code. |
||
} | ||
|
||
func testUpgradeBlockGas(gt *testing.T, testCfg *helpers.TestCfg[upgradeBlockTestCfg]) { | ||
tcfg := testCfg.Custom | ||
t := actionsHelpers.NewDefaultTesting(gt) | ||
testSetup := func(dc *genesis.DeployConfig) { | ||
dc.L1PragueTimeOffset = ptr(hexutil.Uint64(0)) | ||
// activate fork after a few blocks | ||
dc.SetForkTimeOffset(tcfg.fork, ptr(uint64(4))) | ||
} | ||
env := helpers.NewL2FaultProofEnv(t, testCfg, helpers.NewTestParams(), helpers.NewBatcherCfg(), testSetup) | ||
|
||
engine := env.Engine | ||
sequencer := env.Sequencer | ||
miner := env.Miner | ||
rollupCfg := env.Sd.RollupCfg | ||
|
||
miner.ActEmptyBlock(t) | ||
|
||
sequencer.ActL1HeadSignal(t) | ||
sequencer.ActBuildL2ToFork(t, tcfg.fork) | ||
|
||
upgradeHeader := engine.L2Chain().CurrentHeader() | ||
require.Equal(t, tcfg.fork, rollupCfg.IsActivationBlock(upgradeHeader.Time-1, upgradeHeader.Time)) | ||
|
||
upgradeBlock := engine.L2Chain().GetBlockByHash(upgradeHeader.Hash()) | ||
require.Len(t, upgradeBlock.Transactions(), tcfg.numUpgradeTxs+1) | ||
var upgradeGas uint64 | ||
for _, tx := range upgradeBlock.Transactions()[1:] /* skip l1 info deposit */ { | ||
upgradeGas += tx.Gas() | ||
} | ||
require.Equal(t, rollupCfg.Genesis.SystemConfig.GasLimit+upgradeGas, upgradeBlock.GasLimit()) | ||
|
||
env.BatchAndMine(t) | ||
env.Sequencer.ActL1HeadSignal(t) | ||
env.Sequencer.ActL2PipelineFull(t) | ||
|
||
l2SafeHead := engine.L2Chain().CurrentSafeBlock() | ||
require.Equal(t, eth.HeaderBlockID(l2SafeHead), eth.HeaderBlockID(upgradeHeader), "derivation leads to the same block") | ||
|
||
env.RunFaultProofProgram(t, l2SafeHead.Number.Uint64(), testCfg.CheckResult, testCfg.InputParams...) | ||
} | ||
|
||
// TestUpgradeBlockTxOmission tests that the sequencer omits user transactions in upgrade blocks | ||
// and that batches that contain user transactions in an upgrade block are dropped. | ||
func TestUpgradeBlockTxOmission(gt *testing.T) { | ||
matrix := helpers.NewMatrix[upgradeBlockTestCfg]() | ||
defer matrix.Run(gt) | ||
|
||
matrix.AddDefaultTestCases( | ||
upgradeBlockTestCfg{fork: rollup.Isthmus, numUpgradeTxs: 8}, | ||
helpers.NewForkMatrix(helpers.Holocene), | ||
testUpgradeBlockTxOmission, | ||
) //.AddDefaultTestCases( | ||
// upgradeBlockTestCfg{fork: rollup.Interop, numUpgradeTxs: 0}, | ||
// helpers.NewForkMatrix(helpers.Isthmus), | ||
// testUpgradeBlockTxOmission, | ||
// ) | ||
Comment on lines
+90
to
+94
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Uncomment or remove. |
||
} | ||
|
||
func testUpgradeBlockTxOmission(gt *testing.T, testCfg *helpers.TestCfg[upgradeBlockTestCfg]) { | ||
tcfg := testCfg.Custom | ||
t := actionsHelpers.NewDefaultTesting(gt) | ||
offset := uint64(4) | ||
testSetup := func(dc *genesis.DeployConfig) { | ||
dc.L1PragueTimeOffset = ptr(hexutil.Uint64(0)) | ||
// activate fork after a few blocks | ||
dc.SetForkTimeOffset(tcfg.fork, &offset) | ||
} | ||
env := helpers.NewL2FaultProofEnv(t, testCfg, helpers.NewTestParams(), helpers.NewBatcherCfg(), testSetup) | ||
|
||
engine := env.Engine | ||
sequencer := env.Sequencer | ||
miner := env.Miner | ||
rollupCfg := env.Sd.RollupCfg | ||
blockTime := rollupCfg.BlockTime | ||
|
||
miner.ActEmptyBlock(t) | ||
|
||
sequencer.ActL1HeadSignal(t) | ||
for i := 0; i < int(offset)-1; i++ { | ||
sequencer.ActL2EmptyBlock(t) | ||
} | ||
tx := env.Alice.L2.ActMakeTx(t) | ||
sequencer.ActL2StartBlock(t) | ||
// we assert later that the sequencer actually omits this tx in the upgrade block | ||
engine.ActL2IncludeTx(env.Alice.Address()) | ||
sequencer.ActL2EndBlock(t) | ||
|
||
upgradeHeader := engine.L2Chain().CurrentHeader() | ||
require.Equal(t, tcfg.fork, | ||
rollupCfg.IsActivationBlock(upgradeHeader.Time-blockTime, upgradeHeader.Time), | ||
"this block should be upgrade block") | ||
upgradeBlock := engine.L2Chain().GetBlockByHash(upgradeHeader.Hash()) | ||
require.Len(t, upgradeBlock.Transactions(), tcfg.numUpgradeTxs+1, "upgrade block doesn't contain alice tx") | ||
|
||
batcher := env.Batcher | ||
for i := 0; i < int(offset)-1; i++ { | ||
batcher.ActL2BatchBuffer(t) | ||
} | ||
batcher.ActL2BatchBuffer(t, func(block *types.Block) *types.Block { | ||
// inject user tx into upgrade batch | ||
return block.WithBody(types.Body{Transactions: append(block.Transactions(), tx)}) | ||
}) | ||
|
||
batcher.ActL2ChannelClose(t) | ||
batcher.ActL2BatchSubmit(t) | ||
env.Miner.ActL1StartBlock(12)(t) | ||
env.Miner.ActL1IncludeTxByHash(env.Batcher.LastSubmitted.Hash())(t) | ||
env.Miner.ActL1EndBlock(t) | ||
|
||
env.Sequencer.ActL1HeadSignal(t) | ||
env.Sequencer.ActL2PipelineFull(t) | ||
|
||
recs := env.Logs.FindLogs(testlog.NewMessageFilter("dropping batch with user transactions in fork activation block")) | ||
require.Len(t, recs, 1) | ||
|
||
l2SafeHead := engine.L2Chain().CurrentSafeBlock() | ||
preUpgradeHeader := engine.L2Chain().GetHeaderByHash(upgradeHeader.ParentHash) | ||
require.Equal(t, eth.HeaderBlockID(preUpgradeHeader), eth.HeaderBlockID(l2SafeHead), "derivation only reaches pre-upgrade block") | ||
|
||
env.RunFaultProofProgram(t, l2SafeHead.Number.Uint64(), testCfg.CheckResult, testCfg.InputParams...) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,6 +56,25 @@ func holoceneAt(t *uint64) func(*rollup.Config) { | |
} | ||
} | ||
|
||
func isthmusAt(t *uint64) func(*rollup.Config) { | ||
return func(c *rollup.Config) { | ||
c.DeltaTime = &zero64 | ||
c.FjordTime = &zero64 | ||
c.HoloceneTime = &zero64 | ||
c.IsthmusTime = t | ||
} | ||
} | ||
|
||
func interopAt(t *uint64) func(*rollup.Config) { | ||
return func(c *rollup.Config) { | ||
c.DeltaTime = &zero64 | ||
c.FjordTime = &zero64 | ||
c.HoloceneTime = &zero64 | ||
c.IsthmusTime = &zero64 | ||
c.InteropTime = t | ||
} | ||
} | ||
|
||
const defaultBlockTime = 2 | ||
|
||
func TestValidBatch(t *testing.T) { | ||
|
@@ -539,6 +558,42 @@ func TestValidBatch(t *testing.T) { | |
}, | ||
Expected: BatchDrop, // dropped because it could have advanced the epoch to B | ||
}, | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Haven't looked at all batches tests, but in case you don't, I think it'd be worthwhile to add a test here that covers the happy path when there are no txs in the activation block to confirm this still works as intended. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good idea |
||
Name: "transactions in Isthmus upgrade block", | ||
L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C}, | ||
L2SafeHead: l2A0, | ||
Batch: BatchWithL1InclusionBlock{ | ||
L1InclusionBlock: l1B, | ||
Batch: &SingularBatch{ | ||
ParentHash: l2A1.ParentHash, | ||
EpochNum: rollup.Epoch(l2A1.L1Origin.Number), | ||
EpochHash: l2A1.L1Origin.Hash, | ||
Timestamp: l2A1.Time, | ||
Transactions: []hexutil.Bytes{[]byte("invalid tx in isthmus upgrade block")}, | ||
}, | ||
}, | ||
Expected: BatchDrop, | ||
ExpectedLog: "dropping batch with user transactions in fork activation block", | ||
ConfigMod: isthmusAt(&l2A1.Time), | ||
}, | ||
{ | ||
Name: "transactions in Interop upgrade block", | ||
L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C}, | ||
Comment on lines
+580
to
+581
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we can create these automatically so we pick up new forks. We have the list of hard forks already and we could iterate through them, then for each check that they forbid user transactions unless they're in a list that explicitly allows it. Then if we add a new fork, as long as we add it to the list of forks this test will remind us to update the if statement in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea, I was thinking of actually running the whole upgrade block action tests for all forks from Isthmus. But then, as you can see with the commented out code, it just failed for Interop, so I didn't follow up with this idea. Will do this in the batches tests and track it for Interop. |
||
L2SafeHead: l2A0, | ||
Batch: BatchWithL1InclusionBlock{ | ||
L1InclusionBlock: l1B, | ||
Batch: &SingularBatch{ | ||
ParentHash: l2A1.ParentHash, | ||
EpochNum: rollup.Epoch(l2A1.L1Origin.Number), | ||
EpochHash: l2A1.L1Origin.Hash, | ||
Timestamp: l2A1.Time, | ||
Transactions: []hexutil.Bytes{[]byte("invalid tx in isthmus upgrade block")}, | ||
}, | ||
}, | ||
Expected: BatchDrop, | ||
ExpectedLog: "dropping batch with user transactions in fork activation block", | ||
ConfigMod: interopAt(&l2A1.Time), | ||
}, | ||
{ | ||
Name: "empty tx included", | ||
L1Blocks: []eth.L1BlockRef{l1A, l1B}, | ||
|
@@ -630,6 +685,7 @@ func TestValidBatch(t *testing.T) { | |
Expected: BatchDrop, | ||
}, | ||
} | ||
|
||
spanBatchTestCases := []ValidBatchTestCase{ | ||
{ | ||
Name: "missing L1 info", | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should either do this or reference an issue for it. Probably a simple
require
could be added here since we havet
already.