1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package rpc
import (
"context"
"sync/atomic"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-conductor/consensus"
"github.com/ethereum-optimism/optimism/op-service/eth"
)
type conductor interface {
Pause(ctx context.Context) error
Resume(ctx context.Context) error
Stop(ctx context.Context) error
Paused() bool
Stopped() bool
SequencerHealthy(ctx context.Context) bool
Leader(ctx context.Context) bool
LeaderWithID(ctx context.Context) *consensus.ServerInfo
AddServerAsVoter(ctx context.Context, id string, addr string, version uint64) error
AddServerAsNonvoter(ctx context.Context, id string, addr string, version uint64) error
RemoveServer(ctx context.Context, id string, version uint64) error
TransferLeader(ctx context.Context) error
TransferLeaderToServer(ctx context.Context, id string, addr string) error
CommitUnsafePayload(ctx context.Context, payload *eth.ExecutionPayloadEnvelope) error
ClusterMembership(ctx context.Context) (*consensus.ClusterMembership, error)
}
// APIBackend is the backend implementation of the API.
// TODO: (https://github.com/ethereum-optimism/protocol-quest/issues/45) Add metrics tracer here.
type APIBackend struct {
log log.Logger
con conductor
leaderOverride atomic.Bool
}
// NewAPIBackend creates a new APIBackend instance.
func NewAPIBackend(log log.Logger, con conductor) *APIBackend {
return &APIBackend{
log: log,
con: con,
}
}
var _ API = (*APIBackend)(nil)
// OverrideLeader implements API.
func (api *APIBackend) OverrideLeader(ctx context.Context) error {
api.leaderOverride.Store(true)
return nil
}
// Paused implements API.
func (api *APIBackend) Paused(ctx context.Context) (bool, error) {
return api.con.Paused(), nil
}
// Stopped implements API.
func (api *APIBackend) Stopped(ctx context.Context) (bool, error) {
return api.con.Stopped(), nil
}
// Active implements API.
func (api *APIBackend) Active(_ context.Context) (bool, error) {
return !api.con.Stopped() && !api.con.Paused(), nil
}
// AddServerAsNonvoter implements API.
func (api *APIBackend) AddServerAsNonvoter(ctx context.Context, id string, addr string, version uint64) error {
return api.con.AddServerAsNonvoter(ctx, id, addr, version)
}
// AddServerAsVoter implements API.
func (api *APIBackend) AddServerAsVoter(ctx context.Context, id string, addr string, version uint64) error {
return api.con.AddServerAsVoter(ctx, id, addr, version)
}
// RemoveServer implements API.
func (api *APIBackend) RemoveServer(ctx context.Context, id string, version uint64) error {
return api.con.RemoveServer(ctx, id, version)
}
// CommitUnsafePayload implements API.
func (api *APIBackend) CommitUnsafePayload(ctx context.Context, payload *eth.ExecutionPayloadEnvelope) error {
return api.con.CommitUnsafePayload(ctx, payload)
}
// Leader implements API, returns true if current conductor is leader of the cluster.
func (api *APIBackend) Leader(ctx context.Context) (bool, error) {
return api.leaderOverride.Load() || api.con.Leader(ctx), nil
}
// LeaderWithID implements API, returns the leader's server ID and address (not necessarily the current conductor).
func (api *APIBackend) LeaderWithID(ctx context.Context) (*consensus.ServerInfo, error) {
if api.leaderOverride.Load() {
return &consensus.ServerInfo{
ID: "N/A (Leader overridden)",
Addr: "N/A",
Suffrage: 0,
}, nil
}
return api.con.LeaderWithID(ctx), nil
}
// Pause implements API.
func (api *APIBackend) Pause(ctx context.Context) error {
return api.con.Pause(ctx)
}
// Resume implements API.
func (api *APIBackend) Resume(ctx context.Context) error {
return api.con.Resume(ctx)
}
// Stop implements API.
func (api *APIBackend) Stop(ctx context.Context) error {
return api.con.Stop(ctx)
}
// TransferLeader implements API. With Raft implementation, a successful call does not mean that leadership transfer is complete
// It just means that leadership transfer is in progress (current leader has initiated a new leader election round and stepped down as leader)
func (api *APIBackend) TransferLeader(ctx context.Context) error {
return api.con.TransferLeader(ctx)
}
// TransferLeaderToServer implements API. With Raft implementation, a successful call does not mean that leadership transfer is complete
// It just means that leadership transfer is in progress (current leader has initiated a new leader election round and stepped down as leader)
func (api *APIBackend) TransferLeaderToServer(ctx context.Context, id string, addr string) error {
return api.con.TransferLeaderToServer(ctx, id, addr)
}
// SequencerHealthy implements API.
func (api *APIBackend) SequencerHealthy(ctx context.Context) (bool, error) {
return api.con.SequencerHealthy(ctx), nil
}
// ClusterMembership implements API.
func (api *APIBackend) ClusterMembership(ctx context.Context) (*consensus.ClusterMembership, error) {
return api.con.ClusterMembership(ctx)
}