Skip to content

Commit

Permalink
Merge branch 'develop' into willc/dai
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] committed May 24, 2023
2 parents abb4fff + 6066c16 commit 8be4a93
Show file tree
Hide file tree
Showing 56 changed files with 2,910 additions and 646 deletions.
5 changes: 5 additions & 0 deletions .changeset/eleven-experts-crash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@eth-optimism/contracts-bedrock': patch
---

contracts-bedrock was exporting hardhat when it didn't need to be
10 changes: 6 additions & 4 deletions docs/op-stack/src/docs/understand/explainer.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,15 @@ The ability to pause the bridge in case of emergency means that in the worst cas

#### Unfreezing the bridge via L1 soft fork

In order to address the frozen funds, there is a potential final recovery mechanism we call the “L1 Soft Fork Upgrade Recovery” mechanism. This mechanism enables L1 to initiate a bridge upgrade with a soft fork, bypassing all other permissions within the Superchain bridge contracts. The mechanism is as follows:
In order to address the frozen funds, there is a potential final recovery mechanism which has been discussed by the L2 community which we call the “L1 Soft Fork Upgrade Recovery” mechanism. This mechanism enables L1 to initiate a bridge upgrade with a soft fork, bypassing all other permissions within the Superchain bridge contracts. This approach may [introduce systemic risk](https://vitalik.ca/general/2023/05/21/dont_overload.html) to Ethereum and requires research and community buy-in before implementation. It is not required for implementing the Superchain and is being documented for research completeness. Without further research into the implications and safety, it is not an approach the team currently endorses.

*Anyone* may propose an upgrade by submitting a transaction to a special bridge contract, along with a very large bond. This begins a two week challenge period. During this challenge period, *anyone* may submit a challenge which immediately *cancels* the upgrade and claims the bond. Under normal circumstances, it is impossible that an upgrade would go uncancelled for the required two weeks due to the large incentive provided for anyone to cancel the upgrade. However, if the upgrade is accompanied by a modification to Ethereum L1 validator software (the L1 soft fork), which ignores blocks that contain the cancellation transaction then it may succeed.
The mechanism is as follows:

While a successful upgrade of this type would represent a soft fork of Ethereum L1, it would not incur long term technical debt to the Ethereum codebase because the soft fork logic can be removed once the upgrade has completed.
*Anyone* may propose an upgrade by submitting a transaction to a special bridge contract, along with a very large bond. This begins a two week challenge period. During this challenge period, anyone may submit a challenge which immediately cancels the upgrade and claims the bond. Under normal circumstances, it is impossible that an upgrade would go uncancelled for the required two weeks due to the large incentive provided for anyone to cancel the upgrade. However, if the upgrade is accompanied by a modification to Ethereum L1 validator software (the L1 soft fork), which ignores blocks that contain the cancellation transaction then it may succeed.

We expect this escape hatch will never be used, but its very existence should deter malicious behavior.
While a successful upgrade of this type would represent a soft fork of Ethereum L1, it would not incur long term technical debt to the Ethereum codebase because the soft fork logic can be removed once the upgrade has completed.

We expect this escape hatch will never be used, but its very existence could deter malicious behavior.

### The combination of these features results in a system satisfying the core Superchain properties

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ require (
github.com/libp2p/go-libp2p-pubsub v0.9.0
github.com/libp2p/go-libp2p-testing v0.12.0
github.com/mattn/go-isatty v0.0.17
github.com/multiformats/go-base32 v0.1.0
github.com/multiformats/go-multiaddr v0.8.0
github.com/multiformats/go-multiaddr-dns v0.3.1
github.com/olekukonko/tablewriter v0.0.5
Expand Down Expand Up @@ -128,7 +129,6 @@ require (
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/multiformats/go-base32 v0.1.0 // indirect
github.com/multiformats/go-base36 v0.2.0 // indirect
github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
github.com/multiformats/go-multibase v0.1.1 // indirect
Expand Down
36 changes: 36 additions & 0 deletions op-challenger/challenger/oracle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package challenger

import (
"errors"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
)

var ErrMissingEvent = errors.New("missing event")

// BuildOutputLogFilter creates a filter query for the L2OutputOracle contract.
//
// The `OutputProposed` event is encoded as:
// 0: bytes32 indexed outputRoot,
// 1: uint256 indexed l2OutputIndex,
// 2: uint256 indexed l2BlockNumber,
// 3: uint256 l1Timestamp
func BuildOutputLogFilter(l2ooABI *abi.ABI) (ethereum.FilterQuery, error) {
// Get the L2OutputOracle contract `OutputProposed` event
event := l2ooABI.Events["OutputProposed"]

// Sanity check that the `OutputProposed` event is defined
if event.ID == (common.Hash{}) {
return ethereum.FilterQuery{}, ErrMissingEvent
}

query := ethereum.FilterQuery{
Topics: [][]common.Hash{
{event.ID},
},
}

return query, nil
}
52 changes: 52 additions & 0 deletions op-challenger/challenger/oracle_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
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"
)

// TestBuildOutputLogFilter_Succeeds tests that the Output
// Log Filter is built correctly.
func TestBuildOutputLogFilter_Succeeds(t *testing.T) {
// Create a mock event id
event := abi.Event{
ID: [32]byte{0x01},
}

filterQuery := eth.FilterQuery{
Topics: [][]common.Hash{
{event.ID},
},
}

// Mock the ABI
l2ooABI := abi.ABI{
Events: map[string]abi.Event{
"OutputProposed": event,
},
}

// Build the filter
query, err := BuildOutputLogFilter(&l2ooABI)
require.Equal(t, filterQuery, query)
require.NoError(t, err)
}

// TestBuildOutputLogFilter_Fails tests that the Output
// Log Filter fails when the event definition is missing.
func TestBuildOutputLogFilter_Fails(t *testing.T) {
// Mock the ABI
l2ooABI := abi.ABI{
Events: map[string]abi.Event{},
}

// Build the filter
_, err := BuildOutputLogFilter(&l2ooABI)
require.Error(t, err)
require.Equal(t, ErrMissingEvent, err)
}
61 changes: 60 additions & 1 deletion op-e2e/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,29 @@ package op_e2e
import (
"context"
"crypto/ecdsa"
"crypto/rand"
"fmt"
"math/big"
"net"
"os"
"path"
"sort"
"strings"
"testing"
"time"

"github.com/ethereum-optimism/optimism/op-node/p2p/store"
"github.com/ethereum-optimism/optimism/op-service/clock"
ds "github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/sync"
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/peerstore"
"github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem"

ic "github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/peer"
ma "github.com/multiformats/go-multiaddr"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
Expand Down Expand Up @@ -465,7 +479,7 @@ func (cfg SystemConfig) Start(_opts ...SystemConfigOption) (*System, error) {
if p, ok := p2pNodes[name]; ok {
return p, nil
}
h, err := sys.Mocknet.GenPeer()
h, err := sys.newMockNetPeer()
if err != nil {
return nil, fmt.Errorf("failed to init p2p host for node %s", name)
}
Expand Down Expand Up @@ -627,6 +641,51 @@ func (cfg SystemConfig) Start(_opts ...SystemConfigOption) (*System, error) {
return sys, nil
}

// IP6 range that gets blackholed (in case our traffic ever makes it out onto
// the internet).
var blackholeIP6 = net.ParseIP("100::")

// mocknet doesn't allow us to add a peerstore without fully creating the peer ourselves
func (sys *System) newMockNetPeer() (host.Host, error) {
sk, _, err := ic.GenerateECDSAKeyPair(rand.Reader)
if err != nil {
return nil, err
}
id, err := peer.IDFromPrivateKey(sk)
if err != nil {
return nil, err
}
suffix := id
if len(id) > 8 {
suffix = id[len(id)-8:]
}
ip := append(net.IP{}, blackholeIP6...)
copy(ip[net.IPv6len-len(suffix):], suffix)
a, err := ma.NewMultiaddr(fmt.Sprintf("/ip6/%s/tcp/4242", ip))
if err != nil {
return nil, fmt.Errorf("failed to create test multiaddr: %w", err)
}
p, err := peer.IDFromPublicKey(sk.GetPublic())
if err != nil {
return nil, err
}

ps, err := pstoremem.NewPeerstore()
if err != nil {
return nil, err
}
ps.AddAddr(p, a, peerstore.PermanentAddrTTL)
_ = ps.AddPrivKey(p, sk)
_ = ps.AddPubKey(p, sk.GetPublic())

ds := sync.MutexWrap(ds.NewMapDatastore())
eps, err := store.NewExtendedPeerstore(context.Background(), log.Root(), clock.SystemClock, ps, ds)
if err != nil {
return nil, err
}
return sys.Mocknet.AddPeerWithPeerstore(p, eps)
}

func selectEndpoint(node *node.Node) string {
useHTTP := os.Getenv("OP_E2E_USE_HTTP") == "true"
if useHTTP {
Expand Down
2 changes: 1 addition & 1 deletion op-e2e/system_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,7 @@ func TestSystemP2PAltSync(t *testing.T) {
snapLog.SetHandler(log.DiscardHandler())

// Create a peer, and hook up alice and bob
h, err := sys.Mocknet.GenPeer()
h, err := sys.newMockNetPeer()
require.NoError(t, err)
_, err = sys.Mocknet.LinkPeers(sys.RollupNodes["alice"].P2P().Host().ID(), h.ID())
require.NoError(t, err)
Expand Down
68 changes: 68 additions & 0 deletions op-node/metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ type Metricer interface {
ClientPayloadByNumberEvent(num uint64, resultCode byte, duration time.Duration)
ServerPayloadByNumberEvent(num uint64, resultCode byte, duration time.Duration)
PayloadsQuarantineSize(n int)
RecordPeerUnban()
RecordIPUnban()
RecordDial(allow bool)
RecordAccept(allow bool)
}

// Metrics tracks all the metrics for the op-node.
Expand Down Expand Up @@ -133,6 +137,10 @@ type Metrics struct {
PeerScores *prometheus.GaugeVec
GossipEventsTotal *prometheus.CounterVec
BandwidthTotal *prometheus.GaugeVec
PeerUnbans prometheus.Counter
IPUnbans prometheus.Counter
Dials *prometheus.CounterVec
Accepts *prometheus.CounterVec

ChannelInputBytes prometheus.Counter

Expand Down Expand Up @@ -335,6 +343,30 @@ func NewMetrics(procName string) *Metrics {
}, []string{
"direction",
}),
PeerUnbans: factory.NewCounter(prometheus.CounterOpts{
Namespace: ns,
Subsystem: "p2p",
Name: "peer_unbans",
Help: "Count of peer unbans",
}),
IPUnbans: factory.NewCounter(prometheus.CounterOpts{
Namespace: ns,
Subsystem: "p2p",
Name: "ip_unbans",
Help: "Count of IP unbans",
}),
Dials: factory.NewCounterVec(prometheus.CounterOpts{
Namespace: ns,
Subsystem: "p2p",
Name: "dials",
Help: "Count of outgoing dial attempts, with label to filter to allowed attempts",
}, []string{"allow"}),
Accepts: factory.NewCounterVec(prometheus.CounterOpts{
Namespace: ns,
Subsystem: "p2p",
Name: "accepts",
Help: "Count of incoming dial attempts to accept, with label to filter to allowed attempts",
}, []string{"allow"}),

ChannelInputBytes: factory.NewCounter(prometheus.CounterOpts{
Namespace: ns,
Expand Down Expand Up @@ -663,6 +695,30 @@ func (m *Metrics) RecordChannelInputBytes(inputCompressedBytes int) {
m.ChannelInputBytes.Add(float64(inputCompressedBytes))
}

func (m *Metrics) RecordPeerUnban() {
m.PeerUnbans.Inc()
}

func (m *Metrics) RecordIPUnban() {
m.IPUnbans.Inc()
}

func (m *Metrics) RecordDial(allow bool) {
if allow {
m.Dials.WithLabelValues("true").Inc()
} else {
m.Dials.WithLabelValues("false").Inc()
}
}

func (m *Metrics) RecordAccept(allow bool) {
if allow {
m.Accepts.WithLabelValues("true").Inc()
} else {
m.Accepts.WithLabelValues("false").Inc()
}
}

type noopMetricer struct{}

var NoopMetrics Metricer = new(noopMetricer)
Expand Down Expand Up @@ -768,3 +824,15 @@ func (n *noopMetricer) PayloadsQuarantineSize(int) {

func (n *noopMetricer) RecordChannelInputBytes(int) {
}

func (n *noopMetricer) RecordPeerUnban() {
}

func (n *noopMetricer) RecordIPUnban() {
}

func (n *noopMetricer) RecordDial(allow bool) {
}

func (n *noopMetricer) RecordAccept(allow bool) {
}
Loading

0 comments on commit 8be4a93

Please sign in to comment.