-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathbob.go
220 lines (189 loc) · 5.15 KB
/
bob.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
package aip
import (
"encoding/hex"
"errors"
"strconv"
"strings"
ec "github.com/bitcoin-sv/go-sdk/primitives/ec"
"github.com/bitcoinschema/go-bpu"
)
// NewFromTape will create a new AIP object from a bob.Tape
// Using the FromTape() alone will prevent validation (data is needed via SetData to enable)
func NewFromTape(tape bpu.Tape) (a *Aip) {
a = new(Aip)
a.FromTape(tape)
return
}
// FromTape takes a BOB Tape and returns an Aip data structure.
// Using the FromTape() alone will prevent validation (data is needed via SetData to enable)
func (a *Aip) FromTape(tape bpu.Tape) {
// Not a valid tape?
if len(tape.Cell) < 4 {
return
}
// Loop to find start of AIP
var startIndex int
found := false
for i, cell := range tape.Cell {
if *cell.S == Prefix {
startIndex = i
found = true
break
}
}
if !found {
return
}
// Set the AIP fields
if tape.Cell[startIndex+1].S != nil {
a.Algorithm = Algorithm(*tape.Cell[startIndex+1].S)
}
if tape.Cell[startIndex+2].S != nil {
a.AlgorithmSigningComponent = *tape.Cell[startIndex+2].S
}
if tape.Cell[startIndex+3].B != nil {
a.Signature = *tape.Cell[startIndex+3].B
}
// Final index count
finalIndexCount := startIndex + 4
// Store the indices
if len(tape.Cell) > finalIndexCount {
// TODO: Consider OP_RETURN is included in sig when processing a tx using indices
// Loop over remaining indices if they exist and append to indices slice
a.Indices = make([]int, len(tape.Cell)-finalIndexCount)
for x := finalIndexCount - 1; x < len(tape.Cell); x++ {
if tape.Cell[x].S == nil {
continue
}
if index, err := strconv.Atoi(*tape.Cell[x].S); err == nil {
a.Indices = append(a.Indices, index)
}
}
}
}
// NewFromTapes will create a new AIP object from a []bob.Tape
// Using the FromTapes() alone will prevent validation (data is needed via SetData to enable)
func NewFromTapes(tapes []bpu.Tape) (a *Aip) {
// Loop tapes -> cells (only supporting 1 sig right now)
for _, t := range tapes {
for _, cell := range t.Cell {
if cell.S != nil && *cell.S == Prefix {
a = new(Aip)
a.FromTape(t)
a.SetDataFromTapes(tapes)
return
}
}
}
return
}
// SetDataFromTapes sets the data the AIP signature is signing
func (a *Aip) SetDataFromTapes(tapes []bpu.Tape) {
// Set OP_RETURN to be consistent with BitcoinFiles SDK
var data = []string{opReturn}
if len(a.Indices) == 0 {
// Walk over all output values and concatenate them until we hit the AIP prefix, then add in the separator
for _, tape := range tapes {
for _, cell := range tape.Cell {
if cell.S != nil && *cell.S == Prefix {
data = append(data, pipe)
a.Data = data
return
}
// Skip the OPS
// if cell.Ops != nil {
if cell.Op != nil && (*cell.Op == 0 || *cell.Op > 0x4e) {
continue
}
if cell.S != nil {
data = append(data, strings.TrimSpace(*cell.S))
}
}
}
} else {
var indexCt = 0
for _, tape := range tapes {
for _, cell := range tape.Cell {
if cell.S != nil && *cell.S != Prefix && contains(a.Indices, indexCt) {
data = append(data, *cell.S)
} else {
data = append(data, pipe)
}
indexCt++
}
}
a.Data = data
}
}
// SignBobOpReturnData appends a signature to a BOB Tx by adding a
// protocol separator followed by AIP information
func SignBobOpReturnData(privateKey *ec.PrivateKey, algorithm Algorithm, output bpu.Output) (*bpu.Output, *Aip, error) {
// Parse the data to sign
var dataToSign []string
for _, tape := range output.Tape {
for _, cell := range tape.Cell {
if cell.S != nil {
dataToSign = append(dataToSign, *cell.S)
} else {
// TODO: Review this case. Should we assume the b64 is signed?
// Should protocol doc for AIP mention this?
if cell.B != nil {
dataToSign = append(dataToSign, *cell.B)
}
// else if cell.Op != nil {
// dataToSign = append(dataToSign, string(*cell.Op))
// }
}
}
}
// Sign the data
a, err := Sign(privateKey, algorithm, strings.Join(dataToSign, ""))
if err != nil {
return nil, nil, err
}
algoHex := hex.EncodeToString([]byte(algorithm))
algoStr := string(algorithm)
hexAlgoSigningComponent := hex.EncodeToString([]byte(a.AlgorithmSigningComponent))
hexSig := hex.EncodeToString([]byte(a.Signature))
// Create the output tape
output.Tape = append(output.Tape, bpu.Tape{
Cell: []bpu.Cell{{
H: &hexPrefix,
S: &Prefix,
}, {
H: &algoHex,
S: &algoStr,
}, {
H: &hexAlgoSigningComponent,
S: &a.AlgorithmSigningComponent,
}, {
H: &hexSig,
S: &a.Signature,
}},
})
return &output, a, nil
}
// ValidateTapes validates the AIP signature for a given []bob.Tape
func ValidateTapes(tapes []bpu.Tape) (bool, error) {
// Loop tapes -> cells (only supporting 1 sig right now)
for _, tape := range tapes {
for _, cell := range tape.Cell {
// Once we hit AIP Prefix, stop
if cell.S != nil && *cell.S == Prefix {
a := NewFromTape(tape)
a.SetDataFromTapes(tapes)
return a.Validate()
}
}
}
return false, errors.New("no AIP tape found")
}
// contains looks in a slice for a given value
func contains(s []int, e int) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}