Skip to content

Commit

Permalink
Fix delegation stake change calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
PhilippGackstatter committed Oct 1, 2023
1 parent 9c6afcc commit 1e026aa
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 12 deletions.
32 changes: 22 additions & 10 deletions pkg/protocol/engine/ledger/ledger/ledger.go
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ func (l *Ledger) processCreatedAndConsumedAccountOutputs(stateDiff mempool.State
consumedAccounts = make(map[iotago.AccountID]*utxoledger.Output)
destroyedAccounts = ds.NewSet[iotago.AccountID]()

createdAccountDelegation := make(map[iotago.ChainID]*iotago.DelegationOutput)
newAccountDelegation := make(map[iotago.ChainID]*iotago.DelegationOutput)

stateDiff.CreatedStates().ForEachKey(func(outputID iotago.OutputID) bool {
createdOutput, errOutput := l.Output(outputID)
Expand All @@ -499,11 +499,17 @@ func (l *Ledger) processCreatedAndConsumedAccountOutputs(stateDiff mempool.State
}

createdAccounts[accountID] = createdOutput

case iotago.OutputDelegation:
// the delegation output was created => determine later if we need to add the stake to the validator
delegation, _ := createdOutput.Output().(*iotago.DelegationOutput)
createdAccountDelegation[delegation.DelegationID] = delegation
// The Delegation Output output was created or transitioned => determine later if we need to add the stake to the validator.
delegationOutput, _ := createdOutput.Output().(*iotago.DelegationOutput)
delegationID := delegationOutput.DelegationID
// Check if the output was newly created or if it was transitioned to delayed claiming.
// Zeroed Delegation ID => newly created.
// Non-Zero Delegation ID => delayed claiming transition.
if delegationID == iotago.EmptyDelegationID() {
delegationID = iotago.DelegationIDFromOutputID(outputID)
newAccountDelegation[delegationID] = delegationOutput
}

case iotago.OutputBasic:
// if a basic output is sent to an implicit account creation address, we need to create the account
Expand Down Expand Up @@ -545,13 +551,19 @@ func (l *Ledger) processCreatedAndConsumedAccountOutputs(stateDiff mempool.State

case iotago.OutputDelegation:
delegationOutput, _ := spentOutput.Output().(*iotago.DelegationOutput)
delegationID := delegationOutput.DelegationID
if delegationID == iotago.EmptyDelegationID() {
delegationID = iotago.DelegationIDFromOutputID(outputID)
}

// TODO: do we have a testcase that checks transitioning a delegation output twice in the same slot?
if _, createdDelegationExists := createdAccountDelegation[delegationOutput.DelegationID]; createdDelegationExists {
if _, createdDelegationExists := newAccountDelegation[delegationID]; createdDelegationExists {
// the delegation output was created and destroyed in the same slot => do not track the delegation as newly created
delete(createdAccountDelegation, delegationOutput.DelegationID)
} else {
// the delegation output was destroyed => subtract the stake from the validator account
delete(newAccountDelegation, delegationID)
} else if delegationOutput.DelegationID.Empty() {
// The Delegation Output was destroyed or transitioned to delayed claiming => subtract the stake from the validator account.
// We check for a non-zero Delegation ID so we don't remove the stake twice when the output was destroyed
// in delayed claiming, since we already subtract it when the output is transitioned.
accountDiff := getAccountDiff(accountDiffs, delegationOutput.ValidatorAddress.AccountID())
accountDiff.DelegationStakeChange -= int64(delegationOutput.DelegatedAmount)
}
Expand All @@ -567,7 +579,7 @@ func (l *Ledger) processCreatedAndConsumedAccountOutputs(stateDiff mempool.State
return true
})

for _, delegationOutput := range createdAccountDelegation {
for _, delegationOutput := range newAccountDelegation {
// the delegation output was newly created and not transitioned/destroyed => add the stake to the validator account
accountDiff := getAccountDiff(accountDiffs, delegationOutput.ValidatorAddress.AccountID())
accountDiff.DelegationStakeChange += int64(delegationOutput.DelegatedAmount)
Expand Down
5 changes: 3 additions & 2 deletions pkg/tests/accounts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ func Test_TransitionAccount(t *testing.T) {

latestParent = ts.CommitUntilSlot(block4Slot, activeNodes, block4)

// Transitioning to delayed claiming effectively removes the delegation, so we expect a negative delegation stake change.
ts.AssertAccountDiff(newAccountOutput.AccountID, block4Slot, &model.AccountDiff{
BICChange: 0,
PreviousUpdatedTime: 0,
Expand All @@ -280,7 +281,7 @@ func Test_TransitionAccount(t *testing.T) {
ValidatorStakeChange: 0,
StakeEndEpochChange: 0,
FixedCostChange: 0,
DelegationStakeChange: 0,
DelegationStakeChange: -int64(delegatedAmount),
}, false, ts.Nodes()...)

ts.AssertAccountData(&accounts.AccountData{
Expand All @@ -291,7 +292,7 @@ func Test_TransitionAccount(t *testing.T) {
BlockIssuerKeys: iotago.NewBlockIssuerKeys(newAccountBlockIssuerKey),
StakeEndEpoch: 10,
FixedCost: 421,
DelegationStake: iotago.BaseToken(delegatedAmount),
DelegationStake: iotago.BaseToken(0),
ValidatorStake: 10000,
}, ts.Nodes()...)

Expand Down

0 comments on commit 1e026aa

Please sign in to comment.