Skip to content

Commit a1854ed

Browse files
authored
refactor: track def- vs. indef-length on decode of PlutusData (#121)
Fixes #120 Signed-off-by: Aurora Gaffney <[email protected]>
1 parent 883c2ee commit a1854ed

File tree

4 files changed

+99
-27
lines changed

4 files changed

+99
-27
lines changed

data/data.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ type PlutusData interface {
3535
// Constr
3636

3737
type Constr struct {
38-
Tag uint
39-
Fields []PlutusData
38+
Tag uint
39+
Fields []PlutusData
40+
useIndef *bool
4041
}
4142

4243
func (Constr) isPlutusData() {}
@@ -50,7 +51,15 @@ func NewConstr(tag uint, fields ...PlutusData) PlutusData {
5051
if fields == nil {
5152
fields = make([]PlutusData, 0)
5253
}
53-
return &Constr{tag, fields}
54+
return &Constr{Tag: tag, Fields: fields}
55+
}
56+
57+
// NewConstrDefIndef creates a Constr with the ability to specify whether it should use definite- or indefinite-length encoding
58+
func NewConstrDefIndef(useIndef bool, tag uint, fields ...PlutusData) PlutusData {
59+
if fields == nil {
60+
fields = make([]PlutusData, 0)
61+
}
62+
return &Constr{Tag: tag, Fields: fields, useIndef: &useIndef}
5463
}
5564

5665
// Map
@@ -67,7 +76,7 @@ func (m Map) String() string {
6776

6877
// NewMap creates a new Map variant.
6978
func NewMap(pairs [][2]PlutusData) PlutusData {
70-
return &Map{pairs}
79+
return &Map{Pairs: pairs}
7180
}
7281

7382
// Integer
@@ -110,7 +119,8 @@ func NewByteString(value []byte) PlutusData {
110119
// List
111120

112121
type List struct {
113-
Items []PlutusData
122+
Items []PlutusData
123+
useIndef *bool
114124
}
115125

116126
func (List) isPlutusData() {}
@@ -124,5 +134,13 @@ func NewList(items ...PlutusData) PlutusData {
124134
if items == nil {
125135
items = make([]PlutusData, 0)
126136
}
127-
return &List{items}
137+
return &List{Items: items}
138+
}
139+
140+
// NewListDefIndef creates a list with the ability to specify whether it should use definite- or indefinite-length encoding
141+
func NewListDefIndef(useIndef bool, items ...PlutusData) PlutusData {
142+
if items == nil {
143+
items = make([]PlutusData, 0)
144+
}
145+
return &List{Items: items, useIndef: &useIndef}
128146
}

data/data_test.go

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,29 +20,32 @@ var testDefs = []struct {
2020
CborHex: "1903e7",
2121
},
2222
{
23-
Data: NewList(
23+
Data: NewListDefIndef(
24+
true,
2425
NewInteger(big.NewInt(123)),
2526
NewInteger(big.NewInt(456)),
2627
),
2728
CborHex: "9f187b1901c8ff",
2829
},
2930
{
30-
Data: NewList(),
31+
Data: NewListDefIndef(false),
3132
CborHex: "80",
3233
},
3334
{
34-
Data: NewConstr(
35+
Data: NewConstrDefIndef(
36+
true,
3537
1,
3638
NewByteString([]byte{0xab, 0xcd}),
3739
),
3840
CborHex: "d87a9f42abcdff",
3941
},
4042
{
41-
Data: NewConstr(0),
43+
Data: NewConstrDefIndef(false, 0),
4244
CborHex: "d87980",
4345
},
4446
{
45-
Data: NewList(
47+
Data: NewListDefIndef(
48+
true,
4649
NewInteger(big.NewInt(1)),
4750
NewInteger(big.NewInt(2)),
4851
),
@@ -63,12 +66,14 @@ var testDefs = []struct {
6366
Data: NewMap(
6467
[][2]PlutusData{
6568
{
66-
NewConstr(
69+
NewConstrDefIndef(
70+
true,
6771
0,
6872
NewInteger(big.NewInt(0)),
6973
NewInteger(big.NewInt(406)),
7074
),
71-
NewConstr(
75+
NewConstrDefIndef(
76+
true,
7277
0,
7378
NewInteger(big.NewInt(1725522262478821201)),
7479
),
@@ -78,17 +83,20 @@ var testDefs = []struct {
7883
CborHex: "a1d8799f00190196ffd8799f1b17f2495b03141751ff",
7984
},
8085
{
81-
Data: NewConstr(
86+
Data: NewConstrDefIndef(
87+
true,
8288
0,
8389
NewMap(
8490
[][2]PlutusData{
8591
{
86-
NewConstr(
92+
NewConstrDefIndef(
93+
true,
8794
0,
8895
NewInteger(big.NewInt(0)),
8996
NewInteger(big.NewInt(406)),
9097
),
91-
NewConstr(
98+
NewConstrDefIndef(
99+
true,
92100
0,
93101
NewInteger(big.NewInt(1725522262478821201)),
94102
),
@@ -99,7 +107,8 @@ var testDefs = []struct {
99107
CborHex: "d8799fa1d8799f00190196ffd8799f1b17f2495b03141751ffff",
100108
},
101109
{
102-
Data: NewConstr(
110+
Data: NewConstrDefIndef(
111+
true,
103112
999,
104113
NewInteger(big.NewInt(6)),
105114
NewInteger(big.NewInt(7)),
@@ -111,17 +120,20 @@ var testDefs = []struct {
111120
Data: NewMap(
112121
[][2]PlutusData{
113122
{
114-
NewList(
123+
NewListDefIndef(
124+
true,
115125
NewInteger(big.NewInt(1)),
116126
NewInteger(big.NewInt(2)),
117127
),
118128
NewMap(
119129
[][2]PlutusData{
120130
{
121-
NewList(
131+
NewListDefIndef(
132+
true,
122133
NewByteString(nil),
123134
),
124-
NewConstr(
135+
NewConstrDefIndef(
136+
true,
125137
0,
126138
NewInteger(big.NewInt(2)),
127139
NewInteger(big.NewInt(1)),
@@ -154,6 +166,17 @@ var testDefs = []struct {
154166
// {{1: 2}: 3}
155167
CborHex: "a1a1010203",
156168
},
169+
{
170+
Data: NewConstrDefIndef(
171+
false,
172+
1,
173+
NewConstrDefIndef(
174+
false,
175+
0,
176+
),
177+
),
178+
CborHex: "d87a81d87980",
179+
},
157180
}
158181

159182
func TestPlutusDataEncode(t *testing.T) {

data/decode.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ const (
1616

1717
// Only the top 3 bytes are used to specify the type
1818
CborTypeMask uint8 = 0xe0
19+
20+
CborIndefFlag uint8 = 0x1f
1921
)
2022

2123
// Decode decodes a CBOR-encoded byte slice into a PlutusData value.
@@ -78,6 +80,7 @@ func decodeCborRaw(data []byte) (any, error) {
7880
}
7981

8082
func decodeCborRawList(data []byte) (any, error) {
83+
useIndef := (data[0] & CborIndefFlag) == CborIndefFlag
8184
var tmpData []cbor.RawMessage
8285
if err := cborUnmarshal(data, &tmpData); err != nil {
8386
return nil, err
@@ -94,7 +97,9 @@ func decodeCborRawList(data []byte) (any, error) {
9497
}
9598
tmpItems[i] = tmpPd
9699
}
97-
return NewList(tmpItems...), nil
100+
ret := NewList(tmpItems...)
101+
ret.(*List).useIndef = &useIndef
102+
return ret, nil
98103
}
99104

100105
func decodeCborRawMap(data []byte) (any, error) {
@@ -128,7 +133,8 @@ func decodeCborRawMap(data []byte) (any, error) {
128133
},
129134
)
130135
}
131-
return NewMap(pairs), nil
136+
ret := NewMap(pairs)
137+
return ret, nil
132138
}
133139

134140
// decodeRaw converts a raw CBOR-decoded value into PlutusData.
@@ -269,7 +275,9 @@ func decodeConstr(tag uint64, content cbor.RawMessage) (PlutusData, error) {
269275

270276
fields := tmpList.Items
271277

272-
return NewConstr(uint(tag), fields...), nil
278+
ret := NewConstr(uint(tag), fields...)
279+
ret.(*Constr).useIndef = tmpList.useIndef
280+
return ret, nil
273281
}
274282

275283
// decodeBignum decodes a big integer from CBOR tag content (expected to be bytes).

data/encode.go

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,27 @@ func encodeToRaw(pd PlutusData) (any, error) {
5555

5656
// encodeConstr encodes a Constr to CBOR tag format.
5757
func encodeConstr(c *Constr) (any, error) {
58+
useIndef := len(c.Fields) > 0
59+
if c.useIndef != nil {
60+
useIndef = *c.useIndef
61+
}
5862
// Encode fields first
5963
var fields any
60-
if len(c.Fields) == 0 {
64+
if !useIndef {
6165
// Encode empty fields as simple array
62-
fields = make([]any, 0)
66+
tmpFields := make([]any, len(c.Fields))
67+
for i, item := range c.Fields {
68+
encoded, err := encodeToRaw(item)
69+
if err != nil {
70+
return nil, fmt.Errorf("failed to encode Constr field %d: %w", i, err)
71+
}
72+
encodedCbor, err := cborMarshal(encoded)
73+
if err != nil {
74+
return nil, fmt.Errorf("failed to encode Constr field item %d: %w", i, err)
75+
}
76+
tmpFields[i] = cbor.RawMessage(encodedCbor)
77+
}
78+
fields = tmpFields
6379
} else {
6480
// Encode as indefinite-length array
6581
tmpData := []byte{
@@ -176,8 +192,15 @@ func encodeByteString(bs *ByteString) (any, error) {
176192

177193
// encodeList encodes a List to CBOR array format.
178194
func encodeList(l *List) (any, error) {
179-
if len(l.Items) == 0 {
180-
ret := make([]any, 0)
195+
useIndef := len(l.Items) > 0
196+
if l.useIndef != nil {
197+
useIndef = *l.useIndef
198+
}
199+
if !useIndef {
200+
ret := make([]any, len(l.Items))
201+
for i, item := range l.Items {
202+
ret[i] = item
203+
}
181204
return ret, nil
182205
}
183206
tmpData := []byte{

0 commit comments

Comments
 (0)