Skip to content

Commit

Permalink
Merge pull request #706 from iotaledger/bif-syntactic-requirement
Browse files Browse the repository at this point in the history
Make Block Issuer Feature when Staking Feature is present requirement syntactic
  • Loading branch information
muXxer authored Mar 7, 2024
2 parents a87b511 + 464b3a1 commit 0798c51
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 183 deletions.
106 changes: 52 additions & 54 deletions api/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,59 +218,58 @@ const (
TxFailureIssuerFeatureNotUnlocked TransactionFailureReason = 27

TxFailureStakingRewardInputMissing TransactionFailureReason = 28
TxFailureStakingBlockIssuerFeatureMissing TransactionFailureReason = 29
TxFailureStakingCommitmentInputMissing TransactionFailureReason = 30
TxFailureStakingRewardClaimingInvalid TransactionFailureReason = 31
TxFailureStakingFeatureRemovedBeforeUnbonding TransactionFailureReason = 32
TxFailureStakingFeatureModifiedBeforeUnbonding TransactionFailureReason = 33
TxFailureStakingStartEpochInvalid TransactionFailureReason = 34
TxFailureStakingEndEpochTooEarly TransactionFailureReason = 35

TxFailureBlockIssuerCommitmentInputMissing TransactionFailureReason = 36
TxFailureBlockIssuanceCreditInputMissing TransactionFailureReason = 37
TxFailureBlockIssuerNotExpired TransactionFailureReason = 38
TxFailureBlockIssuerExpiryTooEarly TransactionFailureReason = 39
TxFailureManaMovedOffBlockIssuerAccount TransactionFailureReason = 40
TxFailureAccountLocked TransactionFailureReason = 41

TxFailureTimelockCommitmentInputMissing TransactionFailureReason = 42
TxFailureTimelockNotExpired TransactionFailureReason = 43

TxFailureExpirationCommitmentInputMissing TransactionFailureReason = 44
TxFailureExpirationNotUnlockable TransactionFailureReason = 45

TxFailureReturnAmountNotFulFilled TransactionFailureReason = 46

TxFailureNewChainOutputHasNonZeroedID TransactionFailureReason = 47
TxFailureChainOutputImmutableFeaturesChanged TransactionFailureReason = 48

TxFailureImplicitAccountDestructionDisallowed TransactionFailureReason = 49
TxFailureMultipleImplicitAccountCreationAddresses TransactionFailureReason = 50

TxFailureAccountInvalidFoundryCounter TransactionFailureReason = 51

TxFailureAnchorInvalidStateTransition TransactionFailureReason = 52
TxFailureAnchorInvalidGovernanceTransition TransactionFailureReason = 53

TxFailureFoundryTransitionWithoutAccount TransactionFailureReason = 54
TxFailureFoundrySerialInvalid TransactionFailureReason = 55

TxFailureDelegationCommitmentInputMissing TransactionFailureReason = 56
TxFailureDelegationRewardInputMissing TransactionFailureReason = 57
TxFailureDelegationRewardsClaimingInvalid TransactionFailureReason = 58
TxFailureDelegationOutputTransitionedTwice TransactionFailureReason = 59
TxFailureDelegationModified TransactionFailureReason = 60
TxFailureDelegationStartEpochInvalid TransactionFailureReason = 61
TxFailureDelegationAmountMismatch TransactionFailureReason = 62
TxFailureDelegationEndEpochNotZero TransactionFailureReason = 63
TxFailureDelegationEndEpochInvalid TransactionFailureReason = 64

TxFailureCapabilitiesNativeTokenBurningNotAllowed TransactionFailureReason = 65
TxFailureCapabilitiesManaBurningNotAllowed TransactionFailureReason = 66
TxFailureCapabilitiesAccountDestructionNotAllowed TransactionFailureReason = 67
TxFailureCapabilitiesAnchorDestructionNotAllowed TransactionFailureReason = 68
TxFailureCapabilitiesFoundryDestructionNotAllowed TransactionFailureReason = 69
TxFailureCapabilitiesNFTDestructionNotAllowed TransactionFailureReason = 70
TxFailureStakingCommitmentInputMissing TransactionFailureReason = 29
TxFailureStakingRewardClaimingInvalid TransactionFailureReason = 30
TxFailureStakingFeatureRemovedBeforeUnbonding TransactionFailureReason = 31
TxFailureStakingFeatureModifiedBeforeUnbonding TransactionFailureReason = 32
TxFailureStakingStartEpochInvalid TransactionFailureReason = 33
TxFailureStakingEndEpochTooEarly TransactionFailureReason = 34

TxFailureBlockIssuerCommitmentInputMissing TransactionFailureReason = 35
TxFailureBlockIssuanceCreditInputMissing TransactionFailureReason = 36
TxFailureBlockIssuerNotExpired TransactionFailureReason = 37
TxFailureBlockIssuerExpiryTooEarly TransactionFailureReason = 38
TxFailureManaMovedOffBlockIssuerAccount TransactionFailureReason = 39
TxFailureAccountLocked TransactionFailureReason = 40

TxFailureTimelockCommitmentInputMissing TransactionFailureReason = 41
TxFailureTimelockNotExpired TransactionFailureReason = 42

TxFailureExpirationCommitmentInputMissing TransactionFailureReason = 43
TxFailureExpirationNotUnlockable TransactionFailureReason = 44

TxFailureReturnAmountNotFulFilled TransactionFailureReason = 45

TxFailureNewChainOutputHasNonZeroedID TransactionFailureReason = 46
TxFailureChainOutputImmutableFeaturesChanged TransactionFailureReason = 47

TxFailureImplicitAccountDestructionDisallowed TransactionFailureReason = 48
TxFailureMultipleImplicitAccountCreationAddresses TransactionFailureReason = 49

TxFailureAccountInvalidFoundryCounter TransactionFailureReason = 50

TxFailureAnchorInvalidStateTransition TransactionFailureReason = 51
TxFailureAnchorInvalidGovernanceTransition TransactionFailureReason = 52

TxFailureFoundryTransitionWithoutAccount TransactionFailureReason = 53
TxFailureFoundrySerialInvalid TransactionFailureReason = 54

TxFailureDelegationCommitmentInputMissing TransactionFailureReason = 55
TxFailureDelegationRewardInputMissing TransactionFailureReason = 56
TxFailureDelegationRewardsClaimingInvalid TransactionFailureReason = 57
TxFailureDelegationOutputTransitionedTwice TransactionFailureReason = 58
TxFailureDelegationModified TransactionFailureReason = 59
TxFailureDelegationStartEpochInvalid TransactionFailureReason = 60
TxFailureDelegationAmountMismatch TransactionFailureReason = 61
TxFailureDelegationEndEpochNotZero TransactionFailureReason = 62
TxFailureDelegationEndEpochInvalid TransactionFailureReason = 63

TxFailureCapabilitiesNativeTokenBurningNotAllowed TransactionFailureReason = 64
TxFailureCapabilitiesManaBurningNotAllowed TransactionFailureReason = 65
TxFailureCapabilitiesAccountDestructionNotAllowed TransactionFailureReason = 66
TxFailureCapabilitiesAnchorDestructionNotAllowed TransactionFailureReason = 67
TxFailureCapabilitiesFoundryDestructionNotAllowed TransactionFailureReason = 68
TxFailureCapabilitiesNFTDestructionNotAllowed TransactionFailureReason = 69

TxFailureSemanticValidationFailed TransactionFailureReason = 255
)
Expand Down Expand Up @@ -337,7 +336,6 @@ var txErrorsFailureReasonMap = map[error]TransactionFailureReason{

// staking feature
iotago.ErrStakingRewardInputMissing: TxFailureStakingRewardInputMissing,
iotago.ErrStakingBlockIssuerFeatureMissing: TxFailureStakingBlockIssuerFeatureMissing,
iotago.ErrStakingCommitmentInputMissing: TxFailureStakingCommitmentInputMissing,
iotago.ErrStakingRewardClaimingInvalid: TxFailureStakingRewardClaimingInvalid,
iotago.ErrStakingFeatureRemovedBeforeUnbonding: TxFailureStakingFeatureRemovedBeforeUnbonding,
Expand Down
2 changes: 1 addition & 1 deletion api/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ func Test_CoreAPIJSONSerialization(t *testing.T) {
"transactionId": "0x010000000000000000000000000000000000000000000000000000000000000000000000",
"transactionState": "failed",
"earliestAttachmentSlot": 5,
"transactionFailureReason": 58,
"transactionFailureReason": 57,
"transactionFailureDetails": "details"
}`,
},
Expand Down
7 changes: 6 additions & 1 deletion output.go
Original file line number Diff line number Diff line change
Expand Up @@ -476,10 +476,15 @@ func OutputsSyntacticalAccount() ElementValidationFunc[Output] {
return ierrors.WithMessagef(ErrAccountOutputCyclicAddress, "output %d", index)
}

if stakingFeat := accountOutput.FeatureSet().Staking(); stakingFeat != nil {
accountFeatures := accountOutput.FeatureSet()
if stakingFeat := accountFeatures.Staking(); stakingFeat != nil {
if accountOutput.Amount < stakingFeat.StakedAmount {
return ierrors.WithMessagef(ErrAccountOutputAmountLessThanStakedAmount, "output %d", index)
}

if accountFeatures.BlockIssuer() == nil {
return ierrors.WithMessagef(ErrStakingBlockIssuerFeatureMissing, "output %d", index)
}
}

return nil
Expand Down
57 changes: 49 additions & 8 deletions output_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,11 @@ func TestOutputsSyntacticalNativeTokensCount(t *testing.T) {
}

func TestOutputsSyntacticalAccount(t *testing.T) {
exampleBlockIssuerFeature := &iotago.BlockIssuerFeature{
ExpirySlot: 3,
BlockIssuerKeys: tpkg.RandBlockIssuerKeys(2),
}

tests := []struct {
name string
outputs iotago.Outputs[iotago.Output]
Expand Down Expand Up @@ -567,6 +572,7 @@ func TestOutputsSyntacticalAccount(t *testing.T) {
&iotago.AddressUnlockCondition{Address: tpkg.RandAccountAddress()},
},
Features: iotago.AccountOutputFeatures{
exampleBlockIssuerFeature,
&iotago.StakingFeature{StakedAmount: OneIOTA},
},
},
Expand All @@ -584,6 +590,7 @@ func TestOutputsSyntacticalAccount(t *testing.T) {
&iotago.AddressUnlockCondition{Address: tpkg.RandAccountAddress()},
},
Features: iotago.AccountOutputFeatures{
exampleBlockIssuerFeature,
&iotago.StakingFeature{StakedAmount: OneIOTA},
},
},
Expand All @@ -601,25 +608,59 @@ func TestOutputsSyntacticalAccount(t *testing.T) {
&iotago.AddressUnlockCondition{Address: tpkg.RandAccountAddress()},
},
Features: iotago.AccountOutputFeatures{
exampleBlockIssuerFeature,
&iotago.StakingFeature{StakedAmount: OneIOTA + 1},
},
},
},
wantErr: iotago.ErrAccountOutputAmountLessThanStakedAmount,
},
{
name: "ok - staking feature present with block issuer feature",
outputs: iotago.Outputs[iotago.Output]{
&iotago.AccountOutput{
Amount: OneIOTA,
AccountID: tpkg.Rand32ByteArray(),
FoundryCounter: 1337,
UnlockConditions: iotago.AccountOutputUnlockConditions{
&iotago.AddressUnlockCondition{Address: tpkg.RandAccountAddress()},
},
Features: iotago.AccountOutputFeatures{
exampleBlockIssuerFeature,
&iotago.StakingFeature{StakedAmount: OneIOTA},
},
},
},
wantErr: nil,
},
{
name: "fail - staking feature present without block issuer feature",
outputs: iotago.Outputs[iotago.Output]{
&iotago.AccountOutput{
Amount: OneIOTA,
AccountID: tpkg.Rand32ByteArray(),
FoundryCounter: 1337,
UnlockConditions: iotago.AccountOutputUnlockConditions{
&iotago.AddressUnlockCondition{Address: tpkg.RandAccountAddress()},
},
Features: iotago.AccountOutputFeatures{
&iotago.StakingFeature{StakedAmount: OneIOTA},
},
},
},
wantErr: iotago.ErrStakingBlockIssuerFeatureMissing,
},
}
valFunc := iotago.OutputsSyntacticalAccount()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
var runErr error
for index, output := range tt.outputs {
if err := valFunc(index, output); err != nil {
runErr = err
}
var runErr error
for index, output := range tt.outputs {
if err := valFunc(index, output); err != nil {
runErr = err
}
require.ErrorIs(t, runErr, tt.wantErr)
})
}
require.ErrorIs(t, runErr, tt.wantErr)
})
}
}
Expand Down
102 changes: 0 additions & 102 deletions vm/nova/stvf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,46 +350,6 @@ func TestAccountOutput_ValidateStateTransition(t *testing.T) {
},
wantErr: iotago.ErrStakingEndEpochTooEarly,
},
{
name: "fail - staking feature without block issuer feature",
next: &iotago.AccountOutput{
Amount: 100,
AccountID: iotago.AccountID{},
UnlockConditions: iotago.AccountOutputUnlockConditions{
&iotago.AddressUnlockCondition{Address: tpkg.RandEd25519Address()},
},
Features: iotago.AccountOutputFeatures{
&iotago.StakingFeature{
StakedAmount: 50,
FixedCost: 5,
StartEpoch: currentEpoch,
EndEpoch: iotago.MaxEpochIndex,
},
},
},
input: nil,
transType: iotago.ChainTransitionTypeGenesis,
svCtx: &vm.Params{
API: tpkg.ZeroCostTestAPI,
WorkingSet: &vm.WorkingSet{
Commitment: &iotago.Commitment{
Slot: currentSlot,
},
UnlockedAddrs: vm.UnlockedAddresses{
exampleIssuer.Key(): {UnlockedAtInputIndex: 0},
},
Tx: &iotago.Transaction{
API: tpkg.ZeroCostTestAPI,
TransactionEssence: &iotago.TransactionEssence{
CreationSlot: currentSlot,
Capabilities: iotago.TransactionCapabilitiesBitMaskWithCapabilities(iotago.WithTransactionCanDoAnything()),
},
},
BIC: exampleBIC,
},
},
wantErr: iotago.ErrStakingBlockIssuerFeatureMissing,
},
{
name: "ok - valid staking transition",
input: &vm.ChainOutputWithIDs{
Expand Down Expand Up @@ -776,68 +736,6 @@ func TestAccountOutput_ValidateStateTransition(t *testing.T) {
},
wantErr: iotago.ErrStakingEndEpochTooEarly,
},
{
name: "fail - account removes block issuer feature while having a staking feature",
input: &vm.ChainOutputWithIDs{
OutputID: tpkg.RandOutputIDWithCreationSlot(1000, 0),
ChainID: exampleAccountID,
Output: &iotago.AccountOutput{
Amount: 100,
AccountID: exampleAccountID,
UnlockConditions: iotago.AccountOutputUnlockConditions{
&iotago.AddressUnlockCondition{Address: exampleAddress},
},
Features: iotago.AccountOutputFeatures{
&iotago.StakingFeature{
StakedAmount: 50,
FixedCost: 5,
StartEpoch: currentEpoch,
EndEpoch: iotago.MaxEpochIndex,
},
&iotago.BlockIssuerFeature{
BlockIssuerKeys: tpkg.RandBlockIssuerKeys(1),
ExpirySlot: 990,
},
},
},
},
next: &iotago.AccountOutput{
Amount: 100,
AccountID: exampleAccountID,
UnlockConditions: iotago.AccountOutputUnlockConditions{
&iotago.AddressUnlockCondition{Address: exampleAddress},
},
Features: iotago.AccountOutputFeatures{
&iotago.StakingFeature{
StakedAmount: 50,
FixedCost: 5,
StartEpoch: currentEpoch,
EndEpoch: iotago.MaxEpochIndex,
},
},
},
transType: iotago.ChainTransitionTypeStateChange,
svCtx: &vm.Params{
API: tpkg.ZeroCostTestAPI,
WorkingSet: &vm.WorkingSet{
Commitment: &iotago.Commitment{
Slot: currentSlot,
},
UnlockedAddrs: vm.UnlockedAddresses{
exampleIssuer.Key(): {UnlockedAtInputIndex: 0},
},
Tx: &iotago.Transaction{
API: tpkg.ZeroCostTestAPI,
TransactionEssence: &iotago.TransactionEssence{
CreationSlot: currentSlot,
Capabilities: iotago.TransactionCapabilitiesBitMaskWithCapabilities(iotago.WithTransactionCanDoAnything()),
},
},
BIC: exampleBIC,
},
},
wantErr: iotago.ErrStakingBlockIssuerFeatureMissing,
},
{
name: "fail - expired staking feature removed without specifying reward input",
input: &vm.ChainOutputWithIDs{
Expand Down
Loading

0 comments on commit 0798c51

Please sign in to comment.