Skip to content
This repository was archived by the owner on Jan 24, 2025. It is now read-only.

Implement account transition unit test #676

Merged
merged 39 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
a113b0a
Fix retrieving blockIssuerKey in mock.Wallet
jkrvivian Jan 17, 2024
6a07645
Implement combined account transition test
jkrvivian Jan 17, 2024
8e13a4d
Check transaction outputs in serialized bytes
jkrvivian Jan 17, 2024
a29a425
Cleanup and add more comments
jkrvivian Jan 17, 2024
3b843f6
Merge branch 'develop' into test-scenario
jkrvivian Jan 18, 2024
b925d80
Remove log
jkrvivian Jan 18, 2024
a99d53d
DumpContainerLogsToFiles after nodes are synced
jkrvivian Jan 18, 2024
95e9802
Implement Test_AccountTransitions
jkrvivian Jan 18, 2024
48ce234
Add comments
jkrvivian Jan 18, 2024
9702554
Bump iota.go version
jkrvivian Jan 18, 2024
d71bbcf
Address account output immutable feature and foundry output builder c…
jkrvivian Jan 18, 2024
cb71ea1
Add dockertests flag to account transition test file
jkrvivian Jan 18, 2024
23563ce
Add comments
jkrvivian Jan 18, 2024
7b742d6
Fix linter errors
jkrvivian Jan 18, 2024
6837714
Improve logs
jkrvivian Jan 18, 2024
3e2aae4
Remove commitment input in AllotManaTo
jkrvivian Jan 19, 2024
aa6a228
Check errors and renaming
jkrvivian Jan 19, 2024
d78609d
Allow to specify minted amount and max supply of native tokens
jkrvivian Jan 19, 2024
c2f3774
Add TODO comment in AssertTransaction
jkrvivian Jan 19, 2024
fed028a
Fix irrelevant checks on Native Token creation
jkrvivian Jan 19, 2024
fa8de5b
Simplify tx builder options in testsuite
muXxer Jan 19, 2024
883ec0e
Dump logs after running and synced
jkrvivian Jan 22, 2024
53c48b3
Merge branch 'develop' into test-scenario
jkrvivian Jan 23, 2024
3da6c69
Disable transaction capabilities by default
jkrvivian Jan 23, 2024
2bdbf70
Remove unnecessary step in the test
alexsporn Jan 24, 2024
5945369
Fixed equality check
alexsporn Jan 24, 2024
e4b7922
Merge branch 'develop' into test-scenario
jkrvivian Jan 24, 2024
ebe0f13
Merge branch 'test-scenario' of github.com:iotaledger/iota-core into …
jkrvivian Jan 24, 2024
73803c7
Merge branch 'develop' of github.com:iotaledger/iota-core into test-s…
jkrvivian Jan 25, 2024
877f026
Adapt changes to develop
jkrvivian Jan 25, 2024
baa771c
Merge branch 'develop' into test-scenario
jkrvivian Jan 26, 2024
7b61447
Adapt to latest changes in iota.go
jkrvivian Jan 26, 2024
8e5f244
Fix wrong AccountDiff in Test_AccountStateTransition
jkrvivian Jan 26, 2024
a512205
Set logger level to Info in Test_AccountStateTransition
jkrvivian Jan 26, 2024
9377c25
Explain the flow of Test_AccountTransitions
jkrvivian Jan 26, 2024
b457457
Merge branch 'develop' into test-scenario
jkrvivian Jan 26, 2024
fc100e2
Fix public key type conversion in CreateAccount
jkrvivian Jan 29, 2024
0d93eea
Fix data race on DockerTestFramework nodes
jkrvivian Jan 29, 2024
db66666
Merge branch 'develop' of github.com:iotaledger/iota-core into test-s…
muXxer Feb 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
261 changes: 261 additions & 0 deletions pkg/tests/combined_account_transition_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
package tests

import (
"testing"

"github.com/iotaledger/iota-core/pkg/model"
"github.com/iotaledger/iota-core/pkg/protocol/engine/accounts"
"github.com/iotaledger/iota-core/pkg/testsuite"
"github.com/iotaledger/iota-core/pkg/testsuite/mock"
iotago "github.com/iotaledger/iota.go/v4"
"github.com/iotaledger/iota.go/v4/tpkg"
)

// Test_AccountStateTransition follows the account state transition flow described in:
// https://github.com/iotaledger/iota-core/issues/660#issuecomment-1892596243
func Test_AccountStateTransition(t *testing.T) {
ts := testsuite.NewTestSuite(t,
testsuite.WithProtocolParametersOptions(
iotago.WithTimeProviderOptions(
0,
testsuite.GenesisTimeWithOffsetBySlots(200, testsuite.DefaultSlotDurationInSeconds),
testsuite.DefaultSlotDurationInSeconds,
testsuite.DefaultSlotsPerEpochExponent,
),
),
)
defer ts.Shutdown()

node1 := ts.AddValidatorNode("node1")
node2 := ts.AddValidatorNode("node2")
wallet := ts.AddDefaultWallet(node1)

ts.Run(true)

// split genesis output into 4 outputs for the further usage.
tx1 := wallet.CreateBasicOutputsEquallyFromInput("TX1", 4, "Genesis:0")
ts.IssueBasicBlockWithOptions("block0", wallet, tx1)

// Issue some more blocks to make transaction accepted
{
ts.IssueValidationBlockWithHeaderOptions("vblock0", node2, mock.WithStrongParents(ts.BlockID("block0")))
ts.IssueValidationBlockWithHeaderOptions("vblock1", node1, mock.WithStrongParents(ts.BlockID("vblock0")))
ts.IssueValidationBlockWithHeaderOptions("vblock2", node2, mock.WithStrongParents(ts.BlockID("vblock1")))

ts.AssertTransactionsInCacheAccepted(wallet.Transactions("TX1"), true, node1, node2)
}

// create the account1 from TX1:0 with wallet "first"
// generated (block1, TX2)
ts.AddWallet("first", node1, iotago.EmptyAccountID)
createFullAccount(ts)

// create the account2, from implicit to full account from TX1:1 with wallet "second"
// generated (block2, TX3), (block3, TX4)
ts.AddWallet("second", node1, iotago.EmptyAccountID)
account2ID := createImplicitToFullAccount(ts)

// send funds to account2, with TX1:2
// generated (block4, TX5)
sendFunds(ts)

// allot 1000 mana to account2 with TX1:3
// generated (block5, TX6)
allotManaTo(ts, account2ID)

// create native token from "TX5:0" and account2 (TX4:0)
// generated (block6, TX7)
createNativetoken(ts)
}

func createFullAccount(ts *testsuite.TestSuite) iotago.AccountID {
node1 := ts.Node("node1")
newUserWallet := ts.Wallet("first")

// CREATE NEW ACCOUNT WITH BLOCK ISSUER FROM BASIC UTXO
newAccountBlockIssuerKey := tpkg.RandBlockIssuerKey()
// set the expiry slot of the transitioned genesis account to the latest committed + MaxCommittableAge
newAccountExpirySlot := node1.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Slot() + ts.API.ProtocolParameters().MaxCommittableAge()

tx1 := ts.DefaultWallet().CreateAccountFromInput(
"TX2",
"TX1:0",
newUserWallet,
mock.WithBlockIssuerFeature(iotago.BlockIssuerKeys{newAccountBlockIssuerKey}, newAccountExpirySlot),
mock.WithAccountAmount(mock.MinIssuerAccountAmount(ts.API.ProtocolParameters())),
mock.WithAccountMana(mock.MaxBlockManaCost(ts.DefaultWallet().Node.Protocol.CommittedAPI().ProtocolParameters())),
)

block1 := ts.IssueBasicBlockWithOptions("block1", ts.DefaultWallet(), tx1)
var block1Slot iotago.SlotIndex = block1.ID().Slot()

ts.CommitUntilSlot(block1Slot, block1.ID())

newAccount := newUserWallet.AccountOutput("TX2:0")
newAccountOutput := newAccount.Output().(*iotago.AccountOutput)

ts.AssertAccountDiff(newAccountOutput.AccountID, block1Slot, &model.AccountDiff{
BICChange: 0,
PreviousUpdatedSlot: 0,
NewExpirySlot: newAccountExpirySlot,
PreviousExpirySlot: 0,
NewOutputID: newAccount.OutputID(),
PreviousOutputID: iotago.EmptyOutputID,
BlockIssuerKeysAdded: iotago.NewBlockIssuerKeys(newAccountBlockIssuerKey),
BlockIssuerKeysRemoved: iotago.NewBlockIssuerKeys(),
ValidatorStakeChange: 0,
StakeEndEpochChange: 0,
FixedCostChange: 0,
DelegationStakeChange: 0,
}, false, ts.Nodes()...)

ts.AssertAccountData(&accounts.AccountData{
ID: newAccountOutput.AccountID,
Credits: accounts.NewBlockIssuanceCredits(0, block1Slot),
ExpirySlot: newAccountExpirySlot,
OutputID: newAccount.OutputID(),
BlockIssuerKeys: iotago.NewBlockIssuerKeys(newAccountBlockIssuerKey),
}, ts.Nodes()...)

return newAccountOutput.AccountID
}

func createImplicitToFullAccount(ts *testsuite.TestSuite) iotago.AccountID {
node1 := ts.Node("node1")
newUserWallet := ts.Wallet("second")

// CREATE IMPLICIT ACCOUNT FROM GENESIS BASIC UTXO, SENT TO A NEW USER WALLET.
// a default wallet, already registered in the ledger, will issue the transaction and block.
tx3 := ts.DefaultWallet().CreateImplicitAccountFromInput(
"TX3",
"TX1:1",
newUserWallet,
)
block2 := ts.IssueBasicBlockWithOptions("block2", ts.DefaultWallet(), tx3)
block2Slot := block2.ID().Slot()
latestParents := ts.CommitUntilSlot(block2Slot, block2.ID())

implicitAccountOutput := newUserWallet.Output("TX3:0")
implicitAccountOutputID := implicitAccountOutput.OutputID()
implicitAccountID := iotago.AccountIDFromOutputID(implicitAccountOutputID)
var implicitBlockIssuerKey iotago.BlockIssuerKey = iotago.Ed25519PublicKeyHashBlockIssuerKeyFromImplicitAccountCreationAddress(newUserWallet.ImplicitAccountCreationAddress())

// the new implicit account should now be registered in the accounts ledger.
ts.AssertAccountData(&accounts.AccountData{
ID: implicitAccountID,
Credits: accounts.NewBlockIssuanceCredits(0, block2Slot),
ExpirySlot: iotago.MaxSlotIndex,
OutputID: implicitAccountOutputID,
BlockIssuerKeys: iotago.NewBlockIssuerKeys(implicitBlockIssuerKey),
}, ts.Nodes()...)

// TRANSITION IMPLICIT ACCOUNT TO ACCOUNT OUTPUT.
block3Slot := ts.CurrentSlot()
tx4 := newUserWallet.TransitionImplicitAccountToAccountOutput(
"TX4",
"TX3:0",
mock.WithBlockIssuerFeature(
iotago.BlockIssuerKeys{implicitBlockIssuerKey},
iotago.MaxSlotIndex,
),
mock.WithAccountAmount(mock.MinIssuerAccountAmount(ts.API.ProtocolParameters())),
)
block2Commitment := node1.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment()
block3 := ts.IssueBasicBlockWithOptions("block3", newUserWallet, tx4, mock.WithStrongParents(latestParents...))
latestParents = ts.CommitUntilSlot(block3Slot, block3.ID())

fullAccountOutputID := newUserWallet.Output("TX4:0").OutputID()
allotted := iotago.BlockIssuanceCredits(tx4.Transaction.Allotments.Get(implicitAccountID))
burned := iotago.BlockIssuanceCredits(block3.WorkScore()) * iotago.BlockIssuanceCredits(block2Commitment.ReferenceManaCost)
// the implicit account should now have been transitioned to a full account in the accounts ledger.
ts.AssertAccountDiff(implicitAccountID, block3Slot, &model.AccountDiff{
BICChange: allotted - burned,
PreviousUpdatedSlot: block2Slot,
NewOutputID: fullAccountOutputID,
PreviousOutputID: implicitAccountOutputID,
PreviousExpirySlot: iotago.MaxSlotIndex,
NewExpirySlot: iotago.MaxSlotIndex,
ValidatorStakeChange: 0,
StakeEndEpochChange: 0,
FixedCostChange: 0,
DelegationStakeChange: 0,
}, false, ts.Nodes()...)

ts.AssertAccountData(&accounts.AccountData{
ID: implicitAccountID,
Credits: accounts.NewBlockIssuanceCredits(allotted-burned, block3Slot),
ExpirySlot: iotago.MaxSlotIndex,
OutputID: fullAccountOutputID,
BlockIssuerKeys: iotago.NewBlockIssuerKeys(implicitBlockIssuerKey),
}, ts.Nodes()...)

return implicitAccountID
}

func sendFunds(ts *testsuite.TestSuite) {
node1 := ts.Node("node1")
node2 := ts.Node("node2")
wallet := ts.DefaultWallet()
secondWallet := ts.Wallet("second")

// send funds from defaultWallet to secondWallet
tx := wallet.SendFundsToWallet("TX5", secondWallet, "TX1:2")
ts.IssueBasicBlockWithOptions("block4", wallet, tx)

ts.AssertTransactionsExist(wallet.Transactions("TX5"), true, node1)
ts.AssertTransactionsInCacheBooked(wallet.Transactions("TX5"), true, node1)

// Issue some more blocks to make transaction accepted
{
ts.IssueValidationBlockWithHeaderOptions("vblock9", node2, mock.WithStrongParents(ts.BlockID("block4")))
ts.IssueValidationBlockWithHeaderOptions("vblock10", node1, mock.WithStrongParents(ts.BlockID("vblock9")))
ts.IssueValidationBlockWithHeaderOptions("vblock11", node2, mock.WithStrongParents(ts.BlockID("vblock10")))

ts.AssertTransactionsInCacheAccepted(wallet.Transactions("TX5"), true, node1, node2)
}
}

func allotManaTo(ts *testsuite.TestSuite, to iotago.AccountID) {
wallet := ts.DefaultWallet()
node1 := ts.Node("node1")
node2 := ts.Node("node2")

tx6 := wallet.AllotManaFromInputs("TX6",
iotago.Allotments{&iotago.Allotment{
AccountID: to,
Mana: iotago.Mana(1000),
}}, "TX1:3")
commitment := node1.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment()
ts.IssueBasicBlockWithOptions("block5", wallet, tx6, mock.WithSlotCommitment(commitment))

// Issue some more blocks to make transaction accepted
{
ts.IssueValidationBlockWithHeaderOptions("vblock6", node2, mock.WithStrongParents(ts.BlockID("block5")))
ts.IssueValidationBlockWithHeaderOptions("vblock7", node1, mock.WithStrongParents(ts.BlockID("vblock6")))
ts.IssueValidationBlockWithHeaderOptions("vblock8", node2, mock.WithStrongParents(ts.BlockID("vblock7")))

ts.AssertTransactionsInCacheAccepted(wallet.Transactions("TX6"), true, node1, node2)
}
}

// createNativetoken creates a native token from the given input and account.
func createNativetoken(ts *testsuite.TestSuite) {
wallet := ts.Wallet("second")
node1 := ts.Node("node1")
node2 := ts.Node("node2")

tx := wallet.CreateNativeTokenFromInput("TX7", "TX5:0", "TX4:0", 5_000_000, 10_000_000_000)
ts.IssueBasicBlockWithOptions("block6", wallet, tx)

ts.AssertTransactionsExist(wallet.Transactions("TX7"), true, node1)
ts.AssertTransactionsInCacheBooked(wallet.Transactions("TX7"), true, node1)

// Issue some more blocks to make transaction accepted
{
ts.IssueValidationBlockWithHeaderOptions("vblock12", node2, mock.WithStrongParents(ts.BlockID("block6")))
ts.IssueValidationBlockWithHeaderOptions("vblock13", node1, mock.WithStrongParents(ts.BlockID("vblock12")))
ts.IssueValidationBlockWithHeaderOptions("vblock14", node2, mock.WithStrongParents(ts.BlockID("vblock13")))

ts.AssertTransactionsInCacheAccepted(wallet.Transactions("TX7"), true, node1, node2)
}
}
4 changes: 2 additions & 2 deletions pkg/testsuite/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,11 @@ func (t *TestSuite) AssertAccountDiff(accountID iotago.AccountID, index iotago.S
return ierrors.Errorf("AssertAccountDiff: %s: expected previous output ID %s but actual %s for account %s at slot %d", node.Name, accountDiff.PreviousOutputID, actualAccountDiff.PreviousOutputID, accountID, index)
}

if !assert.Equal(t.fakeTesting, accountDiff.BlockIssuerKeysAdded, actualAccountDiff.BlockIssuerKeysAdded) {
if !assert.True(t.fakeTesting, accountDiff.BlockIssuerKeysAdded.Equal(actualAccountDiff.BlockIssuerKeysAdded)) {
return ierrors.Errorf("AssertAccountDiff: %s: expected pub keys added %s but actual %s for account %s at slot %d", node.Name, accountDiff.BlockIssuerKeysAdded, actualAccountDiff.BlockIssuerKeysAdded, accountID, index)
}

if !assert.Equal(t.fakeTesting, accountDiff.BlockIssuerKeysRemoved, actualAccountDiff.BlockIssuerKeysRemoved) {
if !assert.True(t.fakeTesting, accountDiff.BlockIssuerKeysRemoved.Equal(actualAccountDiff.BlockIssuerKeysRemoved)) {
return ierrors.Errorf("AssertAccountDiff: %s: expected pub keys removed %s but actual %s for account %s at slot %d", node.Name, accountDiff.BlockIssuerKeysRemoved, actualAccountDiff.BlockIssuerKeysRemoved, accountID, index)
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/testsuite/mock/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func MaxBlockManaCost(protocolParameters iotago.ProtocolParameters) iotago.Mana

// TransactionBuilder options

func WithInputs(inputs utxoledger.Outputs) options.Option[builder.TransactionBuilder] {
func WithInputs(inputs ...*utxoledger.Output) options.Option[builder.TransactionBuilder] {
return func(txBuilder *builder.TransactionBuilder) {
for _, input := range inputs {
switch input.OutputType() {
Expand Down Expand Up @@ -124,7 +124,7 @@ func WithRewardInput(input *iotago.RewardInput, mana iotago.Mana) options.Option
}
}

func WithOutputs(outputs iotago.Outputs[iotago.Output]) options.Option[builder.TransactionBuilder] {
func WithOutputs(outputs ...iotago.Output) options.Option[builder.TransactionBuilder] {
return func(txBuilder *builder.TransactionBuilder) {
for _, output := range outputs {
txBuilder.AddOutput(output)
Expand Down
2 changes: 1 addition & 1 deletion pkg/testsuite/mock/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func (w *Wallet) SetBlockIssuer(accountID iotago.AccountID) {

func (w *Wallet) BlockIssuerKey() iotago.BlockIssuerKey {
if w.BlockIssuer != nil {
return w.BlockIssuerKey()
return w.BlockIssuer.BlockIssuerKey()
}
_, pub := w.keyManager.KeyPair()

Expand Down
Loading