Documentation
¶
Overview ¶
Package acceptance decides how much evidence a payment needs before the receiver acts on it, and gathers that evidence.
A payment is worth what it pays only once it is mined: until then its payer can spend the same coins elsewhere. Waiting for a block on every payment makes every paid answer as slow as a block, while acting on a payment at once makes the receiver carry every double spend. The rule this package applies is a value discriminator:
- a payment at or below a threshold takes the fast path: its ancestry verifies to mined proofs against the receiver's headers, it is well formed, final and pays what it must, the receiver broadcasts it itself, the network's broadcaster answers it accepted with no double-spend status, and the node's spend view names no other spender, through an optional conflict-watch window;
- a payment above the threshold is held for confirmation: it is broadcast the same way, and acted on only once it is mined with a merkle proof that verifies against the receiver's headers;
- a payment that fails a check is refused.
The threshold is in satoshis. It is a static value, or one converted from a fiat amount through a PriceSource the application supplies; a price that is unknown, zero or stale makes the threshold zero, so every payment is held: the package fails toward waiting, never toward trusting.
Many small payments add up, so the fast path is also bounded per payer and in total: an Exposure counts the satoshis taken on the fast path and not yet mined within a rolling window, and a payment that would carry either sum past its limit is held instead. The receiver decides, always: a payer may ask to be held, never to be fast.
A payment taken on the fast path is watched until it mines (Monitor). One that the network refuses, or whose input the node shows spent by another transaction, is reported to the application's hook and its payer flagged, so that the payer's later payments are held for confirmation. What else follows (revoking a service, telling an operator) is the application's.
The zero Policy holds every payment: a receiver that configures nothing waits for a block, as it did before this package.
Index ¶
- Constants
- Variables
- type Ask
- type CachedPrice
- type Decision
- type Event
- type EventKind
- type Exposure
- func (x *Exposure) Charge(payer, txid string, sats uint64, at time.Time)
- func (x *Exposure) Flag(payer, why string)
- func (x *Exposure) Flagged(payer string) (string, bool)
- func (x *Exposure) Release(txid string)
- func (x *Exposure) Unflag(payer string)
- func (x *Exposure) Unmined(payer string, window time.Duration) (forPayer, total uint64)
- type Monitor
- type Output
- type Payment
- type Policy
- type Price
- type PriceSource
- type ProofSource
- type Reason
- type Request
- type SpendView
- type StaticPrice
- type StatusSource
- type Verdict
- type Verifier
Constants ¶
const ( DefaultThresholdSats uint64 = 25_000_000 DefaultThresholdCents uint64 = 2_500 DefaultWindow = time.Hour DefaultTotalFactor uint64 = 10 DefaultWait = 10 * time.Second DefaultPoll = 500 * time.Millisecond DefaultMaxPriceAge = time.Hour // SatsPerCoin is satoshis in one coin. SatsPerCoin uint64 = 100_000_000 )
Defaults DefaultPolicy uses. DefaultThresholdSats is 25 US dollars at a price of 100 US dollars a coin, set high on purpose: if the price is lower, the threshold is worth less than 25 dollars, which is the safe side. Review it against the price on a schedule.
Variables ¶
var ErrNoHeaders = errors.New("acceptance: no headers to verify the payment against")
ErrNoHeaders is a Verifier with no headers to check a payment against.
var ErrNoPrice = errors.New("acceptance: no price")
ErrNoPrice is a source with no price to give.
Functions ¶
This section is empty.
Types ¶
type Ask ¶
type Ask int
Ask is what a payer asks of the receiver. A payer may ask to be held; it may ask to be fast, which changes nothing: the receiver decides.
type CachedPrice ¶
type CachedPrice struct {
Source PriceSource
TTL time.Duration
// contains filtered or unexported fields
}
CachedPrice asks Source at most once per TTL and answers its last good price between asks. A failed ask is not cached: the last good price is answered, with its own date, so a Policy's MaxPriceAge still ends it, and with no good price yet the error is answered.
type Decision ¶
type Decision int
Decision is what a receiver does with a payment.
const ( // Refuse is a payment that fails a check: the receiver does not act on // it. It is the zero Decision, so a Verdict nobody filled in refuses. Refuse Decision = iota // Fast is a payment the receiver acts on now, on the fast path's // evidence, and watches until it mines. Fast // Hold is a payment the receiver acts on only once it is mined with a // proof against its headers. Hold )
type Event ¶
type Event struct {
Kind EventKind
Txid string
Payer string
Sats uint64
Height uint32
// Why is the evidence in words; it may hold text another party wrote.
Why string
}
Event is a payment taken on the fast path that the Monitor stopped watching, and why.
type EventKind ¶
type EventKind int
EventKind is what became of a payment taken on the fast path.
const ( // Confirmed is a payment mined with a proof against the headers: it is // money, and no longer counts against its payer. Confirmed EventKind = iota + 1 // DoubleSpent is a payment whose input the node shows spent by another // transaction: it will never mine. DoubleSpent // Refused is a payment a broadcaster answers REJECTED. Refused // Unmined is a payment not mined within the Monitor's MaxAge. It may // still mine; it is reported once and no longer watched. Unmined )
type Exposure ¶
type Exposure struct {
// contains filtered or unexported fields
}
Exposure counts what the fast path has taken and not seen mined, per payer and in total, and the payers whose fast payment was double spent. It is safe for concurrent use. It is memory only: an application that restarts restores what it still watches with Charge and Flag.
func (*Exposure) Charge ¶
Charge counts a fast payment taken earlier, at the time it was taken: what an application restores after a restart.
func (*Exposure) Flag ¶
Flag marks a payer whose fast payment was double spent or refused: every later payment of theirs is held. why is kept for the verdict.
func (*Exposure) Release ¶
Release stops counting a payment: one decided fast and then not acted on, or one that has mined.
type Monitor ¶
type Monitor struct {
Verifier *Verifier
// MaxAge is how long a payment may stay unmined before it is reported
// Unmined; zero never reports it.
MaxAge time.Duration
// Hook receives each event, outside the Monitor's lock.
Hook func(Event)
// contains filtered or unexported fields
}
Monitor watches the payments the fast path took until each mines or is lost. A lost payment (DoubleSpent, Refused, Unmined) flags its payer in the Verifier's Exposure, so the payer's later payments are held, and is reported to Hook: revoking what it bought, or telling an operator, is the application's. Sweep is one pass and is idempotent, so it runs on a timer; Run runs it on one.
func (*Monitor) Sweep ¶
Sweep asks once after every watched payment and reports each that mined or was lost. It answers the events it reported.
func (*Monitor) Watch ¶
func (m *Monitor) Watch(tx *transaction.Transaction, payer string, sats uint64)
Watch starts watching a payment the fast path took.
type Output ¶
Output is one output a payment must hold: at index Vout, locked by Script (the key the receiver derived for the payment), at least Sats.
type Payment ¶
type Payment struct {
Tx *transaction.Transaction
Payer string
Pays []Output
Ask Ask
}
Payment is a payment offered to the receiver: the transaction, read from its BEEF so that its inputs carry their source transactions (guard first), the payer it is charged to, the outputs it must hold, and what the payer asked.
type Policy ¶
type Policy struct {
// ThresholdSats is the largest payment, in satoshis, the fast path
// takes. With ThresholdCents set too, the smaller of the two applies.
ThresholdSats uint64
// ThresholdCents is the threshold in US cents, converted through
// Price. A price that is unknown, zero or older than MaxPriceAge makes
// the threshold zero: every payment is held.
ThresholdCents uint64
Price PriceSource
MaxPriceAge time.Duration
// Window is how long a fast payment counts against its payer and the
// total once taken, unless it mines first; zero counts it until it
// mines or is released.
Window time.Duration
// PayerLimit bounds the satoshis one payer has on the fast path and
// not yet mined within Window; TotalLimit bounds them across every
// payer. A payment that would pass either is held. Zero allows
// nothing.
PayerLimit uint64
TotalLimit uint64
// Agree is how many status sources must answer a fast payment
// accepted; zero is one.
Agree int
// Wait bounds how long the fast path waits for that acceptance; zero
// is DefaultWait. Watch is how long it keeps watching for a conflict
// before it answers fast, zero for none; a Watch longer than Wait
// extends Wait. Poll paces both; zero is DefaultPoll.
Wait time.Duration
Watch time.Duration
Poll time.Duration
}
Policy is a receiver's rule for which payments are fast. The zero Policy holds every payment.
func DefaultPolicy ¶
func DefaultPolicy() Policy
DefaultPolicy is the recommended policy over a static threshold: 25 dollars at 100 dollars a coin, each payer bounded to one threshold and all payers to ten, within an hour, no watch window.
func (Policy) Decide ¶
Decide weighs a payment's value against the policy and the exposure already taken. On Fast it charges the payment to x at once, so two payments decided together cannot both pass a limit; a caller that then does not act on it calls x.Release. It looks at nothing but value: the checks are the Verifier's.
type Price ¶
Price is a coin's price in US cents, and when it was read. A zero At is a price with no date, which never goes stale: a static price an operator set and reviews.
type PriceSource ¶
PriceSource is where a threshold set in cents gets its price. The library names no live price service: an application wraps the one it trusts. Any error, or a zero price, makes the threshold zero, so every payment is held.
type ProofSource ¶
type ProofSource interface {
Proof(ctx context.Context, txid string) (*transaction.MerklePath, uint32, error)
}
ProofSource is where a mined transaction's proof is read (*nodeapi.Asset); an error is not mined yet, or not known.
type Reason ¶
type Reason string
Reason is why a Verdict is what it is: a short fixed label, fit for a metric.
const ( // Fast. ReasonAtOrBelow Reason = "at-or-below-threshold" ReasonMined Reason = "mined" // Hold. ReasonAboveThreshold Reason = "above-threshold" ReasonPriceUnknown Reason = "price-unknown" ReasonPriceStale Reason = "price-stale" ReasonPayerLimit Reason = "payer-limit" ReasonTotalLimit Reason = "total-limit" ReasonFlagged Reason = "payer-flagged" ReasonAskedHold Reason = "payer-asked-hold" ReasonNoExposure Reason = "no-exposure-ledger" ReasonNoBroadcast Reason = "no-broadcast-leg" ReasonBroadcastUnknown Reason = "broadcast-unconfirmed" ReasonNoVerdict Reason = "no-network-verdict" ReasonConflict Reason = "double-spend-attempted" ReasonSpendUnknown Reason = "spend-view-unknown" ReasonNoTxid Reason = "no-txid" ReasonAlreadyCharged Reason = "already-charged" // Refuse. ReasonNothing Reason = "pays-nothing" ReasonMalformed Reason = "malformed" ReasonUnderpaid Reason = "underpaid" ReasonWrongScript Reason = "wrong-script" ReasonNotFinal Reason = "not-final" ReasonOverspends Reason = "outputs-exceed-inputs" ReasonSPV Reason = "spv-failed" ReasonNetworkRefused Reason = "network-refused" ReasonDoubleSpent Reason = "double-spent" )
The reasons.
type Request ¶
Request is a payment as Decide weighs it: who pays, how much, and its txid, by which a fast payment is charged to its payer.
type SpendView ¶
SpendView is the node's view of an output (*nodeapi.Asset): the txid that spent it, "" with a nil error only when the node says it is unspent, and an error for any other answer.
type StatusSource ¶
type StatusSource interface {
Status(ctx context.Context, txid string) (*publish.ArcadeStatus, error)
}
StatusSource is a broadcaster's view of one transaction, as arcade's GET /tx/{txid} answers it (*publish.Arcade).
type Verdict ¶
type Verdict struct {
Decision Decision
Reason Reason
// Detail is the reason in words, for a log; it may hold text another
// party wrote (a broadcaster's answer), so filter it before a terminal.
Detail string
// Sats is what the payment pays the receiver, and Threshold the
// threshold it was held to (zero when it was never reached).
Sats uint64
Threshold uint64
}
Verdict is a decision on one payment and why.
type Verifier ¶
type Verifier struct {
Policy Policy
Exposure *Exposure
// Headers are the receiver's own headers, which every proof is checked
// against.
Headers chaintracker.ChainTracker
// Settler is the leg the receiver broadcasts on; without one, no
// payment is fast.
Settler publish.Settler
// Status are the broadcasters asked for the network's verdict; Policy
// .Agree of them must answer accepted. Spends, when set, is the node's
// spend view, which must name no other spender and must answer for
// every input. Proofs is where Confirm reads a proof.
Status []StatusSource
Spends SpendView
Proofs ProofSource
}
Verifier gathers a payment's evidence under a Policy.
func (*Verifier) Accept ¶
Accept checks a payment and decides on it:
- the checks every payment passes (Check): well formed, final, pays every Output, spends no more than it has, and its ancestry verifies to mined proofs against Headers;
- Decide on its value; a mined payment that verifies is Fast at once;
- the receiver broadcasts it, its unmined ancestors first, whatever the decision, so that a held payment mines too;
- for a Fast decision, the network's verdict: Agree status sources answer it accepted with no double-spend status and no competing transaction, and the spend view names no other spender of any input, through Policy.Watch.
A payment that loses its fast evidence for a reason that may pass (no verdict yet, a spend view that does not answer) is held; one the network refuses, or whose input is spent by another transaction, is refused. The error is only for a Verifier that cannot decide at all, or a context that ended.
func (*Verifier) Check ¶
Check is the checks every payment passes before its value is weighed. It answers what the payment pays the receiver, and a refusal, or nil when it passes.
func (*Verifier) Confirm ¶
func (v *Verifier) Confirm(ctx context.Context, tx *transaction.Transaction) (*transaction.MerklePath, uint32, error)
Confirm waits for tx to mine and answers its proof, checked against Headers. While it waits, an input the spend view shows spent by another transaction ends the wait with a *nodeapi.SpentError (errors.Is nodeapi.ErrDoubleSpent). ctx bounds the wait.