Skip to content
This repository was archived by the owner on May 20, 2025. It is now read-only.

Commit bfc447d

Browse files
authored
Merge pull request #398 from iotaledger/fix/max-input-output-count
Fix RefUTXOIndexMax off-by-one error
2 parents 6c127db + cb8ef79 commit bfc447d

File tree

3 files changed

+136
-7
lines changed

3 files changed

+136
-7
lines changed

input_utxo.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const (
1313
// RefUTXOIndexMin is the minimum index of a referenced UTXO.
1414
RefUTXOIndexMin = 0
1515
// RefUTXOIndexMax is the maximum index of a referenced UTXO.
16-
RefUTXOIndexMax = 126
16+
RefUTXOIndexMax = MaxOutputsCount - 1
1717

1818
// UTXOInputSize is the size of a UTXO input: input type + tx id + index
1919
UTXOInputSize = serializer.SmallTypeDenotationByteSize + TransactionIDLength + serializer.UInt16ByteSize

tpkg/util.go

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -256,16 +256,45 @@ func ReferenceUnlock(index uint16) *iotago.ReferenceUnlock {
256256

257257
// RandTransactionEssence returns a random transaction essence.
258258
func RandTransactionEssence() *iotago.TransactionEssence {
259+
return RandTransactionEssenceWithInputOutputCount(rand.Intn(iotago.MaxInputsCount)+1, rand.Intn(iotago.MaxOutputsCount)+1)
260+
}
261+
262+
// RandTransactionEssenceWithInputCount returns a random transaction essence with a specific amount of inputs..
263+
func RandTransactionEssenceWithInputCount(inputCount int) *iotago.TransactionEssence {
264+
return RandTransactionEssenceWithInputOutputCount(inputCount, rand.Intn(iotago.MaxOutputsCount)+1)
265+
}
266+
267+
// RandTransactionEssenceWithOutputCount returns a random transaction essence with a specific amount of outputs.
268+
func RandTransactionEssenceWithOutputCount(outputCount int) *iotago.TransactionEssence {
269+
return RandTransactionEssenceWithInputOutputCount(rand.Intn(iotago.MaxInputsCount)+1, outputCount)
270+
}
271+
272+
// RandTransactionEssenceWithInputOutputCount returns a random transaction essence with a specific amount of inputs and outputs.
273+
func RandTransactionEssenceWithInputOutputCount(inputCount int, outputCount int) *iotago.TransactionEssence {
259274
tx := &iotago.TransactionEssence{
260275
NetworkID: TestNetworkID,
261276
}
262277

263-
inputCount := rand.Intn(10) + 1
264278
for i := inputCount; i > 0; i-- {
265279
tx.Inputs = append(tx.Inputs, RandUTXOInput())
266280
}
267281

268-
outputCount := rand.Intn(10) + 1
282+
for i := outputCount; i > 0; i-- {
283+
tx.Outputs = append(tx.Outputs, RandBasicOutput(iotago.AddressEd25519))
284+
}
285+
286+
return tx
287+
}
288+
289+
// RandTransactionEssenceWithInputs returns a random transaction essence with a specific slice of inputs.
290+
func RandTransactionEssenceWithInputs(inputs iotago.Inputs) *iotago.TransactionEssence {
291+
tx := &iotago.TransactionEssence{
292+
NetworkID: TestNetworkID,
293+
}
294+
295+
tx.Inputs = inputs
296+
297+
outputCount := rand.Intn(iotago.MaxOutputsCount) + 1
269298
for i := outputCount; i > 0; i-- {
270299
tx.Outputs = append(tx.Outputs, RandBasicOutput(iotago.AddressEd25519))
271300
}
@@ -377,10 +406,9 @@ func RandBlock(withPayloadType iotago.PayloadType) *iotago.Block {
377406
}
378407
}
379408

380-
// RandTransaction returns a random transaction.
381-
func RandTransaction() *iotago.Transaction {
409+
// RandTransactionWithEssence returns a random transaction with a specific essence.
410+
func RandTransactionWithEssence(essence *iotago.TransactionEssence) *iotago.Transaction {
382411
sigTxPayload := &iotago.Transaction{}
383-
essence := RandTransactionEssence()
384412
sigTxPayload.Essence = essence
385413

386414
unlocksCount := len(essence.Inputs)
@@ -391,6 +419,26 @@ func RandTransaction() *iotago.Transaction {
391419
return sigTxPayload
392420
}
393421

422+
// RandTransaction returns a random transaction.
423+
func RandTransaction() *iotago.Transaction {
424+
return RandTransactionWithEssence(RandTransactionEssence())
425+
}
426+
427+
// RandTransactionWithInputCount returns a random transaction with a specific amount of inputs.
428+
func RandTransactionWithInputCount(inputCount int) *iotago.Transaction {
429+
return RandTransactionWithEssence(RandTransactionEssenceWithInputCount(inputCount))
430+
}
431+
432+
// RandTransactionWithOutputCount returns a random transaction with a specific amount of outputs.
433+
func RandTransactionWithOutputCount(outputCount int) *iotago.Transaction {
434+
return RandTransactionWithEssence(RandTransactionEssenceWithOutputCount(outputCount))
435+
}
436+
437+
// RandTransactionWithInputOutputCount returns a random transaction with a specific amount of inputs and outputs.
438+
func RandTransactionWithInputOutputCount(inputCount int, outputCount int) *iotago.Transaction {
439+
return RandTransactionWithEssence(RandTransactionEssenceWithInputOutputCount(inputCount, outputCount))
440+
}
441+
394442
// RandTreasuryInput returns a random treasury input.
395443
func RandTreasuryInput() *iotago.TreasuryInput {
396444
treasuryInput := &iotago.TreasuryInput{}
@@ -401,11 +449,15 @@ func RandTreasuryInput() *iotago.TreasuryInput {
401449

402450
// RandUTXOInput returns a random UTXO input.
403451
func RandUTXOInput() *iotago.UTXOInput {
452+
return RandUTXOInputWithIndex(uint16(rand.Intn(iotago.RefUTXOIndexMax)))
453+
}
454+
455+
// RandUTXOInputWithIndex returns a random UTXO input with a specific index.
456+
func RandUTXOInputWithIndex(index uint16) *iotago.UTXOInput {
404457
utxoInput := &iotago.UTXOInput{}
405458
txID := RandBytes(iotago.TransactionIDLength)
406459
copy(utxoInput.TransactionID[:], txID)
407460

408-
index := uint16(rand.Intn(iotago.RefUTXOIndexMax))
409461
utxoInput.TransactionOutputIndex = index
410462
return utxoInput
411463
}

transaction_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/stretchr/testify/require"
99

10+
"github.com/iotaledger/hive.go/serializer/v2"
1011
iotago "github.com/iotaledger/iota.go/v3"
1112
"github.com/iotaledger/iota.go/v3/tpkg"
1213
)
@@ -24,6 +25,82 @@ func TestTransactionDeSerialize(t *testing.T) {
2425
}
2526
}
2627

28+
func TestTransactionDeSerialize_MaxInputsCount(t *testing.T) {
29+
tests := []deSerializeTest{
30+
{
31+
name: "ok",
32+
source: tpkg.RandTransactionWithInputCount(iotago.MaxInputsCount),
33+
target: &iotago.Transaction{},
34+
seriErr: nil,
35+
deSeriErr: nil,
36+
},
37+
{
38+
name: "too many inputs",
39+
source: tpkg.RandTransactionWithInputCount(iotago.MaxInputsCount + 1),
40+
target: &iotago.Transaction{},
41+
seriErr: serializer.ErrArrayValidationMaxElementsExceeded,
42+
deSeriErr: nil,
43+
},
44+
}
45+
for _, tt := range tests {
46+
t.Run(tt.name, tt.deSerialize)
47+
}
48+
}
49+
50+
func TestTransactionDeSerialize_MaxOutputsCount(t *testing.T) {
51+
tests := []deSerializeTest{
52+
{
53+
name: "ok",
54+
source: tpkg.RandTransactionWithOutputCount(iotago.MaxOutputsCount),
55+
target: &iotago.Transaction{},
56+
seriErr: nil,
57+
deSeriErr: nil,
58+
},
59+
{
60+
name: "too many outputs",
61+
source: tpkg.RandTransactionWithOutputCount(iotago.MaxOutputsCount + 1),
62+
target: &iotago.Transaction{},
63+
seriErr: serializer.ErrArrayValidationMaxElementsExceeded,
64+
deSeriErr: nil,
65+
},
66+
}
67+
for _, tt := range tests {
68+
t.Run(tt.name, tt.deSerialize)
69+
}
70+
}
71+
72+
func TestTransactionDeSerialize_RefUTXOIndexMax(t *testing.T) {
73+
tests := []deSerializeTest{
74+
{
75+
name: "ok",
76+
source: tpkg.RandTransactionWithEssence(tpkg.RandTransactionEssenceWithInputs(iotago.Inputs{
77+
&iotago.UTXOInput{
78+
TransactionID: tpkg.RandTransactionID(),
79+
TransactionOutputIndex: iotago.RefUTXOIndexMax,
80+
},
81+
})),
82+
target: &iotago.Transaction{},
83+
seriErr: nil,
84+
deSeriErr: nil,
85+
},
86+
{
87+
name: "wrong ref index",
88+
source: tpkg.RandTransactionWithEssence(tpkg.RandTransactionEssenceWithInputs(iotago.Inputs{
89+
&iotago.UTXOInput{
90+
TransactionID: tpkg.RandTransactionID(),
91+
TransactionOutputIndex: iotago.RefUTXOIndexMax + 1,
92+
},
93+
})),
94+
target: &iotago.Transaction{},
95+
seriErr: iotago.ErrRefUTXOIndexInvalid,
96+
deSeriErr: nil,
97+
},
98+
}
99+
for _, tt := range tests {
100+
t.Run(tt.name, tt.deSerialize)
101+
}
102+
}
103+
27104
func TestNFTTransition(t *testing.T) {
28105
_, ident1, ident1AddrKeys := tpkg.RandEd25519Identity()
29106

0 commit comments

Comments
 (0)