From c8f11e236df9a726a99ad5922c9ce9bf887beee9 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Tue, 5 Mar 2024 14:39:14 +0800 Subject: [PATCH 01/48] Panic on missing UTXO --- signed_transaction.go | 2 -- vm/nova/vm.go | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/signed_transaction.go b/signed_transaction.go index 050ec028d..26ad196ad 100644 --- a/signed_transaction.go +++ b/signed_transaction.go @@ -10,8 +10,6 @@ import ( ) var ( - // ErrMissingUTXO gets returned if an UTXO is missing to commence a certain operation. - ErrMissingUTXO = ierrors.New("missing utxo") // ErrInputOutputBaseTokenMismatch gets returned if a transaction does not spend the entirety of the inputs to the outputs. ErrInputOutputBaseTokenMismatch = ierrors.New("inputs and outputs do not spend/deposit the same amount of base tokens") // ErrManaOverflow gets returned when there is an under- or overflow in Mana calculations. diff --git a/vm/nova/vm.go b/vm/nova/vm.go index 7177e344f..5d260da85 100644 --- a/vm/nova/vm.go +++ b/vm/nova/vm.go @@ -42,7 +42,7 @@ func NewVMParamsWorkingSet(api iotago.API, t *iotago.Transaction, resolvedInputs workingSet.InputIDToInputIndex[txInputID] = uint16(inputIndex) input, ok := workingSet.UTXOInputsSet[txInputID] if !ok { - return nil, ierrors.Wrapf(iotago.ErrMissingUTXO, "utxo for input %d not supplied", inputIndex) + panic(fmt.Sprintf("UTXO for input %d should be supplied %s", inputIndex, txInputID)) } workingSet.UTXOInputs = append(workingSet.UTXOInputs, input) } From f86b188cc1f38d090cfdd1ff9ce383b6df0a039f Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Tue, 5 Mar 2024 14:39:36 +0800 Subject: [PATCH 02/48] Remove unused `EssenceMsgToSign` --- vm/nova/vm.go | 5 ----- vm/vm.go | 2 -- 2 files changed, 7 deletions(-) diff --git a/vm/nova/vm.go b/vm/nova/vm.go index 5d260da85..8043cc338 100644 --- a/vm/nova/vm.go +++ b/vm/nova/vm.go @@ -47,11 +47,6 @@ func NewVMParamsWorkingSet(api iotago.API, t *iotago.Transaction, resolvedInputs workingSet.UTXOInputs = append(workingSet.UTXOInputs, input) } - workingSet.EssenceMsgToSign, err = t.SigningMessage() - if err != nil { - return nil, err - } - txID, err := workingSet.Tx.ID() if err != nil { return nil, err diff --git a/vm/vm.go b/vm/vm.go index 76ad433cf..a55f1caf7 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -41,8 +41,6 @@ type WorkingSet struct { InputIDToInputIndex map[iotago.OutputID]uint16 // The transaction for which this semantic validation happens. Tx *iotago.Transaction - // The message which signatures are signing. - EssenceMsgToSign []byte // The ChainOutput(s) at the input side. InChains ChainInputSet // The sum of NativeTokens at the input side. From 3789cabf634c71e1ea56f4b346337177af04f566 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Wed, 6 Mar 2024 11:11:36 +0800 Subject: [PATCH 03/48] Improve mana errors --- mana.go | 2 +- mana_decay_provider.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mana.go b/mana.go index 28737526b..36d296485 100644 --- a/mana.go +++ b/mana.go @@ -74,7 +74,7 @@ func (r RewardsParameters) TargetReward(epoch EpochIndex, api API) (Mana, error) // Rewards start at epoch 0. decayedInitialReward, err := api.ManaDecayProvider().DecayManaByEpochs(api.ProtocolParameters().RewardsParameters().InitialTargetRewardsRate, 0, epoch) if err != nil { - return 0, ierrors.Errorf("failed to calculate decayed initial reward: %w", err) + return 0, ierrors.Wrap(err, "failed to calculate decayed initial reward") } return decayedInitialReward, nil diff --git a/mana_decay_provider.go b/mana_decay_provider.go index 9376b4ed7..ea66b3a91 100644 --- a/mana_decay_provider.go +++ b/mana_decay_provider.go @@ -173,7 +173,7 @@ func (p *ManaDecayProvider) DecayManaBySlots(mana Mana, creationSlot SlotIndex, // DecayManaByEpochs applies the decay between the creation and target epochs to the given mana. func (p *ManaDecayProvider) DecayManaByEpochs(mana Mana, creationEpoch EpochIndex, targetEpoch EpochIndex) (Mana, error) { if creationEpoch > targetEpoch { - return 0, ierrors.Wrapf(ErrManaDecayCreationIndexExceedsTargetIndex, "the creation epoch was greater than the target epoch: %d > %d", creationEpoch, targetEpoch) + return 0, ierrors.WithMessagef(ErrManaDecayCreationIndexExceedsTargetIndex, "the creation epoch (%d) was greater than the target epoch (%d)", creationEpoch, targetEpoch) } return p.decay(mana, targetEpoch-creationEpoch) @@ -182,7 +182,7 @@ func (p *ManaDecayProvider) DecayManaByEpochs(mana Mana, creationEpoch EpochInde // GenerateManaAndDecayBySlots generates mana from the given base token amount and returns the decayed result. func (p *ManaDecayProvider) GenerateManaAndDecayBySlots(amount BaseToken, creationSlot SlotIndex, targetSlot SlotIndex) (Mana, error) { if creationSlot > targetSlot { - return 0, ierrors.Wrapf(ErrManaDecayCreationIndexExceedsTargetIndex, "the creation slot was greater than the target slot: %d > %d", creationSlot, targetSlot) + return 0, ierrors.WithMessagef(ErrManaDecayCreationIndexExceedsTargetIndex, "the creation slot (%d) was greater than the target slot (%d)", creationSlot, targetSlot) } creationEpoch := p.timeProvider.EpochFromSlot(creationSlot) From 3ac83eb1ded024db3e0e61a915a07243f6af9999 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Wed, 6 Mar 2024 11:12:08 +0800 Subject: [PATCH 04/48] Panic on failed ID computation --- vm/nova/vm.go | 2 +- vm/vm.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vm/nova/vm.go b/vm/nova/vm.go index ca9007087..a6b798fb6 100644 --- a/vm/nova/vm.go +++ b/vm/nova/vm.go @@ -49,7 +49,7 @@ func NewVMParamsWorkingSet(api iotago.API, t *iotago.Transaction, resolvedInputs txID, err := workingSet.Tx.ID() if err != nil { - return nil, err + panic(fmt.Sprintf("transaction ID computation should have succeeded: %s", err)) } workingSet.InChains = utxoInputsSet.ChainInputSet() diff --git a/vm/vm.go b/vm/vm.go index a55f1caf7..447150047 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -76,7 +76,7 @@ func TotalManaIn(manaDecayProvider *iotago.ManaDecayProvider, storageScoreStruct // stored Mana manaStored, err := manaDecayProvider.DecayManaBySlots(input.StoredMana(), outputID.CreationSlot(), txCreationSlot) if err != nil { - return 0, ierrors.Wrapf(err, "input %s stored mana calculation failed", outputID) + return 0, ierrors.Wrapf(err, "stored mana calculation failed for input %s", outputID) } totalIn, err = safemath.SafeAdd(totalIn, manaStored) if err != nil { @@ -435,12 +435,12 @@ func ValidateUnlocks(signedTransaction *iotago.SignedTransaction, resolvedInputs txID, err := signedTransaction.Transaction.ID() if err != nil { - return nil, ierrors.Wrapf(err, "failed to compute transaction ID") + panic(fmt.Sprintf("transaction ID computation should have succeeded: %s", err)) } essenceMsgToSign, err := signedTransaction.Transaction.SigningMessage() if err != nil { - return nil, ierrors.Wrapf(err, "failed to compute signing message") + panic(fmt.Sprintf("signing message computation should have succeeded: %s", err)) } unlockedAddrsSet := &unlockedAddressesSet{ From 77e65e828f1a34c23c97cf7941f67308c67e577f Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Wed, 6 Mar 2024 11:57:23 +0800 Subject: [PATCH 05/48] Improve errors in merkle_proof --- merklehasher/merkle_proof.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/merklehasher/merkle_proof.go b/merklehasher/merkle_proof.go index c6555005a..f082de5f7 100644 --- a/merklehasher/merkle_proof.go +++ b/merklehasher/merkle_proof.go @@ -18,6 +18,11 @@ const ( MerkleHashableTypeValueHash MerkleHashableType = 2 ) +var ( + // ErrProofValueNotFound gets returned when the value for which to compute the proof was not found. + ErrProofValueNotFound = ierrors.New("the value for which to compute the inclusion proof was not found in the supplied list") +) + type MerkleHashable[V Value] interface { hash(hasher *Hasher[V]) []byte } @@ -65,7 +70,7 @@ func (t *Hasher[V]) ComputeProof(values []V, valueToProof V) (*Proof[V], error) } } if !found { - return nil, ierrors.Errorf("value %s is not contained in the given list", hexutil.EncodeHex(valueToProofBytes)) + return nil, ierrors.WithMessagef(ErrProofValueNotFound, "value %s is not contained in the given values list", hexutil.EncodeHex(valueToProofBytes)) } return t.ComputeProofForIndex(values, index) @@ -74,10 +79,10 @@ func (t *Hasher[V]) ComputeProof(values []V, valueToProof V) (*Proof[V], error) // ComputeProofForIndex computes the audit path given the values and the index of the value we want to create the inclusion proof for. func (t *Hasher[V]) ComputeProofForIndex(values []V, index int) (*Proof[V], error) { if len(values) < 1 { - return nil, ierrors.New("you need at least 1 item to create an inclusion proof") + return nil, ierrors.New("at least one item is needed to create an inclusion proof") } if index >= len(values) { - return nil, ierrors.Errorf("index %d out of bounds len=%d", index, len(values)) + return nil, ierrors.Errorf("index %d out of bounds for 'values' of len %d", index, len(values)) } data := make([][]byte, len(values)) From 019a2c737c3e13170c8b5112c8f1077a76d9ece2 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Wed, 6 Mar 2024 14:14:23 +0800 Subject: [PATCH 06/48] Make invalid identifier length error anonymous --- gen/identifier.tmpl | 4 +--- identifier.gen.go | 4 +--- identifier_account.gen.go | 4 +--- identifier_anchor.gen.go | 4 +--- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/gen/identifier.tmpl b/gen/identifier.tmpl index b2e90fc36..b81d62e6c 100644 --- a/gen/identifier.tmpl +++ b/gen/identifier.tmpl @@ -19,8 +19,6 @@ const ( var ( Empty{{.Name}} = {{.Name}}{} - - ErrInvalid{{.Name}}Length = ierrors.New("invalid {{.Name}} length") ) // {{.Name}} is a 32 byte hash value. @@ -58,7 +56,7 @@ func Must{{.Name}}FromHexString(hex string) {{.Name}} { func {{.Name}}FromBytes(bytes []byte) ({{.Name}}, int, error) { var {{.Receiver}} {{.Name}} if len(bytes) < {{.Name}}Length { - return {{.Receiver}}, 0, ErrInvalid{{.Name}}Length + return {{.Receiver}}, 0, ierrors.Errorf("invalid {{.Name}} length: expected %d bytes, got %d bytes", {{.Name}}Length, len(bytes)) } copy({{.Receiver}}[:], bytes) diff --git a/identifier.gen.go b/identifier.gen.go index f4fab5769..81d4cc874 100644 --- a/identifier.gen.go +++ b/identifier.gen.go @@ -19,8 +19,6 @@ const ( var ( EmptyIdentifier = Identifier{} - - ErrInvalidIdentifierLength = ierrors.New("invalid Identifier length") ) // Identifier is a 32 byte hash value. @@ -58,7 +56,7 @@ func MustIdentifierFromHexString(hex string) Identifier { func IdentifierFromBytes(bytes []byte) (Identifier, int, error) { var i Identifier if len(bytes) < IdentifierLength { - return i, 0, ErrInvalidIdentifierLength + return i, 0, ierrors.Errorf("invalid Identifier length: expected %d bytes, got %d bytes", IdentifierLength, len(bytes)) } copy(i[:], bytes) diff --git a/identifier_account.gen.go b/identifier_account.gen.go index 282098a2a..aee5aad2d 100644 --- a/identifier_account.gen.go +++ b/identifier_account.gen.go @@ -19,8 +19,6 @@ const ( var ( EmptyAccountID = AccountID{} - - ErrInvalidAccountIDLength = ierrors.New("invalid AccountID length") ) // AccountID is a 32 byte hash value. @@ -58,7 +56,7 @@ func MustAccountIDFromHexString(hex string) AccountID { func AccountIDFromBytes(bytes []byte) (AccountID, int, error) { var a AccountID if len(bytes) < AccountIDLength { - return a, 0, ErrInvalidAccountIDLength + return a, 0, ierrors.Errorf("invalid AccountID length: expected %d bytes, got %d bytes", AccountIDLength, len(bytes)) } copy(a[:], bytes) diff --git a/identifier_anchor.gen.go b/identifier_anchor.gen.go index 971529fce..f950fe91e 100644 --- a/identifier_anchor.gen.go +++ b/identifier_anchor.gen.go @@ -19,8 +19,6 @@ const ( var ( EmptyAnchorID = AnchorID{} - - ErrInvalidAnchorIDLength = ierrors.New("invalid AnchorID length") ) // AnchorID is a 32 byte hash value. @@ -58,7 +56,7 @@ func MustAnchorIDFromHexString(hex string) AnchorID { func AnchorIDFromBytes(bytes []byte) (AnchorID, int, error) { var a AnchorID if len(bytes) < AnchorIDLength { - return a, 0, ErrInvalidAnchorIDLength + return a, 0, ierrors.Errorf("invalid AnchorID length: expected %d bytes, got %d bytes", AnchorIDLength, len(bytes)) } copy(a[:], bytes) From b42dce194a9a8824ca1cd8f9daf04984d1ce59d8 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Wed, 6 Mar 2024 14:27:57 +0800 Subject: [PATCH 07/48] Make invalid slot identifier len error anonymous --- block_id.gen.go | 4 +--- commitment_id.gen.go | 4 +--- gen/slot_identifier.tmpl | 8 +++----- output_id.gen.go | 4 +--- signed_transaction_id.gen.go | 4 +--- transaction_id.gen.go | 4 +--- 6 files changed, 8 insertions(+), 20 deletions(-) diff --git a/block_id.gen.go b/block_id.gen.go index d8354e732..65cf92779 100644 --- a/block_id.gen.go +++ b/block_id.gen.go @@ -21,8 +21,6 @@ const ( ) var ( - ErrInvalidBlockIDLength = ierrors.New("invalid blockID length") - EmptyBlockID = BlockID{} ) @@ -57,7 +55,7 @@ func BlockIDFromHexString(hex string) (BlockID, error) { // BlockIDFromBytes returns a new BlockID represented by the passed bytes. func BlockIDFromBytes(b []byte) (BlockID, int, error) { if len(b) < BlockIDLength { - return EmptyBlockID, 0, ErrInvalidBlockIDLength + return EmptyBlockID, 0, ierrors.Errorf("invalid blockID length: expected %d bytes, got %d bytes", BlockIDLength, len(b)) } return BlockID(b), BlockIDLength, nil diff --git a/commitment_id.gen.go b/commitment_id.gen.go index 9151663e7..926cfc417 100644 --- a/commitment_id.gen.go +++ b/commitment_id.gen.go @@ -21,8 +21,6 @@ const ( ) var ( - ErrInvalidCommitmentIDLength = ierrors.New("invalid commitmentID length") - EmptyCommitmentID = CommitmentID{} ) @@ -57,7 +55,7 @@ func CommitmentIDFromHexString(hex string) (CommitmentID, error) { // CommitmentIDFromBytes returns a new CommitmentID represented by the passed bytes. func CommitmentIDFromBytes(b []byte) (CommitmentID, int, error) { if len(b) < CommitmentIDLength { - return EmptyCommitmentID, 0, ErrInvalidCommitmentIDLength + return EmptyCommitmentID, 0, ierrors.Errorf("invalid commitmentID length: expected %d bytes, got %d bytes", CommitmentIDLength, len(b)) } return CommitmentID(b), CommitmentIDLength, nil diff --git a/gen/slot_identifier.tmpl b/gen/slot_identifier.tmpl index 33af7fb88..ee0f80e95 100644 --- a/gen/slot_identifier.tmpl +++ b/gen/slot_identifier.tmpl @@ -21,18 +21,16 @@ import ( const ( {{if index .Features "output"}} - // OutputIndexLength defines the length of an OutputIndex. + // OutputIndexLength defines the length of an OutputIndex. OutputIndexLength = serializer.UInt16ByteSize // OutputIDLength defines the length of an OutputID. OutputIDLength = TransactionIDLength + OutputIndexLength {{else}} - {{.Name}}Length = IdentifierLength + SlotIndexLength + {{.Name}}Length = IdentifierLength + SlotIndexLength {{end}} ) var ( - ErrInvalid{{.Name}}Length = ierrors.New("invalid {{firstLower .Name}} length") - Empty{{.Name}} = {{.Name}}{} ) @@ -73,7 +71,7 @@ func {{.Name}}FromHexString(hex string) ({{.Name}}, error) { // {{.Name}}FromBytes returns a new {{.Name}} represented by the passed bytes. func {{.Name}}FromBytes(b []byte) ({{.Name}}, int, error) { if len(b) < {{.Name}}Length { - return Empty{{.Name}}, 0, ErrInvalid{{.Name}}Length + return Empty{{.Name}}, 0, ierrors.Errorf("invalid {{firstLower .Name}} length: expected %d bytes, got %d bytes", {{.Name}}Length, len(b)) } return {{.Name}}(b), {{.Name}}Length, nil diff --git a/output_id.gen.go b/output_id.gen.go index 8026cf86e..ba4e8c599 100644 --- a/output_id.gen.go +++ b/output_id.gen.go @@ -24,8 +24,6 @@ const ( ) var ( - ErrInvalidOutputIDLength = ierrors.New("invalid outputID length") - EmptyOutputID = OutputID{} ) @@ -47,7 +45,7 @@ func OutputIDFromHexString(hex string) (OutputID, error) { // OutputIDFromBytes returns a new OutputID represented by the passed bytes. func OutputIDFromBytes(b []byte) (OutputID, int, error) { if len(b) < OutputIDLength { - return EmptyOutputID, 0, ErrInvalidOutputIDLength + return EmptyOutputID, 0, ierrors.Errorf("invalid outputID length: expected %d bytes, got %d bytes", OutputIDLength, len(b)) } return OutputID(b), OutputIDLength, nil diff --git a/signed_transaction_id.gen.go b/signed_transaction_id.gen.go index fd7fcbbb0..58bbd9e34 100644 --- a/signed_transaction_id.gen.go +++ b/signed_transaction_id.gen.go @@ -21,8 +21,6 @@ const ( ) var ( - ErrInvalidSignedTransactionIDLength = ierrors.New("invalid signedTransactionID length") - EmptySignedTransactionID = SignedTransactionID{} ) @@ -57,7 +55,7 @@ func SignedTransactionIDFromHexString(hex string) (SignedTransactionID, error) { // SignedTransactionIDFromBytes returns a new SignedTransactionID represented by the passed bytes. func SignedTransactionIDFromBytes(b []byte) (SignedTransactionID, int, error) { if len(b) < SignedTransactionIDLength { - return EmptySignedTransactionID, 0, ErrInvalidSignedTransactionIDLength + return EmptySignedTransactionID, 0, ierrors.Errorf("invalid signedTransactionID length: expected %d bytes, got %d bytes", SignedTransactionIDLength, len(b)) } return SignedTransactionID(b), SignedTransactionIDLength, nil diff --git a/transaction_id.gen.go b/transaction_id.gen.go index 9f79a80d4..620c0631d 100644 --- a/transaction_id.gen.go +++ b/transaction_id.gen.go @@ -21,8 +21,6 @@ const ( ) var ( - ErrInvalidTransactionIDLength = ierrors.New("invalid transactionID length") - EmptyTransactionID = TransactionID{} ) @@ -57,7 +55,7 @@ func TransactionIDFromHexString(hex string) (TransactionID, error) { // TransactionIDFromBytes returns a new TransactionID represented by the passed bytes. func TransactionIDFromBytes(b []byte) (TransactionID, int, error) { if len(b) < TransactionIDLength { - return EmptyTransactionID, 0, ErrInvalidTransactionIDLength + return EmptyTransactionID, 0, ierrors.Errorf("invalid transactionID length: expected %d bytes, got %d bytes", TransactionIDLength, len(b)) } return TransactionID(b), TransactionIDLength, nil From 8bfe46e5883bd4654bda75b4829229e74171e5db Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Wed, 6 Mar 2024 14:58:50 +0800 Subject: [PATCH 08/48] Add IsValid* functions for identifiers --- block_id.gen.go | 13 +++++++++++-- commitment_id.gen.go | 13 +++++++++++-- gen/identifier.tmpl | 13 +++++++++++-- gen/slot_identifier.tmpl | 13 +++++++++++-- identifier.gen.go | 13 +++++++++++-- identifier_account.gen.go | 13 +++++++++++-- identifier_anchor.gen.go | 13 +++++++++++-- nodeclient/http_api_client_test.go | 4 ++-- output_id.gen.go | 13 +++++++++++-- signed_transaction_id.gen.go | 13 +++++++++++-- transaction_id.gen.go | 13 +++++++++++-- 11 files changed, 112 insertions(+), 22 deletions(-) diff --git a/block_id.gen.go b/block_id.gen.go index 65cf92779..ffa16bfca 100644 --- a/block_id.gen.go +++ b/block_id.gen.go @@ -52,10 +52,19 @@ func BlockIDFromHexString(hex string) (BlockID, error) { return s, err } +// IsValidBlockID returns an error if the passed bytes are not a valid BlockID, otherwise nil. +func IsValidBlockID(b []byte) error { + if len(b) != BlockIDLength { + return ierrors.Errorf("invalid blockID length: expected %d bytes, got %d bytes", BlockIDLength, len(b)) + } + + return nil +} + // BlockIDFromBytes returns a new BlockID represented by the passed bytes. func BlockIDFromBytes(b []byte) (BlockID, int, error) { - if len(b) < BlockIDLength { - return EmptyBlockID, 0, ierrors.Errorf("invalid blockID length: expected %d bytes, got %d bytes", BlockIDLength, len(b)) + if err := IsValidBlockID(b); err != nil { + return EmptyBlockID, 0, err } return BlockID(b), BlockIDLength, nil diff --git a/commitment_id.gen.go b/commitment_id.gen.go index 926cfc417..3f3cf9ea9 100644 --- a/commitment_id.gen.go +++ b/commitment_id.gen.go @@ -52,10 +52,19 @@ func CommitmentIDFromHexString(hex string) (CommitmentID, error) { return s, err } +// IsValidCommitmentID returns an error if the passed bytes are not a valid CommitmentID, otherwise nil. +func IsValidCommitmentID(b []byte) error { + if len(b) != CommitmentIDLength { + return ierrors.Errorf("invalid commitmentID length: expected %d bytes, got %d bytes", CommitmentIDLength, len(b)) + } + + return nil +} + // CommitmentIDFromBytes returns a new CommitmentID represented by the passed bytes. func CommitmentIDFromBytes(b []byte) (CommitmentID, int, error) { - if len(b) < CommitmentIDLength { - return EmptyCommitmentID, 0, ierrors.Errorf("invalid commitmentID length: expected %d bytes, got %d bytes", CommitmentIDLength, len(b)) + if err := IsValidCommitmentID(b); err != nil { + return EmptyCommitmentID, 0, err } return CommitmentID(b), CommitmentIDLength, nil diff --git a/gen/identifier.tmpl b/gen/identifier.tmpl index b81d62e6c..f53a76ebe 100644 --- a/gen/identifier.tmpl +++ b/gen/identifier.tmpl @@ -53,10 +53,19 @@ func Must{{.Name}}FromHexString(hex string) {{.Name}} { return {{.Receiver}} } +// IsValid{{.Name}} returns an error if the passed bytes are not a valid {{.Name}}, otherwise nil. +func IsValid{{.Name}}(b []byte) error { + if len(b) != {{.Name}}Length { + return ierrors.Errorf("invalid {{.Name}} length: expected %d bytes, got %d bytes", {{.Name}}Length, len(b)) + } + + return nil +} + func {{.Name}}FromBytes(bytes []byte) ({{.Name}}, int, error) { var {{.Receiver}} {{.Name}} - if len(bytes) < {{.Name}}Length { - return {{.Receiver}}, 0, ierrors.Errorf("invalid {{.Name}} length: expected %d bytes, got %d bytes", {{.Name}}Length, len(bytes)) + if err := IsValid{{.Name}}(bytes); err != nil { + return {{.Receiver}}, 0, err } copy({{.Receiver}}[:], bytes) diff --git a/gen/slot_identifier.tmpl b/gen/slot_identifier.tmpl index ee0f80e95..2a210a48f 100644 --- a/gen/slot_identifier.tmpl +++ b/gen/slot_identifier.tmpl @@ -68,10 +68,19 @@ func {{.Name}}FromHexString(hex string) ({{.Name}}, error) { return s, err } +// IsValid{{.Name}} returns an error if the passed bytes are not a valid {{.Name}}, otherwise nil. +func IsValid{{.Name}}(b []byte) error { + if len(b) != {{.Name}}Length { + return ierrors.Errorf("invalid {{firstLower .Name}} length: expected %d bytes, got %d bytes", {{.Name}}Length, len(b)) + } + + return nil +} + // {{.Name}}FromBytes returns a new {{.Name}} represented by the passed bytes. func {{.Name}}FromBytes(b []byte) ({{.Name}}, int, error) { - if len(b) < {{.Name}}Length { - return Empty{{.Name}}, 0, ierrors.Errorf("invalid {{firstLower .Name}} length: expected %d bytes, got %d bytes", {{.Name}}Length, len(b)) + if err := IsValid{{.Name}}(b); err != nil { + return Empty{{.Name}}, 0, err } return {{.Name}}(b), {{.Name}}Length, nil diff --git a/identifier.gen.go b/identifier.gen.go index 81d4cc874..8c59f4490 100644 --- a/identifier.gen.go +++ b/identifier.gen.go @@ -53,10 +53,19 @@ func MustIdentifierFromHexString(hex string) Identifier { return i } +// IsValidIdentifier returns an error if the passed bytes are not a valid Identifier, otherwise nil. +func IsValidIdentifier(b []byte) error { + if len(b) != IdentifierLength { + return ierrors.Errorf("invalid Identifier length: expected %d bytes, got %d bytes", IdentifierLength, len(b)) + } + + return nil +} + func IdentifierFromBytes(bytes []byte) (Identifier, int, error) { var i Identifier - if len(bytes) < IdentifierLength { - return i, 0, ierrors.Errorf("invalid Identifier length: expected %d bytes, got %d bytes", IdentifierLength, len(bytes)) + if err := IsValidIdentifier(bytes); err != nil { + return i, 0, err } copy(i[:], bytes) diff --git a/identifier_account.gen.go b/identifier_account.gen.go index aee5aad2d..96dbd6ee1 100644 --- a/identifier_account.gen.go +++ b/identifier_account.gen.go @@ -53,10 +53,19 @@ func MustAccountIDFromHexString(hex string) AccountID { return a } +// IsValidAccountID returns an error if the passed bytes are not a valid AccountID, otherwise nil. +func IsValidAccountID(b []byte) error { + if len(b) != AccountIDLength { + return ierrors.Errorf("invalid AccountID length: expected %d bytes, got %d bytes", AccountIDLength, len(b)) + } + + return nil +} + func AccountIDFromBytes(bytes []byte) (AccountID, int, error) { var a AccountID - if len(bytes) < AccountIDLength { - return a, 0, ierrors.Errorf("invalid AccountID length: expected %d bytes, got %d bytes", AccountIDLength, len(bytes)) + if err := IsValidAccountID(bytes); err != nil { + return a, 0, err } copy(a[:], bytes) diff --git a/identifier_anchor.gen.go b/identifier_anchor.gen.go index f950fe91e..6bbc53596 100644 --- a/identifier_anchor.gen.go +++ b/identifier_anchor.gen.go @@ -53,10 +53,19 @@ func MustAnchorIDFromHexString(hex string) AnchorID { return a } +// IsValidAnchorID returns an error if the passed bytes are not a valid AnchorID, otherwise nil. +func IsValidAnchorID(b []byte) error { + if len(b) != AnchorIDLength { + return ierrors.Errorf("invalid AnchorID length: expected %d bytes, got %d bytes", AnchorIDLength, len(b)) + } + + return nil +} + func AnchorIDFromBytes(bytes []byte) (AnchorID, int, error) { var a AnchorID - if len(bytes) < AnchorIDLength { - return a, 0, ierrors.Errorf("invalid AnchorID length: expected %d bytes, got %d bytes", AnchorIDLength, len(bytes)) + if err := IsValidAnchorID(bytes); err != nil { + return a, 0, err } copy(a[:], bytes) diff --git a/nodeclient/http_api_client_test.go b/nodeclient/http_api_client_test.go index 0c5251f33..83d7fa8f7 100644 --- a/nodeclient/http_api_client_test.go +++ b/nodeclient/http_api_client_test.go @@ -183,7 +183,7 @@ func TestClient_Health(t *testing.T) { func TestClient_BlockIssuance(t *testing.T) { defer gock.Off() - parentsHex := []string{"0x733ed2810f2333e9d6cd702c7d5c8264cd9f1ae454b61e75cf702c451f68611d0000000000000000", "0x5e4a89c549456dbec74ce3a21bde719e9cd84e655f3b1c5a09058d0fbf9417fe0000000000000000"} + parentsHex := []string{"0x733ed2810f2333e9d6cd702c7d5c8264cd9f1ae454b61e75cf702c451f68611d00000000", "0x5e4a89c549456dbec74ce3a21bde719e9cd84e655f3b1c5a09058d0fbf9417fe00000000"} parents, err := iotago.BlockIDsFromHexString(parentsHex) require.NoError(t, err) @@ -195,7 +195,7 @@ func TestClient_BlockIssuance(t *testing.T) { LatestFinalizedSlot: iotago.SlotIndex(20), } - prevID, err := iotago.CommitmentIDFromHexString(hexutil.EncodeHex(tpkg.RandBytes(40))) + prevID, err := iotago.CommitmentIDFromHexString(hexutil.EncodeHex(tpkg.RandBytes(36))) require.NoError(t, err) rootsID, err := iotago.IdentifierFromHexString(hexutil.EncodeHex(tpkg.RandBytes(32))) require.NoError(t, err) diff --git a/output_id.gen.go b/output_id.gen.go index ba4e8c599..00ff615b1 100644 --- a/output_id.gen.go +++ b/output_id.gen.go @@ -42,10 +42,19 @@ func OutputIDFromHexString(hex string) (OutputID, error) { return s, err } +// IsValidOutputID returns an error if the passed bytes are not a valid OutputID, otherwise nil. +func IsValidOutputID(b []byte) error { + if len(b) != OutputIDLength { + return ierrors.Errorf("invalid outputID length: expected %d bytes, got %d bytes", OutputIDLength, len(b)) + } + + return nil +} + // OutputIDFromBytes returns a new OutputID represented by the passed bytes. func OutputIDFromBytes(b []byte) (OutputID, int, error) { - if len(b) < OutputIDLength { - return EmptyOutputID, 0, ierrors.Errorf("invalid outputID length: expected %d bytes, got %d bytes", OutputIDLength, len(b)) + if err := IsValidOutputID(b); err != nil { + return EmptyOutputID, 0, err } return OutputID(b), OutputIDLength, nil diff --git a/signed_transaction_id.gen.go b/signed_transaction_id.gen.go index 58bbd9e34..d3678ac04 100644 --- a/signed_transaction_id.gen.go +++ b/signed_transaction_id.gen.go @@ -52,10 +52,19 @@ func SignedTransactionIDFromHexString(hex string) (SignedTransactionID, error) { return s, err } +// IsValidSignedTransactionID returns an error if the passed bytes are not a valid SignedTransactionID, otherwise nil. +func IsValidSignedTransactionID(b []byte) error { + if len(b) != SignedTransactionIDLength { + return ierrors.Errorf("invalid signedTransactionID length: expected %d bytes, got %d bytes", SignedTransactionIDLength, len(b)) + } + + return nil +} + // SignedTransactionIDFromBytes returns a new SignedTransactionID represented by the passed bytes. func SignedTransactionIDFromBytes(b []byte) (SignedTransactionID, int, error) { - if len(b) < SignedTransactionIDLength { - return EmptySignedTransactionID, 0, ierrors.Errorf("invalid signedTransactionID length: expected %d bytes, got %d bytes", SignedTransactionIDLength, len(b)) + if err := IsValidSignedTransactionID(b); err != nil { + return EmptySignedTransactionID, 0, err } return SignedTransactionID(b), SignedTransactionIDLength, nil diff --git a/transaction_id.gen.go b/transaction_id.gen.go index 620c0631d..239b0bf2b 100644 --- a/transaction_id.gen.go +++ b/transaction_id.gen.go @@ -52,10 +52,19 @@ func TransactionIDFromHexString(hex string) (TransactionID, error) { return s, err } +// IsValidTransactionID returns an error if the passed bytes are not a valid TransactionID, otherwise nil. +func IsValidTransactionID(b []byte) error { + if len(b) != TransactionIDLength { + return ierrors.Errorf("invalid transactionID length: expected %d bytes, got %d bytes", TransactionIDLength, len(b)) + } + + return nil +} + // TransactionIDFromBytes returns a new TransactionID represented by the passed bytes. func TransactionIDFromBytes(b []byte) (TransactionID, int, error) { - if len(b) < TransactionIDLength { - return EmptyTransactionID, 0, ierrors.Errorf("invalid transactionID length: expected %d bytes, got %d bytes", TransactionIDLength, len(b)) + if err := IsValidTransactionID(b); err != nil { + return EmptyTransactionID, 0, err } return TransactionID(b), TransactionIDLength, nil From 3685d8d251ffd9d9574ae77300333eb5a7a56a0a Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Thu, 7 Mar 2024 15:38:09 +0800 Subject: [PATCH 09/48] Remove ErrUnknownAddrType and panic instead --- address.go | 6 ++---- address_signer.go | 13 ++++++++----- api_common.go | 13 +++++++++++-- tpkg/rand_address.go | 4 ++-- wallet/keymanager.go | 3 ++- 5 files changed, 25 insertions(+), 14 deletions(-) diff --git a/address.go b/address.go index 1f4d056cf..ccdb6ef67 100644 --- a/address.go +++ b/address.go @@ -14,8 +14,6 @@ import ( ) var ( - // ErrUnknownAddrType gets returned for unknown address types. - ErrUnknownAddrType = ierrors.New("unknown address type") // ErrInvalidAddressType gets returned when an address type is invalid. ErrInvalidAddressType = ierrors.New("invalid address type") // ErrInvalidRestrictedAddress gets returned when a RestrictedAddress is invalid. @@ -192,7 +190,7 @@ func newAddress(addressType AddressType) (address Address, err error) { case AddressRestricted: return &RestrictedAddress{}, nil default: - return nil, ierrors.Wrapf(ErrUnknownAddrType, "type %d", addressType) + panic(fmt.Sprintf("unknown address type %d", addressType)) } } @@ -312,6 +310,6 @@ func AddressFromReader(reader io.ReadSeeker) (Address, error) { return RestrictedAddressFromReader(reader) default: - return nil, ierrors.Wrapf(ErrUnknownAddrType, "type %d", addressType) + panic(fmt.Sprintf("unknown address type %d", addressType)) } } diff --git a/address_signer.go b/address_signer.go index 2d6b1338b..c124993be 100644 --- a/address_signer.go +++ b/address_signer.go @@ -3,6 +3,7 @@ package iotago import ( "crypto" "crypto/ed25519" + "fmt" "github.com/iotaledger/hive.go/ierrors" ) @@ -56,8 +57,10 @@ func NewAddressKeysForRestrictedEd25519Address(addr *RestrictedAddress, prvKey e switch addr.Address.(type) { case *Ed25519Address: return AddressKeys{Address: addr, Keys: prvKey}, nil + case *ImplicitAccountCreationAddress: + panic("ImplicitAccountCreationAddress is not allowed in restricted addresses") default: - return AddressKeys{}, ierrors.Wrapf(ErrUnknownAddrType, "unknown underlying address type %T in restricted address", addr) + panic(fmt.Sprintf("address type %T is not supported in the address signer since it only handles addresses backed by keypairs", addr)) } } @@ -123,14 +126,14 @@ func (s *InMemoryAddressSigner) privateKeyForAddress(addr Address) (crypto.Priva case *Ed25519Address: return privateKeyForEd25519Address(underlyingAddr) default: - return nil, ierrors.Wrapf(ErrUnknownAddrType, "unknown underlying address type %T in restricted address", addr) + panic(fmt.Sprintf("underlying address type %T in restricted address is not supported in the the address signer since it only handles addresses backed by keypairs", addr)) } case *ImplicitAccountCreationAddress: return privateKeyForEd25519Address(address) default: - return nil, ierrors.Wrapf(ErrUnknownAddrType, "type %T", addr) + panic(fmt.Sprintf("address type %T is not supported in the address signer since it only handles addresses backed by keypairs", addr)) } } @@ -183,12 +186,12 @@ func (s *InMemoryAddressSigner) EmptySignatureForAddress(addr Address) (signatur case *Ed25519Address: return &Ed25519Signature{}, nil default: - return nil, ierrors.Wrapf(ErrUnknownAddrType, "unknown underlying address type %T in restricted address", addr) + panic(fmt.Sprintf("underlying address type %T in restricted address is not supported in the address signer since it only handles addresses backed by keypairs", addr)) } case *ImplicitAccountCreationAddress: return &Ed25519Signature{}, nil default: - return nil, ierrors.Wrapf(ErrUnknownAddrType, "type %T", addr) + panic(fmt.Sprintf("address type %T is not supported in the address signer since it only handles addresses backed by keypairs", addr)) } } diff --git a/api_common.go b/api_common.go index a31591916..bacd12121 100644 --- a/api_common.go +++ b/api_common.go @@ -2,6 +2,7 @@ package iotago import ( "context" + "fmt" "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/hive.go/serializer/v2/serix" @@ -41,7 +42,11 @@ var ( case *RestrictedAddress: return ierrors.Wrapf(ErrInvalidNestedAddressType, "address with index %d is a restricted address inside a multi address", idx) default: - return ierrors.Wrapf(ErrUnknownAddrType, "address with index %d has an unknown address type (%T) inside a multi address", idx, addr) + // We're switching on the Go address type here, so we can only run into the default case + // if we added a new address type and have not handled it above or a user passed a type + // implementing the address interface (only possible when iota.go is used as a library). + // In both cases we want to panic. + panic(fmt.Sprintf("address with index %d has an unknown address type (%T) inside a multi address", idx, addr)) } // check for minimum address weight @@ -80,7 +85,11 @@ var ( case *RestrictedAddress: return ierrors.Wrap(ErrInvalidNestedAddressType, "underlying address is a restricted address inside a restricted address") default: - return ierrors.Wrapf(ErrUnknownAddrType, "underlying address has an unknown address type (%T) inside a restricted address", addr) + // We're switching on the Go address type here, so we can only run into the default case + // if we added a new address type and have not handled it above or a user passed a type + // implementing the address interface (only possible when iota.go is used as a library). + // In both cases we want to panic. + panic(fmt.Sprintf("underlying address of the restricted address is of unknown address type (%T)", addr)) } return nil diff --git a/tpkg/rand_address.go b/tpkg/rand_address.go index 782b5976b..40874600f 100644 --- a/tpkg/rand_address.go +++ b/tpkg/rand_address.go @@ -2,9 +2,9 @@ package tpkg import ( "bytes" + "fmt" "slices" - "github.com/iotaledger/hive.go/ierrors" iotago "github.com/iotaledger/iota.go/v4" ) @@ -141,6 +141,6 @@ func RandAddress(addressType ...iotago.AddressType) iotago.Address { case iotago.AddressAnchor: return RandAnchorAddress() default: - panic(ierrors.Wrapf(iotago.ErrUnknownAddrType, "type %d", addrType)) + panic(fmt.Sprintf("unknown address type %d", addrType)) } } diff --git a/wallet/keymanager.go b/wallet/keymanager.go index 9e50d36b8..b4fc84b33 100644 --- a/wallet/keymanager.go +++ b/wallet/keymanager.go @@ -2,6 +2,7 @@ package wallet import ( "crypto/ed25519" + "fmt" "github.com/iotaledger/iota-crypto-demo/pkg/bip32path" "github.com/iotaledger/iota-crypto-demo/pkg/bip39" @@ -135,6 +136,6 @@ func (k *KeyManager) Address(addressType iotago.AddressType, index ...uint32) io case iotago.AddressImplicitAccountCreation: return iotago.ImplicitAccountCreationAddressFromPubKey(pubKey) default: - panic(ierrors.Wrapf(iotago.ErrUnknownAddrType, "type %d", addressType)) + panic(fmt.Sprintf("address type %s is not supported", addressType)) } } From 19a0bd5bb60f91da681daa84550cf6278ecf7575 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Thu, 7 Mar 2024 16:40:37 +0800 Subject: [PATCH 10/48] Replace Errorf with idiomatic Wrap --- address.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/address.go b/address.go index ccdb6ef67..7f2f74150 100644 --- a/address.go +++ b/address.go @@ -207,7 +207,7 @@ func bech32StringBytes(hrp NetworkPrefix, bytes []byte) string { func ParseBech32(s string) (NetworkPrefix, Address, error) { hrp, addrData, err := bech32.Decode(s) if err != nil { - return "", nil, ierrors.Errorf("invalid bech32 encoding: %w", err) + return "", nil, ierrors.Wrap(err, "invalid bech32 encoding") } if len(addrData) == 0 { @@ -223,7 +223,7 @@ func ParseBech32(s string) (NetworkPrefix, Address, error) { case AddressMulti: multiAddrRef, _, err := MultiAddressReferenceFromBytes(addrData) if err != nil { - return "", nil, ierrors.Errorf("invalid multi address: %w", err) + return "", nil, ierrors.Wrap(err, "invalid multi address") } return NetworkPrefix(hrp), multiAddrRef, nil @@ -236,13 +236,13 @@ func ParseBech32(s string) (NetworkPrefix, Address, error) { if underlyingAddrType == AddressMulti { multiAddrRef, consumed, err := MultiAddressReferenceFromBytes(addrData[1:]) if err != nil { - return "", nil, ierrors.Errorf("invalid multi address: %w", err) + return "", nil, ierrors.Wrap(err, "invalid multi address") } // get the address capabilities from the remaining bytes capabilities, _, err := AddressCapabilitiesBitMaskFromBytes(addrData[1+consumed:]) if err != nil { - return "", nil, ierrors.Errorf("invalid address capabilities: %w", err) + return "", nil, ierrors.Wrap(err, "invalid restricted address capabilities") } return NetworkPrefix(hrp), &RestrictedAddress{ From 14e4902a8b9595dbcac17d76bdfe3855592ef88f Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Thu, 7 Mar 2024 16:48:30 +0800 Subject: [PATCH 11/48] Use WithMessagef over Wrap in api common --- allotment.go | 4 ++-- api_common.go | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/allotment.go b/allotment.go index e80d7cb6a..c5e5f3621 100644 --- a/allotment.go +++ b/allotment.go @@ -80,11 +80,11 @@ func allotmentMaxManaValidator(maxManaValue Mana) ElementValidationFunc[*Allotme var err error sum, err = safemath.SafeAdd(sum, next.Mana) if err != nil { - return ierrors.Errorf("%w: %w: allotment mana sum calculation failed at allotment %d", ErrMaxManaExceeded, err, index) + return ierrors.Join(ErrMaxManaExceeded, ierrors.Wrapf(err, "allotment mana sum calculation failed at allotment %d", index)) } if sum > maxManaValue { - return ierrors.Wrapf(ErrMaxManaExceeded, "sum of allotted mana exceeds max value with allotment %d", index) + return ierrors.WithMessagef(ErrMaxManaExceeded, "sum of allotted mana exceeds max value with allotment %d", index) } return nil diff --git a/api_common.go b/api_common.go index bacd12121..00cdd9a6a 100644 --- a/api_common.go +++ b/api_common.go @@ -36,11 +36,11 @@ var ( case *NFTAddress: case *AnchorAddress: case *ImplicitAccountCreationAddress: - return ierrors.Wrapf(ErrInvalidNestedAddressType, "address with index %d is an implicit account creation address inside a multi address", idx) + return ierrors.WithMessagef(ErrInvalidNestedAddressType, "address with index %d is an implicit account creation address inside a multi address", idx) case *MultiAddress: - return ierrors.Wrapf(ErrInvalidNestedAddressType, "address with index %d is a multi address inside a multi address", idx) + return ierrors.WithMessagef(ErrInvalidNestedAddressType, "address with index %d is a multi address inside a multi address", idx) case *RestrictedAddress: - return ierrors.Wrapf(ErrInvalidNestedAddressType, "address with index %d is a restricted address inside a multi address", idx) + return ierrors.WithMessagef(ErrInvalidNestedAddressType, "address with index %d is a restricted address inside a multi address", idx) default: // We're switching on the Go address type here, so we can only run into the default case // if we added a new address type and have not handled it above or a user passed a type @@ -51,7 +51,7 @@ var ( // check for minimum address weight if address.Weight == 0 { - return ierrors.Wrapf(ErrMultiAddressWeightInvalid, "address with index %d needs to have at least weight=1", idx) + return ierrors.WithMessagef(ErrMultiAddressWeightInvalid, "address with index %d needs to have at least weight=1", idx) } cumulativeWeight += uint16(address.Weight) @@ -59,10 +59,10 @@ var ( // check for valid threshold if addr.Threshold > cumulativeWeight { - return ierrors.Wrapf(ErrMultiAddressThresholdInvalid, "the threshold value exceeds the cumulative weight of all addresses (%d>%d)", addr.Threshold, cumulativeWeight) + return ierrors.WithMessagef(ErrMultiAddressThresholdInvalid, "the threshold value exceeds the cumulative weight of all addresses (%d>%d)", addr.Threshold, cumulativeWeight) } if addr.Threshold < 1 { - return ierrors.Wrap(ErrMultiAddressThresholdInvalid, "multi addresses need to have at least threshold=1") + return ierrors.WithMessage(ErrMultiAddressThresholdInvalid, "multi addresses need to have at least threshold=1") } return nil @@ -74,16 +74,16 @@ var ( // 3. The bitmask does not contain trailing zero bytes. restrictedAddressValidatorFunc = func(_ context.Context, addr RestrictedAddress) error { if err := BitMaskNonTrailingZeroBytesValidatorFunc(addr.AllowedCapabilities); err != nil { - return ierrors.Wrapf(ErrInvalidRestrictedAddress, "invalid allowed capabilities bitmask: %w", err) + return ierrors.WithMessagef(ErrInvalidRestrictedAddress, "invalid allowed capabilities bitmask: %w", err) } switch addr.Address.(type) { case *Ed25519Address, *AccountAddress, *NFTAddress, *AnchorAddress, *MultiAddress: // allowed address types case *ImplicitAccountCreationAddress: - return ierrors.Wrap(ErrInvalidNestedAddressType, "underlying address is an implicit account creation address inside a restricted address") + return ierrors.WithMessage(ErrInvalidNestedAddressType, "underlying address is an implicit account creation address inside a restricted address") case *RestrictedAddress: - return ierrors.Wrap(ErrInvalidNestedAddressType, "underlying address is a restricted address inside a restricted address") + return ierrors.WithMessage(ErrInvalidNestedAddressType, "underlying address is a restricted address inside a restricted address") default: // We're switching on the Go address type here, so we can only run into the default case // if we added a new address type and have not handled it above or a user passed a type From fe3cbfc44c07e79e5ffca6791e0ccea78fb88075 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Fri, 8 Mar 2024 11:31:49 +0800 Subject: [PATCH 12/48] Use constant over magic number --- api.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api.go b/api.go index f3ad51f02..bcf461e99 100644 --- a/api.go +++ b/api.go @@ -19,11 +19,11 @@ func (v Version) Bytes() ([]byte, error) { } func VersionFromBytes(b []byte) (Version, int, error) { - if len(b) < 1 { + if len(b) < VersionLength { return 0, 0, ierrors.New("invalid version bytes length") } - return Version(b[0]), 1, nil + return Version(b[0]), VersionLength, nil } // VersionSignalingParameters defines the parameters used by signaling protocol parameters upgrade. From 2608125f01bef757c93b2dbb77aafc6f2f89dfe3 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Fri, 8 Mar 2024 11:54:15 +0800 Subject: [PATCH 13/48] Use Wrap over custom wrapping in attestation --- attestation.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/attestation.go b/attestation.go index 1b679df4f..761fc20fc 100644 --- a/attestation.go +++ b/attestation.go @@ -74,12 +74,12 @@ func (a *Attestation) Compare(other *Attestation) int { func (a *Attestation) BlockID() (BlockID, error) { signatureBytes, err := a.API.Encode(a.Signature) if err != nil { - return EmptyBlockID, ierrors.Errorf("failed to create blockID: %w", err) + return EmptyBlockID, ierrors.Wrap(err, "failed to create blockID") } headerHash, err := a.Header.Hash(a.API) if err != nil { - return EmptyBlockID, ierrors.Errorf("failed to create blockID: %w", err) + return EmptyBlockID, ierrors.Wrap(err, "failed to create blockID") } id := blockIdentifier(headerHash, a.BodyHash, signatureBytes) @@ -91,7 +91,7 @@ func (a *Attestation) BlockID() (BlockID, error) { func (a *Attestation) signingMessage() ([]byte, error) { headerHash, err := a.Header.Hash(a.API) if err != nil { - return nil, ierrors.Errorf("failed to create signing message: %w", err) + return nil, ierrors.Wrap(err, "failed to create signing message") } return blockSigningMessage(headerHash, a.BodyHash), nil From bd7e537877f603456deb21701b486ba34e0b2a6b Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Fri, 8 Mar 2024 12:08:30 +0800 Subject: [PATCH 14/48] Fix order in bech32 errors --- bech32/bech32.go | 14 +++++++------- bech32/errors.go | 12 ++++++------ bech32/internal/base32/base32.go | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/bech32/bech32.go b/bech32/bech32.go index 9dd6d5e8f..c6faca64a 100644 --- a/bech32/bech32.go +++ b/bech32/bech32.go @@ -21,15 +21,15 @@ var charset = newEncoding("qpzry9x8gf2tvdw0s3jn54khce6mua7l") func Encode(hrp string, src []byte) (string, error) { dataLen := base32.EncodedLen(len(src)) if len(hrp)+dataLen+checksumLength+1 > maxStringLength { - return "", ierrors.Wrapf(ErrInvalidLength, "String length=%d, data length=%d", len(hrp), dataLen) + return "", ierrors.WithMessagef(ErrInvalidLength, "hrp length=%d, data length=%d", len(hrp), dataLen) } // validate the human-readable part if len(hrp) < 1 { - return "", ierrors.Wrap(ErrInvalidLength, "String must not be empty") + return "", ierrors.WithMessage(ErrInvalidLength, "hrp must not be empty") } for _, c := range hrp { if !isValidHRPChar(c) { - return "", ierrors.Wrap(ErrInvalidCharacter, "not US-ASCII character in human-readable part") + return "", ierrors.WithMessage(ErrInvalidCharacter, "non US-ASCII character in human-readable part") } } if err := validateCase(hrp); err != nil { @@ -66,7 +66,7 @@ func Encode(hrp string, src []byte) (string, error) { // An SyntaxError is returned when the error can be matched to a certain position in s. func Decode(s string) (string, []byte, error) { if len(s) > maxStringLength { - return "", nil, &SyntaxError{ierrors.Wrap(ErrInvalidLength, "maximum length exceeded"), maxStringLength} + return "", nil, &SyntaxError{ierrors.WithMessage(ErrInvalidLength, "maximum length exceeded"), maxStringLength} } // validate the separator hrpLen := strings.LastIndex(s, string(separator)) @@ -74,12 +74,12 @@ func Decode(s string) (string, []byte, error) { return "", nil, ErrMissingSeparator } if hrpLen < 1 || hrpLen+checksumLength > len(s) { - return "", nil, &SyntaxError{ierrors.Wrap(ErrInvalidSeparator, "invalid position"), hrpLen} + return "", nil, &SyntaxError{ierrors.WithMessage(ErrInvalidSeparator, "invalid position"), hrpLen} } // validate characters in human-readable part for i, c := range s[:hrpLen] { if !isValidHRPChar(c) { - return "", nil, &SyntaxError{ierrors.Wrap(ErrInvalidCharacter, "not US-ASCII character in human-readable part"), i} + return "", nil, &SyntaxError{ierrors.WithMessage(ErrInvalidCharacter, "non US-ASCII character in human-readable part"), i} } } // validate that the case of the entire string is consistent @@ -95,7 +95,7 @@ func Decode(s string) (string, []byte, error) { // decode the data part data, err := charset.decode(chars) if err != nil { - return "", nil, &SyntaxError{ierrors.Wrap(ErrInvalidCharacter, "non-charset character in data part"), hrpLen + 1 + len(data)} + return "", nil, &SyntaxError{ierrors.WithMessage(ErrInvalidCharacter, "non-charset character in data part"), hrpLen + 1 + len(data)} } // validate the checksum diff --git a/bech32/errors.go b/bech32/errors.go index 30f48e8fb..f3de827af 100644 --- a/bech32/errors.go +++ b/bech32/errors.go @@ -4,12 +4,12 @@ import "github.com/iotaledger/hive.go/ierrors" // Errors reported during bech32 decoding. var ( - ErrInvalidLength = ierrors.New("invalid length") - ErrMissingSeparator = ierrors.New("missing separator '" + string(separator) + "'") - ErrInvalidSeparator = ierrors.New("separator '" + string(separator) + "' at invalid position") - ErrMixedCase = ierrors.New("mixed case") - ErrInvalidCharacter = ierrors.New("invalid character") - ErrInvalidChecksum = ierrors.New("invalid checksum") + ErrInvalidLength = ierrors.New("invalid bech32 length") + ErrMissingSeparator = ierrors.New("missing bech32 separator '" + string(separator) + "'") + ErrInvalidSeparator = ierrors.New("bech32 separator '" + string(separator) + "' at invalid position") + ErrMixedCase = ierrors.New("mixed case in bech32 string") + ErrInvalidCharacter = ierrors.New("invalid bech32 character") + ErrInvalidChecksum = ierrors.New("invalid bech32 checksum") ) // A SyntaxError is a description of a Bech32 syntax error. diff --git a/bech32/internal/base32/base32.go b/bech32/internal/base32/base32.go index 8fb69f6df..3d7c80226 100644 --- a/bech32/internal/base32/base32.go +++ b/bech32/internal/base32/base32.go @@ -63,9 +63,9 @@ func Encode(dst []uint8, src []byte) int { var ( // ErrInvalidLength reports an attempt to decode an input of invalid length. - ErrInvalidLength = ierrors.New("invalid length") + ErrInvalidLength = ierrors.New("invalid base32 length") // ErrNonZeroPadding reports an attempt to decode an input without zero padding. - ErrNonZeroPadding = ierrors.New("non-zero padding") + ErrNonZeroPadding = ierrors.New("non-zero padding in base32") ) // A CorruptInputError is a description of a base32 syntax error. From 4fd2cf77b9379e31c6497808d974294da1c5722c Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Fri, 8 Mar 2024 13:19:50 +0800 Subject: [PATCH 15/48] Improve block errors --- block.go | 51 +++++++++++++++++++++++++++------------------------ block_test.go | 2 +- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/block.go b/block.go index e36f6c4e5..e168b3010 100644 --- a/block.go +++ b/block.go @@ -39,6 +39,7 @@ var ( ErrBlockMaxSizeExceeded = ierrors.New("block exceeds the max size") ErrCommitmentInputNewerThanCommitment = ierrors.New("a block cannot contain a commitment input with index newer than the commitment index") ErrBlockNetworkIDInvalid = ierrors.New("invalid network ID in block header") + ErrHighestSupportedVersionTooSmall = ierrors.New("highest supported version must be greater equal the block's protocol version") ) // BlockBodyType denotes a type of Block Body. @@ -70,7 +71,7 @@ type BlockHeader struct { func (b *BlockHeader) Hash(api API) (Identifier, error) { headerBytes, err := api.Encode(b) if err != nil { - return Identifier{}, ierrors.Errorf("failed to serialize block header: %w", err) + return Identifier{}, ierrors.Wrap(err, "failed to serialize block header") } return blake2b.Sum256(headerBytes), nil @@ -170,7 +171,7 @@ func (b *Block) VerifySignature() (valid bool, err error) { } if edSig.PublicKey == [ed25519.PublicKeySize]byte{} { - return false, ierrors.New("empty publicKeys are invalid") + return false, ierrors.New("ed25519 public key must not be empty") } return hiveEd25519.Verify(edSig.PublicKey[:], signingMessage, edSig.Signature[:]), nil @@ -180,12 +181,12 @@ func (b *Block) VerifySignature() (valid bool, err error) { func (b *Block) ID() (BlockID, error) { data, err := b.API.Encode(b) if err != nil { - return BlockID{}, ierrors.Errorf("can't compute block ID: %w", err) + return BlockID{}, ierrors.Wrap(err, "failed to compute blockID") } id, err := BlockIdentifierFromBlockBytes(data) if err != nil { - return BlockID{}, err + return BlockID{}, ierrors.Wrap(err, "failed to compute blockID") } slot := b.API.TimeProvider().SlotFromTime(b.Header.IssuingTime) @@ -256,12 +257,12 @@ func (b *Block) Size() int { func (b *Block) ManaCost(rmc Mana) (Mana, error) { workScore, err := b.WorkScore() if err != nil { - return 0, ierrors.Errorf("failed to calculate block workscore: %w", err) + return 0, ierrors.Wrap(err, "failed to calculate block workscore") } manaCost, err := ManaCost(rmc, workScore) if err != nil { - return 0, ierrors.Errorf("failed to calculate mana cost: %w", err) + return 0, ierrors.Wrap(err, "failed to calculate block mana cost") } return manaCost, nil @@ -270,16 +271,16 @@ func (b *Block) ManaCost(rmc Mana) (Mana, error) { // syntacticallyValidate syntactically validates the Block. func (b *Block) syntacticallyValidate() error { if b.Size() > MaxBlockSize { - return ierrors.Wrapf(ErrBlockMaxSizeExceeded, "max size of a block is %d but got %d bytes", MaxBlockSize, b.Size()) + return ierrors.WithMessagef(ErrBlockMaxSizeExceeded, "max size of a block is %d but got %d bytes", MaxBlockSize, b.Size()) } if b.API.ProtocolParameters().Version() != b.Header.ProtocolVersion { - return ierrors.Wrapf(ErrInvalidBlockVersion, "mismatched protocol version: wanted %d, got %d in block", b.API.ProtocolParameters().Version(), b.Header.ProtocolVersion) + return ierrors.WithMessagef(ErrInvalidBlockVersion, "mismatched protocol version: expected %d, got %d in block", b.API.ProtocolParameters().Version(), b.Header.ProtocolVersion) } expectedNetworkID := b.API.ProtocolParameters().NetworkID() if b.Header.NetworkID != expectedNetworkID { - return ierrors.Wrapf(ErrBlockNetworkIDInvalid, "got %v, want %v (%s)", b.Header.NetworkID, expectedNetworkID, b.API.ProtocolParameters().NetworkName()) + return ierrors.WithMessagef(ErrBlockNetworkIDInvalid, "expected %d (%s), got %d", expectedNetworkID, b.API.ProtocolParameters().NetworkName(), b.Header.NetworkID) } block := b.Body @@ -296,26 +297,27 @@ func (b *Block) syntacticallyValidate() error { } } + blockID, err := b.ID() + if err != nil { + return ierrors.Wrap(err, "failed to syntactically validate block") + } + protocolParams := b.API.ProtocolParameters() genesisSlot := protocolParams.GenesisSlot() minCommittableAge := protocolParams.MinCommittableAge() maxCommittableAge := protocolParams.MaxCommittableAge() commitmentSlot := b.Header.SlotCommitmentID.Slot() - blockID, err := b.ID() - if err != nil { - return ierrors.Wrapf(err, "failed to syntactically validate block") - } blockSlot := blockID.Slot() // check that commitment is not too recent. if commitmentSlot > genesisSlot && // Don't filter commitments to genesis based on being too recent. blockSlot < commitmentSlot+minCommittableAge { - return ierrors.Wrapf(ErrCommitmentTooRecent, "block at slot %d committing to slot %d", blockSlot, b.Header.SlotCommitmentID.Slot()) + return ierrors.WithMessagef(ErrCommitmentTooRecent, "block at slot %d committing to slot %d, min committable age %d", blockSlot, b.Header.SlotCommitmentID.Slot(), minCommittableAge) } // Check that commitment is not too old. if blockSlot > commitmentSlot+maxCommittableAge { - return ierrors.Wrapf(ErrCommitmentTooOld, "block at slot %d committing to slot %d, max committable age %d", blockSlot, b.Header.SlotCommitmentID.Slot(), maxCommittableAge) + return ierrors.WithMessagef(ErrCommitmentTooOld, "block at slot %d committing to slot %d, max committable age %d", blockSlot, b.Header.SlotCommitmentID.Slot(), maxCommittableAge) } return b.Body.syntacticallyValidate(b) @@ -374,7 +376,7 @@ func (b *BasicBlockBody) ShallowLikeParentIDs() BlockIDs { func (b *BasicBlockBody) Hash() (Identifier, error) { blockBytes, err := b.API.Encode(b) if err != nil { - return Identifier{}, ierrors.Errorf("failed to serialize basic block: %w", err) + return Identifier{}, ierrors.Wrap(err, "failed to serialize basic block") } return blake2b.Sum256(blockBytes), nil @@ -408,18 +410,19 @@ func (b *BasicBlockBody) syntacticallyValidate(block *Block) error { if b.Payload != nil && b.Payload.PayloadType() == PayloadSignedTransaction { blockID, err := block.ID() if err != nil { - return ierrors.Wrap(err, "error while calculating block ID during syntactical validation") + return ierrors.Wrap(err, "failed to calculate basic block ID during syntactical validation") } blockSlot := blockID.Slot() minCommittableAge := block.API.ProtocolParameters().MinCommittableAge() maxCommittableAge := block.API.ProtocolParameters().MaxCommittableAge() - signedTransaction, _ := b.Payload.(*SignedTransaction) + //nolint:forcetypeassert // we can safely assume that this is a SignedTransaction + signedTransaction := b.Payload.(*SignedTransaction) // check that transaction CreationSlot is smaller or equal than the block that contains it if blockSlot < signedTransaction.Transaction.CreationSlot { - return ierrors.Wrapf(ErrTransactionCreationSlotTooRecent, "block at slot %d with commitment input to slot %d", blockSlot, signedTransaction.Transaction.CreationSlot) + return ierrors.WithMessagef(ErrTransactionCreationSlotTooRecent, "transaction creation slot %d exceeds block slot %d", signedTransaction.Transaction.CreationSlot, blockSlot) } if cInput := signedTransaction.Transaction.CommitmentInput(); cInput != nil { @@ -427,15 +430,15 @@ func (b *BasicBlockBody) syntacticallyValidate(block *Block) error { // check that commitment input is not too recent. if cInputSlot > 0 && // Don't filter commitments to genesis based on being too recent. blockSlot < cInputSlot+minCommittableAge { // filter commitments to future slots. - return ierrors.Wrapf(ErrCommitmentInputTooRecent, "block at slot %d with commitment input to slot %d", blockSlot, cInput.CommitmentID.Slot()) + return ierrors.WithMessagef(ErrCommitmentInputTooRecent, "block at slot %d with commitment input to slot %d, min committable age %d", blockSlot, cInput.CommitmentID.Slot(), minCommittableAge) } // Check that commitment input is not too old. if blockSlot > cInputSlot+maxCommittableAge { - return ierrors.Wrapf(ErrCommitmentInputTooOld, "block at slot %d committing to slot %d, max committable age %d", blockSlot, cInput.CommitmentID.Slot(), maxCommittableAge) + return ierrors.WithMessagef(ErrCommitmentInputTooOld, "block at slot %d with commitment input to slot %d, max committable age %d", blockSlot, cInput.CommitmentID.Slot(), maxCommittableAge) } if cInputSlot > block.Header.SlotCommitmentID.Slot() { - return ierrors.Wrapf(ErrCommitmentInputNewerThanCommitment, "transaction in a block contains CommitmentInput to slot %d while max allowed is %d", cInput.CommitmentID.Slot(), block.Header.SlotCommitmentID.Slot()) + return ierrors.WithMessagef(ErrCommitmentInputNewerThanCommitment, "transaction in a block contains CommitmentInput to slot %d while max allowed is %d", cInput.CommitmentID.Slot(), block.Header.SlotCommitmentID.Slot()) } } } @@ -479,7 +482,7 @@ func (b *ValidationBlockBody) ShallowLikeParentIDs() BlockIDs { func (b *ValidationBlockBody) Hash() (Identifier, error) { blockBytes, err := b.API.Encode(b) if err != nil { - return Identifier{}, ierrors.Errorf("failed to serialize validation block: %w", err) + return Identifier{}, ierrors.Wrap(err, "failed to serialize validation block") } return IdentifierFromData(blockBytes), nil @@ -502,7 +505,7 @@ func (b *ValidationBlockBody) Size() int { // syntacticallyValidate syntactically validates the ValidationBlock. func (b *ValidationBlockBody) syntacticallyValidate(block *Block) error { if b.HighestSupportedVersion < block.Header.ProtocolVersion { - return ierrors.Errorf("highest supported version %d must be greater equal protocol version %d", b.HighestSupportedVersion, block.Header.ProtocolVersion) + return ierrors.WithMessagef(ErrHighestSupportedVersionTooSmall, "highest supported version %d, block header protocol version %d", b.HighestSupportedVersion, block.Header.ProtocolVersion) } return nil diff --git a/block_test.go b/block_test.go index 0a4748490..2ff6670f2 100644 --- a/block_test.go +++ b/block_test.go @@ -621,7 +621,7 @@ func TestValidationBlock_HighestSupportedVersion(t *testing.T) { block2 := &iotago.Block{} _, err = tpkg.ZeroCostTestAPI.Decode(blockBytes, block2, serix.WithValidation()) - require.ErrorContains(t, err, "highest supported version") + require.ErrorIs(t, err, iotago.ErrHighestSupportedVersionTooSmall) } // Valid HighestSupportedVersion. From 993e539859b8e7ad69ebe2a130ba95c189c1bb64 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Fri, 8 Mar 2024 13:29:32 +0800 Subject: [PATCH 16/48] Improve block issuer key errors --- block_issuer_key.go | 2 +- block_issuer_key_ed25519_pubkeyhash.go | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/block_issuer_key.go b/block_issuer_key.go index 2c6152c76..7f6a39ec2 100644 --- a/block_issuer_key.go +++ b/block_issuer_key.go @@ -142,7 +142,7 @@ func BlockIssuerKeysFromReader(reader io.ReadSeeker) (BlockIssuerKeys, error) { if err := stream.ReadCollection(reader, serializer.SeriLengthPrefixTypeAsByte, func(i int) error { blockIssuerKey, err := BlockIssuerKeyFromReader(reader) if err != nil { - return ierrors.Wrapf(err, "unable to read block issuer key %d", i) + return ierrors.Wrapf(err, "unable to read block issuer key at index %d", i) } b = append(b, blockIssuerKey) diff --git a/block_issuer_key_ed25519_pubkeyhash.go b/block_issuer_key_ed25519_pubkeyhash.go index b3018cfb1..2559e1e58 100644 --- a/block_issuer_key_ed25519_pubkeyhash.go +++ b/block_issuer_key_ed25519_pubkeyhash.go @@ -8,6 +8,7 @@ import ( "golang.org/x/crypto/blake2b" hiveEd25519 "github.com/iotaledger/hive.go/crypto/ed25519" + "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/hive.go/serializer/v2" ) @@ -49,7 +50,7 @@ func Ed25519PublicKeyHashBlockIssuerKeyFromBytes(bytes []byte) (*Ed25519PublicKe blockIssuerKey := &Ed25519PublicKeyHashBlockIssuerKey{} n, err := CommonSerixAPI().Decode(context.TODO(), bytes, blockIssuerKey) if err != nil { - return nil, 0, err + return nil, 0, ierrors.Wrap(err, "failed to deserialize Ed25519PublicKeyHashBlockIssuerKey") } return blockIssuerKey, n, nil From 657d659108d2ea121564ed9eae5916b05c90480b Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Fri, 8 Mar 2024 14:11:57 +0800 Subject: [PATCH 17/48] Improve builder errors --- address_signer.go | 8 ++++---- builder/block_builder.go | 6 +++--- builder/transaction_builder.go | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/address_signer.go b/address_signer.go index c124993be..46a493898 100644 --- a/address_signer.go +++ b/address_signer.go @@ -111,7 +111,7 @@ func (s *InMemoryAddressSigner) privateKeyForAddress(addr Address) (crypto.Priva prvKey, ok := maybePrvKey.(ed25519.PrivateKey) if !ok { - return nil, ierrors.Wrapf(ErrAddressKeysWrongType, "Ed25519 address needs to have a %T private key mapped but got %T", ed25519.PrivateKey{}, maybePrvKey) + return nil, ierrors.WithMessagef(ErrAddressKeysWrongType, "Ed25519 address needs to have a %T private key mapped but got %T", ed25519.PrivateKey{}, maybePrvKey) } return prvKey, nil @@ -147,7 +147,7 @@ func (s *InMemoryAddressSigner) SignerUIDForAddress(addr Address) (Identifier, e ed25519PrvKey, ok := prvKey.(ed25519.PrivateKey) if !ok { - return EmptyIdentifier, ierrors.Wrapf(ErrAddressKeysWrongType, "Ed25519 address needs to have a %T private key mapped but got %T", ed25519.PrivateKey{}, prvKey) + return EmptyIdentifier, ierrors.WithMessagef(ErrAddressKeysWrongType, "Ed25519 address needs to have a %T private key mapped but got %T", ed25519.PrivateKey{}, prvKey) } // the UID is the blake2b 256 hash of the public key @@ -158,12 +158,12 @@ func (s *InMemoryAddressSigner) SignerUIDForAddress(addr Address) (Identifier, e func (s *InMemoryAddressSigner) Sign(addr Address, msg []byte) (signature Signature, err error) { prvKey, err := s.privateKeyForAddress(addr) if err != nil { - return nil, ierrors.Errorf("can't sign message for address: %w", err) + return nil, ierrors.Wrap(err, "can't sign message for address") } ed25519PrvKey, ok := prvKey.(ed25519.PrivateKey) if !ok { - return nil, ierrors.Wrapf(ErrAddressKeysWrongType, "Ed25519 address needs to have a %T private key mapped but got %T", ed25519.PrivateKey{}, prvKey) + return nil, ierrors.WithMessagef(ErrAddressKeysWrongType, "Ed25519 address needs to have a %T private key mapped but got %T", ed25519.PrivateKey{}, prvKey) } ed25519Sig := &Ed25519Signature{} diff --git a/builder/block_builder.go b/builder/block_builder.go index b2697ed08..87543e7fd 100644 --- a/builder/block_builder.go +++ b/builder/block_builder.go @@ -121,7 +121,7 @@ func (b *BasicBlockBuilder) SignWithSigner(accountID iotago.AccountID, signer io signature, err := b.protocolBlock.Sign(signer, addr) if err != nil { - b.err = ierrors.Errorf("error signing block: %w", err) + b.err = ierrors.Wrap(err, "failed to sign basic block") return b } @@ -199,7 +199,7 @@ func (b *BasicBlockBuilder) CalculateAndSetMaxBurnedMana(rmc iotago.Mana) *Basic burnedMana, err := b.protocolBlock.ManaCost(rmc) if err != nil { - b.err = ierrors.Wrap(err, "error calculating mana cost") + b.err = ierrors.Wrap(err, "failed to calculate mana cost") return b } @@ -320,7 +320,7 @@ func (v *ValidationBlockBuilder) SignWithSigner(accountID iotago.AccountID, sign signature, err := v.protocolBlock.Sign(signer, addr) if err != nil { - v.err = ierrors.Errorf("error signing block: %w", err) + v.err = ierrors.Wrap(err, "failed to sign validation block") return v } diff --git a/builder/transaction_builder.go b/builder/transaction_builder.go index 7ec6d32b5..8cd152951 100644 --- a/builder/transaction_builder.go +++ b/builder/transaction_builder.go @@ -252,7 +252,7 @@ func (b *TransactionBuilder) getStoredManaOutputAccountID(storedManaOutputIndex } default: - return iotago.EmptyAccountID, ierrors.Wrapf(iotago.ErrUnknownOutputType, "output type %T does not support stored mana", output) + return iotago.EmptyAccountID, ierrors.WithMessagef(iotago.ErrUnknownOutputType, "output type %T does not support stored mana", output) } return storedManaOutputAccountID, nil @@ -677,7 +677,7 @@ func (b *TransactionBuilder) build(signEssence bool) (*iotago.SignedTransaction, case b.occurredBuildErr != nil: return nil, b.occurredBuildErr case b.signer == nil: - return nil, ierrors.Wrap(ErrTransactionBuilder, "must supply signer") + return nil, ierrors.WithMessage(ErrTransactionBuilder, "must supply signer") } b.transaction.Allotments.Sort() @@ -762,7 +762,7 @@ func (b *TransactionBuilder) build(signEssence bool) (*iotago.SignedTransaction, signature, err = b.signer.EmptySignatureForAddress(owner) } if err != nil { - return nil, ierrors.Wrapf(err, "failed to sign tx transaction: %s", txEssenceData) + return nil, ierrors.Wrapf(err, "failed to sign transaction") } // add the new signature to the unlocks From d0350ff9778a6382d24fff1724bb1a730d909145 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Fri, 8 Mar 2024 14:17:51 +0800 Subject: [PATCH 18/48] Remove unused chain output code --- chain.go | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/chain.go b/chain.go index f5ecdf076..2d1d6a382 100644 --- a/chain.go +++ b/chain.go @@ -1,7 +1,5 @@ package iotago -import "github.com/iotaledger/hive.go/ierrors" - // ChainOutput is a type of Output which represents a chain of state transitions. type ChainOutput interface { Output @@ -28,36 +26,5 @@ const ( ChainTransitionTypeDestroy ) -// ChainOutputs is a slice of ChainOutput. -type ChainOutputs []ChainOutput - // ChainOutputSet is a map of ChainID to ChainOutput. type ChainOutputSet map[ChainID]ChainOutput - -// Includes checks whether all chains included in other exist in this set. -func (set ChainOutputSet) Includes(other ChainOutputSet) error { - for chainID := range other { - if _, has := set[chainID]; !has { - return ierrors.Wrapf(ErrChainMissing, "%s missing in source", chainID) - } - } - - return nil -} - -// Merge merges other with this set in a new set. -// Returns an error if a chain isn't unique across both sets. -func (set ChainOutputSet) Merge(other ChainOutputSet) (ChainOutputSet, error) { - newSet := make(ChainOutputSet) - for k, v := range set { - newSet[k] = v - } - for k, v := range other { - if _, has := newSet[k]; has { - return nil, ierrors.Wrapf(ErrNonUniqueChainOutputs, "chain %s exists in both sets", k) - } - newSet[k] = v - } - - return newSet, nil -} From 429a43a38acd1e2a0e7240826704245af0f6e00f Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Fri, 8 Mar 2024 14:24:52 +0800 Subject: [PATCH 19/48] Improve errors in feats --- commitment.go | 2 +- feat.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/commitment.go b/commitment.go index 1cc97db64..d7bbe3123 100644 --- a/commitment.go +++ b/commitment.go @@ -39,7 +39,7 @@ func NewEmptyCommitment(api API) *Commitment { func (c *Commitment) ID() (CommitmentID, error) { data, err := CommonSerixAPI().Encode(context.TODO(), c) if err != nil { - return CommitmentID{}, ierrors.Errorf("failed to serialize commitment: %w", err) + return CommitmentID{}, ierrors.Wrap(err, "failed to serialize commitment") } return CommitmentIDRepresentingData(c.Slot, data), nil diff --git a/feat.go b/feat.go index cd896c5ca..d62e4012a 100644 --- a/feat.go +++ b/feat.go @@ -325,9 +325,9 @@ func FeatureUnchanged(featType FeatureType, inFeatSet FeatureSet, outFeatSet Fea switch { case outHas && !inHas: - return ierrors.Wrapf(ErrInvalidFeatureTransition, "%s in next state but not in previous", featType) + return ierrors.WithMessagef(ErrInvalidFeatureTransition, "%s in next state but not in previous", featType) case !outHas && inHas: - return ierrors.Wrapf(ErrInvalidFeatureTransition, "%s in current state but not in next", featType) + return ierrors.WithMessagef(ErrInvalidFeatureTransition, "%s in current state but not in next", featType) } // not in both sets @@ -336,7 +336,7 @@ func FeatureUnchanged(featType FeatureType, inFeatSet FeatureSet, outFeatSet Fea } if !in.Equal(out) { - return ierrors.Wrapf(ErrInvalidFeatureTransition, "%s changed, in %v / out %v", featType, in, out) + return ierrors.WithMessagef(ErrInvalidFeatureTransition, "%s changed, in %v / out %v", featType, in, out) } return nil From d38c3a92a16d2ae9fcf876568d824ed3dc582b2c Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Fri, 8 Mar 2024 14:42:03 +0800 Subject: [PATCH 20/48] Replace Unknown Context Input error with panic --- input_context.go | 15 +++++---------- transaction.go | 8 ++++---- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/input_context.go b/input_context.go index 36801835a..876ed8bbe 100644 --- a/input_context.go +++ b/input_context.go @@ -21,11 +21,6 @@ const ( ContextInputReward ) -var ( - // ErrUnknownContextInputType gets returned for unknown context input types. - ErrUnknownContextInputType = ierrors.New("unknown context input type") -) - func (inputType ContextInputType) String() string { if int(inputType) >= len(contextInputNames) { return fmt.Sprintf("unknown input type: %d", inputType) @@ -102,11 +97,11 @@ func ContextInputsRewardInputMaxIndex(inputsCount uint16) ElementValidationFunc[ case *RewardInput: utxoIndex := castInput.Index if utxoIndex >= inputsCount { - return ierrors.Wrapf(ErrInputRewardIndexExceedsMaxInputsCount, "reward input %d references index %d which is equal or greater than the inputs count %d", + return ierrors.WithMessagef(ErrInputRewardIndexExceedsMaxInputsCount, "reward input %d references index %d which is equal or greater than the inputs count %d", index, utxoIndex, inputsCount) } default: - return ierrors.Wrapf(ErrUnknownContextInputType, "context input %d, tx can only contain CommitmentInputs, BlockIssuanceCreditInputs or RewardInputs", index) + panic("all known context input types should be handled above") } return nil @@ -126,14 +121,14 @@ func ContextInputsCommitmentInputRequirement() ElementValidationFunc[ContextInpu seenCommitmentInput = true case *BlockIssuanceCreditInput: if !seenCommitmentInput { - return ierrors.Wrapf(ErrCommitmentInputMissing, "block issuance credit input at index %d requires a commitment input", index) + return ierrors.WithMessagef(ErrCommitmentInputMissing, "block issuance credit input at index %d requires a commitment input", index) } case *RewardInput: if !seenCommitmentInput { - return ierrors.Wrapf(ErrCommitmentInputMissing, "reward input at index %d requires a commitment input", index) + return ierrors.WithMessagef(ErrCommitmentInputMissing, "reward input at index %d requires a commitment input", index) } default: - return ierrors.Wrapf(ErrUnknownContextInputType, "context input %d, tx can only contain CommitmentInputs, BlockIssuanceCreditInputs or RewardInputs", index) + panic("all known context input types should be handled above") } return nil diff --git a/transaction.go b/transaction.go index cbc7bbe11..ff9522749 100644 --- a/transaction.go +++ b/transaction.go @@ -168,7 +168,7 @@ func (t *Transaction) ContextInputs() (TransactionContextInputs, error) { case *CommitmentInput, *BlockIssuanceCreditInput, *RewardInput: references = append(references, castInput) default: - return nil, ErrUnknownContextInputType + panic("all known context input types should be handled above") } } @@ -184,7 +184,7 @@ func (t *Transaction) BICInputs() ([]*BlockIssuanceCreditInput, error) { case *CommitmentInput, *RewardInput: // ignore this type default: - return nil, ErrUnknownContextInputType + panic("all known context input types should be handled above") } } @@ -200,7 +200,7 @@ func (t *Transaction) RewardInputs() ([]*RewardInput, error) { case *CommitmentInput, *BlockIssuanceCreditInput: // ignore this type default: - return nil, ErrUnknownContextInputType + panic("all known context input types should be handled above") } } @@ -216,7 +216,7 @@ func (t *Transaction) CommitmentInput() *CommitmentInput { case *CommitmentInput: return castInput default: - return nil + panic("all known context input types should be handled above") } } From 7276ff0ae8fccf6a7e617f3417639e67d33ec652 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Fri, 8 Mar 2024 16:15:07 +0800 Subject: [PATCH 21/48] Replace Unknown Input Type error with panic --- error.go | 2 -- input.go | 8 ++++---- transaction.go | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/error.go b/error.go index ff8b11651..fbe419c77 100644 --- a/error.go +++ b/error.go @@ -30,8 +30,6 @@ var ( var ( // ErrUTXOInputInvalid gets returned when the UTXO input is invalid. ErrUTXOInputInvalid = ierrors.New("UTXO input is invalid") - // ErrUnknownInputType gets returned for unknown input types. - ErrUnknownInputType = ierrors.New("unknown input type") // ErrUnknownOutputType gets returned for unknown output types. ErrUnknownOutputType = ierrors.New("unknown output type") // ErrCommitmentInputMissing gets returned when the commitment has not been provided when needed. diff --git a/input.go b/input.go index 1cefc66d1..6a76f4563 100644 --- a/input.go +++ b/input.go @@ -90,11 +90,11 @@ func InputsSyntacticalUnique() ElementValidationFunc[Input] { referencedOutputID := castInput.OutputID() k := string(referencedOutputID[:]) if j, has := utxoSet[k]; has { - return ierrors.Wrapf(ErrInputUTXORefsNotUnique, "input %d and %d share the same referenced UTXO index", j, index) + return ierrors.WithMessagef(ErrInputUTXORefsNotUnique, "input %d and %d share the same referenced UTXO index", j, index) } utxoSet[k] = index default: - return ierrors.Wrapf(ErrUnknownInputType, "input %d, tx can only contain UTXO inputs", index) + panic("all known input types should be handled above") } return nil @@ -108,10 +108,10 @@ func InputsSyntacticalIndicesWithinBounds() ElementValidationFunc[Input] { case *UTXOInput: // TODO: do we really want to check the max value on the input side? if castInput.Index() < RefUTXOIndexMin || castInput.Index() > RefUTXOIndexMax { - return ierrors.Wrapf(ErrRefUTXOIndexInvalid, "input %d", index) + return ierrors.WithMessagef(ErrRefUTXOIndexInvalid, "input %d", index) } default: - return ierrors.Wrapf(ErrUnknownInputType, "input %d, tx can only contain UTXInput inputs", index) + panic("all known input types should be handled above") } return nil diff --git a/transaction.go b/transaction.go index ff9522749..b7fe46547 100644 --- a/transaction.go +++ b/transaction.go @@ -140,7 +140,7 @@ func (t *Transaction) Inputs() ([]*UTXOInput, error) { case *UTXOInput: references = append(references, castInput) default: - return nil, ErrUnknownInputType + panic("all known input types should be handled above") } } From e43ba291c85b17420a0d5ebef8f141a86dbb92ba Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Fri, 8 Mar 2024 16:36:45 +0800 Subject: [PATCH 22/48] Fix error wrapping in nodeclient --- nodeclient/http.go | 12 ++++++------ nodeclient/http_api_client.go | 4 ++-- nodeclient/indexer_client.go | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/nodeclient/http.go b/nodeclient/http.go index dd3ee66a5..c14a880c2 100644 --- a/nodeclient/http.go +++ b/nodeclient/http.go @@ -47,7 +47,7 @@ const ( func readBody(res *http.Response) ([]byte, error) { resBody, err := io.ReadAll(res.Body) if err != nil { - return nil, ierrors.Errorf("unable to read response body: %w", err) + return nil, ierrors.Wrap(err, "unable to read response body") } return resBody, nil @@ -84,7 +84,7 @@ func interpretBody(ctx context.Context, serixAPI *serix.API, res *http.Response, errRes := &HTTPErrorResponseEnvelope{} if len(resBody) > 0 { if err := json.Unmarshal(resBody, errRes); err != nil { - return ierrors.Errorf("unable to read error from response body: %w", err) + return ierrors.Wrap(err, "unable to read error from response body") } } @@ -93,7 +93,7 @@ func interpretBody(ctx context.Context, serixAPI *serix.API, res *http.Response, err = ErrHTTPUnknownError } - return ierrors.Wrapf(err, "url %s, error message: %s", res.Request.URL.String(), errRes.Error.Message) + return ierrors.WithMessagef(err, "url %s, error message: %s", res.Request.URL.String(), errRes.Error.Message) } func do( @@ -119,7 +119,7 @@ func do( if rawData, ok := reqObj.(*RawDataEnvelope); !ok { data, err = serixAPI.JSONEncode(ctx, reqObj) if err != nil { - return nil, ierrors.Errorf("unable to serialize request object to JSON: %w", err) + return nil, ierrors.Wrap(err, "unable to serialize request object to JSON") } } else { data = rawData.Data @@ -142,7 +142,7 @@ func do( return bytes.NewReader(data) }()) if err != nil { - return nil, ierrors.Errorf("unable to build http request: %w", err) + return nil, ierrors.Wrap(err, "unable to build http request") } if userInfo != nil { @@ -180,7 +180,7 @@ func encodeURLWithQueryParams(endpoint string, queryParams url.Values) (string, if len(queryParams) > 0 { base, err := url.Parse(endpoint) if err != nil { - return "", ierrors.Errorf("failed to parse endpoint: %w", err) + return "", ierrors.Wrap(err, "failed to parse endpoint") } // encode the query params diff --git a/nodeclient/http_api_client.go b/nodeclient/http_api_client.go index b3c97d060..1cb2c68c4 100644 --- a/nodeclient/http_api_client.go +++ b/nodeclient/http_api_client.go @@ -392,7 +392,7 @@ func (client *Client) OutputByID(ctx context.Context, outputID iotago.OutputID) } if derivedOutputID != outputID { - return nil, ierrors.Errorf("output ID mismatch. Expected %s, got %s", outputID.ToHex(), derivedOutputID.ToHex()) + return nil, ierrors.Errorf("requested output ID %s does not match computed output ID %s", outputID.ToHex(), derivedOutputID.ToHex()) } return outputResponse.Output, nil @@ -432,7 +432,7 @@ func (client *Client) OutputWithMetadataByID(ctx context.Context, outputID iotag } if derivedOutputID != outputID { - return nil, nil, ierrors.Errorf("output ID mismatch. Expected %s, got %s", outputID.ToHex(), derivedOutputID.ToHex()) + return nil, nil, ierrors.Errorf("requested output ID %s does not match computed output ID %s", outputID.ToHex(), derivedOutputID.ToHex()) } return outputResponse.Output, outputResponse.Metadata, nil diff --git a/nodeclient/indexer_client.go b/nodeclient/indexer_client.go index e348d774e..7f86996dc 100644 --- a/nodeclient/indexer_client.go +++ b/nodeclient/indexer_client.go @@ -86,7 +86,7 @@ func (resultSet *IndexerResultSet) Outputs(ctx context.Context) (iotago.Outputs[ for i, outputID := range outputIDs { output, err := resultSet.client.OutputByID(ctx, outputID) if err != nil { - return nil, ierrors.Errorf("unable to fetch output %s: %w", outputID.ToHex(), err) + return nil, ierrors.Wrapf(err, "unable to fetch output %s", outputID.ToHex()) } outputs[i] = output } @@ -160,7 +160,7 @@ func (client *indexerClient) singleOutputQuery(ctx context.Context, route string } if len(res.Items) == 0 { - return nil, nil, res.CommittedSlot, ierrors.Errorf("%w for route %s", ErrIndexerNotFound, route) + return nil, nil, res.CommittedSlot, ierrors.WithMessagef(ErrIndexerNotFound, "route %s", route) } outputID := res.Items.MustOutputIDs()[0] From b35652f9ce3c094783fc7f622e607580a83983fb Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Fri, 8 Mar 2024 17:11:30 +0800 Subject: [PATCH 23/48] Fix wrap order in output files --- output.go | 16 ++++++++-------- output_anchor.go | 4 ++-- output_id_proof.go | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/output.go b/output.go index 533fc531e..a2b64ce00 100644 --- a/output.go +++ b/output.go @@ -365,7 +365,7 @@ func OutputsSyntacticalDepositAmount(protoParams ProtocolParameters, storageScor var err error sum, err = safemath.SafeAdd(sum, amount) if err != nil { - return ierrors.WithMessagef(ErrOutputsSumExceedsTotalSupply, "%w: output %d", err, index) + return ierrors.Join(ErrOutputsSumExceedsTotalSupply, ierrors.WithMessagef(err, "output %d", index)) } if sum > protoParams.TokenSupply() { return ierrors.WithMessagef(ErrOutputsSumExceedsTotalSupply, "output %d", index) @@ -422,7 +422,7 @@ func OutputsSyntacticalStoredMana(maxManaValue Mana) ElementValidationFunc[Outpu var err error sum, err = safemath.SafeAdd(sum, storedMana) if err != nil { - return ierrors.WithMessagef(ErrMaxManaExceeded, "%w: stored mana sum calculation failed at output %d", err, index) + return ierrors.Join(ierrors.Wrapf(ErrMaxManaExceeded, "stored mana sum calculation failed at output %d", index), err) } if sum > maxManaValue { @@ -694,7 +694,7 @@ func OutputsSyntacticalImplicitAccountCreationAddress() ElementValidationFunc[Ou return ierrors.WithMessagef(ErrImplicitAccountCreationAddressInInvalidOutput, "output %d", index) } default: - panic("unrecognized output type") + panic("all known output types should be handled above") } return nil @@ -743,7 +743,7 @@ func OutputsSyntacticalUnlockConditionLexicalOrderAndUniqueness() ElementValidat } } default: - panic("unrecognized output type") + panic("all known output types should be handled above") } return nil @@ -812,7 +812,7 @@ func OutputsSyntacticalFeaturesLexicalOrderAndUniqueness() ElementValidationFunc // This output does not have features. return nil default: - panic("unrecognized output type") + panic("all known output types should be handled above") } return nil @@ -900,16 +900,16 @@ func OutputsSyntacticalCommitmentInput(hasCommitmentInput bool) ElementValidatio return func(index int, output Output) error { hasStakingFeature := output.FeatureSet().Staking() != nil if hasStakingFeature && !hasCommitmentInput { - return ierrors.Wrapf(ErrStakingCommitmentInputMissing, "output %d", index) + return ierrors.WithMessagef(ErrStakingCommitmentInputMissing, "output %d", index) } hasBlockIssuerFeature := output.FeatureSet().BlockIssuer() != nil if hasBlockIssuerFeature && !hasCommitmentInput { - return ierrors.Wrapf(ErrBlockIssuerCommitmentInputMissing, "output %d", index) + return ierrors.WithMessagef(ErrBlockIssuerCommitmentInputMissing, "output %d", index) } if output.Type() == OutputDelegation && !hasCommitmentInput { - return ierrors.Wrapf(ErrDelegationCommitmentInputMissing, "output %d", index) + return ierrors.WithMessagef(ErrDelegationCommitmentInputMissing, "output %d", index) } return nil diff --git a/output_anchor.go b/output_anchor.go index 692cdc28b..773c5b233 100644 --- a/output_anchor.go +++ b/output_anchor.go @@ -136,7 +136,7 @@ func (a *AnchorOutput) Owner(nextState OwnerTransitionDependentOutput) (Address, } otherAnchorOutput, isAnchorOutput := nextState.(*AnchorOutput) if !isAnchorOutput { - return nil, ierrors.Wrapf(ErrOwnerTransitionDependentOutputNextInvalid, "expected AnchorOutput but got %s for owner computation", nextState.Type()) + return nil, ierrors.WithMessagef(ErrOwnerTransitionDependentOutputNextInvalid, "expected AnchorOutput but got %s for owner computation", nextState.Type()) } switch { case a.StateIndex == otherAnchorOutput.StateIndex: @@ -144,7 +144,7 @@ func (a *AnchorOutput) Owner(nextState OwnerTransitionDependentOutput) (Address, case a.StateIndex+1 == otherAnchorOutput.StateIndex: return a.StateController(), nil default: - return nil, ierrors.Wrap(ErrOwnerTransitionDependentOutputNextInvalid, "can not compute right owner for anchor output as state index delta is invalid") + return nil, ierrors.WithMessage(ErrOwnerTransitionDependentOutputNextInvalid, "can not compute right owner for anchor output as state index delta is invalid") } } diff --git a/output_id_proof.go b/output_id_proof.go index 70863273e..a917035dc 100644 --- a/output_id_proof.go +++ b/output_id_proof.go @@ -19,7 +19,7 @@ type OutputIDProof struct { func OutputIDProofFromTransaction(tx *Transaction, outputIndex uint16) (*OutputIDProof, error) { if tx.API == nil { - return nil, ierrors.New("API not set") + panic("API on transaction not set") } transactionCommitment, err := tx.TransactionCommitment() @@ -32,7 +32,7 @@ func OutputIDProofFromTransaction(tx *Transaction, outputIndex uint16) (*OutputI func NewOutputIDProof(api API, txCommitment Identifier, txCreationSlot SlotIndex, outputs TxEssenceOutputs, outputIndex uint16) (*OutputIDProof, error) { if int(outputIndex) >= len(outputs) { - return nil, ierrors.Errorf("index %d out of bounds len=%d", outputIndex, len(outputs)) + return nil, ierrors.Errorf("index %d out of bounds for outputs slice of len %d", outputIndex, len(outputs)) } //nolint:nosnakecase // false positive @@ -72,7 +72,7 @@ func (p *OutputIDProof) SetDeserializationContext(ctx context.Context) { func (p *OutputIDProof) OutputID(output Output) (OutputID, error) { if p.API == nil { - return EmptyOutputID, ierrors.New("API not set") + panic("API on OutputIDProof not set") } //nolint:nosnakecase // false positive @@ -85,7 +85,7 @@ func (p *OutputIDProof) OutputID(output Output) (OutputID, error) { // The proof does not contain a hash of the output if !contains { - return EmptyOutputID, ierrors.Errorf("proof does not contain the given output") + return EmptyOutputID, ierrors.New("proof does not contain the given output") } // Hash the proof to get the root From 50a06a49c0c1be19719dc0cbb8c723b398b2433a Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 11 Mar 2024 09:40:04 +0800 Subject: [PATCH 24/48] Fix error wrap order in signed transaction --- signature_ed25519.go | 4 ++-- signed_transaction.go | 12 +++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/signature_ed25519.go b/signature_ed25519.go index 9aa9a09e9..6c2e4925c 100644 --- a/signature_ed25519.go +++ b/signature_ed25519.go @@ -83,10 +83,10 @@ func (e *Ed25519Signature) Valid(msg []byte, addr *Ed25519Address) error { // an address is the Blake2b 256 hash of the public key addrFromPubKey := Ed25519AddressFromPubKey(e.PublicKey[:]) if !addr.Equal(addrFromPubKey) { - return ierrors.Wrapf(ErrEd25519PubKeyAndAddrMismatch, "address %s, address from public key %v", addr, addrFromPubKey) + return ierrors.WithMessagef(ErrEd25519PubKeyAndAddrMismatch, "address %s, address from public key %v", addr, addrFromPubKey) } if valid := hiveEd25519.Verify(e.PublicKey[:], msg, e.Signature[:]); !valid { - return ierrors.Wrapf(ErrEd25519SignatureInvalid, "address %s, public key %v, signature %v", addr, hexutil.EncodeHex(e.PublicKey[:]), hexutil.EncodeHex(e.Signature[:])) + return ierrors.WithMessagef(ErrEd25519SignatureInvalid, "address %s, public key %v, signature %v", addr, hexutil.EncodeHex(e.PublicKey[:]), hexutil.EncodeHex(e.Signature[:])) } return nil diff --git a/signed_transaction.go b/signed_transaction.go index 22dde73d3..67d4b35cd 100644 --- a/signed_transaction.go +++ b/signed_transaction.go @@ -16,6 +16,8 @@ var ( ErrManaOverflow = ierrors.New("under- or overflow in Mana calculations") // ErrUnknownSignatureType gets returned for unknown signature types. ErrUnknownSignatureType = ierrors.New("unknown signature type") + // ErrInputUnlockCountMismatch gets returned when the unlock count and input count mismatch. + ErrInputUnlockCountMismatch = ierrors.New("unlock count and input count mismatch") // ErrSignatureAndAddrIncompatible gets returned if an address of an input has a companion signature unlock with the wrong signature type. ErrSignatureAndAddrIncompatible = ierrors.New("address and signature type are not compatible") // ErrSenderFeatureNotUnlocked gets returned when an output contains a SenderFeature with an address which is not unlocked. @@ -45,12 +47,12 @@ type SignedTransaction struct { func (t *SignedTransaction) ID() (SignedTransactionID, error) { transactionBytes, err := t.API.Encode(t.Transaction) if err != nil { - return EmptySignedTransactionID, ierrors.Errorf("can't compute unlock bytes: %w", err) + return EmptySignedTransactionID, ierrors.Wrap(err, "can't compute unlock bytes") } unlocksBytes, err := t.API.Encode(t.Unlocks) if err != nil { - return EmptySignedTransactionID, ierrors.Errorf("can't compute unlock bytes: %w", err) + return EmptySignedTransactionID, ierrors.Wrap(err, "can't compute unlock bytes") } return SignedTransactionIDRepresentingData(t.Transaction.CreationSlot, byteutils.ConcatBytes(transactionBytes, unlocksBytes)), nil @@ -106,17 +108,17 @@ func (t *SignedTransaction) syntacticallyValidate() error { } if len(t.Unlocks) != len(inputs) { - return ierrors.Errorf("unlock block count must match inputs in transaction, %d vs. %d", len(t.Unlocks), len(inputs)) + return ierrors.WithMessagef(ErrInputUnlockCountMismatch, "unlock count %d does not match inputs count %d", len(t.Unlocks), len(inputs)) } if err := t.Transaction.SyntacticallyValidate(t.API); err != nil { - return ierrors.Errorf("transaction is invalid: %w", err) + return ierrors.Wrap(err, "transaction is invalid") } if err := ValidateUnlocks(t.Unlocks, SignaturesUniqueAndReferenceUnlocksValidator(t.API), ); err != nil { - return ierrors.Errorf("invalid unlocks: %w", err) + return ierrors.Wrap(err, "invalid unlocks") } return nil From b27b166508a8d973a2a90d52dde3c2f0c79feeec Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 11 Mar 2024 09:54:12 +0800 Subject: [PATCH 25/48] Fix error order in token scheme --- storagescore.go | 2 +- syntax_validator.go | 4 ++-- token_scheme_simple.go | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/storagescore.go b/storagescore.go index 8ae9fe115..5b5662bf0 100644 --- a/storagescore.go +++ b/storagescore.go @@ -146,7 +146,7 @@ func (r *StorageScoreStructure) CoversMinDeposit(object NonEphemeralObject, amou return 0, ierrors.Wrap(err, "failed to compute min deposit") } if amount < minDeposit { - return 0, ierrors.Wrapf(ErrStorageDepositNotCovered, "needed %d but only got %d", minDeposit, amount) + return 0, ierrors.WithMessagef(ErrStorageDepositNotCovered, "expected at least %d, got %d", minDeposit, amount) } return minDeposit, nil diff --git a/syntax_validator.go b/syntax_validator.go index 7d16cc1a5..5bea5b33f 100644 --- a/syntax_validator.go +++ b/syntax_validator.go @@ -21,9 +21,9 @@ func LexicalOrderAndUniquenessValidator[T constraints.Comparable[T]]() ElementVa } else { switch (*prev).Compare(next) { case 1: - return ierrors.Wrapf(ErrArrayValidationOrderViolatesLexicalOrder, "element %d should have been before element %d", index, prevIndex) + return ierrors.WithMessagef(ErrArrayValidationOrderViolatesLexicalOrder, "element %d should have been before element %d", index, prevIndex) case 0: - return ierrors.Wrapf(ErrArrayValidationViolatesUniqueness, "element %d and element %d are duplicates", index, prevIndex) + return ierrors.WithMessagef(ErrArrayValidationViolatesUniqueness, "element %d and element %d are duplicates", index, prevIndex) } prev = &next diff --git a/token_scheme_simple.go b/token_scheme_simple.go index e49647d39..e7376cd56 100644 --- a/token_scheme_simple.go +++ b/token_scheme_simple.go @@ -79,18 +79,18 @@ func (s *SimpleTokenScheme) Type() TokenSchemeType { func (s *SimpleTokenScheme) SyntacticalValidation() error { if r := s.MaximumSupply.Cmp(common.Big0); r != 1 { - return ierrors.Wrap(ErrSimpleTokenSchemeInvalidMaximumSupply, "less than equal zero") + return ierrors.WithMessage(ErrSimpleTokenSchemeInvalidMaximumSupply, "less than equal zero") } // minted - melted > 0: can never have melted more than minted mintedMeltedDelta := big.NewInt(0).Sub(s.MintedTokens, s.MeltedTokens) if r := mintedMeltedDelta.Cmp(common.Big0); r == -1 { - return ierrors.Wrapf(ErrSimpleTokenSchemeInvalidMintedMeltedTokens, "minted/melted delta less than zero: %s", mintedMeltedDelta) + return ierrors.WithMessagef(ErrSimpleTokenSchemeInvalidMintedMeltedTokens, "minted/melted delta less than zero: %s", mintedMeltedDelta) } // minted - melted <= max supply: can never have minted more than max supply if r := mintedMeltedDelta.Cmp(s.MaximumSupply); r == 1 { - return ierrors.Wrapf(ErrSimpleTokenSchemeInvalidMintedMeltedTokens, "minted/melted delta more than maximum supply: %s (delta) vs. %s (max supply)", mintedMeltedDelta, s.MaximumSupply) + return ierrors.WithMessagef(ErrSimpleTokenSchemeInvalidMintedMeltedTokens, "minted/melted delta more than maximum supply: %s (delta) vs. %s (max supply)", mintedMeltedDelta, s.MaximumSupply) } return nil @@ -126,7 +126,7 @@ func (s *SimpleTokenScheme) genesisValid(outSum *big.Int) error { func (s *SimpleTokenScheme) destructionValid(out *big.Int, in *big.Int) error { tokenDiff := big.NewInt(0).Sub(out, in) if big.NewInt(0).Add(s.MintedTokens, tokenDiff).Cmp(s.MeltedTokens) != 0 { - return ierrors.Wrapf(ErrNativeTokenSumUnbalanced, "all minted tokens must have been melted up on destruction: minted (%s) + token diff (%d) != melted tokens (%s)", s.MintedTokens, tokenDiff, s.MeltedTokens) + return ierrors.WithMessagef(ErrNativeTokenSumUnbalanced, "all minted tokens must have been melted up on destruction: minted (%s) + token diff (%d) != melted tokens (%s)", s.MintedTokens, tokenDiff, s.MeltedTokens) } return nil From 23ce6c16a95e6922ce1dc8c1f547a87cee883db0 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 11 Mar 2024 10:00:20 +0800 Subject: [PATCH 26/48] Improve error wrapping in transaction --- transaction.go | 8 ++++---- transaction_essence.go | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/transaction.go b/transaction.go index b7fe46547..3373c4658 100644 --- a/transaction.go +++ b/transaction.go @@ -26,7 +26,7 @@ var ( // ErrTxEssenceNetworkIDInvalid gets returned when a network ID within a Transaction is invalid. ErrTxEssenceNetworkIDInvalid = ierrors.New("invalid network ID") // ErrTxEssenceCapabilitiesInvalid gets returned when the capabilities within a Transaction are invalid. - ErrTxEssenceCapabilitiesInvalid = ierrors.New("invalid capabilities") + ErrTxEssenceCapabilitiesInvalid = ierrors.New("invalid transaction capabilities") // ErrInputUTXORefsNotUnique gets returned if multiple inputs reference the same UTXO. ErrInputUTXORefsNotUnique = ierrors.New("inputs must each reference a unique UTXO") // ErrInputRewardIndexExceedsMaxInputsCount gets returned if a reward input references an index greater than max inputs count. @@ -72,12 +72,12 @@ type Transaction struct { func (t *Transaction) ID() (TransactionID, error) { transactionCommitment, err := t.TransactionCommitment() if err != nil { - return EmptyTransactionID, ierrors.Errorf("can't compute transaction commitment: %w", err) + return EmptyTransactionID, ierrors.Wrap(err, "failed to compute transaction commitment") } outputCommitment, err := t.OutputCommitment() if err != nil { - return TransactionID{}, ierrors.Errorf("can't compute output commitment: %w", err) + return TransactionID{}, ierrors.Wrap(err, "failed to compute output commitment") } return TransactionIDFromTransactionCommitmentAndOutputCommitment(t.CreationSlot, transactionCommitment, outputCommitment), nil @@ -101,7 +101,7 @@ func TransactionIDFromTransactionCommitmentAndOutputCommitment(slot SlotIndex, t func (t *Transaction) TransactionCommitment() (Identifier, error) { essenceBytes, err := t.API.Encode(t.TransactionEssence) if err != nil { - return EmptyIdentifier, ierrors.Errorf("can't compute essence bytes: %w", err) + return EmptyIdentifier, ierrors.Wrap(err, "failed to serialize transaction essence") } return IdentifierFromData(essenceBytes), nil diff --git a/transaction_essence.go b/transaction_essence.go index 68b2df5b8..d8dbc8089 100644 --- a/transaction_essence.go +++ b/transaction_essence.go @@ -113,13 +113,13 @@ func (u *TransactionEssence) WorkScore(workScoreParameters *WorkScoreParameters) // and since the essence is embedded in the Transaction a similar name could easily lead to confusion. func (u *TransactionEssence) syntacticallyValidateEssence(api API) error { if err := BitMaskNonTrailingZeroBytesValidatorFunc(u.Capabilities); err != nil { - return ierrors.Wrapf(ErrTxEssenceCapabilitiesInvalid, "invalid capabilities bitmask: %w", err) + return ierrors.Join(ErrTxEssenceCapabilitiesInvalid, err) } protoParams := api.ProtocolParameters() expectedNetworkID := protoParams.NetworkID() if u.NetworkID != expectedNetworkID { - return ierrors.Wrapf(ErrTxEssenceNetworkIDInvalid, "got %v, want %v (%s)", u.NetworkID, expectedNetworkID, protoParams.NetworkName()) + return ierrors.WithMessagef(ErrTxEssenceNetworkIDInvalid, "expected %d (%s), got %d", expectedNetworkID, protoParams.NetworkName(), u.NetworkID) } err := SliceValidatorMapper(u.Inputs, From 85f83af5b40f923c1b4ffb42b0024a9d388da09b Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 11 Mar 2024 10:28:29 +0800 Subject: [PATCH 27/48] Fix wrap order in unlock --- unlock.go | 30 +++++++++++++++--------------- unlock_cond.go | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/unlock.go b/unlock.go index 630f78cf4..12538ae54 100644 --- a/unlock.go +++ b/unlock.go @@ -163,19 +163,19 @@ func SignaturesUniqueAndReferenceUnlocksValidator(api API) UnlockValidatorFunc { switch unlock := u.(type) { case *SignatureUnlock: if unlock.Signature == nil { - return ierrors.Wrapf(ErrSignatureUnlockHasNilSignature, "at index %d is nil", index) + return ierrors.WithMessagef(ErrSignatureUnlockHasNilSignature, "signature at unlock index %d is nil", index) } signerUID := unlock.Signature.SignerUID() // we check for duplicated signer UIDs in SignatureUnlock(s) if existingIndex, exists := seenSignerUIDs[signerUID]; exists { - return ierrors.Wrapf(ErrSignatureUnlockNotUnique, "signature unlock block at index %d is the same as %d", index, existingIndex) + return ierrors.WithMessagef(ErrSignatureUnlockNotUnique, "signature unlock block at index %d is the same as %d", index, existingIndex) } // we also need to check for duplicated signer UIDs in MultiUnlock(s) if existingIndex, exists := seenSignerUIDsInMultiUnlocks[signerUID]; exists { - return ierrors.Wrapf(ErrSignatureUnlockNotUnique, "signature unlock block at index %d is the same as in multi unlock at index %d", index, existingIndex) + return ierrors.WithMessagef(ErrSignatureUnlockNotUnique, "signature unlock block at index %d is the same as in multi unlock at index %d", index, existingIndex) } seenSignatureUnlocks[uint16(index)] = struct{}{} @@ -184,7 +184,7 @@ func SignaturesUniqueAndReferenceUnlocksValidator(api API) UnlockValidatorFunc { case ReferentialUnlock: if prevReferentialUnlock := seenReferentialUnlocks[unlock.ReferencedInputIndex()]; prevReferentialUnlock != nil { if !unlock.Chainable() { - return ierrors.Wrapf(ErrReferentialUnlockInvalid, "%d references existing referential unlock %d but it does not support chaining", index, unlock.ReferencedInputIndex()) + return ierrors.WithMessagef(ErrReferentialUnlockInvalid, "unlock at index %d references existing referential unlock %d but it does not support chaining", index, unlock.ReferencedInputIndex()) } seenReferentialUnlocks[uint16(index)] = unlock @@ -195,32 +195,32 @@ func SignaturesUniqueAndReferenceUnlocksValidator(api API) UnlockValidatorFunc { _, hasSignatureUnlock := seenSignatureUnlocks[unlock.ReferencedInputIndex()] _, hasMultiUnlock := seenMultiUnlocks[unlock.ReferencedInputIndex()] if !hasSignatureUnlock && !hasMultiUnlock { - return ierrors.Wrapf(ErrReferentialUnlockInvalid, "%d references non existent unlock %d", index, unlock.ReferencedInputIndex()) + return ierrors.WithMessagef(ErrReferentialUnlockInvalid, "unlock at index %d references non existent unlock %d", index, unlock.ReferencedInputIndex()) } seenReferentialUnlocks[uint16(index)] = unlock case *MultiUnlock: multiUnlockBytes, err := api.Encode(unlock) if err != nil { - return ierrors.Errorf("unable to serialize multi unlock block at index %d for dup check: %w", index, err) + return ierrors.Wrapf(err, "unable to serialize multi unlock block at index %d for dup check", index) } if existingIndex, exists := seenMultiUnlockBytes[string(multiUnlockBytes)]; exists { - return ierrors.Wrapf(ErrMultiUnlockNotUnique, "multi unlock block at index %d is the same as %d", index, existingIndex) + return ierrors.WithMessagef(ErrMultiUnlockNotUnique, "multi unlock block at index %d is the same as %d", index, existingIndex) } for subIndex, subU := range unlock.Unlocks { switch subUnlock := subU.(type) { case *SignatureUnlock: if subUnlock.Signature == nil { - return ierrors.Wrapf(ErrSignatureUnlockHasNilSignature, "at index %d.%d is nil", index, subIndex) + return ierrors.WithMessagef(ErrSignatureUnlockHasNilSignature, "unlock at index %d.%d is nil", index, subIndex) } signerUID := subUnlock.Signature.SignerUID() // we check for duplicated signer UIDs in SignatureUnlock(s) if existingIndex, exists := seenSignerUIDs[signerUID]; exists { - return ierrors.Wrapf(ErrSignatureUnlockNotUnique, "signature unlock block at index %d.%d is the same as %d", index, subIndex, existingIndex) + return ierrors.WithMessagef(ErrSignatureUnlockNotUnique, "signature unlock block at index %d.%d is the same as %d", index, subIndex, existingIndex) } // we don't set the index here in "seenSignatureUnlocks" because there is no concept of reference unlocks inside of multi unlocks @@ -231,7 +231,7 @@ func SignaturesUniqueAndReferenceUnlocksValidator(api API) UnlockValidatorFunc { case ReferentialUnlock: if prevRef := seenReferentialUnlocks[subUnlock.ReferencedInputIndex()]; prevRef != nil { if !subUnlock.Chainable() { - return ierrors.Wrapf(ErrReferentialUnlockInvalid, "%d.%d references existing referential unlock %d but it does not support chaining", index, subIndex, subUnlock.ReferencedInputIndex()) + return ierrors.WithMessagef(ErrReferentialUnlockInvalid, "%d.%d references existing referential unlock %d but it does not support chaining", index, subIndex, subUnlock.ReferencedInputIndex()) } // we don't set the index here in "seenReferentialUnlocks" because it's not allowed to reference an unlock within a multi unlock @@ -240,29 +240,29 @@ func SignaturesUniqueAndReferenceUnlocksValidator(api API) UnlockValidatorFunc { // must reference a sig unlock here // we don't check for "seenMultiUnlocks" here because we don't want to nest "reference unlocks to multi unlocks" in multi unlocks if _, has := seenSignatureUnlocks[subUnlock.ReferencedInputIndex()]; !has { - return ierrors.Wrapf(ErrReferentialUnlockInvalid, "%d.%d references non existent unlock %d", index, subIndex, subUnlock.ReferencedInputIndex()) + return ierrors.WithMessagef(ErrReferentialUnlockInvalid, "%d.%d references non existent unlock %d", index, subIndex, subUnlock.ReferencedInputIndex()) } // we don't set the index here in "seenReferentialUnlocks" because it's not allowed to reference an unlock within a multi unlock case *MultiUnlock: - return ierrors.Wrapf(ErrNestedMultiUnlock, "unlock at index %d.%d is invalid", index, subIndex) + return ierrors.WithMessagef(ErrNestedMultiUnlock, "unlock at index %d.%d is invalid", index, subIndex) case *EmptyUnlock: // empty unlocks are allowed inside of multi unlocks continue default: - return ierrors.Wrapf(ErrUnknownUnlockType, "unlock at index %d.%d is of unknown type %T", index, subIndex, subUnlock) + return ierrors.WithMessagef(ErrUnknownUnlockType, "unlock at index %d.%d is of unknown type %T", index, subIndex, subUnlock) } } seenMultiUnlocks[uint16(index)] = struct{}{} seenMultiUnlockBytes[string(multiUnlockBytes)] = index case *EmptyUnlock: - return ierrors.Wrapf(ErrEmptyUnlockOutsideMultiUnlock, "unlock at index %d is invalid", index) + return ierrors.WithMessagef(ErrEmptyUnlockOutsideMultiUnlock, "unlock at index %d is invalid", index) default: - return ierrors.Wrapf(ErrUnknownUnlockType, "unlock at index %d is of unknown type %T", index, unlock) + return ierrors.WithMessagef(ErrUnknownUnlockType, "unlock at index %d is of unknown type %T", index, unlock) } return nil diff --git a/unlock_cond.go b/unlock_cond.go index ad2e1b0b8..d0677d798 100644 --- a/unlock_cond.go +++ b/unlock_cond.go @@ -329,7 +329,7 @@ func (f UnlockConditionSet) TimelocksExpired(futureBoundedSlot SlotIndex) error } if futureBoundedSlot < timelock.Slot { - return ierrors.Wrapf(ErrTimelockNotExpired, "slot cond is %d, while tx creation slot could be up to %d", timelock.Slot, futureBoundedSlot) + return ierrors.WithMessagef(ErrTimelockNotExpired, "slot cond is %d, while tx creation slot could be up to %d", timelock.Slot, futureBoundedSlot) } return nil From 132b54f5ea2b317955c44dcf245bb9d70d48e1ca Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 11 Mar 2024 11:35:26 +0800 Subject: [PATCH 28/48] Fix error order in vm --- vm/nova/vm.go | 61 ++++++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/vm/nova/vm.go b/vm/nova/vm.go index 121442096..1f6a1a7d9 100644 --- a/vm/nova/vm.go +++ b/vm/nova/vm.go @@ -294,7 +294,7 @@ func accountGenesisValid(vmParams *vm.Params, next *iotago.AccountOutput, accoun pastBoundedSlot := vmParams.PastBoundedSlotIndex(vmParams.WorkingSet.Commitment.Slot) if nextBlockIssuerFeat.ExpirySlot < pastBoundedSlot { return ierrors.Join(iotago.ErrInvalidBlockIssuerTransition, - ierrors.WithMessagef(iotago.ErrBlockIssuerExpiryTooEarly, "(is %d, must be >= %d)", nextBlockIssuerFeat.ExpirySlot, pastBoundedSlot), + ierrors.WithMessagef(iotago.ErrBlockIssuerExpiryTooEarly, "is %d, must be >= %d", nextBlockIssuerFeat.ExpirySlot, pastBoundedSlot), ) } } @@ -361,12 +361,12 @@ func accountBlockIssuerSTVF(vmParams *vm.Params, input *vm.ChainOutputWithIDs, c // if the block issuer feature has not expired, it can not be removed. if nextBlockIssuerFeat == nil { return ierrors.Join(iotago.ErrInvalidBlockIssuerTransition, - ierrors.WithMessagef(iotago.ErrBlockIssuerNotExpired, "(current slot: %d, expiry slot: %d)", commitmentInputSlot, currentBlockIssuerFeat.ExpirySlot), + ierrors.WithMessagef(iotago.ErrBlockIssuerNotExpired, "current slot: %d, expiry slot: %d", commitmentInputSlot, currentBlockIssuerFeat.ExpirySlot), ) } if nextBlockIssuerFeat.ExpirySlot != currentBlockIssuerFeat.ExpirySlot && nextBlockIssuerFeat.ExpirySlot < pastBoundedSlot { return ierrors.Join(iotago.ErrInvalidBlockIssuerTransition, - ierrors.WithMessagef(iotago.ErrBlockIssuerExpiryTooEarly, "(is %d, must be >= %d)", nextBlockIssuerFeat.ExpirySlot, pastBoundedSlot), + ierrors.WithMessagef(iotago.ErrBlockIssuerExpiryTooEarly, "is %d, must be >= %d", nextBlockIssuerFeat.ExpirySlot, pastBoundedSlot), ) } } else if nextBlockIssuerFeat != nil { @@ -375,7 +375,7 @@ func accountBlockIssuerSTVF(vmParams *vm.Params, input *vm.ChainOutputWithIDs, c // In both cases the expiry slot must be set sufficiently far in the future. if nextBlockIssuerFeat.ExpirySlot < pastBoundedSlot { return ierrors.Join(iotago.ErrInvalidBlockIssuerTransition, - ierrors.WithMessagef(iotago.ErrBlockIssuerExpiryTooEarly, "(is %d, must be >= %d)", nextBlockIssuerFeat.ExpirySlot, pastBoundedSlot), + ierrors.WithMessagef(iotago.ErrBlockIssuerExpiryTooEarly, "is %d, must be >= %d", nextBlockIssuerFeat.ExpirySlot, pastBoundedSlot), ) } } @@ -494,7 +494,7 @@ func accountStakingGenesisValidation(vmParams *vm.Params, stakingFeat *iotago.St unbondingEpoch := pastBoundedEpoch + vmParams.API.ProtocolParameters().StakingUnbondingPeriod() if stakingFeat.EndEpoch < unbondingEpoch { - return ierrors.Wrapf(iotago.ErrStakingEndEpochTooEarly, "(i.e. end epoch %d should be >= %d)", stakingFeat.EndEpoch, unbondingEpoch) + return ierrors.Wrapf(iotago.ErrStakingEndEpochTooEarly, "end epoch %d should be >= %d", stakingFeat.EndEpoch, unbondingEpoch) } return nil @@ -508,18 +508,19 @@ func accountStakingNonExpiredValidation( earliestUnbondingEpoch iotago.EpochIndex, ) error { if nextStakingFeat == nil { - return ierrors.Wrapf(iotago.ErrInvalidStakingTransition, "%w", iotago.ErrStakingFeatureRemovedBeforeUnbonding) + return ierrors.Join(iotago.ErrInvalidStakingTransition, iotago.ErrStakingFeatureRemovedBeforeUnbonding) } if currentStakingFeat.StakedAmount != nextStakingFeat.StakedAmount || currentStakingFeat.FixedCost != nextStakingFeat.FixedCost || currentStakingFeat.StartEpoch != nextStakingFeat.StartEpoch { - return ierrors.Wrapf(iotago.ErrInvalidStakingTransition, "%w", iotago.ErrStakingFeatureModifiedBeforeUnbonding) + return ierrors.Join(iotago.ErrInvalidStakingTransition, iotago.ErrStakingFeatureModifiedBeforeUnbonding) } if currentStakingFeat.EndEpoch != nextStakingFeat.EndEpoch && nextStakingFeat.EndEpoch < earliestUnbondingEpoch { - return ierrors.Wrapf(iotago.ErrInvalidStakingTransition, "%w (i.e. end epoch %d should be >= %d) or the end epoch must match on input and output side", iotago.ErrStakingEndEpochTooEarly, nextStakingFeat.EndEpoch, earliestUnbondingEpoch) + return ierrors.Join(iotago.ErrInvalidStakingTransition, + ierrors.WithMessagef(iotago.ErrStakingEndEpochTooEarly, "end epoch %d should be >= %d or the end epoch must match on input and output side", nextStakingFeat.EndEpoch, earliestUnbondingEpoch)) } return nil @@ -538,7 +539,7 @@ func accountStakingExpiredValidation( } else if !currentStakingFeat.Equal(nextStakingFeat) { // If an expired feature is changed it must be transitioned as if newly added. if err := accountStakingGenesisValidation(vmParams, nextStakingFeat); err != nil { - return ierrors.Wrapf(iotago.ErrInvalidStakingTransition, "%w: rewards claiming without removing the feature requires updating the feature", err) + return ierrors.Join(iotago.ErrInvalidStakingTransition, ierrors.Wrap(err, "rewards claiming without removing the feature requires updating the feature")) } // If staking feature genesis validation succeeds, the start epoch has been reset which means the new epoch range // is disjoint from the current staking feature's, which can therefore be considered as removing and re-adding @@ -610,7 +611,7 @@ func accountDestructionValid(vmParams *vm.Params, input *vm.ChainOutputWithIDs, if blockIssuerFeat.ExpirySlot >= vmParams.WorkingSet.Commitment.Slot { return ierrors.Join(iotago.ErrInvalidBlockIssuerTransition, - ierrors.WithMessagef(iotago.ErrBlockIssuerNotExpired, "(current slot: %d, expiry slot: %d)", + ierrors.WithMessagef(iotago.ErrBlockIssuerNotExpired, "current slot: %d, expiry slot: %d", vmParams.WorkingSet.Commitment.Slot, blockIssuerFeat.ExpirySlot), ) } @@ -635,7 +636,7 @@ func accountDestructionValid(vmParams *vm.Params, input *vm.ChainOutputWithIDs, if futureBoundedEpoch <= stakingFeat.EndEpoch { return ierrors.WithMessagef( - iotago.ErrStakingFeatureRemovedBeforeUnbonding, "(current epoch is %d, must be > %d)", futureBoundedEpoch, stakingFeat.EndEpoch, + iotago.ErrStakingFeatureRemovedBeforeUnbonding, "future bounded epoch is %d, must be > %d", futureBoundedEpoch, stakingFeat.EndEpoch, ) } @@ -648,14 +649,14 @@ func accountDestructionValid(vmParams *vm.Params, input *vm.ChainOutputWithIDs, func accountBlockIssuanceCreditLocked(input *vm.ChainOutputWithIDs, bicSet vm.BlockIssuanceCreditInputSet) error { accountID, is := input.ChainID.(iotago.AccountID) if !is { - return ierrors.Wrapf(iotago.ErrBlockIssuanceCreditInputMissing, "cannot convert chain ID %s to account ID", + return ierrors.WithMessagef(iotago.ErrBlockIssuanceCreditInputMissing, "cannot convert chain ID %s to account ID", input.ChainID.ToHex()) } if bic, exists := bicSet[accountID]; !exists { return iotago.ErrBlockIssuanceCreditInputMissing } else if bic < 0 { - return ierrors.Wrapf(iotago.ErrAccountLocked, "cannot destroy locked account with ID %s", accountID.ToHex()) + return iotago.ErrAccountLocked } return nil @@ -733,13 +734,13 @@ func anchorGovernanceSTVF(input *vm.ChainOutputWithIDs, next *iotago.AnchorOutpu switch { case current.Amount != next.Amount: - return ierrors.Wrapf(iotago.ErrAnchorInvalidGovernanceTransition, "amount changed, in %d / out %d ", current.Amount, next.Amount) + return ierrors.WithMessagef(iotago.ErrAnchorInvalidGovernanceTransition, "amount changed, in %d / out %d ", current.Amount, next.Amount) case current.StateIndex != next.StateIndex: - return ierrors.Wrapf(iotago.ErrAnchorInvalidGovernanceTransition, "state index changed, in %d / out %d", current.StateIndex, next.StateIndex) + return ierrors.WithMessagef(iotago.ErrAnchorInvalidGovernanceTransition, "state index changed, in %d / out %d", current.StateIndex, next.StateIndex) } if err := iotago.FeatureUnchanged(iotago.FeatureStateMetadata, current.Features.MustSet(), next.Features.MustSet()); err != nil { - return ierrors.Wrapf(iotago.ErrAnchorInvalidGovernanceTransition, "%w", err) + return ierrors.Join(iotago.ErrAnchorInvalidGovernanceTransition, err) } return nil @@ -750,15 +751,15 @@ func anchorStateSTVF(input *vm.ChainOutputWithIDs, next *iotago.AnchorOutput) er current := input.Output.(*iotago.AnchorOutput) switch { case !current.StateController().Equal(next.StateController()): - return ierrors.Wrapf(iotago.ErrAnchorInvalidStateTransition, "state controller changed, in %v / out %v", current.StateController(), next.StateController()) + return ierrors.WithMessagef(iotago.ErrAnchorInvalidStateTransition, "state controller changed, in %v / out %v", current.StateController(), next.StateController()) case !current.GovernorAddress().Equal(next.GovernorAddress()): - return ierrors.Wrapf(iotago.ErrAnchorInvalidStateTransition, "governance controller changed, in %v / out %v", current.GovernorAddress(), next.GovernorAddress()) + return ierrors.WithMessagef(iotago.ErrAnchorInvalidStateTransition, "governance controller changed, in %v / out %v", current.GovernorAddress(), next.GovernorAddress()) case current.StateIndex+1 != next.StateIndex: - return ierrors.Wrapf(iotago.ErrAnchorInvalidStateTransition, "state index %d on the input side but %d on the output side", current.StateIndex, next.StateIndex) + return ierrors.WithMessagef(iotago.ErrAnchorInvalidStateTransition, "state index %d on the input side but %d on the output side", current.StateIndex, next.StateIndex) } if err := iotago.FeatureUnchanged(iotago.FeatureMetadata, current.Features.MustSet(), next.Features.MustSet()); err != nil { - return ierrors.Wrapf(iotago.ErrAnchorInvalidStateTransition, "%w", err) + return ierrors.Join(iotago.ErrAnchorInvalidStateTransition, err) } return nil @@ -893,7 +894,7 @@ func foundryStateChangeValid(current *iotago.FoundryOutput, next *iotago.Foundry // no matching foundry to be found to validate the state transition against if current.MustFoundryID() != next.MustFoundryID() { // impossible invariant as the STVF should be called via the matching next foundry output - panic(fmt.Sprintf("foundry IDs mismatch in state transition validation function: have %v got %v", current.MustFoundryID(), next.MustFoundryID())) + panic(fmt.Sprintf("foundry IDs mismatch in state transition validation function: have %s got %s", current.MustFoundryID(), next.MustFoundryID())) } nativeTokenID := current.MustNativeTokenID() @@ -972,7 +973,8 @@ func delegationSTVF(vmParams *vm.Params, input *vm.ChainOutputWithIDs, transType case iotago.ChainTransitionTypeStateChange: _, isClaiming := vmParams.WorkingSet.Rewards[input.ChainID] if isClaiming { - return ierrors.Wrapf(iotago.ErrDelegationTransitionInvalid, "%w: cannot claim rewards during delegation output transition", iotago.ErrDelegationRewardsClaimingInvalid) + return ierrors.Join(iotago.ErrDelegationTransitionInvalid, + ierrors.WithMessage(iotago.ErrDelegationRewardsClaimingInvalid, "cannot claim rewards during delegation output transition")) } //nolint:forcetypeassert // we can safely assume that this is an DelegationOutput current := input.Output.(*iotago.DelegationOutput) @@ -982,7 +984,8 @@ func delegationSTVF(vmParams *vm.Params, input *vm.ChainOutputWithIDs, transType case iotago.ChainTransitionTypeDestroy: _, isClaiming := vmParams.WorkingSet.Rewards[input.ChainID] if !isClaiming { - return ierrors.Wrapf(iotago.ErrDelegationTransitionInvalid, "%w: cannot destroy delegation output without a rewards input", iotago.ErrDelegationRewardInputMissing) + return ierrors.Join(iotago.ErrDelegationTransitionInvalid, + ierrors.WithMessage(iotago.ErrDelegationRewardInputMissing, "cannot destroy delegation output without a rewards input")) } return nil @@ -1015,15 +1018,16 @@ func delegationGenesisValid(vmParams *vm.Params, current *iotago.DelegationOutpu } if current.StartEpoch != expectedStartEpoch { - return ierrors.Wrapf(iotago.ErrDelegationTransitionInvalid, "%w (is %d, expected %d)", iotago.ErrDelegationStartEpochInvalid, current.StartEpoch, expectedStartEpoch) + return ierrors.Join(iotago.ErrDelegationTransitionInvalid, + ierrors.WithMessagef(iotago.ErrDelegationStartEpochInvalid, "is %d, expected %d", current.StartEpoch, expectedStartEpoch)) } if current.DelegatedAmount != current.Amount { - return ierrors.Wrapf(iotago.ErrDelegationTransitionInvalid, "%w", iotago.ErrDelegationAmountMismatch) + return ierrors.Join(iotago.ErrDelegationTransitionInvalid, iotago.ErrDelegationAmountMismatch) } if current.EndEpoch != 0 { - return ierrors.Wrapf(iotago.ErrDelegationTransitionInvalid, "%w", iotago.ErrDelegationEndEpochNotZero) + return ierrors.Join(iotago.ErrDelegationTransitionInvalid, iotago.ErrDelegationEndEpochNotZero) } return nil @@ -1042,7 +1046,7 @@ func delegationStateChangeValid(vmParams *vm.Params, current *iotago.DelegationO if current.DelegatedAmount != next.DelegatedAmount || !current.ValidatorAddress.Equal(next.ValidatorAddress) || current.StartEpoch != next.StartEpoch { - return ierrors.Wrapf(iotago.ErrDelegationTransitionInvalid, "%w", iotago.ErrDelegationModified) + return ierrors.Join(iotago.ErrDelegationTransitionInvalid, iotago.ErrDelegationModified) } timeProvider := vmParams.API.TimeProvider() @@ -1062,7 +1066,8 @@ func delegationStateChangeValid(vmParams *vm.Params, current *iotago.DelegationO } if next.EndEpoch != expectedEndEpoch { - return ierrors.Wrapf(iotago.ErrDelegationTransitionInvalid, "%w (is %d, expected %d)", iotago.ErrDelegationEndEpochInvalid, next.EndEpoch, expectedEndEpoch) + return ierrors.Join(iotago.ErrDelegationTransitionInvalid, + ierrors.WithMessagef(iotago.ErrDelegationEndEpochInvalid, "is %d, expected %d", next.EndEpoch, expectedEndEpoch)) } return nil From aec49496b03ee6f64f0e721da7b577363351696c Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 11 Mar 2024 12:17:08 +0800 Subject: [PATCH 29/48] Refactor chain transition error --- output.go | 12 ++++----- output_delegation.go | 2 -- vm/nova/vm.go | 62 +++++++++++++++++++++++++++++--------------- 3 files changed, 46 insertions(+), 30 deletions(-) diff --git a/output.go b/output.go index a2b64ce00..4eee7dcc7 100644 --- a/output.go +++ b/output.go @@ -142,18 +142,16 @@ var ( // ChainTransitionError gets returned when a state transition validation fails for a ChainOutput. type ChainTransitionError struct { - Inner error - Msg string + Inner error + ChainType OutputType + ChainID ChainID } func (i *ChainTransitionError) Error() string { var s strings.Builder - s.WriteString("invalid chain transition") + s.WriteString(fmt.Sprintf("invalid chain transition for %s %s", i.ChainType, i.ChainID.ToHex())) if i.Inner != nil { - s.WriteString(fmt.Sprintf("; inner err: %s", i.Inner)) - } - if len(i.Msg) > 0 { - s.WriteString(fmt.Sprintf("; %s", i.Msg)) + s.WriteString(fmt.Sprintf(": %s", i.Inner)) } return s.String() diff --git a/output_delegation.go b/output_delegation.go index 88b8b3911..3d8514634 100644 --- a/output_delegation.go +++ b/output_delegation.go @@ -14,8 +14,6 @@ const ( ) var ( - // ErrDelegationTransitionInvalid gets returned when a Delegation Output is doing an invalid state transition. - ErrDelegationTransitionInvalid = ierrors.New("invalid delegation output transition") // ErrDelegationCommitmentInputMissing gets returned when no commitment input was passed in a TX containing a Delegation Output. ErrDelegationCommitmentInputMissing = ierrors.New("delegation output validation requires a commitment input") // ErrDelegationRewardInputMissing gets returned when no reward input was passed in a TX destroying a Delegation Output. diff --git a/vm/nova/vm.go b/vm/nova/vm.go index 1f6a1a7d9..f264cbf4b 100644 --- a/vm/nova/vm.go +++ b/vm/nova/vm.go @@ -916,19 +916,31 @@ func nftSTVF(vmParams *vm.Params, input *vm.ChainOutputWithIDs, transType iotago switch transType { case iotago.ChainTransitionTypeGenesis: if err := nftGenesisValid(vmParams, next); err != nil { - return &iotago.ChainTransitionError{Inner: err, Msg: fmt.Sprintf("NFT %s", next.NFTID)} + return &iotago.ChainTransitionError{ + Inner: err, + ChainType: next.Type(), + ChainID: next.NFTID, + } } case iotago.ChainTransitionTypeStateChange: //nolint:forcetypeassert // we can safely assume that this is an NFTOutput current := input.Output.(*iotago.NFTOutput) if err := nftStateChangeValid(current, next); err != nil { - return &iotago.ChainTransitionError{Inner: err, Msg: fmt.Sprintf("NFT %s", current.NFTID)} + return &iotago.ChainTransitionError{ + Inner: err, + ChainType: current.Type(), + ChainID: current.NFTID, + } } case iotago.ChainTransitionTypeDestroy: //nolint:forcetypeassert // we can safely assume that this is an NFTOutput current := input.Output.(*iotago.NFTOutput) if err := nftDestructionValid(vmParams); err != nil { - return &iotago.ChainTransitionError{Inner: err, Msg: fmt.Sprintf("NFT %s", current.NFTID)} + return &iotago.ChainTransitionError{ + Inner: err, + ChainType: current.Type(), + ChainID: current.NFTID, + } } default: panic("unknown chain transition type in NFTOutput") @@ -968,24 +980,35 @@ func delegationSTVF(vmParams *vm.Params, input *vm.ChainOutputWithIDs, transType switch transType { case iotago.ChainTransitionTypeGenesis: if err := delegationGenesisValid(vmParams, next); err != nil { - return &iotago.ChainTransitionError{Inner: err, Msg: fmt.Sprintf("Delegation %s", next.DelegationID)} + return &iotago.ChainTransitionError{ + Inner: err, + ChainType: next.Type(), + ChainID: next.DelegationID, + } } case iotago.ChainTransitionTypeStateChange: _, isClaiming := vmParams.WorkingSet.Rewards[input.ChainID] if isClaiming { - return ierrors.Join(iotago.ErrDelegationTransitionInvalid, - ierrors.WithMessage(iotago.ErrDelegationRewardsClaimingInvalid, "cannot claim rewards during delegation output transition")) + return ierrors.WithMessage(iotago.ErrDelegationRewardsClaimingInvalid, "cannot claim rewards during delegation output transition") } //nolint:forcetypeassert // we can safely assume that this is an DelegationOutput current := input.Output.(*iotago.DelegationOutput) if err := delegationStateChangeValid(vmParams, current, next); err != nil { - return &iotago.ChainTransitionError{Inner: err, Msg: fmt.Sprintf("Delegation %s", current.DelegationID)} + return &iotago.ChainTransitionError{ + Inner: err, + ChainType: current.Type(), + ChainID: current.DelegationID, + } } case iotago.ChainTransitionTypeDestroy: _, isClaiming := vmParams.WorkingSet.Rewards[input.ChainID] if !isClaiming { - return ierrors.Join(iotago.ErrDelegationTransitionInvalid, - ierrors.WithMessage(iotago.ErrDelegationRewardInputMissing, "cannot destroy delegation output without a rewards input")) + err := ierrors.WithMessage(iotago.ErrDelegationRewardInputMissing, "cannot destroy delegation output without a rewards input") + return &iotago.ChainTransitionError{ + Inner: err, + ChainType: input.Output.Type(), + ChainID: input.ChainID, + } } return nil @@ -998,7 +1021,7 @@ func delegationSTVF(vmParams *vm.Params, input *vm.ChainOutputWithIDs, transType func delegationGenesisValid(vmParams *vm.Params, current *iotago.DelegationOutput) error { if !current.DelegationID.Empty() { - return ierrors.Join(iotago.ErrDelegationTransitionInvalid, iotago.ErrNewChainOutputHasNonZeroedID) + return iotago.ErrNewChainOutputHasNonZeroedID } timeProvider := vmParams.API.TimeProvider() @@ -1018,16 +1041,15 @@ func delegationGenesisValid(vmParams *vm.Params, current *iotago.DelegationOutpu } if current.StartEpoch != expectedStartEpoch { - return ierrors.Join(iotago.ErrDelegationTransitionInvalid, - ierrors.WithMessagef(iotago.ErrDelegationStartEpochInvalid, "is %d, expected %d", current.StartEpoch, expectedStartEpoch)) + return ierrors.WithMessagef(iotago.ErrDelegationStartEpochInvalid, "is %d, expected %d", current.StartEpoch, expectedStartEpoch) } if current.DelegatedAmount != current.Amount { - return ierrors.Join(iotago.ErrDelegationTransitionInvalid, iotago.ErrDelegationAmountMismatch) + return iotago.ErrDelegationAmountMismatch } if current.EndEpoch != 0 { - return ierrors.Join(iotago.ErrDelegationTransitionInvalid, iotago.ErrDelegationEndEpochNotZero) + return iotago.ErrDelegationEndEpochNotZero } return nil @@ -1037,16 +1059,15 @@ func delegationStateChangeValid(vmParams *vm.Params, current *iotago.DelegationO // State transitioning a Delegation Output is always a transition to the delayed claiming state. // Since they can only be transitioned once, the input will always need to have a zeroed ID. if !current.DelegationID.Empty() { - return ierrors.Join(iotago.ErrDelegationTransitionInvalid, - ierrors.WithMessagef(iotago.ErrDelegationOutputTransitionedTwice, - "delegation output can only be transitioned if it has a zeroed ID", - )) + return ierrors.WithMessagef(iotago.ErrDelegationOutputTransitionedTwice, + "delegation output can only be transitioned if it has a zeroed ID", + ) } if current.DelegatedAmount != next.DelegatedAmount || !current.ValidatorAddress.Equal(next.ValidatorAddress) || current.StartEpoch != next.StartEpoch { - return ierrors.Join(iotago.ErrDelegationTransitionInvalid, iotago.ErrDelegationModified) + return iotago.ErrDelegationModified } timeProvider := vmParams.API.TimeProvider() @@ -1066,8 +1087,7 @@ func delegationStateChangeValid(vmParams *vm.Params, current *iotago.DelegationO } if next.EndEpoch != expectedEndEpoch { - return ierrors.Join(iotago.ErrDelegationTransitionInvalid, - ierrors.WithMessagef(iotago.ErrDelegationEndEpochInvalid, "is %d, expected %d", next.EndEpoch, expectedEndEpoch)) + return ierrors.WithMessagef(iotago.ErrDelegationEndEpochInvalid, "is %d, expected %d", next.EndEpoch, expectedEndEpoch) } return nil From 9ebe7fb49c9dbafb285534414f69d9523e2d21b6 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 11 Mar 2024 14:12:15 +0800 Subject: [PATCH 30/48] Add chain error context once --- feat_blockissuer.go | 2 -- output.go | 24 ------------- vm/nova/vm.go | 88 +++++++++++++-------------------------------- vm/vm.go | 7 ++-- 4 files changed, 28 insertions(+), 93 deletions(-) diff --git a/feat_blockissuer.go b/feat_blockissuer.go index 64efddfc8..400d315da 100644 --- a/feat_blockissuer.go +++ b/feat_blockissuer.go @@ -15,8 +15,6 @@ const ( ) var ( - // ErrInvalidBlockIssuerTransition gets returned when an account tries to transition block issuer expiry too soon. - ErrInvalidBlockIssuerTransition = ierrors.New("invalid block issuer transition") // ErrBlockIssuerCommitmentInputMissing gets returned when no commitment input was passed in a TX containing a Block Issuer Feature. ErrBlockIssuerCommitmentInputMissing = ierrors.New("commitment input missing for block issuer feature") // ErrBlockIssuanceCreditInputMissing gets returned when a transaction containing an account with a block issuer feature diff --git a/output.go b/output.go index 4eee7dcc7..cbb06b2db 100644 --- a/output.go +++ b/output.go @@ -4,7 +4,6 @@ import ( "fmt" "math" "math/big" - "strings" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" @@ -132,35 +131,12 @@ var ( ErrChainMissing = ierrors.New("chain missing") // ErrNonUniqueChainOutputs gets returned when multiple ChainOutputs(s) with the same ChainID exist within sets. ErrNonUniqueChainOutputs = ierrors.New("non unique chain outputs") - // ErrChainTransitionInvalid gets returned when the chain transition was invalid. - ErrChainTransitionInvalid = ierrors.New("chain transition is invalid") // ErrNewChainOutputHasNonZeroedID gets returned when a new chain output has a non-zeroed ID. ErrNewChainOutputHasNonZeroedID = ierrors.New("new chain output has non-zeroed ID") // ErrChainOutputImmutableFeaturesChanged gets returned when a chain output's immutable features are modified in a transition. ErrChainOutputImmutableFeaturesChanged = ierrors.New("immutable features in chain output modified during transition") ) -// ChainTransitionError gets returned when a state transition validation fails for a ChainOutput. -type ChainTransitionError struct { - Inner error - ChainType OutputType - ChainID ChainID -} - -func (i *ChainTransitionError) Error() string { - var s strings.Builder - s.WriteString(fmt.Sprintf("invalid chain transition for %s %s", i.ChainType, i.ChainID.ToHex())) - if i.Inner != nil { - s.WriteString(fmt.Sprintf(": %s", i.Inner)) - } - - return s.String() -} - -func (i *ChainTransitionError) Unwrap() error { - return i.Inner -} - // Outputs is a slice of Output. type Outputs[T Output] []T diff --git a/vm/nova/vm.go b/vm/nova/vm.go index f264cbf4b..700f9cc35 100644 --- a/vm/nova/vm.go +++ b/vm/nova/vm.go @@ -156,7 +156,12 @@ func (novaVM *virtualMachine) ChainSTVF(vmParams *vm.Params, transType iotago.Ch } } - return implicitAccountSTVF(vmParams, castedInput, input.OutputID, nextAccount, transType) + err := implicitAccountSTVF(vmParams, castedInput, input.OutputID, nextAccount, transType) + if err != nil { + return ierrors.Wrapf(err, "transition failed for implicit account with output ID %s", input.OutputID.ToHex()) + } + + return nil case *iotago.AnchorOutput: var nextAnchor *iotago.AnchorOutput @@ -250,21 +255,15 @@ func accountSTVF(vmParams *vm.Params, input *vm.ChainOutputWithIDs, transType io switch transType { case iotago.ChainTransitionTypeGenesis: if err := accountGenesisValid(vmParams, next, true); err != nil { - return ierrors.Wrapf(err, " account %s", next.AccountID) + return err } case iotago.ChainTransitionTypeStateChange: if err := accountStateChangeValid(vmParams, input, next, isRemovingStakingFeature); err != nil { - //nolint:forcetypeassert // we can safely assume that this is an AccountOutput - a := input.Output.(*iotago.AccountOutput) - - return ierrors.Wrapf(err, "account %s", a.AccountID) + return err } case iotago.ChainTransitionTypeDestroy: if err := accountDestructionValid(vmParams, input, isRemovingStakingFeature); err != nil { - //nolint:forcetypeassert // we can safely assume that this is an AccountOutput - a := input.Output.(*iotago.AccountOutput) - - return ierrors.Wrapf(err, "account %s", a.AccountID) + return err } default: panic("unknown chain transition type in AccountOutput") @@ -293,9 +292,7 @@ func accountGenesisValid(vmParams *vm.Params, next *iotago.AccountOutput, accoun pastBoundedSlot := vmParams.PastBoundedSlotIndex(vmParams.WorkingSet.Commitment.Slot) if nextBlockIssuerFeat.ExpirySlot < pastBoundedSlot { - return ierrors.Join(iotago.ErrInvalidBlockIssuerTransition, - ierrors.WithMessagef(iotago.ErrBlockIssuerExpiryTooEarly, "is %d, must be >= %d", nextBlockIssuerFeat.ExpirySlot, pastBoundedSlot), - ) + return ierrors.WithMessagef(iotago.ErrBlockIssuerExpiryTooEarly, "is %d, must be >= %d", nextBlockIssuerFeat.ExpirySlot, pastBoundedSlot) } } @@ -351,7 +348,7 @@ func accountBlockIssuerSTVF(vmParams *vm.Params, input *vm.ChainOutputWithIDs, c } if vmParams.WorkingSet.Commitment == nil { - return ierrors.Join(iotago.ErrInvalidBlockIssuerTransition, iotago.ErrBlockIssuerCommitmentInputMissing) + return iotago.ErrBlockIssuerCommitmentInputMissing } commitmentInputSlot := vmParams.WorkingSet.Commitment.Slot @@ -360,23 +357,17 @@ func accountBlockIssuerSTVF(vmParams *vm.Params, input *vm.ChainOutputWithIDs, c if currentBlockIssuerFeat != nil && currentBlockIssuerFeat.ExpirySlot >= commitmentInputSlot { // if the block issuer feature has not expired, it can not be removed. if nextBlockIssuerFeat == nil { - return ierrors.Join(iotago.ErrInvalidBlockIssuerTransition, - ierrors.WithMessagef(iotago.ErrBlockIssuerNotExpired, "current slot: %d, expiry slot: %d", commitmentInputSlot, currentBlockIssuerFeat.ExpirySlot), - ) + return ierrors.WithMessagef(iotago.ErrBlockIssuerNotExpired, "current slot: %d, expiry slot: %d", commitmentInputSlot, currentBlockIssuerFeat.ExpirySlot) } if nextBlockIssuerFeat.ExpirySlot != currentBlockIssuerFeat.ExpirySlot && nextBlockIssuerFeat.ExpirySlot < pastBoundedSlot { - return ierrors.Join(iotago.ErrInvalidBlockIssuerTransition, - ierrors.WithMessagef(iotago.ErrBlockIssuerExpiryTooEarly, "is %d, must be >= %d", nextBlockIssuerFeat.ExpirySlot, pastBoundedSlot), - ) + return ierrors.WithMessagef(iotago.ErrBlockIssuerExpiryTooEarly, "is %d, must be >= %d", nextBlockIssuerFeat.ExpirySlot, pastBoundedSlot) } } else if nextBlockIssuerFeat != nil { // The block issuer feature was newly added, // or the current feature has expired but it was not removed. // In both cases the expiry slot must be set sufficiently far in the future. if nextBlockIssuerFeat.ExpirySlot < pastBoundedSlot { - return ierrors.Join(iotago.ErrInvalidBlockIssuerTransition, - ierrors.WithMessagef(iotago.ErrBlockIssuerExpiryTooEarly, "is %d, must be >= %d", nextBlockIssuerFeat.ExpirySlot, pastBoundedSlot), - ) + return ierrors.WithMessagef(iotago.ErrBlockIssuerExpiryTooEarly, "is %d, must be >= %d", nextBlockIssuerFeat.ExpirySlot, pastBoundedSlot) } } @@ -434,9 +425,7 @@ func accountBlockIssuerSTVF(vmParams *vm.Params, input *vm.ChainOutputWithIDs, c } if manaIn < manaOut { - return ierrors.Join(iotago.ErrInvalidBlockIssuerTransition, - ierrors.WithMessagef(iotago.ErrManaMovedOffBlockIssuerAccount, "mana in %d, mana out %d", manaIn, manaOut), - ) + return ierrors.WithMessagef(iotago.ErrManaMovedOffBlockIssuerAccount, "mana in %d, mana out %d", manaIn, manaOut) } return nil @@ -606,14 +595,12 @@ func accountDestructionValid(vmParams *vm.Params, input *vm.ChainOutputWithIDs, blockIssuerFeat := outputToDestroy.FeatureSet().BlockIssuer() if blockIssuerFeat != nil { if vmParams.WorkingSet.Commitment == nil { - return ierrors.Join(iotago.ErrInvalidBlockIssuerTransition, iotago.ErrBlockIssuerCommitmentInputMissing) + return iotago.ErrBlockIssuerCommitmentInputMissing } if blockIssuerFeat.ExpirySlot >= vmParams.WorkingSet.Commitment.Slot { - return ierrors.Join(iotago.ErrInvalidBlockIssuerTransition, - ierrors.WithMessagef(iotago.ErrBlockIssuerNotExpired, "current slot: %d, expiry slot: %d", - vmParams.WorkingSet.Commitment.Slot, blockIssuerFeat.ExpirySlot), - ) + return ierrors.WithMessagef(iotago.ErrBlockIssuerNotExpired, "current slot: %d, expiry slot: %d", + vmParams.WorkingSet.Commitment.Slot, blockIssuerFeat.ExpirySlot) } if err := accountBlockIssuanceCreditLocked(input, vmParams.WorkingSet.BIC); err != nil { @@ -916,31 +903,17 @@ func nftSTVF(vmParams *vm.Params, input *vm.ChainOutputWithIDs, transType iotago switch transType { case iotago.ChainTransitionTypeGenesis: if err := nftGenesisValid(vmParams, next); err != nil { - return &iotago.ChainTransitionError{ - Inner: err, - ChainType: next.Type(), - ChainID: next.NFTID, - } + return err } case iotago.ChainTransitionTypeStateChange: //nolint:forcetypeassert // we can safely assume that this is an NFTOutput current := input.Output.(*iotago.NFTOutput) if err := nftStateChangeValid(current, next); err != nil { - return &iotago.ChainTransitionError{ - Inner: err, - ChainType: current.Type(), - ChainID: current.NFTID, - } + return err } case iotago.ChainTransitionTypeDestroy: - //nolint:forcetypeassert // we can safely assume that this is an NFTOutput - current := input.Output.(*iotago.NFTOutput) if err := nftDestructionValid(vmParams); err != nil { - return &iotago.ChainTransitionError{ - Inner: err, - ChainType: current.Type(), - ChainID: current.NFTID, - } + return err } default: panic("unknown chain transition type in NFTOutput") @@ -980,11 +953,7 @@ func delegationSTVF(vmParams *vm.Params, input *vm.ChainOutputWithIDs, transType switch transType { case iotago.ChainTransitionTypeGenesis: if err := delegationGenesisValid(vmParams, next); err != nil { - return &iotago.ChainTransitionError{ - Inner: err, - ChainType: next.Type(), - ChainID: next.DelegationID, - } + return err } case iotago.ChainTransitionTypeStateChange: _, isClaiming := vmParams.WorkingSet.Rewards[input.ChainID] @@ -994,21 +963,12 @@ func delegationSTVF(vmParams *vm.Params, input *vm.ChainOutputWithIDs, transType //nolint:forcetypeassert // we can safely assume that this is an DelegationOutput current := input.Output.(*iotago.DelegationOutput) if err := delegationStateChangeValid(vmParams, current, next); err != nil { - return &iotago.ChainTransitionError{ - Inner: err, - ChainType: current.Type(), - ChainID: current.DelegationID, - } + return err } case iotago.ChainTransitionTypeDestroy: _, isClaiming := vmParams.WorkingSet.Rewards[input.ChainID] if !isClaiming { - err := ierrors.WithMessage(iotago.ErrDelegationRewardInputMissing, "cannot destroy delegation output without a rewards input") - return &iotago.ChainTransitionError{ - Inner: err, - ChainType: input.Output.Type(), - ChainID: input.ChainID, - } + return ierrors.WithMessage(iotago.ErrDelegationRewardInputMissing, "cannot destroy delegation output without a rewards input") } return nil diff --git a/vm/vm.go b/vm/vm.go index 447150047..7c307d92a 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -757,6 +757,7 @@ func ExecFuncBalancedBaseTokens() ExecFunc { for addr, returnSum := range inputSumReturnAmountPerAddress { outSum, has := outputSimpleTransfersPerAddr[addr] if !has { + // TODO: Printed address is garbage. return ierrors.Wrapf(iotago.ErrReturnAmountNotFulFilled, "return amount of %d not fulfilled as there is no output for %s", returnSum, addr) } if outSum < returnSum { @@ -796,13 +797,13 @@ func ExecFuncChainTransitions() ExecFunc { next := vmParams.WorkingSet.OutChains[chainID] if next == nil { if err := vm.ChainSTVF(vmParams, iotago.ChainTransitionTypeDestroy, inputChain, nil); err != nil { - return ierrors.Join(iotago.ErrChainTransitionInvalid, ierrors.Wrapf(err, "input chain %s (%T) destruction transition failed", chainID, inputChain)) + return ierrors.Wrapf(err, "invalid destruction for %s %s", inputChain.Output.Type(), chainID) } continue } if err := vm.ChainSTVF(vmParams, iotago.ChainTransitionTypeStateChange, inputChain, next); err != nil { - return ierrors.Join(iotago.ErrChainTransitionInvalid, ierrors.Wrapf(err, "chain %s (%T) state transition failed", chainID, inputChain)) + return ierrors.Wrapf(err, "invalid transition for %s %s", inputChain.Output.Type(), chainID) } } @@ -812,7 +813,7 @@ func ExecFuncChainTransitions() ExecFunc { } if err := vm.ChainSTVF(vmParams, iotago.ChainTransitionTypeGenesis, nil, outputChain); err != nil { - return ierrors.Join(iotago.ErrChainTransitionInvalid, ierrors.Wrapf(err, "new chain %s (%T) state transition failed", chainID, outputChain)) + return ierrors.Wrapf(err, "invalid creation of %s %s", outputChain.Type(), chainID) } } From 59759d7007cc17bccc8a48fb15aebb87384d722c Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 11 Mar 2024 14:36:06 +0800 Subject: [PATCH 31/48] Remove unnecessary invalid transition errors --- feat_staking.go | 2 - output_account.go | 2 - output_foundry.go | 2 - output_nft.go | 6 --- vm/nova/vm.go | 102 +++++++++++++++++++++------------------------- 5 files changed, 46 insertions(+), 68 deletions(-) diff --git a/feat_staking.go b/feat_staking.go index 5f847c0b2..3743bba68 100644 --- a/feat_staking.go +++ b/feat_staking.go @@ -8,8 +8,6 @@ import ( ) var ( - // ErrInvalidStakingTransition gets returned when an account tries to do an invalid transition with a Staking Feature. - ErrInvalidStakingTransition = ierrors.New("invalid staking transition") // ErrStakingStartEpochInvalid gets returned when a new Staking Feature's start epoch // is not set to the epoch of the transaction. ErrStakingStartEpochInvalid = ierrors.New("staking start epoch must be the epoch of the transaction") diff --git a/output_account.go b/output_account.go index f13a2d757..aa169c3d6 100644 --- a/output_account.go +++ b/output_account.go @@ -6,8 +6,6 @@ import ( ) var ( - // ErrInvalidAccountStateTransition gets returned when an account is doing an invalid state transition. - ErrInvalidAccountStateTransition = ierrors.New("invalid account state transition") // ErrImplicitAccountDestructionDisallowed gets returned if an implicit account is destroyed, which is not allowed. ErrImplicitAccountDestructionDisallowed = ierrors.New("cannot destroy implicit account; must be transitioned to account") // ErrMultipleImplicitAccountCreationAddresses gets return when there is more than one diff --git a/output_foundry.go b/output_foundry.go index eeb52e0cb..40ed21a57 100644 --- a/output_foundry.go +++ b/output_foundry.go @@ -17,8 +17,6 @@ const ( ) var ( - // ErrInvalidFoundryStateTransition gets returned when a foundry is doing an invalid state transition. - ErrInvalidFoundryStateTransition = ierrors.New("invalid foundry state transition") // ErrFoundryTransitionWithoutAccount gets returned when a foundry output is transitioned // without an accompanying account on the input or output side. ErrFoundryTransitionWithoutAccount = ierrors.New("foundry output transitioned without accompanying account on input or output side") diff --git a/output_nft.go b/output_nft.go index 730c25f57..fe508b7f3 100644 --- a/output_nft.go +++ b/output_nft.go @@ -3,16 +3,10 @@ package iotago import ( "golang.org/x/crypto/blake2b" - "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/hive.go/serializer/v2" "github.com/iotaledger/iota.go/v4/hexutil" ) -var ( - // ErrInvalidNFTStateTransition gets returned when a NFT is doing an invalid state transition. - ErrInvalidNFTStateTransition = ierrors.New("invalid NFT state transition") -) - const ( // NFTIDLength is the byte length of an NFTID. NFTIDLength = blake2b.Size256 diff --git a/vm/nova/vm.go b/vm/nova/vm.go index 700f9cc35..0eaa5d563 100644 --- a/vm/nova/vm.go +++ b/vm/nova/vm.go @@ -270,11 +270,11 @@ func accountSTVF(vmParams *vm.Params, input *vm.ChainOutputWithIDs, transType io } if isClaimingRewards && !*isRemovingStakingFeature { - return ierrors.Join(iotago.ErrInvalidStakingTransition, iotago.ErrStakingRewardClaimingInvalid) + return iotago.ErrStakingRewardClaimingInvalid } if !isClaimingRewards && *isRemovingStakingFeature { - return ierrors.Join(iotago.ErrInvalidStakingTransition, iotago.ErrStakingRewardInputMissing) + return iotago.ErrStakingRewardInputMissing } return nil @@ -282,7 +282,7 @@ func accountSTVF(vmParams *vm.Params, input *vm.ChainOutputWithIDs, transType io func accountGenesisValid(vmParams *vm.Params, next *iotago.AccountOutput, accountIDMustBeZeroed bool) error { if accountIDMustBeZeroed && !next.AccountID.Empty() { - return ierrors.Join(iotago.ErrInvalidAccountStateTransition, iotago.ErrNewChainOutputHasNonZeroedID) + return iotago.ErrNewChainOutputHasNonZeroedID } if nextBlockIssuerFeat := next.FeatureSet().BlockIssuer(); nextBlockIssuerFeat != nil { @@ -298,7 +298,7 @@ func accountGenesisValid(vmParams *vm.Params, next *iotago.AccountOutput, accoun if stakingFeat := next.FeatureSet().Staking(); stakingFeat != nil { if err := accountStakingGenesisValidation(vmParams, stakingFeat); err != nil { - return ierrors.Join(iotago.ErrInvalidStakingTransition, err) + return err } } @@ -309,10 +309,9 @@ func accountStateChangeValid(vmParams *vm.Params, input *vm.ChainOutputWithIDs, //nolint:forcetypeassert // we can safely assume that this is an AccountOutput current := input.Output.(*iotago.AccountOutput) if !current.ImmutableFeatures.Equal(next.ImmutableFeatures) { - return ierrors.Join(iotago.ErrInvalidAccountStateTransition, - ierrors.WithMessagef( - iotago.ErrChainOutputImmutableFeaturesChanged, "old state %s, next state %s", current.ImmutableFeatures, next.ImmutableFeatures, - )) + return ierrors.WithMessagef( + iotago.ErrChainOutputImmutableFeaturesChanged, "old state %s, next state %s", current.ImmutableFeatures, next.ImmutableFeatures, + ) } // If a Block Issuer Feature is present on the input side of the transaction, @@ -439,7 +438,7 @@ func accountStakingSTVF(vmParams *vm.Params, current *iotago.AccountOutput, next commitment := vmParams.WorkingSet.Commitment if commitment == nil { - return ierrors.Join(iotago.ErrInvalidStakingTransition, iotago.ErrStakingCommitmentInputMissing) + return iotago.ErrStakingCommitmentInputMissing } timeProvider := vmParams.API.TimeProvider() @@ -497,19 +496,18 @@ func accountStakingNonExpiredValidation( earliestUnbondingEpoch iotago.EpochIndex, ) error { if nextStakingFeat == nil { - return ierrors.Join(iotago.ErrInvalidStakingTransition, iotago.ErrStakingFeatureRemovedBeforeUnbonding) + return iotago.ErrStakingFeatureRemovedBeforeUnbonding } if currentStakingFeat.StakedAmount != nextStakingFeat.StakedAmount || currentStakingFeat.FixedCost != nextStakingFeat.FixedCost || currentStakingFeat.StartEpoch != nextStakingFeat.StartEpoch { - return ierrors.Join(iotago.ErrInvalidStakingTransition, iotago.ErrStakingFeatureModifiedBeforeUnbonding) + return iotago.ErrStakingFeatureModifiedBeforeUnbonding } if currentStakingFeat.EndEpoch != nextStakingFeat.EndEpoch && nextStakingFeat.EndEpoch < earliestUnbondingEpoch { - return ierrors.Join(iotago.ErrInvalidStakingTransition, - ierrors.WithMessagef(iotago.ErrStakingEndEpochTooEarly, "end epoch %d should be >= %d or the end epoch must match on input and output side", nextStakingFeat.EndEpoch, earliestUnbondingEpoch)) + return ierrors.WithMessagef(iotago.ErrStakingEndEpochTooEarly, "end epoch %d should be >= %d or the end epoch must match on input and output side", nextStakingFeat.EndEpoch, earliestUnbondingEpoch) } return nil @@ -528,7 +526,7 @@ func accountStakingExpiredValidation( } else if !currentStakingFeat.Equal(nextStakingFeat) { // If an expired feature is changed it must be transitioned as if newly added. if err := accountStakingGenesisValidation(vmParams, nextStakingFeat); err != nil { - return ierrors.Join(iotago.ErrInvalidStakingTransition, ierrors.Wrap(err, "rewards claiming without removing the feature requires updating the feature")) + return ierrors.Wrap(err, "rewards claiming without removing the feature requires updating the feature") } // If staking feature genesis validation succeeds, the start epoch has been reset which means the new epoch range // is disjoint from the current staking feature's, which can therefore be considered as removing and re-adding @@ -541,10 +539,9 @@ func accountStakingExpiredValidation( func accountFoundryCounterSTVF(vmParams *vm.Params, current *iotago.AccountOutput, next *iotago.AccountOutput) error { if current.FoundryCounter > next.FoundryCounter { - return ierrors.Join(iotago.ErrInvalidAccountStateTransition, - ierrors.WithMessagef(iotago.ErrAccountInvalidFoundryCounter, - "foundry counter of next state is less than previous, in %d / out %d", current.FoundryCounter, next.FoundryCounter, - )) + return ierrors.WithMessagef(iotago.ErrAccountInvalidFoundryCounter, + "foundry counter of next state is less than previous, in %d / out %d", current.FoundryCounter, next.FoundryCounter, + ) } // check that for a foundry counter change, X amount of foundries were actually created @@ -573,12 +570,11 @@ func accountFoundryCounterSTVF(vmParams *vm.Params, current *iotago.AccountOutpu expectedNewFoundriesCount := next.FoundryCounter - current.FoundryCounter if expectedNewFoundriesCount != seenNewFoundriesOfAccount { - return ierrors.Join(iotago.ErrInvalidAccountStateTransition, - ierrors.WithMessagef(iotago.ErrAccountInvalidFoundryCounter, - "%d new foundries were created but the account output's foundry counter changed by %d", - seenNewFoundriesOfAccount, - expectedNewFoundriesCount, - )) + return ierrors.WithMessagef(iotago.ErrAccountInvalidFoundryCounter, + "%d new foundries were created but the account output's foundry counter changed by %d", + seenNewFoundriesOfAccount, + expectedNewFoundriesCount, + ) } return nil @@ -586,7 +582,7 @@ func accountFoundryCounterSTVF(vmParams *vm.Params, current *iotago.AccountOutpu func accountDestructionValid(vmParams *vm.Params, input *vm.ChainOutputWithIDs, isRemovingStakingFeature *bool) error { if vmParams.WorkingSet.Tx.Capabilities.CannotDestroyAccountOutputs() { - return ierrors.Join(iotago.ErrInvalidAccountStateTransition, iotago.ErrTxCapabilitiesAccountDestructionNotAllowed) + return iotago.ErrTxCapabilitiesAccountDestructionNotAllowed } //nolint:forcetypeassert // we can safely assume that this is an AccountOutput @@ -614,7 +610,7 @@ func accountDestructionValid(vmParams *vm.Params, input *vm.ChainOutputWithIDs, // which also requires a commitment input. commitment := vmParams.WorkingSet.Commitment if commitment == nil { - return ierrors.Join(iotago.ErrInvalidStakingTransition, iotago.ErrStakingCommitmentInputMissing) + return iotago.ErrStakingCommitmentInputMissing } timeProvider := vmParams.API.TimeProvider() @@ -799,18 +795,16 @@ func foundryGenesisValid(vmParams *vm.Params, current *iotago.FoundryOutput, thi accountID := current.Owner().(*iotago.AccountAddress).AccountID() inAccount, ok := vmParams.WorkingSet.InChains[accountID] if !ok { - return ierrors.Join(iotago.ErrInvalidFoundryStateTransition, - ierrors.WithMessagef(iotago.ErrFoundryTransitionWithoutAccount, - "missing input transitioning account output %s for new foundry output %s", accountID, thisFoundryID, - )) + return ierrors.WithMessagef(iotago.ErrFoundryTransitionWithoutAccount, + "missing input transitioning account output %s for new foundry output %s", accountID, thisFoundryID, + ) } outAccount, ok := vmParams.WorkingSet.OutChains[accountID] if !ok { - return ierrors.Join(iotago.ErrInvalidFoundryStateTransition, - ierrors.WithMessagef(iotago.ErrFoundryTransitionWithoutAccount, - "missing output transitioning account output %s for new foundry output %s", accountID, thisFoundryID, - )) + return ierrors.WithMessagef(iotago.ErrFoundryTransitionWithoutAccount, + "missing output transitioning account output %s for new foundry output %s", accountID, thisFoundryID, + ) } //nolint:forcetypeassert // we can safely assume that this is an AccountOutput @@ -822,11 +816,10 @@ func foundrySerialNumberValid(vmParams *vm.Params, current *iotago.FoundryOutput startSerial := inAccount.FoundryCounter endIncSerial := outAccount.FoundryCounter if startSerial >= current.SerialNumber || current.SerialNumber > endIncSerial { - return ierrors.Join(iotago.ErrInvalidFoundryStateTransition, - ierrors.WithMessagef( - iotago.ErrFoundrySerialInvalid, - "new foundry output %s's serial number %d is not between the foundry counter interval of [%d,%d)", thisFoundryID, current.SerialNumber, startSerial, endIncSerial, - )) + return ierrors.WithMessagef( + iotago.ErrFoundrySerialInvalid, + "new foundry output %s's serial number %d is not between the foundry counter interval of [%d,%d)", thisFoundryID, current.SerialNumber, startSerial, endIncSerial, + ) } // OPTIMIZE: this loop happens on every STVF of every new foundry output @@ -856,11 +849,10 @@ func foundrySerialNumberValid(vmParams *vm.Params, current *iotago.FoundryOutput } if otherFoundryOutput.SerialNumber >= current.SerialNumber { - return ierrors.Join(iotago.ErrInvalidFoundryStateTransition, - ierrors.WithMessagef( - iotago.ErrFoundrySerialInvalid, - "new foundry output %s at index %d has bigger equal serial number than this foundry %s", otherFoundryID, outputIndex, thisFoundryID, - )) + return ierrors.WithMessagef( + iotago.ErrFoundrySerialInvalid, + "new foundry output %s at index %d has bigger equal serial number than this foundry %s", otherFoundryID, outputIndex, thisFoundryID, + ) } } @@ -869,11 +861,10 @@ func foundrySerialNumberValid(vmParams *vm.Params, current *iotago.FoundryOutput func foundryStateChangeValid(current *iotago.FoundryOutput, next *iotago.FoundryOutput, inSums iotago.NativeTokenSum, outSums iotago.NativeTokenSum) error { if !current.ImmutableFeatures.Equal(next.ImmutableFeatures) { - return ierrors.Join(iotago.ErrInvalidFoundryStateTransition, - ierrors.WithMessagef( - iotago.ErrChainOutputImmutableFeaturesChanged, - "old state %s, next state %s", current.ImmutableFeatures, next.ImmutableFeatures, - )) + return ierrors.WithMessagef( + iotago.ErrChainOutputImmutableFeaturesChanged, + "old state %s, next state %s", current.ImmutableFeatures, next.ImmutableFeatures, + ) } // the check for the serial number and token scheme not being mutated is implicit @@ -891,7 +882,7 @@ func foundryStateChangeValid(current *iotago.FoundryOutput, next *iotago.Foundry func foundryDestructionValid(vmParams *vm.Params, current *iotago.FoundryOutput, inSums iotago.NativeTokenSum, outSums iotago.NativeTokenSum) error { if vmParams.WorkingSet.Tx.Capabilities.CannotDestroyFoundryOutputs() { - return ierrors.Join(iotago.ErrInvalidFoundryStateTransition, iotago.ErrTxCapabilitiesFoundryDestructionNotAllowed) + return iotago.ErrTxCapabilitiesFoundryDestructionNotAllowed } nativeTokenID := current.MustNativeTokenID() @@ -924,7 +915,7 @@ func nftSTVF(vmParams *vm.Params, input *vm.ChainOutputWithIDs, transType iotago func nftGenesisValid(vmParams *vm.Params, current *iotago.NFTOutput) error { if !current.NFTID.Empty() { - return ierrors.Join(iotago.ErrInvalidNFTStateTransition, iotago.ErrNewChainOutputHasNonZeroedID) + return iotago.ErrNewChainOutputHasNonZeroedID } return vm.IsIssuerOnOutputUnlocked(current, vmParams.WorkingSet.UnlockedAddrs) @@ -932,10 +923,9 @@ func nftGenesisValid(vmParams *vm.Params, current *iotago.NFTOutput) error { func nftStateChangeValid(current *iotago.NFTOutput, next *iotago.NFTOutput) error { if !current.ImmutableFeatures.Equal(next.ImmutableFeatures) { - return ierrors.Join(iotago.ErrInvalidNFTStateTransition, - ierrors.WithMessagef(iotago.ErrChainOutputImmutableFeaturesChanged, - "old state %s, next state %s", current.ImmutableFeatures, next.ImmutableFeatures, - )) + return ierrors.WithMessagef(iotago.ErrChainOutputImmutableFeaturesChanged, + "old state %s, next state %s", current.ImmutableFeatures, next.ImmutableFeatures, + ) } return nil @@ -943,7 +933,7 @@ func nftStateChangeValid(current *iotago.NFTOutput, next *iotago.NFTOutput) erro func nftDestructionValid(vmParams *vm.Params) error { if vmParams.WorkingSet.Tx.Capabilities.CannotDestroyNFTOutputs() { - return ierrors.Join(iotago.ErrInvalidNFTStateTransition, iotago.ErrTxCapabilitiesNFTDestructionNotAllowed) + return iotago.ErrTxCapabilitiesNFTDestructionNotAllowed } return nil From d6a6e5f759b0d7b272a89f958a5883abb3752397 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 11 Mar 2024 14:40:36 +0800 Subject: [PATCH 32/48] Avoid using inaccurate "current slot" --- vm/nova/vm.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/nova/vm.go b/vm/nova/vm.go index 0eaa5d563..3f92fb79b 100644 --- a/vm/nova/vm.go +++ b/vm/nova/vm.go @@ -356,7 +356,7 @@ func accountBlockIssuerSTVF(vmParams *vm.Params, input *vm.ChainOutputWithIDs, c if currentBlockIssuerFeat != nil && currentBlockIssuerFeat.ExpirySlot >= commitmentInputSlot { // if the block issuer feature has not expired, it can not be removed. if nextBlockIssuerFeat == nil { - return ierrors.WithMessagef(iotago.ErrBlockIssuerNotExpired, "current slot: %d, expiry slot: %d", commitmentInputSlot, currentBlockIssuerFeat.ExpirySlot) + return ierrors.WithMessagef(iotago.ErrBlockIssuerNotExpired, "commitment slot: %d, expiry slot: %d", commitmentInputSlot, currentBlockIssuerFeat.ExpirySlot) } if nextBlockIssuerFeat.ExpirySlot != currentBlockIssuerFeat.ExpirySlot && nextBlockIssuerFeat.ExpirySlot < pastBoundedSlot { return ierrors.WithMessagef(iotago.ErrBlockIssuerExpiryTooEarly, "is %d, must be >= %d", nextBlockIssuerFeat.ExpirySlot, pastBoundedSlot) @@ -595,7 +595,7 @@ func accountDestructionValid(vmParams *vm.Params, input *vm.ChainOutputWithIDs, } if blockIssuerFeat.ExpirySlot >= vmParams.WorkingSet.Commitment.Slot { - return ierrors.WithMessagef(iotago.ErrBlockIssuerNotExpired, "current slot: %d, expiry slot: %d", + return ierrors.WithMessagef(iotago.ErrBlockIssuerNotExpired, "commitment slot: %d, expiry slot: %d", vmParams.WorkingSet.Commitment.Slot, blockIssuerFeat.ExpirySlot) } From 8a1e93bdabb90c43dc4c688f5dcdf4d3f2c2f91d Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 11 Mar 2024 16:02:18 +0800 Subject: [PATCH 33/48] Fix error wrap order in vm --- feat_native_token.go | 2 +- vm/vm.go | 48 ++++++++++++++++++++++---------------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/feat_native_token.go b/feat_native_token.go index c0f686d1f..3b149fa87 100644 --- a/feat_native_token.go +++ b/feat_native_token.go @@ -22,7 +22,7 @@ var ( // ErrNativeTokenSumExceedsUint256 gets returned when a NativeToken.Amount addition results in a value bigger than the max value of a uint256. ErrNativeTokenSumExceedsUint256 = ierrors.New("native token sum exceeds max value of a uint256") // ErrNativeTokenSumUnbalanced gets returned when two NativeTokenSum(s) are unbalanced. - ErrNativeTokenSumUnbalanced = ierrors.New("native token sums are unbalanced") + ErrNativeTokenSumUnbalanced = ierrors.New("native token sum is unbalanced") // ErrFoundryIDNativeTokenIDMismatch gets returned when a native token features exists in a foundry output but the IDs mismatch. ErrFoundryIDNativeTokenIDMismatch = ierrors.New("native token ID in foundry output must match the foundry ID") // ErrNativeTokenSetInvalid gets returned when the provided native tokens are invalid. diff --git a/vm/vm.go b/vm/vm.go index 7c307d92a..70c3a35f8 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -8,6 +8,7 @@ import ( "github.com/iotaledger/hive.go/core/safemath" "github.com/iotaledger/hive.go/ierrors" iotago "github.com/iotaledger/iota.go/v4" + "github.com/iotaledger/iota.go/v4/hexutil" ) // VirtualMachine executes and validates transactions. @@ -80,7 +81,7 @@ func TotalManaIn(manaDecayProvider *iotago.ManaDecayProvider, storageScoreStruct } totalIn, err = safemath.SafeAdd(totalIn, manaStored) if err != nil { - return 0, ierrors.Wrapf(iotago.ErrManaOverflow, "%w", err) + return 0, ierrors.WithMessagef(iotago.ErrManaOverflow, "%w", err) } manaPotential, err := iotago.PotentialMana(manaDecayProvider, storageScoreStructure, input, outputID.CreationSlot(), txCreationSlot) if err != nil { @@ -88,7 +89,7 @@ func TotalManaIn(manaDecayProvider *iotago.ManaDecayProvider, storageScoreStruct } totalIn, err = safemath.SafeAdd(totalIn, manaPotential) if err != nil { - return 0, ierrors.Wrapf(iotago.ErrManaOverflow, "%w", err) + return 0, ierrors.WithMessagef(iotago.ErrManaOverflow, "%w", err) } } @@ -97,7 +98,7 @@ func TotalManaIn(manaDecayProvider *iotago.ManaDecayProvider, storageScoreStruct var err error totalIn, err = safemath.SafeAdd(totalIn, reward) if err != nil { - return 0, ierrors.Wrapf(iotago.ErrManaOverflow, "%w", err) + return 0, ierrors.WithMessagef(iotago.ErrManaOverflow, "%w", err) } } @@ -111,13 +112,13 @@ func TotalManaOut(outputs iotago.Outputs[iotago.TxEssenceOutput], allotments iot for _, output := range outputs { totalOut, err = safemath.SafeAdd(totalOut, output.StoredMana()) if err != nil { - return 0, ierrors.Wrapf(iotago.ErrManaOverflow, "%w", err) + return 0, ierrors.WithMessagef(iotago.ErrManaOverflow, "%w", err) } } for _, allotment := range allotments { totalOut, err = safemath.SafeAdd(totalOut, allotment.Mana) if err != nil { - return 0, ierrors.Wrapf(iotago.ErrManaOverflow, "%w", err) + return 0, ierrors.WithMessagef(iotago.ErrManaOverflow, "%w", err) } } @@ -269,7 +270,7 @@ func (s *unlockedAddressesSet) ReferentialUnlockDirectlyUnlockable(owner iotago. // adds the index of the input to the set of unlocked inputs by this address. func (s *unlockedAddressesSet) MultiUnlock(addr *iotago.MultiAddress, multiUnlock *iotago.MultiUnlock, essenceMsg []byte, inputIndex uint16) error { if len(addr.Addresses) != len(multiUnlock.Unlocks) { - return ierrors.Wrapf(iotago.ErrMultiAddressLengthUnlockLengthMismatch, "input %d has a multi address (%T) but the amount of addresses does not match the unlocks %d != %d", inputIndex, addr, len(addr.Addresses), len(multiUnlock.Unlocks)) + return ierrors.WithMessagef(iotago.ErrMultiAddressLengthUnlockLengthMismatch, "input %d has a multi address (%T) but the amount of addresses does not match the unlocks %d != %d", inputIndex, addr, len(addr.Addresses), len(multiUnlock.Unlocks)) } var cumulativeUnlockedWeight uint16 @@ -281,7 +282,7 @@ func (s *unlockedAddressesSet) MultiUnlock(addr *iotago.MultiAddress, multiUnloc continue case *iotago.MultiUnlock: - return ierrors.Wrapf(iotago.ErrNestedMultiUnlock, "unlock at index %d.%d is invalid", inputIndex, subIndex) + return ierrors.WithMessagef(iotago.ErrNestedMultiUnlock, "unlock at index %d.%d is invalid", inputIndex, subIndex) default: // ATTENTION: we perform the checks only, but we do not unlock the input yet. @@ -295,7 +296,7 @@ func (s *unlockedAddressesSet) MultiUnlock(addr *iotago.MultiAddress, multiUnloc // check if the threshold for a successful unlock was reached if cumulativeUnlockedWeight < addr.Threshold { - return ierrors.Wrapf(iotago.ErrMultiAddressUnlockThresholdNotReached, "input %d has a multi address (%T) but the threshold of valid unlocks was not reached %d < %d", inputIndex, addr, cumulativeUnlockedWeight, addr.Threshold) + return ierrors.WithMessagef(iotago.ErrMultiAddressUnlockThresholdNotReached, "input %d has a multi address but the threshold of valid unlocks was not reached %d < %d", inputIndex, cumulativeUnlockedWeight, addr.Threshold) } // for multi addresses we don't "unlock" the signatures, only the multi address itself so it can be used for a referential unlock. @@ -624,11 +625,11 @@ func unlockAddress(ownerAddr iotago.Address, unlock iotago.Unlock, inputIndex ui } if err := unlockedAddrsSet.MultiUnlock(owner, uBlock, essenceMsgToSign, inputIndex); err != nil { - return ierrors.Join(iotago.ErrMultiAddressUnlockInvalid, err) + return ierrors.WithMessagef(iotago.ErrMultiAddressUnlockInvalid, "%w", err) } default: - return ierrors.WithMessagef(iotago.ErrMultiAddressUnlockInvalid, "input %d has a multi address (%T) but its corresponding unlock is of type %T", inputIndex, owner, unlock) + return ierrors.WithMessagef(iotago.ErrMultiAddressUnlockInvalid, "input %d has a multi address but its corresponding unlock is of type %s", inputIndex, unlock.Type()) } default: @@ -652,7 +653,7 @@ func resolveUnderlyingAddress(addr iotago.Address) iotago.Address { func unlockOutput(transaction *iotago.Transaction, commitmentInput VMCommitmentInput, input iotago.Output, unlock iotago.Unlock, inputIndex uint16, unlockedAddrsSet *unlockedAddressesSet, outChains iotago.ChainOutputSet, essenceMsgToSign []byte) error { ownerAddr, err := addressToUnlock(transaction, input, inputIndex, outChains) if err != nil { - return ierrors.Errorf("unable to retrieve address to unlock of input %d: %w", inputIndex, err) + return ierrors.Wrapf(err, "unable to retrieve address to unlock of input %d", inputIndex) } if actualAddrToUnlock, err := checkExpiration(input, commitmentInput, transaction.API.ProtocolParameters()); err != nil { @@ -677,7 +678,7 @@ func ExecFuncSenderUnlocked() ExecFunc { // check unlocked sender := resolveUnderlyingAddress(senderFeat.Address) if _, isUnlocked := vmParams.WorkingSet.UnlockedAddrs[sender.Key()]; !isUnlocked { - return ierrors.Wrapf(iotago.ErrSenderFeatureNotUnlocked, "output %d", outputIndex) + return ierrors.WithMessagef(iotago.ErrSenderFeatureNotUnlocked, "output %d", outputIndex) } } @@ -691,7 +692,7 @@ func ExecFuncBalancedMana() ExecFunc { txCreationSlot := vmParams.WorkingSet.Tx.CreationSlot for outputID := range vmParams.WorkingSet.UTXOInputsSet { if outputID.CreationSlot() > txCreationSlot { - return ierrors.Wrapf(iotago.ErrInputCreationAfterTxCreation, "input %s has creation slot %d, tx creation slot %d", outputID, outputID.CreationSlot(), txCreationSlot) + return ierrors.WithMessagef(iotago.ErrInputCreationAfterTxCreation, "input %s has creation slot %d, tx creation slot %d", outputID, outputID.CreationSlot(), txCreationSlot) } } manaIn := vmParams.WorkingSet.TotalManaIn @@ -699,11 +700,11 @@ func ExecFuncBalancedMana() ExecFunc { if manaIn < manaOut { // less mana on input side than on output side => not allowed - return ierrors.Wrapf(iotago.ErrInputOutputManaMismatch, "Mana in %d, Mana out %d", manaIn, manaOut) + return ierrors.WithMessagef(iotago.ErrInputOutputManaMismatch, "Mana in %d, Mana out %d", manaIn, manaOut) } else if manaIn > manaOut { // less mana on output side than on input side => check if mana burning is allowed if vmParams.WorkingSet.Tx.Capabilities.CannotBurnMana() { - return ierrors.Join(iotago.ErrInputOutputManaMismatch, iotago.ErrTxCapabilitiesManaBurningNotAllowed) + return ierrors.WithMessagef(iotago.ErrInputOutputManaMismatch, "%w", iotago.ErrTxCapabilitiesManaBurningNotAllowed) } } @@ -751,17 +752,16 @@ func ExecFuncBalancedBaseTokens() ExecFunc { } if in != out { - return ierrors.Wrapf(iotago.ErrInputOutputBaseTokenMismatch, "in %d, out %d", in, out) + return ierrors.WithMessagef(iotago.ErrInputOutputBaseTokenMismatch, "in %d, out %d", in, out) } for addr, returnSum := range inputSumReturnAmountPerAddress { outSum, has := outputSimpleTransfersPerAddr[addr] if !has { - // TODO: Printed address is garbage. - return ierrors.Wrapf(iotago.ErrReturnAmountNotFulFilled, "return amount of %d not fulfilled as there is no output for %s", returnSum, addr) + return ierrors.WithMessagef(iotago.ErrReturnAmountNotFulFilled, "return amount of %d not fulfilled as there is no output for address (serialized) %s", returnSum, hexutil.EncodeHex([]byte(addr))) } if outSum < returnSum { - return ierrors.Wrapf(iotago.ErrReturnAmountNotFulFilled, "return amount of %d not fulfilled as output is only %d for %s", returnSum, outSum, addr) + return ierrors.WithMessagef(iotago.ErrReturnAmountNotFulFilled, "return amount of %d not fulfilled as output is only %d for address (serialized) %s", returnSum, outSum, hexutil.EncodeHex([]byte(addr))) } } @@ -830,12 +830,12 @@ func ExecFuncBalancedNativeTokens() ExecFunc { var err error vmParams.WorkingSet.InNativeTokens, err = vmParams.WorkingSet.UTXOInputs.NativeTokenSum() if err != nil { - return ierrors.Join(iotago.ErrNativeTokenSetInvalid, ierrors.Errorf("invalid input native token set: %w", err)) + return ierrors.WithMessagef(iotago.ErrNativeTokenSetInvalid, "invalid input native token set: %w", err) } vmParams.WorkingSet.OutNativeTokens, err = vmParams.WorkingSet.Tx.Outputs.NativeTokenSum() if err != nil { - return ierrors.Join(iotago.ErrNativeTokenSetInvalid, err) + return ierrors.WithMessagef(iotago.ErrNativeTokenSetInvalid, "%w", err) } // check invariants for when token foundry is absent @@ -849,10 +849,10 @@ func ExecFuncBalancedNativeTokens() ExecFunc { if vmParams.WorkingSet.Tx.Capabilities.CannotBurnNativeTokens() && (outSum == nil || inSum.Cmp(outSum) != 0) { // if burning is not allowed, the input sum must be equal to the output sum - return ierrors.Wrapf(iotago.ErrTxCapabilitiesNativeTokenBurningNotAllowed, "%w: native token %s is less on output (%d) than input (%d) side but burning is not allowed in the transaction and the foundry is absent for melting", iotago.ErrNativeTokenSumUnbalanced, nativeTokenID, outSum, inSum) + return ierrors.WithMessagef(iotago.ErrTxCapabilitiesNativeTokenBurningNotAllowed, "%w: native token %s is less on output (%d) than input (%d) side but burning is not allowed in the transaction and the foundry is absent for melting", iotago.ErrNativeTokenSumUnbalanced, nativeTokenID, outSum, inSum) } else if (outSum != nil) && (inSum.Cmp(outSum) == -1) { // input sum must be greater equal the output sum (burning allows it to be greater) - return ierrors.Wrapf(iotago.ErrNativeTokenSumUnbalanced, "native token %s is less on input (%d) than output (%d) side but the foundry is absent for minting", nativeTokenID, inSum, outSum) + return ierrors.WithMessagef(iotago.ErrNativeTokenSumUnbalanced, "native token %s is less on input (%d) than output (%d) side but the foundry is absent for minting", nativeTokenID, inSum, outSum) } } @@ -864,7 +864,7 @@ func ExecFuncBalancedNativeTokens() ExecFunc { // foundry must be present when native tokens only reside on the output side // as they need to get minted by it within the tx if vmParams.WorkingSet.InNativeTokens[nativeTokenID] == nil { - return ierrors.Wrapf(iotago.ErrNativeTokenSumUnbalanced, "native token %s is new on the output side but the foundry is not transitioning", nativeTokenID) + return ierrors.WithMessagef(iotago.ErrNativeTokenSumUnbalanced, "native token %s is new on the output side but the foundry is not transitioning", nativeTokenID) } } From 4d576dfbea22fbdeb3fb2e5605867a05761058f9 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 11 Mar 2024 16:04:49 +0800 Subject: [PATCH 34/48] Improve keymanager errors --- wallet/keymanager.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wallet/keymanager.go b/wallet/keymanager.go index b4fc84b33..856615cc3 100644 --- a/wallet/keymanager.go +++ b/wallet/keymanager.go @@ -41,7 +41,7 @@ func NewKeyManagerFromRandom(path string) (*KeyManager, error) { func NewKeyManagerFromMnemonic(mnemonic string, path string) (*KeyManager, error) { mnemonicSentence := bip39.ParseMnemonic(mnemonic) if len(mnemonicSentence) != 24 { - return nil, ierrors.Errorf("mnemomic contains an invalid sentence length. Mnemonic should be 24 words") + return nil, ierrors.New("mnemomic sentence is not 24 words long") } seed, err := bip39.MnemonicToSeed(mnemonicSentence, "") @@ -56,7 +56,7 @@ func NewKeyManagerFromMnemonic(mnemonic string, path string) (*KeyManager, error func NewKeyManager(seed []byte, path string) (*KeyManager, error) { bip32Path, err := bip32path.ParsePath(path) if err != nil { - return nil, ierrors.Wrap(err, "failed to parse path") + return nil, ierrors.Wrap(err, "failed to parse bip32 path") } return &KeyManager{ From 09676f02cbf807df787dc2417c12fe1355230fad Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 11 Mar 2024 16:45:15 +0800 Subject: [PATCH 35/48] Remove other unknown type errors and panic instead --- builder/transaction_builder.go | 2 +- error.go | 2 -- signed_transaction.go | 2 -- tpkg/rand_unlock.go | 3 +-- unlock.go | 6 ++---- 5 files changed, 4 insertions(+), 11 deletions(-) diff --git a/builder/transaction_builder.go b/builder/transaction_builder.go index 8cd152951..1584b8e5a 100644 --- a/builder/transaction_builder.go +++ b/builder/transaction_builder.go @@ -252,7 +252,7 @@ func (b *TransactionBuilder) getStoredManaOutputAccountID(storedManaOutputIndex } default: - return iotago.EmptyAccountID, ierrors.WithMessagef(iotago.ErrUnknownOutputType, "output type %T does not support stored mana", output) + return iotago.EmptyAccountID, ierrors.Errorf("output type %s does not support stored mana", output.Type()) } return storedManaOutputAccountID, nil diff --git a/error.go b/error.go index fbe419c77..4843539f5 100644 --- a/error.go +++ b/error.go @@ -30,8 +30,6 @@ var ( var ( // ErrUTXOInputInvalid gets returned when the UTXO input is invalid. ErrUTXOInputInvalid = ierrors.New("UTXO input is invalid") - // ErrUnknownOutputType gets returned for unknown output types. - ErrUnknownOutputType = ierrors.New("unknown output type") // ErrCommitmentInputMissing gets returned when the commitment has not been provided when needed. ErrCommitmentInputMissing = ierrors.New("commitment input required with reward or BIC input") // ErrCommitmentInputReferenceInvalid gets returned when the commitment input references an invalid commitment. diff --git a/signed_transaction.go b/signed_transaction.go index 67d4b35cd..aa1949d7f 100644 --- a/signed_transaction.go +++ b/signed_transaction.go @@ -14,8 +14,6 @@ var ( ErrInputOutputBaseTokenMismatch = ierrors.New("inputs and outputs do not spend/deposit the same amount of base tokens") // ErrManaOverflow gets returned when there is an under- or overflow in Mana calculations. ErrManaOverflow = ierrors.New("under- or overflow in Mana calculations") - // ErrUnknownSignatureType gets returned for unknown signature types. - ErrUnknownSignatureType = ierrors.New("unknown signature type") // ErrInputUnlockCountMismatch gets returned when the unlock count and input count mismatch. ErrInputUnlockCountMismatch = ierrors.New("unlock count and input count mismatch") // ErrSignatureAndAddrIncompatible gets returned if an address of an input has a companion signature unlock with the wrong signature type. diff --git a/tpkg/rand_unlock.go b/tpkg/rand_unlock.go index c66aefa60..1600e4cdc 100644 --- a/tpkg/rand_unlock.go +++ b/tpkg/rand_unlock.go @@ -1,7 +1,6 @@ package tpkg import ( - "github.com/iotaledger/hive.go/ierrors" iotago "github.com/iotaledger/iota.go/v4" ) @@ -30,7 +29,7 @@ func RandUnlock(allowEmptyUnlock bool) iotago.Unlock { case iotago.UnlockEmpty: return &iotago.EmptyUnlock{} default: - panic(ierrors.Wrapf(iotago.ErrUnknownUnlockType, "type %d", unlockType)) + panic("all known unlock types should be handled above") } } diff --git a/unlock.go b/unlock.go index 12538ae54..871c9eff1 100644 --- a/unlock.go +++ b/unlock.go @@ -64,8 +64,6 @@ var ( ErrReferentialUnlockInvalid = ierrors.New("invalid referential unlock") // ErrSignatureUnlockHasNilSignature gets returned if a signature unlock contains a nil signature. ErrSignatureUnlockHasNilSignature = ierrors.New("signature is nil") - // ErrUnknownUnlockType gets returned for unknown unlock. - ErrUnknownUnlockType = ierrors.New("unknown unlock type") // ErrNestedMultiUnlock gets returned when a MultiUnlock is nested inside a MultiUnlock. ErrNestedMultiUnlock = ierrors.New("multi unlocks can't be nested") // ErrEmptyUnlockOutsideMultiUnlock gets returned when an empty unlock was not nested inside of a multi unlock. @@ -252,7 +250,7 @@ func SignaturesUniqueAndReferenceUnlocksValidator(api API) UnlockValidatorFunc { continue default: - return ierrors.WithMessagef(ErrUnknownUnlockType, "unlock at index %d.%d is of unknown type %T", index, subIndex, subUnlock) + panic("all known unlock types should be handled above") } } seenMultiUnlocks[uint16(index)] = struct{}{} @@ -262,7 +260,7 @@ func SignaturesUniqueAndReferenceUnlocksValidator(api API) UnlockValidatorFunc { return ierrors.WithMessagef(ErrEmptyUnlockOutsideMultiUnlock, "unlock at index %d is invalid", index) default: - return ierrors.WithMessagef(ErrUnknownUnlockType, "unlock at index %d is of unknown type %T", index, unlock) + panic("all known unlock types should be handled above") } return nil From 9c46f17ce246e219d637fa55932b7431820ed146 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 11 Mar 2024 16:46:11 +0800 Subject: [PATCH 36/48] Rephrase panic message --- input.go | 4 ++-- input_context.go | 4 ++-- output.go | 6 +++--- tpkg/rand_unlock.go | 2 +- transaction.go | 10 +++++----- unlock.go | 4 ++-- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/input.go b/input.go index 6a76f4563..5c01ee426 100644 --- a/input.go +++ b/input.go @@ -94,7 +94,7 @@ func InputsSyntacticalUnique() ElementValidationFunc[Input] { } utxoSet[k] = index default: - panic("all known input types should be handled above") + panic("all supported input types should be handled above") } return nil @@ -111,7 +111,7 @@ func InputsSyntacticalIndicesWithinBounds() ElementValidationFunc[Input] { return ierrors.WithMessagef(ErrRefUTXOIndexInvalid, "input %d", index) } default: - panic("all known input types should be handled above") + panic("all supported input types should be handled above") } return nil diff --git a/input_context.go b/input_context.go index 876ed8bbe..71a962489 100644 --- a/input_context.go +++ b/input_context.go @@ -101,7 +101,7 @@ func ContextInputsRewardInputMaxIndex(inputsCount uint16) ElementValidationFunc[ index, utxoIndex, inputsCount) } default: - panic("all known context input types should be handled above") + panic("all supported context input types should be handled above") } return nil @@ -128,7 +128,7 @@ func ContextInputsCommitmentInputRequirement() ElementValidationFunc[ContextInpu return ierrors.WithMessagef(ErrCommitmentInputMissing, "reward input at index %d requires a commitment input", index) } default: - panic("all known context input types should be handled above") + panic("all supported context input types should be handled above") } return nil diff --git a/output.go b/output.go index cbb06b2db..ec5055313 100644 --- a/output.go +++ b/output.go @@ -668,7 +668,7 @@ func OutputsSyntacticalImplicitAccountCreationAddress() ElementValidationFunc[Ou return ierrors.WithMessagef(ErrImplicitAccountCreationAddressInInvalidOutput, "output %d", index) } default: - panic("all known output types should be handled above") + panic("all supported output types should be handled above") } return nil @@ -717,7 +717,7 @@ func OutputsSyntacticalUnlockConditionLexicalOrderAndUniqueness() ElementValidat } } default: - panic("all known output types should be handled above") + panic("all supported output types should be handled above") } return nil @@ -786,7 +786,7 @@ func OutputsSyntacticalFeaturesLexicalOrderAndUniqueness() ElementValidationFunc // This output does not have features. return nil default: - panic("all known output types should be handled above") + panic("all supported output types should be handled above") } return nil diff --git a/tpkg/rand_unlock.go b/tpkg/rand_unlock.go index 1600e4cdc..8e5100662 100644 --- a/tpkg/rand_unlock.go +++ b/tpkg/rand_unlock.go @@ -29,7 +29,7 @@ func RandUnlock(allowEmptyUnlock bool) iotago.Unlock { case iotago.UnlockEmpty: return &iotago.EmptyUnlock{} default: - panic("all known unlock types should be handled above") + panic("all supported unlock types should be handled above") } } diff --git a/transaction.go b/transaction.go index 3373c4658..170c905f2 100644 --- a/transaction.go +++ b/transaction.go @@ -140,7 +140,7 @@ func (t *Transaction) Inputs() ([]*UTXOInput, error) { case *UTXOInput: references = append(references, castInput) default: - panic("all known input types should be handled above") + panic("all supported input types should be handled above") } } @@ -168,7 +168,7 @@ func (t *Transaction) ContextInputs() (TransactionContextInputs, error) { case *CommitmentInput, *BlockIssuanceCreditInput, *RewardInput: references = append(references, castInput) default: - panic("all known context input types should be handled above") + panic("all supported context input types should be handled above") } } @@ -184,7 +184,7 @@ func (t *Transaction) BICInputs() ([]*BlockIssuanceCreditInput, error) { case *CommitmentInput, *RewardInput: // ignore this type default: - panic("all known context input types should be handled above") + panic("all supported context input types should be handled above") } } @@ -200,7 +200,7 @@ func (t *Transaction) RewardInputs() ([]*RewardInput, error) { case *CommitmentInput, *BlockIssuanceCreditInput: // ignore this type default: - panic("all known context input types should be handled above") + panic("all supported context input types should be handled above") } } @@ -216,7 +216,7 @@ func (t *Transaction) CommitmentInput() *CommitmentInput { case *CommitmentInput: return castInput default: - panic("all known context input types should be handled above") + panic("all supported context input types should be handled above") } } diff --git a/unlock.go b/unlock.go index 871c9eff1..fe364b2d5 100644 --- a/unlock.go +++ b/unlock.go @@ -250,7 +250,7 @@ func SignaturesUniqueAndReferenceUnlocksValidator(api API) UnlockValidatorFunc { continue default: - panic("all known unlock types should be handled above") + panic("all supported unlock types should be handled above") } } seenMultiUnlocks[uint16(index)] = struct{}{} @@ -260,7 +260,7 @@ func SignaturesUniqueAndReferenceUnlocksValidator(api API) UnlockValidatorFunc { return ierrors.WithMessagef(ErrEmptyUnlockOutsideMultiUnlock, "unlock at index %d is invalid", index) default: - panic("all known unlock types should be handled above") + panic("all supported unlock types should be handled above") } return nil From 5f56f3d70509730f6a7a2625e89ad391bffc5daa Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 11 Mar 2024 16:47:52 +0800 Subject: [PATCH 37/48] Remove error from funcs that panic instead --- address.go | 21 +++++++++------------ signed_transaction.go | 5 +---- signed_transaction_test.go | 9 +++------ testlog.txt | 0 transaction.go | 16 ++++++++-------- vm/vm.go | 5 +---- 6 files changed, 22 insertions(+), 34 deletions(-) create mode 100644 testlog.txt diff --git a/address.go b/address.go index 7f2f74150..23dbaa964 100644 --- a/address.go +++ b/address.go @@ -173,22 +173,22 @@ type UTXOIDChainID interface { FromOutputID(id OutputID) ChainID } -func newAddress(addressType AddressType) (address Address, err error) { +func newAddress(addressType AddressType) Address { switch addressType { case AddressEd25519: - return &Ed25519Address{}, nil + return &Ed25519Address{} case AddressAccount: - return &AccountAddress{}, nil + return &AccountAddress{} case AddressNFT: - return &NFTAddress{}, nil + return &NFTAddress{} case AddressAnchor: - return &AnchorAddress{}, nil + return &AnchorAddress{} case AddressImplicitAccountCreation: - return &ImplicitAccountCreationAddress{}, nil + return &ImplicitAccountCreationAddress{} case AddressMulti: - return &MultiAddress{}, nil + return &MultiAddress{} case AddressRestricted: - return &RestrictedAddress{}, nil + return &RestrictedAddress{} default: panic(fmt.Sprintf("unknown address type %d", addressType)) } @@ -252,10 +252,7 @@ func ParseBech32(s string) (NetworkPrefix, Address, error) { } } - addr, err := newAddress(addrType) - if err != nil { - return "", nil, err - } + addr := newAddress(addrType) serixAPI := CommonSerixAPI() n, err := serixAPI.Decode(context.TODO(), addrData, addr) diff --git a/signed_transaction.go b/signed_transaction.go index aa1949d7f..61d333d4a 100644 --- a/signed_transaction.go +++ b/signed_transaction.go @@ -100,10 +100,7 @@ func (t *SignedTransaction) String() string { // syntacticallyValidate syntactically validates the SignedTransaction. func (t *SignedTransaction) syntacticallyValidate() error { // limit unlock block count = input count - inputs, err := t.Transaction.Inputs() - if err != nil { - return err - } + inputs := t.Transaction.Inputs() if len(t.Unlocks) != len(inputs) { return ierrors.WithMessagef(ErrInputUnlockCountMismatch, "unlock count %d does not match inputs count %d", len(t.Unlocks), len(inputs)) diff --git a/signed_transaction_test.go b/signed_transaction_test.go index 9e9dab614..9eb26ba86 100644 --- a/signed_transaction_test.go +++ b/signed_transaction_test.go @@ -246,17 +246,14 @@ func TestTransaction_InputTypes(t *testing.T) { }), )) - utxoInputs, err := signedTransaction.Transaction.Inputs() - require.NoError(t, err) + utxoInputs := signedTransaction.Transaction.Inputs() commitmentInput := signedTransaction.Transaction.CommitmentInput() require.NotNil(t, commitmentInput) - bicInputs, err := signedTransaction.Transaction.BICInputs() - require.NoError(t, err) + bicInputs := signedTransaction.Transaction.BICInputs() - rewardInputs, err := signedTransaction.Transaction.RewardInputs() - require.NoError(t, err) + rewardInputs := signedTransaction.Transaction.RewardInputs() require.Equal(t, 2, len(utxoInputs)) require.Equal(t, 2, len(bicInputs)) diff --git a/testlog.txt b/testlog.txt new file mode 100644 index 000000000..e69de29bb diff --git a/transaction.go b/transaction.go index 170c905f2..2a311a654 100644 --- a/transaction.go +++ b/transaction.go @@ -133,7 +133,7 @@ func (t *Transaction) Clone() *Transaction { } } -func (t *Transaction) Inputs() ([]*UTXOInput, error) { +func (t *Transaction) Inputs() []*UTXOInput { references := make([]*UTXOInput, 0, len(t.TransactionEssence.Inputs)) for _, input := range t.TransactionEssence.Inputs { switch castInput := input.(type) { @@ -144,7 +144,7 @@ func (t *Transaction) Inputs() ([]*UTXOInput, error) { } } - return references, nil + return references } // OutputsSet returns an OutputSet from the Transaction's outputs, mapped by their OutputID. @@ -161,7 +161,7 @@ func (t *Transaction) OutputsSet() (OutputSet, error) { return set, nil } -func (t *Transaction) ContextInputs() (TransactionContextInputs, error) { +func (t *Transaction) ContextInputs() TransactionContextInputs { references := make(TransactionContextInputs, 0, len(t.TransactionEssence.ContextInputs)) for _, input := range t.TransactionEssence.ContextInputs { switch castInput := input.(type) { @@ -172,10 +172,10 @@ func (t *Transaction) ContextInputs() (TransactionContextInputs, error) { } } - return references, nil + return references } -func (t *Transaction) BICInputs() ([]*BlockIssuanceCreditInput, error) { +func (t *Transaction) BICInputs() []*BlockIssuanceCreditInput { references := make([]*BlockIssuanceCreditInput, 0, len(t.TransactionEssence.ContextInputs)) for _, input := range t.TransactionEssence.ContextInputs { switch castInput := input.(type) { @@ -188,10 +188,10 @@ func (t *Transaction) BICInputs() ([]*BlockIssuanceCreditInput, error) { } } - return references, nil + return references } -func (t *Transaction) RewardInputs() ([]*RewardInput, error) { +func (t *Transaction) RewardInputs() []*RewardInput { references := make([]*RewardInput, 0, len(t.TransactionEssence.ContextInputs)) for _, input := range t.TransactionEssence.ContextInputs { switch castInput := input.(type) { @@ -204,7 +204,7 @@ func (t *Transaction) RewardInputs() ([]*RewardInput, error) { } } - return references, nil + return references } // Returns the first commitment input in the transaction if it exists or nil. diff --git a/vm/vm.go b/vm/vm.go index 70c3a35f8..d4e17f800 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -424,10 +424,7 @@ type ExecFunc func(vm VirtualMachine, svCtx *Params) error // ValidateUnlocks produces the UnlockedAddresses which will be set into the given Params and verifies that inputs are // correctly unlocked and that the inputs commitment matches. func ValidateUnlocks(signedTransaction *iotago.SignedTransaction, resolvedInputs ResolvedInputs) (unlockedAddrs UnlockedAddresses, err error) { - utxoInputs, err := signedTransaction.Transaction.Inputs() - if err != nil { - return nil, ierrors.Wrap(err, "failed to get inputs from transaction") - } + utxoInputs := signedTransaction.Transaction.Inputs() var inputs iotago.Outputs[iotago.Output] for _, input := range utxoInputs { From b0e23ff54b7f31d639c3fc865df6968135900008 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 11 Mar 2024 18:13:12 +0800 Subject: [PATCH 38/48] Revert length FromBytes changes --- block_id.gen.go | 4 ++-- commitment_id.gen.go | 4 ++-- epoch_index.gen.go | 2 +- gen/identifier.tmpl | 4 ++-- gen/index.tmpl | 2 +- gen/slot_identifier.tmpl | 4 ++-- identifier.gen.go | 4 ++-- identifier_account.gen.go | 4 ++-- identifier_anchor.gen.go | 4 ++-- output_id.gen.go | 4 ++-- signed_transaction_id.gen.go | 4 ++-- slot_index.gen.go | 2 +- transaction_id.gen.go | 4 ++-- 13 files changed, 23 insertions(+), 23 deletions(-) diff --git a/block_id.gen.go b/block_id.gen.go index ffa16bfca..e98d82085 100644 --- a/block_id.gen.go +++ b/block_id.gen.go @@ -63,8 +63,8 @@ func IsValidBlockID(b []byte) error { // BlockIDFromBytes returns a new BlockID represented by the passed bytes. func BlockIDFromBytes(b []byte) (BlockID, int, error) { - if err := IsValidBlockID(b); err != nil { - return EmptyBlockID, 0, err + if len(b) < BlockIDLength { + return EmptyBlockID, 0, ierrors.Errorf("invalid length for blockID, expected at least %d bytes, got %d bytes", BlockIDLength, len(b)) } return BlockID(b), BlockIDLength, nil diff --git a/commitment_id.gen.go b/commitment_id.gen.go index 3f3cf9ea9..1ea2a0f9b 100644 --- a/commitment_id.gen.go +++ b/commitment_id.gen.go @@ -63,8 +63,8 @@ func IsValidCommitmentID(b []byte) error { // CommitmentIDFromBytes returns a new CommitmentID represented by the passed bytes. func CommitmentIDFromBytes(b []byte) (CommitmentID, int, error) { - if err := IsValidCommitmentID(b); err != nil { - return EmptyCommitmentID, 0, err + if len(b) < CommitmentIDLength { + return EmptyCommitmentID, 0, ierrors.Errorf("invalid length for commitmentID, expected at least %d bytes, got %d bytes", CommitmentIDLength, len(b)) } return CommitmentID(b), CommitmentIDLength, nil diff --git a/epoch_index.gen.go b/epoch_index.gen.go index c9952fce2..aec42b6bb 100644 --- a/epoch_index.gen.go +++ b/epoch_index.gen.go @@ -20,7 +20,7 @@ type EpochIndex uint32 func EpochIndexFromBytes(b []byte) (EpochIndex, int, error) { if len(b) < EpochIndexLength { - return 0, 0, ierrors.New("invalid epoch index size") + return 0, 0, ierrors.Errorf("invalid length for epoch index, expected at least %d bytes, got %d bytes", EpochIndexLength, len(b)) } return EpochIndex(binary.LittleEndian.Uint32(b)), EpochIndexLength, nil diff --git a/gen/identifier.tmpl b/gen/identifier.tmpl index f53a76ebe..114670f46 100644 --- a/gen/identifier.tmpl +++ b/gen/identifier.tmpl @@ -64,8 +64,8 @@ func IsValid{{.Name}}(b []byte) error { func {{.Name}}FromBytes(bytes []byte) ({{.Name}}, int, error) { var {{.Receiver}} {{.Name}} - if err := IsValid{{.Name}}(bytes); err != nil { - return {{.Receiver}}, 0, err + if len(bytes) < {{.Name}}Length { + return {{.Receiver}}, 0, ierrors.Errorf("invalid length for {{firstLower .Name}}, expected at least %d bytes, got %d bytes", {{.Name}}Length, len(bytes)) } copy({{.Receiver}}[:], bytes) diff --git a/gen/index.tmpl b/gen/index.tmpl index 5c5afa53a..9b6964429 100644 --- a/gen/index.tmpl +++ b/gen/index.tmpl @@ -20,7 +20,7 @@ type {{.Name}}Index uint32 func {{.Name}}IndexFromBytes(b []byte) ({{.Name}}Index, int, error) { if len(b) < {{.Name}}IndexLength { - return 0, 0, ierrors.New("invalid {{firstLower .Name}} index size") + return 0, 0, ierrors.Errorf("invalid length for {{firstLower .Name}} index, expected at least %d bytes, got %d bytes", {{.Name}}IndexLength, len(b)) } return {{.Name}}Index(binary.LittleEndian.Uint32(b)), {{.Name}}IndexLength, nil diff --git a/gen/slot_identifier.tmpl b/gen/slot_identifier.tmpl index 2a210a48f..4ee3a3923 100644 --- a/gen/slot_identifier.tmpl +++ b/gen/slot_identifier.tmpl @@ -79,8 +79,8 @@ func IsValid{{.Name}}(b []byte) error { // {{.Name}}FromBytes returns a new {{.Name}} represented by the passed bytes. func {{.Name}}FromBytes(b []byte) ({{.Name}}, int, error) { - if err := IsValid{{.Name}}(b); err != nil { - return Empty{{.Name}}, 0, err + if len(b) < {{.Name}}Length { + return Empty{{.Name}}, 0, ierrors.Errorf("invalid length for {{firstLower .Name}}, expected at least %d bytes, got %d bytes", {{.Name}}Length, len(b)) } return {{.Name}}(b), {{.Name}}Length, nil diff --git a/identifier.gen.go b/identifier.gen.go index 8c59f4490..903998851 100644 --- a/identifier.gen.go +++ b/identifier.gen.go @@ -64,8 +64,8 @@ func IsValidIdentifier(b []byte) error { func IdentifierFromBytes(bytes []byte) (Identifier, int, error) { var i Identifier - if err := IsValidIdentifier(bytes); err != nil { - return i, 0, err + if len(bytes) < IdentifierLength { + return i, 0, ierrors.Errorf("invalid length for identifier, expected at least %d bytes, got %d bytes", IdentifierLength, len(bytes)) } copy(i[:], bytes) diff --git a/identifier_account.gen.go b/identifier_account.gen.go index 96dbd6ee1..155f55d74 100644 --- a/identifier_account.gen.go +++ b/identifier_account.gen.go @@ -64,8 +64,8 @@ func IsValidAccountID(b []byte) error { func AccountIDFromBytes(bytes []byte) (AccountID, int, error) { var a AccountID - if err := IsValidAccountID(bytes); err != nil { - return a, 0, err + if len(bytes) < AccountIDLength { + return a, 0, ierrors.Errorf("invalid length for accountID, expected at least %d bytes, got %d bytes", AccountIDLength, len(bytes)) } copy(a[:], bytes) diff --git a/identifier_anchor.gen.go b/identifier_anchor.gen.go index 6bbc53596..32f009da1 100644 --- a/identifier_anchor.gen.go +++ b/identifier_anchor.gen.go @@ -64,8 +64,8 @@ func IsValidAnchorID(b []byte) error { func AnchorIDFromBytes(bytes []byte) (AnchorID, int, error) { var a AnchorID - if err := IsValidAnchorID(bytes); err != nil { - return a, 0, err + if len(bytes) < AnchorIDLength { + return a, 0, ierrors.Errorf("invalid length for anchorID, expected at least %d bytes, got %d bytes", AnchorIDLength, len(bytes)) } copy(a[:], bytes) diff --git a/output_id.gen.go b/output_id.gen.go index 00ff615b1..77af5e615 100644 --- a/output_id.gen.go +++ b/output_id.gen.go @@ -53,8 +53,8 @@ func IsValidOutputID(b []byte) error { // OutputIDFromBytes returns a new OutputID represented by the passed bytes. func OutputIDFromBytes(b []byte) (OutputID, int, error) { - if err := IsValidOutputID(b); err != nil { - return EmptyOutputID, 0, err + if len(b) < OutputIDLength { + return EmptyOutputID, 0, ierrors.Errorf("invalid length for outputID, expected at least %d bytes, got %d bytes", OutputIDLength, len(b)) } return OutputID(b), OutputIDLength, nil diff --git a/signed_transaction_id.gen.go b/signed_transaction_id.gen.go index d3678ac04..8ecf09f5a 100644 --- a/signed_transaction_id.gen.go +++ b/signed_transaction_id.gen.go @@ -63,8 +63,8 @@ func IsValidSignedTransactionID(b []byte) error { // SignedTransactionIDFromBytes returns a new SignedTransactionID represented by the passed bytes. func SignedTransactionIDFromBytes(b []byte) (SignedTransactionID, int, error) { - if err := IsValidSignedTransactionID(b); err != nil { - return EmptySignedTransactionID, 0, err + if len(b) < SignedTransactionIDLength { + return EmptySignedTransactionID, 0, ierrors.Errorf("invalid length for signedTransactionID, expected at least %d bytes, got %d bytes", SignedTransactionIDLength, len(b)) } return SignedTransactionID(b), SignedTransactionIDLength, nil diff --git a/slot_index.gen.go b/slot_index.gen.go index 0d9bee106..b8518be5f 100644 --- a/slot_index.gen.go +++ b/slot_index.gen.go @@ -20,7 +20,7 @@ type SlotIndex uint32 func SlotIndexFromBytes(b []byte) (SlotIndex, int, error) { if len(b) < SlotIndexLength { - return 0, 0, ierrors.New("invalid slot index size") + return 0, 0, ierrors.Errorf("invalid length for slot index, expected at least %d bytes, got %d bytes", SlotIndexLength, len(b)) } return SlotIndex(binary.LittleEndian.Uint32(b)), SlotIndexLength, nil diff --git a/transaction_id.gen.go b/transaction_id.gen.go index 239b0bf2b..85b702b00 100644 --- a/transaction_id.gen.go +++ b/transaction_id.gen.go @@ -63,8 +63,8 @@ func IsValidTransactionID(b []byte) error { // TransactionIDFromBytes returns a new TransactionID represented by the passed bytes. func TransactionIDFromBytes(b []byte) (TransactionID, int, error) { - if err := IsValidTransactionID(b); err != nil { - return EmptyTransactionID, 0, err + if len(b) < TransactionIDLength { + return EmptyTransactionID, 0, ierrors.Errorf("invalid length for transactionID, expected at least %d bytes, got %d bytes", TransactionIDLength, len(b)) } return TransactionID(b), TransactionIDLength, nil From 2637ec751920d42c656bde25c54f203e2d557e9c Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 11 Mar 2024 19:57:55 +0800 Subject: [PATCH 39/48] Remove accidental addition --- testlog.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 testlog.txt diff --git a/testlog.txt b/testlog.txt deleted file mode 100644 index e69de29bb..000000000 From 097f1b2e1041b4bbb9b34f5c5b7ac3ad54d040fb Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Tue, 12 Mar 2024 10:36:34 +0800 Subject: [PATCH 40/48] Call `Error()` on errors before formatting --- vm/nova/vm.go | 2 +- vm/vm.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/vm/nova/vm.go b/vm/nova/vm.go index 3f92fb79b..111667945 100644 --- a/vm/nova/vm.go +++ b/vm/nova/vm.go @@ -49,7 +49,7 @@ func NewVMParamsWorkingSet(api iotago.API, t *iotago.Transaction, resolvedInputs txID, err := workingSet.Tx.ID() if err != nil { - panic(fmt.Sprintf("transaction ID computation should have succeeded: %s", err)) + panic(fmt.Sprintf("transaction ID computation should have succeeded: %s", err.Error())) } workingSet.InChains = utxoInputsSet.ChainInputSet() diff --git a/vm/vm.go b/vm/vm.go index d4e17f800..115eee1eb 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -433,12 +433,12 @@ func ValidateUnlocks(signedTransaction *iotago.SignedTransaction, resolvedInputs txID, err := signedTransaction.Transaction.ID() if err != nil { - panic(fmt.Sprintf("transaction ID computation should have succeeded: %s", err)) + panic(fmt.Sprintf("transaction ID computation should have succeeded: %s", err.Error())) } essenceMsgToSign, err := signedTransaction.Transaction.SigningMessage() if err != nil { - panic(fmt.Sprintf("signing message computation should have succeeded: %s", err)) + panic(fmt.Sprintf("signing message computation should have succeeded: %s", err.Error())) } unlockedAddrsSet := &unlockedAddressesSet{ From 4fcc275daefae83477ae44146142816a7c0cbd81 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Tue, 12 Mar 2024 10:39:29 +0800 Subject: [PATCH 41/48] Revert newAddress panic change --- address.go | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/address.go b/address.go index 23dbaa964..49e8c3ffa 100644 --- a/address.go +++ b/address.go @@ -173,24 +173,24 @@ type UTXOIDChainID interface { FromOutputID(id OutputID) ChainID } -func newAddress(addressType AddressType) Address { +func newAddress(addressType AddressType) (Address, error) { switch addressType { case AddressEd25519: - return &Ed25519Address{} + return &Ed25519Address{}, nil case AddressAccount: - return &AccountAddress{} + return &AccountAddress{}, nil case AddressNFT: - return &NFTAddress{} + return &NFTAddress{}, nil case AddressAnchor: - return &AnchorAddress{} + return &AnchorAddress{}, nil case AddressImplicitAccountCreation: - return &ImplicitAccountCreationAddress{} + return &ImplicitAccountCreationAddress{}, nil case AddressMulti: - return &MultiAddress{} + return &MultiAddress{}, nil case AddressRestricted: - return &RestrictedAddress{} + return &RestrictedAddress{}, nil default: - panic(fmt.Sprintf("unknown address type %d", addressType)) + return nil, ierrors.Errorf("unknown address type %d", addressType) } } @@ -252,7 +252,10 @@ func ParseBech32(s string) (NetworkPrefix, Address, error) { } } - addr := newAddress(addrType) + addr, err := newAddress(addrType) + if err != nil { + return "", nil, err + } serixAPI := CommonSerixAPI() n, err := serixAPI.Decode(context.TODO(), addrData, addr) @@ -307,6 +310,6 @@ func AddressFromReader(reader io.ReadSeeker) (Address, error) { return RestrictedAddressFromReader(reader) default: - panic(fmt.Sprintf("unknown address type %d", addressType)) + return nil, ierrors.Errorf("unknown address type %d", addressType) } } From fd38e63a10a7f473764169d01769d5c75cf6c52f Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Tue, 12 Mar 2024 11:04:35 +0800 Subject: [PATCH 42/48] Print address in bech32 in errors --- vm/nova/vm.go | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/vm/nova/vm.go b/vm/nova/vm.go index 111667945..24d446bd9 100644 --- a/vm/nova/vm.go +++ b/vm/nova/vm.go @@ -658,7 +658,7 @@ func anchorSTVF(vmParams *vm.Params, input *vm.ChainOutputWithIDs, transType iot return ierrors.Wrapf(err, " anchor %s", next.AnchorID) } case iotago.ChainTransitionTypeStateChange: - if err := anchorStateChangeValid(input, next); err != nil { + if err := anchorStateChangeValid(input, next, vmParams); err != nil { //nolint:forcetypeassert // we can safely assume that this is an AnchorOutput a := input.Output.(*iotago.AnchorOutput) @@ -686,7 +686,7 @@ func anchorGenesisValid(vmParams *vm.Params, current *iotago.AnchorOutput, ancho return vm.IsIssuerOnOutputUnlocked(current, vmParams.WorkingSet.UnlockedAddrs) } -func anchorStateChangeValid(input *vm.ChainOutputWithIDs, next *iotago.AnchorOutput) error { +func anchorStateChangeValid(input *vm.ChainOutputWithIDs, next *iotago.AnchorOutput, vmParams *vm.Params) error { //nolint:forcetypeassert // we can safely assume that this is an AnchorOutput current := input.Output.(*iotago.AnchorOutput) @@ -708,7 +708,7 @@ func anchorStateChangeValid(input *vm.ChainOutputWithIDs, next *iotago.AnchorOut return anchorGovernanceSTVF(input, next) } - return anchorStateSTVF(input, next) + return anchorStateSTVF(input, next, vmParams) } func anchorGovernanceSTVF(input *vm.ChainOutputWithIDs, next *iotago.AnchorOutput) error { @@ -729,14 +729,22 @@ func anchorGovernanceSTVF(input *vm.ChainOutputWithIDs, next *iotago.AnchorOutpu return nil } -func anchorStateSTVF(input *vm.ChainOutputWithIDs, next *iotago.AnchorOutput) error { +func anchorStateSTVF(input *vm.ChainOutputWithIDs, next *iotago.AnchorOutput, vmParams *vm.Params) error { //nolint:forcetypeassert // we can safely assume that this is an AnchorOutput current := input.Output.(*iotago.AnchorOutput) switch { case !current.StateController().Equal(next.StateController()): - return ierrors.WithMessagef(iotago.ErrAnchorInvalidStateTransition, "state controller changed, in %v / out %v", current.StateController(), next.StateController()) + return ierrors.WithMessagef(iotago.ErrAnchorInvalidStateTransition, + "state controller changed, in %s / out %s", + current.StateController().Bech32(vmParams.API.ProtocolParameters().Bech32HRP()), + next.StateController().Bech32(vmParams.API.ProtocolParameters().Bech32HRP()), + ) case !current.GovernorAddress().Equal(next.GovernorAddress()): - return ierrors.WithMessagef(iotago.ErrAnchorInvalidStateTransition, "governance controller changed, in %v / out %v", current.GovernorAddress(), next.GovernorAddress()) + return ierrors.WithMessagef(iotago.ErrAnchorInvalidStateTransition, + "governance controller changed, in %s / out %s", + current.GovernorAddress().Bech32(vmParams.API.ProtocolParameters().Bech32HRP()), + next.GovernorAddress().Bech32(vmParams.API.ProtocolParameters().Bech32HRP()), + ) case current.StateIndex+1 != next.StateIndex: return ierrors.WithMessagef(iotago.ErrAnchorInvalidStateTransition, "state index %d on the input side but %d on the output side", current.StateIndex, next.StateIndex) } From 8bb8e21ae5abe5ce45fef33ab4bcaa807ea3a1a8 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Tue, 12 Mar 2024 11:13:49 +0800 Subject: [PATCH 43/48] Remove unnecessary error wrapping in chain stvfs --- vm/nova/vm.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/vm/nova/vm.go b/vm/nova/vm.go index 24d446bd9..e4f8afc95 100644 --- a/vm/nova/vm.go +++ b/vm/nova/vm.go @@ -655,21 +655,15 @@ func anchorSTVF(vmParams *vm.Params, input *vm.ChainOutputWithIDs, transType iot switch transType { case iotago.ChainTransitionTypeGenesis: if err := anchorGenesisValid(vmParams, next, true); err != nil { - return ierrors.Wrapf(err, " anchor %s", next.AnchorID) + return err } case iotago.ChainTransitionTypeStateChange: if err := anchorStateChangeValid(input, next, vmParams); err != nil { - //nolint:forcetypeassert // we can safely assume that this is an AnchorOutput - a := input.Output.(*iotago.AnchorOutput) - - return ierrors.Wrapf(err, "anchor %s", a.AnchorID) + return err } case iotago.ChainTransitionTypeDestroy: if err := anchorDestructionValid(vmParams); err != nil { - //nolint:forcetypeassert // we can safely assume that this is an AnchorOutput - a := input.Output.(*iotago.AnchorOutput) - - return ierrors.Wrapf(err, "anchor %s", a.AnchorID) + return err } default: panic("unknown chain transition type in AnchorOutput") @@ -771,19 +765,19 @@ func foundrySTVF(vmParams *vm.Params, input *vm.ChainOutputWithIDs, transType io switch transType { case iotago.ChainTransitionTypeGenesis: if err := foundryGenesisValid(vmParams, next, next.MustFoundryID(), outSums); err != nil { - return ierrors.Wrapf(err, "foundry %s, token %s", next.MustFoundryID(), next.MustNativeTokenID()) + return err } case iotago.ChainTransitionTypeStateChange: //nolint:forcetypeassert // we can safely assume that this is a FoundryOutput current := input.Output.(*iotago.FoundryOutput) if err := foundryStateChangeValid(current, next, inSums, outSums); err != nil { - return ierrors.Wrapf(err, "foundry %s, token %s", current.MustFoundryID(), current.MustNativeTokenID()) + return err } case iotago.ChainTransitionTypeDestroy: //nolint:forcetypeassert // we can safely assume that this is a FoundryOutput current := input.Output.(*iotago.FoundryOutput) if err := foundryDestructionValid(vmParams, current, inSums, outSums); err != nil { - return ierrors.Wrapf(err, "foundry %s, token %s", current.MustFoundryID(), current.MustNativeTokenID()) + return err } default: panic("unknown chain transition type in FoundryOutput") From ae4a038db80dc3da893e4af0a1381798f5027185 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Tue, 12 Mar 2024 11:26:51 +0800 Subject: [PATCH 44/48] Add note on why we panic --- input.go | 8 ++++++++ input_context.go | 8 ++++++++ output.go | 12 ++++++++++++ transaction.go | 20 ++++++++++++++++++++ unlock.go | 8 ++++++++ 5 files changed, 56 insertions(+) diff --git a/input.go b/input.go index 5c01ee426..eaae42f48 100644 --- a/input.go +++ b/input.go @@ -94,6 +94,10 @@ func InputsSyntacticalUnique() ElementValidationFunc[Input] { } utxoSet[k] = index default: + // We're switching on the Go input type here, so we can only run into the default case + // if we added a new input type and have not handled it above or a user constructed a type + // implementing the interface (only possible when iota.go is used as a library). + // In both cases we want to panic. panic("all supported input types should be handled above") } @@ -111,6 +115,10 @@ func InputsSyntacticalIndicesWithinBounds() ElementValidationFunc[Input] { return ierrors.WithMessagef(ErrRefUTXOIndexInvalid, "input %d", index) } default: + // We're switching on the Go input type here, so we can only run into the default case + // if we added a new input type and have not handled it above or a user constructed a type + // implementing the interface (only possible when iota.go is used as a library). + // In both cases we want to panic. panic("all supported input types should be handled above") } diff --git a/input_context.go b/input_context.go index 71a962489..000e57885 100644 --- a/input_context.go +++ b/input_context.go @@ -101,6 +101,10 @@ func ContextInputsRewardInputMaxIndex(inputsCount uint16) ElementValidationFunc[ index, utxoIndex, inputsCount) } default: + // We're switching on the Go context input type here, so we can only run into the default case + // if we added a new context input type and have not handled it above or a user constructed a type + // implementing the interface (only possible when iota.go is used as a library). + // In both cases we want to panic. panic("all supported context input types should be handled above") } @@ -128,6 +132,10 @@ func ContextInputsCommitmentInputRequirement() ElementValidationFunc[ContextInpu return ierrors.WithMessagef(ErrCommitmentInputMissing, "reward input at index %d requires a commitment input", index) } default: + // We're switching on the Go context input type here, so we can only run into the default case + // if we added a new context input type and have not handled it above or a user constructed a type + // implementing the interface (only possible when iota.go is used as a library). + // In both cases we want to panic. panic("all supported context input types should be handled above") } diff --git a/output.go b/output.go index ec5055313..a276e5f8c 100644 --- a/output.go +++ b/output.go @@ -668,6 +668,10 @@ func OutputsSyntacticalImplicitAccountCreationAddress() ElementValidationFunc[Ou return ierrors.WithMessagef(ErrImplicitAccountCreationAddressInInvalidOutput, "output %d", index) } default: + // We're switching on the Go output type here, so we can only run into the default case + // if we added a new output type and have not handled it above or a user constructed a type + // implementing the interface (only possible when iota.go is used as a library). + // In both cases we want to panic. panic("all supported output types should be handled above") } @@ -717,6 +721,10 @@ func OutputsSyntacticalUnlockConditionLexicalOrderAndUniqueness() ElementValidat } } default: + // We're switching on the Go output type here, so we can only run into the default case + // if we added a new output type and have not handled it above or a user constructed a type + // implementing the interface (only possible when iota.go is used as a library). + // In both cases we want to panic. panic("all supported output types should be handled above") } @@ -786,6 +794,10 @@ func OutputsSyntacticalFeaturesLexicalOrderAndUniqueness() ElementValidationFunc // This output does not have features. return nil default: + // We're switching on the Go output type here, so we can only run into the default case + // if we added a new output type and have not handled it above or a user constructed a type + // implementing the interface (only possible when iota.go is used as a library). + // In both cases we want to panic. panic("all supported output types should be handled above") } diff --git a/transaction.go b/transaction.go index 2a311a654..544f55f0a 100644 --- a/transaction.go +++ b/transaction.go @@ -140,6 +140,10 @@ func (t *Transaction) Inputs() []*UTXOInput { case *UTXOInput: references = append(references, castInput) default: + // We're switching on the Go input type here, so we can only run into the default case + // if we added a new input type and have not handled it above or a user constructed a type + // implementing the interface (only possible when iota.go is used as a library). + // In both cases we want to panic. panic("all supported input types should be handled above") } } @@ -168,6 +172,10 @@ func (t *Transaction) ContextInputs() TransactionContextInputs { case *CommitmentInput, *BlockIssuanceCreditInput, *RewardInput: references = append(references, castInput) default: + // We're switching on the Go context input type here, so we can only run into the default case + // if we added a new context input type and have not handled it above or a user constructed a type + // implementing the interface (only possible when iota.go is used as a library). + // In both cases we want to panic. panic("all supported context input types should be handled above") } } @@ -184,6 +192,10 @@ func (t *Transaction) BICInputs() []*BlockIssuanceCreditInput { case *CommitmentInput, *RewardInput: // ignore this type default: + // We're switching on the Go context input type here, so we can only run into the default case + // if we added a new context input type and have not handled it above or a user constructed a type + // implementing the interface (only possible when iota.go is used as a library). + // In both cases we want to panic. panic("all supported context input types should be handled above") } } @@ -200,6 +212,10 @@ func (t *Transaction) RewardInputs() []*RewardInput { case *CommitmentInput, *BlockIssuanceCreditInput: // ignore this type default: + // We're switching on the Go context input type here, so we can only run into the default case + // if we added a new context input type and have not handled it above or a user constructed a type + // implementing the interface (only possible when iota.go is used as a library). + // In both cases we want to panic. panic("all supported context input types should be handled above") } } @@ -216,6 +232,10 @@ func (t *Transaction) CommitmentInput() *CommitmentInput { case *CommitmentInput: return castInput default: + // We're switching on the Go context input type here, so we can only run into the default case + // if we added a new context input type and have not handled it above or a user constructed a type + // implementing the interface (only possible when iota.go is used as a library). + // In both cases we want to panic. panic("all supported context input types should be handled above") } } diff --git a/unlock.go b/unlock.go index fe364b2d5..edc0fa149 100644 --- a/unlock.go +++ b/unlock.go @@ -250,6 +250,10 @@ func SignaturesUniqueAndReferenceUnlocksValidator(api API) UnlockValidatorFunc { continue default: + // We're switching on the Go unlock type here, so we can only run into the default case + // if we added a new unlock type and have not handled it above or a user constructed a type + // implementing the interface (only possible when iota.go is used as a library). + // In both cases we want to panic. panic("all supported unlock types should be handled above") } } @@ -260,6 +264,10 @@ func SignaturesUniqueAndReferenceUnlocksValidator(api API) UnlockValidatorFunc { return ierrors.WithMessagef(ErrEmptyUnlockOutsideMultiUnlock, "unlock at index %d is invalid", index) default: + // We're switching on the Go unlock type here, so we can only run into the default case + // if we added a new unlock type and have not handled it above or a user constructed a type + // implementing the interface (only possible when iota.go is used as a library). + // In both cases we want to panic. panic("all supported unlock types should be handled above") } From 330e0e9702b35765fc8460a5666ff56221825759 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Tue, 12 Mar 2024 12:18:20 +0800 Subject: [PATCH 45/48] Fix timelock expiration error message --- unlock_cond.go | 2 +- vm/nova/vm.go | 2 +- vm/vm.go | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/unlock_cond.go b/unlock_cond.go index d0677d798..9518197fd 100644 --- a/unlock_cond.go +++ b/unlock_cond.go @@ -329,7 +329,7 @@ func (f UnlockConditionSet) TimelocksExpired(futureBoundedSlot SlotIndex) error } if futureBoundedSlot < timelock.Slot { - return ierrors.WithMessagef(ErrTimelockNotExpired, "slot cond is %d, while tx creation slot could be up to %d", timelock.Slot, futureBoundedSlot) + return ierrors.WithMessagef(ErrTimelockNotExpired, "timelock expires in slot %d but the future bounded slot is only %d", timelock.Slot, futureBoundedSlot) } return nil diff --git a/vm/nova/vm.go b/vm/nova/vm.go index e4f8afc95..d1ef990ea 100644 --- a/vm/nova/vm.go +++ b/vm/nova/vm.go @@ -482,7 +482,7 @@ func accountStakingGenesisValidation(vmParams *vm.Params, stakingFeat *iotago.St unbondingEpoch := pastBoundedEpoch + vmParams.API.ProtocolParameters().StakingUnbondingPeriod() if stakingFeat.EndEpoch < unbondingEpoch { - return ierrors.Wrapf(iotago.ErrStakingEndEpochTooEarly, "end epoch %d should be >= %d", stakingFeat.EndEpoch, unbondingEpoch) + return ierrors.WithMessagef(iotago.ErrStakingEndEpochTooEarly, "end epoch %d should be >= %d", stakingFeat.EndEpoch, unbondingEpoch) } return nil diff --git a/vm/vm.go b/vm/vm.go index 115eee1eb..13a8da3ea 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -769,7 +769,7 @@ func ExecFuncBalancedBaseTokens() ExecFunc { // ExecFuncTimelocks validates that the inputs' timelocks are expired. func ExecFuncTimelocks() ExecFunc { return func(_ VirtualMachine, vmParams *Params) error { - for inputIndex, input := range vmParams.WorkingSet.UTXOInputsSet { + for inputID, input := range vmParams.WorkingSet.UTXOInputsSet { if input.UnlockConditionSet().HasTimelockCondition() { commitment := vmParams.WorkingSet.Commitment @@ -778,7 +778,7 @@ func ExecFuncTimelocks() ExecFunc { } futureBoundedIndex := vmParams.FutureBoundedSlotIndex(commitment.Slot) if err := input.UnlockConditionSet().TimelocksExpired(futureBoundedIndex); err != nil { - return ierrors.Wrapf(err, "input at index %d's timelocks are not expired", inputIndex) + return ierrors.Wrapf(err, "timelock of input %s is not expired", inputID.ToHex()) } } } From f315c7579c46adfc934daa4a4b36540944abf9e0 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Tue, 12 Mar 2024 13:38:53 +0800 Subject: [PATCH 46/48] Fix wrap order in sender feature unlock --- vm/vm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/vm.go b/vm/vm.go index 13a8da3ea..930eca145 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -675,7 +675,7 @@ func ExecFuncSenderUnlocked() ExecFunc { // check unlocked sender := resolveUnderlyingAddress(senderFeat.Address) if _, isUnlocked := vmParams.WorkingSet.UnlockedAddrs[sender.Key()]; !isUnlocked { - return ierrors.WithMessagef(iotago.ErrSenderFeatureNotUnlocked, "output %d", outputIndex) + return ierrors.Wrapf(iotago.ErrSenderFeatureNotUnlocked, "output %d", outputIndex) } } From 62c1dd737fe2672cb65c680a1f1e354353d11fe4 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Tue, 12 Mar 2024 16:05:17 +0800 Subject: [PATCH 47/48] Use hexified IDs in error messages --- vm/nova/vm.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/vm/nova/vm.go b/vm/nova/vm.go index d1ef990ea..99abdb74c 100644 --- a/vm/nova/vm.go +++ b/vm/nova/vm.go @@ -798,14 +798,14 @@ func foundryGenesisValid(vmParams *vm.Params, current *iotago.FoundryOutput, thi inAccount, ok := vmParams.WorkingSet.InChains[accountID] if !ok { return ierrors.WithMessagef(iotago.ErrFoundryTransitionWithoutAccount, - "missing input transitioning account output %s for new foundry output %s", accountID, thisFoundryID, + "missing input transitioning account output %s for new foundry output %s", accountID.ToHex(), thisFoundryID.ToHex(), ) } outAccount, ok := vmParams.WorkingSet.OutChains[accountID] if !ok { return ierrors.WithMessagef(iotago.ErrFoundryTransitionWithoutAccount, - "missing output transitioning account output %s for new foundry output %s", accountID, thisFoundryID, + "missing output transitioning account output %s for new foundry output %s", accountID.ToHex(), thisFoundryID.ToHex(), ) } @@ -820,7 +820,7 @@ func foundrySerialNumberValid(vmParams *vm.Params, current *iotago.FoundryOutput if startSerial >= current.SerialNumber || current.SerialNumber > endIncSerial { return ierrors.WithMessagef( iotago.ErrFoundrySerialInvalid, - "new foundry output %s's serial number %d is not between the foundry counter interval of [%d,%d)", thisFoundryID, current.SerialNumber, startSerial, endIncSerial, + "new foundry output %s's serial number %d is not between the foundry counter interval of [%d,%d)", thisFoundryID.ToHex(), current.SerialNumber, startSerial, endIncSerial, ) } @@ -853,7 +853,7 @@ func foundrySerialNumberValid(vmParams *vm.Params, current *iotago.FoundryOutput if otherFoundryOutput.SerialNumber >= current.SerialNumber { return ierrors.WithMessagef( iotago.ErrFoundrySerialInvalid, - "new foundry output %s at index %d has bigger equal serial number than this foundry %s", otherFoundryID, outputIndex, thisFoundryID, + "new foundry output %s at index %d has bigger equal serial number than this foundry %s", otherFoundryID.ToHex(), outputIndex, thisFoundryID.ToHex(), ) } } @@ -874,7 +874,7 @@ func foundryStateChangeValid(current *iotago.FoundryOutput, next *iotago.Foundry // no matching foundry to be found to validate the state transition against if current.MustFoundryID() != next.MustFoundryID() { // impossible invariant as the STVF should be called via the matching next foundry output - panic(fmt.Sprintf("foundry IDs mismatch in state transition validation function: have %s got %s", current.MustFoundryID(), next.MustFoundryID())) + panic(fmt.Sprintf("foundry IDs mismatch in state transition validation function: have %s got %s", current.MustFoundryID().ToHex(), next.MustFoundryID().ToHex())) } nativeTokenID := current.MustNativeTokenID() From 74f8cf10c361ae0a3fa6f1beed98cbb15b876f5a Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Wed, 13 Mar 2024 14:57:35 +0800 Subject: [PATCH 48/48] Use String() for account ids for alias support --- vm/nova/vm.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/nova/vm.go b/vm/nova/vm.go index 99abdb74c..5c05aec72 100644 --- a/vm/nova/vm.go +++ b/vm/nova/vm.go @@ -798,14 +798,14 @@ func foundryGenesisValid(vmParams *vm.Params, current *iotago.FoundryOutput, thi inAccount, ok := vmParams.WorkingSet.InChains[accountID] if !ok { return ierrors.WithMessagef(iotago.ErrFoundryTransitionWithoutAccount, - "missing input transitioning account output %s for new foundry output %s", accountID.ToHex(), thisFoundryID.ToHex(), + "missing input transitioning account output %s for new foundry output %s", accountID, thisFoundryID.ToHex(), ) } outAccount, ok := vmParams.WorkingSet.OutChains[accountID] if !ok { return ierrors.WithMessagef(iotago.ErrFoundryTransitionWithoutAccount, - "missing output transitioning account output %s for new foundry output %s", accountID.ToHex(), thisFoundryID.ToHex(), + "missing output transitioning account output %s for new foundry output %s", accountID, thisFoundryID.ToHex(), ) }