|
15 | 15 | package ledger |
16 | 16 |
|
17 | 17 | import ( |
| 18 | + "encoding/hex" |
18 | 19 | "fmt" |
19 | 20 |
|
20 | 21 | "github.com/blinklabs-io/gouroboros/cbor" |
21 | 22 | "github.com/blinklabs-io/gouroboros/internal/base58" |
22 | 23 | "github.com/blinklabs-io/gouroboros/internal/bech32" |
| 24 | + |
| 25 | + "golang.org/x/crypto/blake2b" |
23 | 26 | ) |
24 | 27 |
|
| 28 | +type Blake2b256 [32]byte |
| 29 | + |
| 30 | +func NewBlake2b256(data []byte) Blake2b256 { |
| 31 | + b := Blake2b256{} |
| 32 | + copy(b[:], data) |
| 33 | + return b |
| 34 | +} |
| 35 | + |
| 36 | +func (b Blake2b256) String() string { |
| 37 | + return hex.EncodeToString([]byte(b[:])) |
| 38 | +} |
| 39 | + |
| 40 | +func (b Blake2b256) Bytes() []byte { |
| 41 | + return b[:] |
| 42 | +} |
| 43 | + |
| 44 | +type Blake2b224 [28]byte |
| 45 | + |
| 46 | +func NewBlake2b224(data []byte) Blake2b224 { |
| 47 | + b := Blake2b224{} |
| 48 | + copy(b[:], data) |
| 49 | + return b |
| 50 | +} |
| 51 | + |
| 52 | +func (b Blake2b224) String() string { |
| 53 | + return hex.EncodeToString([]byte(b[:])) |
| 54 | +} |
| 55 | + |
| 56 | +func (b Blake2b224) Bytes() []byte { |
| 57 | + return b[:] |
| 58 | +} |
| 59 | + |
| 60 | +type Blake2b160 [20]byte |
| 61 | + |
| 62 | +func NewBlake2b160(data []byte) Blake2b160 { |
| 63 | + b := Blake2b160{} |
| 64 | + copy(b[:], data) |
| 65 | + return b |
| 66 | +} |
| 67 | + |
| 68 | +func (b Blake2b160) String() string { |
| 69 | + return hex.EncodeToString([]byte(b[:])) |
| 70 | +} |
| 71 | + |
| 72 | +func (b Blake2b160) Bytes() []byte { |
| 73 | + return b[:] |
| 74 | +} |
| 75 | + |
25 | 76 | // MultiAsset represents a collection of policies, assets, and quantities. It's used for |
26 | 77 | // TX outputs (uint64) and TX asset minting (int64 to allow for negative values for burning) |
27 | 78 | type MultiAsset[T int64 | uint64] struct { |
@@ -65,6 +116,40 @@ func (m *MultiAsset[T]) Asset(policyId Blake2b224, assetName []byte) T { |
65 | 116 | return policy[cbor.ByteString(assetName)] |
66 | 117 | } |
67 | 118 |
|
| 119 | +type AssetFingerprint struct { |
| 120 | + policyId []byte |
| 121 | + assetName []byte |
| 122 | +} |
| 123 | + |
| 124 | +func NewAssetFingerprint(policyId []byte, assetName []byte) AssetFingerprint { |
| 125 | + return AssetFingerprint{ |
| 126 | + policyId: policyId, |
| 127 | + assetName: assetName, |
| 128 | + } |
| 129 | +} |
| 130 | + |
| 131 | +func (a AssetFingerprint) Hash() Blake2b160 { |
| 132 | + // We can ignore the error return here because our fixed size/key arguments will |
| 133 | + // never trigger an error |
| 134 | + tmpHash, _ := blake2b.New(20, nil) |
| 135 | + tmpHash.Write(a.policyId) |
| 136 | + tmpHash.Write(a.assetName) |
| 137 | + return NewBlake2b160(tmpHash.Sum(nil)) |
| 138 | +} |
| 139 | + |
| 140 | +func (a AssetFingerprint) String() string { |
| 141 | + // Convert data to base32 and encode as bech32 |
| 142 | + convData, err := bech32.ConvertBits(a.Hash().Bytes(), 8, 5, true) |
| 143 | + if err != nil { |
| 144 | + panic(fmt.Sprintf("unexpected error converting data to base32: %s", err)) |
| 145 | + } |
| 146 | + encoded, err := bech32.Encode("asset", convData) |
| 147 | + if err != nil { |
| 148 | + panic(fmt.Sprintf("unexpected error encoding data as bech32: %s", err)) |
| 149 | + } |
| 150 | + return encoded |
| 151 | +} |
| 152 | + |
68 | 153 | const ( |
69 | 154 | addressHeaderTypeMask = 0xF0 |
70 | 155 | addressHeaderNetworkMask = 0x0F |
|
0 commit comments