forked from hyperledger-labs/go-perun
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathadjudicator.go
313 lines (269 loc) · 10.5 KB
/
adjudicator.go
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
// Copyright 2025 - See NOTICE file for copyright holders.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package channel
import (
"context"
"fmt"
"time"
"github.com/pkg/errors"
"perun.network/go-perun/wallet"
)
//go:generate mockery --name AdjudicatorSubscription --output ../watcher/internal/mocks
//go:generate mockery --name RegisterSubscriber --output ../watcher/internal/mocks
type (
// Adjudicator is the interface that groups the Register, Withdraw,
// Progress and Subscribe methods.
//
// An adjudicator represents an adjudicator contract on the blockchain. A
// channel state needs to be registered before it can be progressed or
// withdrawn. No-App channels skip the force-execution phase and can be
// withdrawn directly after the refutation phase has finished. Channels in
// a final state can directly be withdrawn after registration.
//
// Furthermore, an Adjudicator has a method for subscribing to
// AdjudicatorEvents. Those events might be triggered by a Register or
// Progress call on the adjudicator from any channel participant.
Adjudicator interface {
Registerer
Withdrawer
Progresser
EventSubscriber
}
// RegisterSubscriber is the interface that groups the Register and
// Subscribe methods.
//
// These methods are used to watch for adjudicator events on the blockchain
// and dispute, if the event is a RegisteredEvent and the state in the
// event is not the latest.
RegisterSubscriber interface {
Registerer
EventSubscriber
}
// Registerer is the interface that wraps the Register method.
//
// Register should register the given ledger channel state on-chain.
// If the channel has locked funds into sub-channels, the corresponding
// signed sub-channel states must be provided.
Registerer interface {
Register(context.Context, AdjudicatorReq, []SignedState) error
}
// Withdrawer is the interface that wraps the Withdraw method.
//
// Withdraw should conclude and withdraw the registered state, so that the
// final outcome is set on the asset holders and funds are withdrawn
// (dependent on the architecture of the contracts). It must be taken into
// account that a peer might already have concluded the same channel.
// If the channel has locked funds in sub-channels, the states of the
// corresponding sub-channels need to be supplied additionally.
Withdrawer interface {
Withdraw(context.Context, AdjudicatorReq, StateMap) error
}
// Progresser is the interface that wraps the Progress method.
//
// Progress should try to progress an on-chain registered state to the new
// state given in ProgressReq. The Transaction field only needs to
// contain the state, the signatures can be nil, since the old state is
// already registered on the adjudicator.
Progresser interface {
Progress(context.Context, ProgressReq) error
}
// EventSubscriber is the interface that wraps the Subscribe method.
//
// Subscribe returns an AdjudicatorEvent subscription.
//
// The context should only be used to establish the subscription. The
// subscription should be closed by calling Close on the subscription after
// the channel is closed.
EventSubscriber interface {
Subscribe(context.Context, ID) (AdjudicatorSubscription, error)
}
// An AdjudicatorReq collects all necessary information to make calls to the
// adjudicator.
//
// If the Secondary flag is set to true, it is assumed that this is an
// on-chain request that is executed by the other channel participants as well
// and the Adjudicator backend may run an optimized on-chain transaction
// protocol, possibly saving unnecessary double sending of transactions.
AdjudicatorReq struct {
Params *Params
Acc map[wallet.BackendID]wallet.Account
Tx Transaction
Idx Index // Always the own index
Secondary bool // Optimized secondary call protocol
}
// SignedState represents a signed channel state including parameters.
SignedState struct {
Params *Params
State *State
Sigs []wallet.Sig
}
// A ProgressReq collects all necessary information to do a progress call to
// the adjudicator.
ProgressReq struct {
AdjudicatorReq // Tx should refer to the currently registered state
NewState *State // New state to progress into
Sig wallet.Sig // Own signature on the new state
}
// An AdjudicatorSubscription is a subscription to AdjudicatorEvents for a
// specific channel. The subscription should also return the most recent past
// event, if there is any. It should skip all Registered events and only
// return the most recent Progressed event if the channel already got progressed.
//
// The usage of the subscription should be similar to that of an iterator.
// Next calls should block until a new event is generated (or the first past
// event has been found). If the subscription is closed or an error is
// produced, Next should return nil and Err should tell the possible error.
AdjudicatorSubscription interface {
// Next returns the most recent past or next future event. If the subscription is
// closed or any other error occurs, it should return nil.
Next() AdjudicatorEvent
// Err returns the error status of the subscription. After Next returns nil,
// Err should be checked for an error.
Err() error
// Close closes the subscription. Any call to Next should immediately return
// nil.
Close() error
}
// An AdjudicatorEvent is any event that an on-chain adjudicator call might
// cause, currently either a Registered or Progressed event.
// The type of the event should be checked with a type switch.
AdjudicatorEvent interface {
ID() ID
Timeout() Timeout
Version() uint64
}
// An AdjudicatorEventBase implements the AdjudicatorEvent interface. It can
// be embedded to implement an AdjudicatorEvent.
AdjudicatorEventBase struct {
IDV ID // Channel ID
TimeoutV Timeout // Current phase timeout
VersionV uint64 // Registered version
}
// ProgressedEvent is the abstract event that signals an on-chain progression.
ProgressedEvent struct {
AdjudicatorEventBase // Channel ID and ForceExec phase timeout
State *State // State that was progressed into
Idx Index // Index of the participant who progressed
}
// RegisteredEvent is the abstract event that signals a successful state
// registration on the blockchain.
RegisteredEvent struct {
AdjudicatorEventBase // Channel ID and Refutation phase timeout
State *State
Sigs []wallet.Sig
}
// ConcludedEvent signals channel conclusion.
ConcludedEvent struct {
AdjudicatorEventBase
}
// A Timeout is an abstract timeout of a channel dispute. A timeout can be
// elapsed and it can be waited on it to elapse.
Timeout interface {
// IsElapsed should return whether the timeout has elapsed at the time of
// the call of this method.
IsElapsed(context.Context) bool
// Wait waits for the timeout to elapse. If the context is canceled, Wait
// should return immediately with the context's error.
Wait(context.Context) error
}
// StateMap represents a channel state tree.
StateMap map[ID]*State
)
// NewProgressReq creates a new ProgressReq object.
func NewProgressReq(ar AdjudicatorReq, newState *State, sig wallet.Sig) *ProgressReq {
return &ProgressReq{ar, newState, sig}
}
// NewAdjudicatorEventBase creates a new AdjudicatorEventBase object.
func NewAdjudicatorEventBase(c ID, t Timeout, v uint64) *AdjudicatorEventBase {
return &AdjudicatorEventBase{
IDV: c,
TimeoutV: t,
VersionV: v,
}
}
// ID returns the channel ID.
func (b AdjudicatorEventBase) ID() ID { return b.IDV }
// Timeout returns the phase timeout.
func (b AdjudicatorEventBase) Timeout() Timeout { return b.TimeoutV }
// Version returns the channel version.
func (b AdjudicatorEventBase) Version() uint64 { return b.VersionV }
// NewRegisteredEvent creates a new RegisteredEvent.
func NewRegisteredEvent(id ID, timeout Timeout, version uint64, state *State, sigs []wallet.Sig) *RegisteredEvent {
return &RegisteredEvent{
AdjudicatorEventBase: AdjudicatorEventBase{
IDV: id,
TimeoutV: timeout,
VersionV: version,
},
State: state,
Sigs: sigs,
}
}
// NewProgressedEvent creates a new ProgressedEvent.
func NewProgressedEvent(id ID, timeout Timeout, state *State, idx Index) *ProgressedEvent {
return &ProgressedEvent{
AdjudicatorEventBase: AdjudicatorEventBase{
IDV: id,
TimeoutV: timeout,
VersionV: state.Version,
},
State: state,
Idx: idx,
}
}
// NewConcludedEvent creates a new ConcludedEvent.
func NewConcludedEvent(id ID, timeout Timeout, version uint64) *ConcludedEvent {
return &ConcludedEvent{
AdjudicatorEventBase: AdjudicatorEventBase{
IDV: id,
TimeoutV: timeout,
VersionV: version,
},
}
}
// ElapsedTimeout is a Timeout that is always elapsed.
type ElapsedTimeout struct{}
// IsElapsed returns true.
func (t *ElapsedTimeout) IsElapsed(context.Context) bool { return true }
// Wait immediately return nil.
func (t *ElapsedTimeout) Wait(context.Context) error { return nil }
// String says that this is an always elapsed timeout.
func (t *ElapsedTimeout) String() string { return "<Always elapsed timeout>" }
// TimeTimeout is a Timeout that elapses after a fixed time.Time.
type TimeTimeout struct{ time.Time }
// IsElapsed returns whether the current time is after the fixed timeout.
func (t *TimeTimeout) IsElapsed(context.Context) bool { return t.After(time.Now()) }
// Wait waits until the timeout has elapsed or the context is cancelled.
func (t *TimeTimeout) Wait(ctx context.Context) error {
select {
case <-time.After(time.Until(t.Time)):
return nil
case <-ctx.Done():
return errors.Wrap(ctx.Err(), "ctx done")
}
}
// String returns the timeout's date and time string.
func (t *TimeTimeout) String() string {
return fmt.Sprintf("<Timeout: %v>", t.Time)
}
// MakeStateMap creates a new StateMap object.
func MakeStateMap() StateMap {
return make(map[ID]*State)
}
// Add adds the given states to the state map.
func (m StateMap) Add(states ...*State) {
for _, s := range states {
m[s.ID] = s
}
}