Skip to content

Commit 9dc6c5e

Browse files
committed
refactor: use CBOR set tags when encoding TX body fields
This creates a generic CBOR set type wrapper that supports the optional encoding format for various transaction body attributes Fixes #1063 Fixes #1064 Fixes #1065 Signed-off-by: Aurora Gaffney <[email protected]>
1 parent 9ba5f28 commit 9dc6c5e

File tree

8 files changed

+263
-113
lines changed

8 files changed

+263
-113
lines changed

cbor/tags.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,3 +159,54 @@ type Set []any
159159

160160
// Map corresponds to CBOR tag 259 and is used to represent a map with key/value operations
161161
type Map map[any]any
162+
163+
// SetType is a generic type for wrapping other types in an optional CBOR set tag
164+
type SetType[T any] struct {
165+
useTag bool
166+
items []T
167+
}
168+
169+
func NewSetType[T any](items []T, useTag bool) SetType[T] {
170+
return SetType[T]{
171+
items: items,
172+
useTag: useTag,
173+
}
174+
}
175+
176+
func (t *SetType[T]) UnmarshalCBOR(data []byte) error {
177+
// Check if the set is wrapped in a CBOR tag
178+
// This is mostly needed so we can remember whether it was Set-wrapped for CBOR encoding
179+
var tmpTag RawTag
180+
t.useTag = false
181+
if _, err := Decode(data, &tmpTag); err == nil {
182+
if tmpTag.Number != CborTagSet {
183+
return errors.New("unexpected tag type")
184+
}
185+
data = []byte(tmpTag.Content)
186+
t.useTag = true
187+
}
188+
var tmpData []T
189+
if _, err := Decode(data, &tmpData); err != nil {
190+
return err
191+
}
192+
t.items = tmpData
193+
return nil
194+
}
195+
196+
func (t *SetType[T]) MarshalCBOR() ([]byte, error) {
197+
tmpItems := make([]any, len(t.items))
198+
for i, item := range t.items {
199+
tmpItems[i] = item
200+
}
201+
var tmpData any = tmpItems
202+
if t.useTag {
203+
tmpData = Set(tmpItems)
204+
}
205+
return Encode(tmpData)
206+
}
207+
208+
func (t *SetType[T]) Items() []T {
209+
ret := make([]T, len(t.items))
210+
copy(ret, t.items)
211+
return ret
212+
}

ledger/alonzo/alonzo.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,8 @@ type AlonzoTransactionBody struct {
172172
TxValidityIntervalStart uint64 `cbor:"8,keyasint,omitempty"`
173173
TxMint *common.MultiAsset[common.MultiAssetTypeMint] `cbor:"9,keyasint,omitempty"`
174174
TxScriptDataHash *common.Blake2b256 `cbor:"11,keyasint,omitempty"`
175-
TxCollateral []shelley.ShelleyTransactionInput `cbor:"13,keyasint,omitempty"`
176-
TxRequiredSigners []common.Blake2b224 `cbor:"14,keyasint,omitempty"`
175+
TxCollateral cbor.SetType[shelley.ShelleyTransactionInput] `cbor:"13,keyasint,omitempty,omitzero"`
176+
TxRequiredSigners cbor.SetType[common.Blake2b224] `cbor:"14,keyasint,omitempty,omitzero"`
177177
NetworkId uint8 `cbor:"15,keyasint,omitempty"`
178178
}
179179

@@ -246,14 +246,14 @@ func (b *AlonzoTransactionBody) AssetMint() *common.MultiAsset[common.MultiAsset
246246

247247
func (b *AlonzoTransactionBody) Collateral() []common.TransactionInput {
248248
ret := []common.TransactionInput{}
249-
for _, collateral := range b.TxCollateral {
249+
for _, collateral := range b.TxCollateral.Items() {
250250
ret = append(ret, collateral)
251251
}
252252
return ret
253253
}
254254

255255
func (b *AlonzoTransactionBody) RequiredSigners() []common.Blake2b224 {
256-
return b.TxRequiredSigners[:]
256+
return b.TxRequiredSigners.Items()
257257
}
258258

259259
func (b *AlonzoTransactionBody) ScriptDataHash() *common.Blake2b256 {

ledger/alonzo/rules_test.go

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -997,9 +997,12 @@ func TestUtxoValidateInsufficientCollateral(t *testing.T) {
997997
t.Run(
998998
"insufficient collateral",
999999
func(t *testing.T) {
1000-
testTx.Body.TxCollateral = []shelley.ShelleyTransactionInput{
1001-
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1002-
}
1000+
testTx.Body.TxCollateral = cbor.NewSetType[shelley.ShelleyTransactionInput](
1001+
[]shelley.ShelleyTransactionInput{
1002+
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1003+
},
1004+
false,
1005+
)
10031006
err := alonzo.UtxoValidateInsufficientCollateral(
10041007
testTx,
10051008
testSlot,
@@ -1027,10 +1030,13 @@ func TestUtxoValidateInsufficientCollateral(t *testing.T) {
10271030
t.Run(
10281031
"sufficient collateral",
10291032
func(t *testing.T) {
1030-
testTx.Body.TxCollateral = []shelley.ShelleyTransactionInput{
1031-
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1032-
shelley.NewShelleyTransactionInput(testInputTxId, 1),
1033-
}
1033+
testTx.Body.TxCollateral = cbor.NewSetType[shelley.ShelleyTransactionInput](
1034+
[]shelley.ShelleyTransactionInput{
1035+
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1036+
shelley.NewShelleyTransactionInput(testInputTxId, 1),
1037+
},
1038+
false,
1039+
)
10341040
err := alonzo.UtxoValidateInsufficientCollateral(
10351041
testTx,
10361042
testSlot,
@@ -1087,10 +1093,13 @@ func TestUtxoValidateCollateralContainsNonAda(t *testing.T) {
10871093
t.Run(
10881094
"coin and assets",
10891095
func(t *testing.T) {
1090-
testTx.Body.TxCollateral = []shelley.ShelleyTransactionInput{
1091-
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1092-
shelley.NewShelleyTransactionInput(testInputTxId, 1),
1093-
}
1096+
testTx.Body.TxCollateral = cbor.NewSetType[shelley.ShelleyTransactionInput](
1097+
[]shelley.ShelleyTransactionInput{
1098+
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1099+
shelley.NewShelleyTransactionInput(testInputTxId, 1),
1100+
},
1101+
false,
1102+
)
10941103
err := alonzo.UtxoValidateCollateralContainsNonAda(
10951104
testTx,
10961105
testSlot,
@@ -1118,9 +1127,12 @@ func TestUtxoValidateCollateralContainsNonAda(t *testing.T) {
11181127
t.Run(
11191128
"coin only",
11201129
func(t *testing.T) {
1121-
testTx.Body.TxCollateral = []shelley.ShelleyTransactionInput{
1122-
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1123-
}
1130+
testTx.Body.TxCollateral = cbor.NewSetType[shelley.ShelleyTransactionInput](
1131+
[]shelley.ShelleyTransactionInput{
1132+
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1133+
},
1134+
false,
1135+
)
11241136
err := alonzo.UtxoValidateCollateralContainsNonAda(
11251137
testTx,
11261138
testSlot,
@@ -1192,9 +1204,12 @@ func TestUtxoValidateNoCollateralInputs(t *testing.T) {
11921204
t.Run(
11931205
"collateral",
11941206
func(t *testing.T) {
1195-
testTx.Body.TxCollateral = []shelley.ShelleyTransactionInput{
1196-
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1197-
}
1207+
testTx.Body.TxCollateral = cbor.NewSetType[shelley.ShelleyTransactionInput](
1208+
[]shelley.ShelleyTransactionInput{
1209+
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1210+
},
1211+
false,
1212+
)
11981213
err := alonzo.UtxoValidateNoCollateralInputs(
11991214
testTx,
12001215
testSlot,

ledger/babbage/babbage.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,12 @@ type BabbageTransactionBody struct {
245245
TxValidityIntervalStart uint64 `cbor:"8,keyasint,omitempty"`
246246
TxMint *common.MultiAsset[common.MultiAssetTypeMint] `cbor:"9,keyasint,omitempty"`
247247
TxScriptDataHash *common.Blake2b256 `cbor:"11,keyasint,omitempty"`
248-
TxCollateral []shelley.ShelleyTransactionInput `cbor:"13,keyasint,omitempty"`
249-
TxRequiredSigners []common.Blake2b224 `cbor:"14,keyasint,omitempty"`
248+
TxCollateral cbor.SetType[shelley.ShelleyTransactionInput] `cbor:"13,keyasint,omitempty,omitzero"`
249+
TxRequiredSigners cbor.SetType[common.Blake2b224] `cbor:"14,keyasint,omitempty,omitzero"`
250250
NetworkId uint8 `cbor:"15,keyasint,omitempty"`
251251
TxCollateralReturn *BabbageTransactionOutput `cbor:"16,keyasint,omitempty"`
252252
TxTotalCollateral uint64 `cbor:"17,keyasint,omitempty"`
253-
TxReferenceInputs []shelley.ShelleyTransactionInput `cbor:"18,keyasint,omitempty"`
253+
TxReferenceInputs cbor.SetType[shelley.ShelleyTransactionInput] `cbor:"18,keyasint,omitempty,omitzero"`
254254
}
255255

256256
func (b *BabbageTransactionBody) UnmarshalCBOR(cborData []byte) error {
@@ -322,14 +322,14 @@ func (b *BabbageTransactionBody) AssetMint() *common.MultiAsset[common.MultiAsse
322322

323323
func (b *BabbageTransactionBody) Collateral() []common.TransactionInput {
324324
ret := []common.TransactionInput{}
325-
for _, collateral := range b.TxCollateral {
325+
for _, collateral := range b.TxCollateral.Items() {
326326
ret = append(ret, collateral)
327327
}
328328
return ret
329329
}
330330

331331
func (b *BabbageTransactionBody) RequiredSigners() []common.Blake2b224 {
332-
return b.TxRequiredSigners[:]
332+
return b.TxRequiredSigners.Items()
333333
}
334334

335335
func (b *BabbageTransactionBody) ScriptDataHash() *common.Blake2b256 {
@@ -338,7 +338,7 @@ func (b *BabbageTransactionBody) ScriptDataHash() *common.Blake2b256 {
338338

339339
func (b *BabbageTransactionBody) ReferenceInputs() []common.TransactionInput {
340340
ret := []common.TransactionInput{}
341-
for _, input := range b.TxReferenceInputs {
341+
for _, input := range b.TxReferenceInputs.Items() {
342342
ret = append(ret, &input)
343343
}
344344
return ret

ledger/babbage/rules_test.go

Lines changed: 64 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -998,9 +998,12 @@ func TestUtxoValidateInsufficientCollateral(t *testing.T) {
998998
t.Run(
999999
"insufficient collateral",
10001000
func(t *testing.T) {
1001-
testTx.Body.TxCollateral = []shelley.ShelleyTransactionInput{
1002-
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1003-
}
1001+
testTx.Body.TxCollateral = cbor.NewSetType[shelley.ShelleyTransactionInput](
1002+
[]shelley.ShelleyTransactionInput{
1003+
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1004+
},
1005+
false,
1006+
)
10041007
err := babbage.UtxoValidateInsufficientCollateral(
10051008
testTx,
10061009
testSlot,
@@ -1028,10 +1031,13 @@ func TestUtxoValidateInsufficientCollateral(t *testing.T) {
10281031
t.Run(
10291032
"sufficient collateral",
10301033
func(t *testing.T) {
1031-
testTx.Body.TxCollateral = []shelley.ShelleyTransactionInput{
1032-
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1033-
shelley.NewShelleyTransactionInput(testInputTxId, 1),
1034-
}
1034+
testTx.Body.TxCollateral = cbor.NewSetType[shelley.ShelleyTransactionInput](
1035+
[]shelley.ShelleyTransactionInput{
1036+
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1037+
shelley.NewShelleyTransactionInput(testInputTxId, 1),
1038+
},
1039+
false,
1040+
)
10351041
err := babbage.UtxoValidateInsufficientCollateral(
10361042
testTx,
10371043
testSlot,
@@ -1110,10 +1116,13 @@ func TestUtxoValidateCollateralContainsNonAda(t *testing.T) {
11101116
t.Run(
11111117
"coin and assets",
11121118
func(t *testing.T) {
1113-
testTx.Body.TxCollateral = []shelley.ShelleyTransactionInput{
1114-
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1115-
shelley.NewShelleyTransactionInput(testInputTxId, 1),
1116-
}
1119+
testTx.Body.TxCollateral = cbor.NewSetType[shelley.ShelleyTransactionInput](
1120+
[]shelley.ShelleyTransactionInput{
1121+
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1122+
shelley.NewShelleyTransactionInput(testInputTxId, 1),
1123+
},
1124+
false,
1125+
)
11171126
err := babbage.UtxoValidateCollateralContainsNonAda(
11181127
testTx,
11191128
testSlot,
@@ -1141,9 +1150,12 @@ func TestUtxoValidateCollateralContainsNonAda(t *testing.T) {
11411150
t.Run(
11421151
"coin only",
11431152
func(t *testing.T) {
1144-
testTx.Body.TxCollateral = []shelley.ShelleyTransactionInput{
1145-
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1146-
}
1153+
testTx.Body.TxCollateral = cbor.NewSetType[shelley.ShelleyTransactionInput](
1154+
[]shelley.ShelleyTransactionInput{
1155+
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1156+
},
1157+
false,
1158+
)
11471159
err := babbage.UtxoValidateCollateralContainsNonAda(
11481160
testTx,
11491161
testSlot,
@@ -1162,10 +1174,13 @@ func TestUtxoValidateCollateralContainsNonAda(t *testing.T) {
11621174
t.Run(
11631175
"coin and assets with return",
11641176
func(t *testing.T) {
1165-
testTx.Body.TxCollateral = []shelley.ShelleyTransactionInput{
1166-
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1167-
shelley.NewShelleyTransactionInput(testInputTxId, 1),
1168-
}
1177+
testTx.Body.TxCollateral = cbor.NewSetType[shelley.ShelleyTransactionInput](
1178+
[]shelley.ShelleyTransactionInput{
1179+
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1180+
shelley.NewShelleyTransactionInput(testInputTxId, 1),
1181+
},
1182+
false,
1183+
)
11691184
testTx.Body.TxCollateralReturn = &babbage.BabbageTransactionOutput{
11701185
OutputAmount: mary.MaryTransactionOutputValue{
11711186
Amount: testCollateralAmount,
@@ -1190,9 +1205,12 @@ func TestUtxoValidateCollateralContainsNonAda(t *testing.T) {
11901205
t.Run(
11911206
"coin and zero assets with return",
11921207
func(t *testing.T) {
1193-
testTx.Body.TxCollateral = []shelley.ShelleyTransactionInput{
1194-
shelley.NewShelleyTransactionInput(testInputTxId, 2),
1195-
}
1208+
testTx.Body.TxCollateral = cbor.NewSetType[shelley.ShelleyTransactionInput](
1209+
[]shelley.ShelleyTransactionInput{
1210+
shelley.NewShelleyTransactionInput(testInputTxId, 2),
1211+
},
1212+
false,
1213+
)
11961214
testTx.Body.TxCollateralReturn = &babbage.BabbageTransactionOutput{
11971215
OutputAmount: mary.MaryTransactionOutputValue{
11981216
Amount: testCollateralAmount,
@@ -1269,9 +1287,12 @@ func TestUtxoValidateNoCollateralInputs(t *testing.T) {
12691287
t.Run(
12701288
"collateral",
12711289
func(t *testing.T) {
1272-
testTx.Body.TxCollateral = []shelley.ShelleyTransactionInput{
1273-
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1274-
}
1290+
testTx.Body.TxCollateral = cbor.NewSetType[shelley.ShelleyTransactionInput](
1291+
[]shelley.ShelleyTransactionInput{
1292+
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1293+
},
1294+
false,
1295+
)
12751296
err := babbage.UtxoValidateNoCollateralInputs(
12761297
testTx,
12771298
testSlot,
@@ -1374,9 +1395,12 @@ func TestUtxoValidateCollateralEqBalance(t *testing.T) {
13741395
testTx := &babbage.BabbageTransaction{
13751396
Body: babbage.BabbageTransactionBody{
13761397
TxTotalCollateral: testTotalCollateral,
1377-
TxCollateral: []shelley.ShelleyTransactionInput{
1378-
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1379-
},
1398+
TxCollateral: cbor.NewSetType[shelley.ShelleyTransactionInput](
1399+
[]shelley.ShelleyTransactionInput{
1400+
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1401+
},
1402+
false,
1403+
),
13801404
},
13811405
}
13821406
testLedgerState := test.MockLedgerState{
@@ -1492,10 +1516,13 @@ func TestUtxoValidateTooManyCollateralInputs(t *testing.T) {
14921516
t.Run(
14931517
"too many collateral inputs",
14941518
func(t *testing.T) {
1495-
testTx.Body.TxCollateral = []shelley.ShelleyTransactionInput{
1496-
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1497-
shelley.NewShelleyTransactionInput(testInputTxId, 1),
1498-
}
1519+
testTx.Body.TxCollateral = cbor.NewSetType[shelley.ShelleyTransactionInput](
1520+
[]shelley.ShelleyTransactionInput{
1521+
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1522+
shelley.NewShelleyTransactionInput(testInputTxId, 1),
1523+
},
1524+
false,
1525+
)
14991526
err := babbage.UtxoValidateTooManyCollateralInputs(
15001527
testTx,
15011528
testSlot,
@@ -1523,9 +1550,12 @@ func TestUtxoValidateTooManyCollateralInputs(t *testing.T) {
15231550
t.Run(
15241551
"single collateral input",
15251552
func(t *testing.T) {
1526-
testTx.Body.TxCollateral = []shelley.ShelleyTransactionInput{
1527-
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1528-
}
1553+
testTx.Body.TxCollateral = cbor.NewSetType[shelley.ShelleyTransactionInput](
1554+
[]shelley.ShelleyTransactionInput{
1555+
shelley.NewShelleyTransactionInput(testInputTxId, 0),
1556+
},
1557+
false,
1558+
)
15291559
err := babbage.UtxoValidateTooManyCollateralInputs(
15301560
testTx,
15311561
testSlot,

0 commit comments

Comments
 (0)