Skip to content

Commit

Permalink
Merge pull request #702 from iotaledger/context-input-syntactical
Browse files Browse the repository at this point in the history
Make some Commitment Input checks syntactical
  • Loading branch information
muXxer authored Mar 5, 2024
2 parents b06fbda + 08c8c30 commit f6c86e8
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 14 deletions.
5 changes: 5 additions & 0 deletions feat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@ func TestBlockIssuerFeatureSyntacticValidation(t *testing.T) {
bik3,
}),
}
t.TransactionEssence.ContextInputs = append(t.TransactionEssence.ContextInputs, tpkg.RandCommitmentInput())
},
),
Target: &iotago.SignedTransaction{},
Expand All @@ -442,6 +443,7 @@ func TestBlockIssuerFeatureSyntacticValidation(t *testing.T) {
bik3,
}),
}
t.TransactionEssence.ContextInputs = append(t.TransactionEssence.ContextInputs, tpkg.RandCommitmentInput())
}),
Target: &iotago.SignedTransaction{},
SeriErr: iotago.ErrArrayValidationOrderViolatesLexicalOrder,
Expand All @@ -458,6 +460,7 @@ func TestBlockIssuerFeatureSyntacticValidation(t *testing.T) {
bik2,
}),
}
t.TransactionEssence.ContextInputs = append(t.TransactionEssence.ContextInputs, tpkg.RandCommitmentInput())
}),
Target: &iotago.SignedTransaction{},
SeriErr: iotago.ErrArrayValidationViolatesUniqueness,
Expand All @@ -469,6 +472,7 @@ func TestBlockIssuerFeatureSyntacticValidation(t *testing.T) {
t.Outputs = iotago.TxEssenceOutputs{
accountWithKeys(iotago.BlockIssuerKeys{}),
}
t.TransactionEssence.ContextInputs = append(t.TransactionEssence.ContextInputs, tpkg.RandCommitmentInput())
}),
Target: &iotago.SignedTransaction{},
SeriErr: serializer.ErrArrayValidationMinElementsNotReached,
Expand All @@ -480,6 +484,7 @@ func TestBlockIssuerFeatureSyntacticValidation(t *testing.T) {
t.Outputs = iotago.TxEssenceOutputs{
accountWithKeys(tpkg.RandBlockIssuerKeys(iotago.MaxBlockIssuerKeysCount + 1)),
}
t.TransactionEssence.ContextInputs = append(t.TransactionEssence.ContextInputs, tpkg.RandCommitmentInput())
}),
Target: &iotago.SignedTransaction{},
SeriErr: serializer.ErrArrayValidationMaxElementsExceeded,
Expand Down
24 changes: 24 additions & 0 deletions output.go
Original file line number Diff line number Diff line change
Expand Up @@ -886,3 +886,27 @@ func OutputsSyntacticalMetadataFeatureMaxSize() ElementValidationFunc[Output] {
return nil
}
}

// Checks that a Commitment Input is present for
// - Accounts with a Staking Feature.
// - Accounts with a Block Issuer Feature.
// - Delegation Outputs.
func OutputsSyntacticalCommitmentInput(hasCommitmentInput bool) ElementValidationFunc[Output] {
return func(index int, output Output) error {
hasStakingFeature := output.FeatureSet().Staking() != nil
if hasStakingFeature && !hasCommitmentInput {
return ierrors.Wrapf(ErrStakingCommitmentInputMissing, "output %d", index)
}

hasBlockIssuerFeature := output.FeatureSet().BlockIssuer() != nil
if hasBlockIssuerFeature && !hasCommitmentInput {
return ierrors.Wrapf(ErrBlockIssuerCommitmentInputMissing, "output %d", index)
}

if output.Type() == OutputDelegation && !hasCommitmentInput {
return ierrors.Wrapf(ErrDelegationCommitmentInputMissing, "output %d", index)
}

return nil
}
}
2 changes: 2 additions & 0 deletions transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ func (t *Transaction) SyntacticallyValidate(api API) error {
}

var maxManaValue Mana = (1 << protoParams.ManaParameters().BitsCount) - 1
hasCommitmentInput := t.CommitmentInput() != nil

return SyntacticallyValidateOutputs(t.Outputs,
OutputsSyntacticalUnlockConditionLexicalOrderAndUniqueness(),
Expand All @@ -276,6 +277,7 @@ func (t *Transaction) SyntacticallyValidate(api API) error {
OutputsSyntacticalDelegation(),
OutputsSyntacticalAddressRestrictions(),
OutputsSyntacticalImplicitAccountCreationAddress(),
OutputsSyntacticalCommitmentInput(hasCommitmentInput),
)
}

Expand Down
96 changes: 96 additions & 0 deletions transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1260,3 +1260,99 @@ func TestTransactionIDsLexicalOrderAndUniqueness(t *testing.T) {
t.Run(tt.Name, tt.Run)
}
}

func TestCommitmentInputSyntacticalValidation(t *testing.T) {
accountWithFeatures := func(feats iotago.AccountOutputFeatures) *iotago.AccountOutput {
return &iotago.AccountOutput{
Amount: 100_000_000,
UnlockConditions: iotago.AccountOutputUnlockConditions{
&iotago.AddressUnlockCondition{
Address: tpkg.RandAccountAddress(),
},
},
ImmutableFeatures: iotago.AccountOutputImmFeatures{},
Features: feats,
}
}

tests := []*frameworks.DeSerializeTest{
// fail - BlockIssuerFeature on output side without Commitment Input
{
Name: "fail - BlockIssuerFeature on output side without Commitment Input",
Source: tpkg.RandSignedTransaction(tpkg.ZeroCostTestAPI, func(t *iotago.Transaction) {
t.Outputs = iotago.TxEssenceOutputs{
accountWithFeatures(
iotago.AccountOutputFeatures{
&iotago.BlockIssuerFeature{
ExpirySlot: 100,
BlockIssuerKeys: tpkg.RandBlockIssuerKeys(3),
},
},
),
}
// Make sure there are no Context Inputs added by the rand function for this test.
t.TransactionEssence.ContextInputs = nil
}),
Target: &iotago.SignedTransaction{},
SeriErr: iotago.ErrBlockIssuerCommitmentInputMissing,
DeSeriErr: iotago.ErrBlockIssuerCommitmentInputMissing,
},
// fail - StakingFeature on output side without Commitment Input
{
Name: "fail - StakingFeature on output side without Commitment Input",
Source: tpkg.RandSignedTransaction(tpkg.ZeroCostTestAPI, func(t *iotago.Transaction) {
t.Outputs = iotago.TxEssenceOutputs{
accountWithFeatures(
iotago.AccountOutputFeatures{
&iotago.BlockIssuerFeature{
ExpirySlot: 100,
BlockIssuerKeys: tpkg.RandBlockIssuerKeys(3),
},
&iotago.StakingFeature{
StakedAmount: 1,
FixedCost: 1,
StartEpoch: 10,
EndEpoch: 12,
},
},
),
}
// Make sure there are no Context Inputs added by the rand function for this test.
t.TransactionEssence.ContextInputs = nil
}),
Target: &iotago.SignedTransaction{},
SeriErr: iotago.ErrStakingCommitmentInputMissing,
DeSeriErr: iotago.ErrStakingCommitmentInputMissing,
},
// fail - Delegation Output on output side without Commitment Input
{
Name: "fail - Delegation Output on output side without Commitment Input",
Source: tpkg.RandSignedTransaction(tpkg.ZeroCostTestAPI, func(t *iotago.Transaction) {
t.Outputs = iotago.TxEssenceOutputs{
&iotago.DelegationOutput{
Amount: 10,
DelegatedAmount: 10,
DelegationID: tpkg.RandDelegationID(),
ValidatorAddress: tpkg.RandAccountAddress(),
StartEpoch: 10,
EndEpoch: 12,
UnlockConditions: iotago.DelegationOutputUnlockConditions{
&iotago.AddressUnlockCondition{
Address: tpkg.RandEd25519Address(),
},
},
},
}
// Make sure there are no Context Inputs added by the rand function for this test.
t.TransactionEssence.ContextInputs = nil
}),
Target: &iotago.SignedTransaction{},
SeriErr: iotago.ErrDelegationCommitmentInputMissing,
DeSeriErr: iotago.ErrDelegationCommitmentInputMissing,
},
}

for _, tt := range tests {
t.Run(tt.Name, tt.Run)
}
}
5 changes: 2 additions & 3 deletions vm/nova/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ func accountGenesisValid(vmParams *vm.Params, next *iotago.AccountOutput, accoun

if nextBlockIssuerFeat := next.FeatureSet().BlockIssuer(); nextBlockIssuerFeat != nil {
if vmParams.WorkingSet.Commitment == nil {
return ierrors.Join(iotago.ErrInvalidBlockIssuerTransition, iotago.ErrBlockIssuerCommitmentInputMissing)
panic("commitment input should be present for block issuer features on the output side which should be validated syntactically")
}

pastBoundedSlot := vmParams.PastBoundedSlotIndex(vmParams.WorkingSet.Commitment.Slot)
Expand Down Expand Up @@ -485,10 +485,9 @@ func accountStakingSTVF(vmParams *vm.Params, current *iotago.AccountOutput, next
// or one which was effectively removed and added within the same transaction.
// This is allowed as long as the epoch range of the old and new feature are disjoint.
func accountStakingGenesisValidation(vmParams *vm.Params, next *iotago.AccountOutput, stakingFeat *iotago.StakingFeature) error {
// It should already never be nil here, but for 100% safety, we'll check again.
commitment := vmParams.WorkingSet.Commitment
if commitment == nil {
return iotago.ErrStakingCommitmentInputMissing
panic("commitment input should be present for staking features on the output side which should be validated syntactically")
}

pastBoundedSlot := vmParams.PastBoundedSlotIndex(commitment.Slot)
Expand Down
35 changes: 24 additions & 11 deletions vm/nova/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7526,7 +7526,7 @@ func TestTxSemanticImplicitAccountCreationAndTransition(t *testing.T) {
name string
inputs []TestInput
keys []iotago.AddressKeys
resolvedCommitmentInput iotago.Commitment
resolvedCommitmentInput *iotago.Commitment
resolvedBICInputSet vm.BlockIssuanceCreditInputSet
outputs []iotago.Output
wantErr error
Expand Down Expand Up @@ -7686,7 +7686,7 @@ func TestTxSemanticImplicitAccountCreationAndTransition(t *testing.T) {
resolvedBICInputSet: vm.BlockIssuanceCreditInputSet{
accountID1: iotago.BlockIssuanceCredits(0),
},
resolvedCommitmentInput: iotago.Commitment{
resolvedCommitmentInput: &iotago.Commitment{
Slot: commitmentSlot,
},
outputs: []iotago.Output{
Expand Down Expand Up @@ -7735,7 +7735,7 @@ func TestTxSemanticImplicitAccountCreationAndTransition(t *testing.T) {
resolvedBICInputSet: vm.BlockIssuanceCreditInputSet{
accountID1: iotago.BlockIssuanceCredits(0),
},
resolvedCommitmentInput: iotago.Commitment{
resolvedCommitmentInput: &iotago.Commitment{
Slot: commitmentSlot,
},
outputs: []iotago.Output{
Expand Down Expand Up @@ -7792,7 +7792,7 @@ func TestTxSemanticImplicitAccountCreationAndTransition(t *testing.T) {
resolvedBICInputSet: vm.BlockIssuanceCreditInputSet{
accountID1: iotago.BlockIssuanceCredits(0),
},
resolvedCommitmentInput: iotago.Commitment{
resolvedCommitmentInput: &iotago.Commitment{
Slot: commitmentSlot,
},
outputs: []iotago.Output{
Expand Down Expand Up @@ -7829,7 +7829,7 @@ func TestTxSemanticImplicitAccountCreationAndTransition(t *testing.T) {
resolvedBICInputSet: vm.BlockIssuanceCreditInputSet{
accountID1: iotago.BlockIssuanceCredits(0),
},
resolvedCommitmentInput: iotago.Commitment{
resolvedCommitmentInput: &iotago.Commitment{
Slot: commitmentSlot,
},
outputs: []iotago.Output{
Expand Down Expand Up @@ -7862,7 +7862,7 @@ func TestTxSemanticImplicitAccountCreationAndTransition(t *testing.T) {
resolvedBICInputSet: vm.BlockIssuanceCreditInputSet{
accountID1: iotago.BlockIssuanceCredits(0),
},
resolvedCommitmentInput: iotago.Commitment{
resolvedCommitmentInput: &iotago.Commitment{
Slot: commitmentSlot,
},
outputs: []iotago.Output{
Expand Down Expand Up @@ -7909,7 +7909,7 @@ func TestTxSemanticImplicitAccountCreationAndTransition(t *testing.T) {
resolvedBICInputSet: vm.BlockIssuanceCreditInputSet{
accountID1: iotago.BlockIssuanceCredits(0),
},
resolvedCommitmentInput: iotago.Commitment{
resolvedCommitmentInput: &iotago.Commitment{
Slot: commitmentSlot,
},
outputs: []iotago.Output{
Expand Down Expand Up @@ -7965,7 +7965,7 @@ func TestTxSemanticImplicitAccountCreationAndTransition(t *testing.T) {
resolvedBICInputSet: vm.BlockIssuanceCreditInputSet{
accountID1: iotago.BlockIssuanceCredits(0),
},
resolvedCommitmentInput: iotago.Commitment{
resolvedCommitmentInput: &iotago.Commitment{
Slot: commitmentSlot,
},
outputs: []iotago.Output{
Expand Down Expand Up @@ -8022,7 +8022,7 @@ func TestTxSemanticImplicitAccountCreationAndTransition(t *testing.T) {
accountID1: iotago.BlockIssuanceCredits(0),
accountID2: iotago.BlockIssuanceCredits(0),
},
resolvedCommitmentInput: iotago.Commitment{
resolvedCommitmentInput: &iotago.Commitment{
Slot: commitmentSlot,
},
outputs: []iotago.Output{
Expand Down Expand Up @@ -8084,7 +8084,7 @@ func TestTxSemanticImplicitAccountCreationAndTransition(t *testing.T) {
resolvedBICInputSet: vm.BlockIssuanceCreditInputSet{
accountID1: iotago.BlockIssuanceCredits(0),
},
resolvedCommitmentInput: iotago.Commitment{
resolvedCommitmentInput: &iotago.Commitment{
Slot: commitmentSlot,
},
outputs: []iotago.Output{
Expand Down Expand Up @@ -8131,6 +8131,19 @@ func TestTxSemanticImplicitAccountCreationAndTransition(t *testing.T) {
iotago.TransactionCapabilitiesBitMaskWithCapabilities(iotago.WithTransactionCanBurnNativeTokens(true)),
)

// Add the BIC and Commitment Inputs to the TX builder since they are required syntactically.
// Note that this has no effect on the actual test.
for accountID := range tests[idx].resolvedBICInputSet {
txBuilder.AddBlockIssuanceCreditInput(&iotago.BlockIssuanceCreditInput{
AccountID: accountID,
})
}
if tests[idx].resolvedCommitmentInput != nil {
txBuilder.AddCommitmentInput(&iotago.CommitmentInput{
CommitmentID: tests[idx].resolvedCommitmentInput.MustID(),
})
}

for _, input := range tests[idx].inputs {
txBuilder.AddInput(&builder.TxInput{
UnlockTarget: input.unlockTarget,
Expand All @@ -8148,7 +8161,7 @@ func TestTxSemanticImplicitAccountCreationAndTransition(t *testing.T) {
tx := lo.PanicOnErr(txBuilder.Build())

resolvedInputs.BlockIssuanceCreditInputSet = tests[idx].resolvedBICInputSet
resolvedInputs.CommitmentInput = &tests[idx].resolvedCommitmentInput
resolvedInputs.CommitmentInput = tests[idx].resolvedCommitmentInput

t.Run(tt.name, func(t *testing.T) {
var err error
Expand Down

0 comments on commit f6c86e8

Please sign in to comment.