Skip to content

Commit

Permalink
Merge pull request #476 from iotaledger/feat/add-anchor-output
Browse files Browse the repository at this point in the history
Add Anchor Output
  • Loading branch information
muXxer authored Oct 31, 2023
2 parents 426e751 + 7c3766a commit 86b2c44
Show file tree
Hide file tree
Showing 13 changed files with 113 additions and 50 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ require (
github.com/iotaledger/hive.go/stringify v0.0.0-20231027195901-620bd7470e42
github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231028104239-869296c43f26
github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231028104044-69b02af0058d
github.com/iotaledger/iota.go/v4 v4.0.0-20231028103644-b834fd54b02a
github.com/iotaledger/iota.go/v4 v4.0.0-20231031113109-5d7d59311967
github.com/labstack/echo/v4 v4.11.2
github.com/labstack/gommon v0.4.0
github.com/libp2p/go-libp2p v0.31.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -309,8 +309,8 @@ github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231028104239-869296c43f26 h1:ZZs7I
github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231028104239-869296c43f26/go.mod h1:aFS0dN6QgKGgZakGgEv57NOLw+pLGdEiGcfDZ3h9GL0=
github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231028104044-69b02af0058d h1:0SVvkN04C+Ylc2puM/c77HuvRMmHRl0BkNjlZx1YWeA=
github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231028104044-69b02af0058d/go.mod h1:WFa5hHen6fi3RBX4K6r4fzhGpoh+7KJVIyFztZHdM84=
github.com/iotaledger/iota.go/v4 v4.0.0-20231028103644-b834fd54b02a h1:WLW4iaJAx4N9Pujv+gzHklnnjCt5MPrtXyVcK3UXdNc=
github.com/iotaledger/iota.go/v4 v4.0.0-20231028103644-b834fd54b02a/go.mod h1:jqbLYq4a/FwuiPBqFfkAwwxU8vs3+kReRq2/tyX5qRA=
github.com/iotaledger/iota.go/v4 v4.0.0-20231031113109-5d7d59311967 h1:qiBW4TiRdzVJshLu7RoWn9ur64SFLCOQ0oFtQWtz4bk=
github.com/iotaledger/iota.go/v4 v4.0.0-20231031113109-5d7d59311967/go.mod h1:jqbLYq4a/FwuiPBqFfkAwwxU8vs3+kReRq2/tyX5qRA=
github.com/ipfs/boxo v0.13.1 h1:nQ5oQzcMZR3oL41REJDcTbrvDvuZh3J9ckc9+ILeRQI=
github.com/ipfs/boxo v0.13.1/go.mod h1:btrtHy0lmO1ODMECbbEY1pxNtrLilvKSYLoGQt1yYCk=
github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=
Expand Down
8 changes: 5 additions & 3 deletions pkg/protocol/engine/utxoledger/iteration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func TestUTXOComputeBalance(t *testing.T) {
initialOutput := tpkg.RandLedgerStateOutputOnAddressWithAmount(iotago.OutputBasic, utils.RandAddress(iotago.AddressEd25519), 2_134_656_365)
require.NoError(t, manager.AddGenesisUnspentOutput(initialOutput))
require.NoError(t, manager.AddGenesisUnspentOutput(tpkg.RandLedgerStateOutputOnAddressWithAmount(iotago.OutputAccount, utils.RandAddress(iotago.AddressAccount), 56_549_524)))
require.NoError(t, manager.AddGenesisUnspentOutput(tpkg.RandLedgerStateOutputOnAddressWithAmount(iotago.OutputAnchor, utils.RandAddress(iotago.AddressAccount), 56_549_524)))
require.NoError(t, manager.AddGenesisUnspentOutput(tpkg.RandLedgerStateOutputOnAddressWithAmount(iotago.OutputFoundry, utils.RandAddress(iotago.AddressAccount), 25_548_858)))
require.NoError(t, manager.AddGenesisUnspentOutput(tpkg.RandLedgerStateOutputOnAddressWithAmount(iotago.OutputNFT, utils.RandAddress(iotago.AddressEd25519), 545_699_656)))
require.NoError(t, manager.AddGenesisUnspentOutput(tpkg.RandLedgerStateOutputOnAddressWithAmount(iotago.OutputBasic, utils.RandAddress(iotago.AddressAccount), 626_659_696)))
Expand All @@ -43,12 +44,12 @@ func TestUTXOComputeBalance(t *testing.T) {

unspent, err := manager.UnspentOutputs()
require.NoError(t, err)
require.Equal(t, 5, len(unspent))
require.Equal(t, 6, len(unspent))

balance, count, err := manager.ComputeLedgerBalance()
require.NoError(t, err)
require.Equal(t, 5, count)
require.Equal(t, iotago.BaseToken(2_134_656_365+56_549_524+25_548_858+545_699_656+626_659_696), balance)
require.Equal(t, 6, count)
require.Equal(t, iotago.BaseToken(2_134_656_365+56_549_524+56_549_524+25_548_858+545_699_656+626_659_696), balance)
}

func TestUTXOIteration(t *testing.T) {
Expand All @@ -67,6 +68,7 @@ func TestUTXOIteration(t *testing.T) {
tpkg.RandLedgerStateOutputOnAddress(iotago.OutputNFT, utils.RandAddress(iotago.AddressNFT)),
tpkg.RandLedgerStateOutputOnAddress(iotago.OutputNFT, utils.RandAddress(iotago.AddressAccount)),
tpkg.RandLedgerStateOutputOnAddress(iotago.OutputAccount, utils.RandAddress(iotago.AddressEd25519)),
tpkg.RandLedgerStateOutputOnAddress(iotago.OutputAnchor, utils.RandAddress(iotago.AddressEd25519)),
tpkg.RandLedgerStateOutputOnAddress(iotago.OutputFoundry, utils.RandAddress(iotago.AddressAccount)),
tpkg.RandLedgerStateOutputOnAddress(iotago.OutputFoundry, utils.RandAddress(iotago.AddressAccount)),
tpkg.RandLedgerStateOutputOnAddress(iotago.OutputFoundry, utils.RandAddress(iotago.AddressAccount)),
Expand Down
10 changes: 6 additions & 4 deletions pkg/protocol/engine/utxoledger/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func TestConfirmationApplyAndRollbackToEmptyLedger(t *testing.T) {
tpkg.RandLedgerStateOutputWithType(iotago.OutputNFT), // spent
tpkg.RandLedgerStateOutputWithType(iotago.OutputBasic), // spent
tpkg.RandLedgerStateOutputWithType(iotago.OutputAccount),
tpkg.RandLedgerStateOutputWithType(iotago.OutputAnchor),
tpkg.RandLedgerStateOutputWithType(iotago.OutputNFT),
tpkg.RandLedgerStateOutputWithType(iotago.OutputFoundry),
}
Expand All @@ -45,15 +46,15 @@ func TestConfirmationApplyAndRollbackToEmptyLedger(t *testing.T) {

return true
}))
require.Equal(t, 7, outputCount)
require.Equal(t, 8, outputCount)

var unspentCount int
require.NoError(t, manager.ForEachUnspentOutput(func(_ *utxoledger.Output) bool {
unspentCount++

return true
}))
require.Equal(t, 5, unspentCount)
require.Equal(t, 6, unspentCount)

var spentCount int
require.NoError(t, manager.ForEachSpentOutput(func(_ *utxoledger.Spent) bool {
Expand Down Expand Up @@ -113,6 +114,7 @@ func TestConfirmationApplyAndRollbackToPreviousLedger(t *testing.T) {
tpkg.RandLedgerStateOutputWithType(iotago.OutputFoundry),
tpkg.RandLedgerStateOutputWithType(iotago.OutputBasic), // spent
tpkg.RandLedgerStateOutputWithType(iotago.OutputAccount),
tpkg.RandLedgerStateOutputWithType(iotago.OutputAnchor),
}

index := iotago.SlotIndex(49)
Expand Down Expand Up @@ -161,7 +163,7 @@ func TestConfirmationApplyAndRollbackToPreviousLedger(t *testing.T) {
return true
}))
require.Empty(t, outputByOutputID)
require.Equal(t, 7, outputCount)
require.Equal(t, 8, outputCount)

var unspentCount int
require.NoError(t, manager.ForEachUnspentOutput(func(output *utxoledger.Output) bool {
Expand All @@ -172,7 +174,7 @@ func TestConfirmationApplyAndRollbackToPreviousLedger(t *testing.T) {

return true
}))
require.Equal(t, 4, unspentCount)
require.Equal(t, 5, unspentCount)
require.Empty(t, unspentByOutputID)

var spentCount int
Expand Down
50 changes: 46 additions & 4 deletions pkg/protocol/engine/utxoledger/output_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,8 +290,7 @@ func TestAccountOutputSerialization(t *testing.T) {
txCreationSlot := utils.RandSlotIndex()
blockID := utils.RandBlockID()
aliasID := utils.RandAccountID()
stateController := utils.RandAccountID()
governor := utils.RandAddress(iotago.AddressEd25519).(*iotago.Ed25519Address)
address := utils.RandAccountID().ToAddress()
issuer := utils.RandNFTID()
sender := utils.RandAccountID()
amount := iotago_tpkg.RandBaseToken(iotago.MaxBaseToken)
Expand All @@ -301,6 +300,49 @@ func TestAccountOutputSerialization(t *testing.T) {
Amount: amount,
AccountID: aliasID,
Conditions: iotago.AccountOutputUnlockConditions{
&iotago.AddressUnlockCondition{
Address: address,
},
},
Features: iotago.AccountOutputFeatures{
&iotago.SenderFeature{
Address: sender.ToAddress(),
},
},
ImmutableFeatures: iotago.AccountOutputImmFeatures{
&iotago.IssuerFeature{
Address: issuer.ToAddress(),
},
},
}

outputProof, err := iotago.NewOutputIDProof(iotago_tpkg.TestAPI, txCommitment, txCreationSlot, iotago.TxEssenceOutputs{iotaOutput}, 0)
require.NoError(t, err)

output := CreateOutputAndAssertSerialization(t, blockID, index, iotaOutput, outputProof)
spent := CreateSpentAndAssertSerialization(t, output)
outputID := output.OutputID()

require.ElementsMatch(t, byteutils.ConcatBytes([]byte{utxoledger.StoreKeyPrefixOutputUnspent}, outputID[:]), output.UnspentLookupKey())
AssertOutputUnspentAndSpentTransitions(t, output, spent)
}

func TestAnchorOutputSerialization(t *testing.T) {
txCommitment := iotago_tpkg.Rand32ByteArray()
txCreationSlot := utils.RandSlotIndex()
blockID := utils.RandBlockID()
aliasID := utils.RandAnchorID()
stateController := utils.RandAnchorID()
governor := utils.RandAddress(iotago.AddressEd25519).(*iotago.Ed25519Address)
issuer := utils.RandNFTID()
sender := utils.RandAnchorID()
amount := iotago_tpkg.RandBaseToken(iotago.MaxBaseToken)
index := utils.RandSlotIndex()

iotaOutput := &iotago.AnchorOutput{
Amount: amount,
AnchorID: aliasID,
Conditions: iotago.AnchorOutputUnlockConditions{
&iotago.StateControllerAddressUnlockCondition{
Address: stateController.ToAddress(),
},
Expand All @@ -309,12 +351,12 @@ func TestAccountOutputSerialization(t *testing.T) {
},
},
StateMetadata: make([]byte, 0),
Features: iotago.AccountOutputFeatures{
Features: iotago.AnchorOutputFeatures{
&iotago.SenderFeature{
Address: sender.ToAddress(),
},
},
ImmutableFeatures: iotago.AccountOutputImmFeatures{
ImmutableFeatures: iotago.AnchorOutputImmFeatures{
&iotago.IssuerFeature{
Address: issuer.ToAddress(),
},
Expand Down
28 changes: 10 additions & 18 deletions pkg/testsuite/mock/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ func WithInputs(inputs utxoledger.Outputs) options.Option[builder.TransactionBui
InputID: input.OutputID(),
Input: input.Output(),
})
case iotago.OutputAccount:
// For alias we need to unlock the state controller
case iotago.OutputAnchor:
// For anchor outputs we need to unlock the state controller
txBuilder.AddInput(&builder.TxInput{
UnlockTarget: input.Output().UnlockConditionSet().StateControllerAddress().Address,
InputID: input.OutputID(),
Expand All @@ -42,14 +42,12 @@ func WithInputs(inputs utxoledger.Outputs) options.Option[builder.TransactionBui
}
}

func WithAccountInput(input *utxoledger.Output, governorTransition bool) options.Option[builder.TransactionBuilder] {
func WithAccountInput(input *utxoledger.Output) options.Option[builder.TransactionBuilder] {
return func(txBuilder *builder.TransactionBuilder) {
switch input.OutputType() {
case iotago.OutputAccount:
address := input.Output().UnlockConditionSet().StateControllerAddress().Address
if governorTransition {
address = input.Output().UnlockConditionSet().GovernorAddress().Address
}
address := input.Output().UnlockConditionSet().Address().Address

txBuilder.AddInput(&builder.TxInput{
UnlockTarget: address,
InputID: input.OutputID(),
Expand Down Expand Up @@ -145,22 +143,19 @@ func WithBlockIssuerFeature(keys iotago.BlockIssuerKeys, expirySlot iotago.SlotI

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

func WithBlockIssuerKeys(keys iotago.BlockIssuerKeys) options.Option[builder.AccountOutputBuilder] {
return func(accountBuilder *builder.AccountOutputBuilder) {
transition := accountBuilder.GovernanceTransition()
transition.BlockIssuerTransition().Keys(keys)
accountBuilder.BlockIssuerTransition().Keys(keys)
}
}

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

Expand Down Expand Up @@ -209,12 +204,9 @@ func WithAccountConditions(conditions iotago.AccountOutputUnlockConditions) opti
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:
case iotago.UnlockConditionAddress:
//nolint:forcetypeassert
accountBuilder.Governor(condition.(*iotago.GovernorAddressUnlockCondition).Address)
accountBuilder.Address(condition.(*iotago.AddressUnlockCondition).Address)
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/testsuite/mock/wallet_transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
func (w *Wallet) CreateAccountFromInput(transactionName string, inputName string, recipientWallet *Wallet, creationSlot iotago.SlotIndex, opts ...options.Option[builder.AccountOutputBuilder]) *iotago.SignedTransaction {
input := w.Output(inputName)

accountOutput := options.Apply(builder.NewAccountOutputBuilder(recipientWallet.Address(), recipientWallet.Address(), input.BaseTokenAmount()).
accountOutput := options.Apply(builder.NewAccountOutputBuilder(recipientWallet.Address(), input.BaseTokenAmount()).
Mana(input.StoredMana()),
opts).MustBuild()

Expand Down Expand Up @@ -151,7 +151,7 @@ func (w *Wallet) TransitionAccount(transactionName string, inputName string, opt

signedTransaction := lo.PanicOnErr(w.createSignedTransactionWithOptions(
transactionName,
WithAccountInput(input, true),
WithAccountInput(input),
WithContextInputs(iotago.TxEssenceContextInputs{
&iotago.BlockIssuanceCreditInput{
AccountID: accountOutput.AccountID,
Expand Down Expand Up @@ -192,7 +192,7 @@ func (w *Wallet) DestroyAccount(transactionName string, inputName string, creati
CommitmentID: w.Node.Protocol.MainEngineInstance().Storage.Settings().LatestCommitment().Commitment().MustID(),
},
}),
WithAccountInput(input, true),
WithAccountInput(input),
WithOutputs(destructionOutputs),
WithSlotCreated(creationSlot),
))
Expand Down Expand Up @@ -250,7 +250,7 @@ func (w *Wallet) TransitionImplicitAccountToAccountOutput(transactionName string
panic(fmt.Sprintf("output with alias %s is not an implicit account", inputName))
}

accountOutput := options.Apply(builder.NewAccountOutputBuilder(w.Address(), w.Address(), MinIssuerAccountAmount).
accountOutput := options.Apply(builder.NewAccountOutputBuilder(w.Address(), MinIssuerAccountAmount).
AccountID(iotago.AccountIDFromOutputID(input.OutputID())),
opts).MustBuild()

Expand Down
3 changes: 1 addition & 2 deletions pkg/testsuite/snapshotcreator/snapshotcreator.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,7 @@ func createAccount(accountID iotago.AccountID, address iotago.Address, tokenAmou
Mana: mana,
AccountID: accountID,
Conditions: iotago.AccountOutputUnlockConditions{
&iotago.StateControllerAddressUnlockCondition{Address: address},
&iotago.GovernorAddressUnlockCondition{Address: address},
&iotago.AddressUnlockCondition{Address: address},
},
Features: iotago.AccountOutputFeatures{
&iotago.BlockIssuerFeature{
Expand Down
38 changes: 32 additions & 6 deletions pkg/utils/rand.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,17 @@ func RandNFTID() iotago.NFTID {
}

func RandAccountID() iotago.AccountID {
alias := iotago.AccountID{}
copy(alias[:], RandBytes(iotago.AccountIDLength))
accountID := iotago.AccountID{}
copy(accountID[:], RandBytes(iotago.AccountIDLength))

return alias
return accountID
}

func RandAnchorID() iotago.AnchorID {
anchorID := iotago.AnchorID{}
copy(anchorID[:], RandBytes(iotago.AnchorIDLength))

return anchorID
}

func RandSlotIndex() iotago.SlotIndex {
Expand Down Expand Up @@ -146,7 +153,7 @@ func RandAddress(addressType iotago.AddressType) iotago.Address {
}

func RandOutputType() iotago.OutputType {
outputTypes := []iotago.OutputType{iotago.OutputBasic, iotago.OutputAccount, iotago.OutputFoundry, iotago.OutputNFT, iotago.OutputDelegation}
outputTypes := []iotago.OutputType{iotago.OutputBasic, iotago.OutputAccount, iotago.OutputAnchor, iotago.OutputFoundry, iotago.OutputNFT, iotago.OutputDelegation}

return outputTypes[RandomIntn(len(outputTypes)-1)]
}
Expand Down Expand Up @@ -181,12 +188,27 @@ func RandOutputOnAddressWithAmount(outputType iotago.OutputType, address iotago.
},
Features: iotago.BasicOutputFeatures{},
}

case iotago.OutputAccount:
//nolint:forcetypeassert // we already checked the type
iotaOutput = &iotago.AccountOutput{
Amount: amount,
AccountID: RandAccountID(),
Conditions: iotago.AccountOutputUnlockConditions{
&iotago.AddressUnlockCondition{
Address: address,
},
},
Features: iotago.AccountOutputFeatures{},
ImmutableFeatures: iotago.AccountOutputImmFeatures{},
}

case iotago.OutputAnchor:
//nolint:forcetypeassert // we already checked the type
iotaOutput = &iotago.AnchorOutput{
Amount: amount,
AnchorID: RandAnchorID(),
Conditions: iotago.AnchorOutputUnlockConditions{
&iotago.StateControllerAddressUnlockCondition{
Address: address,
},
Expand All @@ -195,9 +217,10 @@ func RandOutputOnAddressWithAmount(outputType iotago.OutputType, address iotago.
},
},
StateMetadata: make([]byte, 0),
Features: iotago.AccountOutputFeatures{},
ImmutableFeatures: iotago.AccountOutputImmFeatures{},
Features: iotago.AnchorOutputFeatures{},
ImmutableFeatures: iotago.AnchorOutputImmFeatures{},
}

case iotago.OutputFoundry:
if address.Type() != iotago.AddressAccount {
panic("not an alias address")
Expand All @@ -221,6 +244,7 @@ func RandOutputOnAddressWithAmount(outputType iotago.OutputType, address iotago.
Features: iotago.FoundryOutputFeatures{},
ImmutableFeatures: iotago.FoundryOutputImmFeatures{},
}

case iotago.OutputNFT:
//nolint:forcetypeassert // we already checked the type
iotaOutput = &iotago.NFTOutput{
Expand All @@ -234,6 +258,7 @@ func RandOutputOnAddressWithAmount(outputType iotago.OutputType, address iotago.
Features: iotago.NFTOutputFeatures{},
ImmutableFeatures: iotago.NFTOutputImmFeatures{},
}

case iotago.OutputDelegation:
//nolint:forcetypeassert // we already checked the type
iotaOutput = &iotago.DelegationOutput{
Expand All @@ -249,6 +274,7 @@ func RandOutputOnAddressWithAmount(outputType iotago.OutputType, address iotago.
},
},
}

default:
panic("unhandled output type")
}
Expand Down
2 changes: 1 addition & 1 deletion tools/gendoc/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ require (
github.com/iotaledger/hive.go/stringify v0.0.0-20231027195901-620bd7470e42 // indirect
github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231028104239-869296c43f26 // indirect
github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231028104044-69b02af0058d // indirect
github.com/iotaledger/iota.go/v4 v4.0.0-20231028103644-b834fd54b02a // indirect
github.com/iotaledger/iota.go/v4 v4.0.0-20231031113109-5d7d59311967 // indirect
github.com/ipfs/boxo v0.13.1 // indirect
github.com/ipfs/go-cid v0.4.1 // indirect
github.com/ipfs/go-datastore v0.6.0 // indirect
Expand Down
Loading

0 comments on commit 86b2c44

Please sign in to comment.