generated from bitcoin-sv/template
-
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbap.go
More file actions
241 lines (210 loc) · 7.27 KB
/
bap.go
File metadata and controls
241 lines (210 loc) · 7.27 KB
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
package bitcom
import (
"encoding/json"
"strconv"
"strings"
"github.com/bsv-blockchain/go-sdk/script"
)
// BAPPrefix is the bitcom protocol prefix for Bitcoin Attestation Protocol (BAP)
const (
BAPPrefix = "1BAPSuaPnfGnSBM3GLV9yhxUdYe4vGbdMT"
pipeSeparator string = "|"
)
// AttestationType is an enum for BAP Type Constants
type AttestationType string
// BAP attestation type constants
const (
ATTEST AttestationType = "ATTEST"
ID AttestationType = "ID"
REVOKE AttestationType = "REVOKE"
ALIAS AttestationType = "ALIAS"
)
// Bap represents a Bitcoin Attestation Protocol data structure
type Bap struct {
BitcomIndex uint `json:"ii,omitempty"` // Index of the AIP in the Bitcom transaction
Type AttestationType `json:"type"`
IDKey string `json:"id_key,omitempty"` // ID: Identity key, ATTEST: URN Hash
Address string `json:"address,omitempty"` // Address value
Sequence uint64 `json:"sequence"`
Algorithm string `json:"algorithm,omitempty"` // AIP algorithm
SignerAddr string `json:"signer_addr,omitempty"` // AIP signing address
Signature string `json:"signature,omitempty"` // AIP signature
RootAddress string `json:"root_address,omitempty"` // For ID
IsSignedByID bool `json:"is_signed_by_id"` // Whether it's signed by the ID
Profile json.RawMessage `json:"profile,omitempty"` // Profile for ID
}
// DecodeBAP decodes a BAP protocol message from a Bitcom structure
func DecodeBAP(b *Bitcom) *Bap {
// Safety check for nil
if b == nil || len(b.Protocols) == 0 {
return nil
}
// Look for the BAP protocol data
for ii, proto := range b.Protocols {
// Check if this is a BAP protocol entry
if proto.Protocol == BAPPrefix {
// Create a BAP struct to hold the decoded data
bap := &Bap{
BitcomIndex: uint(ii),
}
// Parse script into chunks for analysis
scr := script.NewFromBytes(proto.Script)
if scr == nil {
continue
}
/*
I fixed this in bitcom.Lock(). It was constructing the script improperly and pushing it as one big pushdata
*/
// // Try a direct approach to extract the data
// s := proto.Script
// var pos int
// // Skip the first byte if it's a length byte (like 0x3c which is 60 in decimal)
// if len(s) > 0 && s[0] > 0 && s[0] < 0x4c {
// pos = 1
// }
// // Create a temp slice for the script data without the length byte
// scriptData := s[pos:]
// tempScr := script.NewFromBytes(scriptData)
// if tempScr == nil {
// continue
// }
// Now try to get the chunks
chunks, err := scr.Chunks()
if err != nil || len(chunks) < 2 { // Need at least TYPE and one other field
// If parsing as chunks failed, try a different approach
// Check if we can find the ID or ATTEST type in the script
scriptStr := string(*scr)
if strings.Contains(scriptStr, string(ID)) {
// Found ID type
parts := strings.SplitN(scriptStr, string(ID), 2)
if len(parts) > 1 {
bap.Type = ID
remainingParts := strings.SplitN(parts[1], " ", 3)
if len(remainingParts) >= 2 {
bap.IDKey = strings.TrimSpace(remainingParts[0])
bap.Address = strings.TrimSpace(remainingParts[1])
return bap
}
}
} else if strings.Contains(scriptStr, string(ATTEST)) {
// Found ATTEST type
parts := strings.SplitN(scriptStr, string(ATTEST), 2)
if len(parts) > 1 {
bap.Type = ATTEST
remainingParts := strings.SplitN(parts[1], " ", 3)
if len(remainingParts) >= 2 {
bap.IDKey = strings.TrimSpace(remainingParts[0])
bap.Sequence, _ = strconv.ParseUint(remainingParts[1], 10, 64)
return bap
}
}
}
continue
}
// Parse BAP data fields
// First chunk should be the TYPE (ATTEST, ID, REVOKE, ALIAS)
bap.Type = AttestationType(chunks[0].Data)
// Process based on the BAP type
switch bap.Type {
case ID:
// ID structure: ID <identity key> <address>
if len(chunks) >= 3 {
bap.IDKey = string(chunks[1].Data)
bap.Address = string(chunks[2].Data)
// Look for AIP signature data which follows a pipe separator
pipeIdx := -1
for i := 3; i < len(chunks); i++ {
if string(chunks[i].Data) == pipeSeparator {
pipeIdx = i
break
}
}
if pipeIdx >= 0 && pipeIdx+3 < len(chunks) {
// AIP signature data found
bap.Algorithm = string(chunks[pipeIdx+2].Data)
bap.SignerAddr = string(chunks[pipeIdx+3].Data)
if pipeIdx+4 < len(chunks) {
bap.Signature = string(chunks[pipeIdx+4].Data)
bap.RootAddress = bap.SignerAddr // In ID, the signer is the root address
bap.IsSignedByID = true
}
}
}
case ATTEST:
// ATTEST structure: ATTEST <txid> <sequence number>
if len(chunks) >= 3 {
bap.IDKey = string(chunks[1].Data) // TXID being attested to
bap.Sequence, _ = strconv.ParseUint(string(chunks[2].Data), 10, 64)
// Look for AIP signature data
pipeIdx := -1
for i := 3; i < len(chunks); i++ {
if string(chunks[i].Data) == pipeSeparator {
pipeIdx = i
break
}
}
if pipeIdx >= 0 && pipeIdx+3 < len(chunks) {
// AIP signature data found
bap.Algorithm = string(chunks[pipeIdx+2].Data)
bap.SignerAddr = string(chunks[pipeIdx+3].Data)
if pipeIdx+4 < len(chunks) {
bap.Signature = string(chunks[pipeIdx+4].Data)
// Check if signer matches an ID pattern - would require additional context
bap.IsSignedByID = false // Default to false until we verify
}
}
}
case REVOKE:
// REVOKE structure: REVOKE <txid> <sequence number>
if len(chunks) >= 3 {
bap.IDKey = string(chunks[1].Data) // TXID being revoked
bap.Sequence, _ = strconv.ParseUint(string(chunks[2].Data), 10, 64)
// Look for AIP signature data
pipeIdx := -1
for i := 3; i < len(chunks); i++ {
if string(chunks[i].Data) == pipeSeparator {
pipeIdx = i
break
}
}
if pipeIdx >= 0 && pipeIdx+3 < len(chunks) {
// AIP signature data found
bap.Algorithm = string(chunks[pipeIdx+2].Data)
bap.SignerAddr = string(chunks[pipeIdx+3].Data)
if pipeIdx+4 < len(chunks) {
bap.Signature = string(chunks[pipeIdx+4].Data)
// Check if signer matches an ID pattern - would require additional context
bap.IsSignedByID = false // Default to false until we verify
}
}
}
case ALIAS:
// ALIAS structure: ALIAS <alias> <address>
if len(chunks) >= 3 {
bap.IDKey = string(chunks[1].Data) // Alias
bap.Profile = chunks[2].Data
// Look for AIP signature data
pipeIdx := -1
for i := 3; i < len(chunks); i++ {
if string(chunks[i].Data) == pipeSeparator {
pipeIdx = i
break
}
}
if pipeIdx >= 0 && pipeIdx+3 < len(chunks) {
// AIP signature data found
bap.Algorithm = string(chunks[pipeIdx+2].Data)
bap.SignerAddr = string(chunks[pipeIdx+3].Data)
if pipeIdx+4 < len(chunks) {
bap.Signature = string(chunks[pipeIdx+4].Data)
// Check if signer matches an ID pattern - would require additional context
bap.IsSignedByID = false // Default to false until we verify
}
}
}
}
return bap
}
}
return nil
}