From d0abed06a0a31c7621bd725019ca3eb81d8eb298 Mon Sep 17 00:00:00 2001 From: Alexander Sporn Date: Mon, 9 Oct 2023 15:08:28 +0200 Subject: [PATCH 1/2] Always load output over the mempool. Only load the spents from the ledger if the mempool returns an error that is was already spent. --- pkg/protocol/engine/ledger/ledger/ledger.go | 27 +++++++++------------ 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/pkg/protocol/engine/ledger/ledger/ledger.go b/pkg/protocol/engine/ledger/ledger/ledger.go index bddd760d4..e783a00ee 100644 --- a/pkg/protocol/engine/ledger/ledger/ledger.go +++ b/pkg/protocol/engine/ledger/ledger/ledger.go @@ -255,25 +255,22 @@ func (l *Ledger) Output(outputID iotago.OutputID) (*utxoledger.Output, error) { } func (l *Ledger) OutputOrSpent(outputID iotago.OutputID) (*utxoledger.Output, *utxoledger.Spent, error) { - l.utxoLedger.ReadLockLedger() - - unspent, err := l.utxoLedger.IsOutputIDUnspentWithoutLocking(outputID) + output, err := l.Output(outputID) if err != nil { - l.utxoLedger.ReadUnlockLedger() - return nil, nil, err - } - - if !unspent { - spent, err := l.utxoLedger.ReadSpentForOutputIDWithoutLocking(outputID) - l.utxoLedger.ReadUnlockLedger() + if ierrors.Is(iotago.ErrInputAlreadySpent, err) { + l.utxoLedger.ReadLockLedger() + defer l.utxoLedger.ReadUnlockLedger() - return nil, spent, err - } + spent, err := l.utxoLedger.ReadSpentForOutputIDWithoutLocking(outputID) + if err != nil { + return nil, nil, err + } - l.utxoLedger.ReadUnlockLedger() + return nil, spent, err + } - // l.Output might read-lock the ledger again if the mem-pool needs to resolve the output, so we cannot be in a locked state - output, err := l.Output(outputID) + return nil, nil, err + } return output, nil, err } From e8db2a6e772eff527ed969a0cc22317c6ad73c9c Mon Sep 17 00:00:00 2001 From: Alexander Sporn Date: Mon, 9 Oct 2023 15:36:10 +0200 Subject: [PATCH 2/2] Refactored OutputOrSpent to also check if the output was already spent in the mempool --- pkg/protocol/engine/ledger/ledger/ledger.go | 36 +++++++++++++-------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/pkg/protocol/engine/ledger/ledger/ledger.go b/pkg/protocol/engine/ledger/ledger/ledger.go index e783a00ee..62ff01004 100644 --- a/pkg/protocol/engine/ledger/ledger/ledger.go +++ b/pkg/protocol/engine/ledger/ledger/ledger.go @@ -231,31 +231,36 @@ func (l *Ledger) PastAccounts(accountIDs iotago.AccountIDs, targetIndex iotago.S return l.accountsLedger.PastAccounts(accountIDs, targetIndex) } -func (l *Ledger) Output(outputID iotago.OutputID) (*utxoledger.Output, error) { - stateWithMetadata, err := l.memPool.StateMetadata(outputID.UTXOInput()) - if err != nil { - return nil, err - } - - switch castState := stateWithMetadata.State().(type) { +func (l *Ledger) outputFromState(state mempool.State) *utxoledger.Output { + switch output := state.(type) { case *utxoledger.Output: - if castState.SlotBooked() == 0 { - txWithMetadata, exists := l.memPool.TransactionMetadata(outputID.TransactionID()) + // If this output was not booked yet, then it came directly from the mempool, so we need to set the earliest attachment and the booking slot + if output.SlotBooked() == 0 { + txWithMetadata, exists := l.memPool.TransactionMetadata(output.OutputID().TransactionID()) if exists { earliestAttachment := txWithMetadata.EarliestIncludedAttachment() - return utxoledger.CreateOutput(l.apiProvider, castState.OutputID(), earliestAttachment, earliestAttachment.Slot(), castState.Output()), nil + return utxoledger.CreateOutput(l.apiProvider, output.OutputID(), earliestAttachment, earliestAttachment.Slot(), output.Output()) } } - return castState, nil + return output default: panic("unexpected State type") } } +func (l *Ledger) Output(outputID iotago.OutputID) (*utxoledger.Output, error) { + stateWithMetadata, err := l.memPool.StateMetadata(outputID.UTXOInput()) + if err != nil { + return nil, err + } + + return l.outputFromState(stateWithMetadata.State()), nil +} + func (l *Ledger) OutputOrSpent(outputID iotago.OutputID) (*utxoledger.Output, *utxoledger.Spent, error) { - output, err := l.Output(outputID) + stateWithMetadata, err := l.memPool.StateMetadata(outputID.UTXOInput()) if err != nil { if ierrors.Is(iotago.ErrInputAlreadySpent, err) { l.utxoLedger.ReadLockLedger() @@ -272,7 +277,12 @@ func (l *Ledger) OutputOrSpent(outputID iotago.OutputID) (*utxoledger.Output, *u return nil, nil, err } - return output, nil, err + spender, spent := stateWithMetadata.AcceptedSpender() + if spent { + return nil, utxoledger.NewSpent(l.outputFromState(stateWithMetadata.State()), spender.ID(), spender.ID().Slot()), nil + } + + return l.outputFromState(stateWithMetadata.State()), nil, nil } func (l *Ledger) ForEachUnspentOutput(consumer func(output *utxoledger.Output) bool) error {