Skip to content

tapfreighter: input selection stays unbounded for sends with pinned inputs #2257

Description

@GeorgeTsagk

Summary

#2252 bounded input selection so that funding a send lists coins in pages of
descending amounts and stops once the target is covered. Sends that pin their
inputs are excluded from that: they still list every eligible coin the node
holds. On a node with 2461 assets, coin selection for a pinned-input send takes
1.53s against 68ms for the bounded path.

Where

The prev-ID filter is applied in Go, after the query has already built an
AnchoredCommitment for every eligible coin:

// tapdb/assets_store.go, ListEligibleCoins
selectedCommitments, err := a.queryCommitments(ctx, assetFilter)
if err != nil {
        return nil, fmt.Errorf("unable to query commitments: %w", err)
}

// If we want to restrict on specific inputs, we do the filtering now.
if len(constraints.PrevIDs) > 0 {
        selectedCommitments = filterCommitmentsByPrevIDs(
                selectedCommitments, constraints.PrevIDs,
        )

So a bounded page could contain none of the pinned inputs, and #2252 rejects
the combination rather than returning a wrong answer:

if constraints.CoinLimit > 0 && len(constraints.PrevIDs) > 0 {
        return nil, fmt.Errorf("coin limit cannot be combined with " +
                "specific prev IDs")
}

CoinSelect.listCoins correspondingly sets the page size to zero when
PrevIDs is set, which is what makes the listing unbounded.

Pinned inputs reach coin selection from FundVirtualPsbt and the other funding
RPCs, via FundingDescriptor.PrevIDs.

Measurement

Same node and harness used for #2249 and #2250: a copy of a real regtest node,
sqlite, 2461 assets, dominant asset group holding 621 coins.

  operation                        before #2251/#2252    on current main
  SelectCoins, pinned prev IDs                16.66s              1.53s
  SelectCoins, 1-unit group send              16.57s               68ms

The pinned path is 10x better than it was, purely from the query batching in
#2251, but it is still linear in total holdings where the bounded path is not.

Possible direction

Push the prev-ID set into the SQL filter, so the query returns only the pinned
coins instead of everything followed by a Go-side filter. Each prev ID is an
(anchor outpoint, asset ID, script key) triple, all of which the query already
joins on.

A keyset cursor over (amount, assets.asset_id) in place of LIMIT/OFFSET would
also let pinned listings be paged, and would additionally remove the unbounded
retry #2252 needs when a multi-page listing falls short. See the discussion on
#2252 for that variant.

Environment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status
    🆕 New

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions