Documentation
¶
Overview ¶
Package reader asks overlay hosts the ls_borg questions and checks every answer itself (docs/spec.md sections 10.2, 12 and 13): a token's record, lock and field signature and its proof against the reader's own headers; a carrier by the host's own admission rules and by SPV through its funding parent; a sweep by its proof. A host is trusted for nothing: an answer is verified here or reported as refused, naming the host. An empty answer means only that the host holds nothing that answers.
A reader that asks several hosts compares their answers and reports a difference as such, never merging it silently: a host can withhold a token, a grant or a wrap, and only a comparison (or a root) shows it.
The package is importable by other modules at a tagged version (docs/packages.md names the supported part of it).
Index ¶
- Constants
- Variables
- func GroupHex(g [borgrec.GroupIDLen]byte) string
- func Hash(txid string) ([32]byte, error)
- func JoinSeqs(s []uint64) string
- func ParseGroup(s string) ([borgrec.GroupIDLen]byte, error)
- func Seq(n uint64) string
- func Short(txid string) string
- func SubjectID(beef []byte) string
- func VerifySweep(ctx context.Context, beef []byte, hall string, ...) ([]transaction.Outpoint, string, error)
- type Car
- type ChainSource
- type ChainView
- type Client
- func (c *Client) Ask(ctx context.Context, host string, q Query) ([]lookup.Output, error)
- func (c *Client) Carrier(ctx context.Context, beef []byte, hall string, want borgrec.TxKind) (*Car, error)
- func (c *Client) Chain(ctx context.Context, hall string, org []byte, chain string) (*ChainView, error)
- func (c *Client) GrantsOf(ctx context.Context, ov *OrgView, k []byte) ([]*Car, []Refusal, map[string]error)
- func (c *Client) Group(ctx context.Context, hall string, org []byte, group string) (*GroupView, error)
- func (c *Client) GroupsAll(ctx context.Context, hall string, org []byte) ([]string, map[string]error)
- func (c *Client) GroupsOf(ctx context.Context, hall string, org, k []byte) ([]string, map[string]error)
- func (c *Client) Held(ctx context.Context, host string, q Query, txid string) (bool, error)
- func (c *Client) Open(ctx context.Context, w wallet.Interface, me []byte, gv *GroupView, ...) Opened
- func (c *Client) Org(ctx context.Context, hall string, org []byte) (*OrgView, error)
- func (c *Client) Pages(ctx context.Context, host string, q Query, afterClass bool, ...) HostOutputs
- func (c *Client) Recipients(ctx context.Context, gv *GroupView, epoch uint32) (map[string]bool, []Refusal)
- func (c *Client) Roll(ctx context.Context, ov *OrgView) []RollAnswer
- func (c *Client) Sweep(ctx context.Context, beef []byte, hall string) ([]transaction.Outpoint, string, error)
- func (c *Client) Token(ctx context.Context, beef []byte, hall string, org []byte) (*Tok, error)
- func (c *Client) Wraps(ctx context.Context, gv *GroupView, epoch uint32) ([]*Car, []Refusal)
- type Floor
- type Fresh
- type GrantStatus
- type GroupView
- type HostOutputs
- type NoChainError
- type Opened
- type OrgView
- type Query
- type Refusal
- type RollAnswer
- type RollbackError
- type Spends
- type Terms
- type Tok
Constants ¶
const ChainOrg = "org"
ChainOrg is the chain member of a question about an organisation's own chain.
const DefaultMaxAnswer = limits.MaxAnswer
DefaultMaxAnswer is the most bytes a reader takes for one lookup answer when Client.MaxAnswer is zero: a full page of the largest objects the plane carries fits several times over.
Variables ¶
var ErrHost = errors.New("reader: the host refused the question")
ErrHost is a host that answered with an error: its words travel with it.
var ErrNoChain = errors.New("reader: no host answers this chain")
ErrNoChain is a chain no host answered.
var ErrNoHeaders = errors.New("reader: no header source; refusing to verify against a default")
ErrNoHeaders refuses to verify against no header source.
var ErrNoTerms = errors.New("reader: the host serves no terms document, so it charges for nothing")
ErrNoTerms is a host that serves no terms document: it charges for nothing.
var ErrOverfull = errors.New("reader: the host answered more outputs than a page holds")
ErrOverfull is an answer to one question that holds more outputs than a page of this contract may (pageOutputs): a host fault. Only the first pageOutputs are read, and the host is named.
var ErrTruncated = errors.New("reader: the host's answer was cut at the reader's bound (a page over the bound, a walk too long, or a host repeating itself); what it answered past the cut is not read")
ErrTruncated is a paged answer the reader cut (Pages): what the host answered past the cut is not read.
Functions ¶
func GroupHex ¶
func GroupHex(g [borgrec.GroupIDLen]byte) string
GroupHex is a group id as a question writes it.
func ParseGroup ¶
func ParseGroup(s string) ([borgrec.GroupIDLen]byte, error)
ParseGroup reads a group id from 32 lowercase hex digits.
func SubjectID ¶
SubjectID is the txid of a BEEF's subject: the one an Atomic BEEF names in its header, else the one the BEEF parses to, else a note that there is none.
func VerifySweep ¶
func VerifySweep(ctx context.Context, beef []byte, hall string, headers chaintracker.ChainTracker) ([]transaction.Outpoint, string, error)
VerifySweep checks an answered sweep: admitted by the host's rules (a mined transaction whose output 0 is funding-shaped) against the reader's own headers, and returns the outpoints it spends.
Types ¶
type Car ¶
type Car struct {
Txid string
Beef []byte
Tx *transaction.Transaction
// Carrier is the carrier by the host's own rules: its record, owner,
// commitment and funding outpoint.
Carrier *borgrec.Carrier
Kind borgrec.TxKind
// Context is the BRC-24 context the host answered with it.
Context []byte
}
Car is one answered carrier that verified.
func VerifyCarrier ¶
func VerifyCarrier(ctx context.Context, beef []byte, hall string, want borgrec.TxKind, headers chaintracker.ChainTracker) (*Car, error)
VerifyCarrier checks one answered carrier: the host's admission rules for hall (every carrier rule and the grant's content rules), SPV through its funding parent against headers, and its kind.
type ChainSource ¶ added in v0.1.1
type ChainSource interface {
MerkleProof(ctx context.Context, txid string) (*transaction.MerklePath, error)
}
ChainSource is a source of a mined transaction's current proof, by its txid: a node's asset API (nodeapi.Asset is one).
type ChainView ¶
type ChainView struct {
Hall string
Org []byte
Chain string // ChainOrg or a group id
// Tokens are the canonical chain, seq 0 to the latest.
Tokens []*Tok
// Statuses are the fold's status of each token (spec section 11.2).
Statuses []string
// HostLatest is the highest seq each host answered to the latest
// class; HostErr the hosts that could not be asked.
HostLatest map[string]uint64
HostErr map[string]error
// Lacks are, per host that answered, the canonical seqs it did not
// answer: a host behind, or withholding.
Lacks map[string][]uint64
// Refused are answered tokens that did not verify.
Refused []Refusal
// Forks are tokens that verified and do not continue the canonical
// chain: at a seq of it, or at the seq the walk stopped at.
Forks []*Tok
// Claimed are the verified tokens the hosts answered as the chain's
// latest.
Claimed []*Tok
// Replaced is set when a Floor remembered another token at a seq of
// the canonical chain than the one read now: its txid. A reorganisation
// displaced the remembered one, or the chain forked there.
Replaced string
}
ChainView is one chain as a reader established it from every host it asked (spec section 13): the canonical chain, which runs by spends from the first-mined seq 0 token, each token verified against the reader's headers, and how each host's answers compare with it.
func (*ChainView) Agree ¶
Agree reports whether every host that answered holds the whole canonical chain.
func (*ChainView) Beyond ¶ added in v0.1.1
Beyond are the verified tokens of the chain at a seq above the chain read, each once: a host answered them (as the latest, or at the seq the walk could not link), and no chain of predecessors reaches them from the first token. A token between was withheld, or the organisation signed a stray one; either way what was read may be an older state than the chain's latest, and a reader with no memory of its own has nothing else to tell it so.
type Client ¶
type Client struct {
// Hosts are the overlay hosts' base URLs, asked in order.
Hosts []string
// Headers is the reader's own header source. It is required: an answer
// is only as good as the headers it is checked against.
Headers chaintracker.ChainTracker
// Timeout bounds each request; HTTP, when set, is the client used.
Timeout time.Duration
HTTP *http.Client
// MaxPages bounds the pages one question reads per host.
MaxPages int
// MaxAnswer bounds the bytes taken for one answer; zero is
// DefaultMaxAnswer.
MaxAnswer int64
// Source, when set, is a chain source asked for a transaction's
// current proof when the proof a host stored no longer verifies
// against Headers (docs/spec.md section 11.5). Without one, such an
// answer is refused as unmined.
Source ChainSource
// Floor, when set, is the caller's memory of the chains it verified:
// Chain refuses hosts that answer an older state than it holds, and
// records what it verified (floor.go). Without one a reader has no
// memory, and a host set that withholds the latest tokens of a chain
// is believed.
Floor Floor
}
Client asks hosts and checks their answers.
func (*Client) Ask ¶
Ask asks one host one question and returns the outputs it answered. An answer over MaxAnswer bytes is an error, not read.
func (*Client) Carrier ¶ added in v0.1.1
func (c *Client) Carrier(ctx context.Context, beef []byte, hall string, want borgrec.TxKind) (*Car, error)
Carrier is VerifyCarrier against the client's headers, with a stale proof of the carrier's funding tree replaced by the chain source's current one.
func (*Client) Chain ¶
func (c *Client) Chain(ctx context.Context, hall string, org []byte, chain string) (*ChainView, error)
Chain establishes a chain of org's in hall: chain is ChainOrg or a group id. Every host is asked the latest class; then the chain is walked seq by seq from 0, every host asked each seq, so the reader folds the chain from seq 0 itself. A seq is asked for only once the one before it is on the chain, and the walk ends at the highest seq any host claimed as the latest or at the first seq nothing continues: the work is bounded by the chain itself, never by a seq a host, or a stray token of the organisation's, names. Verified tokens above where the walk ended are reported (Beyond).
With a Floor, hosts that answer the chain only below the seq the caller verified before are refused with a *RollbackError, and what is verified now is recorded.
func (*Client) GrantsOf ¶
func (c *Client) GrantsOf(ctx context.Context, ov *OrgView, k []byte) ([]*Car, []Refusal, map[string]error)
GrantsOf asks every host the member class for k and returns the grants that verified, each with whether its inclusion path verifies against the roll of the token it names.
func (*Client) Group ¶
func (c *Client) Group(ctx context.Context, hall string, org []byte, group string) (*GroupView, error)
Group establishes a group's chain and folds its members.
func (*Client) GroupsAll ¶
func (c *Client) GroupsAll(ctx context.Context, hall string, org []byte) ([]string, map[string]error)
GroupsAll asks every host the groups class for org (all pages).
func (*Client) GroupsOf ¶
func (c *Client) GroupsOf(ctx context.Context, hall string, org, k []byte) ([]string, map[string]error)
GroupsOf asks every host the member-of class for k (all pages) and returns the group ids any host answered.
func (*Client) Held ¶
Held asks whether host answers a question with an output whose subject is txid: a unicast publisher's confirmation that a host that admitted nothing holds the object (spec section 12).
func (*Client) Open ¶
func (c *Client) Open(ctx context.Context, w wallet.Interface, me []byte, gv *GroupView, epoch uint32) Opened
Open is the member's checks of spec section 6.4 for one epoch of a group, in order: the token that began the epoch, from the reader's own canonical chain; a wrap record that verifies and holds an entry for me; the wallet's decryption of it; the commitment. Every host is asked, and every wrap each answers is checked, after one has opened the key: a host that answers what does not verify is reported in Refused beside an honest one, never passed over. Host is the first host whose wrap opened the key; the key a later host's wrap opens is the same key (the commitment binds it).
func (*Client) Pages ¶
func (c *Client) Pages(ctx context.Context, host string, q Query, afterClass bool, cursor func(lookup.Output) string) HostOutputs
Pages asks one host a paged class, page after page, until a page is short. cursor gives the -after member for an output, or "" when it has none (which ends the walk). What one page and one walk hold is bounded here, whatever a host answers: a page over the contract's is cut, a walk over walkBytes stops, and a host that answers nothing past the cursor it was given ends the walk. Each is reported as Truncated, never accumulated.
func (*Client) Recipients ¶
func (c *Client) Recipients(ctx context.Context, gv *GroupView, epoch uint32) (map[string]bool, []Refusal)
Recipients asks every host the wraps class for an epoch (all pages) and returns every recipient of every wrap that verified against the epoch's token, for the member enumeration of spec section 13.
func (*Client) Roll ¶
func (c *Client) Roll(ctx context.Context, ov *OrgView) []RollAnswer
Roll asks every host the roll class (all pages), verifies every grant and computes the root of what each host answered: a root other than the token's means the host withheld or added a grant (spec section 13).
func (*Client) Sweep ¶ added in v0.1.1
func (c *Client) Sweep(ctx context.Context, beef []byte, hall string) ([]transaction.Outpoint, string, error)
Sweep is VerifySweep against the client's headers, with a stale stored proof replaced by the chain source's current one.
type Floor ¶ added in v0.1.1
type Floor interface {
// Seen is the latest token of the chain this caller verified before:
// ok is false when it remembers none. org is the organisation's key in
// hex, chain ChainOrg or a group id.
Seen(hall, org, chain string) (seq uint64, txid string, ok bool)
// See records the latest token of the chain verified now.
See(hall, org, chain string, seq uint64, txid string)
}
Floor is a caller's memory of the chains it verified: for each chain, the seq and txid of the latest token a read of it reached. Chain reads it before it returns and raises it after. An implementation persists what See is given before the caller acts on the chain read, and never lowers a seq on its own: forgetting is the caller's decision.
type Fresh ¶
type Fresh struct {
Tok *Tok
// Unspent is the positive answer; By the spender when it is spent.
Unspent bool
By string
// Next is set when the spender is the group's next token and keeps the
// epoch: the writer goes on under the same epoch.
Next *borgrec.Group
At time.Time
}
Fresh is a writer's evidence about a group's latest token (spec section 6.5): its proof verified against the writer's headers, and what the spend source said about its output.
func Latest ¶
func Latest(ctx context.Context, spends Spends, raw func(ctx context.Context, txid string) ([]byte, error), gv *GroupView, now time.Time) (Fresh, error)
Latest asks spends whether the latest token's output is unspent. A spender is fetched with raw: when it is the group's next token and keeps the epoch, the writer goes on; any other spender (a new epoch, or a transaction that is not a token, which terminates the chain) is no evidence. An error from the source is no evidence: fail closed.
type GrantStatus ¶
type GrantStatus struct {
Car *Car
Grant *borgrec.Grant
// Values are the fields in the clear, from the reveal, when it carries
// one.
Values map[string]string
// InRoll is set when the grant's commitment is in the roll of the
// organisation's latest usable token.
InRoll bool
// Member is set when the scope grants member.
Member bool
// Expired is set when its expiry has passed at the reader's clock.
Expired bool
// Spent names the transaction that spends its revocation outpoint;
// Unknown is set when no source could say whether it is spent.
Spent string
Unknown bool
// Current is the verdict; Why the first reason it is not.
Current bool
Why string
}
GrantStatus is one grant as a reader judged it (spec sections 5.6 and 13).
func Judge ¶
Judge applies the reader's checks to a verified grant carrier: in the roll, a scope that grants member, an expiry not passed (allowing the skew), a reveal unless the organisation is opaque, and a revocation outpoint positively unspent. spends may be nil: then "unspent" is never known, and the grant is never current for anything that releases a key.
type GroupView ¶
type GroupView struct {
*ChainView
// Members are the members after the last usable token, ascending.
Members [][]byte
// Removed are every key a usable token removed.
Removed map[string]bool
// Begins maps an epoch to the token that began it.
Begins map[uint32]*Tok
// Token is the last usable token's record.
Token *borgrec.Group
// Rejoined is set when a usable token re-added a removed key without a
// new epoch: the group is inconsistent (spec section 4.3).
Rejoined bool
}
GroupView is a group's state from its canonical chain.
func NewGroupView ¶
NewGroupView folds a group's chain view.
type HostOutputs ¶
HostOutputs is what one host answered to one question, all pages.
type NoChainError ¶ added in v0.1.5
type NoChainError struct {
Refused []Refusal
// HostErr are the hosts that could not be asked.
HostErr map[string]error
// contains filtered or unexported fields
}
NoChainError is ErrNoChain where hosts did answer tokens and none of them verified: not an absence. Each is in Refused, naming its host. When every answer fails the same way (unmined, say), the reader's own header source is as likely the cause as the hosts. errors.Is(err, ErrNoChain) holds.
func (*NoChainError) Error ¶ added in v0.1.5
func (e *NoChainError) Error() string
func (*NoChainError) Unwrap ¶ added in v0.1.5
func (e *NoChainError) Unwrap() error
type Opened ¶
type Opened struct {
Epoch uint32
Key [32]byte
EpochID [32]byte
Commitment [32]byte
// Label is "" when the key opened, else the check that failed (spec
// section 6.4): token, entry, undecryptable or commitment.
Label string
Err error
// Host is the host whose wrap opened it.
Host string
// Refused are the wraps a host answered for this member and epoch that
// did not verify: a carrier that breaks the carrier rules or SPV (bytes
// substituted or damaged), or one that is not of this epoch's token.
// Each names its host. What a host refused here is not used, whether
// or not another host's wrap opened the key: a caller names each host
// and treats the read as incomplete (docs/packages.md).
Refused []Refusal
// HostErr are the hosts that could not be asked.
HostErr map[string]error
}
Opened is what a member opened, or could not, for one epoch.
type OrgView ¶
type OrgView struct {
*ChainView
// Roll is the roll after the last usable token, ascending.
Roll [][32]byte
// Token is the last usable token's record.
Token *borgrec.Org
}
OrgView is an organisation's state from its canonical chain.
type RollAnswer ¶
type RollAnswer struct {
Host string
Grants []*Car
// Root is the root of the commitments answered; Matches is set when it
// is the latest usable token's roll.
Root [32]byte
Matches bool
Refused []Refusal
Err error
Truncated bool
}
RollAnswer is the roll as one host answered it.
type RollbackError ¶ added in v0.1.1
type RollbackError struct {
// Answered is the seq of the latest token read now; Seq and Txid the
// token the floor remembers.
Answered uint64
Seq uint64
Txid string
}
RollbackError is a chain the hosts answer only below the seq the caller's Floor remembers: an older state than one it verified.
func (*RollbackError) Error ¶ added in v0.1.1
func (e *RollbackError) Error() string
type Spends ¶
Spends is a source that indexes spends: a node's view of whether an output is spent, and by what. nodeapi.Asset is one. An answer of "" with no error is the positive answer "unspent"; an error is no answer at all.
type Terms ¶
type Terms struct {
Service string `json:"service"`
Terms int `json:"terms"`
Classes []struct {
Class string `json:"class"`
Satoshis uint64 `json:"satoshis"`
} `json:"classes"`
}
Terms is a host's terms document (spec section 10.3).
type Tok ¶
type Tok struct {
Txid string
Tx *transaction.Transaction
// Token is the token output, its record decoded.
Token *borgrec.ChainToken
// Height and Pos place it: its block and its offset in the block.
Height uint32
Pos uint64
// Beef is the token as answered (the engine's stored form).
Beef []byte
}
Tok is one answered chain token that verified.
func VerifyToken ¶
func VerifyToken(ctx context.Context, beef []byte, hall string, org []byte, headers chaintracker.ChainTracker) (*Tok, error)
VerifyToken checks one answered token as a reader does (spec section 13): the transaction holds exactly one well-formed token (its record, its lock derived from the record's organisation, its field signature), of hall and organisation org, and its proof verifies against headers.
func (*Tok) Before ¶
Before reports whether t was mined before u: lower height, then lower position in its block (spec section 13).