/
command.go
121 lines (107 loc) · 3.32 KB
/
command.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
package fsm
import (
"time"
"github.com/interstellar/starlight/errors"
"github.com/interstellar/starlight/worizon/xlm"
)
// CommandName is the type of a user-command constant.
type CommandName string
// User commands.
const (
CreateChannel CommandName = "CreateChannel"
CleanUp CommandName = "CleanUp"
CloseChannel CommandName = "CloseChannel"
TopUp CommandName = "TopUp"
ChannelPay CommandName = "ChannelPay"
ForceClose CommandName = "ForceClose"
Pay CommandName = "Pay"
AddAsset CommandName = "AddAsset"
RemoveAsset CommandName = "RemoveAsset"
)
// Command contains a command name and its required arguments.
type Command struct {
Name CommandName
Amount xlm.Amount // for TopUp, ChannelPay, or Pay
Time time.Time
Recipient string // for Pay
AssetCode string // for AddAsset, RemoveAsset
Issuer string // for AddAsset, RemoveAsset
}
var commandFuncs = map[CommandName]func(*Command, *Updater) error{
CreateChannel: createChannelFn,
CleanUp: cleanUpFn,
CloseChannel: closeChannelFn,
TopUp: topUpFn,
ChannelPay: channelPayFn,
ForceClose: forceCloseFn,
}
func createChannelFn(_ *Command, u *Updater) error {
if u.C.State != Start {
return errors.Wrapf(ErrUnexpectedState, "got %s, want %s", u.C.State, Start)
}
return u.transitionTo(SettingUp)
}
func cleanUpFn(_ *Command, u *Updater) error {
if u.C.State != ChannelProposed {
return errors.Wrapf(ErrUnexpectedState, "got %s, want %s", u.C.State, ChannelProposed)
}
// Get back funds associated with funding tx.
// Setup balances are added back in processing MergeOps.
u.H.NativeBalance += u.C.totalFundingTxAmount()
u.H.Seqnum++
return u.transitionTo(AwaitingCleanup)
}
func closeChannelFn(_ *Command, u *Updater) error {
if u.C.State != Open {
return errors.Wrapf(ErrUnexpectedState, "got %s, want %s", u.C.State, Open)
}
return u.transitionTo(AwaitingClose)
}
func topUpFn(c *Command, u *Updater) error {
if u.C.State != Open {
return errors.Wrapf(ErrUnexpectedState, "got %s, want %s", u.C.State, Open)
}
if u.C.Role != Host {
return errors.Wrap(errUnexpectedRole, "only host can top up")
}
if u.C.TopUpAmount != 0 {
return errTopUpInProgress
}
if c.Amount > u.H.NativeBalance {
return errors.Wrapf(ErrInsufficientFunds, "balance %d", u.C.HostAmount)
}
u.C.TopUpAmount = c.Amount
u.H.NativeBalance -= c.Amount
u.H.NativeBalance -= u.C.HostFeerate
u.H.Seqnum++
return u.transitionTo(Open)
}
func channelPayFn(c *Command, u *Updater) error {
if u.C.State != Open {
return errors.Wrapf(ErrUnexpectedState, "got %s, want %s", u.C.State, Open)
}
u.C.PendingAmountSent = c.Amount
if u.C.PaymentTime.After(c.Time) {
u.C.PendingPaymentTime = u.C.PaymentTime
} else {
u.C.PendingPaymentTime = c.Time
}
switch u.C.Role {
case Guest:
if u.C.GuestAmount < c.Amount {
return errors.Wrapf(ErrInsufficientFunds, "balance %d", u.C.GuestAmount)
}
case Host:
if u.C.HostAmount < c.Amount {
return errors.Wrapf(ErrInsufficientFunds, "balance %d", u.C.HostAmount)
}
}
u.C.RoundNumber++
return u.transitionTo(PaymentProposed)
}
func forceCloseFn(_ *Command, u *Updater) error {
if isSetupState(u.C.State) || isForceCloseState(u.C.State) {
return errors.Wrapf(ErrUnexpectedState, "got %s, want non-starting, non-force close state", u.C.State)
}
return u.setForceCloseState()
}