Skip to content

Commit ae13a86

Browse files
authored
Merge pull request #300 from blinklabs-io/feat/tx-input-output-json
feat: JSON marshaling for TX inputs/outputs
2 parents e6dc59e + 2d3bdd4 commit ae13a86

File tree

8 files changed

+156
-13
lines changed

8 files changed

+156
-13
lines changed

ledger/alonzo.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package ledger
1616

1717
import (
18+
"encoding/json"
1819
"fmt"
1920

2021
"github.com/blinklabs-io/gouroboros/cbor"
@@ -120,6 +121,19 @@ func (o *AlonzoTransactionOutput) UnmarshalCBOR(cborData []byte) error {
120121
return nil
121122
}
122123

124+
func (o AlonzoTransactionOutput) MarshalJSON() ([]byte, error) {
125+
tmpObj := struct {
126+
Address Address `json:"address"`
127+
Amount uint64 `json:"amount"`
128+
Assets *MultiAsset[MultiAssetTypeOutput] `json:"assets,omitempty"`
129+
}{
130+
Address: o.OutputAddress,
131+
Amount: o.OutputAmount.Amount,
132+
Assets: o.OutputAmount.Assets,
133+
}
134+
return json.Marshal(&tmpObj)
135+
}
136+
123137
func (o AlonzoTransactionOutput) Address() Address {
124138
return o.OutputAddress
125139
}
@@ -128,7 +142,7 @@ func (o AlonzoTransactionOutput) Amount() uint64 {
128142
return o.OutputAmount.Amount
129143
}
130144

131-
func (o AlonzoTransactionOutput) Assets() *MultiAsset[uint64] {
145+
func (o AlonzoTransactionOutput) Assets() *MultiAsset[MultiAssetTypeOutput] {
132146
return o.OutputAmount.Assets
133147
}
134148

ledger/babbage.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package ledger
1616

1717
import (
18+
"encoding/json"
1819
"fmt"
1920

2021
"github.com/blinklabs-io/gouroboros/cbor"
@@ -165,6 +166,19 @@ func (o *BabbageTransactionOutput) UnmarshalCBOR(cborData []byte) error {
165166
return nil
166167
}
167168

169+
func (o BabbageTransactionOutput) MarshalJSON() ([]byte, error) {
170+
tmpObj := struct {
171+
Address Address `json:"address"`
172+
Amount uint64 `json:"amount"`
173+
Assets *MultiAsset[MultiAssetTypeOutput] `json:"assets,omitempty"`
174+
}{
175+
Address: o.OutputAddress,
176+
Amount: o.OutputAmount.Amount,
177+
Assets: o.OutputAmount.Assets,
178+
}
179+
return json.Marshal(&tmpObj)
180+
}
181+
168182
func (o BabbageTransactionOutput) Address() Address {
169183
return o.OutputAddress
170184
}
@@ -173,7 +187,7 @@ func (o BabbageTransactionOutput) Amount() uint64 {
173187
return o.OutputAmount.Amount
174188
}
175189

176-
func (o BabbageTransactionOutput) Assets() *MultiAsset[uint64] {
190+
func (o BabbageTransactionOutput) Assets() *MultiAsset[MultiAssetTypeOutput] {
177191
return o.OutputAmount.Assets
178192
}
179193

ledger/common.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package ledger
1616

1717
import (
1818
"encoding/hex"
19+
"encoding/json"
1920
"fmt"
2021

2122
"github.com/blinklabs-io/gouroboros/cbor"
@@ -73,12 +74,23 @@ func (b Blake2b160) Bytes() []byte {
7374
return b[:]
7475
}
7576

77+
type MultiAssetTypeOutput = uint64
78+
type MultiAssetTypeMint = int64
79+
7680
// MultiAsset represents a collection of policies, assets, and quantities. It's used for
7781
// TX outputs (uint64) and TX asset minting (int64 to allow for negative values for burning)
78-
type MultiAsset[T int64 | uint64] struct {
82+
type MultiAsset[T MultiAssetTypeOutput | MultiAssetTypeMint] struct {
7983
data map[Blake2b224]map[cbor.ByteString]T
8084
}
8185

86+
type multiAssetJson[T MultiAssetTypeOutput | MultiAssetTypeMint] struct {
87+
Name string `json:"name"`
88+
NameHex string `json:"nameHex"`
89+
PolicyId string `json:"policyId"`
90+
Fingerprint string `json:"fingerprint"`
91+
Amount T `json:"amount"`
92+
}
93+
8294
func (m *MultiAsset[T]) UnmarshalCBOR(data []byte) error {
8395
_, err := cbor.Decode(data, &(m.data))
8496
return err
@@ -88,6 +100,23 @@ func (m *MultiAsset[T]) MarshalCBOR() ([]byte, error) {
88100
return cbor.Encode(&(m.data))
89101
}
90102

103+
func (m MultiAsset[T]) MarshalJSON() ([]byte, error) {
104+
tmpAssets := []multiAssetJson[T]{}
105+
for policyId, policyData := range m.data {
106+
for assetName, amount := range policyData {
107+
tmpObj := multiAssetJson[T]{
108+
Name: string(assetName),
109+
NameHex: hex.EncodeToString(assetName.Bytes()),
110+
Amount: amount,
111+
PolicyId: policyId.String(),
112+
Fingerprint: NewAssetFingerprint(policyId.Bytes(), assetName.Bytes()).String(),
113+
}
114+
tmpAssets = append(tmpAssets, tmpObj)
115+
}
116+
}
117+
return json.Marshal(&tmpAssets)
118+
}
119+
91120
func (m *MultiAsset[T]) Policies() []Blake2b224 {
92121
var ret []Blake2b224
93122
for policyId := range m.data {
@@ -241,3 +270,7 @@ func (a Address) String() string {
241270
return encoded
242271
}
243272
}
273+
274+
func (a Address) MarshalJSON() ([]byte, error) {
275+
return []byte(`"` + a.String() + `"`), nil
276+
}

ledger/common_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ package ledger
22

33
import (
44
"encoding/hex"
5+
"encoding/json"
56
"testing"
7+
8+
"github.com/blinklabs-io/gouroboros/cbor"
9+
"github.com/blinklabs-io/gouroboros/internal/test"
610
)
711

812
func TestAssetFingerprint(t *testing.T) {
@@ -43,3 +47,59 @@ func TestAssetFingerprint(t *testing.T) {
4347
}
4448
}
4549
}
50+
51+
func TestMultiAssetJson(t *testing.T) {
52+
testDefs := []struct {
53+
multiAssetObj interface{}
54+
expectedJson string
55+
}{
56+
{
57+
multiAssetObj: MultiAsset[MultiAssetTypeOutput]{
58+
data: map[Blake2b224]map[cbor.ByteString]MultiAssetTypeOutput{
59+
NewBlake2b224(test.DecodeHexString("29a8fb8318718bd756124f0c144f56d4b4579dc5edf2dd42d669ac61")): {
60+
cbor.ByteString(test.DecodeHexString("6675726e697368613239686e")): 123456,
61+
},
62+
},
63+
},
64+
expectedJson: `[{"name":"furnisha29hn","nameHex":"6675726e697368613239686e","policyId":"29a8fb8318718bd756124f0c144f56d4b4579dc5edf2dd42d669ac61","fingerprint":"asset1jdu2xcrwlqsjqqjger6kj2szddz8dcpvcg4ksz","amount":123456}]`,
65+
},
66+
{
67+
multiAssetObj: MultiAsset[MultiAssetTypeOutput]{
68+
data: map[Blake2b224]map[cbor.ByteString]MultiAssetTypeOutput{
69+
NewBlake2b224(test.DecodeHexString("eaf8042c1d8203b1c585822f54ec32c4c1bb4d3914603e2cca20bbd5")): {
70+
cbor.ByteString(test.DecodeHexString("426f7764757261436f6e63657074733638")): 234567,
71+
},
72+
},
73+
},
74+
expectedJson: `[{"name":"BowduraConcepts68","nameHex":"426f7764757261436f6e63657074733638","policyId":"eaf8042c1d8203b1c585822f54ec32c4c1bb4d3914603e2cca20bbd5","fingerprint":"asset1kp7hdhqc7chmyqvtqrsljfdrdt6jz8mg5culpe","amount":234567}]`,
75+
},
76+
{
77+
multiAssetObj: MultiAsset[MultiAssetTypeOutput]{
78+
data: map[Blake2b224]map[cbor.ByteString]MultiAssetTypeOutput{
79+
NewBlake2b224(test.DecodeHexString("cf78aeb9736e8aa94ce8fab44da86b522fa9b1c56336b92a28420525")): {
80+
cbor.ByteString(test.DecodeHexString("363438346330393264363164373033656236333233346461")): 12345678,
81+
},
82+
},
83+
},
84+
expectedJson: `[{"name":"6484c092d61d703eb63234da","nameHex":"363438346330393264363164373033656236333233346461","policyId":"cf78aeb9736e8aa94ce8fab44da86b522fa9b1c56336b92a28420525","fingerprint":"asset1rx3cnlsvh3udka56wyqyed3u695zd5q2jck2yd","amount":12345678}]`,
85+
},
86+
}
87+
for _, test := range testDefs {
88+
var err error
89+
var jsonData []byte
90+
switch v := test.multiAssetObj.(type) {
91+
case MultiAsset[MultiAssetTypeOutput]:
92+
jsonData, err = json.Marshal(&v)
93+
case MultiAsset[MultiAssetTypeMint]:
94+
jsonData, err = json.Marshal(&v)
95+
default:
96+
t.Fatalf("unexpected test object type: %T", test.multiAssetObj)
97+
}
98+
if err != nil {
99+
t.Fatalf("failed to marshal MultiAsset object into JSON: %s", err)
100+
}
101+
if string(jsonData) != test.expectedJson {
102+
t.Fatalf("MultiAsset object did not marshal into expected JSON\n got: %s\n wanted: %s", jsonData, test.expectedJson)
103+
}
104+
}
105+
}

ledger/mary.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package ledger
1616

1717
import (
18+
"encoding/json"
1819
"fmt"
1920

2021
"github.com/blinklabs-io/gouroboros/cbor"
@@ -79,8 +80,8 @@ func (h *MaryBlockHeader) Era() Era {
7980

8081
type MaryTransactionBody struct {
8182
AllegraTransactionBody
82-
TxOutputs []MaryTransactionOutput `cbor:"1,keyasint,omitempty"`
83-
Mint MultiAsset[int64] `cbor:"9,keyasint,omitempty"`
83+
TxOutputs []MaryTransactionOutput `cbor:"1,keyasint,omitempty"`
84+
Mint MultiAsset[MultiAssetTypeMint] `cbor:"9,keyasint,omitempty"`
8485
}
8586

8687
func (b *MaryTransactionBody) UnmarshalCBOR(cborData []byte) error {
@@ -108,6 +109,19 @@ type MaryTransactionOutput struct {
108109
OutputAmount MaryTransactionOutputValue
109110
}
110111

112+
func (o MaryTransactionOutput) MarshalJSON() ([]byte, error) {
113+
tmpObj := struct {
114+
Address Address `json:"address"`
115+
Amount uint64 `json:"amount"`
116+
Assets *MultiAsset[MultiAssetTypeOutput] `json:"assets,omitempty"`
117+
}{
118+
Address: o.OutputAddress,
119+
Amount: o.OutputAmount.Amount,
120+
Assets: o.OutputAmount.Assets,
121+
}
122+
return json.Marshal(&tmpObj)
123+
}
124+
111125
func (o MaryTransactionOutput) Address() Address {
112126
return o.OutputAddress
113127
}
@@ -116,15 +130,15 @@ func (o MaryTransactionOutput) Amount() uint64 {
116130
return o.OutputAmount.Amount
117131
}
118132

119-
func (o MaryTransactionOutput) Assets() *MultiAsset[uint64] {
133+
func (o MaryTransactionOutput) Assets() *MultiAsset[MultiAssetTypeOutput] {
120134
return o.OutputAmount.Assets
121135
}
122136

123137
type MaryTransactionOutputValue struct {
124138
cbor.StructAsArray
125139
Amount uint64
126140
// We use a pointer here to allow it to be nil
127-
Assets *MultiAsset[uint64]
141+
Assets *MultiAsset[MultiAssetTypeOutput]
128142
}
129143

130144
func (v *MaryTransactionOutputValue) UnmarshalCBOR(data []byte) error {

ledger/mary_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ import (
2323
"github.com/blinklabs-io/gouroboros/internal/test"
2424
)
2525

26-
func createMaryTransactionOutputValueAssets(policyId []byte, assetName []byte, amount uint64) *MultiAsset[uint64] {
26+
func createMaryTransactionOutputValueAssets(policyId []byte, assetName []byte, amount uint64) *MultiAsset[MultiAssetTypeOutput] {
2727
data := map[Blake2b224]map[cbor.ByteString]uint64{}
2828
policyIdKey := Blake2b224{}
2929
copy(policyIdKey[:], policyId)
3030
assetKey := cbor.ByteString(assetName)
3131
data[policyIdKey] = map[cbor.ByteString]uint64{
3232
assetKey: amount,
3333
}
34-
return &MultiAsset[uint64]{data: data}
34+
return &MultiAsset[MultiAssetTypeOutput]{data: data}
3535
}
3636

3737
func TestMaryTransactionOutputValueEncodeDecode(t *testing.T) {

ledger/shelley.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,10 +179,18 @@ func (i ShelleyTransactionInput) Index() uint32 {
179179
return i.OutputIndex
180180
}
181181

182+
func (i ShelleyTransactionInput) String() string {
183+
return fmt.Sprintf("%s#%d", i.TxId, i.OutputIndex)
184+
}
185+
186+
func (i ShelleyTransactionInput) MarshalJSON() ([]byte, error) {
187+
return []byte("\"" + i.String() + "\""), nil
188+
}
189+
182190
type ShelleyTransactionOutput struct {
183191
cbor.StructAsArray
184-
OutputAddress Address
185-
OutputAmount uint64
192+
OutputAddress Address `json:"address"`
193+
OutputAmount uint64 `json:"amount"`
186194
}
187195

188196
func (o ShelleyTransactionOutput) Address() Address {
@@ -193,7 +201,7 @@ func (o ShelleyTransactionOutput) Amount() uint64 {
193201
return o.OutputAmount
194202
}
195203

196-
func (o ShelleyTransactionOutput) Assets() *MultiAsset[uint64] {
204+
func (o ShelleyTransactionOutput) Assets() *MultiAsset[MultiAssetTypeOutput] {
197205
return nil
198206
}
199207

ledger/tx.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ type TransactionInput interface {
3636
type TransactionOutput interface {
3737
Address() Address
3838
Amount() uint64
39-
Assets() *MultiAsset[uint64]
39+
Assets() *MultiAsset[MultiAssetTypeOutput]
4040
}
4141

4242
func NewTransactionFromCbor(txType uint, data []byte) (interface{}, error) {

0 commit comments

Comments
 (0)