diff --git a/components/inx/server_accounts.go b/components/inx/server_accounts.go index 164d121a5..e823c4d0c 100644 --- a/components/inx/server_accounts.go +++ b/components/inx/server_accounts.go @@ -20,7 +20,7 @@ func (s *Server) ReadIsValidatorAccount(_ context.Context, accountInfoRequest *i return nil, ierrors.Wrapf(err, "error when retrieving account data for %s", accountID) } - return inx.WrapBoolResponse(exists && account.StakeEndEpoch <= deps.Protocol.APIForSlot(slot).TimeProvider().EpochFromSlot(slot)), nil + return inx.WrapBoolResponse(exists && account.StakeEndEpoch() <= deps.Protocol.APIForSlot(slot).TimeProvider().EpochFromSlot(slot)), nil } func (s *Server) ReadIsCommitteeMember(_ context.Context, accountInfoRequest *inx.AccountInfoRequest) (*inx.BoolResponse, error) { diff --git a/components/prometheus/metrics_accounts.go b/components/prometheus/metrics_accounts.go index 381d38bdb..b20cd6166 100644 --- a/components/prometheus/metrics_accounts.go +++ b/components/prometheus/metrics_accounts.go @@ -25,7 +25,7 @@ var AccountMetrics = collector.NewCollection(accountNamespace, deps.Protocol.Events.Engine.BlockGadget.BlockAccepted.Hook(func(block *blocks.Block) { accountData, exists, _ := deps.Protocol.Engines.Main.Get().Ledger.Account(block.IssuerID(), deps.Protocol.Engines.Main.Get().SyncManager.LatestCommitment().Slot()) if exists { - deps.Collector.Update(accountNamespace, credits, float64(accountData.Credits.Value), accountData.ID.String()) + deps.Collector.Update(accountNamespace, credits, float64(accountData.Credits().Value()), accountData.ID().String()) } }, event.WithWorkerPool(Component.WorkerPool)) }), diff --git a/pkg/model/account_diff.go b/pkg/model/account_diff.go index f07ad0a03..8cbba56ee 100644 --- a/pkg/model/account_diff.go +++ b/pkg/model/account_diff.go @@ -2,6 +2,7 @@ package model import ( "io" + "strconv" "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/hive.go/lo" @@ -83,8 +84,8 @@ func (d *AccountDiff) String() string { builder.AddField(stringify.NewStructField("PreviousExpirySlot", uint32(d.PreviousExpirySlot))) builder.AddField(stringify.NewStructField("NewOutputID", d.NewOutputID)) builder.AddField(stringify.NewStructField("PreviousOutputID", d.PreviousOutputID)) - builder.AddField(stringify.NewStructField("BlockIssuerKeysAdded", d.BlockIssuerKeysAdded)) - builder.AddField(stringify.NewStructField("BlockIssuerKeysRemoved", d.BlockIssuerKeysRemoved)) + builder.AddField(stringify.NewStructField("BlockIssuerKeysAdded", func() string { return strconv.Itoa(d.BlockIssuerKeysAdded.Size()) }())) + builder.AddField(stringify.NewStructField("BlockIssuerKeysRemoved", func() string { return strconv.Itoa(d.BlockIssuerKeysRemoved.Size()) }())) builder.AddField(stringify.NewStructField("ValidatorStakeChange", d.ValidatorStakeChange)) builder.AddField(stringify.NewStructField("DelegationStakeChange", d.DelegationStakeChange)) builder.AddField(stringify.NewStructField("FixedCostChange", d.FixedCostChange)) diff --git a/pkg/protocol/commitment.go b/pkg/protocol/commitment.go index c293db19b..7be3ebf10 100644 --- a/pkg/protocol/commitment.go +++ b/pkg/protocol/commitment.go @@ -103,7 +103,7 @@ func newCommitment(commitments *Commitments, model *model.Commitment) *Commitmen WarpSyncBlocks: reactive.NewVariable[bool](), BlocksToWarpSync: reactive.NewVariable[ds.Set[iotago.BlockID]](), Weight: reactive.NewVariable[uint64](), - AttestedWeight: reactive.NewVariable[uint64](func(currentValue uint64, newValue uint64) uint64 { return max(currentValue, newValue) }), //nolint:gocritic // easier to read + AttestedWeight: reactive.NewVariable[uint64](func(currentValue uint64, newValue uint64) uint64 { return max(currentValue, newValue) }), CumulativeWeight: reactive.NewVariable[uint64](), CumulativeAttestedWeight: reactive.NewVariable[uint64](), CumulativeVerifiedWeight: reactive.NewVariable[uint64](), diff --git a/pkg/protocol/commitment_verifier.go b/pkg/protocol/commitment_verifier.go index 12014fa2c..d26b85af5 100644 --- a/pkg/protocol/commitment_verifier.go +++ b/pkg/protocol/commitment_verifier.go @@ -160,7 +160,7 @@ func (c *CommitmentVerifier) verifyAttestations(attestations []*iotago.Attestati switch signature := att.Signature.(type) { case *iotago.Ed25519Signature: // We found the accountData, but we don't know the public key used to sign this block/attestation. Ignore. - if !accountData.BlockIssuerKeys.Has(iotago.Ed25519PublicKeyHashBlockIssuerKeyFromPublicKey(signature.PublicKey)) { + if !accountData.BlockIssuerKeys().Has(iotago.Ed25519PublicKeyHashBlockIssuerKeyFromPublicKey(signature.PublicKey)) { continue } diff --git a/pkg/protocol/engine/accounts/accounts.go b/pkg/protocol/engine/accounts/accounts.go index 09503bca7..558c8161b 100644 --- a/pkg/protocol/engine/accounts/accounts.go +++ b/pkg/protocol/engine/accounts/accounts.go @@ -6,6 +6,7 @@ import ( "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/hive.go/runtime/options" + "github.com/iotaledger/hive.go/runtime/syncutils" "github.com/iotaledger/hive.go/serializer/v2/stream" "github.com/iotaledger/hive.go/stringify" "github.com/iotaledger/iota-core/pkg/model" @@ -16,62 +17,211 @@ import ( type AccountsData []*AccountData type AccountData struct { - ID iotago.AccountID - Credits *BlockIssuanceCredits - ExpirySlot iotago.SlotIndex - OutputID iotago.OutputID - BlockIssuerKeys iotago.BlockIssuerKeys + mutex syncutils.RWMutex + + id iotago.AccountID + credits *BlockIssuanceCredits + expirySlot iotago.SlotIndex + outputID iotago.OutputID + blockIssuerKeys iotago.BlockIssuerKeys + + validatorStake iotago.BaseToken + delegationStake iotago.BaseToken + fixedCost iotago.Mana + stakeEndEpoch iotago.EpochIndex + latestSupportedProtocolVersionAndHash model.VersionAndHash +} + +// Getters. +func (a *AccountData) ID() iotago.AccountID { + a.mutex.RLock() + defer a.mutex.RUnlock() + + return a.id +} + +func (a *AccountData) Credits() *BlockIssuanceCredits { + a.mutex.RLock() + defer a.mutex.RUnlock() + + return a.credits +} + +func (a *AccountData) ExpirySlot() iotago.SlotIndex { + a.mutex.RLock() + defer a.mutex.RUnlock() + + return a.expirySlot +} + +func (a *AccountData) OutputID() iotago.OutputID { + a.mutex.RLock() + defer a.mutex.RUnlock() + + return a.outputID +} + +func (a *AccountData) BlockIssuerKeys() iotago.BlockIssuerKeys { + a.mutex.RLock() + defer a.mutex.RUnlock() + + return a.blockIssuerKeys +} + +func (a *AccountData) ValidatorStake() iotago.BaseToken { + a.mutex.RLock() + defer a.mutex.RUnlock() + + return a.validatorStake +} + +func (a *AccountData) DelegationStake() iotago.BaseToken { + a.mutex.RLock() + defer a.mutex.RUnlock() + + return a.delegationStake +} + +func (a *AccountData) FixedCost() iotago.Mana { + a.mutex.RLock() + defer a.mutex.RUnlock() + + return a.fixedCost +} + +func (a *AccountData) StakeEndEpoch() iotago.EpochIndex { + a.mutex.RLock() + defer a.mutex.RUnlock() + + return a.stakeEndEpoch +} +func (a *AccountData) LatestSupportedProtocolVersionAndHash() model.VersionAndHash { + a.mutex.RLock() + defer a.mutex.RUnlock() + + return a.latestSupportedProtocolVersionAndHash +} + +// Setters. +func (a *AccountData) SetID(id iotago.AccountID) { + a.mutex.Lock() + defer a.mutex.Unlock() + + a.id = id +} + +func (a *AccountData) SetCredits(credits *BlockIssuanceCredits) { + a.mutex.Lock() + defer a.mutex.Unlock() + + a.credits = credits +} + +func (a *AccountData) SetExpirySlot(expirySlot iotago.SlotIndex) { + a.mutex.Lock() + defer a.mutex.Unlock() + + a.expirySlot = expirySlot +} + +func (a *AccountData) SetOutputID(outputID iotago.OutputID) { + a.mutex.Lock() + defer a.mutex.Unlock() + + a.outputID = outputID +} + +func (a *AccountData) SetBlockIssuerKeys(blockIssuerKeys iotago.BlockIssuerKeys) { + a.mutex.Lock() + defer a.mutex.Unlock() + + a.blockIssuerKeys = blockIssuerKeys +} + +func (a *AccountData) SetValidatorStake(validatorStake iotago.BaseToken) { + a.mutex.Lock() + defer a.mutex.Unlock() + + a.validatorStake = validatorStake +} - ValidatorStake iotago.BaseToken - DelegationStake iotago.BaseToken - FixedCost iotago.Mana - StakeEndEpoch iotago.EpochIndex - LatestSupportedProtocolVersionAndHash model.VersionAndHash +func (a *AccountData) SetDelegationStake(delegationStake iotago.BaseToken) { + a.mutex.Lock() + defer a.mutex.Unlock() + + a.delegationStake = delegationStake +} + +func (a *AccountData) SetFixedCost(fixedCost iotago.Mana) { + a.mutex.Lock() + defer a.mutex.Unlock() + + a.fixedCost = fixedCost +} + +func (a *AccountData) SetStakeEndEpoch(stakeEndEpoch iotago.EpochIndex) { + a.mutex.Lock() + defer a.mutex.Unlock() + + a.stakeEndEpoch = stakeEndEpoch +} + +func (a *AccountData) SetLatestSupportedProtocolVersionAndHash(latestSupportedProtocolVersionAndHash model.VersionAndHash) { + a.mutex.Lock() + defer a.mutex.Unlock() + + a.latestSupportedProtocolVersionAndHash = latestSupportedProtocolVersionAndHash } func NewAccountData(id iotago.AccountID, opts ...options.Option[AccountData]) *AccountData { return options.Apply(&AccountData{ - ID: id, - Credits: &BlockIssuanceCredits{}, - ExpirySlot: 0, - OutputID: iotago.EmptyOutputID, - BlockIssuerKeys: iotago.NewBlockIssuerKeys(), - ValidatorStake: 0, - DelegationStake: 0, - FixedCost: 0, - StakeEndEpoch: 0, - LatestSupportedProtocolVersionAndHash: model.VersionAndHash{}, + id: id, + credits: &BlockIssuanceCredits{}, + expirySlot: 0, + outputID: iotago.EmptyOutputID, + blockIssuerKeys: iotago.NewBlockIssuerKeys(), + validatorStake: 0, + delegationStake: 0, + fixedCost: 0, + stakeEndEpoch: 0, + latestSupportedProtocolVersionAndHash: model.VersionAndHash{}, }, opts) } func (a *AccountData) AddBlockIssuerKeys(blockIssuerKeys ...iotago.BlockIssuerKey) { + a.mutex.Lock() + defer a.mutex.Unlock() + for _, blockIssuerKey := range blockIssuerKeys { - a.BlockIssuerKeys.Add(blockIssuerKey) + a.blockIssuerKeys.Add(blockIssuerKey) } } -func (a *AccountData) RemoveBlockIssuerKey(blockIssuerKeys ...iotago.BlockIssuerKey) { +func (a *AccountData) RemoveBlockIssuerKeys(blockIssuerKeys ...iotago.BlockIssuerKey) { + a.mutex.Lock() + defer a.mutex.Unlock() + for _, blockIssuerKey := range blockIssuerKeys { - a.BlockIssuerKeys.Remove(blockIssuerKey) + a.blockIssuerKeys.Remove(blockIssuerKey) } } func (a *AccountData) Clone() *AccountData { - return &AccountData{ - ID: a.ID, - Credits: &BlockIssuanceCredits{ - Value: a.Credits.Value, - UpdateSlot: a.Credits.UpdateSlot, - }, - ExpirySlot: a.ExpirySlot, - OutputID: a.OutputID, - BlockIssuerKeys: a.BlockIssuerKeys.Clone(), + a.mutex.RLock() + defer a.mutex.RUnlock() - ValidatorStake: a.ValidatorStake, - DelegationStake: a.DelegationStake, - FixedCost: a.FixedCost, - StakeEndEpoch: a.StakeEndEpoch, - LatestSupportedProtocolVersionAndHash: a.LatestSupportedProtocolVersionAndHash, + return &AccountData{ + id: a.id, + credits: NewBlockIssuanceCredits(a.credits.Value(), a.credits.UpdateSlot()), + expirySlot: a.expirySlot, + outputID: a.outputID, + blockIssuerKeys: a.blockIssuerKeys.Clone(), + + validatorStake: a.validatorStake, + delegationStake: a.delegationStake, + fixedCost: a.fixedCost, + stakeEndEpoch: a.stakeEndEpoch, + latestSupportedProtocolVersionAndHash: a.latestSupportedProtocolVersionAndHash, } } @@ -82,38 +232,40 @@ func AccountDataFromReader(reader io.ReadSeeker) (*AccountData, error) { } a := NewAccountData(accountID) + a.mutex.Lock() + defer a.mutex.Unlock() - if a.Credits, err = stream.ReadObject(reader, BlockIssuanceCreditsBytesLength, BlockIssuanceCreditsFromBytes); err != nil { + if a.credits, err = stream.ReadObject(reader, BlockIssuanceCreditsBytesLength, BlockIssuanceCreditsFromBytes); err != nil { return nil, ierrors.Wrap(err, "unable to read credits") } - if a.ExpirySlot, err = stream.Read[iotago.SlotIndex](reader); err != nil { + if a.expirySlot, err = stream.Read[iotago.SlotIndex](reader); err != nil { return nil, ierrors.Wrap(err, "unable to read expiry slot") } - if a.OutputID, err = stream.Read[iotago.OutputID](reader); err != nil { + if a.outputID, err = stream.Read[iotago.OutputID](reader); err != nil { return nil, ierrors.Wrap(err, "unable to read outputID") } - if a.BlockIssuerKeys, err = stream.ReadObjectFromReader(reader, iotago.BlockIssuerKeysFromReader); err != nil { + if a.blockIssuerKeys, err = stream.ReadObjectFromReader(reader, iotago.BlockIssuerKeysFromReader); err != nil { return nil, ierrors.Wrap(err, "unable to read block issuer keys") } - if a.ValidatorStake, err = stream.Read[iotago.BaseToken](reader); err != nil { + if a.validatorStake, err = stream.Read[iotago.BaseToken](reader); err != nil { return nil, ierrors.Wrap(err, "unable to read validator stake") } - if a.DelegationStake, err = stream.Read[iotago.BaseToken](reader); err != nil { + if a.delegationStake, err = stream.Read[iotago.BaseToken](reader); err != nil { return nil, ierrors.Wrap(err, "unable to read delegation stake") } - if a.FixedCost, err = stream.Read[iotago.Mana](reader); err != nil { + if a.fixedCost, err = stream.Read[iotago.Mana](reader); err != nil { return nil, ierrors.Wrap(err, "unable to read fixed cost") } - if a.StakeEndEpoch, err = stream.Read[iotago.EpochIndex](reader); err != nil { + if a.stakeEndEpoch, err = stream.Read[iotago.EpochIndex](reader); err != nil { return nil, ierrors.Wrap(err, "unable to read stake end epoch") } - if a.LatestSupportedProtocolVersionAndHash, err = stream.ReadObject(reader, model.VersionAndHashSize, model.VersionAndHashFromBytes); err != nil { + if a.latestSupportedProtocolVersionAndHash, err = stream.ReadObject(reader, model.VersionAndHashSize, model.VersionAndHashFromBytes); err != nil { return nil, ierrors.Wrap(err, "unable to read latest supported protocol version and hash") } @@ -129,38 +281,41 @@ func AccountDataFromBytes(b []byte) (*AccountData, int, error) { } func (a *AccountData) Bytes() ([]byte, error) { + a.mutex.RLock() + defer a.mutex.RUnlock() + byteBuffer := stream.NewByteBuffer() - if err := stream.Write(byteBuffer, a.ID); err != nil { + if err := stream.Write(byteBuffer, a.id); err != nil { return nil, ierrors.Wrap(err, "failed to write AccountID") } - if err := stream.WriteObject(byteBuffer, a.Credits, (*BlockIssuanceCredits).Bytes); err != nil { + if err := stream.WriteObject(byteBuffer, a.credits, (*BlockIssuanceCredits).Bytes); err != nil { return nil, ierrors.Wrap(err, "failed to write Credits") } - if err := stream.Write(byteBuffer, a.ExpirySlot); err != nil { + if err := stream.Write(byteBuffer, a.expirySlot); err != nil { return nil, ierrors.Wrap(err, "failed to write ExpirySlot") } - if err := stream.Write(byteBuffer, a.OutputID); err != nil { + if err := stream.Write(byteBuffer, a.outputID); err != nil { return nil, ierrors.Wrap(err, "failed to write OutputID") } - if err := stream.WriteObject(byteBuffer, a.BlockIssuerKeys, iotago.BlockIssuerKeys.Bytes); err != nil { + if err := stream.WriteObject(byteBuffer, a.blockIssuerKeys, iotago.BlockIssuerKeys.Bytes); err != nil { return nil, ierrors.Wrap(err, "failed to write BlockIssuerKeys") } - if err := stream.Write(byteBuffer, a.ValidatorStake); err != nil { + if err := stream.Write(byteBuffer, a.validatorStake); err != nil { return nil, ierrors.Wrap(err, "failed to write ValidatorStake") } - if err := stream.Write(byteBuffer, a.DelegationStake); err != nil { + if err := stream.Write(byteBuffer, a.delegationStake); err != nil { return nil, ierrors.Wrap(err, "failed to write DelegationStake") } - if err := stream.Write(byteBuffer, a.FixedCost); err != nil { + if err := stream.Write(byteBuffer, a.fixedCost); err != nil { return nil, ierrors.Wrap(err, "failed to write FixedCost") } - if err := stream.Write(byteBuffer, a.StakeEndEpoch); err != nil { + if err := stream.Write(byteBuffer, a.stakeEndEpoch); err != nil { return nil, ierrors.Wrap(err, "failed to write StakeEndEpoch") } - if err := stream.WriteObject(byteBuffer, a.LatestSupportedProtocolVersionAndHash, model.VersionAndHash.Bytes); err != nil { + if err := stream.WriteObject(byteBuffer, a.latestSupportedProtocolVersionAndHash, model.VersionAndHash.Bytes); err != nil { return nil, ierrors.Wrap(err, "failed to write LatestSupportedProtocolVersionAndHash") } @@ -168,72 +323,73 @@ func (a *AccountData) Bytes() ([]byte, error) { } func (a *AccountData) String() string { + a.mutex.RLock() + defer a.mutex.RUnlock() + return stringify.Struct("AccountData", - stringify.NewStructField("ID", a.ID), - stringify.NewStructField("Credits", a.Credits), - stringify.NewStructField("ExpirySlot", uint32(a.ExpirySlot)), - stringify.NewStructField("OutputID", a.OutputID), - stringify.NewStructField("BlockIssuerKeys", func() string { return strconv.Itoa(a.BlockIssuerKeys.Size()) }()), - stringify.NewStructField("ValidatorStake", uint64(a.ValidatorStake)), - stringify.NewStructField("DelegationStake", uint64(a.DelegationStake)), - stringify.NewStructField("FixedCost", uint64(a.FixedCost)), - stringify.NewStructField("StakeEndEpoch", uint64(a.StakeEndEpoch)), - stringify.NewStructField("LatestSupportedProtocolVersionAndHash", a.LatestSupportedProtocolVersionAndHash), + stringify.NewStructField("ID", a.id), + stringify.NewStructField("Credits", a.credits), + stringify.NewStructField("ExpirySlot", uint32(a.expirySlot)), + stringify.NewStructField("OutputID", a.outputID), + stringify.NewStructField("BlockIssuerKeys", func() string { return strconv.Itoa(len(a.blockIssuerKeys)) }()), + stringify.NewStructField("ValidatorStake", uint64(a.validatorStake)), + stringify.NewStructField("DelegationStake", uint64(a.delegationStake)), + stringify.NewStructField("FixedCost", uint64(a.fixedCost)), + stringify.NewStructField("StakeEndEpoch", uint64(a.stakeEndEpoch)), + stringify.NewStructField("LatestSupportedProtocolVersionAndHash", a.latestSupportedProtocolVersionAndHash), ) } func WithCredits(credits *BlockIssuanceCredits) options.Option[AccountData] { return func(a *AccountData) { - a.Credits = credits + a.SetCredits(credits) } } func WithExpirySlot(expirySlot iotago.SlotIndex) options.Option[AccountData] { return func(a *AccountData) { - a.ExpirySlot = expirySlot + a.SetExpirySlot(expirySlot) } } func WithOutputID(outputID iotago.OutputID) options.Option[AccountData] { return func(a *AccountData) { - a.OutputID = outputID + a.SetOutputID(outputID) } } func WithBlockIssuerKeys(blockIssuerKeys ...iotago.BlockIssuerKey) options.Option[AccountData] { return func(a *AccountData) { - for _, blockIssuerKey := range blockIssuerKeys { - a.BlockIssuerKeys.Add(blockIssuerKey) - } + a.AddBlockIssuerKeys(blockIssuerKeys...) } } func WithValidatorStake(validatorStake iotago.BaseToken) options.Option[AccountData] { return func(a *AccountData) { - a.ValidatorStake = validatorStake + a.SetValidatorStake(validatorStake) } } func WithDelegationStake(delegationStake iotago.BaseToken) options.Option[AccountData] { return func(a *AccountData) { - a.DelegationStake = delegationStake + a.SetDelegationStake(delegationStake) } } func WithFixedCost(fixedCost iotago.Mana) options.Option[AccountData] { return func(a *AccountData) { - a.FixedCost = fixedCost + a.SetFixedCost(fixedCost) } } func WithStakeEndEpoch(stakeEndEpoch iotago.EpochIndex) options.Option[AccountData] { return func(a *AccountData) { - a.StakeEndEpoch = stakeEndEpoch + a.SetStakeEndEpoch(stakeEndEpoch) } } -func WithLatestSupportedProtocolVersion(versionAndHash model.VersionAndHash) options.Option[AccountData] { +func WithLatestSupportedProtocolVersionAndHash(versionAndHash model.VersionAndHash) options.Option[AccountData] { return func(a *AccountData) { - a.LatestSupportedProtocolVersionAndHash = versionAndHash + a.SetLatestSupportedProtocolVersionAndHash(versionAndHash) } } diff --git a/pkg/protocol/engine/accounts/accountsledger/manager.go b/pkg/protocol/engine/accounts/accountsledger/manager.go index 57f7c3512..29ec1c10f 100644 --- a/pkg/protocol/engine/accounts/accountsledger/manager.go +++ b/pkg/protocol/engine/accounts/accountsledger/manager.go @@ -39,8 +39,7 @@ type Manager struct { // at the latest committed slot, it is updated on the slot commitment. accountsTree ads.Map[iotago.Identifier, iotago.AccountID, *accounts.AccountData] - // TODO: add in memory shrink version of the slot diffs - // slot diffs for the Account between [LatestCommittedSlot - MCA, LatestCommittedSlot]. + // slot diffs for the Account. slotDiff func(iotago.SlotIndex) (*slotstore.AccountDiffs, error) // block is a function that returns a block from the cache or from the database. @@ -228,15 +227,16 @@ func (m *Manager) account(accountID iotago.AccountID, targetSlot iotago.SlotInde } if !exists { - loadedAccount = accounts.NewAccountData(accountID, accounts.WithCredits(accounts.NewBlockIssuanceCredits(0, targetSlot))) + loadedAccount = accounts.NewAccountData(accountID) } - _, wasDestroyed, err := m.rollbackAccountTo(loadedAccount, targetSlot) + wasDestroyed, err := m.rollbackAccountTo(loadedAccount, targetSlot) if err != nil { return nil, false, err } - // account not present in the accountsTree, and it was not marked as destroyed in slots between targetSlot and latestCommittedSlot + // the account is not present in the accountsTree, + // and it was not marked as destroyed in slots between targetSlot and latestCommittedSlot if !exists && !wasDestroyed { return nil, false, nil } @@ -261,7 +261,7 @@ func (m *Manager) PastAccounts(accountIDs iotago.AccountIDs, targetSlot iotago.S if !exists { loadedAccount = accounts.NewAccountData(accountID, accounts.WithCredits(accounts.NewBlockIssuanceCredits(0, targetSlot))) } - _, wasDestroyed, err := m.rollbackAccountTo(loadedAccount, targetSlot) + wasDestroyed, err := m.rollbackAccountTo(loadedAccount, targetSlot) if err != nil { continue } @@ -276,20 +276,20 @@ func (m *Manager) PastAccounts(accountIDs iotago.AccountIDs, targetSlot iotago.S return result, nil } - func (m *Manager) Rollback(targetSlot iotago.SlotIndex) error { - processedAccounts := ds.NewSet[iotago.AccountID]() - for slot := m.latestCommittedSlot; slot > targetSlot; slot-- { + m.mutex.RLock() + defer m.mutex.RUnlock() + + return m.rollbackFromTo(m.latestCommittedSlot, targetSlot, false) +} + +func (m *Manager) rollbackFromTo(fromSlot iotago.SlotIndex, toSlot iotago.SlotIndex, deleteRevertedDiffs bool) error { + for slot := fromSlot; slot > toSlot; slot-- { slotDiff := lo.PanicOnErr(m.slotDiff(slot)) var internalErr error //nolint:revive if err := slotDiff.Stream(func(accountID iotago.AccountID, accountDiff *model.AccountDiff, destroyed bool) bool { - // We rollback each account directly to targetSlot, therefore, we should rollback each account only once. - if processedAccounts.Has(accountID) { - return true - } - accountData, exists, err := m.accountsTree.Get(accountID) if err != nil { internalErr = ierrors.Wrapf(err, "unable to retrieve account %s to rollback in slot %d", accountID, slot) @@ -298,22 +298,38 @@ func (m *Manager) Rollback(targetSlot iotago.SlotIndex) error { } if !exists { + // The Account was not found in the tree, so we need to re-create it accountData = accounts.NewAccountData(accountID) } - if _, _, err := m.rollbackAccountTo(accountData, targetSlot); err != nil { - internalErr = ierrors.Wrapf(err, "unable to rollback account %s to target slot %d", accountID, targetSlot) + wasCreatedAfterTargetSlot, wasDestroyed, err := m.rollbackSlotDiffOnAccount(accountData, slotDiff) + if err != nil { + internalErr = ierrors.Wrapf(err, "unable to rollback account %s to target slot %d", accountID, toSlot) return false } - if err := m.accountsTree.Set(accountID, accountData); err != nil { - internalErr = ierrors.Wrapf(err, "failed to save rolled back account %s to target slot %d", accountID, targetSlot) + if !exists && !wasDestroyed || exists && wasDestroyed { + internalErr = ierrors.Errorf("incorrect account state %s at slot %d (exists: %t wasDestroyed: %t)", accountID, slot, exists, wasDestroyed) return false } - processedAccounts.Add(accountID) + if wasCreatedAfterTargetSlot && exists { + if _, err := m.accountsTree.Delete(accountID); err != nil { + internalErr = ierrors.Wrapf(err, "failed to delete account %s from slot %d", accountID, slot) + + return false + } + + return true + } + + if err := m.accountsTree.Set(accountID, accountData); err != nil { + internalErr = ierrors.Wrapf(err, "failed to save rolled back account %s to target slot %d", accountID, toSlot) + + return false + } return true }); err != nil { @@ -323,6 +339,12 @@ func (m *Manager) Rollback(targetSlot iotago.SlotIndex) error { if internalErr != nil { return ierrors.Wrapf(internalErr, "error in rolling back account for slot %s", slot) } + + if deleteRevertedDiffs { + if err := slotDiff.Clear(); err != nil { + return ierrors.Wrapf(err, "error while deleting reverted diff for slot %s", slot) + } + } } if err := m.accountsTree.Commit(); err != nil { @@ -383,82 +405,99 @@ func (m *Manager) Reset() { m.latestSupportedVersionSignals.Clear() } -func (m *Manager) rollbackAccountTo(accountData *accounts.AccountData, targetSlot iotago.SlotIndex) (wasCreatedAfterTargetSlot bool, wasDestroyed bool, err error) { +func (m *Manager) rollbackAccountTo(accountData *accounts.AccountData, targetSlot iotago.SlotIndex) (wasDestroyed bool, err error) { // to reach targetSlot, we need to rollback diffs from the current latestCommittedSlot down to targetSlot + 1 for diffSlot := m.latestCommittedSlot; diffSlot > targetSlot; diffSlot-- { diffStore, err := m.slotDiff(diffSlot) if err != nil { - return false, false, ierrors.Errorf("can't retrieve account, could not find diff store for slot %d", diffSlot) + return false, ierrors.Errorf("can't retrieve account, could not find diff store for slot %d", diffSlot) } - found, err := diffStore.Has(accountData.ID) + createdInTheDiff, destroyed, err := m.rollbackSlotDiffOnAccount(accountData, diffStore) if err != nil { - return false, false, ierrors.Wrapf(err, "can't retrieve account, could not check if diff store for slot %d has account %s", diffSlot, accountData.ID) + return false, ierrors.Wrapf(err, "can't retrieve account, could not rollback diff for account %s in slot %d", accountData.ID(), diffStore.Slot()) + } else if createdInTheDiff { + // If the account was created in the diffSlot, then don't need to iterate previous slots as the account diff is definitely not there. + return false, nil } - // no changes for this account in this slot - if !found { - continue - } + // collected to see if an account was destroyed between slotIndex and b.latestCommittedSlot index. + wasDestroyed = wasDestroyed || destroyed + } - diffChange, destroyed, err := diffStore.Load(accountData.ID) - if err != nil { - return false, false, ierrors.Wrapf(err, "can't retrieve account, could not load diff for account %s in slot %d", accountData.ID, diffSlot) - } + return wasDestroyed, nil +} - // update the account data with the diff - accountData.Credits.Update(-diffChange.BICChange, diffChange.PreviousUpdatedSlot) - // update the expiry slot of the account if it was changed - if diffChange.PreviousExpirySlot != diffChange.NewExpirySlot { - accountData.ExpirySlot = diffChange.PreviousExpirySlot - } +func (m *Manager) rollbackSlotDiffOnAccount(accountData *accounts.AccountData, diffStore *slotstore.AccountDiffs) (wasCreatedInTheDiff bool, wasDestroyed bool, err error) { + found, err := diffStore.Has(accountData.ID()) + if err != nil { + return false, false, ierrors.Wrapf(err, "can't retrieve account, could not check if diff store for slot %d has account %s", diffStore.Slot(), accountData.ID()) + } - if diffChange.PreviousOutputID == iotago.EmptyOutputID && diffChange.NewOutputID != iotago.EmptyOutputID { - // Account was created in this slot, so we need to remove it - m.LogDebug("Account was created in this slot, so we need to remove it", "accountID", accountData.ID, "slot", diffSlot, "diffChange.PreviousOutputID", diffChange.PreviousOutputID, "diffChange.NewOutputID", diffChange.NewOutputID) - return true, false, nil - } + // no changes for this account in this slot + if !found { + return false, false, nil + } - // update the output ID of the account if it was changed - if diffChange.PreviousOutputID != iotago.EmptyOutputID { - accountData.OutputID = diffChange.PreviousOutputID - } + diffChange, destroyed, err := diffStore.Load(accountData.ID()) + if err != nil { + return false, false, ierrors.Wrapf(err, "can't retrieve account, could not load diff for account %s in slot %d", accountData.ID(), diffStore.Slot()) + } - accountData.AddBlockIssuerKeys(diffChange.BlockIssuerKeysRemoved...) - accountData.RemoveBlockIssuerKey(diffChange.BlockIssuerKeysAdded...) + m.LogDebug("Rolling back account", "accountID", accountData.ID(), "diffSlot", diffStore.Slot(), "accountData", accountData, "diffChange", diffChange, "destroyed", destroyed) - validatorStake, err := safemath.SafeSub(int64(accountData.ValidatorStake), diffChange.ValidatorStakeChange) - if err != nil { - return false, false, ierrors.Wrapf(err, "can't retrieve account, validator stake underflow for account %s in slot %d: %d - %d", accountData.ID, diffSlot, accountData.ValidatorStake, diffChange.ValidatorStakeChange) - } - accountData.ValidatorStake = iotago.BaseToken(validatorStake) + // update the account data with the diff + if diffChange.BICChange != 0 || destroyed { + accountData.Credits().Update(-diffChange.BICChange, diffChange.PreviousUpdatedSlot) + } - delegationStake, err := safemath.SafeSub(int64(accountData.DelegationStake), diffChange.DelegationStakeChange) - if err != nil { - return false, false, ierrors.Wrapf(err, "can't retrieve account, delegation stake underflow for account %s in slot %d: %d - %d", accountData.ID, diffSlot, accountData.DelegationStake, diffChange.DelegationStakeChange) - } - accountData.DelegationStake = iotago.BaseToken(delegationStake) + // update the expiry slot of the account if it was changed + if diffChange.PreviousExpirySlot != diffChange.NewExpirySlot { + accountData.SetExpirySlot(diffChange.PreviousExpirySlot) + } - stakeEpochEnd, err := safemath.SafeSub(int64(accountData.StakeEndEpoch), diffChange.StakeEndEpochChange) - if err != nil { - return false, false, ierrors.Wrapf(err, "can't retrieve account, stake end epoch underflow for account %s in slot %d: %d - %d", accountData.ID, diffSlot, accountData.StakeEndEpoch, diffChange.StakeEndEpochChange) - } - accountData.StakeEndEpoch = iotago.EpochIndex(stakeEpochEnd) + if diffChange.PreviousOutputID == iotago.EmptyOutputID && diffChange.NewOutputID != iotago.EmptyOutputID { + // Account was created in this slot, so we need to remove it + m.LogDebug("Account was created in this slot, so we need to remove it", "accountID", accountData.ID(), "slot", diffStore.Slot(), "diffChange.PreviousOutputID", diffChange.PreviousOutputID, "diffChange.NewOutputID", diffChange.NewOutputID) + return true, false, nil + } - fixedCost, err := safemath.SafeSub(int64(accountData.FixedCost), diffChange.FixedCostChange) - if err != nil { - return false, false, ierrors.Wrapf(err, "can't retrieve account, fixed cost underflow for account %s in slot %d: %d - %d", accountData.ID, diffSlot, accountData.FixedCost, diffChange.FixedCostChange) - } - accountData.FixedCost = iotago.Mana(fixedCost) - if diffChange.PrevLatestSupportedVersionAndHash != diffChange.NewLatestSupportedVersionAndHash { - accountData.LatestSupportedProtocolVersionAndHash = diffChange.PrevLatestSupportedVersionAndHash - } + // update the output ID of the account if it was changed + if diffChange.PreviousOutputID != iotago.EmptyOutputID { + accountData.SetOutputID(diffChange.PreviousOutputID) + } - // collected to see if an account was destroyed between slotIndex and b.latestCommittedSlot index. - wasDestroyed = wasDestroyed || destroyed + accountData.AddBlockIssuerKeys(diffChange.BlockIssuerKeysRemoved...) + accountData.RemoveBlockIssuerKeys(diffChange.BlockIssuerKeysAdded...) + + validatorStake, err := safemath.SafeSub(int64(accountData.ValidatorStake()), diffChange.ValidatorStakeChange) + if err != nil { + return false, false, ierrors.Wrapf(err, "can't retrieve account, validator stake underflow for account %s in slot %d: %d - %d", accountData.ID(), diffStore.Slot(), accountData.ValidatorStake(), diffChange.ValidatorStakeChange) + } + accountData.SetValidatorStake(iotago.BaseToken(validatorStake)) + + delegationStake, err := safemath.SafeSub(int64(accountData.DelegationStake()), diffChange.DelegationStakeChange) + if err != nil { + return false, false, ierrors.Wrapf(err, "can't retrieve account, delegation stake underflow for account %s in slot %d: %d - %d", accountData.ID(), diffStore.Slot(), accountData.DelegationStake(), diffChange.DelegationStakeChange) + } + accountData.SetDelegationStake(iotago.BaseToken(delegationStake)) + + stakeEpochEnd, err := safemath.SafeSub(int64(accountData.StakeEndEpoch()), diffChange.StakeEndEpochChange) + if err != nil { + return false, false, ierrors.Wrapf(err, "can't retrieve account, stake end epoch underflow for account %s in slot %d: %d - %d", accountData.ID(), diffStore.Slot(), accountData.StakeEndEpoch(), diffChange.StakeEndEpochChange) + } + accountData.SetStakeEndEpoch(iotago.EpochIndex(stakeEpochEnd)) + + fixedCost, err := safemath.SafeSub(int64(accountData.FixedCost()), diffChange.FixedCostChange) + if err != nil { + return false, false, ierrors.Wrapf(err, "can't retrieve account, fixed cost underflow for account %s in slot %d: %d - %d", accountData.ID(), diffStore.Slot(), accountData.FixedCost(), diffChange.FixedCostChange) + } + accountData.SetFixedCost(iotago.Mana(fixedCost)) + if diffChange.PrevLatestSupportedVersionAndHash != diffChange.NewLatestSupportedVersionAndHash { + accountData.SetLatestSupportedProtocolVersionAndHash(diffChange.PrevLatestSupportedVersionAndHash) } - return false, wasDestroyed, nil + return false, destroyed, nil } func (m *Manager) preserveDestroyedAccountData(accountID iotago.AccountID) (accountDiff *model.AccountDiff, err error) { @@ -475,20 +514,24 @@ func (m *Manager) preserveDestroyedAccountData(accountID iotago.AccountID) (acco // it does not matter if there are any changes in this slot, as the account was destroyed anyway and the data was lost // we store the accountState in the form of a diff, so we can roll back to the previous state slotDiff := model.NewAccountDiff() - slotDiff.BICChange = -accountData.Credits.Value + + slotDiff.BICChange = -accountData.Credits().Value() + slotDiff.PreviousUpdatedSlot = accountData.Credits().UpdateSlot() + slotDiff.NewExpirySlot = iotago.SlotIndex(0) - slotDiff.PreviousExpirySlot = accountData.ExpirySlot + slotDiff.PreviousExpirySlot = accountData.ExpirySlot() + slotDiff.NewOutputID = iotago.EmptyOutputID - slotDiff.PreviousOutputID = accountData.OutputID - slotDiff.PreviousUpdatedSlot = accountData.Credits.UpdateSlot - slotDiff.BlockIssuerKeysRemoved = accountData.BlockIssuerKeys.Clone() - - slotDiff.ValidatorStakeChange = -int64(accountData.ValidatorStake) - slotDiff.DelegationStakeChange = -int64(accountData.DelegationStake) - slotDiff.StakeEndEpochChange = -int64(accountData.StakeEndEpoch) - slotDiff.FixedCostChange = -int64(accountData.FixedCost) + slotDiff.PreviousOutputID = accountData.OutputID() + + slotDiff.BlockIssuerKeysRemoved = accountData.BlockIssuerKeys().Clone() + + slotDiff.ValidatorStakeChange = -int64(accountData.ValidatorStake()) + slotDiff.DelegationStakeChange = -int64(accountData.DelegationStake()) + slotDiff.StakeEndEpochChange = -int64(accountData.StakeEndEpoch()) + slotDiff.FixedCostChange = -int64(accountData.FixedCost()) slotDiff.NewLatestSupportedVersionAndHash = model.VersionAndHash{} - slotDiff.PrevLatestSupportedVersionAndHash = accountData.LatestSupportedProtocolVersionAndHash + slotDiff.PrevLatestSupportedVersionAndHash = accountData.LatestSupportedProtocolVersionAndHash() return slotDiff, err } @@ -519,7 +562,7 @@ func (m *Manager) computeBlockBurnsForSlot(slot iotago.SlotIndex, rmc iotago.Man return nil, ierrors.Wrapf(err, "cannot compute penalty for over-issuing validator, account %s could not be retrieved", accountID) } punishmentEpochs := apiForSlot.ProtocolParameters().PunishmentEpochs() - manaPunishment, err := apiForSlot.ManaDecayProvider().GenerateManaAndDecayBySlots(accountData.ValidatorStake, slot, slot+apiForSlot.TimeProvider().EpochDurationSlots()*iotago.SlotIndex(punishmentEpochs)) + manaPunishment, err := apiForSlot.ManaDecayProvider().GenerateManaAndDecayBySlots(accountData.ValidatorStake(), slot, slot+apiForSlot.TimeProvider().EpochDurationSlots()*iotago.SlotIndex(punishmentEpochs)) if err != nil { return nil, ierrors.Wrapf(err, "cannot compute penalty for over-issuing validator with account ID %s due to problem with mana generation", accountID) } @@ -554,58 +597,60 @@ func (m *Manager) commitAccountTree(slot iotago.SlotIndex, accountDiffChanges ma if diffChange.BICChange != 0 || !exists { // decay the credits to the current slot if the account exists - if exists && accountData.Credits.Value > 0 { - decayedPreviousCredits, err := m.apiProvider.APIForSlot(slot).ManaDecayProvider().DecayManaBySlots(iotago.Mana(accountData.Credits.Value), accountData.Credits.UpdateSlot, slot) + if exists && accountData.Credits().Value() > 0 { + decayedPreviousCredits, err := m.apiProvider.APIForSlot(slot).ManaDecayProvider().DecayManaBySlots(iotago.Mana(accountData.Credits().Value()), accountData.Credits().UpdateSlot(), slot) if err != nil { - return ierrors.Wrapf(err, "can't retrieve account, could not decay credits for account %s in slot %d", accountData.ID, slot) + return ierrors.Wrapf(err, "can't retrieve account, could not decay credits for account %s in slot %d", accountData.ID(), slot) } // update the account data diff taking into account the decay, the modified diff will be stored in the calling // ApplyDiff function to be able to properly rollback the account to a previous slot. - diffChange.BICChange -= accountData.Credits.Value - iotago.BlockIssuanceCredits(decayedPreviousCredits) + diffChange.BICChange -= accountData.Credits().Value() - iotago.BlockIssuanceCredits(decayedPreviousCredits) } - accountData.Credits.Update(diffChange.BICChange, slot) + if diffChange.BICChange != 0 || !exists { + accountData.Credits().Update(diffChange.BICChange, slot) + } } // update the expiry slot of the account if it changed if diffChange.PreviousExpirySlot != diffChange.NewExpirySlot { - accountData.ExpirySlot = diffChange.NewExpirySlot + accountData.SetExpirySlot(diffChange.NewExpirySlot) } // update the outputID only if the account got actually transitioned, not if it was only an allotment target if diffChange.NewOutputID != iotago.EmptyOutputID { - accountData.OutputID = diffChange.NewOutputID + accountData.SetOutputID(diffChange.NewOutputID) } accountData.AddBlockIssuerKeys(diffChange.BlockIssuerKeysAdded...) - accountData.RemoveBlockIssuerKey(diffChange.BlockIssuerKeysRemoved...) + accountData.RemoveBlockIssuerKeys(diffChange.BlockIssuerKeysRemoved...) - validatorStake, err := safemath.SafeAdd(int64(accountData.ValidatorStake), diffChange.ValidatorStakeChange) + validatorStake, err := safemath.SafeAdd(int64(accountData.ValidatorStake()), diffChange.ValidatorStakeChange) if err != nil { - return ierrors.Wrapf(err, "can't retrieve account, validator stake overflow for account %s in slot %d: %d + %d", accountData.ID, slot, accountData.ValidatorStake, diffChange.ValidatorStakeChange) + return ierrors.Wrapf(err, "can't retrieve account, validator stake overflow for account %s in slot %d: %d + %d", accountData.ID(), slot, accountData.ValidatorStake(), diffChange.ValidatorStakeChange) } - accountData.ValidatorStake = iotago.BaseToken(validatorStake) + accountData.SetValidatorStake(iotago.BaseToken(validatorStake)) - delegationStake, err := safemath.SafeAdd(int64(accountData.DelegationStake), diffChange.DelegationStakeChange) + delegationStake, err := safemath.SafeAdd(int64(accountData.DelegationStake()), diffChange.DelegationStakeChange) if err != nil { - return ierrors.Wrapf(err, "can't retrieve account, delegation stake overflow for account %s in slot %d: %d + %d", accountData.ID, slot, accountData.DelegationStake, diffChange.DelegationStakeChange) + return ierrors.Wrapf(err, "can't retrieve account, delegation stake overflow for account %s in slot %d: %d + %d", accountData.ID(), slot, accountData.DelegationStake(), diffChange.DelegationStakeChange) } - accountData.DelegationStake = iotago.BaseToken(delegationStake) + accountData.SetDelegationStake(iotago.BaseToken(delegationStake)) - stakeEndEpoch, err := safemath.SafeAdd(int64(accountData.StakeEndEpoch), diffChange.StakeEndEpochChange) + stakeEndEpoch, err := safemath.SafeAdd(int64(accountData.StakeEndEpoch()), diffChange.StakeEndEpochChange) if err != nil { - return ierrors.Wrapf(err, "can't retrieve account, stake end epoch overflow for account %s in slot %d: %d + %d", accountData.ID, slot, accountData.StakeEndEpoch, diffChange.StakeEndEpochChange) + return ierrors.Wrapf(err, "can't retrieve account, stake end epoch overflow for account %s in slot %d: %d + %d", accountData.ID(), slot, accountData.StakeEndEpoch(), diffChange.StakeEndEpochChange) } - accountData.StakeEndEpoch = iotago.EpochIndex(stakeEndEpoch) + accountData.SetStakeEndEpoch(iotago.EpochIndex(stakeEndEpoch)) - fixedCost, err := safemath.SafeAdd(int64(accountData.FixedCost), diffChange.FixedCostChange) + fixedCost, err := safemath.SafeAdd(int64(accountData.FixedCost()), diffChange.FixedCostChange) if err != nil { - return ierrors.Wrapf(err, "can't retrieve account, validator fixed cost overflow for account %s in slot %d: %d + %d", accountData.ID, slot, accountData.FixedCost, diffChange.FixedCostChange) + return ierrors.Wrapf(err, "can't retrieve account, validator fixed cost overflow for account %s in slot %d: %d + %d", accountData.ID(), slot, accountData.FixedCost(), diffChange.FixedCostChange) } - accountData.FixedCost = iotago.Mana(fixedCost) + accountData.SetFixedCost(iotago.Mana(fixedCost)) - if diffChange.PrevLatestSupportedVersionAndHash != diffChange.NewLatestSupportedVersionAndHash && accountData.LatestSupportedProtocolVersionAndHash.Version < diffChange.NewLatestSupportedVersionAndHash.Version { - accountData.LatestSupportedProtocolVersionAndHash = diffChange.NewLatestSupportedVersionAndHash + if diffChange.PrevLatestSupportedVersionAndHash != diffChange.NewLatestSupportedVersionAndHash && accountData.LatestSupportedProtocolVersionAndHash().Version < diffChange.NewLatestSupportedVersionAndHash.Version { + accountData.SetLatestSupportedProtocolVersionAndHash(diffChange.NewLatestSupportedVersionAndHash) } if err := m.accountsTree.Set(accountID, accountData); err != nil { @@ -642,11 +687,11 @@ func (m *Manager) updateSlotDiffWithBurns(slot iotago.SlotIndex, accountDiffs ma if !exists { panic(ierrors.Errorf("trying to burn Mana from account %s which is not present in slot %d", id, slot-1)) } - accountDiff.PreviousUpdatedSlot = accountData.Credits.UpdateSlot - accountDiff.NewExpirySlot = accountData.ExpirySlot - accountDiff.PreviousExpirySlot = accountData.ExpirySlot - accountDiff.NewOutputID = accountData.OutputID - accountDiff.PreviousOutputID = accountData.OutputID + accountDiff.PreviousUpdatedSlot = accountData.Credits().UpdateSlot() + accountDiff.NewExpirySlot = accountData.ExpirySlot() + accountDiff.PreviousExpirySlot = accountData.ExpirySlot() + accountDiff.NewOutputID = accountData.OutputID() + accountDiff.PreviousOutputID = accountData.OutputID() } accountDiff.BICChange -= iotago.BlockIssuanceCredits(burn) @@ -680,10 +725,10 @@ func (m *Manager) updateSlotDiffWithVersionSignals(slot iotago.SlotIndex, accoun Version: signaledBlock.HighestSupportedVersion, Hash: signaledBlock.ProtocolParametersHash, } - if accountData.LatestSupportedProtocolVersionAndHash != newVersionAndHash && - accountData.LatestSupportedProtocolVersionAndHash.Version < newVersionAndHash.Version { + if accountData.LatestSupportedProtocolVersionAndHash() != newVersionAndHash && + accountData.LatestSupportedProtocolVersionAndHash().Version < newVersionAndHash.Version { accountDiff.NewLatestSupportedVersionAndHash = newVersionAndHash - accountDiff.PrevLatestSupportedVersionAndHash = accountData.LatestSupportedProtocolVersionAndHash + accountDiff.PrevLatestSupportedVersionAndHash = accountData.LatestSupportedProtocolVersionAndHash() accountDiffs[id] = accountDiff } } diff --git a/pkg/protocol/engine/accounts/accountsledger/snapshot.go b/pkg/protocol/engine/accounts/accountsledger/snapshot.go index 345f46e42..ada6b81ba 100644 --- a/pkg/protocol/engine/accounts/accountsledger/snapshot.go +++ b/pkg/protocol/engine/accounts/accountsledger/snapshot.go @@ -4,7 +4,6 @@ import ( "io" "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/serializer/v2" "github.com/iotaledger/hive.go/serializer/v2/stream" "github.com/iotaledger/iota-core/pkg/model" @@ -16,15 +15,25 @@ func (m *Manager) Import(reader io.ReadSeeker) error { m.mutex.Lock() defer m.mutex.Unlock() - // populate the account tree, account tree should be empty at this point + targetSlot, err := stream.Read[iotago.SlotIndex](reader) + if err != nil { + return ierrors.Wrap(err, "unable to read target slot") + } + + accountsTreeSlot, err := stream.Read[iotago.SlotIndex](reader) + if err != nil { + return ierrors.Wrap(err, "unable to read accounts tree slot") + } + + // populate the account tree, the account tree should be empty at this point if err := stream.ReadCollection(reader, serializer.SeriLengthPrefixTypeAsUint64, func(i int) error { accountData, err := stream.ReadObjectFromReader(reader, accounts.AccountDataFromReader) if err != nil { return ierrors.Wrapf(err, "unable to read account data at index %d", i) } - if err := m.accountsTree.Set(accountData.ID, accountData); err != nil { - return ierrors.Wrapf(err, "unable to set account %s", accountData.ID) + if err := m.accountsTree.Set(accountData.ID(), accountData); err != nil { + return ierrors.Wrapf(err, "unable to set account %s", accountData.ID()) } m.LogDebug("Imported account", "accountData", accountData) @@ -38,120 +47,70 @@ func (m *Manager) Import(reader io.ReadSeeker) error { return ierrors.Wrap(err, "unable to import slot diffs") } - if err := m.accountsTree.Commit(); err != nil { - return ierrors.Wrap(err, "unable to commit account tree") + if err := m.rollbackFromTo(accountsTreeSlot, targetSlot, true); err != nil { + return ierrors.Wrapf(err, "unable to rollback to slot %d", targetSlot) } return nil } -func (m *Manager) Export(writer io.WriteSeeker, targetIndex iotago.SlotIndex) error { +func (m *Manager) Export(writer io.WriteSeeker, targetSlot iotago.SlotIndex) error { m.mutex.Lock() defer m.mutex.Unlock() + m.LogDebug("Exporting AccountsLedger", "latestCommittedSlot", m.latestCommittedSlot, "targetIndex", targetSlot) + + if err := stream.Write[iotago.SlotIndex](writer, targetSlot); err != nil { + return ierrors.Wrap(err, "unable to write latest committed slot") + } + + if err := stream.Write[iotago.SlotIndex](writer, m.latestCommittedSlot); err != nil { + return ierrors.Wrap(err, "unable to write latest committed slot") + } + if err := stream.WriteCollection(writer, serializer.SeriLengthPrefixTypeAsUint64, func() (int, error) { - elements, err := m.exportAccountTree(writer, targetIndex) + elements, err := m.exportAccountTree(writer) if err != nil { return 0, ierrors.Wrap(err, "can't write account tree") } return elements, nil }); err != nil { - return ierrors.Wrapf(err, "unable to export accounts for slot %d", targetIndex) + return ierrors.Wrapf(err, "unable to export accounts for slot %d", targetSlot) } if err := stream.WriteCollection(writer, serializer.SeriLengthPrefixTypeAsUint64, func() (elementsCount int, err error) { - elementsCount, err = m.writeSlotDiffs(writer, targetIndex) + elementsCount, err = m.writeSlotDiffs(writer, targetSlot) if err != nil { return 0, ierrors.Wrap(err, "can't write slot diffs") } return elementsCount, nil }); err != nil { - return ierrors.Wrapf(err, "unable to export slot diffs for slot %d", targetIndex) + return ierrors.Wrapf(err, "unable to export slot diffs for slot %d", targetSlot) } return nil } -// exportAccountTree exports the AccountTree at a certain target slot, returning the total amount of exported accounts. -func (m *Manager) exportAccountTree(writer io.WriteSeeker, targetIndex iotago.SlotIndex) (int, error) { +// exportAccountTree exports the current AccountTree. +func (m *Manager) exportAccountTree(writer io.WriteSeeker) (int, error) { var accountCount int if err := m.accountsTree.Stream(func(id iotago.AccountID, account *accounts.AccountData) error { - wasCreatedAfterTargetSlot, _, err := m.rollbackAccountTo(account, targetIndex) - if err != nil { - return ierrors.Wrapf(err, "unable to rollback account %s", id) - } - - // Account was created after the target slot, so we don't need to export it. - if wasCreatedAfterTargetSlot { - m.LogTrace("account was created after target slot", "id", id, "targetSlot", targetIndex) - - return nil - } + m.LogTrace("exportAccountTree", "accountID", id, "account", account) - if err = stream.WriteObject(writer, account, (*accounts.AccountData).Bytes); err != nil { + if err := stream.WriteObject(writer, account, (*accounts.AccountData).Bytes); err != nil { return ierrors.Wrapf(err, "unable to write account %s", id) } accountCount++ - m.LogTrace("exported account", "id", id, "account", account) - return nil }); err != nil { return 0, ierrors.Wrap(err, "error in streaming account tree") } - // we might have entries that were destroyed, that are present in diffs but not in the tree from the latestCommittedIndex we streamed above - recreatedAccountsCount, err := m.recreateDestroyedAccounts(writer, targetIndex) - - return accountCount + recreatedAccountsCount, err -} - -func (m *Manager) recreateDestroyedAccounts(writer io.WriteSeeker, targetSlot iotago.SlotIndex) (int, error) { - var recreatedAccountsCount int - destroyedAccounts := make(map[iotago.AccountID]*accounts.AccountData) - - for slot := m.latestCommittedSlot; slot > targetSlot; slot-- { - // it should be impossible that `m.slotDiff(slot)` returns an error, because it is impossible to export a pruned slot - err := lo.PanicOnErr(m.slotDiff(slot)).StreamDestroyed(func(accountID iotago.AccountID) bool { - // actual data will be filled in by rollbackAccountTo - accountData := accounts.NewAccountData(accountID) - - destroyedAccounts[accountID] = accountData - - return true - }) - if err != nil { - return 0, err - } - } - - for accountID, accountData := range destroyedAccounts { - m.LogDebug("Exporting recreated destroyed account", "accountID", accountID, "outputID", accountData.OutputID, "credits.value", accountData.Credits.Value, "credits.updateSlot", accountData.Credits.UpdateSlot) - - if wasCreatedAfterTargetSlot, wasDestroyed, err := m.rollbackAccountTo(accountData, targetSlot); err != nil { - return 0, ierrors.Wrapf(err, "unable to rollback account %s to target slot %d", accountID, targetSlot) - } else if wasCreatedAfterTargetSlot { - // Account was created after the target slot, so we don't need to export it. - m.LogDebug("Exporting recreated destroyed account was created after target slot", "accountID", accountID, "targetSlot", targetSlot) - - continue - } else if !wasDestroyed { - return 0, ierrors.Errorf("account %s was not destroyed", accountID) - } - - m.LogDebug("Exporting recreated destroyed account after rollback", "accountID", accountID, "outputID", accountData.OutputID, "credits.value", accountData.Credits.Value, "credits.updateSlot", accountData.Credits.UpdateSlot) - - if err := stream.WriteObject(writer, accountData, (*accounts.AccountData).Bytes); err != nil { - return 0, ierrors.Wrapf(err, "unable to write account %s", accountID) - } - - recreatedAccountsCount++ - } - - return recreatedAccountsCount, nil + return accountCount, nil } func (m *Manager) readSlotDiffs(reader io.ReadSeeker) error { @@ -179,13 +138,9 @@ func (m *Manager) readSlotDiffs(reader io.ReadSeeker) error { return ierrors.Wrapf(err, "unable to read destroyed flag for accountID %s", accountID) } - var accountDiff *model.AccountDiff - if !destroyed { - if accountDiff, err = stream.ReadObjectFromReader(reader, model.AccountDiffFromReader); err != nil { - return ierrors.Wrapf(err, "unable to read account diff for accountID %s", accountID) - } - } else { - accountDiff = model.NewAccountDiff() + accountDiff, err := stream.ReadObjectFromReader(reader, model.AccountDiffFromReader) + if err != nil { + return ierrors.Wrapf(err, "unable to read account diff for accountID %s", accountID) } m.LogDebug("Imported account diff", "slot", slot, "accountID", accountID, "destroyed", destroyed, "accountDiff", accountDiff) @@ -210,14 +165,13 @@ func (m *Manager) readSlotDiffs(reader io.ReadSeeker) error { func (m *Manager) writeSlotDiffs(writer io.WriteSeeker, targetSlot iotago.SlotIndex) (int, error) { var slotDiffsCount int - // write slot diffs until being able to reach targetSlot, where the exported tree is at - slot := iotago.SlotIndex(1) + lowestSlot := iotago.SlotIndex(1) maxCommittableAge := m.apiProvider.APIForSlot(targetSlot).ProtocolParameters().MaxCommittableAge() if targetSlot > maxCommittableAge { - slot = targetSlot - maxCommittableAge + lowestSlot = targetSlot - maxCommittableAge } - for ; slot <= targetSlot; slot++ { + for slot := m.latestCommittedSlot; slot >= lowestSlot; slot-- { var accountsInDiffCount int if err := stream.Write(writer, slot); err != nil { @@ -243,12 +197,9 @@ func (m *Manager) writeSlotDiffs(writer io.WriteSeeker, targetSlot iotago.SlotIn innerErr = ierrors.Wrapf(err, "unable to write destroyed flag for account %s", accountID) return false } - - if !destroyed { - if err = stream.WriteObject(writer, accountDiff, (*model.AccountDiff).Bytes); err != nil { - innerErr = ierrors.Wrapf(err, "unable to write account diff for account %s", accountID) - return false - } + if err = stream.WriteObject(writer, accountDiff, (*model.AccountDiff).Bytes); err != nil { + innerErr = ierrors.Wrapf(err, "unable to write account diff for account %s", accountID) + return false } m.LogDebug("Exported account diff", "slot", slot, "accountID", accountID, "destroyed", destroyed, "accountDiff", accountDiff) diff --git a/pkg/protocol/engine/accounts/accountsledger/snapshot_test.go b/pkg/protocol/engine/accounts/accountsledger/snapshot_test.go index 944c26c1c..2da28f412 100644 --- a/pkg/protocol/engine/accounts/accountsledger/snapshot_test.go +++ b/pkg/protocol/engine/accounts/accountsledger/snapshot_test.go @@ -16,7 +16,7 @@ func TestManager_Import_Export(t *testing.T) { latestSupportedVersionHash1 := tpkg.Rand32ByteArray() latestSupportedVersionHash2 := tpkg.Rand32ByteArray() - accountTreeRoots := []iotago.Identifier{} + var accountTreeRoots []iotago.Identifier accountTreeRoots = append(accountTreeRoots, ts.Instance.AccountsTreeRoot()) diff --git a/pkg/protocol/engine/accounts/accountsledger/testsuite_test.go b/pkg/protocol/engine/accounts/accountsledger/testsuite_test.go index 38d18b173..f4631d2ac 100644 --- a/pkg/protocol/engine/accounts/accountsledger/testsuite_test.go +++ b/pkg/protocol/engine/accounts/accountsledger/testsuite_test.go @@ -12,6 +12,7 @@ import ( "github.com/iotaledger/hive.go/kvstore" "github.com/iotaledger/hive.go/kvstore/mapdb" "github.com/iotaledger/hive.go/lo" + "github.com/iotaledger/hive.go/log" "github.com/iotaledger/hive.go/runtime/module" "github.com/iotaledger/iota-core/pkg/model" "github.com/iotaledger/iota-core/pkg/protocol/engine/accounts" @@ -80,7 +81,9 @@ func (t *TestSuite) initAccountLedger() *accountsledger.Manager { return storage.Get(id) } - manager := accountsledger.New(module.NewTestModule(t.T), t.apiProvider, blockFunc, slotDiffFunc, mapdb.NewMapDB()) + testModule := module.NewTestModule(t.T) + testModule.SetLogLevel(log.LevelTrace) + manager := accountsledger.New(testModule, t.apiProvider, blockFunc, slotDiffFunc, mapdb.NewMapDB()) return manager } @@ -238,16 +241,17 @@ func (t *TestSuite) assertAccountState(slot iotago.SlotIndex, accountID iotago.A require.True(t.T, exists) - require.Equal(t.T, accountID, actualState.ID) - require.Equal(t.T, expectedCredits, actualState.Credits, "slot: %d, accountID %s: expected: %v, actual: %v", slot, accountID, expectedCredits, actualState.Credits) - require.True(t.T, expectedBlockIssuerKeys.Equal(actualState.BlockIssuerKeys), "slot: %d, accountID %s: expected: %s, actual: %s", slot, accountID, expectedBlockIssuerKeys, actualState.BlockIssuerKeys) - - require.Equal(t.T, t.OutputID(expectedState.OutputID, false), actualState.OutputID) - require.Equal(t.T, expectedState.StakeEndEpoch, actualState.StakeEndEpoch, "slot: %d, accountID %s: expected StakeEndEpoch: %d, actual: %d", slot, accountID, expectedState.StakeEndEpoch, actualState.StakeEndEpoch) - require.Equal(t.T, expectedState.ValidatorStake, actualState.ValidatorStake, "slot: %d, accountID %s: expected ValidatorStake: %d, actual: %d", slot, accountID, expectedState.ValidatorStake, actualState.ValidatorStake) - require.Equal(t.T, expectedState.FixedCost, actualState.FixedCost, "slot: %d, accountID %s: expected FixedCost: %d, actual: %d", slot, accountID, expectedState.FixedCost, actualState.FixedCost) - require.Equal(t.T, expectedState.DelegationStake, actualState.DelegationStake, "slot: %d, accountID %s: expected DelegationStake: %d, actual: %d", slot, accountID, expectedState.DelegationStake, actualState.DelegationStake) - require.Equal(t.T, expectedState.LatestSupportedProtocolVersionAndHash, actualState.LatestSupportedProtocolVersionAndHash, "slot: %d, accountID %s: expected LatestSupportedProtocolVersionAndHash: %d, actual: %d", slot, accountID, expectedState.LatestSupportedProtocolVersionAndHash, actualState.LatestSupportedProtocolVersionAndHash) + require.Equal(t.T, accountID, actualState.ID()) + require.Equal(t.T, expectedCredits.Value(), actualState.Credits().Value(), "slot: %d, accountID %s: expected: %v, actual: %v", slot, accountID, expectedCredits, actualState.Credits().Value()) + require.Equal(t.T, expectedCredits.UpdateSlot(), actualState.Credits().UpdateSlot(), "slot: %d, accountID %s: expected: %v, actual: %v", slot, accountID, expectedCredits, actualState.Credits().UpdateSlot()) + require.True(t.T, expectedBlockIssuerKeys.Equal(actualState.BlockIssuerKeys()), "slot: %d, accountID %s: expected: %s, actual: %s", slot, accountID, expectedBlockIssuerKeys, actualState.BlockIssuerKeys()) + + require.Equal(t.T, t.OutputID(expectedState.OutputID, false), actualState.OutputID()) + require.Equal(t.T, expectedState.StakeEndEpoch, actualState.StakeEndEpoch(), "slot: %d, accountID %s: expected StakeEndEpoch: %d, actual: %d", slot, accountID, expectedState.StakeEndEpoch, actualState.StakeEndEpoch()) + require.Equal(t.T, expectedState.ValidatorStake, actualState.ValidatorStake(), "slot: %d, accountID %s: expected ValidatorStake: %d, actual: %d", slot, accountID, expectedState.ValidatorStake, actualState.ValidatorStake()) + require.Equal(t.T, expectedState.FixedCost, actualState.FixedCost(), "slot: %d, accountID %s: expected FixedCost: %d, actual: %d", slot, accountID, expectedState.FixedCost, actualState.FixedCost()) + require.Equal(t.T, expectedState.DelegationStake, actualState.DelegationStake(), "slot: %d, accountID %s: expected DelegationStake: %d, actual: %d", slot, accountID, expectedState.DelegationStake, actualState.DelegationStake()) + require.Equal(t.T, expectedState.LatestSupportedProtocolVersionAndHash, actualState.LatestSupportedProtocolVersionAndHash(), "slot: %d, accountID %s: expected LatestSupportedProtocolVersionAndHash: %d, actual: %d", slot, accountID, expectedState.LatestSupportedProtocolVersionAndHash, actualState.LatestSupportedProtocolVersionAndHash()) } diff --git a/pkg/protocol/engine/accounts/credits.go b/pkg/protocol/engine/accounts/credits.go index 90c6cd11b..08b1edf59 100644 --- a/pkg/protocol/engine/accounts/credits.go +++ b/pkg/protocol/engine/accounts/credits.go @@ -2,6 +2,7 @@ package accounts import ( "github.com/iotaledger/hive.go/ierrors" + "github.com/iotaledger/hive.go/runtime/syncutils" "github.com/iotaledger/hive.go/serializer/v2" "github.com/iotaledger/hive.go/serializer/v2/stream" "github.com/iotaledger/hive.go/stringify" @@ -12,34 +13,63 @@ const BlockIssuanceCreditsBytesLength = serializer.Int64ByteSize + iotago.SlotIn // BlockIssuanceCredits is a weight annotated with the slot it was last updated in. type BlockIssuanceCredits struct { - Value iotago.BlockIssuanceCredits - UpdateSlot iotago.SlotIndex + mutex syncutils.RWMutex + + value iotago.BlockIssuanceCredits + updateSlot iotago.SlotIndex } // NewBlockIssuanceCredits creates a new Credits instance. func NewBlockIssuanceCredits(value iotago.BlockIssuanceCredits, updateTime iotago.SlotIndex) (newCredits *BlockIssuanceCredits) { return &BlockIssuanceCredits{ - Value: value, - UpdateSlot: updateTime, + value: value, + updateSlot: updateTime, } } +func (c *BlockIssuanceCredits) Clone() *BlockIssuanceCredits { + c.mutex.RLock() + defer c.mutex.RUnlock() + + return NewBlockIssuanceCredits(c.value, c.updateSlot) +} + +func (c *BlockIssuanceCredits) Value() iotago.BlockIssuanceCredits { + c.mutex.RLock() + defer c.mutex.RUnlock() + + return c.value +} + +func (c *BlockIssuanceCredits) UpdateSlot() iotago.SlotIndex { + c.mutex.RLock() + defer c.mutex.RUnlock() + + return c.updateSlot +} + func (c *BlockIssuanceCredits) String() string { + c.mutex.RLock() + defer c.mutex.RUnlock() + return stringify.Struct("BlockIssuanceCredits", - stringify.NewStructField("Value", int64(c.Value)), - stringify.NewStructField("UpdateSlot", uint32(c.UpdateSlot)), + stringify.NewStructField("Value", int64(c.value)), + stringify.NewStructField("UpdateSlot", uint32(c.updateSlot)), ) } // Bytes returns a serialized version of the Credits. func (c *BlockIssuanceCredits) Bytes() ([]byte, error) { + c.mutex.RLock() + defer c.mutex.RUnlock() + byteBuffer := stream.NewByteBuffer() - if err := stream.Write(byteBuffer, c.Value); err != nil { + if err := stream.Write(byteBuffer, c.value); err != nil { return nil, ierrors.Wrap(err, "failed to write value") } - if err := stream.Write(byteBuffer, c.UpdateSlot); err != nil { + if err := stream.Write(byteBuffer, c.updateSlot); err != nil { return nil, ierrors.Wrap(err, "failed to write updateTime") } @@ -48,15 +78,17 @@ func (c *BlockIssuanceCredits) Bytes() ([]byte, error) { func BlockIssuanceCreditsFromBytes(bytes []byte) (*BlockIssuanceCredits, int, error) { c := new(BlockIssuanceCredits) + c.mutex.Lock() + defer c.mutex.Unlock() var err error byteReader := stream.NewByteReader(bytes) - if c.Value, err = stream.Read[iotago.BlockIssuanceCredits](byteReader); err != nil { + if c.value, err = stream.Read[iotago.BlockIssuanceCredits](byteReader); err != nil { return nil, 0, ierrors.Wrap(err, "failed to read value") } - if c.UpdateSlot, err = stream.Read[iotago.SlotIndex](byteReader); err != nil { + if c.updateSlot, err = stream.Read[iotago.SlotIndex](byteReader); err != nil { return nil, 0, ierrors.Wrap(err, "failed to read updateTime") } @@ -65,8 +97,11 @@ func BlockIssuanceCreditsFromBytes(bytes []byte) (*BlockIssuanceCredits, int, er // Update updates the Credits increasing Value and updateTime. func (c *BlockIssuanceCredits) Update(change iotago.BlockIssuanceCredits, updateSlot ...iotago.SlotIndex) { - c.Value += change + c.mutex.Lock() + defer c.mutex.Unlock() + + c.value += change if len(updateSlot) > 0 { - c.UpdateSlot = updateSlot[0] + c.updateSlot = updateSlot[0] } } diff --git a/pkg/protocol/engine/accounts/mana/manager.go b/pkg/protocol/engine/accounts/mana/manager.go index 7e9a26ec7..131875d0b 100644 --- a/pkg/protocol/engine/accounts/mana/manager.go +++ b/pkg/protocol/engine/accounts/mana/manager.go @@ -222,9 +222,9 @@ func (m *Manager) getBIC(accountID iotago.AccountID, slot iotago.SlotIndex) (bic if !exists { return 0, 0, ierrors.Errorf("account data for %s in slot %s does not exist", accountID, slot) } - if accountBIC.Credits.Value <= 0 { + if accountBIC.Credits().Value() <= 0 { return 0, 0, nil } - return iotago.Mana(accountBIC.Credits.Value), accountBIC.Credits.UpdateSlot, nil + return iotago.Mana(accountBIC.Credits().Value()), accountBIC.Credits().UpdateSlot(), nil } diff --git a/pkg/protocol/engine/accounts/mana/manager_test.go b/pkg/protocol/engine/accounts/mana/manager_test.go index 16511364e..60edd1bc3 100644 --- a/pkg/protocol/engine/accounts/mana/manager_test.go +++ b/pkg/protocol/engine/accounts/mana/manager_test.go @@ -67,36 +67,29 @@ func TestManager_GetManaOnAccountOverflow(t *testing.T) { accountRetriever := func(id iotago.AccountID, index iotago.SlotIndex) (*accounts.AccountData, bool, error) { switch id { case accountIDRecentBIC: - return &accounts.AccountData{ - ID: id, - Credits: &accounts.BlockIssuanceCredits{ - Value: iotago.MaxBlockIssuanceCredits/2 + iotago.MaxBlockIssuanceCredits/4, - UpdateSlot: 1, - }, - ExpirySlot: iotago.MaxSlotIndex, - OutputID: iotago.OutputID{}, - BlockIssuerKeys: nil, - ValidatorStake: 0, - DelegationStake: 0, - FixedCost: 0, - StakeEndEpoch: 0, - LatestSupportedProtocolVersionAndHash: model.VersionAndHash{}, - }, true, nil + return accounts.NewAccountData(id, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2+iotago.MaxBlockIssuanceCredits/4, 1)), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithOutputID(iotago.OutputID{}), + accounts.WithBlockIssuerKeys(nil), + accounts.WithValidatorStake(0), + accounts.WithDelegationStake(0), + accounts.WithFixedCost(0), + accounts.WithStakeEndEpoch(0), + accounts.WithLatestSupportedProtocolVersionAndHash(model.VersionAndHash{}), + ), true, nil default: - return &accounts.AccountData{ - ID: id, - Credits: &accounts.BlockIssuanceCredits{ - Value: iotago.MaxBlockIssuanceCredits/2 + iotago.MaxBlockIssuanceCredits/4, - }, - ExpirySlot: iotago.MaxSlotIndex, - OutputID: iotago.OutputID{}, - BlockIssuerKeys: nil, - ValidatorStake: 0, - DelegationStake: 0, - FixedCost: 0, - StakeEndEpoch: 0, - LatestSupportedProtocolVersionAndHash: model.VersionAndHash{}, - }, true, nil + return accounts.NewAccountData(id, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2+iotago.MaxBlockIssuanceCredits/4, 0)), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithOutputID(iotago.OutputID{}), + accounts.WithBlockIssuerKeys(nil), + accounts.WithValidatorStake(0), + accounts.WithDelegationStake(0), + accounts.WithFixedCost(0), + accounts.WithStakeEndEpoch(0), + accounts.WithLatestSupportedProtocolVersionAndHash(model.VersionAndHash{}), + ), true, nil } } diff --git a/pkg/protocol/engine/filter/postsolidfilter/postsolidblockfilter/post_solid_block_filter.go b/pkg/protocol/engine/filter/postsolidfilter/postsolidblockfilter/post_solid_block_filter.go index dcfd17d8f..86be22380 100644 --- a/pkg/protocol/engine/filter/postsolidfilter/postsolidblockfilter/post_solid_block_filter.go +++ b/pkg/protocol/engine/filter/postsolidfilter/postsolidblockfilter/post_solid_block_filter.go @@ -146,7 +146,7 @@ func (c *PostSolidBlockFilter) ProcessSolidBlock(block *blocks.Block) { // Check that the issuer of this block has non-negative block issuance credit { - if accountData.Credits.Value < 0 { + if accountData.Credits().Value() < 0 { c.filterBlock( block, ierrors.WithMessagef(iotago.ErrAccountLocked, "block issuer account %s", block.IssuerID()), @@ -158,10 +158,10 @@ func (c *PostSolidBlockFilter) ProcessSolidBlock(block *blocks.Block) { // Check that the account is not expired { - if accountData.ExpirySlot < block.ProtocolBlock().Header.SlotCommitmentID.Slot() { + if accountData.ExpirySlot() < block.ProtocolBlock().Header.SlotCommitmentID.Slot() { c.filterBlock( block, - ierrors.WithMessagef(iotago.ErrAccountExpired, "block issuer account %s is expired, expiry slot %d in commitment %d", block.IssuerID(), accountData.ExpirySlot, block.ProtocolBlock().Header.SlotCommitmentID.Slot()), + ierrors.WithMessagef(iotago.ErrAccountExpired, "block issuer account %s is expired, expiry slot %d in commitment %d", block.IssuerID(), accountData.ExpirySlot(), block.ProtocolBlock().Header.SlotCommitmentID.Slot()), ) return @@ -174,7 +174,7 @@ func (c *PostSolidBlockFilter) ProcessSolidBlock(block *blocks.Block) { case *iotago.Ed25519Signature: expectedBlockIssuerKey := iotago.Ed25519PublicKeyHashBlockIssuerKeyFromPublicKey(signature.PublicKey) - if !accountData.BlockIssuerKeys.Has(expectedBlockIssuerKey) { + if !accountData.BlockIssuerKeys().Has(expectedBlockIssuerKey) { c.filterBlock( block, ierrors.WithMessagef(iotago.ErrInvalidSignature, "block issuer account %s does not have block issuer key corresponding to public key %s in slot %d", block.IssuerID(), hexutil.EncodeHex(signature.PublicKey[:]), block.ProtocolBlock().Header.SlotCommitmentID.Index()), diff --git a/pkg/protocol/engine/ledger/ledger/ledger.go b/pkg/protocol/engine/ledger/ledger/ledger.go index 98a81a6ca..e0b177089 100644 --- a/pkg/protocol/engine/ledger/ledger/ledger.go +++ b/pkg/protocol/engine/ledger/ledger/ledger.go @@ -425,7 +425,7 @@ func (l *Ledger) prepareAccountDiffs(accountDiffs map[iotago.AccountID]*model.Ac accountDiff.NewOutputID = createdOutput.OutputID() accountDiff.NewExpirySlot = createdOutput.Output().FeatureSet().BlockIssuer().ExpirySlot - oldPubKeysSet := accountData.BlockIssuerKeys + oldPubKeysSet := accountData.BlockIssuerKeys() newPubKeysSet := iotago.NewBlockIssuerKeys() for _, blockIssuerKey := range createdOutput.Output().FeatureSet().BlockIssuer().BlockIssuerKeys { newPubKeysSet.Add(blockIssuerKey) @@ -449,14 +449,14 @@ func (l *Ledger) prepareAccountDiffs(accountDiffs map[iotago.AccountID]*model.Ac if stakingFeature := createdOutput.Output().FeatureSet().Staking(); stakingFeature != nil { // staking feature is created or updated - create the diff between the account data and new account - accountDiff.ValidatorStakeChange = int64(stakingFeature.StakedAmount) - int64(accountData.ValidatorStake) - accountDiff.StakeEndEpochChange = int64(stakingFeature.EndEpoch) - int64(accountData.StakeEndEpoch) - accountDiff.FixedCostChange = int64(stakingFeature.FixedCost) - int64(accountData.FixedCost) + accountDiff.ValidatorStakeChange = int64(stakingFeature.StakedAmount) - int64(accountData.ValidatorStake()) + accountDiff.StakeEndEpochChange = int64(stakingFeature.EndEpoch) - int64(accountData.StakeEndEpoch()) + accountDiff.FixedCostChange = int64(stakingFeature.FixedCost) - int64(accountData.FixedCost()) } else if consumedOutput.Output().FeatureSet().Staking() != nil { // staking feature was removed from an account - accountDiff.ValidatorStakeChange = -int64(accountData.ValidatorStake) - accountDiff.StakeEndEpochChange = -int64(accountData.StakeEndEpoch) - accountDiff.FixedCostChange = -int64(accountData.FixedCost) + accountDiff.ValidatorStakeChange = -int64(accountData.ValidatorStake()) + accountDiff.StakeEndEpochChange = -int64(accountData.StakeEndEpoch()) + accountDiff.FixedCostChange = -int64(accountData.FixedCost()) } } @@ -675,15 +675,15 @@ func (l *Ledger) processStateDiffTransactions(stateDiff mempool.StateDiff) (spen } accountDiff.BICChange += iotago.BlockIssuanceCredits(allotment.Mana) - accountDiff.PreviousUpdatedSlot = accountData.Credits.UpdateSlot + accountDiff.PreviousUpdatedSlot = accountData.Credits().UpdateSlot() // we are not transitioning the allotted account, so the new and previous expiry slots are the same - accountDiff.PreviousExpirySlot = accountData.ExpirySlot - accountDiff.NewExpirySlot = accountData.ExpirySlot + accountDiff.PreviousExpirySlot = accountData.ExpirySlot() + accountDiff.NewExpirySlot = accountData.ExpirySlot() // we are not transitioning the allotted account, so the new and previous outputIDs are the same - accountDiff.NewOutputID = accountData.OutputID - accountDiff.PreviousOutputID = accountData.OutputID + accountDiff.NewOutputID = accountData.OutputID() + accountDiff.PreviousOutputID = accountData.OutputID() } } @@ -705,17 +705,17 @@ func (l *Ledger) resolveAccountOutput(accountID iotago.AccountID, slot iotago.Sl l.utxoLedger.ReadLockLedger() defer l.utxoLedger.ReadUnlockLedger() - isUnspent, err := l.utxoLedger.IsOutputIDUnspentWithoutLocking(accountMetadata.OutputID) + isUnspent, err := l.utxoLedger.IsOutputIDUnspentWithoutLocking(accountMetadata.OutputID()) if err != nil { - return nil, ierrors.Wrapf(err, "error while checking whether account with output id %s is unspent", accountMetadata.OutputID.ToHex()) + return nil, ierrors.Wrapf(err, "error while checking whether account with output id %s is unspent", accountMetadata.OutputID().ToHex()) } if !isUnspent { - return nil, ierrors.WithMessagef(mempool.ErrStateNotFound, "unspent account with output id %s not found", accountMetadata.OutputID.ToHex()) + return nil, ierrors.WithMessagef(mempool.ErrStateNotFound, "unspent account with output id %s not found", accountMetadata.OutputID().ToHex()) } - accountOutput, err := l.utxoLedger.ReadOutputByOutputIDWithoutLocking(accountMetadata.OutputID) + accountOutput, err := l.utxoLedger.ReadOutputByOutputIDWithoutLocking(accountMetadata.OutputID()) if err != nil { - return nil, ierrors.Wrapf(err, "error while retrieving account with output id %s", accountMetadata.OutputID.ToHex()) + return nil, ierrors.Wrapf(err, "error while retrieving account with output id %s", accountMetadata.OutputID().ToHex()) } return accountOutput, nil diff --git a/pkg/protocol/engine/ledger/ledger/vm.go b/pkg/protocol/engine/ledger/ledger/vm.go index 97d4db578..8f5ea4c1a 100644 --- a/pkg/protocol/engine/ledger/ledger/vm.go +++ b/pkg/protocol/engine/ledger/ledger/vm.go @@ -103,7 +103,7 @@ func (v *VM) ValidateSignatures(signedTransaction mempool.SignedTransaction, res return nil, ierrors.WithMessagef(iotago.ErrBICInputReferenceInvalid, "BIC input does not exist for account %s in slot %d", inp.AccountID, commitmentInput.Slot) } - bicInputSet[inp.AccountID] = accountData.Credits.Value + bicInputSet[inp.AccountID] = accountData.Credits().Value() } rewardInputSet := make(iotagovm.RewardsInputSet) diff --git a/pkg/protocol/sybilprotection/seatmanager/mock/mockseatmanager.go b/pkg/protocol/sybilprotection/seatmanager/mock/mockseatmanager.go index 99f50dd79..0389a4fe6 100644 --- a/pkg/protocol/sybilprotection/seatmanager/mock/mockseatmanager.go +++ b/pkg/protocol/sybilprotection/seatmanager/mock/mockseatmanager.go @@ -181,12 +181,12 @@ func (m *ManualPOA) RotateCommittee(epoch iotago.EpochIndex, validators accounts m.accounts = account.NewAccounts() for _, validatorData := range validators { - if err := m.accounts.Set(validatorData.ID, &account.Pool{ - PoolStake: validatorData.ValidatorStake + validatorData.DelegationStake, - ValidatorStake: validatorData.ValidatorStake, - FixedCost: validatorData.FixedCost, + if err := m.accounts.Set(validatorData.ID(), &account.Pool{ + PoolStake: validatorData.ValidatorStake() + validatorData.DelegationStake(), + ValidatorStake: validatorData.ValidatorStake(), + FixedCost: validatorData.FixedCost(), }); err != nil { - return nil, ierrors.Wrapf(err, "error while setting pool for epoch %d for validator %s", epoch, validatorData.ID.String()) + return nil, ierrors.Wrapf(err, "error while setting pool for epoch %d for validator %s", epoch, validatorData.ID().String()) } } m.committee = m.accounts.SeatedAccounts() diff --git a/pkg/protocol/sybilprotection/seatmanager/poa/poa.go b/pkg/protocol/sybilprotection/seatmanager/poa/poa.go index 73cfd40f3..e6f6d1f18 100644 --- a/pkg/protocol/sybilprotection/seatmanager/poa/poa.go +++ b/pkg/protocol/sybilprotection/seatmanager/poa/poa.go @@ -100,12 +100,12 @@ func (s *SeatManager) RotateCommittee(epoch iotago.EpochIndex, validators accoun committeeAccounts := account.NewAccounts() for _, validatorData := range validators { - if err := committeeAccounts.Set(validatorData.ID, &account.Pool{ - PoolStake: validatorData.ValidatorStake + validatorData.DelegationStake, - ValidatorStake: validatorData.ValidatorStake, - FixedCost: validatorData.FixedCost, + if err := committeeAccounts.Set(validatorData.ID(), &account.Pool{ + PoolStake: validatorData.ValidatorStake() + validatorData.DelegationStake(), + ValidatorStake: validatorData.ValidatorStake(), + FixedCost: validatorData.FixedCost(), }); err != nil { - return nil, ierrors.Wrapf(err, "error while setting committee for epoch %d for validator %s", epoch, validatorData.ID.String()) + return nil, ierrors.Wrapf(err, "error while setting committee for epoch %d for validator %s", epoch, validatorData.ID().String()) } } diff --git a/pkg/protocol/sybilprotection/seatmanager/topstakers/topstakers.go b/pkg/protocol/sybilprotection/seatmanager/topstakers/topstakers.go index 4f7538122..715e085a6 100644 --- a/pkg/protocol/sybilprotection/seatmanager/topstakers/topstakers.go +++ b/pkg/protocol/sybilprotection/seatmanager/topstakers/topstakers.go @@ -251,27 +251,30 @@ func (s *SeatManager) ReuseCommittee(currentEpoch iotago.EpochIndex, targetEpoch func (s *SeatManager) selectNewCommitteeAccounts(epoch iotago.EpochIndex, candidates accounts.AccountsData) (*account.Accounts, error) { sort.Slice(candidates, func(i int, j int) bool { // Prioritize the candidate that has a larger pool stake. - if candidates[i].ValidatorStake+candidates[i].DelegationStake != candidates[j].ValidatorStake+candidates[j].DelegationStake { - return candidates[i].ValidatorStake+candidates[i].DelegationStake > candidates[j].ValidatorStake+candidates[j].DelegationStake + if candidates[i].ValidatorStake()+candidates[i].DelegationStake() != candidates[j].ValidatorStake()+candidates[j].DelegationStake() { + return candidates[i].ValidatorStake()+candidates[i].DelegationStake() > candidates[j].ValidatorStake()+candidates[j].DelegationStake() } // Prioritize the candidate that has a larger validator stake. - if candidates[i].ValidatorStake != candidates[j].ValidatorStake { - return candidates[i].ValidatorStake > candidates[j].ValidatorStake + if candidates[i].ValidatorStake() != candidates[j].ValidatorStake() { + return candidates[i].ValidatorStake() > candidates[j].ValidatorStake() } // Prioritize the candidate that declares a longer staking period. - if candidates[i].StakeEndEpoch != candidates[j].StakeEndEpoch { - return candidates[i].StakeEndEpoch > candidates[j].StakeEndEpoch + if candidates[i].StakeEndEpoch() != candidates[j].StakeEndEpoch() { + return candidates[i].StakeEndEpoch() > candidates[j].StakeEndEpoch() } // Prioritize the candidate that has smaller FixedCost. - if candidates[i].FixedCost != candidates[j].FixedCost { - return candidates[i].FixedCost < candidates[j].FixedCost + if candidates[i].FixedCost() != candidates[j].FixedCost() { + return candidates[i].FixedCost() < candidates[j].FixedCost() } // two candidates never have the same account ID because they come in a map - return bytes.Compare(candidates[i].ID[:], candidates[j].ID[:]) > 0 + candidatesI := candidates[i].ID() + candidatesJ := candidates[j].ID() + + return bytes.Compare(candidatesI[:], candidatesJ[:]) > 0 }) // We try to select up to targetCommitteeSize candidates to be part of the committee. If there are fewer candidates @@ -282,12 +285,12 @@ func (s *SeatManager) selectNewCommitteeAccounts(epoch iotago.EpochIndex, candid newCommitteeAccounts := account.NewAccounts() for _, candidateData := range candidates[:committeeSize] { - if err := newCommitteeAccounts.Set(candidateData.ID, &account.Pool{ - PoolStake: candidateData.ValidatorStake + candidateData.DelegationStake, - ValidatorStake: candidateData.ValidatorStake, - FixedCost: candidateData.FixedCost, + if err := newCommitteeAccounts.Set(candidateData.ID(), &account.Pool{ + PoolStake: candidateData.ValidatorStake() + candidateData.DelegationStake(), + ValidatorStake: candidateData.ValidatorStake(), + FixedCost: candidateData.FixedCost(), }); err != nil { - return nil, ierrors.Wrapf(err, "error while setting pool for committee candidate %s", candidateData.ID.String()) + return nil, ierrors.Wrapf(err, "error while setting pool for committee candidate %s", candidateData.ID().String()) } } diff --git a/pkg/protocol/sybilprotection/seatmanager/topstakers/topstakers_test.go b/pkg/protocol/sybilprotection/seatmanager/topstakers/topstakers_test.go index 81ae8094a..2028854a3 100644 --- a/pkg/protocol/sybilprotection/seatmanager/topstakers/topstakers_test.go +++ b/pkg/protocol/sybilprotection/seatmanager/topstakers/topstakers_test.go @@ -167,23 +167,25 @@ func TestTopStakers_RotateCommittee(t *testing.T) { { candidate0ID := tpkg.RandAccountID() candidate0ID.RegisterAlias("candidate0") - accountsData = append(accountsData, &accounts.AccountData{ - ID: candidate0ID, - ValidatorStake: 100, - DelegationStake: 800 - 399, - FixedCost: 3, - StakeEndEpoch: iotago.MaxEpochIndex, - }) + accountsData = append(accountsData, + accounts.NewAccountData(candidate0ID, + accounts.WithValidatorStake(100), + accounts.WithDelegationStake(800-399), + accounts.WithFixedCost(3), + accounts.WithStakeEndEpoch(iotago.MaxEpochIndex), + ), + ) candidate1ID := tpkg.RandAccountID() candidate1ID.RegisterAlias("candidate1") - accountsData = append(accountsData, &accounts.AccountData{ - ID: candidate1ID, - ValidatorStake: 100, - DelegationStake: 800 - 399, - FixedCost: 3, - StakeEndEpoch: iotago.MaxEpochIndex, - }) + accountsData = append(accountsData, + accounts.NewAccountData(candidate1ID, + accounts.WithValidatorStake(100), + accounts.WithDelegationStake(800-399), + accounts.WithFixedCost(3), + accounts.WithStakeEndEpoch(iotago.MaxEpochIndex), + ), + ) } for i := 2; i <= numCandidates; i++ { @@ -194,13 +196,14 @@ func TestTopStakers_RotateCommittee(t *testing.T) { ValidatorStake: iotago.BaseToken(i * 50), FixedCost: iotago.Mana(i), } - accountsData = append(accountsData, &accounts.AccountData{ - ID: candidateAccountID, - ValidatorStake: iotago.BaseToken(i * 50), - DelegationStake: iotago.BaseToken(i*100) - iotago.BaseToken(i*50), - FixedCost: tpkg.RandMana(iotago.MaxMana), - StakeEndEpoch: tpkg.RandEpoch(), - }) + accountsData = append(accountsData, + accounts.NewAccountData(candidateAccountID, + accounts.WithValidatorStake(iotago.BaseToken(i*50)), + accounts.WithDelegationStake(iotago.BaseToken(i*100)-iotago.BaseToken(i*50)), + accounts.WithFixedCost(tpkg.RandMana(iotago.MaxMana)), + accounts.WithStakeEndEpoch(tpkg.RandEpoch()), + ), + ) if i+int(expectedCommitteeSize) > numCandidates { require.NoError(t, expectedCommitteeInEpoch1.Set(candidateAccountID, candidatePool)) @@ -240,13 +243,14 @@ func TestTopStakers_RotateCommittee(t *testing.T) { candidate0ID := tpkg.RandAccountID() candidate0ID.RegisterAlias("candidate0-epoch2") - accountsData = append(accountsData, &accounts.AccountData{ - ID: candidate0ID, - ValidatorStake: 100, - DelegationStake: 800 - 399, - FixedCost: 3, - StakeEndEpoch: iotago.MaxEpochIndex, - }) + accountsData = append(accountsData, + accounts.NewAccountData(candidate0ID, + accounts.WithValidatorStake(100), + accounts.WithDelegationStake(800-399), + accounts.WithFixedCost(3), + accounts.WithStakeEndEpoch(iotago.MaxEpochIndex), + ), + ) require.NoError(t, expectedCommitteeInEpoch2.Set(candidate0ID, &account.Pool{PoolStake: 1900, ValidatorStake: 900, FixedCost: 11})) // Rotate the committee and make sure that the returned committee matches the expected. diff --git a/pkg/protocol/sybilprotection/sybilprotectionv1/sybilprotection.go b/pkg/protocol/sybilprotection/sybilprotectionv1/sybilprotection.go index fe766d8fd..64ac7fb08 100644 --- a/pkg/protocol/sybilprotection/sybilprotectionv1/sybilprotection.go +++ b/pkg/protocol/sybilprotection/sybilprotectionv1/sybilprotection.go @@ -131,14 +131,14 @@ func (o *SybilProtection) TrackBlock(block *blocks.Block) { blockEpoch := o.apiProvider.APIForSlot(block.ID().Slot()).TimeProvider().EpochFromSlot(block.ID().Slot()) // if the block is issued before the stake end epoch, then it's not a valid validator or candidate block - if accountData.StakeEndEpoch < blockEpoch { + if accountData.StakeEndEpoch() < blockEpoch { return } // if a candidate block is issued in the stake end epoch, // or if block is issued after EpochEndSlot - EpochNearingThreshold, because candidates can register only until that point. // then don't consider it because the validator can't be part of the committee in the next epoch - if accountData.StakeEndEpoch == blockEpoch || + if accountData.StakeEndEpoch() == blockEpoch || block.ID().Slot()+o.apiProvider.APIForSlot(block.ID().Slot()).ProtocolParameters().EpochNearingThreshold() > o.apiProvider.APIForSlot(block.ID().Slot()).TimeProvider().EpochEnd(blockEpoch) { return } @@ -339,7 +339,7 @@ func (o *SybilProtection) EligibleValidators(epoch iotago.EpochIndex) (accounts. return ierrors.Errorf("account of committee candidate %s does not exist", candidate) } // if `End Epoch` is the current one or has passed, validator is no longer considered for validator selection - if accountData.StakeEndEpoch <= epoch { + if accountData.StakeEndEpoch() <= epoch { return nil } validators = append(validators, accountData.Clone()) @@ -374,19 +374,19 @@ func (o *SybilProtection) OrderedRegisteredCandidateValidatorsList(epoch iotago. return ierrors.Errorf("account of committee candidate %s does not exist", candidate) } // if `End Epoch` is the current one or has passed, validator is no longer considered for validator selection - if accountData.StakeEndEpoch <= epoch { + if accountData.StakeEndEpoch() <= epoch { return nil } active := activeCandidates.Has(candidate) validatorResp = append(validatorResp, &api.ValidatorResponse{ - AddressBech32: accountData.ID.ToAddress().Bech32(o.apiProvider.CommittedAPI().ProtocolParameters().Bech32HRP()), - StakingEndEpoch: accountData.StakeEndEpoch, - PoolStake: accountData.ValidatorStake + accountData.DelegationStake, - ValidatorStake: accountData.ValidatorStake, - FixedCost: accountData.FixedCost, + AddressBech32: accountData.ID().ToAddress().Bech32(o.apiProvider.CommittedAPI().ProtocolParameters().Bech32HRP()), + StakingEndEpoch: accountData.StakeEndEpoch(), + PoolStake: accountData.ValidatorStake() + accountData.DelegationStake(), + ValidatorStake: accountData.ValidatorStake(), + FixedCost: accountData.FixedCost(), Active: active, - LatestSupportedProtocolVersion: accountData.LatestSupportedProtocolVersionAndHash.Version, - LatestSupportedProtocolHash: accountData.LatestSupportedProtocolVersionAndHash.Hash, + LatestSupportedProtocolVersion: accountData.LatestSupportedProtocolVersionAndHash().Version, + LatestSupportedProtocolHash: accountData.LatestSupportedProtocolVersionAndHash().Hash, }) return nil diff --git a/pkg/requesthandler/accounts.go b/pkg/requesthandler/accounts.go index c7989746e..4e7e3d823 100644 --- a/pkg/requesthandler/accounts.go +++ b/pkg/requesthandler/accounts.go @@ -42,11 +42,11 @@ func (r *RequestHandler) CongestionByAccountAddress(accountAddress *iotago.Accou return nil, ierrors.WithMessagef(echo.ErrNotFound, "account %s not found", accountID.ToHex()) } - blockIssuanceCredits := acc.Credits.Value + blockIssuanceCredits := acc.Credits().Value() // Apply decay to BIC if the value is positive if blockIssuanceCredits > 0 { manaDecayProvider := r.APIProvider().APIForSlot(targetSlot).ManaDecayProvider() - decayedBIC, err := manaDecayProvider.DecayManaBySlots(iotago.Mana(acc.Credits.Value), acc.Credits.UpdateSlot, targetSlot) + decayedBIC, err := manaDecayProvider.DecayManaBySlots(iotago.Mana(acc.Credits().Value()), acc.Credits().UpdateSlot(), targetSlot) if err != nil { return nil, ierrors.WithMessagef(echo.ErrInternalServerError, "failed to decay BIC for account %s: %w", accountID.ToHex(), err) } @@ -121,13 +121,13 @@ func (r *RequestHandler) ValidatorByAccountAddress(accountAddress *iotago.Accoun return &api.ValidatorResponse{ AddressBech32: accountID.ToAddress().Bech32(r.protocol.CommittedAPI().ProtocolParameters().Bech32HRP()), - PoolStake: accountData.ValidatorStake + accountData.DelegationStake, - ValidatorStake: accountData.ValidatorStake, - StakingEndEpoch: accountData.StakeEndEpoch, - FixedCost: accountData.FixedCost, + PoolStake: accountData.ValidatorStake() + accountData.DelegationStake(), + ValidatorStake: accountData.ValidatorStake(), + StakingEndEpoch: accountData.StakeEndEpoch(), + FixedCost: accountData.FixedCost(), Active: active, - LatestSupportedProtocolVersion: accountData.LatestSupportedProtocolVersionAndHash.Version, - LatestSupportedProtocolHash: accountData.LatestSupportedProtocolVersionAndHash.Hash, + LatestSupportedProtocolVersion: accountData.LatestSupportedProtocolVersionAndHash().Version, + LatestSupportedProtocolHash: accountData.LatestSupportedProtocolVersionAndHash().Hash, }, nil } diff --git a/pkg/storage/prunable/prunable.go b/pkg/storage/prunable/prunable.go index c7590a651..c9e585af2 100644 --- a/pkg/storage/prunable/prunable.go +++ b/pkg/storage/prunable/prunable.go @@ -184,19 +184,21 @@ func (p *Prunable) Rollback(targetEpoch iotago.EpochIndex, startPruneRange iotag return ierrors.Wrapf(err, "failed to rollback committee epochs to target epoch %d", targetEpoch) } - lastPrunedPoolStatsEpoch, _, err := p.poolStats.RollbackEpochs(targetEpoch) - if err != nil { - return ierrors.Wrapf(err, "failed to rollback pool stats epochs to target epoch %d", targetEpoch) - } - - lastPrunedDecidedUpgradeSignalsEpoch, _, err := p.decidedUpgradeSignals.RollbackEpochs(targetEpoch) - if err != nil { - return ierrors.Wrapf(err, "failed to rollback decided upgrade signals epochs to target epoch %d", targetEpoch) - } - + var lastPrunedPoolStatsEpoch iotago.EpochIndex + var lastPrunedDecidedUpgradeSignalsEpoch iotago.EpochIndex var lastPrunedPoolRewardsEpoch iotago.EpochIndex // Do not rollback the epoch if the targetSlot is the end of the epoch, because that is when we calculated the rewards. if targetSlot := startPruneRange - 1; p.apiProvider.APIForSlot(targetSlot).TimeProvider().EpochEnd(targetEpoch) != targetSlot { + lastPrunedPoolStatsEpoch, _, err = p.poolStats.RollbackEpochs(targetEpoch) + if err != nil { + return ierrors.Wrapf(err, "failed to rollback pool stats epochs to target epoch %d", targetEpoch) + } + + lastPrunedDecidedUpgradeSignalsEpoch, _, err = p.decidedUpgradeSignals.RollbackEpochs(targetEpoch) + if err != nil { + return ierrors.Wrapf(err, "failed to rollback decided upgrade signals epochs to target epoch %d", targetEpoch) + } + lastPrunedPoolRewardsEpoch, err = p.poolRewards.RollbackEpochs(targetEpoch) if err != nil { return ierrors.Wrapf(err, "failed to rollback pool rewards epochs to target epoch %d", targetEpoch) diff --git a/pkg/storage/prunable/slotstore/accountdiffs.go b/pkg/storage/prunable/slotstore/accountdiffs.go index 03a5efa9d..5acdcdb65 100644 --- a/pkg/storage/prunable/slotstore/accountdiffs.go +++ b/pkg/storage/prunable/slotstore/accountdiffs.go @@ -42,6 +42,10 @@ func NewAccountDiffs(slot iotago.SlotIndex, store kvstore.KVStore, api iotago.AP } } +func (b *AccountDiffs) Slot() iotago.SlotIndex { + return b.slot +} + // Store stores the given accountID as a root block. func (b *AccountDiffs) Store(accountID iotago.AccountID, accountDiff *model.AccountDiff, destroyed bool) (err error) { if destroyed { @@ -78,20 +82,34 @@ func (b *AccountDiffs) Delete(accountID iotago.AccountID) (err error) { return b.diffChangeStore.Delete(accountID) } +func (b *AccountDiffs) Clear() error { + if err := b.diffChangeStore.Clear(); err != nil { + return err + } + + return b.destroyedAccounts.Clear() +} + // Stream streams all accountIDs changes for a slot index. func (b *AccountDiffs) Stream(consumer func(accountID iotago.AccountID, accountDiff *model.AccountDiff, destroyed bool) bool) error { - // We firstly iterate over the destroyed accounts, as they won't have a corresponding accountDiff. - if storageErr := b.destroyedAccounts.Iterate(kvstore.EmptyPrefix, func(accountID iotago.AccountID, _ types.Empty) bool { - return consumer(accountID, nil, true) - }); storageErr != nil { - return ierrors.Wrapf(storageErr, "failed to iterate over account diffs for slot %s", b.slot) - } + // Existing accounts modified in the slot only have a SlotDiff, but are not part of the b.destroyedAccount store. + // Destroyed accounts should also have a SlotDiff + // and be part of b.destroyedAccount so that it's possible to re-create those. - // For those accounts that still exist, we might have an accountDiff. + var internalErr error if storageErr := b.diffChangeStore.Iterate(kvstore.EmptyPrefix, func(accountID iotago.AccountID, accountDiff *model.AccountDiff) bool { - return consumer(accountID, accountDiff, false) + destroyed, err := b.destroyedAccounts.Has(accountID) + if err != nil { + internalErr = ierrors.Wrapf(err, "failed to check if an account %s was destroyed", accountID) + + return false + } + + return consumer(accountID, accountDiff, destroyed) }); storageErr != nil { return ierrors.Wrapf(storageErr, "failed to iterate over account diffs for slot %s", b.slot) + } else if internalErr != nil { + return internalErr } return nil diff --git a/pkg/tests/accounts_test.go b/pkg/tests/accounts_test.go index ac4505fa0..6c9fefcc2 100644 --- a/pkg/tests/accounts_test.go +++ b/pkg/tests/accounts_test.go @@ -61,35 +61,34 @@ func Test_TransitionAndDestroyAccount(t *testing.T) { // genesis account. genesisAccount := ts.AccountOutput("Genesis:1") genesisAccountOutput := genesisAccount.Output.(*iotago.AccountOutput) - ts.AssertAccountData(&accounts.AccountData{ - ID: genesisAccountOutput.AccountID, - Credits: accounts.NewBlockIssuanceCredits(iotago.BlockIssuanceCredits(123), 0), - OutputID: genesisAccount.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: iotago.NewBlockIssuerKeys(oldGenesisOutputKey), - }, ts.Nodes()...) + ts.AssertAccountData( + accounts.NewAccountData(genesisAccountOutput.AccountID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(iotago.BlockIssuanceCredits(123), 0)), + accounts.WithOutputID(genesisAccount.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(oldGenesisOutputKey), + ), ts.Nodes()...) // validator node account. validatorAccountOutput := ts.AccountOutput("Genesis:2") - ts.AssertAccountData(&accounts.AccountData{ - ID: node1.Validator.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0), - OutputID: validatorAccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: node1.Validator.BlockIssuerKeys(), - StakeEndEpoch: iotago.MaxEpochIndex, - ValidatorStake: mock.MinValidatorAccountAmount(ts.API.ProtocolParameters()), - }, ts.Nodes()...) + ts.AssertAccountData( + accounts.NewAccountData(node1.Validator.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0)), + accounts.WithOutputID(validatorAccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(node1.Validator.BlockIssuerKeys()...), + accounts.WithStakeEndEpoch(iotago.MaxEpochIndex), + accounts.WithValidatorStake(mock.MinValidatorAccountAmount(ts.API.ProtocolParameters())), + ), ts.Nodes()...) // default wallet block issuer account. blockIssuerAccountOutput := ts.AccountOutput("Genesis:3") - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0), - OutputID: blockIssuerAccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(wallet.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0)), + accounts.WithOutputID(blockIssuerAccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) // MODIFY EXISTING GENESIS ACCOUNT newGenesisOutputKey := tpkg.RandBlockIssuerKey() @@ -123,13 +122,12 @@ func Test_TransitionAndDestroyAccount(t *testing.T) { BlockIssuerKeysAdded: iotago.NewBlockIssuerKeys(newGenesisOutputKey), }, false, ts.Nodes()...) - ts.AssertAccountData(&accounts.AccountData{ - ID: genesisAccountOutput.AccountID, - Credits: accounts.NewBlockIssuanceCredits(iotago.BlockIssuanceCredits(123), 0), - OutputID: ts.DefaultWallet().OutputData("TX1:0").ID, - BlockIssuerKeys: iotago.NewBlockIssuerKeys(oldGenesisOutputKey, newGenesisOutputKey), - ExpirySlot: newExpirySlot, - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(genesisAccountOutput.AccountID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(123, 0)), + accounts.WithOutputID(ts.DefaultWallet().OutputData("TX1:0").ID), + accounts.WithBlockIssuerKeys(oldGenesisOutputKey, newGenesisOutputKey), + accounts.WithExpirySlot(newExpirySlot), + ), ts.Nodes()...) // DESTROY GENESIS ACCOUNT // commit until the expiry slot of the transitioned genesis account plus one. @@ -184,25 +182,23 @@ func Test_StakeDelegateAndDelayedClaim(t *testing.T) { // Assert validator and block issuer accounts in genesis snapshot. // Validator node account. validatorAccountOutput := ts.AccountOutput("Genesis:1") - ts.AssertAccountData(&accounts.AccountData{ - ID: node1.Validator.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0), - OutputID: validatorAccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: node1.Validator.BlockIssuerKeys(), - StakeEndEpoch: iotago.MaxEpochIndex, - ValidatorStake: mock.MinValidatorAccountAmount(ts.API.ProtocolParameters()), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(node1.Validator.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0)), + accounts.WithOutputID(validatorAccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(node1.Validator.BlockIssuerKeys()...), + accounts.WithStakeEndEpoch(iotago.MaxEpochIndex), + accounts.WithValidatorStake(mock.MinValidatorAccountAmount(ts.API.ProtocolParameters())), + ), ts.Nodes()...) // Default wallet block issuer account. blockIssuerAccountOutput := ts.AccountOutput("Genesis:2") - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0), - OutputID: blockIssuerAccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(wallet.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0)), + accounts.WithOutputID(blockIssuerAccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) // CREATE NEW ACCOUNT WITH BLOCK ISSUER AND STAKING FEATURES FROM BASIC UTXO newAccountBlockIssuerKey := tpkg.RandBlockIssuerKey() @@ -251,17 +247,16 @@ func Test_StakeDelegateAndDelayedClaim(t *testing.T) { DelegationStakeChange: 0, }, false, ts.Nodes()...) - ts.AssertAccountData(&accounts.AccountData{ - ID: newAccountOutput.AccountID, - Credits: accounts.NewBlockIssuanceCredits(0, block1Slot), - ExpirySlot: newAccountExpirySlot, - OutputID: newAccount.ID, - BlockIssuerKeys: iotago.NewBlockIssuerKeys(newAccountBlockIssuerKey), - StakeEndEpoch: 10, - FixedCost: 421, - DelegationStake: 0, - ValidatorStake: stakedAmount, - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(newAccountOutput.AccountID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(0, block1Slot)), + accounts.WithOutputID(newAccount.ID), + accounts.WithExpirySlot(newAccountExpirySlot), + accounts.WithBlockIssuerKeys(newAccountBlockIssuerKey), + accounts.WithStakeEndEpoch(10), + accounts.WithFixedCost(421), + accounts.WithDelegationStake(0), + accounts.WithValidatorStake(stakedAmount), + ), ts.Nodes()...) // CREATE DELEGATION TO NEW ACCOUNT FROM BASIC UTXO accountAddress := iotago.AccountAddress(newAccountOutput.AccountID) @@ -290,17 +285,16 @@ func Test_StakeDelegateAndDelayedClaim(t *testing.T) { DelegationStakeChange: int64(delegatedAmount), }, false, ts.Nodes()...) - ts.AssertAccountData(&accounts.AccountData{ - ID: newAccountOutput.AccountID, - Credits: accounts.NewBlockIssuanceCredits(0, block1Slot), - ExpirySlot: newAccountExpirySlot, - OutputID: newAccount.ID, - BlockIssuerKeys: iotago.NewBlockIssuerKeys(newAccountBlockIssuerKey), - StakeEndEpoch: 10, - FixedCost: 421, - DelegationStake: iotago.BaseToken(delegatedAmount), - ValidatorStake: stakedAmount, - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(newAccountOutput.AccountID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(0, block1Slot)), + accounts.WithExpirySlot(newAccountExpirySlot), + accounts.WithOutputID(newAccount.ID), + accounts.WithBlockIssuerKeys(newAccountBlockIssuerKey), + accounts.WithStakeEndEpoch(10), + accounts.WithFixedCost(421), + accounts.WithDelegationStake(delegatedAmount), + accounts.WithValidatorStake(stakedAmount), + ), ts.Nodes()...) // transition a delegation output to a delayed claiming state block3Slot := ts.CurrentSlot() @@ -323,17 +317,16 @@ func Test_StakeDelegateAndDelayedClaim(t *testing.T) { DelegationStakeChange: -int64(delegatedAmount), }, false, ts.Nodes()...) - ts.AssertAccountData(&accounts.AccountData{ - ID: newAccountOutput.AccountID, - Credits: accounts.NewBlockIssuanceCredits(0, block1Slot), - ExpirySlot: newAccountExpirySlot, - OutputID: newAccount.ID, - BlockIssuerKeys: iotago.NewBlockIssuerKeys(newAccountBlockIssuerKey), - StakeEndEpoch: 10, - FixedCost: 421, - DelegationStake: iotago.BaseToken(0), - ValidatorStake: stakedAmount, - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(newAccountOutput.AccountID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(0, block1Slot)), + accounts.WithExpirySlot(newAccountExpirySlot), + accounts.WithOutputID(newAccount.ID), + accounts.WithBlockIssuerKeys(newAccountBlockIssuerKey), + accounts.WithStakeEndEpoch(10), + accounts.WithFixedCost(421), + accounts.WithDelegationStake(0), + accounts.WithValidatorStake(stakedAmount), + ), ts.Nodes()...) } func Test_ImplicitAccounts(t *testing.T) { @@ -361,25 +354,23 @@ func Test_ImplicitAccounts(t *testing.T) { // assert validator and block issuer accounts in genesis snapshot. // validator node account. validatorAccountOutput := ts.AccountOutput("Genesis:1") - ts.AssertAccountData(&accounts.AccountData{ - ID: node1.Validator.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0), - OutputID: validatorAccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: node1.Validator.BlockIssuerKeys(), - StakeEndEpoch: iotago.MaxEpochIndex, - ValidatorStake: mock.MinValidatorAccountAmount(ts.API.ProtocolParameters()), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(node1.Validator.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0)), + accounts.WithOutputID(validatorAccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(node1.Validator.BlockIssuerKeys()...), + accounts.WithStakeEndEpoch(iotago.MaxEpochIndex), + accounts.WithValidatorStake(mock.MinValidatorAccountAmount(ts.API.ProtocolParameters())), + ), ts.Nodes()...) // default wallet block issuer account. blockIssuerAccountOutput := ts.AccountOutput("Genesis:2") - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0), - OutputID: blockIssuerAccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(wallet.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0)), + accounts.WithOutputID(blockIssuerAccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) // CREATE IMPLICIT ACCOUNT FROM GENESIS BASIC UTXO, SENT TO A NEW USER WALLET. // this wallet is not registered in the ledger yet. @@ -401,13 +392,12 @@ func Test_ImplicitAccounts(t *testing.T) { 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, block1Slot), - ExpirySlot: iotago.MaxSlotIndex, - OutputID: implicitAccountOutputID, - BlockIssuerKeys: iotago.NewBlockIssuerKeys(implicitBlockIssuerKey), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(implicitAccountID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(0, block1Slot)), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithOutputID(implicitAccountOutputID), + accounts.WithBlockIssuerKeys(implicitBlockIssuerKey), + ), ts.Nodes()...) // TRANSITION IMPLICIT ACCOUNT TO ACCOUNT OUTPUT. // USE IMPLICIT ACCOUNT AS BLOCK ISSUER. @@ -447,13 +437,12 @@ func Test_ImplicitAccounts(t *testing.T) { DelegationStakeChange: 0, }, false, ts.Nodes()...) - ts.AssertAccountData(&accounts.AccountData{ - ID: implicitAccountID, - Credits: accounts.NewBlockIssuanceCredits(allotted-burned, block2Slot), - ExpirySlot: iotago.MaxSlotIndex, - OutputID: fullAccountOutputID, - BlockIssuerKeys: iotago.NewBlockIssuerKeys(fullAccountBlockIssuerKey), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(implicitAccountID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(allotted-burned, block2Slot)), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithOutputID(fullAccountOutputID), + accounts.WithBlockIssuerKeys(fullAccountBlockIssuerKey), + ), ts.Nodes()...) ts.Wait(ts.Nodes()...) } @@ -496,23 +485,21 @@ func Test_NegativeBIC_BlockIssuerLocked(t *testing.T) { // wallet 1 block issuer account. wallet1OutputID := ts.AccountOutput("Genesis:2").ID - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet1.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet1BIC, 0), - OutputID: wallet1OutputID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet1.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(wallet1.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet1BIC, 0)), + accounts.WithOutputID(wallet1OutputID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet1.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) // wallet 2 block issuer account. wallet2OutputID := ts.AccountOutput("Genesis:3").ID - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet2.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet2BIC, 0), - OutputID: wallet2OutputID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet2.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(wallet2.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet2BIC, 0)), + accounts.WithOutputID(wallet2OutputID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet2.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) // MODIFY EXISTING GENESIS ACCOUNT var block1Slot iotago.SlotIndex = 1 @@ -533,21 +520,19 @@ func Test_NegativeBIC_BlockIssuerLocked(t *testing.T) { wallet1BIC -= burned wallet2BIC -= burned - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet1.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet1BIC, block1Slot), - OutputID: wallet1OutputID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet1.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) - - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet2.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet2BIC, block1Slot), - ExpirySlot: iotago.MaxSlotIndex, - OutputID: wallet2OutputID, - BlockIssuerKeys: wallet2.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(wallet1.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet1BIC, block1Slot)), + accounts.WithOutputID(wallet1OutputID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet1.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) + + ts.AssertAccountData(accounts.NewAccountData(wallet2.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet2BIC, block1Slot)), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithOutputID(wallet2OutputID), + accounts.WithBlockIssuerKeys(wallet2.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) } block2Slot := ts.CurrentSlot() @@ -570,21 +555,19 @@ func Test_NegativeBIC_BlockIssuerLocked(t *testing.T) { wallet1BIC -= burned - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet1.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet1BIC, block2Slot), - OutputID: wallet1OutputID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet1.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) - - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet2.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet2BIC, block1Slot), - ExpirySlot: iotago.MaxSlotIndex, - OutputID: wallet2OutputID, - BlockIssuerKeys: wallet2.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(wallet1.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet1BIC, block2Slot)), + accounts.WithOutputID(wallet1OutputID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet1.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) + + ts.AssertAccountData(accounts.NewAccountData(wallet2.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet2BIC, block1Slot)), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithOutputID(wallet2OutputID), + accounts.WithBlockIssuerKeys(wallet2.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) } block3Slot := ts.CurrentSlot() @@ -611,21 +594,19 @@ func Test_NegativeBIC_BlockIssuerLocked(t *testing.T) { wallet1BIC -= burned wallet2BIC += allottedBIC - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet1.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet1BIC, block31.ID().Slot()), - OutputID: wallet1OutputID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet1.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) - - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet2.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet2BIC, block31.ID().Slot()), - ExpirySlot: iotago.MaxSlotIndex, - OutputID: wallet2OutputID, - BlockIssuerKeys: wallet2.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(wallet1.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet1BIC, block31.ID().Slot())), + accounts.WithOutputID(wallet1OutputID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet1.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) + + ts.AssertAccountData(accounts.NewAccountData(wallet2.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet2BIC, block31.ID().Slot())), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithOutputID(wallet2OutputID), + accounts.WithBlockIssuerKeys(wallet2.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) } block4Slot := ts.CurrentSlot() @@ -642,21 +623,19 @@ func Test_NegativeBIC_BlockIssuerLocked(t *testing.T) { wallet2BIC -= burned - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet1.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet1BIC, ts.BlockID("block3.1").Slot()), - OutputID: wallet1OutputID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet1.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) - - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet2.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet2BIC, block4Slot), - ExpirySlot: iotago.MaxSlotIndex, - OutputID: wallet2OutputID, - BlockIssuerKeys: wallet2.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(wallet1.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet1BIC, ts.BlockID("block3.1").Slot())), + accounts.WithOutputID(wallet1OutputID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet1.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) + + ts.AssertAccountData(accounts.NewAccountData(wallet2.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet2BIC, block4Slot)), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithOutputID(wallet2OutputID), + accounts.WithBlockIssuerKeys(wallet2.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) } } @@ -695,34 +674,33 @@ func Test_NegativeBIC_AccountOutput(t *testing.T) { // check that the accounts added in the genesis snapshot were added to the account manager correctly. // validator node account. validatorAccountOutput := ts.AccountOutput("Genesis:1") - ts.AssertAccountData(&accounts.AccountData{ - ID: node1.Validator.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0), - OutputID: validatorAccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: node1.Validator.BlockIssuerKeys(), - StakeEndEpoch: iotago.MaxEpochIndex, - ValidatorStake: mock.MinValidatorAccountAmount(ts.API.ProtocolParameters()), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(node1.Validator.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0)), + accounts.WithOutputID(validatorAccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(node1.Validator.BlockIssuerKeys()...), + accounts.WithStakeEndEpoch(iotago.MaxEpochIndex), + accounts.WithValidatorStake(mock.MinValidatorAccountAmount(ts.API.ProtocolParameters())), + ), ts.Nodes()...) // default wallet block issuer account. wallet1AccountOutput := ts.AccountOutput("Genesis:2") - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet1.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(-1, 0), - OutputID: wallet1AccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet1.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) + + ts.AssertAccountData(accounts.NewAccountData(wallet1.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(-1, 0)), + accounts.WithOutputID(wallet1AccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet1.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) wallet2AccountOutput := ts.AccountOutput("Genesis:3") - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet2.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0), - OutputID: wallet2AccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet2.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) + + ts.AssertAccountData(accounts.NewAccountData(wallet2.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0)), + accounts.WithOutputID(wallet2AccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet2.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) // MODIFY EXISTING GENESIS ACCOUNT newWallet1IssuerKey := tpkg.RandBlockIssuerKey() @@ -753,21 +731,19 @@ func Test_NegativeBIC_AccountOutput(t *testing.T) { latestParents = ts.CommitUntilSlot(block1Slot, ts.BlockIDs("Genesis")...) // The outputID of wallet1 and wallet2 account should remain the same as neither was successfully spent. - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet1.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet1BIC, 0), - OutputID: wallet1AccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet1.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) - - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet2.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet2BIC, 0), - OutputID: wallet2AccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet2.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(wallet1.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet1BIC, 0)), + accounts.WithOutputID(wallet1AccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet1.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) + + ts.AssertAccountData(accounts.NewAccountData(wallet2.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet2BIC, 0)), + accounts.WithOutputID(wallet2AccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet2.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) } block2Slot := ts.CurrentSlot() @@ -801,21 +777,19 @@ func Test_NegativeBIC_AccountOutput(t *testing.T) { wallet1BIC += allottedBIC wallet2BIC -= iotago.BlockIssuanceCredits(block2.WorkScore()) * iotago.BlockIssuanceCredits(ts.API.ProtocolParameters().CongestionControlParameters().MinReferenceManaCost) - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet1.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet1BIC, block2Slot), - OutputID: wallet1AccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet1.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) - - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet2.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet2BIC, block2Slot), - ExpirySlot: iotago.MaxSlotIndex, - OutputID: wallet2AccountOutput.ID, - BlockIssuerKeys: wallet2.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(wallet1.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet1BIC, block2Slot)), + accounts.WithOutputID(wallet1AccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet1.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) + + ts.AssertAccountData(accounts.NewAccountData(wallet2.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet2BIC, block2Slot)), + accounts.WithOutputID(wallet2AccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet2.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) } block3Slot := ts.CurrentSlot() @@ -842,21 +816,19 @@ func Test_NegativeBIC_AccountOutput(t *testing.T) { // The outputID of wallet1 and wallet2 account should remain the same as neither was successfully spent. // The mana on wallet2 account should be subtracted // because it issued the block with a transaction that didn't mutate the ledger. - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet1.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet1BIC, block3Slot), - OutputID: wallet1.AccountOutputData("TX3:0").ID, - ExpirySlot: newExpirySlot, - BlockIssuerKeys: iotago.NewBlockIssuerKeys(wallet1.BlockIssuer.BlockIssuerKeys()[0], newWallet1IssuerKey), - }, ts.Nodes()...) - - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet2.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet2BIC, block2Slot), - OutputID: wallet2AccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet2.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(wallet1.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet1BIC, block3Slot)), + accounts.WithOutputID(wallet1.AccountOutputData("TX3:0").ID), + accounts.WithExpirySlot(newExpirySlot), + accounts.WithBlockIssuerKeys(wallet1.BlockIssuer.BlockIssuerKeys()[0], newWallet1IssuerKey), + ), ts.Nodes()...) + + ts.AssertAccountData(accounts.NewAccountData(wallet2.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet2BIC, block2Slot)), + accounts.WithOutputID(wallet2AccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet2.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) } // DESTROY WALLET 1 ACCOUNT @@ -920,34 +892,31 @@ func Test_NegativeBIC_AccountOwnedBasicOutputLocked(t *testing.T) { // check that the accounts added in the genesis snapshot were added to the account manager correctly. // validator node account. validatorAccountOutput := ts.AccountOutput("Genesis:1") - ts.AssertAccountData(&accounts.AccountData{ - ID: node1.Validator.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0), - OutputID: validatorAccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: node1.Validator.BlockIssuerKeys(), - StakeEndEpoch: iotago.MaxEpochIndex, - ValidatorStake: mock.MinValidatorAccountAmount(ts.API.ProtocolParameters()), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(node1.Validator.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0)), + accounts.WithOutputID(validatorAccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(node1.Validator.BlockIssuerKeys()...), + accounts.WithStakeEndEpoch(iotago.MaxEpochIndex), + accounts.WithValidatorStake(mock.MinValidatorAccountAmount(ts.API.ProtocolParameters())), + ), ts.Nodes()...) // default wallet block issuer account. wallet1AccountOutput := ts.AccountOutput("Genesis:2") - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet1.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet1BIC, 0), - OutputID: wallet1AccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet1.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(wallet1.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet1BIC, 0)), + accounts.WithOutputID(wallet1AccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet1.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) wallet2AccountOutput := ts.AccountOutput("Genesis:3") - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet2.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet2BIC, 0), - OutputID: wallet2AccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet2.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(wallet2.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet2BIC, 0)), + accounts.WithOutputID(wallet2AccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet2.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) var block1Slot iotago.SlotIndex = 1 var latestParents []iotago.BlockID @@ -984,21 +953,19 @@ func Test_NegativeBIC_AccountOwnedBasicOutputLocked(t *testing.T) { wallet2BIC -= iotago.BlockIssuanceCredits(block1.WorkScore()) * iotago.BlockIssuanceCredits(ts.API.ProtocolParameters().CongestionControlParameters().MinReferenceManaCost) // The outputID of wallet1 and wallet2 account should remain the same as neither was successfully spent. - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet1.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet1BIC, 0), - OutputID: wallet1AccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet1.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) - - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet2.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet2BIC, block1Slot), - OutputID: wallet2AccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet2.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(wallet1.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet1BIC, 0)), + accounts.WithOutputID(wallet1AccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet1.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) + + ts.AssertAccountData(accounts.NewAccountData(wallet2.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet2BIC, block1Slot)), + accounts.WithOutputID(wallet2AccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet2.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) } block2Slot := ts.CurrentSlot() @@ -1022,21 +989,19 @@ func Test_NegativeBIC_AccountOwnedBasicOutputLocked(t *testing.T) { latestParents = ts.CommitUntilSlot(block2Slot, latestParents...) // The outputID of wallet1 and wallet2 account should remain the same as neither was successfully spent. - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet1.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet1BIC, 0), - OutputID: wallet1AccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet1.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) - - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet2.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet2BIC, block1Slot), - OutputID: wallet2AccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet2.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(wallet1.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet1BIC, 0)), + accounts.WithOutputID(wallet1AccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet1.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) + + ts.AssertAccountData(accounts.NewAccountData(wallet2.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet2BIC, block1Slot)), + accounts.WithOutputID(wallet2AccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet2.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) } block3Slot := ts.CurrentSlot() @@ -1066,21 +1031,19 @@ func Test_NegativeBIC_AccountOwnedBasicOutputLocked(t *testing.T) { wallet2BIC -= iotago.BlockIssuanceCredits(block3.WorkScore()) * iotago.BlockIssuanceCredits(ts.API.ProtocolParameters().CongestionControlParameters().MinReferenceManaCost) require.GreaterOrEqual(t, wallet1BIC, iotago.BlockIssuanceCredits(0)) - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet1.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet1BIC, block3Slot), - OutputID: wallet1AccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet1.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) - - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet2.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet2BIC, block3Slot), - ExpirySlot: iotago.MaxSlotIndex, - OutputID: wallet2AccountOutput.ID, - BlockIssuerKeys: wallet2.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(wallet1.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet1BIC, block3Slot)), + accounts.WithOutputID(wallet1AccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet1.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) + + ts.AssertAccountData(accounts.NewAccountData(wallet2.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet2BIC, block3Slot)), + accounts.WithOutputID(wallet2AccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet2.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) } block4Slot := ts.CurrentSlot() @@ -1105,20 +1068,18 @@ func Test_NegativeBIC_AccountOwnedBasicOutputLocked(t *testing.T) { wallet1BIC -= iotago.BlockIssuanceCredits(block4.WorkScore()) * iotago.BlockIssuanceCredits(ts.API.ProtocolParameters().CongestionControlParameters().MinReferenceManaCost) // The outputID of wallet1 and wallet2 account should remain the same as neither was successfully spent. - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet1.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet1BIC, block4Slot), - OutputID: wallet1.OutputData("TX4:0").ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet1.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) - - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet2.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(wallet2BIC, block3Slot), - OutputID: wallet2AccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet2.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(wallet1.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet1BIC, block4Slot)), + accounts.WithOutputID(wallet1.OutputData("TX4:0").ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet1.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) + + ts.AssertAccountData(accounts.NewAccountData(wallet2.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(wallet2BIC, block3Slot)), + accounts.WithOutputID(wallet2AccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet2.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) } } diff --git a/pkg/tests/combined_account_transition_test.go b/pkg/tests/combined_account_transition_test.go index 31f05d97d..ef85d873c 100644 --- a/pkg/tests/combined_account_transition_test.go +++ b/pkg/tests/combined_account_transition_test.go @@ -112,13 +112,13 @@ func createFullAccount(ts *testsuite.TestSuite) iotago.AccountID { DelegationStakeChange: 0, }, false, ts.Nodes()...) - ts.AssertAccountData(&accounts.AccountData{ - ID: newAccountOutput.AccountID, - Credits: accounts.NewBlockIssuanceCredits(0, block1Slot), - ExpirySlot: newAccountExpirySlot, - OutputID: newAccount.ID, - BlockIssuerKeys: iotago.NewBlockIssuerKeys(newAccountBlockIssuerKey), - }, ts.Nodes()...) + ts.AssertAccountData( + accounts.NewAccountData(newAccountOutput.AccountID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(0, block1Slot)), + accounts.WithExpirySlot(newAccountExpirySlot), + accounts.WithOutputID(newAccount.ID), + accounts.WithBlockIssuerKeys(newAccountBlockIssuerKey), + ), ts.Nodes()...) return newAccountOutput.AccountID } @@ -144,13 +144,12 @@ func createImplicitToFullAccount(ts *testsuite.TestSuite) iotago.AccountID { 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()...) + ts.AssertAccountData(accounts.NewAccountData(implicitAccountID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(0, block2Slot)), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithOutputID(implicitAccountOutputID), + accounts.WithBlockIssuerKeys(implicitBlockIssuerKey), + ), ts.Nodes()...) // TRANSITION IMPLICIT ACCOUNT TO ACCOUNT OUTPUT. block3Slot := ts.CurrentSlot() @@ -185,13 +184,12 @@ func createImplicitToFullAccount(ts *testsuite.TestSuite) iotago.AccountID { 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()...) + ts.AssertAccountData(accounts.NewAccountData(implicitAccountID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(allotted-burned, block3Slot)), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithOutputID(fullAccountOutputID), + accounts.WithBlockIssuerKeys(implicitBlockIssuerKey), + ), ts.Nodes()...) return implicitAccountID } diff --git a/pkg/tests/reward_test.go b/pkg/tests/reward_test.go index 280581604..c24d45937 100644 --- a/pkg/tests/reward_test.go +++ b/pkg/tests/reward_test.go @@ -41,24 +41,22 @@ func setupRewardTestsuite(t *testing.T) (*testsuite.TestSuite, *mock.Node, *mock // Assert validator and block issuer accounts in genesis snapshot. // Validator node account. validatorAccountOutput := ts.AccountOutput("Genesis:1") - ts.AssertAccountData(&accounts.AccountData{ - ID: node1.Validator.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0), - OutputID: validatorAccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: node1.Validator.BlockIssuerKeys(), - StakeEndEpoch: iotago.MaxEpochIndex, - ValidatorStake: mock.MinValidatorAccountAmount(ts.API.ProtocolParameters()), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(node1.Validator.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0)), + accounts.WithOutputID(validatorAccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(node1.Validator.BlockIssuerKeys()...), + accounts.WithStakeEndEpoch(iotago.MaxEpochIndex), + accounts.WithValidatorStake(mock.MinValidatorAccountAmount(ts.API.ProtocolParameters())), + ), ts.Nodes()...) // Default wallet block issuer account. blockIssuerAccountOutput := ts.AccountOutput("Genesis:2") - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet.BlockIssuer.AccountData.ID, - Credits: accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0), - OutputID: blockIssuerAccountOutput.ID, - ExpirySlot: iotago.MaxSlotIndex, - BlockIssuerKeys: wallet.BlockIssuer.BlockIssuerKeys(), - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(wallet.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0)), + accounts.WithOutputID(blockIssuerAccountOutput.ID), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithBlockIssuerKeys(wallet.BlockIssuer.BlockIssuerKeys()...), + ), ts.Nodes()...) return ts, node1, node2 } @@ -193,15 +191,14 @@ func Test_Account_RemoveStakingFeatureWithoutRewards(t *testing.T) { accountOutput := ts.DefaultWallet().OutputData("TX2:0") accountID := accountOutput.Output.(*iotago.AccountOutput).AccountID - ts.AssertAccountData(&accounts.AccountData{ - ID: accountID, - Credits: &accounts.BlockIssuanceCredits{Value: 0, UpdateSlot: block1Slot}, - OutputID: accountOutput.ID, - ExpirySlot: blockIssuerFeatExpirySlot, - BlockIssuerKeys: iotago.BlockIssuerKeys{blockIssuerFeatKey}, - StakeEndEpoch: 0, - ValidatorStake: 0, - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(accountID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(0, block1Slot)), + accounts.WithOutputID(accountOutput.ID), + accounts.WithExpirySlot(blockIssuerFeatExpirySlot), + accounts.WithBlockIssuerKeys(blockIssuerFeatKey), + accounts.WithStakeEndEpoch(0), + accounts.WithValidatorStake(0), + ), ts.Nodes()...) ts.AssertAccountDiff(accountID, block2Slot, &model.AccountDiff{ BICChange: -iotago.BlockIssuanceCredits(0), diff --git a/pkg/tests/upgrade_signaling_test.go b/pkg/tests/upgrade_signaling_test.go index faea67324..10ff041e6 100644 --- a/pkg/tests/upgrade_signaling_test.go +++ b/pkg/tests/upgrade_signaling_test.go @@ -131,31 +131,29 @@ func Test_Upgrade_Signaling(t *testing.T) { hash1 := lo.PanicOnErr(v5ProtocolParameters.Hash()) hash2 := iotago.Identifier{2} - ts.AssertAccountData(&accounts.AccountData{ - ID: ts.Node("nodeA").Validator.AccountData.ID, - Credits: &accounts.BlockIssuanceCredits{Value: iotago.MaxBlockIssuanceCredits / 2, UpdateSlot: 0}, - ExpirySlot: iotago.MaxSlotIndex, - OutputID: ts.AccountOutput("Genesis:1").ID, - BlockIssuerKeys: ts.Node("nodeA").Validator.BlockIssuerKeys(), - ValidatorStake: mock.MinValidatorAccountAmount(ts.API.ProtocolParameters()), - DelegationStake: 0, - FixedCost: 0, - StakeEndEpoch: iotago.MaxEpochIndex, - LatestSupportedProtocolVersionAndHash: model.VersionAndHash{}, - }, ts.Nodes()...) - - ts.AssertAccountData(&accounts.AccountData{ - ID: wallet.BlockIssuer.AccountData.ID, - Credits: &accounts.BlockIssuanceCredits{Value: iotago.MaxBlockIssuanceCredits / 2, UpdateSlot: 0}, - ExpirySlot: iotago.MaxSlotIndex, - OutputID: ts.AccountOutput("Genesis:5").ID, - BlockIssuerKeys: wallet.BlockIssuer.BlockIssuerKeys(), - ValidatorStake: 0, - DelegationStake: 0, - FixedCost: 0, - StakeEndEpoch: 0, - LatestSupportedProtocolVersionAndHash: model.VersionAndHash{}, - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(ts.Node("nodeA").Validator.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0)), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithOutputID(ts.AccountOutput("Genesis:1").ID), + accounts.WithBlockIssuerKeys(ts.Node("nodeA").Validator.BlockIssuerKeys()...), + accounts.WithValidatorStake(mock.MinValidatorAccountAmount(ts.API.ProtocolParameters())), + accounts.WithDelegationStake(0), + accounts.WithFixedCost(0), + accounts.WithStakeEndEpoch(iotago.MaxEpochIndex), + accounts.WithLatestSupportedProtocolVersionAndHash(model.VersionAndHash{}), + ), ts.Nodes()...) + + ts.AssertAccountData(accounts.NewAccountData(wallet.BlockIssuer.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0)), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithOutputID(ts.AccountOutput("Genesis:5").ID), + accounts.WithBlockIssuerKeys(wallet.BlockIssuer.BlockIssuerKeys()...), + accounts.WithValidatorStake(0), + accounts.WithDelegationStake(0), + accounts.WithFixedCost(0), + accounts.WithStakeEndEpoch(0), + accounts.WithLatestSupportedProtocolVersionAndHash(model.VersionAndHash{}), + ), ts.Nodes()...) // We force the nodes to issue at a specific version/hash to test tracking of votes for the upgrade signaling. ts.Node("nodeA").SetHighestSupportedVersion(4) @@ -165,31 +163,29 @@ func Test_Upgrade_Signaling(t *testing.T) { ts.IssueBlocksAtEpoch("", 0, 4, "Genesis", ts.Nodes(), true, false) // check account data before all nodes set the current version - ts.AssertAccountData(&accounts.AccountData{ - ID: ts.Node("nodeA").Validator.AccountData.ID, - Credits: &accounts.BlockIssuanceCredits{Value: iotago.MaxBlockIssuanceCredits / 2, UpdateSlot: 0}, - ExpirySlot: iotago.MaxSlotIndex, - OutputID: ts.AccountOutput("Genesis:1").ID, - BlockIssuerKeys: ts.Node("nodeA").Validator.BlockIssuerKeys(), - ValidatorStake: mock.MinValidatorAccountAmount(ts.API.ProtocolParameters()), - DelegationStake: 0, - FixedCost: 0, - StakeEndEpoch: iotago.MaxEpochIndex, - LatestSupportedProtocolVersionAndHash: model.VersionAndHash{Version: 4, Hash: hash2}, - }, ts.Nodes()...) - - ts.AssertAccountData(&accounts.AccountData{ - ID: ts.Node("nodeD").Validator.AccountData.ID, - Credits: &accounts.BlockIssuanceCredits{Value: iotago.MaxBlockIssuanceCredits / 2, UpdateSlot: 0}, - ExpirySlot: iotago.MaxSlotIndex, - OutputID: ts.AccountOutput("Genesis:4").ID, - BlockIssuerKeys: ts.Node("nodeD").Validator.BlockIssuerKeys(), - ValidatorStake: mock.MinValidatorAccountAmount(ts.API.ProtocolParameters()), - DelegationStake: 0, - FixedCost: 0, - StakeEndEpoch: iotago.MaxEpochIndex, - LatestSupportedProtocolVersionAndHash: model.VersionAndHash{Version: 3, Hash: hash2}, - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(ts.Node("nodeA").Validator.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0)), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithOutputID(ts.AccountOutput("Genesis:1").ID), + accounts.WithBlockIssuerKeys(ts.Node("nodeA").Validator.BlockIssuerKeys()...), + accounts.WithValidatorStake(mock.MinValidatorAccountAmount(ts.API.ProtocolParameters())), + accounts.WithDelegationStake(0), + accounts.WithFixedCost(0), + accounts.WithStakeEndEpoch(iotago.MaxEpochIndex), + accounts.WithLatestSupportedProtocolVersionAndHash(model.VersionAndHash{Version: 4, Hash: hash2}), + ), ts.Nodes()...) + + ts.AssertAccountData(accounts.NewAccountData(ts.Node("nodeD").Validator.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0)), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithOutputID(ts.AccountOutput("Genesis:4").ID), + accounts.WithBlockIssuerKeys(ts.Node("nodeD").Validator.BlockIssuerKeys()...), + accounts.WithValidatorStake(mock.MinValidatorAccountAmount(ts.API.ProtocolParameters())), + accounts.WithDelegationStake(0), + accounts.WithFixedCost(0), + accounts.WithStakeEndEpoch(iotago.MaxEpochIndex), + accounts.WithLatestSupportedProtocolVersionAndHash(model.VersionAndHash{Version: 3, Hash: hash2}), + ), ts.Nodes()...) // update the latest supported version for the remaining nodes ts.Node("nodeA").SetHighestSupportedVersion(5) @@ -199,24 +195,23 @@ func Test_Upgrade_Signaling(t *testing.T) { ts.IssueBlocksAtEpoch("", 1, 4, "7.3", ts.Nodes(), true, false) - ts.AssertAccountData(&accounts.AccountData{ - ID: ts.Node("nodeA").Validator.AccountData.ID, - Credits: &accounts.BlockIssuanceCredits{Value: iotago.MaxBlockIssuanceCredits / 2, UpdateSlot: 0}, - ExpirySlot: iotago.MaxSlotIndex, - OutputID: ts.AccountOutput("Genesis:1").ID, - BlockIssuerKeys: ts.Node("nodeA").Validator.BlockIssuerKeys(), - ValidatorStake: mock.MinValidatorAccountAmount(ts.API.ProtocolParameters()), - DelegationStake: 0, - FixedCost: 0, - StakeEndEpoch: iotago.MaxEpochIndex, - LatestSupportedProtocolVersionAndHash: model.VersionAndHash{Version: 5, Hash: hash1}, - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(ts.Node("nodeA").Validator.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0)), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithOutputID(ts.AccountOutput("Genesis:1").ID), + accounts.WithBlockIssuerKeys(ts.Node("nodeA").Validator.BlockIssuerKeys()...), + accounts.WithValidatorStake(mock.MinValidatorAccountAmount(ts.API.ProtocolParameters())), + accounts.WithDelegationStake(0), + accounts.WithFixedCost(0), + accounts.WithStakeEndEpoch(iotago.MaxEpochIndex), + accounts.WithLatestSupportedProtocolVersionAndHash(model.VersionAndHash{Version: 5, Hash: hash1}), + ), ts.Nodes()...) // check that rollback is correct pastAccounts, err := ts.Node("nodeA").Protocol.Engines.Main.Get().Ledger.PastAccounts(iotago.AccountIDs{ts.Node("nodeA").Validator.AccountData.ID}, 7) require.NoError(t, err) require.Contains(t, pastAccounts, ts.Node("nodeA").Validator.AccountData.ID) - require.Equal(t, model.VersionAndHash{Version: 4, Hash: hash2}, pastAccounts[ts.Node("nodeA").Validator.AccountData.ID].LatestSupportedProtocolVersionAndHash) + require.Equal(t, model.VersionAndHash{Version: 4, Hash: hash2}, pastAccounts[ts.Node("nodeA").Validator.AccountData.ID].LatestSupportedProtocolVersionAndHash()) ts.IssueBlocksAtEpoch("", 2, 4, "15.3", ts.Nodes(), true, false) ts.IssueBlocksAtEpoch("", 3, 4, "23.3", ts.Nodes(), true, false) @@ -364,31 +359,29 @@ func Test_Upgrade_Signaling(t *testing.T) { }, ts.Nodes()...) // check account data at the end of the test - ts.AssertAccountData(&accounts.AccountData{ - ID: ts.Node("nodeA").Validator.AccountData.ID, - Credits: &accounts.BlockIssuanceCredits{Value: iotago.MaxBlockIssuanceCredits / 2, UpdateSlot: 0}, - ExpirySlot: iotago.MaxSlotIndex, - OutputID: ts.AccountOutput("Genesis:1").ID, - BlockIssuerKeys: ts.Node("nodeA").Validator.BlockIssuerKeys(), - ValidatorStake: mock.MinValidatorAccountAmount(ts.API.ProtocolParameters()), - DelegationStake: 0, - FixedCost: 0, - StakeEndEpoch: iotago.MaxEpochIndex, - LatestSupportedProtocolVersionAndHash: model.VersionAndHash{Version: 5, Hash: hash1}, - }, ts.Nodes()...) - - ts.AssertAccountData(&accounts.AccountData{ - ID: ts.Node("nodeD").Validator.AccountData.ID, - Credits: &accounts.BlockIssuanceCredits{Value: iotago.MaxBlockIssuanceCredits / 2, UpdateSlot: 0}, - ExpirySlot: iotago.MaxSlotIndex, - OutputID: ts.AccountOutput("Genesis:4").ID, - BlockIssuerKeys: ts.Node("nodeD").Validator.BlockIssuerKeys(), - ValidatorStake: mock.MinValidatorAccountAmount(ts.API.ProtocolParameters()), - DelegationStake: 0, - FixedCost: 0, - StakeEndEpoch: iotago.MaxEpochIndex, - LatestSupportedProtocolVersionAndHash: model.VersionAndHash{Version: 5, Hash: hash2}, - }, ts.Nodes()...) + ts.AssertAccountData(accounts.NewAccountData(ts.Node("nodeA").Validator.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0)), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithOutputID(ts.AccountOutput("Genesis:1").ID), + accounts.WithBlockIssuerKeys(ts.Node("nodeA").Validator.BlockIssuerKeys()...), + accounts.WithValidatorStake(mock.MinValidatorAccountAmount(ts.API.ProtocolParameters())), + accounts.WithDelegationStake(0), + accounts.WithFixedCost(0), + accounts.WithStakeEndEpoch(iotago.MaxEpochIndex), + accounts.WithLatestSupportedProtocolVersionAndHash(model.VersionAndHash{Version: 5, Hash: hash1}), + ), ts.Nodes()...) + + ts.AssertAccountData(accounts.NewAccountData(ts.Node("nodeD").Validator.AccountData.ID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0)), + accounts.WithExpirySlot(iotago.MaxSlotIndex), + accounts.WithOutputID(ts.AccountOutput("Genesis:4").ID), + accounts.WithBlockIssuerKeys(ts.Node("nodeD").Validator.BlockIssuerKeys()...), + accounts.WithValidatorStake(mock.MinValidatorAccountAmount(ts.API.ProtocolParameters())), + accounts.WithDelegationStake(0), + accounts.WithFixedCost(0), + accounts.WithStakeEndEpoch(iotago.MaxEpochIndex), + accounts.WithLatestSupportedProtocolVersionAndHash(model.VersionAndHash{Version: 5, Hash: hash2}), + ), ts.Nodes()...) } // TODO: these node start to warpsync and don't manage to catch up diff --git a/pkg/testsuite/accounts.go b/pkg/testsuite/accounts.go index 57ccbd8de..d89ac7693 100644 --- a/pkg/testsuite/accounts.go +++ b/pkg/testsuite/accounts.go @@ -22,16 +22,16 @@ func (t *TestSuite) AssertAccountStake(accountID iotago.AccountID, validatorStak return ierrors.Errorf("AssertAccountData: %s: account %s does not exist with latest committed slot %d", node.Name, accountID, node.Protocol.Engines.Main.Get().SyncManager.LatestCommitment().Slot()) } - if accountID != actualAccountData.ID { - return ierrors.Errorf("AssertAccountData: %s: expected %s, got %s", node.Name, accountID, actualAccountData.ID) + if accountID != actualAccountData.ID() { + return ierrors.Errorf("AssertAccountData: %s: expected %s, got %s", node.Name, accountID, actualAccountData.ID()) } - if validatorStake != actualAccountData.ValidatorStake { - return ierrors.Errorf("AssertAccountData: %s: accountID %s expected validator stake %d, got %d", node.Name, accountID, validatorStake, actualAccountData.ValidatorStake) + if validatorStake != actualAccountData.ValidatorStake() { + return ierrors.Errorf("AssertAccountData: %s: accountID %s expected validator stake %d, got %d", node.Name, accountID, validatorStake, actualAccountData.ValidatorStake()) } - if delegationStake != actualAccountData.DelegationStake { - return ierrors.Errorf("AssertAccountData: %s: accountID %s expected delegation stake %d, got %d", node.Name, accountID, delegationStake, actualAccountData.DelegationStake) + if delegationStake != actualAccountData.DelegationStake() { + return ierrors.Errorf("AssertAccountData: %s: accountID %s expected delegation stake %d, got %d", node.Name, accountID, delegationStake, actualAccountData.DelegationStake()) } return nil @@ -43,56 +43,56 @@ func (t *TestSuite) AssertAccountStake(accountID iotago.AccountID, validatorStak func (t *TestSuite) AssertAccountData(accountData *accounts.AccountData, nodes ...*mock.Node) { for _, node := range nodes { t.Eventually(func() error { - actualAccountData, exists, err := node.Protocol.Engines.Main.Get().Ledger.Account(accountData.ID, node.Protocol.Engines.Main.Get().SyncManager.LatestCommitment().Slot()) + actualAccountData, exists, err := node.Protocol.Engines.Main.Get().Ledger.Account(accountData.ID(), node.Protocol.Engines.Main.Get().SyncManager.LatestCommitment().Slot()) if err != nil { return ierrors.Wrap(err, "AssertAccountData: failed to load account data") } if !exists { - return ierrors.Errorf("AssertAccountData: %s: account %s does not exist with latest committed slot %d", node.Name, accountData.ID, node.Protocol.Engines.Main.Get().SyncManager.LatestCommitment().Slot()) + return ierrors.Errorf("AssertAccountData: %s: account %s does not exist with latest committed slot %d", node.Name, accountData.ID(), node.Protocol.Engines.Main.Get().SyncManager.LatestCommitment().Slot()) } - if accountData.ID != actualAccountData.ID { - return ierrors.Errorf("AssertAccountData: %s: expected %s, got %s", node.Name, accountData.ID, actualAccountData.ID) + if accountData.ID() != actualAccountData.ID() { + return ierrors.Errorf("AssertAccountData: %s: expected %s, got %s", node.Name, accountData.ID(), actualAccountData.ID()) } - if accountData.Credits.Value != actualAccountData.Credits.Value { - return ierrors.Errorf("AssertAccountData: %s: accountID %s expected credits value %d, got %d", node.Name, accountData.ID, accountData.Credits.Value, actualAccountData.Credits.Value) + if accountData.Credits().Value() != actualAccountData.Credits().Value() { + return ierrors.Errorf("AssertAccountData: %s: accountID %s expected credits value %d, got %d", node.Name, accountData.ID(), accountData.Credits().Value(), actualAccountData.Credits().Value()) } - if accountData.Credits.UpdateSlot != actualAccountData.Credits.UpdateSlot { - return ierrors.Errorf("AssertAccountData: %s: accountID %s expected credits update time %d, got %d", node.Name, accountData.ID, accountData.Credits.UpdateSlot, actualAccountData.Credits.UpdateSlot) + if accountData.Credits().UpdateSlot() != actualAccountData.Credits().UpdateSlot() { + return ierrors.Errorf("AssertAccountData: %s: accountID %s expected credits update time %d, got %d", node.Name, accountData.ID(), accountData.Credits().UpdateSlot(), actualAccountData.Credits().UpdateSlot()) } - if accountData.OutputID != actualAccountData.OutputID { - return ierrors.Errorf("AssertAccountData: %s: accountID %s expected output %s, got %s", node.Name, accountData.ID, accountData.OutputID, actualAccountData.OutputID) + if accountData.OutputID() != actualAccountData.OutputID() { + return ierrors.Errorf("AssertAccountData: %s: accountID %s expected output %s, got %s", node.Name, accountData.ID(), accountData.OutputID(), actualAccountData.OutputID()) } - if accountData.ExpirySlot != actualAccountData.ExpirySlot { - return ierrors.Errorf("AssertAccountData: %s: accountID %s expected expiry slot %s, got %s", node.Name, accountData.ID, accountData.ExpirySlot, actualAccountData.ExpirySlot) + if accountData.ExpirySlot() != actualAccountData.ExpirySlot() { + return ierrors.Errorf("AssertAccountData: %s: accountID %s expected expiry slot %s, got %s", node.Name, accountData.ID(), accountData.ExpirySlot(), actualAccountData.ExpirySlot()) } - if !assert.Equal(t.fakeTesting, accountData.BlockIssuerKeys, actualAccountData.BlockIssuerKeys) { - return ierrors.Errorf("AssertAccountData: %s: accountID %s expected pub keys %s, got %s", node.Name, accountData.ID, accountData.BlockIssuerKeys, actualAccountData.BlockIssuerKeys) + if !assert.Equal(t.fakeTesting, accountData.BlockIssuerKeys(), actualAccountData.BlockIssuerKeys()) { + return ierrors.Errorf("AssertAccountData: %s: accountID %s expected pub keys %s, got %s", node.Name, accountData.ID(), accountData.BlockIssuerKeys(), actualAccountData.BlockIssuerKeys()) } - if accountData.StakeEndEpoch != actualAccountData.StakeEndEpoch { - return ierrors.Errorf("AssertAccountData: %s: accountID %s expected stake end epoch %s, got %s", node.Name, accountData.ID, accountData.StakeEndEpoch, actualAccountData.StakeEndEpoch) + if accountData.StakeEndEpoch() != actualAccountData.StakeEndEpoch() { + return ierrors.Errorf("AssertAccountData: %s: accountID %s expected stake end epoch %s, got %s", node.Name, accountData.ID(), accountData.StakeEndEpoch(), actualAccountData.StakeEndEpoch()) } - if accountData.FixedCost != actualAccountData.FixedCost { - return ierrors.Errorf("AssertAccountData: %s: accountID %s expected fixed cost %d, got %d", node.Name, accountData.ID, accountData.FixedCost, actualAccountData.FixedCost) + if accountData.FixedCost() != actualAccountData.FixedCost() { + return ierrors.Errorf("AssertAccountData: %s: accountID %s expected fixed cost %d, got %d", node.Name, accountData.ID(), accountData.FixedCost(), actualAccountData.FixedCost()) } - if accountData.ValidatorStake != actualAccountData.ValidatorStake { - return ierrors.Errorf("AssertAccountData: %s: accountID %s expected validator stake %d, got %d", node.Name, accountData.ID, accountData.ValidatorStake, actualAccountData.ValidatorStake) + if accountData.ValidatorStake() != actualAccountData.ValidatorStake() { + return ierrors.Errorf("AssertAccountData: %s: accountID %s expected validator stake %d, got %d", node.Name, accountData.ID(), accountData.ValidatorStake(), actualAccountData.ValidatorStake()) } - if accountData.DelegationStake != actualAccountData.DelegationStake { - return ierrors.Errorf("AssertAccountData: %s: accountID %s expected delegation stake %d, got %d", node.Name, accountData.ID, accountData.DelegationStake, actualAccountData.DelegationStake) + if accountData.DelegationStake() != actualAccountData.DelegationStake() { + return ierrors.Errorf("AssertAccountData: %s: accountID %s expected delegation stake %d, got %d", node.Name, accountData.ID(), accountData.DelegationStake(), actualAccountData.DelegationStake()) } - if accountData.LatestSupportedProtocolVersionAndHash != actualAccountData.LatestSupportedProtocolVersionAndHash { - return ierrors.Errorf("AssertAccountData: %s: accountID %s expected latest supported protocol version and hash %d, got %d", node.Name, accountData.ID, accountData.LatestSupportedProtocolVersionAndHash, actualAccountData.LatestSupportedProtocolVersionAndHash) + if accountData.LatestSupportedProtocolVersionAndHash() != actualAccountData.LatestSupportedProtocolVersionAndHash() { + return ierrors.Errorf("AssertAccountData: %s: accountID %s expected latest supported protocol version and hash %d, got %d", node.Name, accountData.ID(), accountData.LatestSupportedProtocolVersionAndHash(), actualAccountData.LatestSupportedProtocolVersionAndHash()) } return nil diff --git a/pkg/testsuite/snapshotcreator/snapshotcreator.go b/pkg/testsuite/snapshotcreator/snapshotcreator.go index 5aaf7fe33..fcc38bf53 100644 --- a/pkg/testsuite/snapshotcreator/snapshotcreator.go +++ b/pkg/testsuite/snapshotcreator/snapshotcreator.go @@ -78,20 +78,19 @@ func CreateSnapshot(opts ...options.Option[Options]) error { panic("block issuer key must be of type ed25519") } accountID := blockIssuerKeyEd25519.PublicKeyHash - committeeAccountsData = append(committeeAccountsData, &accounts.AccountData{ - ID: accountID, - Credits: &accounts.BlockIssuanceCredits{Value: snapshotAccountDetails.BlockIssuanceCredits, UpdateSlot: 0}, - ExpirySlot: snapshotAccountDetails.ExpirySlot, + committeeAccountsData = append(committeeAccountsData, accounts.NewAccountData(accountID, + accounts.WithCredits(accounts.NewBlockIssuanceCredits(snapshotAccountDetails.BlockIssuanceCredits, 0)), + accounts.WithExpirySlot(snapshotAccountDetails.ExpirySlot), // OutputID is not used when selecting an initial committee, // so it's safe to use an empty one that is different from the actual outputID in the UTXO Ledger. - OutputID: iotago.OutputID{}, - BlockIssuerKeys: iotago.BlockIssuerKeys{snapshotAccountDetails.IssuerKey}, - ValidatorStake: snapshotAccountDetails.StakedAmount, - DelegationStake: 0, - FixedCost: snapshotAccountDetails.FixedCost, - StakeEndEpoch: snapshotAccountDetails.StakingEndEpoch, - LatestSupportedProtocolVersionAndHash: model.VersionAndHash{}, - }) + accounts.WithOutputID(iotago.OutputID{}), + accounts.WithBlockIssuerKeys(snapshotAccountDetails.IssuerKey), + accounts.WithValidatorStake(snapshotAccountDetails.StakedAmount), + accounts.WithDelegationStake(0), + accounts.WithFixedCost(snapshotAccountDetails.FixedCost), + accounts.WithStakeEndEpoch(snapshotAccountDetails.StakingEndEpoch), + accounts.WithLatestSupportedProtocolVersionAndHash(model.VersionAndHash{}), + )) } } diff --git a/pkg/testsuite/sybilprotection.go b/pkg/testsuite/sybilprotection.go index 0bca507df..5209a97e1 100644 --- a/pkg/testsuite/sybilprotection.go +++ b/pkg/testsuite/sybilprotection.go @@ -112,7 +112,7 @@ func (t *TestSuite) AssertSybilProtectionCandidates(epoch iotago.EpochIndex, exp t.Eventually(func() error { candidates, err := node.Protocol.Engines.Main.Get().SybilProtection.EligibleValidators(epoch) candidateIDs := lo.Map(candidates, func(candidate *accounts.AccountData) iotago.AccountID { - return candidate.ID + return candidate.ID() }) if err != nil { return ierrors.Wrapf(err, "AssertSybilProtectionCandidates: %s: failed to get eligible validators in epoch %d", node.Name, epoch)