From 95e98021da257a0f898225566e6bf3a0e7a6202e Mon Sep 17 00:00:00 2001 From: jkrvivian Date: Thu, 18 Jan 2024 16:03:11 +0800 Subject: [PATCH] Implement Test_AccountTransitions --- .../tests/accounttransition_test.go | 48 +++++ tools/docker-network/tests/dockerframework.go | 171 ++++++++++++++++++ 2 files changed, 219 insertions(+) create mode 100644 tools/docker-network/tests/accounttransition_test.go diff --git a/tools/docker-network/tests/accounttransition_test.go b/tools/docker-network/tests/accounttransition_test.go new file mode 100644 index 000000000..be1b9e21b --- /dev/null +++ b/tools/docker-network/tests/accounttransition_test.go @@ -0,0 +1,48 @@ +package tests + +import ( + "context" + "testing" + "time" + + iotago "github.com/iotaledger/iota.go/v4" + "github.com/stretchr/testify/require" +) + +func Test_AccountTransitions(t *testing.T) { + d := NewDockerTestFramework(t, + WithProtocolParametersOptions( + iotago.WithTimeProviderOptions(5, time.Now().Unix(), 10, 4), + iotago.WithLivenessOptions(10, 10, 2, 4, 8), + iotago.WithRewardsOptions(8, 8, 10, 2, 1, 384), + iotago.WithTargetCommitteeSize(4), + )) + defer d.Stop() + + d.AddValidatorNode("V1", "docker-network-inx-validator-1-1", "http://localhost:8050", "rms1pzg8cqhfxqhq7pt37y8cs4v5u4kcc48lquy2k73ehsdhf5ukhya3y5rx2w6") + d.AddValidatorNode("V2", "docker-network-inx-validator-2-1", "http://localhost:8060", "rms1pqm4xk8e9ny5w5rxjkvtp249tfhlwvcshyr3pc0665jvp7g3hc875k538hl") + d.AddValidatorNode("V3", "docker-network-inx-validator-3-1", "http://localhost:8070", "rms1pp4wuuz0y42caz48vv876qfpmffswsvg40zz8v79sy8cp0jfxm4kunflcgt") + d.AddValidatorNode("V4", "docker-network-inx-validator-4-1", "http://localhost:8040", "rms1pr8cxs3dzu9xh4cduff4dd4cxdthpjkpwmz2244f75m0urslrsvtsshrrjw") + d.AddNode("node5", "docker-network-node-5-1", "http://localhost:8090") + + err := d.Run() + require.NoError(t, err) + + err = d.WaitUntilSync() + require.NoError(t, err) + + // create account1 + account1 := d.CreateAccount() + // create account2 + account2 := d.CreateAccount() + + // request faucet funds + fundsAddr, _ := d.getAddress(iotago.AddressEd25519) + d.RequestFaucetFunds(context.TODO(), fundsAddr) + + // allot 1000 mana from account1 to account2 + d.AllotManaTo(account1, account2, 1000) + + // create native token + account2 = d.CreateNativeToken(account2) +} diff --git a/tools/docker-network/tests/dockerframework.go b/tools/docker-network/tests/dockerframework.go index ec0e208e1..2d28f71f9 100644 --- a/tools/docker-network/tests/dockerframework.go +++ b/tools/docker-network/tests/dockerframework.go @@ -7,6 +7,7 @@ import ( "crypto/ed25519" "fmt" "log" + "math/big" "os/exec" "sort" "strings" @@ -61,6 +62,8 @@ type Account struct { AccountID iotago.AccountID AccountAddress *iotago.AccountAddress BlockIssuerKey ed25519.PrivateKey + AccountOutput *iotago.AccountOutput + OutputID iotago.OutputID } type DockerTestFramework struct { @@ -375,6 +378,8 @@ func (d *DockerTestFramework) CreateAccount(opts ...options.Option[builder.Accou AccountID: accountID, AccountAddress: accountAddress, BlockIssuerKey: accPrivateKey, + AccountOutput: accountOutput, + OutputID: accOutputID, } } @@ -425,6 +430,135 @@ func (d *DockerTestFramework) DelegateToValidator(from *Account, validator *Node return delegationOutput.StartEpoch } +// AllotManaTo allots amount of mana from one account to another. +func (d *DockerTestFramework) AllotManaTo(from *Account, to *Account, manaToAllot iotago.Mana) { + // requesting faucet funds for allotment + ctx := context.TODO() + fundsAddr, privateKey := d.getAddress(iotago.AddressEd25519) + fundsOutputID, fundsUTXOOutput := d.RequestFaucetFunds(ctx, fundsAddr) + fundsAddrSigner := iotago.NewInMemoryAddressSigner(iotago.NewAddressKeysForEd25519Address(fundsAddr.(*iotago.Ed25519Address), privateKey)) + + clt := d.Node("V1").Client + currentSlot := clt.LatestAPI().TimeProvider().SlotFromTime(time.Now()) + apiForSlot := clt.APIForSlot(currentSlot) + + basicOutput, ok := fundsUTXOOutput.(*iotago.BasicOutput) + require.True(d.Testing, ok) + + // Subtract stored mana from source outputs to fund Allotment. + outputBuilder := builder.NewBasicOutputBuilderFromPrevious(basicOutput) + actualAllottedMana := manaToAllot + if manaToAllot >= basicOutput.StoredMana() { + actualAllottedMana = basicOutput.StoredMana() + outputBuilder.Mana(0) + } else { + outputBuilder.Mana(basicOutput.StoredMana() - manaToAllot) + } + + issuerResp, err := clt.BlockIssuance(ctx) + require.NoError(d.Testing, err) + + congestionResp, err := clt.Congestion(ctx, from.AccountAddress, lo.PanicOnErr(issuerResp.LatestCommitment.ID())) + require.NoError(d.Testing, err) + + signedTx, err := builder.NewTransactionBuilder(apiForSlot). + AddInput(&builder.TxInput{ + UnlockTarget: fundsAddr, + InputID: fundsOutputID, + Input: fundsUTXOOutput, + }). + IncreaseAllotment(to.AccountID, actualAllottedMana). + AddOutput(basicOutput). + SetCreationSlot(currentSlot). + AddCommitmentInput(&iotago.CommitmentInput{CommitmentID: lo.Return1(issuerResp.LatestCommitment.ID())}). + WithTransactionCapabilities(iotago.TransactionCapabilitiesBitMaskWithCapabilities(iotago.WithTransactionCanDoAnything())). + Build(fundsAddrSigner) + require.NoError(d.Testing, err) + + blkID := d.SubmitPayload(ctx, signedTx, wallet.NewEd25519Account(from.AccountID, from.BlockIssuerKey), congestionResp, issuerResp) + + d.AwaitTransactionPayloadAccepted(ctx, blkID) +} + +func (d *DockerTestFramework) CreateNativeToken(from *Account) (updatedAccount *Account) { + // requesting faucet funds for native token creation + ctx := context.TODO() + fundsAddr, privateKey := d.getAddress(iotago.AddressEd25519) + fundsOutputID, fundsUTXOOutput := d.RequestFaucetFunds(ctx, fundsAddr) + + mintedAmount := fundsUTXOOutput.BaseTokenAmount() + + clt := d.Node("V1").Client + currentSlot := clt.LatestAPI().TimeProvider().SlotFromTime(time.Now()) + apiForSlot := clt.APIForSlot(currentSlot) + + // increase foundry counter + accTransitionOutput := builder.NewAccountOutputBuilderFromPrevious(from.AccountOutput). + FoundriesToGenerate(1).MustBuild() + + // build foundry output, consume all amount from the UTXO output + foundryID, _ := iotago.FoundryIDFromAddressAndSerialNumberAndTokenScheme(from.AccountAddress, accTransitionOutput.FoundryCounter, iotago.TokenSchemeSimple) + tokenScheme := &iotago.SimpleTokenScheme{ + MintedTokens: big.NewInt(int64(mintedAmount)), + MaximumSupply: big.NewInt(int64(mintedAmount)), + MeltedTokens: big.NewInt(0), + } + + foundryOutput := builder.NewFoundryOutputBuilder(from.AccountAddress, tokenScheme, mintedAmount). + NativeToken(&iotago.NativeTokenFeature{ + ID: foundryID, + Amount: big.NewInt(int64(mintedAmount)), + }). + SerialNumber(accTransitionOutput.FoundryCounter).MustBuild() + + // prepare transaction + issuerResp, err := clt.BlockIssuance(ctx) + require.NoError(d.Testing, err) + + congestionResp, err := clt.Congestion(ctx, from.AccountAddress, lo.PanicOnErr(issuerResp.LatestCommitment.ID())) + require.NoError(d.Testing, err) + + signer := iotago.NewInMemoryAddressSigner(iotago.NewAddressKeysForEd25519Address(fundsAddr.(*iotago.Ed25519Address), privateKey), + iotago.NewAddressKeysForEd25519Address(from.AccountOutput.UnlockConditionSet().Address().Address.(*iotago.Ed25519Address), from.BlockIssuerKey)) + + signedTx, err := builder.NewTransactionBuilder(apiForSlot). + AddInput(&builder.TxInput{ + UnlockTarget: fundsAddr, + InputID: fundsOutputID, + Input: fundsUTXOOutput, + }). + AddInput(&builder.TxInput{ + UnlockTarget: from.AccountOutput.UnlockConditionSet().Address().Address, + InputID: from.OutputID, + Input: from.AccountOutput, + }). + AddOutput(accTransitionOutput). + AddOutput(foundryOutput). + SetCreationSlot(currentSlot). + AddBlockIssuanceCreditInput(&iotago.BlockIssuanceCreditInput{AccountID: from.AccountID}). + AddCommitmentInput(&iotago.CommitmentInput{CommitmentID: lo.Return1(issuerResp.LatestCommitment.ID())}). + WithTransactionCapabilities(iotago.TransactionCapabilitiesBitMaskWithCapabilities(iotago.WithTransactionCanDoAnything())). + AllotAllMana(currentSlot, from.AccountID). + Build(signer) + require.NoError(d.Testing, err) + + blkID := d.SubmitPayload(ctx, signedTx, wallet.NewEd25519Account(from.AccountID, from.BlockIssuerKey), congestionResp, issuerResp) + + updatedAccount = &Account{ + AccountID: from.AccountID, + AccountAddress: from.AccountAddress, + BlockIssuerKey: from.BlockIssuerKey, + AccountOutput: accTransitionOutput, + OutputID: iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(signedTx.Transaction.ID()), 0), + } + + d.AwaitTransactionPayloadAccepted(ctx, blkID) + d.AssertIndexerAccount(updatedAccount) + d.AssertIndexerFoundry(foundryID) + + return updatedAccount +} + func (d *DockerTestFramework) CheckAccountStatus(ctx context.Context, blkID iotago.BlockID, txID iotago.TransactionID, creationOutputID iotago.OutputID, accountAddress *iotago.AccountAddress, checkIndexer ...bool) { // request by blockID if provided, otherwise use txID // we take the slot from the blockID in case the tx is created earlier than the block. @@ -469,6 +603,43 @@ func (d *DockerTestFramework) RequestFaucetFunds(ctx context.Context, receiveAdd return outputID, output } +func (d *DockerTestFramework) AssertIndexerAccount(account *Account) { + d.Eventually(func() error { + ctx := context.TODO() + indexerClt, err := d.Node("V1").Client.Indexer(ctx) + if err != nil { + return err + } + + outputID, output, _, err := indexerClt.Account(ctx, account.AccountAddress) + if err != nil { + return err + } + + require.EqualValues(d.Testing, account.OutputID, outputID) + require.EqualValues(d.Testing, account.AccountOutput, output) + + return nil + }) +} + +func (d *DockerTestFramework) AssertIndexerFoundry(foundryID iotago.FoundryID) { + d.Eventually(func() error { + ctx := context.TODO() + indexerClt, err := d.Node("V1").Client.Indexer(ctx) + if err != nil { + return err + } + + _, _, _, err = indexerClt.Foundry(ctx, foundryID) + if err != nil { + return err + } + + return nil + }) +} + func (d *DockerTestFramework) AssertValidatorExists(accountAddr *iotago.AccountAddress) { d.Eventually(func() error { for _, node := range d.nodes {