@@ -16,6 +16,7 @@ package common
16
16
17
17
import (
18
18
"fmt"
19
+ "hash/crc32"
19
20
"strings"
20
21
21
22
"github.com/blinklabs-io/gouroboros/base58"
@@ -42,16 +43,22 @@ const (
42
43
AddressTypeByron = 0b1000
43
44
AddressTypeNoneKey = 0b1110
44
45
AddressTypeNoneScript = 0b1111
46
+
47
+ ByronAddressTypePubkey = 0
48
+ ByronAddressTypeScript = 1
49
+ ByronAddressTypeRedeem = 2
45
50
)
46
51
47
52
type AddrKeyHash Blake2b224
48
53
49
54
type Address struct {
50
- addressType uint8
51
- networkId uint8
52
- paymentAddress []byte
53
- stakingAddress []byte
54
- extraData []byte
55
+ addressType uint8
56
+ networkId uint8
57
+ paymentAddress []byte
58
+ stakingAddress []byte
59
+ extraData []byte
60
+ byronAddressType uint64
61
+ byronAddressAttr ByronAddressAttributes
55
62
}
56
63
57
64
// NewAddress returns an Address based on the provided bech32/base58 address string
@@ -109,15 +116,59 @@ func NewAddressFromParts(
109
116
}, nil
110
117
}
111
118
119
+ func NewByronAddressFromParts (
120
+ byronAddrType uint64 ,
121
+ paymentAddr []byte ,
122
+ attr ByronAddressAttributes ,
123
+ ) (Address , error ) {
124
+ if len (paymentAddr ) != AddressHashSize {
125
+ return Address {}, fmt .Errorf (
126
+ "invalid payment address hash length: %d" ,
127
+ len (paymentAddr ),
128
+ )
129
+ }
130
+ return Address {
131
+ addressType : AddressTypeByron ,
132
+ paymentAddress : paymentAddr ,
133
+ byronAddressType : byronAddrType ,
134
+ byronAddressAttr : attr ,
135
+ }, nil
136
+ }
137
+
112
138
func (a * Address ) populateFromBytes (data []byte ) error {
113
139
// Extract header info
114
140
header := data [0 ]
115
141
a .addressType = (header & AddressHeaderTypeMask ) >> 4
116
142
a .networkId = header & AddressHeaderNetworkMask
143
+ // Byron Addresses
144
+ if a .addressType == AddressTypeByron {
145
+ var rawAddr byronAddress
146
+ if _ , err := cbor .Decode (data , & rawAddr ); err != nil {
147
+ return err
148
+ }
149
+ payloadBytes , ok := rawAddr .Payload .Content .([]byte )
150
+ if ! ok || rawAddr .Payload .Number != 24 {
151
+ return fmt .Errorf ("invalid Byron address data: unexpected payload content" )
152
+ }
153
+ payloadChecksum := crc32 .ChecksumIEEE (payloadBytes )
154
+ if rawAddr .Checksum != payloadChecksum {
155
+ return fmt .Errorf ("invalid Byron address data: checksum does not match" )
156
+ }
157
+ var byronAddr byronAddressPayload
158
+ if _ , err := cbor .Decode (payloadBytes , & byronAddr ); err != nil {
159
+ return err
160
+ }
161
+ if len (byronAddr .Hash ) != AddressHashSize {
162
+ return fmt .Errorf ("invalid Byron address data: hash is not expected length" )
163
+ }
164
+ a .byronAddressType = byronAddr .AddrType
165
+ a .byronAddressAttr = byronAddr .Attr
166
+ a .paymentAddress = byronAddr .Hash
167
+ return nil
168
+ }
117
169
// Check length
118
170
// We exclude a few address types without fixed sizes that we don't properly support yet
119
- if a .addressType != AddressTypeByron &&
120
- a .addressType != AddressTypeKeyPointer &&
171
+ if a .addressType != AddressTypeKeyPointer &&
121
172
a .addressType != AddressTypeScriptPointer {
122
173
dataLen := len (data )
123
174
// Addresses must be at least the address hash size plus header byte
@@ -135,7 +186,6 @@ func (a *Address) populateFromBytes(data []byte) error {
135
186
}
136
187
}
137
188
// Extract payload
138
- // NOTE: this is definitely incorrect for Byron
139
189
payload := data [1 :]
140
190
a .paymentAddress = payload [:AddressHashSize ]
141
191
payload = payload [AddressHashSize :]
@@ -264,6 +314,31 @@ func (a Address) generateHRP() string {
264
314
265
315
// Bytes returns the underlying bytes for the address
266
316
func (a Address ) Bytes () []byte {
317
+ if a .addressType == AddressTypeByron {
318
+ tmpPayload := []any {
319
+ a .paymentAddress ,
320
+ a .byronAddressAttr ,
321
+ a .byronAddressType ,
322
+ }
323
+ rawPayload , err := cbor .Encode (tmpPayload )
324
+ if err != nil {
325
+ // TODO: handle error
326
+ return nil
327
+ }
328
+ tmpData := []any {
329
+ cbor.Tag {
330
+ Number : 24 ,
331
+ Content : rawPayload ,
332
+ },
333
+ crc32 .ChecksumIEEE (rawPayload ),
334
+ }
335
+ ret , err := cbor .Encode (tmpData )
336
+ if err != nil {
337
+ // TODO: handle error
338
+ return nil
339
+ }
340
+ return ret
341
+ }
267
342
ret := []byte {}
268
343
ret = append (
269
344
ret ,
@@ -301,3 +376,55 @@ func (a Address) String() string {
301
376
func (a Address ) MarshalJSON () ([]byte , error ) {
302
377
return []byte (`"` + a .String () + `"` ), nil
303
378
}
379
+
380
+ type byronAddress struct {
381
+ cbor.StructAsArray
382
+ Payload cbor.Tag
383
+ Checksum uint32
384
+ }
385
+
386
+ type byronAddressPayload struct {
387
+ cbor.StructAsArray
388
+ Hash []byte
389
+ Attr ByronAddressAttributes
390
+ AddrType uint64
391
+ }
392
+
393
+ type ByronAddressAttributes struct {
394
+ Payload []byte
395
+ Network * uint8
396
+ }
397
+
398
+ func (a * ByronAddressAttributes ) UnmarshalCBOR (data []byte ) error {
399
+ var tmpData struct {
400
+ Payload []byte `cbor:"1,keyasint,omitempty"`
401
+ NetworkRaw []byte `cbor:"2,keyasint,omitempty"`
402
+ }
403
+ if _ , err := cbor .Decode (data , & tmpData ); err != nil {
404
+ return err
405
+ }
406
+ a .Payload = tmpData .Payload
407
+ if len (tmpData .NetworkRaw ) > 0 {
408
+ var tmpNetwork uint8
409
+ if _ , err := cbor .Decode (tmpData .NetworkRaw , & tmpNetwork ); err != nil {
410
+ return err
411
+ }
412
+ a .Network = & tmpNetwork
413
+ }
414
+ return nil
415
+ }
416
+
417
+ func (a * ByronAddressAttributes ) MarshalCBOR () ([]byte , error ) {
418
+ tmpData := make (map [int ]any )
419
+ if len (a .Payload ) > 0 {
420
+ tmpData [1 ] = a .Payload
421
+ }
422
+ if a .Network != nil {
423
+ networkRaw , err := cbor .Encode (a .Network )
424
+ if err != nil {
425
+ return nil , err
426
+ }
427
+ tmpData [2 ] = networkRaw
428
+ }
429
+ return cbor .Encode (tmpData )
430
+ }
0 commit comments