-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathpayload.go
208 lines (154 loc) · 4.94 KB
/
payload.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
package kikcode
import (
"crypto/ed25519"
"crypto/rand"
"slices"
"github.com/pkg/errors"
"github.com/code-payments/code-server/pkg/currency"
"github.com/code-payments/code-server/pkg/kikcode/encoding"
)
/*
Layout 0: Cash
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| T | Amount | Nonce |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
(T) Type (1 byte)
The first byte of the data in all Code scan codes is reserved for the scan
code type. This field indicates which type of scan code data is contained
in the scan code. The expected format for each type is outlined below.
Kin Amount in Quarks (8 bytes)
This field indicates the number of quarks the payment is for. It should be
represented as a 64-bit unsigned integer.
Nonce (11 bytes)
This field is an 11-byte randomly-generated nonce. It should be regenerated
each time a new payment is initiated.
Layout 1: Gift Card
Same as layout 0.
Layout 2: Payment Request
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| T | C | Fiat | Nonce |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
(T) Type (1 byte)
The first byte of the data in all Code scan codes is reserved for the scan
code type. This field indicates which type of scan code data is contained
in the scan code. The expected format for each type is outlined below.
(C) Currency Code (1 bytes)
This field indicates the currency code for the fiat amount. The value is an
encoded index less than 255 that maps to a currency code in CurrencyCode.swift
Fiat Amount (7 bytes)
This field indicates the fiat amount the payment is for. It should represent the
value multiplied by 100 in a 7 byte buffer.
Nonce (11 bytes)
This field is an 11-byte randomly-generated nonce. It should be regenerated
each time a new payment is initiated.
*/
type Kind uint8
const (
Cash Kind = iota
GiftCard
PaymentRequest
)
const (
typeSize = 1
amountSize = 8
nonceSize = 11
payloadSize = 20
)
type IdempotencyKey [nonceSize]byte
type Payload struct {
kind Kind
amountBuffer amountBuffer
nonce IdempotencyKey
}
func NewPayloadFromKinAmount(kind Kind, quarks uint64, nonce IdempotencyKey) *Payload {
return &Payload{
kind: kind,
amountBuffer: newKinAmountBuffer(quarks),
nonce: nonce,
}
}
func NewPayloadFromFiatAmount(kind Kind, currency currency.Code, amount float64, nonce IdempotencyKey) (*Payload, error) {
amountBuffer, err := newFiatAmountBuffer(currency, amount)
if err != nil {
return nil, err
}
return &Payload{
kind: kind,
amountBuffer: amountBuffer,
nonce: nonce,
}, nil
}
func (p *Payload) ToBytes() []byte {
var buffer [payloadSize]byte
buffer[0] = byte(p.kind)
amountBuffer := p.amountBuffer.ToBytes()
for i := 0; i < amountSize; i++ {
buffer[i+typeSize] = amountBuffer[i]
}
for i := 0; i < nonceSize; i++ {
buffer[i+typeSize+amountSize] = p.nonce[i]
}
return buffer[:]
}
func (p *Payload) ToQrCodeDescription(dimension float64) (*Description, error) {
viewPayload, err := encoding.Encode(p.ToBytes())
if err != nil {
return nil, err
}
kikCodePayload := CreateKikCodePayload(viewPayload)
return GenerateDescription(dimension, kikCodePayload)
}
func (p *Payload) GetIdempotencyKey() IdempotencyKey {
return p.nonce
}
func (p *Payload) ToRendezvousKey() ed25519.PrivateKey {
return DeriveRendezvousPrivateKey(p)
}
func GenerateRandomIdempotencyKey() IdempotencyKey {
var buffer [nonceSize]byte
rand.Read(buffer[:])
return buffer
}
type amountBuffer interface {
ToBytes() [amountSize]byte
}
type kinAmountBuffer struct {
quarks uint64
}
func newKinAmountBuffer(quarks uint64) amountBuffer {
return &kinAmountBuffer{
quarks: quarks,
}
}
func (b *kinAmountBuffer) ToBytes() [amountSize]byte {
var buffer [amountSize]byte
for i := 0; i < amountSize; i++ {
buffer[i] = byte(b.quarks >> uint64(8*i) & uint64(0xFF))
}
return buffer
}
type fiatAmountBuffer struct {
currency currency.Code
amount float64
}
func newFiatAmountBuffer(currency currency.Code, amount float64) (amountBuffer, error) {
index := slices.Index(supportedCurrenies, currency)
if index < 0 {
return nil, errors.Errorf("%s currency is not supported", currency)
}
return &fiatAmountBuffer{
currency: currency,
amount: amount,
}, nil
}
func (b *fiatAmountBuffer) ToBytes() [amountSize]byte {
var buffer [amountSize]byte
buffer[0] = byte(slices.Index(supportedCurrenies, b.currency))
amountToSerialize := uint64(b.amount * 100)
for i := 1; i < amountSize; i++ {
buffer[i] = byte(amountToSerialize >> uint64(8*(i-1)) & uint64(0xFF))
}
return buffer
}