Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unify and flatten Input interfaces and types #474

Merged
merged 3 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions api_v3.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,13 +437,13 @@ func V3API(protoParams ProtocolParameters) API {
must(api.RegisterTypeSettings(TransactionEssence{}, serix.TypeSettings{}.WithObjectType(TransactionEssenceNormal)))

must(api.RegisterTypeSettings(CommitmentInput{},
serix.TypeSettings{}.WithObjectType(uint8(ContextInputCommitment))),
serix.TypeSettings{}.WithObjectType(uint8(InputCommitment))),
)
must(api.RegisterTypeSettings(BlockIssuanceCreditInput{},
serix.TypeSettings{}.WithObjectType(uint8(ContextInputBlockIssuanceCredit))),
serix.TypeSettings{}.WithObjectType(uint8(InputBlockIssuanceCredit))),
)
must(api.RegisterTypeSettings(RewardInput{},
serix.TypeSettings{}.WithObjectType(uint8(ContextInputReward))),
serix.TypeSettings{}.WithObjectType(uint8(InputReward))),
)

must(api.RegisterTypeSettings(TxEssenceContextInputs{},
Expand Down
6 changes: 3 additions & 3 deletions builder/transaction_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (b *TransactionBuilder) AddInput(input *TxInput) *TransactionBuilder {
type TransactionBuilderInputFilter func(outputID iotago.OutputID, input iotago.Output) bool

// AddContextInput adds the given context input to the builder.
func (b *TransactionBuilder) AddContextInput(contextInput iotago.ContextInput) *TransactionBuilder {
func (b *TransactionBuilder) AddContextInput(contextInput iotago.Input) *TransactionBuilder {
b.essence.ContextInputs = append(b.essence.ContextInputs, contextInput)

return b
Expand Down Expand Up @@ -120,7 +120,7 @@ func (b *TransactionBuilder) Build(signer iotago.AddressSigner) (*iotago.Transac
var inputIDs iotago.OutputIDs
for _, input := range b.essence.Inputs {
//nolint:forcetypeassert // we can safely assume that this is an UTXOInput
inputIDs = append(inputIDs, input.(*iotago.UTXOInput).ID())
inputIDs = append(inputIDs, input.(*iotago.UTXOInput).OutputID())
}

inputs := inputIDs.OrderedSet(b.inputs)
Expand All @@ -139,7 +139,7 @@ func (b *TransactionBuilder) Build(signer iotago.AddressSigner) (*iotago.Transac
unlocks := iotago.Unlocks{}
for i, inputRef := range b.essence.Inputs {
//nolint:forcetypeassert // we can safely assume that this is an UTXOInput
addr := b.inputOwner[inputRef.(*iotago.UTXOInput).ID()]
addr := b.inputOwner[inputRef.(*iotago.UTXOInput).OutputID()]
addrKey := addr.Key()

pos, unlocked := unlockPos[addrKey]
Expand Down
16 changes: 8 additions & 8 deletions builder/transaction_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func TestTransactionBuilder(t *testing.T) {
inputUTXO1 := &iotago.UTXOInput{TransactionID: tpkg.Rand32ByteArray(), TransactionOutputIndex: 0}
input := tpkg.RandBasicOutput(iotago.AddressEd25519)
bdl := builder.NewTransactionBuilder(tpkg.TestAPI).
AddInput(&builder.TxInput{UnlockTarget: inputAddr, InputID: inputUTXO1.ID(), Input: input}).
AddInput(&builder.TxInput{UnlockTarget: inputAddr, InputID: inputUTXO1.OutputID(), Input: input}).
AddOutput(&iotago.BasicOutput{
Amount: 50,
Conditions: iotago.BasicOutputUnlockConditions{
Expand Down Expand Up @@ -84,10 +84,10 @@ func TestTransactionBuilder(t *testing.T) {
)

bdl := builder.NewTransactionBuilder(tpkg.TestAPI).
AddInput(&builder.TxInput{UnlockTarget: inputAddr, InputID: inputID1.ID(), Input: basicOutput}).
AddInput(&builder.TxInput{UnlockTarget: inputAddr, InputID: inputID2.ID(), Input: nftOutput}).
AddInput(&builder.TxInput{UnlockTarget: nftOutput.Chain().ToAddress(), InputID: inputID3.ID(), Input: accountOwnedByNFT}).
AddInput(&builder.TxInput{UnlockTarget: accountOwnedByNFT.Chain().ToAddress(), InputID: inputID4.ID(), Input: basicOwnedByAccount}).
AddInput(&builder.TxInput{UnlockTarget: inputAddr, InputID: inputID1.OutputID(), Input: basicOutput}).
AddInput(&builder.TxInput{UnlockTarget: inputAddr, InputID: inputID2.OutputID(), Input: nftOutput}).
AddInput(&builder.TxInput{UnlockTarget: nftOutput.Chain().ToAddress(), InputID: inputID3.OutputID(), Input: accountOwnedByNFT}).
AddInput(&builder.TxInput{UnlockTarget: accountOwnedByNFT.Chain().ToAddress(), InputID: inputID4.OutputID(), Input: basicOwnedByAccount}).
AddOutput(&iotago.BasicOutput{
Amount: 4000,
Conditions: iotago.BasicOutputUnlockConditions{
Expand All @@ -105,7 +105,7 @@ func TestTransactionBuilder(t *testing.T) {
inputUTXO1 := &iotago.UTXOInput{TransactionID: tpkg.Rand32ByteArray(), TransactionOutputIndex: 0}

bdl := builder.NewTransactionBuilder(tpkg.TestAPI).
AddInput(&builder.TxInput{UnlockTarget: inputAddr, InputID: inputUTXO1.ID(), Input: tpkg.RandBasicOutput(iotago.AddressEd25519)}).
AddInput(&builder.TxInput{UnlockTarget: inputAddr, InputID: inputUTXO1.OutputID(), Input: tpkg.RandBasicOutput(iotago.AddressEd25519)}).
AddOutput(&iotago.BasicOutput{
Amount: 50,
Conditions: iotago.BasicOutputUnlockConditions{
Expand All @@ -124,7 +124,7 @@ func TestTransactionBuilder(t *testing.T) {
inputUTXO1 := &iotago.UTXOInput{TransactionID: tpkg.Rand32ByteArray(), TransactionOutputIndex: 0}

bdl := builder.NewTransactionBuilder(tpkg.TestAPI).
AddInput(&builder.TxInput{UnlockTarget: inputAddr, InputID: inputUTXO1.ID(), Input: tpkg.RandBasicOutput(iotago.AddressEd25519)}).
AddInput(&builder.TxInput{UnlockTarget: inputAddr, InputID: inputUTXO1.OutputID(), Input: tpkg.RandBasicOutput(iotago.AddressEd25519)}).
AddOutput(&iotago.BasicOutput{
Amount: 50,
Conditions: iotago.BasicOutputUnlockConditions{
Expand All @@ -149,7 +149,7 @@ func TestTransactionBuilder(t *testing.T) {
inputUTXO1 := &iotago.UTXOInput{TransactionID: tpkg.Rand32ByteArray(), TransactionOutputIndex: 0}

bdl := builder.NewTransactionBuilder(tpkg.TestAPI).
AddInput(&builder.TxInput{UnlockTarget: inputAddr, InputID: inputUTXO1.ID(), Input: tpkg.RandBasicOutput(iotago.AddressEd25519)}).
AddInput(&builder.TxInput{UnlockTarget: inputAddr, InputID: inputUTXO1.OutputID(), Input: tpkg.RandBasicOutput(iotago.AddressEd25519)}).
AddOutput(&iotago.BasicOutput{
Amount: 50,
Conditions: iotago.BasicOutputUnlockConditions{
Expand Down
9 changes: 9 additions & 0 deletions commitment.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"

"github.com/iotaledger/hive.go/ierrors"
"github.com/iotaledger/hive.go/lo"
"github.com/iotaledger/hive.go/serializer/v2"
)

Expand Down Expand Up @@ -49,6 +50,14 @@ func (c *Commitment) ID() (CommitmentID, error) {
return SlotIdentifierRepresentingData(c.Index, data), nil
}

func (c *Commitment) StateID() Identifier {
return IdentifierFromData(lo.PanicOnErr(c.MustID().Bytes()))
}

func (c *Commitment) Type() StateType {
return InputCommitment
}

func (c *Commitment) MustID() CommitmentID {
id, err := c.ID()
if err != nil {
Expand Down
44 changes: 20 additions & 24 deletions input.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,29 @@ import (
"github.com/iotaledger/hive.go/serializer/v2"
)

// InputType defines the type of inputs.
type InputType byte
// StateType defines the type of inputs.
type StateType byte

const (
// InputUTXO is a type of input which references an unspent transaction output.
InputUTXO InputType = iota
InputUTXO StateType = iota
// InputCommitment is a type of input which references a commitment.
InputCommitment
// InputBlockIssuanceCredit is a type of input which references the block issuance credit from a specific account and commitment, the latter being provided by a commitment input.
InputBlockIssuanceCredit
// InputReward is a type of input which references an Account or Delegation Input for which to claim rewards.
InputReward
)

func (inputType InputType) String() string {
func (inputType StateType) String() string {
if int(inputType) >= len(inputNames) {
return fmt.Sprintf("unknown input type: %d", inputType)
}

return inputNames[inputType]
}

var (
inputNames = [InputUTXO + 1]string{"UTXOInput"}
)
var inputNames = [InputUTXO + 1]string{"UTXOInput"}

var (
// ErrRefUTXOIndexInvalid gets returned on invalid UTXO indices.
Expand Down Expand Up @@ -68,23 +72,15 @@ func (in Inputs[T]) WorkScore(workScoreStructure *WorkScoreStructure) (WorkScore
return workScoreBytes, nil
}

// Input references a UTXO.
// Input references a generic input.
type Input interface {
Sizer
ProcessableObject

// Type returns the type of Input.
Type() InputType
}

// IndexedUTXOReferencer is a type of Input which references a UTXO by the transaction ID and output index.
type IndexedUTXOReferencer interface {
Input
StateID() Identifier

// Ref returns the UTXO this Input references.
Ref() OutputID
// Index returns the output index of the UTXO this Input references.
Index() uint16
// Type returns the type of Input.
Type() StateType
}

// InputsSyntacticalValidationFunc which given the index of an input and the input itself, runs syntactical validations and returns an error if any should fail.
Expand All @@ -96,15 +92,15 @@ func InputsSyntacticalUnique() InputsSyntacticalValidationFunc {

return func(index int, input Input) error {
switch castInput := input.(type) {
case IndexedUTXOReferencer:
utxoRef := castInput.Ref()
case *UTXOInput:
utxoRef := castInput.OutputID()
k := string(utxoRef[:])
if j, has := utxoSet[k]; has {
return ierrors.Wrapf(ErrInputUTXORefsNotUnique, "input %d and %d share the same UTXO ref", j, index)
}
utxoSet[k] = index
default:
return ierrors.Wrapf(ErrUnknownInputType, "input %d, tx can only contain IndexedUTXOReferencer", index)
return ierrors.Wrapf(ErrUnknownInputType, "input %d, tx can only contain UTXO inputs", index)
}

return nil
Expand All @@ -115,12 +111,12 @@ func InputsSyntacticalUnique() InputsSyntacticalValidationFunc {
func InputsSyntacticalIndicesWithinBounds() InputsSyntacticalValidationFunc {
return func(index int, input Input) error {
switch castInput := input.(type) {
case IndexedUTXOReferencer:
case *UTXOInput:
if castInput.Index() < RefUTXOIndexMin || castInput.Index() > RefUTXOIndexMax {
return ierrors.Wrapf(ErrRefUTXOIndexInvalid, "input %d", index)
}
default:
return ierrors.Wrapf(ErrUnknownInputType, "input %d, tx can only contain IndexedUTXOReferencer inputs", index)
return ierrors.Wrapf(ErrUnknownInputType, "input %d, tx can only contain UTXInput inputs", index)
}

return nil
Expand Down
8 changes: 6 additions & 2 deletions input_block_issuance_credit.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ type BlockIssuanceCreditInput struct {
AccountID AccountID `serix:"0,mapKey=accountId"`
}

func (b *BlockIssuanceCreditInput) Type() ContextInputType {
return ContextInputBlockIssuanceCredit
func (b *BlockIssuanceCreditInput) StateID() Identifier {
return IdentifierFromData(b.AccountID[:])
}

func (b *BlockIssuanceCreditInput) Type() StateType {
return InputBlockIssuanceCredit
}

func (b *BlockIssuanceCreditInput) Size() int {
Expand Down
8 changes: 6 additions & 2 deletions input_commitment.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ type CommitmentInput struct {
CommitmentID CommitmentID `serix:"0,mapKey=commitmentId"`
}

func (c *CommitmentInput) Type() ContextInputType {
return ContextInputCommitment
func (c *CommitmentInput) StateID() Identifier {
return IdentifierFromData(c.CommitmentID[:])
}

func (c *CommitmentInput) Type() StateType {
return InputCommitment
}

func (c *CommitmentInput) Size() int {
Expand Down
30 changes: 6 additions & 24 deletions input_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,6 @@ import (
// ContextInputType defines the type of context inputs.
type ContextInputType byte

const (
// ContextInputCommitment is a type of input which references a commitment.
ContextInputCommitment ContextInputType = iota
// ContextInputBlockIssuanceCredit is a type of input which references the block issuance credit from a specific account and commitment, the latter being provided by a commitment input.
ContextInputBlockIssuanceCredit
// ContextInputReward is a type of input which references an Account or Delegation Input for which to claim rewards.
ContextInputReward
)

func (inputType ContextInputType) String() string {
if int(inputType) >= len(contextInputNames) {
return fmt.Sprintf("unknown input type: %d", inputType)
Expand All @@ -27,12 +18,10 @@ func (inputType ContextInputType) String() string {
return contextInputNames[inputType]
}

var (
contextInputNames = [ContextInputReward + 1]string{"CommitmentInput", "BlockIssuanceCreditInput", "RewardInput"}
)
var contextInputNames = [InputReward + 1]string{"CommitmentInput", "BlockIssuanceCreditInput", "RewardInput"}

// ContextInputs is a slice of ContextInput.
type ContextInputs[T ContextInput] []T
type ContextInputs[T Input] []T

func (in ContextInputs[T]) WorkScore(workScoreStructure *WorkScoreStructure) (WorkScore, error) {
// LengthPrefixType
Expand Down Expand Up @@ -65,18 +54,9 @@ func (in ContextInputs[T]) Size() int {
return sum
}

// ContextInput provides an additional contextual input for transaction validation.
type ContextInput interface {
Sizer
ProcessableObject

// Type returns the type of ContextInput.
Type() ContextInputType
}

// ContextInputsSyntacticalValidationFunc which given the index of an input and the input itself,
// runs syntactical validations and returns an error if any should fail.
type ContextInputsSyntacticalValidationFunc func(index int, input ContextInput) error
type ContextInputsSyntacticalValidationFunc func(index int, input Input) error

// ContextInputsSyntacticalUnique returns a ContextInputsSyntacticalValidationFunc
// which checks that
Expand All @@ -88,7 +68,7 @@ func ContextInputsSyntacticalUnique() ContextInputsSyntacticalValidationFunc {
bicSet := map[string]int{}
rewardSet := map[uint16]int{}

return func(index int, input ContextInput) error {
return func(index int, input Input) error {
switch castInput := input.(type) {
case *BlockIssuanceCreditInput:
accountID := castInput.AccountID
Expand All @@ -111,6 +91,8 @@ func ContextInputsSyntacticalUnique() ContextInputsSyntacticalValidationFunc {
return ierrors.Wrapf(ErrMultipleInputCommitments, "input %d is the second commitment input", index)
}
hasCommitment = true
case *UTXOInput:
// ignore as we are evaluating context inputs only
default:
return ierrors.Wrapf(ErrUnknownContextInputType, "context input %d, tx can only contain CommitmentInputs, BlockIssuanceCreditInputs or RewardInputs", index)
}
Expand Down
12 changes: 10 additions & 2 deletions input_reward.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package iotago

import (
"encoding/binary"

"github.com/iotaledger/hive.go/serializer/v2"
)

Expand All @@ -9,8 +11,14 @@ type RewardInput struct {
Index uint16 `serix:"0,mapKey=index"`
}

func (r *RewardInput) Type() ContextInputType {
return ContextInputReward
func (r *RewardInput) StateID() Identifier {
buf := make([]byte, 2)
binary.LittleEndian.PutUint16(buf, r.Index)
return IdentifierFromData(buf)
}

func (r *RewardInput) Type() StateType {
return InputReward
}

func (r *RewardInput) Size() int {
Expand Down
12 changes: 6 additions & 6 deletions input_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ func TestInputsSyntacticalUnique(t *testing.T) {
func TestContextInputsSyntacticalUnique(t *testing.T) {
tests := []struct {
name string
inputs iotago.ContextInputs[iotago.ContextInput]
inputs iotago.ContextInputs[iotago.Input]
wantErr error
}{
{
name: "ok",
inputs: iotago.ContextInputs[iotago.ContextInput]{
inputs: iotago.ContextInputs[iotago.Input]{
&iotago.CommitmentInput{
CommitmentID: tpkg.Rand40ByteArray(),
},
Expand All @@ -88,7 +88,7 @@ func TestContextInputsSyntacticalUnique(t *testing.T) {
},
{
name: "fail - multiple commitment inputs",
inputs: iotago.ContextInputs[iotago.ContextInput]{
inputs: iotago.ContextInputs[iotago.Input]{
&iotago.CommitmentInput{
CommitmentID: tpkg.Rand40ByteArray(),
},
Expand All @@ -100,7 +100,7 @@ func TestContextInputsSyntacticalUnique(t *testing.T) {
},
{
name: "fail - block issuance credit inputs not unique",
inputs: iotago.ContextInputs[iotago.ContextInput]{
inputs: iotago.ContextInputs[iotago.Input]{
&iotago.BlockIssuanceCreditInput{
AccountID: [32]byte{},
},
Expand All @@ -112,7 +112,7 @@ func TestContextInputsSyntacticalUnique(t *testing.T) {
},
{
name: "fail - reward input not unique",
inputs: iotago.ContextInputs[iotago.ContextInput]{
inputs: iotago.ContextInputs[iotago.Input]{
&iotago.RewardInput{
Index: 1,
},
Expand All @@ -124,7 +124,7 @@ func TestContextInputsSyntacticalUnique(t *testing.T) {
},
{
name: "fail - reward input references index greater than max inputs count",
inputs: iotago.ContextInputs[iotago.ContextInput]{
inputs: iotago.ContextInputs[iotago.Input]{
&iotago.RewardInput{
Index: 1,
},
Expand Down
Loading
Loading