Commit 74419cc3 authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into aj/score-histogram

parents f2e442b9 0638daf5
---
'@eth-optimism/contracts-bedrock': patch
---
contracts-bedrock was exporting hardhat when it didn't need to be
---
'@eth-optimism/contracts-periphery': patch
---
Add faucet contract
---
'@eth-optimism/sdk': patch
---
add eco bridge adapter
---
'@eth-optimism/contracts-bedrock': minor
---
Bump XDM semver after #5444
---
'@eth-optimism/sdk': patch
---
Fix firefox bug with getTokenPair
---
'@eth-optimism/chain-mon': patch
---
Fixes a bug in the wd-mon service where a node connection failure event was not handled correctly
---
'@eth-optimism/contracts-bedrock': minor
---
Increase precision in `SafeCall.hasMinGas`
---
'@eth-optimism/sdk': patch
---
Update the migrated withdrawal gas limit for non goerli networks
---
'@eth-optimism/contracts-periphery': patch
---
Change type for auth id on Faucet contracts from bytes to bytes32
---
'@eth-optimism/contracts-bedrock': minor
'@eth-optimism/contracts': minor
'@eth-optimism/sdk': minor
---
Update sdk contract addresses for bedrock
---
'@eth-optimism/sdk': patch
---
Add warning if bedrock is not turned on
...@@ -154,6 +154,7 @@ require ( ...@@ -154,6 +154,7 @@ require (
github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/status-im/keycard-go v0.2.0 // indirect github.com/status-im/keycard-go v0.2.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect
github.com/tklauser/go-sysconf v0.3.10 // indirect github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/tklauser/numcpus v0.5.0 // indirect github.com/tklauser/numcpus v0.5.0 // indirect
......
package node
import "math/big"
var bigZero = big.NewInt(0)
var bigOne = big.NewInt(1)
// returns a new big.Int for `end` to which `end - start` <= size.
// @note (start, end) is an inclusive range
func clampBigInt(start, end *big.Int, size uint64) *big.Int {
temp := new(big.Int)
count := temp.Sub(end, start).Uint64() + 1
if count <= size {
return end
}
// we result the allocated temp as the new end
temp.Add(start, big.NewInt(int64(size-1)))
return temp
}
package node
import (
"math/big"
"testing"
"github.com/stretchr/testify/assert"
)
func bigIntMatcher(num int64) func(*big.Int) bool {
return func(bi *big.Int) bool { return bi.Int64() == num }
}
func TestClampBigInt(t *testing.T) {
assert.True(t, true)
start := big.NewInt(1)
end := big.NewInt(10)
// When the (start, end) boudnds are within range
// the same end pointer should be returned
// larger range
result := clampBigInt(start, end, 20)
assert.True(t, end == result)
// exact range
result = clampBigInt(start, end, 10)
assert.True(t, end == result)
// smaller range
result = clampBigInt(start, end, 5)
assert.False(t, end == result)
assert.Equal(t, uint64(5), result.Uint64())
}
package node
import (
"context"
"math/big"
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
)
const (
// defaultDialTimeout is default duration the processor will wait on
// startup to make a connection to the backend
defaultDialTimeout = 5 * time.Second
// defaultRequestTimeout is the default duration the processor will
// wait for a request to be fulfilled
defaultRequestTimeout = 10 * time.Second
)
type EthClient interface {
FinalizedBlockHeight() (*big.Int, error)
BlockHeadersByRange(*big.Int, *big.Int) ([]*types.Header, error)
RawRpcClient() *rpc.Client
}
type client struct {
rpcClient *rpc.Client
}
func NewEthClient(rpcUrl string) (EthClient, error) {
ctxwt, cancel := context.WithTimeout(context.Background(), defaultDialTimeout)
defer cancel()
rpcClient, err := rpc.DialContext(ctxwt, rpcUrl)
if err != nil {
return nil, err
}
client := &client{rpcClient: rpcClient}
return client, nil
}
func (c *client) RawRpcClient() *rpc.Client {
return c.rpcClient
}
// FinalizedBlockHeight retrieves the latest block height in a finalized state
func (c *client) FinalizedBlockHeight() (*big.Int, error) {
ctxwt, cancel := context.WithTimeout(context.Background(), defaultRequestTimeout)
defer cancel()
var block *types.Block
err := c.rpcClient.CallContext(ctxwt, block, "eth_getBlockByNumber", "finalized", false)
if err != nil {
return nil, err
}
return block.Number(), nil
}
// BlockHeadersByRange will retrieve block headers within the specified range -- includsive. No restrictions
// are placed on the range such as blocks in the "latest", "safe" or "finalized" states. If the specified
// range is too large, `endHeight > latest`, the resulting list is truncated to the available headers
func (c *client) BlockHeadersByRange(startHeight, endHeight *big.Int) ([]*types.Header, error) {
count := new(big.Int).Sub(endHeight, startHeight).Uint64()
batchElems := make([]rpc.BatchElem, count)
for i := uint64(0); i < count; i++ {
height := new(big.Int).Add(startHeight, new(big.Int).SetUint64(i))
batchElems[i] = rpc.BatchElem{
Method: "eth_getBlockByNumber",
Args: []interface{}{toBlockNumArg(height), false},
Result: new(types.Header),
Error: nil,
}
}
ctxwt, cancel := context.WithTimeout(context.Background(), defaultRequestTimeout)
defer cancel()
err := c.rpcClient.BatchCallContext(ctxwt, batchElems)
if err != nil {
return nil, err
}
// Parse the headers.
// - Ensure integrity that they build on top of each other
// - Truncate out headers that do not exist (endHeight > "latest")
size := 0
headers := make([]*types.Header, count)
for i, batchElem := range batchElems {
if batchElem.Error != nil {
return nil, batchElem.Error
} else if batchElem.Result == nil {
break
}
header := batchElem.Result.(*types.Header)
if i > 0 && header.ParentHash != headers[i-1].Hash() {
// Warn here that we got a bad (malicious?) response
break
}
headers[i] = header
size = size + 1
}
headers = headers[:size]
return headers, nil
}
func toBlockNumArg(number *big.Int) string {
if number == nil {
return "latest"
}
pending := big.NewInt(-1)
if number.Cmp(pending) == 0 {
return "pending"
}
return hexutil.EncodeBig(number)
}
package node
import (
"math/big"
"github.com/stretchr/testify/mock"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
)
type MockEthClient struct {
mock.Mock
}
func (m *MockEthClient) FinalizedBlockHeight() (*big.Int, error) {
args := m.Called()
return args.Get(0).(*big.Int), args.Error(1)
}
func (m *MockEthClient) BlockHeadersByRange(from, to *big.Int) ([]*types.Header, error) {
args := m.Called(from, to)
return args.Get(0).([]*types.Header), args.Error(1)
}
func (m *MockEthClient) RawRpcClient() *rpc.Client {
args := m.Called()
return args.Get(0).(*rpc.Client)
}
package node
import (
"errors"
"math/big"
"github.com/ethereum/go-ethereum/core/types"
)
// Max number of headers that's bee returned by the Fetcher at once.
const maxHeaderBatchSize = 50
var ErrFetcherAndProviderMismatchedState = errors.New("the fetcher and provider have diverged in finalized state")
type Fetcher struct {
ethClient EthClient
lastHeader *types.Header
}
// NewFetcher instantiates a new instance of Fetcher against the supplied rpc client.
// The Fetcher will start fetching blocks starting from the supplied header unless
// nil, indicating genesis.
func NewFetcher(ethClient EthClient, fromHeader *types.Header) (*Fetcher, error) {
fetcher := &Fetcher{ethClient: ethClient, lastHeader: fromHeader}
return fetcher, nil
}
// NextConfirmedHeaders retrives the next set of headers that have been
// marked as finalized by the connected client
func (f *Fetcher) NextFinalizedHeaders() ([]*types.Header, error) {
finalizedBlockHeight, err := f.ethClient.FinalizedBlockHeight()
if err != nil {
return nil, err
}
if f.lastHeader != nil && f.lastHeader.Number.Cmp(finalizedBlockHeight) >= 0 {
// Warn if our fetcher is ahead of the provider. The fetcher should always
// be behind or at head with the provider.
return nil, nil
}
nextHeight := bigZero
if f.lastHeader != nil {
nextHeight = new(big.Int).Add(f.lastHeader.Number, bigOne)
}
endHeight := clampBigInt(nextHeight, finalizedBlockHeight, maxHeaderBatchSize)
headers, err := f.ethClient.BlockHeadersByRange(nextHeight, endHeight)
if err != nil {
return nil, err
}
numHeaders := int64(len(headers))
if numHeaders == 0 {
return nil, nil
} else if f.lastHeader != nil && headers[0].ParentHash != f.lastHeader.Hash() {
// The indexer's state is in an irrecoverable state relative to the provider. This
// should never happen since the indexer is dealing with only finalized blocks.
return nil, ErrFetcherAndProviderMismatchedState
}
f.lastHeader = headers[numHeaders-1]
return headers, nil
}
package node
import (
"math/big"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/ethereum/go-ethereum/core/types"
)
// make a set of headers which chain correctly
func makeHeaders(numHeaders uint64, prevHeader *types.Header) []*types.Header {
headers := make([]*types.Header, numHeaders)
for i := range headers {
if i == 0 {
if prevHeader == nil {
// genesis
headers[i] = &types.Header{Number: big.NewInt(0)}
} else {
// chain onto the previous header
headers[i] = &types.Header{Number: big.NewInt(prevHeader.Number.Int64() + 1)}
headers[i].ParentHash = prevHeader.Hash()
}
} else {
prevHeader = headers[i-1]
headers[i] = &types.Header{Number: big.NewInt(prevHeader.Number.Int64() + 1)}
headers[i].ParentHash = prevHeader.Hash()
}
}
return headers
}
func TestFetcherNextFinalizedHeadersNoOp(t *testing.T) {
client := new(MockEthClient)
// start from block 0 as the latest fetched block
lastHeader := &types.Header{Number: bigZero}
fetcher, err := NewFetcher(client, lastHeader)
assert.NoError(t, err)
// no new headers when matched with head
client.On("FinalizedBlockHeight").Return(big.NewInt(0), nil)
headers, err := fetcher.NextFinalizedHeaders()
assert.NoError(t, err)
assert.Empty(t, headers)
}
func TestFetcherNextFinalizedHeadersCursored(t *testing.T) {
client := new(MockEthClient)
// start from genesis
fetcher, err := NewFetcher(client, nil)
assert.NoError(t, err)
// blocks [0..4]
headers := makeHeaders(5, nil)
client.On("FinalizedBlockHeight").Return(big.NewInt(4), nil).Times(1) // Times so that we can override next
client.On("BlockHeadersByRange", mock.MatchedBy(bigIntMatcher(0)), mock.MatchedBy(bigIntMatcher(4))).Return(headers, nil)
headers, err = fetcher.NextFinalizedHeaders()
assert.NoError(t, err)
assert.Len(t, headers, 5)
// blocks [5..9]
headers = makeHeaders(5, headers[len(headers)-1])
client.On("FinalizedBlockHeight").Return(big.NewInt(9), nil)
client.On("BlockHeadersByRange", mock.MatchedBy(bigIntMatcher(5)), mock.MatchedBy(bigIntMatcher(9))).Return(headers, nil)
headers, err = fetcher.NextFinalizedHeaders()
assert.NoError(t, err)
assert.Len(t, headers, 5)
}
func TestFetcherNextFinalizedHeadersMaxHeaderBatch(t *testing.T) {
client := new(MockEthClient)
// start from genesis
fetcher, err := NewFetcher(client, nil)
assert.NoError(t, err)
// blocks [0..maxBatchSize] size == maxBatchSize = 1
headers := makeHeaders(maxHeaderBatchSize, nil)
client.On("FinalizedBlockHeight").Return(big.NewInt(maxHeaderBatchSize), nil)
// clamped by the max batch size
client.On("BlockHeadersByRange", mock.MatchedBy(bigIntMatcher(0)), mock.MatchedBy(bigIntMatcher(maxHeaderBatchSize-1))).Return(headers, nil)
headers, err = fetcher.NextFinalizedHeaders()
assert.NoError(t, err)
assert.Len(t, headers, maxHeaderBatchSize)
// blocks [maxBatchSize..maxBatchSize]
headers = makeHeaders(1, headers[len(headers)-1])
client.On("BlockHeadersByRange", mock.MatchedBy(bigIntMatcher(maxHeaderBatchSize)), mock.MatchedBy(bigIntMatcher(maxHeaderBatchSize))).Return(headers, nil)
headers, err = fetcher.NextFinalizedHeaders()
assert.NoError(t, err)
assert.Len(t, headers, 1)
}
func TestFetcherMismatchedProviderStateError(t *testing.T) {
client := new(MockEthClient)
// start from genesis
fetcher, err := NewFetcher(client, nil)
assert.NoError(t, err)
// blocks [0..4]
headers := makeHeaders(5, nil)
client.On("FinalizedBlockHeight").Return(big.NewInt(4), nil).Times(1) // Times so that we can override next
client.On("BlockHeadersByRange", mock.MatchedBy(bigIntMatcher(0)), mock.MatchedBy(bigIntMatcher(4))).Return(headers, nil)
headers, err = fetcher.NextFinalizedHeaders()
assert.NoError(t, err)
assert.Len(t, headers, 5)
// blocks [5..9]. Next batch is not chained correctly (starts again from genesis)
headers = makeHeaders(5, nil)
client.On("FinalizedBlockHeight").Return(big.NewInt(9), nil)
client.On("BlockHeadersByRange", mock.MatchedBy(bigIntMatcher(5)), mock.MatchedBy(bigIntMatcher(9))).Return(headers, nil)
headers, err = fetcher.NextFinalizedHeaders()
assert.Nil(t, headers)
assert.Equal(t, ErrFetcherAndProviderMismatchedState, err)
}
...@@ -29,11 +29,11 @@ ...@@ -29,11 +29,11 @@
}, },
"devDependencies": { "devDependencies": {
"@babel/eslint-parser": "^7.5.4", "@babel/eslint-parser": "^7.5.4",
"@eth-optimism/contracts": "^0.5.40", "@eth-optimism/contracts": "^0.6.0",
"@eth-optimism/contracts-bedrock": "0.13.2", "@eth-optimism/contracts-bedrock": "0.14.0",
"@eth-optimism/contracts-periphery": "^1.0.7", "@eth-optimism/contracts-periphery": "^1.0.8",
"@eth-optimism/core-utils": "0.12.0", "@eth-optimism/core-utils": "0.12.0",
"@eth-optimism/sdk": "2.0.2", "@eth-optimism/sdk": "2.1.0",
"@ethersproject/abstract-provider": "^5.7.0", "@ethersproject/abstract-provider": "^5.7.0",
"@ethersproject/providers": "^5.7.0", "@ethersproject/providers": "^5.7.0",
"@ethersproject/transactions": "^5.7.0", "@ethersproject/transactions": "^5.7.0",
......
...@@ -182,7 +182,7 @@ func doMigration(mutableDB *state.StateDB, dbFactory util.DBFactory, addresses [ ...@@ -182,7 +182,7 @@ func doMigration(mutableDB *state.StateDB, dbFactory util.DBFactory, addresses [
if noCheck { if noCheck {
log.Error("unknown slot type", "slot", key, "type", slotType) log.Error("unknown slot type", "slot", key, "type", slotType)
} else { } else {
log.Crit("unknown slot type %d, should never happen", slotType) log.Crit("unknown slot type, should never happen", "type", slotType)
} }
} }
...@@ -209,10 +209,10 @@ func doMigration(mutableDB *state.StateDB, dbFactory util.DBFactory, addresses [ ...@@ -209,10 +209,10 @@ func doMigration(mutableDB *state.StateDB, dbFactory util.DBFactory, addresses [
// Print first 10 accounts without balance // Print first 10 accounts without balance
aleft := 10 aleft := 10
log.Info("Listing first %d accounts without balance", aleft) log.Info("Listing first accounts without balance", "num", aleft)
for i, a := range addresses { for i, a := range addresses {
if !seenAccounts[a] { if !seenAccounts[a] {
log.Info("Account[%d] without balance", i, "addr", a) log.Info("Account without balance", "idx", i, "addr", a)
aleft-- aleft--
} }
if aleft == 0 { if aleft == 0 {
......
...@@ -54,6 +54,33 @@ type Challenger struct { ...@@ -54,6 +54,33 @@ type Challenger struct {
networkTimeout time.Duration networkTimeout time.Duration
} }
// From returns the address of the account used to send transactions.
func (c *Challenger) From() common.Address {
return c.txMgr.From()
}
// Client returns the client for the settlement layer.
func (c *Challenger) Client() *ethclient.Client {
return c.l1Client
}
func (c *Challenger) NewOracleSubscription() (*Subscription, error) {
query, err := BuildOutputLogFilter(c.l2ooABI)
if err != nil {
return nil, err
}
return NewSubscription(query, c.Client(), c.log), nil
}
// NewFactorySubscription creates a new [Subscription] listening to the DisputeGameFactory contract.
func (c *Challenger) NewFactorySubscription() (*Subscription, error) {
query, err := BuildDisputeGameLogFilter(c.dgfABI)
if err != nil {
return nil, err
}
return NewSubscription(query, c.Client(), c.log), nil
}
// NewChallenger creates a new Challenger // NewChallenger creates a new Challenger
func NewChallenger(cfg config.Config, l log.Logger, m metrics.Metricer) (*Challenger, error) { func NewChallenger(cfg config.Config, l log.Logger, m metrics.Metricer) (*Challenger, error) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
......
package challenger
import (
"errors"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
)
var ErrMissingFactoryEvent = errors.New("missing factory event")
// BuildDisputeGameLogFilter creates a filter query for the DisputeGameFactory contract.
//
// The `DisputeGameCreated` event is encoded as:
// 0: address indexed disputeProxy,
// 1: GameType indexed gameType,
// 2: Claim indexed rootClaim,
func BuildDisputeGameLogFilter(contract *abi.ABI) (ethereum.FilterQuery, error) {
event := contract.Events["DisputeGameCreated"]
if event.ID == (common.Hash{}) {
return ethereum.FilterQuery{}, ErrMissingFactoryEvent
}
query := ethereum.FilterQuery{
Topics: [][]common.Hash{
{event.ID},
},
}
return query, nil
}
package challenger
import (
"testing"
"github.com/stretchr/testify/require"
eth "github.com/ethereum/go-ethereum"
abi "github.com/ethereum/go-ethereum/accounts/abi"
common "github.com/ethereum/go-ethereum/common"
)
// TestBuildDisputeGameLogFilter_Succeeds tests that the DisputeGame
// Log Filter is built correctly.
func TestBuildDisputeGameLogFilter_Succeeds(t *testing.T) {
event := abi.Event{
ID: [32]byte{0x01},
}
filterQuery := eth.FilterQuery{
Topics: [][]common.Hash{
{event.ID},
},
}
dgfABI := abi.ABI{
Events: map[string]abi.Event{
"DisputeGameCreated": event,
},
}
query, err := BuildDisputeGameLogFilter(&dgfABI)
require.Equal(t, filterQuery, query)
require.NoError(t, err)
}
// TestBuildDisputeGameLogFilter_Fails tests that the DisputeGame
// Log Filter fails when the event definition is missing.
func TestBuildDisputeGameLogFilter_Fails(t *testing.T) {
dgfABI := abi.ABI{
Events: map[string]abi.Event{},
}
_, err := BuildDisputeGameLogFilter(&dgfABI)
require.ErrorIs(t, ErrMissingFactoryEvent, err)
}
...@@ -61,6 +61,11 @@ func (s *Subscription) Started() bool { ...@@ -61,6 +61,11 @@ func (s *Subscription) Started() bool {
return s.started return s.started
} }
// Logs returns the log channel.
func (s *Subscription) Logs() <-chan types.Log {
return s.logs
}
// Subscribe constructs the subscription. // Subscribe constructs the subscription.
func (s *Subscription) Subscribe() error { func (s *Subscription) Subscribe() error {
s.log.Info("Subscribing to", "query", s.query.Topics, "id", s.id) s.log.Info("Subscribing to", "query", s.query.Topics, "id", s.id)
......
package challenger package main
import ( import (
"context" "context"
...@@ -10,8 +10,11 @@ import ( ...@@ -10,8 +10,11 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/metrics" "github.com/ethereum-optimism/optimism/op-challenger/metrics"
"github.com/ethereum-optimism/optimism/op-service/opio" "github.com/ethereum-optimism/optimism/op-service/opio"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof"
oprpc "github.com/ethereum-optimism/optimism/op-service/rpc" "github.com/ethereum-optimism/optimism/op-challenger/challenger"
"github.com/ethereum-optimism/optimism/op-service/pprof"
"github.com/ethereum-optimism/optimism/op-service/rpc"
) )
// Main is the entrypoint into the Challenger. This method executes the // Main is the entrypoint into the Challenger. This method executes the
...@@ -24,7 +27,7 @@ func Main(logger log.Logger, version string, cfg *config.Config) error { ...@@ -24,7 +27,7 @@ func Main(logger log.Logger, version string, cfg *config.Config) error {
m := metrics.NewMetrics("default") m := metrics.NewMetrics("default")
logger.Info("Initializing Challenger") logger.Info("Initializing Challenger")
challenger, err := NewChallenger(*cfg, logger, m) service, err := challenger.NewChallenger(*cfg, logger, m)
if err != nil { if err != nil {
logger.Error("Unable to create the Challenger", "error", err) logger.Error("Unable to create the Challenger", "error", err)
return err return err
...@@ -32,19 +35,19 @@ func Main(logger log.Logger, version string, cfg *config.Config) error { ...@@ -32,19 +35,19 @@ func Main(logger log.Logger, version string, cfg *config.Config) error {
logger.Info("Starting Challenger") logger.Info("Starting Challenger")
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
if err := challenger.Start(); err != nil { if err := service.Start(); err != nil {
cancel() cancel()
logger.Error("Unable to start Challenger", "error", err) logger.Error("Unable to start Challenger", "error", err)
return err return err
} }
defer challenger.Stop() defer service.Stop()
logger.Info("Challenger started") logger.Info("Challenger started")
pprofConfig := cfg.PprofConfig pprofConfig := cfg.PprofConfig
if pprofConfig.Enabled { if pprofConfig.Enabled {
logger.Info("starting pprof", "addr", pprofConfig.ListenAddr, "port", pprofConfig.ListenPort) logger.Info("starting pprof", "addr", pprofConfig.ListenAddr, "port", pprofConfig.ListenPort)
go func() { go func() {
if err := oppprof.ListenAndServe(ctx, pprofConfig.ListenAddr, pprofConfig.ListenPort); err != nil { if err := pprof.ListenAndServe(ctx, pprofConfig.ListenAddr, pprofConfig.ListenPort); err != nil {
logger.Error("error starting pprof", "err", err) logger.Error("error starting pprof", "err", err)
} }
}() }()
...@@ -58,11 +61,11 @@ func Main(logger log.Logger, version string, cfg *config.Config) error { ...@@ -58,11 +61,11 @@ func Main(logger log.Logger, version string, cfg *config.Config) error {
logger.Error("error starting metrics server", err) logger.Error("error starting metrics server", err)
} }
}() }()
m.StartBalanceMetrics(ctx, logger, challenger.l1Client, challenger.txMgr.From()) m.StartBalanceMetrics(ctx, logger, service.Client(), service.From())
} }
rpcCfg := cfg.RPCConfig rpcCfg := cfg.RPCConfig
server := oprpc.NewServer(rpcCfg.ListenAddr, rpcCfg.ListenPort, version, oprpc.WithLogger(logger)) server := rpc.NewServer(rpcCfg.ListenAddr, rpcCfg.ListenPort, version, rpc.WithLogger(logger))
if err := server.Start(); err != nil { if err := server.Start(); err != nil {
cancel() cancel()
return fmt.Errorf("error starting RPC server: %w", err) return fmt.Errorf("error starting RPC server: %w", err)
......
package main package main
import ( import (
"fmt"
"os" "os"
challenger "github.com/ethereum-optimism/optimism/op-challenger/challenger" log "github.com/ethereum/go-ethereum/log"
cli "github.com/urfave/cli"
watch "github.com/ethereum-optimism/optimism/op-challenger/cmd/watch"
config "github.com/ethereum-optimism/optimism/op-challenger/config" config "github.com/ethereum-optimism/optimism/op-challenger/config"
flags "github.com/ethereum-optimism/optimism/op-challenger/flags" flags "github.com/ethereum-optimism/optimism/op-challenger/flags"
version "github.com/ethereum-optimism/optimism/op-challenger/version" version "github.com/ethereum-optimism/optimism/op-challenger/version"
log "github.com/ethereum/go-ethereum/log"
cli "github.com/urfave/cli"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
) )
...@@ -37,7 +36,7 @@ var VersionWithMeta = func() string { ...@@ -37,7 +36,7 @@ var VersionWithMeta = func() string {
func main() { func main() {
args := os.Args args := os.Args
if err := run(args, challenger.Main); err != nil { if err := run(args, Main); err != nil {
log.Crit("Application failed", "err", err) log.Crit("Application failed", "err", err)
} }
} }
...@@ -59,7 +58,7 @@ func run(args []string, action ConfigAction) error { ...@@ -59,7 +58,7 @@ func run(args []string, action ConfigAction) error {
app.Usage = "Challenge Invalid L2OutputOracle Outputs" app.Usage = "Challenge Invalid L2OutputOracle Outputs"
app.Description = "A modular op-stack challenge agent for dispute games written in golang." app.Description = "A modular op-stack challenge agent for dispute games written in golang."
app.Action = func(ctx *cli.Context) error { app.Action = func(ctx *cli.Context) error {
logger, err := setupLogging(ctx) logger, err := config.LoggerFromCLI(ctx)
if err != nil { if err != nil {
return err return err
} }
...@@ -71,15 +70,12 @@ func run(args []string, action ConfigAction) error { ...@@ -71,15 +70,12 @@ func run(args []string, action ConfigAction) error {
} }
return action(logger, VersionWithMeta, cfg) return action(logger, VersionWithMeta, cfg)
} }
app.Commands = []cli.Command{
{
Name: "watch",
Subcommands: watch.Subcommands,
},
}
return app.Run(args) return app.Run(args)
} }
func setupLogging(ctx *cli.Context) (log.Logger, error) {
logCfg := oplog.ReadCLIConfig(ctx)
if err := logCfg.Check(); err != nil {
return nil, fmt.Errorf("log config error: %w", err)
}
logger := oplog.NewLogger(logCfg)
return logger, nil
}
package watch
import (
"github.com/urfave/cli"
"github.com/ethereum-optimism/optimism/op-challenger/config"
)
var Subcommands = cli.Commands{
{
Name: "oracle",
Usage: "Watches the L2OutputOracle for new output proposals",
Action: func(ctx *cli.Context) error {
logger, err := config.LoggerFromCLI(ctx)
if err != nil {
return err
}
logger.Info("Listening for new output proposals")
cfg, err := config.NewConfigFromCLI(ctx)
if err != nil {
return err
}
return Oracle(logger, cfg)
},
},
{
Name: "factory",
Usage: "Watches the DisputeGameFactory for new dispute games",
Action: func(ctx *cli.Context) error {
logger, err := config.LoggerFromCLI(ctx)
if err != nil {
return err
}
logger.Info("Listening for new dispute games")
cfg, err := config.NewConfigFromCLI(ctx)
if err != nil {
return err
}
return Factory(logger, cfg)
},
},
}
package watch
import (
"fmt"
"os"
"os/signal"
"syscall"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-challenger/challenger"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/metrics"
)
// Factory listens to the DisputeGameFactory for newly created dispute games.
func Factory(logger log.Logger, cfg *config.Config) error {
if err := cfg.Check(); err != nil {
return fmt.Errorf("invalid config: %w", err)
}
m := metrics.NewMetrics("default")
service, err := challenger.NewChallenger(*cfg, logger, m)
if err != nil {
logger.Error("Unable to create the Challenger", "error", err)
return err
}
logger.Info("Listening for DisputeGameCreated events from the DisputeGameFactory contract", "dgf", cfg.DGFAddress.String())
subscription, err := service.NewFactorySubscription()
if err != nil {
logger.Error("Unable to create the subscription", "error", err)
return err
}
err = subscription.Subscribe()
if err != nil {
logger.Error("Unable to subscribe to the DisputeGameFactory contract", "error", err)
return err
}
defer subscription.Quit()
interruptChannel := make(chan os.Signal, 1)
signal.Notify(interruptChannel, []os.Signal{
os.Interrupt,
os.Kill,
syscall.SIGTERM,
syscall.SIGQUIT,
}...)
for {
select {
case log := <-subscription.Logs():
logger.Info("Received log", "log", log)
case <-interruptChannel:
logger.Info("Received interrupt signal, exiting...")
}
}
}
package watch
import (
"fmt"
"os"
"os/signal"
"syscall"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-challenger/challenger"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/metrics"
)
// Oracle listens to the L2OutputOracle for newly proposed outputs.
func Oracle(logger log.Logger, cfg *config.Config) error {
if err := cfg.Check(); err != nil {
return fmt.Errorf("invalid config: %w", err)
}
m := metrics.NewMetrics("default")
service, err := challenger.NewChallenger(*cfg, logger, m)
if err != nil {
logger.Error("Unable to create the Challenger", "error", err)
return err
}
logger.Info("Listening for OutputProposed events from the L2OutputOracle contract", "l2oo", cfg.L2OOAddress.String())
subscription, err := service.NewOracleSubscription()
if err != nil {
logger.Error("Unable to create the subscription", "error", err)
return err
}
err = subscription.Subscribe()
if err != nil {
logger.Error("Unable to subscribe to the L2OutputOracle contract", "error", err)
return err
}
defer subscription.Quit()
interruptChannel := make(chan os.Signal, 1)
signal.Notify(interruptChannel, []os.Signal{
os.Interrupt,
os.Kill,
syscall.SIGTERM,
syscall.SIGQUIT,
}...)
for {
select {
case log := <-subscription.Logs():
logger.Info("Received log", "log", log)
case <-interruptChannel:
logger.Info("Received interrupt signal, exiting...")
}
}
}
package config
import (
"fmt"
log "github.com/ethereum/go-ethereum/log"
cli "github.com/urfave/cli"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
)
// LoggerFromCLI creates a [log.Logger] from the
// supplied [cli.Context].
func LoggerFromCLI(ctx *cli.Context) (log.Logger, error) {
logCfg := oplog.ReadCLIConfig(ctx)
if err := logCfg.Check(); err != nil {
return nil, fmt.Errorf("log config error: %w", err)
}
logger := oplog.NewLogger(logCfg)
return logger, nil
}
...@@ -13,19 +13,14 @@ import ( ...@@ -13,19 +13,14 @@ import (
var Mainnet = rollup.Config{ var Mainnet = rollup.Config{
Genesis: rollup.Genesis{ Genesis: rollup.Genesis{
L1: eth.BlockID{ L1: eth.BlockID{
// moose: Update this during migration Hash: common.HexToHash("0x438335a20d98863a4c0c97999eb2481921ccd28553eac6f913af7c12aec04108"),
Hash: common.HexToHash("0x"), Number: 17422590,
// moose: Update this during migration
Number: 0,
}, },
L2: eth.BlockID{ L2: eth.BlockID{
// moose: Update this during migration Hash: common.HexToHash("0xdbf6a80fef073de06add9b0d14026d6e5a86c85f6d102c36d3d8e9cf89c2afd3"),
Hash: common.HexToHash("0x"), Number: 105235063,
// moose: Update this during migration
Number: 0,
}, },
// moose: Update this during migration L2Time: 1686068903,
L2Time: 0,
SystemConfig: eth.SystemConfig{ SystemConfig: eth.SystemConfig{
BatcherAddr: common.HexToAddress("0x6887246668a3b87f54deb3b94ba47a6f63f32985"), BatcherAddr: common.HexToAddress("0x6887246668a3b87f54deb3b94ba47a6f63f32985"),
Overhead: eth.Bytes32(common.HexToHash("0x00000000000000000000000000000000000000000000000000000000000000bc")), Overhead: eth.Bytes32(common.HexToHash("0x00000000000000000000000000000000000000000000000000000000000000bc")),
...@@ -76,9 +71,8 @@ var Goerli = rollup.Config{ ...@@ -76,9 +71,8 @@ var Goerli = rollup.Config{
} }
var NetworksByName = map[string]rollup.Config{ var NetworksByName = map[string]rollup.Config{
"goerli": Goerli, "goerli": Goerli,
// moose: Update this during migration "mainnet": Mainnet,
// "mainnet": Mainnet,
} }
var L2ChainIDToNetworkName = func() map[string]string { var L2ChainIDToNetworkName = func() map[string]string {
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"io" "io"
"io/fs" "io/fs"
"math"
"os" "os"
"os/exec" "os/exec"
...@@ -21,10 +22,13 @@ import ( ...@@ -21,10 +22,13 @@ import (
oppio "github.com/ethereum-optimism/optimism/op-program/io" oppio "github.com/ethereum-optimism/optimism/op-program/io"
"github.com/ethereum-optimism/optimism/op-program/preimage" "github.com/ethereum-optimism/optimism/op-program/preimage"
opservice "github.com/ethereum-optimism/optimism/op-service" opservice "github.com/ethereum-optimism/optimism/op-service"
opclient "github.com/ethereum-optimism/optimism/op-service/client"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
const maxRPCRetries = math.MaxInt
type L2Source struct { type L2Source struct {
*sources.L2Client *sources.L2Client
*sources.DebugClient *sources.DebugClient
...@@ -199,16 +203,21 @@ func makePrefetcher(ctx context.Context, logger log.Logger, kv kvstore.KV, cfg * ...@@ -199,16 +203,21 @@ func makePrefetcher(ctx context.Context, logger log.Logger, kv kvstore.KV, cfg *
return nil, fmt.Errorf("failed to setup L2 RPC: %w", err) return nil, fmt.Errorf("failed to setup L2 RPC: %w", err)
} }
l1Backoff := opclient.NewRetryingClient(l1RPC, maxRPCRetries)
l2Backoff := opclient.NewRetryingClient(l2RPC, maxRPCRetries)
l1ClCfg := sources.L1ClientDefaultConfig(cfg.Rollup, cfg.L1TrustRPC, cfg.L1RPCKind) l1ClCfg := sources.L1ClientDefaultConfig(cfg.Rollup, cfg.L1TrustRPC, cfg.L1RPCKind)
l2ClCfg := sources.L2ClientDefaultConfig(cfg.Rollup, true) l1Cl, err := sources.NewL1Client(l1Backoff, logger, nil, l1ClCfg)
l1Cl, err := sources.NewL1Client(l1RPC, logger, nil, l1ClCfg)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create L1 client: %w", err) return nil, fmt.Errorf("failed to create L1 client: %w", err)
} }
l2Cl, err := sources.NewL2Client(l2RPC, logger, nil, l2ClCfg)
l2ClCfg := sources.L2ClientDefaultConfig(cfg.Rollup, true)
l2Cl, err := sources.NewL2Client(l2Backoff, logger, nil, l2ClCfg)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create L2 client: %w", err) return nil, fmt.Errorf("failed to create L2 client: %w", err)
} }
l2DebugCl := &L2Source{L2Client: l2Cl, DebugClient: sources.NewDebugClient(l2RPC.CallContext)} l2DebugCl := &L2Source{L2Client: l2Cl, DebugClient: sources.NewDebugClient(l2RPC.CallContext)}
return prefetcher.NewPrefetcher(logger, l1Cl, l2DebugCl, kv), nil return prefetcher.NewPrefetcher(logger, l1Cl, l2DebugCl, kv), nil
} }
......
...@@ -42,8 +42,8 @@ type Prefetcher struct { ...@@ -42,8 +42,8 @@ type Prefetcher struct {
func NewPrefetcher(logger log.Logger, l1Fetcher L1Source, l2Fetcher L2Source, kvStore kvstore.KV) *Prefetcher { func NewPrefetcher(logger log.Logger, l1Fetcher L1Source, l2Fetcher L2Source, kvStore kvstore.KV) *Prefetcher {
return &Prefetcher{ return &Prefetcher{
logger: logger, logger: logger,
l1Fetcher: NewRetryingL1Source(logger, l1Fetcher), l1Fetcher: l1Fetcher,
l2Fetcher: NewRetryingL2Source(logger, l2Fetcher), l2Fetcher: l2Fetcher,
kvStore: kvStore, kvStore: kvStore,
} }
} }
......
package prefetcher
import (
"context"
"math"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-service/backoff"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
const maxAttempts = math.MaxInt // Succeed or die trying
type RetryingL1Source struct {
logger log.Logger
source L1Source
strategy backoff.Strategy
}
func NewRetryingL1Source(logger log.Logger, source L1Source) *RetryingL1Source {
return &RetryingL1Source{
logger: logger,
source: source,
strategy: backoff.Exponential(),
}
}
func (s *RetryingL1Source) InfoByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, error) {
var info eth.BlockInfo
err := backoff.DoCtx(ctx, maxAttempts, s.strategy, func() error {
res, err := s.source.InfoByHash(ctx, blockHash)
if err != nil {
s.logger.Warn("Failed to retrieve info", "hash", blockHash, "err", err)
return err
}
info = res
return nil
})
return info, err
}
func (s *RetryingL1Source) InfoAndTxsByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Transactions, error) {
var info eth.BlockInfo
var txs types.Transactions
err := backoff.DoCtx(ctx, maxAttempts, s.strategy, func() error {
i, t, err := s.source.InfoAndTxsByHash(ctx, blockHash)
if err != nil {
s.logger.Warn("Failed to retrieve l1 info and txs", "hash", blockHash, "err", err)
return err
}
info = i
txs = t
return nil
})
return info, txs, err
}
func (s *RetryingL1Source) FetchReceipts(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Receipts, error) {
var info eth.BlockInfo
var rcpts types.Receipts
err := backoff.DoCtx(ctx, maxAttempts, s.strategy, func() error {
i, r, err := s.source.FetchReceipts(ctx, blockHash)
if err != nil {
s.logger.Warn("Failed to fetch receipts", "hash", blockHash, "err", err)
return err
}
info = i
rcpts = r
return nil
})
return info, rcpts, err
}
var _ L1Source = (*RetryingL1Source)(nil)
type RetryingL2Source struct {
logger log.Logger
source L2Source
strategy backoff.Strategy
}
func (s *RetryingL2Source) InfoAndTxsByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Transactions, error) {
var info eth.BlockInfo
var txs types.Transactions
err := backoff.DoCtx(ctx, maxAttempts, s.strategy, func() error {
i, t, err := s.source.InfoAndTxsByHash(ctx, blockHash)
if err != nil {
s.logger.Warn("Failed to retrieve l2 info and txs", "hash", blockHash, "err", err)
return err
}
info = i
txs = t
return nil
})
return info, txs, err
}
func (s *RetryingL2Source) NodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) {
var node []byte
err := backoff.DoCtx(ctx, maxAttempts, s.strategy, func() error {
n, err := s.source.NodeByHash(ctx, hash)
if err != nil {
s.logger.Warn("Failed to retrieve node", "hash", hash, "err", err)
return err
}
node = n
return nil
})
return node, err
}
func (s *RetryingL2Source) CodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) {
var code []byte
err := backoff.DoCtx(ctx, maxAttempts, s.strategy, func() error {
c, err := s.source.CodeByHash(ctx, hash)
if err != nil {
s.logger.Warn("Failed to retrieve code", "hash", hash, "err", err)
return err
}
code = c
return nil
})
return code, err
}
func NewRetryingL2Source(logger log.Logger, source L2Source) *RetryingL2Source {
return &RetryingL2Source{
logger: logger,
source: source,
strategy: backoff.Exponential(),
}
}
var _ L2Source = (*RetryingL2Source)(nil)
package prefetcher
import (
"context"
"errors"
"testing"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-node/testutils"
"github.com/ethereum-optimism/optimism/op-service/backoff"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
func TestRetryingL1Source(t *testing.T) {
ctx := context.Background()
hash := common.Hash{0xab}
info := &testutils.MockBlockInfo{InfoHash: hash}
// The mock really doesn't like returning nil for a eth.BlockInfo so return a value we expect to be ignored instead
wrongInfo := &testutils.MockBlockInfo{InfoHash: common.Hash{0x99}}
txs := types.Transactions{
&types.Transaction{},
}
rcpts := types.Receipts{
&types.Receipt{},
}
t.Run("InfoByHash Success", func(t *testing.T) {
source, mock := createL1Source(t)
defer mock.AssertExpectations(t)
mock.ExpectInfoByHash(hash, info, nil)
result, err := source.InfoByHash(ctx, hash)
require.NoError(t, err)
require.Equal(t, info, result)
})
t.Run("InfoByHash Error", func(t *testing.T) {
source, mock := createL1Source(t)
defer mock.AssertExpectations(t)
expectedErr := errors.New("boom")
mock.ExpectInfoByHash(hash, wrongInfo, expectedErr)
mock.ExpectInfoByHash(hash, info, nil)
result, err := source.InfoByHash(ctx, hash)
require.NoError(t, err)
require.Equal(t, info, result)
})
t.Run("InfoAndTxsByHash Success", func(t *testing.T) {
source, mock := createL1Source(t)
defer mock.AssertExpectations(t)
mock.ExpectInfoAndTxsByHash(hash, info, txs, nil)
actualInfo, actualTxs, err := source.InfoAndTxsByHash(ctx, hash)
require.NoError(t, err)
require.Equal(t, info, actualInfo)
require.Equal(t, txs, actualTxs)
})
t.Run("InfoAndTxsByHash Error", func(t *testing.T) {
source, mock := createL1Source(t)
defer mock.AssertExpectations(t)
expectedErr := errors.New("boom")
mock.ExpectInfoAndTxsByHash(hash, wrongInfo, nil, expectedErr)
mock.ExpectInfoAndTxsByHash(hash, info, txs, nil)
actualInfo, actualTxs, err := source.InfoAndTxsByHash(ctx, hash)
require.NoError(t, err)
require.Equal(t, info, actualInfo)
require.Equal(t, txs, actualTxs)
})
t.Run("FetchReceipts Success", func(t *testing.T) {
source, mock := createL1Source(t)
defer mock.AssertExpectations(t)
mock.ExpectFetchReceipts(hash, info, rcpts, nil)
actualInfo, actualRcpts, err := source.FetchReceipts(ctx, hash)
require.NoError(t, err)
require.Equal(t, info, actualInfo)
require.Equal(t, rcpts, actualRcpts)
})
t.Run("FetchReceipts Error", func(t *testing.T) {
source, mock := createL1Source(t)
defer mock.AssertExpectations(t)
expectedErr := errors.New("boom")
mock.ExpectFetchReceipts(hash, wrongInfo, nil, expectedErr)
mock.ExpectFetchReceipts(hash, info, rcpts, nil)
actualInfo, actualRcpts, err := source.FetchReceipts(ctx, hash)
require.NoError(t, err)
require.Equal(t, info, actualInfo)
require.Equal(t, rcpts, actualRcpts)
})
}
func createL1Source(t *testing.T) (*RetryingL1Source, *testutils.MockL1Source) {
logger := testlog.Logger(t, log.LvlDebug)
mock := &testutils.MockL1Source{}
source := NewRetryingL1Source(logger, mock)
// Avoid sleeping in tests by using a fixed backoff strategy with no delay
source.strategy = backoff.Fixed(0)
return source, mock
}
func TestRetryingL2Source(t *testing.T) {
ctx := context.Background()
hash := common.Hash{0xab}
info := &testutils.MockBlockInfo{InfoHash: hash}
// The mock really doesn't like returning nil for a eth.BlockInfo so return a value we expect to be ignored instead
wrongInfo := &testutils.MockBlockInfo{InfoHash: common.Hash{0x99}}
txs := types.Transactions{
&types.Transaction{},
}
data := []byte{1, 2, 3, 4, 5}
t.Run("InfoAndTxsByHash Success", func(t *testing.T) {
source, mock := createL2Source(t)
defer mock.AssertExpectations(t)
mock.ExpectInfoAndTxsByHash(hash, info, txs, nil)
actualInfo, actualTxs, err := source.InfoAndTxsByHash(ctx, hash)
require.NoError(t, err)
require.Equal(t, info, actualInfo)
require.Equal(t, txs, actualTxs)
})
t.Run("InfoAndTxsByHash Error", func(t *testing.T) {
source, mock := createL2Source(t)
defer mock.AssertExpectations(t)
expectedErr := errors.New("boom")
mock.ExpectInfoAndTxsByHash(hash, wrongInfo, nil, expectedErr)
mock.ExpectInfoAndTxsByHash(hash, info, txs, nil)
actualInfo, actualTxs, err := source.InfoAndTxsByHash(ctx, hash)
require.NoError(t, err)
require.Equal(t, info, actualInfo)
require.Equal(t, txs, actualTxs)
})
t.Run("NodeByHash Success", func(t *testing.T) {
source, mock := createL2Source(t)
defer mock.AssertExpectations(t)
mock.ExpectNodeByHash(hash, data, nil)
actual, err := source.NodeByHash(ctx, hash)
require.NoError(t, err)
require.Equal(t, data, actual)
})
t.Run("NodeByHash Error", func(t *testing.T) {
source, mock := createL2Source(t)
defer mock.AssertExpectations(t)
expectedErr := errors.New("boom")
mock.ExpectNodeByHash(hash, nil, expectedErr)
mock.ExpectNodeByHash(hash, data, nil)
actual, err := source.NodeByHash(ctx, hash)
require.NoError(t, err)
require.Equal(t, data, actual)
})
t.Run("CodeByHash Success", func(t *testing.T) {
source, mock := createL2Source(t)
defer mock.AssertExpectations(t)
mock.ExpectCodeByHash(hash, data, nil)
actual, err := source.CodeByHash(ctx, hash)
require.NoError(t, err)
require.Equal(t, data, actual)
})
t.Run("CodeByHash Error", func(t *testing.T) {
source, mock := createL2Source(t)
defer mock.AssertExpectations(t)
expectedErr := errors.New("boom")
mock.ExpectCodeByHash(hash, nil, expectedErr)
mock.ExpectCodeByHash(hash, data, nil)
actual, err := source.CodeByHash(ctx, hash)
require.NoError(t, err)
require.Equal(t, data, actual)
})
}
func createL2Source(t *testing.T) (*RetryingL2Source, *MockL2Source) {
logger := testlog.Logger(t, log.LvlDebug)
mock := &MockL2Source{}
source := NewRetryingL2Source(logger, mock)
// Avoid sleeping in tests by using a fixed backoff strategy with no delay
source.strategy = backoff.Fixed(0)
return source, mock
}
type MockL2Source struct {
mock.Mock
}
func (m *MockL2Source) InfoAndTxsByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Transactions, error) {
out := m.Mock.MethodCalled("InfoAndTxsByHash", blockHash)
return out[0].(eth.BlockInfo), out[1].(types.Transactions), *out[2].(*error)
}
func (m *MockL2Source) NodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) {
out := m.Mock.MethodCalled("NodeByHash", hash)
return out[0].([]byte), *out[1].(*error)
}
func (m *MockL2Source) CodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) {
out := m.Mock.MethodCalled("CodeByHash", hash)
return out[0].([]byte), *out[1].(*error)
}
func (m *MockL2Source) ExpectInfoAndTxsByHash(blockHash common.Hash, info eth.BlockInfo, txs types.Transactions, err error) {
m.Mock.On("InfoAndTxsByHash", blockHash).Once().Return(info, txs, &err)
}
func (m *MockL2Source) ExpectNodeByHash(hash common.Hash, node []byte, err error) {
m.Mock.On("NodeByHash", hash).Once().Return(node, &err)
}
func (m *MockL2Source) ExpectCodeByHash(hash common.Hash, code []byte, err error) {
m.Mock.On("CodeByHash", hash).Once().Return(code, &err)
}
var _ L2Source = (*MockL2Source)(nil)
package client
import (
"context"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum-optimism/optimism/op-node/client"
"github.com/ethereum-optimism/optimism/op-service/backoff"
)
var (
// ExponentialBackoff is the default backoff strategy.
ExponentialBackoff = backoff.Exponential()
)
// retryingClient wraps a [client.RPC] with a backoff strategy.
type retryingClient struct {
c client.RPC
retryAttempts int
strategy backoff.Strategy
}
// NewRetryingClient creates a new retrying client.
// The backoff strategy is optional, if not provided, the default exponential backoff strategy is used.
func NewRetryingClient(c client.RPC, retries int, strategy ...backoff.Strategy) *retryingClient {
if len(strategy) == 0 {
strategy = []backoff.Strategy{ExponentialBackoff}
}
return &retryingClient{
c: c,
retryAttempts: retries,
strategy: strategy[0],
}
}
// BackoffStrategy returns the [backoff.Strategy] used by the client.
func (b *retryingClient) BackoffStrategy() backoff.Strategy {
return b.strategy
}
func (b *retryingClient) Close() {
b.c.Close()
}
func (b *retryingClient) CallContext(ctx context.Context, result any, method string, args ...any) error {
return backoff.DoCtx(ctx, b.retryAttempts, b.strategy, func() error {
cCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
return b.c.CallContext(cCtx, result, method, args...)
})
}
func (b *retryingClient) BatchCallContext(ctx context.Context, batch []rpc.BatchElem) error {
return backoff.DoCtx(ctx, b.retryAttempts, b.strategy, func() error {
cCtx, cancel := context.WithTimeout(ctx, 20*time.Second)
defer cancel()
err := b.c.BatchCallContext(cCtx, batch)
return err
})
}
func (b *retryingClient) EthSubscribe(ctx context.Context, channel any, args ...any) (ethereum.Subscription, error) {
var sub ethereum.Subscription
err := backoff.DoCtx(ctx, b.retryAttempts, b.strategy, func() error {
var err error
sub, err = b.c.EthSubscribe(ctx, channel, args...)
return err
})
return sub, err
}
package client_test
import (
"context"
"errors"
"testing"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum-optimism/optimism/op-service/backoff"
"github.com/ethereum-optimism/optimism/op-service/client"
opclient "github.com/ethereum-optimism/optimism/op-node/client"
)
type MockRPC struct {
mock.Mock
}
func (m *MockRPC) Close() {
m.Called()
}
func (m *MockRPC) CallContext(ctx context.Context, result any, method string, args ...any) error {
out := m.Mock.MethodCalled("CallContext", ctx, result, method, args)
return *out[0].(*error)
}
func (m *MockRPC) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error {
out := m.Mock.MethodCalled("BatchCallContext", ctx, b)
return *out[0].(*error)
}
func (m *MockRPC) EthSubscribe(ctx context.Context, channel any, args ...any) (ethereum.Subscription, error) {
out := m.Mock.MethodCalled("EthSubscribe", ctx, channel, args)
return *out[0].(*ethereum.Subscription), *out[1].(*error)
}
func (m *MockRPC) ExpectCallContext(err error, result any, method string, arg string) {
m.On("CallContext", mock.Anything, result, method, []interface{}{arg}).Return(&err)
}
func (m *MockRPC) ExpectBatchCallContext(err error, b []rpc.BatchElem) {
m.On("BatchCallContext", mock.Anything, b).Return(&err)
}
func (m *MockRPC) ExpectEthSubscribe(sub ethereum.Subscription, err error, channel any, args ...any) {
m.On("EthSubscribe", mock.Anything, channel, args).Return(&sub, &err)
}
var _ opclient.RPC = (*MockRPC)(nil)
func TestClient_BackoffClient_Strategy(t *testing.T) {
mockRpc := &MockRPC{}
backoffClient := client.NewRetryingClient(mockRpc, 0)
require.Equal(t, backoffClient.BackoffStrategy(), client.ExponentialBackoff)
fixedStrategy := &backoff.FixedStrategy{}
backoffClient = client.NewRetryingClient(mockRpc, 0, fixedStrategy)
require.Equal(t, backoffClient.BackoffStrategy(), fixedStrategy)
}
func TestClient_BackoffClient_Close(t *testing.T) {
mockRpc := &MockRPC{}
mockRpc.On("Close").Return()
backoffClient := client.NewRetryingClient(mockRpc, 0)
backoffClient.Close()
require.True(t, mockRpc.AssertCalled(t, "Close"))
}
func TestClient_BackoffClient_CallContext(t *testing.T) {
mockRpc := &MockRPC{}
mockRpc.ExpectCallContext(nil, nil, "foo", "bar")
backoffClient := client.NewRetryingClient(mockRpc, 1)
err := backoffClient.CallContext(context.Background(), nil, "foo", "bar")
require.NoError(t, err)
require.True(t, mockRpc.AssertCalled(t, "CallContext", mock.Anything, nil, "foo", []interface{}{"bar"}))
}
func TestClient_BackoffClient_CallContext_WithRetries(t *testing.T) {
mockRpc := &MockRPC{}
mockRpc.ExpectCallContext(errors.New("foo"), nil, "foo", "bar")
backoffClient := client.NewRetryingClient(mockRpc, 2)
err := backoffClient.CallContext(context.Background(), nil, "foo", "bar")
require.Error(t, err)
require.True(t, mockRpc.AssertNumberOfCalls(t, "CallContext", 2))
}
func TestClient_BackoffClient_BatchCallContext(t *testing.T) {
mockRpc := &MockRPC{}
mockRpc.ExpectBatchCallContext(nil, []rpc.BatchElem(nil))
backoffClient := client.NewRetryingClient(mockRpc, 1)
err := backoffClient.BatchCallContext(context.Background(), nil)
require.NoError(t, err)
require.True(t, mockRpc.AssertCalled(t, "BatchCallContext", mock.Anything, []rpc.BatchElem(nil)))
}
func TestClient_BackoffClient_BatchCallContext_WithRetries(t *testing.T) {
mockRpc := &MockRPC{}
mockRpc.ExpectBatchCallContext(errors.New("foo"), []rpc.BatchElem(nil))
backoffClient := client.NewRetryingClient(mockRpc, 2)
err := backoffClient.BatchCallContext(context.Background(), nil)
require.Error(t, err)
require.True(t, mockRpc.AssertNumberOfCalls(t, "BatchCallContext", 2))
}
func TestClient_BackoffClient_EthSubscribe(t *testing.T) {
mockRpc := &MockRPC{}
mockRpc.ExpectEthSubscribe(ethereum.Subscription(nil), nil, nil, "foo", "bar")
backoffClient := client.NewRetryingClient(mockRpc, 1)
_, err := backoffClient.EthSubscribe(context.Background(), nil, "foo", "bar")
require.NoError(t, err)
require.True(t, mockRpc.AssertCalled(t, "EthSubscribe", mock.Anything, nil, []interface{}{"foo", "bar"}))
}
func TestClient_BackoffClient_EthSubscribe_WithRetries(t *testing.T) {
mockRpc := &MockRPC{}
mockRpc.ExpectEthSubscribe(ethereum.Subscription(nil), errors.New("foo"), nil, "foo", "bar")
backoffClient := client.NewRetryingClient(mockRpc, 2)
_, err := backoffClient.EthSubscribe(context.Background(), nil, "foo", "bar")
require.Error(t, err)
require.True(t, mockRpc.AssertNumberOfCalls(t, "EthSubscribe", 2))
}
# @eth-optimism/actor-tests # @eth-optimism/actor-tests
## 0.0.25
### Patch Changes
- Updated dependencies [f1e867177]
- Updated dependencies [a1b7ff9e3]
- Updated dependencies [197884eae]
- Updated dependencies [8133872ed]
- Updated dependencies [6eb05430d]
- Updated dependencies [afc2ab8c9]
- Updated dependencies [5063a69fb]
- Updated dependencies [aa854bdd8]
- @eth-optimism/contracts-bedrock@0.14.0
- @eth-optimism/sdk@2.1.0
## 0.0.24 ## 0.0.24
### Patch Changes ### Patch Changes
......
{ {
"name": "@eth-optimism/actor-tests", "name": "@eth-optimism/actor-tests",
"version": "0.0.24", "version": "0.0.25",
"description": "A library and suite of tests to stress test Optimism Bedrock.", "description": "A library and suite of tests to stress test Optimism Bedrock.",
"license": "MIT", "license": "MIT",
"author": "", "author": "",
...@@ -18,9 +18,9 @@ ...@@ -18,9 +18,9 @@
"test:coverage": "yarn test" "test:coverage": "yarn test"
}, },
"dependencies": { "dependencies": {
"@eth-optimism/contracts-bedrock": "0.13.2", "@eth-optimism/contracts-bedrock": "0.14.0",
"@eth-optimism/core-utils": "^0.12.0", "@eth-optimism/core-utils": "^0.12.0",
"@eth-optimism/sdk": "^2.0.2", "@eth-optimism/sdk": "^2.1.0",
"@types/chai": "^4.2.18", "@types/chai": "^4.2.18",
"@types/chai-as-promised": "^7.1.4", "@types/chai-as-promised": "^7.1.4",
"async-mutex": "^0.3.2", "async-mutex": "^0.3.2",
......
# @eth-optimism/drippie-mon # @eth-optimism/drippie-mon
## 0.3.1
### Patch Changes
- a26c484af: Fixes a bug in the wd-mon service where a node connection failure event was not handled correctly
- Updated dependencies [2129dafa3]
- Updated dependencies [a1b7ff9e3]
- Updated dependencies [8133872ed]
- Updated dependencies [afc2ab8c9]
- Updated dependencies [188d1e930]
- Updated dependencies [5063a69fb]
- Updated dependencies [aa854bdd8]
- @eth-optimism/contracts-periphery@1.0.8
- @eth-optimism/sdk@2.1.0
## 0.3.0 ## 0.3.0
### Minor Changes ### Minor Changes
......
{ {
"private": true, "private": true,
"name": "@eth-optimism/chain-mon", "name": "@eth-optimism/chain-mon",
"version": "0.3.0", "version": "0.3.1",
"description": "[Optimism] Chain monitoring services", "description": "[Optimism] Chain monitoring services",
"main": "dist/index", "main": "dist/index",
"types": "dist/index", "types": "dist/index",
...@@ -34,9 +34,9 @@ ...@@ -34,9 +34,9 @@
}, },
"dependencies": { "dependencies": {
"@eth-optimism/common-ts": "0.8.1", "@eth-optimism/common-ts": "0.8.1",
"@eth-optimism/contracts-periphery": "1.0.7", "@eth-optimism/contracts-periphery": "1.0.8",
"@eth-optimism/core-utils": "0.12.0", "@eth-optimism/core-utils": "0.12.0",
"@eth-optimism/sdk": "2.0.2", "@eth-optimism/sdk": "2.1.0",
"ethers": "^5.7.0", "ethers": "^5.7.0",
"@types/dateformat": "^5.0.0", "@types/dateformat": "^5.0.0",
"chai-as-promised": "^7.1.1", "chai-as-promised": "^7.1.1",
......
# @eth-optimism/contracts-bedrock # @eth-optimism/contracts-bedrock
## 0.14.0
### Minor Changes
- 197884eae: Bump XDM semver after #5444
- 6eb05430d: Increase precision in `SafeCall.hasMinGas`
- 5063a69fb: Update sdk contract addresses for bedrock
### Patch Changes
- f1e867177: contracts-bedrock was exporting hardhat when it didn't need to be
## 0.13.2 ## 0.13.2
### Patch Changes ### Patch Changes
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
"finalSystemOwner": "0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A", "finalSystemOwner": "0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A",
"controller": "0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A", "controller": "0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A",
"portalGuardian": "0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A", "portalGuardian": "0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A",
"l1StartingBlockTag": "", "l1StartingBlockTag": "0x438335a20d98863a4c0c97999eb2481921ccd28553eac6f913af7c12aec04108",
"l1ChainID": 1, "l1ChainID": 1,
"l2ChainID": 10, "l2ChainID": 10,
"l2BlockTime": 2, "l2BlockTime": 2,
...@@ -13,8 +13,8 @@ ...@@ -13,8 +13,8 @@
"batchInboxAddress": "0xff00000000000000000000000000000000000010", "batchInboxAddress": "0xff00000000000000000000000000000000000010",
"batchSenderAddress": "0x6887246668a3b87F54DeB3b94Ba47a6f63F32985", "batchSenderAddress": "0x6887246668a3b87F54DeB3b94Ba47a6f63F32985",
"l2OutputOracleSubmissionInterval": 1800, "l2OutputOracleSubmissionInterval": 1800,
"l2OutputOracleStartingTimestamp": 0, "l2OutputOracleStartingTimestamp": 1686068903,
"l2OutputOracleStartingBlockNumber": 0, "l2OutputOracleStartingBlockNumber": 105235063,
"l2OutputOracleProposer": "0x473300df21D047806A082244b417f96b32f13A33", "l2OutputOracleProposer": "0x473300df21D047806A082244b417f96b32f13A33",
"l2OutputOracleChallenger": "0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A", "l2OutputOracleChallenger": "0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A",
"finalizationPeriodSeconds": 604800, "finalizationPeriodSeconds": 604800,
......
...@@ -22,6 +22,7 @@ no_match_contract = 'EchidnaFuzz' ...@@ -22,6 +22,7 @@ no_match_contract = 'EchidnaFuzz'
fs_permissions = [ fs_permissions = [
{ 'access'='read-write', 'path'='./.resource-metering.csv' }, { 'access'='read-write', 'path'='./.resource-metering.csv' },
{ 'access'='read', 'path'='./deployments/' },
] ]
[profile.ci] [profile.ci]
......
{ {
"name": "@eth-optimism/contracts-bedrock", "name": "@eth-optimism/contracts-bedrock",
"version": "0.13.2", "version": "0.14.0",
"description": "Contracts for Optimism Specs", "description": "Contracts for Optimism Specs",
"main": "dist/index", "main": "dist/index",
"types": "dist/index", "types": "dist/index",
......
## Multisig Operation Scripts
A collection of scripts used by multisig signers to verify the
integrity of the transactions to be signed.
### Contract Verification for Bedrock Migration
[CheckForBedrockMigration.s.sol](./CheckForBedrockMigration.s.sol) is
a script used by the Bedrock migration signers before the migration,
to verify the contracts affected by the migration are always under the
control of the multisig, and security critical configurations are
correctly initialized.
Example usage:
``` bash
git clone git@github.com:ethereum-optimism/optimism.git
cd optimism/
git pull
git checkout develop
nvm use
yarn install
yarn clean
yarn build
cd packages/contracts-bedrock
export L1_UPGRADE_KEY=0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A
export BEDROCK_JSON_DIR=deployments/mainnet
forge script scripts/multisig/CheckForBedrockMigration.s.sol --rpc-url <TRUSTWORTHY_L1_RPC_URL>
```
Expected output:
``` bash
Script ran successfully.
BEDROCK_JSON_DIR = deployments/mainnet
Checking AddressManager 0xdE1FCfB0851916CA5101820A69b13a4E276bd81F
-- Success: 0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A == 0xdE1FCfB0851916CA5101820A69b13a4E276bd81F.owner().
Checking L1CrossDomainMessenger 0x2150Bc3c64cbfDDbaC9815EF615D6AB8671bfe43
-- Success: 0xbEb5Fc579115071764c7423A4f12eDde41f106Ed == 0x2150Bc3c64cbfDDbaC9815EF615D6AB8671bfe43.PORTAL().
Checking L1CrossDomainMessengerProxy 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1
-- Success: 0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A == 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1.owner().
-- Success: 0xdE1FCfB0851916CA5101820A69b13a4E276bd81F == 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1.libAddressManager().
Checking L1ERC721Bridge 0x4afDD3A48E13B305e98D9EEad67B1b5867E370DF
-- Success: 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1 == 0x4afDD3A48E13B305e98D9EEad67B1b5867E370DF.messenger().
Checking L1ERC721BridgeProxy 0x5a7749f83b81B301cAb5f48EB8516B986DAef23D
-- Success: 0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A == 0x5a7749f83b81B301cAb5f48EB8516B986DAef23D.admin().
-- Success: 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1 == 0x5a7749f83b81B301cAb5f48EB8516B986DAef23D.messenger().
Checking L1ProxyAdmin 0x543bA4AADBAb8f9025686Bd03993043599c6fB04
-- Success: 0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB == 0x543bA4AADBAb8f9025686Bd03993043599c6fB04.owner().
Checking L1StandardBridge 0xBFB731Cd36D26c2a7287716DE857E4380C73A64a
-- Success: 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1 == 0xBFB731Cd36D26c2a7287716DE857E4380C73A64a.messenger().
Checking L1StandardBridgeProxy 0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1
-- Success: 0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A == 0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1.getOwner().
-- Success: 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1 == 0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1.messenger().
Checking L1UpgradeKeyAddress 0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A
Checking L2OutputOracle 0xd2E67B6a032F0A9B1f569E63ad6C38f7342c2e00
-- Success: 0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A == 0xd2E67B6a032F0A9B1f569E63ad6C38f7342c2e00.CHALLENGER().
-- Success: 0x0000000000000000000000000000000000093A80 == 0xd2E67B6a032F0A9B1f569E63ad6C38f7342c2e00.FINALIZATION_PERIOD_SECONDS().
Checking L2OutputOracleProxy 0xdfe97868233d1aa22e815a266982f2cf17685a27
-- Success: 0x543bA4AADBAb8f9025686Bd03993043599c6fB04 == 0xdfe97868233d1aa22e815a266982f2cf17685a27.admin().
Checking OptimismMintableERC20Factory 0xaE849EFA4BcFc419593420e14707996936E365E2
-- Success: 0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1 == 0xaE849EFA4BcFc419593420e14707996936E365E2.BRIDGE().
Checking OptimismMintableERC20FactoryProxy 0x75505a97BD334E7BD3C476893285569C4136Fa0F
-- Success: 0x543bA4AADBAb8f9025686Bd03993043599c6fB04 == 0x75505a97BD334E7BD3C476893285569C4136Fa0F.admin().
Checking OptimismPortal 0x28a55488fef40005309e2DA0040DbE9D300a64AB
-- Success: 0xdfe97868233d1aa22e815a266982f2cf17685a27 == 0x28a55488fef40005309e2DA0040DbE9D300a64AB.L2_ORACLE().
Checking OptimismPortalProxy 0xbEb5Fc579115071764c7423A4f12eDde41f106Ed
-- Success: 0x543bA4AADBAb8f9025686Bd03993043599c6fB04 == 0xbEb5Fc579115071764c7423A4f12eDde41f106Ed.admin().
Checking PortalSender 0x0A893d9576b9cFD9EF78595963dc973238E78210
-- Success: 0xbEb5Fc579115071764c7423A4f12eDde41f106Ed == 0x0A893d9576b9cFD9EF78595963dc973238E78210.PORTAL().
Checking SystemConfigProxy 0x229047fed2591dbec1eF1118d64F7aF3dB9EB290
-- Success: 0x543bA4AADBAb8f9025686Bd03993043599c6fB04 == 0x229047fed2591dbec1eF1118d64F7aF3dB9EB290.admin().
Checking SystemDictator 0x09E040a72FD3492355C5aEEdbC3154075f83488a
-- Success: 0x0000000000000000000000000000000000000000 == 0x09E040a72FD3492355C5aEEdbC3154075f83488a.owner().
Checking SystemDictatorProxy 0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB
-- Success: 0x09E040a72FD3492355C5aEEdbC3154075f83488a == 0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB.implementation().
-- Success: 0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A == 0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB.owner().
-- Success: 0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A == 0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB.admin().
```
{
"version": "1.0",
"chainId": "1",
"meta": {
"name": "batch1",
"description": "ProxyAdmin.transferOwnership, AddressManager.transferOwnership, L1StandardBridgeProxy.setOwner, L1ERC721BridgeProxy.changeAdmin, and SystemDictatorProxy.phase1.",
"txBuilderVersion": "1.13.3"
},
"transactions": [
{
"to": "0x543bA4AADBAb8f9025686Bd03993043599c6fB04",
"value": "0",
"data": null,
"contractMethod": {
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"payable": false
},
"contractInputsValues": {
"newOwner": "0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB"
}
},
{
"to": "0xdE1FCfB0851916CA5101820A69b13a4E276bd81F",
"value": "0",
"data": null,
"contractMethod": {
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"payable": false
},
"contractInputsValues": {
"newOwner": "0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB"
}
},
{
"to": "0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1",
"value": "0",
"data": null,
"contractMethod": {
"inputs": [
{
"internalType": "address",
"name": "_owner",
"type": "address"
}
],
"name": "setOwner",
"payable": false
},
"contractInputsValues": {
"_owner": "0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB"
}
},
{
"to": "0x5a7749f83b81B301cAb5f48EB8516B986DAef23D",
"value": "0",
"data": null,
"contractMethod": {
"inputs": [
{
"internalType": "address",
"name": "_admin",
"type": "address"
}
],
"name": "changeAdmin",
"payable": false
},
"contractInputsValues": {
"_admin": "0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB"
}
},
{
"to": "0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB",
"value": "0",
"data": null,
"contractMethod": {
"inputs": [],
"name": "phase1",
"payable": false
},
"contractInputsValues": {}
}
]
}
{
"version": "1.0",
"chainId": "1",
"meta": {
"name": "batch2",
"description": "SystemDictatorProxy.updateDynamicConfig.",
"txBuilderVersion": "1.13.3"
},
"transactions": [
{
"to": "0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB",
"value": "0",
"data": null,
"contractMethod": {
"inputs": [
{
"components": [
{
"internalType": "uint256",
"name": "l2OutputOracleStartingBlockNumber",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "l2OutputOracleStartingTimestamp",
"type": "uint256"
}
],
"internalType": "struct SystemDictator.L2OutputOracleDynamicConfig",
"name": "_l2OutputOracleDynamicConfig",
"type": "tuple"
},
{
"internalType": "bool",
"name": "_optimismPortalDynamicConfig",
"type": "bool"
}
],
"name": "updateDynamicConfig",
"payable": false
},
"contractInputsValues": {
"_l2OutputOracleDynamicConfig": "[105235063, 1686068903]",
"_optimismPortalDynamicConfig": "true"
}
}
]
}
{
"version": "1.0",
"chainId": "1",
"meta": {
"name": "batch3",
"description": "SystemDictatorProxy.phase2 and OptimismPortalProxy.unpause.",
"txBuilderVersion": "1.13.3"
},
"transactions": [
{
"to": "0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB",
"value": "0",
"data": null,
"contractMethod": {
"inputs": [],
"name": "phase2",
"payable": false
},
"contractInputsValues": {}
},
{
"to": "0xbEb5Fc579115071764c7423A4f12eDde41f106Ed",
"value": "0",
"data": null,
"contractMethod": {
"inputs": [],
"name": "unpause",
"payable": false
},
"contractInputsValues": {}
}
]
}
{
"version": "1.0",
"chainId": "1",
"meta": {
"name": "EscapeHatch",
"description": "SystemDictatorProxy.exit1 and finalize.",
"txBuilderVersion": "1.14.1"
},
"transactions": [
{
"to": "0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB",
"value": "0",
"data": null,
"contractMethod": {
"inputs": [],
"name": "exit1",
"payable": false
},
"contractInputsValues": null
},
{
"to": "0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB",
"value": "0",
"data": null,
"contractMethod": {
"inputs": [],
"name": "finalize",
"payable": false
},
"contractInputsValues": null
}
]
}
# @eth-optimism/contracts-periphery # @eth-optimism/contracts-periphery
## 1.0.8
### Patch Changes
- 2129dafa3: Add faucet contract
- 188d1e930: Change type for auth id on Faucet contracts from bytes to bytes32
## 1.0.7 ## 1.0.7
### Patch Changes ### Patch Changes
......
{ {
"name": "@eth-optimism/contracts-periphery", "name": "@eth-optimism/contracts-periphery",
"version": "1.0.7", "version": "1.0.8",
"description": "[Optimism] External (out-of-protocol) L1 and L2 smart contracts for Optimism", "description": "[Optimism] External (out-of-protocol) L1 and L2 smart contracts for Optimism",
"main": "dist/index", "main": "dist/index",
"types": "dist/index", "types": "dist/index",
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
"url": "https://github.com/ethereum-optimism/optimism.git" "url": "https://github.com/ethereum-optimism/optimism.git"
}, },
"devDependencies": { "devDependencies": {
"@eth-optimism/contracts-bedrock": "0.13.2", "@eth-optimism/contracts-bedrock": "0.14.0",
"@eth-optimism/core-utils": "^0.12.0", "@eth-optimism/core-utils": "^0.12.0",
"@eth-optimism/hardhat-deploy-config": "^0.2.6", "@eth-optimism/hardhat-deploy-config": "^0.2.6",
"@ethersproject/hardware-wallets": "^5.7.0", "@ethersproject/hardware-wallets": "^5.7.0",
......
# Changelog # Changelog
## 0.6.0
### Minor Changes
- 5063a69fb: Update sdk contract addresses for bedrock
## 0.5.40 ## 0.5.40
### Patch Changes ### Patch Changes
......
{ {
"name": "@eth-optimism/contracts", "name": "@eth-optimism/contracts",
"version": "0.5.40", "version": "0.6.0",
"description": "[Optimism] L1 and L2 smart contracts for Optimism", "description": "[Optimism] L1 and L2 smart contracts for Optimism",
"main": "dist/index", "main": "dist/index",
"types": "dist/index", "types": "dist/index",
......
# data transport layer # data transport layer
## 0.5.56
### Patch Changes
- Updated dependencies [5063a69fb]
- @eth-optimism/contracts@0.6.0
## 0.5.55 ## 0.5.55
### Patch Changes ### Patch Changes
......
{ {
"private": true, "private": true,
"name": "@eth-optimism/data-transport-layer", "name": "@eth-optimism/data-transport-layer",
"version": "0.5.55", "version": "0.5.56",
"description": "[Optimism] Service for shuttling data from L1 into L2", "description": "[Optimism] Service for shuttling data from L1 into L2",
"main": "dist/index", "main": "dist/index",
"types": "dist/index", "types": "dist/index",
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
}, },
"dependencies": { "dependencies": {
"@eth-optimism/common-ts": "0.8.1", "@eth-optimism/common-ts": "0.8.1",
"@eth-optimism/contracts": "0.5.40", "@eth-optimism/contracts": "0.6.0",
"@eth-optimism/core-utils": "0.12.0", "@eth-optimism/core-utils": "0.12.0",
"@ethersproject/providers": "^5.7.0", "@ethersproject/providers": "^5.7.0",
"@ethersproject/transactions": "^5.7.0", "@ethersproject/transactions": "^5.7.0",
......
# @eth-optimism/fault-detector # @eth-optimism/fault-detector
## 0.6.4
### Patch Changes
- Updated dependencies [a1b7ff9e3]
- Updated dependencies [8133872ed]
- Updated dependencies [afc2ab8c9]
- Updated dependencies [5063a69fb]
- Updated dependencies [aa854bdd8]
- @eth-optimism/sdk@2.1.0
- @eth-optimism/contracts@0.6.0
## 0.6.3 ## 0.6.3
### Patch Changes ### Patch Changes
......
{ {
"private": true, "private": true,
"name": "@eth-optimism/fault-detector", "name": "@eth-optimism/fault-detector",
"version": "0.6.3", "version": "0.6.4",
"description": "[Optimism] Service for detecting faulty L2 output proposals", "description": "[Optimism] Service for detecting faulty L2 output proposals",
"main": "dist/index", "main": "dist/index",
"types": "dist/index", "types": "dist/index",
...@@ -48,9 +48,9 @@ ...@@ -48,9 +48,9 @@
}, },
"dependencies": { "dependencies": {
"@eth-optimism/common-ts": "^0.8.1", "@eth-optimism/common-ts": "^0.8.1",
"@eth-optimism/contracts": "^0.5.40", "@eth-optimism/contracts": "^0.6.0",
"@eth-optimism/core-utils": "^0.12.0", "@eth-optimism/core-utils": "^0.12.0",
"@eth-optimism/sdk": "^2.0.2", "@eth-optimism/sdk": "^2.1.0",
"@ethersproject/abstract-provider": "^5.7.0" "@ethersproject/abstract-provider": "^5.7.0"
} }
} }
# @eth-optimism/message-relayer # @eth-optimism/message-relayer
## 0.5.34
### Patch Changes
- Updated dependencies [a1b7ff9e3]
- Updated dependencies [8133872ed]
- Updated dependencies [afc2ab8c9]
- Updated dependencies [5063a69fb]
- Updated dependencies [aa854bdd8]
- @eth-optimism/sdk@2.1.0
## 0.5.33 ## 0.5.33
### Patch Changes ### Patch Changes
......
{ {
"private": true, "private": true,
"name": "@eth-optimism/message-relayer", "name": "@eth-optimism/message-relayer",
"version": "0.5.33", "version": "0.5.34",
"description": "[Optimism] Service for automatically relaying L2 to L1 transactions", "description": "[Optimism] Service for automatically relaying L2 to L1 transactions",
"main": "dist/index", "main": "dist/index",
"types": "dist/index", "types": "dist/index",
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
"dependencies": { "dependencies": {
"@eth-optimism/common-ts": "0.8.1", "@eth-optimism/common-ts": "0.8.1",
"@eth-optimism/core-utils": "0.12.0", "@eth-optimism/core-utils": "0.12.0",
"@eth-optimism/sdk": "2.0.2", "@eth-optimism/sdk": "2.1.0",
"ethers": "^5.7.0" "ethers": "^5.7.0"
}, },
"devDependencies": { "devDependencies": {
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
}, },
"devDependencies": { "devDependencies": {
"commander": "^9.0.0", "commander": "^9.0.0",
"@eth-optimism/contracts": "0.5.40", "@eth-optimism/contracts": "0.6.0",
"@eth-optimism/old-contracts": "npm:@eth-optimism/contracts@0.4.10", "@eth-optimism/old-contracts": "npm:@eth-optimism/contracts@0.4.10",
"ethers": "^5.7.0", "ethers": "^5.7.0",
"ts-node": "^10.9.1" "ts-node": "^10.9.1"
......
# @eth-optimism/sdk # @eth-optimism/sdk
## 2.1.0
### Minor Changes
- 5063a69fb: Update sdk contract addresses for bedrock
### Patch Changes
- a1b7ff9e3: add eco bridge adapter
- 8133872ed: Fix firefox bug with getTokenPair
- afc2ab8c9: Update the migrated withdrawal gas limit for non goerli networks
- aa854bdd8: Add warning if bedrock is not turned on
- Updated dependencies [f1e867177]
- Updated dependencies [197884eae]
- Updated dependencies [6eb05430d]
- Updated dependencies [5063a69fb]
- @eth-optimism/contracts-bedrock@0.14.0
- @eth-optimism/contracts@0.6.0
## 2.0.2 ## 2.0.2
### Patch Changes ### Patch Changes
......
{ {
"name": "@eth-optimism/sdk", "name": "@eth-optimism/sdk",
"version": "2.0.2", "version": "2.1.0",
"description": "[Optimism] Tools for working with Optimism", "description": "[Optimism] Tools for working with Optimism",
"main": "dist/index", "main": "dist/index",
"types": "dist/index", "types": "dist/index",
...@@ -54,9 +54,9 @@ ...@@ -54,9 +54,9 @@
"isomorphic-fetch": "^3.0.0" "isomorphic-fetch": "^3.0.0"
}, },
"dependencies": { "dependencies": {
"@eth-optimism/contracts": "0.5.40", "@eth-optimism/contracts": "0.6.0",
"@eth-optimism/core-utils": "0.12.0", "@eth-optimism/core-utils": "0.12.0",
"@eth-optimism/contracts-bedrock": "0.13.2", "@eth-optimism/contracts-bedrock": "0.14.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"merkletreejs": "^0.2.27", "merkletreejs": "^0.2.27",
"rlp": "^2.2.7" "rlp": "^2.2.7"
......
...@@ -160,7 +160,7 @@ Different transaction types can contain different payloads, and be handled diffe ...@@ -160,7 +160,7 @@ Different transaction types can contain different payloads, and be handled diffe
[fork-choice-rule]: glossary.md#fork-choice-rule [fork-choice-rule]: glossary.md#fork-choice-rule
The fork choice rule is the rule used to determined which block is to be considered as the head of a blockchain. On L1, The fork choice rule is the rule used to determine which block is to be considered as the head of a blockchain. On L1,
this is determined by the proof of stake rules. this is determined by the proof of stake rules.
L2 also has a fork choice rule, although the rules vary depending on whether we want the [safe L2 head][safe-l2-head], L2 also has a fork choice rule, although the rules vary depending on whether we want the [safe L2 head][safe-l2-head],
...@@ -591,8 +591,8 @@ In the current implementation, this is the L1 block number at which the output o ...@@ -591,8 +591,8 @@ In the current implementation, this is the L1 block number at which the output o
[safe-l2-block]: glossary.md#safe-l2-block [safe-l2-block]: glossary.md#safe-l2-block
A safe L2 block is an L2 block can be derived entirely from L1 by a [rollup node][rollup-node]. This can vary between A safe L2 block is an L2 block that can be derived entirely from L1 by a [rollup node][rollup-node]. This can vary
different nodes, based on their view of the L1 chain. between different nodes, based on their view of the L1 chain.
## Safe L2 Head ## Safe L2 Head
......
This diff is collapsed.
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