Skip to content

Commit

Permalink
Merge pull request #398 from iotaledger/fix/max-input-output-count
Browse files Browse the repository at this point in the history
Fix RefUTXOIndexMax off-by-one error
  • Loading branch information
luca-moser authored Jul 21, 2022
2 parents 6c127db + cb8ef79 commit bfc447d
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 7 deletions.
2 changes: 1 addition & 1 deletion input_utxo.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const (
// RefUTXOIndexMin is the minimum index of a referenced UTXO.
RefUTXOIndexMin = 0
// RefUTXOIndexMax is the maximum index of a referenced UTXO.
RefUTXOIndexMax = 126
RefUTXOIndexMax = MaxOutputsCount - 1

// UTXOInputSize is the size of a UTXO input: input type + tx id + index
UTXOInputSize = serializer.SmallTypeDenotationByteSize + TransactionIDLength + serializer.UInt16ByteSize
Expand Down
64 changes: 58 additions & 6 deletions tpkg/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,16 +256,45 @@ func ReferenceUnlock(index uint16) *iotago.ReferenceUnlock {

// RandTransactionEssence returns a random transaction essence.
func RandTransactionEssence() *iotago.TransactionEssence {
return RandTransactionEssenceWithInputOutputCount(rand.Intn(iotago.MaxInputsCount)+1, rand.Intn(iotago.MaxOutputsCount)+1)
}

// RandTransactionEssenceWithInputCount returns a random transaction essence with a specific amount of inputs..
func RandTransactionEssenceWithInputCount(inputCount int) *iotago.TransactionEssence {
return RandTransactionEssenceWithInputOutputCount(inputCount, rand.Intn(iotago.MaxOutputsCount)+1)
}

// RandTransactionEssenceWithOutputCount returns a random transaction essence with a specific amount of outputs.
func RandTransactionEssenceWithOutputCount(outputCount int) *iotago.TransactionEssence {
return RandTransactionEssenceWithInputOutputCount(rand.Intn(iotago.MaxInputsCount)+1, outputCount)
}

// RandTransactionEssenceWithInputOutputCount returns a random transaction essence with a specific amount of inputs and outputs.
func RandTransactionEssenceWithInputOutputCount(inputCount int, outputCount int) *iotago.TransactionEssence {
tx := &iotago.TransactionEssence{
NetworkID: TestNetworkID,
}

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

outputCount := rand.Intn(10) + 1
for i := outputCount; i > 0; i-- {
tx.Outputs = append(tx.Outputs, RandBasicOutput(iotago.AddressEd25519))
}

return tx
}

// RandTransactionEssenceWithInputs returns a random transaction essence with a specific slice of inputs.
func RandTransactionEssenceWithInputs(inputs iotago.Inputs) *iotago.TransactionEssence {
tx := &iotago.TransactionEssence{
NetworkID: TestNetworkID,
}

tx.Inputs = inputs

outputCount := rand.Intn(iotago.MaxOutputsCount) + 1
for i := outputCount; i > 0; i-- {
tx.Outputs = append(tx.Outputs, RandBasicOutput(iotago.AddressEd25519))
}
Expand Down Expand Up @@ -377,10 +406,9 @@ func RandBlock(withPayloadType iotago.PayloadType) *iotago.Block {
}
}

// RandTransaction returns a random transaction.
func RandTransaction() *iotago.Transaction {
// RandTransactionWithEssence returns a random transaction with a specific essence.
func RandTransactionWithEssence(essence *iotago.TransactionEssence) *iotago.Transaction {
sigTxPayload := &iotago.Transaction{}
essence := RandTransactionEssence()
sigTxPayload.Essence = essence

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

// RandTransaction returns a random transaction.
func RandTransaction() *iotago.Transaction {
return RandTransactionWithEssence(RandTransactionEssence())
}

// RandTransactionWithInputCount returns a random transaction with a specific amount of inputs.
func RandTransactionWithInputCount(inputCount int) *iotago.Transaction {
return RandTransactionWithEssence(RandTransactionEssenceWithInputCount(inputCount))
}

// RandTransactionWithOutputCount returns a random transaction with a specific amount of outputs.
func RandTransactionWithOutputCount(outputCount int) *iotago.Transaction {
return RandTransactionWithEssence(RandTransactionEssenceWithOutputCount(outputCount))
}

// RandTransactionWithInputOutputCount returns a random transaction with a specific amount of inputs and outputs.
func RandTransactionWithInputOutputCount(inputCount int, outputCount int) *iotago.Transaction {
return RandTransactionWithEssence(RandTransactionEssenceWithInputOutputCount(inputCount, outputCount))
}

// RandTreasuryInput returns a random treasury input.
func RandTreasuryInput() *iotago.TreasuryInput {
treasuryInput := &iotago.TreasuryInput{}
Expand All @@ -401,11 +449,15 @@ func RandTreasuryInput() *iotago.TreasuryInput {

// RandUTXOInput returns a random UTXO input.
func RandUTXOInput() *iotago.UTXOInput {
return RandUTXOInputWithIndex(uint16(rand.Intn(iotago.RefUTXOIndexMax)))
}

// RandUTXOInputWithIndex returns a random UTXO input with a specific index.
func RandUTXOInputWithIndex(index uint16) *iotago.UTXOInput {
utxoInput := &iotago.UTXOInput{}
txID := RandBytes(iotago.TransactionIDLength)
copy(utxoInput.TransactionID[:], txID)

index := uint16(rand.Intn(iotago.RefUTXOIndexMax))
utxoInput.TransactionOutputIndex = index
return utxoInput
}
Expand Down
77 changes: 77 additions & 0 deletions transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/stretchr/testify/require"

"github.com/iotaledger/hive.go/serializer/v2"
iotago "github.com/iotaledger/iota.go/v3"
"github.com/iotaledger/iota.go/v3/tpkg"
)
Expand All @@ -24,6 +25,82 @@ func TestTransactionDeSerialize(t *testing.T) {
}
}

func TestTransactionDeSerialize_MaxInputsCount(t *testing.T) {
tests := []deSerializeTest{
{
name: "ok",
source: tpkg.RandTransactionWithInputCount(iotago.MaxInputsCount),
target: &iotago.Transaction{},
seriErr: nil,
deSeriErr: nil,
},
{
name: "too many inputs",
source: tpkg.RandTransactionWithInputCount(iotago.MaxInputsCount + 1),
target: &iotago.Transaction{},
seriErr: serializer.ErrArrayValidationMaxElementsExceeded,
deSeriErr: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, tt.deSerialize)
}
}

func TestTransactionDeSerialize_MaxOutputsCount(t *testing.T) {
tests := []deSerializeTest{
{
name: "ok",
source: tpkg.RandTransactionWithOutputCount(iotago.MaxOutputsCount),
target: &iotago.Transaction{},
seriErr: nil,
deSeriErr: nil,
},
{
name: "too many outputs",
source: tpkg.RandTransactionWithOutputCount(iotago.MaxOutputsCount + 1),
target: &iotago.Transaction{},
seriErr: serializer.ErrArrayValidationMaxElementsExceeded,
deSeriErr: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, tt.deSerialize)
}
}

func TestTransactionDeSerialize_RefUTXOIndexMax(t *testing.T) {
tests := []deSerializeTest{
{
name: "ok",
source: tpkg.RandTransactionWithEssence(tpkg.RandTransactionEssenceWithInputs(iotago.Inputs{
&iotago.UTXOInput{
TransactionID: tpkg.RandTransactionID(),
TransactionOutputIndex: iotago.RefUTXOIndexMax,
},
})),
target: &iotago.Transaction{},
seriErr: nil,
deSeriErr: nil,
},
{
name: "wrong ref index",
source: tpkg.RandTransactionWithEssence(tpkg.RandTransactionEssenceWithInputs(iotago.Inputs{
&iotago.UTXOInput{
TransactionID: tpkg.RandTransactionID(),
TransactionOutputIndex: iotago.RefUTXOIndexMax + 1,
},
})),
target: &iotago.Transaction{},
seriErr: iotago.ErrRefUTXOIndexInvalid,
deSeriErr: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, tt.deSerialize)
}
}

func TestNFTTransition(t *testing.T) {
_, ident1, ident1AddrKeys := tpkg.RandEd25519Identity()

Expand Down

0 comments on commit bfc447d

Please sign in to comment.