forked from lnproxy/lnproxy-relay
-
Notifications
You must be signed in to change notification settings - Fork 0
/
token.go
190 lines (155 loc) · 4.85 KB
/
token.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
package lsat
import (
"bytes"
"encoding/binary"
"fmt"
"time"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
"gopkg.in/macaroon.v2"
)
var (
// zeroPreimage is an empty, invalid payment preimage that is used to
// initialize pending tokens with.
zeroPreimage lntypes.Preimage
)
// Token is the main type to store an LSAT token in.
type Token struct {
// PaymentHash is the hash of the LSAT invoice that needs to be paid.
// Knowing the preimage to this hash is seen as proof of payment by the
// authentication server.
PaymentHash lntypes.Hash
// Preimage is the proof of payment indicating that the token has been
// paid for if set. If the preimage is empty, the payment might still
// be in transit.
Preimage lntypes.Preimage
// AmountPaid is the total amount in msat that the user paid to get the
// token. This does not include routing fees.
AmountPaid lnwire.MilliSatoshi
// RoutingFeePaid is the total amount in msat that the user paid in
// routing fee to get the token.
RoutingFeePaid lnwire.MilliSatoshi
// TimeCreated is the moment when this token was created.
TimeCreated time.Time
// baseMac is the base macaroon in its original form as baked by the
// authentication server. No client side caveats have been added to it
// yet.
baseMac *macaroon.Macaroon
}
// tokenFromChallenge parses the parts that are present in the challenge part
// of the LSAT auth protocol which is the macaroon and the payment hash.
func tokenFromChallenge(baseMac []byte, paymentHash *[32]byte) (*Token, error) {
// First, validate that the macaroon is valid and can be unmarshaled.
mac := &macaroon.Macaroon{}
err := mac.UnmarshalBinary(baseMac)
if err != nil {
return nil, fmt.Errorf("unable to unmarshal macaroon: %v", err)
}
token := &Token{
TimeCreated: time.Now(),
baseMac: mac,
Preimage: zeroPreimage,
}
hash, err := lntypes.MakeHash(paymentHash[:])
if err != nil {
return nil, err
}
token.PaymentHash = hash
return token, nil
}
// BaseMacaroon returns the base macaroon as received from the authentication
// server.
func (t *Token) BaseMacaroon() *macaroon.Macaroon {
return t.baseMac.Clone()
}
// PaidMacaroon returns the base macaroon with the proof of payment (preimage)
// added as a first-party-caveat.
func (t *Token) PaidMacaroon() (*macaroon.Macaroon, error) {
mac := t.BaseMacaroon()
err := AddFirstPartyCaveats(
mac, NewCaveat(PreimageKey, t.Preimage.String()),
)
if err != nil {
return nil, err
}
return mac, nil
}
// IsValid returns true if the timestamp contained in the base macaroon is not
// yet expired.
func (t *Token) IsValid() bool {
// TODO(guggero): Extract and validate from caveat once we add an
// expiration date to the LSAT.
return true
}
// isPending returns true if the payment for the LSAT is still in flight and we
// haven't received the preimage yet.
func (t *Token) isPending() bool {
return t.Preimage == zeroPreimage
}
// serializeToken returns a byte-serialized representation of the token.
func serializeToken(t *Token) ([]byte, error) {
var b bytes.Buffer
baseMacBytes, err := t.baseMac.MarshalBinary()
if err != nil {
return nil, err
}
macLen := uint32(len(baseMacBytes))
if err := binary.Write(&b, byteOrder, macLen); err != nil {
return nil, err
}
if err := binary.Write(&b, byteOrder, baseMacBytes); err != nil {
return nil, err
}
if err := binary.Write(&b, byteOrder, t.PaymentHash); err != nil {
return nil, err
}
if err := binary.Write(&b, byteOrder, t.Preimage); err != nil {
return nil, err
}
if err := binary.Write(&b, byteOrder, t.AmountPaid); err != nil {
return nil, err
}
if err := binary.Write(&b, byteOrder, t.RoutingFeePaid); err != nil {
return nil, err
}
timeUnix := t.TimeCreated.UnixNano()
if err := binary.Write(&b, byteOrder, timeUnix); err != nil {
return nil, err
}
return b.Bytes(), nil
}
// deserializeToken constructs a token by reading it from a byte slice.
func deserializeToken(value []byte) (*Token, error) {
r := bytes.NewReader(value)
var macLen uint32
if err := binary.Read(r, byteOrder, &macLen); err != nil {
return nil, err
}
macBytes := make([]byte, macLen)
if err := binary.Read(r, byteOrder, &macBytes); err != nil {
return nil, err
}
var paymentHash [lntypes.HashSize]byte
if err := binary.Read(r, byteOrder, &paymentHash); err != nil {
return nil, err
}
token, err := tokenFromChallenge(macBytes, &paymentHash)
if err != nil {
return nil, err
}
if err := binary.Read(r, byteOrder, &token.Preimage); err != nil {
return nil, err
}
if err := binary.Read(r, byteOrder, &token.AmountPaid); err != nil {
return nil, err
}
if err := binary.Read(r, byteOrder, &token.RoutingFeePaid); err != nil {
return nil, err
}
var unixNano int64
if err := binary.Read(r, byteOrder, &unixNano); err != nil {
return nil, err
}
token.TimeCreated = time.Unix(0, unixNano)
return token, nil
}