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

Fjord: Add FastLZ compression into L1CostFunc #9618

Merged
merged 14 commits into from
May 16, 2024
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
6 changes: 6 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1758,6 +1758,12 @@ workflows:
on_changes: cannon,packages/contracts-bedrock/src/cannon
uses_artifacts: true
requires: ["go-mod-download", "pnpm-monorepo"]
- fuzz-golang:
name: op-e2e-fuzz
package_name: op-e2e
on_changes: op-e2e,packages/contracts-bedrock/src
uses_artifacts: true
requires: ["go-mod-download", "pnpm-monorepo"]
- go-test:
name: op-heartbeat-tests
module: op-heartbeat
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ require (
rsc.io/tmplfunc v0.0.3 // indirect
)

replace github.com/ethereum/go-ethereum => github.com/ethereum-optimism/op-geth v1.101315.1-rc.2
replace github.com/ethereum/go-ethereum => github.com/ethereum-optimism/op-geth v1.101315.1-rc.3

//replace github.com/ethereum/go-ethereum v1.13.9 => ../op-geth

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,8 @@ github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/
github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs=
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc=
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs=
github.com/ethereum-optimism/op-geth v1.101315.1-rc.2 h1:uUrcs8fGrdDnVELB66GcMZRvwIeJow64DOtF+VFdAzY=
github.com/ethereum-optimism/op-geth v1.101315.1-rc.2/go.mod h1:VXVFzx1mr/JyJac5M4k5W/+0cqHZMkqKsIVDsOyj2rs=
github.com/ethereum-optimism/op-geth v1.101315.1-rc.3 h1:BvmzUehVSo7uuqtApy/h/A5uRDAuU2tJQLgHCWTxAUQ=
github.com/ethereum-optimism/op-geth v1.101315.1-rc.3/go.mod h1:VXVFzx1mr/JyJac5M4k5W/+0cqHZMkqKsIVDsOyj2rs=
github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240510200259-4be7024d2ba7 h1:e7oXWZwODAMM2TLo9beGDXaX2cCw7uM7qAqamYBHV40=
github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240510200259-4be7024d2ba7/go.mod h1:7xh2awFQqsiZxFrHKTgEd+InVfDRrkKVUIuK8SAFHp0=
github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY=
Expand Down
87 changes: 85 additions & 2 deletions op-bindings/bindings/gaspriceoracle.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion op-chain-ops/cmd/ecotone-scalar/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func main() {
scalar = uint(decoded.BaseFeeScalar)
blobScalar = uint(decoded.BlobBaseFeeScalar)
} else {
encoded = eth.EncodeScalar(eth.EcostoneScalars{
encoded = eth.EncodeScalar(eth.EcotoneScalars{
BlobBaseFeeScalar: uint32(blobScalar),
BaseFeeScalar: uint32(scalar),
})
Expand Down
2 changes: 1 addition & 1 deletion op-chain-ops/genesis/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ func (d *DeployConfig) FeeScalar() [32]byte {
if d.GasPriceOracleScalar != 0 {
return common.BigToHash(big.NewInt(int64(d.GasPriceOracleScalar)))
}
return eth.EncodeScalar(eth.EcostoneScalars{
return eth.EncodeScalar(eth.EcotoneScalars{
BlobBaseFeeScalar: d.GasPriceOracleBlobBaseFeeScalar,
BaseFeeScalar: d.GasPriceOracleBaseFeeScalar,
})
Expand Down
6 changes: 6 additions & 0 deletions op-e2e/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,9 @@ clean:
rm -r ../.devnet
rm -r ../op-program/bin
.PHONY: clean

fuzz:
go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzFjordCostFunction ./
go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzFastLzGethSolidity ./
go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzFastLzCgo ./

145 changes: 145 additions & 0 deletions op-e2e/actions/fjord_fork_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package actions

import (
"context"
"encoding/hex"
"math/big"
"testing"

"github.com/ethereum-optimism/optimism/op-service/predeploys"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"

"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-service/testlog"
)

var (
fjordGasPriceOracleCodeHash = common.HexToHash("0xa88fa50a2745b15e6794247614b5298483070661adacb8d32d716434ed24c6b2")
// https://basescan.org/tx/0x8debb2fe54200183fb8baa3c6dbd8e6ec2e4f7a4add87416cd60336b8326d16a
txHex = "02f875822105819b8405709fb884057d460082e97f94273ca93a52b817294830ed7572aa591ccfa647fd80881249c58b0021fb3fc080a05bb08ccfd68f83392e446dac64d88a2d28e7072c06502dfabc4a77e77b5c7913a05878d53dd4ebba4f6367e572d524dffcabeec3abb1d8725ee3ac5dc32e1852e3"
)

func TestFjordNetworkUpgradeTransactions(gt *testing.T) {
t := NewDefaultTesting(gt)
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
genesisBlock := hexutil.Uint64(0)
fjordOffset := hexutil.Uint64(2)

dp.DeployConfig.L1CancunTimeOffset = &genesisBlock // can be removed once Cancun on L1 is the default

// Activate all forks at genesis, and schedule Fjord the block after
dp.DeployConfig.L2GenesisRegolithTimeOffset = &genesisBlock
dp.DeployConfig.L2GenesisCanyonTimeOffset = &genesisBlock
dp.DeployConfig.L2GenesisDeltaTimeOffset = &genesisBlock
dp.DeployConfig.L2GenesisEcotoneTimeOffset = &genesisBlock
dp.DeployConfig.L2GenesisFjordTimeOffset = &fjordOffset
require.NoError(t, dp.DeployConfig.Check(), "must have valid config")

sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug)
_, _, _, sequencer, engine, verifier, _, _ := setupReorgTestActors(t, dp, sd, log)
ethCl := engine.EthClient()

// start op-nodes
sequencer.ActL2PipelineFull(t)
verifier.ActL2PipelineFull(t)

// Get gas price from oracle
gasPriceOracle, err := bindings.NewGasPriceOracleCaller(predeploys.GasPriceOracleAddr, ethCl)
require.NoError(t, err)

// Get current implementations addresses (by slot) for L1Block + GasPriceOracle
initialGasPriceOracleAddress, err := ethCl.StorageAt(context.Background(), predeploys.GasPriceOracleAddr, genesis.ImplementationSlot, nil)
require.NoError(t, err)

sequencer.ActBuildL2ToFjord(t)

// get latest block
latestBlock, err := ethCl.BlockByNumber(context.Background(), nil)
require.NoError(t, err)
require.Equal(t, sequencer.L2Unsafe().Number, latestBlock.Number().Uint64())

transactions := latestBlock.Transactions()
// L1Block: 1 set-L1-info + 1 deploys + 1 upgradeTo + 1 enable fjord on GPO
// See [derive.FjordNetworkUpgradeTransactions]
require.Equal(t, 4, len(transactions))

// All transactions are successful
for i := 1; i < 4; i++ {
txn := transactions[i]
receipt, err := ethCl.TransactionReceipt(context.Background(), txn.Hash())
require.NoError(t, err)
require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status)
require.NotEmpty(t, txn.Data(), "upgrade tx must provide input data")
}

expectedGasPriceOracleAddress := crypto.CreateAddress(derive.GasPriceOracleFjordDeployerAddress, 0)

// Gas Price Oracle Proxy is updated
updatedGasPriceOracleAddress, err := ethCl.StorageAt(context.Background(), predeploys.GasPriceOracleAddr, genesis.ImplementationSlot, latestBlock.Number())
require.NoError(t, err)
require.Equal(t, expectedGasPriceOracleAddress, common.BytesToAddress(updatedGasPriceOracleAddress))
require.NotEqualf(t, initialGasPriceOracleAddress, updatedGasPriceOracleAddress, "Gas Price Oracle Proxy address should have changed")
verifyCodeHashMatches(t, ethCl, expectedGasPriceOracleAddress, fjordGasPriceOracleCodeHash)

// Check that Fjord was activated
isFjord, err := gasPriceOracle.IsFjord(nil)
require.NoError(t, err)
require.True(t, isFjord)

// Check GetL1GasUsed is updated
txData, err := hex.DecodeString(txHex)
require.NoError(t, err)

gpoL1GasUsed, err := gasPriceOracle.GetL1GasUsed(&bind.CallOpts{}, txData)
require.NoError(t, err)
require.Equal(gt, uint64(1_888), gpoL1GasUsed.Uint64())

// Check that GetL1Fee takes into account fast LZ
gpoFee, err := gasPriceOracle.GetL1Fee(&bind.CallOpts{}, txData)
require.NoError(t, err)

gethFee := fjordL1Cost(t, gasPriceOracle, types.RollupCostData{
FastLzSize: uint64(types.FlzCompressLen(txData) + 68),
})
require.Equal(t, gethFee.Uint64(), gpoFee.Uint64())

// Check that L1FeeUpperBound works
upperBound, err := gasPriceOracle.GetL1FeeUpperBound(&bind.CallOpts{}, big.NewInt(int64(len(txData))))
require.NoError(t, err)

txLen := len(txData) + 68
flzUpperBound := uint64(txLen + txLen/255 + 16)

upperBoundCost := fjordL1Cost(t, gasPriceOracle, types.RollupCostData{FastLzSize: flzUpperBound})
require.Equal(t, upperBoundCost.Uint64(), upperBound.Uint64())
}

func fjordL1Cost(t require.TestingT, gasPriceOracle *bindings.GasPriceOracleCaller, rollupCostData types.RollupCostData) *big.Int {
baseFeeScalar, err := gasPriceOracle.BaseFeeScalar(nil)
require.NoError(t, err)
l1BaseFee, err := gasPriceOracle.L1BaseFee(nil)
require.NoError(t, err)
blobBaseFeeScalar, err := gasPriceOracle.BlobBaseFeeScalar(nil)
require.NoError(t, err)
blobBaseFee, err := gasPriceOracle.BlobBaseFee(nil)
require.NoError(t, err)

costFunc := types.NewL1CostFuncFjord(
l1BaseFee,
blobBaseFee,
new(big.Int).SetUint64(uint64(baseFeeScalar)),
new(big.Int).SetUint64(uint64(blobBaseFeeScalar)))

fee, _ := costFunc(rollupCostData)
return fee
}
7 changes: 7 additions & 0 deletions op-e2e/actions/l2_sequencer.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,10 @@ func (s *L2Sequencer) ActBuildL2ToEcotone(t Testing) {
s.ActL2EndBlock(t)
}
}
func (s *L2Sequencer) ActBuildL2ToFjord(t Testing) {
require.NotNil(t, s.rollupCfg.FjordTime, "cannot activate FjordTime when it is not scheduled")
for s.L2Unsafe().Time < *s.rollupCfg.FjordTime {
s.ActL2StartBlock(t)
s.ActL2EndBlock(t)
}
}
2 changes: 1 addition & 1 deletion op-e2e/external.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (eec *ExternalEthClient) Close() error {
return nil
}

func (er *ExternalRunner) Run(t *testing.T) *ExternalEthClient {
func (er *ExternalRunner) Run(t testing.TB) *ExternalEthClient {
if er.BinPath == "" {
t.Error("no external bin path set")
}
Expand Down
2 changes: 1 addition & 1 deletion op-e2e/external/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ type TestParms struct {
SkipTests map[string]string `json:"skip_tests"`
}

func (tp TestParms) SkipIfNecessary(t *testing.T) {
func (tp TestParms) SkipIfNecessary(t testing.TB) {
if len(tp.SkipTests) == 0 {
return
}
Expand Down
Loading