-
Notifications
You must be signed in to change notification settings - Fork 26
/
Copy pathoppushdata.go
157 lines (126 loc) · 3.24 KB
/
oppushdata.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
package bscript
import (
"encoding/binary"
"encoding/hex"
"fmt"
)
// EncodeParts takes an array of byte slices and returns a single byte
// slice with the appropriate OP_PUSH commands embedded. The output
// can be encoded to a hex string and viewed as a BitCoin script hex
// string.
//
// For example '76a9140d6cf2ef7bc915d109f77357a71b64fc25e2e11488ac' is
// the hex string of a P2PKH output script.
func EncodeParts(parts [][]byte) ([]byte, error) {
b := make([]byte, 0)
for i, part := range parts {
pd, err := PushDataPrefix(part)
if err != nil {
return nil, fmt.Errorf("%w '%d'", ErrPartTooBig, i)
}
b = append(b, pd...)
b = append(b, part...)
}
return b, nil
}
// PushDataPrefix takes a single byte slice of data and returns its
// OP_PUSHDATA BitCoin encoding prefix based on its length.
//
// For example, the data byte slice '022a8c1a18378885db9054676f17a27f4219045e'
// would be encoded as '14022a8c1a18378885db9054676f17a27f4219045e' in BitCoin.
// The OP_PUSHDATA prefix is '14' since the length of the data is
// 20 bytes (0x14 in decimal is 20).
func PushDataPrefix(data []byte) ([]byte, error) {
b := make([]byte, 0)
l := int64(len(data))
if l <= 75 {
b = append(b, byte(l))
} else if l <= 0xFF {
b = append(b, OpPUSHDATA1)
b = append(b, byte(len(data)))
} else if l <= 0xFFFF {
b = append(b, OpPUSHDATA2)
lenBuf := make([]byte, 2)
binary.LittleEndian.PutUint16(lenBuf, uint16(len(data)))
b = append(b, lenBuf...)
} else if l <= 0xFFFFFFFF { // bt.DefaultSequenceNumber
b = append(b, OpPUSHDATA4)
lenBuf := make([]byte, 4)
binary.LittleEndian.PutUint32(lenBuf, uint32(len(data)))
b = append(b, lenBuf...)
} else {
return nil, ErrDataTooBig
}
return b, nil
}
// DecodeStringParts takes a hex string and decodes the opcodes in it
// returning an array of opcode parts (which could be opcodes or data
// pushed to the stack).
func DecodeStringParts(s string) ([][]byte, error) {
b, err := hex.DecodeString(s)
if err != nil {
return nil, err
}
return DecodeParts(b)
}
// DecodeParts takes bytes and decodes the opcodes in it
// returning an array of opcode parts (which could be opcodes or data
// pushed to the stack).
func DecodeParts(b []byte) ([][]byte, error) {
var r [][]byte
for len(b) > 0 {
// Handle OP codes
switch b[0] {
case OpPUSHDATA1:
if len(b) < 2 {
return r, ErrDataTooSmall
}
l := int(b[1])
b = b[2:]
if len(b) < l {
return r, ErrDataTooSmall
}
part := b[:l]
r = append(r, part)
b = b[l:]
case OpPUSHDATA2:
if len(b) < 3 {
return r, ErrDataTooSmall
}
l := int(binary.LittleEndian.Uint16(b[1:]))
b = b[3:]
if len(b) < l {
return r, ErrDataTooSmall
}
part := b[:l]
r = append(r, part)
b = b[l:]
case OpPUSHDATA4:
if len(b) < 5 {
return r, ErrDataTooSmall
}
l := int(binary.LittleEndian.Uint32(b[1:]))
b = b[5:]
if len(b) < l {
return r, ErrDataTooSmall
}
part := b[:l]
r = append(r, part)
b = b[l:]
default:
if b[0] >= 0x01 && b[0] <= OpPUSHDATA4 {
l := b[0]
if len(b) < int(1+l) {
return r, ErrDataTooSmall
}
part := b[1 : l+1]
r = append(r, part)
b = b[1+l:]
} else {
r = append(r, []byte{b[0]})
b = b[1:]
}
}
}
return r, nil
}