Skip to content

Commit

Permalink
Merge pull request #375 from iotaledger/account-test-framework
Browse files Browse the repository at this point in the history
Use OutputBuilders from iota.go in transaction test framework
  • Loading branch information
alexsporn authored Sep 28, 2023
2 parents a8d05f1 + 4c293fc commit 5bd6b56
Show file tree
Hide file tree
Showing 10 changed files with 105 additions and 155 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ require (
github.com/iotaledger/hive.go/stringify v0.0.0-20230926122307-d671b36a4a65
github.com/iotaledger/inx-app v1.0.0-rc.3.0.20230927112840-e982cb6707c9
github.com/iotaledger/inx/go v1.0.0-rc.2.0.20230925152824-4bfa09b8c132
github.com/iotaledger/iota.go/v4 v4.0.0-20230927112835-2dc846c6153f
github.com/iotaledger/iota.go/v4 v4.0.0-20230927125610-ddf51789ec4d
github.com/labstack/echo/v4 v4.11.1
github.com/labstack/gommon v0.4.0
github.com/libp2p/go-libp2p v0.30.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,8 @@ github.com/iotaledger/inx-app v1.0.0-rc.3.0.20230927112840-e982cb6707c9 h1:vRGNf
github.com/iotaledger/inx-app v1.0.0-rc.3.0.20230927112840-e982cb6707c9/go.mod h1:KbmEuxbhax3fyVrxF4RjBD1/MWLFxHLNDFBnDYUzok4=
github.com/iotaledger/inx/go v1.0.0-rc.2.0.20230925152824-4bfa09b8c132 h1:YHvgNY3/TRx84UxqizkFe7vVUxAMQB2DOuEL8wjHxpg=
github.com/iotaledger/inx/go v1.0.0-rc.2.0.20230925152824-4bfa09b8c132/go.mod h1:DIFr5lt73HLIyn/Lg2jtzfakwhIT0mMZjMFFji3GXeI=
github.com/iotaledger/iota.go/v4 v4.0.0-20230927112835-2dc846c6153f h1:b7mr4pVMRsqp4BMXFA6GR911t/10AmvZIjsvgLJegRQ=
github.com/iotaledger/iota.go/v4 v4.0.0-20230927112835-2dc846c6153f/go.mod h1:wR9xBbsofns9hFyRHFZ2bDYIb861qsfmQPVMBKcPvDo=
github.com/iotaledger/iota.go/v4 v4.0.0-20230927125610-ddf51789ec4d h1:lhYbBhVORcS2LLNviaO/yTmom1suDskJLA1wSvvsLiU=
github.com/iotaledger/iota.go/v4 v4.0.0-20230927125610-ddf51789ec4d/go.mod h1:wR9xBbsofns9hFyRHFZ2bDYIb861qsfmQPVMBKcPvDo=
github.com/ipfs/boxo v0.10.0 h1:tdDAxq8jrsbRkYoF+5Rcqyeb91hgWe2hp7iLu7ORZLY=
github.com/ipfs/boxo v0.10.0/go.mod h1:Fg+BnfxZ0RPzR0nOodzdIq3A7KgoWAOWsEIImrIQdBM=
github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=
Expand Down
14 changes: 3 additions & 11 deletions pkg/tests/accounts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func Test_TransitionAccount(t *testing.T) {

accountInput, accountOutputs, accountWallets := ts.TransactionFramework.TransitionAccount(
"Genesis:1",
testsuite.AddBlockIssuerKey(newGenesisOutputKey),
testsuite.WithAddBlockIssuerKey(newGenesisOutputKey),
testsuite.WithBlockIssuerExpirySlot(1),
)
consumedInputs, equalOutputs, equalWallets := ts.TransactionFramework.CreateBasicOutputsEqually(2, "Genesis:0")
Expand Down Expand Up @@ -127,16 +127,8 @@ func Test_TransitionAccount(t *testing.T) {
&iotago.StateControllerAddressUnlockCondition{Address: ts.TransactionFramework.DefaultAddress()},
&iotago.GovernorAddressUnlockCondition{Address: ts.TransactionFramework.DefaultAddress()},
}),
testsuite.WithBlockIssuerFeature(&iotago.BlockIssuerFeature{
BlockIssuerKeys: iotago.NewBlockIssuerKeys(newAccountBlockIssuerKey),
ExpirySlot: newAccountExpirySlot,
}),
testsuite.WithStakingFeature(&iotago.StakingFeature{
StakedAmount: 10000,
FixedCost: 421,
StartEpoch: 0,
EndEpoch: 10,
}),
testsuite.WithBlockIssuerFeature(iotago.BlockIssuerKeys{newAccountBlockIssuerKey}, newAccountExpirySlot),
testsuite.WithStakingFeature(10000, 421, 0, 10),
)

destroyedAccountInput, destructionOutputs, destroyWallets := ts.TransactionFramework.DestroyAccount("TX1:0")
Expand Down
222 changes: 90 additions & 132 deletions pkg/testsuite/transactions_framework.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,20 +175,14 @@ func (t *TransactionFramework) CreateBasicOutputs(amountDistribution []iotago.Ba
return inputStates, outputStates, []*mock.HDWallet{t.wallet}
}

func (t *TransactionFramework) CreateAccountFromInput(inputAlias string, opts ...options.Option[iotago.AccountOutput]) (utxoledger.Outputs, iotago.Outputs[iotago.Output], []*mock.HDWallet) {
func (t *TransactionFramework) CreateAccountFromInput(inputAlias string, opts ...options.Option[builder.AccountOutputBuilder]) (utxoledger.Outputs, iotago.Outputs[iotago.Output], []*mock.HDWallet) {
input := t.Output(inputAlias)

accountOutput := options.Apply(&iotago.AccountOutput{
Amount: input.BaseTokenAmount(),
Mana: input.StoredMana(),
NativeTokens: iotago.NativeTokens{},
Conditions: iotago.AccountOutputUnlockConditions{
&iotago.StateControllerAddressUnlockCondition{Address: t.DefaultAddress()},
&iotago.GovernorAddressUnlockCondition{Address: t.DefaultAddress()},
},
Features: iotago.AccountOutputFeatures{},
ImmutableFeatures: iotago.AccountOutputImmFeatures{},
}, opts)
accountOutput := options.Apply(builder.NewAccountOutputBuilder(t.DefaultAddress(), t.DefaultAddress(), input.BaseTokenAmount()).
Mana(input.StoredMana()).
StateController(t.DefaultAddress()).
Governor(t.DefaultAddress()),
opts).MustBuild()

outputStates := iotago.Outputs[iotago.Output]{accountOutput}

Expand All @@ -211,20 +205,12 @@ func (t *TransactionFramework) CreateAccountFromInput(inputAlias string, opts ..
// CreateDelegationFromInput creates a new DelegationOutput with given options from an input. If the remainder Output
// is not created, then StoredMana from the input is not passed and can potentially be burned.
// In order not to burn it, it needs to be assigned manually in another output in the transaction.
func (t *TransactionFramework) CreateDelegationFromInput(inputAlias string, opts ...options.Option[iotago.DelegationOutput]) (utxoledger.Outputs, iotago.Outputs[iotago.Output], []*mock.HDWallet) {
func (t *TransactionFramework) CreateDelegationFromInput(inputAlias string, opts ...options.Option[builder.DelegationOutputBuilder]) (utxoledger.Outputs, iotago.Outputs[iotago.Output], []*mock.HDWallet) {
input := t.Output(inputAlias)

delegationOutput := options.Apply(&iotago.DelegationOutput{
Amount: input.BaseTokenAmount(),
DelegatedAmount: input.BaseTokenAmount(),
DelegationID: iotago.DelegationID{},
ValidatorAddress: &iotago.AccountAddress{},
StartEpoch: 0,
EndEpoch: 0,
Conditions: iotago.DelegationOutputUnlockConditions{
&iotago.AddressUnlockCondition{Address: t.DefaultAddress()},
},
}, opts)
delegationOutput := options.Apply(builder.NewDelegationOutputBuilder(&iotago.AccountAddress{}, t.DefaultAddress(), input.BaseTokenAmount()).
DelegatedAmount(input.BaseTokenAmount()),
opts).MustBuild()

if delegationOutput.ValidatorAddress.AccountID() == iotago.EmptyAccountID() ||
delegationOutput.DelegatedAmount == 0 ||
Expand Down Expand Up @@ -257,17 +243,17 @@ func (t *TransactionFramework) DelayedClaimingTransition(inputAlias string, dele
panic(ierrors.Errorf("%s is not a delegation output, cannot transition to delayed claiming state", inputAlias))
}

delegationOutput, ok := input.Output().Clone().(*iotago.DelegationOutput)
prevOutput, ok := input.Output().Clone().(*iotago.DelegationOutput)
if !ok {
panic(ierrors.Errorf("cloned output %s is not a delegation output, cannot transition to delayed claiming state", inputAlias))
}

if delegationOutput.DelegationID == iotago.EmptyDelegationID() {
delegationOutput.DelegationID = iotago.DelegationIDFromOutputID(input.OutputID())
delegationBuilder := builder.NewDelegationOutputBuilderFromPrevious(prevOutput).EndEpoch(delegationEndEpoch)
if prevOutput.DelegationID == iotago.EmptyDelegationID() {
delegationBuilder.DelegationID(iotago.DelegationIDFromOutputID(input.OutputID()))
}
delegationOutput.EndEpoch = delegationEndEpoch

return utxoledger.Outputs{input}, iotago.Outputs[iotago.Output]{delegationOutput}, []*mock.HDWallet{t.wallet}
return utxoledger.Outputs{input}, iotago.Outputs[iotago.Output]{delegationBuilder.MustBuild()}, []*mock.HDWallet{t.wallet}
}

func (t *TransactionFramework) DestroyAccount(alias string) (consumedInputs *utxoledger.Output, outputs iotago.Outputs[iotago.Output], signingWallets []*mock.HDWallet) {
Expand All @@ -286,7 +272,7 @@ func (t *TransactionFramework) DestroyAccount(alias string) (consumedInputs *utx
return output, outputStates, []*mock.HDWallet{t.wallet}
}

func (t *TransactionFramework) TransitionAccount(alias string, opts ...options.Option[iotago.AccountOutput]) (consumedInput *utxoledger.Output, outputs iotago.Outputs[iotago.Output], signingWallets []*mock.HDWallet) {
func (t *TransactionFramework) TransitionAccount(alias string, opts ...options.Option[builder.AccountOutputBuilder]) (consumedInput *utxoledger.Output, outputs iotago.Outputs[iotago.Output], signingWallets []*mock.HDWallet) {
output, exists := t.states[alias]
if !exists {
panic(fmt.Sprintf("account with alias %s does not exist", alias))
Expand All @@ -297,7 +283,8 @@ func (t *TransactionFramework) TransitionAccount(alias string, opts ...options.O
panic(fmt.Sprintf("output with alias %s is not *iotago.AccountOutput", alias))
}

accountOutput = options.Apply(accountOutput, opts)
accountBuilder := builder.NewAccountOutputBuilderFromPrevious(accountOutput)
accountOutput = options.Apply(accountBuilder, opts).MustBuild()

return output, iotago.Outputs[iotago.Output]{accountOutput}, []*mock.HDWallet{t.wallet}
}
Expand Down Expand Up @@ -342,159 +329,130 @@ func (t *TransactionFramework) DefaultAddress() iotago.Address {

// DelegationOutput options

func WithDelegatedAmount(delegatedAmount iotago.BaseToken) options.Option[iotago.DelegationOutput] {
return func(delegationOutput *iotago.DelegationOutput) {
delegationOutput.DelegatedAmount = delegatedAmount
func WithDelegatedAmount(delegatedAmount iotago.BaseToken) options.Option[builder.DelegationOutputBuilder] {
return func(delegationBuilder *builder.DelegationOutputBuilder) {
delegationBuilder.DelegatedAmount(delegatedAmount)
}
}

func WithDelegatedValidatorAddress(validatorAddress *iotago.AccountAddress) options.Option[iotago.DelegationOutput] {
return func(delegationOutput *iotago.DelegationOutput) {
delegationOutput.ValidatorAddress = validatorAddress
func WithDelegatedValidatorAddress(validatorAddress *iotago.AccountAddress) options.Option[builder.DelegationOutputBuilder] {
return func(delegationBuilder *builder.DelegationOutputBuilder) {
delegationBuilder.ValidatorAddress(validatorAddress)
}
}

func WithDelegationStartEpoch(startEpoch iotago.EpochIndex) options.Option[iotago.DelegationOutput] {
return func(delegationOutput *iotago.DelegationOutput) {
delegationOutput.StartEpoch = startEpoch
func WithDelegationStartEpoch(startEpoch iotago.EpochIndex) options.Option[builder.DelegationOutputBuilder] {
return func(delegationBuilder *builder.DelegationOutputBuilder) {
delegationBuilder.StartEpoch(startEpoch)
}
}

func WithDelegationEndEpoch(endEpoch iotago.EpochIndex) options.Option[iotago.DelegationOutput] {
return func(delegationOutput *iotago.DelegationOutput) {
delegationOutput.EndEpoch = endEpoch
func WithDelegationEndEpoch(endEpoch iotago.EpochIndex) options.Option[builder.DelegationOutputBuilder] {
return func(delegationBuilder *builder.DelegationOutputBuilder) {
delegationBuilder.EndEpoch(endEpoch)
}
}

func WithDelegationConditions(delegationConditions iotago.DelegationOutputUnlockConditions) options.Option[iotago.DelegationOutput] {
return func(delegationOutput *iotago.DelegationOutput) {
delegationOutput.Conditions = delegationConditions
func WithDelegationConditions(delegationConditions iotago.DelegationOutputUnlockConditions) options.Option[builder.DelegationOutputBuilder] {
return func(delegationBuilder *builder.DelegationOutputBuilder) {
delegationBuilder.Address(delegationConditions.MustSet().Address().Address)
}
}

func WithDelegationAmount(amount iotago.BaseToken) options.Option[iotago.DelegationOutput] {
return func(delegationOutput *iotago.DelegationOutput) {
delegationOutput.Amount = amount
func WithDelegationAmount(amount iotago.BaseToken) options.Option[builder.DelegationOutputBuilder] {
return func(delegationBuilder *builder.DelegationOutputBuilder) {
delegationBuilder.Amount(amount)
}
}

// BlockIssuer options

func WithBlockIssuerFeature(blockIssuerFeature *iotago.BlockIssuerFeature) options.Option[iotago.AccountOutput] {
return func(accountOutput *iotago.AccountOutput) {
for idx, feature := range accountOutput.Features {
if feature.Type() == iotago.FeatureBlockIssuer {
accountOutput.Features[idx] = blockIssuerFeature
return
}
}

accountOutput.Features = append(accountOutput.Features, blockIssuerFeature)
func WithBlockIssuerFeature(keys iotago.BlockIssuerKeys, expirySlot iotago.SlotIndex) options.Option[builder.AccountOutputBuilder] {
return func(accountBuilder *builder.AccountOutputBuilder) {
accountBuilder.BlockIssuer(keys, expirySlot)
}
}

func AddBlockIssuerKey(key iotago.BlockIssuerKey) options.Option[iotago.AccountOutput] {
return func(accountOutput *iotago.AccountOutput) {
blockIssuer := accountOutput.FeatureSet().BlockIssuer()
if blockIssuer == nil {
panic("cannot add block issuer key to account without BlockIssuer feature")
}
blockIssuer.BlockIssuerKeys = append(blockIssuer.BlockIssuerKeys, key)

blockIssuer.BlockIssuerKeys.Sort()
func WithAddBlockIssuerKey(key iotago.BlockIssuerKey) options.Option[builder.AccountOutputBuilder] {
return func(accountBuilder *builder.AccountOutputBuilder) {
transition := accountBuilder.GovernanceTransition()
transition.BlockIssuerTransition().AddKeys(key)
}
}

func WithBlockIssuerKeys(keys iotago.BlockIssuerKeys) options.Option[iotago.AccountOutput] {
return func(accountOutput *iotago.AccountOutput) {
blockIssuer := accountOutput.FeatureSet().BlockIssuer()
if blockIssuer == nil {
panic("cannot set block issuer keys to account without BlockIssuer feature")
}
blockIssuer.BlockIssuerKeys = keys
}
}

func WithBlockIssuerExpirySlot(expirySlot iotago.SlotIndex) options.Option[iotago.AccountOutput] {
return func(accountOutput *iotago.AccountOutput) {
blockIssuer := accountOutput.FeatureSet().BlockIssuer()
if blockIssuer == nil {
panic("cannot set block issuer expiry slot to account without BlockIssuer feature")
}
blockIssuer.ExpirySlot = expirySlot
func WithBlockIssuerKeys(keys iotago.BlockIssuerKeys) options.Option[builder.AccountOutputBuilder] {
return func(accountBuilder *builder.AccountOutputBuilder) {
transition := accountBuilder.GovernanceTransition()
transition.BlockIssuerTransition().Keys(keys)
}
}

func WithStakingFeature(stakingFeature *iotago.StakingFeature) options.Option[iotago.AccountOutput] {
return func(accountOutput *iotago.AccountOutput) {
for idx, feature := range accountOutput.Features {
if feature.Type() == iotago.FeatureStaking {
accountOutput.Features[idx] = stakingFeature
return
}
}

accountOutput.Features = append(accountOutput.Features, stakingFeature)
func WithBlockIssuerExpirySlot(expirySlot iotago.SlotIndex) options.Option[builder.AccountOutputBuilder] {
return func(accountBuilder *builder.AccountOutputBuilder) {
transition := accountBuilder.GovernanceTransition()
transition.BlockIssuerTransition().ExpirySlot(expirySlot)
}
}

func WithStakingEndEpoch(endEpoch iotago.EpochIndex) options.Option[iotago.AccountOutput] {
return func(accountOutput *iotago.AccountOutput) {
staking := accountOutput.FeatureSet().Staking()
if staking == nil {
panic("cannot update staking end epoch on account without Staking feature")
}
staking.EndEpoch = endEpoch
func WithStakingFeature(amount iotago.BaseToken, fixedCost iotago.Mana, startEpoch iotago.EpochIndex, optEndEpoch ...iotago.EpochIndex) options.Option[builder.AccountOutputBuilder] {
return func(accountBuilder *builder.AccountOutputBuilder) {
accountBuilder.Staking(amount, fixedCost, startEpoch, optEndEpoch...)
}
}

// Account options

func WithAccountMana(mana iotago.Mana) options.Option[iotago.AccountOutput] {
return func(accountOutput *iotago.AccountOutput) {
accountOutput.Mana = mana
}
}

func WithAccountAmount(amount iotago.BaseToken) options.Option[iotago.AccountOutput] {
return func(accountOutput *iotago.AccountOutput) {
accountOutput.Amount = amount
func WithAccountMana(mana iotago.Mana) options.Option[builder.AccountOutputBuilder] {
return func(accountBuilder *builder.AccountOutputBuilder) {
accountBuilder.Mana(mana)
}
}

func WithAccountIncreasedStateIndex() options.Option[iotago.AccountOutput] {
return func(accountOutput *iotago.AccountOutput) {
accountOutput.StateIndex++
func WithAccountAmount(amount iotago.BaseToken) options.Option[builder.AccountOutputBuilder] {
return func(accountBuilder *builder.AccountOutputBuilder) {
accountBuilder.Amount(amount)
}
}

func WithAccountIncreasedFoundryCounter(diff uint32) options.Option[iotago.AccountOutput] {
return func(accountOutput *iotago.AccountOutput) {
accountOutput.FoundryCounter += diff
func WithAccountIncreasedFoundryCounter(diff uint32) options.Option[builder.AccountOutputBuilder] {
return func(accountBuilder *builder.AccountOutputBuilder) {
accountBuilder.FoundriesToGenerate(diff)
}
}

func WithAccountFeatures(features iotago.AccountOutputFeatures) options.Option[iotago.AccountOutput] {
return func(accountOutput *iotago.AccountOutput) {
accountOutput.Features = features
}
}

func WithAccountImmutableFeatures(features iotago.AccountOutputImmFeatures) options.Option[iotago.AccountOutput] {
return func(accountOutput *iotago.AccountOutput) {
accountOutput.ImmutableFeatures = features
func WithAccountImmutableFeatures(features iotago.AccountOutputImmFeatures) options.Option[builder.AccountOutputBuilder] {
return func(accountBuilder *builder.AccountOutputBuilder) {
for _, feature := range features.MustSet() {
switch feature.Type() {
case iotago.FeatureMetadata:
//nolint:forcetypeassert
accountBuilder.ImmutableMetadata(feature.(*iotago.MetadataFeature).Data)
case iotago.FeatureSender:
//nolint:forcetypeassert
accountBuilder.ImmutableSender(feature.(*iotago.SenderFeature).Address)
}
}
}
}

func WithAccountConditions(conditions iotago.AccountOutputUnlockConditions) options.Option[iotago.AccountOutput] {
return func(accountOutput *iotago.AccountOutput) {
accountOutput.Conditions = conditions
func WithAccountConditions(conditions iotago.AccountOutputUnlockConditions) options.Option[builder.AccountOutputBuilder] {
return func(accountBuilder *builder.AccountOutputBuilder) {
for _, condition := range conditions.MustSet() {
switch condition.Type() {
case iotago.UnlockConditionStateControllerAddress:
//nolint:forcetypeassert
accountBuilder.StateController(condition.(*iotago.StateControllerAddressUnlockCondition).Address)
case iotago.UnlockConditionGovernorAddress:
//nolint:forcetypeassert
accountBuilder.Governor(condition.(*iotago.GovernorAddressUnlockCondition).Address)
}
}
}
}

func WithAccountNativeTokens(nativeTokens iotago.NativeTokens) options.Option[iotago.AccountOutput] {
return func(accountOutput *iotago.AccountOutput) {
accountOutput.NativeTokens = nativeTokens
func WithAccountNativeTokens(nativeTokens iotago.NativeTokens) options.Option[builder.AccountOutputBuilder] {
return func(accountBuilder *builder.AccountOutputBuilder) {
accountBuilder.NativeTokens(nativeTokens)
}
}

Expand Down
2 changes: 1 addition & 1 deletion tools/evil-spammer/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ require (
github.com/iotaledger/hive.go/runtime v0.0.0-20230926122307-d671b36a4a65
github.com/iotaledger/iota-core v0.0.0-00010101000000-000000000000
github.com/iotaledger/iota-core/tools/genesis-snapshot v0.0.0-00010101000000-000000000000
github.com/iotaledger/iota.go/v4 v4.0.0-20230927112835-2dc846c6153f
github.com/iotaledger/iota.go/v4 v4.0.0-20230927125610-ddf51789ec4d
github.com/mr-tron/base58 v1.2.0
go.uber.org/atomic v1.11.0
)
Expand Down
Loading

0 comments on commit 5bd6b56

Please sign in to comment.