Commit a3672dd4 authored by Adrian Sutton's avatar Adrian Sutton

Merge branch 'develop' into aj/remove-more-bindings

parents 251a4f62 2bf094a8
...@@ -1420,13 +1420,6 @@ workflows: ...@@ -1420,13 +1420,6 @@ workflows:
- op-stack-go-lint - op-stack-go-lint
- devnet-allocs - devnet-allocs
- l1-geth-version-check - l1-geth-version-check
- go-e2e-test:
name: op-e2e-span-batch-tests
module: op-e2e
target: test-span-batch
requires:
- op-stack-go-lint
- devnet-allocs
- op-program-compat: - op-program-compat:
requires: requires:
- op-program-tests - op-program-tests
......
package main
import (
"bytes"
"context"
"errors"
"flag"
"fmt"
"math/big"
"os"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-service/client"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/sources"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/trie"
)
func CalcBaseFee(parent eth.BlockInfo, elasticity uint64, canyonActive bool) *big.Int {
denomUint := uint64(50)
if canyonActive {
denomUint = uint64(250)
}
parentGasTarget := parent.GasLimit() / elasticity
// If the parent gasUsed is the same as the target, the baseFee remains unchanged.
if parent.GasUsed() == parentGasTarget {
return new(big.Int).Set(parent.BaseFee())
}
var (
num = new(big.Int)
denom = new(big.Int)
)
if parent.GasUsed() > parentGasTarget {
// If the parent block used more gas than its target, the baseFee should increase.
// max(1, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator)
num.SetUint64(parent.GasUsed() - parentGasTarget)
num.Mul(num, parent.BaseFee())
num.Div(num, denom.SetUint64(parentGasTarget))
num.Div(num, denom.SetUint64(denomUint))
baseFeeDelta := math.BigMax(num, common.Big1)
return num.Add(parent.BaseFee(), baseFeeDelta)
} else {
// Otherwise if the parent block used less gas than its target, the baseFee should decrease.
// max(0, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator)
num.SetUint64(parentGasTarget - parent.GasUsed())
num.Mul(num, parent.BaseFee())
num.Div(num, denom.SetUint64(parentGasTarget))
num.Div(num, denom.SetUint64(denomUint))
baseFee := num.Sub(parent.BaseFee(), num)
return math.BigMax(baseFee, common.Big0)
}
}
func ManuallyEncodeReceipts(receipts types.Receipts, canyonActive bool) [][]byte {
v := uint64(1)
for _, receipt := range receipts {
if receipt.Type == types.DepositTxType {
if canyonActive {
receipt.DepositReceiptVersion = &v
} else {
receipt.DepositReceiptVersion = nil
}
}
}
var out [][]byte
for i := range receipts {
var buf bytes.Buffer
receipts.EncodeIndex(i, &buf)
out = append(out, buf.Bytes())
}
return out
}
type rawReceipts [][]byte
func (s rawReceipts) Len() int { return len(s) }
func (s rawReceipts) EncodeIndex(i int, w *bytes.Buffer) {
w.Write(s[i])
}
func HashList(list [][]byte) common.Hash {
hasher := trie.NewStackTrie(nil)
return types.DeriveSha(rawReceipts(list), hasher)
}
type L2Client interface {
BlockByNumber(context.Context, *big.Int) (*types.Block, error)
CodeAt(context.Context, common.Address, *big.Int) ([]byte, error)
InfoByNumber(context.Context, uint64) (eth.BlockInfo, error)
FetchReceipts(context.Context, common.Hash) (eth.BlockInfo, types.Receipts, error)
}
type Client struct {
*ethclient.Client
*sources.L1Client
}
type Args struct {
Number uint64
Elasticity uint64
Client L2Client
}
func ValidateReceipts(ctx Args, canyonActive bool) error {
block, err := ctx.Client.InfoByNumber(context.Background(), ctx.Number)
if err != nil {
return err
}
_, receipts, err := ctx.Client.FetchReceipts(context.Background(), block.Hash())
if err != nil {
return err
}
have := block.ReceiptHash()
want := HashList(ManuallyEncodeReceipts(receipts, canyonActive))
if have != want {
return fmt.Errorf("Receipts do not look correct. canyonActive: %v. have: %v, want: %v", canyonActive, have, want)
}
return nil
}
func Validate1559Params(ctx Args, canyonActive bool) error {
block, err := ctx.Client.InfoByNumber(context.Background(), ctx.Number)
if err != nil {
return err
}
if block.BaseFee().Cmp(big.NewInt(1000)) < 0 {
log.Info("Basefee to low to properly validate", "basefee", block.BaseFee())
return nil
}
parent, err := ctx.Client.InfoByNumber(context.Background(), ctx.Number-1)
if err != nil {
return err
}
want := CalcBaseFee(parent, ctx.Elasticity, canyonActive)
have := block.BaseFee()
if have.Cmp(want) != 0 {
return fmt.Errorf("BaseFee does not match. canyonActive: %v. have: %v, want: %v", canyonActive, have, want)
}
return nil
}
func ValidateWithdrawals(ctx Args, canyonActive bool) error {
block, err := ctx.Client.BlockByNumber(context.Background(), new(big.Int).SetUint64(ctx.Number))
if err != nil {
return err
}
if canyonActive && block.Withdrawals() == nil {
return errors.New("No nonwithdrawals in a canyon block")
} else if canyonActive && len(block.Withdrawals()) > 0 {
return errors.New("Withdrawals length is not zero in a canyon block")
} else if !canyonActive && block.Withdrawals() != nil {
return errors.New("Withdrawals in a pre-canyon block")
}
return nil
}
func ValidateCreate2Deployer(ctx Args, canyonActive bool) error {
addr := common.HexToAddress("0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2")
code, err := ctx.Client.CodeAt(context.Background(), addr, new(big.Int).SetUint64(ctx.Number))
if err != nil {
return err
}
codeHash := crypto.Keccak256Hash(code)
expectedCodeHash := common.HexToHash("0xb0550b5b431e30d38000efb7107aaa0ade03d48a7198a140edda9d27134468b2")
if canyonActive && codeHash != expectedCodeHash {
return fmt.Errorf("Canyon active but code hash does not match. have: %v, want: %v", codeHash, expectedCodeHash)
} else if !canyonActive && codeHash == expectedCodeHash {
return fmt.Errorf("Canyon not active but code hashes do match. codeHash: %v", codeHash)
}
return nil
}
// CheckActivation takes a function f which determines in a specific block follows the rules of a fork.
// forkActivated tells `f` if the fork is active or not. `f` is called twice: First to validate that
// there is no error when checking the new value and second to validate the it returns an error when
// attempting to validate the block against the opposite of what is is.
// If any error is encountered, valid is set to false.
func CheckActivation(f func(Args, bool) error, ctx Args, forkActivated bool, valid *bool) {
if forkActivated {
if err := f(ctx, true); err != nil {
log.Error("Pre-state was invalid when it was expected to be valid", "err", err)
*valid = false
}
if err := f(ctx, false); err == nil {
log.Error("Post-state was valid when it was expected to be invalid")
*valid = false
}
} else {
if err := f(ctx, true); err == nil {
log.Error("Pre-state was valid when it was expected to be invalid")
*valid = false
}
if err := f(ctx, false); err != nil {
log.Error("Post-state was invalid when it was expected to be valid", "err", err)
*valid = false
}
}
}
func main() {
logger := log.New()
// Define the flag variables
var (
canyonActive bool
number uint64
elasticity uint64
rpcURL string
)
valid := true
// Define and parse the command-line flags
flag.BoolVar(&canyonActive, "canyon", false, "Set this flag to assert canyon behavior")
flag.Uint64Var(&number, "number", 31, "Block number to check")
flag.Uint64Var(&elasticity, "elasticity", 6, "Specify the EIP-1559 elasticity. 6 on mainnet/sepolia. 10 on goerli")
flag.StringVar(&rpcURL, "rpc-url", "http://localhost:8545", "Specify the L2 ETH RPC URL")
// Parse the command-line arguments
flag.Parse()
l2RPC, err := client.NewRPC(context.Background(), logger, rpcURL, client.WithDialBackoff(10))
if err != nil {
log.Crit("Error creating RPC", "err", err)
}
c := &rollup.Config{SeqWindowSize: 10}
l2Cfg := sources.L1ClientDefaultConfig(c, true, sources.RPCKindBasic)
sourceClient, err := sources.NewL1Client(l2RPC, logger, nil, l2Cfg)
if err != nil {
log.Crit("Error creating RPC", "err", err)
}
ethClient, err := ethclient.Dial(rpcURL)
if err != nil {
log.Crit("Error creating RPC", "err", err)
}
client := Client{ethClient, sourceClient}
ctx := Args{
Number: number,
Elasticity: elasticity,
Client: client,
}
CheckActivation(ValidateReceipts, ctx, canyonActive, &valid)
CheckActivation(Validate1559Params, ctx, canyonActive, &valid)
CheckActivation(ValidateWithdrawals, ctx, canyonActive, &valid)
CheckActivation(ValidateCreate2Deployer, ctx, canyonActive, &valid)
if !valid {
os.Exit(1)
} else if canyonActive {
log.Info(fmt.Sprintf("Successfully validated block %v as a Canyon block", number))
} else {
log.Info(fmt.Sprintf("Successfully validated block %v as a Pre-Canyon block", number))
}
}
...@@ -39,7 +39,7 @@ type Agent struct { ...@@ -39,7 +39,7 @@ type Agent struct {
log log.Logger log log.Logger
} }
func NewAgent(m metrics.Metricer, loader ClaimLoader, maxDepth int, trace types.TraceProvider, responder Responder, updater types.OracleUpdater, agreeWithProposedOutput bool, log log.Logger) *Agent { func NewAgent(m metrics.Metricer, loader ClaimLoader, maxDepth int, trace types.TraceAccessor, responder Responder, updater types.OracleUpdater, agreeWithProposedOutput bool, log log.Logger) *Agent {
return &Agent{ return &Agent{
metrics: m, metrics: m,
solver: solver.NewGameSolver(maxDepth, trace), solver: solver.NewGameSolver(maxDepth, trace),
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"errors" "errors"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
...@@ -112,10 +113,10 @@ func setupTestAgent(t *testing.T, agreeWithProposedOutput bool) (*Agent, *stubCl ...@@ -112,10 +113,10 @@ func setupTestAgent(t *testing.T, agreeWithProposedOutput bool) (*Agent, *stubCl
logger := testlog.Logger(t, log.LvlInfo) logger := testlog.Logger(t, log.LvlInfo)
claimLoader := &stubClaimLoader{} claimLoader := &stubClaimLoader{}
depth := 4 depth := 4
trace := alphabet.NewTraceProvider("abcd", uint64(depth)) provider := alphabet.NewTraceProvider("abcd", uint64(depth))
responder := &stubResponder{} responder := &stubResponder{}
updater := &stubUpdater{} updater := &stubUpdater{}
agent := NewAgent(metrics.NoopMetrics, claimLoader, depth, trace, responder, updater, agreeWithProposedOutput, logger) agent := NewAgent(metrics.NoopMetrics, claimLoader, depth, trace.NewSimpleTraceAccessor(provider), responder, updater, agreeWithProposedOutput, logger)
return agent, claimLoader, responder return agent, claimLoader, responder
} }
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/responder" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/responder"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-challenger/metrics" "github.com/ethereum-optimism/optimism/op-challenger/metrics"
...@@ -90,8 +91,10 @@ func NewGamePlayer( ...@@ -90,8 +91,10 @@ func NewGamePlayer(
return nil, fmt.Errorf("failed to create the responder: %w", err) return nil, fmt.Errorf("failed to create the responder: %w", err)
} }
accessor := trace.NewSimpleTraceAccessor(provider)
agent := NewAgent(m, loader, int(gameDepth), accessor, responder, updater, cfg.AgreeWithProposedOutput, logger)
return &GamePlayer{ return &GamePlayer{
act: NewAgent(m, loader, int(gameDepth), provider, responder, updater, cfg.AgreeWithProposedOutput, logger).Act, act: agent.Act,
agreeWithProposedOutput: cfg.AgreeWithProposedOutput, agreeWithProposedOutput: cfg.AgreeWithProposedOutput,
loader: loader, loader: loader,
logger: logger, logger: logger,
......
...@@ -12,7 +12,7 @@ type GameSolver struct { ...@@ -12,7 +12,7 @@ type GameSolver struct {
claimSolver *claimSolver claimSolver *claimSolver
} }
func NewGameSolver(gameDepth int, trace types.TraceProvider) *GameSolver { func NewGameSolver(gameDepth int, trace types.TraceAccessor) *GameSolver {
return &GameSolver{ return &GameSolver{
claimSolver: newClaimSolver(gameDepth, trace), claimSolver: newClaimSolver(gameDepth, trace),
} }
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"testing" "testing"
faulttest "github.com/ethereum-optimism/optimism/op-challenger/game/fault/test" faulttest "github.com/ethereum-optimism/optimism/op-challenger/game/fault/test"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
...@@ -113,7 +114,7 @@ func TestCalculateNextActions(t *testing.T) { ...@@ -113,7 +114,7 @@ func TestCalculateNextActions(t *testing.T) {
i, claim.Position.ToGIndex(), claim.Position.TraceIndex(maxDepth), claim.ParentContractIndex, claim.Countered, claim.Value) i, claim.Position.ToGIndex(), claim.Position.TraceIndex(maxDepth), claim.ParentContractIndex, claim.Countered, claim.Value)
} }
solver := NewGameSolver(maxDepth, claimBuilder.CorrectTraceProvider()) solver := NewGameSolver(maxDepth, trace.NewSimpleTraceAccessor(claimBuilder.CorrectTraceProvider()))
actions, err := solver.CalculateNextActions(context.Background(), game) actions, err := solver.CalculateNextActions(context.Background(), game)
require.NoError(t, err) require.NoError(t, err)
for i, action := range actions { for i, action := range actions {
......
...@@ -7,7 +7,6 @@ import ( ...@@ -7,7 +7,6 @@ import (
"fmt" "fmt"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/common"
) )
var ( var (
...@@ -18,14 +17,14 @@ var ( ...@@ -18,14 +17,14 @@ var (
// claimSolver uses a [TraceProvider] to determine the moves to make in a dispute game. // claimSolver uses a [TraceProvider] to determine the moves to make in a dispute game.
type claimSolver struct { type claimSolver struct {
trace types.TraceProvider trace types.TraceAccessor
gameDepth int gameDepth int
} }
// newClaimSolver creates a new [claimSolver] using the provided [TraceProvider]. // newClaimSolver creates a new [claimSolver] using the provided [TraceProvider].
func newClaimSolver(gameDepth int, traceProvider types.TraceProvider) *claimSolver { func newClaimSolver(gameDepth int, trace types.TraceAccessor) *claimSolver {
return &claimSolver{ return &claimSolver{
traceProvider, trace,
gameDepth, gameDepth,
} }
} }
...@@ -53,14 +52,14 @@ func (s *claimSolver) NextMove(ctx context.Context, claim types.Claim, game type ...@@ -53,14 +52,14 @@ func (s *claimSolver) NextMove(ctx context.Context, claim types.Claim, game type
} }
} }
agree, err := s.agreeWithClaim(ctx, claim.ClaimData) agree, err := s.agreeWithClaim(ctx, game, claim)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if agree { if agree {
return s.defend(ctx, claim) return s.defend(ctx, game, claim)
} else { } else {
return s.attack(ctx, claim) return s.attack(ctx, game, claim)
} }
} }
...@@ -93,28 +92,25 @@ func (s *claimSolver) AttemptStep(ctx context.Context, game types.Game, claim ty ...@@ -93,28 +92,25 @@ func (s *claimSolver) AttemptStep(ctx context.Context, game types.Game, claim ty
return StepData{}, ErrStepIgnoreInvalidPath return StepData{}, ErrStepIgnoreInvalidPath
} }
claimCorrect, err := s.agreeWithClaim(ctx, claim.ClaimData) claimCorrect, err := s.agreeWithClaim(ctx, game, claim)
if err != nil { if err != nil {
return StepData{}, err return StepData{}, err
} }
var preState []byte
var proofData []byte
var oracleData *types.PreimageOracleData
var position types.Position
if !claimCorrect { if !claimCorrect {
// Attack the claim by executing step index, so we need to get the pre-state of that index // Attack the claim by executing step index, so we need to get the pre-state of that index
preState, proofData, oracleData, err = s.trace.GetStepData(ctx, claim.Position) position = claim.Position
if err != nil {
return StepData{}, err
}
} else { } else {
// We agree with the claim so Defend and use this claim as the starting point to // Defend and use this claim as the starting point to execute the step after.
// execute the step after. Thus we need the pre-state of the next step. // Thus, we need the pre-state of the next step.
preState, proofData, oracleData, err = s.trace.GetStepData(ctx, claim.MoveRight()) position = claim.Position.MoveRight()
}
preState, proofData, oracleData, err := s.trace.GetStepData(ctx, game, claim, position)
if err != nil { if err != nil {
return StepData{}, err return StepData{}, err
} }
}
return StepData{ return StepData{
LeafClaim: claim, LeafClaim: claim,
...@@ -126,9 +122,9 @@ func (s *claimSolver) AttemptStep(ctx context.Context, game types.Game, claim ty ...@@ -126,9 +122,9 @@ func (s *claimSolver) AttemptStep(ctx context.Context, game types.Game, claim ty
} }
// attack returns a response that attacks the claim. // attack returns a response that attacks the claim.
func (s *claimSolver) attack(ctx context.Context, claim types.Claim) (*types.Claim, error) { func (s *claimSolver) attack(ctx context.Context, game types.Game, claim types.Claim) (*types.Claim, error) {
position := claim.Attack() position := claim.Attack()
value, err := s.traceAtPosition(ctx, position) value, err := s.trace.Get(ctx, game, claim, position)
if err != nil { if err != nil {
return nil, fmt.Errorf("attack claim: %w", err) return nil, fmt.Errorf("attack claim: %w", err)
} }
...@@ -139,12 +135,12 @@ func (s *claimSolver) attack(ctx context.Context, claim types.Claim) (*types.Cla ...@@ -139,12 +135,12 @@ func (s *claimSolver) attack(ctx context.Context, claim types.Claim) (*types.Cla
} }
// defend returns a response that defends the claim. // defend returns a response that defends the claim.
func (s *claimSolver) defend(ctx context.Context, claim types.Claim) (*types.Claim, error) { func (s *claimSolver) defend(ctx context.Context, game types.Game, claim types.Claim) (*types.Claim, error) {
if claim.IsRoot() { if claim.IsRoot() {
return nil, nil return nil, nil
} }
position := claim.Defend() position := claim.Defend()
value, err := s.traceAtPosition(ctx, position) value, err := s.trace.Get(ctx, game, claim, position)
if err != nil { if err != nil {
return nil, fmt.Errorf("defend claim: %w", err) return nil, fmt.Errorf("defend claim: %w", err)
} }
...@@ -155,19 +151,14 @@ func (s *claimSolver) defend(ctx context.Context, claim types.Claim) (*types.Cla ...@@ -155,19 +151,14 @@ func (s *claimSolver) defend(ctx context.Context, claim types.Claim) (*types.Cla
} }
// agreeWithClaim returns true if the claim is correct according to the internal [TraceProvider]. // agreeWithClaim returns true if the claim is correct according to the internal [TraceProvider].
func (s *claimSolver) agreeWithClaim(ctx context.Context, claim types.ClaimData) (bool, error) { func (s *claimSolver) agreeWithClaim(ctx context.Context, game types.Game, claim types.Claim) (bool, error) {
ourValue, err := s.traceAtPosition(ctx, claim.Position) ourValue, err := s.trace.Get(ctx, game, claim, claim.Position)
return bytes.Equal(ourValue[:], claim.Value[:]), err return bytes.Equal(ourValue[:], claim.Value[:]), err
} }
// traceAtPosition returns the [common.Hash] from internal [TraceProvider] at the given [Position].
func (s *claimSolver) traceAtPosition(ctx context.Context, p types.Position) (common.Hash, error) {
return s.trace.Get(ctx, p)
}
// agreeWithClaimPath returns true if the every other claim in the path to root is correct according to the internal [TraceProvider]. // agreeWithClaimPath returns true if the every other claim in the path to root is correct according to the internal [TraceProvider].
func (s *claimSolver) agreeWithClaimPath(ctx context.Context, game types.Game, claim types.Claim) (bool, error) { func (s *claimSolver) agreeWithClaimPath(ctx context.Context, game types.Game, claim types.Claim) (bool, error) {
agree, err := s.agreeWithClaim(ctx, claim.ClaimData) agree, err := s.agreeWithClaim(ctx, game, claim)
if err != nil { if err != nil {
return false, err return false, err
} }
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"testing" "testing"
faulttest "github.com/ethereum-optimism/optimism/op-challenger/game/fault/test" faulttest "github.com/ethereum-optimism/optimism/op-challenger/game/fault/test"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -161,7 +162,7 @@ func TestAttemptStep(t *testing.T) { ...@@ -161,7 +162,7 @@ func TestAttemptStep(t *testing.T) {
t.Run(tableTest.name, func(t *testing.T) { t.Run(tableTest.name, func(t *testing.T) {
builder := claimBuilder.GameBuilder(tableTest.agreeWithOutputRoot, !tableTest.agreeWithOutputRoot) builder := claimBuilder.GameBuilder(tableTest.agreeWithOutputRoot, !tableTest.agreeWithOutputRoot)
tableTest.setupGame(builder) tableTest.setupGame(builder)
alphabetSolver := newClaimSolver(maxDepth, claimBuilder.CorrectTraceProvider()) alphabetSolver := newClaimSolver(maxDepth, trace.NewSimpleTraceAccessor(claimBuilder.CorrectTraceProvider()))
game := builder.Game game := builder.Game
claims := game.Claims() claims := game.Claims()
lastClaim := claims[len(claims)-1] lastClaim := claims[len(claims)-1]
......
package trace
import (
"context"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/common"
)
type ProviderCreator func(ctx context.Context, pre types.Claim, post types.Claim) (types.TraceProvider, error)
func NewSimpleTraceAccessor(trace types.TraceProvider) *Accessor {
selector := func(_ context.Context, _ types.Game, _ types.Claim, _ types.Position) (types.TraceProvider, error) {
return trace, nil
}
return &Accessor{selector}
}
type Accessor struct {
selector func(ctx context.Context, game types.Game, ref types.Claim, pos types.Position) (types.TraceProvider, error)
}
func (t *Accessor) Get(ctx context.Context, game types.Game, ref types.Claim, pos types.Position) (common.Hash, error) {
provider, err := t.selector(ctx, game, ref, pos)
if err != nil {
return common.Hash{}, err
}
return provider.Get(ctx, pos)
}
func (t *Accessor) GetStepData(ctx context.Context, game types.Game, ref types.Claim, pos types.Position) (prestate []byte, proofData []byte, preimageData *types.PreimageOracleData, err error) {
provider, err := t.selector(ctx, game, ref, pos)
if err != nil {
return nil, nil, nil, err
}
return provider.GetStepData(ctx, pos)
}
var _ types.TraceAccessor = (*Accessor)(nil)
package trace
import (
"context"
"fmt"
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/test"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/alphabet"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/stretchr/testify/require"
)
func TestAccessor_UsesSelector(t *testing.T) {
ctx := context.Background()
depth := uint64(4)
provider1 := test.NewAlphabetWithProofProvider(t, int(depth), nil)
provider2 := alphabet.NewTraceProvider("qrstuv", depth)
claim := types.Claim{}
game := types.NewGameState(true, []types.Claim{claim}, depth)
pos1 := types.NewPositionFromGIndex(big.NewInt(4))
pos2 := types.NewPositionFromGIndex(big.NewInt(6))
accessor := &Accessor{
selector: func(ctx context.Context, actualGame types.Game, ref types.Claim, pos types.Position) (types.TraceProvider, error) {
require.Equal(t, game, actualGame)
require.Equal(t, claim, ref)
if pos == pos1 {
return provider1, nil
} else if pos == pos2 {
return provider2, nil
}
return nil, fmt.Errorf("incorrect position requested: %v", pos)
},
}
t.Run("Get", func(t *testing.T) {
actual, err := accessor.Get(ctx, game, claim, pos1)
require.NoError(t, err)
expected, err := provider1.Get(ctx, pos1)
require.NoError(t, err)
require.Equal(t, expected, actual)
actual, err = accessor.Get(ctx, game, claim, pos2)
require.NoError(t, err)
expected, err = provider2.Get(ctx, pos2)
require.NoError(t, err)
require.Equal(t, expected, actual)
})
t.Run("GetStepData", func(t *testing.T) {
actualPrestate, actualProofData, actualPreimageData, err := accessor.GetStepData(ctx, game, claim, pos1)
require.NoError(t, err)
expectedPrestate, expectedProofData, expectedPreimageData, err := provider1.GetStepData(ctx, pos1)
require.NoError(t, err)
require.Equal(t, expectedPrestate, actualPrestate)
require.Equal(t, expectedProofData, actualProofData)
require.Equal(t, expectedPreimageData, actualPreimageData)
actualPrestate, actualProofData, actualPreimageData, err = accessor.GetStepData(ctx, game, claim, pos2)
require.NoError(t, err)
expectedPrestate, expectedProofData, expectedPreimageData, err = provider2.GetStepData(ctx, pos2)
require.NoError(t, err)
require.Equal(t, expectedPrestate, actualPrestate)
require.Equal(t, expectedProofData, actualProofData)
require.Equal(t, expectedPreimageData, actualPreimageData)
})
}
...@@ -57,6 +57,11 @@ type OracleUpdater interface { ...@@ -57,6 +57,11 @@ type OracleUpdater interface {
UpdateOracle(ctx context.Context, data *PreimageOracleData) error UpdateOracle(ctx context.Context, data *PreimageOracleData) error
} }
type TraceAccessor interface {
Get(ctx context.Context, game Game, ref Claim, pos Position) (common.Hash, error)
GetStepData(ctx context.Context, game Game, ref Claim, pos Position) (prestate []byte, proofData []byte, preimageData *PreimageOracleData, err error)
}
// TraceProvider is a generic way to get a claim value at a specific step in the trace. // TraceProvider is a generic way to get a claim value at a specific step in the trace.
type TraceProvider interface { type TraceProvider interface {
// Get returns the claim value at the requested index. // Get returns the claim value at the requested index.
......
...@@ -23,10 +23,6 @@ test-http: pre-test ...@@ -23,10 +23,6 @@ test-http: pre-test
OP_E2E_USE_HTTP=true $(go_test) $(go_test_flags) ./... OP_E2E_USE_HTTP=true $(go_test) $(go_test_flags) ./...
.PHONY: test-http .PHONY: test-http
test-span-batch: pre-test
OP_E2E_USE_SPAN_BATCH=true $(go_test) $(go_test_flags) ./...
.PHONY: test-span-batch
cannon-prestate: cannon-prestate:
make -C .. cannon-prestate make -C .. cannon-prestate
.PHONY: cannon-prestate .PHONY: cannon-prestate
......
...@@ -6,20 +6,47 @@ import ( ...@@ -6,20 +6,47 @@ import (
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// TestBatchInLastPossibleBlocks tests that the derivation pipeline // TestBlockTimeBatchType run each blocktime-related test case in singular batch mode and span batch mode.
func TestBlockTimeBatchType(t *testing.T) {
tests := []struct {
name string
f func(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64)
}{
{"BatchInLastPossibleBlocks", BatchInLastPossibleBlocks},
{"LargeL1Gaps", LargeL1Gaps},
}
for _, test := range tests {
test := test
t.Run(test.name+"_SingularBatch", func(t *testing.T) {
test.f(t, nil)
})
}
spanBatchTimeOffset := hexutil.Uint64(0)
for _, test := range tests {
test := test
t.Run(test.name+"_SpanBatch", func(t *testing.T) {
test.f(t, &spanBatchTimeOffset)
})
}
}
// BatchInLastPossibleBlocks tests that the derivation pipeline
// accepts a batch that is included in the last possible L1 block // accepts a batch that is included in the last possible L1 block
// where there are also no other batches included in the sequence // where there are also no other batches included in the sequence
// window. // window.
// This is a regression test against the bug fixed in PR #4566 // This is a regression test against the bug fixed in PR #4566
func TestBatchInLastPossibleBlocks(gt *testing.T) { func BatchInLastPossibleBlocks(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
dp.DeployConfig.L2GenesisSpanBatchTimeOffset = spanBatchTimeOffset
dp.DeployConfig.SequencerWindowSize = 4 dp.DeployConfig.SequencerWindowSize = 4
dp.DeployConfig.L2BlockTime = 2 dp.DeployConfig.L2BlockTime = 2
...@@ -116,7 +143,7 @@ func TestBatchInLastPossibleBlocks(gt *testing.T) { ...@@ -116,7 +143,7 @@ func TestBatchInLastPossibleBlocks(gt *testing.T) {
verifyChainStateOnSequencer(12, 23, 11, 17, 8) verifyChainStateOnSequencer(12, 23, 11, 17, 8)
} }
// TestLargeL1Gaps tests the case that there is a gap between two L1 blocks which // LargeL1Gaps tests the case that there is a gap between two L1 blocks which
// is larger than the sequencer drift. // is larger than the sequencer drift.
// This test has the following parameters: // This test has the following parameters:
// L1 Block time: 4s. L2 Block time: 2s. Sequencer Drift: 32s // L1 Block time: 4s. L2 Block time: 2s. Sequencer Drift: 32s
...@@ -127,13 +154,14 @@ func TestBatchInLastPossibleBlocks(gt *testing.T) { ...@@ -127,13 +154,14 @@ func TestBatchInLastPossibleBlocks(gt *testing.T) {
// Then it generates 3 more L1 blocks. // Then it generates 3 more L1 blocks.
// At this point it can verify that the batches where properly generated. // At this point it can verify that the batches where properly generated.
// Note: It batches submits when possible. // Note: It batches submits when possible.
func TestLargeL1Gaps(gt *testing.T) { func LargeL1Gaps(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
dp.DeployConfig.L1BlockTime = 4 dp.DeployConfig.L1BlockTime = 4
dp.DeployConfig.L2BlockTime = 2 dp.DeployConfig.L2BlockTime = 2
dp.DeployConfig.SequencerWindowSize = 4 dp.DeployConfig.SequencerWindowSize = 4
dp.DeployConfig.MaxSequencerDrift = 32 dp.DeployConfig.MaxSequencerDrift = 32
dp.DeployConfig.L2GenesisSpanBatchTimeOffset = spanBatchTimeOffset
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug) log := testlog.Logger(t, log.LvlDebug)
......
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
...@@ -20,7 +21,36 @@ import ( ...@@ -20,7 +21,36 @@ import (
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
) )
func TestBatcher(gt *testing.T) { // TestL2BatcherBatchType run each batcher-related test case in singular batch mode and span batch mode.
func TestL2BatcherBatchType(t *testing.T) {
tests := []struct {
name string
f func(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64)
}{
{"NormalBatcher", NormalBatcher},
{"L2Finalization", L2Finalization},
{"L2FinalizationWithSparseL1", L2FinalizationWithSparseL1},
{"GarbageBatch", GarbageBatch},
{"ExtendedTimeWithoutL1Batches", ExtendedTimeWithoutL1Batches},
{"BigL2Txs", BigL2Txs},
}
for _, test := range tests {
test := test
t.Run(test.name+"_SingularBatch", func(t *testing.T) {
test.f(t, nil)
})
}
spanBatchTimeOffset := hexutil.Uint64(0)
for _, test := range tests {
test := test
t.Run(test.name+"_SpanBatch", func(t *testing.T) {
test.f(t, &spanBatchTimeOffset)
})
}
}
func NormalBatcher(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
p := &e2eutils.TestParams{ p := &e2eutils.TestParams{
MaxSequencerDrift: 20, // larger than L1 block time we simulate in this test (12) MaxSequencerDrift: 20, // larger than L1 block time we simulate in this test (12)
...@@ -29,6 +59,7 @@ func TestBatcher(gt *testing.T) { ...@@ -29,6 +59,7 @@ func TestBatcher(gt *testing.T) {
L1BlockTime: 12, L1BlockTime: 12,
} }
dp := e2eutils.MakeDeployParams(t, p) dp := e2eutils.MakeDeployParams(t, p)
dp.DeployConfig.L2GenesisSpanBatchTimeOffset = spanBatchTimeOffset
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug) log := testlog.Logger(t, log.LvlDebug)
miner, seqEngine, sequencer := setupSequencerTest(t, sd, log) miner, seqEngine, sequencer := setupSequencerTest(t, sd, log)
...@@ -55,7 +86,7 @@ func TestBatcher(gt *testing.T) { ...@@ -55,7 +86,7 @@ func TestBatcher(gt *testing.T) {
To: &dp.Addresses.Bob, To: &dp.Addresses.Bob,
Value: e2eutils.Ether(2), Value: e2eutils.Ether(2),
}) })
require.NoError(gt, cl.SendTransaction(t.Ctx(), tx)) require.NoError(t, cl.SendTransaction(t.Ctx(), tx))
sequencer.ActL2PipelineFull(t) sequencer.ActL2PipelineFull(t)
verifier.ActL2PipelineFull(t) verifier.ActL2PipelineFull(t)
...@@ -97,9 +128,10 @@ func TestBatcher(gt *testing.T) { ...@@ -97,9 +128,10 @@ func TestBatcher(gt *testing.T) {
require.NotNil(t, vTx) require.NotNil(t, vTx)
} }
func TestL2Finalization(gt *testing.T) { func L2Finalization(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
dp.DeployConfig.L2GenesisSpanBatchTimeOffset = spanBatchTimeOffset
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug) log := testlog.Logger(t, log.LvlDebug)
miner, engine, sequencer := setupSequencerTest(t, sd, log) miner, engine, sequencer := setupSequencerTest(t, sd, log)
...@@ -202,10 +234,11 @@ func TestL2Finalization(gt *testing.T) { ...@@ -202,10 +234,11 @@ func TestL2Finalization(gt *testing.T) {
require.Equal(t, heightToSubmit, sequencer.SyncStatus().FinalizedL2.Number, "unknown/bad finalized L1 blocks are ignored") require.Equal(t, heightToSubmit, sequencer.SyncStatus().FinalizedL2.Number, "unknown/bad finalized L1 blocks are ignored")
} }
// TestL2FinalizationWithSparseL1 tests that safe L2 blocks can be finalized even if we do not regularly get a L1 finalization signal // L2FinalizationWithSparseL1 tests that safe L2 blocks can be finalized even if we do not regularly get a L1 finalization signal
func TestL2FinalizationWithSparseL1(gt *testing.T) { func L2FinalizationWithSparseL1(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
dp.DeployConfig.L2GenesisSpanBatchTimeOffset = spanBatchTimeOffset
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug) log := testlog.Logger(t, log.LvlDebug)
miner, engine, sequencer := setupSequencerTest(t, sd, log) miner, engine, sequencer := setupSequencerTest(t, sd, log)
...@@ -258,13 +291,14 @@ func TestL2FinalizationWithSparseL1(gt *testing.T) { ...@@ -258,13 +291,14 @@ func TestL2FinalizationWithSparseL1(gt *testing.T) {
require.Equal(t, finalStatus.FinalizedL2.Number, finalStatus.UnsafeL2.Number, "sequencer submitted its L2 block and it finalized") require.Equal(t, finalStatus.FinalizedL2.Number, finalStatus.UnsafeL2.Number, "sequencer submitted its L2 block and it finalized")
} }
// TestGarbageBatch tests the behavior of an invalid/malformed output channel frame containing // GarbageBatch tests the behavior of an invalid/malformed output channel frame containing
// valid batches being submitted to the batch inbox. These batches should always be rejected // valid batches being submitted to the batch inbox. These batches should always be rejected
// and the safe L2 head should remain unaltered. // and the safe L2 head should remain unaltered.
func TestGarbageBatch(gt *testing.T) { func GarbageBatch(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
p := defaultRollupTestParams p := defaultRollupTestParams
dp := e2eutils.MakeDeployParams(t, p) dp := e2eutils.MakeDeployParams(t, p)
dp.DeployConfig.L2GenesisSpanBatchTimeOffset = spanBatchTimeOffset
for _, garbageKind := range GarbageKinds { for _, garbageKind := range GarbageKinds {
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlError) log := testlog.Logger(t, log.LvlError)
...@@ -340,7 +374,7 @@ func TestGarbageBatch(gt *testing.T) { ...@@ -340,7 +374,7 @@ func TestGarbageBatch(gt *testing.T) {
} }
} }
func TestExtendedTimeWithoutL1Batches(gt *testing.T) { func ExtendedTimeWithoutL1Batches(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
p := &e2eutils.TestParams{ p := &e2eutils.TestParams{
MaxSequencerDrift: 20, // larger than L1 block time we simulate in this test (12) MaxSequencerDrift: 20, // larger than L1 block time we simulate in this test (12)
...@@ -349,6 +383,7 @@ func TestExtendedTimeWithoutL1Batches(gt *testing.T) { ...@@ -349,6 +383,7 @@ func TestExtendedTimeWithoutL1Batches(gt *testing.T) {
L1BlockTime: 12, L1BlockTime: 12,
} }
dp := e2eutils.MakeDeployParams(t, p) dp := e2eutils.MakeDeployParams(t, p)
dp.DeployConfig.L2GenesisSpanBatchTimeOffset = spanBatchTimeOffset
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlError) log := testlog.Logger(t, log.LvlError)
miner, engine, sequencer := setupSequencerTest(t, sd, log) miner, engine, sequencer := setupSequencerTest(t, sd, log)
...@@ -386,7 +421,7 @@ func TestExtendedTimeWithoutL1Batches(gt *testing.T) { ...@@ -386,7 +421,7 @@ func TestExtendedTimeWithoutL1Batches(gt *testing.T) {
require.Equal(t, sequencer.L2Unsafe(), sequencer.L2Safe(), "same for sequencer") require.Equal(t, sequencer.L2Unsafe(), sequencer.L2Safe(), "same for sequencer")
} }
// TestBigL2Txs tests a high-throughput case with constrained batcher: // BigL2Txs tests a high-throughput case with constrained batcher:
// - Fill 40 L2 blocks to near max-capacity, with txs of 120 KB each // - Fill 40 L2 blocks to near max-capacity, with txs of 120 KB each
// - Buffer the L2 blocks into channels together as much as possible, submit data-txs only when necessary // - Buffer the L2 blocks into channels together as much as possible, submit data-txs only when necessary
// (just before crossing the max RLP channel size) // (just before crossing the max RLP channel size)
...@@ -398,7 +433,7 @@ func TestExtendedTimeWithoutL1Batches(gt *testing.T) { ...@@ -398,7 +433,7 @@ func TestExtendedTimeWithoutL1Batches(gt *testing.T) {
// The goal of this test is to quickly run through an otherwise very slow process of submitting and including lots of data. // The goal of this test is to quickly run through an otherwise very slow process of submitting and including lots of data.
// This does not test the batcher code, but is really focused at testing the batcher utils // This does not test the batcher code, but is really focused at testing the batcher utils
// and channel-decoding verifier code in the derive package. // and channel-decoding verifier code in the derive package.
func TestBigL2Txs(gt *testing.T) { func BigL2Txs(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
p := &e2eutils.TestParams{ p := &e2eutils.TestParams{
MaxSequencerDrift: 100, MaxSequencerDrift: 100,
...@@ -407,6 +442,7 @@ func TestBigL2Txs(gt *testing.T) { ...@@ -407,6 +442,7 @@ func TestBigL2Txs(gt *testing.T) {
L1BlockTime: 12, L1BlockTime: 12,
} }
dp := e2eutils.MakeDeployParams(t, p) dp := e2eutils.MakeDeployParams(t, p)
dp.DeployConfig.L2GenesisSpanBatchTimeOffset = spanBatchTimeOffset
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlInfo) log := testlog.Logger(t, log.LvlInfo)
miner, engine, sequencer := setupSequencerTest(t, sd, log) miner, engine, sequencer := setupSequencerTest(t, sd, log)
...@@ -464,7 +500,7 @@ func TestBigL2Txs(gt *testing.T) { ...@@ -464,7 +500,7 @@ func TestBigL2Txs(gt *testing.T) {
Value: big.NewInt(0), Value: big.NewInt(0),
Data: data, Data: data,
}) })
require.NoError(gt, cl.SendTransaction(t.Ctx(), tx)) require.NoError(t, cl.SendTransaction(t.Ctx(), tx))
engine.ActL2IncludeTx(dp.Addresses.Alice)(t) engine.ActL2IncludeTx(dp.Addresses.Alice)(t)
} }
sequencer.ActL2EndBlock(t) sequencer.ActL2EndBlock(t)
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -14,9 +15,34 @@ import ( ...@@ -14,9 +15,34 @@ import (
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
) )
func TestProposer(gt *testing.T) { // TestProposerBatchType run each proposer-related test case in singular batch mode and span batch mode.
func TestProposerBatchType(t *testing.T) {
tests := []struct {
name string
f func(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64)
}{
{"RunProposerTest", RunProposerTest},
}
for _, test := range tests {
test := test
t.Run(test.name+"_SingularBatch", func(t *testing.T) {
test.f(t, nil)
})
}
spanBatchTimeOffset := hexutil.Uint64(0)
for _, test := range tests {
test := test
t.Run(test.name+"_SpanBatch", func(t *testing.T) {
test.f(t, &spanBatchTimeOffset)
})
}
}
func RunProposerTest(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
dp.DeployConfig.L2GenesisSpanBatchTimeOffset = spanBatchTimeOffset
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug) log := testlog.Logger(t, log.LvlDebug)
miner, seqEngine, sequencer := setupSequencerTest(t, sd, log) miner, seqEngine, sequencer := setupSequencerTest(t, sd, log)
......
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "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/core/types"
"github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
...@@ -21,8 +22,9 @@ import ( ...@@ -21,8 +22,9 @@ import (
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
) )
func setupReorgTest(t Testing, config *e2eutils.TestParams) (*e2eutils.SetupData, *e2eutils.DeployParams, *L1Miner, *L2Sequencer, *L2Engine, *L2Verifier, *L2Engine, *L2Batcher) { func setupReorgTest(t Testing, config *e2eutils.TestParams, spanBatchTimeOffset *hexutil.Uint64) (*e2eutils.SetupData, *e2eutils.DeployParams, *L1Miner, *L2Sequencer, *L2Engine, *L2Verifier, *L2Engine, *L2Batcher) {
dp := e2eutils.MakeDeployParams(t, config) dp := e2eutils.MakeDeployParams(t, config)
dp.DeployConfig.L2GenesisSpanBatchTimeOffset = spanBatchTimeOffset
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug) log := testlog.Logger(t, log.LvlDebug)
...@@ -44,9 +46,38 @@ func setupReorgTestActors(t Testing, dp *e2eutils.DeployParams, sd *e2eutils.Set ...@@ -44,9 +46,38 @@ func setupReorgTestActors(t Testing, dp *e2eutils.DeployParams, sd *e2eutils.Set
return sd, dp, miner, sequencer, seqEngine, verifier, verifEngine, batcher return sd, dp, miner, sequencer, seqEngine, verifier, verifEngine, batcher
} }
func TestReorgOrphanBlock(gt *testing.T) { // TestReorgBatchType run each reorg-related test case in singular batch mode and span batch mode.
func TestReorgBatchType(t *testing.T) {
tests := []struct {
name string
f func(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64)
}{
{"ReorgOrphanBlock", ReorgOrphanBlock},
{"ReorgFlipFlop", ReorgFlipFlop},
{"DeepReorg", DeepReorg},
{"RestartOpGeth", RestartOpGeth},
{"ConflictingL2Blocks", ConflictingL2Blocks},
{"SyncAfterReorg", SyncAfterReorg},
}
for _, test := range tests {
test := test
t.Run(test.name+"_SingularBatch", func(t *testing.T) {
test.f(t, nil)
})
}
spanBatchTimeOffset := hexutil.Uint64(0)
for _, test := range tests {
test := test
t.Run(test.name+"_SpanBatch", func(t *testing.T) {
test.f(t, &spanBatchTimeOffset)
})
}
}
func ReorgOrphanBlock(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
sd, _, miner, sequencer, _, verifier, verifierEng, batcher := setupReorgTest(t, defaultRollupTestParams) sd, _, miner, sequencer, _, verifier, verifierEng, batcher := setupReorgTest(t, defaultRollupTestParams, spanBatchTimeOffset)
verifEngClient := verifierEng.EngineClient(t, sd.RollupCfg) verifEngClient := verifierEng.EngineClient(t, sd.RollupCfg)
sequencer.ActL2PipelineFull(t) sequencer.ActL2PipelineFull(t)
...@@ -112,9 +143,9 @@ func TestReorgOrphanBlock(gt *testing.T) { ...@@ -112,9 +143,9 @@ func TestReorgOrphanBlock(gt *testing.T) {
require.Equal(t, verifier.L2Safe(), sequencer.L2Safe(), "verifier and sequencer see same safe L2 block, while only verifier dealt with the orphan and replay") require.Equal(t, verifier.L2Safe(), sequencer.L2Safe(), "verifier and sequencer see same safe L2 block, while only verifier dealt with the orphan and replay")
} }
func TestReorgFlipFlop(gt *testing.T) { func ReorgFlipFlop(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
sd, _, miner, sequencer, _, verifier, verifierEng, batcher := setupReorgTest(t, defaultRollupTestParams) sd, _, miner, sequencer, _, verifier, verifierEng, batcher := setupReorgTest(t, defaultRollupTestParams, spanBatchTimeOffset)
minerCl := miner.L1Client(t, sd.RollupCfg) minerCl := miner.L1Client(t, sd.RollupCfg)
verifEngClient := verifierEng.EngineClient(t, sd.RollupCfg) verifEngClient := verifierEng.EngineClient(t, sd.RollupCfg)
checkVerifEngine := func() { checkVerifEngine := func() {
...@@ -333,7 +364,7 @@ func TestReorgFlipFlop(gt *testing.T) { ...@@ -333,7 +364,7 @@ func TestReorgFlipFlop(gt *testing.T) {
// Verifier // Verifier
// - Unsafe head is 62 // - Unsafe head is 62
// - Safe head is 42 // - Safe head is 42
func TestDeepReorg(gt *testing.T) { func DeepReorg(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
// Create actor and verification engine client // Create actor and verification engine client
...@@ -342,7 +373,7 @@ func TestDeepReorg(gt *testing.T) { ...@@ -342,7 +373,7 @@ func TestDeepReorg(gt *testing.T) {
SequencerWindowSize: 20, SequencerWindowSize: 20,
ChannelTimeout: 120, ChannelTimeout: 120,
L1BlockTime: 4, L1BlockTime: 4,
}) }, spanBatchTimeOffset)
minerCl := miner.L1Client(t, sd.RollupCfg) minerCl := miner.L1Client(t, sd.RollupCfg)
l2Client := seqEngine.EthClient() l2Client := seqEngine.EthClient()
verifEngClient := verifierEng.EngineClient(t, sd.RollupCfg) verifEngClient := verifierEng.EngineClient(t, sd.RollupCfg)
...@@ -566,9 +597,9 @@ type rpcWrapper struct { ...@@ -566,9 +597,9 @@ type rpcWrapper struct {
client.RPC client.RPC
} }
// TestRestartOpGeth tests that the sequencer can restart its execution engine without rollup-node restart, // RestartOpGeth tests that the sequencer can restart its execution engine without rollup-node restart,
// including recovering the finalized/safe state of L2 chain without reorging. // including recovering the finalized/safe state of L2 chain without reorging.
func TestRestartOpGeth(gt *testing.T) { func RestartOpGeth(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
dbPath := path.Join(t.TempDir(), "testdb") dbPath := path.Join(t.TempDir(), "testdb")
dbOption := func(_ *ethconfig.Config, nodeCfg *node.Config) error { dbOption := func(_ *ethconfig.Config, nodeCfg *node.Config) error {
...@@ -576,6 +607,7 @@ func TestRestartOpGeth(gt *testing.T) { ...@@ -576,6 +607,7 @@ func TestRestartOpGeth(gt *testing.T) {
return nil return nil
} }
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
dp.DeployConfig.L2GenesisSpanBatchTimeOffset = spanBatchTimeOffset
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug) log := testlog.Logger(t, log.LvlDebug)
jwtPath := e2eutils.WriteDefaultJWT(t) jwtPath := e2eutils.WriteDefaultJWT(t)
...@@ -660,12 +692,13 @@ func TestRestartOpGeth(gt *testing.T) { ...@@ -660,12 +692,13 @@ func TestRestartOpGeth(gt *testing.T) {
require.Equal(t, statusBeforeRestart.SafeL2, sequencer.L2Safe(), "expecting the safe block to catch up to what it was before shutdown after syncing from L1, and not be stuck at the finalized block") require.Equal(t, statusBeforeRestart.SafeL2, sequencer.L2Safe(), "expecting the safe block to catch up to what it was before shutdown after syncing from L1, and not be stuck at the finalized block")
} }
// TestConflictingL2Blocks tests that a second copy of the sequencer stack cannot introduce an alternative // ConflictingL2Blocks tests that a second copy of the sequencer stack cannot introduce an alternative
// L2 block (compared to something already secured by the first sequencer): // L2 block (compared to something already secured by the first sequencer):
// the alt block is not synced by the verifier, in unsafe and safe sync modes. // the alt block is not synced by the verifier, in unsafe and safe sync modes.
func TestConflictingL2Blocks(gt *testing.T) { func ConflictingL2Blocks(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
dp.DeployConfig.L2GenesisSpanBatchTimeOffset = spanBatchTimeOffset
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug) log := testlog.Logger(t, log.LvlDebug)
...@@ -772,7 +805,7 @@ func TestConflictingL2Blocks(gt *testing.T) { ...@@ -772,7 +805,7 @@ func TestConflictingL2Blocks(gt *testing.T) {
require.Equal(t, sequencer.L2Unsafe(), altSequencer.L2Unsafe(), "and gets back in harmony with original sequencer") require.Equal(t, sequencer.L2Unsafe(), altSequencer.L2Unsafe(), "and gets back in harmony with original sequencer")
} }
func TestSyncAfterReorg(gt *testing.T) { func SyncAfterReorg(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
testingParams := e2eutils.TestParams{ testingParams := e2eutils.TestParams{
MaxSequencerDrift: 60, MaxSequencerDrift: 60,
...@@ -780,7 +813,7 @@ func TestSyncAfterReorg(gt *testing.T) { ...@@ -780,7 +813,7 @@ func TestSyncAfterReorg(gt *testing.T) {
ChannelTimeout: 2, ChannelTimeout: 2,
L1BlockTime: 12, L1BlockTime: 12,
} }
sd, dp, miner, sequencer, seqEngine, verifier, _, batcher := setupReorgTest(t, &testingParams) sd, dp, miner, sequencer, seqEngine, verifier, _, batcher := setupReorgTest(t, &testingParams, spanBatchTimeOffset)
l2Client := seqEngine.EthClient() l2Client := seqEngine.EthClient()
log := testlog.Logger(t, log.LvlDebug) log := testlog.Logger(t, log.LvlDebug)
addresses := e2eutils.CollectAddresses(sd, dp) addresses := e2eutils.CollectAddresses(sd, dp)
......
...@@ -23,9 +23,35 @@ import ( ...@@ -23,9 +23,35 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestDerivationWithFlakyL1RPC(gt *testing.T) { // TestSyncBatchType run each sync test case in singular batch mode and span batch mode.
func TestSyncBatchType(t *testing.T) {
tests := []struct {
name string
f func(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64)
}{
{"DerivationWithFlakyL1RPC", DerivationWithFlakyL1RPC},
{"FinalizeWhileSyncing", FinalizeWhileSyncing},
}
for _, test := range tests {
test := test
t.Run(test.name+"_SingularBatch", func(t *testing.T) {
test.f(t, nil)
})
}
spanBatchTimeOffset := hexutil.Uint64(0)
for _, test := range tests {
test := test
t.Run(test.name+"_SpanBatch", func(t *testing.T) {
test.f(t, &spanBatchTimeOffset)
})
}
}
func DerivationWithFlakyL1RPC(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
dp.DeployConfig.L2GenesisSpanBatchTimeOffset = spanBatchTimeOffset
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlError) // mute all the temporary derivation errors that we forcefully create log := testlog.Logger(t, log.LvlError) // mute all the temporary derivation errors that we forcefully create
_, _, miner, sequencer, _, verifier, _, batcher := setupReorgTestActors(t, dp, sd, log) _, _, miner, sequencer, _, verifier, _, batcher := setupReorgTestActors(t, dp, sd, log)
...@@ -62,9 +88,10 @@ func TestDerivationWithFlakyL1RPC(gt *testing.T) { ...@@ -62,9 +88,10 @@ func TestDerivationWithFlakyL1RPC(gt *testing.T) {
require.Equal(t, sequencer.L2Unsafe(), verifier.L2Safe(), "verifier is synced") require.Equal(t, sequencer.L2Unsafe(), verifier.L2Safe(), "verifier is synced")
} }
func TestFinalizeWhileSyncing(gt *testing.T) { func FinalizeWhileSyncing(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
dp.DeployConfig.L2GenesisSpanBatchTimeOffset = spanBatchTimeOffset
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlError) // mute all the temporary derivation errors that we forcefully create log := testlog.Logger(t, log.LvlError) // mute all the temporary derivation errors that we forcefully create
_, _, miner, sequencer, _, verifier, _, batcher := setupReorgTestActors(t, dp, sd, log) _, _, miner, sequencer, _, verifier, _, batcher := setupReorgTestActors(t, dp, sd, log)
......
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "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/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -19,13 +20,40 @@ import ( ...@@ -19,13 +20,40 @@ import (
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
) )
// TestBatcherKeyRotation tests that batcher A can operate, then be replaced with batcher B, then ignore old batcher A, // TestSystemConfigBatchType run each system config-related test case in singular batch mode and span batch mode.
func TestSystemConfigBatchType(t *testing.T) {
tests := []struct {
name string
f func(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64)
}{
{"BatcherKeyRotation", BatcherKeyRotation},
{"GPOParamsChange", GPOParamsChange},
{"GasLimitChange", GasLimitChange},
}
for _, test := range tests {
test := test
t.Run(test.name+"_SingularBatch", func(t *testing.T) {
test.f(t, nil)
})
}
spanBatchTimeOffset := hexutil.Uint64(0)
for _, test := range tests {
test := test
t.Run(test.name+"_SpanBatch", func(t *testing.T) {
test.f(t, &spanBatchTimeOffset)
})
}
}
// BatcherKeyRotation tests that batcher A can operate, then be replaced with batcher B, then ignore old batcher A,
// and that the change to batcher B is reverted properly upon reorg of L1. // and that the change to batcher B is reverted properly upon reorg of L1.
func TestBatcherKeyRotation(gt *testing.T) { func BatcherKeyRotation(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
dp.DeployConfig.L2BlockTime = 2 dp.DeployConfig.L2BlockTime = 2
dp.DeployConfig.L2GenesisSpanBatchTimeOffset = spanBatchTimeOffset
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug) log := testlog.Logger(t, log.LvlDebug)
miner, seqEngine, sequencer := setupSequencerTest(t, sd, log) miner, seqEngine, sequencer := setupSequencerTest(t, sd, log)
...@@ -198,11 +226,12 @@ func TestBatcherKeyRotation(gt *testing.T) { ...@@ -198,11 +226,12 @@ func TestBatcherKeyRotation(gt *testing.T) {
require.Equal(t, sequencer.L2Unsafe(), verifier.L2Unsafe(), "verifier synced") require.Equal(t, sequencer.L2Unsafe(), verifier.L2Unsafe(), "verifier synced")
} }
// TestGPOParamsChange tests that the GPO params can be updated to adjust fees of L2 transactions, // GPOParamsChange tests that the GPO params can be updated to adjust fees of L2 transactions,
// and that the L1 data fees to the L2 transaction are applied correctly before, during and after the GPO update in L2. // and that the L1 data fees to the L2 transaction are applied correctly before, during and after the GPO update in L2.
func TestGPOParamsChange(gt *testing.T) { func GPOParamsChange(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
dp.DeployConfig.L2GenesisSpanBatchTimeOffset = spanBatchTimeOffset
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug) log := testlog.Logger(t, log.LvlDebug)
miner, seqEngine, sequencer := setupSequencerTest(t, sd, log) miner, seqEngine, sequencer := setupSequencerTest(t, sd, log)
...@@ -326,12 +355,13 @@ func TestGPOParamsChange(gt *testing.T) { ...@@ -326,12 +355,13 @@ func TestGPOParamsChange(gt *testing.T) {
require.Equal(t, "2.3", receipt.FeeScalar.String(), "2_300_000 divided by 6 decimals = float(2.3)") require.Equal(t, "2.3", receipt.FeeScalar.String(), "2_300_000 divided by 6 decimals = float(2.3)")
} }
// TestGasLimitChange tests that the gas limit can be configured to L1, // GasLimitChange tests that the gas limit can be configured to L1,
// and that the L2 changes the gas limit instantly at the exact block that adopts the L1 origin with // and that the L2 changes the gas limit instantly at the exact block that adopts the L1 origin with
// the gas limit change event. And checks if a verifier node can reproduce the same gas limit change. // the gas limit change event. And checks if a verifier node can reproduce the same gas limit change.
func TestGasLimitChange(gt *testing.T) { func GasLimitChange(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
dp.DeployConfig.L2GenesisSpanBatchTimeOffset = spanBatchTimeOffset
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug) log := testlog.Logger(t, log.LvlDebug)
miner, seqEngine, sequencer := setupSequencerTest(t, sd, log) miner, seqEngine, sequencer := setupSequencerTest(t, sd, log)
......
...@@ -13,10 +13,12 @@ import ( ...@@ -13,10 +13,12 @@ import (
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
) )
type regolithScheduledTest struct { type hardforkScheduledTest struct {
name string name string
regolithTime *hexutil.Uint64 regolithTime *hexutil.Uint64
spanBatchTime *hexutil.Uint64
activateRegolith bool activateRegolith bool
activateSpanBatch bool
} }
// TestCrossLayerUser tests that common actions of the CrossLayerUser actor work in various regolith configurations: // TestCrossLayerUser tests that common actions of the CrossLayerUser actor work in various regolith configurations:
...@@ -31,11 +33,18 @@ func TestCrossLayerUser(t *testing.T) { ...@@ -31,11 +33,18 @@ func TestCrossLayerUser(t *testing.T) {
zeroTime := hexutil.Uint64(0) zeroTime := hexutil.Uint64(0)
futureTime := hexutil.Uint64(20) futureTime := hexutil.Uint64(20)
farFutureTime := hexutil.Uint64(2000) farFutureTime := hexutil.Uint64(2000)
tests := []regolithScheduledTest{ tests := []hardforkScheduledTest{
{name: "NoRegolith", regolithTime: nil, activateRegolith: false}, {name: "NoRegolith", regolithTime: nil, activateRegolith: false, spanBatchTime: nil, activateSpanBatch: false},
{name: "NotYetRegolith", regolithTime: &farFutureTime, activateRegolith: false}, {name: "NotYetRegolith", regolithTime: &farFutureTime, activateRegolith: false, spanBatchTime: nil, activateSpanBatch: false},
{name: "RegolithAtGenesis", regolithTime: &zeroTime, activateRegolith: true}, {name: "RegolithAtGenesis", regolithTime: &zeroTime, activateRegolith: true, spanBatchTime: nil, activateSpanBatch: false},
{name: "RegolithAfterGenesis", regolithTime: &futureTime, activateRegolith: true}, {name: "RegolithAfterGenesis", regolithTime: &futureTime, activateRegolith: true, spanBatchTime: nil, activateSpanBatch: false},
{name: "NoSpanBatch", regolithTime: &zeroTime, activateRegolith: true, spanBatchTime: nil, activateSpanBatch: false},
{name: "NotYetSpanBatch", regolithTime: &zeroTime, activateRegolith: true,
spanBatchTime: &farFutureTime, activateSpanBatch: false},
{name: "SpanBatchAtGenesis", regolithTime: &zeroTime, activateRegolith: true,
spanBatchTime: &zeroTime, activateSpanBatch: true},
{name: "SpanBatchAfterGenesis", regolithTime: &zeroTime, activateRegolith: true,
spanBatchTime: &futureTime, activateSpanBatch: true},
} }
for _, test := range tests { for _, test := range tests {
test := test // Use a fixed reference as the tests run in parallel test := test // Use a fixed reference as the tests run in parallel
...@@ -45,10 +54,11 @@ func TestCrossLayerUser(t *testing.T) { ...@@ -45,10 +54,11 @@ func TestCrossLayerUser(t *testing.T) {
} }
} }
func runCrossLayerUserTest(gt *testing.T, test regolithScheduledTest) { func runCrossLayerUserTest(gt *testing.T, test hardforkScheduledTest) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
dp.DeployConfig.L2GenesisRegolithTimeOffset = test.regolithTime dp.DeployConfig.L2GenesisRegolithTimeOffset = test.regolithTime
dp.DeployConfig.L2GenesisSpanBatchTimeOffset = test.spanBatchTime
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug) log := testlog.Logger(t, log.LvlDebug)
......
...@@ -59,7 +59,6 @@ func MakeDeployParams(t require.TestingT, tp *TestParams) *DeployParams { ...@@ -59,7 +59,6 @@ func MakeDeployParams(t require.TestingT, tp *TestParams) *DeployParams {
deployConfig.L1BlockTime = tp.L1BlockTime deployConfig.L1BlockTime = tp.L1BlockTime
deployConfig.L2GenesisRegolithTimeOffset = nil deployConfig.L2GenesisRegolithTimeOffset = nil
deployConfig.L2GenesisCanyonTimeOffset = CanyonTimeOffset() deployConfig.L2GenesisCanyonTimeOffset = CanyonTimeOffset()
deployConfig.L2GenesisSpanBatchTimeOffset = SpanBatchTimeOffset()
require.NoError(t, deployConfig.Check()) require.NoError(t, deployConfig.Check())
require.Equal(t, addresses.Batcher, deployConfig.BatchSenderAddress) require.Equal(t, addresses.Batcher, deployConfig.BatchSenderAddress)
...@@ -186,14 +185,6 @@ func SystemConfigFromDeployConfig(deployConfig *genesis.DeployConfig) eth.System ...@@ -186,14 +185,6 @@ func SystemConfigFromDeployConfig(deployConfig *genesis.DeployConfig) eth.System
} }
} }
func SpanBatchTimeOffset() *hexutil.Uint64 {
if os.Getenv("OP_E2E_USE_SPAN_BATCH") == "true" {
offset := hexutil.Uint64(0)
return &offset
}
return nil
}
func CanyonTimeOffset() *hexutil.Uint64 { func CanyonTimeOffset() *hexutil.Uint64 {
if os.Getenv("OP_E2E_USE_CANYON") == "true" { if os.Getenv("OP_E2E_USE_CANYON") == "true" {
offset := hexutil.Uint64(0) offset := hexutil.Uint64(0)
......
...@@ -88,7 +88,6 @@ func DefaultSystemConfig(t *testing.T) SystemConfig { ...@@ -88,7 +88,6 @@ func DefaultSystemConfig(t *testing.T) SystemConfig {
deployConfig := config.DeployConfig.Copy() deployConfig := config.DeployConfig.Copy()
deployConfig.L1GenesisBlockTimestamp = hexutil.Uint64(time.Now().Unix()) deployConfig.L1GenesisBlockTimestamp = hexutil.Uint64(time.Now().Unix())
deployConfig.L2GenesisCanyonTimeOffset = e2eutils.CanyonTimeOffset() deployConfig.L2GenesisCanyonTimeOffset = e2eutils.CanyonTimeOffset()
deployConfig.L2GenesisSpanBatchTimeOffset = e2eutils.SpanBatchTimeOffset()
require.NoError(t, deployConfig.Check(), "Deploy config is invalid, do you need to run make devnet-allocs?") require.NoError(t, deployConfig.Check(), "Deploy config is invalid, do you need to run make devnet-allocs?")
l1Deployments := config.L1Deployments.Copy() l1Deployments := config.L1Deployments.Copy()
require.NoError(t, l1Deployments.Check()) require.NoError(t, l1Deployments.Check())
...@@ -684,7 +683,7 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste ...@@ -684,7 +683,7 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste
} }
var batchType uint = derive.SingularBatchType var batchType uint = derive.SingularBatchType
if os.Getenv("OP_E2E_USE_SPAN_BATCH") == "true" { if cfg.DeployConfig.L2GenesisSpanBatchTimeOffset != nil && *cfg.DeployConfig.L2GenesisSpanBatchTimeOffset == hexutil.Uint64(0) {
batchType = derive.SpanBatchType batchType = derive.SpanBatchType
} }
batcherMaxL1TxSizeBytes := cfg.BatcherMaxL1TxSizeBytes batcherMaxL1TxSizeBytes := cfg.BatcherMaxL1TxSizeBytes
......
...@@ -15,25 +15,42 @@ import ( ...@@ -15,25 +15,42 @@ import (
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestVerifyL2OutputRoot(t *testing.T) { func TestVerifyL2OutputRoot(t *testing.T) {
testVerifyL2OutputRoot(t, false) testVerifyL2OutputRoot(t, false, false)
}
func TestVerifyL2OutputRootSpanBatch(t *testing.T) {
testVerifyL2OutputRoot(t, false, true)
} }
func TestVerifyL2OutputRootDetached(t *testing.T) { func TestVerifyL2OutputRootDetached(t *testing.T) {
testVerifyL2OutputRoot(t, true) testVerifyL2OutputRoot(t, true, false)
}
func TestVerifyL2OutputRootDetachedSpanBatch(t *testing.T) {
testVerifyL2OutputRoot(t, true, true)
} }
func TestVerifyL2OutputRootEmptyBlock(t *testing.T) { func TestVerifyL2OutputRootEmptyBlock(t *testing.T) {
testVerifyL2OutputRootEmptyBlock(t, false) testVerifyL2OutputRootEmptyBlock(t, false, false)
}
func TestVerifyL2OutputRootEmptyBlockSpanBatch(t *testing.T) {
testVerifyL2OutputRootEmptyBlock(t, false, true)
} }
func TestVerifyL2OutputRootEmptyBlockDetached(t *testing.T) { func TestVerifyL2OutputRootEmptyBlockDetached(t *testing.T) {
testVerifyL2OutputRootEmptyBlock(t, true) testVerifyL2OutputRootEmptyBlock(t, true, false)
}
func TestVerifyL2OutputRootEmptyBlockDetachedSpanBatch(t *testing.T) {
testVerifyL2OutputRootEmptyBlock(t, true, true)
} }
// TestVerifyL2OutputRootEmptyBlock asserts that the program can verify the output root of an empty block // TestVerifyL2OutputRootEmptyBlock asserts that the program can verify the output root of an empty block
...@@ -46,7 +63,7 @@ func TestVerifyL2OutputRootEmptyBlockDetached(t *testing.T) { ...@@ -46,7 +63,7 @@ func TestVerifyL2OutputRootEmptyBlockDetached(t *testing.T) {
// - reboot the batch submitter // - reboot the batch submitter
// - update the state root via a tx // - update the state root via a tx
// - run program // - run program
func testVerifyL2OutputRootEmptyBlock(t *testing.T, detached bool) { func testVerifyL2OutputRootEmptyBlock(t *testing.T, detached bool, spanBatchActivated bool) {
InitParallel(t) InitParallel(t)
ctx := context.Background() ctx := context.Background()
...@@ -56,6 +73,13 @@ func testVerifyL2OutputRootEmptyBlock(t *testing.T, detached bool) { ...@@ -56,6 +73,13 @@ func testVerifyL2OutputRootEmptyBlock(t *testing.T, detached bool) {
// Use a small sequencer window size to avoid test timeout while waiting for empty blocks // Use a small sequencer window size to avoid test timeout while waiting for empty blocks
// But not too small to ensure that our claim and subsequent state change is published // But not too small to ensure that our claim and subsequent state change is published
cfg.DeployConfig.SequencerWindowSize = 16 cfg.DeployConfig.SequencerWindowSize = 16
if spanBatchActivated {
// Activate span batch hard fork
minTs := hexutil.Uint64(0)
cfg.DeployConfig.L2GenesisSpanBatchTimeOffset = &minTs
} else {
cfg.DeployConfig.L2GenesisSpanBatchTimeOffset = nil
}
sys, err := cfg.Start(t) sys, err := cfg.Start(t)
require.Nil(t, err, "Error starting up system") require.Nil(t, err, "Error starting up system")
...@@ -147,13 +171,20 @@ func testVerifyL2OutputRootEmptyBlock(t *testing.T, detached bool) { ...@@ -147,13 +171,20 @@ func testVerifyL2OutputRootEmptyBlock(t *testing.T, detached bool) {
}) })
} }
func testVerifyL2OutputRoot(t *testing.T, detached bool) { func testVerifyL2OutputRoot(t *testing.T, detached bool, spanBatchActivated bool) {
InitParallel(t) InitParallel(t)
ctx := context.Background() ctx := context.Background()
cfg := DefaultSystemConfig(t) cfg := DefaultSystemConfig(t)
// We don't need a verifier - just the sequencer is enough // We don't need a verifier - just the sequencer is enough
delete(cfg.Nodes, "verifier") delete(cfg.Nodes, "verifier")
if spanBatchActivated {
// Activate span batch hard fork
minTs := hexutil.Uint64(0)
cfg.DeployConfig.L2GenesisSpanBatchTimeOffset = &minTs
} else {
cfg.DeployConfig.L2GenesisSpanBatchTimeOffset = nil
}
sys, err := cfg.Start(t) sys, err := cfg.Start(t)
require.Nil(t, err, "Error starting up system") require.Nil(t, err, "Error starting up system")
......
...@@ -45,6 +45,31 @@ import ( ...@@ -45,6 +45,31 @@ import (
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
) )
// TestSystemBatchType run each system e2e test case in singular batch mode and span batch mode.
// If the test case tests batch submission and advancing safe head, it should be tested in both singular and span batch mode.
func TestSystemBatchType(t *testing.T) {
tests := []struct {
name string
f func(gt *testing.T, spanBatchTimeOffset *hexutil.Uint64)
}{
{"StopStartBatcher", StopStartBatcher},
}
for _, test := range tests {
test := test
t.Run(test.name+"_SingularBatch", func(t *testing.T) {
test.f(t, nil)
})
}
spanBatchTimeOffset := hexutil.Uint64(0)
for _, test := range tests {
test := test
t.Run(test.name+"_SpanBatch", func(t *testing.T) {
test.f(t, &spanBatchTimeOffset)
})
}
}
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
if config.ExternalL2Shim != "" { if config.ExternalL2Shim != "" {
fmt.Println("Running tests with external L2 process adapter at ", config.ExternalL2Shim) fmt.Println("Running tests with external L2 process adapter at ", config.ExternalL2Shim)
...@@ -1222,10 +1247,11 @@ func TestFees(t *testing.T) { ...@@ -1222,10 +1247,11 @@ func TestFees(t *testing.T) {
require.Equal(t, balanceDiff, totalFee, "balances should add up") require.Equal(t, balanceDiff, totalFee, "balances should add up")
} }
func TestStopStartBatcher(t *testing.T) { func StopStartBatcher(t *testing.T, spanBatchTimeOffset *hexutil.Uint64) {
InitParallel(t) InitParallel(t)
cfg := DefaultSystemConfig(t) cfg := DefaultSystemConfig(t)
cfg.DeployConfig.L2GenesisSpanBatchTimeOffset = spanBatchTimeOffset
sys, err := cfg.Start(t) sys, err := cfg.Start(t)
require.Nil(t, err, "Error starting up system") require.Nil(t, err, "Error starting up system")
defer sys.Close() defer sys.Close()
......
...@@ -326,6 +326,12 @@ func BuildBlocksValidator(log log.Logger, cfg *rollup.Config, runCfg GossipRunti ...@@ -326,6 +326,12 @@ func BuildBlocksValidator(log log.Logger, cfg *rollup.Config, runCfg GossipRunti
return pubsub.ValidationReject return pubsub.ValidationReject
} }
// [REJECT] if a V2 Block has non-empty withdrawals
if blockVersion == eth.BlockV2 && len(*payload.Withdrawals) != 0 {
log.Warn("payload is on v2 topic, but has non-empty withdrawals", "bad_hash", payload.BlockHash.String(), "withdrawal_count", len(*payload.Withdrawals))
return pubsub.ValidationReject
}
seen, ok := blockHeightLRU.Get(uint64(payload.BlockNumber)) seen, ok := blockHeightLRU.Get(uint64(payload.BlockNumber))
if !ok { if !ok {
seen = new(seenBlocks) seen = new(seenBlocks)
......
package p2p package p2p
import ( import (
"bytes"
"context" "context"
"fmt"
"math/big" "math/big"
"testing" "testing"
"time"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/golang/snappy"
// "github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/testutils" "github.com/ethereum-optimism/optimism/op-service/testutils"
"github.com/ethereum/go-ethereum/common" "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/crypto"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
pubsub "github.com/libp2p/go-libp2p-pubsub" pubsub "github.com/libp2p/go-libp2p-pubsub"
pubsub_pb "github.com/libp2p/go-libp2p-pubsub/pb"
"github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peer"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -89,3 +99,65 @@ func TestVerifyBlockSignature(t *testing.T) { ...@@ -89,3 +99,65 @@ func TestVerifyBlockSignature(t *testing.T) {
require.Equal(t, pubsub.ValidationIgnore, result) require.Equal(t, pubsub.ValidationIgnore, result)
}) })
} }
func createSignedP2Payload(payload *eth.ExecutionPayload, signer Signer, l2ChainID *big.Int) ([]byte, error) {
var buf bytes.Buffer
buf.Write(make([]byte, 65))
if _, err := payload.MarshalSSZ(&buf); err != nil {
return nil, fmt.Errorf("failed to encoded execution payload to publish: %w", err)
}
data := buf.Bytes()
payloadData := data[65:]
sig, err := signer.Sign(context.TODO(), SigningDomainBlocksV1, l2ChainID, payloadData)
if err != nil {
return nil, fmt.Errorf("failed to sign execution payload with signer: %w", err)
}
copy(data[:65], sig[:])
// compress the full message
// This also copies the data, freeing up the original buffer to go back into the pool
return snappy.Encode(nil, data), nil
}
// TestBlockValidator does some very basic tests of the p2p block validation logic
func TestBlockValidator(t *testing.T) {
// Params Set 1: Create the validation function
cfg := &rollup.Config{
L2ChainID: big.NewInt(100),
}
secrets, err := e2eutils.DefaultMnemonicConfig.Secrets()
require.NoError(t, err)
runCfg := &testutils.MockRuntimeConfig{P2PSeqAddress: crypto.PubkeyToAddress(secrets.SequencerP2P.PublicKey)}
signer := &PreparedSigner{Signer: NewLocalSigner(secrets.SequencerP2P)}
// valFnV1 := BuildBlocksValidator(testlog.Logger(t, log.LvlCrit), rollupCfg, runCfg, eth.BlockV1)
valFnV2 := BuildBlocksValidator(testlog.Logger(t, log.LvlCrit), cfg, runCfg, eth.BlockV2)
// Params Set 2: Call the validation function
peerID := peer.ID("foo")
// Valid Case
payload := eth.ExecutionPayload{
Timestamp: hexutil.Uint64(time.Now().Unix()),
Withdrawals: &types.Withdrawals{},
}
payload.BlockHash, _ = payload.CheckBlockHash() // hack to generate the block hash easily.
data, err := createSignedP2Payload(&payload, signer, cfg.L2ChainID)
require.NoError(t, err)
message := &pubsub.Message{Message: &pubsub_pb.Message{Data: data}}
res := valFnV2(context.TODO(), peerID, message)
require.Equal(t, res, pubsub.ValidationAccept)
// Invalid because non-empty withdrawals when Canyon is active
payload = eth.ExecutionPayload{
Timestamp: hexutil.Uint64(time.Now().Unix()),
Withdrawals: &types.Withdrawals{&types.Withdrawal{Index: 1, Validator: 1}},
}
payload.BlockHash, _ = payload.CheckBlockHash()
data, err = createSignedP2Payload(&payload, signer, cfg.L2ChainID)
require.NoError(t, err)
message = &pubsub.Message{Message: &pubsub_pb.Message{Data: data}}
res = valFnV2(context.TODO(), peerID, message)
require.Equal(t, res, pubsub.ValidationReject)
}
...@@ -110,5 +110,9 @@ func LoadOPStackRollupConfig(chainID uint64) (*Config, error) { ...@@ -110,5 +110,9 @@ func LoadOPStackRollupConfig(chainID uint64) (*Config, error) {
cfg.ChannelTimeout = 120 cfg.ChannelTimeout = 120
cfg.MaxSequencerDrift = 1200 cfg.MaxSequencerDrift = 1200
} }
if chainID == pgnSepolia {
cfg.MaxSequencerDrift = 1000
cfg.SeqWindowSize = 7200
}
return cfg, nil return cfg, nil
} }
...@@ -20,6 +20,7 @@ type BlockInfo interface { ...@@ -20,6 +20,7 @@ type BlockInfo interface {
BaseFee() *big.Int BaseFee() *big.Int
ReceiptHash() common.Hash ReceiptHash() common.Hash
GasUsed() uint64 GasUsed() uint64
GasLimit() uint64
// HeaderRLP returns the RLP of the block header as per consensus rules // HeaderRLP returns the RLP of the block header as per consensus rules
// Returns an error if the header RLP could not be written // Returns an error if the header RLP could not be written
...@@ -100,6 +101,10 @@ func (h headerBlockInfo) GasUsed() uint64 { ...@@ -100,6 +101,10 @@ func (h headerBlockInfo) GasUsed() uint64 {
return h.Header.GasUsed return h.Header.GasUsed
} }
func (h headerBlockInfo) GasLimit() uint64 {
return h.Header.GasLimit
}
func (h headerBlockInfo) HeaderRLP() ([]byte, error) { func (h headerBlockInfo) HeaderRLP() ([]byte, error) {
return rlp.EncodeToBytes(h.Header) return rlp.EncodeToBytes(h.Header)
} }
......
...@@ -78,6 +78,10 @@ func (h headerInfo) GasUsed() uint64 { ...@@ -78,6 +78,10 @@ func (h headerInfo) GasUsed() uint64 {
return h.Header.GasUsed return h.Header.GasUsed
} }
func (h headerInfo) GasLimit() uint64 {
return h.Header.GasLimit
}
func (h headerInfo) HeaderRLP() ([]byte, error) { func (h headerInfo) HeaderRLP() ([]byte, error) {
return rlp.EncodeToBytes(h.Header) return rlp.EncodeToBytes(h.Header)
} }
......
...@@ -23,6 +23,7 @@ type MockBlockInfo struct { ...@@ -23,6 +23,7 @@ type MockBlockInfo struct {
InfoBaseFee *big.Int InfoBaseFee *big.Int
InfoReceiptRoot common.Hash InfoReceiptRoot common.Hash
InfoGasUsed uint64 InfoGasUsed uint64
InfoGasLimit uint64
InfoHeaderRLP []byte InfoHeaderRLP []byte
} }
...@@ -66,6 +67,10 @@ func (l *MockBlockInfo) GasUsed() uint64 { ...@@ -66,6 +67,10 @@ func (l *MockBlockInfo) GasUsed() uint64 {
return l.InfoGasUsed return l.InfoGasUsed
} }
func (l *MockBlockInfo) GasLimit() uint64 {
return l.InfoGasLimit
}
func (l *MockBlockInfo) ID() eth.BlockID { func (l *MockBlockInfo) ID() eth.BlockID {
return eth.BlockID{Hash: l.InfoHash, Number: l.InfoNum} return eth.BlockID{Hash: l.InfoHash, Number: l.InfoNum}
} }
......
...@@ -140,7 +140,7 @@ GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (g ...@@ -140,7 +140,7 @@ GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (g
GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 88797) GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 88797)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68320) GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68320)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 68973) GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 68973)
GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 143295) GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 143303)
GasPriceOracle_Test:test_baseFee_succeeds() (gas: 8348) GasPriceOracle_Test:test_baseFee_succeeds() (gas: 8348)
GasPriceOracle_Test:test_decimals_succeeds() (gas: 6234) GasPriceOracle_Test:test_decimals_succeeds() (gas: 6234)
GasPriceOracle_Test:test_gasPrice_succeeds() (gas: 8340) GasPriceOracle_Test:test_gasPrice_succeeds() (gas: 8340)
......
...@@ -20,5 +20,22 @@ ...@@ -20,5 +20,22 @@
"faucetOnchainAuthModuleAmount": 1000000000000000000, "faucetOnchainAuthModuleAmount": 1000000000000000000,
"faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC", "faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOffchainAuthModuleTtl": 86400, "faucetOffchainAuthModuleTtl": 86400,
"faucetOffchainAuthModuleAmount": 50000000000000000 "faucetOffchainAuthModuleAmount": 50000000000000000,
"opL1BridgeAddress": "0xFBb0621E0B23b5478B630BD55a5f21f67730B0F1",
"baseL1BridgeAddress": "0xfd0Bf71F60660E2f608ed56e1659C450eB113120",
"pgnL1BridgeAddress": "0xFaE6abCAF30D23e233AC7faF747F2fC3a5a6Bfa3",
"zoraL1BridgeAddress": "0x5376f1D543dcbB5BD416c56C189e4cB7399fCcCB",
"orderlyL1BridgeAddress": "0x1Af0494040d6904A9F3EE21921de4b359C736333",
"modeL1BridgeAddress": "0xbC5C679879B2965296756CD959C3C739769995E2",
"lyraL1BridgeAddress": "0x915f179A77FB2e1AeA8b56Ebc0D75A7e1A8a7A17",
"installOpChainFaucetsDrips": false,
"archivePreviousOpChainFaucetsDrips": false,
"dripVersion": 1,
"previousDripVersion": 0,
"smallOpChainFaucetDripValue": 2000000000000000000,
"smallOpChainFaucetDripInterval": 86400,
"largeOpChainFaucetDripValue": 34000000000000000000,
"largeOpChainFaucetDripInterval": 86400,
"opChainAdminWalletDripValue": 1000000000000000000,
"opChainAdminWalletDripInterval": 2592000
} }
...@@ -20,5 +20,22 @@ ...@@ -20,5 +20,22 @@
"faucetOnchainAuthModuleAmount": 1000000000000000000, "faucetOnchainAuthModuleAmount": 1000000000000000000,
"faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC", "faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOffchainAuthModuleTtl": 86400, "faucetOffchainAuthModuleTtl": 86400,
"faucetOffchainAuthModuleAmount": 50000000000000000 "faucetOffchainAuthModuleAmount": 50000000000000000,
"opL1BridgeAddress": "0xFBb0621E0B23b5478B630BD55a5f21f67730B0F1",
"baseL1BridgeAddress": "0xfd0Bf71F60660E2f608ed56e1659C450eB113120",
"pgnL1BridgeAddress": "0xFaE6abCAF30D23e233AC7faF747F2fC3a5a6Bfa3",
"zoraL1BridgeAddress": "0x5376f1D543dcbB5BD416c56C189e4cB7399fCcCB",
"orderlyL1BridgeAddress": "0x1Af0494040d6904A9F3EE21921de4b359C736333",
"modeL1BridgeAddress": "0xbC5C679879B2965296756CD959C3C739769995E2",
"lyraL1BridgeAddress": "0x915f179A77FB2e1AeA8b56Ebc0D75A7e1A8a7A17",
"installOpChainFaucetsDrips": false,
"archivePreviousOpChainFaucetsDrips": false,
"dripVersion": 1,
"previousDripVersion": 0,
"smallOpChainFaucetDripValue": 2000000000000000000,
"smallOpChainFaucetDripInterval": 86400,
"largeOpChainFaucetDripValue": 34000000000000000000,
"largeOpChainFaucetDripInterval": 86400,
"opChainAdminWalletDripValue": 1000000000000000000,
"opChainAdminWalletDripInterval": 2592000
} }
...@@ -20,5 +20,22 @@ ...@@ -20,5 +20,22 @@
"faucetOnchainAuthModuleAmount": 1000000000000000000, "faucetOnchainAuthModuleAmount": 1000000000000000000,
"faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC", "faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOffchainAuthModuleTtl": 86400, "faucetOffchainAuthModuleTtl": 86400,
"faucetOffchainAuthModuleAmount": 50000000000000000 "faucetOffchainAuthModuleAmount": 50000000000000000,
"opL1BridgeAddress": "0xFBb0621E0B23b5478B630BD55a5f21f67730B0F1",
"baseL1BridgeAddress": "0xfd0Bf71F60660E2f608ed56e1659C450eB113120",
"pgnL1BridgeAddress": "0xFaE6abCAF30D23e233AC7faF747F2fC3a5a6Bfa3",
"zoraL1BridgeAddress": "0x5376f1D543dcbB5BD416c56C189e4cB7399fCcCB",
"orderlyL1BridgeAddress": "0x1Af0494040d6904A9F3EE21921de4b359C736333",
"modeL1BridgeAddress": "0xbC5C679879B2965296756CD959C3C739769995E2",
"lyraL1BridgeAddress": "0x915f179A77FB2e1AeA8b56Ebc0D75A7e1A8a7A17",
"installOpChainFaucetsDrips": false,
"archivePreviousOpChainFaucetsDrips": false,
"dripVersion": 1,
"previousDripVersion": 0,
"smallOpChainFaucetDripValue": 2000000000000000000,
"smallOpChainFaucetDripInterval": 86400,
"largeOpChainFaucetDripValue": 34000000000000000000,
"largeOpChainFaucetDripInterval": 86400,
"opChainAdminWalletDripValue": 1000000000000000000,
"opChainAdminWalletDripInterval": 2592000
} }
...@@ -20,5 +20,22 @@ ...@@ -20,5 +20,22 @@
"faucetOnchainAuthModuleAmount": 1000000000000000000, "faucetOnchainAuthModuleAmount": 1000000000000000000,
"faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC", "faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOffchainAuthModuleTtl": 86400, "faucetOffchainAuthModuleTtl": 86400,
"faucetOffchainAuthModuleAmount": 50000000000000000 "faucetOffchainAuthModuleAmount": 50000000000000000,
"opL1BridgeAddress": "0xFBb0621E0B23b5478B630BD55a5f21f67730B0F1",
"baseL1BridgeAddress": "0xfd0Bf71F60660E2f608ed56e1659C450eB113120",
"pgnL1BridgeAddress": "0xFaE6abCAF30D23e233AC7faF747F2fC3a5a6Bfa3",
"zoraL1BridgeAddress": "0x5376f1D543dcbB5BD416c56C189e4cB7399fCcCB",
"orderlyL1BridgeAddress": "0x1Af0494040d6904A9F3EE21921de4b359C736333",
"modeL1BridgeAddress": "0xbC5C679879B2965296756CD959C3C739769995E2",
"lyraL1BridgeAddress": "0x915f179A77FB2e1AeA8b56Ebc0D75A7e1A8a7A17",
"installOpChainFaucetsDrips": false,
"archivePreviousOpChainFaucetsDrips": false,
"dripVersion": 1,
"previousDripVersion": 0,
"smallOpChainFaucetDripValue": 2000000000000000000,
"smallOpChainFaucetDripInterval": 86400,
"largeOpChainFaucetDripValue": 34000000000000000000,
"largeOpChainFaucetDripInterval": 86400,
"opChainAdminWalletDripValue": 1000000000000000000,
"opChainAdminWalletDripInterval": 2592000
} }
...@@ -20,5 +20,22 @@ ...@@ -20,5 +20,22 @@
"faucetOnchainAuthModuleAmount": 1000000000000000000, "faucetOnchainAuthModuleAmount": 1000000000000000000,
"faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC", "faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOffchainAuthModuleTtl": 86400, "faucetOffchainAuthModuleTtl": 86400,
"faucetOffchainAuthModuleAmount": 50000000000000000 "faucetOffchainAuthModuleAmount": 50000000000000000,
"opL1BridgeAddress": "0xFBb0621E0B23b5478B630BD55a5f21f67730B0F1",
"baseL1BridgeAddress": "0xfd0Bf71F60660E2f608ed56e1659C450eB113120",
"pgnL1BridgeAddress": "0xFaE6abCAF30D23e233AC7faF747F2fC3a5a6Bfa3",
"zoraL1BridgeAddress": "0x5376f1D543dcbB5BD416c56C189e4cB7399fCcCB",
"orderlyL1BridgeAddress": "0x1Af0494040d6904A9F3EE21921de4b359C736333",
"modeL1BridgeAddress": "0xbC5C679879B2965296756CD959C3C739769995E2",
"lyraL1BridgeAddress": "0x915f179A77FB2e1AeA8b56Ebc0D75A7e1A8a7A17",
"installOpChainFaucetsDrips": false,
"archivePreviousOpChainFaucetsDrips": false,
"dripVersion": 1,
"previousDripVersion": 0,
"smallOpChainFaucetDripValue": 2000000000000000000,
"smallOpChainFaucetDripInterval": 86400,
"largeOpChainFaucetDripValue": 34000000000000000000,
"largeOpChainFaucetDripInterval": 86400,
"opChainAdminWalletDripValue": 1000000000000000000,
"opChainAdminWalletDripInterval": 2592000
} }
...@@ -20,5 +20,22 @@ ...@@ -20,5 +20,22 @@
"faucetOnchainAuthModuleAmount": 1000000000000000000, "faucetOnchainAuthModuleAmount": 1000000000000000000,
"faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC", "faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOffchainAuthModuleTtl": 86400, "faucetOffchainAuthModuleTtl": 86400,
"faucetOffchainAuthModuleAmount": 50000000000000000 "faucetOffchainAuthModuleAmount": 50000000000000000,
"opL1BridgeAddress": "0xFBb0621E0B23b5478B630BD55a5f21f67730B0F1",
"baseL1BridgeAddress": "0xfd0Bf71F60660E2f608ed56e1659C450eB113120",
"pgnL1BridgeAddress": "0xFaE6abCAF30D23e233AC7faF747F2fC3a5a6Bfa3",
"zoraL1BridgeAddress": "0x5376f1D543dcbB5BD416c56C189e4cB7399fCcCB",
"orderlyL1BridgeAddress": "0x1Af0494040d6904A9F3EE21921de4b359C736333",
"modeL1BridgeAddress": "0xbC5C679879B2965296756CD959C3C739769995E2",
"lyraL1BridgeAddress": "0x915f179A77FB2e1AeA8b56Ebc0D75A7e1A8a7A17",
"installOpChainFaucetsDrips": false,
"archivePreviousOpChainFaucetsDrips": false,
"dripVersion": 1,
"previousDripVersion": 0,
"smallOpChainFaucetDripValue": 2000000000000000000,
"smallOpChainFaucetDripInterval": 86400,
"largeOpChainFaucetDripValue": 34000000000000000000,
"largeOpChainFaucetDripInterval": 86400,
"opChainAdminWalletDripValue": 1000000000000000000,
"opChainAdminWalletDripInterval": 2592000
} }
...@@ -20,5 +20,22 @@ ...@@ -20,5 +20,22 @@
"faucetOnchainAuthModuleAmount": 1000000000000000000, "faucetOnchainAuthModuleAmount": 1000000000000000000,
"faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC", "faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOffchainAuthModuleTtl": 86400, "faucetOffchainAuthModuleTtl": 86400,
"faucetOffchainAuthModuleAmount": 50000000000000000 "faucetOffchainAuthModuleAmount": 50000000000000000,
"opL1BridgeAddress": "0x636Af16bf2f682dD3109e60102b8E1A089FedAa8",
"baseL1BridgeAddress": "0xfd0Bf71F60660E2f608ed56e1659C450eB113120",
"pgnL1BridgeAddress": "0xFaE6abCAF30D23e233AC7faF747F2fC3a5a6Bfa3",
"zoraL1BridgeAddress": "0x5376f1D543dcbB5BD416c56C189e4cB7399fCcCB",
"orderlyL1BridgeAddress": "0x1Af0494040d6904A9F3EE21921de4b359C736333",
"modeL1BridgeAddress": "0xbC5C679879B2965296756CD959C3C739769995E2",
"lyraL1BridgeAddress": "0x915f179A77FB2e1AeA8b56Ebc0D75A7e1A8a7A17",
"installOpChainFaucetsDrips": false,
"archivePreviousOpChainFaucetsDrips": false,
"dripVersion": 1,
"previousDripVersion": 0,
"smallOpChainFaucetDripValue": 2000000000000000000,
"smallOpChainFaucetDripInterval": 86400,
"largeOpChainFaucetDripValue": 34000000000000000000,
"largeOpChainFaucetDripInterval": 86400,
"opChainAdminWalletDripValue": 1000000000000000000,
"opChainAdminWalletDripInterval": 2592000
} }
...@@ -20,5 +20,22 @@ ...@@ -20,5 +20,22 @@
"faucetOnchainAuthModuleAmount": 1000000000000000000, "faucetOnchainAuthModuleAmount": 1000000000000000000,
"faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC", "faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOffchainAuthModuleTtl": 86400, "faucetOffchainAuthModuleTtl": 86400,
"faucetOffchainAuthModuleAmount": 50000000000000000 "faucetOffchainAuthModuleAmount": 50000000000000000,
"opL1BridgeAddress": "0xFBb0621E0B23b5478B630BD55a5f21f67730B0F1",
"baseL1BridgeAddress": "0xfd0Bf71F60660E2f608ed56e1659C450eB113120",
"pgnL1BridgeAddress": "0xFaE6abCAF30D23e233AC7faF747F2fC3a5a6Bfa3",
"zoraL1BridgeAddress": "0x5376f1D543dcbB5BD416c56C189e4cB7399fCcCB",
"orderlyL1BridgeAddress": "0x1Af0494040d6904A9F3EE21921de4b359C736333",
"modeL1BridgeAddress": "0xbC5C679879B2965296756CD959C3C739769995E2",
"lyraL1BridgeAddress": "0x915f179A77FB2e1AeA8b56Ebc0D75A7e1A8a7A17",
"installOpChainFaucetsDrips": false,
"archivePreviousOpChainFaucetsDrips": false,
"dripVersion": 1,
"previousDripVersion": 0,
"smallOpChainFaucetDripValue": 2000000000000000000,
"smallOpChainFaucetDripInterval": 86400,
"largeOpChainFaucetDripValue": 34000000000000000000,
"largeOpChainFaucetDripInterval": 86400,
"opChainAdminWalletDripValue": 1000000000000000000,
"opChainAdminWalletDripInterval": 2592000
} }
{
"faucetAdmin": "0x212E789D4523D4BAF464f8Fb2A9B9dff2B36e5A6",
"faucetDrippieOwner": "0x10ab157483dd308f8B38aCF2ad823dfD255F56b5",
"faucetDripV1Value": 20000000000000000000,
"faucetDripV1Interval": 3600,
"faucetDripV1Threshold": 100000000000000000000,
"faucetDripV2Interval": 604800,
"faucetDripV2Threshold": 20000000000000000000,
"faucetDripV2Value": 500000000000000000000,
"faucetAdminDripV1Interval": 86400,
"faucetAdminDripV1Threshold": 100000000000000000,
"faucetAdminDripV1Value": 1000000000000000000,
"faucetGelatoTreasury": "0x644CB00854EDC55FE8CCC9c1967BABb22F08Ad2f",
"faucetGelatoRecipient": "0x0E9b4649eB0760A4F01646636E032D68cFDe58FF",
"faucetGelatoBalanceV1DripInterval": 86400,
"faucetGelatoBalanceV1Value": 1000000000000000000,
"faucetGelatoThreshold": 100000000000000000,
"faucetOnchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOnchainAuthModuleTtl": 86400,
"faucetOnchainAuthModuleAmount": 1000000000000000000,
"faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOffchainAuthModuleTtl": 86400,
"faucetOffchainAuthModuleAmount": 50000000000000000,
"opL1BridgeAddress": "0xFBb0621E0B23b5478B630BD55a5f21f67730B0F1",
"baseL1BridgeAddress": "0xfd0Bf71F60660E2f608ed56e1659C450eB113120",
"pgnL1BridgeAddress": "0xFaE6abCAF30D23e233AC7faF747F2fC3a5a6Bfa3",
"zoraL1BridgeAddress": "0x5376f1D543dcbB5BD416c56C189e4cB7399fCcCB",
"orderlyL1BridgeAddress": "0x1Af0494040d6904A9F3EE21921de4b359C736333",
"modeL1BridgeAddress": "0xbC5C679879B2965296756CD959C3C739769995E2",
"lyraL1BridgeAddress": "0x915f179A77FB2e1AeA8b56Ebc0D75A7e1A8a7A17",
"installOpChainFaucetsDrips": true,
"archivePreviousOpChainFaucetsDrips": false,
"dripVersion": 2,
"previousDripVersion": 1,
"smallOpChainFaucetDripValue": 2000000000000000000,
"smallOpChainFaucetDripInterval": 86400,
"largeOpChainFaucetDripValue": 34000000000000000000,
"largeOpChainFaucetDripInterval": 86400,
"opChainAdminWalletDripValue": 1000000000000000000,
"opChainAdminWalletDripInterval": 2592000
}
...@@ -88,7 +88,10 @@ abstract contract Deployer is Script { ...@@ -88,7 +88,10 @@ abstract contract Deployer is Script {
string memory chainIdPath = string.concat(deploymentsDir, "/.chainId"); string memory chainIdPath = string.concat(deploymentsDir, "/.chainId");
try vm.readFile(chainIdPath) returns (string memory localChainId) { try vm.readFile(chainIdPath) returns (string memory localChainId) {
if (vm.envOr("STRICT_DEPLOYMENT", true)) { if (vm.envOr("STRICT_DEPLOYMENT", true)) {
require(vm.parseUint(localChainId) == chainId, "Misconfigured networks"); require(
vm.parseUint(localChainId) == chainId,
string.concat("Misconfigured networks: ", localChainId, " != ", vm.toString(chainId))
);
} }
} catch { } catch {
vm.writeFile(chainIdPath, vm.toString(chainId)); vm.writeFile(chainIdPath, vm.toString(chainId));
......
...@@ -34,6 +34,25 @@ contract PeripheryDeployConfig is Script { ...@@ -34,6 +34,25 @@ contract PeripheryDeployConfig is Script {
address public faucetOffchainAuthModuleAdmin; address public faucetOffchainAuthModuleAdmin;
uint256 public faucetOffchainAuthModuleTtl; uint256 public faucetOffchainAuthModuleTtl;
uint256 public faucetOffchainAuthModuleAmount; uint256 public faucetOffchainAuthModuleAmount;
bool public installOpChainFaucetsDrips;
bool public archivePreviousOpChainFaucetsDrips;
uint256 public smallOpChainFaucetDripValue;
uint256 public smallOpChainFaucetDripInterval;
uint256 public largeOpChainFaucetDripValue;
uint256 public largeOpChainFaucetDripInterval;
uint256 public opChainAdminWalletDripValue;
uint256 public opChainAdminWalletDripInterval;
address public opL1BridgeAddress;
address public baseL1BridgeAddress;
address public zoraL1BridgeAddress;
address public pgnL1BridgeAddress;
address public orderlyL1BridgeAddress;
address public modeL1BridgeAddress;
address public lyraL1BridgeAddress;
address[5] public smallFaucetsL1BridgeAddresses;
address[2] public largeFaucetsL1BridgeAddresses;
uint256 public dripVersion;
uint256 public previousDripVersion;
constructor(string memory _path) { constructor(string memory _path) {
console.log("PeripheryDeployConfig: reading file %s", _path); console.log("PeripheryDeployConfig: reading file %s", _path);
...@@ -66,5 +85,37 @@ contract PeripheryDeployConfig is Script { ...@@ -66,5 +85,37 @@ contract PeripheryDeployConfig is Script {
faucetOffchainAuthModuleAdmin = stdJson.readAddress(_json, "$.faucetOffchainAuthModuleAdmin"); faucetOffchainAuthModuleAdmin = stdJson.readAddress(_json, "$.faucetOffchainAuthModuleAdmin");
faucetOffchainAuthModuleTtl = stdJson.readUint(_json, "$.faucetOffchainAuthModuleTtl"); faucetOffchainAuthModuleTtl = stdJson.readUint(_json, "$.faucetOffchainAuthModuleTtl");
faucetOffchainAuthModuleAmount = stdJson.readUint(_json, "$.faucetOffchainAuthModuleAmount"); faucetOffchainAuthModuleAmount = stdJson.readUint(_json, "$.faucetOffchainAuthModuleAmount");
installOpChainFaucetsDrips = stdJson.readBool(_json, "$.installOpChainFaucetsDrips");
archivePreviousOpChainFaucetsDrips = stdJson.readBool(_json, "$.archivePreviousOpChainFaucetsDrips");
opL1BridgeAddress = stdJson.readAddress(_json, "$.opL1BridgeAddress");
baseL1BridgeAddress = stdJson.readAddress(_json, "$.baseL1BridgeAddress");
zoraL1BridgeAddress = stdJson.readAddress(_json, "$.zoraL1BridgeAddress");
pgnL1BridgeAddress = stdJson.readAddress(_json, "$.pgnL1BridgeAddress");
orderlyL1BridgeAddress = stdJson.readAddress(_json, "$.orderlyL1BridgeAddress");
modeL1BridgeAddress = stdJson.readAddress(_json, "$.modeL1BridgeAddress");
lyraL1BridgeAddress = stdJson.readAddress(_json, "$.lyraL1BridgeAddress");
dripVersion = stdJson.readUint(_json, "$.dripVersion");
previousDripVersion = stdJson.readUint(_json, "$.previousDripVersion");
smallOpChainFaucetDripValue = stdJson.readUint(_json, "$.smallOpChainFaucetDripValue");
smallOpChainFaucetDripInterval = stdJson.readUint(_json, "$.smallOpChainFaucetDripInterval");
largeOpChainFaucetDripValue = stdJson.readUint(_json, "$.largeOpChainFaucetDripValue");
largeOpChainFaucetDripInterval = stdJson.readUint(_json, "$.largeOpChainFaucetDripInterval");
opChainAdminWalletDripValue = stdJson.readUint(_json, "$.opChainAdminWalletDripValue");
opChainAdminWalletDripInterval = stdJson.readUint(_json, "$.opChainAdminWalletDripInterval");
largeFaucetsL1BridgeAddresses[0] = opL1BridgeAddress;
largeFaucetsL1BridgeAddresses[1] = baseL1BridgeAddress;
smallFaucetsL1BridgeAddresses[0] = zoraL1BridgeAddress;
smallFaucetsL1BridgeAddresses[1] = pgnL1BridgeAddress;
smallFaucetsL1BridgeAddresses[2] = orderlyL1BridgeAddress;
smallFaucetsL1BridgeAddresses[3] = modeL1BridgeAddress;
smallFaucetsL1BridgeAddresses[4] = lyraL1BridgeAddress;
}
function getSmallFaucetsL1BridgeAddressesCount() public view returns (uint256 count) {
return smallFaucetsL1BridgeAddresses.length;
}
function getLargeFaucetsL1BridgeAddressesCount() public view returns (uint256 count) {
return largeFaucetsL1BridgeAddresses.length;
} }
} }
...@@ -82,6 +82,6 @@ ...@@ -82,6 +82,6 @@
"change-case": "4.1.2", "change-case": "4.1.2",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"viem": "^1.18.4" "viem": "^1.18.6"
} }
} }
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
"jsdom": "^22.1.0", "jsdom": "^22.1.0",
"tsup": "^7.2.0", "tsup": "^7.2.0",
"typescript": "^5.2.2", "typescript": "^5.2.2",
"viem": "^1.18.4", "viem": "^1.18.6",
"vite": "^4.5.0", "vite": "^4.5.0",
"vitest": "^0.34.2" "vitest": "^0.34.2"
}, },
......
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typedoc": "^0.25.3", "typedoc": "^0.25.3",
"typescript": "^5.2.2", "typescript": "^5.2.2",
"viem": "^1.18.4", "viem": "^1.18.6",
"vitest": "^0.34.2", "vitest": "^0.34.2",
"zod": "^3.22.4" "zod": "^3.22.4"
}, },
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
"@vitest/coverage-istanbul": "^0.34.6", "@vitest/coverage-istanbul": "^0.34.6",
"tsup": "^7.2.0", "tsup": "^7.2.0",
"typescript": "^5.2.2", "typescript": "^5.2.2",
"viem": "^1.18.4", "viem": "^1.18.6",
"vite": "^4.5.0", "vite": "^4.5.0",
"vitest": "^0.34.1", "vitest": "^0.34.1",
"zod": "^3.22.4" "zod": "^3.22.4"
......
This diff is collapsed.
...@@ -694,9 +694,12 @@ equivalents. The `v2` methods are backwards compatible with `v1` payloads but su ...@@ -694,9 +694,12 @@ equivalents. The `v2` methods are backwards compatible with `v1` payloads but su
[`engine_getPayloadV2`]: exec-engine.md#engine_getpayloadv2 [`engine_getPayloadV2`]: exec-engine.md#engine_getpayloadv2
[`engine_newPayloadV2`]: exec-engine.md#engine_newpayloadv2 [`engine_newPayloadV2`]: exec-engine.md#engine_newpayloadv2
The execution payload is an object of type [`ExecutionPayloadV1`][eth-payload]. The execution payload is an object of type [`ExecutionPayloadV2`][eth-payload].
[eth-payload]: https://github.com/ethereum/execution-apis/blob/main/src/engine/paris.md#executionpayloadv1 [eth-payload]: https://github.com/ethereum/execution-apis/blob/main/src/engine/shanghai.md#payloadattributesv2
With V2 of the execution payload, before Canyon the withdrawals field is required to be nil. After Canyon the
withdrawals field is required to be non-nil. The op-node should set the withdrawals field to be an empty list.
#### Forkchoice synchronization #### Forkchoice synchronization
...@@ -892,7 +895,7 @@ without dispute (fault proof challenge window), a name-collision with the proof- ...@@ -892,7 +895,7 @@ without dispute (fault proof challenge window), a name-collision with the proof-
[deriving-payload-attr]: #deriving-payload-attributes [deriving-payload-attr]: #deriving-payload-attributes
For every L2 block derived from L1 data, we need to build [payload attributes][g-payload-attr], For every L2 block derived from L1 data, we need to build [payload attributes][g-payload-attr],
represented by an [expanded version][expanded-payload] of the [`PayloadAttributesV1`][eth-payload] object, represented by an [expanded version][expanded-payload] of the [`PayloadAttributesV2`][eth-payload] object,
which includes additional `transactions` and `noTxPool` fields. which includes additional `transactions` and `noTxPool` fields.
This process happens during the payloads-attributes queue ran by a verifier node, as well as during block-production This process happens during the payloads-attributes queue ran by a verifier node, as well as during block-production
...@@ -912,7 +915,7 @@ This block is part of a [sequencing epoch][g-sequencing-epoch], ...@@ -912,7 +915,7 @@ This block is part of a [sequencing epoch][g-sequencing-epoch],
whose number matches that of an L1 block (its *[L1 origin][g-l1-origin]*). whose number matches that of an L1 block (its *[L1 origin][g-l1-origin]*).
This L1 block is used to derive L1 attributes and (for the first L2 block in the epoch) user deposits. This L1 block is used to derive L1 attributes and (for the first L2 block in the epoch) user deposits.
Therefore, a [`PayloadAttributesV1`][expanded-payload] object must include the following transactions: Therefore, a [`PayloadAttributesV2`][expanded-payload] object must include the following transactions:
- one or more [deposited transactions][g-deposited], of two kinds: - one or more [deposited transactions][g-deposited], of two kinds:
- a single *[L1 attributes deposited transaction][g-l1-attr-deposit]*, derived from the L1 origin. - a single *[L1 attributes deposited transaction][g-l1-attr-deposit]*, derived from the L1 origin.
...@@ -933,7 +936,7 @@ entries. ...@@ -933,7 +936,7 @@ entries.
[payload attributes]: #building-individual-payload-attributes [payload attributes]: #building-individual-payload-attributes
After deriving the transactions list, the rollup node constructs a [`PayloadAttributesV1`][extended-attributes] as After deriving the transactions list, the rollup node constructs a [`PayloadAttributesV2`][extended-attributes] as
follows: follows:
- `timestamp` is set to the batch's timestamp. - `timestamp` is set to the batch's timestamp.
...@@ -943,6 +946,7 @@ follows: ...@@ -943,6 +946,7 @@ follows:
encoded with [EIP-2718]. encoded with [EIP-2718].
- `noTxPool` is set to `true`, to use the exact above `transactions` list when constructing the block. - `noTxPool` is set to `true`, to use the exact above `transactions` list when constructing the block.
- `gasLimit` is set to the current `gasLimit` value in the [system configuration][g-system-config] of this payload. - `gasLimit` is set to the current `gasLimit` value in the [system configuration][g-system-config] of this payload.
- 'withdrawals' is set to nil prior to Canyon and an empty array after Canyon
[extended-attributes]: exec-engine.md#extended-payloadattributesv1 [extended-attributes]: exec-engine.md#extended-payloadattributesv1
[Fee Vaults]: exec-engine.md#fee-vaults [Fee Vaults]: exec-engine.md#fee-vaults
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** **Table of Contents**
- [1559 Parameters](#1559-parameters)
- [Deposited transaction processing](#deposited-transaction-processing) - [Deposited transaction processing](#deposited-transaction-processing)
- [Deposited transaction boundaries](#deposited-transaction-boundaries) - [Deposited transaction boundaries](#deposited-transaction-boundaries)
- [Fees](#fees) - [Fees](#fees)
...@@ -13,7 +14,7 @@ ...@@ -13,7 +14,7 @@
- [L1-Cost fees (L1 Fee Vault)](#l1-cost-fees-l1-fee-vault) - [L1-Cost fees (L1 Fee Vault)](#l1-cost-fees-l1-fee-vault)
- [Engine API](#engine-api) - [Engine API](#engine-api)
- [`engine_forkchoiceUpdatedV2`](#engine_forkchoiceupdatedv2) - [`engine_forkchoiceUpdatedV2`](#engine_forkchoiceupdatedv2)
- [Extended PayloadAttributesV1](#extended-payloadattributesv1) - [Extended PayloadAttributesV2](#extended-payloadattributesv2)
- [`engine_newPayloadV2`](#engine_newpayloadv2) - [`engine_newPayloadV2`](#engine_newpayloadv2)
- [`engine_getPayloadV2`](#engine_getpayloadv2) - [`engine_getPayloadV2`](#engine_getpayloadv2)
- [`engine_signalSuperchainV1`](#engine_signalsuperchainv1) - [`engine_signalSuperchainV1`](#engine_signalsuperchainv1)
...@@ -26,6 +27,14 @@ ...@@ -26,6 +27,14 @@
This document outlines the modifications, configuration and usage of a L1 execution engine for L2. This document outlines the modifications, configuration and usage of a L1 execution engine for L2.
## 1559 Parameters
The execution engine must be able to take a per chain configuration which specifies the EIP-1559 Denominator
and EIP-1559 elasticity. After Canyon it should also take a new value `EIP1559DenominatorCanyon` and use that as
the denominator in the 1559 formula rather than the prior denominator.
The formula for EIP-1559 is not otherwise modified.
## Deposited transaction processing ## Deposited transaction processing
The Engine interfaces abstract away transaction types with [EIP-2718][eip-2718]. The Engine interfaces abstract away transaction types with [EIP-2718][eip-2718].
...@@ -140,17 +149,18 @@ Within the rollup, the types of forkchoice updates translate as: ...@@ -140,17 +149,18 @@ Within the rollup, the types of forkchoice updates translate as:
- `finalizedBlockHash`: irreversible block hash, matches lower boundary of the dispute period. - `finalizedBlockHash`: irreversible block hash, matches lower boundary of the dispute period.
To support rollup functionality, one backwards-compatible change is introduced To support rollup functionality, one backwards-compatible change is introduced
to [`engine_forkchoiceUpdatedV2`][engine_forkchoiceUpdatedV2]: the extended `PayloadAttributesV1` to [`engine_forkchoiceUpdatedV2`][engine_forkchoiceUpdatedV2]: the extended `PayloadAttributesV2`
#### Extended PayloadAttributesV1 #### Extended PayloadAttributesV2
[`PayloadAttributesV1`][PayloadAttributesV1] is extended to: [`PayloadAttributesV2`][PayloadAttributesV2] is extended to:
```js ```js
PayloadAttributesV1: { PayloadAttributesV2: {
timestamp: QUANTITY timestamp: QUANTITY
random: DATA (32 bytes) random: DATA (32 bytes)
suggestedFeeRecipient: DATA (20 bytes) suggestedFeeRecipient: DATA (20 bytes)
withdrawals: array of WithdrawalV1
transactions: array of DATA transactions: array of DATA
noTxPool: bool noTxPool: bool
gasLimit: QUANTITY or null gasLimit: QUANTITY or null
...@@ -163,7 +173,7 @@ to a JSON array. ...@@ -163,7 +173,7 @@ to a JSON array.
Each item of the `transactions` array is a byte list encoding a transaction: `TransactionType || Each item of the `transactions` array is a byte list encoding a transaction: `TransactionType ||
TransactionPayload` or `LegacyTransaction`, as defined in [EIP-2718][eip-2718]. TransactionPayload` or `LegacyTransaction`, as defined in [EIP-2718][eip-2718].
This is equivalent to the `transactions` field in [`ExecutionPayloadV1`][ExecutionPayloadV1] This is equivalent to the `transactions` field in [`ExecutionPayloadV2`][ExecutionPayloadV2]
The `transactions` field is optional: The `transactions` field is optional:
...@@ -301,7 +311,7 @@ the operation within the engine is the exact same as with L1 (although with an E ...@@ -301,7 +311,7 @@ the operation within the engine is the exact same as with L1 (although with an E
[eip-2718-transactions]: https://eips.ethereum.org/EIPS/eip-2718#transactions [eip-2718-transactions]: https://eips.ethereum.org/EIPS/eip-2718#transactions
[exec-api-data]: https://github.com/ethereum/execution-apis/blob/769c53c94c4e487337ad0edea9ee0dce49c79bfa/src/engine/specification.md#structures [exec-api-data]: https://github.com/ethereum/execution-apis/blob/769c53c94c4e487337ad0edea9ee0dce49c79bfa/src/engine/specification.md#structures
[l1-api-spec]: https://github.com/ethereum/execution-apis/blob/769c53c94c4e487337ad0edea9ee0dce49c79bfa/src/engine/specification.md [l1-api-spec]: https://github.com/ethereum/execution-apis/blob/769c53c94c4e487337ad0edea9ee0dce49c79bfa/src/engine/specification.md
[PayloadAttributesV1]: https://github.com/ethereum/execution-apis/blob/769c53c94c4e487337ad0edea9ee0dce49c79bfa/src/engine/specification.md#PayloadAttributesV1 [PayloadAttributesV2]: https://github.com/ethereum/execution-apis/blob/584905270d8ad665718058060267061ecfd79ca5/src/engine/shanghai.md#PayloadAttributesV2
[ExecutionPayloadV1]: https://github.com/ethereum/execution-apis/blob/769c53c94c4e487337ad0edea9ee0dce49c79bfa/src/engine/specification.md#ExecutionPayloadV1 [ExecutionPayloadV1]: https://github.com/ethereum/execution-apis/blob/769c53c94c4e487337ad0edea9ee0dce49c79bfa/src/engine/specification.md#ExecutionPayloadV1
[engine_forkchoiceUpdatedV2]: https://github.com/ethereum/execution-apis/blob/584905270d8ad665718058060267061ecfd79ca5/src/engine/shanghai.md#engine_forkchoiceupdatedv2 [engine_forkchoiceUpdatedV2]: https://github.com/ethereum/execution-apis/blob/584905270d8ad665718058060267061ecfd79ca5/src/engine/shanghai.md#engine_forkchoiceupdatedv2
[engine_newPayloadV2]: https://github.com/ethereum/execution-apis/blob/584905270d8ad665718058060267061ecfd79ca5/src/engine/shanghai.md#engine_newpayloadv2 [engine_newPayloadV2]: https://github.com/ethereum/execution-apis/blob/584905270d8ad665718058060267061ecfd79ca5/src/engine/shanghai.md#engine_newpayloadv2
......
...@@ -797,7 +797,7 @@ In these specifications, "execution engine" always refer to the L2 execution eng ...@@ -797,7 +797,7 @@ In these specifications, "execution engine" always refer to the L2 execution eng
[Solidity events]: https://docs.soliditylang.org/en/latest/contracts.html?highlight=events#events [Solidity events]: https://docs.soliditylang.org/en/latest/contracts.html?highlight=events#events
[nano-header]: https://github.com/norswap/nanoeth/blob/cc5d94a349c90627024f3cd629a2d830008fec72/src/com/norswap/nanoeth/blocks/BlockHeader.java#L22-L156 [nano-header]: https://github.com/norswap/nanoeth/blob/cc5d94a349c90627024f3cd629a2d830008fec72/src/com/norswap/nanoeth/blocks/BlockHeader.java#L22-L156
[yellow]: https://ethereum.github.io/yellowpaper/paper.pdf [yellow]: https://ethereum.github.io/yellowpaper/paper.pdf
[engine-api]: https://github.com/ethereum/execution-apis/blob/main/src/engine/paris.md#PayloadAttributesV1 [engine-api]: https://github.com/ethereum/execution-apis/blob/main/src/engine/shanghai.md#PayloadAttributesV2
[merge]: https://ethereum.org/en/eth2/merge/ [merge]: https://ethereum.org/en/eth2/merge/
[mempool]: https://www.quicknode.com/guides/defi/how-to-access-ethereum-mempool [mempool]: https://www.quicknode.com/guides/defi/how-to-access-ethereum-mempool
[L1 consensus layer]: https://github.com/ethereum/consensus-specs/#readme [L1 consensus layer]: https://github.com/ethereum/consensus-specs/#readme
......
...@@ -291,6 +291,7 @@ An [extended-validator] checks the incoming messages as follows, in order of ope ...@@ -291,6 +291,7 @@ An [extended-validator] checks the incoming messages as follows, in order of ope
- `[REJECT]` if the `block_hash` in the `payload` is not valid - `[REJECT]` if the `block_hash` in the `payload` is not valid
- `[REJECT]` if the block is on the V1 topic and has withdrawals - `[REJECT]` if the block is on the V1 topic and has withdrawals
- `[REJECT]` if the block is on the V2 topic and does not have withdrawals - `[REJECT]` if the block is on the V2 topic and does not have withdrawals
- `[REJECT]` if the block is on the V2 topic and has a non-zero amount of withdrawals
- `[REJECT]` if more than 5 different blocks have been seen with the same block height - `[REJECT]` if more than 5 different blocks have been seen with the same block height
- `[IGNORE]` if the block has already been seen - `[IGNORE]` if the block has already been seen
- `[REJECT]` if the signature by the sequencer is not valid - `[REJECT]` if the signature by the sequencer is not valid
......
...@@ -64,6 +64,8 @@ Span-batches address these inefficiencies, with a new batch format version. ...@@ -64,6 +64,8 @@ Span-batches address these inefficiencies, with a new batch format version.
## Span batch format ## Span batch format
[span-batch-format]: #span-batch-format
Note that span-batches, unlike previous singular batches, Note that span-batches, unlike previous singular batches,
encode *a range of consecutive* L2 blocks at the same time. encode *a range of consecutive* L2 blocks at the same time.
...@@ -268,7 +270,13 @@ Span-batches share the same queue with v0 batches: batches are processed in L1 i ...@@ -268,7 +270,13 @@ Span-batches share the same queue with v0 batches: batches are processed in L1 i
A set of modified validation rules apply to the span-batches. A set of modified validation rules apply to the span-batches.
Rules are enforced with the [contextual definitions](./derivation.md#batch-queue) as v0-batch validation: Rules are enforced with the [contextual definitions](./derivation.md#batch-queue) as v0-batch validation:
`batch`, `epoch`, `inclusion_block_number`, `next_timestamp`, `next_epoch`, `batch_origin` `epoch`, `inclusion_block_number`, `next_timestamp`
Definitions:
- `batch` as defined in the [Span batch format section][span-batch-format].
- `prev_l2_block` is the L2 block from the current safe chain,
whose timestamp is at `span_start.timestamp - l2_block_time`
Span-batch rules, in validation order: Span-batch rules, in validation order:
...@@ -279,10 +287,12 @@ Span-batch rules, in validation order: ...@@ -279,10 +287,12 @@ Span-batch rules, in validation order:
- If known, then define `batch_origin` as `next_epoch` - If known, then define `batch_origin` as `next_epoch`
- `batch_origin.timestamp < span_batch_upgrade_timestamp` -> `drop`: - `batch_origin.timestamp < span_batch_upgrade_timestamp` -> `drop`:
i.e. enforce the [span batch upgrade activation rule](#span-batch-activation-rule). i.e. enforce the [span batch upgrade activation rule](#span-batch-activation-rule).
- `batch.start_timestamp > next_timestamp` -> `future`: i.e. the batch must be ready to process. - `span_start.timestamp > next_timestamp` -> `future`: i.e. the batch must be ready to process,
- `batch.start_timestamp < next_timestamp` -> `drop`: i.e. the batch must not be too old. but does not have to start exactly at the `next_timestamp`, since it can overlap with previously processed blocks,
- `batch.parent_check != safe_l2_head.hash[:20]` -> `drop`: i.e. the checked part of the parent hash must be equal - `span_end.timestamp < next_timestamp` -> `drop`: i.e. the batch must have at least one new block to process.
to the L2 safe head block hash. - If there's no `prev_l2_block` in the current safe chain -> `drop`: i.e. the timestamp must be aligned.
- `batch.parent_check != prev_l2_block.hash[:20]` -> `drop`:
i.e. the checked part of the parent hash must be equal to the same part of the corresponding L2 block hash.
- Sequencing-window checks: - Sequencing-window checks:
- Note: The sequencing window is enforced for the *batch as a whole*: - Note: The sequencing window is enforced for the *batch as a whole*:
if the batch was partially invalid instead, it would drop the oldest L2 blocks, if the batch was partially invalid instead, it would drop the oldest L2 blocks,
...@@ -294,7 +304,7 @@ Span-batch rules, in validation order: ...@@ -294,7 +304,7 @@ Span-batch rules, in validation order:
- Rules: - Rules:
- `start_epoch_num + sequence_window_size < inclusion_block_number` -> `drop`: - `start_epoch_num + sequence_window_size < inclusion_block_number` -> `drop`:
i.e. the batch must be included timely. i.e. the batch must be included timely.
- `start_epoch_num > epoch.number + 1` -> `drop`: - `start_epoch_num > prev_l2_block.l1_origin.number + 1` -> `drop`:
i.e. the L1 origin cannot change by more than one L1 block per L2 block. i.e. the L1 origin cannot change by more than one L1 block per L2 block.
- If `batch.l1_origin_check` does not match the canonical L1 chain at `end_epoch_num` -> `drop`: - If `batch.l1_origin_check` does not match the canonical L1 chain at `end_epoch_num` -> `drop`:
verify the batch is intended for this L1 chain. verify the batch is intended for this L1 chain.
...@@ -302,9 +312,8 @@ Span-batch rules, in validation order: ...@@ -302,9 +312,8 @@ Span-batch rules, in validation order:
is past `inclusion_block_number` because of the following invariant. is past `inclusion_block_number` because of the following invariant.
- Invariant: the epoch-num in the batch is always less than the inclusion block number, - Invariant: the epoch-num in the batch is always less than the inclusion block number,
if and only if the L1 epoch hash is correct. if and only if the L1 epoch hash is correct.
- `start_epoch_num < epoch.number` -> `drop`: must have been duplicate batch, - `start_epoch_num < prev_l2_block.l1_origin.number` -> `drop`:
we may be past this L1 block in the safe L2 chain. If a span-batch overlaps with older information, epoch number cannot be older than the origin of parent block
it is dropped, since partially valid span-batches are not accepted.
- Max Sequencer time-drift checks: - Max Sequencer time-drift checks:
- Note: The max time-drift is enforced for the *batch as a whole*, to keep the possible output variants small. - Note: The max time-drift is enforced for the *batch as a whole*, to keep the possible output variants small.
- Variables: - Variables:
...@@ -313,7 +322,7 @@ Span-batch rules, in validation order: ...@@ -313,7 +322,7 @@ Span-batch rules, in validation order:
- `next_epoch`: `block_input.origin`'s next L1 block. - `next_epoch`: `block_input.origin`'s next L1 block.
It may reach to the next origin outside the L1 origins of the span. It may reach to the next origin outside the L1 origins of the span.
- Rules: - Rules:
- For each `block_input` that can be read from the span-batch: - For each `block_input` whose timestamp is greater than `safe_head.timestamp`:
- `block_input.timestamp < block_input.origin.time` -> `drop`: enforce the min L2 timestamp rule. - `block_input.timestamp < block_input.origin.time` -> `drop`: enforce the min L2 timestamp rule.
- `block_input.timestamp > block_input.origin.time + max_sequencer_drift`: enforce the L2 timestamp drift rule, - `block_input.timestamp > block_input.origin.time + max_sequencer_drift`: enforce the L2 timestamp drift rule,
but with exceptions to preserve above min L2 timestamp invariant: but with exceptions to preserve above min L2 timestamp invariant:
...@@ -332,6 +341,16 @@ Span-batch rules, in validation order: ...@@ -332,6 +341,16 @@ Span-batch rules, in validation order:
that is invalid or derived by other means exclusively: that is invalid or derived by other means exclusively:
- any transaction that is empty (zero length `tx_data`) - any transaction that is empty (zero length `tx_data`)
- any [deposited transactions][g-deposit-tx-type] (identified by the transaction type prefix byte in `tx_data`) - any [deposited transactions][g-deposit-tx-type] (identified by the transaction type prefix byte in `tx_data`)
- Overlapped blocks checks:
- Note: If the span batch overlaps the current L2 safe chain, we must validate all overlapped blocks.
- Variables:
- `block_input`: an L2 block derived from the span-batch.
- `safe_block`: an L2 block from the current L2 safe chain, at same timestamp as `block_input`
- Rules:
- For each `block_input`, whose timestamp is less than `next_timestamp`:
- `block_input.l1_origin.number != safe_block.l1_origin.number` -> `drop`
- `block_input.transactions != safe_block.transactions` -> `drop`
- compare excluding deposit transactions
Once validated, the batch-queue then emits a block-input for each of the blocks included in the span-batch. Once validated, the batch-queue then emits a block-input for each of the blocks included in the span-batch.
The next derivation stage is thus only aware of individual block inputs, similar to the previous V0 batch, The next derivation stage is thus only aware of individual block inputs, similar to the previous V0 batch,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment