Add RFP-027 (LEZ Node API) and supporting appendices - #200
fryorcraken wants to merge 68 commits into
Conversation
Surveys what blockchain node APIs and client SDKs expose across Ethereum, Bitcoin, Solana, XRPL, Cosmos, Stellar, NEAR, and Sui: 34 API functions, transport types, and SDK languages per ecosystem. Logos L1 and LEZ appear as rows throughout, with a gap summary of what is absent or narrower than the surveyed norm. Every claim cites first-party documentation or, for Logos, a file and line in logos-blockchain and logos-execution-zone. All 83 references were fetched and confirmed to resolve. Unsourceable entries are marked [NOT FOUND] rather than inferred. Supports the blockchain API RFPs tracked in logos-co/ecosystem#219, #220, and #222. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two integrity failures: - Section 1.15 attributed two quoted phrases to Stellar's documentation that do not appear on that page. Replaced with the wording the source uses. - Section 3.1 claimed XRPL's docs label a Go SDK "official" and built a conflict narrative on it. That page contains no maintainership labels. Factual corrections: - Logos L1 does accept a stream start position: /cryptarchia/blocks_range takes slot_from and slot_to. Stream resumption was wrongly listed as a gap absent on both targets. - XRPL publishes a first-party Rust SDK (XRPLF/xrpl-rust); it was marked [NOT FOUND], which also contradicted the surrounding prose. - LEZ exposes getChannelId, so network identification is absent on Logos L1 alone, not on both targets. - Estimate execution cost is 5 of 8, not 6. - Corrected an XRPL pagination misquote and an overstatement of what the bdk-ffi repository itself builds. Also aligned the Sui row in 1.4 with the [NOT FOUND] convention used for Stellar and NEAR, qualified LEZ getSchema as a type schema rather than an interface description, recited the Cosmos REST broadcast paths to CometBFT, and made the Bitcoin cell in 1.15 agree with the stated count. All 85 references re-fetched and resolving; every citation marker is matched to a reference with no orphans. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Section 3.2 cited Stellar's build-your-own-SDK guide as evidence that Stellar argues for hand-writing client SDKs. That page scopes itself to contract SDKs: "This is for building an SDK for writing smart contracts." It says nothing about client SDK strategy. Replaced with what the sources support: no surveyed chain publishes a written argument for hand-writing client SDKs over generating them. Found by an origin-research agent that checked a premise it had been given rather than accepting it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds two dimensions the catalogue lacked: what each call returns, and why it came to exist. Both are drawn from standards documents, architecture decision records, changelogs, and maintainer discussions, all fetched and quoted verbatim. Response shapes, for the calls where the request is empty and the response carries the design: - Discovery: Cosmos returns a dependency manifest with per-module versions rather than a version string; Logos L1 returns a bare JSON string with no field structure at all. - Health and sync: this is where retention bounds live. Bitcoin splits sync into validated-chain and known-header heights and expresses retention as four fields; XRPL expresses it as a possibly disjoint range expression and signals finality by which field is present. - LEZ discloses indexer staleness through getStatus, which most surveyed chains do not, and which the appendix previously did not credit. - Simulation, submission, and construction-parameter responses. Origin history, replacing six [NOT FOUND] entries: - ERC-191 and NEP-413 independently identify the same hazard, that a signed message must not be reinterpretable as a transaction, and solve it the same way. - XRPL renamed a response field after the old name's implied guarantee was exploited to steal from institutions; shipped behind an API version. - Aptos ships server-side encoding and warns in its own source not to trust it, which is why construction stays client-side elsewhere. - Removing node-side signing let go-ethereum log RPC payloads safely for the first time. - EIP-1898 on read coherence under reorg; EIP-4444 on why retention must be declared rather than discovered; EIP-234 on why an empty result must still identify its position. - Cosmos ADR-070 names IBC relayers and exchanges as the users hurt by sequence-number fetching. Also records two retirement mechanics worth reusing: Algorand's 410-versus-404 distinction, and Solana's fifteen-endpoint removal. 32 new references, all fetched and resolving. Every quotation was verified character-for-character against raw source with whitespace normalised. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Response shapes were prose blobs listing per-chain field names. They are now tables keyed by what the caller learns, so the same concept aggregates across chains that name it differently: "retention floor" gathers Cosmos earliest_block_height and Bitcoin pruneheight, "did it succeed" gathers Cosmos code and XRPL engine_result, and so on. Five sections restructured: node version, health and sync, simulation, submission, and execution effects. Adds a short section introducing the two absence markers, which were previously explained in a single buried sentence. It distinguishes what each means, lists the three reasons a table cell might read [NOT FOUND] (the capability does not exist, exists under an unrecognised name, or lives somewhere unread), and states plainly that neither marker is evidence of a gap in the chain, only the boundary of what was verified. Rationale prose now reads "No specific or relevant context has been found" rather than [NOT FOUND]; the short marker stays in table cells where column width is tight and the meaning is narrower. Also corrects a stale count: the intro claimed rationale was sourced in four cases, which predated the origin research. It is eleven of thirty-four. Every field name in the new tables verified against its cited source. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Section 1.2 marked Logos L1 as having no health or sync-status call. It does. GET /cryptarchia/info returns a state field carrying the consensus engine state (Bootstrapping or Online) and a phase field carrying a four-stage lifecycle: AwaitingGenesisTime, InitialBlockDownload, ProlongedBootstrapPeriod, Following. That is finer-grained than the boolean most surveyed chains return, and the first phase is the only case in the survey where a node documents that it will serve reads but not writes. The response also carries lib and lib_slot, a finality anchor alongside the tip. Records one inconsistency found while checking: the endpoint's OpenAPI annotation declares the body as CryptarchiaInfo, while the project's own HTTP client deserialises it as ChainServiceInfo, which additionally carries phase. The published contract and the first-party client disagree about whether phase is part of the response. This is the false-negative case the document's own sourcing section warns about: a capability present in the code but not found by the survey. Caught by user challenge, not by the fact-checking passes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A source-level re-audit of both Logos codebases, prompted by the sync-status false negative, found nine wrong cells. Several were false positives, claiming capability that is not there: - Validator set: the SDP routes list Blend mixnet providers, not consensus participants; the service-type enumeration has one variant. - Broadcast: POST /mempool/add/tx returns an empty body, so unlike every surveyed chain L1 returns no identifier to track a transaction by. - Health: the LEZ sequencer's checkHealth is an unconditional empty success, so all sync reporting comes from the indexer alone. - Transaction status: the OpenAPI declares one type while the service returns another, and the status enum is only Unknown or Pending, so Unknown conflates never-seen, mined, and evicted. - Machine-readable description: L1's OpenAPI omits five wired routes including both signing endpoints and registers three schemas. - Network identity: a chain id is inscribed at genesis and read at ledger init, so the earlier flat absence was overstated. Adds a Potential gaps subsection to each of the 34 functions, with one entry for Logos L1 and one for LEZ, stating what is missing or narrower relative to surveyed practice and citing the source read. Where a surface is comparable it says so rather than inventing a gap. These describe observed state only; the RFP does the prescribing. Notable: a second contract-versus-implementation disagreement, matching the one already recorded at 1.2; L1 signing routes accept a caller-supplied digest with no transaction attached; LEZ programs emit no events at all. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A layer-by-layer inventory of what the Logos blockchain APIs expose today, so other documents can cite a stable reference rather than re-deriving it from source each time. Covers the LEZ indexer FFI (the read surface applications reach), the indexer RPC behind it, the sequencer RPC, the wallet FFI and lez_core module, the L1 C bindings, the L1 node HTTP routes, and the L1 module. The structural finding it records: two independent stacks serve LEZ and they do not meet. The lez_core module holds no indexer client, and the indexer stack performs no writes, so a capability reaches an application only through the module at the top of one stack or the other. Two boundaries are marked explicitly. Four indexer RPC methods stop at the FFI rather than at the module, including getAccountAtBlock and the finalized-block subscription, so the FFI is where the LEZ read surface is bounded. And nine sequencer methods have no indexer counterpart, of which getAccountBalance and getAccountsNonces are derivable from data the indexer already stores, since its Account record carries both balance and nonce. All counts read from source at the commits named in the document. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three adversarial reviewers checked the API surface reference against the codebases, one per area. All three returned FAIL. Corrections: Factually wrong: - The C header is not committed. It is gitignored and generated by cbindgen at build time, so a fresh clone contains none. The document described "the committed header" and miscounted it as eleven declarations; the stale local artefact has ten, two naming functions that no longer exist. - The indexer FFI does distinguish not-found from failure, contrary to what the document claimed. The source says an empty result "is distinct from an error"; blocks and transactions carry the same distinction through an option type. The real weaknesses are narrower and now stated: query_account cannot express not-found at all because the store returns a default account, and query_status has no status channel. - start_indexer takes three parameters and returns a pointer result carrying the handle, not a bare status. - The OpenAPI document omits six wired routes, not five. - Six sequencer methods lack an indexer counterpart, not nine. The document's own table already said six. - The lez_core module exposes 48 methods, not 46; the count missed two const-qualified accessors. - The indexer module has 11 public methods, not 12, and does not wrap the FFI one to one: reset_storage wraps nothing, calling the module's own stop and deleting the store directory from C++. Imprecise, now fixed: - The L1 module commit is pinned rather than left as "current". - "Most successful responses" replaced with twenty of thirty-six. - Section 6 explains why its table lists 44 paths against 42 constants. Also records that the indexer module flattens the FFI's error signalling into an empty string, losing a distinction the FFI does make. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Potential gaps notes assessed LEZ against the sequencer and indexer RPC. That is the wrong altitude: an application reaches LEZ through a Logos Core module over an FFI, so a capability present on a service RPC but absent from every FFI is not available to anyone and is a gap. Twelve LEZ bullets rewritten. Nine credited capabilities that stop below the FFI: - Network identity, machine-readable description, pinned historical read, and block subscription all exist on the indexer RPC with no FFI export. - Node version, balance for an arbitrary address, transaction lookup carrying a block id, and batch nonces all exist on the sequencer RPC with no FFI export. Two were analysing the quality of something unreachable: the pinned-read note discussed retention for a read applications cannot make, and the stream-resume note discussed reconnection for a subscription that cannot be opened. Three were understated rather than wrong. Transaction status has a reachable call, poll_transaction_status, returning a bare boolean that conflates never-seen, pending, and rejected. The wait note described a retry loop that lives below the boundary. The balance note criticised the wrong function. Adds a framing paragraph to the Gap Summary naming the boundary and listing the ten methods that stop at it, and cites the companion surfaces appendix. Found by an adversarial review of all 34 functions against the four repos, corroborating an independent pre-check. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The node API survey had a blind spot: it treated node-side signing only as a custody boundary chains retreat from, and never asked what an integrator uses instead. It contained no mention of BIP-32, BIP-39, BIP-44, mnemonics, derivation paths, or extended public keys, and none of its 34 functions covered key or address derivation. An exchange needs deposit-address derivation and offline signing before it needs any read method. This is a separate appendix rather than a section, because wallet libraries are a different category from the node API surfaces the other document inventories. rust-bitcoin, BDK, ethers.js, and bitcoinjs-lib derive keys and sign locally, much of which never touches a node. Corrects two things the research established against my initial framing. "Ethereum removed node signing" is too strong: geth removed its own custody namespace, while eth_sendTransaction, eth_sign, and eth_signTransaction remain in the standard specification. And geth's documented replacement is Clef, an external signer that still speaks RPC, not solely the wallet-library path that integrators actually take. Both are recorded, with the removed-to-replacement mapping from geth's own deprecation page. Records the finding that watch-only deposit-address derivation, the capability an exchange needs most, splits on the signature curve rather than on design: secp256k1 chains support public derivation, ed25519 chains permit only hardened derivation and so cannot. Stellar's SEP-0005 states both halves of that case. Chains without it adopt a different deposit model, such as muxed accounts or destination tags. Also notes that Bitcoin and Ethereum, the two largest, are the only two with no first-party wallet library, and covers hardware and offline signing including the Cosmos constraint that Ledger accepts only the deprecated Amino encoding. All 20 references fetched and resolving; quotations verified verbatim. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Convert the bracketed [n] citation markers to [[n]](#ref-n) links resolving to anchored reference entries, so citations are navigable in both GitHub and Obsidian. Hyperlink the five source repositories in the API surfaces table. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Defines the read API for LEZ as exported functions on the indexer FFI: 24 Functionality requirements covering effects, simulation, per-transaction status, batch and pinned account reads, subscriptions, pagination, and error semantics. Each requirement carries a readiness marker (Ready, Ready but not exposed, Computed but not persisted, or New) classified against the LEZ source, so a proposer can see which requirements are exposure work and which are new. Scoped to the indexer read surface, one of six deliverables for the wider blockchain API and SDK work (logos-co/ecosystem#219). The sequencer is excluded: its own source marks its account queries transitional, and indexer to sequencer communication is expected to move to a libp2p mempool. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A program event system merged to the LEZ default branch on 2026-08-27, after the commit these appendices pin. ProgramOutput gained an events field, and events are queryable by getEvents, by a subscribeToEvents subscription, and over the FFI as query_events, filtered by block range, transaction hash, program, and selector. The LEZ cells for sections 1.28 and 1.32 said [NOT FOUND]. They now name the methods, with a new reference 121 pinned to the dev branch rather than restating the survey's own pinned commit. Two limits are recorded: events are dropped for privacy-preserving transactions, and an indexer captures only what its configured event filter matches. Also notes that a ninth FFI query, query_events, exists after the commit the API surfaces appendix pins. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Functionality section specified 24 deltas against the current FFI, so functions that exist and are used daily (query_block, query_account, query_transaction, query_status, query_block_by_hash, query_events) had no requirement at all. A proposer had to read LEZ source to learn the baseline, and existing behaviour had no acceptance criteria. It now defines 21 functions across eight groups, each with a signature, its semantics, and requirements carrying a readiness marker: 15 Ready, 5 Ready but not exposed, 5 Computed but not persisted, 22 New. Each group opens with an itemised list of the rough equivalent on Ethereum, Bitcoin, Solana, and Zcash. Zcash is included because it is the closest production analogue to LEZ: every transparent read is address-indexed and open, every shielded read is viewing-key scoped, which is the same boundary LEZ has. Adds simulation cost reporting. LEZ caps public execution at 32M cycles and already runs execute-only, so the executor's user-cycle count is computed on every public transaction and discarded at lee/state_machine/src/program/mod.rs. Returning it costs nothing. Cost is reported as cycles rather than a fee, since there is no fee system on the default branch, and exhausting the cycle budget must be distinguishable from a program error: every executor failure currently collapses into ProgramExecutionFailed(String). Corrections found by verifying every claimed-existing signature against source: query_account returns a bare FfiAccount, not FfiOption, so the target signature is now stated separately from the current one; query_chain_tip has no capability below the FFI and is New rather than exposure work; the zone identifier is read from indexer config, not derived from chain data; the before-exclusivity requirement is split from the has-more signal, which does not exist; and two citations of the transaction wire types are corrected to their current lines. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
RFP-021 cited Zcash in six places as a surveyed chain, but the appendix surveyed only eight and never mentioned it. Those claims were unsourced. Zcash now has a row in all 34 function tables plus the chains, transport, and SDK tables, with 12 first-party references. Counts that quantified the survey were recomputed rather than incremented, since Zcash falls on the supported side of several of them. Corrections found against first-party sources: Zcash carries no testmempoolaccept, so it has neither simulation nor an acceptance check; getaddressbalance, getaddresstxids, getspentinfo and getblockdeltas are insight-explorer gated rather than open on an arbitrary node; and z_getbalanceforviewingkey requires the key already imported into the node's wallet. RFP-021 still needs updating to match. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… API The section argued from a source comment marking sequencer methods for removal, which conceded the sequencer was a target that happened to be going away. The architectural point is stronger: the sequencer is inside the black box, and the indexer is the only surface a consumer sees. The indexer serves every read as a cache and proxy, sourcing from wherever inside LEZ the data lives. The libp2p migration now argues that internal routing is invisible to consumers rather than sitting as an aside. Also drops effects requirement 5. No integrator in this RFP's own list acts differently for it: an exchange must not credit a private deposit either way, and a wallet reads bodies rather than effect summaries. Requirement 2 already discriminates the transaction body type. Renumbering is deferred to a later pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The wallet syncs private accounts by pulling ascending block ranges from the sequencer and trial-decrypting each privacy-preserving body, and it fetches commitment membership proofs from the sequencer to spend a note. Both are consumers reaching into what the rationale now calls internal, and neither had an indexer requirement. query_block_vec becomes query_blocks, matching the plural-noun naming of query_accounts and query_transactions_by_account and dropping a container type from an API name, and takes an ordering parameter on the same terms as query_transactions_by_account. A descending-only read cannot serve a forward scan. The as-built name stays in the API surfaces appendix. Adds commitment membership proofs as a read, carrying the shape the sequencer's getProofsAndRoot returns today. Out of Scope no longer argues method by method. That mapping was both the wrong argument under the new rationale and incomplete, omitting getBlockRange and getProofsAndRoot. Numbering is left inconsistent for the deferred renumbering pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every Zcash bullet was written before Zcash was in the appendix, so each was checked against the survey rows and their prose. Four cited methods are insight-explorer gated rather than generally available: getaddressbalance, getaddresstxids, getspentinfo and getblockdeltas need a node started with txindex, experimentalfeatures and insightexplorer, then reindexed. That is a property of the deployment, not of the chain, and the RFP described getaddressbalance as open to anyone. z_getbalanceforviewingkey carries a second condition the RFP omitted: the key must already be imported into the node's wallet, so it is not a query against an arbitrary node with a key presented inline. The claim that getblockchaininfo reports the active consensus branch is not supported by the appendix, so it is removed rather than left standing on an unverified source. Zcash also publishes a ZeroMQ surface, worth stating where the Bitcoin bullet beside it cites ZeroMQ, and it lacks Bitcoin's -zmqpubsequence loss-detection publisher. Resolves the simulation TODO: Zcash has no equivalent on either leg, and carries no testmempoolaccept, so it does not inherit even the acceptance check Bitcoin offers. Survey counts move from eight chains to nine. Simulation stays at six, Zcash adding to the denominator only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
On LEZ a privacy-preserving transaction is executed by the wallet, which runs the program locally over notes only it can decrypt and submits a proof that the execution was correct. The proof is an input to the transaction rather than a result of executing it, so a node holds neither the witness material nor the plaintext a simulation would need, and there is nothing for the indexer to simulate before the wallet has already done the work. Zcash reaches the same conclusion from the same premise. Public execution could be simulated by the indexer, but siting it there splits one capability across two components and answers against finalised state rather than the state the caller is building on. The wallet already holds the executor for the private path, so it can answer for both kinds of transaction against a state it chose. That is also what simulation is for elsewhere: Stellar returns the transaction data and minimum resource fee the caller copies back in before submitting, which makes it a construction step rather than a read. The design rationale now records why the one norm an RPC provider is expected to offer does not fit LEZ, and Out of Scope names it so a proposer finds it where they look. Drops the six simulation requirements and the reliability, performance, usability and test obligations that referenced them. Resolves the simulation privacy TODO, the gas-assumption TODO, and the misleading figure TODO, all of which sat on requirements that no longer exist. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every function in the RFP took the handle as its first argument, so repeating it two dozen times said nothing a reader did not already know from the document being an indexer API. The handle is real: a C ABI has no receiver, so it is a parameter in the generated header, and each export null-checks it. That convention is now stated once in the readiness preamble, along with the null-handle outcome, rather than being implied by every signature and stated nowhere. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A count is not how pagination works. The requirement that every paginated response reports whether more results remain is what lets a caller walk to the end, and it already stands. No surveyed chain exposes a count of an account's transactions on its node API. Every chain that answers for address history returns a listing: Bitcoin's listtransactions takes count as a page size rather than returning a total, Solana, XRPL, Cosmos, Stellar and Sui return entries, and Ethereum and NEAR have no such method at all. Ethereum's eth_getTransactionCount is the nonce, a construction parameter rather than a history count. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The RFP required that a caller walking an account's transactions neither skips nor repeats an entry, and then specified an offset, which cannot hold that property: entries landing ahead of the offset shift every later position. The reference integrator is an exchange reading deposits newest-first, which is exactly the walk that breaks. query_transactions_by_account and query_pending_digests now take an opaque cursor, and every paginated response carries the cursor to resume from alongside the has-more signal, so a caller never constructs a position itself. The pending set is the sharper case, since entries leave it as they are included, so an offset there names a different entry between calls. Cursors are also the dominant idiom among the surveyed chains: XRPL, Stellar and Sui cursor, Solana anchors on a signature, and Bitcoin alone pages by offset. query_blocks keeps its block bound, which is a position in an append-only sequence rather than an index into a shifting set, and stays in the pagination section: it walks history, while the metadata section answers where the chain ends and which network this is. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The signatures no longer carry the handle and the convention is stated in the readiness preamble; only the note asking for it was left behind. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The RFP asked the account reads to distinguish a never-seen account from one holding zero, which does not survive contact with what the record holds. An Account is program_owner, balance, data and nonce, all Default-derived, so the record returned for an unknown identifier is the default account, which is also what an uninitialised account holds. An account that has ever signed carries a nonce, one owned by a program carries an owner, one holding data carries data, so the cases a caller cares about are already legible from the record. What remains ambiguous is an account materialised by a zero-value touch and never used again, which is empty in every sense a caller acts on. That is thin ground for an ABI break on the one existing export, so the signature stands and the API documents that the read answers what an account holds rather than whether it exists. The reads are now scoped explicitly to public state. A private balance has no account record to return at all: private state is a commitment set and a nullifier set carrying no account identifier, so it is not an encrypted row the API declines to read, it is not a row. A default record therefore says the state holds no public balance for that identifier, and is not evidence the party holds nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
BlockId is a u64 height counting from a genesis of 1, and the codebase treats it arithmetically: pagination subtracts one from it, snapshots index by dividing it, and the sequencer assigns a local named new_block_height straight into the field. The hash is the separate 32-byte value the header carries, held in storage as a secondary index onto the height. The distinction matters because a height names a position rather than a block. The sequencer's reorg handling describes inscribing a second block at a height the channel already holds, so one identifier can resolve to different blocks at different times while a hash resolves to one block or to none. A new rationale section defines the two terms. The block subscription now delivers height and hash together, since a height alone does not say which block occupied it; BlockMeta already exists as that pair. The pinned account read returns the hash it answered against, and reliability #1 asks for the same so a repeated read can be shown to have resolved to the same block. Also fixes the chain tip requirement, which asked for "the block identifier together with the block's height" as though those were two values. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
query_zone_id becomes query_network_identity and answers both halves of the question an integrator is really asking, since a zone identifier alone does not distinguish the same zone running against different L1 networks, and answering the two separately lets a caller check one and assume the other. The chain identifier is the one inscribed in the Logos Blockchain genesis block as a Cryptarchia parameter, a bounded UTF-8 string such as logos-chain-1, rather than a value the indexer is configured with: a configured string agrees with whatever an operator typed rather than with the chain the node settles to. That value is real but unserved. It is read at ledger initialisation, no L1 route returns it, and the indexer's Bedrock configuration carries only an endpoint, so reaching it is work outside the indexer and a proposal says how. Platform Dependencies now records this as the one exception to the no-blockers claim, and a further requirement keeps the surface deliverable meanwhile by having the call report the value as unavailable rather than returning a placeholder. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The section already asked for a README and two doc packets, but neither covers the module author's path: how to take an empty repository to a Logos Core module that consumes this FFI. The README documents building and operating what this RFP delivers, which is a different reader. The versioned API reference was absent entirely. It is specified as generated from the machine-readable surface description required by Functionality #50 rather than maintained by hand, so it cannot drift from the header it describes, and it is a standing per-version obligation rather than a packet submitted once. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The privacy paragraph said a node cannot read "which account, which amount, or which program state changed". All three were loose. It is a private account that is concealed; amounts on the public leg are readable, since a public action carries its post-state in the clear; and programs hold bytecode, while accounts hold the state, which is balance, data and nonce. Remove the rhetorical scaffolding through the section and the BDK section that precedes it: "that is not nothing", "it is worth describing", "the one that matters", "even in principle". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… rationale Functionality #12 required the account reads to answer for public state alone and to say so. They cannot do otherwise: query_account takes an AccountId and reads a HashMap<AccountId, Account>, while private state is a commitment set and a nullifier set holding no account identifier at all. There is no path by which the read could consult private state and no way for a caller to ask it to, so the requirement stated that a function reading the public map reads the public map. #11 already carries the part an integrator needs, that the read answers what an account holds rather than whether it exists. Renumber the Functionality list and the references into it. Point each Design Rationale section at the requirements it justifies, so a reader can get from an argument to the text that carries it: gas to #5 and #6, retention to #56 and #57, the privacy boundary to #2, #4, #16, #17 and #42, and simulation to Out of Scope. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three of the five requirements it cites already hold: #2 is Ready and #4 is Computed, not persisted, so describing them as a gap misreported the state of the work. Fold the paragraph into the one above it, which was already saying what the constraint shapes, and separate the two halves by what they need: the consumer-facing reads exist and the work on them is to document the boundary, while the wallet's proofs and forward ordering are new. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The wallet reaches the sequencer's getProofsAndRoot directly today (lez/wallet/src/lib.rs:660-667), so the membership proofs #16 and #17 require are not a capability that does not exist. What is new is serving them from the node, which is the point the surrounding rationale makes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CI runs mdformat --check, which the file failed. Most of the diff is rewrapping to the 80 column setting. The Supportability list gains zero padding because it runs past nine items, which renders the same and is cited by nobody. One change was a real defect: renumbering the Functionality list left a tab in front of the Transaction effects heading, so mdformat read it as a code block. The mermaid blocks are byte identical, and all three diagrams still render. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Android diagram read as though Android implied the LEZ-DK, when the division is what an application is built from rather than what it runs on. Name the existing case for what it is, an application already shipping that is adding LEZ support, and add a fourth diagram for a Basecamp app on Android, which reaches lez_core over the Logos Core FFI and uses no kit at all. Say what the LEZ-DK is for while there: integrating LEZ into applications that already exist, and reaching the users those applications already have. A developer starting something new is encouraged to build on Logos Core and Basecamp instead, whether or not LEZ is the whole of what they are building. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Carry the diagram titles to a single convention: capitalise only proper nouns, so a pre-existing Android wallet and a pre-existing desktop wallet app read as descriptions rather than product names. Name the two Basecamp cases in parallel, as new apps that use Basecamp and Logos Core, which is the division the four diagrams turn on. Say in the overview that Basecamp and Logos Core remain the first recommended way to build on LEZ on every platform, and that the kit exists so an existing application can add LEZ support without re-architecting. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Basecamp case does not vary by platform, so a second diagram of it on Android showed the same shape twice. The remaining Basecamp diagram already says it holds on any platform it runs on. Drop desktop from the embedded-node case for the same reason: an application that links the node rather than reaching one over a transport is making a deployment choice, not a platform one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Build both libraries and report what they weigh, since the architecture offers a choice between an embedded node and a remote one without saying what embedding costs. At --release for x86_64-unknown-linux-gnu, the LEZ node FFI is 28.0 MiB stripped and the Logos Blockchain FFI is 84.5 MiB, so an application embedding both starts near 112 MiB for one protocol pair. The LEZ figure excludes proving: the risc0 prove feature is enabled by the wallet FFI and a benchmark tool alone. Neither figure is a mobile one, and neither can be yet, because the ZK circuit artifacts both libraries depend on are published for Linux, macOS and Windows only. Record that, and record a candidate strategy for it: let the published artefacts differ by platform, carrying the node on desktop and server and omitting it on mobile, since a developer consuming a prebuilt library cannot set a build feature. Draw the Logos Blockchain node the LEZ node reads finalised state from, which no diagram showed. It appears as blockchain_module beside lez_core in the Basecamp app, and inside the remote host for the Android integration. That host is named logosctl, per logos-co/logos-docs, rather than the headless Logos Core it was called before. Link the Logos Blockchain tracking issues where the text said TODO, and fix an anchor that pointed at a placeholder. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The event-system exclusion promised "the two reasons given in the Design Rationale", but that rationale was removed earlier and the bullet states both reasons itself, so drop the pointer. It also said "the requirements below" from inside Out of Scope, which is after them. Rename the identifiers left behind by today's edits: the third diagram kept desktop and desktopApp after desktop was dropped from its framing, and lbmod named a subgraph in one diagram and a leaf node in another. The comparison with bdk-android undercut itself, calling the LEZ node "close to twice a complete Bitcoin kit" and then explaining two sentences later that the figure excludes the wallet and proving. Say instead that the node alone already outweighs a complete kit, and name the architecture the 15.3 MiB is for. Say the embedded-node diagram omits the L1 node "unlike the two above", since the other two now show it. Narrow a config citation that straddled two structs. Settle on artefact over artifact, the spelling the heading anchor depends on. Carry the renames into the README's placeholder rows. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two things bear on the architecture and neither is decided. Mobile support is not planned for testnet 0.3, so the Basecamp shape the first diagram describes is reachable on desktop only for now. And whether a mobile device runs light chain nodes on the peer-to-peer networks, reaches remote nodes over RPC, or combines the two is unsettled, which leaves the sizes and the per-platform artefact strategy provisional until it is chosen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
bitcoind still carries a wallet API in the node and geth once did. The ecosystem has moved away from it, which the wallet libraries appendix already documents, so the section points there rather than restating the history. Worth recording anyway: because lez_core carries the wallet beside the node, the JSON-RPC proxy module of deliverable 3 is the only further component needed to offer that shape, which would reach server and cloud environments alongside the Logos Core path. Whether to offer it is a product decision, and this RFP defines only the node half. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The target architecture, the artefact sizes, the node wallet shape and the readiness notes describe the Logos stack as a whole rather than the node API, and they had grown to a third of the RFP. They now sit in Appendix: Integrating the Logos Technology Stack, shared background for the RFPs that define each component rather than part of any one of them. The RFP keeps a short pointer naming the five components and linking the appendix, and its Overview otherwise runs straight to what it specifies. References that read as positions in a list, "deliverable 3" and "the second of the five deliverables", now name what they mean, since the list they counted lives elsewhere. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… split Fold the "TODO: rework below" material into the outline above it. The framing axes and integration scenarios move into Integration paths, and the target architecture, artefact size, node wallet and readiness sections follow the Recommendation directly. Distinguish logos-liblogos, the host runtime, from the language SDKs, which are guest-side. A standalone application can host modules through liblogos_core and call them through the consumer surface, so "a Logos SDK for language X" denotes two FFI wraps rather than one. Record the four gaps that stand between the host runtime and a mobile application, and cite the Rust and Electron proofs of concept. Add Multizone integration as an open R&D question: multizone within one core module against several instances behind a proxy, with the wallet treated separately since key management should be zone-abstract whatever the node does. Add a JSON-RPC client module so a Basecamp application reaches the node API over IPC whether the node is local or remote, and keep the local-or-remote choice open on both integration paths. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
State what a library must provide per language: consuming a module's API, managing modules, and the token and capability handshake. The artefact each wraps is unsettled, but the capabilities hold regardless, and a delivery covering fewer than all three does not work end to end. Record how the Qt dependency bears on each differently: a build-time cost for module management, the reason a Qt-free route reaches introspection but not invocation for API consumption, and the substance of the problem for the handshake. Leave open whether the three stay three. If the capability surface becomes reachable over a plain transport the handshake folds into API consumption; if it stays behind a token manager, another language needs a different answer, and driving a separate binary per call suits mobile poorly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| build focused native libraries for the **wallet library + JSON-RPC client** | ||
| integration model. | ||
|
|
||
| 1. **Dedicated native library for the Logos Blockchain wallet.** Logos |
There was a problem hiding this comment.
The "signatures" for Logos Blockchain transactions are zk proof; the Logos Blockchain circuits are provided as c libraries, and wrapped in Rust for the current wallet lib; so in any case, the c libraries have to be embedded, and this will be more complex/relevant for private UTXO.
There was a problem hiding this comment.
The zone SDK has an external wallet implementation of the logos blockchain, it does utxo tracking, etc.
It is non-trivial, especially for fork tracking, reorg and managing of pending state. Hence, Logos Blockchain wallet should probably be one of our Rust library (eg zone sdk) wrapped in FFI.
There was a problem hiding this comment.
There might be an option to write a spec; zone sdk is complex, but has more than necessary;could be possible to write a spec out of it for UTXO tracking but unclear.
Driving a separate binary per call suits an embedded integration poorly and mobile worse. The preferable shape is for an application to host the same gateway logosctl's daemon registers in process, so a development kit offers a real call(module, method, args) without a second copy of the runtime in the artefact. Record what that costs and what is unconfirmed: the invocation path inside the gateway runs over Qt's remote objects, so hosting it is C++ work even where everything above it is not, and the proof of concept pursuing it has not yet driven a module through a running daemon's gateway. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| Logos Core FFI on the `lez_core` module, and through the `lez_wallet_ffi` | ||
| crate so it can be wrapped in libraries for other languages (see 4). | ||
|
|
||
| 3. **The JSON-RPC proxy module and its client library** |
There was a problem hiding this comment.
then another client module could be created to provide again IPC API of other module over delivery.
| a consumer may reach the node through either path, the two must express the same | ||
| semantics. | ||
|
|
||
| ## Multizone integration |
There was a problem hiding this comment.
Need to be clear on the commercial strategy here and risk of liquidity fragmentation for both healthy DeFi protocols and security.
There was a problem hiding this comment.
Zone-as-a-service LEE vs custom
| The Logos technology stack comprises four main components: | ||
|
|
||
| - **Logos Blockchain**: the L1 settlement layer. | ||
| - **LEZ (Logos Execution Zone)**: the programmable execution layer. |
There was a problem hiding this comment.
programmable money
Restate the capabilities as the three pieces they are: runtime lifecycle, provider hosting, and invocation with authorisation, each naming the library it ships from. Provider hosting is the barrier, since registration goes through a QObject with no C ABI over it, and invocation is half solved, since a universal string and JSON interface already exists but cannot be reached without Qt. Point each upstream change at whether it appears planned, hedged where the match is partial. The roadmap pages predate much of the Qt removal work, so note that an absent item is not evidence a piece is unplanned. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Superseded by a two-PR split:
The content is the same, minus the local working notes. Merge #205 first. Closing this in favour of those two. |
Important
Superseded and closed. This PR has been replaced by a two-PR split:
master.Merge #205 first. The review comments on this PR are being carried across to those two.
The original description follows, kept for reference.
Draft. RFP-027 needs review before this is ready.
Adds RFP-027 (LEZ Node API) and the three appendices supporting it.
Numbered 027 rather than 021: three older open pull requests already claim
021 (#158,
#169,
#57), so this takes the first free
number past every current allocation.
RFP-027: LEZ Node API
Defines the LEZ node API, scoped to
logos-co/ecosystem#235.
25 functions across ten groups, each with a signature, its semantics, and
requirements: 64 in Functionality plus Usability, Reliability, Performance,
Supportability and Privacy. Functions that already work are specified too,
rather than assumed, so a proposer can build and a reviewer can accept against
the document alone.
Each group opens with the rough equivalent on Ethereum, Bitcoin, Solana, and
Zcash.
Each Functionality requirement carries a readiness marker, classified against
LEZ source:
The markers describe effort, not priority. The intent is that a proposer can
see which requirements are exposure work and which are genuinely new.
One of five deliverables, each with its own issue: the LEZ wallet API
(#236), the JSON-RPC proxy
module and its client library
(#237), the LEZ Development
Kit (#238), and further
transport modules with their clients
(#222). The separate
indexer client library issue was folded into #237, since a wire format and the
client that parses it are two halves of one contract.
Target architecture
Three diagrams show what the deliverables build towards. They differ in what
the application is built on, and then in where the node runs: a new app on
Basecamp is a Logos UI module paired with a Logos Core module reaching
lez_coreover the Logos Core FFI, while an application not built from Logosmodules uses the LEZ-DK for its language and either reaches a remote node or
embeds one.
Basecamp and Logos Core stay the recommended way to build on LEZ. The LEZ-DK
exists so an existing application can add LEZ support without re-architecting,
which reaches distribution channels LEZ otherwise would not.
The positions worth reviewing
The LEZ node API is the only API for LEZ chain state access. A LEZ node is
a black box: what it is made of, which part answers a given call, and how those
parts talk to each other are implementation rather than API. It carries writes
as well as reads, since a signed transaction and the new commitments a
privacy-preserving one produces reach the network through it. Consequences: the
wallet's private account sync and its commitment membership proofs are required
of the node, transaction submission sits on this surface because a wallet that
has signed a transaction otherwise has nowhere sanctioned to send it, and the
pending set is exposed, which is what lets a caller tell a declined transaction
from one still in flight.
A transaction status states what it means, not how it is computed. Final
means the transaction's block is final on the underlying L1, so a caller acting
on it is acting on a settled result. It is deliberately not a statement about
how much of the chain a node has processed, which would report a node catching
up rather than a result settling.
Simulation is not here. Six of the nine surveyed chains execute an
unsubmitted transaction and return its outcome. On LEZ a privacy-preserving
transaction is executed by the wallet, which runs the program locally over
notes only it can decrypt and submits a proof that the execution was correct,
so the proof is an input rather than a result and there is nothing for the node
to simulate first. Siting public simulation on the node alone would split one
capability across two components and answer against finalised state rather than
the state the caller is building on. It belongs to the wallet API.
What a node can answer about a privacy-preserving transaction. Such a
transaction is not opaque in full: its public actions carry account identifiers
and post-states in the clear, and each private action carries a nullifier, a
commitment, the root it was proven against, and ciphertext. A node can report
that a private action occurred, prove a commitment's membership, and serve the
ciphertext; it cannot read the private account touched or the state it now
carries, and it cannot link a commitment to a nullifier. The requirements make
each read say which side of that boundary it answers for.
Errors specify distinctions, not codes. The RFP does not enumerate an error
taxonomy. It requires that errors be machine-readable and stable, that two
failures a caller would recover from differently never arrive as the same
value, and it names the distinctions a caller acts on. Which values exist is
the implementation's decision.
Artefact size, measured
The architecture offers a choice between an embedded node and a remote one, so
both libraries were built to see what embedding costs. At
--releaseforx86_64-unknown-linux-gnu, the LEZ node FFI is 33.6 MiB unstripped and 28.0MiB stripped; the Logos Blockchain FFI, which a LEZ node reads finalised state
from, is 84.5 MiB. An application embedding both starts near 112 MiB for one
protocol pair, against
bdk-android3.0.0 shipping 15.3 MiB for a wallet andfour chain backends. The LEZ figure excludes proving: the risc0
provefeatureis enabled by the wallet FFI and a benchmark tool alone.
Neither figure is a mobile one, and neither can be yet, because the ZK circuit
artifacts both libraries depend on are published for Linux, macOS and Windows
only. The section records that, and a candidate strategy for it: let the
published artefacts differ by platform, carrying the node on desktop and server
and omitting it on mobile, since a developer consuming a prebuilt library
cannot set a build feature.
Appendices
blockchain-api-sdk-ecosystem.md— 34 API functions across nine chains,with transports, SDK languages, response shapes, documented origin history,
and per-function gap notes for Logos L1 and LEZ.
logos-api-surfaces.md— as-built inventory of the LEZ indexer FFI andRPC, the sequencer RPC, the wallet FFI and
lez_coremodule, and the L1bindings, routes, and module.
wallet-libraries-ecosystem.md— where wallet functionality lives perchain, key derivation standards, watch-only support, and hardware and
offline signing. Input for the wallet API RFP rather than this one.
Zcash was added as a ninth surveyed chain in this branch. The RFP cited it in
six places while the appendix surveyed only eight, so those claims were
unsourced; sourcing them corrected three, including that Zcash carries no
testmempoolacceptand so has neither simulation nor an acceptance check.Sourcing
Every appendix claim carries a first-party URL that was fetched, and every
quotation was checked against the source text. Logos claims cite
path:linein the repositories at pinned commits.
The surface was also checked against its real consumers: the LEZ wallet UI and
the
lez_coremodule. Every sequencer method those depend on has an equivalentrequired here,
getProofsAndRootincluded.Note that the as-built component names are unchanged throughout:
lez_indexer_module,libindexer_ffiand the appendix's inventory still referto the indexer, which is what those artefacts are called. The rename applies to
the API this RFP defines, not to the code that implements it.
Known gap
The L1 chain identifier
query_network_identityreturns is inscribed in theLogos Blockchain genesis block and read at ledger initialisation, but no L1
route serves it and the node's Bedrock configuration carries only an endpoint.
Reaching it is work outside this RFP, recorded under Platform Dependencies, and
the surface stays deliverable meanwhile by reporting the value as unavailable
rather than guessing.
🤖 Generated with Claude Code