Skip to content

Commit

Permalink
Merge pull request #416 from iotaledger/feat/always-read-over-mempool
Browse files Browse the repository at this point in the history
Always load output over the mempool. Only load the spents from the ledger if the mempool returns an error that is was already spent.
  • Loading branch information
alexsporn authored Oct 9, 2023
2 parents 730fdfc + e8db2a6 commit 15da8e1
Showing 1 changed file with 33 additions and 26 deletions.
59 changes: 33 additions & 26 deletions pkg/protocol/engine/ledger/ledger/ledger.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,51 +231,58 @@ 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) OutputOrSpent(outputID iotago.OutputID) (*utxoledger.Output, *utxoledger.Spent, error) {
l.utxoLedger.ReadLockLedger()

unspent, err := l.utxoLedger.IsOutputIDUnspentWithoutLocking(outputID)
func (l *Ledger) Output(outputID iotago.OutputID) (*utxoledger.Output, error) {
stateWithMetadata, err := l.memPool.StateMetadata(outputID.UTXOInput())
if err != nil {
l.utxoLedger.ReadUnlockLedger()
return nil, nil, err
return nil, err
}

if !unspent {
spent, err := l.utxoLedger.ReadSpentForOutputIDWithoutLocking(outputID)
l.utxoLedger.ReadUnlockLedger()
return l.outputFromState(stateWithMetadata.State()), nil
}

return nil, spent, err
}
func (l *Ledger) OutputOrSpent(outputID iotago.OutputID) (*utxoledger.Output, *utxoledger.Spent, error) {
stateWithMetadata, err := l.memPool.StateMetadata(outputID.UTXOInput())
if err != nil {
if ierrors.Is(iotago.ErrInputAlreadySpent, err) {
l.utxoLedger.ReadLockLedger()
defer l.utxoLedger.ReadUnlockLedger()

spent, err := l.utxoLedger.ReadSpentForOutputIDWithoutLocking(outputID)
if err != nil {
return nil, nil, err
}

return nil, spent, err
}

l.utxoLedger.ReadUnlockLedger()
return nil, nil, 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)
spender, spent := stateWithMetadata.AcceptedSpender()
if spent {
return nil, utxoledger.NewSpent(l.outputFromState(stateWithMetadata.State()), spender.ID(), spender.ID().Slot()), nil
}

return output, nil, err
return l.outputFromState(stateWithMetadata.State()), nil, nil
}

func (l *Ledger) ForEachUnspentOutput(consumer func(output *utxoledger.Output) bool) error {
Expand Down

0 comments on commit 15da8e1

Please sign in to comment.