Help us shape the future of Go! Take the 2026 Go Developer Survey

reader

package
v0.1.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 4, 2026 License: Apache-2.0 Imports: 25 Imported by: 0

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

View Source
const ChainOrg = "org"

ChainOrg is the chain member of a question about an organisation's own chain.

View Source
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

View Source
var ErrHost = errors.New("reader: the host refused the question")

ErrHost is a host that answered with an error: its words travel with it.

View Source
var ErrNoChain = errors.New("reader: no host answers this chain")

ErrNoChain is a chain no host answered.

View Source
var ErrNoHeaders = errors.New("reader: no header source; refusing to verify against a default")

ErrNoHeaders refuses to verify against no header source.

View 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.

View Source
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.

View Source
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 Hash

func Hash(txid string) ([32]byte, error)

Hash is a display txid as a hash (hash byte order).

func JoinSeqs

func JoinSeqs(s []uint64) string

JoinSeqs words a list of seqs.

func ParseGroup

func ParseGroup(s string) ([borgrec.GroupIDLen]byte, error)

ParseGroup reads a group id from 32 lowercase hex digits.

func Seq

func Seq(n uint64) string

Seq is a seq as a question writes it.

func Short

func Short(txid string) string

Short is a txid's first 12 digits, for notes.

func SubjectID

func SubjectID(beef []byte) string

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

func (v *ChainView) Agree() bool

Agree reports whether every host that answered holds the whole canonical chain.

func (*ChainView) Beyond added in v0.1.1

func (v *ChainView) Beyond() []*Tok

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.

func (*ChainView) Latest

func (v *ChainView) Latest() *Tok

Latest is the canonical chain's last token, or nil.

func (*ChainView) Usable

func (v *ChainView) Usable() int

Usable is how many tokens from seq 0 fold without a mismatch: a reader fails closed from the first mismatched token on.

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

func (c *Client) Ask(ctx context.Context, host string, q Query) ([]lookup.Output, error)

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

func (c *Client) Held(ctx context.Context, host string, q Query, txid string) (bool, error)

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) Org

func (c *Client) Org(ctx context.Context, hall string, org []byte) (*OrgView, error)

Org establishes an organisation's chain and folds its roll.

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.

func (*Client) Token added in v0.1.1

func (c *Client) Token(ctx context.Context, beef []byte, hall string, org []byte) (*Tok, error)

Token is VerifyToken against the client's headers, with a stale stored proof replaced by the chain source's current one.

func (*Client) Wraps

func (c *Client) Wraps(ctx context.Context, gv *GroupView, epoch uint32) ([]*Car, []Refusal)

Wraps asks every host the wraps class for an epoch (all pages) and returns every wrap carrier that verified against the epoch's token, once each.

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.

func (Fresh) OK

func (f Fresh) OK() bool

OK reports whether a writer may key content under the token's epoch.

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

func Judge(ctx context.Context, car *Car, ov *OrgView, spends Spends, now time.Time) GrantStatus

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

func NewGroupView(v *ChainView) (*GroupView, error)

NewGroupView folds a group's chain view.

func (*GroupView) Epoch

func (g *GroupView) Epoch() uint32

Epoch is the epoch in force at the last usable token.

func (*GroupView) EpochsOf

func (g *GroupView) EpochsOf(k []byte) []uint32

EpochsOf are the epochs k was a member of, ascending: an epoch counts when k was a member at any token of it.

func (*GroupView) IsMember

func (g *GroupView) IsMember(k []byte) bool

IsMember reports whether k is a member at the last usable token.

type HostOutputs

type HostOutputs struct {
	Host      string
	Outputs   []lookup.Output
	Err       error
	Truncated bool
}

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.

func (*OrgView) InRoll

func (o *OrgView) InRoll(c [32]byte) bool

InRoll reports whether commitment c (hash byte order) is in the roll.

func (*OrgView) PathHolds

func (o *OrgView) PathHolds(car *Car) bool

PathHolds reports whether a grant's inclusion context verifies against the roll of the canonical token at the seq it names.

type Query

type Query map[string]string

Query is one ls_borg question: its members, every one a string.

type Refusal

type Refusal struct {
	Host   string
	Txid   string
	Reason string
	Err    error
}

Refusal is an answered output that did not verify.

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

type Spends interface {
	Spender(ctx context.Context, txid string, vout uint32) (string, error)
}

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).

func FetchTerms

func FetchTerms(ctx context.Context, hc *http.Client, base string) (*Terms, error)

FetchTerms reads <base>/ls_borg/terms. Members and classes a client does not know are ignored; a free class with a price is reported, and a client never pays for one.

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

func (t *Tok) Before(u *Tok) bool

Before reports whether t was mined before u: lower height, then lower position in its block (spec section 13).

func (*Tok) Prev

func (t *Tok) Prev() string

Prev is the txid its record names as its predecessor's, display order.

func (*Tok) Seq

func (t *Tok) Seq() uint64

Seq is the token's seq.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL