forked from mit-dci/lit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
portxo.go
367 lines (309 loc) · 9.08 KB
/
portxo.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
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
package portxo
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/adiabat/btcd/wire"
)
type TxoMode uint8
/* PorTxo specify a utxo, and all the information needed to spend it.
The first 3 fields (Op, Amt, Mode) are required in all cases.
If KeyGen.Depth != 0, that means no key path is supplied, and PrivKey
is probably empty / ignored. Having both a KeyGen and a PrivKey is redunant.
Having neither KeyGen nor PrivKey means there's no private key, and no
indication of how to get it; in that case get the private key from somewhere else.
If BOTH KeyGen AND PrivKey are filled in, add em up! Add the two private keys,
modulo the curve order.
PkScript can also be left empty depending on the mode. Basically only script-hash
modes need it, as the previous pkscript can be generated
PreSigStack are data pushes that happen before the signature is pushed.
I was thinking of putting PostSigStack as well but I think it makes more sense to
always have the data before the sig, since there's pretty much only CHECKSIGVERIFY
now and you might as well put the sig check last.
Also makes sense that with a MAST or pay-to-script-merkle-root type of structure,
you'd want a bunch stuff before the sig.
*/
type PorTxo struct {
// got rid of NetID. If you want to specify different networks / coins,
// use KeyGen.Step[1], that's what it's for.
// Heck, set KeyGen.Depth to 0 and still use Step[1] as the network / coin...
// NetID byte // indicates what network / coin utxo is in
Op wire.OutPoint // unique outpoint
Value int64 // higher is better
Height int32 // block height of utxo (not needed? nice to know?)
Seq uint32 // used for relative timelock
Mode TxoMode // what kind of output
KeyGen
PkScript []byte // if empty, try to generate based on mode and priv key
PreSigStack [][]byte // items to push before the sig
}
// Constants defining txo modes
const (
// Flags which combined can turn into full utxo modes
FlagTxoPubKeyHash TxoMode = 0x01
FlagTxoScript TxoMode = 0x02
FlagTxoWitness TxoMode = 0x04
FlagTxoCompressed TxoMode = 0x08
FlagTxoUncompressed TxoMode = 0x10
// fully specified tx output modes
// raw pubkey outputs (old school)
TxoP2PKUncomp = FlagTxoUncompressed
TxoP2PKComp = FlagTxoCompressed
// pub key hash outputs, standard p2pkh (common)
TxoP2PKHUncomp = FlagTxoPubKeyHash | FlagTxoUncompressed
TxoP2PKHComp = FlagTxoCompressed | FlagTxoPubKeyHash
// script hash
TxoP2SHUncomp = FlagTxoScript | FlagTxoUncompressed
TxoP2SHComp = FlagTxoScript | FlagTxoCompressed
// witness p2wpkh modes
TxoP2WPKHUncomp = FlagTxoWitness | FlagTxoPubKeyHash | FlagTxoUncompressed
TxoP2WPKHComp = FlagTxoWitness | FlagTxoPubKeyHash | FlagTxoCompressed
// witness script hash
TxoP2WSHUncomp = FlagTxoWitness | FlagTxoScript | FlagTxoUncompressed
TxoP2WSHComp = FlagTxoWitness | FlagTxoScript | FlagTxoCompressed
// unknown
TxoUnknownMode = 0x80
)
var modeStrings = map[TxoMode]string{
TxoP2PKUncomp: "raw pubkey uncompressed",
TxoP2PKComp: "raw pubkey compressed",
TxoP2PKHUncomp: "pubkey hash uncompressed",
TxoP2PKHComp: "pubkey hash compressed",
TxoP2SHUncomp: "script hash uncompressed",
TxoP2SHComp: "script hash compressed",
TxoP2WPKHUncomp: "witness pubkey hash uncompressed",
TxoP2WPKHComp: "witness pubkey hash compressed",
TxoP2WSHUncomp: "witness script hash uncompressed",
TxoP2WSHComp: "witness script hash compressed",
}
// String returns the InvType in human-readable form.
func (m TxoMode) String() string {
s, ok := modeStrings[m]
if ok {
return s
}
return fmt.Sprintf("unknown TxoMode %x", uint8(m))
}
// Compare deep-compares two portable utxos, returning true if they're the same
func (u *PorTxo) Equal(z *PorTxo) bool {
if u == nil || z == nil {
return false
}
if !u.Op.Hash.IsEqual(&z.Op.Hash) {
return false
}
if u.Op.Index != z.Op.Index {
return false
}
if u.Value != z.Value || u.Seq != z.Seq || u.Mode != z.Mode || u.Height != z.Height {
return false
}
if u.KeyGen.PrivKey != z.KeyGen.PrivKey {
return false
}
if !bytes.Equal(u.KeyGen.Bytes(), z.KeyGen.Bytes()) {
return false
}
if !bytes.Equal(u.PkScript, z.PkScript) {
return false
}
// compare pre sig stack lengths
if len(u.PreSigStack) != len(z.PreSigStack) {
return false
}
// if we're here, lenghts for both are the same. Iterate and compare stacks
for i, _ := range u.PreSigStack {
if !bytes.Equal(u.PreSigStack[i], z.PreSigStack[i]) {
return false
}
}
return true
}
func (u *PorTxo) String() string {
var s string
var empty [32]byte
if u == nil {
return "nil utxo"
}
s = u.Op.String()
s += fmt.Sprintf("\n\ta:%d h:%d seq:%d %s\n",
u.Value, u.Height, u.Seq, u.Mode.String())
if u.KeyGen.PrivKey == empty {
s += fmt.Sprintf("\tprivate key not available (zero)\n")
} else {
s += fmt.Sprintf("\tprivate key available (non-zero)\n")
}
if u.KeyGen.Depth == 0 || u.KeyGen.Depth > 5 {
s += fmt.Sprintf("\tno key derivation path\n")
} else {
s += fmt.Sprintf("%s\n", u.KeyGen.String())
}
s += fmt.Sprintf("\tPkScript (len %d): %x\n", len(u.PkScript), u.PkScript)
// list pre-sig elements
s += fmt.Sprintf("\t%d pre-sig elements:", len(u.PreSigStack))
for _, e := range u.PreSigStack {
s += fmt.Sprintf(" [%x]", e)
}
s += fmt.Sprintf("\n")
return s
}
/* serialized (im/ex)Portable Utxos are 106 up to 357 bytes.
Op 36
Amt 8
Height 4
Seq 4
Mode 1
Keygen 53
PreStackNum (1 byte)
PreStackItemLen (1 byte)
PreStackItem (max 255 bytes each)
PostStackNum (1 byte)
PostStackItemLen (1 byte)
PostStackItem (max 255 bytes each)
PkScriptLen (1 byte)
PkScript (max 255 bytes)
*/
func PorTxoFromBytes(b []byte) (*PorTxo, error) {
// should be max 1KiB for now
if len(b) < 106 || len(b) > 1024 {
return nil, fmt.Errorf("%d bytes, need 106-1024", len(b))
}
buf := bytes.NewBuffer(b)
var u PorTxo
var err error
err = u.Op.Hash.SetBytes(buf.Next(32))
if err != nil {
return nil, err
}
err = binary.Read(buf, binary.BigEndian, &u.Op.Index)
if err != nil {
return nil, err
}
err = binary.Read(buf, binary.BigEndian, &u.Value)
if err != nil {
return nil, err
}
err = binary.Read(buf, binary.BigEndian, &u.Height)
if err != nil {
return nil, err
}
err = binary.Read(buf, binary.BigEndian, &u.Seq)
if err != nil {
return nil, err
}
err = binary.Read(buf, binary.BigEndian, &u.Mode)
if err != nil {
return nil, err
}
var kgenarr [53]byte
copy(kgenarr[:], buf.Next(53))
u.KeyGen = KeyGenFromBytes(kgenarr)
// get PkScript length byte
PkScriptLen, err := buf.ReadByte()
if err != nil {
return nil, err
}
// make PkScript the right size to write into
u.PkScript = make([]byte, PkScriptLen)
// write from buffer into PkScript
_, err = buf.Read(u.PkScript)
if err != nil {
return nil, err
}
// get number of pre-sig stack items
PreSigStackNumItems, err := buf.ReadByte()
if err != nil {
return nil, err
}
u.PreSigStack = make([][]byte, PreSigStackNumItems)
// iterate through reading each presigStack item
for i, _ := range u.PreSigStack {
// read length of this element
elementLength, err := buf.ReadByte()
if err != nil {
return nil, err
}
// size element slice
u.PreSigStack[i] = make([]byte, elementLength)
// copy element data
_, err = buf.Read(u.PreSigStack[i])
if err != nil {
return nil, err
}
}
return &u, nil
}
func (u *PorTxo) Bytes() ([]byte, error) {
if u == nil {
return nil, fmt.Errorf("Can't serialize nil Utxo")
}
var buf bytes.Buffer
// _, err := buf.Write(u.Op.Hash.CloneBytes())
_, err := buf.Write(u.Op.Hash.CloneBytes())
if err != nil {
return nil, err
}
err = binary.Write(&buf, binary.BigEndian, u.Op.Index)
if err != nil {
return nil, err
}
err = binary.Write(&buf, binary.BigEndian, u.Value)
if err != nil {
return nil, err
}
err = binary.Write(&buf, binary.BigEndian, u.Height)
if err != nil {
return nil, err
}
err = binary.Write(&buf, binary.BigEndian, u.Seq)
if err != nil {
return nil, err
}
err = binary.Write(&buf, binary.BigEndian, u.Mode) // mode
if err != nil {
return nil, err
}
_, err = buf.Write(u.KeyGen.Bytes()) // keypath
if err != nil {
return nil, err
}
// check pkScript length
if len(u.PkScript) > 255 {
return nil, fmt.Errorf("PkScript too long (255 byte max)")
}
// write length of pkScript
err = buf.WriteByte(uint8(len(u.PkScript)))
if err != nil {
return nil, err
}
// write PkScript
_, err = buf.Write(u.PkScript)
// check Pre-sig stack number of elements
if len(u.PreSigStack) > 255 {
return nil, fmt.Errorf("Too many PreSigStack items (255 items max)")
}
// write number of PreSigStack items
err = buf.WriteByte(uint8(len(u.PreSigStack)))
if err != nil {
return nil, err
}
// iterate through PreSigStack items and write each
for i, element := range u.PreSigStack {
// check element length
if len(element) > 255 {
return nil, fmt.Errorf("PreSigStack item %d %d bytes (255 max)",
i, len(element))
}
// write length of element
err = buf.WriteByte(uint8(len(element)))
if err != nil {
return nil, err
}
// write element itself
_, err = buf.Write(element)
if err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}