Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Ty Everett ([email protected])
The BRC-9 specification defines a process for Simplified Payment Verification (SPV) that allows payment recipients to verify the validity of a transaction without downloading and verifying the entire blockchain. The process involves verifying the headers and proofs of a BRC-8 Transaction Envelope, ensuring that all SPV proofs link back to the genesis block and the chain with the most proof of-work, and confirming that the transaction is valid according to the rules of Bitcoin. This standard aims to facilitate fast, secure, and efficient payment processing for merchants and users.
The Bitcoin blockchain is a decentralized, distributed ledger that records every transaction on the network. However, downloading and verifying the entire blockchain can be time-consuming and resource-intensive, making it impractical for users who want to make quick, simple transactions. Simplified Payment Verification (SPV) provides a way for payment recipients to verify the validity of a transaction without the need for a full blockchain download. This protocol is essential for enabling fast and efficient payment processing, especially for small, casual transactions.
The steps for verifying the validity of a payment transaction using the BRC-9 SPV protocol are as follows:
Ensure that the received Envelope is in the correct format
Add any previously-unknown headers provided in the Envelope to the chain of headers
Ensure that all SPV proofs link back to the genesis block and the chain with the most proof of-work
The above steps ensure that the transaction is valid and secure according to the Bitcoin protocol. Merchants may take additional measures to increase security, such as subscribing to mAPI status updates and double spend notifications, verifying the identity of the sender, waiting for confirmations, or using an escrow service.
Any wallet that has access to a source of block headers is capable of verifying merkle proofs, and any computer with the resources needed to execute the Bitcoin script programs from the chain of spends is capable of verifying the chain of custody. This means that almost any modern digital computer can easily perform Simplified Payment Verification by following the steps outlined in this document.
Ty Everett ([email protected])
The BRC-46 architecture for digital assets stored within wallet baskets enables a wide range of use cases. However, it lacks support for more than a rudimentary permission system. Wallets can grant applications blanket access to basketed assets, or deny access entirely, but cannot make insightful decisions based on the specific assets stored, their output scripts, or the tokenized value they represent. To support the future development of new permission schemes covering fungible and non-fungible digital assets within wallets, we propose reserving certain basket identifiers to prevent their use by applications and ensure compatibility with future standards.
The motivation for this proposal is to future-proof the architecture by enabling the seamless integration of new permission schemes applicable to assets stored in or retrieved from wallet-managed UTXO baskets. By specifying reserved identifiers, we can ensure that new security and permission paradigms can be implemented without conflicts or unintended behavior.
To accommodate future basket permission schemes, wallets must reject any operation requests made under basket IDs beginning with p (a lowercase “p” followed by a space).
Future permission schemes must define their ID formats, as follows:
The scheme IDs cannot contain spaces.
The basket IDs must start with p , followed by the scheme ID, a space, and the rest of the basket identifier.
A basket ID such as p dollarToken xxxxx could represent a specific token type (e.g., tokenized dollars), where:
p designates an alternative permission scheme.
dollarToken identifies the permission scheme.
xxxxx forms the basket ID under the alternative scheme.
Wallets must differentiate between standard and alternative permission schemes by recognizing the p prefix followed by a distinct, space-free scheme ID. To ensure unambiguous parsing.
Upon recognizing a basket ID structured as p <scheme ID> <rest of the ID>, wallets may apply the specific rules defined by the scheme associated with the scheme ID. These rules could define:
Constraints based on specific locking scripts or script templates of UTXOs.
The conditions under which operations can be executed.
Mechanisms for allowing applications to access only a certain set number of only a specific asset type, according to the rules of some tokenization protocol, overlay service or script template.
Specific counterparty requirements and other customizable permission attributes.
To maintain clarity and prevent conflicts:
Basket IDs beginning with p must be reserved for future use.
Wallets must reject operations involving such IDs unless they explicitly support the scheme ID.
A space must immediately follow the scheme ID to separate it from other elements.
This specification allows future permission schemes to extend beyond current models (e.g. paradigms), enabling flexible and innovative wallet permissions that evolve with user and application needs.
For example, a wallet could allow access to a maximum of 10 dollars of tokenized fiat money per month within an application.
By reserving basket IDs starting with p and specifying rules for future permission schemes,this specification ensures forward compatibility and robust wallet permission functionalities. It enables seamless integration of new schemes without disrupting existing applications or introducing parsing ambiguities.
Jane Doe ([email protected])
This proposal introduces an absurd, humorously impractical method for controlling embedded Bitcoin wallets in web-based applications using the power of bananas.
The Abstract section should concisely describe your proposal at a high-level.
The purpose of this proposal is to provide a lighthearted example of how to create a standard that adheres to the BRC format while sparking laughter and enjoyment among readers.
The Motivation section should let people know the context for your proposal, and why it was written.
Wallets must be embedded within a real banana fruit, using cutting-edge bio-organic engineering techniques.
Web-based applications must communicate with the banana-embedded wallet using a Banana Communication Protocol (BCP) involving a series of gentle squeezes.
Squeezes will be translated into binary code, with a short squeeze representing '0' and a long squeeze representing '1'.
The Specification section of your proposal should stipulate all information needed to implement the standard, and make up the bulk of the document. Generally, people should be able to create a compatible implementation with only the specification.
Wallet developers must carefully select the finest bananas available, ensuring they are both ripe and durable.
A custom JavaScript library, BananaJS, must be developed for web-based applications to interact with the wallet using the BCP.
Wallet applications must include a sophisticated squeeze detection system to ensure accurate communication.
Wallet developers must implement a "peel screech" alarm system for added security.
The Implementations section should contain information about places where the standard is implemented, or examples of its implementation.
1: Doe, J. (2023). Banana-Powered Bitcoin Wallet Control Protocol: A Humorous Guide. Bananaverse Press.
2: Smith, T. (2022). The Art of Banana Communication (Volume IV): Avoiding Single Points of Failure in Banana Communications. FruitTech Publishing.
The References section should contain any footnotes used throughout the document.
Ty Everett ([email protected])
This standard provides a script template that enables data-rich tokens on the Bitcoin SV blockchain, while still allowing for the representation of transfers of ownership. By pushing arbitrary data into stack elements and subsequently dropping them, followed by adding a simple P2PK lock, this script template allows for the creation of tokens with metadata, which can be exchanged on overlay networks. This standard facilitates improved scalability by representing tokens as UTXOs, which can be easily modeled and used in graph structures.
The need for this standard arises from the growing demand for data-rich tokens in the ecosystem. Current tokenization methods such as OP_RETURN lack output spendability and representation of ownership, making them unsuitable for many use cases. This standard allows developers to push arbitrary data onto the stack, while still enabling ownership transfer with a simple P2PK lock. This feature facilitates improved scalability by representing tokens as UTXOs, which can be more easily modeled and used in graph structures. Use-cases for these tokens are numerous, ranging from deeds to cars to metadata-rich digital assets. While it does not purport to solve every use-case, such as those requiring complicated spending constraints, this standard is a crucial step in the direction of enabling more sophisticated data-rich tokenization on the Bitcoin network.
We specify that output scripts must first push data onto the stack, followed by using OP_DROP and OP_2DROP to drop the pushed data. Next, the public key of the owner of the token is pushed, followed by OP_CHECKSIG. The unlocking script comprises a digital signature from the owner's public key, which facilitates spending of the token. Each token output must contain at least one satoshi. This standard does not define specific forms of tokens or specific requirements for higher-order overlays with validation rules for their transfer and conveyance.
The following is an example output script for a token as defined by this standard:
This script template has been implemented within the .
This script template enables tokenization on the Bitcoin SV blockchain by providing a simple and effective way to create data-rich tokens that are still spendable and locked to their owners. This is achieved by pushing arbitrary data onto the stack, dropping it with OP_DROP or OP_2DROP, and then adding a simple P2PK lock with a public key and OP_CHECKSIG.
When a token is created using this template, it is represented as an unspent transaction output (UTXO) that contains at least one satoshi. This UTXO can then be transferred to another user by creating a transaction that spends the UTXO and includes the owner's digital signature in the unlocking script.
The template facilitates tokenization by allowing developers to specify any number of stack elements with arbitrary data, representing tokens on overlays, while still enabling spending and ownership with simple locks. This makes it easier to model and use tokens in graph structures, improving scalability and enabling a wide range of use-cases.
Ty Everett ([email protected])
We propose a peer-to-peer token exchange protocol under which two parties can securely exchange digital tokens (assets) within Bitcoin SV's BRC-22 UTXO-based Overlay Networks.
Ty Everett ([email protected])
This document serves to clarify that there is no BRC-20 standard for tokenization on Bitcoin SV. While Ethereum has the ERC-20 standard for account-based tokens, there is no equivalent for UTXO-based systems such as Bitcoin SV. In order to avoid confusion and prevent people from conflating ERC-20 with BRC-20, it is necessary to clarify that BRC-20 does not exist on Bitcoin SV. Instead, proposals for tokenization should be judged on their own merits. Being associated with ERC-20 will not make any specific tokenization proposal better, and there is not a desire to confer greater legitimacy to one proposal or another.
Ty Everett ([email protected])
In BRC-22 overlay networks, various forms of state tracking for UTXOs can be incorporated. Servers can track UTXOs according to many criteria, enabling many use-cases. There is no fundamental requirement that a server relies on strictly on-chain data for these operations. In this document, we explore the use of off-chain secrets and offset values, enabling private overlay networks to be tracked among transactions that, on the surface, appear only to be normal P2PKH spends. Various models will be explored, from overlay network secrets to data linkages revealed off-chain. All of these methodologies rely on the isomorphic properties of the curve, enabling the owners of the UTXOs to retain spendability rights even when the P2PKH outputs also contain other information stored within offsets from the root key. This information is used to make on-chain record of private, off-chain data, which can later be revealed.
The motivation behind this protocol is the need for an efficient and secure mechanism to exchange different types of tokenized assets on top of overlay networks between two parties in the BSV ecosystem.
The protocol is initiated when two parties, Alice and Bob, desire to conduct an asset exchange. It assumes that both parties are online. We specify this protocol conceptually, by example:
Offer Initiation: Alice wants to sell 5 apples for 3 USD. She creates a new transaction moving her 5 apples into a new output. This output includes her exchange request, peer-to-peer contact information (such as BRC-33), and the asset ID for the asset she would like to exchange, reflected in a new transaction on the overlay network.
Revocation by the Initiator: If Alice decides to cancel her proposition, she can spend the offer output and move her apples back, retracting the offer.
Offer Acceptance: Bob is looking to purchase 5 apples for his 3 USD. He sees Alice's offer, verifying her offered UTXO(s) are registered on the overlay. Bob creates a new transaction, conditionally signing his USD over to Alice using SIGHASH_SINGLE. He ensures the signature is only valid over all the inputs from both Alice and Bob. The single output which he signs pays himself the 5 apples from Alice's inputs.
Offer Presentation: Bob contacts Alice, identifies himself and shares the transaction and signature with her.
Verification: Alice verifies the information and conducts due diligence to confirm Bob's outpoints on the overlay network. She verifies the signature and the validity of the transaction. If she decides not to proceed, she can simply do nothing or send Bob a rejection message.
Transaction Settlement: Alice responds by signing her inputs with a SIGHASH_ALL signature, spending her inputs, and adding an output that pays her Bob's 3 USD. She adds her signature to the transaction, and broadcasts it to Bob and the overlay network. Once her transaction reaches the overlay network, Alice now possesses Bob's 3 USD and Bob is the owner of Alice's 5 apples.
This Token Exchange Protocol can be implemented on any UTXO-based overlay network adhering to BRC-59, BRC-45, BRC-22 and BRC-24. This protocol has security built-in, stipulating both SIGHASH type usage and broadcasting and topical membership verification methods.
In general, overlay network recovery mechanisms for uncooperative participant handling (non-registration or improper broadcast of transactions) need to be defined as part of future specifications. Further tooling and guidance for developers would expand adoption.
As the Bitcoin ecosystem continues to mature and grow, there is an increasing desire to create standards for tokenization. However, it is important to recognize that the architecture of Bitcoin is fundamentally different from that of Ethereum. While Ethereum is an account-based system, Bitcoin is a UTXO-based system. This means that there is no direct equivalent for something like ERC-20, which defines account-based tokens on top of Ethereum. The goal of this document is to prevent unnecessary confusion.
UTXO-based systems are fundamentally different from account-based systems. While Ethereum has the ERC-20 standard for account-based tokens, there is no equivalent for UTXO-based systems such as Bitcoin. Tokenization will necessarily require a different approach, one which should not be conflated.
Avoiding confusion is essential. If there were a BRC-20 standard for Bitcoin SV, it would likely be confused with the ERC-20 standard on Ethereum. This could lead to a lack of clarity and understanding among developers and users, which could ultimately harm the growth and adoption of tokenization in both Bitcoin and Ethereum.
Proposals for tokenization should be judged on their own merits. The absence of a BRC-20 standard on Bitcoin SV does not mean that tokenization is not possible. Instead, proposals for tokenization should be evaluated based on their own technical merits and practical considerations. A proposal called BRC-20 will not be better on account of its name.
In summary, there is no BRC-20 standard for tokenization on Bitcoin SV. While there may be a desire to create such a standard, it is important to recognize that the architecture of Bitcoin is fundamentally different from that of Ethereum. Instead of trying to emulate the ERC-20 standard, proposals for tokenization should be evaluated based on their own technical merits and practical considerations. This approach will ultimately lead to a more mature and professional ecosystem for tokenization.
As with any other BRC, tokenization proposals will be assigned sequential numbering.
In the first use-case, a user sends a transaction to an overlay. The transaction pays some Bitcoins to a P2PKH address. The user reveals the public key to the overlay node at time of transaction submision. The user has computed the pblic key used as follows:
Take off-chain data (d) and hash it to get h
Add the hash to the actual recipient key to obtain the final public key
Now the user can also reveal to the overlay node the value for d which produced h, and can identify the party who is receiving it.
Ensure that all specified minerID public keys have endorsed all mAPI responses
Ensure that, if only confirmed inputs were allowed, every specified input transaction is directly proven with an SPV proof rather than relying on a chain of mAPI responses
Ensure that the transaction is valid according to the rules of Bitcoin (sum of input amounts is greater than or equal to the sum of output amounts)
Ensure that the lock time of the transaction is as requested in the invoice
Ensure that, if the lock time is in the future, all sequence numbers are UINT_MAX, unless you are working with an application that supports continuously-updating payment channel transactions and expect to get another updated transaction later
Ensure that evaluating the locking scripts from the input transactions and then the unlocking scripts provided in each input is successful, thus ensuring that all inputs were properly spent
Ensure that the transaction was properly signed
Ensure that the transaction contains the output scripts and amounts that were requested in the invoice
Ensure that the transaction pays a sufficient fee according to the rules of the fee model you are using
Wallets will be recharged by leaving them in direct sunlight for a minimum of 2 hours per day.
The protocol must support a minimum of 2 bananas connected simultaneously to avoid a single point of failure2.
Web-based applications must visually display the connection and energy status of each banana wallet.
To avoid ambiguity in referring to the TSC Merkle Proof Standardized Format, it is important to have a specific identifier. The use of BRC-10 as a reference point allows for clear and unambiguous communication among developers and the community. The BRC-10 designation is similar to other proposals, such as BRC-32, which are also referenced by their BRC numbers.
You can read the TSC standard on the website.
As with any other BRC, feel free to open issues in the BRC repository that discuss extensions of BRC-10.
Ty Everett ([email protected])
This document proposes a solution for tracking transaction histories within UTXO-based overlay networks by extending the BRC-22 standard to include transaction inputs and the BRC-24 standard's query responses to include such data in extended BRC-36 input envelopes. By associating previous transaction inputs with topic-specific UTXOs and facilitating history traversal, it allows network participants to access a more complete record of UTXO histories. This standard outlines the parameters and steps needed to capture, maintain, and access historical transactional data in overlay networks.
BRC-22 successfully enables the tracking and synchronization of UTXOs across various topics in overlay networks, yet it does not accommodate the preservation of UTXO histories which can hold valuable network data. Transaction histories are key to understanding the full lifecycle of UTXOs, acting as a crucial component for certain applications in areas like network analysis and auditability. The absence of a history tracking standard leads to information gaps, preventing a comprehensive understanding of network state changes. This document presents a solution to this problem by providing a clear and standardized mechanism for maintaining and accessing transaction histories in UTXO-based overlay networks.
We introduce extensions to the BRC-22 and BRC-24 standards to encapsulate the input history of network transactions. Topic managers in BRC-22 can now prescribe which transaction inputs should be conserved and linked with the admitted transaction outputs, designating inputs that are pertinent for tracking. The BRC-24 standard, meanwhile, is adjusted to support the retrieval of past renditions of UTXOs.
Topic managers in overlay networks now perform the following additional steps:
Verify the inputs from the transaction tagged with their topic labels to determine their relevance.
Provide a return value back to the Confederacy node directing it to retain these relevant transaction inputs, so that the node can maintain them alongside admitted transaction outputs for the specified topic.
Relative location metadata and other associated data should be stored by the Confederacy node along with these inputs, enabling efficient topic-specific historical data retrieval.
The lookup services specified under BRC-24 can now:
Accept queries for the previous renditions of specific UTXOs (formats for these queries depend on the specific lookup service).
Assemble and return a responsive list comprising of the series of transactions that involve the queried UTXOs along their states across the network's history.
Extend the BRC-36 envelope return format, including transactions as inputs when they contribute to the history of the queried UTXOs.
Developers should adapt their existing BRC-22 based systems to collect and preserve pertinent transaction inputs when admitting new transaction outputs. Lookup services following the BRC-24 standard must be updated to handle queries for prior renditions of UTXOs and effectively traverse transactional histories, extending their BRC-36 envelope returns to incorporate key transaction input data for the queried UTXOs. By adhering to the changes specified in this document, network participants can ensure interoperability, efficient data access and complete historical tracking of UTXOs across overlay networks.
Ty Everett ([email protected])
Currently, there are only HTTPS URLs within SHIP and SLAP advertisements
This https: scheme indicates that, according to pre-defined rules (like headers and the /submit or /lookup paths), submission or lookups are facilitated over the HTTPS protocol.
However, sometimes we want to do things like:
Authenticate users before accepting transactions or facilitating lookup
Charge a payment for transaction submission, or pay the sender if a transaction is accepted
Charge a payment for lookup queries
Submit private or off-chain values alongside a transaction
Submit non-final transactions that deal with interim states
Facilitate real-time lookups with WebSocket or based on live / non-final transactions
Advertise certain IPv6 capabilities and bridges
Advertise non-HTTPS or non-internet communications systems like radio / JS8 Call
In these scenarios, other schemes can be used within the "protocol" portion of the URL.
For example, current SHIP/SLAP only contemplates URL schemes like https://example.com.
However, a new URL might be something like:
SHIP https+bsvauth+smf://example.com (HTTPS with BSV Auth and Service Monetization Framework enabled)
SHIP https+bsvauth+scrypt-offchain://example.com (HTTPS with BSV Auth and sCrypt off-chain values for transaction submission)
SHIP https+rtt://example.com (HTTPS with real-time transacting support, e.g. non-finals accepted)
SLAP wss://example.com (real-time event-listening live web-socket lookup response streaming)
SLAP js8c+bsvauth+smf:?lat=40&long=130&freq=40meters&radius=1000miles (lookup is advertised using JS8 Call protocol at a given set of GPS coordinates, a given frequency and radius, with BSV Auth and Service Monetization Framework enabled.
These new SHIP and SLAP schemes allow for the advertisement of new Overlay Services that have more advanced capabilities, including real-time updates from lookup services, non-final transaction submission, mutual authentication, payment, off-chain / private value submission, non-HTTP transport mechanisms, and much more.
Compared to plain HTTPS, they make a significant improvement. Facilitators for each of these custom "protocol" fields in the advertised SHIP/SLAP URLs can be implemented into standard libraries, the associated lookup services can be updated to facilitate querying by them, and new default SLAP trackers can be added so that users are able to stay connected in more ways than one.
<arbitrary data> <arbitrary data> <arbitrary data> OP_DROP OP_2DROP <public key> OP_CHECKSIGCertain checks that are unique to each implementation, such as detecting encoding errors or malformed transactions, are not covered here. Although these checks are essential, they vary according to the implementation and are not included in the definitive list presented below.
Checks made on receipt of a transaction from a counterparty:
Script evaluation of each unlocking script results in TRUE.
The sum of the satoshis-in must be greater than the sum of the satoshis-out.
Each input must be associated with a Merkle path to a block.
nLocktime, and nSequence of each input are set to the expected values.
Often this is referred to as "check the signatures" which is indeed usually the case but it is possible to have transactions which do not require signatures so for the sake of technical exactitude - running script evaluation is really what is happening here. The interpretation requires that each input unlocking script is concatenated with the previous locking script and the interpreter runs given that input. The result should be a truthy value left on the stack after execution, otherwise the predicate has failed and the utxo has not been unlocked.
Each input is a pointer to a previous output, each output has a satoshi value. Therefore to calculate the input satoshis we must have details of the previous transactions from which we are spending utxos. We use this information to determine the fees paid by the transaction. We compare these fees with the size of the Tx in bytes to arrive at a rate: sats/byte. The acceptable rate is well known, various services publish this information. At the time of publishing this rate is equivalent to 1 satoshi for a standard transaction, therefore the check could simply be:
If all inputs come from Transactions which are mined in a block, then the associated Merkle path is one which leads that txid to a Merkle root. If some inputs are not in a block then we must include previous transaction data, and follow the history of all inputs until we arrive at a point where all inputs are associated with previous inputs which appear in a mined block. Various formats will handle this differently, but the universal rule is that SPV requires that we prove that all inputs come from legitimate transactions.
In a long chain of transactions conducted while not connected to the internet, each new transaction is appended to the end of the SPV data such that all prior transaction ancestry is propagated to all counterparties, until they are broadcasted. So we would expect large SPV data payloads only when many transactions happen offline, which in today's age will be extremely rare.
The nLocktime default is 00000000, and nSequence default is FFFFFFFF. If these values are not default then there is a naunced condition to the transaction which is explained here.
Ty Everett ([email protected])
We define a mechanism by which BRC-1 applications can denote UTXOs to be unlocked and redeemed by wallets as part of new Bitcoin transactions. We extend the message format defined by BRC-1 with information about the inputs requested by the application, including all information needed for a wallet to check the veracity of any inputs being redeemed.
defines a mechanism for an application to request the creation of a Bitcoin transaction by a wallet, but it is incomplete without a way for applications to consume and use tokens that previously existed. Allowing applications to unlock and redeem inputs as part of their transactions also facilitates their ability to update, extend or delete assets that are tokenized within Bitcoin UTXOs.
We extend the Transaction Creation Request message with an additional field, the inputs field. This is an object whose keys are the TXIDs of transactions that contain outputs which are to be spent as part of this transaction, and whose values comprise extended transaction envelopes.
In addition to the normal envelope fields, we specify that these input envelopes contain an additional field called outputsToRedeem, which is an array of objects. Each of the objects comprises an output from the subject transaction that is to be redeemed and used as input to the transaction being requested by the application.
We specify that each of the objects in the array contains index and unlockingScript. The index value is an integer that denotes which output to redeem from the transaction, and the unlockingScript comprises a hex-formatted input script.
We further specify that each of the elements in each of the outputsToRedeem arrays may contain an additional spendingDescription string that describes the redemption of the tokens being used.
Here is an example of a Transaction Creation Request object that contains an input:
This functionality has been implemented as part of the , in the createAction function.
Ty Everett ([email protected])
BRC-65 extends the functionality of BRC-56 by introducing the ability to label Bitcoin transactions when they are created using the BRC-1 Transaction Creation Request. This extension allows applications to organize and categorize transactions for different purposes. BRC-65 also introduces the capability to list labeled transactions, providing applications with an easy way to retrieve specific sets of transactions based on their labels. This standardization improves interoperability between wallets and applications and enhances the user experience by enabling the display of transaction lists relevant to specific categories or actions.
The motivation behind BRC-65 is to enhance the functionality of the Bitcoin wallet messaging layer defined in BRC-56 by introducing the ability to label transactions. This functionality allows applications to categorize and organize transactions based on specific criteria. By labeling transactions, applications can easily retrieve and display transaction lists that are relevant to specific actions or categories. This simplifies the user experience and enables users to quickly find, review, and analyze specific sets of transactions.
By defining a standard mechanism for labeling transactions and listing labeled transactions, BRC-65 promotes interoperability between wallets and applications. With this standardization, applications can expect consistent behavior across different wallets, making it easier for developers to create Bitcoin-powered applications without having to build custom wallet functionality. Furthermore, users can switch between wallets seamlessly without losing access to their labeled transactions.
BRC-65 extends the functionality of BRC-56 by introducing the concept of transaction labels and the ability to list labeled transactions. This extension adds two new features to the wallet-to-application messaging layer: labeling transactions at the point of creation and retrieving labeled transactions.
The labels field is introduced as an optional parameter next to the outputs array and description of the BRC-1 Transaction Creation Request in BRC-56. This field allows applications to label transactions. Each label must be no longer than 300 characters and can only contain letters, numbers, and underscores.
The updated Transaction Creation Request with the labels field is as follows:
In this example, the application is creating a transaction with the labels "payment" and "personal". These labels can be used to categorize and organize the transaction in a way that is meaningful to the application.
BRC-65 introduces the listActions message (the Actions List Request) to retrieve a list of Bitcoin transactions based on their labels. The Actions List Request message takes the following parameters:
The Actions List Request message retrieves Bitcoin transactions that have been labeled with the specified label. The skip parameter allows applications to retrieve a subset of transactions by skipping a certain number of transactions from the beginning of the list. The limit parameter defines the maximum number of transactions to return. If skip and limit are not provided, the request will return all transactions with the specified label.
The Actions List Response comprises an array of BRC-8 Transaction Envelopes as defined in BRC-56, representing the labeled transactions, along with a totalActions field denoting the total number of transactions with the specified label.
An example Actions List Request message with the label "payment" and a limit of 10 transactions is as follows:
The corresponding Actions List Response would contain an array of BRC-8 Transaction Envelopes as well as the totalActions field:
In this example, the actions list response includes an array of BRC-8 Transaction Envelopes representing the labeled transactions with the label "payment". The totalActions field indicates that there are a total of 42 transactions with the "payment" label.
Implementations of this specification will need to extend the existing wallet software in order to support the labeling of transactions and the listing of labeled transactions. Wallets will need to ensure that they can create transactions with labeled outputs and store the labels for future retrieval. Applications will need to handle the labeling of transactions and make use of the Actions List Request to retrieve labeled transactions.
Some existing implementations of BRC-56 may already support the labeling and listing of transactions, while others may need to update their software to include this additional functionality. The Babbage MetaNet Client, for example, has already implemented the Transaction Labels and List Actions functionality.
Ty Everett ([email protected])
This document proposes an examination of Unspent Transaction Outputs (UTXOs) as the central framework of tokenization in Bitcoin, justifying their usage based on their inherent simplicity, efficiency, and secure transfer processes. Through UTXOs, Bitcoin allows the delineation of rules governing the transfer of valuable tokens via scripting. In contrast to alternative tokenization proposals, UTXOs render the need for comprehensive, trusted indexing and chain scanning systems obsolete. Instead, transaction parties require only to validate transactions pertinent to them, employing miners to mitigate double-spend attempts, thus achieving tokenization of assets at scale.
Tokenization, in the realm of digital assets, represents the process of substituting valuable, real-world assets with digital tokens on a blockchain. Within the context of Bitcoin, tokenization predominantly revolves around Unspent Transaction Outputs (UTXOs).
Unspent Transaction Outputs (UTXOs) are remnants of Bitcoin transactions yet to be spent or used as inputs for newer transactions. Each UTXO in Bitcoin can be seen as a distinct token. Unlike other tokenization methods, UTXOs are directly transferred from sender to recipient, the legitimacy of which can be independently verified, thus significantly reducing computational and time complexities.
Scripts in Bitcoin define the conditions under which UTXOs can be spent. These conditions form the essence of token transfer rules, ensuring secure transactions of digital tokens.
Simplified Payment Verification (SPV) is a method used in Bitcoin for lightweight clients to verify transactions without requiring the entire blockchain's download. The process for SPV enables recipients to verify transfers quickly.
UTXOs offer a unique advantage as the foundational unit of tokenization in Bitcoin due to their inherent characteristics and the robustness they bring to the transaction system:
Decentralization and Trust Minimization: With UTXOs, parties need only to validate transactions that concern them, eliminating the necessity for a trusted intermediary to index and scan every transaction on the blockchain. This process enhances the decentralized nature of Bitcoin and minimizes trust requirements.
Scalability: By treating UTXOs as the base unit of tokenization, we can facilitate efficient asset tokenization at scale. With direct transfers, the UTXO model bypasses complex indexing systems, thus reducing computational overhead and increasing transaction speed.
Double-Spend Protection: Miners play an integral role in UTXO transactions by preventing double-spending. As each UTXO can only be spent once, miners verify and confirm that no UTXO is duplicated or spent twice, preserving the security of the blockchain.
In summary, the UTXO model provides an efficient and scalable framework for tokenization in Bitcoin, while upholding the principles of decentralization and trust minimization. Its distinctive attributes such as direct transfer, double-spend prevention, and simplified verification lend themselves to a secure and streamlined tokenization process, validating the assertion that UTXOs are the fundamental unit of tokenization in Bitcoin.
The OP_FALSE OP_RETURN script template is a method for storing data on the Bitcoin SV blockchain. It creates a non-spendable output and appends the desired data to the end of the script for storage. This standard aims to define the rules and best practices for using this script template.
The low transaction fees and high capacity of the Bitcoin SV network make it an attractive option for storing data on the blockchain. The OP_FALSE OP_RETURN script template is a widely used method for achieving this goal due to its simplicity and ease-of-use.
To use the OP_FALSE OP_RETURN script template, an output must be included in a Bitcoin transaction with a locking script comprising OP_FALSE followed by OP_RETURN, followed by the data to store in the script. The data can be pushed into one or multiple stack elements after the OP_RETURN opcode.
For example:
Several implementations and protocols make use of the OP_RETURN script template, including the and .
The main limitation of using OP_FALSE OP_RETURN is the non-spendability of these outputs and their ability to be pruned by miners. They are records rather than tokens. Artefacts predominantly as a proof of existence of data at a certain time - timestamped by the Bitcoin system as a hash with a merkleproof to a block, even if miners themselves prune the data.
Ty Everett ([email protected])
This BRC extends by adding the ability to remove specific outputs from a basket and delete digital certificates that are no longer required. Applications can request that a wallet remove outputs from a basket by providing the transaction ID (txid) and the output index (vout
Tone Engel ([email protected])
The BEEF serialization format (as defined in ) is essentially two things:
An array of mined transaction proof validation data (BUMPS )
Ty Everett ([email protected])
Bitcoin uses scripts to control transactions, and three of the most common ways to represent a script are binary, hexadecimal, and ASM. This standard aims to provide a detailed description of these formats to facilitate their use in BSV transactions.
Ty Everett ([email protected])
This document describes the Universal Hash Resolution Protocol (UHRP), a standard framework for content availability advertisement implemented with a UTXO-based overlay network. UHRP enables content hosts to advertise the availability of a particular file by creating a UTXO-based advertisement token, which is then submitted to the UHRP overlay and tracked by overlay network nodes. UHRP provides resilience to single points of failure and ensures content accessibility even if one host is offline, because multiple entities can be paid by content providers to keep content available.
Deggen ([email protected])
We specify a paymail capability extension which supports the passing of BEEF Transactions between hosts. The procedure for service discovery and requesting outputs is not detailed, rather this proposal contains only a recommendation to replace hex data with hex data.
Ty Everett ([email protected])
This document proposes standardized naming conventions for topic managers in BRC-22 and lookup services in BRC-24 to ensure clarity, interoperability, and uniformity across different implementations. The goal is to provide consistent and easily understandable names that facilitate efficient communication and integration between overlay network nodes and services within the BSV ecosystem.
This BRC standard outlines the implementation and use of bare multi-signature (multi-sig) transaction output scripts within the Bitcoin SV digital asset ecosystem. By employing OP_MULTISIG opcodes directly, this approach offers simplicity and ease of implementation while providing enhanced security and access control for transactions. However, it also highlights the trade-offs in terms of privacy for participants. The standard comprises a motivation section, detailing the benefits of bare multi-sig transactions; a specification section, explaining the structure of these transactions; an example section, demonstrating a 2-of-3 multi-signature locking and unlocking script; and a "how it works" section, delving into the fundamentals of bare multi-sig transactions and their role in the Bitcoin SV ecosystem.
Ty Everett ([email protected])
In one view of Bitcoin, only spendable output scripts with satoshis attached constitute valid Bitcoin tokens, because Bitcoin tokens are UTXOs. Since traditional scripts are not compatible with this view, we propose a methodology for creating OP_RETURN scripts that are spendable and contain satoshis. This provides a way for software to automatically convert non-compliant OP_RETURNs into proper Bitcoin tokens while encouraging developers to consider the spendability constraints that govern their tokens.
Ty Everett ([email protected])
This document builds upon the insights of and proposes a scalable, efficient IPv6 multicast protocol specifically designed for the broadcast of blockchain transactions and the delivery of transaction status updates, such as merkle proofs and double-spend attempt notifications. Recognizing the limitations of existing IPv6 multicast protocols, including Multicast Listener Discovery (MLD), this protocol addresses the needs for massive scalability, efficient data delivery, and economic sustainability required by modern blockchain networks. This protocol employs a layered approach using MLDv2 at the edges, multicast Border Gateway Protocol (mBGP) at the core, and introduces an intermediate aggregation/summarization layer to ensure global scalability and efficiency.
This document is intended for network engineers and architects familiar with IPv6 and multicast technologies but may not have extensive knowledge of blockchain technology. The technical details provided aim to bridge the knowledge gap and foster understanding of the specific network demands and solutions in blockchain operations.
Ty Everett ([email protected])
We define a set of reserved protocol namespaces that can be employed by clients utilizing the invoice numbering scheme to be set aside for administrative and internal use by the client software itself. This enables client software to manage its own internal state without the risk that application software will utilize the same internal protocols.
sumInputAmounts > sumOutputAmountsBitcoin transactions are the mechanism for transferring custody of bitcoin tokens from one party to another. It is crucial to have a clear and unambiguous specification for their format. The raw hex format is widely-used and understanding its structure is important for developers and other stakeholders in the Bitcoin ecosystem.
A Bitcoin transaction consists of a version number, a locktime value, a list of inputs, and a list of outputs. The format for a raw hex Bitcoin transaction is as follows:
Version: 4-byte integer (little-endian)
Input Count: variable-length integer
Inputs: a list of input objects, where each input object has the following fields:
Previous Transaction Hash: 32-byte hash (little-endian)
Previous Transaction Output Index: 4-byte integer (little-endian)
Script Length: variable-length integer
Unlocking Script: variable-length script
Sequence Number: 4-byte integer (little-endian)
Output Count: variable-length integer
Outputs: a list of output objects, where each output object has the following fields:
Value: 8-byte integer (little-endian)
Script Length: variable-length integer
Locking Script: variable-length script
Locktime: 4-byte integer (little-endian)
The variable-length integer is a compact representation of an integer value. The first byte of the integer determines the format of the integer:
If the first byte is less than 0xfd, then the integer is that byte value.
If the first byte is 0xfd, then the integer is the next two bytes in little-endian format.
If the first byte is 0xfe, then the integer is the next four bytes in little-endian format.
If the first byte is 0xff, then the integer is the next eight bytes in little-endian format.
The script fields in the input and output objects are interpreted as bytecode for a Bitcoin Script, which is a stack-based language used to define spending conditions for bitcoin.
The transaction hash (referred to as the "TXID") is calculated by taking the double-SHA256 hash of the entire transaction. This hash is used as a unique identifier for the transaction on the Bitcoin network.
Currently, there is no unified naming convention for topic managers and lookup services utilized in BRC-22 and BRC-24 standards. This absence can lead to confusion and errors in integration.
Network services that deal with the same transactions and protocols should negotiate to agree upon a common name for their topic managers and lookup services. This ensures resiliency, in that network users are able to access the same services at known locations across nodes, even if one node in an overlay network goes down. It also aids node operators in synchronizing relevant transactions with one another.
Consistency: Names should be consistent across different implementations to avoid ambiguity.
Clarity: Names should be clear and descriptive enough for developers to understand their purpose.
Length: While names should be descriptive, they should also be concise to ensure ease of use in configurations and codebases.
Only lower-case letters and underscores.
Must not start or end with an underscore.
No consecutive underscores.
No longer than 50 characters.
Prefix: Use the prefix tm_ to indicate a topic manager.
Topic Identifier: Follow the prefix with a short descriptor of the topic.
Example:
tm_uhrp_files
tm_tempo_songs
Prefix: Use the prefix ls_ to denote a lookup service.
Service Identifier: Follow the prefix with a brief descriptor of the lookup service functionality.
Example:
ls_uhrp_files
ls_tempo_songs_search
Each overlay network node and service provider must update their naming conventions to comply with the standardized guidelines described in this document. This update involves renaming existing topic managers and lookup services accordingly and ensuring that new implementations follow these standardized conventions.
By standardizing naming conventions for topic managers and lookup services, we can significantly enhance the clarity, consistency, and interoperability of overlay networks on the BSV blockchain.
Simplified Verification: The BRC-9 process for SPV enables easy verification of transfers without needing the full blockchain data. In UTXO-based transactions, the recipient can independently validate the transaction, making it efficient and user-friendly.
In the digital world, consumers often require access to specific content without regard for the hosting source. Content providers, on the other hand, have a vested interest in maintaining wide availability of their offerings, even during host outages. Existing systems, however, do not provide a standardized and robust means of connecting users to their required files.
UHRP addresses this by enabling multiple content hosts to advertise the availability of content using UTXOs. Users can seek the content based on its hash, ensuring they connect to the files they need via an overlay network that is resilient to single points of failure. Content providers wishing to ensure consistent availability can pay multiple hosts, so that content remains available even if one host goes down.
This new paradigm not only streamlines content availability but also enables new hosts to build reputation by demonstrating opportunity cost (for example, by paying higher than normal transaction fees to miners on hosting commitments). Over time, service providers and applications can build a trusted list of UHRP hosts, thereby improving the overall reliability and trustworthiness of the content hosting ecosystem.
A UHRP advertisement token comprises a Bitcoin UTXO with a locking script containing the following components:
<public key>: This is the public key associated with the host that is making the advertisement.
OP_CHECKSIG: Ensures that if the token becomes spent, a signature was made using the host's private key.
Protocol prefix: A protocol prefix pushed onto the stack, with a value of 1UHRPYnMHPuQ5Tgb3AF8JXqwKkmZVy5hG.
<address>: The base58 version of the address corresponding to the <public key>.
<hash>: The next 32 bytes are the SHA-256 hash of the content being advertised.
<url>: The HTTPS URL where the host is advertising that the content is available for download.
<expiryTime>: A UTF-8 encoded version of a decimal integer representing the Unix timestamp of the advertisement expiry.
<contentLength>: A UTF-8 encoded version of a decimal integer representing the byte length of the content.
<signature>: A digital signature over the concatenated fields fields from the host's <public key>.
Upon creation and submission of an advertisement to a BRC-22 overlay network node for the UHRP topic, the node will track the UTXO and facilitate its lookup via a BRC-24 lookup service with the provider name UHRP. If the token ever becomes spent, the advertisement is canceled. The spending transaction may contain more information justifying the host's revocation (shch as a DMCA notice), but any such stipulation is beyond the scope of this initial document, and left for others to specify later.
Tools such as NanoSeek (for downloading) and NanoStore-Publisher (for uploading to a NanoStore provider) have been created by the Babbage team. To implement the serialization format, the uhrp-url tool can be used.
Questions about this initial draft specification should be directed to the author.
With the increasing volume of transactions in blockchain networks and the need for rapid propagation, traditional methods of transaction broadcast and update delivery are becoming inadequate. MLD, as noted in BRC-80, provides mechanisms for IPv6 hosts to report interest in multicast groups, but does not scale to the needs of global blockchain operations, which involve potentially trillions of multicast groups.
MLDv2 allows IPv6 nodes to report not only their interest in specific multicast groups but also to specify interest in blocks of addresses. This capability is crucial for managing the high volume of multicast groups involved in blockchain networks.
Local Router Management: Routers at the network edge use MLDv2 to manage local subscriptions efficiently, dynamically handling group memberships as user interests change.
mBGP manages the exchange of multicast routing information between autonomous systems, crucial for the global delivery of multicast traffic.
Global Traffic Routing: mBGP ensures that multicast traffic is routed efficiently across different ISPs and backbone networks, based on aggregated routing information that represents the interests of multiple local networks.
An intermediate layer that aggregates multicast interests into larger, more manageable groups significantly reduces the complexity and volume of routing information needed at the global level.
Regional Aggregation: Dedicated multicast routers or servers summarize the multicast group information from multiple local networks, reducing the granularity of information propagated globally.
Nodes subscribe to multicast groups relevant to their interests via MLDv2, which informs local routers of these interests. When transactions are broadcast or updates are sent, they are addressed to specific multicast groups based on transaction attributes such as version numbers and portions of transaction IDs.
Local Handling: Local routers manage multicast traffic within their networks based on MLDv2 reports.
Global Distribution: mBGP is used by ISPs to route traffic globally based on summarized interests from the aggregation layer.
Efficiency and Scalability: The aggregation/summarization layer updates mBGP information periodically, balancing the need for real-time accuracy with global routing efficiency.
To ensure the economic sustainability of this multicast architecture, a mechanism for out-of-band payments must be implemented alongside these lower-level routing protocols, allowing nodes to compensate network providers for the data they consume. This payment mechanism should be designed to prevent DoS attacks by making it costly to spam the network with unnecessary subscriptions. MLDv2 subscriptions or mBGP advertisements made without respect to an associated payment must be ignored and dropped.
Payment Integration: Implement a secure, blockchain-based payment system for subscription services to ensure that providers of network resources are compensated for their services. This is an area of future work.
Security Measures: Implement robust security measures to authenticate and authorize multicast subscriptions to prevent unauthorized and costly subscriptions that have not been paid for.
Monitoring and Management: Deploy real-time monitoring tools to manage multicast traffic flows and performance across all layers of the network.
Further research is needed to refine the aggregation algorithms and to enhance the scalability of the mBGP routing updates. Additionally, exploring the integration of an out-of-band payment mechanism for these multicast communications at scale will be crucial.
The proposed protocol provides a comprehensive solution for the scalable and efficient distribution of blockchain transaction data across global networks using IPv6 multicast. By leveraging MLDv2 at the edges for dynamic subscription management, mBGP at the core for robust global routing, and an aggregation/summarization layer in between, this protocol addresses the unique challenges posed by the high data volumes and rapid dynamics of modern blockchain networks.
This document serves as a foundational step towards implementing a robust multicast protocol tailored for blockchain applications, ensuring efficient data dissemination and economic viability.
BRC-431 defines an open-ended way to create protocols and systems of interaction within a BRC-422 key derivation architecture. However, client software implementing the BRC-431 invoice numbering scheme needs a way to manage its own internal state, encrypt data and perform administrative tasks like permissions management without interference from applications.
With this specification, we define a list of namespaces in which applications are never allowed to derive keys, and any client that follows this specification will refuse requests made by applications to perform these operations.
We reserve the following protocol IDs for the administrative and internal use of clients, no matter the security level:
Any protocol ID that starts with admin
label
The label to filter the transactions by.
skip
The number of transactions to skip before returning results (optional).
limit
The maximum number of transactions to return (optional).
Applications need the ability to remove specific outputs from a basket when they are no longer needed, without having to spend them. Furthermore, certificates may need to be deleted when they expire or are no longer relevant. This functionality allows for tokens and certificates to be managed more efficiently within a wallet, improving user experience and reducing clutter.
This BRC introduces two new message types to the BRC-56 messaging layer:
The Output Basket Removal Request message is sent by an application to request the removal of a specific output from a basket. It contains the following fields:
txid
The transaction ID of the output to be removed.
vout
The output index of the output to be removed.
basket
The name of the basket from which the output will be removed.
The wallet will validate the request and remove the requested output from the specified basket if it exists. If the requested output does not exist or the user denies permission, the wallet will respond with an error.
The Output Basket Removal Response message is sent by the wallet in response to an Output Basket Removal Request. If the removal was successful, the response will contain a success message. Otherwise, if the request was denied or the output could not be found, an error message will be included.
If the wallet is unable to fulfill the Output Basket Removal Request for any reason, it will respond with a JSON-formatted Output Basket Removal Error. The fields for the error message are as follows:
status
This should always be the string "error".
code
A machine-readable error code.
description
A human-readable description of the error.
One example of an Output Basket Removal Error is as follows:
The Certificate Deletion Request message is sent by an application to request the deletion of a specific digital certificate. It contains the following fields:
certifier
The certifier responsible for the certificate.
serialNumber
The serial number of the certificate.
certificateType
The type of the certificate to be deleted.
The wallet will validate the request and delete the specified certificate if it exists. If the certificate does not exist or the user denies permission, the wallet will respond with an error.
The Certificate Deletion Response message is sent by the wallet in response to a Certificate Deletion Request. If the deletion was successful, the response will contain a success message. Otherwise, if the request was denied or the certificate could not be found, an error message will be included.
If the wallet is unable to fulfill the Certificate Deletion Request for any reason, it will respond with a JSON-formatted Certificate Deletion Error. The fields for the error message are as follows:
status
This should always be the string "error".
code
A machine-readable error code.
description
A human-readable description of the error.
One example of a Certificate Deletion Error is as follows:
Implementations of this specification will need to extend the existing implementation of BRC-56 to include the functionality for output basket removal and certificate deletion. The wallet will need to handle incoming request messages, validate them, and perform the specified operations. The application will need to handle the response and error messages accordingly.
The use of scripts in Bitcoin SV transactions allows for greater flexibility and customization of transaction rules. To properly utilize these scripts, it is important to understand the different formats they can be written in. This standard aims to provide a clear and detailed description of the binary, hexadecimal, and ASM formats used in BSV scripts to facilitate their use in BSV transactions.
We specify three commonly-used Bitcoin script formats as follows:
The binary format is a series of bytes that represent the script. Each opcode in the script is represented by a single byte, with any arguments or data being represented by subsequent bytes. The script is executed sequentially, with each opcode performing a specific action on the stack. Bitcoin script uses reverse polish notation.
The hexadecimal format is a string of hexadecimal digits that represents the binary script. Each byte in the script is represented by two hexadecimal digits. The following is an example of a script in hexadecimal format:
Not to be confused with the full BRC-15 Assembly Language.
The ASM format is a human-readable format that represents the script in a more intuitive way. Each opcode and its arguments are represented by a single string. For example, the following script in ASM format checks that a given public key has signed the transaction:
The SASM format is a shortening of the above ASM format which uses lowercase and removes the OP_ prefix from op codes to aid in quick typing and clearer readability. For example, the above ASM script is repeated now in SASM:
BSV scripts can be written in any of the above formats and included in a transaction as a locking script or an unlocking script. To create a script in binary format, the opcodes can be written in hexadecimal and then converted to binary. To create a script in hexadecimal format, the binary script can be converted to hexadecimal. To create a script in the ASM format, the opcodes and arguments can be written as a string in the appropriate format.
There are various libraries and tools available for creating and working with BSV scripts in these formats, such as the bitcoin-cli command-line tool for the node software. These tools allow for the creation and manipulation of BSV scripts in any of the supported formats.
Bare multi-sig transactions offer a simple and straightforward method to enable multiple parties to authorize a transaction in a decentralized manner. This approach is particularly useful for securing funds, enhancing trust between parties, and enabling flexible access control. Although bare multi-sig transactions come with certain privacy trade-offs, their ease of implementation and direct use of low-level Bitcoin scripting constructs make them a valuable option in the Bitcoin SV ecosystem.
A bare multi-signature transaction output script adheres to the following structure:
minimum_signatures: The minimum number of signatures required to unlock the funds (also known as the "M" value).
pubkey1, pubkey2, ..., pubkeyn: The public keys involved in the multi-signature scheme.
maximum_signatures: The maximum number of public keys (also known as the "N" value).
OP_CHECKMULTISIG: The Bitcoin opcode that validates the provided signatures against the specified public keys.
The following example demonstrates a 2-of-3 multi-signature locking script:
Locking Script:
To spend the funds locked by this script, an unlocking script containing the required signatures must be provided:
In this example, the OP_0 opcode is required due to a known bug in the original implementation of the OP_CHECKMULTISIG opcode, which results in an extra item being consumed from the stack.
Bare multi-sig transactions work by requiring a minimum number of signatures from a given set of public keys to unlock the funds. When the locking script is executed, the OP_CHECKMULTISIG opcode verifies if the provided signatures correspond to the specified public keys and meet the minimum signature requirement. If the validation succeeds, the funds are unlocked and can be spent in a new transaction.
While bare multi-sig transactions offer simplicity in implementation, they also expose the public keys and the multi-signature scheme directly in the transaction output script, which may reveal information about the participants and their relationships. Despite these privacy concerns, bare multi-sig remains an important tool for creating secure and flexible transactions within the Bitcoin SV digital asset ecosystem.
Bitcoin is a token system, and as such, every output should be spendable and have value in satoshis. This fundamental principle ensures that Bitcoin remains a secure and reliable system that can be trusted by users around the world. However, traditional BRC-18 scripts, employing the OP_FALSE OP_RETURN pattern, are not compatible with this view.
OP_FALSE OP_RETURN outputs are non-spendable and carry no value, which means they cannot be considered tokens under this definition. This creates a problem for developers who want to store data on the blockchain using OP_RETURN, as it goes against the basic principles of the Bitcoin token system.
To address this issue, we propose a new methodology for creating OP_RETURN scripts that are spendable and contain satoshis. This provides a way for software to automatically convert non-compliant OP_RETURNs into proper Bitcoin tokens while encouraging developers to consider the spendability constraints that govern their tokens.
By adding the cost of one single satoshi and the fully-open spendability constraints to OP_RETURN outputs, developers will be encouraged to consider the actual constraints that govern their tokens. This will drive them towards script patterns that protect what their tokens represent and promote a more robust and secure token system for Bitcoin.
This standard offers a solution that maintains the fundamental principles of the Bitcoin token system while providing a practical way for developers to store data on the blockchain using OP_RETURN. By adopting this methodology, we can ensure that Bitcoin remains a secure and reliable system that can be trusted by users worldwide.
We specify the same script template as BRC-18, except that instead of OP_FALSE being the first opcode, it is replaced with OP_TRUE. We also stipulate that at least one satoshi must be locked in the output.
For example:
Because BRC-18 implementers did not define the constraints under which their Bitcoin tokens would be unlocked, we have defined a fully-open system in which anyone can redeem these tokens. If implementers see this as a problem, they should consider being cognisant of the mechanisms that define the spendability constraints of their Bitcoin outputs.
{
"inputs": {
"900b7e9ced44f8a7605bd4c1b7054b8fb958385998954d772820a8b81eabb56f": {
"rawTx": "010000000135620d3a8fb626b763cb7b9a3c3197eda9bd6709ef1e4ebc2b359607800eaa95020000006b483045022100c3ec1792f1780e453c6ea692271bcc5388fb179d6455897f4b4200706e8b9f150220352cc2ff81a5c698e4626aec8976643134efab91ca1c80729670c2d007c77cfd4121037798f038cb7fc18b9d67baa87ed33122eb7be65b95b0dd304dc476c60043773dffffffff03e803000000000000c4210399322f558d92ced9c45d3bfe7dc5c01b830a4b61d71695ab0a1fa2cd90aba8b9ac2131546f446f44744b7265457a6248594b466a6d6f42756475466d53585855475a473462812d4eeb030fa496c2965f7e0f0b0e825d0547aceb7a9d4c76fd17e795753d8e2e0e82274ab36744a7968a43dc45b1cbdfab6c473045022100a58914da5346960ecb4de964f0f1e1be43a7645a11449c3a209f3fd69b34209b02205d4d4294d04c5f87fb16c2cdc3d9b41f9866fb3d7a690da08089c972d1393d816d75c8000000000000001976a91473a95c0b12e33b3d79f80508200a075bbab0906588ac4ea80200000000001976a91478daf10df51b3ea4c9292a72bf2e1e1d48ebe11288ac00000000",
"proof": {
"index": 2,
"txOrId": "900b7e9ced44f8a7605bd4c1b7054b8fb958385998954d772820a8b81eabb56f",
"targetType": "header",
"target": "00000020ed40984238c0d6ed157cfd4fda4b284b48bb5c3b00b3172a2b030000000000006dfcabcc55b822548d10198ad1ddc62700947c8053213bf254b5229c92b9f448c6d32f64f13d051aa8201615",
"nodes": [
"9c97050e4c5fcc7f3ae7f7de83668d1968a626d70690654dab0398b75ca447a1",
"fde37bbbec5cb48c085749aea7059412cc59c339bbc3e98960c081632382d538"
]
},
"outputsToRedeem": [
{
"index": 0,
"unlockingScript": "473044022040ac4ef063c5139ef940860ec993dea2b38f2226cb841c231d60833d185934d50220760c48202fc150e3090ffbda4ccfe4ebce36715b4396d3555fb2f3dc03346e5cc2",
"spendingDescription": "Complete a ToDo list item"
}
]
}
},
"description": "Complete a TODO task: \\"test\\""
}{
"description": "Pay John Galt 3,301 satoshis",
"labels": ["payment", "personal"],
"outputs": [
{
"script": "76a914b10f7d6c7fda3285e9b98297428ed814374cbd4088ac",
"satoshis": 3301
}
]
}{
"label": "payment",
"limit": 10
}{
"transactions": [
{
"rawTx": "...",
"inputs": { ... },
"mapiResponses": { ... },
"txid": "..."
},
...
],
"totalActions": 42
}.lock
FALSE RETURN
<data1>
<data2>
<data3>{
"status": "error",
"code": "ERR_PERMISSION_DENIED",
"description": "The user has denied permission to remove this output from the basket."
}{
"status": "error",
"code": "ERR_CERTIFICATE_NOT_FOUND",
"description": "The specified certificate could not be found."
}<Buffer 76 a9 14 57 f5 0e 13 db 98 a8 8e 9e 1b 8f 03 f6 5a 80 9d 6e 9c 9c ba 88 ac>76a91457f50e13db98a88e9e1b8f03f65a809d6e9c9cba88acOP_DUP OP_HASH160 57f50e13db98a88e9e1b8f03f65a809d6e9c9cba OP_EQUALVERIFY OP_CHECKSIGdup hash160 57f50e13db98a88e9e1b8f03f65a809d6e9c9cba equalverify checksig<minimum_signatures> <pubkey1> <pubkey2> ... <pubkeyn> <maximum_signatures> OP_CHECKMULTISIG2 <pubkey1> <pubkey2> <pubkey3> 3 OP_CHECKMULTISIGOP_0 <signature1> <signature2>.lock
TRUE RETURN
<data1>
<data2>
<data3>An array serialized Bitcoin transactions
In practical use, BEEFs are exchanged between parties to build and process new transactions as well as validate arbitrary data recorded on the blockchain.
The extension proposed here is to allow for "agreed upon transactions" to be represented in the array of transactions as just their transaction hash (txid),
This avoids the need to include potentially significant amounts of data already known to the parties exchanging BEEFs.
In addition, this extension formally acknowledges that a BEEF may contain mulitple transaction "roots".
Consider when two parties cooperate over a short amount of time to construct one or more transactions.
Inputs may be added by either party. New inputs may come from unmined and mined transactions.
At each exchange of information along this process, one party sends a BEEF to the other to validate the new inputs or transaction(s) they originate.
The BEEF standard is well suited to this with two extensions:
In the general case, a BEEF does not need to be a single transaction tree.
A method of referencing what the process has previously validated.
For example, when adding new inputs to an incomplete transaction, the set of source transactions for these inputs may need to be transmitted to a second party. A BEEF of this validation information might include multiple trees of unmined transactions.
Furthermore, some of these inputs might be from transactions created earlier in the process for which complete validation data - back to mined transactions - has already been shared.
Consider in particular if some of these transactions are truly large or if the rate of linked transaction generation is high.
The serialized format for the transaction array is updated and the BEEF version number is incremented to 0200BEEF.
For each serialized transaction, the byte previously used to indicate BUMP (01) or no BUMP (00) is renamed the Tx Data Format, it is now the first thing written for each transaction, and the value of (02) is now used when only a 32 byte txid is serialized.
Version no
Version number starts at 4022206466, encoded Uint32LE => 0200BEEF
4 bytes
nBUMPs
VarInt number of BSV Unified Merkle Paths which follow
1-9 bytes
BUMP data
All of the BUMPs required to prove inclusion of inputs in longest chain of blocks
many bytes x nBUMPs
nTransactions
VarInt number of transactions which follow
1-9 bytes
A txid only transaction is treated as implicitly valid, no raw transaction or BUMP index is included and no BUMP data is required.
Additional transactions that consume outputs from a txid only transaction treat those inputs as fully validated.
The ordering of transactions obeys the V1 rules: Parents, including txid only, must occur before children.
The essential function of the BEEF format is to efficiently represent transaction validation data.
When multiple beefs are merged, all common BUMPS, merkle paths, and parent transactions are collapsed to a single copy.
Alice: Initiates the sync process.
Bob: Responds and participates in the sync process.
Initialization
Alice starts by sending a bloom filter containing all current spendable TXID+VOUTs as elements.
Receiving and Building List
Bob receives the filter and builds a list of his items that are not members of the set.
Transaction Verification
Bob sends an INV (Inventory) message to Alice for each item not in the set.
The INV includes:
Alice's Response
For each INV, Alice responds with a list of input transactions she does not know about.
If Alice has the transaction but the metadata hash differs, she requests updated metadata.
If Alice lacks the transaction, she requests the entire transaction.
Recursive Transaction Sync
Bob responds to Alice's requests with an INV containing the encompassing transaction, done recursively.
When including the full transaction, all metadata is provided.
Error Handling and Recovery
In case of errors, affected transactions are ignored and not synced.
If errors prevent a party from fully anchoring transactions back to the blockchain, these transactions are ignored.
Failures experienced by one party are not communicated to the other due to the declaratory nature of the protocol.
Finalization of Sync
The process continues until there are no more INVs for Bob to send.
Once complete, all of Bob's records are considered synced with Alice.
Role Reversal
The roles reverse, with Bob sending Alice a bloom filter.
The parties then exchange data in the other direction, following the same steps.
Verification of merkle proofs and the longest chain of block headers.
Recursively requesting information until all inputs are fully proven.
Invalidating transactions that cannot be linked back to a valid proof.
The protocol is adaptable to various blockchain environments.
The recursive nature ensures thorough and complete data synchronization.
The protocol emphasizes security, efficiency, and data integrity.
GASP offers a robust and secure method for synchronizing transaction data between parties in a blockchain network, leveraging recursive data exchange and thorough verification mechanisms.
We propose that p2p destination and receive raw transaction functionality between hosts to include the data required for the counterparty to run SPV on the transaction.
A random ID was generated in order to avoid label collisions in the capability document of a paymail server.
We specify that an endpoint for delivery of transaction data ought to be added to the capabilities object in the .well-known response of a paymail service with the label: 5c55a7fdb7bb.
The sender must replace the {alias} and {domain.tld} placeholders in the URI template provided by capabilities.5c55a7fdb7bb with a valid Paymail handle. The client must then perform a POST HTTP request with the following body:
BEEF transaction hex is a string encoding of the binary format detailed in BRC-62.
The server must validate the transaction and respond with the accepted transaction ID and an optional human-readable note.
A swimlanes diagram is included here for further clarification: https://swimlanes.io/u/6J2q8QCEb
Ty Everett ([email protected])
We define a comprehensive and authoritative specification of the deployment-info.json schema. It is intended to serve as a reference for developers building BSV blockchain applications that integrate with the LARS (Local Automated Runtime System) and CARS (Cloud Automated Runtime System) tooling and beyond. By following this specification, projects can ensure a standardized and interoperable structure that other tools and workflows can rely upon.
The deployment-info.json file defines the structure and metadata of a BSV-based application for both local development (via LARS) and cloud deployment (via CARS). It describes the app’s topic managers, lookup services, frontend build configuration, contract compilation requirements, and various deployment configurations.
Example:
schema (string)Required: Yes
Valid Values: "bsv-app"
A fixed string identifying the schema type for this file. Must be "bsv-app".
schemaVersion (string)Required: Yes
Indicates the version of this schema. Example: "1.0".
As the schema evolves, this can help tooling handle backward compatibility.
topicManagers (object)Required: No (You may have no Topic Managers if your app does not define an overlay.)
Maps topic manager names (strings) to paths of their implementing modules.
Key (Topic Manager Name): A unique name (string) identifying the Topic Manager within the app. For example, "tm_meter", as per BRC-87.
lookupServices (object)Required: No (Only if your app needs overlay retrieval/lookup functionalities.)
Maps lookup service names (strings) to configuration objects describing how to instantiate them.
Key (Lookup Service Name): A unique name (e.g., "ls_meter"), as per BRC-87.
frontend (object)Required: No (Only if your project has a frontend.)
Describes how the frontend portion of the app is set up.
Fields:
language (string): The frontend tech stack. Common values:
Example:
contracts (object)Required: No (Only if your project uses on-chain contracts.)
Describes contract language and location for source and artifacts.
Fields:
language (string): The contract language. Common values:
Example:
If contracts is present and language is "sCrypt", LARS/CARS can trigger automatic contract compilation steps when contracts change.
configs (array)Required: Yes (although it can be empty initially)
An array of configuration objects defining different deployment targets or modes for the project.
Each config object corresponds to either:
LARS
Common Fields in Each Config:
name (string, required): A human-readable name for the configuration. E.g., "Local LARS", "production", "staging".
provider (string, required): Indicates whether this config uses LARS or CARS. Common values:
LARS-Specific Fields:
run (array of strings, optional): Which parts of the project to run locally. Usually ["backend"], may include "frontend" if supported, but often the frontend is served separately.
Example:
CARS-Specific Fields:
CARSCloudURL (string, required for CARS): URL of the CARS cloud service. E.g., "https://cars-cloud.example.com".
projectID (string, required for CARS): The Project ID on the CARS Cloud. Used for managing deployments, logs, admins, etc.
deploy (array of strings, required): Which parts of the application to deploy to the cloud. E.g., ["frontend", "backend"]
Example CARS Config:
configs:Multiple CARS configs can coexist (e.g., one for "staging", one for "production", different networks, resilient deployment across clouds).
Only one LARS config is typically present (local dev environment). Tools like LARS assume a single local configuration.
Tools like CARS will prompt or require the user to pick a configuration if multiple apply.
LARS uses deployment-info.json to:
Determine which topic managers and lookup services to load locally.
Compile contracts if specified under contracts.
By maintaining a consistent deployment-info.json schema, both local and cloud tools can parse and understand the application’s structure, enabling smooth transitions from local dev (LARS) to production deployments (CARS).
schema and schemaVersion: Future updates to the schema may introduce new fields or optional properties. Always check if your tools (LARS, CARS, or others) support the version you’re using.
Optional Fields: Many fields are optional, allowing minimal setups. For example, you can omit frontend if you have no frontend. You can omit lookupServices if you have no shared state coordination needs.
deployment-info.jsonNo frontend, no contracts, no lookup services, and a single LARS config.
deployment-info.jsonThis reference provides a complete specification of the deployment-info.json schema used by LARS, CARS, and related tooling. By adhering to this schema, developers create a consistent and predictable environment, enabling a smooth, automated workflow from local development to production deployment.
Ty Everett ([email protected])
_unwriter
This document presents the technical standard for the TXO (Transaction Object) format, a structured representation of Bitcoin transactions that enables powerful queries, processes, and filters. The TXO format captures transaction data in a hierarchical structure, facilitating its storage in document databases (e.g., MongoDB) and enabling real-time filtering with JSON filter libraries (e.g., JQ).
Traditional raw one-dimensional Bitcoin transactions, as described in , are challenging to process and filter, which hinders the development of applications that rely on transaction data. The TXO format overcomes these limitations by transforming raw Bitcoin transactions into a structured, queryable, and hierarchical format, thereby enabling developers to build powerful applications and services using transaction data.
The TXO format represents a Bitcoin transaction as a JSON object with the following high-level structure:
At the top level, the TXO format includes two objects: tx and blk.
tx (transaction): contains:
h (transaction hash)
r (raw transaction)
The next level of the TXO format includes two arrays: in (input scripts) and out (output scripts).
Each input and output is essentially a Bitcoin script. Attributes for each script include:
i: the index of the current script within the in (input) or out (output) array.
b0, b1, b2, ...: the script's push data.
The third level of a TXO interpreted transaction focuses on the graphs that represent the relationships between transactions. Graphs play a crucial role in understanding the flow of transactions in the Bitcoin network. Each item in the in and out arrays has an attribute called e (edge), which represents the graph structure of each transaction. The edge attribute helps in connecting the inputs and outputs of a transaction, as well as tracking the flow of satoshis between addresses.
For inputs, the edge is an "incoming edge" that represents the outputs from the previous linked transaction. It contains the following fields:
h: The hash of the transaction that contains the previous output.
i: The index of the previous output within its transaction output set.
a: The sender address if it can be parsed into an address.
For outputs, the edge is an "outgoing edge" that represents the outputs to the next linked transaction. It contains the following fields:
v: The amount of satoshis sent.
i: The output index within the transaction.
a: The receiver address if the output is linking to an address.
To implement the TXO format, follow these steps:
Deserialize the raw Bitcoin transaction into its input and output scripts.
Parse the input and output scripts into a JSON object following the TXO schema described in the specification section. This includes constructing the transaction, script, and graph levels for each transaction.
If applicable, store the TXO JSON object in a document database (such as MongoDB) to enable powerful queries, or filter the transactions in real-time using JSON filter libraries (such as JQ).
The reference implementation was written by _unwriter of the 21st Century Motor Company and is available .
Deggen ([email protected])
We propose a binary format for a Single Merkle Path optimized for storage in a key value database.
This BRC is licensed under the Open BSV license.
The includes oddities in it for future extensions which are no longer necessary since they are covered by the compound merkle path format defined in . So now we attempt to specify the smallest possible encoding of a simple merkle path.
We take the JSON version from eg.
Encoding in bytes we start with a VarInt for index, followed by nPath being the number of leaves to follow, followed by 32 byte leaves.
Let's start by dumping this format as hex into a Buffer and parsing it into an object with a Buffer Reader. Then we construct an object
Ty Everett ([email protected])
Bitcoin script is a programming language used in Bitcoin transactions to control the spending of coins. While the Bitcoin script opcodes are machine-readable, they are not easily human-readable. We define an assembly language that provides a human-readable format for expressing Bitcoin script opcodes, making it easier for developers to understand and write Bitcoin scripts. This specification defines the rules for expressing Bitcoin script opcodes in assembly format, including indentation, comments, expressing text strings and numbers, templating and other guidelines.
Bitcoin script is a powerful tool for controlling the spending of coins, but its opcodes can be difficult for developers to read and understand. By providing a human-readable assembly format for expressing Bitcoin script opcodes, developers can more easily write and debug Bitcoin scripts. This specification aims to standardize the assembly format for Bitcoin script opcodes, making it easier for developers to work with Bitcoin transactions.
This specification is intended to start the discussion and propose an initial format for representing Bitcoin scripts in an assembly language. Iterative improvement through future BRCs will help improve this standard.
We specify an assembly language for Bitcoin script programs as follows:
Opcodes should be expressed in uppercase and without OP_ prefixing, e.g. CHECKSIG.
Data to push on the stack is represented by strings, hex values, or template variables.
Whitespace should be used for indentation and to separate tokens.
For example:
Numeric values should be expressed as their corresponding opcodes, or in hexadecimal format.
String values should be enclosed in single quotes.
Hex values should be expressed in lowercase, without 0x prefixing.
For example:
Conditional statements should use the IF opcode, followed by the conditional expression and the ELSE or ENDIF opcodes.
For example:
We specify that .basm files can be used to represent Bitcoin assembly language programs.
The process of assembling programs written in this assembly language comprises:
Obtaining the values for template variables, either programmatically or by seeking user input
Substituting template variable placeholders for the actual values
Removing comments
Computing and adding the correct PUSHDATA opcodes for adding string and hexadecimal values to the stack
Push TX is a technique that enables users to enforce and access transactional states and conditions within Bitcoin script using ECDSA signature messages. It allows for the enforcement of several transaction elements, such as the number of inputs, nSequence values, input and output values, and script conditions, as well as the ability to specify the script into which tokens will be spent. The Push TX algorithm works by pushing the transaction pre-image message that generates the signature onto the stack as part of the input's unlocking script. This message can be pushed as a single contiguous blob, multiple separate elements, or as a partial set, with the remaining elements of the message set via the output's locking script. The signature is then computed on-chain within the script and checked against the public key using OP_CHECKSIG to ensure the current transaction is valid. This standard outlines the motivation and specifications for implementing the Push TX technique within Bitcoin script.
The Push TX technique offers several benefits to Bitcoin users and developers. Firstly, it allows for the enforcement of complex transactional states and conditions at the consensus layer, making it easier to create Turing complete machines within the Bitcoin ecosystem. This is achieved by enabling users to specify the script into which tokens will be spent, which can be used to enforce next state conditions. Additionally, it enables users to specify and enforce various transaction elements, such as input and output values, nSequence values, and the nLocktime condition. This improves the security and efficiency of Bitcoin transactions by ensuring that all transactional conditions are met and enforced at the consensus layer.
The Push TX algorithm works as follows:
The user or process that is using the UTXO pushes the transaction pre-image message that generates the signature onto the stack as part of the unlocking script. This message can be pushed as a single contiguous blob, multiple separate elements, or as a partial set, with the remaining elements of the message set via the output's locking script.
The algorithm pushes the current transaction onto the stack.
The algorithm pushes a dummy private key onto the stack.
The Push TX algorithm can be used to enforce several elements of the transaction, including but not limited to the number of inputs, nSequence values, input and output values, and script conditions, as well as the ability to specify the script into which tokens will be spent.
This has been .
Bitcoin script is often misunderstood as being limited to the data provided in the locking and unlocking scripts. However, the Push TX technique allows for the inspection of the entire transaction within a contract itself, including all inputs and outputs. This opens up boundless possibilities for smart contracts on Bitcoin.
The ability to place arbitrary constraints on inputs and outputs within a contract means that a wide range of use cases can be implemented on Bitcoin. For example, contracts that enforce multi-signature transactions or time-based restrictions can be easily created using Push TX. Additionally, the technique enables the creation of more complex contracts that can enforce next state conditions, making it possible to create Turing complete machines within the Bitcoin ecosystem.
The high-level functionality of Push TX is relatively simple: it allows for the inspection and enforcement of transactional states and conditions within Bitcoin script. However, the implications of this functionality are far-reaching, as it enables the creation of a wide range of smart contracts and decentralized applications on the Bitcoin blockchain. This is important for Bitcoin because it expands its use cases and makes it a more versatile platform for developers and businesses looking to leverage blockchain technology.
Ty Everett ([email protected])
This standard describes PacketPay, a mechanism for facilitating micropayments using Bitcoin SV within HTTP requests. It relies on the BRC-29 payment protocol for processing payments, and BRC-31 Authrite for authenticating communications between the API provider and consumer. PacketPay enables secure, per-request monetization of APIs, allowing API providers to charge consumers on a pay-per-use basis. This standard defines the required HTTP headers, the process of making and verifying payments, and the necessary steps to implement PacketPay in API interactions.
The rapid growth of web services and APIs has created a need for efficient, secure, and scalable methods of monetizing these services. Traditional payment methods are often cumbersome and impose high fees, making them unsuitable for micropayments. PacketPay addresses this need by providing a secure, efficient, and low-cost solution for facilitating micropayments in HTTP requests. By leveraging Bitcoin SV's capabilities, PacketPay enables API providers to monetize their services on a per-request basis, and API consumers to pay only for the resources they use.
PacketPay uses the following HTTP headers for facilitating payments between API consumers and providers:
x-bsv-payment-satoshis-required: Sent by the API provider in a 402 response, it indicates the number of satoshis required for the requested resource.
x-bsv-payment: Sent by the API consumer in a request to a payment-enabled API endpoint, it contains a JSON stringified object representing a payment.
x-bsv-payment-satoshis-paid: Sent by the API provider in the response to a successfully paid API request, it indicates the number of satoshis paid for the requested resource.
The PacketPay process is divided into the following stages:
Authentication: The API consumer and provider exchange Authrite requests and responses to authenticate their identities and share necessary identity information.
Initial Request: The API consumer sends an HTTP request to the API provider without a payment. If the requested resource requires a payment, the API provider responds with a 402 status code and includes the x-bsv-payment-satoshis-required header, indicating the required payment amount.
Payment Processing: The API consumer reads the x-bsv-payment-satoshis-required
To implement PacketPay, API providers and consumers must follow these steps:
Implement for processing payments and for authenticating communications between parties.
For API providers, implement logic to determine the payment required for each request and include the x-bsv-payment-satoshis-required header in the 402 response.
For API consumers, implement logic to read the x-bsv-payment-satoshis-required header, construct a payment, and include the x-bsv-payment
By following the above implementation steps, API providers and consumers can ensure secure and efficient micropayments using the PacketPay system. This will enable a new revenue model for APIs, allowing for greater flexibility and scalability in the ever-growing world of web services.
The PacketPay and have been implemented in JavaScript by the Babbage Team.
Ty Everett ([email protected])
This document outlines the Overlay Services Synchronization Architecture, detailing the Services Host Interconnect Protocol (SHIP) and Services Lookup Availability Protocol (SLAP). These protocols enable efficient peer discovery and data synchronization across UTXO-based overlay networks. This BRC defines the components, interactions, and responsibilities of nodes running these protocols to maintain reliable and up-to-date overlay services.
The proliferation of UTXO-based overlay networks necessitates robust mechanisms for peer discovery and data synchronization. While defines transaction submission and processing, SHIP and SLAP extend this by providing standardized methods for discovering the overlay hosts that run particular services. This architecture ensures seamless interaction between network participants, improving the efficiency and reliability of data propagation. It can also enable the users of services to find hosts that are interested in their transactions.
Readers of this document should be familiar with BRCs 45, 59, 22, 24, 23, and 25.
The Overlay Services Synchronization Architecture comprises two fundamental protocols:
Services Host Interconnect Protocol (SHIP)
Services Lookup Availability Protocol (SLAP)
Each node running overlay services will implement topic managers and lookup services for both SHIP and SLAP, ensuring alignment between advertised services and their actual configuration.
SHIP/SLAP Topic Managers: Manage topic-specific transaction admittance.
SHIP/SLAP Lookup Services: Provide mechanisms for querying UTXO states within topics.
Advertiser: Handles the creation and revocation of SHIP and SLAP advertisements.
Overlay Services Engine: Coordinates the processing of transactions, alignment of advertisements, and synchronization of data across various services.
Each node must ensure alignment between the SHIP and SLAP advertisements it has sent out and the current set of topic managers and lookup services it is configured with.
Nodes host copies of the SHIP and SLAP overlay networks, ensuring they are updated with new advertisements or revocations.
Submission: Any node can submit SHIP/SLAP advertisements to another node if it knows its domain name, updating the overlay network.
Internal Updates: Nodes submit their own advertisement transactions to themselves to update the overlay networks about new advertisements or revocations, thereby ensuring their hosted copies are always current and propagating them to other nodes.
As part of the transaction submission process, nodes notify each other about transactions on topics they host and care about.
When new transactions are submitted to a node, the Engine:
Ensures transactions are propagated to relevant nodes based on SHIP/SLAP advertisements made by them.
Updates lookup services with new or spent UTXOs.
Delegates transaction submission, verification, and output admittance to the SHIP/SLAP topic managers.
The advertiser creates a transaction output containing the token and submits it to known nodes, including their own. The process involves:
Deriving the locking key using the advertiser's identity key.
Computing the signature over the token fields.
Creating and submitting the transaction output containing the token.
Nodes validate the identity key's link to the signature-producing key before admitting the output. The steps include:
Extracting token fields.
Verifying the SHIP/SLAP identifier.
Verifying the locking key and signature.
Admitting the token if valid.
Developers should implement an HTTPS server that hosts the SHIP and SLAP topic managers and lookup services. The server should be responsible for notifying other nodes of relevant transactions and maintaining synchronization.
This technical standard describes a method for hierarchical key derivation called BIP32. Hierarchical key derivation enables the generation of a large number of public-private key pairs from a single master seed, and the ability to derive child keys from parent keys in a deterministic manner. BIP32 is a widely used standard in various fields such as wallets, but has various limitations and drawbacks that should be considered carefully before further adoption.
In many applications, it is necessary to generate a large number of public-private key pairs. However, generating and securely storing many keys can be challenging. Hierarchical key derivation solves this problem by generating an unlimited number of keys from a single master seed. This reduces the complexity of managing multiple keys and enhances security. BIP32 provides a standard for implementing hierarchical key derivation, ensuring interoperability between different systems.
BIP32 is a method for hierarchical key derivation that enables the generation of a virtually unlimited number of public-private key pairs from a single master seed. The master seed is used to generate the master private key, which is used to derive child keys. Child keys can in turn be used to derive grandchildren keys, great-grandchildren keys, and so on, forming a hierarchical tree-like structure.
Each key in the hierarchy is determined using a combination of its parent key and an index value. The index value determines the position of the key in the hierarchy, and the parent key serves as a starting point for deriving the child key. The combination of the parent key and the index value is used to generate a new private key, which can then be used to derive a new public key.
BIP32 specifies the use of a deterministic algorithm for deriving keys. This means that given the same master seed and index value, the same key will be generated every time. This is important for ensuring that different systems can generate the same keys, and for enabling secure backup and recovery of keys.
BIP32 also provides a standard for deriving hardened keys, which are child keys that cannot be derived from their parent public key alone. Hardened keys provide an additional layer of security, as they can only be derived using the parent private key.
BIP32 is a widely used standard for implementing hierarchical key derivation. It enables the generation of a large number of public-private key pairs from a single master seed, and ensures interoperability between different systems.
BIP32 defines a hierarchical tree-like structure for generating an unlimited number of public-private key pairs from a single master seed. This structure is organized using a derivation path, which is a sequence of index numbers and hardened/not-hardened markers that uniquely identify a particular key in the hierarchy.
A derivation path always starts with the letter "m", which stands for "master", followed by a forward slash ("/") and then a series of numbers and markers that specify the child keys to be derived. For example, the path "m/0/1/2" indicates that the third child private key should be derived from the second child private key of the first child private key of the master key.
In addition to simple index numbers, BIP32 allows for "hardened" derivation, which means that the derived key can only be generated using the parent's private key. Hardened keys are denoted with an apostrophe (') after the index number. For example, the path "m/44'/0'/0'/0/0" is commonly used for generating the first receiving address of a Bitcoin wallet using BIP44 standard (discussed below).
BIP44 is a widely used standard for generating hierarchical deterministic wallets with a specific structure for addressing different cryptocurrencies. This standard provides a consistent method for generating and organizing addresses across different wallets and platforms.
The BIP44 structure uses a derivation path that starts with "m/44'/<coin_type>'/", where <coin_type> is a numeric identifier for the cryptocurrency being used. For example, "m/44'/0'/" is the path for generating Bitcoin keys, and "m/44'/60'/" is the path for generating Ethereum keys.
Following the coin_type index, BIP44 specifies several other indices to organize keys, including account, change, and address indices. Account indices are used to manage multiple accounts within a single wallet, change indices are used to differentiate between incoming and outgoing transactions, and address indices are used to generate unique receiving addresses.
For example, the path "m/44'/0'/0'/0/0" generates the first receiving address for the first account of a Bitcoin wallet. The "0'" after the coin_type index indicates a hardened key, ensuring that the derived key can only be generated using the parent's private key. The "0" after the hardened index specifies the first account, and the "0" after that specifies the first address in the account.
While BIP32 is a widely used standard for hierarchical key derivation, there are several potential drawbacks that should be considered before further adoption.
One major limitation is that BIP32 lacks privacy guarantees. Since it is common for all child keys in a wallet to be derived from a single node, any knowledge of the root public key could compromise the privacy of the entire key hierarchy for the wallet — since public keys can be derived from parent public keys, there is a risk of linkability between addresses that use the same parent key.
Additionally, BIP32 is limited to a maximum of 2^31 possible child keys per parent, which may not be sufficient for applications that require a very large number of keys.
To address these limitations, other key derivation schemes adopt various alternative approaches. By incorporating a shared secret between the sender and the recipient into the key derivation process, we can protect privacy even when the root public key becomes known. enables this functionality, and allows an unlimited number of child keys to be derived.
1:
Ty Everett ([email protected])
This document describes a protocol for a portable message encryption and description scheme. It follows the BRC-42 key derivation and BRC-43 invoice number standards to protect the confidentiality of message content, without requiring the added complexity of agreeing on a specific BRC-43 protocol for the parties to use.
Message encryption is a fundamental requirement as it ensures the privacy and confidentiality of the content transmitted between parties. The current standards like Electrum ECIES don't provide a comprehensive solution for message encryption by integrating with BRC-42 and BRC-43. This document aims to enhance security by developing a protocol for message encryption and decryption, utilising the BRC-43 invoice numbers. The protocol facilitaes a way to exchange encrypted data in a general way.
The protocol employs the BRC-2 encryption process and makes use of the BRC-42 key derivation and BRC-43 invoice numbering scheme.
The encryption procedure follows these steps:
The sender employs BRC-43 with security level 2, protocol ID message encryption, and a randomly generated 256-bit key ID to compute the invoice number to facilitate BRC-42 key derivation. We specify the key ID is in base64 format when added to the invoice number.
The sender derives their own child private key and the child public key of the recipient using BRC-42 key derivation.
The sender then computes an ECDH shared secret between the two child keys which is used in symmetric encryption with AES-256-GCM.
For decryption:
The recipient computes their own private key and the public key of the sender using BRC-42 key derivation.
The recipient computes the same Shared Secret and uses it along with the received initialization vector to decrypt the ciphertext.
The serialized data for transmission includes a version number, the identity key of the sender, the identity key of the recipient, and the AES-256-GCM ciphertext (with the initialization vector prepended). Note that anyone is not permitted for encrypted data:
This standard serialization format contributes towards a standardized way of securely transmitting and decrypting encrypted messages.
BRC-27 defines an abstract messaging protocol that facilitates payments on the Bitcoin SV network. This specification extends the protocol with the specifics required for a concrete HTTPS implementation that includes message size limits and HTTPS-specific implementation details.
HTTPS is one of the most widely used transport mechanisms for internet traffic. Enabling users of HTTPS to benefit from the use of the DPP will facilitate increased industry adoption and growth. Defining implementation-specific stipulations for HTTPS-based DPP will address problems and ambiguities specific to this communications substrate.
Below is the table with messages requested from client (wallet) to merchant (server).
The paymentID parameter is assumed to be known as a result of request between client-server before DPP protocol operation.
PaymentTerms messages larger than 10 MB should be rejected by the wallet application, to mitigate denial-of-service attacks.
Payment messages larger than 10 MB should be rejected by the payment host’s server, to mitigate denial-of-service attacks.
PaymentACK messages larger than 11 MB bytes should be rejected by the wallet application, to mitigate denial-of-service attacks. This is larger than the limits on Payment and PaymentTerms messages as PaymentACK contains a full Payment message within it.
The BRC-55 API is implemented .
Deggen ([email protected])
We present a data model and JSON format for a Merkle Path which will be passed from a Transaction Lookup service to a Lite Client via web API.
Tone Engel ([email protected]), TonesNotes
This is an argument for using block height as the standard for indexing block headers within merkle proofs.
It argues for a backward compatible protocol extension in the short term and through deprecation, a long term reduction in the space cost of a usable local copy of block headers by a factor of two.
The referenced specification, the "Spec":
Ty Everett ([email protected])
This document outlines the BRC-8 standard for Everett-style Transaction Envelopes. The standard defines the structure and format of the envelopes used to send Bitcoin transactions. The primary goal of this standard is to ensure interoperability between different Bitcoin wallet software and services, allowing them to exchange transaction data in a consistent manner. This standard also aims to facilitate the implementation of the Simplified Payment Verification (SPV) process, as defined in , by providing a standardized methodology for exchanging merkle proofs and input transactions.
Ty Everett ([email protected])
The Pay-to-Public-Key-Hash (P2PKH) output script is a commonly used script in the Bitcoin network that allows users to send funds to a specific Bitcoin address. This standard provides a detailed specification of the P2PKH output script, providing detail on the operations performed, and giving examples for serialized Bitcoin addresses.
Ty Everett ([email protected])
This document outlines a protocol that allows an entity to publish trust anchor details, including a public key, at a location on the internet. The information is stored in the /manifest.json file of the domain. This approach provides a user-friendly method for individuals to easily retrieve a trusted entity's public key by entering a known domain name.
Ty Everett ([email protected])
The architecture for wallet protocol permissions enables a wide range of use cases. By reserving protocol identifiers and preventing their use by applications, we ensure compatibility with future standards, supporting the future development of new protocol permission schemes.
brfcid: 5c55a7fdb7bb
title: Background Evaluation Extended Format Transaction
author: Darren Kellenschwiler
version: 1.0.0<!-- capabilities document of the host -->
{
"capabilities": {
"5c55a7fdb7bb": "https://paymail.domain.tld/api/v1/beef/{alias}@{domain.tld}",
<!-- ...others -->
}
}{
"beef": "<beef_transaction_hex>", <!-- note the label is different >
"metadata": {
"sender": "<sender_paymail>",
"pubkey": "<sender_public_key>",
"signature": "<signature_of_txid>",
"note": "<optional_human_readable_note>"
},
"reference": "<payment_reference>"
}{
"txid": "<accepted_txid>",
"note": "<optional_human_readable_note>"
}Any metadata (such as labels, descriptions, or local timestamps) associated with transactions or outputs.
A list of VOUTs spent by its inputs and associated metadata hashes.
The preimage for each hash, including all metadata and the merkle proof or broadcast response.
The algorithm pushes the public key derived from the private key onto the stack.
The algorithm uses OP_CHECKSIG to check the signature against the public key, ensuring that the current transaction is valid.
Field 1
The string SHIP, denoting a SHIP advertisement.
Field 2
The BRC-31 identity key of the advertiser.
Field 3
The domain name of the HTTPS server hosting the network node.
Field 4
The topic name hosted by the advertiser.
Field 1
The string SLAP, denoting a SLAP advertisement.
Field 2
The BRC-31 identity key of the advertiser.
Field 3
The domain name of the HTTPS server hosting the network node.
Field 4
The service name, represented by a BRC-24 provider ID.
PaymentTerms
GET /api/v1/payment/{paymentID}
application/json
200 – contains PaymentTerms JSON object with all data used by the payee to construct a transaction 404 – returned if the paymentID has not been found 500 – returned if there is an unexpected internal error
Payment
POST /api/v1/payment/{paymentID}
application/json
201 – contains PaymentACK JSON object 400 – returned if the user input is invalid, usually an issue with the paymentID 404 – returned if the paymentID has not been found 500 – returned if there is an unexpected internal error
Tx Data Format
00 raw transaction without BUMP index; 01 raw transaction followed by BUMP index; 02 txid only
1 byte
BUMP index
Format 01: VarInt index number indicating the BUMP to which Raw Transaction belongs.
1-9 bytes
Raw Transaction
Format 00 or 01: RawTx bytes as in standard format BRC-12
many bytes
txid only
Format 02: 32 byte transaction hash in reverse byte order
32 bytes
The motivation for this proposal is to future-proof the BRC-43 architecture by enabling the seamless integration of new protocol permission schemes. By specifying reserved identifiers, we can ensure that new security and permission paradigms can be implemented without conflicts or unintended behavior.
To accommodate future protocol permission schemes, wallets must reject operation requests using protocol IDs beginning with p (a lowercase “p” followed by a space), regardless of security level.
Future permission schemes must define their ID formats as follows:
Scheme IDs cannot contain spaces.
Protocol IDs must start with p , followed by the scheme ID, a space, and the rest of the protocol identifier.
A protocol ID like p 222 xxxxx could represent a specific invoice number (e.g., 2-p 222 xxxxx-kkkkk), where:
2 indicates the BRC-43 security level.
p designates an alternative permission scheme.
222 identifies the permission scheme.
xxxxx forms the remainder of the protocol ID under the alternative scheme.
kkkkk denotes the key ID.
Wallets must differentiate between standard and alternative permission schemes by recognizing the p prefix followed by a distinct, space-free scheme ID. To ensure unambiguous parsing.
Upon recognizing a protocol ID structured as p <scheme ID> <rest of the ID>, wallets may apply the specific rules defined by the scheme associated with the scheme ID. These rules could define:
Permitted protocols and key IDs.
Conditions for operation execution.
Counterparty requirements and customizable permission attributes.
To maintain clarity and prevent conflicts:
Protocol IDs beginning with p must be reserved for future use.
Wallets must reject operations involving such IDs unless they explicitly support the scheme ID.
A space must immediately follow the scheme ID to separate it from other elements.
This specification allows future permission schemes to extend beyond current BRC-43 models (e.g., security levels or BRC-73 paradigms), enabling flexible and innovative wallet permissions that evolve with user and application needs.
By reserving protocol IDs starting with p and specifying rules for future permission schemes, this specification ensures forward compatibility and robust wallet permission functionalities. It enables seamless integration of new schemes without disrupting existing applications or introducing parsing ambiguities.
x-bsv-payment header of the subsequent API request.Payment Verification: The API provider reads the x-bsv-payment header, verifies the payment using BRC-29, and processes the request if the payment is valid.
Payment Acknowledgment: The API provider sends a response containing the x-bsv-payment-satoshis-paid header, indicating the number of satoshis paid for the requested resource. The API consumer reads this header to confirm successful payment.
For API providers, implement logic to read and verify the x-bsv-payment header using BRC-29, process the request if the payment is valid, and include the x-bsv-payment-satoshis-paid header in the response.
For API consumers, implement logic to read the x-bsv-payment-satoshis-paid header and confirm successful payment.
blk (block): contains:
i (block index)
h (block hash)
t (block time)
Opcodes: stored as a JSON object with a single key op and the Bitcoin opcode value (e.g., {"op": 106} for the 'return' opcode).
Non-Opcodes: stored as a base64 encoded string.
lb0, lb1, lb2, ...: base64 encoded string, used when the push data size is larger than 512 bytes.
s0, s1, s2, ...: UTF8 encoded representation of the push data, used for full-text search and simple string matching.
ls0, ls1, ls2, ...: UTF8 encoded representation, used when the push data size is larger than 512 bytes.
str: full string representation of the script.
e: contains the graph structure of each transaction.
index
VarInt tx index number from within a block
1-9 bytes
nLeaves
VarInt number of leaves which follow
1-9 bytes
leaf
Each leaf of the path is a 32 byte hash
32 bytes x nLeaves
Comments should start with the # character and extend to the end of the line.
.unlock and .lock can be used to denote the boundary between an unlocking script and its corresponding lock.
<hash>Converting all string values to hexadecimal
Substituting all opcode names for their hexadecimal coded values
Removing .unlock and .lock annotations if present
Removing all whitespace to arrive at the fully-assembled program
With a random initialization vector, the message is encrypted and the vector is prepended to the ciphertext.
Ciphertext
Variable
The encrypted message data (AES-256-GCM with IV prepended)
Version
4 bytes
Defines the version of the standard used. Currently 0x42421033.
Sender ID
33 bytes
Identity key of the sender (encryptor)
Recipient ID
33 bytes
Identity key of the recipient (decryptor)
Key ID
32 bytes
The specific key ID used for the signature
.ts module with a default export for a class that implements the TopicManager interface from @bsv/overlay.Example:
Fields in the Service Config Object:
serviceFactory (string, required): A path to a .ts module containing a default export of a factory function that creates a LookupService instance. The factory function may accept database connections (e.g., a MongoDB Db object) and return a class that implements the LookupService interface from @bsv/overlay.
hydrateWith (string, required): Defines how the service should be backed by persistent storage. Accepted values:
"mongo": Indicates that the lookup service’s storage uses a MongoDB database. LARS/CARS will provide a mongoDb instance.
"knex": Indicates that the service uses a SQL-based storage via Knex. LARS/CARS will provide a knex instance.
"react""html"sourceDirectory (string): Path to the frontend source files relative to the project root.
"sCrypt"baseDirectory (string): Path to the directory containing contract source code and related build outputs, depending on the language.
CARS: A cloud deployment configuration.
You can have multiple CARS configs (e.g., "staging", "production") and at most one LARS config (by convention, though not strictly enforced).
"LARS": Local environment config"CARS": Cloud environment config
network (string, optional): "mainnet" or "testnet". Specifies which BSV network this config targets.
For LARS: Required to know if the local environment simulates mainnet or testnet conditions.
For CARS: Determines which network the release should be associated with.
frontendHostingMethod (string, optional): How the frontend is hosted in the cloud. Common values:
"HTTPS": Host frontend over HTTPS (CDN or static hosting, default)
"UHRP": Host via the UHRP protocol (if integrated and supported by the specific CARS Cloud)
LARS config in configs to know what network to run, what keys to use, and what parts of the app to start.CARS uses deployment-info.json to:
Identify CARS configs and connect to the specified CARS Cloud environment.
Build and upload artifacts, define which components to deploy.
Manage projects, logs, admins, and releases in a cloud environment based on project ID.
BRC-10 otherwise known as the TSC Merkle Proof Standardized Format has a few competing ideas baked into it rather than it being a clear standard to be used in one way only. This has lead to an ugly solution which can be ambiguous. This JSON format is a simple recommendation for how we might use a prettier structure in future to clarify the intended use.
The name Merkle Proof suggests that it can on its own prove something. I don't think this is the case as a blockheader is required to validate. Therefore the suggested name is a "Merkle Path".
A Merkle Path needs to indicate the txid of the transaction in question, but this is assumed to be the key of the object rather than contained within the object itself. Also needed is an index number which indicates position with a block.
The proposed JSON is:
The idea is to use the Merkle Path like so:
Convert all hex string into reverse bytes, txid and all leaves.
Calculate the Merkle root by hashing the txid with each element in the path.
Checking the last bit of the index number, if it's 1 then the txid should be on the right workingHash = sha256d([...leaf, ...txid]) else other way around workingHash = sha256d([...txid, ...leaf])
Right shift the index number
Check the new last bit, if it's 1 then the workingHash should be on the right workingHash = sha256d([...leaf, ...workingHash]) else other way round workingHash = sha256d([...workingHash, ...leaf])
Once all leaves are accumulated into a single hash with the above method - we have the Merkle root which we reverse and convert back into a hex string.
This root can be used to look up a block header.
If the blockheader is part of the longest chain of work then we have a "Merkle Proof" that the tx is included in that block.
You would only likely see a Merkle Proof when you are in a position where you have to present evidence to a court for dispute resolution or something to that effect. In day to day use the Merkle Path format detailed above is all that need be kept within the transaction store, the block header information can be kept separately.
The main difference between this and the TSC Merkle Proof Standard Format is renaming a few labels:
txOrId is dropped in favor of txid because the ambiguity is unnecessary, and full transactions have their own format considerations to focus on, hence separation of concerns.
nodes are renamed to path because this more accurately describes the specific hashes we are referring to as those without children in a Merkle tree, and also disambiguates between these and bitcoin nodes or network nodes in general. Citing the original Merkle Tree patent we see this referred to as "path", with no mention of nodes anywhere.
The order of items in the JSON is not strict but does imply a better general understanding. As a sentence: This transaction in this block is at index 12 with path... etc.
The P2PKH output script is used to ensure that only the owner of a specific Bitcoin address can spend the funds sent to that address. This is achieved by requiring the spender to provide a valid signature that corresponds to the public key associated with the address. The P2PKH output script is widely used in the Bitcoin network and is the most common output script used in transactions.
The P2PKH output script is defined as follows:
Where:
OP_DUP duplicates the top stack item.
OP_HASH160 computes the RIPEMD160 hash of the SHA256 hash of the top stack item.
<PubKeyHash> is the RIPEMD160 hash of the SHA256 hash of the public key associated with the Bitcoin address.
OP_EQUALVERIFY checks if the top two stack items are equal and removes them if they are.
OP_CHECKSIG verifies that the signature provided by the spender is valid for the public key associated with the address.
The P2PKH output script is serialized using Base58 encoding. The serialized format is as follows:
Where:
[Version Byte] is a single byte that identifies the network and the script type. The value of the version byte is 0x00 for the mainnet and 0x6F for the testnet.
[PubKeyHash] is a 20-byte RIPEMD160 hash of the SHA256 hash of the public key associated with the Bitcoin address.
[Checksum] is a 4-byte checksum computed using the first four bytes of the double-SHA256 hash of the serialized script.
P2PKH Bitcoin addresses for mainnet always start with a 1 because the [Version Byte] is 0x00. On testnet, they will always start with either m or, less often, n, because the [Version Byte] is 0x6F.
Mainnet example P2PKH address: 1PyWzkfKrq1kakvLTeaCdAL8y8UJAcZAqU
Testnet example P2PKH address: mineSVDRCrSg2gzBRsY4Swb5QHFgdnGkis
When a user creates a new Bitcoin address, a public key and a corresponding private key are generated. The public key is then hashed, first with SHA256 and then the result is hashed again using the RIPEMD160 algorithm to obtain a 20-byte hash value, which is the PubKeyHash used in the P2PKH output script.
To send funds to a P2PKH address, the sender creates a transaction that includes an output with the P2PKH output script and the address's PubKeyHash. To spend the funds, the recipient must provide a valid signature that corresponds to the public key associated with the address. The signature is verified using the OP_CHECKSIG opcode in the output script.
The P2PKH output script is implemented in most Bitcoin wallets and is widely used in the Bitcoin network. There are also many libraries available that provide functions for creating and validating P2PKH transactions.
{
"tx": {
"h": [TRANSACTION HASH],
"r": [RAW TRANSACTION]
},
"blk": {
"i": [BLOCK INDEX],
"h": [BLOCK HASH],
"t": [BLOCK TIME]
},
"in": [
INPUT1,
INPUT2,
...
],
"out": [
OUTPUT1,
OUTPUT2,
...
],
"coinbase": [COINBASE]
}
{
"index": 136,
"path": [
"6cf512411d03ab9b61643515e7aa9afd005bf29e1052ade95410b3475f02820c",
"cd73c0c6bb645581816fa960fd2f1636062fcbf23cb57981074ab8d708a76e3b",
"b4c8d919190a090e77b73ffcd52b85babaaeeb62da000473102aca7f070facef",
"3470d882cf556a4b943639eba15dc795dffdbebdc98b9a98e3637fda96e3811e"
]
}88040c82025f47b31054e9ad52109ef25b00fd9aaae7153564619bab031d4112f56c3b6ea708d7b84a078179b53cf2cb2f0636162ffd60a96f81815564bbc6c073cdefac0f077fca2a10730400da62ebaebaba852bd5fc3fb7770e090a1919d9c8b41e81e396da7f63e3989a8bc9bdbefddf95c75da1eb3936944b6a55cf82d8703488 // index VarInt
04 // nLeaves
0c82025f47b31054e9ad52109ef25b00fd9aaae7153564619bab031d4112f56c // leaf
3b6ea708d7b84a078179b53cf2cb2f0636162ffd60a96f81815564bbc6c073cd // etc.
efac0f077fca2a10730400da62ebaebaba852bd5fc3fb7770e090a1919d9c8b4
1e81e396da7f63e3989a8bc9bdbefddf95c75da1eb3936944b6a55cf82d87034const { Br } = require('bsv')
const reader = new Br()
reader.buf = Buffer.from('88040c82025f47b31054e9ad52109ef25b00fd9aaae7153564619bab031d4112f56c3b6ea708d7b84a078179b53cf2cb2f0636162ffd60a96f81815564bbc6c073cdefac0f077fca2a10730400da62ebaebaba852bd5fc3fb7770e090a1919d9c8b41e81e396da7f63e3989a8bc9bdbefddf95c75da1eb3936944b6a55cf82d87034', 'hex')
let merklePath = { path: [] }
merklePath.index = reader.readVarIntNum()
let nLeaves = reader.readVarIntNum()
for (x = 0;x < nLeaves; x++) {
const leaf = reader.read(32).reverse().toString('hex')
merklePath.path.push(leaf)
}
console.log({ merklePath }).unlock
<sig> # The signature used to unlock the script
<key> # The public key that unlocks the script
.lock
DUP HASH160 # Duplicate the key and take its hash
1a98d1ea5702a518b8c4ad9bb736bf34fa9e7291 EQUALVERIFY # Check the hashes are equal
CHECKSIG # Check that the signature from this key is validOVER 3 SPLIT NIP TRUE SPLIT SWAP SPLIT DROP HASH160 <hash> EQUALVERIFY CHECKSIG2 3 ADD 5 EQUAL IF # if 2 + 3 = 5
'yes' RETURN # Return 'yes'
ELSE # Otherwise
'no' RETURN # Return 'no'
ENDIF # End10334242 # version
032e5bd6b837cfb30208bbb1d571db9ddf2fb1a7b59fb4ed2a31af632699f770a1 # Sender
02e5e1a150745253aff65cdbcef722873110d89c396223e3d8715f018e72f7d4f8 # Recipient
46a897fa6c3b4e1269284f28fb46827dc3a6a88d424f7570aca296e587612c52 # key ID
7511245c12f83e8e5ad5bd2e536ce33e06cdb76bfb80022830e0976db7866a6607cede3f9b5c95011a0cb04b0816c9c3586f106be31effc73dd8e24d1eca818bc3cdf0f9d330e2696786d375ea8c8bc38ac7f67eb2436eb4daf5c7739047d9d341cdd8eaa10d7f577122726b2ab08ffa8ca88fb2ad2c69f04edcf877a1712c2999f8a85cf94e5ae94a13862d00ec4ffd71782b7ab8b98f8844d0e011cf3843dbb3f763087e3d94693d24d57a9d389aa466bd3779adbe862ba146bbec8ac59991d56a5f2fe282720b42ce058838f0fd577d39a2e2309b4ac765f3cd64b38ed8296bd044641b814000a840fa8c0577d89ff74578c75b4b7883180bf3f994fba623 # ciphertext{
"tm_meter": "./backend/src/topic-managers/MeterTopicManager.ts"
}{
"schema": "bsv-app",
"schemaVersion": "1.0",
"topicManagers": {
"tm_meter": "./backend/src/topic-managers/MeterTopicManager.ts"
},
"lookupServices": {
"ls_meter": {
"serviceFactory": "./backend/src/lookup-services/MeterLookupServiceFactory.ts",
"hydrateWith": "mongo"
}
},
"frontend": {
"language": "react",
"sourceDirectory": "./frontend"
},
"contracts": {
"language": "sCrypt",
"baseDirectory": "./backend"
},
"configs": [
{
"name": "Local LARS",
"network": "testnet",
"provider": "LARS",
"run": [
"backend"
]
},
{
"name": "production",
"provider": "CARS",
"CARSCloudURL": "http://some-cloud.example.com",
"projectID": "your-project-id",
"network": "mainnet",
"deploy": [
"frontend",
"backend"
],
"frontendHostingMethod": "HTTPS"
}
]
}"frontend": {
"language": "react",
"sourceDirectory": "./frontend"
}"contracts": {
"language": "sCrypt",
"baseDirectory": "./backend"
}{
"name": "Local LARS",
"network": "testnet",
"provider": "LARS",
"run": ["backend"]
}{
"name": "production",
"provider": "CARS",
"CARSCloudURL": "http://cloud.example.com",
"projectID": "abc123",
"network": "mainnet",
"deploy": ["frontend", "backend"],
"frontendHostingMethod": "HTTPS"
}{
"schema": "bsv-app",
"schemaVersion": "1.0",
"topicManagers": {},
"lookupServices": {},
"configs": [
{
"name": "Local LARS",
"provider": "LARS",
"network": "testnet",
"run": ["backend"]
}
]
}{
"schema": "bsv-app",
"schemaVersion": "1.0",
"topicManagers": {
"tm_meter": "./backend/src/topic-managers/MeterTopicManager.ts"
},
"lookupServices": {
"ls_meter": {
"serviceFactory": "./backend/src/lookup-services/MeterLookupServiceFactory.ts",
"hydrateWith": "mongo"
}
},
"frontend": {
"language": "react",
"sourceDirectory": "./frontend"
},
"contracts": {
"language": "sCrypt",
"baseDirectory": "./backend"
},
"configs": [
{
"name": "Local LARS",
"network": "testnet",
"provider": "LARS",
"run": ["backend"]
},
{
"name": "staging",
"provider": "CARS",
"CARSCloudURL": "http://staging-cloud.example.com",
"projectID": "staging-project-id",
"network": "testnet",
"deploy": ["frontend", "backend"],
"frontendHostingMethod": "HTTPS"
},
{
"name": "production",
"provider": "CARS",
"CARSCloudURL": "https://cars-cloud.example.com",
"projectID": "your-production-project-id",
"network": "mainnet",
"deploy": ["frontend", "backend"],
"frontendHostingMethod": "HTTPS"
}
]
}{
"serviceFactory": "./backend/src/lookup-services/MeterLookupServiceFactory.ts",
"hydrateWith": "mongo"
}// filename or key is a 32 byte txid in hex
{
"index": 12, // JSON Number - technically has no MAX size
"path": [
"...leaf of the merkle path", // hex string 32 bytes
"...leaf of the merkle path",
"...leaf of the merkle path",
"...leaf of the merkle path"
]
}// fake data example merkle proof
{
"txid": "cefffc5415620292081f7e941bb74d11a3188144312c4d7550c462b2a151c64d",
"index": 657813,
"path": [
"6cf512411d03ab9b61643515e7aa9afd005bf29e1052ade95410b3475f02820c",
"cd73c0c6bb645581816fa960fd2f1636062fcbf23cb57981074ab8d708a76e3b",
"b4c8d919190a090e77b73ffcd52b85babaaeeb62da000473102aca7f070facef",
"3470d882cf556a4b943639eba15dc795dffdbebdc98b9a98e3637fda96e3811e"
]
"block": {
"header": {
"version": 536870912,
"prevBlockHash": "0000000000000000005d2328b618e043d80d0e5cd33f79b8351965305482cb6b",
"merkleRoot": "d66e56fb408763e36e8622eb56a8a1072ccc606476fe9e0765cca0dff95949b1",
"creationTimestamp": 1534851560,
"difficultyTarget": 402787433,
"nonce": 3785175761,
},
"hash": "000000000000000001fc2f61db1087c820da44599a82bda8ede1f3c82f67098c",
"work": 2305305885491475308752,
"height": 544379
}OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG[Version Byte][PubKeyHash][Checksum]The Spec implies that a compatible implementation support the capability to lookup block hash and merkle root values, to confirm that they correspond to actual mined blocks.
The Project Babbage team has implemented a block header management system called Chaintracks primarily to support validating merkle proofs as defined in the Spec.
We have a focus on space efficiency as we target the full range of client application deployment scenarios, including mobile and IoT.
You hear that a full set of block headers is only 50MB and only grows at 4MB per year - and this is true - but it refers to the serialized binary space required for just the headers, without the indices required to implement the Spec.
Because the required indices are hash values (merkle root and block header hash), which together make up 64 of the 80 block header bytes, it is likely unavoidable that any implementation will be approximately the size of the headers themselves: To turn a hash into a height, the indices must list all the hash values paired with their height values.
While it is possible to use partial indices and trade index size for IO operations, the indices are not actually needed at all. As Elon says, "the best part is no part."
The purpose of a merkle proof is to prove that a particular bit of data, a bitcoin transaction, was recorded at a particular location in the block chain.
The nodes in the proof enable the computation of what the merkle root should be for a block containing the transaction at a particular index position.
To complete the proof, it must be verified that the computed merkle root matches the actual merkle root of the target block.
Being given a merkle root value as part of the proof does NOT complete the proof.
The only thing that completes the proof is to confirm from a trusted copy of block headers that one exists with the computed merkle root value.
Being given a merkle root, block hash, or block header as part of the proof is only useful as an identifier for which block header to check in the trusted copy. If a matching block header isn't found, the proof is invalid, no matter what additional target values are provided.
Furthermore, if the trusted copy includes a merkle root index, then proof can be completed directly from the computed merkle root value: Look it up in your own index, if it is found, the proof is valid and the block is identified.
It is therefore the case, that the existing target and targetType values in a proof per the Spec are unnecessary.
It may actually be the case, that the only useful targetType would be height. With a height value, no indices are needed at all, the target block header can be found directly since all headers are exactly 80 bytes long. The trusted copy does not require any indices.
In "Binary form", proofs reserve two bits of "flags" bytes (bits 1 and 2) to identify what the "JSON form" calls the targetType of the proof.
Here are the current and proposed values for targetType:
Block Hash
existing
0
'hash' (default)
32 bytes
64 hex chars
Block Header
As discussed in Motivation above, the existing target types and target values are irrelevant to being able to complete a merkle proof given the remaining values.
The Project Babbage Chaintracks package currently implements the Spec with this extension.
A comparison of Chaintracks and the Pulse block header packages is listed under References.
The "Spec": TSC Merkle proof standardised format
The use of Bitcoin as a means of payment and store of value has gained significant traction in recent years. As the adoption of Bitcoin continues to increase, there is a need for a standard format for the envelopes that contain Bitcoin transactions. The lack of a standardized format for envelopes can lead to interoperability issues and hinder the development of new Bitcoin-native applications.
The SPV process, outlined in BRC-9, allows payment recipients to verify the validity of a transaction without downloading and verifying the entire blockchain. However, without a standard methodology for exchanging the required merkle proofs and input transactions, applications and businesses are unable to fully benefit from adopting SPV. BRC-8 addresses this issue by providing a consistent format for transaction envelopes that can be used for SPV.
We specify that the Everett-style Transaction Envelope is a JSON object that consists of the following fields:
rawTx (required): The BRC-12 hex transaction contained in the envelope in serialized hex string format.
inputs (required unless proof is provided): An object whose keys are TXIDs of each of the outpoints spent by the transaction. The values are objects containing:
rawTx (required): The transaction that was spent in serialized hex string format.
proof (required unless inputs is provided): If the transaction is confirmed in a block, the SPV proof of the transaction's inclusion is given in format as a JSON object.
mapiResponses (optional): An array of signed mAPI response objects, each of which is an object conforming to the mAPI specifications (either a successful broadcast acknowledgment or an affirmative status response).
inputs (optional): If the transaction is not yet confirmed in a block, this field is the same as the field from the root object, except referring to the previous transaction instead of the root transaction. The field nests recursively back in the tree of the previous unconfirmed transactions until each of the branches can ultimately be traced back to the chain of blocks with a proof at the end.
proof (optional): If the root transaction in the envelope is already confirmed in a block, the SPV proof of the transaction’s inclusion is given in format as a JSON object.
mapiResponses (optional): An array of signed mAPI response objects, each of which is an object conforming to the mAPI specifications (either a successful broadcast acknowledgment or an affirmative status response).
headers (optional): An array of 80-byte block header strings in hex format, starting with the block after the recipient's latest known block (indicated and communicated out-of-band) and ending with the latest header known to the sender.
pruned: true (optional): Indicates references to the same TXID are omitted in the inputs object, known as deduplication.
The inputs object is required unless the proof field is provided for the transaction. The inputs object can be used to create a tree data structure that nests recursively back in the tree of the previous unconfirmed transactions until each of the branches can ultimately be traced back to the chain of blocks with a proof at the end. The inputs object is optional when the transaction is already confirmed in a block and proof field is provided. The proof field serves as the proof of the transaction's inclusion in a block and is given in BRC-10 format as a JSON object.
The mapiResponses field is an array of signed mAPI response objects that are optional as per this specification, but may be required by higher-layer payment protocols such as BRC-29. If included, it should be omitted if proof is given. mAPI responses provide assurance that the transaction has been accepted and broadcast to multiple miners, making double-spend attacks harder to achieve.
The headers field is an optional array of 80-byte block header strings in hex format. This field can be omitted if no new headers are necessary for verification. The mechanism by which the parties negotiate about which headers are to be provided is beyond the scope of this specification.
The Everett-style Transaction Envelope uses a tree structure instead of a flat list structure to avoid providing information for the same transaction more than once. The tree structure has advantages as it can easily be validated with a simple recursive function, allowing parallelized processing of nodes further back in the tree at the same time as parents without needing to keep and reference a copy of the entire envelope while validating each node. This approach also allows for a degree of modularity because each node in the tree is itself a fully-valid envelope, and there is no need to re-scan and re-calculate which other transactions would be required to get a valid sub-envelope (very efficient for adding/removing/working with chains of dependent transactions).
A special case can be added when nodes at different depths in the tree depend on a common transaction, to remove tree-level duplications. The first time a transaction appears, it can be included as normal. Subsequent references to the same TXID can be omitted where they appear in the inputs object. To indicate that an envelope has been deduplicated in this way, the root object MUST contain pruned: true. With the issue of duplication solved, we believe a tree structure is the most intuitive and efficient approach.
The use of mAPI is optional and can be disabled by those who do not wish to use it in their implementations. Disabling mAPI allows “receiver-only broadcast” implementations to work. By not allowing mAPI as part of the data structure, the control is taken away from application developers and stakeholders who have widely varying requirements.
There are a wide array of potential applications, higher-layer payment protocols and use-cases for these envelopes. We stipulate that many systems will append (or even remove) certain fields from various nodes in these structures. This specification defines a baseline, and deviations from this baseline need only be documented by other standards which inherit from this one, such as BRC-29.
A web tool has been implemented which, given any TXID, will produce a valid Everett-style Transaction Envelope.
Developers are encouraged to create their own implementations and contribute to the ecosystem by sharing their work with the community. By doing so, they can help improve the interoperability between different Bitcoin wallet software and services, and further promote the adoption of the BRC-8 standard.
In addition to the web tool, developers should consult the BRC-9 and BRC-10 specifications for more information on implementing the Simplified Payment Verification (SPV) process and the format for SPV proofs, respectively. By adhering to these specifications, developers can ensure their implementations are compatible with existing and future Bitcoin wallet software and services that also follow the BRC standards.
While this standard provides a solid foundation for Everett-style Transaction Envelopes, there is always room for improvement and expansion. Future work could include:
Developing more efficient algorithms for constructing and validating the tree structure of transaction envelopes.
Expanding on the negotiation process for providing block headers, making it more seamless and efficient.
Creating additional tools and libraries to simplify the implementation of the BRC-8 standard in various programming languages and platforms.
Defining higher-layer protocols for live-updating non-final transactions, peer-to-peer data exchange, and efficient tree state reconciliation.
The internet today has an increasingly complex landscape of entities that users may or may not trust. As systems decentralize, the need to independently verify the identity of a digital entity becomes vital. By allowing entities to publish trust details at known locations (their domains), users are provided with a recognizable and straightforward method to retrieve and validate their public keys, hence building a more trustable web.
The trust anchor details MUST be published in a file named manifest.json.
The manifest.json file MUST be hosted using the HTTPS protocol. Plain HTTP is not supported.
Only Fully Qualified Domain Names (FQDNs) are supported for hosting the manifest.json file.
The trust details should be stored under the key babbage in the manifest.json file. Under the babbage key, the trust object should be defined with the following properties:
name: The human-readable name of the entity.
note: A brief note describing the role or function of the entity.
icon: A URL pointing to an icon image representing the entity.
publicKey: The public key of the entity in hexadecimal format.
name
MUST be a string.
MUST contain between 5 to 30 characters, inclusive.
note
MUST be a string.
MUST contain between 5 to 50 characters, inclusive.
icon
MUST be a valid image URL.
Should be accessed over HTTPS.
publicKey
MUST be a string in DER format.
MUST be compressed.
MUST use the secp256k1 elliptic curve.
Here is a valid example of a manifest.json containing the trust anchor details:
For this example:
name is "SigniCert", which has a length between 5 and 30 characters.
note has a length between 5 and 50 characters.
icon points to an image hosted over HTTPS.
publicKey is a compressed DER-encoded key using the secp256k1 curve, starting with "02" and is 66 characters long.
This technical specification provides a comprehensive framework for entities to publish trust anchor details at a known internet domain. By adhering to the defined structure and validation rules, we ensure a standardized method for users to access and trust these details. This is a step towards creating a more transparent and trustable digital landscape.
Ty Everett ([email protected])
The R-puzzle technique is a powerful cryptographic tool that can be used to create Bitcoin output scripts that can be unlocked by anyone who is able to solve for a value k, without revealing k to the world. The technique relies on the fact that in an ECDSA digital signature, the R component is computed by multiplying a generator point on the elliptic curve with this k-value. By constructing a Bitcoin output script that requires knowledge of this value, it is possible to create puzzles that can be solved by anyone who is able to compute the correct value for k. The R-puzzle technique is flexible and can be used for a variety of applications, including those that benefit when the solver does not reveal their solution to the world, only proving knowledge of it. R-puzzles also provide a way to create Bitcoin output scripts that can be unlocked without requiring knowledge of a specific private key, while still maintaining a high level of security and cryptographic strength. By serializing short-hand script templates in a standard way, implementers can maintain ecosystem-wide compatibility.
The creation of puzzles that can be unlocked by anyone who is able to solve a complex problem has become increasingly popular in recent years, particularly in the field of distributed computing. However, it is important to ensure that these puzzles are designed in such a way that they maintain a high level of security and cryptographic strength, while still allowing anyone to solve them without revealing any sensitive information.
To achieve this, it is valuable to create a script template that enables the solver to prove knowledge over a value, k, without revealing the k-value to the world. This provides a way to create puzzles that are secure and difficult to solve, while still allowing anyone to claim the solution and the associated rewards. In addition, it is desirable to employ a script template that does not require the creator of the puzzle to know which key will be used by the solver in claiming the solution, at the time the puzzle was created. Finally, there is also a need to standardize the way these scripts are expressed and exchanged between users of the Bitcoin network, to enable greater cross-compatibility and wider adoption.
We specify the following Bitcoin script template for R-puzzles:
We specify that the same private key and k-value must NEVER EVER be used, and that steps MUST be taken to prevent the same private key and k-value from ever being used to sign different messages in ECDSA. Failure to follow this provision will result in leakage of private keys and k-values.
We specify that the P2RPH output script is serialized using Base58 encoding. The serialized format is as follows:
Where:
[Version Bytes] is a prefix that identifies the network and the script type. The value of the version byte is 0x0e00 for the mainnet and 0x1c00 for the testnet.
[PubKeyHash] is a 20-byte RIPEMD160 hash of the SHA256 hash of the R-value.
P2RPH Bitcoin addresses for mainnet always start with an R because the [Version Bytes] we selected are 0x0e00. On testnet, they will always start with lowercase r because the [Version Bytes] are 0x1c00.
Mainnet example P2RPH address: RsQphNVccTzET8zwMNESLmTeJ4cJYxyc9cx
Testnet example P2RPH address: rjQPANNiecDK1a6jedyCsQ6UND4SnK6rJNe
The locking script is the script that is attached to the Bitcoin output that is being locked with the R-puzzle. When a Bitcoin transaction is created that spends this output, the unlocking script must provide the correct input to satisfy this locking script.
The locking script essentially creates a puzzle that can only be unlocked by providing an ECDSA digital signature that satisfies certain conditions. Specifically, the signature must have an R component that, when hashed with SHA256 and then RIPEMD160, produces the same value as the <hash> specified in the script. The S component of the signature can be computed using any key of the solver's choosing.
The unlocking script essentially satisfies the conditions set out by the locking script by providing a valid ECDSA digital signature with an R component that matches the hash specified in the locking script, and an S component that is computed using the solver's chosen private key.
The locking and unlocking scripts for the R-puzzle comprise a powerful and flexible tool for creating puzzles that can be solved by anyone who is able to compute the correct value for the k parameter used in the ECDSA digital signature. By using this technique, it is possible to create Bitcoin outputs that can be unlocked by anyone who is able to solve the puzzle, without requiring knowledge of a specific private key.
It is crucial to emphasize the importance of never using the same private key and k-value for different messages in ECDSA. Doing so can compromise the security of the digital signatures and result in the leakage of private keys and k-values, which can lead to the loss of funds or assets associated with those signatures.
Therefore, it is essential to take steps to ensure that private keys and k-values are never reused. One way to achieve this is by using a secure random number generator to generate new values for each signature. It is also important to store private keys securely and to use secure communication channels when transmitting them.
To further emphasize the importance of this provision, it is advisable to include clear warnings and instructions in any documentation or educational material related to the R-puzzle technique. This will help ensure that users understand the potential risks of reusing private keys and k-values and take the necessary precautions to prevent such incidents from occurring.
The first-known implementation for R-puzzles was created by .
Ty Everett ([email protected])
This document defines a protocol for creating and verifying digital message signatures in the Bitcoin ecosystem. It leverages the BRC-42 key derivation and BRC-43 invoice numbers to facilitate privately-verifiable signatures, increasing security and privacy in Bitcoin message interactions.
While Bitcoin Signed Messages (BSM) offer a simplistic solution for digitally signing Bitcoin messages, they lack capabilities for privately-verifiable signatures and they re-use keys, potentially compromising security and privacy. By incorporating BRC-42 key derivation, this standard enhances the security and private signing capabilities, providing a robust and unified standard for the Bitcoin ecosystem. Private signing enables a specific verifier to check the signature, without revealing its validity to the rest of the world.
The scope of this specification is to convey a message signature. The message data (the thing being signed) is not serialized as part of this signature structure.
One notable aspect of BSM is the need to prefix all messages with "Bitcoin signed message" to avoid inadvertently signing a Bitcoin transaction. However, since the new approach utilizes a new signing key for each message, there is no danger of signing Bitcoin transactions with a key capable of spending locked coins.
The protocol employs the BRC-3 digital signature process and makes use of the BRC-42 key derivation and BRC-43 invoice numbering scheme.
We specify message signing as the BRC-43 protocol ID, with security level 2. We specify that the signer generates a random 256-bit value to be used as the key ID when the signature is computed for a given counterparty.
The system operates in the following way:
Message sender computes the BRC-43 invoice number based on the security level (2), protocol ID (message signing), and their 256-bit key ID. We specify the key ID is in base64 format when added to the invoice number.
Message sender derives a child private key for the counterparty (or anyone) with the computed invoice number using the BRC-42 key derivation process.
Message sender then computes the digital signature using the ECDSA with the derived private key.
The standard serialization format encapsulates all the necessary information required for message verification into a binary representation. The format starts with a version number, followed by the identity key of the signer, the identity key of the recipient (or 0x00 to represent anyone if it's publicly verifiable), the 256-bit key ID used for the signature, and the ECDSA signature over the message. This structure ensures a standardized way for efficiently and securely transmitting and verifying signatures:
Ty Everett ([email protected])
The BSV network is designed to process transactions at scale, leveraging a robust, distributed system of miners, validators, and aggregators. This system balances parallel processing with sequential validation, ensuring transactions are both processed rapidly and recorded in the correct order. Below, we document the detailed processes by which BSV miners will come to handle transactions at scale.
Users and services across the globe create and sign Bitcoin transactions, broadcasting them to the network. The network's miners collect these transactions at numerous Points of Presence (PoPs), which act as edge nodes in the BSV ecosystem. The following steps outline the process:
Preliminary Checks: Upon receiving a transaction, miners at PoPs verify its scripts, ensuring they comply with network standards and contain valid signatures. If valid, the transaction proceeds to the next stage.
Batching: After initial verification, the miners batch transactions, preparing them for forwarding to regional aggregators. This batching process occurs periodically, facilitating efficient transfer and further processing.
After initial verification, transactions undergo a stripping process, which reduces each transaction to its essential elements:
Stripped Data: The remaining data includes:
The TXID and output indices of the consumed outputs.
The TXID of the transaction itself.
The number of new outputs it creates.
Where possible, edge validators summarize stripped transaction graphs before sending them to regional aggregators. This process is crucial in reducing the workload of higher-level aggregators:
Internal Transactions: The summarized graph only needs to include:
The TXIDs and output indices of previously consumed outputs.
The newly created outputs.
A merkle root for the transactions it includes.
After receiving stripped transactions and summarized graphs, regional aggregators build subtrees of transactions:
Subtree Formation: Subtrees are groups of transactions or graphs, each forming a summarized graph with a merkle root and lists of outputs consumed and created.
Multi-Level Aggregation: Multiple levels of regional aggregation may occur in parallel, gradually integrating more of the completed transaction graph into larger structures.
Fee Tracking: Fees from child graphs are aggregated to track the total fees collected by the parent graph.
Regional aggregators send subtree summaries to global aggregation systems, which then prepare the final block:
Graph Consistency: The system checks graph edges for consistency, ensuring no edge is consumed by one graph before being created by another.
Block Template: The block assembler combines these subtrees, including the coinbase transaction with the correct amount of fees, forming a finalized block template ready for hashing.
Proof of Work: Upon finding a valid proof-of-work header, the new block header is immediately propagated throughout the network for parallel validation by all systems.
Once a valid header is found, it triggers a cascading parallel proof completion process:
Downstream Propagation: The header propagates down to all regional aggregators, who append the upper layers of the merkle tree to their subgraph roots.
Notifier Chain: Each regional aggregator notifies its child aggregators or edge validators, which in turn notify their children. This chain of notifications continues until all edge validators have completed proofs for all transactions they submitted.
End-User Notification: Finally, edge validators notify network end-users of the completed proofs available for their transactions.
The BSV network effectively balances parallel processing and sequential validation to achieve scalable transaction processing. By employing edge-level validation, regional aggregation, and global assembly, the system reduces the workload at higher levels, ensuring rapid, consistent processing. This layered, hierarchical approach to aggregation, alongside the use of transaction stripping and summarization, makes the BSV network a robust solution for scalable blockchain processing.
To model and describe the BSV network quantitatively, it is essential to focus on the parameters and metrics that influence the performance and scalability of the system. Here are some key components and steps to construct a quantitative model of the BSV transaction processing system:
Transaction Arrival Rate: The rate at which transactions are received at edge nodes, typically expressed in transactions per second (tps).
Batch Size: The number of transactions processed together in batches at edge nodes before being sent to regional aggregators.
Transaction Processing Time: Time taken to verify and strip a single transaction.
Propagation Delay
Throughput: The total number of transactions processed per unit time across the network.
Latency: The total time taken from when a transaction is submitted until it is included in a validated block.
Resource Utilization: Measures how efficiently resources (computational, bandwidth) are used.
Scalability: The ability of the network to handle increasing transaction loads without a proportional increase in latency or resource costs.
Model the transaction inputs as a Poisson process or another suitable stochastic process that reflects real-world arrival patterns.
Model the batch processing as a queue where transactions are collected, verified, and stripped. Queuing theory, specifically a batch service queue model, can be applied here.
Calculate the processing rate of batches based on the transaction processing time and batch size.
Use a hierarchical tree model to represent the multi-level aggregation process. Each node in the tree represents an aggregation point (edge, regional, global).
Model the propagation of transaction summaries through this tree, accounting for delays and processing times at each node.
Model the final block assembly as a sequential process that only begins once all necessary summaries and proofs are available.
Consider the proof-of-work process and its impact on block propagation and finality.
Construct a series of equations or a simulation model that integrates these components. For example, use differential equations to represent the rate of change in the queue sizes, or develop a discrete-event simulation to model the dynamic interactions and delays.
Validate the model against real data or simulated data to ensure it accurately represents the BSV network's behavior.
Use the model to simulate different scenarios, such as increased transaction loads, changes in batch sizes, or different network delays, to study their impacts on throughput, latency, and scalability.
Optimization: Use the model to find optimal parameters (e.g., batch size, number of edge nodes) that maximize throughput or minimize latency.
Capacity Planning: Estimate the resources needed to achieve certain performance metrics under expected future loads.
Scenario Analysis: Assess the impact of network changes or growth on performance and resource needs.
By quantitatively modeling these processes, the BSV network's design and operation can be better understood, optimized, and scaled to meet the demands of a high-volume, real-time transaction processing environment.
Ty Everett ([email protected])
This BRC defines Atomic Background Evaluation Extended Format (Atomic BEEF) Transactions, a variant of the BEEF transaction format (as defined in BRC-62) that focuses on atomicity—specifically, ensuring that all the data within a BEEF structure relates to a single "subject transaction." This format disallows unrelated transactions within the BEEF structure, reinforcing a clear, minimalistic data set for transaction validation while maintaining efficiency in bandwidth usage.
Atomic BEEF is designed to solve problems arising from multiple graphs of unrelated transactions being aggregated into a single BEEF, which can complicate validation and increase data complexity. Atomic BEEF introduces a strict structure optimized for single-transaction use cases, ensuring that every transaction in the BEEF relates to a defined subject transaction. This format is particularly suited for applications where the verification of a singular transaction and its direct ancestry is critical.
The introduction of the BEEF format (BRC-62) provided a solution for transmitting transaction data in a binary format to allow (SPV) with minimal bandwidth. However, BEEF allows the inclusion of multiple, potentially unrelated transactions aggregated within the same structure, making it flexible but not ideal for cases where strict atomicity is needed.
Atomic BEEF is motivated by the following challenges:
Transaction Ambiguity: In BEEF, there is no explicit constraint to keep transactions related to a single subject transaction. This can lead to ambiguity when unrelated transaction graphs are included in the same BEEF structure, making validation and verification more complex.
Simplified Use Cases: Many use cases of SPV revolve around validating a single payment or transaction, not multiple. Atomic BEEF optimizes for this by focusing entirely on the recursive transaction graph relevant to a specific transaction.
Data Minimalism: For micropayments or systems requiring highly efficient data transfer, limiting the number of transactions included in the BEEF to only the required set for validation of a single subject transaction reduces unnecessary overhead.
In response to these motivations, Atomic BEEF introduces a new mechanism for enforcing the atomicity of BEEF transactions.
Atomic BEEF is an extension of BEEF (BRC-62) with specific structural and validation rules that ensure atomicity. It adheres to the same encoding and transaction structure principles, with the following additional constraints:
Atomic BEEF introduces a unique header prefix that ensures the atomicity of the BEEF structure. The Atomic BEEF format starts with a constant prefix followed by the TXID of the "subject transaction." This TXID serves as the reference transaction that the entire BEEF structure must relate to.
Prefix: The first 4 bytes of the Atomic BEEF structure must be 0x01010101, a fixed constant to indicate Atomic BEEF.
Subject TXID: The next 32 bytes are the TXID of the "subject transaction"—the primary transaction that the rest of the structure must validate and verify.
Atomic BEEF strictly enforces that all transactions included in the structure must be part of the subject transaction's dependency graph. This means that every transaction in the BEEF structure must either be:
The subject transaction itself, or
An ancestor transaction required to validate the inputs of the subject transaction.
Transactions that fall outside of this graph, or are unrelated, are not allowed in Atomic BEEF. This ensures that the BEEF is entirely focused on a single, verifiable transaction and its direct dependencies.
The validation of an Atomic BEEF structure follows these steps:
Subject Transaction Identification:
The first step in validation is to extract the subject TXID from the header. If the BEEF structure does not include the subject transaction or if any unrelated transactions are present, the validation fails.
Transaction Graph Validation:
Atomic BEEF introduces two specific error conditions that must cause validation to fail:
Missing Subject Transaction: If the BEEF structure does not include the subject transaction identified by the subject TXID in the header, validation fails.
Unrelated Transactions: If any transactions within the BEEF structure fall outside the dependency graph of the subject transaction, validation fails.
These error conditions ensure that Atomic BEEF maintains strict atomicity and avoids the ambiguity and complexity of unrelated transactions within the same structure.
Ty Everett ([email protected])
We define a mechanism for requesting and receiving digital signatures over the abstract communications channel first described by . We rely on the invoice numbering and permission scheme on top of for key derivation, enabling the creation of private digital signatures that can only be verified by the counterparty. A signer derives their own child private key and uses it to compute an ECDSA signature, which is then communicated to the verified, together with the signer's identity key, the message, and the protocol ID and key ID used. The verifier uses this information to derive the corresponding child public key for the signer, which is then checked against the signature and message with ECDSA. Without the shared secret, no third parties can discover the correct child public key, making then unable to validate the signature.
Brayden Langley ([email protected])
The Window Wallet Communication Substrate is a standardized interface that enables seamless integration between web applications and browser embedded Bitcoin wallets. It serves as a unified gateway for applications to access various wallet functionalities, including the creation of Bitcoin transactions, encryption, digital signature creation, and more. By establishing this standardized interface, applications gain the ability to support multiple wallet providers, enhancing flexibility and choice for users in managing their Bitcoin-related tasks. This interface empowers users with greater control and accessibility while maintaining compatibility across different applications and wallets.
Ty Everett ([email protected])
This BRC specifies a protocol for exchanging simple P2PKH payments between a sender and a recipient in a way that is SPV-compliant and cross-compatible. The protocol makes use of key derivation to derive public and private keys between users, transaction envelopes to relay SPV-related information between the sender and the recipient, and a SPV implementation to enable the recipient to obtain confidence that the transactions are valid and legitimate. This BRC specifies the JSON version of the payment message format used by the sender when exchanging the payment with the recipient, and the steps and processes involved in constructing and validating a payment message.
Darren Kellenschwiler ([email protected]) Damian Orzepowski ([email protected])
Allowing humans to securely exchange public keys using web apis and an off channel sharing of TOTPs.
Brayden Langley ([email protected])
To ensure a seamless process for submitting payments to a wallet, there must be a standardized method by which this process takes place. Wallets that implement this standard will be able to support the direct submission of transactions from applications to a user's wallet along with the associated SPV information as defined by . The required message structure and fields are defined below in the section, and the expected response to provide is defined in the section. Wallets that choose to implement this specification will allow applications that facilitate the exchange of payments to be built in a way that is interoperable, extensible, and SPV compliant.
{
"name": "SigniCert",
"babbage": {
"trust": {
"name": "SigniCert",
"note": "Certifies legal first and last name, and photos",
"icon": "https://signia.babbage.systems/images/signiaIcon.png",
"publicKey": "0295bf1c7842d14babf60daf2c733956c331f9dcb2c79e41f85fd1dda6a3fa4549"
}
}
}The string should start with either "02" or "03".
MUST be in hexadecimal format, and 66 characters in length (including the starting "02" or "03").
Graph Representation: The stripped transaction thus forms a node in the transaction graph, containing only the necessary information to link it to other transactions.
The total amount of fees left for the miners.
Merkle Paths: The summarized graph also contains partial merkle paths, linking all transactions or child graphs to the merkle root of the summarized graph. However, this information only needs to be retained locally and never shared upwards.
Aggregation Time: Time required to aggregate transactions into subtrees and summarize them into merkle roots at various levels (edge, regional, global).
existing
1
'header'
80 bytes
160 hex chars
Merkle Root
existing
2
'merkleRoot'
32 bytes
64 hex chars
Height
new
3
'height'
Bitcoin VarInt
integer
[Checksum]Message sender serializes their own public key, the recipient public key, the selected key ID, and the ECDSA signature according to the serialization format below.
A message recipient can then deserialize the structure to learn the sender's public key and the correct key ID for verification.
The recipient can then use their own private key, the public key of the sender, and the computed invoice number to verify the signature over the message.
32 bytes
The specific key ID used for the signature
Signature
Variable
The ECDSA signature of the message, in DER format
Version
4 bytes
Defines the version of the standard used. Currently 0x42423301
Signer ID
33 bytes
Identity key of the signer (DER, compressed)
Verifier ID
1 or 33 bytes
Identity key of the recipient (DER, compressed), or 0x00 if anyone
Key ID
Streamlined Validation: By restricting the BEEF to a single subject transaction and its dependencies, Atomic BEEF reduces the complexity of the validation process, as there is a clear expectation of the data's scope.
The structure must not contain any transactions that are not part of the subject transaction's dependency graph.
Merkle Proof Validation:
As with the original BEEF format, any included transactions that have been mined must be accompanied by BSV Universal Merkle Path (BUMP) data to prove their inclusion in the longest chain of blocks.
The Merkle roots derived from the BUMP data are verified against the local header service.
Final Transaction Validation:
The final validation is complete when all necessary transactions (including script evaluations) and proofs have been processed, and the subject transaction is confirmed to be valid based on its dependencies.
Atomic Prefix
Fixed constant 0x01010101, indicates the start of an Atomic BEEF structure
4 bytes
Subject TXID
The TXID of the subject transaction that this BEEF structure will validate
32 bytes
When you use hosted servers for payment output generation there's no way to detect if the host has been compromised and is sharing keys not associated with the intended recipient.
A random ID was generated in order to avoid label collisions in the capability document of a paymail server.
Roughly speaking you create a shared secret between you and the counterparty using their public key and your private key. If you each do this you can arrive at a shared secret. You then derive a time hashed one time pass code to prove you both have the same value - each sharing the TOTP and validating the counterparty's. Thereafter you can use that public key knowing that it really is that counterparty without fear of MITM attacks.
Sender wallet: generate random hash
Sender wallet generate public key from private key and random hash
Sender wallet is sending "add contact request" to Sender BUX containing: a. contact paymail address b. random hash c. generated public key
Sender BUX is performing Paymail host discovery (which is described on this page: https://tsc.bsvblockchain.org/standards/paymail/#ServiceDiscovery)
Sender BUX is performing Paymail capability discovery on Receiver BUX by requesting /.well-known/bsvalias
If Receiver BUX doesn't contain Proven Identity Key Exchange (PIKE) capability, Sender BUX should return an error to Sender Wallet
else the Sender BUX is sending a "Add a contact" request to Receiver BUX on the endpoint provided in PIKE capability. This request contains: a. sender paymail address b. random hash c. generated public key
Receiver BUX stores that "contact" as "waiting" in database
Receiver BUX is answering to Sender BUX with success
Sender BUX is storing the "contact" as "waiting"
Receiver Wallet asks Receiver BUX for "waiting" "contact" to confirm or reject
Receiver BUX is responding with the list of contacts
If Receiver Wallet reject the contact, then: a. it is sending rejection request to Receiver BUX b. Receiver BUX is removing the contact from database c. In that case the flow ends
else: Receiver Wallet generate public key from private key and random hash from the contact
Receiver Wallet is sending the request for Receiver BUX for accepting the contact with informations: a. paymail address for contact to accept b. generated public key
Receiver BUX is marking a "contact" as "accepted"
Receiver BUX is sending the "contact accepted" request to Sender BUX with: a. paymail address of Receiver b. generated public key c. random hash
Sender BUX is validating random hash, and stores received public key in "contact" and marks it as accepted
Sender Wallet is asking Sender to confirm contact
Sender asks Receiver for TOTP
Receiver checks TOTP for Senders paymail in Receiver Wallet
Receiver Wallet calculates shared secret based on private key and Senders public key
Receiver Wallet generates TOTP based on shared secret
Receiver Wallet sends back TOTP to Receiver
Receiver is responding with TOTP to Sender
Sender is providing TOTP to Sender Wallet
Sender Wallet calculates shared secret based on private key and Receiver public key
Sender Wallet generates TOTP based on shared secret
Sender Wallet is comparing received TOTP with generated TOTP. If their match then it marks contact as confirmed.
Sender Wallet is now generating another TOTP and provides to Sender
Sender is providing TOTP to Receiver
Receiver is providing TOTP to Receiver Wallet
Receiver Wallet is generating TOTP and comparing it with received TOTP If their match then it marks contact as confirmed.
.unlock
<ECDSA sig> # This is the first stack element and represents the ECDSA digital signature provided by the
# solver. It must have an R component that, when hashed with SHA256 and then RIPEMD160,
# produces the same value as the <hash> specified in the locking script. The S component can
# be computed using any key of the solver's choosing.
<key> # This is the second stack element and represents the private key that was used to compute the S component of the
# signature. It can be any key of the solver's choosing, as long as it produces a valid S component for the signature that
# was provided as the first stack element.
.lock
OVER # Duplicates the second stack element (the signature) so that it can be used later in the script. Now the stack has
# three elements, <signature> <key> <signature> and we can work with the top <signature> without bothering the bottom one.
3 SPLIT #
NIP #
TRUE SPLIT # This section of the script picks out the R-value from the signature.
SWAP #
SPLIT #
DROP #
HASH160 # Here, we hash the R-value with SHA256 and then again with RIPEMD160 (the combined operation is HASH160).
<hash> EQUALVERIFY # The hash we calculated in the previous step is compared with <hash> and if they are not equal then the script fails.
CHECKSIG # Finally, the ECDSA signature is checked.[Version Bytes][RPuzzleHash][Checksum]42423301 # version
032e5bd6b837cfb30208bbb1d571db9ddf2fb1a7b59fb4ed2a31af632699f770a1 # signer
02e5e1a150745253aff65cdbcef722873110d89c396223e3d8715f018e72f7d4f8 # verifier
8166f775e9128dfea9ddb8fc92c655cfaae6795e18911d7d1b6d981ac8664e36 # key ID
30450221009fd5c15bb289bd3a26454131b269b4f550f32dca4c21fde29cef3ebaa913363c022052d421eae633c363b1dad674a1da4821c885425cc636147388b8b08b439721be # signature42423301 # version
032e5bd6b837cfb30208bbb1d571db9ddf2fb1a7b59fb4ed2a31af632699f770a1 # signer
00 # verifier (anyone)
e8ff1624ede8d6ca5303badb68dec8f226284385bed3bc7c19b578a905866587 # key ID
3045022100b304d38e25106c732f0247fab70b9cf47d5434c08d7a94ef4ad7069bae9a9a0e02202f4e3b4e5d7d698cfebe64f643dbcddc1631e6f09aece0850613f602ec4d50e6 # signature01010101 // Atomic BEEF Prefix
2222222222222222222222222222222222222222222222222222222222222222 // Subject TXID
0100beef // Start of standard BEEF structure...
...brfcid: 8c4ed5ef8ace
title: Proven Identity Key Exchange (PIKE)
author: Darren Kellenschwiler, Damian Orzepowski
version: 1.0.0sequenceDiagram
actor Sender
participant Sender Wallet
participant Sender BUX
participant Receiver BUX
participant Receiver Wallet
actor Receiver
Sender->>Sender Wallet: Add receiver as contact
activate Sender Wallet
Sender Wallet->>Sender Wallet: Generate random hash
Sender Wallet->>Sender Wallet: Generate public key from private key and random hash
deactivate Sender Wallet
activate Sender Wallet
Sender Wallet->>Sender BUX: Add contact request
activate Sender BUX
Sender BUX->>Sender BUX: Paymail host discovery
Sender BUX->>Receiver BUX: Paymail capability discovery
activate Receiver BUX
Sender BUX->>Receiver BUX: Add a contact request
Receiver BUX->>Receiver BUX: Store contact as "waiting" in database
Receiver BUX->>Sender BUX: Success
deactivate Receiver BUX
Sender BUX->>Sender BUX: Store contact as "waiting"
Sender BUX->>Sender Wallet: success
deactivate Sender BUX
Sender Wallet->>Sender: Display success
deactivate Sender Wallet
Receiver Wallet->>Receiver BUX: Get "waiting" "contact" to confirm or reject
activate Receiver Wallet
activate Receiver BUX
Receiver BUX->>Receiver Wallet: List of contacts
deactivate Receiver BUX
alt reject contatct
Receiver Wallet->>Receiver BUX: Rejection request
activate Receiver BUX
Receiver BUX->>Receiver BUX: Remove contact from database
deactivate Receiver BUX
else accept contact
Receiver Wallet->>Receiver Wallet: Generate public key from private key and random hash
Receiver Wallet->>Receiver BUX: Accept contact request
activate Receiver BUX
Receiver BUX->>Receiver BUX: Mark contact as "accepted"
Receiver BUX->>Sender BUX: Contact accepted request
Sender BUX->>Sender BUX: Validate random hash and store received public key
Sender BUX->>Receiver BUX: success
Receiver BUX->>Receiver Wallet: success
deactivate Receiver BUX
deactivate Receiver Wallet
end
Sender Wallet->>Sender: Confirm contact?
activate Sender
Sender->>Receiver: TOTP request
activate Receiver
activate Receiver
Receiver->>Receiver Wallet: Check TOTP for Senders paymail
activate Receiver Wallet
Receiver Wallet->>Receiver Wallet: Calculate shared secret based on private key and Senders public key
Receiver Wallet->>Receiver Wallet: Generate TOTP based on shared secret
Receiver Wallet->>Receiver: TOTP
deactivate Receiver Wallet
Receiver->>Sender: TOTP
deactivate Receiver
Sender->>Sender Wallet: TOTP
activate Sender
activate Sender Wallet
Sender Wallet->>Sender Wallet: Calculate shared secret based on private key and Receiver public key
Sender Wallet->>Sender Wallet: Generate TOTP based on shared secret
Sender Wallet->>Sender Wallet: Compare received TOTP with generated TOTP
Sender Wallet->>Sender Wallet: Mark contact as confirmed
Sender Wallet->>Sender Wallet: Generate another TOTP
Sender Wallet->>Sender: TOTP
deactivate Sender Wallet
deactivate Sender
Sender->>Receiver: TOTP
deactivate Sender
activate Receiver
Receiver->>Receiver Wallet: TOTP
activate Receiver Wallet
Receiver Wallet->>Receiver Wallet: Generate TOTP and compare it with received TOTP
Receiver Wallet->>Receiver Wallet: Mark contact as confirmed
deactivate Receiver Wallet
deactivate Receiver
deactivate ReceiverThe increasing use of Bitcoin wallets has highlighted the need for a secure and interoperable digital signature standard that can be used across different applications. Digital signatures provide a vital aspect of data authentication, allowing for the verification of information and secure transactions, and are critical to ensuring the integrity of the Bitcoin network.
While other solutions have proposed digital signatures within a wallet, none of them have provided a unified and open standard that supports both privately and publicly verifiable signatures, while also incorporating proper BRC-42 key derivation. The lack of such a standard has created a fragmentation within the ecosystem, with each application requiring its own solution, thus hindering interoperability and leading to duplicated effort.
To address this issue, the BRC-3 standard has been created to enable the creation and verification of digital signatures using BRC-43 invoice numbering and permission scheme on top of BRC-42 for key derivation. This allows for the creation of private digital signatures that can only be verified by the intended counterparty, while maintaining security against third-party tampering.
The BRC-3 standard not only provides a secure and interoperable solution for wallet implementations, but it also enables new use cases and experiences that were previously not possible. With a unified standard, different wallets can create and verify each other's digital signatures seamlessly, reducing friction and enabling greater innovation.
We start with the same constructs defined in BRC-43: users with clients, protocols and applications. We stipulate the use of BRC-43 invoice numbers in the context of BRC-42 key derivation, and we build on top of the permissions architecture defined by BRC-43.
When an application sends a message to a client requesting that data be signed, the message comprises:
The data to sign
We stipulate the following process for digital signature creation:
The message signer begins by computing the BRC-43 invoice number based on the security level, protocol ID, and key ID
The message signer uses BRC-42 key derivation with the computed invoice number to derive their own child private key
The message signer computes the digital signature using ECDSA with their derived child private key (this specification is silent about ECDSA k-value utilization)
We stipulate the following process for message verification:
The verifier somehow comes to know the signature, the counterparty (signer), the security level, protocol ID, and key ID. The mechanism for conveying this information to the verifier is beyond the scope of this specification.
The verifier begins by computing the BRC-43 invoice number based on the security level, protocol ID, and key ID
The verifier uses BRC-42 key derivation with the computed invoice number, their own private key and the public key of the sender, to compute the signer's child public key
The verifier uses ECDSA to verify the signature against the message using the signer's child public key
We build upon the abstract messaging layer first described in BRC-1. Specifically, we define five new BRC-1 messages to facilitate requests and responses for signatures and verification operations, and error handling. For each of the messages, we stipulate that there exists some out-of-band mechanism for the parties to communicate which of the messages are being exchanged, removing the need for a message type field. Finally, specifically for the request messages, we stipulate that the message comprises a header and a payload, with the payload containing the data (ciphertext or plaintext), and the header containing the other information. For the response messages, no message header is defined and the payload simply contains the specified data.
The signature creation request is a message sent by the BRC-43 application to the client. It contains a header with the following information:
protocolID
The security level and protocol ID represented as an array. For example, [0, "hello world"] represents a level-0 protocol with open permissions, while [2, "document signing"] represents a level 2 protocol.
keyID
The key ID
counterparty
The counterparty, anyone or self
The message payload comprises the data to sign.
The response message comprises the ECDSA digital signature in DER format.
The signature verification request is a message sent by the application to the client. It contains a header with the following information:
protocolID
The security level and protocol ID represented as an array. For example, [0, "hello world"] represents a level-0 protocol with open permissions, while [2, "document signing"] represents a level 2 protocol.
keyID
The key ID
counterparty
The counterparty, self or anyone
signature
The DER-formatted signature for verification
The message payload comprises the data to verify.
The response message comprises a JSON payload containing the following fields:
result
The value is true if the signature is valid, otherwise it is false.
If the client is unable to fulfill the signature creation or verification requests for any reason, we specify that it should respond with a JSON-formatted Signature Error. The fields for the object are specified as follows:
status
This should always be a string comprising "error".
code
A machine-readable error code. Extensions to this standard can define specific error codes and standardize additional fields. Codes are strings, for example "ERR_VERIFICATION_FAILED".
description
All errors must have a human-readable description field that describes the error. This allows the application to represent the error for the user.
One example of a Signature Error is given below:
For compatibility with this signature scheme, we stipulate the following:
Any user who knows the following identity private key (counterparty=anyone)...
...should be able to use security level 2, the BRC3 Test protocolID with keyID 42 and the following counterparty (signer)...
...to verify the message with the following digital signature (DER format)...
The message that was signed is:
This digital signature capability is incorporated into the Babbage SDK
The motivation behind this standard is to enable web browsers to directly integrate Bitcoin wallet functionality without relying on an additional application running on the client's device.
Although BRC-5 defines a standard for local communication over HTTP, integrating wallet functionality in the browser eliminates the need for external wallet applications, reducing the overhead of inter-process communication and network requests.
This also eliminates the need for users to switch between multiple applications when approving permissions, creating transactions, etc. They can perform all wallet-related tasks within the web application they are already using, resulting in a more cohesive and convenient user experience.
We define a specification for providing access to Bitcoin wallet functionality via the global window object that is directly available in all standard browser implementations.
Once the browser has verified that the user is authenticated, an object labeled CWI should be added to the window object to provide access to the standard wallet functionality as defined by BRC-56.
For each of the message pairs (request and response) incorporated into BRC-56, we specify the existence of a corresponding message type with a specific function name:
window.CWI.createAction()
window.CWI.encrypt()
window.CWI.decrypt()
window.CWI.createSignature()
window.CWI.verifySignature()
This will allow applications to call functions with the following syntax:
We specify that all required parameters are provided in an object to the CWI functions.
Example Identity Key Request
Example Encrypt Function
Implementors of this BRC should follow the abstract messaging layer as defined by BRC-56 to provide support for standard messaging types, and then modify the window object created to include this functionality in a CWI object.
The motivation for this BRC is to establish a simple, secure, and cross-compatible payment protocol that facilitates P2PKH payments across the BSV ecosystem. The protocol utilizes BRC-42 key derivation to provide a privacy-enhanced approach to P2PKH transactions. The incorporation of BRC-9 SPV checking ensures secure validation of transactions while maintaining efficient processing times. This standard aims to simplify the process of P2PKH payments for wallets and applications, providing a straightforward implementation path for developers.
We specify a protocol for exchanging simple P2PKH payments between a sender and a recipient. The protocol employs BRC-42 key derivation to derive related public and private keys with invoice numbers, BRC-8 transaction envelopes to relay SPV-related information, and BRC-9 SPV checks to enable the recipient to validate the transactions.
As defined by BRC-42, the sender and the recipient each have a master private key and a master public key. The invoice number is used to derive related keys, which can be used to facilitate payments. A payment sender can derive a public key for the recipient, and with the same invoice number, the recipient can derive the corresponding private key.
We start with the need for a unique identifier for the payment, used for key derivation. To provide for the possibility that multiple outputs can be exchanged as part of the same payment, we also need a UTXO-specific number. We specify these two values as follows:
Derivation Prefix: A unique value generated by the sender of a payment to distinguish keys used within this payment from keys used in other payments.
Derivation Suffix: Each suffix is a unique value generated by the sender, so that each UTXO in the same payment has a different key, and so that the keys are not linkable by third parties.
The keys that protect the funds exchanged under this protocol are derived with BRC-42 key derivation by the sender from the identity key of the recipient. The invoice number is specified as follows:
Where:
2 is a protocol security level denoting the access restrictions applied under this protocol (see BRC-43 Security Levels)
3241645161d8 is a magic number that denotes compliance with this BRC
<derivationPrefix> is the payment-specific prefix used to arrive at a common key universe for all outputs in this payment
<derivationSuffix> is the UTXO-specific suffix used to arrive at a unique key for a specific P2PKH UTXO
The payment message format used by the sender when exchanging the payment with the recipient is a JSON object comprising:
"protocol": This field denotes that the JSON object comprises a payment message according to this protocol. Its value should be set to 3241645161d8.
"senderIdentityKey": The recipient will need to know the public identity key of the sender in order to validate the incoming payment. This field's value should be the 33-byte, compressed, hex-encoded secp256k1 public key of the sender
"derivationPrefix": This field denotes the payment-wide derivation prefix used by the sender when the keys for the payment UTXOs were derived
"transactions": This field comprises an array of one or more extended transaction envelopes. Each of the envelopes is extended with an additional "outputs" field, which is specified below
Within each of the BRC-8 transaction envelopes, there is an additional field called outputs. This field is an object whose keys are integers that correspond to the output numbers in the specific transaction, all of which are intended by the sender to be redeemable by the recipient as part of this payment. The object values are objects that contain the suffix property, which is the derivation suffix for the specific UTXO being referenced. Here is an example of the outputs field:
This example would be affixed onto an envelope that contained a transaction where the fourth, fifth, and eighth outputs (zero-indexing) are intended by the sender to be redeemable by the recipient as part of a payment.
The steps for a sender to send a payment under this protocol are as follows:
Learn the identity key of the recipient, determine the total amount of the payment and generate a derivation prefix.
Decide on the number of outputs (across one or multiple transactions) that will comprise the payment.
For each output, generate a derivation suffix for the output and decide on the number of satoshis in the output.
For each output, use key derivation to derive a public key for the recipient for the output, and use the public key to construct a P2PKH Bitcoin output script as per .
Use any wallet capable of creating Transaction Envelopes to construct and sign transactions that contain the output scripts derived for the recipient from the previous step.
With all of the transactions, the derivation prefix, the derivation suffixes, and your sender identity key in hand, construct a payment message for the recipient that contains this information.
When the recipient processes their incoming transactions, they check that the public key hashes in the output scripts are properly derived. The recipient should also perform the standard BRC-9 SPV checks. If these checks pass, the payment is marked as accepted and acknowledged, and goods or services can be rendered to the payee.
Implementations of this protocol should adhere to the key derivation scheme and the payment message format specified in this BRC. The transaction submission system within any wallet can implement this protocol and validate that incoming transactions submitted under this protocol contain UTXOs that are properly derivable by the recipient. The protocol is intended to be cross-compatible and SPV-compliant.
BRC-50 standardizes payment submission to a wallet, improving the user experience for incoming Bitcoin payments. This enables higher-layer applications to add funds to a user's wallet without needing to maintain their own external balance. By standardizing payment submission, developers can create interoperable, extensible, and SPV compliant applications. This ensures a seamless and secure payment process, benefiting both wallet providers and end-users.
For this specification, we assume that there exists a wallet that facilitates a channel by which communication can occur with an application, such as over HTTP as defined in BRC-5. Based on this premise, we specify a standard by which payments can be submitted from an application to a wallet which then receives and processes the payment.
We define an extension to the abstract BRC-1 messaging layer as the Payment Submission Message which comprises a JSON object with the following fields:
Several of the same fields as defined in BRC-29 are present in this message. This standard essentially facilitates an implementation of BRC-29 over an abstract, wallet-to-application interface.
protocol
(string, required)
This field denotes that the JSON object comprises a payment message according to the given protocol (such as 3241645161d8 for ).
transaction
(object, required)
The transaction envelope to submit, including key derivation information (the "Outputs Extension" as defined in )
senderIdentityKey
(string, required)
The recipient will need to know the public identity key of the sender in order to validate the incoming payment. This field's value should be the 33-byte, compressed, hex-encoded secp256k1 public key of the sender
The transaction field is a JSON object that conforms to the BRC-8, with the BRC-29 Outputs Extension. Only one transaction can be submitted per request.
This provides the wallet with enough information to verify and receive payments from an application.
Here is an example of a simple Payment Submission Message:
The Payment Acknowledgment Message is the response that should be returned from the wallet to the calling application with the status of the request, and a reference number for the transaction submitted.
Here is an example response from a successful payment submission:
If an error occurs during submission, an internal error should be thrown which can be caught and handled by the application code.
Wallets can implement this specification by providing access to a submitDirectTransaction route over a given BRC-1 communications substrate, such as BRC-5, BRC-6 or BRC-7.
A specific implementation of this BRC can be seen in the Babbage Dojo/Ninja architecture.
Ty Everett ([email protected])
This document proposes a solution for synchronizing data across UTXO-based overlay networks by specifying a secure and efficient mechanism for submitting and processing transactions. By utilizing a BRC-31 protected server hosting an API endpoint, overlay network nodes can process and admit transactions based on their relevance to the hosted topics. This standard defines the parameters and processing steps needed to track topical UTXOs.
As discussed in , UTXO-based overlay networks provide a secure and scalable solution to managing states derived from the Bitcoin network. In order to maintain a consistent and up-to-date state across nodes, a method of data synchronization is required. Additionally, it is essential to provide a standard way for network participants to submit new transactions and notify network operators of their relevance to hosted topics. This document aims to address these needs by outlining a clear and well-defined process for submitting transactions and processing them based on topic-specific rules.
We start with a server that is set up to track the state of one or more topics. Every individual server implementing this specification decides what specific topic labels mean, and which rules they want to apply when admitting new UTXOs.
The protected server will host a POST /submit API endpoint, which accepts JSON request payloads. The JSON request body will include a transaction envelope (rawTx, inputs, mapiResponses, proof) and an additional field called topics. The transaction envelope facilitates SPV checking, while the topics field denotes which overlay network topics should be notified about the transaction.
Upon receiving a transaction, the overlay network node will perform the following steps:
Verify the identity of the sender and establish the required level of identity for submitting transactions, negotiating under the protocol to the satisfaction of the network operator and the transaction sender.
Check whether the node hosts at least one of the tagged topics from the payload. If none are hosted, the node returns an early response indicating no outputs were admitted. Otherwise, it can drop any unhosted topics from the list before proceeding.
Use the process to verify the submitted envelope and confirm the transaction's legitimacy.
We provide an example of an HTTP request and response.
In this example, a client submits a transaction to the /submit API endpoint. The JSON payload contains the transaction envelope (rawTx, inputs, mapiResponses, proof) and an additional field called topics, which includes two example topics: "example_topic_1" and "example_topic_2".
The overlay network node processes the transaction and determines that outputs 0 and 2 are admissible for "example_topic_1" and outputs 1 and 2 are admissible for "example_topic_2". The node then returns an HTTP response with a status of "success" and a topics object containing the admitted outputs for each topic.
To implement this data synchronization solution, developers should first set up a protected server to host the POST /submit API endpoint. The server should be capable of processing JSON request payloads and performing the necessary transaction validation using .
Overlay network nodes must be programmed to process incoming transaction submissions following the steps outlined in the specification. This includes verifying the sender's identity, checking for hosted topics, validating the transaction using , and applying topic-specific logic to determine output admittance.
Developers should ensure that their implementation adheres to the JSON request and response structures specified in this document, enabling seamless interaction between overlay network participants.
The core DPP (Direct Payment Protocol) has been defined by BRC-27, but it is non-functional without at least one mode to facilitate payments. We extend the core DPP by introducing the Hybrid Payment Mode. This flexible and open-ended payment mode facilitates payments with BSV as well as various token types, such as loyalty points and stable coins. The Hybrid Payment Mode allows a payment host to stipulate payment from a set of funding types created with AND and OR connections, enabling a wallet to choose the most suitable option based on user resources.
The motivation behind the Hybrid Payment Mode is to provide a flexible and versatile payment mode within the Direct Payment Protocol. This mode aims to accommodate various payment scenarios and combinations by enabling payment hosts to define multiple payment options with different funding types. The Hybrid Payment Mode can address the growing demand for handling diverse payment scenarios in the evolving digital currency landscape.
HybridPaymentMode (BRFCID: ef63d9775da5) will be described together with related Payment and PaymentACK objects. It is the first defined payment mode capable of fulfilling numerous requirements due to its flexible nature.
The Hybrid Payment Mode contains a dictionary of various options (options are OR relation, so they are alternative sets of outputs, and the customer or their wallet will pick one).
Every option contains a set of transactions (AND relation, if the customer chooses the option, they must provide all the required funds to satisfy the transaction). The interface for transaction object and transaction’s internal objects are described below.
A transaction object is a set that contains 3 parts:
Outputs: A way of specifying a Bitcoin transaction output, including the value and script for various token standards.
Inputs: A way of declaring which specific inputs should be used (useful for multisig, payment_channels, etc).
Policies: A way of requesting specific TX policies like fees, SPV, nLockTime, etc.
List of outputs – payment destinations.
This is a regular native BSV output which specifies the amount and recipient in bitcoin script form (usually p2pkh).
This output is used for getting tokens using the STAS protocol. It is required to define tokenId (with symbol), amount, and recipient.
The structure for this object is specific to each particular token standard. It may be based on the BRFC reference if the token standard hasn't published a name yet, or otherwise based upon a particular token's name.
A list of input objects contains data needed to specify the required inputs which should be used.
An object containing some policy information like fees or SPV envelope.
This object defines fields required by HybridPaymentMode. In this mode, important data are the chosen payment option (paymentId) and a list of transactions which fulfill this option:
This object defines fields required by HybridPaymentMode:
An example of a Hybrid Payment Mode implementation is provided below:
The BRC-54 Hybrid Payment Mode specification provides a comprehensive structure for enabling diverse payment scenarios within the Direct Payment Protocol. By carefully following the structures, processes, and context described in this document, a developer can create a compatible implementation within the BRC-54 specification.
Ty Everett ([email protected])
This BRC proposes an extensible format for including zero-knowledge proofs (ZKPs) in specific key linkage revelations as per BRC-69 Method 2. While BRC-94 addresses limitations of BRC-69 Method 1 through a Schnorr-based ZKP, there is currently no standardized method for provable specific key linkage claims in Method 2. Given the rapid evolution of ZKP technologies, this specification introduces a proof-type enumeration scheme to accommodate future proof mechanisms. By defining a flexible proof-type field, we allow for the inclusion of various ZKP schemes as they become available, ensuring that wallets and applications can adopt and support them over time, eventually converging on standardized formats.
We aim to provide a method for proving specific key linkage revelations under Method 2. However, current ZKP techniques may not fully support this requirement, as these technologies are still maturing. This proposal allows us to proceed with standardizing wallet interactions and linkage proofs while accommodating future advancements in ZKP capabilities. By introducing an extensible proof-type field, we create a flexible mechanism for integrating new proof schemes as they emerge, fostering innovation and facilitating eventual convergence on standard proofs.
This specification focuses on proofs of a specific computation: that a prover (Alice) has computed a shared secret between herself and a counterparty (Bob), and used it as a SHA-256-HMAC key over a defined invoice number to derive a specific linkage offset value. The approach is constrained to non-interactive proof schemes, as interactive proofs are impractical for our use case.
We make the following assumptions:
Non-Interactive Proofs: Only non-interactive proof schemes are considered, as per existing standards like .
External Verification: Proof verification is expected to be performed by external systems or verifiers, not within the wallet itself.
Encrypted Proof Payloads: Proof payloads are encrypted according to to ensure confidentiality during transmission.
We build on the existing standards:
: Defines methods for revealing key linkages.
: Specifies encryption of linkage information in transit using .
: Provides a Schnorr-based ZKP for counterparty-level linkage revelation (Method 1).
This proposal extends these standards by defining an extensible proof-type format for specific key linkage claims (Method 2).
All proofs are encrypted using the mechanism defined in .
Counterparty-Level Revelations: Encrypted according to BRC-72, including both the shared secret and the Schnorr proof as per .
Specific Key Revelations: Encrypted according to BRC-72, including the linkage offset and the proof payload defined herein.
We define a binary format for the encrypted Schnorr proof payload for counterparty-level revelations:
This binary data is concatenated in the above order, then encrypted as per alongside the shared secret, and returned by the wallet to the verifier.
We define a binary format for the encrypted payload for specific key linkage proofs:
This binary data is assembled by first specifying the Proof-Type, followed by the Proof payload (if applicable), then encrypted as per alongside the specific linkage offset, and returned by the wallet to the verifier.
We introduce a proof-type numbering scheme:
Proof-Type 0: Indicates no proof is provided. Verifiers must trust the prover when the proof-type is zero. The Proof payload is empty.
Proof-Types 1-255: Reserved for future proof schemes. As new ZKP methods become available, they can be assigned unique proof-type identifiers within this range, along with their specific proof formats.
When Proof-Type is zero:
The Proof payload is empty.
The verifier receives the encrypted linkage offset but must trust the prover's claim, as there's no way to independently verify the correctness without a proof.
Wallets: Should implement the ability to generate and include the Proof-Type and Proof payload in the encrypted data when performing specific key linkage revelations.
Verifiers: Should be able to parse the Proof-Type field and handle the Proof payload accordingly, based on supported proof schemes.
As new ZKP methods are developed and standardized, new BRCs can define additional proof-types (1-255) and their corresponding proof formats.
Wallets and verifiers should be designed to be extensible, allowing for the addition of new proof-types without significant changes to underlying architectures.
Confidentiality: All proof payloads must be encrypted as per to ensure that sensitive linkage information is protected during transit.
Trust: When using Proof-Type 0 (no proof), verifiers must be aware that they are relying on the prover's honesty, as no independent verification is possible.
Future specifications may define new proof-types (1-255) along with their proof formats and verification methods. Potential avenues include:
Proof-Type 1: Could be assigned to a specific ZKP scheme (e.g., Bulletproofs, STARKs, SNARKs) that is suitable for proving specific key linkage claims.
Standardization: As the ecosystem converges on preferred proof schemes, updates to this BRC can formalize these proofs, promoting interoperability.
This BRC provides a flexible and extensible framework for including proof schemes in specific key linkage revelations, accommodating future advancements in ZKP technology. By standardizing the proof-type field, we enable wallets and applications to adopt new proof mechanisms as they emerge, facilitating independent verification of specific key linkage claims while maintaining backward compatibility and fostering innovation in the BSV blockchain ecosystem.
Ty Everett ([email protected])
We define an extension to that enables a wallet to facilitate the tracking of specific application-defined transaction outputs within baskets. A new set of messages across the abstract messaging interface facilitates applications' access to unspent outputs stored in these baskets, with a permission system similar to that described in employed to regulate access by applications. Spending an output stored in a basket removes it, while new outputs can be added by specifying their basket as part of transaction creation requests.
Ty Everett ([email protected])
We define methods for an application to request that a wallet create and prove identity certificates. We define a set of additional messages that extend the application-to-wallet messaging layer with this functionality. We specify the functionality to be performed by the wallet as part of these processes, including a standard methodology for wallets to contact identity certificate certifiers over HTTP and carry out the signing of these documents. To keep users in control over how their data is processed and used, we allow for the wallet to obtain user consent prior to carrying out these operations.
A repository for submitting, discussing, sharing, and indexing technical proposals for use across the Bitcoin ecosystem. Data models, user interfaces, script templates, encoding formats, communication protocols, and constructive critique of existing industry practice are all welcome. The goal is to provide a platform for sharing ideas without any bureaucratic overhead.
Contributions from all builders are welcome and encouraged. To propose a new BRC, fork the repo and create a new markdown file using the as the template. The common structure is outlined below, which is a guideline to aid you rather than a strict requirement. Once your proposal is ready to share, submit a pull request so that others can review and discuss it.
To participate in discussions about existing proposals, simply open an issue and link back to the BRC file in question.
Ty Everett ([email protected])
We devise a method for applications to request the encryption and decryption of data. Specifically, we define a mechanism for data encryption within the key derivation system, utilizing the protocol and key ID scheme. During encryption, the sender derives their own child private key and the child public key of the recipient using the process, then computes an ECDH shared secret between the child keys which is used in symmetric encryption with AES-256-GCM. The initialization vector, together with the ciphertext, are sent to the recipient. During decryption, the recipient computes their own private key and the public key of the sender, and uses ECDH to compute the same shared secret. The key is then used together with the provided initialization vector to decrypt the ciphertext. When no counterparty exists, we stipulate substitution for the sender's own public key in the child key derivation process.
Deggen ([email protected])
Minimalist protocol for tokenization, issuance, transfer, recovery, and redemption.
Ty Everett ([email protected])
We propose a standard format for invoice numbers that enables secure and permissioned access to a set of restricted key derivation universes within the context of derivation. The format defines a string that includes the security level, protocol ID, and key ID separated by hyphens. Security level 0 implies no permissions, level 1 grants permissions to all key IDs and all counterparties for the given protocol ID, and level 2 grants permissions only to a particular counterparty. The standard aims to enable the creation of protocols that can request permission to access data controlled by a user in a secure and standardized way.
Ty Everett ([email protected])
This document proposes a solution for querying the state of UTXO-based overlay networks by specifying a mechanism for accessing data using lookup services. By utilizing a protected server hosting an API endpoint, overlay network nodes can return relevant UTXOs based on the topics they host and the user's query. This standard defines the parameters and processing steps needed to query for and access topical UTXOs.
{
"status": "error",
"code": "ERR_PERMISSION_DENIED",
"description": "You have denied permission for signing this data."
}00000000000000000000000000000000000000000000000000000000000000010294c479f762f6baa97fbcd4393564c1d7bd8336ebd15928135bbcf575cd1a71a1[48, 68, 2, 32, 43, 34, 58, 156, 219, 32, 50, 70, 29, 240, 155, 137, 88, 60, 200, 95, 243, 198, 201, 21, 56, 82, 141, 112, 69, 196, 170, 73, 156, 6, 44, 48, 2, 32, 118, 125, 254, 201, 44, 87, 177, 170, 93, 11, 193, 134, 18, 70, 9, 31, 234, 27, 170, 177, 54, 96, 181, 140, 166, 196, 144, 14, 230, 118, 106, 105]BRC-3 Compliance Validated!window.CWI.<functionName>const identityKey = await window.CWI.getPublicKey({
identityKey: true
})const ciphertext = await window.CWI.encrypt({
plaintext: Buffer.from('Hello BRCs!'),
protocolID: [0, 'Hello World'],
keyID: '1'
})“2-3241645161d8-<derivationPrefix> <derivationSuffix>”outputs: {
3: { suffix: 'abcdefg' },
4: { suffix: 'hijklmnop' },
7: { suffix: 'qrstuvwxyz' }
}{
"protocol": "3241645161d8", // Simple Authenticated BSV P2PKH Payment Protocol
"transaction": {
"rawTx": "01000000017...",
"inputs": {
"b7d1297158f01bfc3fdace31f62a0d4634e9471d11b2a99f0784621cedb9367c": {
"proof": {...},
"rawTx": "01000000011..."
}
},
"mapiResponses": [...],
"txid": "737a1e90af9745da3f22ef74bc71a089c5e2a76e9662a0c9fa5b7cf94fe32e75"
},
"senderIdentityKey": "031d903f5b32a6121f29c59c547d2ea41ee8157ab0f0c2b5190be24a032816f827",
"note": "Payment for spelling fix pull request.",
"amount": 1033,
"derivationPrefix:": "8217bb4e7fa0541e0f5e04fea764ab91"
}{
"reference": "cd5b1e4947e304476c788cd474fb579a"
}Field
Bytes
Description
R
33
Nonce public key point R in compressed 33-byte DER format.
S'
33
Nonce shared secret point S' in compressed 33-byte DER format.
z
Variable
Response scalar z as a big-endian integer.
Field
Bytes
Description
Proof-Type
1
One-byte unsigned integer (0-255), indicating the proof scheme used.
Proof
Variable
Proof payload, format depends on Proof-Type.
derivationPrefix
(string, required)
This field denotes the payment-wide derivation prefix used by the sender when the keys for the payment UTXOs were derived
note
(string, required)
Human-readable description for the transaction.
amount
(number, optional)
Amount of satoshis associated with the transaction. If provided, it is used to verify that the amount returned from processing the transaction matches the payment amount.
window.CWI.createCertificate()
window.CWI.proveCertificate()
window.CWI.createHmac()
window.CWI.verifyHmac()
window.CWI.getPublicKey()
window.CWI.findCertificates
window.CWI.getVersion()
window.CWI.getNetwork()
window.CWI.isAuthenticated()
window.CWI.waitForAuthentication()


Options {
"ID": Transactions // required, min one key-value pair.
}Transaction {
outputs // list of output objects. required
inputs // list of input objects. optional
policies // additional properties and requirements for transaction. optional
}Outputs {
native // list of native output objects. optional.
brfcXYZ // list of brfc objects of XYZ token. optional.
tokenABC // list of ABC token objects. optional.
...
}Native output {
amount // number. required.
script // string. required. hexadecimal script.
description // string. optional. must not have JSON string length of greater than 100.
}STAS output {
tokenId // string. required.
amount // number. required.
recipient // string. bitcoin address or paymail.
}brfc1234 {
properties_1
properties_2
...
}
unicornToken {
tokenId // string. required
script // string. required. hexadecimal script.
...
}Input {
scriptSig // string. required.
txid // string. required.
vout // integer. required.
value // integer. required.
nSequence// number. optional.
}Policies {
fees // dictionary. optional. Nested dictionary which include on Fee objects on 3rd level.
SPVRequired // boolean. optional. default is false.
lockTime // number. optional.
}"ef63d9775da5" {
optionId // string. ID of chosen payment options
transactions // a list of raw transactions. required
ancestors // object. optional.
}{
transactionIds // a list of transaction ids. required
peerChannel // object. optional
}{
optionId: "choiceID1",
transactions: [
" RAW_TRANSACTION for 'choiceID1.transactions[0]' ",
" RAW_TRANSACTION for 'choiceID1.transactions[1]' ",
],
ancestors: {
" TXID of RAW_TRANSACTION for 'choiceID1.transactions[1]' ": {
/* This object has the ancestors of 'choiceID1.transactions[1]' */
}
}
}{
transactions: [
" TXID of transaction for 'choiceID1.transactions[0]' ",
" TXID of transaction for 'choiceID1.transactions[1]' ",
],
// Things like peer_channel should probably still be defined at the paymentAck root level. Different transactions should probably not have different peer_channel-s.
peerChannel: {
host: "peerchannels:25009",
token: "token",
channel_id: "channelid",
}
}Apply topic-specific logic to the transaction by iterating through all specified topics and performing the following for each topic:
Check the transaction's list of inputs for UTXOs that are part of the current topical overlay. Flag these inputs and communicate the information to the topic's management logic.
Apply the topic-specific management logic and protocol rules to determine output admittance for each transaction output.
Update the node's tracking system, associating each relevant output with the topic labels of topics that have admitted those outpoints. If implementing BRC-24, notify lookup services about the new incoming entries.
Remove any now-spent inputs that were previously associated with a topic from the tracking system, as they have now become spent by this transaction. If implementing , notify lookup services about the old outgoing entries.
Generate and return a JSON response object containing the transaction processing results, including a status key with a value of success, and a topics key with an object containing topic labels and arrays of output numbers denoting which outputs were admitted into the specific topics.
Pursuant to any peering or data synchronization arrangements or contracts that the node operator may have negotiated with other node operators with whom they would like to remain in sync, use the agreed-upon exchange mechanisms to notify the other node operators about the received transaction, potentially in conjunction with an incoming or outgoing BRC-41 payment.
Transaction outputs in Bitcoin take many forms, and serve many purposes. While BRC-1 defines a way for applications to request the creation of transaction outputs by wallets, there is no way for applications to request that a wallet tracks these outputs. Enabling applications to request that wallets track outputs provides a number of advantages: First, applications may no longer need to rely on external data storage and retrieval systems, simplifying their architecture. Second, there is the potential to represent different types of Bitcoin-native tokens within specific baskets, and define protocols for manipulating specific types of tokens based on which baskets they are stored in. Finally, when permission to access a basket is decided by the user on a per-application basis, it facilitates a greater degree of control for users over their tokens, enabling multiple applications to access and use the same tokens.
We extend the BRC-1 Transaction Creation Request message so that, in addition to the normal script and satoshis fields defined in each element of the outputs array, there is another basket field. The new basket field is a string comprising the name of the basket into which this output is to be inserted and tracked by the wallet. We specify that, when a wallet creates a transaction responsive to the Transaction Creation Request, it utilizes some internal storage and retrieval system (beyond the scope of this specification) to associate the specified output with the specified basket.
To allow applications to specify information that would later be needed to unlock and use outputs that are stored in baskets, an optional customInstructions field can also be added when basket is given. This field is retained by the wallet and included as part of the Transaction Outputs Response (defined below).
To facilitate permissioned access to baskets by applications, we stipulate that the wallet, upon receiving a transaction creation request, may prompt the user to grant permission for the application to use the basket. The method by which the wallet seeks the consent of the user is out-of-scope for this standard, but the process, if it occurs, must be facilitated by the wallet asynchronously between the receipt of the Transaction Creation Request and the furnishment of any Transaction Creation Response or Transaction Creation Error messages.
To facilitate access to the tokens stored within baskets by applications, we define a new set of messages across the abstract communications channel between the wallet and the application, as follows:
This message constitutes a request by the application for a list of Bitcoin UTXOs that are responsive to the request. The message contains the following information:
basket
yes
The basket from which outputs should be returned
includeEnvelope
no
Whether transaction envelopes are requested for the outputs
limit
no
The maximum number of outputs to return
This provides the wallet with enough information to furnish the outputs requested by the application. Here is an example of a simple Transaction Outputs Request in JSON:
In this example, the application is stipulating that a maximum of 25 outputs from the todo tokens basket are to be returned, including their BRC-8 transaction envelopes.
The response comprises a list of transaction outputs currently in the specified basket. This is an array of objects where each object has the following fields:
amount
The number of satoshis in the transaction output
txid
The TXID of the transaction that created the output, given as a hex string
vout
The output index number of this output within the transaction that created it
outputScript
The hex-encoded Bitcoin script program that locks the output
customInstructions
The spending instructions that were stored when the output was created
Here is an example of a simple Transaction Outputs Response:
If the Bitcoin wallet is unable to fulfill the Transaction Outputs Request for any reason, we specify that it should respond with a JSON-formatted Transaction Outputs Error. The fields for the object are specified as follows:
status
This should always be a string comprising "error".
code
A machine-readable error code. Extensions to this standard can define specific error codes and standardize additional fields. Codes are strings, for example "ERR_PERMISSION_DENIED".
description
All errors must have a human-readable description field that describes the error. This allows the application to represent the error for the user.
One example of a Transaction Outputs Error is given below:
This functionality is implemented as part of the Babbage SDK, via the getTransactionOutputs function.
The BRC-52 identity certificate standard provides a decentralized, privacy-centric solution to digital identity, allowing users to selectively reveal their identity data. However, without a standard method for applications to create and prove these certificates, wallet support will be limited. BRC-53 provides a set of standardized methods for applications to request and interact with BRC-52 certificates, enabling wider adoption and integration of BRC-52 certificates within the ecosystem.
We define two new sets of messages to be sent over the abstract BRC-1 messaging layer:
An application may request a wallet to create a BRC-52 certificate by providing the following parameters:
certificateType
The type of certificate to create.
fieldObject
An object containing the fields to be added to the certificate.
certifierUrl
The URL of the certifier responsible for signing the certificate.
certifierPublicKey
The public identity key of the certifier responsible for signing the certificate.
The wallet will then carry out the following steps:
Initialize a BRC-53 client with the primary identity key of the wallet.
Generate a client nonce.
Request the validationKey and serialNumber from the certifier by making a BRC-53-enabled HTTPS POST request to the certifierUrl's /initialRequest endpoint with the client nonce.
Validate the received serialNumber and validationKey using the client and server nonces.
Encrypt the fields of the fieldObject using encryption and store the concealed fields and encrypted field revelation keys in the fields and keyring objects, respectively.
Create a Certificate Signing Request (CSR) containing the certificate type, nonces, validation key, serial number, fields, and keyring.
Send the CSR to the certifier's /signCertificate endpoint via an HTTP POST request.
Receive and verify the signed certificate's authenticity.
Store the signed certificate in the wallet's data store (how the wallet stores its data is beyond the scope of this specification).
The response sent back over the abstract messaging layer from the wallet to the application will constitute a valid, fully-signed BRC-52 identity certificate with no keyring.
If the Bitcoin wallet is unable to fulfill the Certificate Creation Request for any reason, we specify that it should respond with a JSON-formatted Certificate Creation Error. The fields for the object are specified as follows:
status
This should always be a string comprising "error".
code
A machine-readable error code. Extensions to this standard can define specific error codes and standardize additional fields. Codes are strings, for example "ERR_CERTIFIER_REJECTED_CSR".
description
All errors must have a human-readable description field that describes the error. This allows the application to represent the error for the user.
One example of a Certificate Creation Error is given below:
An application may request a wallet to prove a BRC-52 certificate by providing the following parameters:
certificate
The certificate to be proven.
fieldsToReveal
An array containing the names of the fields to be revealed to the verifier.
verifierPublicIdentityKey
The public identity key of the verifier.
The wallet will then carry out the following steps:
Verify the authenticity of the provided certificate.
Ensure that the application and the verifier have been granted access to the requested certificate fields (optional permissions checks).
Decrypt the encrypted field revelation keys using the wallet's primary identity key.
Encrypt the decrypted field revelation keys for the verifier using the verifierPublicIdentityKey.
Add the encrypted field revelation keys to the certificate field revelation keyring (as defined in ) object.
Attach the field revelation keyring to the certificate.
Return the certificate with the attached field revelation keyring for presentation to the verifier for field examination.
The response sent back over the abstract messaging layer from the wallet to the application will constitute a valid, fully-signed BRC-52 identity certificate with the attached field revelation keyring for the verifier.
If the Bitcoin wallet is unable to fulfill the Certificate Proof Request for any reason, we specify that it should respond with a JSON-formatted Certificate Proof Error. The fields for the object are specified as follows:
status
This should always be a string comprising "error".
code
A machine-readable error code. Extensions to this standard can define specific error codes and standardize additional fields. Codes are strings, for example "ERR_PERMISSION_DENIED".
description
All errors must have a human-readable description field that describes the error. This allows the application to represent the error for the user.
One example of a Certificate Proof Error is given below:
The wallet and certifier communicate in a standard way over a BRC-31 protected HTTPS interface to facilitate the requesting and signing of a certificate. Here, we specify the requirements and fields for these communications.
We require that the wallet engages in the BRC-31 authentication process with the certifier, and we further require that the wallet authenticate using the same key which is the subject of the certificate issuance process. This provides a way for the certifier to know that the person making the request is the person who will be receiving the identity certificate.
The wallet initiates the communication with the certifier by sending an initial request. The initial request includes a client nonce, generated randomly by the wallet. The request is sent to the /initialRequest endpoint using the HTTP POST method.
Request fields
clientNonce: A randomly generated 32-byte string in base64 format.
The certifier processes the initial request and generates two nonces: serialNonce and validationNonce. The certifier calculates the serialNumber and validationKey by hashing the concatenation of clientNonce with the respective nonces. The certifier then returns these values in the response.
Response fields
validationKey: A base64 encoded string calculated by hashing the concatenation of clientNonce and validationNonce using SHA256.
serialNumber: A base64 encoded string calculated by hashing the concatenation of clientNonce and serialNonce using SHA256.
validationNonce: A base64 encoded nonce generated by the certifier.
serialNonce: A base64 encoded nonce generated by the certifier.
After receiving and validating the values from the initial request, the wallet creates a Certificate Signing Request (CSR) and sends it to the /signCertificate endpoint using the HTTP POST method.
Request fields
messageType: The string "certificateSigningRequest".
type: The type of certificate to create.
clientNonce: The client nonce generated by the wallet during the initial request.
serverSerialNonce: The serial nonce received from the certifier in the initial request.
serverValidationNonce: The validation nonce received from the certifier in the initial request.
validationKey: The validation key received from the certifier in the initial request.
serialNumber: The serial number received from the certifier in the initial request.
fields: An object containing encrypted fields of the certificate.
keyring: An object containing encrypted field revelation keys, revealed from the subject to the certifier.
The certifier processes the CSR and signs the certificate using its private signing key. The signed certificate is then returned to the wallet in the response.
Response fields
status: The status of the signing process, either "success" or "error".
description: A description of the error, if applicable.
code: An error code, if applicable.
certificate: The signed certificate, if the signing process is successful.
These processes have been implemented into the createCertificate and proveCertificate functions of the Babbage SDK. The Computing with Integrity kernel (currently proprietary) implements the wallet-to-certifier communications protocol, and CoolCert implements the certifier side of the protocol.
We believe in encouraging discussion and iterative improvement of proposals, resulting in incremental improvement within the bounds of the Bitcoin protocol. We welcome suggestions for improvement and are committed to working with contributors to improve proposals and ensure that they align with our guidelines.
Note that substantial revisions to standards (beyond fixing typos, adding context or wording) should go into a new standard that extends or revises the old one, so as not to disrupt existing implementations.
We look forward to your contributions and helping to create a world where transactions are seamlessly formed, and applications interact with each other with ease.
Read more about areas of interest on OpenStandards.cash
The BRCs repository is organized into directories, each representing a different category of proposal. Categories may include, but are not limited to:
Transaction Templates
Bitcoin Script Templates
Communication Protocols
Each proposal should be written as a markdown file and should loosely adhere to the following:
Title: A descriptive title for the standard being defined.
Author(s): Who wrote the standard and where did it come from? How can they be reached?
Abstract: A brief description of the proposed standard or template.
Motivation: The reasoning behind the proposal and why it is needed.
Specification: A detailed technical specification of the proposal.
Implementations: Information on how the proposal has been or can be implemented.
References: Any relevant literature or external resources related to the proposal.
Note that additional relevant content, identifiers or other information may be added to the document. Documents that already existed before the repository may not follow these requirements.
Things that help depict and understand the document, such as media, may also be added in a media subdirectory where appropriate.
Refer to the Banana-Powered Bitcoin Wallet Control Protocol for a fun example template you can copy when proposing your own standards.
0
1
2
3
4
5
Everything in this repository is subject to the Open BSV License.
The Bitcoin ecosystem has demonstrated a clear desire for wallets to support data encryption between parties1. This capability facilitates secure exchange of information, improves user privacy, and enables novel applications that require direct and secure communication between users. The unique economic incentives of micropayment-based applications, particularly those facilitated by Bitcoin wallets, make user privacy paramount, and have led to the adoption of end-to-end encryption by many applications2. While several encryption systems have been developed and integrated into various platforms, there is currently no standard methodology that supports both single-party and multi-party encryption, protocol-level permissions management, and leverages BRC-42 key derivation. The BRC-2 standard aims to fill this gap and provide a secure and standardized framework for encryption and decryption within the Bitcoin ecosystem.
We start with the same constructs defined in BRC-43: users with clients, protocols and applications. We stipulate the use of BRC-43 invoice numbers in the context of BRC-42 key derivation, and we build on top of the permissions architecture defined by BRC-43.
When an application sends a message to a client requesting that data be encrypted, the message comprises:
The data to encrypt
We stipulate the following process for encryption:
The message sender begins by computing the BRC-43 invoice number based on the security level, protocol ID, and key ID
The message sender uses BRC-42 key derivation with the computed invoice number to derive a child public key for the recipient
The message sender uses BRC-42 key derivation with the computed invoice number to derive their own child private key
The message sender computes the ECDH shared secret between the two derived child keys
The resulting elliptic curve point's X and Y values are hashed with SHA256 to create an AES-256-GCM symmetric encryption key
The resulting 256-bit value is used in conjunction with a randomly-generated 256-bit initialization vector to encrypt the message with AES-256-GCM
The initialization vector is prepended to the ciphertext, and the combined value is returned by the client to the application over the abstract communications substrate.
We stipulate the following process for message decryption:
The recipient somehow comes to know the ciphertext (prepended with the initialization vector), the counterparty, the security level, protocol ID, and key ID. The mechanism for conveying this information to the recipient is beyond the scope of this specification.
The recipient begins by computing the BRC-43 invoice number based on the security level, protocol ID, and key ID
The recipient uses BRC-42 key derivation with the computed invoice number, their own private key and the public key of the sender, to compute the sender's child public key
The recipient uses the same process to compute his own child private key
The recipient computes a shared secret between the two child keys, using the hash of the X and Y values as an AES-256-GCM symmetric key
The recipient then uses the symmetric key to decrypt the ciphertext with the provided initialization vector
We build upon the abstract messaging layer first described in BRC-1. Specifically, we define five new BRC-1 messages to facilitate requests and responses for encryption and decryption, and error handling. For each of the messages, we stipulate that there exists some out-of-band mechanism for the parties to communicate which of the messages are being exchanged, removing the need for a message type field. Finally, specifically for the request messages, we stipulate that the message comprises a header and a payload, with the payload containing the data (ciphertext or plaintext), and the header containing the other information. For the response messages, no message header is defined and the payload simply contains the specified data.
The encryption request is a message sent by the BRC-43 application to the client. It contains a header with the following information:
protocolID
The security level and protocol ID represented as an array. For example, [0, "hello world"] represents a level-0 protocol with open permissions, while [2, "document signing"] represents a level 2 protocol.
keyID
The key ID
counterparty
The counterparty, or self
The message payload comprises the data to encrypt.
The response message comprises a payload containing the encrypted ciphertext, prepended with the 32-byte initialization vector.
The decryption request is a message sent by the application to the client. It contains a header with the following information:
protocolID
The security level and protocol ID represented as an array. For example, [0, "hello world"] represents a level-0 protocol with open permissions, while [2, "document signing"] represents a level 2 protocol.
keyID
The key ID
counterparty
The counterparty, or self
The message payload comprises the data to decrypt, prepended with the 32-byte initialization vector.
The response message comprises a payload containing the decrypted plaintext.
If the client is unable to fulfill the encryption or decryption requests for any reason, we specify that it should respond with a JSON-formatted Cryptography Error. The fields for the object are specified as follows:
status
This should always be a string comprising "error".
code
A machine-readable error code. Extensions to this standard can define specific error codes and standardize additional fields. Codes are strings, for example "ERR_DECRYPTION_FAILED".
description
All errors must have a human-readable description field that describes the error. This allows the application to represent the error for the user.
One example of a Cryptography Error is given below:
For compatibility with this encryption scheme, we stipulate the following:
A user who has the following identity private key...
...which implies the followign identity public key for that user...
...should be able to use security level 2, the BRC2 Test protocolID with keyID 42 and the following counterparty...
...to decrypt the message with the following ciphertext (with prepended initialization vector)...
... and receive the following plaintext:
...and to validate the message with the following HMAC...
... the message whose HMAC is above as being:
This encryption capability is incorporated into the Babbage SDK
There is a lack of clarity with respect to how tokens can be defined and managed within the context of Overlays. This proposal aims to demonstrate the minimim viable solution for tokens, having considered all available options, picked the most viable, and made small improvements to allow for simple extension of functionality.
Use a genesis transaction output as an assetId concatenating the txid and vout.
Push that assetId to the stack in any output script you want to send that token to so that overlays, wallets, and smart contracts can evaluate it.
Push the amount of tokens the output represents if it's a fungible token.
Drop the data so that you can use whatever functional logic you like thereafter ...
1 satoshi assigned to each output.
Prefix everything with a UTF8 exclamation point ! (0x21 in hex) for sake of measuring adoption.
The sum of input token amounts must equal the sum of output token amounts of the same assetId.
output holding 9 tokens
21 assetId 04 OP_DROP OP_2DROP ...
21 assetId 05 OP_DROP OP_2DROP ...
The order of inputs and outputs is disregarded.
Token outputs each refer to the genesis output as a way to avoid collisions when identifying the asset they represent. This acts as a universal asset identifier for enabling swap contracts and token based payment protocols. The randomness helps us avoid things like people competeing to claim the "USD" assetId or other potentially popular labels.
They also include a prefix of ! in utf8 as a way to tag outputs for tracking global adoption of the protocol; to reduce the cost of recovery from archival services; and for use within the context of IPv6 multicast group address routing.
The only reason to include the data in the outputs at all is for access to the data in smart contracts, any metadata ought to be kept in the application layer if needed.
Bitcoin Number format is used for the token amount.
Transaction outpoint format is used for the genesis outpoint information.
This is to ensure smart contracts can more easily parse the data within a transaction to enforce conditional logic based on token values, and enforce token type in a format which is already incorporated into the transaction format itself.
All outputs have 1 satoshi assigned to avoid AML problems like sending “a worthless bean token” to someone which actually has 100 BSV under it.
Non-Fungible tokenization is the process of associating something with a particular transaction output which will thereafter represent a claim to that something. We assume that the issuer has already registered a public key as associated with them: identityPublicKey.
We propose this key be referred to as a BoundKey.
The output looks like a regular P2PK but if you know the owner’s identity public key and the token details you are able to verify the association. The txid and vout of this transaction form the assetId for this NFT.
Fungible tokens sometimes require administrative management: multiple issuances to increase available supply, redemptions to reduce supply, recovery from loss in case of errors or theft. These things require an known issuer to steward the token system as a whole while keeping individual transactions private.
Registration
There is an issuer who is responsible for maintaining the relationship between tokens and the real world assets they represent. Before we issue any tokens, we register a public key by creating an authorized outpoint with a key derived from the issuer’s identity key. This transaction is used as the basis for a particular fungible token.
any
21 OP_DROP boundKey OP_CHECKSIG (the genesis outpoint, assetId, and authorized outpoint 0)
Issue
Once registered, tokens can be issued by creating a single transaction which:
spends the genesis outpoint
creates token outputs
creates the next authorized outpoint
authorized outpoint n
21 OP_DROP boundKey OP_CHECKSIG (authorized outpoint n+1)
21 assetId 09 OP_2DROP P2PKH (creates 9 tokens)
Redeem
Redemption transactions spend token outputs without creating new ones, taking them out of circulation. The boundKey in this case could incorporate data associated with a withdrawal of funds from an associated bank account for example.
authorized outpoint n
21 OP_DROP boundKey OP_CHECKSIG (authorized outpoint n+1)
Any token outpoint with our genesis_outpoint
no token outputs
A chain of authorization outpoints is created such that the transaction DAG works as an immutable linked hash chain of all administrative actions taken since genesis. Public audit-ability. No hidden issuances or redemptions. The token supply is known and provable.
When a token owner creates a valid BSV transaction which does not conform to the token transfer rules, the tokens are burned, and the transaction will not be accepted by the token overlay, despite perhaps being broadcast on the blockchain itself.
Valid tokens
no token outputs, or too few
In this case, tokens in does not equal tokens out.
Post burning, tokens which were lost can be recovered by the issuer by spending and creating an authorized output while including metadata into the derivation of a pubkey to indicate which txids are to be processed as having been spent invalidly.
authorized outpoint n
21 OP_DROP boundKey OP_CHECKSIG (authorized outpoint n+1)
21 assetId 05 OP_2DROP P2PKH
Not yet available. Proposal stage.
The BRC-42 key derivation architecture allows parties to derive child keys for each other based on flexible invoice numbering schemes. However, the lack of a standard format for invoice numbers creates challenges for using different sets of keys for different purposes. This standard proposes a solution to address these challenges by defining a common standard for formatting invoice numbers.
By enabling the development of various classes of protocols, each with different security models, this standard allows for the sharing of a common set of root keys while keeping key universes separate. This approach provides an easy-to-use solution for various applications, including Bitcoin and MetaNet client software, where different applications require access to various classes of a user's data.
The proposed scheme for key IDs is open-ended and can be defined by the rules for each protocol, making it easy to implement and apply to a wide range of use cases. This standard makes it easier for software using BRC-42 key derivation for cryptographic operations to protect user data by granting permission for particular parties to access and use keys based on the security level for the protocol. Overall, the adoption of this standard will simplify and enhance the use of BRC-42 key derivation in a variety of contexts, improving security and facilitating innovation.
We specify that an invoice number has the following three components:
Security Level
Denotes the level of permissioning security applied to protocols using the scheme.
Protocol ID
The identifier for the protocol that is using the scheme.
Key ID
Protocol-specific information used to arrive at a particular key under a particular protocol.
We specify the following format for invoice numbers:
We specify that there are users who have clients, there are applications and there are protocols. Protocols define a key derivation scheme that facilitates access by applications to user-held keys via their client. For example, an application could use a particular protocol to encrypt some data with the user's client. The keys used for encryption under one protocol are different than the keys used for other protocols, because the invoice number used for key derivation contains the specific protocol ID being used.
We specify the counterparty as the other party whose key is being used for derivation. When there is only one party, we specify that their single key be used both as the sender and the recipient. This is known as self-derivation. When the sender wishes to create a scheme where anyone can derive the corresponding key, we specify the use of the number 1 as the private key. This is known as anyone-derivation.
We specify a permission system in which the security level defined as part of the protocol is employed to determine whether the user is prompted to allow a key derivation operation to succeed. We specify that there exists some mechanism for the client to prompt the user, transparently to the application, about these permission requests. We specify that all permission requests are granted by the client on a per-application basis, and that the client has some reliable manner of identifying applications.
When the protocol specifies a security level of 0, no permissions are required and the key derivation operation is always allowed to succeed without user input. When the security level is 1, the user must grant the application permission to use the protocol, and the application can use the protocol for any counterparty without additional grants of permission. When the security level is 2, the client will require a new permission grant for every new counterparty, and the grants of permission made under level-2 protocols are counterparty-specific.
We specify that the client may provide a mechanism for the expiration of permission grants by the user, at which time the user would need to re-authorize an application's continued use of their keys. Since the permissions process is handled transparently by the client, it is beyond the scope of this standard.
Protocol IDs are normalized by the following rules:
only letters, numbers and spaces
no multiple space " "
all lower case when used
maximum 280 characters
must be at least 5 characters
must not end with " protocol"
leading and trailing spaces are removed
All strings that normalize to the same value identify the same protocol.
Key IDs must be a string of at least one byte and no more than 1033 bytes.
Some protocol IDs are used internally by various clients, and thus are never allowed within applications. These are specified by other standards, such as BRC-44.
To illustrate how the system is intended to function, we provide several examples.
An application, example.com, sends a request to the user's client for encrypting some data (as per BRC-2). The application is requesting to use security level 0, a protocol ID of Hello World, a key ID of 1, and counterparty of self.
The client makes no permission requests because the security level is 0.
The client computes 0-hello world-1 as the invoice number.
The client uses its own private key and the corresponding public key for derivation, because counterparty is self.
The client derives a child public key as the sender and a child private key as the recipient, using the same invoice number for both operations.
The client computes a shared secret between the two child keys.
The client uses the shared secret key as a symmetric key for the encryption process.
An application, example.com, sends a request to the user's client for creating a digital signature over some data (as per ). The application is requesting to use security level 1, a protocol ID of Document Signing, a key ID of 1, and counterparty of anyone.
The client checks if example.com has already been granted permission to use this protocol. Because the security level is
An application, example.com, sends a request to Alice's client for creating a digital signature over some data (as per ). The application is requesting to use security level 2, a protocol ID of Private Document Signing, a key ID of 1337, and counterparty of Bob's public key.
Alice's client checks if example.com has already been granted permission to use this protocol specifically for interacting with Bob. Because the security level is 2
The system is implemented into the Babbage SDK, which employs protocol IDs, key IDs and security levels when facilitating the functionality of the encryption and digital signature creation components.
As introduced in BRC-22, UTXO-based overlay networks provide a secure and scalable solution to managing states derived from the Bitcoin network. In order to enable users to query the state of these overlay networks and retrieve relevant UTXOs, a standardized method for lookup services is required. This document aims to address this need by outlining a clear and well-defined process for submitting queries to overlay network nodes and retrieving the resultant UTXOs.
We build on the node first described in BRC-22 and hook into the output admittance and spend events that naturally occur as transactions are submitted. When transactions are received, topical logic determines which outputs will be part of which overlay networks, and we specify that the lookup services respond to these events.
The BRC-31 protected server will host a POST /lookup API endpoint, which accepts JSON request payloads. The JSON request body will include a provider, which is a string denoting the chosen provider, and a query field, whose meaning is determined by the specific provider being chosen.
Upon receiving a query, the overlay network node will perform the following steps:
Engage in BRC-31 authentication and learn the identity of the person making the request, to the extent required in order to fulfill the request.
If consistent with the policy set by the node operator, charge a BRC-41 payment based on the query being performed, providing a mechanism by which the node can monetize its operations.
Check that the provider stipulated by the request is supported on this node. If not, return a JSON error response with a status key of error, a code of ERR_LOOKUP_SERVICE_NOT_SUPPORTED, and a description comprising a human-readable error message.
Send the query to the provider for processing. The provider will use the current known state of the overlay in conjunction with its data storage and retrieval system to fulfill the query.
The provider returns a responsive list of current topical UTXO identifiers (topic labels + TXIDs + output numbers) to the overlay network node, where each identifier is for a UTXO that is currently admitted into a topical overlay.
After retrieving the list of UTXOs responsive to the query from the lookup service provider, the overlay network node will hydrate each UTXO with its output script, amount, encompassing transaction, and other information. The format for the UTXOs constructed by this process is defined by .
The node will then return a JSON response to the consumer, comprising an array of -style UTXOs.
Each lookup service is assigned a provider identifier by the overlay network node so that multiple can be installed simultaneously. For each provider, the overlay network node will send events when new UTXOs are added and when they later become spent. The events will contain the output script, the number of satoshis, the TXID, the output number from the transaction, and the topic identifier for the topic where the output was added or removed.
Each lookup service maintains its own data storage and retrieval mechanism independently. This allows each service to use the most appropriate solution for the specific data being managed. Each service chooses how it will respond to these events, which could involve adding, removing, incrementing, decrementing, replacing, updating, or otherwise mutating data storage and retrieval systems applicable to its specific use-case or indexing methodology. Providers are under no obligation to process events for all topics and can freely drop events they deem irrelevant to their operations. Each service will do this in a way so that it can later provide effective responses to queries.
The /lookup route may be behind a BRC-41 paywall to provide a monetization mechanism for overlay network server operators. The rules for this paywall are determined by the specific server being queried and can be based on any aspect of the query being made. All state consumers must be prepared to furnish a BRC-41 payment for lookup requests if enabled by the server, unless the client knows for certain that a payment will not be charged.
This means that consumers should be prepared to handle potential payment requirements when making lookup requests, as the server operator may have implemented a BRC-41 paywall to monetize access to the overlay network's state.
In either case, all lookup provider queries are always protected by BRC-31 authentication, as with BRC-22's /submit route, ensuring there are digitally signed records of all requests and responses between the parties.
Below are examples of an HTTP request and response for the /lookup route.
In this example, a client submits a query to the /lookup API endpoint. The JSON payload contains a provider field specifying the desired lookup service provider and a query field with the specific query parameters.
The overlay network node processes the query and returns an HTTP response containing an array of BRC-36-style UTXOs that match the query's criteria. The response includes the topic, TXID, output index, output script, number of satoshis, and other BRC-8 fields for each UTXO.
Developers should extend their BRC-31 protected server to host the POST /lookup API endpoint. For monetization, a BRC-41 paywall may be configured in front of the /lookup endpoint, and may charge for lookups based on the queries being performed or the volume of information requested. Developers should ensure that their implementation adheres to the JSON request and response structures specified in this document.
Ty Everett ([email protected])
We outline the Confederacy Host Interconnect Protocol (CHIP), a peer discovery mechanism for UTXO-based overlay networks. CHIP is an overlay network which tracks active network operators hosting specific topics, facilitating UTXO discovery, providing robust availability guarantees, thus enabling fault tolerance. This document specifies the format for CHIP tokens, their propagation across the network, and the process of using them to find and connect with hosts.
The standard defines a method for running a server that accepts transactions, admits their outputs into topics, and tracks topical UTXO sets. However, there is a need for a connectivity and peer discovery mechanism for users who want to stay loosely in sync with one another. CHIP addresses this need, ensuring efficient synchronization and data propagation by facilitating the discovery of network operators actively hosting specific topics.
We define the various components of the Confederacy Host Interconnect architecture.
CHIP tokens are registered on the Bitcoin SV blockchain as single-satoshi outputs representing hosting advertisements by Confederacy network operators. The token fields are ordered as follows:
The advertiser creates a transaction output containing the token and submits it to known nodes, including their own. The locking key must be linked to the advertiser's identity key using the and methodologies.
The advertiser follows these steps to derive the locking key, compute the signature, and advertise the transaction to all known nodes:
Deriving the Locking Key:
Use the advertiser's identity key as the sender private key in the key derivation, with the "anyone" public key (1 by G) as the counterparty.
Compute the invoice number using the
There is no need to advertise that nodes host CHIP itself. It is assumed that all nodes which understand and receive CHIP advertisements intrinsically host CHIP, as they are already participating in the CHIP network and processing the advertisements.
Nodes accepting CHIP tokens must validate the identity key's link to the signature-producing key before admitting the output. Invalid tokens must be rejected. The following steps detail the token validation process:
Step 1: Extract token fields. Upon receiving a CHIP token, the node should first extract the four fields as defined in the token format. These fields include the CHIP identifier, the advertiser's identity key, the domain name of the HTTPS server hosting the network node, and the topic name hosted by the advertiser.
Step 2: Verify the CHIP identifier. Check that the first field of the token is the string CHIP. If this condition is not met, reject the token as invalid.
Step 3: Verify the locking key.
The CHIP lookup service enables network users to access and query for CHIP advertisement UTXOs in a standardized way. By facilitating access to these UTXOs, users can discover other nodes that may have the data they need, enhancing connectivity across the UTXO-based overlay network.
The specified provider name for the lookup service is CHIP. This standardized name allows implementations to easily recognize and interact with the service.
The lookup service processes queries as JSON objects containing "topic" and "advertiser" keys. These keys are used in an AND stipulation to filter the results based on the provided values. The service searches the UTXOs for matches that satisfy both conditions (if present) and returns the corresponding results.
The query fields are as follows:
The AND stipulation operates by requiring both topic and advertiser conditions to be satisfied for a UTXO to be included in the result set. If only one key is provided, the lookup service will return UTXOs matching that single condition. If both keys are present, the service will only return UTXOs that match both the specified topic and advertiser.
Query Examples
Example 1: Query with only "topic"
In this example, the lookup service will return all UTXOs with the specified "example_topic", regardless of the advertiser.
Example 2: Query with only "advertiser"
Here, the service will return all UTXOs from the specified advertiser, regardless of the topic.
Example 3: Query with both "topic" and "advertiser"
In this case, the lookup service will return UTXOs that match both the specified topic and advertiser, providing more precise results.
This section details the process for an overlay network user to discover nodes hosting a particular topic they are interested in. The user will follow these steps to locate relevant nodes and notify them about a new transaction in the topic:
Query the CHIP Lookup Service. The user initiates the process by querying the CHIP lookup service through a known node. The query contains the desired "topic" key, and optionally the "advertiser" key if the user wants to find a specific advertiser hosting the topic. The lookup service returns a list of UTXOs with corresponding CHIP tokens containing relevant domain names and identity keys of advertisers.
Contacting Servers and Notifying about the New Transaction. Upon receiving the list of UTXOs, the user extracts the domain names and identity keys of the relevant nodes. The user then contacts each server at their respective domain via the protocol, notifying them about a new transaction in the topic.
Verifying Identity Key during Transaction Submission.
By following these steps, an overlay network user can discover nodes hosting a specific topic of interest and securely engage with them to submit transactions to the relevant topic. This process enhances the connectivity and efficiency of UTXO-based overlay networks and ensures the security of transactions between nodes.
There are no known implementations of this specification at this time. The specification will be updated when an implementation is published.
We specify the widely-used protocols for exchanging payments between users of the Paymail system within the Bitcoin SV network. Paymail is a user-friendly alternative to traditional Bitcoin addresses, leveraging human-readable names at a domain, such as [email protected]. This standard aims to provide a clear and concise specification for wallet implementers to facilitate incoming and outgoing payments using Paymail addresses. BRC-28 encompasses the pre-existing payment destination and payment exchange protocols, ensuring compatibility and interoperability across Bitcoin SV wallet applications.
The primary motivation behind BRC-28 is to address the usability and complexity issues associated with traditional Bitcoin addresses. These addresses, comprised of long strings of alphanumeric characters, can be difficult to remember and prone to user errors when transcribing or sharing. By introducing the Paymail protocol, which replaces complex addresses with human-readable names, the overall user experience of Bitcoin SV transactions is significantly improved.
However, for Paymail to be widely adopted and to ensure seamless transactions across wallet applications, a standardized approach is necessary. BRC-28 aims to provide this standardization by specifying the payment destination and payment exchange protocols, allowing wallet implementers to incorporate Paymail support in a consistent and interoperable manner.
Furthermore, Paymail provides a scalable solution to the problem of blockchain scanning. With traditional addresses, the recipient must scan the blockchain to be notified about their incoming payments. With the peer-to-peer Paymail approach, transactions are handed directly from the sender to the recipient and subsequently broadcasted upon acceptance, removing the need for blockchain scanning services.
The BRC-28 specification comprises two main components: P2P Transactions and P2P Payment Destination. These components work in tandem to facilitate the exchange of Paymail transactions in a peer-to-peer manner, eliminating the need for blockchain scanning services and enabling scalable Bitcoin SV wallet applications.
The Paymail provider's .well-known/bsvalias document must be updated to include a declaration of the endpoint for receiving transactions:
The capabilities.5f1323cddf31 field contains a URL where the sender must POST the transaction data.
The sender must replace the {alias} and {domain.tld} placeholders in the URI template provided by capabilities.5f1323cddf31 with a valid Paymail handle. The client must then perform a POST HTTP request with the following body:
The server must validate the transaction and respond with the accepted transaction ID and an optional human-readable note.
The Paymail provider's .well-known/bsvalias document must be updated to include a declaration of the endpoint for generating P2P payment destinations:
The capabilities.2a40af698840 field contains a URL where the sender must POST the required satoshis for the transaction.
The sender must replace the {alias} and {domain.tld} placeholders in the URI template provided by capabilities.2a40af698840 with a valid Paymail handle. The client must then perform a POST HTTP request with the following body:
The server must generate a list of outputs and a reference number for the payment and respond with the following structure:
The BRC-28 specification, encompassing P2P Transactions and P2P Payment Destination, provides a clear and concise protocol for wallet implementers to support Paymail-based transactions in a consistent and interoperable manner. By adhering to this specification, developers can create compatible implementations that facilitate seamless Paymail transactions, benefiting end-users.
Various implementations have been created which facilitate Paymail payment destination discovery, and the hosting of Paymail servers:
is a server-side package that hosts a set of compatible endpoints.
is a JavaScript client for interacting with the Paymail protocol.
is the Paymail client created by Project Babbage [deprecated (no longer in active use)].
In the interest of completeness, we provide some auxiliary information that may be useful for implementers wishing to run Paymail servers. While the main objective of BRC-28 is to define the specifications for peer-to-peer payment destinations and transaction submission, we provide this additional information about service and capability discovery so that future implementers can interoperate.
Paymail addresses follow the format <alias>@<domain.tld>, similar to email addresses. The <alias> represents a user or an account within a domain, while <domain.tld> represents the domain owner's unique domain name. Paymail addresses must only contain alphanumeric characters, periods, and hyphens, with the @ symbol separating the alias from the domain.
Paymail Host Discovery is the process of determining the appropriate host to query for Capability Discovery. Host Discovery relies on SRV DNS records to indicate the specific web host to interrogate. Domain owners can create an SRV record with specified parameters to delegate authority to a third-party Paymail service provider or to configure their own Paymail service:
Paymail Capability Discovery is the process by which a Paymail client learns the supported features of a Paymail service, as well as their respective endpoints and configurations. Capability Discovery uses a well-known file, a machine-readable JSON-formatted document placed in a predictable location on a web server.
The well-known file's location should be:
This file contains the supported capabilities and their associated endpoints, with template strings {alias} and {domain.tld} used to represent the components of a Paymail address. Clients should replace these template strings with the actual values from the Paymail address.
To perform Capability Discovery, a Paymail client constructs an HTTP GET request to https://<target>:<port>/.well-known/bsvalias after retrieving a Target:Port pair from Host Discovery. A successful request provides the client with the necessary configuration information to interact with the Paymail service and access its supported extension protocols.
Ty Everett ([email protected])
We specify the Confederacy Lookup Availability Protocol (CLAP), a protocol for advertising the availability of BRC-24 lookup services on overlay network nodes. CLAP is an extension of BRC-23 that focuses on advertising the availability of lookup services instead of topics. By enabling the discovery of lookup services provided by various network operators, CLAP facilitates efficient and decentralized access to relevant data. This document details the format for CLAP tokens, their propagation across the network, and the process of using them to find and connect with hosts offering lookup services.
While provides a mechanism for resolving which topics are hosted on which nodes, there is a need for a similar protocol that advertises the availability of lookup services. The underlying motivations for BRC-25 are similar to those for , with the primary goal of facilitating efficient access to and discovery of lookup services in a decentralized manner.
In a similar manner to CHIP, we specify the various components of the CLAP architecture:
CLAP tokens are registered on the Bitcoin SV blockchain as single-satoshi outputs representing lookup service advertisements by Confederacy network operators. The token fields are ordered as follows:
The advertiser creates a transaction output containing the token and submits it to known nodes, including their own. The locking key must be linked to the advertiser's identity key using the and methodologies.
The advertiser follows these steps to derive the locking key, compute the signature, and advertise the transaction to all known nodes:
Deriving the Locking Key:
Use the advertiser's identity key as the sender private key in the key derivation, with the "anyone" public key (1 by G) as the counterparty.
Compute the invoice number using the
There is no need to advertise that nodes host CLAP itself. It is assumed that all nodes which understand and receive CLAP advertisements intrinsically host CLAP, as they are already participating in the CLAP network and processing the advertisements.
Nodes accepting CLAP tokens must validate the identity key's link to the signature-producing key before admitting the output. Invalid tokens must be rejected. The following steps detail the token validation process:
Step 1: Extract token fields. Upon receiving a CLAP token, the node should first extract the four fields as defined in the token format. These fields include the CLAP identifier, the advertiser's identity key, the domain name of the HTTPS server hosting the network node, and the service name represented by a provider ID.
Step 2: Verify the CLAP identifier. Check that the first field of the token is the string CLAP. If this condition is not met, reject the token as invalid.
Step 3: Verify the
The CLAP lookup service enables network users to access and query for CLAP advertisement UTXOs in a standardized way. By facilitating access to these UTXOs, users can discover other nodes that may have the lookup services they need, enhancing connectivity across the UTXO-based overlay network.
The specified provider name for the lookup service is CLAP. This standardized name allows implementations to easily recognize and interact with the service.
The lookup service processes queries as JSON objects containing "service" and "advertiser" keys. These keys are used in an AND stipulation to filter the results based on the provided values. The service searches the UTXOs for matches that satisfy both conditions (if present) and returns the corresponding results.
The query fields are as follows:
The AND stipulation operates by requiring both service and advertiser conditions to be satisfied for a UTXO to be included in the result set. If only one key is provided, the lookup service will return UTXOs matching that single condition. If both keys are present, the service will only return UTXOs that match both the specified service and advertiser.
Query Examples
Example 1: Query with only "service"
In this example, the lookup service will return all UTXOs with the specified "example_service", regardless of the advertiser.
Example 2: Query with only "advertiser"
Here, the service will return all UTXOs from the specified advertiser, regardless of the service.
Example 3: Query with both "service" and "advertiser"
In this case, the lookup service will return UTXOs that match both the specified service and advertiser, providing more precise results.
This section details the process for an overlay network user to discover nodes hosting a particular lookup service they are interested in. The user will follow these steps to locate relevant nodes and make a query from the service:
Query the CLAP Lookup Service. The user initiates the process by querying the CLAP lookup service through a known node. The query contains the desired "service" key, and optionally the "advertiser" key if the user wants to find a specific advertiser hosting the service. The CLAP lookup service returns a list of UTXOs with corresponding CLAP tokens containing relevant domain names and identity keys of advertisers.
Contacting Servers and Performing Queries. Upon receiving the list of UTXOs, the user extracts the domain names and identity keys of the relevant nodes. The user then contacts each server at their respective domain via the protocol, making use of the service to run a query and receive the results.
Verifying Identity Key during Lookup. During the UTXO lookup process, the user verifies that the identity key of the advertiser matches the key used by the server for authentication. This step ensures that the server is the intended recipient and maintains the integrity of the lookup process.
By following these steps, an overlay network user can discover nodes hosting a specific lookup service of interest and securely engage with them to learn about the current state of various overlays. This process enhances the connectivity and efficiency of UTXO-based overlay networks.
There are no known implementations of this specification at this time. The specification will be updated when an implementation is published.
Ty Everett ([email protected])
This technical standard proposes a key derivation method that allows two parties to derive multiple keys for each other in an open-ended manner. The method employs invoice numbers as a simple way for multiple keys to be derived. A sender can derive a public key for a recipient without knowing the recipient's corresponding private key, and the recipient can later use the sender's public key and the same invoice number to derive a corresponding private key. The proposed method removes the limit of 4 billion keys per child that is present in BIP32, improves the privacy of the involved parties by employing ECDH shared secrets, and enables an open-ended invoice numbering scheme for key derivation that can be further standardized for specific use-cases. The method can be used for private invoicing, digital signatures, symmetric cryptography, and other applications in Bitcoin and CBDC ecosystems.
Jake Jones ([email protected])
This proposal extends BRC-92 (Mandala Token Protocol) with cryptographic commitments to provide tamper-evident token amounts and SPV-friendly verification while maintaining the minimalist design philosophy. The enhancement adds a commitment hash to each token output that cryptographically binds the token amount to its transaction history, enabling lightweight clients to verify token integrity without accessing the full blockchain or trusting overlay networks.
POST /submit HTTP/1.1
Host: example-overlay-node.com
Content-Type: application/json
X-Authrite: 0.1
X-Authrite-Identity-Key: ...
X-Authrite-Signature: ...
X-Authrite-Nonce: ...
X-Authrite-YourNonce: ...
X-Authrite-Certificates: ...{
"rawTx": "0100000001abcdef...",
"inputs": {
"...": "..."
},
"mapiResponses": [
{
"payload": "0100000001abcdef...",
"signature": "MEQCIGXFM...",
"publicKey": "036b8c86..."
}
],
"topics": [
"example_topic_1",
"example_topic_2"
]
}HTTP/1.1 200 OK
Content-Type: application/json
X-Authrite: 0.1
X-Authrite-Identity-Key: ...
X-Authrite-Signature: ...
X-Authrite-Nonce: ...
X-Authrite-YourNonce: ...
X-Authrite-Certificates: ...{
"status": "success",
"topics": {
"example_topic_1": [0, 2],
"example_topic_2": [1, 2]
}
}{
"basket": "todo tokens",
"includeEnvelope": true,
"limit": 25,
"offset": 0,
"spendable": true
}[{
"amount":1000,
"customInstructions":null,
"envelope":{
"rawTx":"010000000135620d3a8fb626b763cb7b9a3c3197eda9bd6709ef1e4ebc2b359607800eaa95020000006b483045022100c3ec1792f1780e453c6ea692271bcc5388fb179d6455897f4b4200706e8b9f150220352cc2ff81a5c698e4626aec8976643134efab91ca1c80729670c2d007c77cfd4121037798f038cb7fc18b9d67baa87ed33122eb7be65b95b0dd304dc476c60043773dffffffff03e803000000000000c4210399322f558d92ced9c45d3bfe7dc5c01b830a4b61d71695ab0a1fa2cd90aba8b9ac2131546f446f44744b7265457a6248594b466a6d6f42756475466d53585855475a473462812d4eeb030fa496c2965f7e0f0b0e825d0547aceb7a9d4c76fd17e795753d8e2e0e82274ab36744a7968a43dc45b1cbdfab6c473045022100a58914da5346960ecb4de964f0f1e1be43a7645a11449c3a209f3fd69b34209b02205d4d4294d04c5f87fb16c2cdc3d9b41f9866fb3d7a690da08089c972d1393d816d75c8000000000000001976a91473a95c0b12e33b3d79f80508200a075bbab0906588ac4ea80200000000001976a91478daf10df51b3ea4c9292a72bf2e1e1d48ebe11288ac00000000",
"mapiResponses":[{
"publicKey":"030d1fe5c1b560efe196ba40540ce9017c20daa9504c4c4cec6184fc702d9f274e",
"payload":"{\"apiVersion\":\"1.5.0\",\"timestamp\":\"2023-04-07T08:19:10.2186219Z\",\"txid\":\"900b7e9ced44f8a7605bd4c1b7054b8fb958385998954d772820a8b81eabb56f\",\"returnResult\":\"success\",\"resultDescription\":\"\",\"minerId\":\"030d1fe5c1b560efe196ba40540ce9017c20daa9504c4c4cec6184fc702d9f274e\",\"currentHighestBlockHash\":\"000000000000032b2a17b3003b5cbb484b284bda4ffd7c15edd6c038429840ed\",\"currentHighestBlockHeight\":1545664,\"txSecondMempoolExpiry\":0,\"warnings\":[],\"failureRetryable\":false}",
"signature":"30440220066e711073f08d6302a33acb2863557b13465f5381a3355eaee9a5e08909abfc0220695fedf9800226d9f430f8a0177c1e4bde8134d350a8eb3bfd0d5d77925cac4d"
}],
"inputs":{
"95aa0e800796352bbc4e1eef0967bda9ed97313c9a7bcb63b726b68f3a0d6235":{
"rawTx":"0100000001ce31fcf049468a1e3a2cd56c598d5452f6bfbc95bac036eda769531795e3198c020000006a4730440220721e1e12c2540cccf01fed09560daf2dddebec959e39481c6f27bf366d8b3c5d0220040562db8f91b13b7ad33ac9f01adbbfa748c375ff2c8201929828537e7fb81841210306d6a463a7f204b050ffeba596fe9a2466f416b008d6954ce425304660abe450ffffffff0322020000000000001976a914268b68eb0ee5ed10decbf38fa47cdc2e3df7af2b88acc8000000000000001976a91426db399bdfe2ff5e413c60930b3f760a4db625db88ac30ad0200000000001976a9149cbb583bcc1d80d37669e930dca22655d621990088ac00000000",
"proof":{
"txOrId":"95aa0e800796352bbc4e1eef0967bda9ed97313c9a7bcb63b726b68f3a0d6235","target":"a4f45102baefabafb1ad7376f754c219411e45439fd31c2b082051f0e58e08e3",
"targetType":"merkleRoot",
"nodes":[
"8d8403f47e3e9a461848b5d45ee173d49ef4afb2c952e26ff2ffa95e2ae96a7c",
"04f653cc1e2e5376600ec83c93d3ac7309dd29662b69f329511a020c9ecf5b67",
"3c8a8b183008fa56dfe432ab7ccb10e47e36537c21a152466de7e1a3572a5286",
"cb798749f39ce21c360cea90973a2eeb4740a7db05b7f42f1b33b81448369753",
"3fb4036561bb38c0c2096512818bcfc425ead33f0a10272def77d8bf6243ae94"
],
"index":15
}
}
}
},
"outputScript":"210399322f558d92ced9c45d3bfe7dc5c01b830a4b61d71695ab0a1fa2cd90aba8b9ac2131546f446f44744b7265457a6248594b466a6d6f42756475466d53585855475a473462812d4eeb030fa496c2965f7e0f0b0e825d0547aceb7a9d4c76fd17e795753d8e2e0e82274ab36744a7968a43dc45b1cbdfab6c473045022100a58914da5346960ecb4de964f0f1e1be43a7645a11449c3a209f3fd69b34209b02205d4d4294d04c5f87fb16c2cdc3d9b41f9866fb3d7a690da08089c972d1393d816d75",
"spendable":true,
"txid":"900b7e9ced44f8a7605bd4c1b7054b8fb958385998954d772820a8b81eabb56f",
"vout":0
}]{
"status": "error",
"code": "ERR_PERMISSION_DENIED",
"description": "You have denied permission for accessing this output basket."
}{
"status": "error",
"code": "ERR_CERTIFIER_REJECTED_CSR",
"description": "The certifier has rejected the CSR and refused to sign the certificate."
}{
"status": "error",
"code": "ERR_PERMISSION_DENIED",
"description": "The user denied the request to reveal these fields to this verifier at this time."
}{
"status": "error",
"code": "ERR_PERMISSION_DENIED",
"description": "You have denied permission for encrypting this data."
}6a2991c9de20e38b31d7ea147bf55f5039e4bbc073160f5e0d541d1f17e321b8025ad43a22ac38d0bc1f8bacaabb323b5d634703b7a774c4268f6a09e4ddf790970294c479f762f6baa97fbcd4393564c1d7bd8336ebd15928135bbcf575cd1a71a1[252, 203, 216, 184, 29, 161, 223, 212, 16, 193, 94, 99, 31, 140, 99, 43, 61, 236, 184, 67, 54, 105, 199, 47, 11, 19, 184, 127, 2, 165, 125, 9, 188, 195, 196, 39, 120, 130, 213, 95, 186, 89, 64, 28, 1, 80, 20, 213, 159, 133, 98, 253, 128, 105, 113, 247, 197, 152, 236, 64, 166, 207, 113, 134, 65, 38, 58, 24, 127, 145, 140, 206, 47, 70, 146, 84, 186, 72, 95, 35, 154, 112, 178, 55, 72, 124]BRC-2 Encryption Compliance Validated![81, 240, 18, 153, 163, 45, 174, 85, 9, 246, 142, 125, 209, 133, 82, 76, 254, 103, 46, 182, 86, 59, 219, 61, 126, 30, 176, 232, 233, 100, 234, 14]BRC-2 HMAC Compliance Validated!21 <assetId> <amount> OP_DROP OP_2DROP ...21 <assetId> OP_2DROP ...const details = {
entity: 'Local Comedy Troupe',
asset: 'Ticket for show on 9/01/2024',
address: '412 E 6th St, Austin, TX 78701'
}
const commitment = hash(details)
const anyone = new PrivateKey(1)
const pubkey = identityPublicKey.deriveChild(anyone, commitment)<securityLevel>-<protocolID>-<keyID>POST /lookup HTTP/1.1
Host: example-overlay-node.com
Content-Type: application/json
X-Authrite: 0.1
X-Authrite-Identity-Key: ...
X-Authrite-Signature: ...
X-Authrite-Nonce: ...
X-Authrite-YourNonce: ...
X-Authrite-Certificates: ...{
"provider": "example_provider",
"query": {
"topic": "example_topic",
"search": "example_search_criteria"
}
}HTTP/1.1 200 OK
Content-Type: application/json
X-Authrite: 0.1
X-Authrite-Identity-Key: ...
X-Authrite-Signature: ...
X-Authrite-Nonce: ...
X-Authrite-YourNonce: ...
X-Authrite-Certificates: ...[
{
"txid":"7001295a287fee8e7ad5de58ed30bd61923977ddc9b7a44830ebb9a68b12dc39",
"vout":0,"outputScript":"4104ca0a8ce950bf2bc85115bd68818455ae2f187efc96c7527dfad98b69531ae65d13cff3e6f07263dcc64c8ccfd03884983a896b0c5887f2ec5bfd7ad739b76119ac213155485250596e4d4850755135546762334146384a5871774b6b6d5a56793568472231485438347a7272467645594658567a6b5343436f6968706d33437754586a74726b200c613f338ad70e228eaee180f65c34d72a63bdf4c9e167dd5bf70a61539e80a8096164766572746973654468747470733a2f2f73746167696e672d6e616e6f73746f72652e626162626167652e73797374656d732f63646e2f54436f5a366269715743634b574b4d6e5065695854620a31383338383430313032043834303046304402205da55600d8f260d760b37491bc3286b9faacd0d408c187e69119ddc1d98bf3fe02207996fda8d6aba1b7494d7fe8a9b7e5111ea492348f11162bc898b3f7ab2ebf486d6d6d6d",
"topic":"UHRP",
"satoshis":500,
"rawTx":"0100000001e09afc3f6ed8842e3f231aac29d325c0f90137a5441bf861c7a9ee7499fafc9a020000006b483045022100cfef7385daedeb6f54a68c1188bf7d9061774f88eb391e132daea49cfcb883c60220010ba950bbb2952a1d4ace35c6721a254774c6370be75e804334be13544e4f3c412102dc35718aeb818a6075a926e8f68bd6656e5fa007a083d8f5b6193b7b1c34e3f7ffffffff03f401000000000000fd53014104ca0a8ce950bf2bc85115bd68818455ae2f187efc96c7527dfad98b69531ae65d13cff3e6f07263dcc64c8ccfd03884983a896b0c5887f2ec5bfd7ad739b76119ac213155485250596e4d4850755135546762334146384a5871774b6b6d5a56793568472231485438347a7272467645594658567a6b5343436f6968706d33437754586a74726b200c613f338ad70e228eaee180f65c34d72a63bdf4c9e167dd5bf70a61539e80a8096164766572746973654468747470733a2f2f73746167696e672d6e616e6f73746f72652e626162626167652e73797374656d732f63646e2f54436f5a366269715743634b574b4d6e5065695854620a31383338383430313032043834303046304402205da55600d8f260d760b37491bc3286b9faacd0d408c187e69119ddc1d98bf3fe02207996fda8d6aba1b7494d7fe8a9b7e5111ea492348f11162bc898b3f7ab2ebf486d6d6d6dc8000000000000001976a9148055582669432149141abcf887fced6881f7404288ac207c1202000000001976a9146a1a5f00deb78b2ef39e9a80d3e3ea2c97d0710c88ac00000000",
"proof":null,
"inputs":"{\"9afcfa9974eea9c761f81b44a53701f9c025d329ac1a233f2e84d86e3ffc9ae0\":{\"proof\":{\"flags\":2,\"index\":3,\"txOrId\":\"9afcfa9974eea9c761f81b44a53701f9c025d329ac1a233f2e84d86e3ffc9ae0\",\"target\":\"c99dfb20991b131142a915b086eb6413d78fb472477c31ef06495c7d712dc4e4\",\"nodes\":[\"5206842a42c144617bee40869bd73b0f65ecf6d19c75e73c7106ee2f5b271308\",\"624c68a9e2c8e83df9e55c0bc4b3a560924d6cb027285e6d6499f6445149e4ac\",\"4c02bfcd6098e40ecebab74783e1bd14d8495bccb02e4b5447057de31ed485d6\"],\"targetType\":\"merkleRoot\"},\"rawTx\":\"010000000166dee27f6d4ca528dbd664c1b6a686f9a3b18336ca396bcead7f2065ec0f07ac020000006b483045022100effd3358e1a602898e2a80b0d45b3a01bb36dfc9148b0766bc6e8185f4cf3c0c02203c3d6c6ff180d395b1300b89f96056d9ec915d6cfbf02c7cff2d6f4bb9611d1941210228ec497f7097a26ea1049ef732931d5abc40335e7693cac56d4534f15ff60aefffffffff03204e0000000000001976a9145b296c72cbec349171445808df953c2edaa3a1d388acc8000000000000001976a914d013936b4177bcbb0d934c6348b0d9ec3cc05dac88ac1e7f1202000000001976a914b281d7f257d79aebb910a5ba2c8e787f7ea635ab88ac00000000\"}}",
"mapiResponses":"[{\"payload\":\"{\\\"apiVersion\\\":\\\"1.5.0\\\",\\\"timestamp\\\":\\\"2023-04-10T20:55:03.2794204Z\\\",\\\"txid\\\":\\\"7001295a287fee8e7ad5de58ed30bd61923977ddc9b7a44830ebb9a68b12dc39\\\",\\\"returnResult\\\":\\\"success\\\",\\\"resultDescription\\\":\\\"\\\",\\\"minerId\\\":\\\"030d1fe5c1b560efe196ba40540ce9017c20daa9504c4c4cec6184fc702d9f274e\\\",\\\"currentHighestBlockHash\\\":\\\"00000000000004863e21305bc8cee6dd66b80a443416f14ccc5c74895fdefcac\\\",\\\"currentHighestBlockHeight\\\":1546251,\\\"txSecondMempoolExpiry\\\":0,\\\"warnings\\\":[],\\\"failureRetryable\\\":false}\",\"publicKey\":\"030d1fe5c1b560efe196ba40540ce9017c20daa9504c4c4cec6184fc702d9f274e\",\"signature\":\"304402205cd7c1b8f64de17c5824fec87549ecf46d69f122ed96971878562795d432a00702202097e309da4db38876bcaecafdd9f6234fcee999113a5fd54c909383d223a24f\"}]"
}
]1The client makes a permission request, which the user accepts.
The client computes 1-document signing-1 as the invoice number.
The client uses its own private key as the sender, and the public key 1 * G as the counterparty, as the counterparty is anyone.
The client derives a child private key as the recipient using the invoice number, and uses the derived key to compute the digital signature.
Any verifier can now use 1 as a private key in conjunction with the signer's root public key and the same invoice number to derive the corresponding child public key used for the signature.
The verifier can then check the signature against the derived child public key for validity.
Alice's client makes a permission request, which Alice accepts.
Alice's client computes 2-private document signing-1337 as the invoice number.
Alice's client uses her own private key as the sender, and Bob's public key as the counterparty.
Alice's client derives a child private key as the recipient using the invoice number, and uses the derived key to compute the digital signature.
Bob's client can now use his private key in conjunction with Alice's root public key and the same invoice number to derive the corresponding child public key used for the signature.
Bob's client can then check the signature against the derived child public key for validity. No one aside from Alice and Bob can check the signature for validity.
offset
no
The number of outputs from the beginning of the list to skip
spendable
no
Always considered to be true, included by some implementations for historical reasons
envelope
If envelopes were requested, the BRC-8 Transaction Envelope for this transaction
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
(unused, will assign next BRC to this number)
36
37
38
User Wallet Data Format
39
User Wallet Data Format Encryption Extension
40
User Wallet Data Synchronization
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
2CHIP1Derive the signing private key from your identity key using the computed invoice number.
Computing the Signature:
Use the derived private key to compute the BRC-48 signature over the token fields.
Advertising the Transaction:
Create a transaction output containing the CHIP token.
Submit the transaction to all known nodes, including the advertiser's own node.
1) as the recipient. Compute the invoice number using the 2, protocol ID CHIP, and key ID of 1. Derive the public key from the advertiser's identity key using this invoice number. Ensure that the computed key matches the Step 4: Verify the BRC-48 signature. Check the signature over the token fields by using the derived public key from the previous step. Ensure that the signature is valid and corresponds to the fields provided in the token.
Step 5: Admit the token. If all previous steps have been successfully completed and the token is deemed valid, admit the token into the CHIP topic.
Field 1
The string CHIP, denoting a CHIP advertisement.
Field 2
The BRC-31 identity key of the advertiser creating the token.
Field 3
The internet domain name of the HTTPS server hosting the network node.
Field 4
The topic name hosted by the advertiser.
topic
The topic name hosted by the advertiser, used to filter UTXOs based on the desired topic.
advertiser
The BRC-31 identity key of the advertiser, used to filter UTXOs based on the specific advertiser.
Priority
10
Weight
10
Port
443
Target
<endpoint-discovery-host>
Service
_bsvalias
Proto
_tcp
Name
<domain>.<tld>.
TTL (Time to Live)
3600 (recommended for production deployments)
Class
IN
2CLAP1Derive the signing private key from your identity key using the computed invoice number.
Computing the Signature:
Use the derived private key to compute the BRC-48 signature over the token fields.
Advertising the Transaction:
Create a transaction output containing the CLAP token.
Submit the transaction to all known nodes, including the advertiser's own node.
1) as the recipient. Compute the invoice number using the 2, protocol ID CLAP, and key ID of 1. Derive the public key from the advertiser's identity key using this invoice number. Ensure that the computed key matches the Step 4: Verify the BRC-48 signature. Check the signature over the token fields by using the derived public key from the previous step. Ensure that the signature is valid and corresponds to the fields provided in the token.
Step 5: Admit the token. If all previous steps have been successfully completed and the token is deemed valid, admit the token into the CLAP topic.
Field 1
The string CLAP, denoting a Confederacy Lookup Availability Protocol advertisement.
Field 2
The BRC-31 identity key of the advertiser creating the token.
Field 3
The internet domain name of the HTTPS server hosting the network node.
Field 4
The service name, represented by a BRC-24 provider ID.
service
The BRC-24 provider ID of the lookup service, used to filter UTXOs based on the desired service.
advertiser
The BRC-31 identity key of the advertiser, used to filter UTXOs based on the specific advertiser.
This technical standard is proposed to address the need for a standard method for key derivation between parties that enables greater ecosystem interoperability. While BIP321 provided a naive approach, it enabled only one key derivation universe per master public key. Conversely, this specification enables new key universes for every combination of two parties who interact, because the shared secret between their keys is employed. This also protects the privacy of the parties involved from outsiders who do not know the shared secret.
The proposed key derivation method offers several benefits. It enables two parties to derive many different keys for one another in an open-ended way, thereby providing greater flexibility. The use of invoice numbers as a simple way for multiple keys to be derived makes the method easy to use and enables further standardization. The method also employs ECDH shared secrets to improve privacy, and can be auditable by revealing shared secrets between specific counterparties to tax agencies for an audit to prove transactions. Additionally, higher-order protocol identifiers, key universes, and security levels within Bitcoin wallet and application architectures have been defined for this method.
Overall, this proposed key derivation method offers an improved approach to key derivation that can be used in various applications. It removes the limitations of existing key derivation methods such as BIP321 and provides greater flexibility, privacy, and auditability.
The key derivation is specified as follows:
Each party has a master private key and a master public key that are derived from the secp256k1 elliptic curve.
The sender computes the shared secret by multiplying his master private key with the recipient's master public key using elliptic curve point multiplication.
The sender uses the shared secret as the key to generate an HMAC over the invoice number.
The HMAC is then converted into a scalar using big-endian encoding.
The generator point G is multiplied by the scalar to generate a new point on the elliptic curve.
The resulting point is added to the recipient's master public key point to produce a new point on the curve.
The resulting point is then used to generate a new child public key.
To derive the corresponding private key, the recipient computes the shared secret by multiplying his master private key with the sender's master public key using elliptic curve point multiplication.
The recipient then generates the HMAC over the invoice number using the shared secret and computes the scalar in the same way as the sender.
The scalar is then added to the recipient's master private key to generate the corresponding child private key.
The invoice number is treated as a string and is converted into a buffer using UTF-8 encoding.
The sender generates a shared secret by multiplying his master private key with the recipient's master public key using elliptic curve point multiplication.
The sender generates an HMAC over the invoice number using the shared secret as the key.
The HMAC is converted into a scalar using big-endian encoding.
The generator point G is multiplied by the scalar to generate a new point on the elliptic curve.
The resulting point is added to the recipient's master public key point to produce a new point on the curve.
The resulting point is then used to generate a new child public key for the given invoice number.
The recipient generates the shared secret by multiplying his master private key with the sender's master public key using elliptic curve point multiplication.
The recipient generates the HMAC over the invoice number using the shared secret as the key.
The HMAC is converted into a scalar using big-endian encoding.
The scalar is added to the recipient's master private key (mod N) to generate the corresponding child private key for the given invoice number.
The security of this key derivation method relies on keeping the master private keys secure and the shared secret confidential. Any party with access to the shared secret could potentially derive the child keys as well, so it is important to keep the shared secret confidential. Private keys derived using this method should be treated with the same level of security as any other private key.
This specification provides a method for key derivation that enables two parties to derive many different keys for one another in an open-ended way. This method employs invoice numbers as a simple way for multiple keys to be derived and uses the secp256k1 elliptic curve for key derivation. The method is designed to be flexible and auditable, with potential applications including private invoicing,
This specification has been implemented into both the BSV TypeScript and Go libraries.
Test vectors for this standard are provided:
Credit is given to the people who have worked on making these ideas into reality. In particular, we thank Xiaohui Liu for creating the first known implementation of private addresses using this scheme, and Dr. Craig Wright for first describing it.
The original BRC-92 Mandala Token Protocol provides a minimalist approach to tokenization on BSV, but relies entirely on overlay networks to validate token conservation rules. In real-world usage scenarios, particularly SPV wallets and point-of-sale transactions, users need cryptographic assurance that token amounts haven't been tampered with. This enhancement addresses these limitations while preserving the simplicity that makes BRC-92 attractive.
Key problems this solves:
SPV wallets cannot verify token conservation without trusting overlays
No cryptographic proof linking token amounts to their history
Potential for amount tampering between transaction creation and overlay submission
Lack of standardized verification protocol for lightweight clients
Building upon BRC-92's structure, we add a cryptographic commitment field:
Where:
21 (0x21): UTF-8 exclamation mark prefix (unchanged from BRC-92)
assetId: Genesis transaction outpoint (txid:vout) (unchanged)
amount: Token amount in Bitcoin number format (unchanged)
commitment: NEW - Cryptographic commitment hash
...: Functional locking script (e.g., P2PKH) (unchanged)
The commitment is calculated as:
Where:
H: SHA-256 hash function
||: Concatenation operator
prevTxid: Transaction ID of the input being spent
nonce: Optional 32-byte random value for privacy
For NFTs, the commitment is:
When transferring tokens in an SPV context, the sender provides:
Recipients MUST perform the following verification:
Commitment Verification: For each input and output, verify:
Conservation Check: Verify that:
Merkle Proof Validation: Verify SPV proofs for recent transactions
Accept/Reject: Transaction is valid if all checks pass
This enhancement maintains full backward compatibility with BRC-92:
Existing BRC-92 tokens continue to function
Overlays that don't recognize commitments can ignore them (treated as extra data)
The drop pattern ensures scripts execute identically
Migration path: New tokens use commitments, old tokens grandfathered
The commitment includes an optional nonce field to prevent amount analysis:
Without nonce: Same amounts produce same commitments (linkable)
With nonce: Same amounts produce different commitments (unlinkable)
Bitcoin Script's execution model prevents cross-input/output verification within scripts. Cryptographic commitments provide the next best solution:
Tamper-evident: Can't change amounts without breaking commitments
SPV-compatible: Verifiable with just transaction data
Minimalist: Adds only one hash field
Overlay-independent: Cryptographic proof doesn't require trust
Including the previous transaction ID in the commitment:
Creates an unforgeable chain of custody
Prevents replay attacks with old commitments
Links each token to its complete history
Enables proof of lineage back to genesis
The nonce field balances transparency and privacy:
Public tokens (securities): No nonce for full transparency
Private tokens (cash-like): Nonce prevents amount correlation
User choice: Applications decide based on use case
Input (100 tokens from previous tx):
Outputs:
Bob receives a payment at a coffee shop:
Alice sends: Transaction + commitment proofs + merkle proof
Bob's wallet verifies (< 100ms):
Commitment: H(assetId||30||prevTxid||nonce) == provided_commitment ✓
Conservation: 50 in == 30 + 20 out ✓
Merkle proof: Transaction exists in blockchain ✓
Bob accepts: Payment verified without blockchain access
Wallets implementing this standard should:
Generate commitments when creating token transactions
Store nonces for privacy-enabled tokens
Verify commitments before accepting tokens
Provide SPV proof packages to recipients
Overlay networks can:
Reject transactions with invalid commitments
Use commitments for faster validation
Provide commitment verification as a service
Index tokens by commitment for quick lookups
Additional data: 32 bytes per output (commitment)
Computation: One SHA-256 hash per input/output
Verification time: < 1ms per token on modern devices
Storage: Negligible increase (< 5% for typical token transactions)
Forging a commitment requires finding a hash collision:
SHA-256 preimage resistance: 2^256 operations
Collision resistance: 2^128 operations
Practically impossible with current technology
Including prevTxid prevents replay:
Old commitments won't match new transactions
Each commitment is unique to its transaction chain
Changing amounts breaks commitments:
Detectable by any verifier
Rejected by overlays and recipients
Phase 1: Wallets add commitment generation (optional)
Phase 2: Overlays prefer commitment-enabled tokens
Phase 3: Recipients require commitments for new tokens
Phase 4: Full ecosystem adoption
BRC-92: Mandala Token Protocol
BRC-62: Background Evaluation Extended Format (BEEF) Transactions
BRC-67: Simplified Payment Verification
Input:
Output:
Valid (conserves tokens):
Invalid (creates tokens):
Reference implementations available at:
TypeScript: Coming Soon
Go: Coming Soon
Python: Coming Soon
{
"topic": "example_topic"
}{
"advertiser": "02bc91718b3572462a471de6193f357b6e85ee0f8636cb87db456cb1590f913bea"
}{
"topic": "example_topic",
"advertiser": "02bc91718b3572462a471de6193f357b6e85ee0f8636cb87db456cb1590f913bea"
}{
"bsvalias": "1.0",
"capabilities": {
"5f1323cddf31": "https://example.bsvalias.tld/api/receive-rawtx/{alias}@{domain.tld}"
}
}{
"hex": "<raw_transaction_hex>",
"metadata": {
"sender": "<sender_paymail>",
"pubkey": "<sender_public_key>",
"signature": "<signature_of_txid>",
"note": "<optional_human_readable_note>"
},
"reference": "<payment_reference>"
}{
"txid": "<accepted_txid>",
"note": "<optional_human_readable_note>"
}{
"bsvalias": "1.0",
"capabilities": {
"2a40af698840": "https://example.bsvalias.tld/api/p2p-payment-destination/{alias}@{domain.tld}"
}
}{
"satoshis": <number_of_satoshis>
}{
"outputs": [
{
"script": "<locking_script_hex_1>",
"satoshis": <satoshis_1>
},
{
"script": "<locking_script_hex_2>",
"satoshis": <satoshis_2>
}
],
"reference": "<payment_reference>"
}https://<host-discovery-target>:<host-discovery-port>/.well-known/bsvalias{
"service": "example_service"
}{
"advertiser": "02bc91718b3572462a471de6193f357b6e85ee0f8636cb87db456cb1590f913bea"
}{
"service": "example_service",
"advertiser": "02bc91718b3572462a471de6193f357b6e85ee0f8636cb87db456cb1590f913bea"
}[
{
senderPublicKey: '033f9160df035156f1c48e75eae99914fa1a1546bec19781e8eddb900200bff9d1',
recipientPrivateKey: '6a1751169c111b4667a6539ee1be6b7cd9f6e9c8fe011a5f2fe31e03a15e0ede',
invoiceNumber: 'f3WCaUmnN9U=',
privateKey: '761656715bbfa172f8f9f58f5af95d9d0dfd69014cfdcacc9a245a10ff8893ef'
},
{
senderPublicKey: '027775fa43959548497eb510541ac34b01d5ee9ea768de74244a4a25f7b60fae8d',
recipientPrivateKey: 'cab2500e206f31bc18a8af9d6f44f0b9a208c32d5cca2b22acfe9d1a213b2f36',
invoiceNumber: '2Ska++APzEc=',
privateKey: '09f2b48bd75f4da6429ac70b5dce863d5ed2b350b6f2119af5626914bdb7c276'
},
{
senderPublicKey: '0338d2e0d12ba645578b0955026ee7554889ae4c530bd7a3b6f688233d763e169f',
recipientPrivateKey: '7a66d0896f2c4c2c9ac55670c71a9bc1bdbdfb4e8786ee5137cea1d0a05b6f20',
invoiceNumber: 'cN/yQ7+k7pg=',
privateKey: '7114cd9afd1eade02f76703cc976c241246a2f26f5c4b7a3a0150ecc745da9f0'
},
{
senderPublicKey: '02830212a32a47e68b98d477000bde08cb916f4d44ef49d47ccd4918d9aaabe9c8',
recipientPrivateKey: '6e8c3da5f2fb0306a88d6bcd427cbfba0b9c7f4c930c43122a973d620ffa3036',
invoiceNumber: 'm2/QAsmwaA4=',
privateKey: 'f1d6fb05da1225feeddd1cf4100128afe09c3c1aadbffbd5c8bd10d329ef8f40'
},
{
senderPublicKey: '03f20a7e71c4b276753969e8b7e8b67e2dbafc3958d66ecba98dedc60a6615336d',
recipientPrivateKey: 'e9d174eff5708a0a41b32624f9b9cc97ef08f8931ed188ee58d5390cad2bf68e',
invoiceNumber: 'jgpUIjWFlVQ=',
privateKey: 'c5677c533f17c30f79a40744b18085632b262c0c13d87f3848c385f1389f79a6'
}
][
{
senderPrivateKey: '583755110a8c059de5cd81b8a04e1be884c46083ade3f779c1e022f6f89da94c',
recipientPublicKey: '02c0c1e1a1f7d247827d1bcf399f0ef2deef7695c322fd91a01a91378f101b6ffc',
invoiceNumber: 'IBioA4D/OaE=',
publicKey: '03c1bf5baadee39721ae8c9882b3cf324f0bf3b9eb3fc1b8af8089ca7a7c2e669f'
},
{
senderPrivateKey: '2c378b43d887d72200639890c11d79e8f22728d032a5733ba3d7be623d1bb118',
recipientPublicKey: '039a9da906ecb8ced5c87971e9c2e7c921e66ad450fd4fc0a7d569fdb5bede8e0f',
invoiceNumber: 'PWYuo9PDKvI=',
publicKey: '0398cdf4b56a3b2e106224ff3be5253afd5b72de735d647831be51c713c9077848'
},
{
senderPrivateKey: 'd5a5f70b373ce164998dff7ecd93260d7e80356d3d10abf928fb267f0a6c7be6',
recipientPublicKey: '02745623f4e5de046b6ab59ce837efa1a959a8f28286ce9154a4781ec033b85029',
invoiceNumber: 'X9pnS+bByrM=',
publicKey: '0273eec9380c1a11c5a905e86c2d036e70cbefd8991d9a0cfca671f5e0bbea4a3c'
},
{
senderPrivateKey: '46cd68165fd5d12d2d6519b02feb3f4d9c083109de1bfaa2b5c4836ba717523c',
recipientPublicKey: '031e18bb0bbd3162b886007c55214c3c952bb2ae6c33dd06f57d891a60976003b1',
invoiceNumber: '+ktmYRHv3uQ=',
publicKey: '034c5c6bf2e52e8de8b2eb75883090ed7d1db234270907f1b0d1c2de1ddee5005d'
},
{
senderPrivateKey: '7c98b8abd7967485cfb7437f9c56dd1e48ceb21a4085b8cdeb2a647f62012db4',
recipientPublicKey: '03c8885f1e1ab4facd0f3272bb7a48b003d2e608e1619fb38b8be69336ab828f37',
invoiceNumber: 'PPfDTTcl1ao=',
publicKey: '03304b41cfa726096ffd9d8907fe0835f888869eda9653bca34eb7bcab870d3779'
}
]commitment == H(assetId || amount || prevTxid || nonce)sum(input_amounts) == sum(output_amounts)21 <assetId> <amount> <commitment> OP_2DROP OP_2DROP OP_DROP ...commitment = H(assetId || amount || prevTxid || nonce)21 <assetId> <commitment> OP_2DROP OP_DROP ...commitment = H(assetId || prevTxid || metadata_hash){
"transaction": "hex_encoded_transaction",
"tokenProofs": {
"inputs": [
{
"index": 0,
"amount": 100,
"commitment": "abc123...",
"prevTx": "def456...",
"merkleProof": "..."
}
],
"outputs": [
{
"index": 0,
"amount": 60,
"commitment": "ghi789..."
},
{
"index": 1,
"amount": 40,
"commitment": "jkl012..."
}
],
"conservationProof": {
"totalIn": 100,
"totalOut": 100
}
}
}Previous output:
21 <assetId> <100> <H(assetId||100||prev_prev_txid||nonce)> OP_2DROP OP_2DROP OP_DROPOutput 0 (60 tokens to Bob):
21 <assetId> <60> <H(assetId||60||current_input_txid||nonce1)> OP_2DROP OP_2DROP OP_DROP P2PKH(Bob)
Output 1 (40 tokens change to Alice):
21 <assetId> <40> <H(assetId||40||current_input_txid||nonce2)> OP_2DROP OP_2DROP OP_DROP P2PKH(Alice)assetId = "abc...def:0"
amount = 100
prevTxid = "123...456"
nonce = "000...000"commitment = SHA256("abc...def:0" || 100 || "123...456" || "000...000")
= "a7b8c9d0e1f2..."Inputs: [50, 30, 20] (sum = 100)
Outputs: [60, 40] (sum = 100)
Result: VALID ✓Inputs: [50, 30] (sum = 80)
Outputs: [60, 40] (sum = 100)
Result: INVALID ✗Regular Bitcoin transactions do not contain all the data that is needed to verify that the signatures in the transactions are valid. To sign an input of a Bitcoin transaction, the signer needs to know the transaction ID, output index, output satoshis and the locking script of the input transaction. When sending a Bitcoin transaction to a node, only the previous transaction ID and the output index are part of the serialized transaction, the node will look up the locking script and output amount of the input transaction.
We propose an Extended Format (EF) for a Bitcoin transaction, that includes the locking script and the amount in satoshis of all inputs of the transaction. This allows a broadcast service to validate all aspects of a transaction without having to contact a node or an indexer for the utxos of the inputs of a transaction, speeding up the validation.
This BRC is licensed under the Open BSV license.
Verifying that a transaction is valid, including all signatures, is not possible at the moment without getting the unspent transaction outputs (utxos) from the transactions that are used as inputs from a Bitcoin node (or a Bitcoin indexer). This lookup of the utxos always happens inside a Bitcoin node when validating a transaction, but for a broadcast service to be able to fully validate a transaction (including the fee being paid) it also needs to look up the utxos being spent, which complicates scalability, since this lookup needs to happen on a node (via RPC), that might be too busy to react within an acceptable time frame.
A broadcast service would be able to validate a transaction almost in full if the sender would also send the missing data (previous locking scripts and satoshi outputs) from the utxos being used in the transaction. When creating a new transaction, the previous locking scripts and satoshi outputs are needed to be able to properly sign the transaction, so the missing data is available at the time of the transaction creation. Serializing the transaction to Extended Format, instead of the standard format, is at the point of creating the transaction no extra work, but does make it much easier for a broadcast service to validate the transaction when being received, before sending the transaction to a node.
The main motivation for this proposal is therefore scalability. When incoming transactions contain all the data that is needed to validate them, without having to contact an external service for missing data, the broadcast service becomes much more scalable.
Current Transaction format:
The Extended Format adds a marker to the transaction format:
The Extended Format marker allows a library that supports the format to recognize that it is dealing with a transaction in extended format, while a library that does not support extended format will read the transaction as having 0 inputs, 0 outputs and a future nLock time. This has been done to minimize the possible problems a legacy library will have when reading the extended format. It can in no way be recognized as a valid transaction.
The input structure is the only additional thing that is changed in the Extended Format. The current input structure looks like this:
In the Extended Format, we extend the input structure to include the previous locking script and satoshi outputs:
The Extended Format is not backwards compatible, but has been designed in such a way that existing software should not read a transaction in Extend Format as a valid (partial) transaction. The Extended Format header (0000000000EF) will be read as an empty transaction with a future nLock time in a library that does not support the Extended Format.
The Extended Format has been implemented in and a standalone JavaScript library .
Ty Everett ([email protected])
This specification outlines the architecture and structure of permissions granted to apps. It focuses on the grouping of permissions to provide a concise overview for users. The key permissions include Protocol Permissions, Spending Authorizations, Basket Access, and Certificate Field Access Grants.
As applications become more integrated with user data and cryptographic operations, the need for a clear and concise permission system has never been greater. This specification seeks to simplify user decisions by grouping permissions, ensuring transparency and user autonomy over their identities, their data and their digital assets.
There are four primary permission types that apps can be granted by users, each with its significance, function, and associated specifications:
Protocol Permissions (BRC-43): This permission grants an application the ability to utilize a user's keys to execute cryptographic operations (such as BRC-2 encryption or BRC-3 digital signatures).
Spending Authorizations (BRC-1): As the name suggests, this allows apps to transact or spend a predefined number of satoshis over a specified time frame, within BRC-1 transactions.
Basket Access (BRC-46): This pertains to the app's capability to insert into and spend UTXO-based tokens from specific BRC-46 Output Baskets. The significance of this permission is to manage token operations within the app ecosystem. Each "basket" can be thought of as a container or grouping for tokens, and BRC-46 provides the framework for how apps can interact with these baskets, ensuring a structured approach to token management.
Applications initiate the permission request process via the waitForAuthentication function, which is part of the BRC-100 suite.
Wallets determine the group permissions from the application's manifest.json file.
The user receives a prompt detailing the permissions. They can choose to accept or deny each or all permissions. The wallet then applies their choices.
manifest.jsonThe file is to be served over HTTPS and requested from the application by the wallet. HTTP is only accepted when running on localhost for development purposes.
The existing babbage key is expanded to include the groupPermissions key.
The groupPermissions
groupPermissions key in manifest.jsonThe groupPermissions key is an object that encompasses various types of permissions, which the app might request from a user. This key contains four sub-keys: protocolPermissions, spendingAuthorization, basketAccess, and certificateAccess.
protocolPermissions is an array of objects, where each object defines a specific Protocol Permission. Each object contains three fields: protocolID (a protocol ID array with two elements, the first a security level and the second a protocol string), counterparty (a string in hexadecimal format representing a public key, required only if the security level of the protocolID is 2), and description (a short text explaining the purpose of this request).
NOTE: We do not support privileged key use in group permissions for security reasons. For the purpose of grouped permission requests,
privilegedis alwaysfalse. By nature, every use of a privileged key must be requested on a one-off basis. Thus privileged operations can never be covered by a grouped permission scheme.
spendingAuthorization is an object that provides details about the satoshis an app is requesting authorization to spend. It has three fields: amount (a number indicating the amount in satoshis), duration (a number representing the time in seconds for the authorization's validity), and description.
basketAccess is an array of objects, where each object denotes the access to a specific BRC-46 basket. The objects here have two fields: basket (the name of the BRC-46 basket) and description
With this structured approach, app developers can seamlessly integrate their permission requests within their manifest.json, and users can be assured of a clear understanding of the permissions they're granting.
For the avoidance of doubt, the following sections provide tables that define each specific field, and example data structures.
protocolPermissions Example:
spendingAuthorization Example:
basketAccess Example:
certificateAccess Example:
App developers should prioritize user understanding and transparency when requesting permissions. The description field must be utilized effectively to convey the reason for the request, allowing users to make informed decisions.
This approach ensures a streamlined and transparent process for apps to request and for users to grant permissions. It promotes a higher level of trust and clarity between applications and their users.
Deggen ([email protected]) Damian Orzepowski ([email protected])
We propose a binary format for Compound Merkle Paths (CMP hereon) optimized for minimal data bandwidth during transmission.
This BRC is licensed under the Open BSV license.
Current format standards do not cover merkle paths for multiple txids within the same block. This would help reduce the overall size of data needed to express any set of paths from the same block. The larger the set the bigger the space saving.
For each level of the merkle path the opposite hash from the one which can be calculated is provided.
Formatting Syntax
offsetandleafare repeated fornLeavesat each height
heightdoes not need to be repeated, the inference is that height starts as the max height of the tree and is decremented by one each time we reach the end of the current set of leaves. Onceheight === -1
We must include the txid and offset within a block at height 0. This is the most efficient way to store the index data required to pull out individual paths when given only a txid. In the example below we encode txids at indices 0 and 3:
By convention we reverse the bytes of a txid hex string so these sequences will be seen in their inverse endian form below.
Let's start by dumping this format as hex into a Buffer and parsing it into an object with a Buffer Reader. Then we construct an object
If we JSON encode the leaves we get the following. Height is encoded as the position of the leaf object within the outermost array.
You can derive individual paths for particular transaction indices as necessary using the following algorithm:
Reading index 3 from the Compound Merkle Path.
Which yields:
Important to understand that we only kept the paths for indices 0 and 3 for this particular example. If you attempt to run the algo above for any other indices, an error would be thrown. This allowed us to keep 7 hashes out of 14 total. Keeping each path separately we'd have to keep minimum 6 hashes, and if we added another index then the compound method would only require one more hash, whereas saving individual paths would require another 3. The total bandwidth saving would be significant if we had thousands of transactions all in the same block.
We use this to prove inclusion in a block by running a Merkle Proof algorithm on the txid, index, and path. We arrive at a Merkle root hash. This can then be used as the key in a Block Header lookup to determine whether the txid is included within a block which is part of the longest chain of work.
Ty Everett ([email protected])
We define a concise, transport-independent method by which applications can request the creation of Bitcoin transactions. We specify the use of BRC-8 transaction envelopes, and provide an open-ended and extensible approach to output stipulation by the requesting application. We define the formats for request, response and error messages exchanged between application and wallet software.
The motivation behind this Bitcoin wallet transaction creation specification is to provide a standardized method for third-party applications to communicate with Bitcoin wallets. This specification aims to enable applications to request the creation of Bitcoin transactions in a concise and transport-independent way, and to allow wallets to focus on wallet and Bitcoin infrastructure, rather than application-specific functionality — conversely, applications can avoid re-inventing the wheel and the need to become their own Bitcoin wallet and infrastructure providers.
By standardizing the communication protocol between applications and wallets, this specification aims to simplify the development of Bitcoin-related applications, reduce development time and costs, and improve the user experience of Bitcoin-powered applications. Additionally, this specification aims to promote interoperability between different wallets and applications, making it easier for users to switch between wallets and for applications to work with multiple wallets.
Implementing this specification will provide a standard way for web and mobile applications to interface with on-device Bitcoin wallets, improving the security of Bitcoin-related operations and enabling users to better control their Bitcoin transactions. With this specification, applications can avoid the overhead of creating their own Bitcoin wallets, and wallets can focus on their core functionality of managing Bitcoin keys, signing transactions, and interacting with the Bitcoin network.
Overall, this specification aims to provide a simple, open-ended, and extensible approach to Bitcoin transaction creation, with a transport-independent method of communication that enables wallets and applications to work together seamlessly.
In order to implement this specification, Bitcoin wallet software must be able to perform the following functions:
Create Bitcoin transactions with any number of outputs that use arbitrary Bitcoin scripts and amounts.
Prompt the user for acceptance of transactions requested by third-party applications.
Unlock a sufficient number of UTXOs to fund the transaction and capture left-over change outputs.
Provide merkle proofs on all inputs to build the
Wallets that meet these pre-requisites will be able to fulfill the requirements of this specification.
The scope of this specification is the definition of standard JSON formats for requesting the creation of a Bitcoin transaction, providing the completed transaction once created, and any errors that prevent the transaction from being created.
The specification is intended to be transport-independent, so considerations related to how these messages are communicated, or matters related to the security or authenticity of such communications, are beyond the scope of this specification.
We have sought to keep the base version of this specification as minimal as possible, while still providing all the information needed by applications. Therefore, we have decided to exclude arbitrary input redemption from this specification, and move it to an extension — namely, . We have also chosen to exclude transaction output tracking from the base specification, moving it to .
We specify that there are two relevant parties: an application and a Bitcoin wallet. The application is software that runs on an end-user device (such as a webpage, mobile app, or desktop app) that wants to make use of Bitcoin without building wallet infrastructure themselves. The wallet is software that runs on an end-user device that specializes in the management of a user's Bitcoin-related identity information, private keys, facilitates the transaction creation and signing process, and communicates with the Bitcoin network.
We specify that there exists some abstract communications channel (beyond the scope of this specification) that enables the application and the Bitcoin wallet to communicate in a secure and authenticated manner. This mechanism must facilitate the application making a request to the Bitcoin wallet, and the wallet providing a response back to the application.
We specify that JSON is the format that will be used for the messages exchanged in accordance with this specification. We further specify that there exists some out-of-band means for the application to indicate to the Bitcoin wallet that it intends to invoke and utilize this protocol, as opposed to some other protocol.
For example, if the underlying communications method was HTTP (), the application could indicate its intention to use this protocol by sending requests to
/v1/createActioninstead of/v1/foobar. In effect, this means that there is no need for JSON fields in this specification denoting the message type, since both parties have already established that they are exchanging messages according to the protocol. Specific communication mechanisms that facilitate this are defined by other standards such as , and .
We specify that the first message exchanged under this protocol originates from the application, and constitutes a request by the application that a Bitcoin transaction be created by the Bitcoin wallet, as specified by the application.
The Transaction Creation Request comprises a JSON object with the following fields, all of which are required:
Output Objects are JSON objects that have the following fields:
This provides the wallet with enough information to create the transaction requested by the application. Here is an example of a simple Transaction Creation Request:
In this example, the application is stipulating that one output should be created, containing 3301 satoshis, and that the output should be locked using a pay-to-public-key-hash () locking script.
The application also provides a simple description that can be used by the wallet when prompting the user for authorization to proceed with the creation of the transaction, or later when showing the transaction in a list.
If the transaction is successfully created by the Bitcoin wallet according to the instructions provided by the application, we specify that the Bitcoin wallet assembles a Transaction Envelope and returns it to the user. We specify that, in addition to the required envelope fields, a txid field is also included in the returned Envelope Object for the convenience of the application.
For added clarity, we denote a table comprising the standard Transaction Envelope fields with our additional txid field:
Here is an example of a simple Transaction Creation Response:
If the Bitcoin wallet is unable to fulfill the Transaction Creation Request for any reason, we specify that it should respond with a JSON-formatted Transaction Creation Error. The fields for the object are specified as follows:
One example of a Transaction Creation Error is given below:
This specification has been implemented into various wallets and applications:
The Babbage MetaNet Client implements , the local HTTP substrate including this specification. Transaction Creation Request objcts are sent to the client, which then returns either a Transaction Creation Response or a Transaction Creation Error, after prompting the user.
Babbage has implemented a ToDo List application that creates Transaction Creation Request objects which are then received and processed by any wallet software running on a user device that implements this specification.
The Ninja Bitcoin wallet, through its getTransactionWithOutputs function, accepts a Transaction Creation Request and returns either a Transaction Creation Response or a Transaction Creation Error.
1: Transaction responses are
Ty Everett ([email protected])
This document introduces a message box architecture and relay interface to enable data exchange between two parties using a authenticated server. This system allows for message exchange and forwarding between parties who might be offline or unable to establish a direct peer-to-peer connection. While the use of a server as a go-between for peer-to-peer exchange is not novel, this system offers enough flexibility to support federation, removing the need for a single central server.
Ty Everett ([email protected])
Brayden Langley ([email protected])
This specification defines how to
Brayden Langley ([email protected])
Ty Everett ([email protected])
positive integer VI = [[VarInt]]
1 - 9 bytes
list of outputs
Transaction Output Structure
qty with variable length per output
nLocktime
if non-zero and sequence numbers are < 0xFFFFFFFF: block height or timestamp when transaction is final
4 bytes
Extended Format transaction Input Structure
qty with variable length per input
Out-counter
positive integer VI = [[VarInt]]
1 - 9 bytes
list of outputs
Transaction Output Structure
qty with variable length per output
nLocktime
if non-zero and sequence numbers are < 0xFFFFFFFF: block height or timestamp when transaction is final
4 bytes
Script
-many bytes
Sequence_no
Used to iterate inputs inside a payment channel. Input is final when nSequence = 0xFFFFFFFF
4 bytes
Script
-many bytes
Sequence_no
Used to iterate inputs inside a payment channel. Input is final when nSequence = 0xFFFFFFFF
4 bytes
Previous TX satoshi output
Output value in satoshis of previous input
4 bytes
Previous TX script length
Non negative integer VI = VarInt
1 - 9 bytes
Previous TX locking script
Script
<script length>-many bytes
Version no
currently 2
4 bytes
In-counter
positive integer VI = [[VarInt]]
1 - 9 bytes
list of inputs
Transaction Input Structure
qty with variable length per input
Version no
currently 2
4 bytes
EF marker
marker for extended format
0000000000EF
In-counter
positive integer VI = [[VarInt]]
1 - 9 bytes
Previous Transaction hash
TXID of the transaction the output was created in
32 bytes
Previous Txout-index
Index of the output (Non negative integer)
4 bytes
Txin-script length
Non negative integer VI = VarInt
1 - 9 bytes
Previous Transaction hash
TXID of the transaction the output was created in
32 bytes
Previous Txout-index
Index of the output (Non negative integer)
4 bytes
Txin-script length
Non negative integer VI = VarInt
1 - 9 bytes
Out-counter
list of inputs
Txin-script / scriptSig
Txin-script / scriptSig
Certificate Field Access Grants (BRC-52): This is about identity verification. The permission allows an app to reveal specific fields from a BRC-52 identity certificate to designated verifiers. It's a crucial step in processes that require identity verification or authentication.
protocolPermissionsspendingAuthorizationbasketAccesscertificateAccesscertificateAccess is an array of objects that represent the different certificate types an app wants access to. Each object here consists of type (indicating the certificate type), fields (an array of strings naming the fields the app wants to reveal), verifierPublicKey (a string in hex format, representing the public key of the verifier), and description.
Full manifest.json Example:
protocolPermissions
Array of Objects
Contains Protocol Permissions requests.
spendingAuthorization
Object
Contains Spending Authorization details.
basketAccess
Array of Objects
Contains Basket Access requests.
certificateAccess
Array of Objects
protocolID
Array
BRC-43 protocol ID array with two elements. First the security level between 0 and 2, followed by the protocol string.
counterparty
String (Hexadecimal)
Public key, 33-byte in hex format. Only required if protocolID security level is 2.
description
String
Justification for the request, less than 50 characters.
amount
Positive Number
Satoshis being authorized for the app to spend.
duration
Positive Number
Time in seconds for the authorization's validity.
description
String
Justification for the request, less than 50 characters.
basket
String
Name of the BRC-46 basket.
description
String
Justification for the request, less than 50 characters.
type
String (base64)
Certificate type field of a BRC-52 identity certificate.
fields
Array of Strings
Array of field names being requested for revelation.
verifierPublicKey
String (Hexadecimal)
Public key of the verifier in 33-byte hex format.
description
String
Contains Certificate Field Access Grant requests.
Justification for the request, less than 50 characters.
description
A present-tense, human-readable description of the action being performed or represented by this transaction.
outputs
An array of Output Objects.
script
A hex-formatted Bitcoin output script, as defined in BRC-14.
satoshis
The number of satoshis that are to be in the output, given as a positive integer.
rawTx
The BRC-12 transaction in hex format
inputs
An object whose keys are the TXIDs of all inputs to this transaction and whose values are BRC-8 envelopes for the respective transactions (the child envelopes are not required to contain the extra txid field), (required unless proof is provided)
mapiResponses
An array of objects where each object comprises a positive endorsement of the transaction from a Bitcoin miner (required unless proof is provided)
proof
A BRC-10 merkle proof for the transaction in JSON format (required unless inputs and mapiResponses are given)
txid
The Bitcoin TXID associated with this transaction
status
This should always be a string comprising "error".
code
A machine-readable error code. Extensions to this standard can define specific error codes and standardize additional fields. Codes are strings, for example "ERR_NOT_SUFFICIENT_FUNDS".
description
All errors must have a human-readable description field that describes the error. This allows the application to represent the error for the user.
BIP: 239
Layer: Applications
Title: Transaction Extended Format (TEF)
Author:
Simon Ordish (@ordishs)
Siggi Oskarsson (@icellan)
Comments-Summary: No comments yet.
Comments-URI: -
Status: Proposal
Type: Standards Track
Created: 2022-11-09{
"babbage": {
"groupPermissions": {
"protocolPermissions": [
{
"protocolID": [2, "Convo"],
"counterparty": "...",
"description": "For encrypted messaging."
}
],
"spendingAuthorization": {
"amount": 10000,
"duration": 3600,
"description": "For in-app purchases."
},
"basketAccess": [
{
"basket": "BRC-46 Gold",
"description": "For in-game items."
}
],
"certificateAccess": [
{
"type": "...",
"fields": ["Name", "DOB"],
"verifierPublicKey": "...",
"description": "For age verification."
}
]
}
}
}{
"protocolID": [2, "Convo"],
"counterparty": "...",
"description": "For encrypted messaging."
}{
"amount": 10000,
"duration": 3600,
"description": "For in-app purchases."
}{
"basket": "BRC-46 Gold",
"description": "For in-game items."
}{
"type": "...",
"fields": ["Name", "DOB"],
"verifierPublicKey": "...",
"description": "For age verification."
}{
"description": "Pay John Galt 3,301 satoshis",
"outputs": [{
"script": "76a914b10f7d6c7fda3285e9b98297428ed814374cbd4088ac",
"satoshis": 3301
}]
}{
"rawTx": "01000000017c36b9ed1c6284079fa9b2111d47e934460d2af631ceda3ffc1bf0587129d1b7020000006b483045022100c655d8c0bfc895c861dda240010c23d5267fd4414894794d3f3591fb7d59fe090220627246ff28e1776c34fd09fbbcfc5bb2efa8090350b35192f1638dd9143ea2a8412102c6848b9988deb921e645fbac47f196e7bec44a6f8612874632240aadc4a35b5bffffffff03e50c0000000000001976a914b10f7d6c7fda3285e9b98297428ed814374cbd4088acc8000000000000001976a914652138f4b89dd615def2c3aa7e13bc1900b5cff588ac1f450000000000001976a91428e5b6157e4a72d8bc8d2374f7acec1388be52f988ac00000000",
"inputs": {
"b7d1297158f01bfc3fdace31f62a0d4634e9471d11b2a99f0784621cedb9367c": {
"proof": {
"index": 16349,
"txOrId": "b7d1297158f01bfc3fdace31f62a0d4634e9471d11b2a99f0784621cedb9367c",
"targetType": "header",
"target": "00c0322ac50d5e7f9cb2f9be53a34f78fe1cdd7029a03eeeb50b780000000000000000002cb6066c3101dddf29a7853051487bada9adeb9807d2ab4360ca47d3c30581138d9a1264eb370d1802029180",
"nodes": [
"95733f96fa3a1f0ba779627d7dc62891f6eee8a13eb0e5a5610bf0d5d1686401",
"ac95afde3cb7d2f639e3a36d07db7b0becc4bbcb393dce51e322adc38f78c5a1",
"4054dae1209fa48797dc459239eb396b6220618c9a8bd93776f7485f14759fb7",
"8f6327bf637241b3e5b029475f3bf5856b263248fa18272e04ec8dae35131351",
"4da5d84ccde803ba4515ee1f8912b49c738bbc145850d1f88885474ff57d1f58",
"ec1e6e5c0f50ac2eb8e493c1c372aedde359cbf706643a6bdfaa3aebf4996f01",
"cedf75aa7314f898121bccdf27eb8b6769262324460a98bf84cdaa6a0379895e",
"246a798ac6e7e3a5893cb24aa26ca92f71309f41200e1fafbad33a138ea709d6",
"a5df0501e2c74677b0201fa2b2a6b9cf4156f8266bbc73767e4a784d0793d557",
"774bdbf5a5992baf1aea1cb80afb68ed93d03aba37b16b57c9fb4cf596e522e6",
"b621a1f5f431843ed450a09c9a09f245290db204ffe2068732b7c278f4fa2605",
"93dd7a58841fa52e8d1ac8e5d4bef852450a716c79a603dd4b85772abd0cc0db",
"57860c6a1f278979fc4540c98f43dc41d64c6f52dffa6aaaf7b07d206e8ca5b5",
"5de27bdb3b0123c2149d3cc8a54cab926ddc50f93639b02be0885ff9c4365fc8",
"5cdd1beb2c59b2dbf9be5790633c0a58ea274e2669f52226343648c1a1ca31cc",
"560490ecae5c1d075dbaf6ee26b61bd758bfbadd09a6cee475156f1b0a3fc9c7"
]
},
"rawTx": "01000000011a348074d8fe477dec48fe3c07b493371c9fa8b5a49d5d0424916a65b65e77be020000006b4830450221009acfd97f48c819d0fe0ae59df2110eb8a7f6defe948bcf556562b4d1f5e7129602202586aa5f2cb767eba3978aa3185f09e8ec745fcb1111296ca42fb384f7355b6c412102466ef576171e0e0684081f2a99b5e6b5786dac80b5d6d64fec3b033628763a4fffffffff0394070000000000001976a914d75cf5dc3ce0767b005b71b2a8705809ba4b815088acc8000000000000001976a914931c8716ddfb85f4ea4bbdad395d27bd6323b57688acea520000000000001976a914dbdbf9c3216c9cf526e18bc9f976018881972fff88ac00000000"
}
},
"mapiResponses": [
{
"payload": "{\"apiVersion\":\"\",\"timestamp\":\"2023-03-17T05:09:51.554Z\",\"txid\":\"737a1e90af9745da3f22ef74bc71a089c5e2a76e9662a0c9fa5b7cf94fe32e75\",\"returnResult\":\"success\",\"resultDescription\":\"\",\"minerId\":\"03ad780153c47df915b3d2e23af727c68facaca4facd5f155bf5018b979b9aeb83\",\"currentHighestBlockHash\":\"00000000000000000138b377cab18dc72cc7ac38d6631949c9694071855bcce8\",\"currentHighestBlockHeight\":783508,\"txSecondMempoolExpiry\":0}",
"publicKey": "03ad780153c47df915b3d2e23af727c68facaca4facd5f155bf5018b979b9aeb83",
"signature": "304402201529238a9cf64b02dad0d4573ef5a0780bddb2fe6d6afdca1473e340a6e1512202204845c0721bdb8b6b8ec0e255f120e5749c373a8e2fe13fed208c3154197846c0"
},
{
"payload": "{\"apiVersion\":\"1.5.0\",\"timestamp\":\"2023-03-17T05:09:51.7263993Z\",\"txid\":\"737a1e90af9745da3f22ef74bc71a089c5e2a76e9662a0c9fa5b7cf94fe32e75\",\"returnResult\":\"success\",\"resultDescription\":\"\",\"minerId\":\"030d1fe5c1b560efe196ba40540ce9017c20daa9504c4c4cec6184fc702d9f274e\",\"currentHighestBlockHash\":\"00000000000000000138b377cab18dc72cc7ac38d6631949c9694071855bcce8\",\"currentHighestBlockHeight\":783508,\"txSecondMempoolExpiry\":0,\"warnings\":[],\"failureRetryable\":false}",
"publicKey": "030d1fe5c1b560efe196ba40540ce9017c20daa9504c4c4cec6184fc702d9f274e",
"signature": "3044022013546dbe4b9f402ae684f3ac51bbdef5ebe5a1b1abd646b04b9a35e94515a29e022041f4b047caa6d49d1fc4ff202646939d91abd661776d75d9e5dee6b2b355645a"
}
],
"txid": "737a1e90af9745da3f22ef74bc71a089c5e2a76e9662a0c9fa5b7cf94fe32e75"
}{
"status": "error",
"code": "ERR_PERMISSION_DENIED",
"description": "You have denied permission for creating this Bitcoin transaction."
}Each leaf is a 32 byte hash
32 bytes
nLeaves is repeated for each height, followed by the corresponding offset and leaf for each.
height
The height of the tree up to a max 64
1 byte
nLeaves
VarInt number of leaves at this height
1-9 bytes
offset
VarInt offset from left hand side within tree
1-9 bytes
0
e86ec5732f55490a73677fe88a37c875cea49f572e4bc822b83fe96093bb008c
3
3b5a16dc41bbed3e58ad2a9017fb8954e7541975e2a4f37343761d96f431b3e5
leaf
The Bitcoin whitepaper reveals that no existing mechanism allows payment exchange over a communication channel. Bitcoin addresses this issue by introducing a distributed timestamp server. However, obstacles such as NAT traversal issues and slow CGA adoption hinder the creation of channels for peer-to-peer general message exchange. The current internet infrastructure is not conducive to reliable peer-to-peer message exchange, lacking a proper mechanism for such exchanges between peers. This standard proposes a solution built on a BRC-31 authenticated server, which enables a simple message box architecture and relay system for peers who may not always be online.
The system involves a sender, a recipient, and a server. The server enforces BRC-31 authentication and identifies all parties (senders and recipients) based on their identity keys. The server offers several interfaces to facilitate message delivery, receipt, and acknowledgment between the sender and recipient through various message boxes.
The server operates over HTTPS at an internet domain name and enforces BRC-31 authentication for all requests. It implements the following three endpoints:
Send a message to a recipient's message box.
message
object
Message object containing all relevant information
message.recipient
string
The recipient's public key
message.messageBox
string
The name of the recipient's message box
Example Request:
status
string
The status of the message sending operation
Example Response:
List messages from the specified message box.
messageBox
string
The name of the message box to retrieve messages from
Example Request:
status
string
The status of the message listing operation
messages
array
Array of message objects
messages[].messageId
integer
Unique identifier for the message
Example Response:
Acknowledge that a message has been received. After acknowledgment, the server deletes a message.
messageIds
array
Array of message IDs to acknowledge
Example Request:
status
string
The status of the message acknowledgment operation
We leave it to higher-level protocols and systems to implement appropriate digital signature schemes and message authenticity checks for their relevant use-cases. Notably, all senders and all recipients use BRC-31 authentication to communicate with the server. The messages exchanged between the parties over the server could form a second-layer BRC-31 channel, between the two users themselves.
BRC-34 and BRC-35 define the mechanism by which multiple servers can discover and route messages to one another, making use of an overlay network. When properly implemented, this enables two parties to exchange messages with one another even when not using the same server.
We leave open the possibility for monetization by future specifications via BRC-41 when server operators relay or synchronize messages. When implemented, BRC-41 acts as a drop-in replacement for BRC-31 and the server operators decide on a price. Participants choose which servers they are willing to pay to interact with.
This messaging system is not intended for long-term storage, but only for transport of messages. It should not be used, for example, to maintain a reliable list of all messages in a messaging application, only to facilitate the delivery of those messages to recipients. Once a recipient receives a message, they should generally store or act upon it in some way and then acknowledge it, removing it from the server.
The message relay architecture has been implemented in the PeerServ API, created by the Babbage Team.
A .well-known/auth endpoint for non-general BRC-103 messages (e.g. handshake, certificate requests).
A mechanism for embedding “general” messages in normal HTTP requests/responses using custom x-bsv-auth headers and an encoded request/response “payload.”
Clear rules for which HTTP headers are included in the signature and which are excluded, ensuring consistent signing and verification on both client and server.
An approach for referencing the remote peer’s identity key and session parameters in standard HTTP requests/responses.
By adopting this scheme, implementers can re-use BRC-103’s cryptographic handshake and selective certificate exchange logic in a typical browser–server or server–server HTTP environment.
BRC-103 [1] defines a peer-to-peer mutual authentication flow but leaves the transport unspecified. This document describes an HTTP layering for that flow. It details:
Endpoints and message paths for BRC-103 handshake messages.
A scheme for “general” messages that carry an entire HTTP request/response inside a single BRC-103 message.
Custom HTTP headers that store BRC-103 parameters (nonces, signatures, identity keys, requested certificates, etc.).
Mechanisms to handle typical web scenarios such as 402 Payment Required or other status codes.
Where BRC-103 gives a general outline (initialRequest, initialResponse, general, certificateRequest, certificateResponse), BRC-104 explains how those messages appear as HTTP requests and HTTP responses.
Mandatory Prerequisite: Implementers MUST implement or rely on BRC-103’s handshake, message types, and signature logic.
This specification extends BRC-103 by mapping each BRC-103 message type onto HTTP requests and responses, describing /.well-known/auth usage, specialized headers (x-bsv-auth-*), and the structured “payload” approach for general messages.
Whenever conflicts arise, BRC-103 is authoritative on handshake semantics and certificate exchange. BRC-104 only clarifies how to wrap those messages in HTTP.
BRC-103 Message: One of initialRequest, initialResponse, certificateRequest, certificateResponse, or general.
Non-General Message: A BRC-103 message of type initialRequest, initialResponse, certificateRequest, or certificateResponse.
General Message: A BRC-103 message of type general, used to transmit an entire HTTP request or response under the authenticated session.
Peer: A user agent (client) or server implementing the protocol.
x-bsv-auth- Headers: A family of custom HTTP headers that store BRC-103 parameters (identity key, nonce, signature, etc.).
x-bsv-payment Header: An optional header used for embedding BSV payment data (e.g. 402 flows).
.well-known/auth Endpoint: The canonical path for sending non-general messages as JSON in a POST body.
Non-General BRC-103 messages (initialRequest, initialResponse, certificateRequest, certificateResponse) are sent as:
HTTP POST to /.well-known/auth, with a JSON-serialized AuthMessage in the request body.
The server processes it, possibly returning an AuthMessage in JSON.
General BRC-103 messages are carried by a normal HTTP request to any path (e.g., /api/v1/orders), but encoded within custom headers plus a raw binary “payload.”
The server intercepts that request, reconstructs the “general” BRC-103 message, verifies signatures, and eventually returns a corresponding “general” BRC-103 response.
This layering effectively merges the BRC-103 handshake and certificate logic with standard HTTP request/response cycles.
MUST define at least one endpoint:
POST /.well-known/auth
Accepts a JSON body representing a BRC-103 AuthMessage (any non-general type).
The server verifies the BRC-103 signature. On success, it MAY respond with another non-general BRC-103 message as JSON (e.g. initialResponse or certificateResponse).
If the handshake is incomplete or invalid, the server responds with an HTTP 4xx.
Other resource endpoints (e.g. /myapp/orders) MAY handle general messages. The request body is an ordinary HTTP request body, while key BRC-103 fields (signature, identityKey, etc.) are placed in custom headers and an encoded “payload.”
BRC-104 introduces the following headers:
x-bsv-auth-version: The BRC-103 version string (e.g. "1.0").
x-bsv-auth-identity-key: The sender’s public identity key (33-byte compressed secp256k1 pubkey, typically hex).
x-bsv-auth-nonce & x-bsv-auth-your-nonce: 32-byte ephemeral nonces used in BRC-103’s handshake.
x-bsv-auth-signature: ECDSA signature in hex, verifying the message.
x-bsv-auth-request-id: A client-generated 32-byte random ID (base64) to correlate request/response pairs in “general” messages.
x-bsv-auth-message-type: (Optional) The BRC-103 message type if returning a certificateRequest or certificateResponse via HTTP.
x-bsv-auth-requested-certificates: (Optional) JSON-encoded RequestedCertificateSet for certificate requests.
x-bsv-auth- headers MUST be excluded from the set of user-signed headers. They are ephemeral parameters or are used for BRC-103 signature itself.
When building a “general” message, the BRC-103 specification requires a signature across certain fields. In BRC-104:
Included in signature:
Request method (GET, POST, etc.).
Request path and query string.
“Whitelisted” headers only:
authorization (client and server)
content-type (client-only)
Any header with prefix x-bsv- except x-bsv-auth-...
Request body bytes.
Excluded:
Non-whitelisted headers (like x-bsv-auth-*, accept)
Cookies, user-agent, etc.
Servers do not sign content-type because certain server middleware tends to modify the content-type header. For example, express appends charset=utf-8 to the content-type resulting in ambiguity over the pre-image being signed.
Clients however do not generally modify the content-type header in requests which allows it to be included in the pre-image being signed.
The order of included headers will be lexicographical to ensure matching preimages on both sides.
Client perspective:
Construct an HTTP request as usual, but store the entire method, path, query, selected headers, and body inside a “payload” field in the BRC-103 general message.
The BRC-103 identity key, version, nonce, etc., go into x-bsv-auth-* headers.
Send the request over standard HTTP.
The server verifies authenticity, processes the request, and returns a BRC-103 “general” message in the response:
The server includes x-bsv-auth-version, x-bsv-auth-signature, x-bsv-auth-identity-key, etc.
The actual response status code, headers, and body are re-encoded as a “payload.”
Server perspective:
On receiving the request, parse the x-bsv-auth-* headers (nonce, signature, identityKey, etc.).
Construct the “general” BRC-103 message from the payload.
If signature checks pass, handle the HTTP request in memory (similar to a normal route).
Instead of sending the result directly to the wire, embed your final status code, headers, and body in a new “general” BRC-103 message.
Write it back in x-bsv-auth-* headers + the binary payload within the same HTTP response.
Non-general BRC-103 messages—like initialRequest, initialResponse, certificateRequest, certificateResponse—MUST be exchanged via:
POST /.well-known/auth, with a JSON body:
The server decodes the AuthMessage, verifies it, and possibly replies with another AuthMessage in JSON form:
On success, the client merges that result into the BRC-103 session state.
If the handshake or signature fails, the server MUST return 401 Unauthorized (or another 4xx suitable code) with a JSON body describing the error.
If the user sets invalid data, the server MAY respond with 400 Bad Request.
When sending a “general” message:
If the path has a value, it must be serialized as UTF-8 bytes with a length prefix.
If the path is empty, it must default to /, serialized as UTF-8 bytes with a length prefix.
If query parameters are present, the entire query string (including the leading ?) must be serialized as UTF-8 bytes with a length prefix.
If no query parameters are present, specify a length of -1 in the payload.
The body is included raw in the BRC-103 payload, but only after a length prefix.
a. If the content-type is application/json:
The JSON object must be stringified and converted to UTF-8 bytes.
If the body is empty, an empty JSON object {} should be stringified and converted to UTF-8 bytes.
b. If the body consists of binary data (e.g. images):
The binary is included directly as bytes.
If the body is empty, specify a length of -1 in the payload.
The same rules apply to the server’s response body in a “general” response message.
BRC-103 specifies that the preimage is constructed by concatenating these fields in a fixed order:
x-bsv-auth-request-id (32 bytes, base64-decoded).
Request method: length + UTF-8 bytes.
Path: length + UTF-8 bytes (use / if the path is empty).
Query string (including ?): length + UTF-8 bytes (or -1 if none).
Included headers: count them in the order they are transmitted. For each header:
VarInt of key length, followed by key bytes
VarInt of value length, followed by value bytes
Body: length + body bytes (or -1 if none).
Finally, sign the preimage using the identity key. The server reconstructs the same preimage from the request data and verifies the signature contained in x-bsv-auth-signature.
In response to client request the server creates a BRC-103 payload as follows:
x-bsv-auth-request-id (echoing back the client's requestId as seen on line 174).
Response status (varInt)
Included headers count, given in order of transmittal over the wire. For each header:
VarInt of key length, key bytes
VarInt of value length, value bytes
Body length + body bytes (or -1 if none).
A reference “client” does the following:
When sending a non-general message (e.g. handshake, certificate request), it:
Performs POST /.well-known/auth with Content-Type: application/json.
Embeds the entire AuthMessage (BRC-103 structure) in JSON.
When sending a general message:
Serializes method, path, query, whitelisted headers, and body into a BRC-103 payload.
Supplies x-bsv-auth-version, x-bsv-auth-identity-key, x-bsv-auth-nonce, x-bsv-auth-your-nonce (if applicable), x-bsv-auth-signature, and x-bsv-auth-request-id in the request headers.
Intercept POST /.well-known/auth requests, parse them as JSON, treat them as BRC-103 “non-general.”
For other paths, it checks for x-bsv-auth-request-id to see if it’s a “general” message.
It re-routes the normal Express request logic so that instead of returning data directly, the server packs the data into a BRC-103 “general” message and responds accordingly.
This approach ensures a typical server can remain mostly unmodified, while the middleware handles the signature verification and re-encoding of the final response.
Replay Attacks: The nonce fields in BRC-103 ensure freshness. Servers MUST reject repeated nonces.
Header Injection: Only a fixed set of headers is signed. If malicious headers are injected, they are outside the signature scope. Implementers should sanitize or ignore unknown headers.
Transport Encryption: BRC-104 does not replace TLS. Implementers SHOULD still use HTTPS to hide private or sensitive information from eavesdroppers.
Certificate Revocation: If identity certificates are used, the logic for checking revocationOutpoints remains as in BRC-103.
BRC-103: Peer-to-Peer Mutual Authentication and Certificate Exchange Protocol (provides handshake and message definitions).
The motivation for this standard interface is to provide a common way for applications to connect with Bitcoin wallets. Currently, many Bitcoin wallets provide their own APIs, which makes it difficult for applications to support multiple wallets. The Bitcoin Wallet HTTP Interface standardizes the way that applications can interact with wallets, enabling them to be more easily integrated into various applications. This interface is designed to be flexible enough to support a range of wallets and use cases, while also providing a secure and standardized method of communication.
We define a standard HTTP substrate by which wallets can provide a communication channel to applications for the necessary tasks such as creating transactions, creating certificates, signature verification, and more.
All implementors of the HTTP substrate should conform to the following standard for the wallet HTTP API that allows applications to communicate in a predefined and standard way.
We define the wallet HTTP server to support communication over localhost on port 3301. New API versions can be specified as needed using the standard of v1, v2, etc.
Example Encrypt Request URL: http://localhost:3301/v1/encrypt
We define standardized HTTP requests that applications can use to communicate with wallets for performing various tasks.
Transaction Creation
POST
createAction
No Params
JSON
JSON
Encryption
Implementers of applications and wallets will need to create and process HTTP requests in the manner described, according to the various fields and properties as required by BRC-56, and in a manner consistent with the reference implementation, which is the Babbage SDK's HTTP substrate functionality.
Deggen ([email protected]) Damian Orzepowski ([email protected]) Wojciech Regulski ([email protected]) Arkadiusz Osowski ([email protected])
We propose a binary format for sending Transactions between peers to allow Simple Payment Verification (SPV). The format is optimized for minimal bandwidth while maintaining data required to independently validate the transaction in full.
Assumption: Every user has an independent source of Block Headers indexed by Merkle Root.
The simplest form is two transactions, one confirmed in a block which has a corresponding Merkle path and includes an output which is being spent in the second. The second is a newly signed transaction which constitutes the actual payment.
In cases where one or more inputs are not yet mined, each ancestral transaction is included. We step back through the Transaction DAG until every input has a corresponding parent transaction with a Merkle path.
This format is aligned with Dr. Craig Wright's explanation of SPV from this . He makes reference to a new paradigm, this format is an attempt to bring forth that paradigm.
Users can adopt this format to transmit the data required to independently verify that a transaction is valid and is spending a real utxo from an existing block. The control mechanism to ensure no previous spend of the utxo is the economics and law. The format is intended for micropayments, so the risk is low. It is also the case that there is a cost to faking the data, since the attacker would really have to have previously owned an actual utxo. Any malfeasant would be signing incriminating evidence and sending it directly to the plaintiff if they were to defraud someone using this format. Without Merkle paths the data would be easier to fake, and there is no skin in the game required since the data could be randomly generated. Considering all these factors, the validation process is detailed in a later section.
This BRC is licensed under the Open BSV license.
Simplified Payment Verification formats for transmitting transactions between peers has yet to see wide adoption. This proposal advocates for complete ecosystem adoption of the principles of SPV; acknowledges that the system is secured by economic incentives, and law; and lays out a binary format for transmitting the data between parties optimizing for minimal bandwidth.
Three prior formats should be mentioned:
Extended Format incorporates the utxo script for script evaluation and satoshis for checking amounts. This format would still work when sending to nodes as they have a utxo lookup which is indexed by a hash of the extended data, meaning that invalid EF data would be detected immediately.
which was created for use within the , this uses an array of rawtxs, Merkle proofs, and Mapi responses to transport the data required for SPV.
Everett-style Transaction Envelopes uses a recursive JSON format which is otherwise similar to the above format originally designed for use within . One of the ideas which this proposal highlights is that the target of merkle proofs could be height rather than blockhash or root, to save on bytes. Thinking along these lines, we propose that no target be provided at all, and the root simply be calculated at the receieving end as needed. Using the root to look up headers also avoids any implementation complexity associated with competing blocks. For example, when a competing block encapsulates the transactions it may not have the same height, if all transactions are being stored with paths at a height rather than blockhash or merkle root (which are unique to specific versions of a block) then updating a set of the transactions with merkle paths from the new block will be difficult to sort out. If they're indexed by blockhash then it would be trivial to set them all as "in need of updated paths" without the need for disambiguation.
Mapi is to be deprecated in the near future, so any new recommended formats should not include Mapi Responses as part of the solution.
The array of rawtxs within the Tx Ancestors spec makes some sense in that there are strange cases where the same rawtx has two outputs which are spent in different transactions, both within the ancestry of the tx we are validating. You don't want to have embedded a copy of the same proof twice, hence a hash map would make more sense than just including the merkle path bytes in the tx itself. Everett-style Transaction Envelope deals with this well even given its recursive object design. Txids are used as pointers to existing transactions when complex dependency graphs occur. This proposal uses the same thinking, but for a binary format lists are used rather than recursive objects.
The ordering of data has streaming validation in mind - an idea raised in EF - such that a receiver can start processing the validation immediately on receipt of the first few bytes, and any later bytes rely on previous bytes for their validation to even begin.
Merkle Paths
Oldest Tx Anchored by Path
Newer Txs depending on Oldest parent
Newest Tx
As soon as the Merkle Paths are receieved we can calculate the roots and lookup their blockheaders. If they're not valid then validation can stop there - rejected. Then we look at the oldest ancestor - if it's valid then its children can be validated, and so on until we reach the most recent tx.
BEEF combines thinking from several formats into one binary stream, prefixed with a very specific version number for disambiguation.
Raw Transaction Format:
BSV Universal Merkle Path (BUMP):
The encoding version number 4022206465 is chosen such that when seen in hex encoded as 4 bytes little endian it reads: 0100BEEF. This is to allow multiple versions to be defined in future while keeping the data minimal and leaving an obvious sequence which developers can eyeball. Often Bitcoin developers will see a sequence 0100000000... for rawtx. When they instead see 0100BEEF... they will know this data is BEEF format when debugging and so on. If there are future improvements then the next version would be 0200BEEF for example, this marker will remain "BEEF" for tens of thousands of versions until we reach 4022271999 which is obviously much more than would ever be required.
Order is important - we must ensure that we end with the tx being evaluated, and its inputs are above, and their inputs are above that. is a well known solution to the problem in Graph Theory. Running this on the transaction DAG subset is recommended for complex transaction chains where order is not clear. Example below to demonstrate with extraneous data removed for clarity.
We parse the BUMPs storing each in an array so that we can address them by index later.
We parse each transaction.
RawTx bytes double sha256 to get the txid.
Store in hashmap from txid => parsedTx
The format will be built into BUX to begin real world testing. Thereafter common libraries such as and for easy incorporation into existing stacks.
Ty Everett ([email protected])
Brayden Langley ([email protected])
This document specifies an HTTP-based micropayment framework that extends (mutual authentication and certificate exchange) and (HTTP transport for BRC-103). It defines how a server can advertise payment requirements for a given HTTP request and how a client can respond with a BSV transaction fulfilling these requirements. By leveraging:
HTTP 402 Payment Required responses
Nonce-based derivation prefixes ()
BRC-103 Identity keys
this specification enables an authenticated, verifiable mechanism to monetize any web service with micropayments. The server could even cryptographically bind each payment to the mutual-authenticated session, ensuring that the payment correlates uniquely to the requested action.
Current HTTP-based APIs often rely on subscription models or external billing/invoicing solutions. This approach can be cumbersome for fine-grained (micropayment-level) usage-based billing. By contrast, BSV Blockchain capabilities allow on-chain micropayments. However, bridging these seamlessly into existing HTTP flows requires:
Standardized BSV wallet functionality ()
A standard approach to advertise payment requirements (HTTP 402)
A mechanism to tie a user’s payment to their session identity (via BRC-103 identity keys)
Nonce-based replay protection (ensuring the same payment is not reused maliciously)
BRC-105 addresses these needs by specifying how a BRC-103–authenticated server can request and verify a BSV payment from the client within a standard HTTP request–response cycle. Clients can use specialized 402-handling agents (e.g. AuthFetch or custom code) to automatically respond to 402 Payment Required, building and broadcasting a transaction on the user’s behalf.
Payer (or Client): The party sending the HTTP request, who must pay the server if required. They are already mutually authenticated to the server via BRC-103/104.
Payee (or Server): The HTTP endpoint receiving the request, which may respond with 402 Payment Required, specifying the needed payment details.
Derivation Prefix: A random nonce string used to derive or reference a unique public key / payment output. Defined in .
BRC-103 defines mutual authentication and certificate exchange but does not define how to handle micropayments. BRC-104 applies BRC-103 to HTTP, detailing how to wrap BRC-103 handshake data in HTTP messages.
BRC-105 sits on top of BRC-104, specifying a payment layer for monetizing HTTP endpoints. It reuses:
identityKey: The user’s 33-byte compressed public key from the BRC-103 session.
Nonce generation: The server can generate derivation prefixes that are cryptographically bound to its identity key, used by the parties to derive an ephemeral key pair for the payment.
HTTP 402 status code**: A recognized but typically unused code, repurposed here for BSV micropayments.
Client Authenticates
The client (payer) establishes a BRC-103 session with the server (payee) using BRC-104. This ensures both parties have authenticated and exchanged any necessary certificates to establish identity.
Server Determines Price
x-bsv-payment-version
The current supported version is 1.0.
If a client does not explicitly support the server's indicated version, the client MUST NOT submit a payment.
A mutually authenticated request arrives (client has previously performed BRC-103 handshake). The request may or may not contain x-bsv-payment data:
If no payment is needed (the server’s calculateRequestPrice returns 0), proceed normally.
If the request has a non-zero price and the client did not supply x-bsv-payment, or the payment is invalid, the server responds with 402 Payment Required.
When the server determines a payment is needed but not yet provided or invalid:
HTTP Status: 402 Payment Required.
Headers:
x-bsv-payment-version: 1.0
After receiving this, the client is expected to construct and broadcast a payment transaction referencing this prefix.
Subsequent request includes the header x-bsv-payment, JSON:
derivationPrefix: Must match what the server gave in the 402 response.
transaction: A fully signed transaction paying an output script derived from the prefix, acceptable on the BSV network.
Upon receiving x-bsv-payment, the server:
Ensures the prefix is the same as previously advertised (and not used before).
Validates the transaction using wallet.internalizeAction() or similar. Steps might include:
Ensuring the output script pays to the correct derivation from derivationPrefix.
If validated, the server MUST proceed to handle the request logic, and is obliged to provide the requested service, according to what the client has paid for.
If payment is successful, the server responds with a normal (e.g., 200 OK) plus any business logic response body. The server may add:
x-bsv-payment-satoshis-paid: <value>
Possibly additional data about the transaction or usage.
Auth Check (BRC-103/104)
If not authenticated, respond 401 Unauthorized.
Calculate Price
Send Request
If server returns 402, parse x-bsv-payment-version + x-bsv-payment-satoshis-required + x-bsv-payment-derivation-prefix.
Construct Payment
Replay Attacks
The server and its wallet must ensure each derivationPrefix can only be used once. Typically, this prefix is stored in a wallet database or in-memory structure. Once a valid transaction is processed, subsequent attempts to reuse the prefix or submit the same transaction for multiple requests must fail.
Man-in-the-Middle
Advanced Pricing: BRC-105 does not define how to calculate price. Implementation can adopt static or dynamic logic.
Refund Handling: Out of scope for this version. The server might store overpayments or offer on-chain refunds via additional flows.
Multi-Output Payments: Some use cases might require paying multiple outputs. The basic 402 flow still applies but must be extended with more sophisticated transaction logic.
: Peer-to-Peer Mutual Authentication and Certificate Exchange Protocol
: HTTP Transport for BRC-103
: Derivation-based Payment Protocol
BRC-105 introduces a standard approach to HTTP-based micropayment flows, leveraging the authentication and transport models defined in BRC-103 and BRC-104. By responding with 402 Payment Required and specifying a nonce-based derivation, the server ensures each payment is uniquely bound to the mutual-authenticated session and request. This framework enables straightforward pay-per-use APIs and extends the BSV ecosystem with a robust, standardized mechanism for monetizing web services at scale.
Ty Everett ([email protected])
This document specifies a peer-to-peer protocol for mutual authentication and signed data exchange. The protocol uses certificates, nonce-based challenges, and digital signatures to protect the integrity and authenticity of messages, enabling:
Selective field disclosure of certificates without revealing unnecessary information.
Highly extensible message types, suitable for a variety of use cases such as transaction coordination, identity verification, and arbitrary data sharing.
Interchangeable transport layers, which can be HTTP, NFC, WebSockets, or any mechanism supporting bidirectional message exchange.
The specification provides a standard approach for issuance, storage, retrieval, and verification of identity certificates, as well as the end-to-end handshake and messaging protocols between peers.
Secure interactions among different wallets and services are critical. These interactions typically require:
Proving identity in a privacy-preserving manner, possibly revealing only some data fields (e.g. “I am over 18” without revealing full date of birth).
Ensuring that both parties are authenticated to each other (mutual authentication), reducing the risk of man-in-the-middle or impersonation attacks.
A standard protocol for mutual authentication and certificate exchange addresses these needs and fosters interoperability between applications, wallets, and vendors.
Wallet: An interface or application managing the user’s keys, capable of signing, encrypting, and decrypting data in conformance with .
Certificate: A data structure that attests to certain user attributes, signed by a certifier.
Master Certificate: A special form of certificate that has been encrypted field-by-field, each with a unique symmetric key, stored in an internal keyring.
Certificates are stored and transmitted in a binary format to optimize size and parsing consistency. We specify each field in table form, together with how it is conventionally represented when not in binary. The binary format is always used for signing and verification, but the conventions are useful for human readability. A certificate has the following primary fields:
Important: Each certificate field is encrypted with a field-specific key. The encrypted version is signed.
A Master Certificate includes a master keyring. Each field in the certificate is encrypted with a unique symmetric key. Those keys, in turn, are encrypted for the certificate holder (subject). Hence, a master certificate is able to decrypt any of its own fields locally but can selectively re-encrypt only some fields for a verifier.
A Verifiable Certificate is derived from a master certificate. It still contains all fields in ciphertext form (so that signatures can be verified) but also includes a “verifier-specific keyring.” For fields the subject wishes to disclose, the subject decrypts the corresponding symmetric key from the master keyring and re-encrypts it for the verifier. Consequently, the verifier can decrypt only those chosen fields.
Master Keyring Generation
For each field f, create a random symmetric key K_f.
Encrypt the field’s plaintext using K_f, store as Fields[f] = E(K_f, fPlaintext).
Certificate Creation
User (subject) or certifier forms certificate fields (including Type, SerialNumber, etc.).
Each field is encrypted.
The certifier signs the certificate data.
Certificate Verification
A party verifying a certificate reassembles the binary structure.
Checks the signature against the “signature preimage” (all primary certificate fields except the signature itself, and not including their verifier keyring).
Considers the RevocationOutpoint to confirm the certificate is not on-chain revoked (if it is not 36 zero bytes).
Selective Disclosure
The subject issues a Verifiable Certificate containing:
Original field ciphertext.
Re-encrypted field keys for fields to reveal.
The certifier’s signature.
The verifier can decrypt each revealed field’s ciphertext with the provided key, verifying the signature across the entire certificate to ensure authenticity.
Each message exchanged between peers contains:
Version: The protocol version string (e.g. “0.1”).
MessageType: One of: initialRequest, initialResponse, certificateRequest, certificateResponse, general.
The protocol does not mandate a specific transport. Any medium that can:
Deliver messages in a reliable or semi-reliable manner, and
Allow receiving peer messages,
can be used. HTTP requests, WebSockets, Bluetooth, NFC, or local function calls are all valid.
A peer typically maintains a session record containing:
isAuthenticated: Whether the peer is recognized as fully authenticated.
sessionNonce: A locally generated random 256-bit value.
peerNonce: The peer’s random 256-bit value.
peerIdentityKey: The peer’s public identity key.
A session is created or updated whenever a handshake starts or completes. Nonces and signatures ensure that replaying old messages fails.
The handshake typically has two messages in the simplest form: an initialRequest and an initialResponse.
initialRequest
Sender (A) generates a random nonce (A_Nonce).
A sends initialRequest containing:
Each party uses a cryptographically secure random generator for the nonce (e.g., 32 bytes).
The verifying side must ensure the nonce is matched or “echoed” in subsequent messages.
Optionally, one may use an HMAC-based approach to bind nonces to their key, avoiding the need to keep track of all nonces that they created.
Signature ensures authenticity. The signing steps:
Collect relevant data from the message. For instance, a general message might sign the raw payload array plus ephemeral fields such as (requestNonce + peerNonce) to bind the message to the session.
Use the BKDS-based BRC-100 signature creation and verification mechanisms.
Place the resulting signature in the message’s signature field.
Verification:
Recompute the same message preimage.
Verify the signature with the alleged identityKey.
If valid, accept the message. Otherwise, reject or treat as an error.
After or during handshake, a peer may request:
certificateRequest: “Please provide certificate(s) of type(s) X, issued by Y (or Z).”
certificateResponse: Peer responds by attaching a list of verifiable certificates that match the requested type and certifier.
Request:
Contains a RequestedCertificates structure:
certifiers: array of public keys representing permissible certifiers.
types: a dictionary of certificateTypeID -> array of fields requested.
Response:
Contains an array of VerifiableCertificates, each possibly containing:
Encrypted fields.
Re-encrypted keys for the requester.
The certifier’s signature.
Once both sides have completed the handshake (i.e. set isAuthenticated=true in their session records), they can exchange arbitrary data:
messageType = "general"
payload can be any binary or text data encoded consistently.
The message includes a fresh nonce and references the peer’s nonce for ephemeral binding to the session.
Replay Attacks:
Nonces must be unique and used once. The receiver ensures that the same nonce is not accepted more than once.
Man-in-the-Middle:
The handshake uses mutual signature verification. If a middle party tries to modify data, the signature verification fails.
Data Types:
Nonces are 32 bytes.
Public keys are 33 bytes in compressed DER format.
Certificate fields may vary in size, so a length-prefix technique (varint) is standard.
This protocol is heavily inspired by existing cryptographic handshake approaches (SSH, TLS) and identity-certificate systems (X.509), adapted for a BSV-based environment. Contributors within the ecosystem have refined these ideas to align with BKDS, peer-to-peer exchange, selective disclosure, and other needs.
Ty Everett ([email protected])
Cross-document messaging enables web-based applications to communicate with one another in a secure manner, without allowing the two applications to manipulate each other's DOM trees. It defines a mechanism by which messages can be sent and received, with browser-based attestation of the origin of each message. We define the framework and conventions for operating a wallet over XDM, enabling a parent page that runs a wallet to communicate with one or multiple child pages that run applications.
The Direct Payment Protocol (DPP) is a comprehensive standard for invoice-based peer-to-peer payments on the Bitcoin SV network, encompassing the evolution of BIP70 through BIP270 and its related invoicing standards, including BIP271, BIP272, BIP273, BIP274, BIP275, and BIP282. The DPP harmonizes extensions to these standards, allowing for various payment options and combinations, while maintaining a well-maintained and up-to-date source of information for developers and industry players. The protocol facilitates communication between a payment host (e.g., merchant, payment processor, recipient's wallet) and sender (e.g., customer, acquaintance, IoT device) to enhance customer experience, simplify wallet infrastructure, enable advanced wallet features, and improve wallet security against payment process attacks.
020101cd73c0c6bb645581816fa960fd2f1636062fcbf23cb57981074ab8d708a76e3b02003470d882cf556a4b943639eba15dc795dffdbebdc98b9a98e3637fda96e3811e01c58e40f22b9e9fcd05a09689a9b19e6e62dbfd3335c5253d09a7a7cd755d9a3c04008c00bb9360e93fb822c84b2e579fa4ce75c8378ae87f67730a49552f73c56ee801da256f78ae0ad74bbf539662cdb9122aa02ba9a9d883f1d52468d96290515adb02b4c8d919190a090e77b73ffcd52b85babaaeeb62da000473102aca7f070facef03e5b331f4961d764373f3a4e2751954e75489fb17902aad583eedbb41dc165a3b02 // height = 2
01 // nLeafs at this height VarInt
// ----------------------
01 // offset VarInt
cd73c0c6bb645581816fa960fd2f1636062fcbf23cb57981074ab8d708a76e3b // 32 byte hash
// ----------------------
// implied end of leaves at this height
// height of next leaves is therefore 1
02 // nLeafs at this height VarInt
// ----------------------
00 // offset VarInt
3470d882cf556a4b943639eba15dc795dffdbebdc98b9a98e3637fda96e3811e // 32 byte hash
// ----------------------
01 // offset VarInt
c58e40f22b9e9fcd05a09689a9b19e6e62dbfd3335c5253d09a7a7cd755d9a3c // 32 byte hash
// ----------------------
// implied end of leaves at this height
// height of next leaves is therefore 0
04 // nLeafs at this height VarInt
// ----------------------
00 // offset VarInt
8c00bb9360e93fb822c84b2e579fa4ce75c8378ae87f67730a49552f73c56ee8 // 32 byte hash (this is the txid at index 0)
// ----------------------
01 // offset VarInt
da256f78ae0ad74bbf539662cdb9122aa02ba9a9d883f1d52468d96290515adb // 32 byte hash
// ----------------------
02 // offset VarInt
b4c8d919190a090e77b73ffcd52b85babaaeeb62da000473102aca7f070facef // 32 byte hash
// ----------------------
03 // offset VarInt
e5b331f4961d764373f3a4e2751954e75489fb17902aad583eedbb41dc165a3b // 32 byte hash (this is the txid at index 3)
// ----------------------
// implied end of data because new height would be -1const { Br } = require('openspv')
const reader = new Br()
reader.buf = Buffer.from('020101cd73c0c6bb645581816fa960fd2f1636062fcbf23cb57981074ab8d708a76e3b02003470d882cf556a4b943639eba15dc795dffdbebdc98b9a98e3637fda96e3811e01c58e40f22b9e9fcd05a09689a9b19e6e62dbfd3335c5253d09a7a7cd755d9a3c04008c00bb9360e93fb822c84b2e579fa4ce75c8378ae87f67730a49552f73c56ee801da256f78ae0ad74bbf539662cdb9122aa02ba9a9d883f1d52468d96290515adb02b4c8d919190a090e77b73ffcd52b85babaaeeb62da000473102aca7f070facef03e5b331f4961d764373f3a4e2751954e75489fb17902aad583eedbb41dc165a3b', 'hex')
let maxHeight = parseInt(reader.read(1).toString('hex'), 16)
let compoundPath = Array(maxHeight + 1).fill(0).map(() => ({}))
let height = maxHeight
let x = 0
let nLeavesAtThisHeight = reader.readVarIntNum()
let startOfNextHeight = nLeavesAtThisHeight
while (height >= 0) {
offset = reader.readVarIntNum()
hash = reader.read(32).reverse().toString('hex')
compoundPath[height][hash] = offset
x++
if (x == startOfNextHeight) {
height--
if (height < 0) break
nLeavesAtThisHeight = reader.readVarIntNum()
startOfNextHeight = nLeavesAtThisHeight + x
}
}
console.log({ compoundPath })[ // index within the outer array corresponds to the height
{ // within each height there must be one or more hashes with their corresponding offsets { [hash]: offset }
"e86ec5732f55490a73677fe88a37c875cea49f572e4bc822b83fe96093bb008c": 0, // txid at index 0
"db5a519062d96824d5f183d8a9a92ba02a12b9cd629653bf4bd70aae786f25da": 1,
"efac0f077fca2a10730400da62ebaebaba852bd5fc3fb7770e090a1919d9c8b4": 2,
"3b5a16dc41bbed3e58ad2a9017fb8954e7541975e2a4f37343761d96f431b3e5": 3 // txid at index 3
},
{
"1e81e396da7f63e3989a8bc9bdbefddf95c75da1eb3936944b6a55cf82d87034": 0,
"3c9a5d75cda7a7093d25c53533fddb626e9eb1a98996a005cd9f9e2bf2408ec5": 1
},
{
"3b6ea708d7b84a078179b53cf2cb2f0636162ffd60a96f81815564bbc6c073cd": 1
}
]
// let's say we want to derive the path for txid 3b5a16dc41bbed3e58ad2a9017fb8954e7541975e2a4f37343761d96f431b3e5
// first we determine from the 0th array in the compound merkle path that the block index associated is 3.
const example = {
index: 3,
path: []
}
compoundPath.map((leaves, height) => {
const indexOffset = example.index >> height ^ 1
for (const hash in leaves) {
if (leaves[hash] === indexOffset) {
example.path.push(hash)
return true
}
}
return Error(`We do not have a hash for this index at height: ${height}`)
}){
index: 3,
path: [
'3b6ea708d7b84a078179b53cf2cb2f0636162ffd60a96f81815564bbc6c073cd',
'1e81e396da7f63e3989a8bc9bdbefddf95c75da1eb3936944b6a55cf82d87034',
'efac0f077fca2a10730400da62ebaebaba852bd5fc3fb7770e090a1919d9c8b4'
]
}// pseudocode
function merkleProof(txid, index, path) {
try {
const root = deriveRootFromPath(txid, index, path)
const blockHeader = await lookupHeaderByRoot(root)
if (blockHeader.state === 'LONGEST_CHAIN') return true
else return false
} catch (error) {
console.log({ error })
return false
}
}{
"message": {
"recipient": "028d37b941208cd6b8a4c28288eda5f2f16c2b3ab0fcb6d13c18b47fe37b971fc1",
"messageBox": "payment_inbox",
"body": "hello"
}
}{
"status": "success"
}{
"messageBox": "payment_inbox"
}{
"status": "success",
"messages": [
{
"messageId": 3301,
"body": "hello",
"sender": "028d37b941208cd6b8a4c28288eda5f2f16c2b3ab0fcb6d13c18b47fe37b971fc1"
}
]
}{
"messageIds": [
3301
]
}{
"status": "success"
}{
"version": "1.0",
"messageType": "initialRequest",
"identityKey": "...",
"initialNonce": "...",
"signature": "...",
...
}{
"version": "1.0",
"messageType": "initialResponse",
"identityKey": "...",
"nonce": "...",
"yourNonce": "...",
"signature": "...",
...
}Host URL: http://localhost
Standard Port: 3301
Versioning Standard: v1, v2, etcconst httpResult = await makeHttpRequest(
'http://localhost:3301/v1/<routeName>',
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
<requestBody>
})
}
)On receiving the server’s HTTP response, decodes those same fields from the response headers, building a BRC-103 message that includes the returned status code, headers, and body as a “payload.”
Peer: A party participating in communication, using the protocol to exchange messages.
Session: The state tracking an authenticated channel between two peers.
Nonce: A random value used once within a cryptographic exchange to ensure freshness and avoid replay attacks.
Signature: A digital signature produced by the user’s or certifier’s private key.
Transport: The underlying communication medium that relays messages between peers.
RevocationOutpoint
32-byte TXID + varint output idx
On-chain reference to track revocation (if spent, revoked).
Fields
Varint count + repeated pairs
Pairs of: <fieldNameLength, fieldName, fieldValueLength, fieldValue>
Signature
Varint length + bytes
Certifier’s signature over all fields (excluding itself).
After all fields are encrypted, the certifier signs the assembled certificate, comprising all encrypted fields.
The certifier can then encrypt the key K_f for the subject’s own identity: EncryptedKey_f -> E_subject(K_f), the subject can store in the master keyring.
Disclosure to a Verifier
When selectively disclosing field f, the subject decrypts EncryptedKey_f, obtains K_f, and re-encrypts K_f with the verifier’s identity key, producing K_f_for_verifier.
The subject includes K_f_for_verifier in the verifier’s keyring for that verifiable certificate.
Nonce Fields: For challenge–response patterns (e.g. yourNonce, initialNonce).
Signature: A digital signature by the sender’s private key over the relevant content.
Optional Data:
Certificates: A list of verifiable certificates.
RequestedCertificates: A set specifying certifiers and certificate types with fields.
Payload: Arbitrary data included in general messages.
messageType = "initialRequest"identityKey = A_publicKey
initialNonce = A_Nonce
Optionally, a requestedCertificates set if A wants B’s certificates.
initialResponse
Receiver (B) verifies the request, creates its own random nonce (B_Nonce).
B sends initialResponse containing:
messageType = "initialResponse"
identityKey = B_publicKey
initialNonce = B_Nonce (its own newly created nonce)
yourNonce = A_Nonce (echoing A’s nonce)
Optionally, any certificates B wants to share right away.
A signature verifying B truly created this message.
When A receives this initialResponse, it verifies B’s authenticity via the signature over (A_Nonce + B_Nonce), marking the session as authenticated.
The signature covers the payload plus nonces.
Certificate Revocation:
The revocationOutpoint can be polled or monitored in the ledger to confirm it has not been spent. A spent outpoint implies the certificate is revoked Non-zero outpoints must be checked.
Selective Disclosure:
Properly encrypt fields with randomly derived keys.
Re-encrypt those keys only for intended verifiers.
Privilege Escalation:
Carefully manage session state to ensure a partially authenticated session does not gain privileges.
Transport Security:
Although each message is authenticated, transport-level encryption (e.g. TLS) can still be beneficial, especially to hide message lengths or frequencies.
This protocol does not encrypt any data by itself.
Storage:
A wallet may store certificates in a local database.
A session manager maps sessionNonce and peerIdentityKey to a single in-memory record.
Type
32-byte (base64) string
Certificate category identifier (e.g., unique type associated with “social handles”).
SerialNumber
32-byte (base64) string
Uniquely identifies this certificate instance.
Subject
33-byte compressed pubkey (hex)
The wallet’s (subject’s) public key.
Certifier
33-byte compressed pubkey (hex)
The certifier's public identity key.
POST
encrypt
BRC-56 defined
Binary or String
Binary
BRC-2 Decryption
POST
decrypt
BRC-56 defined
Binary or String
Binary
BRC-3 Signature Creation
POST
createSignature
No Params
JSON
Binary
BRC-3 Signature Verification
POST
verifySignature
BRC-56 defined
Binary or String
JSON
BRC-53 Certificate Creation
POST
createCertificate
No Params
JSON
JSON
BRC-53 Certificate Verification
POST
proveCertificate
No Params
JSON
JSON
BRC-56 HMAC Creation
POST
createHmac
BRC-56 defined
Binary or String
Binary
BRC-56 HMAC Verification
POST
verifyHmac
BRC-56 defined
Binary or String
Boolean
BRC-56 Public Key Derivation
GET
getPublicKey
BRC-56 defined
None
JSON
BRC-56 Certificate List
POST
findCertificates
No Params
JSON
JSON
BRC-56 Version Request
GET
getVersion
No Params
None
String
BRC-56 Network Request
POST
getNetwork
No Params
None
String
BRC-56 Authentication Request
GET
isAuthenticated
No Params
None
JSON
BRC-56 Async Auth Request
POST
waitForAuthentication
No Params
JSON
JSON
message.body
string
The content of the message
messages[].body
string
The content of the message
messages[].sender
string
The sender's public key

VarInt number of transactions which follow
1-9 bytes
Raw Transaction
RawTx bytes as in standard format
many bytes
Has BUMP
01 if so, followed by the BUMP index; 00 if not, followed by nothing.
1 byte
BUMP index
VarInt index number - indicating the BUMP to which the prior tx belongs if there is one.
1-9 bytes
If there is a Merkle path after the tx then we lookup the BUMP using the BUMP index number.
Lookup the txid within level 0 leaves of the BUMP to get the index of the txid within a block.
Calculate the Merkle root with the index, txid, and BUMP data.
Add the merkle root to an array which will be used in a request to our local header service once all transactions have been parsed.
Mark the tx as valid or not as soon as the header service responds.
Otherwise we run local validation on the transaction, stopping at the soonest failure.
Check the txid exists in memory, and is marked as valid.
Check that all scripts evaluate to TRUE.
Check that the sum of satoshis in > satoshis out + fee
Mark the tx as valid.
We make a request to the local header service as soon as we have all merkle roots calculated.
This involves sending a list of Merkle roots to the Pulse service which will validate that the merkle roots provided are all part of headers within the longest chain.
If any of the roots are not part of the longest chain, the response is negative and the whole validation has failed.
We await the final transaction being marked as valid since it depends on all other processes.
Version no
Version number starts at 4022206465, encoded Uint32LE => 0100BEEF
4 bytes
nBUMPs
VarInt number of BSV Unified Merkle Paths which follow
1-9 bytes
BUMP data
All of the BUMPs required to prove inclusion of inputs in longest chain of blocks BRC-74
many bytes x nBUMPs
nTransactions
Derivation Suffix: An additional, output/script-level field included by the client with the payment transaction that is used by the payee's wallet for verification. Also defined in BRC-29.
x-bsv-payment header: A custom header carrying JSON data, including the transaction references and derivation prefixes/suffixes.
internalizeAction(): A function defined by the payee’s wallet or server that finalizes the transaction acceptance logic (e.g., verifying the correct output script, ensuring the derivation prefix is valid, confirming the transaction is not a duplicate, etc.).
402 Payment Required (if fee > 0 and no valid payment provided)
If the client has not included a valid x-bsv-payment header, the server responds with HTTP status 402 and sets:
x-bsv-payment-satoshis-required: Number of satoshis required.
x-bsv-payment-derivation-prefix: A random nonce () for the transaction.
Client Submits Payment
The client’s wallet or user agent sees the 402 response.
It constructs a BSV transaction paying the requested amount to the server’s derivation.
The client re-sends the request with a header x-bsv-payment containing JSON:
Server Verifies Payment
The server checks the prefix (derivationPrefix) and suffix (derivationSuffix), ensures it matches what was advertised, and that the transaction properly pays the required amount.
If successful, the server considers the request to be funded.
Server Continues
Assuming the payment is valid, the server proceeds with the requested operation and returns a normal success response (e.g., 200).
x-bsv-payment-satoshis-requiredInteger number of satoshis needed for this request.
Sent from server → client in 402 Payment Required.
x-bsv-payment-derivation-prefix
Payment-level nonce for deriving the payment output script.
Sent from server → client in 402 Payment Required.
x-bsv-payment
JSON structure containing the actual transaction data.
Sent from client → server in a subsequent request or retry after 402.
x-bsv-payment-satoshis-required: <price in satoshis>
x-bsv-payment-derivation-prefix: <nonce> (often base64 or hex)
Body: Optional JSON describing the error or instructions for convenience:
Checking if the amount is at least the required satoshisRequired.
Preventing double-spend or replay.
price = 200 satoshis.If x-bsv-payment Provided
Parse and verify. If valid, continue. If invalid, return 400 Bad Request.
If Not Paid
Return 402 + x-bsv-payment-version + x-bsv-payment-derivation-prefix + x-bsv-payment-satoshis-required.
If Paid
Proceed to business logic.
Create transaction paying the server’s derivation.
Store it in x-bsv-payment JSON.
Re-Send
Re-send the request with x-bsv-payment header.
If accepted, receives normal 200 OK.
If server fraudulently absconds with client funds:
Clients should request more robust identity certificates using in the future.
Do not use the service again.
Make reports to the issuers of the server's certificates to have them revoked.
Do not trust certifiers who are willing to issue certificates to fraudulent entities.
Because all requests are already mutually authenticated via BRC-103, a MITM cannot hijack the payment flow. Tampering with the transaction or the prefix invalidates the request and leads to cryptographic failure.
Underpayment
The server should reject transactions that pay less than required. Or, if partial payments are allowed, the server must define how to handle partial coverage. Servers should set clear refund policies, and policies for services not used or partially paid. Generally, partial payment is prohibited.
Double Spends
The server’s wallet.internalizeAction() logic must handle double-spend scenarios, ensuring the transaction is fully-signed, final and valid on the network.
Data Confidentiality
BRC-105 does not itself encrypt data. Transport-level encryption (e.g., TLS) is recommended to protect transaction details and requests from eavesdroppers.
BRC-56 defines a suite of abstract messages used by applications and wallets to facilitate various Bitcoin and MetaNet operations that enable micropayments, protect user privacy and ensure secure authentication without the need for each application to maintain a separate user account. While BRC-5 defines a method of using a BRC-56 wallet over HTTP on the local machine, some mobile devices are unable to support running HTTP servers due to platform-specific limitations. Additionally, it is sometimes desirable for a MetaNet environment to run fully in the browser, enabling users to access their identities on devices or platforms where a desktop-based BRC-5 client cannot be installed. This specification provides a ubiquitous communications substrate enabling the use of BRC-56 functionality across a wide range of devices and deployment contexts, including fully in-browser experiences.
We specify that the parent page runs the wallet, responding to messages from child pages. This has several advantages:
The wallet can always pop-up any necessary permission popups or user-interactive authorization screens without interference from client pages
When the user visits the parent page, they log in once, then they can access any applications after login
Multiple applications, all running in the same parent page, can access and utilize the BRC-56 wallet at once
Reducing the number of wallets running in parallel reduces the chances of UTXO synchronization or corruption issues
It is possible to run the parent wallet page locally, connecting to remote services only when required by specific applications
If a specific application was the parent and the wallet was a child, failure of the parent page to respond could constitute failure of a user to access their identity or assets, which might otherwise be available through another application
We start with the message relay interface defined by XDM. JavaScript code for sending messages from the application to the wallet is as follows:
We stipulate that:
All messages (requests, responses and errors) have a type property equal to CWI (this value, which stands for Computing with Integrity, is historical)
A random message ID is generated by the application
The application listens for new, incoming response messages. As part of the listener, the application:
Drops any events without the correct type in the event data
Drops any events where isTrusted is not true
Drops any events where the event data contains the isInvocation flag, denoting any outgoing messages that were echoed back
Drops any events where the event data contains an id field other than the one generated by step 2
Handles any error messages if the event's data contains a status field equal to error, relying on the code and description fields to construct an appropriate error
Otherwise, if no errors are detected, the application is now free to make use of the result field from the event's data payload, which will be the response from the wallet as specified by the relevant BRC standard for the specific message being sent
With the listener in place and ready to process the response when it arrives, the application now constructs and sends the message to the wallet through the parent window:
Like all messages, the outgoing message contains a type field equal to CWI
An isInvocation flag is set to true
This application-side interface facilitates exchanging and receiving messages with the BRC-56 wallet over XDM. We now proceed to how the wallet handles its side of the interaction.
The wallet listens for incoming messages and replies to the originator with appropriate responses after obtaining permission from the user (if applicable) and processing the request. Some JavaScript code that implements this functionality is provided:
We stipulate, before any client applications are loaded which might send any messages to the wallet, that the wallet running on the parent page must bind a message event handler that:
Upon receipt of a message with an event data field type not equal to CWI will drop the message
Upon receipt of an untrusted message, or one without a call will drop the message
Upon receipt of a message with an unknown or unsupported call will proceed to step 6
Based on the call and params will perform the necessary steps as required by the relevant BRC specifications for the specific operation
Compose a response message and send it back to the originator, the response message comprising an event payload with the following fields:
A type value of CWI
The id that was specified by the application when the message was created
In case of any errors with the operation, the wallet will instead send back a response message comprising an event payload with the following fields:
A type value of CWI
A status value of error
For each of the message pairs (request and response) incorporated into BRC-56, we specify the existence of a corresponding XDM message pair with a specific call value:
Message Pair
call Value
Specific Implementation Notes
Transaction Creation
createAction
Encryption
encrypt
Decryption
decrypt
Signature Creation
Implementers of applications and wallets will need to create and process XDM messages in the manner described, according to the various fields and properties as required by BRC-56, and in a manner consistent with the reference implementation, which is the Babbage SDK's XDM substrate functionality.
Implementation questions should be directed to the author.
One (crude) example of a deployed architecture in which a parent page uses XDM to communicate with child application pages, facilitating the operation of multiple client applications which communicate with the parent wallet, has been implemented by the Babbage team at BabbageOS.com.
The current state of BIP270, BIP271, BIP272, BIP273, BIP274, BIP275, and BIP282 lacks a unified standard owned by a dedicated committee, leading to multiple versions on GitHub that do not accurately represent field implementations. This situation makes it challenging for new players to implement invoice-based payments correctly from the outset. Furthermore, industry representatives have expressed the need for extensions to the existing standards, such as accommodating meta-assets or accepting discount coupons, which lower the amount of BSV required to satisfy the invoiced amount.
The DPP addresses these challenges by re-evaluating and baselining the existing standards to align with common field implementations, while maintaining backward compatibility. The protocol seeks input from industry players on extension needs and aims to find the smallest denominator for such needs, allowing for future custom extensions without compromising backward compatibility.
The Direct Payment Protocol (DPP) is a standard for facilitating direct payments between a customer and a merchant by specifying the necessary messages, objects, and components required for secure and efficient transactions. This section outlines the high-level concepts involved in the DPP standard.
The DPP protocol consists of three main messages: PaymentTerms, Payment, and PaymentACK. These messages are communicated between the customer and the payment host's server, serving to initiate, execute, and acknowledge payment transactions.
The DPP protocol relies on JSON objects to represent and exchange data throughout the payment process. These objects are used to define payment details, payment host and customer information, and payment acknowledgment information.
PaymentTerms is the first message in the DPP protocol, sent by the payment host's server in response to a customer's payment initiation. It contains details about the required payment, such as the available payment modes, beneficiary information, and policies.
PaymentModes define the various ways a customer can make a payment. The PaymentTerms message lists the supported modes, and the customer must choose one of these modes to proceed with the payment. Each mode has its own specific requirements and format for the Payment and PaymentACK messages.
The Beneficiary object contains information about the merchant, such as their name, email, address, and payment reference. This data can be used by the payment host to identify and associate PaymentTerms with the merchant.
Policies define any additional requirements set by the merchant for processing the Payment. These can include required originator fields or other constraints that the customer must adhere to when submitting the Payment message.
The Payment message is sent by the customer after they have authorized the payment. It specifies the chosen payment mode and provides the necessary data required for that mode, such as transaction details, originator information, and any mode-specific objects.
The Originator object contains information about the payer, which can be used for identification, refunds, and other purposes. This data can include the payer's name, paymail, return address, return script, and other optional data.
The PaymentACK message is the final message in the DPP protocol, sent from the payment host's server to the customer's wallet in response to a Payment message. It acknowledges receipt of the payment and provides any mode-specific data or additional information, such as a secure peer channel for direct communication or a redirect URL.
Now that we have covered the basic concepts, we specify the data structures necessary to achieve compliance with the DPP protocol.
The DPP consists of three primary messages: PaymentTerms, Payment, and PaymentACK. Each message is a JSON object with specific fields and requirements.
The PaymentTerms message provides the customer with the necessary information to make a payment to the payment host. It includes details about the required payment, expiration data, and additional metadata:
network (string, required)
Identifies the blockchain network. In production, this should always be "bitcoin-sv".
version (string, required)
Specifies the DPP version.
outputs (array of outputs, optional, deprecated)
Deprecated, for backward compatibility only.
creationTimestamp (number, required)
Unix timestamp (seconds since 1-Jan-1970 UTC) when the PaymentTerms was created.
expirationTimestamp (number, optional)
Unix timestamp (UTC) after which the PaymentTerms should be considered invalid.
The Payment message is sent after the customer has authorized the payment. It specifies the chosen payment mode and provides the required data for that mode.
modeId (string, required)
The ID of the chosen mode from the PaymentTerms message.
mode (object, required)
The Payment object for the specific mode used.
originator (object, optional)
Payer data needed for identification and refund purposes.
transaction (hex-formatted string, optional, deprecated)
A fully-signed and valid Bitcoin transaction. Deprecated.
memo (string, optional)
A plain-text note from the customer to the payment host.
The PaymentACK message is the final message in the DPP, sent from the payment host's server to the Bitcoin wallet in response to a Payment message.
modeId (string, required)
The ID of the chosen mode from the PaymentTerms message.
mode (object, required)
The Payment object for the specific mode used.
peerChannel (object, optional)
Provides data needed to communicate via the created channel in a secure manner.
redirectUrl (string, optional)
An optional URL to redirect the customer after the payment is completed.
Several components are used within the messages described above. This section provides a detailed description of each component.
PaymentModes define the various ways a customer can make a payment. The PaymentTerms message lists the supported modes, and the customer must choose one of these modes to proceed with the payment. Each mode has its own specific requirements and format for the Payment and PaymentACK messages. The main part of the PaymentTerms object is the Modes component. For now, we propose one flexible payment mode called HybridPaymentMode, which can be used to pay with BSV, tokens, or a combination of funding sources. In the future, if HybridPaymentMode cannot fulfill some requirements, other PaymentModes can be specified.
modeId (string, required)
Unique identifier for the payment mode.
description (string, required)
A short description of the payment mode.
requirements (object, required)
An object containing the required fields and data for the chosen payment mode.
The Beneficiary object contains information about the merchant, such as their name, email, address, and payment reference. This data can be used by the payment host to identify and associate PaymentTerms with the merchant.
name (string, required)
Name of the merchant.
email (string, required)
Official merchant email.
address (string, required)
Merchant's address.
paymentReference (string, required)
ID of the payment/invoice.
avatar (string, optional)
URL to an avatar.
Policies define any additional requirements set by the merchant for processing the Payment. These can include required originator fields or other constraints that the customer must adhere to when submitting the Payment message.
requiredOriginatorFields (array of strings, optional)
List of the fields which are required by the merchant to process the payment (regardless of whether DPP defines them as optional ones).
The Originator object contains information about the payer, which can be used for identification, refunds, and other purposes. This data can include the payer's name, paymail, return address, return script, and other optional data.
name (string, required)
Name of the payer.
paymail (string, optional)
Payer's paymail (where, e.g., refunds will be sent, identity can be used somehow, etc.).
return_address (string, optional)
Payer's return bitcoin address on which the merchant can refund the payment back if there is such a case (if paymail cannot be used, another way paymail is a better option).
return_script (string, optional)
Similar to the above but a script instead of a raw address (it is more flexible, e.g., the payer might want to have a refund in a stable coin).
avatar (string, optional)
URL to an avatar.
The PeerChannel object describes and defines the format of the peerChannel field in the PaymentACK message. It is used to provide data that specifies a created channel for secure direct communication between the payer and the merchant.
host (string, required)
The hostname and port of the server handling the secure direct communication channel.
token (string, required)
A unique token used for authentication and authorization within the secure communication channel.
channel_id (string, required)
A unique identifier for the specific communication channel established between the payer and the merchant.
We use JSON as JSON is naturally extensible. Any property of the objects not specified in this standard is a candidate to convey information for extensions. Other standards can be created to denote specific payment modes. One such standard is BRC-54, which defines the Hybrid Payment Mode. This protocol defines an abstract messaging layer with JSON objects, but other standards such as BRC-55 can stipulate concrete transport mechanisms and such as HTTPS.
The Direct Payment Protocol (over HTTPS) is implemented in this example from Jad Wahab.
Ty Everett ([email protected])
This document outlines the PeerServ Host Interconnect Protocol (PHIP), a peer discovery mechanism specifically designed for the PeerServ message relay system. Analogous to the scalability and resiliency enabled by CHIP (Confederacy Host Interconnect Protocol) for overlay networks, PHIP allows PeerServ instances to discover and connect with other active instances where a particular user receives messages, thus enabling robust and efficient message delivery across these instances. It also defines a mechanism for instances to prove the authenticity of the original message sender to one another using key linkage information.
Jake Jones ([email protected])
This proposal extends the Enhanced Mandala Token Protocol (BRC-107) with identity certificate integration based on BRC-52/53. It enables tokens to enforce real-world compliance requirements, KYC/AML regulations, and identity-based access controls while preserving privacy through selective field revelation. The protocol supports regulated security tokens, accredited investor requirements, geographic restrictions, and identity-based recovery mechanisms, all validated through SPV-friendly cryptographic proofs.
// khan's algorithm
function khanTopologicalSort(graph) {
const inDegree = {}
const queue = []
const result = []
for (let node in graph) {
inDegree[node] = 0
}
for (let node in graph) {
for (let neighbor in graph[node]) {
inDegree[neighbor]++
}
}
for (let node in inDegree) {
if (inDegree[node] === 0) {
queue.push(node)
}
}
while (queue.length) {
let node = queue.shift()
result.push(node)
for (let neighbor in graph[node]) {
inDegree[neighbor]--
if (inDegree[neighbor] === 0) {
queue.push(neighbor)
}
}
}
return result.reverse()
}
const txs = [
{
txid: '2222222222222222222222222222222222222222222222222222222222222222',
inputs: ['1111111111111111111111111111111111111111111111111111111111111111'],
},
{
txid: '1111111111111111111111111111111111111111111111111111111111111111',
inputs: ['0000000000000000000000000000000000000000000000000000000000000000'],
},
{
txid: '0000000000000000000000000000000000000000000000000000000000000000',
inputs: [],
},
{
txid: '4444444444444444444444444444444444444444444444444444444444444444',
inputs: [
'3333333333333333333333333333333333333333333333333333333333333333',
'2222222222222222222222222222222222222222222222222222222222222222',
],
},
{
txid: '3333333333333333333333333333333333333333333333333333333333333333',
inputs: [
'2222222222222222222222222222222222222222222222222222222222222222',
'1111111111111111111111111111111111111111111111111111111111111111',
],
},
]
const graph = {}
for (let tx of txs) {
graph[tx.txid] = {}
for (let input of tx.inputs) {
graph[tx.txid][input] = true
}
}
console.log({ graph })
console.log({ correctOrder: khanTopologicalSort(graph) })0100beef01fe636d0c0007021400fe507c0c7aa754cef1f7889d5fd395cf1f785dd7de98eed895dbedfe4e5bc70d1502ac4e164f5bc16746bb0868404292ac8318bbac3800e4aad13a014da427adce3e010b00bc4ff395efd11719b277694cface5aa50d085a0bb81f613f70313acd28cf4557010400574b2d9142b8d28b61d88e3b2c3f44d858411356b49a28a4643b6d1a6a092a5201030051a05fc84d531b5d250c23f4f886f6812f9fe3f402d61607f977b4ecd2701c19010000fd781529d58fc2523cf396a7f25440b409857e7e221766c57214b1d38c7b481f01010062f542f45ea3660f86c013ced80534cb5fd4c19d66c56e7e8c5d4bf2d40acc5e010100b121e91836fd7cd5102b654e9f72f3cf6fdbfd0b161c53a9c54b12c841126331020100000001cd4e4cac3c7b56920d1e7655e7e260d31f29d9a388d04910f1bbd72304a79029010000006b483045022100e75279a205a547c445719420aa3138bf14743e3f42618e5f86a19bde14bb95f7022064777d34776b05d816daf1699493fcdf2ef5a5ab1ad710d9c97bfb5b8f7cef3641210263e2dee22b1ddc5e11f6fab8bcd2378bdd19580d640501ea956ec0e786f93e76ffffffff013e660000000000001976a9146bfd5c7fbe21529d45803dbcf0c87dd3c71efbc288ac0000000001000100000001ac4e164f5bc16746bb0868404292ac8318bbac3800e4aad13a014da427adce3e000000006a47304402203a61a2e931612b4bda08d541cfb980885173b8dcf64a3471238ae7abcd368d6402204cbf24f04b9aa2256d8901f0ed97866603d2be8324c2bfb7a37bf8fc90edd5b441210263e2dee22b1ddc5e11f6fab8bcd2378bdd19580d640501ea956ec0e786f93e76ffffffff013c660000000000001976a9146bfd5c7fbe21529d45803dbcf0c87dd3c71efbc288ac00000000000100beef // version
01 // VarInt nBUMPs
fe636d0c0007021400fe507c0c7aa754cef1f7889d5fd395cf1f785dd7de98eed895dbedfe4e5bc70d1502ac4e164f5bc16746bb0868404292ac8318bbac3800e4aad13a014da427adce3e010b00bc4ff395efd11719b277694cface5aa50d085a0bb81f613f70313acd28cf4557010400574b2d9142b8d28b61d88e3b2c3f44d858411356b49a28a4643b6d1a6a092a5201030051a05fc84d531b5d250c23f4f886f6812f9fe3f402d61607f977b4ecd2701c19010000fd781529d58fc2523cf396a7f25440b409857e7e221766c57214b1d38c7b481f01010062f542f45ea3660f86c013ced80534cb5fd4c19d66c56e7e8c5d4bf2d40acc5e010100b121e91836fd7cd5102b654e9f72f3cf6fdbfd0b161c53a9c54b12c841126331 // see BRC-74 for details of BUMP format
02 // VarInt nTransactions = 2
// rawtx parent follows
0100000001cd4e4cac3c7b56920d1e7655e7e260d31f29d9a388d04910f1bbd72304a79029010000006b483045022100e75279a205a547c445719420aa3138bf14743e3f42618e5f86a19bde14bb95f7022064777d34776b05d816daf1699493fcdf2ef5a5ab1ad710d9c97bfb5b8f7cef3641210263e2dee22b1ddc5e11f6fab8bcd2378bdd19580d640501ea956ec0e786f93e76ffffffff013e660000000000001976a9146bfd5c7fbe21529d45803dbcf0c87dd3c71efbc288ac00000000
01 // above tx has merkle path
00 // VarInt the index of the path for this tx in the above list
// rawtx current payment follows
0100000001ac4e164f5bc16746bb0868404292ac8318bbac3800e4aad13a014da427adce3e000000006a47304402203a61a2e931612b4bda08d541cfb980885173b8dcf64a3471238ae7abcd368d6402204cbf24f04b9aa2256d8901f0ed97866603d2be8324c2bfb7a37bf8fc90edd5b441210263e2dee22b1ddc5e11f6fab8bcd2378bdd19580d640501ea956ec0e786f93e76ffffffff013c660000000000001976a9146bfd5c7fbe21529d45803dbcf0c87dd3c71efbc288ac00000000
00 // above tx doesn't have merkle path, but instead has local parent{
"derivationPrefix": "AAAAA...",
"derivationSuffix": "...",
"transaction": "SGVsbG8s" // a BSV transaction in AtomicBEEF format encoded as base64
}{
"status": "error",
"code": "ERR_PAYMENT_REQUIRED",
"satoshisRequired": 200,
"description": "A BSV payment is required..."
}{
"derivationPrefix": "<prefix from server>",
"derivationSuffix": "<optional suffix>",
"transaction": "SGVsbG8s"
}new Promise((resolve, reject) => {
const id = 'abcdabcd' // get a random message ID
window.addEventListener('message', async e => {
if (e.data.type !== 'CWI' || !e.isTrusted || e.data.id !== id || e.data.isInvocation) return
if (e.data.status === 'error') {
const err = new Error(e.data.description)
err.code = e.data.code
reject(err)
} else {
resolve(e.data.result)
}
})
window.parent.postMessage({
type: 'CWI',
isInvocation: true,
id,
call: 'getVersion',
params: {}
}, '*')
})window.addEventListener('message', async e => {
if (e.data.type !== 'CWI' || !e.isTrusted || typeof e.data.call !== 'string') return
// This is where the wallet will do its processing, based on `call` and `params`.
// ... in a rudamentary implementation ...
if (e.data.call === 'createAction') { // BRC-1
try {
let result = await doBRC1Thing({
...e.data.params,
originator: e.origin // You might let BRC1Thing know which app is sending the request, for permission purposes
})
e.source.postMessage({
type: 'CWI', result, id: e.data.id
}, e.origin)
} catch (error) {
e.source.postMessage({
type: 'CWI',
id: e.data.id,
status: 'error',
code: error.code || 'ERR_UNKNOWN',
description: error.message
}, e.origin)
}
} else if (e.data.call === 'encrypt') { // BRC-2 encrypt
try {
let result = await doBRC2Thing({
...e.data.params,
originator: e.origin // You might let BRC2Thing know which app is sending the request, for permission purposes
})
e.source.postMessage({
type: 'CWI', result, id: e.data.id
}, e.origin)
} catch (error) {
e.source.postMessage({
type: 'CWI',
id: e.data.id,
status: 'error',
code: error.code || 'ERR_UNKNOWN',
description: error.message
}, e.origin)
}
} // ... implement all functions ...
})memo (string, optional)
A note to be displayed to the customer, explaining the purpose of the PaymentTerms. Maximum length is 50 characters.
paymentUrl (string, required)
Secure HTTPS location where a Payment message will be sent to obtain a PaymentACK.
beneficiary (object, optional)
Contains data about the merchant.
modes (key-value map, required)
A dictionary of possible payment modes specified by ID, each mode describing detailed instructions about payment requirements.
policies (object, optional)
Specifies special merchant requirements according to the Payment object.
extendedData (object, optional)
Additional optional data as a freestyle object.
extendedData (object, optional)
Additional optional data as a freestyle object.
Contact your local police department.
File an IC3 complaint.
The id that was generated in step 2 is included. The same id must be used by wallets when sending back the response
The call determines which message is being sent. Specific call values are defined below
The params field comprises the specific parameters as specified in BRCs that define specific message types
A result value that is the result of the operation being performed, as specified by the particular operation
The id that was specified by the application when the message was created
A relevant code for the error, as specified by the particular operation in question
A human-readable description for the error
createSignature
BRC-3 Signature Verification
verifySignature
BRC-53 Certificate Creation
createCertificate
BRC-53 Certificate Verification
proveCertificate
BRC-56 HMAC Creation
createHmac
BRC-56 HMAC Verification
verifyHmac
BRC-56 Public Key Derivation
getPublicKey
BRC-56 Certificate List
ninja.findCertificates
This call name prefix is historical and retained for compatibility
BRC-56 Version Request
getVersion
BRC-56 Network Request
getNetwork
BRC-56 Authentication Request
isAuthenticated
BRC-56 Async Auth Request
waitForAuthentication

Current token systems on BSV lack native integration with identity verification, making regulatory compliance difficult and limiting adoption for security tokens and regulated assets. While BRC-52/53 provides a robust identity certificate system, there's no standard for linking these certificates to token ownership and transfers.
This proposal addresses:
Regulatory compliance for security tokens requiring KYC/AML
Accredited investor verification for restricted offerings
Geographic restrictions for regulatory jurisdictions
Identity-based recovery for lost keys
Privacy-preserving compliance through selective revelation
Fraud prevention through identity linkage
We define three levels of identity requirements for tokens:
Open Tokens: No identity requirements (backward compatible with BRC-92/BRC-107)
Verified Tokens: Require valid BRC-52 certificate
Restricted Tokens: Require specific certificate fields or types
The genesis transaction establishes identity requirements:
Where:
issuerCertificateHash: SHA-256 hash of issuer's BRC-52 certificate
complianceRules: Encoded rules for token transfers
identityCommitment: H(assetId || issuerCertificate || complianceRules || maxSupply)
Where:
recipientCertificateHash: SHA-256 hash of recipient's BRC-52 certificate
identityCommitment: H(assetId || amount || certificateHash || prevTxid)
When transferring identity-linked tokens, the sender provides:
Recipients and overlays MUST verify:
Commitment verification
Conservation check
Merkle proof validation
Certificate holders reveal only required fields:
Determine Required Fields: Based on token's compliance rules
Encrypt for Recipient: Using BRC-52/53 key derivation
Include in Transfer: Add encrypted keys to certificate keyring
Recipient Decrypts: Using their identity key
Example revealing only country for geographic restriction:
Tokens can include recovery conditions:
Where:
recoveryCommitment: H(certificateType || recoveryFields || recoveryPubkey)
recoveryDelay: Time lock before recovery (e.g., 30 days)
For sensitive compliance checks, support ZK proofs:
Prove age > 18 without revealing exact age
Prove accredited status without revealing net worth
Prove country membership without revealing specific country
All certificate fields remain encrypted until revelation:
Only revealed fields are decrypted
Revelation is counterparty-specific
No permanent plaintext exposure
Users can maintain pseudonymity while proving compliance:
Certificate links to pubkey, not real name
Only certifier knows real identity
Selective revelation preserves privacy
Always verify certificate signatures
Check revocation status via UTXO
Validate certifier is trusted
Ensure certificate hasn't expired
Include prevTxid in commitments
Timestamp compliance attestations
Use nonces for uniqueness
Certificate revocation via UTXO spending
Recovery mechanisms for token access
Time-locked recovery periods
Open tokens work without identity
Existing BRC-92/106 tokens unaffected
Optional identity for gradual adoption
Phase 1: Deploy identity-aware wallets
Phase 2: Certifiers issue BRC-52 certificates
Phase 3: Token issuers adopt compliance rules
Phase 4: Overlays enforce identity requirements
Phase 5: Full ecosystem adoption
BRC-52: Identity Certificates
BRC-53: Certificate Creation and Revelation
BRC-92: Mandala Token Protocol
BRC-107: Enhanced Mandala Token Protocol
BRC-43: Security Levels, Protocol IDs, Key IDs and Counterparties
BRC-2: Data Encryption and Decryption
BRC-3: Digital Signature Creation and Verification
Reference implementations:
TypeScript: Coming Soon
Go: Coming Soon
Python: Coming Soon
21 <assetId>
<issuerCertificateHash>
<complianceRules>
<identityCommitment>
OP_2DROP OP_2DROP OP_2DROP OP_DROP ...{
"version": 1,
"requireIdentity": true,
"allowedCertificateTypes": [
"8l5phhdm2Hi80s6QqFOLS0NwUzDzJhlUTWv2BezmstE="
],
"requiredFields": ["country", "accreditedStatus"],
"restrictions": {
"allowedCountries": ["US", "CA", "GB"],
"minCertificateLevel": "enhanced_kyc",
"maxAmountPerIdentity": 10000,
"cooldownPeriod": 86400
}
}21 <assetId> <amount>
<recipientCertificateHash>
<identityCommitment>
OP_2DROP OP_2DROP OP_2DROP OP_DROP P2PKH{
"transaction": "hex_encoded_transaction",
"tokenProofs": {
"inputs": [...],
"outputs": [...],
"conservationProof": {...}
},
"identityProofs": {
"sender": {
"certificate": {
"type": "8l5phhdm2Hi80s6QqFOLS0NwUzDzJhlUTWv2BezmstE=",
"subject": "0376d67c86b45be3c36c328c2aa5c5dd79c546d2...",
"serialNumber": "kUahacBHmYL2nkzemkatFg==",
"certifier": "035ce8cc44dbcf4c991d666d381d67263aed9123...",
"revocationOutpoint": "48645047cd66f7b48b24efb080ec7e27...1",
"signature": "3045022100c0686907...",
"keyring": {
"country": "encrypted_for_recipient",
"accreditedStatus": "encrypted_for_recipient"
}
},
"certificateProof": {
"merkleProof": "...",
"blockHeight": 850000
}
},
"recipient": {
"certificateHash": "sha256_hash_of_recipient_certificate",
"certificateType": "8l5phhdm2Hi80s6QqFOLS0NwUzDzJhlUTWv2BezmstE="
}
},
"complianceAttestation": {
"rulesVersion": 1,
"checksPassed": [
"country_allowed",
"accredited_investor",
"amount_within_limit"
],
"timestamp": 1234567890,
"attestorSignature": "..."
}
}function verifyIdentityCompliance(transfer) {
// Verify sender's certificate
if (!verifyCertificate(transfer.identityProofs.sender.certificate)) {
return false;
}
// Check certificate not revoked
if (isSpent(transfer.identityProofs.sender.certificate.revocationOutpoint)) {
return false;
}
// Verify certificate matches output
senderCertHash = H(transfer.identityProofs.sender.certificate);
if (senderCertHash != transfer.transaction.inputs[0].certificateHash) {
return false;
}
// Decrypt and verify required fields
fields = decryptFields(transfer.identityProofs.sender.certificate.keyring);
if (!meetsComplianceRules(fields, token.complianceRules)) {
return false;
}
// Verify recipient certificate type
if (!token.complianceRules.allowedCertificateTypes.includes(
transfer.identityProofs.recipient.certificateType)) {
return false;
}
return true;
}{
"certificate": {
"fields": {
"firstName": "encrypted_not_revealed",
"lastName": "encrypted_not_revealed",
"country": "encrypted_value",
"city": "encrypted_not_revealed"
},
"keyring": {
"country": "encrypted_revelation_key_for_recipient"
}
}
}21 <assetId> <amount>
<primaryCertificateHash>
<recoveryCommitment>
OP_IF
<recoveryDelay> OP_CHECKSEQUENCEVERIFY OP_DROP
<recoveryCertificateHash> OP_EQUAL
OP_ELSE
<primaryCertificateHash> OP_EQUAL
OP_ENDIF{
"certificateLevels": {
"basic_kyc": {
"maxTransferAmount": 1000,
"dailyLimit": 5000,
"allowedOperations": ["transfer", "receive"]
},
"enhanced_kyc": {
"maxTransferAmount": 10000,
"dailyLimit": 50000,
"allowedOperations": ["transfer", "receive", "stake"]
},
"institutional": {
"maxTransferAmount": null,
"dailyLimit": null,
"allowedOperations": ["transfer", "receive", "stake", "mint", "burn"]
}
}
}// Genesis specifies accredited investor requirement
complianceRules = {
requiredFields: ["accreditedStatus", "country"],
allowedCountries: ["US"],
restrictions: {
mustBeAccredited: true,
minInvestment: 10000
}
}// Token only transferable within specific regions
complianceRules = {
requiredFields: ["country"],
allowedCountries: ["EU", "UK", "CH"],
restrictions: {
blockRestrictedCountries: ["US", "CN", "RU"]
}
}// Gaming tokens requiring age verification
complianceRules = {
requiredFields: ["ageRange"],
restrictions: {
minimumAge: 18,
requireAgeAttestation: true
}
}// Different limits based on certificate type
if (certificate.type == "retail") {
maxDailyVolume = 10000;
} else if (certificate.type == "qualified") {
maxDailyVolume = 100000;
} else if (certificate.type == "institutional") {
maxDailyVolume = unlimited;
}async function createIdentityToken(params) {
// Get issuer's certificate
const issuerCert = await wallet.getCertificate();
// Define compliance rules
const rules = {
requireIdentity: true,
allowedCertificateTypes: [params.certificateTypeId],
requiredFields: params.requiredFields,
restrictions: params.restrictions
};
// Create genesis with identity binding
const genesis = {
assetId: generateAssetId(),
issuerCertificateHash: hash(issuerCert),
complianceRules: rules,
identityCommitment: hash(
assetId + issuerCert + rules + params.maxSupply
)
};
return createGenesisTransaction(genesis);
}async function transferWithCompliance(amount, recipient) {
// Get certificates
const senderCert = await wallet.getCertificate();
const recipientCert = await requestCertificate(recipient);
// Check compliance
const compliance = await checkCompliance(
senderCert,
recipientCert,
token.complianceRules
);
if (!compliance.passed) {
throw new Error(`Compliance failed: ${compliance.reason}`);
}
// Reveal required fields
const keyring = await revealFields(
senderCert,
token.complianceRules.requiredFields,
recipient
);
// Create transfer
return {
transaction: createTokenTransfer(amount, recipient),
identityProofs: {
sender: {
certificate: senderCert,
keyring: keyring
},
recipient: {
certificateHash: hash(recipientCert)
}
},
complianceAttestation: compliance.attestation
};
}Input:
assetId: "abc...def:0"
certificateHash: "123...789"
amount: 100
prevTxid: "def...456"
Output:
commitment = SHA256(assetId || amount || certificateHash || prevTxid)
= "xyz...123"Certificate Fields:
country: "US"
accreditedStatus: "true"
Compliance Rules:
allowedCountries: ["US", "CA"]
mustBeAccredited: true
Result: PASS ✓BRC-33 establishes a reliable message relay architecture, providing a solution for situations where users need to exchange messages but are unable to do so directly over the network. While a centralized server is simpler to deploy, a federated approach that enables instances to cooperate and assist each other in delivering messages removes the need for everyone on the network to rely on a single server. By proposing an interconnect protocol for PeerServ hosts, this document aims to address this requirement and enhance the scalability and reliability of the PeerServ message relay system.
The PHIP token is a BRC-48 output on the Bitcoin SV network which hosts an advertisement created by the PeerServ server operator. The protocol comprises the following ordered fields:
Field 1
The term 'PHIP', representing a PHIP advertisement.
Field 2
The identity key of the instance operator who submitted the advertisement.
Field 3
The domain name of the HTTPS server hosting the PeerServ instance.
Field 4
The identity key of the user who the instance operator believes will receive messages via the PeerServ instance.
Field 5
A signature from the recipient authorizing this host to make this advertisement. The recipient signature is specified below.
The purpose of the recipient signature is to prevent malicious PeerServ hosts from creating advertisements that route messages destined for certain recipients to their servers without authorization. The recipient's PeerServ host will need to obtain this signature from the recipient prior to creating any advertisements to receive messages on their behalf.
The message template for the recipient signature is as follows:
Where:
<host key> is the BRC-31 identity key of the PeerServ host the recipient is authorizing
<host domain> is the domain the recipient knows the host to reside at, and
<box regex> is either the regular expression, or if no regular expression is provided, the string ALL
For example: 028d37b941208cd6b8a4c28288eda5f2f16c2b3ab0fcb6d13c18b47fe37b971fc1 peerserv.babbage.systems ALL
The signature is to be computed by the recipient according to the BRC-3 process. The BRC-43 security level is 1, the protocol ID is PHIP authorization and the key ID is 1. The counterparty is anyone, meaning that anyone can verify the recipient's signature.
We specify the creation of a new BRC-22 overlay network (with accompanying BRC-24 lookup service) that will host and track PHIP tokens from various PeerServ instance operators across the network. When it wishes to advertise that messages can be routed to a particular user through their server, the PeerServ instance operator creates a new PHIP token and submits it to the overlay. Upon receiving a new submission, the overlay operators must confirm that the BRC-48 locking key of the PeerServ instance operator is connected to their claimed BRC-31 identity key by ensuring that the BRC-42 child key used for signing can be linked back to the claimed identity key.
The token creation process followed by a PeerServ instance operator comprises several steps:
Obtaining the recipient's signature to authorize the message routing advertisement.
Deriving the BRC-48 Locking Key: The PeerServ instance operator should use BRC-42 key derivation, with the operator's BRC-31 identity key as the sender private key, while the 'anyone' public key (1 times G) should be the counterparty. The invoice number for BRC-43 is calculated at security level 2, with protocol ID PHIP and key ID 1. Then, the signing private key can be derived from the identity key using this invoice number.
Computing the Signature: The derived private key from the earlier step is used to calculate the signature covering the fields of the PHIP token.
Broadcast the Transaction: A transaction output is embedded with the PHIP token, and this transaction is then submitted to the PHIP overlay network.
Before admitting a PHIP token into the overlay, PHIP overlay operators should verify that the claimed identity key from the PeerServ instance operator corresponds to the BRC-48 signing key. Any invalid tokens must not be admitted into the overlay. The validation and verification process is specified in the steps below:
Verify the PHIP identifier. The first field of the PHIP token should be equal to PHIP. If the PHIP identifier fails to verify, the token has to be rejected as invalid.
Validate the BRC-48 locking key. The overlay operator should apply the BRC-42 key derivation method, with the advertiser's BRC-31 identity key as the sender public key and the 'anyone' private key (1) as the recipient. The BRC-43 methodology should then be used to calculate the invoice number at security level 2, protocol ID PHIP and key ID 1. The public key derived through the advertiser's identity key using this invoice number must match the locking key; if not, the token is rejected as invalid.
Validate the signature. The signature produced in the process, using the public key derived, should be verified over the fields of the PHIP token. Only valid signatures which satisfy the necessary conditions should be considered authentic.
Verify the recipient signature. Use the recipient's claimed identity public key to derive the appropriate signing key for the PHIP authorization protocol, with security level 1 and key ID 1 for the anyone counterparty. Check the signature against the specified message based on the provided fields.
Blacklist checks. If the overlay network operator maintains a blacklist of known-malicious PeerServ instance operators, they can choose not to admit the token.
Allow the token into the PHIP overlay. The token is deemed valid if all conditions are successfully met, and can be admitted into the PHIP overlay network.
As alluded to earlier, we specify the creation of a new BRC-24 PHIP lookup service which allows network users to find and query various PHIP advertisement UTXOs, thereby facilitating the navigation and interaction with other operators' PeerServ instances. As specified later, PeerServ instance operators looking to forward new messages to the correct place so that the recipient can access them can make use of this lookup service to find which PeerServ instance is in use by the PeerServ message recipient.
We specify PHIP as the BRC-24 Lookup Service Provider Name.
The lookup service accepts a JSON object as its query. The object is specified to contain a recipient key, which the lookup service will use to find records for PeerServ hosts who claim to deliver to the specified recipient.
The network user performs a lookup service query for the 'user' key, which is the BRC-31 identity key of the message recipient they want to reach. The lookup service will search and return all corresponding UTXOs linked to that key. The network user can then utilize the HTTPS URL(s) for the host record(s) returned, in order to effectuate message delivery.
Before a PeerServ instance operator advertises that it is able to receive incoming messages on behalf of final recipients, it will need a way to collect authorizations from said recipients. These authorizations are sent to a new BRC-31-protected endpoint (in addition to those specified by BRC-33) which collects and verifies recipient authorization signatures. We specify this HTTPS, JSON, POST endpoint as follows: POST /authorizeRouting
message
The message that has been signed by the recipient (required for the avoidance of doubt, makes validation and error handling easier)
signature
The recipient authorization signature, as specified
The PeerServ instance that receives this request will validate the signature, storing and using it if it later decides to advertise this recipient's availability for the receipt of incoming messages across the network.
If the server received and validated the authorization from the recipient, a success response is returned.
If there was an error, appropriate HTTP status codes should be used. An example of a reasonable error response is provided.
When a PeerServ instance operator advertises that it is able to receive incoming messages from other hosts, it must be prepared to accept requests to a new BRC-31-protected endpoint (in addition to those specified by BRC-33) to propagate incoming messages. We specify this HTTPS, JSON, POST endpoint as follows: POST /propagateMessage
originalSender
The identity key of the original message sender
keyLinkage
The (Method 2) key linkage information that links the original sender with the Authrite message signature
signedMessage
The message comprising the original Authrite request made by the original sender, which will contain all relevant information, including the message recipient, message box, and message body
senderSignature
The signature that the original sender provided for the message when it was delivered to the sender's local, original PeerServ host
The PeerServ instance that receives this request will perform the following steps:
Compute the child public key for the original message sender by adding (by point addition) the keyLinkage value to the originalSender public key.
Check the senderSignature against the computed public key for validity.
Examine the signedMessage to determine the appropriate message box and recipient.
If the server is willing to add this message to the specified message box for the specified recipient, the message is added.
If the message was successfully stored in the recipient's appropriate PeerServ message box, a success response is returned.
If there was an error, appropriate HTTP status codes should be used. An example of a reasonable error response is provided.
When a BRC-33 message sender submits a new message destined for a recipient to his local PeerServ instance, the PeerServ instance will perform the following steps to effectuate message delivery across the network:
Use the PHIP lookup service to look up UTXOs associated with the PeerServ instances in use by the recipient.
If Regular Expression fields were provided for any of the tokens, test the message box where the message is to be delivered against these values, discarding any tokens that fail the Regular Expression check, but keeping any tokens that did not provide one.
Apply the steps defined by the Validation and Verification of Tokens section of this document, verifying for themself that the tokens provided by the lookup service are legitimate, and dropping any that fail this verification.
Make use of the Message Delivery Endpoint to deliver the message to each of the PeerServ instance operators with valid PHIP tokens, providing the stipulated parameters.
It is possible to make use of BRC-41 within the Message Delivery Endpoint to monetize the routing and delivery of PeerServ messages, which would allow operators to collect fees for relaying and synchronizing messages. Subject to routing agreements between hosts, people who make use of this endpoint should be prepared to handle a 402 Payment Required error, as specified by BRC-41. Network operators may also choose to route messages for free, but this is not sustainable at large scale.
There are no known implementations of this standard at present. This specification will be updated as and when an appropriate implementation is developed.
Todd Price ([email protected])
The Genealogical Identity Protocol (GIP) represents an innovative leap in identity verification within the Bitcoin ecosystem. Merging genealogical data in GEDCOM files with advanced cryptographic techniques, the GIP creates an immutable and secure identity system. This aligns seamlessly with Dan Roble's Innovation Bank proposal and integrates with global identity protocols, thereby enhancing interoperability.
The GIP uses GEDCOM files' rich historical and familial data as a unique identity marker, fortified by an adaptive versioning system for continuous updates and refinement. This system verifies the longest unbroken chain of ancestors, adding robust security layers to identity verification.
Furthermore, the GIP handles hash-based dependencies via a novel signing scheme, encapsulating the diversity of modern family structures. It mitigates complex familial relationships while maintaining high-security standards. By offering a dynamic and secure solution to digital identities, the GIP enhances the Bitcoin ecosystem and forecasts future breakthroughs in blockchain-based identity management.
<host key> <host domain> <box regex>{
"user": "028d37b941208cd6b8a4c28288eda5f2f16c2b3ab0fcb6d13c18b47fe37b971fc1"
}{
"status": "success"
}{
"status": "error",
"code": "ERR_SIGNATURE_INVALID",
"description": "The recipient authorization signature could not be verified."
}{
"status": "success"
}{
"status": "error",
"code": "ERR_SENDER_LINKAGE_VERIFICATION_FAILED",
"description": "Unable to link the signature to the original message sender."
}The motivation for developing the Genealogical Identity Protocol (GIP) stems from a need to overcome inherent identity verification challenges within the Bitcoin ecosystem and the broader digital world. The GIP is designed to cater to a global audience, seamlessly integrating with international standards for self-sovereign identity, thus ensuring its relevance and application on a global scale.
One of the primary incentives for the GIP's development is the requirement for a decentralized and secure identity verification method, fitting with the ethos of Bitcoin. GIP leverages the rich genealogical data embedded in GEDCOM files, thus creating a unique identity marker. This approach eliminates dependence on centralized authorities or government-issued identity documents, thereby reducing security risks and enhancing verifiability.
GIP's innovative approach to digital identity also allows for the creation of secondary data markets, enabling individuals to leverage their identities for personal gain. The GIP's detailed and secure identity system, combined with its sophisticated data privacy controls, creates the potential for individuals to monetize their anonymized data. This approach empowers users to control their data and benefit from its value, fundamentally shifting the dynamics of the digital economy.
Furthermore, the GIP’s flexible nature makes it capable of handling the complexities and dynamics of modern familial relationships. Its ability to manage hash-based dependencies results in a more accurate and comprehensive representation of real-world identities. This intricacy of identity representation extends to include coats of arms, heraldry, and intersection with various reputation, expertise, and public office portfolios, thus offering a more holistic and adaptable identity management system.
The GIP's end-to-end authentication capabilities form another key motivation for its implementation. By facilitating the derivation of shared secrets for key exchanges, deterministic hash chains, ECDH, and CGA++, GIP offers a more secure and efficient framework within the Bitcoin ecosystem.
Finally, the GIP is motivated by the aspiration to contribute to a globally interoperable digital identity management system. Its design allows seamless integration with both self-sovereign and international identity protocols, fostering a more unified, secure, and user-centric digital identity ecosystem.
In conclusion, the GIP is driven by the urgent need for improved identity verification methods in decentralized systems like Bitcoin, the aim to empower individuals to benefit from their digital identities, and the vision to contribute to a globally interoperable, secure, and user-centric digital identity framework.
The Genealogical Identity Protocol (GIP) takes a revolutionary step forward in identity management within the Bitcoin ecosystem by harnessing the untapped potential of Genealogical Data Communication (GEDCOM) files. The salient feature of these files is the vast genealogical information they contain about individuals and families. When used as identity markers, they create a layered, historically authenticated representation that is innately resistant to forgery and fraud.
In the GIP system, GEDCOM files form the foundation of identity representation. Each GEDCOM file serves as a detailed, dynamic, and individualized identity dossier, accurately reflecting the unique familial lineage and heritage of the user. This distinctive identity marker enhances security within the Bitcoin ecosystem by providing a robust defense against identity theft and impersonation.
However, the role of GEDCOM files within the GIP extends beyond being static identity markers. These files are dynamic entities that drive various interactions within the Bitcoin ecosystem, including transactions, authorizations, access controls, and other blockchain engagements. The user's GEDCOM-based identity is tied to these functionalities, ensuring a secure, transparent transactional environment characterized by verifiable authenticity.
The GIP further enhances the utility of GEDCOM files through an adaptive versioning system, permitting continuous updates and refinements. This feature enables each new version of a GEDCOM file to undergo independent authentication, with associated levels of signatures. The continuous refinement and authentication process ensures the ongoing enrichment of an individual's genealogical data, fostering user engagement and community validation.
This adaptive versioning system effectively addresses the inherent complexity of genealogical data, which often involves potential discrepancies or inaccuracies, particularly with respect to distant relatives. Each GEDCOM file version becomes a timestamped snapshot of a person's genealogical data at a particular point in time, with subsequent versions forming a chronological archive. This methodology provides a granular understanding of the data's evolution and adds an extra layer of authenticity.
Further enhancing security, the GIP introduces a proofing mechanism against the longest unbroken chain of ancestors. This system uses the Longest Common Substring as a verification tool to maintain consistency and coherence across different versions of a GEDCOM file.
The integration of GEDCOM files in the GIP represents a significant departure from traditional digital identity systems. The GIP presents a comprehensive, secure, and flexible identity management solution that not only ensures privacy and security but also offers flexibility, adaptability, and a reflection of real-world complexities. By empowering individuals with control over their identities and recognizing their personal history's complexity, GIP lays the foundation for a dynamic and multidimensional identity system.
The Genealogical Identity Protocol (GIP) employs hash-based dependencies to encapsulate the nuances and dynamism of modern familial relationships within its innovative genealogical model. These dependencies encode intricate family bonds into the Bitcoin blockchain, leading to a highly secure, accurate, and verifiable representation of complex familial structures.
Families in the modern world often break away from traditional boundaries, encompassing non-traditional structures such as step-parenting, adoption, co-parenting, and more. The GIP, through the use of digital signatures, incorporates these diverse family structures, ensuring that every unique familial bond is authenticated and represented accurately within the system.
However, the mathematical modeling of these dynamic relationships poses a significant challenge, primarily due to their inherent dynamism and potential circular dependencies. In response, the GIP models hash-based dependencies as dynamic edges, signifying that the unique identifier (hash) of a GEDCOM file is not static and can evolve over time as new information becomes available or when newer versions of the GEDCOM files are created.
To illustrate, consider a situation where an individual and their parents have completed their GEDCOM files, but the grandparents' files remain incomplete. The incomplete grandparents' hash needs to be integrated into the GEDCOM files of both the individual and their parents. This interdependency forms a circular loop, where the completion of one hash depends on the finalization of another. The GIP's versioning system provides a solution to this complexity.
The versioning system, in conjunction with the dynamic nature of hash-based dependencies, manages these intricacies effectively. Each version of a GEDCOM file presents a snapshot of an individual's genealogical data at a particular moment. As more information becomes available, or as updates occur, newer versions of the GEDCOM files can be created, which include the updated hash. This iterative process ensures the system remains flexible and responsive to changes and updates.
The GIP employs a unique signing scheme to manage hash-based dependencies and facilitate the instantiation of GEDCOM files. Once an individual finalizes their GEDCOM file, they sign it and send it to their parents. The parents then integrate the child's hash into their GEDCOM file, sign it, and send it back. The child then generates a new version of their GEDCOM file, incorporating the parent's signed hash, and signs the new version. This process can be replicated across multiple generations, resulting in an authenticated, complete family tree.
The integration of hash-based dependencies and the adaptive versioning system in the GIP addresses the challenges of digitally representing complex familial relationships. By accurately capturing these relationships and managing their inherent dynamism, the GIP extends the concept of digital identity beyond traditional paradigms, providing an identity model that truly reflects the complexities of human relationships in the digital era.
Digital signatures play a pivotal role within the Genealogical Identity Protocol (GIP), acting as the foundation for transaction authorization and asset ownership verification within the Bitcoin ecosystem. Their versatility extends across different contexts, allowing individuals to manage their degree of identity disclosure, ranging from full disclosure in high-stake environments like passport offices to pseudonymous interactions on low-risk platforms such as social media.
This flexibility stems from the GIP's unique approach to constructing signing identities. Depending on the context, various collections of data fields can be used to create distinct signing identities, encompassing realms such as business interactions, social media engagements, and legal proceedings. This approach integrates seamlessly with key management software, allowing users to authenticate with services requesting specific identity elements from their portfolio and GEDCOM file.
When a user needs to authenticate with a service that may request the signing of an engagement log, a fresh key can be generated. This key maps to the selective exposure of specific values from all the possible identity elements. The assignment of keys to identities hinges on the particular service's requirement threshold, allowing the system to cater to different levels of identity verification requirements, such as a "100 points of ID" system or a zero-knowledge proof verifying one's age.
Digital signatures within the GIP offer a robust method for secure transaction authorization and identity verification. This system bridges the gap between digital and physical identities and enables secure interactions within the Bitcoin ecosystem. The integration fosters trust and confidence in transactions and engagements within this ecosystem.
However, the practical application of digital signatures extends beyond transaction security. The GIP grants users control over the extent of their identity disclosure, accommodating the fluidity and complexity of modern identities. This user-centric approach ensures a high level of privacy, marking a significant departure from traditional identity verification methods, which often lack the adaptability to cater to the diverse needs of users in the digital era.
In essence, the implementation of digital signatures within the GIP signifies a remarkable innovation in digital identity management. By offering a secure, flexible, and user-focused system for identity management, the GIP adapts to the demands and intricacies of the modern world. This system empowers individuals to exercise control over their digital identities, emphasizing privacy and individuality.
An integral component of the Genealogical Identity Protocol (GIP) is its identity verification process. Relying on merkle proofs of subsets of data fields from a user's identity portfolio, corroborated by family tree data structures, the GIP forges a reliable and secure system for validating identities. The inherent interconnectedness and the unalterable nature of familial bonds create a robust infrastructure, enhancing the credibility of identity assertions.
These proofs offer a flexible system that can be tailored to various components of the identity protocol, adjusting to specific use cases. For example, certain proofs may necessitate a genealogical intersection with the societal family tree, while others could require attestations from designated experts in specific business sectors, with a particular emphasis on their intersector network connections.
This adaptive identity verification system contributes significantly to the robustness and integrity of the data. It safeguards privacy by managing the exposure of sensitive data through selective disclosure. Users can regulate who can access their data and under what conditions, fortifying privacy protection.
The GIP also introduces the concept of self-sovereign data markets. It empowers users to leverage their anonymized data by selling it while proving its genetic provenance and demographic suitability without disclosing personally identifiable information. This mechanism could stimulate the emergence of secondary data markets, where individuals have the freedom and security to benefit from their anonymized personal data.
Moreover, the GIP offers cryptographic assurances for identity verification. This includes the use of Public Key Infrastructure (PKI) to sign digital certificates, ensuring that the identity claims are made by the rightful owners and are not tampered with. The protocol also incorporates deterministic hash chains, which provide a way to construct a series of related hashes, where each hash depends on the previous one, adding an extra layer of security and traceability.
In summary, the GIP's approach to identity verification provides a comprehensive, resilient, and adaptable solution. Through a combination of merkle proofs, family tree data structures, expert attestations, and advanced cryptographic techniques, it addresses the complexities inherent in digital identities. Furthermore, by facilitating selective access management and the creation of secondary data markets, the GIP stands as a progressive, user-centric solution in the realm of digital identities.
The Genealogical Identity Protocol (GIP) is designed to work in harmony with a broad range of identity protocols worldwide, inclusive of self-sovereign identity (SSI) frameworks and internationally recognized standards like the ISO/IEC 29115:2013 entity authentication assurance framework. This interoperability is made possible due to the GIP's state-of-the-art key management strategies, ensuring secure interfacing with various external protocols while safeguarding user privacy.
Adherence to the principles of self-sovereign identity forms the backbone of GIP. Principles like user-centric control over personal data, transparency, interoperability, and persistent identities are central to its design. This foundational alignment enables GIP to integrate seamlessly with SSI frameworks that are being developed and adopted globally, empowering users to control, manage, and utilize their digital identities across a myriad of applications. These applications span from social media platforms to financial services, while concurrently preserving user privacy and data security.
Similarly, the GIP is compliant with international identity standards such as ISO/IEC 29115:2013, which provides an entity authentication assurance framework. Such compliance assures that GIP identities are recognized, verified, and accepted in the global digital market, opening doors to a wide spectrum of uses and applications for GIP users around the world. This adaptability positions the GIP as a flexible and universal solution for digital identity management, capable of operating within a diverse range of regulatory environments and digital ecosystems.
In essence, the GIP's global alignment and seamless integration with international identity standards, supplemented by its pioneering features and groundbreaking design, positions it as a versatile and forward-thinking solution for digital identity management. By offering a secure, adaptable, and user-centered approach to identity management, the GIP signifies a significant leap forward in the domain of digital identities. Its compatibility with a diverse range of international regulatory environments and digital ecosystems further amplifies its utility and potential, marking it as a valuable asset for individuals and organizations in today's digital world.
The Genealogical Identity Protocol (GIP) is constructed with regulatory compliance at its core, particularly focusing on key global standards such as the European General Data Protection Regulation (GDPR).
GDPR compliance is particularly significant as it's one of the most comprehensive and stringent data protection frameworks worldwide. It outlines robust requirements for the handling of personal data of EU citizens, including principles like data minimization, purpose limitation, accuracy, and the rights to access, rectification, and erasure.
In accordance with GDPR, the GIP's design ensures that only the minimum necessary data is collected and processed. All data handling operations align with the purpose limitation principle, i.e., data is used solely for the purpose for which it was collected, with no scope for unauthorized or unexpected usage. Data accuracy is maintained through the GIP's innovative use of blockchain technology, which enables secure, tamper-proof record-keeping.
Moreover, the GIP empowers users with direct control over their data. They can access their data at any time, correct inaccuracies, or request deletion of their data, aligning with the GDPR's rights of access, rectification, and erasure. This is made possible by the self-sovereign nature of the GIP, which places data control directly in the hands of the users.
The GIP's use of sophisticated cryptographic techniques for data protection further enhances its GDPR compliance. These techniques protect the confidentiality, integrity, and availability of data, meeting the GDPR's requirements for data security.
In addition, the GIP is designed to be adaptable to other data protection regulations globally. Its principles of minimal data collection, purpose limitation, accuracy, security, and user control are common to many data protection frameworks, making it easier for the GIP to comply with varying regulatory requirements across different jurisdictions.
In conclusion, the GIP's strong focus on regulatory compliance, especially with the GDPR, makes it a secure and trustworthy solution for digital identity management. By prioritizing user privacy and data protection, the GIP enables individuals to engage with digital services with confidence, safe in the knowledge that their personal data is being handled responsibly and securely.
The GIP, in alignment with Dan Roble's Innovation Bank model, considers an individual's genealogical data stored in GEDCOM files as a form of 'Knowledge Asset' (K-Asset). In this context, a K-Asset is seen as the fundamental unit of account, a validated claim that defines an individual's unique identity and family lineage.
Under the GIP, each GEDCOM file - a record of a user's family history - serves as a K-Asset, providing a vast repository of quantifiable and qualifiable data. This data not only holds the key to the individual's identity but also represents potential economic value that can be leveraged within the broader Bitcoin ecosystem.
Just as a physical asset like gold or real estate can be quantified (by weight or square footage, for instance) and qualitatively assessed (based on purity or location), so too can these K-Assets be evaluated. The quantity, in this case, refers to the extensive data points contained within each GEDCOM file, while the quality refers to the accuracy and completeness of this data, verified through the protocol's robust identity verification mechanisms.
The concept of K-Assets aligns with the growing global movement towards self-sovereign identity (SSI) protocols. These protocols aim to empower individuals by granting them ownership and control over their personal data. By framing genealogical data as a K-Asset, the GIP pioneers a new form of SSI, one that leverages the inherent value of an individual's family history.
In the larger context of network value (NV) versus hierarchy value (HV), the GIP serves as a decentralized network platform that emphasizes NV. Drawing parallels to modern platforms such as Google, Facebook, and AirBnB, the GIP aims to harness the power of network effects, using the interconnectedness of genealogical data to bridge disparate communities and derive value from the intrinsic coordination of those combined communities.
In this network-centric model, the value of the GIP ecosystem grows as more users join the network and contribute their unique genealogical data. This stands in contrast to traditional, hierarchy-based systems, which often struggle to adapt to the dynamic and diverse needs of users.
By categorizing genealogical data as K-Assets, the GIP pushes the boundaries of what constitutes an asset in the digital era, blurring the line between tangible and intangible, physical and digital. This unique approach empowers individuals to take control of their identities, facilitates the creation of secondary data markets, and paves the way for new forms of economic interaction within the Bitcoin ecosystem.
The Genealogical Identity Protocol (GIP) features an innovative mechanism known as 'Proof of Ancestor', which utilizes the power of genealogical data and advanced cryptography to deliver irrefutable proofs of lineage.
This unique system is founded upon the principle that each individual's unique lineage constitutes a secure backbone for their identity. By verifying their connection to specific ancestors, an individual bolsters the credibility and authenticity of their identity.
The implementation of 'Proof of Ancestor' extends beyond identity validation; it also provides a robust system for managing, authenticating, and preserving historical data. Through the digitization and encoding of each person's lineage into the Bitcoin blockchain via GEDCOM files, an immutable ancestral footprint is established. This information is openly verifiable, offering a decentralized method for maintaining and accessing historical data while maintaining privacy through pseudonymity.
Considering the complexity and depth of genealogical data, the GIP takes into account the management of discrepancies and potential conflicts inherent to this information. The 'Proof of Ancestor' mechanism allows for timestamped snapshots of an individual's genealogical data, serving as version-controlled GEDCOM files that encapsulate the historical progression of a person's lineage. This enables the verification of data consistency and coherence, ensuring its accuracy and authenticity over time.
An additional layer of security is established through the implementation of the Longest Common Substring (LCS) method. By verifying the longest unbroken chain of ancestors across different versions of GEDCOM files, the LCS process ensures data consistency and integrity, providing a robust defense against potential data tampering or falsification.
In conclusion, the 'Proof of Ancestor' mechanism within the GIP revolutionizes the concept of identity verification within the Bitcoin ecosystem. It not only enhances security and trust within the system, but it also pioneers a novel method of historical data preservation and access. The unique blend of genealogical data, advanced cryptographic techniques, and blockchain technology yields a secure, verifiable, and resilient approach to digital identity management.
To implement the GIP within the Bitcoin ecosystem, a step-by-step approach must be taken. The implementation can be outlined in four broad stages - Setup, Data Collection, Identity Verification, and Transaction Authorization.
To ensure the GIP's successful implementation, it is essential to conduct regular system audits and maintenance. This will help to identify and resolve any potential issues or vulnerabilities in the system, maintaining its effectiveness and security.
By following this approach, the Genealogical Identity Protocol can be successfully implemented within the Bitcoin ecosystem, providing a secure, reliable, and innovative solution for identity management. Its successful implementation will serve as a significant milestone in the development of the Bitcoin ecosystem, offering an unprecedented level of security and verifiability for digital identities.
The proposed software implementation to operationalize the Genealogical Identity Protocol (GIP) indeed requires careful planning and development. The outlined functionalities and user-experience are critical for adoption and efficient use of the system.
The first significant component is the software for creating and managing GEDCOM files. This software should enable users to input their data efficiently and accurately. It should guide users through the process, providing clear instructions and explanations to ensure the accuracy and completeness of the data. In addition, it should have features for checking and validating the data, including automated checks for common errors and inconsistencies.
A user-friendly interface is essential for broad adoption and efficient use of the software. It should be designed with a strong focus on usability and user experience, ensuring that even users who are not familiar with genealogical data or blockchain technologies can use the software effectively. The interface should be clean, intuitive, and responsive, with clear navigation and workflow.
The software should support an attestation mechanism, allowing users to pass their GEDCOM file to others for verification and attestation. Additionally, an inbuilt versioning system is crucial for managing updates and changes to the GEDCOM file. Users should be able to create new versions of their GEDCOM files, collect digital signature attestations on them, and easily traverse past versions.
To accommodate hash-based dependencies, the software needs to extend the data fields of the GEDCOM file. It should allow for the input of these dependencies in the connecting values between parent, child, and sibling nodes. This requires careful planning and design to ensure that the software can handle these additional data complexities effectively.
Alongside the GEDCOM file management software, a robust key management system needs to be developed. This system will manage the digital signatures used for transaction authorization and asset ownership verification. It must be secure, efficient, and able to handle high transaction volumes.
Once developed, the software should be thoroughly tested to ensure its functionality, performance, and reliability. It should be deployed in a phased manner within the Bitcoin ecosystem, with each phase thoroughly tested before moving to the next. This allows for any issues to be identified and addressed promptly.
Implementing this software solution is a considerable undertaking that will require a highly skilled and experienced development team. However, with careful planning and execution, it can significantly enhance the security, efficiency, and user experience of the GIP, driving its adoption and success.
Implementing data security and privacy protocols in the Genealogical Identity Protocol (GIP) is essential given the sensitive nature of the genealogical data stored in GEDCOM files. These protocols should be designed to protect the data from unauthorized access and ensure its confidentiality and integrity. Here are some key considerations:
Data Encryption: All genealogical data should be encrypted both at rest and in transit. Strong encryption algorithms should be employed to ensure that even if the data is intercepted or accessed without authorization, it cannot be read or used.
Access Controls: Proper access controls should be in place to ensure that only authorized individuals can access and modify the GEDCOM files. This can involve the use of multi-factor authentication, biometric verification, or other advanced security measures. Access controls should also be granular, allowing different levels of access and permissions based on roles and needs.
Data Minimization and Anonymization: Whenever possible, the system should use data minimization and anonymization techniques to limit the amount of personal data stored and processed. This can involve only collecting and storing the minimum necessary data, and anonymizing data when it is used for processing or analytics.
Secure Key Management: The key management system used for managing the digital signatures should be highly secure, and should follow best practices for key generation, storage, and use. This can involve the use of secure hardware for key storage, regular key rotation, and protocols for key revocation and renewal.
Auditing and Monitoring: The system should have robust auditing and monitoring capabilities to track all actions performed on the data. This can help detect any unauthorized or suspicious activities and respond quickly to any potential security incidents.
Privacy-by-Design: The system should follow the principles of privacy-by-design, which involves considering privacy and data protection issues at the design phase of the system, and building in appropriate safeguards from the start.
With these protocols in place, GIP can ensure robust security and privacy protection for the sensitive genealogical data it handles, building trust with its users and ensuring compliance with data protection laws and standards.
Gathering and inputting genealogical data is a vital step in the deployment of the Genealogical Identity Protocol (GIP). Here's how it can be effectively done:
Self-Reporting: Users within the Bitcoin ecosystem will self-report their genealogical data. This method puts users in control of their data, ensuring they are the primary source of their own genealogical information.
User Interface: The interface provided for data input should be intuitive and user-friendly, with fields clearly labeled and guidance provided for each input. This can be facilitated via an easy-to-use software application designed for creating and managing GEDCOM files. The software should guide the user through the data entry process, helping them understand what information is required, why it's needed, and how it will be used.
Data Validation: To ensure the accuracy of the genealogical data provided, there should be mechanisms in place to validate the data. This could involve automated checks for consistency and completeness, prompts for users to review and confirm their data, and support for verifying data through cross-referencing with other sources or users.
User Support: It is crucial to provide ample support to users throughout the data gathering and input process. This could involve tutorials, FAQ sections, and readily available customer service to guide users through the process.
Iterative Updates: Given the complexity and evolving nature of genealogical data, the system should support iterative updates. This feature would allow users to update their GEDCOM files as new information becomes available or as changes occur in their genealogical data.
Privacy and Consent: Before gathering and inputting genealogical data, the system should clearly inform users about the privacy measures in place to protect their data and obtain their informed consent. Users should have a clear understanding of how their data will be used, who will have access to it, and how they can control or update their information.
By adopting this structured and user-centric approach to data gathering, the GIP can ensure accurate and efficient collection of genealogical data, facilitating the creation of a secure and unique identity management system within the Bitcoin ecosystem.
Accuracy of genealogical data is a cornerstone in the successful implementation of the Genealogical Identity Protocol (GIP). Inaccuracies could indeed jeopardize the system's integrity and effectiveness. To maintain this accuracy, several measures could be undertaken:
User Education: Users should be educated about the importance of accurate data entry. They need to understand that the GIP relies on the correctness of this information for proper functioning. Guidelines and educational materials could be provided to users explaining the significance of accurate data, potential impacts of inaccuracies, and methods for ensuring the correctness of their entries.
Cross-Verification: The system could cross-verify the information provided by users against other reliable sources such as external genealogical databases or government records. While this might pose privacy challenges, it could be handled carefully with the consent of the users and by ensuring the confidentiality of the data checked.
Cryptographic Attestations: Another way to ensure data accuracy is by involving family members and relatives in the process. Once a user submits their genealogical data, relatives could provide cryptographic attestations confirming the correctness of the information. This not only ensures the accuracy of the data but also adds an additional layer of security to the system.
Continuous Updates and Correction: The GIP should be designed to allow for easy updates and corrections. As genealogical data is dynamic and might change or evolve over time, users should be able to make changes to their GEDCOM files as needed. They should be able to rectify any errors or update their files to reflect new information.
Auditing and Reporting Mechanisms: To maintain data accuracy, periodic audits could be performed. Users could also be encouraged to report any discrepancies they notice. This vigilance will help in maintaining the integrity of the system and ensuring its continuous accuracy.
By incorporating these measures, the GIP can ensure the accuracy and integrity of the genealogical data collected. This will not only make the system more reliable but also enhance its acceptance and credibility among users.
The fifth step in implementing the Genealogical Identity Protocol (GIP) is identity verification, a crucial component that ensures the authenticity of individuals within the Bitcoin ecosystem.
Utilizing GEDCOM Files for Identity Verification: Each GEDCOM file is unique to an individual and is the primary tool for identity verification within the GIP. These files serve as an individual's digital fingerprint within the system, representing their unique genealogical identity. When an individual makes a claim of identity, the system cross-references this claim against the data contained in the individual's GEDCOM file. This process provides a highly secure and reliable form of identity verification.
Cryptographic Methods in Identity Verification: The use of cryptographic methods such as digital signatures and hash-based dependencies significantly enhance the security and robustness of the identity verification process. Digital signatures authenticate the identity claims made by individuals, ensuring that the claim is indeed made by the individual it purports to be. Hash-based dependencies, on the other hand, ensure the integrity of the data within the GEDCOM files. By verifying that the genealogical data has not been tampered with, they provide an additional layer of security to the system.
Proof of Ancestor Mechanisms: These mechanisms offer an additional level of identity verification within the GIP. By cross-referencing an individual's genealogical data with other familial connections within the ecosystem, they verify the individual's lineage claims. This provides an extra layer of authentication, further bolstering the credibility and reliability of the system.
Ensuring a Trustworthy System: The identity verification process is integral to establishing a trustworthy GIP. By accurately verifying the identities of individuals within the Bitcoin ecosystem, it ensures the security and reliability of the system. The use of advanced cryptographic methods further mitigates the risk of fraudulent activity, fostering an environment of trust and confidence among users.
The identity verification process is a crucial component of the GIP implementation. It employs sophisticated cryptographic techniques to verify the authenticity of identity claims and to protect the integrity of the genealogical data. In doing so, it establishes a secure and trustworthy identity management system within the Bitcoin ecosystem
The final stage in the GIP implementation process, transaction authorization, ensures that every transaction made within the Bitcoin ecosystem is secure, authenticated, and involves identities that have been verified using the GEDCOM files. This is the stage where all the previous steps come together to provide a seamless and trustworthy transaction experience for all participants in the ecosystem.
Peer-to-Peer Transaction Model: A core aspect of transaction authorization in the GIP involves a move away from the traditional server-based model towards a peer-to-peer transaction model. This enables users to authenticate transactions directly with their network peers. This eliminates the need for a central server and provides a highly scalable network architecture that enables efficient transaction validation at the edge of the network.
Contact Books of Network Peers: In this system, users create 'contact books' or lists of network peers whom they can trust. These contact books are built based on validated GEDCOM files and verified identities, providing a solid foundation of trust within the network. Transactions can then be authorized directly between peers, leveraging the security and verification features provided by the GIP.
End-to-End Authentication: The GIP employs end-to-end authentication to secure the transactions that are passed from one peer to another. This advanced security feature ensures that only the intended receiver can accept and validate the transaction, thereby significantly reducing the chances of fraud or unauthorized access.
Double-Spend Check: Despite the decentralization of transaction authorization, a mechanism for preventing double-spending is necessary to maintain the integrity of the Bitcoin ecosystem. In this scenario, the 'small world miner network' comes into play. This network of miners, who are closer in terms of network hops, can efficiently verify whether the same Bitcoin has been spent twice, thereby maintaining the overall security of the system.
In conclusion, the transaction authorization phase is critical for implementing the GIP. By using a decentralized model and advanced security features, it ensures that transactions are secure, reliable, and involve only verified identities. This fosters an environment of trust and confidence, laying the groundwork for a secure and efficient system of identity management and transactions within the Bitcoin ecosystem.
##References Robles, D.R., Layton, B.E. "The Innovation Bank: Blockchain Technology and the Decentralization of the Engineering Professions." Available at: https://bico.media/7c7161e2ff483e6df2fdc4a4d5d0396811cd1defb74c96d0ecf44797ad0bde0b
Field 6
Optional. If provided, comprises a Regular Expression that is used to test if the message box is supported. If the regular expression passes, the PeerServ instance supports the message box. This is only an optional "fail fast" mechanism, and instance operators must be prepared to reject messages that are destined for users or message boxes they are unwilling to support.
Ty Everett ([email protected])
Brayden Langley ([email protected])
The importance of a standard interface by which Bitcoin applications can communicate with wallets and underlying infrastructure cannot be overstated. We propose an identity interface facilitating the creation of Bitcoin transactions, the use of user-held keys for encryption and digital signatures, and the employment of identity certificates to authenticate users with their counterparties. We set a baseline standard and create a versioning system that facilitates continued expansion and extensibility over time.
Computing has long been subject to shortfalls in the areas of information centralization and architectural cross-compatibility. Users on the internet struggle with complex and insecure authentication systems which leave them vulnerable and leak their data. Websites rely on advertising to monetize their offerings, but ads warp the incentives of platforms and creators in ways that ultimately harm everyone involved. By defining a standard interface by which users can identify themselves, protect their data and engage in e-commerce with Bitcoin, this standard offers a solution to problems that have long plagued the existing model.
We specify a baseline standard for an abstract messaging layer which facilitates an identity interface between an application and an underlying MetaNet Client. As part of this messaging layer, we incorporate various message types defined by other BRC standards:
: We incorporate by reference the message types relating to Bitcoin transaction creation as specified by . This facilitates micropayment-based interactions as part of applications, enabling new monetization models that are not subject to the same problematic incentives as internet-based advertising.
: We incorporate by reference the message types relating to encryption as specified by . This facilitates user privacy and provides a secure foundation for user-to-user communication, enabling applications which protect privacy to emerge and gain traction.
:
With these core components in place, we are left only with the need to specify a few other messages that are necessary to facilitate versioning, user network checking and user-to-wallet authentication status discovery. We now proceed to the specification of those auxiliary messages.
The introduction of HMACs are the most significant addition aside from the core message types. HMACs facilitate authenticating messages sent between users, and are generally useful for a wide range of applications.
We stipulate the following process for HMAC creation:
The message sender begins by computing the invoice number based on the security level, protocol ID, and key ID
The message sender uses key derivation with the computed invoice number to derive a child public key for the recipient
The message sender uses key derivation with the computed invoice number to derive their own child private key
We stipulate the following process for HMAC verification:
The recipient somehow comes to know the HMAC, the message, the counterparty, the security level, protocol ID, and key ID. The mechanism for conveying this information to the recipient is beyond the scope of this specification.
The recipient begins by computing the invoice number based on the security level, protocol ID, and key ID
The recipient uses key derivation with the computed invoice number, their own private key and the public key of the sender, to compute the sender's child public key
The HMAC Creation Request is a message sent by the application to the client. It contains a header with the following information:
The message payload comprises the data to HMAC.
The response message comprises a payload containing the computed HMAC value.
The HMAC Verification Request is a message sent by the application to the client. It contains a header with the following information:
The message payload comprises the data to verify against the HMAC provided in the header.
The response message comprises a payload containing a boolean that indicates whether the HMAC was successfully verified.
If the client is unable to fulfill the HMAC Creation or Verification Requests for any reason except failed verification, we specify that it should respond with a JSON-formatted HMAC Error. The fields for the object are specified as follows:
One example of an HMAC Error is given below:
Facilitating access to public keys and certificates are a crucial part of the wallet messaging layer as they provide the necessary components for identity verification. is used for defining the protocolID and keyID formats, and defines the format of certificates requested.
To request a public key, we define the following standard request fields:
When the wallet receives this request from the application, after obtaining requisite permission from the user, we stipulate the following process for key derivation:
If identityKey is true, the wallet returns its root public identity key to the application without proceeding to the other steps.
The wallet checks the invoice number based on the security level, protocol ID, and key ID
If forSelf is false, the wallet uses key derivation with the computed invoice number to derive a child public key for the counterparty, returning the public key.
The response message comprises a 33-byte, DER-encoded public key X coordinate value.
If the client is unable to fulfill the Public Key Request for any reason, we specify that it should respond with a JSON-formatted Public Key Error. The fields for the object are specified as follows:
One example of a Public Key Error is given below:
To request a list of identity certificates belonging to the user, we define these standard request fields:
When the wallet receives this message from the application, the wallet will ascertain based on user preferences and the information provided which certificates will be returned. The wallet is by no means required to return all responsive certificates, if for example the user does not want to reveal a particular affiliation in a particular context. The wallet composes a list, which may be empty, of responsive certificates.
The response from the wallet to the application comprises an array of identity certificates, which may be empty.
If the client is unable to fulfill the Certificate List Request for any reason except an empty list, we specify that it should respond with a JSON-formatted HMAC Error. The fields for the object are specified as follows:
One example of a Certificate List Error is given below:
Another important aspect of the wallet messaging layer is allowing applications the ability to check which Bitcoin network is in use, and whether or not a user has been authenticated.
To determine which Bitcoin network a user's MetaNet Client is currently using, applications can make requests to a standardized endpoint to ensure compatibility.
We define that the request message has no content (beyond the fact of it being a Bitcoin Network Request) and simply returns a string of either "test" or "main" indicating the current network in use.
We define the standard client version request to contain no parameters (beyond the fact of it being a Client Version Request) and to simply return the version of the MetaNet Client in use as a string.
For historical reasons, to denote compatibility with these specifications, we stipulate that the version should be in the range 0.3.x. For example, 0.3.80.
The authentication status request requires no parameters and returns a boolean indicating whether the current user is "authenticated" or not.
The purpose of this functionality is to facilitate software where the user can "log in" once in their client, and never need a separate login for MetaNet applications. When not authenticated, the application should assume that none of the other functionality, aside from client version checks, will work. It provides a way for applications, when they load, to ensure that the communication system with the client is operational, as a sort of "ping" request that also ensures the user is set up with a wallet.
The asynchronous authentication request requires no parameters and waits until the user is authenticated before returning a response of true.
Implementations of this specification will need to decide on a concrete transport layer (referred to as a "substrate") to facilitate communication between the wallet and applications. Application developers will then need to write their applications in a way that facilitates communication over the substrate, and wallets will need to properly handle incoming requests from applications.
Some examples of communication substrates include:
, for operating a wallet over HTTP on a local machine, enabling applications on that machine to interact with the running wallet.
, for cross-document messaging between a parent page which runs a wallet and a set of child pages which run various applications.
, for exposing an identity interface via the window object in web browsers.
The BRC-56 functions and messages, across these three substrates, have been implemented by the Babbage team in the .
Brayden Langley ([email protected])
Ty Everett ([email protected])
BRC-4: We incorporate by reference the extension to BRC-1 as defined by BRC-4, providing for the consumption and utilization of arbitrary UTXO-based Bitcoin tokens as part of transactions. This facilitates the creation and transfer between users of tokenized assets without restrictions beyond those set by their respective output locking scripts.
BRC-46: We incorporate by reference the extension to BRC-1 as defined by BRC-46, providing for the tracking and management of Bitcoin UTXOs in baskets. This facilitates the storage and retrieval of single-party tokens and digital assets in the user's wallet while removing the need to rely on a single trusted application for asset management.
BRC-50: We incorporate by reference the message types relating to incoming transaction submission as specified by BRC-50, ensuring users have a way to receive funds into their wallets from within applications. This gives them a way to receive payment for the value they create as part of their interactions within applications.
BRC-53: We incorporate by reference the message types relating to digital identity certificate creation and revelation, as specified by BRC-53. This provides a way for users to maintain and selectively reveal their identities with specific counterparties in the digital world, facilitating an increased level of trust and accountability.
The resulting elliptic curve point's X and Y values are hashed with SHA256 to create a SHA-256 HMAC key
The resulting 256-bit value is used to compute the SHA-256 HMAC for the message
The HMAC value is returned by the client to the application over the abstract BRC-1 communications substrate.
The recipient uses the same process to compute his own child private key
The recipient computes a shared secret between the two child keys, using the hash of the X and Y values as a SHA-256 HMAC key
The recipient then uses the HMAC key to compute the HMAC of the message, checking that the computed value matches the provided value
Otherwise, if forSelf is true, the wallet uses BRC-42 key derivation with the computed invoice number to derive their own child private key. The wallet then multiplies the private key by the generator point, G, to arrive at their own child public key, as would be derived by a counterparty. The computed public key is returned.
protocolID
The BRC-43 security level and protocol ID represented as an array. For example, [0, "hello world"] represents a level-0 protocol with open permissions, while [2, "document signing"] represents a level 2 protocol.
keyID
The BRC-43 key ID
counterparty
The BRC-43 counterparty, or self
protocolID
The BRC-43 security level and protocol ID represented as an array. For example, [0, "hello world"] represents a level-0 protocol with open permissions, while [2, "document signing"] represents a level 2 protocol.
keyID
The BRC-43 key ID
counterparty
The BRC-43 counterparty, or self
hmac
This is the HMAC value that is to be verified, for the provided message
status
This should always be a string comprising "error".
code
A machine-readable error code. Extensions to this standard can define specific error codes and standardize additional fields. Codes are strings, for example "ERR_PERMISSION_DENIED".
description
All errors must have a human-readable description field that describes the error. This allows the application to represent the error for the user.
protocolID
The BRC-43 security level and protocol ID represented as an array. For example, [0, "hello world"] represents a level-0 protocol with open permissions, while [2, "document signing"] represents a level 2 protocol.
keyID
The BRC-43 key ID
counterparty
The BRC-43 counterparty, self, or anyone
forSelf
Whether the derived child public key corresponds to a private key held by the current user. (optional, default false)
identityKey
If true, the identity key will be returned, and no key derivation will be performed (optional, default false). Overrides protocolID, keyID, counterparty, and forSelf.
status
This should always be a string comprising "error".
code
A machine-readable error code. Extensions to this standard can define specific error codes and standardize additional fields. Codes are strings, for example "ERR_PERMISSION_DENIED".
description
All errors must have a human-readable description field that describes the error. This allows the application to represent the error for the user.
certifiers
An array of the public keys for certifiers to filter certificates by: only certificates issued by these certifiers will be returned.
types
The certificate types to filter certificates by, given as an object whose keys are types and whose values are, for historical reasons, arrays of fields to request from certificates of the given type. Note that unless all fields are returned, no one can validate the certificate signature. We therefore specify that if true is provided in place of an array of fields, that all fields should always be returned.
status
This should always be a string comprising "error".
code
A machine-readable error code. Extensions to this standard can define specific error codes and standardize additional fields. Codes are strings, for example "ERR_PERMISSION_DENIED".
description
All errors must have a human-readable description field that describes the error. This allows the application to represent the error for the user.
{
"status": "error",
"code": "ERR_PERMISSION_DENIED",
"description": "The user has denied permission for computing an HMAC over this data."
}{
"status": "error",
"code": "ERR_PERMISSION_DENIED",
"description": "The user has denied permission for revealing their identity public key."
}{
"status": "error",
"code": "ERR_PERMISSION_DENIED",
"description": "The user has denied permission for obtaining a list of certificates."
}The motivation behind this standard is to address the limitations of digital signatures for verifying the true identity of internet users, a verification process which currently relies heavily on passwords and trusted third parties.
The goal is to create an open-ended, privacy-centric, and ubiquitous solution for digital identity certificates that facilitates selective revelation and UTXO-based revocation. This standard aims to provide a decentralized and reliable method for identity verification without compromising user privacy, and could be useful in various scenarios, including e-commerce, financial transactions, and secure communication.
By providing a secure method for linking a user's public key to their true identity, the standard would enable secure and private online transactions and collaborations, while also protecting against fraudulent activities and identity theft. By recognizing that key compromises do occur and supporting certificate revocation, we allow users to recover from breaches without tying their entire digital identity to a single key.
We start by specifying the data structures needed to represent various components of identity certificates, then proceed to key derivation schemes, signing and verification procedures, the secure verifier field revelation protocol, and finally UTXO-based revocation and responsible key compromise handling procedures.
PublicKey
The PublicKey structure is a compressed, DER-encoded secp256k1 public key (only the X coordinate), formatted as a 33-byte hexadecimal string.
Example:
02e087b19a205ae13f512341d1cfc0c712d66cacad24a809fce7edb3529e78b5da
Nonce
The Nonce structure is a 256-bit number, converted to a base64 string.
Example:
L9t1aQLkWkmMw1Poi1ofIjHV6GO+BNN63v8Na8/gW4Y=
Signature
The Signature structure is a DER-encoded ECDSA signature converted into a hexadecimal string.
Example
3045022100c0686907d8914abfa7f356c560c7d17045dde5c10135b1cbf9bf6e4cc52e09820220366eec15372bc34db10f997e21755f603a2023c96578e9d71fca2570f23055d6
RevocationOutpoint
The Outpoint structure is a Bitcoin SV TXID (a hexadecimal string) and output index (a decimal integer), separated by a decimal point (“.”). When a certificate outpoint has been spent, the certificate has been revoked.
Example
48645047cd66f7b48b24efb080ec7e27f3f448c21109939b80afd11e40898c92.1
CertificateTypeID
The CertificateTypeID structure is a 256-bit number that represents the distinct type or class of certificate issued by the certifier, converted to a base64 string. All certificate types with the same unique Type ID can be expected to have the same fields, and fields can be expected to have the same meanings.
Example
8l5phhdm2Hi80s6QqFOLS0NwUzDzJhlUTWv2BezmstE=
CertificateValidationKey
The CertificateValidationKey structure is a 256-bit key converted to a base64 string. The key is used by verifiers to check the signature on the certificate.
Example
G4pBfpBEZ7n5iEHrtG5MFOnIO0U1D758naHJilcK/RU=/7lzk4XTKI=+BNN63v8Na8/gW4Y=
CertificateSerialNumber
The CertificateSerialNumber structure is a 128-bit number that represents the unique identifier of the certificate, converted to a base64 string. No two certificates should have the same serial number, especially certificates from the same certifier or of the same type.
Example
kUahacBHmYL2nkzemkatFg==
CertificateFieldValue
The CertificateFieldValue structure is a piece of certified information that is encrypted with one of the CertificateFieldRevelationKeys. The 256-bit initialization vector used is prepended to the beginning of the data, and the AES-256-GCM algorithm is used. The structure is represented as a base64 string.
Example
IlgngMNcUM0d4GyzkSGqXaIBw6/n8bB6BG0pprO6oGhxUcivP6wDfuMWjWrG64KQJYUMN3QjHoX40r3SvAyWQaAA9ldVuPLgZwi9q2TCECKHCyEuiATCM9PdB/Ba2rxp
CertificateFieldRevelationKey
The CertificateFieldRevelationKey structure is a 256-bit AES-GCM encryption key used to decrypt and reveal one of the CertificateFieldValues. It is represented as a base64 string.
Example
ZG3C7x11lIeqd5Fi9BDzeTXMEAIlh+uDOB53d03EV5s=
CertificateFields
The CertificateFields structure is an object whose keys are certificate field names, and whose values are CertificateFieldValue structures.
Example
The CertificateFieldRevelationKeyring structure is an object whose keys are certificate field names and whose values are encrypted versions of the corresponding CertificateFieldRevelationKeys that decrypt them.
When a sender is communicating with a counterparty over a communications channel, the sender encrypts the field revelation keys by following the BRC-2 Encryption process (each CertificateFieldRevelationKey is encrypted with AES-256-GCM, and the 256-bit initialization vector is prepended to the ciphertext).
For each field revelation key that is included, the sender and recipient of the information use the following BRC-43 protocol and key IDs:
Attribute
Value
Security Level
2
Protocol ID
authrite certificate field encryption xxxxx
Key ID
yyyyy zzzzz
Counterparty
The sender uses the recipient's identity key, the recipient uses the sender's identity key.
Where:
xxxxx is the certificate type ID
yyyyy is the certificate Serial Number
zzzzz is the field name which the CertificateFieldRevelationKey decrypts
NOTE: The bearer of a certificate is not obligated to reveal the keys to all certificate fields at all times to all parties. Therefore, a Certificate structure that contains an incomplete CertificateFieldRevelationKeyring (lacking keys for certain fields) can still be considered valid. It is up to the verifier how much information they need to examine.
Example
The Certificate structure is an object with the following JSON fields:
type
A CertificateTypeID structure indicating the type of certificate.
subject
A PublicKey structure denoting the key belonging to the party being certified.
validationKey
A CertificateValidationKey structure representing the validation key of the certificate.
serialNumber
A CertificateSerialNumber structure representing the unique serial number of the certificate.
fields
A CertificateFields structure containing all of the encrypted fields that were certified by the certifier.
Example
The signature for a certificate is over all the fields, the subject, the certifier, the type, the serial number, the revocation outpoint, and the validation key. Each field is organized deterministically, in alphabetical order, in a JSON object. We stipulate the use of the (default) Stable Stringify Algorithm to avoid ambiguous ordering.
When the certificate is signed, we specify that the certifier utilizes the BRC-3 signing process with their certifier private key and the following BRC-43 attributes:
Attribute
Value
Security Level
2
Protocol ID
authrite certificate signature xxxxx
Key ID
yyyyy
Counterparty
The public key of the validation key included in the certificate.
Where:
xxxxx is the certificate type ID
yyyyy is the certificate Serial Number
NOTE: The validation key is part of the certificate, so anyone with this key can check the certificate signature.
For signature verification, we specify that the verifier checks the BRC-3 signature by using the certificate's validation key as the BRC-42 identity private key and the certifier's identity public key as the counterparty.
The subject (the person being certified) may use this key derivation scheme to derive the field revelation keys used to encrypt the various fields of their certificate, based on their own identity private key.
Attribute
Value
Security Level
2
Protocol ID
authrite certificate field xxxxx
Key ID
yyyyy zzzzz
Counterparty
self
Where:
xxxxx is the certificate type ID
yyyyy is the certificate Serial Number
zzzzz is the field name from the certificate
In order to preserve data integrity, we need a way to keep certificate fields encrypted such that only particular fields can be revealed as needed. We stipulate the use of a keyring structure for selective field revelation to verifiers. As the keyring is not included in the certificate's signature (only the encrypted field values themselves), verifiers can still check the authenticity of the certificate without access to all keyring keys.
When a prover reveals the fields to a verifier, they will follow these steps:
Retrieve the relevant symmetric field encryption keys
Encrypt the keys for the verifier following the BRC-2 process for counterparty encryption.
Construct the keyring with the field revelation keys and their associated field names according to the structure defined above.
For step 2, we stipulate the use of the following BRC-43 attributes when deriving the encryption keys:
Attribute
Value
Security Level
2
Protocol ID
authrite certificate field encryption
Key ID
xxxxx yyyyy
Counterparty
self
Where:
xxxxx is the certificate Serial Number
yyyyy is the field name from the certificate
It is strictly forbidden for a verifier to store or reveal the field revelation keys or the field values themselves to anyone else without the consent of the certificate holder. Doing so would be a violation of the terms of agreement and would result in the revocation of the certificate for that verifier.
One of the problems with identity certificates of the past is that certificate authorities would have to maintain a list of any certificates that were revoked due to key compromises, violation of the terms of agreements, or other reasons. Then anyone that wanted to check if a certificate was still valid would have to check against this list, assuming it wasn't compromised, to make sure it hadn't been revoked.
A simple solution to this problem is to make use of an associated Bitcoin transaction and output created at the time the certificate is issued. As described above, this revocation outpoint structure points to a UTXO that, if ever spent, indicates that the certificate is revoked.
This allows verifiers to easily check if a certificate has been revoked, without having to rely on a trusted centralized revocation list.
If the private key corresponding to a certificate issuer's public key ever becomes compromised, it is important that the issuer generate a new key pair, revoke existing certificates, and issue new certificates to the relevant certificate holders.
If one of the certificate holders has a key compromised, there needs to be a standard way to request a new certificate.
Certifiers should establish a process for coordinating with certificate holders to manage the reissuance of certificates.
Future BRCs can define protocols by which this process can occur.
An example implementation of Identity Certificates can be seen in Authrite, and the MYAC tutorial can be followed for creating your own Authrite Certifier.
Darren Kellenschwiler ([email protected]), Deggen Tone Engel ([email protected]), TonesNotes Ty Everett ([email protected]) Damian Orzepowski ([email protected]) Arkadiusz Osowski ([email protected])
We propose the BSV Unified Merkle Path format in both binary and JSON encoding optimized for generation by transaction processors, and also happens to be convenient for proof validating clients.
At a high level the format encodes a number of txids which all exist within one particular block, along with each of their merkle paths and the blockHeight.
The blockHeight is encoded first, followed by level 0 of the Merkle tree, which includes the txids of interest, and their corresponding siblings. Thereafter we encode each level of the tree thereafter, but only include branches of the tree which are required to calculate the Merkle root the txids which are of interest to us. For example if we only have one txid of interest, we will include it and its sibling, followed by one leaf per level of the tree.
{
“first_name”: “IlgngMNcUM0d4GyzkSGqXaIBw6/n8bB6BG0pprO6oGhxUcivP6wDfuMWjWrG64KQJYUMN3QjHoX40r3SvAyWQaAA9ldVuPLgZwi9q2TCECKHCyEuiATCM9PdB/Ba2rxp”,
“last_name”: “3o0jTew8WMW54svE8Btx9Aks1FlQCHOxGKLZpc7SR0ESSIomToAYFtCq6U7NUuf3ktwLgnv5XdCrCTo+mOTHl/H+oyjcxB7xfdwav+bBx6vG5y9voDI/Z2MQGxE/Pmeg”,
“address_line_1”: “WGqZGJo7VHeQ5tx7uqeyA5q08tRVxJLUoprjAiJ8FmzOoe4gt0+6PGDgvrhkbzauSxGY9yRyOeP+irHclCIrzjwjbfkK19thoyUhr8u1283FobikPs2xKrhUvcYhDOV7”,
“address_line_2”: “IjI5UXJnMbcSEqIVkH8B5a9cY8EtWO7RvJH9gAWuIq0Y3koINgfqNjmockAgXnGxLvdQXvWdlCIm+foedIdHhqs9Xi60wR7hLSs9emjo6nq1Mx01wYFRW1le//Sup80x”,
“city”: “Ed4Nr8UL3h7AqX3PlExwLQ0Mi/QLBH9wCIpdeMb442HDbQlodF1eUuJQRXt+0ajzmXEqaGNSD59xEXmsFouLDLa6sOOzXr2psCKMXkU1+IFHGv8TgJx/vDTCxMvL7t2T”,
“state”: “6oiC8AMFFrLRGhpbWUO1HkA05/QPmfaUg1be4cP2vbcadIv4quaBgfwmNWE6xItpQNAfEwrx1ub9b4cNIKnN+6JAsiDiqs42mbjsgTuZL90fSEL9yPTwRuvW1ccvJ43z”,
“zipcode”: “Y+iqr8cSMEMMq8WqpIudzni1DIiQT5BsK29ld8lFCZddrrjep0qQyKoaP9+46ikIu9VOQIoOJayhiW7KifUSYt+D/WODyPei4Ru7tqfgR47Vo4u0EFI3KLBXyC3DgiN+”
}{
“first_name”: “G2GmxIWNPkO2SUP0XpQvQqX31yz58aLHk9e7JxO+q/MT3k9ZjTe3t8aN2Qwg2+AtBulXZpKhuvxOgxicfd8GqCjFkGtyUrbSdmCFjPUJm7z/P/LWkJHByeBzh3/yOWa7”,
“last_name”: “YZAHgs+P8Tgci3+5uuAuHr0k3CXPEcB3trpqtEUygV+uwoTgDWk01XTT1eOW6n7qjcDt4g7tlzuWtkawDkvRbCcVe3oBsEI5X8+A10s8TAsyNQV5O1R8mkGoL8/Hec4K”
}{
“type”: ”8l5phhdm2Hi80s6QqFOLS0NwUzDzJhlUTWv2BezmstE=”,
“subject”: ”0376d67c86b45be3c36c328c2aa5c5dd79c546d22859e39439bd5d934058345abc”,
“validationKey”: ”G4pBfpBEZ7n5iEHrtG5MFOnIO0U1D758naHJilcK/RU=/7lzk4XTKI=+BNN63v8Na8/gW4Y=”,
“serialNumber”: ”kUahacBHmYL2nkzemkatFg==”,
“fields”: {
“first_name”: “IlgngMNcUM0d4GyzkSGqXaIBw6/n8bB6BG0pprO6oGhxUcivP6wDfuMWjWrG64KQJYUMN3QjHoX40r3SvAyWQaAA9ldVuPLgZwi9q2TCECKHCyEuiATCM9PdB/Ba2rxp”,
“last_name”: “3o0jTew8WMW54svE8Btx9Aks1FlQCHOxGKLZpc7SR0ESSIomToAYFtCq6U7NUuf3ktwLgnv5XdCrCTo+mOTHl/H+oyjcxB7xfdwav+bBx6vG5y9voDI/Z2MQGxE/Pmeg”,
“address_line_1”: “WGqZGJo7VHeQ5tx7uqeyA5q08tRVxJLUoprjAiJ8FmzOoe4gt0+6PGDgvrhkbzauSxGY9yRyOeP+irHclCIrzjwjbfkK19thoyUhr8u1283FobikPs2xKrhUvcYhDOV7”,
“address_line_2”: “IjI5UXJnMbcSEqIVkH8B5a9cY8EtWO7RvJH9gAWuIq0Y3koINgfqNjmockAgXnGxLvdQXvWdlCIm+foedIdHhqs9Xi60wR7hLSs9emjo6nq1Mx01wYFRW1le//Sup80x”,
“city”: “Ed4Nr8UL3h7AqX3PlExwLQ0Mi/QLBH9wCIpdeMb442HDbQlodF1eUuJQRXt+0ajzmXEqaGNSD59xEXmsFouLDLa6sOOzXr2psCKMXkU1+IFHGv8TgJx/vDTCxMvL7t2T”,
“state”: “6oiC8AMFFrLRGhpbWUO1HkA05/QPmfaUg1be4cP2vbcadIv4quaBgfwmNWE6xItpQNAfEwrx1ub9b4cNIKnN+6JAsiDiqs42mbjsgTuZL90fSEL9yPTwRuvW1ccvJ43z”,
“zipcode”: “Y+iqr8cSMEMMq8WqpIudzni1DIiQT5BsK29ld8lFCZddrrjep0qQyKoaP9+46ikIu9VOQIoOJayhiW7KifUSYt+D/WODyPei4Ru7tqfgR47Vo4u0EFI3KLBXyC3DgiN+”
},
“certifier”: ”035ce8cc44dbcf4c991d666d381d67263aed9123f9b15cca215b54f1314152d23c”,
“revocationOutpoint”: “48645047cd66f7b48b24efb080ec7e27f3f448c21109939b80afd11e40898c92.1”,
“signature”: “3045022100c0686907d8914abfa7f356c560c7d17045dde5c10135b1cbf9bf6e4cc52e09820220366eec15372bc34db10f997e21755f603a2023c96578e9d71fca2570f23055d6”,
“keyring”: {
“first_name”: “G2GmxIWNPkO2SUP0XpQvQqX31yz58aLHk9e7JxO+q/MT3k9ZjTe3t8aN2Qwg2+AtBulXZpKhuvxOgxicfd8GqCjFkGtyUrbSdmCFjPUJm7z/P/LWkJHByeBzh3/yOWa7”,
“last_name”: “YZAHgs+P8Tgci3+5uuAuHr0k3CXPEcB3trpqtEUygV+uwoTgDWk01XTT1eOW6n7qjcDt4g7tlzuWtkawDkvRbCcVe3oBsEI5X8+A10s8TAsyNQV5O1R8mkGoL8/Hec4K”
}
}certifier
A PublicKey structure denoting the well-known public key of the entity issuing the certificate.
revocationOutpoint
A RevocationOutpoint structure pointing to a Bitcoin SV UTXO. If the UTXO has been spent by the certifier, the certificate should be considered revoked. If all zeroes, this functionality is disabled for the certificate. Disabling UTXO-based revocation should be in accordance with the rules defining this certificate type.
signature
A Signature structure denoting the signature made by the certifying entity over the certified fields of the subject.
keyring
A CertificateRevelationKeyring structure containing keys for some or all of the fields of the certificate. If no fields are being revealed, this is optional.

This BRC is licensed under the Open BSV license.
BUMP Showcase can help form an understanding of how BUMP works to encode all necessary data.
Several formats have made their own improvements to the original format which was returned by a Bitcoin node via json-rpc method getmerkleproof.
Improvements include:
BRC-10 a TSC creation which was subsequently returned by the node's json-rpc method getmerkleproof2
BRC-11 removing the need for specifying targets, replacing with height to improve validation speed.
BRC-58 removal of all extraneous data to minimize data size.
introduction of a compound path encoding which allows representation of multiple paths within the same block.
The purpose of defining this new specification is to capture the incremental improvements under one spec which encapsulates the pros of each, and removes the cons. This new spec should allow:
Inclusion of height makes lookup extremely fast while only adding maximum 9 bytes to the data size.
Multiple paths can be expressed in the same data model.
One format for everything, so that there is no need to convert from single to compound path.
Size optimization allowing us to skip encoding of far right leaves when duplication of working hash would suffice.
The top level encoding specifies a block height and a tree height.
block height
VarInt block height in which the transactions are encapsulated
1-9 bytes
tree height
The height of the Merkle Tree in this block, max 64
1 byte
Thereafter the number of leaves at the top height is specified, and the leaves for this height follow.
nLeaves
VarInt number of leaves at this height
1-9 bytes
leaves
Each leaf encoded in the format below.
sum of leaf sizes
Once all leaves at this height have been specified, an implied increment of the height in the tree occurs and we specify the number of leaves in the next level up, and so on until we have specified the leaves at level (treeHeight - 1) at which point we stop. We do not need to encode the root hash as it is always calculable.
offset
VarInt offset from left hand side within tree
1-9 bytes
flags
Flags can be 00, or 01, or 02 - detailed meaning in table below
1 byte
hash
A hash representing a txid, sibling hash, or a branch
0 or 32 bytes
The first flag is to indicate whether or not to duplicate the working hash or use the following data. The second flag indicates whether the hash is a relevant txid or just a sibling hash.
0000 0000
00
data follows, not a client txid
0000 0001
01
nothing follows, duplicate working hash
0000 0010
02
data follows, and is a client txid
In the JSON encoding - we start with a height of a block in which transactions from BUMP are mined. A path Array index corresponds to the height within the Merkle tree, so we start with level 0 which includes all of the txid's of interest to a client in this block and the txid's of additional transactions required to begin the merkle root computation. Within each array element we contain an array of one or more leaves which are specified as a leaf.
Within the leaf itself we have am offset - the only required parameter, along with optional hash, txid and duplicate. The hash is a hex string encoding reversed bytes of the hash at this position in the Merkle tree, the duplicate true is a boolean and represents a "no data" for this position, this is to encode for the right hand side of the merkle tree. The expected behavior is for a parser to duplicate the working hash in this case, therefore no further data is required. A txid boolean is included if true - to indicate whether the hash in question is considered a relevant txid to the receiving party, rather than just a sibling hash needed to calculate the root.
Let's start by dumping this format as hex into a Buffer in JavaScript and parsing it into an object with a Buffer Reader. Then we can calculate the merkle root from any of the included txids.
A note on compounding multiple BUMPs together. The first check should always be the blockHeight - ensure it matches. The second check is the root. Each BUMP calculates its root, and if they don't match - you cannot combine them. If they match then the process is a simple inclusion of all leaves, dropping duplicates.
Ty Everett ([email protected])
This specification defines Authrite, a system for the mutual authentication of two parties over a communications channel. It facilitates the exchange of identity certificates between users, allowing them to certify each other's identities and share identity information selectively. Authrite leverages the Identity Keys of MetaNet Users and relies on Permissions and Certificate management functions to allow Users to control how identity information is shared. This specification defines the nonce exchanges, certificate and field exchange negotiation protocol, and message signing scheme for two-party Authrite communications channels.
fe8a6a0c000c04fde80b0011774f01d26412f0d16ea3f0447be0b5ebec67b0782e321a7a01cbdf7f734e30fde90b02004e53753e3fe4667073063a17987292cfdea278824e9888e52180581d7188d8fdea0b025e441996fc53f0191d649e68a200e752fb5f39e0d5617083408fa179ddc5c998fdeb0b0102fdf405000671394f72237d08a4277f4435e5b6edf7adc272f25effef27cdfe805ce71a81fdf50500262bccabec6c4af3ed00cc7a7414edea9c5efa92fb8623dd6160a001450a528201fdfb020101fd7c010093b3efca9b77ddec914f8effac691ecb54e2c81d0ab81cbc4c4b93befe418e8501bf01015e005881826eb6973c54003a02118fe270f03d46d02681c8bc71cd44c613e86302f8012e00e07a2bb8bb75e5accff266022e1e5e6e7b4d6d943a04faadcf2ab4a22f796ff30116008120cafa17309c0bb0e0ffce835286b3a2dcae48e4497ae2d2b7ced4f051507d010a00502e59ac92f46543c23006bff855d96f5e648043f0fb87a7a5949e6a9bebae430104001ccd9f8f64f4d0489b30cc815351cf425e0e78ad79a589350e4341ac165dbe45010301010000af8764ce7e1cc132ab5ed2229a005c87201c9a5ee15c0f91dd53eff31ab30cd4fe8a6a0c00 // blockHeight (813706), VarInt
0c // treeHeight (12), byte
// Level 0, client TXIDs and sibling TXIDs (TXIDs required only to compute internal tree hash).
04 // nLeaves, VarInt
fde80b // offset, VarInt
00 // flags
11774f01d26412f0d16ea3f0447be0b5ebec67b0782e321a7a01cbdf7f734e30 // hash
fde90b // offset VarInt
02 // flags = CLIENT_TXID
004e53753e3fe4667073063a17987292cfdea278824e9888e52180581d7188d8 // hash
fdea0b // offset VarInt
02 // flags = CLIENT_TXID
5e441996fc53f0191d649e68a200e752fb5f39e0d5617083408fa179ddc5c998 // hash
fdeb0b // offset VarInt
01 // flags = DUPLICATE_WORKING_HASH
// Level 1, internal merkle tree hashes
02 // nLeaves, VarInt
fdf405 // offset, VarInt
00 // flags
0671394f72237d08a4277f4435e5b6edf7adc272f25effef27cdfe805ce71a81 // hash
fdf505 // offset VarInt
00 // flags
262bccabec6c4af3ed00cc7a7414edea9c5efa92fb8623dd6160a001450a5282 // hash
// Level 2, internal merkle tree hashes
01 // nLeaves VarInt at level 2
fdfb02 // offset VarInt
01 // flags = DUPLICATE_WORKING_HASH
// Level 3, internal merkle tree hashes
01 // nLeaves VarInt at level 3
fd7c01 // offset VarInt (three hundred and eighty)
00 // flags
93b3efca9b77ddec914f8effac691ecb54e2c81d0ab81cbc4c4b93befe418e85 // hash
// Level 4, internal merkle tree hashes
01 // nLeaves VarInt at level 4
bf // offset VarInt
01 // flags = DUPLICATE_WORKING_HASH
// Level 5, internal merkle tree hashes
01 // nLeaves VarInt at level 5
5e // offset VarInt
00 // flags
5881826eb6973c54003a02118fe270f03d46d02681c8bc71cd44c613e86302f8 // hash
// Level 6, internal merkle tree hashes
01 // nLeaves VarInt at level 6
2e // offset VarInt
00 // flags
e07a2bb8bb75e5accff266022e1e5e6e7b4d6d943a04faadcf2ab4a22f796ff3 // hash
// Level 7, internal merkle tree hashes
01 // nLeaves VarInt at level 7
16 // offset VarInt
00 // flags
8120cafa17309c0bb0e0ffce835286b3a2dcae48e4497ae2d2b7ced4f051507d // hash
// Level 8, internal merkle tree hashes
01 // nLeaves VarInt at level 8
0a // offset VarInt
00 // flags
502e59ac92f46543c23006bff855d96f5e648043f0fb87a7a5949e6a9bebae43 // hash
// Level 9, internal merkle tree hashes
01 // nLeaves VarInt at level 9
04 // offset VarInt
00 // flags
1ccd9f8f64f4d0489b30cc815351cf425e0e78ad79a589350e4341ac165dbe45 // hash
// Level 10, internal merkle tree hashes
01 // nLeaves VarInt at level 10
03 // offset VarInt
01 // flags = DUPLICATE_WORKING_HASH
// Level 11, internal merkle tree hashes
01 // nLeaves VarInt at level 11
00 // offset VarInt
00 // flags
af8764ce7e1cc132ab5ed2229a005c87201c9a5ee15c0f91dd53eff31ab30cd4 // hash{
"blockHeight": 813706,
"path": [
[
{
"offset": 3048,
"hash": "304e737fdfcb017a1a322e78b067ecebb5e07b44f0a36ed1f01264d2014f7711"
},
{
"offset": 3049,
"txid": true,
"hash": "d888711d588021e588984e8278a2decf927298173a06737066e43f3e75534e00"
},
{
"offset": 3050,
"txid": true,
"hash": "98c9c5dd79a18f40837061d5e0395ffb52e700a2689e641d19f053fc9619445e"
},
{
"offset": 3051,
"duplicate": true
}
],
[
{
"offset": 1524,
"hash": "811ae75c80fecd27efff5ef272c2adf7edb6e535447f27a4087d23724f397106"
},
{
"offset": 1525,
"hash": "82520a4501a06061dd2386fb92fa5e9ceaed14747acc00edf34a6cecabcc2b26"
}
],
[
{
"offset": 763,
"duplicate": true
}
],
[
{
"offset": 380,
"hash": "858e41febe934b4cbc1cb80a1dc8e254cb1e69acff8e4f91ecdd779bcaefb393"
}
],
[
{
"offset": 191,
"duplicate": true
}
],
[
{
"offset": 94,
"hash": "f80263e813c644cd71bcc88126d0463df070e28f11023a00543c97b66e828158"
}
],
[
{
"offset": 46,
"hash": "f36f792fa2b42acfadfa043a946d4d7b6e5e1e2e0266f2cface575bbb82b7ae0"
}
],
[
{
"offset": 22,
"hash": "7d5051f0d4ceb7d2e27a49e448aedca2b3865283ceffe0b00b9c3017faca2081"
}
],
[
{
"offset": 10,
"hash": "43aeeb9b6a9e94a5a787fbf04380645e6fd955f8bf0630c24365f492ac592e50"
}
],
[
{
"offset": 4,
"hash": "45be5d16ac41430e3589a579ad780e5e42cf515381cc309b48d0f4648f9fcd1c"
}
],
[
{
"offset": 3,
"duplicate": true
}
],
[
{
"offset": 0,
"hash": "d40cb31af3ef53dd910f5ce15e9a1c20875c009a22d25eab32c11c7ece6487af"
}
]
]
}const { createHash } = require('crypto')
const { Br, Bw } = require('bsv')
// Displaying hashes as hex strings in reverse byte order is a matter of convention with respect to txids.
// The functions below handle the conversions such that when we "hash()" something, we are running sha256 -
// digesting the reverse bytes of a hex string, and returning the reverse bytes encoded as a hex string.
const hexRevToBuf = (str) => Buffer.from(str, 'hex').reverse()
const bufRevToHex = (buf) => Buffer.from(buf.toString('hex'), 'hex').reverse().toString('hex')
const hash = (str) => bufRevToHex(createHash('sha256').update(createHash('sha256').update(hexRevToBuf(str)).digest()).digest())
function bumpHexToJSON(str) {
const reader = new Br()
reader.buf = Buffer.from(str, 'hex')
let blockHeight = reader.readVarIntNum()
let treeHeight = parseInt(reader.read(1).toString('hex'), 16)
let path = Array(treeHeight).fill(0).map(() => ([]))
let flags, offset, nLeavesAtThisHeight
for (let level = 0; level < treeHeight; level++) {
nLeavesAtThisHeight = reader.readVarIntNum()
while (nLeavesAtThisHeight) {
offset = reader.readVarIntNum()
flags = parseInt(reader.read(1).toString('hex'), 16)
const leaf = { offset }
if (flags & 1) {
leaf.duplicate = true
} else {
if (flags & 2) leaf.txid = true
leaf.hash = reader.read(32).reverse().toString('hex')
}
path[level].push(leaf)
nLeavesAtThisHeight--
}
path[level].sort((a, b) => a.offset - b.offset)
}
return { blockHeight, path }
}
function bumpJSONtoHex({ blockHeight, path }) {
const bw = new Bw()
bw.writeVarIntNum(blockHeight)
let treeHeight = path.length
bw.writeUInt8(treeHeight)
for (let level = 0; level < treeHeight; level++) {
let nLeaves = Object.keys(path[level]).length
bw.writeVarIntNum(nLeaves)
for (const leaf of path[level]) {
bw.writeVarIntNum(leaf.offset)
let flags = 0
if (!!leaf?.duplicate) flags |= 1
if (!!leaf?.txid) flags |= 2
bw.writeUInt8(flags)
if ((flags & 1) === 0)
bw.write(hexRevToBuf(leaf.hash))
}
}
return bw.toBuffer().toString('hex')
}
function calculateMerkleRootFromBUMP(bump, txid) {
// Find the index of the txid at the lowest level of the Merkle tree
const index = bump.path[0].find(l => l.hash === txid).offset
if (!index) throw Error(`The BUMP does not contain the txid: ${txid}`)
// Calculate the root using the index as a way to determine which direction to concatenate.
let workingHash = txid
bump.path.map((leaves, height) => {
const offset = index >> height ^ 1
const leaf = leaves.find(l => l.offset === offset)
if (!leaf) throw new Error(`We do not have a hash for this index at height: ${height}`)
if (leaf.duplicate) {
workingHash = hash(workingHash + workingHash)
} else if (offset % 2) {
workingHash = hash(leaf.hash + workingHash)
} else {
workingHash = hash(workingHash + leaf.hash)
}
})
return workingHash
}
const bump = bumpHexToJSON('fe8a6a0c000c04fde80b0011774f01d26412f0d16ea3f0447be0b5ebec67b0782e321a7a01cbdf7f734e30fde90b02004e53753e3fe4667073063a17987292cfdea278824e9888e52180581d7188d8fdea0b025e441996fc53f0191d649e68a200e752fb5f39e0d5617083408fa179ddc5c998fdeb0b0102fdf405000671394f72237d08a4277f4435e5b6edf7adc272f25effef27cdfe805ce71a81fdf50500262bccabec6c4af3ed00cc7a7414edea9c5efa92fb8623dd6160a001450a528201fdfb020101fd7c010093b3efca9b77ddec914f8effac691ecb54e2c81d0ab81cbc4c4b93befe418e8501bf01015e005881826eb6973c54003a02118fe270f03d46d02681c8bc71cd44c613e86302f8012e00e07a2bb8bb75e5accff266022e1e5e6e7b4d6d943a04faadcf2ab4a22f796ff30116008120cafa17309c0bb0e0ffce835286b3a2dcae48e4497ae2d2b7ced4f051507d010a00502e59ac92f46543c23006bff855d96f5e648043f0fb87a7a5949e6a9bebae430104001ccd9f8f64f4d0489b30cc815351cf425e0e78ad79a589350e4341ac165dbe45010301010000af8764ce7e1cc132ab5ed2229a005c87201c9a5ee15c0f91dd53eff31ab30cd4')
calculateMerkleRootFromBUMP(bump, '304e737fdfcb017a1a322e78b067ecebb5e07b44f0a36ed1f01264d2014f7711') // '57aab6e6fb1b697174ffb64e062c4728f2ffd33ddcfa02a43b64d8cd29b483b4'
calculateMerkleRootFromBUMP(bump, 'd888711d588021e588984e8278a2decf927298173a06737066e43f3e75534e00') // '57aab6e6fb1b697174ffb64e062c4728f2ffd33ddcfa02a43b64d8cd29b483b4'
calculateMerkleRootFromBUMP(bump, '98c9c5dd79a18f40837061d5e0395ffb52e700a2689e641d19f053fc9619445e') // '57aab6e6fb1b697174ffb64e062c4728f2ffd33ddcfa02a43b64d8cd29b483b4'
bumpJSONtoHex(bump)function combinePaths(one, two) {
if (one.blockHeight !== two.blockHeight)
throw Error('You cannot combine paths which do not have the same blockHeight.')
const txid1 = one.path[0].find(leaf => !!leaf?.hash).hash
const root1 = calculateMerkleRootFromBUMP(one, txid1)
const txid2 = two.path[0].find(leaf => !!leaf?.hash).hash
const root2 = calculateMerkleRootFromBUMP(two, txid2)
if (root1 !== root2)
throw Error('You cannot combine paths which do not have the same root.')
const combinedPath = []
for (let h = 0; h < one.path.length; h++) {
combinedPath.push([])
for (let l = 0; l < one.path[h].length; l++) {
combinedPath[h].push(one.path[h][l])
}
for (let l = 0; l < two.path[h].length; l++) {
if (!combinedPath[h].find(leaf => leaf.offset === two.path[h][l].offset)) {
combinedPath[h].push(two.path[h][l])
} else {
// Ensure that any elements which appear in both are not downgraded to a non txid.
if (!!two.path[h][l]?.txid)
combinedPath[h].find(leaf => leaf.offset === two.path[h][l]).txid = true
}
}
}
return { blockHeight: one.blockHeight, path: combinedPath }
}Identity on the internet relies heavily on trusted third parties to authenticate users. However, this centralization creates potential security and privacy risks, as third parties may collect and misuse user data. BRC-52 addresses this problem by introducing a privacy-centric version of digital identity certificates that facilitate selective revelation and UTXO-based revocation. This standard builds upon BRC-52 by providing a secure and efficient mechanism for exchanging identity information between users over an authenticated communications channel. By leveraging the Identity Keys of MetaNet Users, Authrite provides a decentralized approach to mutual authentication that reduces reliance on third parties and thereby enhances user privacy.
Authrite facilitates the exchange of identity information between two parties over a communications channel, allowing them to mutually authenticate each other. The following sections describe the requisite data structures, nonce exchanges, negotiation protocol, and message signing scheme for Authrite.
When Alice initiates communication with Bob, she requests Bob's identity public key, and optionally, a list of certificate type IDs and certifiers she trusts. Bob responds with his identity public key, any certificates that Alice requested, and a list of certificate type IDs and certifiers he needs from Alice. To verify Bob's information, Alice validates his signature, checks that the certified key from all certificates matches his identity public key, and decrypts certificate fields with provided keys. If Alice has met Bob's certification requirements, she may send any messages of her choosing to Bob, including certificates he indicated he could trust. Bob verifies Alice's information and may respond with messages and certificates of his own. If at any point either party wants to re-scope the certificate inclusion in messages, they can initiate a new request for the other's identity public key.
We incorporate by reference all the data structures that were defined by BRC-52. Nonces, as well as some other necessary higher-level data structures are specified as follows:
The Nonce structure is a 256-bit number, converted to a base64 string.
Example
The RequestedCertificateCertifierList structure is an array of PublicKey structures, each denoting a certifier which the sending party will trust.
Example
The RequestedCertificateTypeIDAndFieldList structure is an object whose keys are CertificateTypeID structures and whose values are each an array of field names from certificates of that type which the sending party requests to examine.
Example
The RequestedCertificateSet structure is an object with the following JSON fields:
certifiers — A RequestedCertificateCertifierList structure denoting the certifiers trusted by the sending party.
types — A RequestedCertificateTypeIDAndFieldList structure denoting the types of certificates trusted by the sending party, and which fields from each type the sending party would like to examine.
Example
The CertificateList structure is an array comprising a list of Certificate structures, given in response to a RequestedCertificateSet structure.
Example
When Alice starts an interaction with Bob, she sends a message with the following JSON fields:
authrite — The version of this specification to use (currently 0.1).
messageType — For Alice’s initial message, this should be initialRequest
identityKey — A PublicKey structure comprising the Identity Public Key belonging to Alice.
nonce — A Nonce structure which was randomly generated by Alice.
requestedCertificates — A RequestedCertificateSet structure indicating which certificates Bob should include in future messages, if he has them. If Alice does not require certificates from Bob, this is optional.
NOTE that Alice cannot create a signature for this initial request, because she does not yet know Bob’s identity public key.
Example
When Bob receives the Initial Request message from Alice, he responds by sending a message with the following JSON fields:
authrite — The version of this specification to use (currently 0.1).
messageType — For Bob’s initial response this should be initialResponse
identityKey — A PublicKey structure comprising the Identity Public Key belonging to Bob.
nonce — A Nonce structure which was randomly generated by Bob.
certificates — A CertificateList structure created in response to Alice’s RequestedCertificateSet: Bob includes every certificate he has matching the CertificateTypeIDs Alice specified, as long as it was issued by one of Alice’s trusted certifiers. If Alice did not ask for any certificates, or if Bob does not have (or wish to provide) any of the certificates which Alice specified, this is optional.
requestedCertificates — A RequestedCertificateSet structure indicating which certificates Alice should include in her future messages, if she has them. If Bob does not require certificates from Alice, this is optional.
signature — A Signature structure created in accordance with the Message Signing section.
Example
Bob's Initial Message Signing
Attribute
Value
Security Level
2
Protocol ID
authrite message signature
Key ID
xxxxx yyyyy
Counterparty
Alice's public key.
Where:
xxxxx is the nonce provided by Alice, and
yyyyy is the nonce provided by Bob.
The data to be signed by Bob with this key is the nonce provided by Alice, concatenated with the nonce Bob is sending back.
Bob's Initial Message Verification
When Alice receives the initialResponse message type from Bob, she will verify the BRC-3 signature with the corresponding values, with Bob as counterparty.
After checking that the signature is valid, Alice then checks that the subject of all the provided certificates is equal to Bob’s identity public key. She then moves on to checking the validity of the certificates themselves, as per BRC-52.
After the initialRequest and initialResponse message types have been exchanged, either party can exchange general messages that include arbitrary signed payloads. These payloads constitute the authenticated communications channel defined by Authrite. The messages should generally have fields that convey the following information:
authrite — The version of this specification to use (currently 0.1).
identityKey — A PublicKey structure comprising the Identity Public Key belonging to the sender.
nonce — A Nonce structure which was randomly generated by the sender.
yourNonce — The Nonce structure which the recipient had originally provided as part of the initial request or initial response.
certificates — A CertificateList structure created in response to the counterparty’s RequestedCertificateSet: The sender includes every certificate they have matching the CertificateTypeIDs the counterparty specified, as long as it was issued by one of the counterparty’s trusted certifiers. If the counterparty did not ask for any certificates, or if the sender does not have any of the certificates which the counterparty had specified, this is optional.
payload — This is the data to be sent to the counterparty. It could be a JSON object, an image, or any other generalized data of any kind. Examples of different Authrite messages implemented in different contexts are given below.
signature — A Signature structure created in accordance with the Message Signing section.
General Message Signing
The sender creates a BRC-3 digital signature for the recipient with the following BRC-43 protocol and key IDs:
Attribute
Value
Security Level
2
Protocol ID
authrite message signature
Key ID
xxxxx yyyyy
Counterparty
The recipient's public key.
Where:
xxxxx is the original nonce (from the initial request or initial response) provided by the counterparty, and
yyyyy is the nonce provided by the sender, which is specified as part of this message.
The data to be signed by the sender with this key is the message payload.
General Message Verification
When the recipient receives the message from the sender, they will verify the BRC-3 signature with the corresponding values, with the sender as counterparty.
After checking that the signature is valid, the recipient then checks that the subject of all the provided certificates is equal to the sender's identity public key. They then move on to checking the validity of the certificates themselves, as per BRC-52.
The recipient should also ensure that the yourNonce value was originally generated by them if they rely on it during verification.
When JSON is used, a message payload can simply be added to an Authrite JSON object:
When Authrite messages are sent over HTTP, X-Authrite-* headers can be added in addition to the normal headers. The signature is created over the data in the HTTP request or response body. If there is not a body to sign, the URL can be signed:
Certificate Inclusion Re-scoping is when one or both parties change the certificates and/or certificate fields that they are requesting the other party provide. This mechanism can also be used to add or remove certifiers from the list of trusted entities, causing old certificates to be removed or new ones to be added.
For Alice to trigger this process, she simply sends a new initial request to Bob. This new request contains her updated RequestedCertificateSet, and Bob should reflect the update for any future communications.
For Bob to trigger the process, he must return an error and ask Alice to send a new initial request. Bob replies with a JSON object like the following:
The Authrite protocol has been implemented in JavaScript through the following two NPM packages:
Authrite-JS implements the client-side functionality
Authrite-Express implements the server-side functionality, for use on Express servers
L9t1aQLkWkmMw1Poi1ofIjHV6GO+BNN63v8Na8/gW4Y=[
“0295ddf37f406a1dcdf23a0418d5cdc824b4d092e165add14697ce1b8d31e4f3e8”,
“035ce8cc44dbcf4c991d666d381d67263aed9123f9b15cca215b54f1314152d23c”,
“020940955c81c7052583b4bab9c87c81cca92ab7e84d360812a7cdf327d4143ca9”
]{
“eHFASF5sUWTHV40NNKigyFFC57pSEnobe/7lzk4XTKI=+BNN63v8Na8/gW4Y=”: [
“first_name”,
“last_name”
],
“4nst50RUw4WD1VberI0pJzGMAwX81ozrzvL9fMe7ZhE=”: [
“firstName”,
“middleInitial”,
“lastName”
],
“M4Al7B4C8rY4ajTODGCLl2i0OQlScgOagNg9xEtbud0=”: [
“full_legal_name”
]
}{
“certifiers”: [
“0295ddf37f406a1dcdf23a0418d5cdc824b4d092e165add14697ce1b8d31e4f3e8”,
“035ce8cc44dbcf4c991d666d381d67263aed9123f9b15cca215b54f1314152d23c”,
“020940955c81c7052583b4bab9c87c81cca92ab7e84d360812a7cdf327d4143ca9”
],
“types”: {
“eHFASF5sUWTHV40NNKigyFFC57pSEnobe/7lzk4XTKI=+BNN63v8Na8/gW4Y=”: [
“first_name”,
“last_name”
],
“4nst50RUw4WD1VberI0pJzGMAwX81ozrzvL9fMe7ZhE=”: [
“firstName”,
“middleInitial”,
“lastName”
],
“M4Al7B4C8rY4ajTODGCLl2i0OQlScgOagNg9xEtbud0=”: [
“full_legal_name”
]
}
}[{
“type”: ”8l5phhdm2Hi80s6QqFOLS0NwUzDzJhlUTWv2BezmstE=”,
“subject”: ”0376d67c86b45be3c36c328c2aa5c5dd79c546d22859e39439bd5d934058345abc”,
“validationKey”: ”G4pBfpBEZ7n5iEHrtG5MFOnIO0U1D758naHJilcK/RU=/7lzk4XTKI=+BNN63v8Na8/gW4Y=”,
“serialNumber”: ”kUahacBHmYL2nkzemkatFg==”,
“fields”: {
“first_name”: “IlgngMNcUM0d4GyzkSGqXaIBw6/n8bB6BG0pprO6oGhxUcivP6wDfuMWjWrG64KQJYUMN3QjHoX40r3SvAyWQaAA9ldVuPLgZwi9q2TCECKHCyEuiATCM9PdB/Ba2rxp”,
“last_name”: “3o0jTew8WMW54svE8Btx9Aks1FlQCHOxGKLZpc7SR0ESSIomToAYFtCq6U7NUuf3ktwLgnv5XdCrCTo+mOTHl/H+oyjcxB7xfdwav+bBx6vG5y9voDI/Z2MQGxE/Pmeg”,
“address_line_1”: “WGqZGJo7VHeQ5tx7uqeyA5q08tRVxJLUoprjAiJ8FmzOoe4gt0+6PGDgvrhkbzauSxGY9yRyOeP+irHclCIrzjwjbfkK19thoyUhr8u1283FobikPs2xKrhUvcYhDOV7”,
“address_line_2”: “IjI5UXJnMbcSEqIVkH8B5a9cY8EtWO7RvJH9gAWuIq0Y3koINgfqNjmockAgXnGxLvdQXvWdlCIm+foedIdHhqs9Xi60wR7hLSs9emjo6nq1Mx01wYFRW1le//Sup80x”,
“city”: “Ed4Nr8UL3h7AqX3PlExwLQ0Mi/QLBH9wCIpdeMb442HDbQlodF1eUuJQRXt+0ajzmXEqaGNSD59xEXmsFouLDLa6sOOzXr2psCKMXkU1+IFHGv8TgJx/vDTCxMvL7t2T”,
“state”: “6oiC8AMFFrLRGhpbWUO1HkA05/QPmfaUg1be4cP2vbcadIv4quaBgfwmNWE6xItpQNAfEwrx1ub9b4cNIKnN+6JAsiDiqs42mbjsgTuZL90fSEL9yPTwRuvW1ccvJ43z”,
“zipcode”: “Y+iqr8cSMEMMq8WqpIudzni1DIiQT5BsK29ld8lFCZddrrjep0qQyKoaP9+46ikIu9VOQIoOJayhiW7KifUSYt+D/WODyPei4Ru7tqfgR47Vo4u0EFI3KLBXyC3DgiN+”
},
“certifier”: ”035ce8cc44dbcf4c991d666d381d67263aed9123f9b15cca215b54f1314152d23c”,
“revocationOutpoint”: “48645047cd66f7b48b24efb080ec7e27f3f448c21109939b80afd11e40898c92.1”,
“signature”: “3045022100c0686907d8914abfa7f356c560c7d17045dde5c10135b1cbf9bf6e4cc52e09820220366eec15372bc34db10f997e21755f603a2023c96578e9d71fca2570f23055d6”,
“keyring”: {
“first_name”: “kulDaJWJmzDH/5/qYuEIaDyHrS/PbLCSXPGClC4gLc0=”,
“last_name”: “26ymqbJl9gMxb00do+kOg950/aE5H+m/PyCgwEXBD/8=”
}
}]{
“authrite”: “0.1”,
“messageType”: “initialRequest”,
“identityKey”: “02a08a4bbba07eadf350f2821a7ab6deaecda3a1c91d487576c80333e9f7a2a635”,
“nonce”: “0LzpPd0hSjqzVWeW6yOwrms/GD/obTz7Skc/mMxsgg8=”,
“requestedCertificates”: {
“certifiers”: [
“0295ddf37f406a1dcdf23a0418d5cdc824b4d092e165add14697ce1b8d31e4f3e8”,
“035ce8cc44dbcf4c991d666d381d67263aed9123f9b15cca215b54f1314152d23c”,
“020940955c81c7052583b4bab9c87c81cca92ab7e84d360812a7cdf327d4143ca9”
],
“types”: {
“eHFASF5sUWTHV40NNKigyFFC57pSEnobe/7lzk4XTKI=+BNN63v8Na8/gW4Y=”: [
“first_name”,
“last_name”
],
“4nst50RUw4WD1VberI0pJzGMAwX81ozrzvL9fMe7ZhE=”: [
“firstName”,
“middleInitial”,
“lastName”
],
“M4Al7B4C8rY4ajTODGCLl2i0OQlScgOagNg9xEtbud0=”: [
“full_legal_name”
]
}
}
}{
“authrite”: “0.1”,
“messageType”: “initialResponse”,
“identityKey”: “0375454679f3b020c2a255d1cedde607339102704b8cebf11c63e1d7fe6a329327”,
“nonce”: “Ej0uwfGivItPlY0TWbm554qLIidwmliSRe6xPXK0FH0=”,
“certificates”: [{
“type”: ”8l5phhdm2Hi80s6QqFOLS0NwUzDzJhlUTWv2BezmstE=”,
“subject”: ”0376d67c86b45be3c36c328c2aa5c5dd79c546d22859e39439bd5d934058345abc”,
“validationKey”: ”G4pBfpBEZ7n5iEHrtG5MFOnIO0U1D758naHJilcK/RU=/7lzk4XTKI=+BNN63v8Na8/gW4Y=”,
“serialNumber”: ”kUahacBHmYL2nkzemkatFg==”,
“fields”: {
“first_name”: “IlgngMNcUM0d4GyzkSGqXaIBw6/n8bB6BG0pprO6oGhxUcivP6wDfuMWjWrG64KQJYUMN3QjHoX40r3SvAyWQaAA9ldVuPLgZwi9q2TCECKHCyEuiATCM9PdB/Ba2rxp”,
“last_name”: “3o0jTew8WMW54svE8Btx9Aks1FlQCHOxGKLZpc7SR0ESSIomToAYFtCq6U7NUuf3ktwLgnv5XdCrCTo+mOTHl/H+oyjcxB7xfdwav+bBx6vG5y9voDI/Z2MQGxE/Pmeg”,
“address_line_1”: “WGqZGJo7VHeQ5tx7uqeyA5q08tRVxJLUoprjAiJ8FmzOoe4gt0+6PGDgvrhkbzauSxGY9yRyOeP+irHclCIrzjwjbfkK19thoyUhr8u1283FobikPs2xKrhUvcYhDOV7”,
“address_line_2”: “IjI5UXJnMbcSEqIVkH8B5a9cY8EtWO7RvJH9gAWuIq0Y3koINgfqNjmockAgXnGxLvdQXvWdlCIm+foedIdHhqs9Xi60wR7hLSs9emjo6nq1Mx01wYFRW1le//Sup80x”,
“city”: “Ed4Nr8UL3h7AqX3PlExwLQ0Mi/QLBH9wCIpdeMb442HDbQlodF1eUuJQRXt+0ajzmXEqaGNSD59xEXmsFouLDLa6sOOzXr2psCKMXkU1+IFHGv8TgJx/vDTCxMvL7t2T”,
“state”: “6oiC8AMFFrLRGhpbWUO1HkA05/QPmfaUg1be4cP2vbcadIv4quaBgfwmNWE6xItpQNAfEwrx1ub9b4cNIKnN+6JAsiDiqs42mbjsgTuZL90fSEL9yPTwRuvW1ccvJ43z”,
“zipcode”: “Y+iqr8cSMEMMq8WqpIudzni1DIiQT5BsK29ld8lFCZddrrjep0qQyKoaP9+46ikIu9VOQIoOJayhiW7KifUSYt+D/WODyPei4Ru7tqfgR47Vo4u0EFI3KLBXyC3DgiN+”
},
“certifier”: ”035ce8cc44dbcf4c991d666d381d67263aed9123f9b15cca215b54f1314152d23c”,
“revocationOutpoint”: “48645047cd66f7b48b24efb080ec7e27f3f448c21109939b80afd11e40898c92.1”,
“signature”: “3045022100c0686907d8914abfa7f356c560c7d17045dde5c10135b1cbf9bf6e4cc52e09820220366eec15372bc34db10f997e21755f603a2023c96578e9d71fca2570f23055d6”,
“keyring”: {
“first_name”: “G2GmxIWNPkO2SUP0XpQvQqX31yz58aLHk9e7JxO+q/MT3k9ZjTe3t8aN2Qwg2+AtBulXZpKhuvxOgxicfd8GqCjFkGtyUrbSdmCFjPUJm7z/P/LWkJHByeBzh3/yOWa7”,
“last_name”: “YZAHgs+P8Tgci3+5uuAuHr0k3CXPEcB3trpqtEUygV+uwoTgDWk01XTT1eOW6n7qjcDt4g7tlzuWtkawDkvRbCcVe3oBsEI5X8+A10s8TAsyNQV5O1R8mkGoL8/Hec4K”
}
}],
“requestedCertificates”: {
“certifiers”: [
“0295ddf37f406a1dcdf23a0418d5cdc824b4d092e165add14697ce1b8d31e4f3e8”,
“035ce8cc44dbcf4c991d666d381d67263aed9123f9b15cca215b54f1314152d23c”,
“020940955c81c7052583b4bab9c87c81cca92ab7e84d360812a7cdf327d4143ca9”
],
“types”: {
“eHFASF5sUWTHV40NNKigyFFC57pSEnobe/7lzk4XTKI=+BNN63v8Na8/gW4Y=”: [
“first_name”,
“last_name”
],
“4nst50RUw4WD1VberI0pJzGMAwX81ozrzvL9fMe7ZhE=”: [
“firstName”,
“middleInitial”,
“lastName”
],
“M4Al7B4C8rY4ajTODGCLl2i0OQlScgOagNg9xEtbud0=”: [
“full_legal_name”
]
}
},
“signature”: “3045022100fc0946c97393ffd48b34a333a82a69343bed3d43b52932da62cb0e75cc07ca1602200822693d7fd2cb3cd54ebeb610bd1646eaea34cfb99109f393a3bf59ccc50231”
}{
“authrite”: “0.1”,
“identityKey”: “0375454679f3b020c2a255d1cedde607339102704b8cebf11c63e1d7fe6a329327”,
“nonce”: “Ej0uwfGivItPlY0TWbm554qLIidwmliSRe6xPXK0FH0=”,
“yourNonce”: “VBEk9m0CFlgG/cCpRf0QWKErMGI1wNuHIoCpWSY8+50=”,
“certificates”: [{
“type”: ”8l5phhdm2Hi80s6QqFOLS0NwUzDzJhlUTWv2BezmstE=”,
“subject”: ”0376d67c86b45be3c36c328c2aa5c5dd79c546d22859e39439bd5d934058345abc”,
“validationKey”: ”G4pBfpBEZ7n5iEHrtG5MFOnIO0U1D758naHJilcK/RU=/7lzk4XTKI=+BNN63v8Na8/gW4Y=”,
“serialNumber”: ”kUahacBHmYL2nkzemkatFg==”,
“fields”: {
“first_name”: “IlgngMNcUM0d4GyzkSGqXaIBw6/n8bB6BG0pprO6oGhxUcivP6wDfuMWjWrG64KQJYUMN3QjHoX40r3SvAyWQaAA9ldVuPLgZwi9q2TCECKHCyEuiATCM9PdB/Ba2rxp”,
“last_name”: “3o0jTew8WMW54svE8Btx9Aks1FlQCHOxGKLZpc7SR0ESSIomToAYFtCq6U7NUuf3ktwLgnv5XdCrCTo+mOTHl/H+oyjcxB7xfdwav+bBx6vG5y9voDI/Z2MQGxE/Pmeg”,
“address_line_1”: “WGqZGJo7VHeQ5tx7uqeyA5q08tRVxJLUoprjAiJ8FmzOoe4gt0+6PGDgvrhkbzauSxGY9yRyOeP+irHclCIrzjwjbfkK19thoyUhr8u1283FobikPs2xKrhUvcYhDOV7”,
“address_line_2”: “IjI5UXJnMbcSEqIVkH8B5a9cY8EtWO7RvJH9gAWuIq0Y3koINgfqNjmockAgXnGxLvdQXvWdlCIm+foedIdHhqs9Xi60wR7hLSs9emjo6nq1Mx01wYFRW1le//Sup80x”,
“city”: “Ed4Nr8UL3h7AqX3PlExwLQ0Mi/QLBH9wCIpdeMb442HDbQlodF1eUuJQRXt+0ajzmXEqaGNSD59xEXmsFouLDLa6sOOzXr2psCKMXkU1+IFHGv8TgJx/vDTCxMvL7t2T”,
“state”: “6oiC8AMFFrLRGhpbWUO1HkA05/QPmfaUg1be4cP2vbcadIv4quaBgfwmNWE6xItpQNAfEwrx1ub9b4cNIKnN+6JAsiDiqs42mbjsgTuZL90fSEL9yPTwRuvW1ccvJ43z”,
“zipcode”: “Y+iqr8cSMEMMq8WqpIudzni1DIiQT5BsK29ld8lFCZddrrjep0qQyKoaP9+46ikIu9VOQIoOJayhiW7KifUSYt+D/WODyPei4Ru7tqfgR47Vo4u0EFI3KLBXyC3DgiN+”
},
“certifier”: ”035ce8cc44dbcf4c991d666d381d67263aed9123f9b15cca215b54f1314152d23c”,
“revocationOutpoint”: “48645047cd66f7b48b24efb080ec7e27f3f448c21109939b80afd11e40898c92.1”,
“signature”: “3045022100c0686907d8914abfa7f356c560c7d17045dde5c10135b1cbf9bf6e4cc52e09820220366eec15372bc34db10f997e21755f603a2023c96578e9d71fca2570f23055d6”,
“keyring”: {
“first_name”: “G2GmxIWNPkO2SUP0XpQvQqX31yz58aLHk9e7JxO+q/MT3k9ZjTe3t8aN2Qwg2+AtBulXZpKhuvxOgxicfd8GqCjFkGtyUrbSdmCFjPUJm7z/P/LWkJHByeBzh3/yOWa7”,
“last_name”: “YZAHgs+P8Tgci3+5uuAuHr0k3CXPEcB3trpqtEUygV+uwoTgDWk01XTT1eOW6n7qjcDt4g7tlzuWtkawDkvRbCcVe3oBsEI5X8+A10s8TAsyNQV5O1R8mkGoL8/Hec4K”
}
}],
“payload”: {
“hello”: “Authrite!”
},
“signature”: “3045022100fc0946c97393ffd48b34a333a82a69343bed3d43b52932da62cb0e75cc07ca1602200822693d7fd2cb3cd54ebeb610bd1646eaea34cfb99109f393a3bf59ccc50231”
}Content-Type: video/mp4
Content-Length: 140546213245
X-Authrite: 0.1
X-Authrite-Identity-Key: 0375454679f3b020c2a255d1cedde607339102704b8cebf11c63e1d7fe6a329327
X-Authrite-Nonce: Ej0uwfGivItPlY0TWbm554qLIidwmliSRe6xPXK0FH0=
X-Authrite-YourNonce: VBEk9m0CFlgG/cCpRf0QWKErMGI1wNuHIoCpWSY8+50=
X-Authrite-Certificates: [{
“type”: ”8l5phhdm2Hi80s6QqFOLS0NwUzDzJhlUTWv2BezmstE=”,
“subject”: ”0376d67c86b45be3c36c328c2aa5c5dd79c546d22859e39439bd5d934058345abc”,
“validationKey”: ”G4pBfpBEZ7n5iEHrtG5MFOnIO0U1D758naHJilcK/RU=/7lzk4XTKI=+BNN63v8Na8/gW4Y=”,
“serialNumber”: ”kUahacBHmYL2nkzemkatFg==”,
“fields”: {
“first_name”: “IlgngMNcUM0d4GyzkSGqXaIBw6/n8bB6BG0pprO6oGhxUcivP6wDfuMWjWrG64KQJYUMN3QjHoX40r3SvAyWQaAA9ldVuPLgZwi9q2TCECKHCyEuiATCM9PdB/Ba2rxp”,
“last_name”: “3o0jTew8WMW54svE8Btx9Aks1FlQCHOxGKLZpc7SR0ESSIomToAYFtCq6U7NUuf3ktwLgnv5XdCrCTo+mOTHl/H+oyjcxB7xfdwav+bBx6vG5y9voDI/Z2MQGxE/Pmeg”,
“address_line_1”: “WGqZGJo7VHeQ5tx7uqeyA5q08tRVxJLUoprjAiJ8FmzOoe4gt0+6PGDgvrhkbzauSxGY9yRyOeP+irHclCIrzjwjbfkK19thoyUhr8u1283FobikPs2xKrhUvcYhDOV7”,
“address_line_2”: “IjI5UXJnMbcSEqIVkH8B5a9cY8EtWO7RvJH9gAWuIq0Y3koINgfqNjmockAgXnGxLvdQXvWdlCIm+foedIdHhqs9Xi60wR7hLSs9emjo6nq1Mx01wYFRW1le//Sup80x”,
“city”: “Ed4Nr8UL3h7AqX3PlExwLQ0Mi/QLBH9wCIpdeMb442HDbQlodF1eUuJQRXt+0ajzmXEqaGNSD59xEXmsFouLDLa6sOOzXr2psCKMXkU1+IFHGv8TgJx/vDTCxMvL7t2T”,
“state”: “6oiC8AMFFrLRGhpbWUO1HkA05/QPmfaUg1be4cP2vbcadIv4quaBgfwmNWE6xItpQNAfEwrx1ub9b4cNIKnN+6JAsiDiqs42mbjsgTuZL90fSEL9yPTwRuvW1ccvJ43z”,
“zipcode”: “Y+iqr8cSMEMMq8WqpIudzni1DIiQT5BsK29ld8lFCZddrrjep0qQyKoaP9+46ikIu9VOQIoOJayhiW7KifUSYt+D/WODyPei4Ru7tqfgR47Vo4u0EFI3KLBXyC3DgiN+”
},
“certifier”: ”035ce8cc44dbcf4c991d666d381d67263aed9123f9b15cca215b54f1314152d23c”,
“revocationOutpoint”: “48645047cd66f7b48b24efb080ec7e27f3f448c21109939b80afd11e40898c92.1”,
“signature”: “3045022100c0686907d8914abfa7f356c560c7d17045dde5c10135b1cbf9bf6e4cc52e09820220366eec15372bc34db10f997e21755f603a2023c96578e9d71fca2570f23055d6”,
“keyring”: {
“first_name”: “kulDaJWJmzDH/5/qYuEIaDyHrS/PbLCSXPGClC4gLc0=”,
“last_name”: “26ymqbJl9gMxb00do+kOg950/aE5H+m/PyCgwEXBD/8=”
}
}]
X-Authrite-Signature: 3045022100fc0946c97393ffd48b34a333a82a69343bed3d43b52932da62cb0e75cc07ca1602200822693d7fd2cb3cd54ebeb610bd1646eaea34cfb99109f393a3bf59ccc50231{
“authrite”: “0.1”,
“messageType”: “rescopingTrigger”,
“message”: “Send a new initialRequest message to re-scope required certificates, fields and/or trusted certifiers.”
}Freddie Honohan ([email protected])
This proposal introduces a standardised method of dealing with the ASM representation of Bitcoin Script across the ecosystem language libraries (Py-SDK, TS-SDK, Go-SDK, Teranode etc).
The purpose of this proposal is to provide a generalised standard in order for deterministic translation of Bitcoin Script to and from ASM for cross-language compatibility.
Currently, different implementations may produce inconsistent ASM representations for the same script. For example, the boolean value false might be rendered as OP_0, OP_FALSE depending on the library used. This lack of standardisation creates interoperability issues when scripts are shared across different tools and platforms.
It is important that ASM format scripts operate correctly regardless of where they were produced or processed.
The Hexadecimal and Binary representations are defined in BRC-14.
Since ASM format is primarily used for human-readability, op-codes with multiple representations like the boolean pair [OP_0, OP_1] are to be represented as their english full-name counterpart e.g. [OP_FALSE, OP_TRUE] Example:
Other discrepancies were found and documented and rules are set out below to deal with them.
The logic dealing with Bitcoin Script ASM is to be standardised across libraries and languages as described hereunder.
Op-Codes with several names must be parsed into the correct hex/binary byte despite the chosen ASM format name. Example Input Parsing:
However op-codes with several names must be output into the most human-readable format (e.g. OP_0 will always output as OP_FALSE). Example Output Serialization:
Full Op-Code to Hex Table With Statuses
: Bitcoin Script Binary and Hex Formats - Ty Everett ([email protected])
OP_PUSHDATA1
0x4c
✅
✅
77
OP_PUSHDATA2
0x4d
✅
✅
78
OP_PUSHDATA4
0x4e
✅
✅
79
OP_1NEGATE
0x4f
✅
✅
80
OP_RESERVED
0x50
Reserved
Reserved
81
OP_TRUE
0x51
✅
✅
82
OP_2
0x52
✅
✅
83
OP_3
0x53
✅
✅
84
OP_4
0x54
✅
✅
85
OP_5
0x55
✅
✅
86
OP_6
0x56
✅
✅
87
OP_7
0x57
✅
✅
88
OP_8
0x58
✅
✅
89
OP_9
0x59
✅
✅
90
OP_10
0x5a
✅
✅
91
OP_11
0x5b
✅
✅
92
OP_12
0x5c
✅
✅
93
OP_13
0x5d
✅
✅
94
OP_14
0x5e
✅
✅
95
OP_15
0x5f
✅
✅
96
OP_16
0x60
✅
✅
97
OP_NOP
0x61
✅
✅
98
OP_VER
0x62
❌
✅
99
OP_IF
0x63
✅
✅
100
OP_NOTIF
0x64
✅
✅
101
OP_VERIF
0x65
❌
✅
102
OP_VERNOTIF
0x66
❌
✅
103
OP_ELSE
0x67
✅
✅
104
OP_ENDIF
0x68
✅
✅
105
OP_VERIFY
0x69
✅
✅
106
OP_RETURN
0x6a
✅
✅
107
OP_TOALTSTACK
0x6b
✅
✅
108
OP_FROMALTSTACK
0x6c
✅
✅
109
OP_2DROP
0x6d
✅
✅
110
OP_2DUP
0x6e
✅
✅
111
OP_3DUP
0x6f
✅
✅
112
OP_2OVER
0x70
✅
✅
113
OP_2ROT
0x71
✅
✅
114
OP_2SWAP
0x72
✅
✅
115
OP_IFDUP
0x73
✅
✅
116
OP_DEPTH
0x74
✅
✅
117
OP_DROP
0x75
✅
✅
118
OP_DUP
0x76
✅
✅
119
OP_NIP
0x77
✅
✅
120
OP_OVER
0x78
✅
✅
121
OP_PICK
0x79
✅
✅
122
OP_ROLL
0x7a
✅
✅
123
OP_ROT
0x7b
✅
✅
124
OP_SWAP
0x7c
✅
✅
125
OP_TUCK
0x7d
✅
✅
126
OP_CAT
0x7e
✅
✅
127
OP_SPLIT
0x7f
✅
✅
128
OP_NUM2BIN
0x80
✅
✅
129
OP_BIN2NUM
0x81
✅
✅
130
OP_SIZE
0x82
✅
✅
131
OP_INVERT
0x83
✅
✅
132
OP_AND
0x84
✅
✅
133
OP_OR
0x85
✅
✅
134
OP_XOR
0x86
✅
✅
135
OP_EQUAL
0x87
✅
✅
136
OP_EQUALVERIFY
0x88
✅
✅
137
OP_RESERVED1
0x89
Reserved
Reserved
138
OP_RESERVED2
0x8a
Reserved
Reserved
139
OP_1ADD
0x8b
✅
✅
140
OP_1SUB
0x8c
✅
✅
141
OP_2MUL
0x8d
❌
✅
142
OP_2DIV
0x8e
❌
✅
143
OP_NEGATE
0x8f
✅
✅
144
OP_ABS
0x90
✅
✅
145
OP_NOT
0x91
✅
✅
146
OP_0NOTEQUAL
0x92
✅
✅
147
OP_ADD
0x93
✅
✅
148
OP_SUB
0x94
✅
✅
149
OP_MUL
0x95
✅
✅
150
OP_DIV
0x96
✅
✅
151
OP_MOD
0x97
✅
✅
152
OP_LSHIFT
0x98
✅
✅
153
OP_RSHIFT
0x99
✅
✅
154
OP_BOOLAND
0x9a
✅
✅
155
OP_BOOLOR
0x9b
✅
✅
156
OP_NUMEQUAL
0x9c
✅
✅
157
OP_NUMEQUALVERIFY
0x9d
✅
✅
158
OP_NUMNOTEQUAL
0x9e
✅
✅
159
OP_LESSTHAN
0x9f
✅
✅
160
OP_GREATERTHAN
0xa0
✅
✅
161
OP_LESSTHANOREQUAL
0xa1
✅
✅
162
OP_GREATERTHANOREQUAL
0xa2
✅
✅
163
OP_MIN
0xa3
✅
✅
164
OP_MAX
0xa4
✅
✅
165
OP_WITHIN
0xa5
✅
✅
166
OP_RIPEMD160
0xa6
✅
✅
167
OP_SHA1
0xa7
✅
✅
168
OP_SHA256
0xa8
✅
✅
169
OP_HASH160
0xa9
✅
✅
170
OP_HASH256
0xaa
✅
✅
171
OP_CODESEPARATOR
0xab
✅
✅
172
OP_CHECKSIG
0xac
✅
✅
173
OP_CHECKSIGVERIFY
0xad
✅
✅
174
OP_CHECKMULTISIG
0xae
✅
✅
175
OP_CHECKMULTISIGVERIFY
0xaf
✅
✅
176
OP_NOP1
0xb0
✅
✅
177
OP_NOP2
0xb1
✅
✅
178
OP_NOP3
0xb2
✅
✅
179
OP_SUBSTR
0xb3
❌
✅
180
OP_LEFT
0xb4
❌
✅
181
OP_RIGHT
0xb5
❌
✅
182
OP_NOP4
0xb6
✅
✅
183
OP_NOP5
0xb7
✅
✅
184
OP_NOP6
0xb8
✅
✅
185
OP_NOP7
0xb9
✅
✅
186
OP_NOP8
0xba
✅
✅
187
OP_NOP9
0xbb
✅
✅
188
OP_NOP10
0xbc
✅
✅
253
OP_PUBKEYHASH
0xfd
Pseudo
Pseudo
254
OP_PUBKEY
0xfe
Pseudo
Pseudo
255
OP_INVALIDOPCODE
0xff
Invalid
Invalid
0
OP_FALSE
0x00
✅
✅
1-75
OP_PUSHDATA
0x01-0x4b
✅
✅
76
Hex: 0x00 0x51
ASM: OP_FALSE OP_TRUE
Not: OP_0 OP_1 // All of these should parse to 0x00
parseASM("OP_0") // ✓ valid
parseASM("OP_FALSE") // ✓ valid
// Both should output the same hex
parseASM("OP_0") === parseASM("OP_FALSE") // true // Input hex, always outputs human-readable form
toASM(0x00) // "OP_FALSE" (not "OP_0")
toASM(0x51) // "OP_TRUE" (not "OP_1")This file contains the full specification from the Substack article by Dr. Craig S Wright, including abstract, sections 1-17, and state machine diagram description.
The PCW-1 specification was designed and architected by Dr. Craig S. Wright and the protocol was engineered and implemented by Dr. Roy Murphy. The reference protocol implementation is located here.
This work specifies a Bitcoin wallet workflow that conducts direct IP-to-IP negotiation between two identified parties and settles a payment as many independent on-chain transactions (“notes”). Identity keys are used only for ECDH and message authentication; they never appear on-chain. For each invoice and note index, a shared secret and an invoice fingerprint deterministically derive a unique recipient public key, ensuring unlinkability outside the two parties. The payer splits the total into bounded denominations agreed with the recipient and constructs one standard P2PKH transaction per note. Each transaction is funded with a disjoint input set and (if required) returns change to a per-note sender address derived deterministically so that change never overlaps across notes. Either party may submit any subset of fully signed transactions at any time; settlement is established by confirmation depth. The paper formalises notation, message frames, per-note key derivation, deterministic bounded splitting, disjoint coin-selection and change algorithms, ordering and pacing of broadcasts, selective-disclosure receipts, reissue rules prior to broadcast, and behaviours under reorgs. The result is a practical, auditable, and privacy-preserving method to enforce recipient constraints while keeping every note an independent on-chain payment.
What is it.
A payment is settled as a set of small, standard on-chain transactions (“notes”), each paying a bounded amount to a unique address that only the recipient can spend. Bounds (per-note minimum and maximum) are negotiated up front, and the total is split into note amounts that sum exactly to the invoice total. Every note is valid on its own, funded by inputs that no other note in the same invoice uses, and optionally returns change to a unique, invoice-scoped sender address. Either side may submit any note to the network; confirmation depth defines settlement for that note.
Two long-lived identity keys authenticate the off-chain session and never appear on-chain. A per-invoice shared element derived from those identities, together with the hash of the canonical invoice JSON, scopes all derivations: per-note recipient addresses, per-note sender change addresses, the exact split of amounts, labels, and receipts. Because the scope is per invoice, derivations are never reused across invoices, and the same index i under a different invoice produces unrelated addresses.
Why it exists.
Policy-compliant intake for the payee. A recipient can publish firm bounds and a fee-rate floor once, then accept large totals without ever receiving an out-of-policy note. This simplifies operations and avoids exposing internal wallet structure. • Determinism and symmetry for the payer. The payer derives exactly the same note set the recipient expects, without shipping secrets or bespoke scripts. If both ends can parse the same canonical JSON and run the same hashes and curve operations already used in Bitcoin, they interoperate. • Audit without surveillance. A Merkle root commits to the entire set of notes. Later, either party can reveal proofs for any subset (for example, to an auditor) without disclosing the remainder. Off-chain logs are signed by identity keys, allowing reconstruction of intent without putting identities on-chain.
All of this uses only primitives that already exist in Bitcoin: secp256k1 keys, SHA-256 and RIPEMD-160, standard P2PKH outputs, and raw transaction serialisation. No new opcodes, no new script types, no new cryptographic gadgets.
What does it do (functional view)
Identity and scope. Alice (payer) and Bob (payee) authenticate using long-lived identity keypairs. They exchange two compact, signed messages: the policy (Bob’s bounds, anchor, fee floor, expiry) and the invoice (Alice’s total, unit, terms, and a reference to the policy). From these they compute (i) a shared element via ECDH and (ii) the invoice fingerprint, the SHA-256 hash of the canonical invoice JSON. The pair {Z, H_I} defines the scope for everything that follows.
Bounded splitting. Within the published bounds, the total is decomposed into N note amounts. The number N is feasible by construction (between ceil(T ÷ v_max) and floor(T ÷ v_min)). The amounts are derived deterministically from {Z, H_I} so both parties compute the same vector and then permute it to remove any index-to-size correlation. Off-chain, the split leaks only that each note lies within bounds.
Per-note addressing. For each index i, the payer derives the recipient’s public key for that note by tweaking the recipient’s on-chain anchor with a scalar computed from {Z, H_I, i} and a role label. Only the recipient can compute the corresponding private key. In the same scope, the payer derives a unique sender change address for index i. Identity keys are never used on-chain; only anchors appear in settlement keys.
Disjoint funding and change. The payer assigns a strictly disjoint set of inputs to each note from a snapshot of her UTXO pool. No input appears in two notes. A standard size estimator and the negotiated fee-rate floor decide whether a note has one output (exact) or two (payee + change). When present, change always pays to the per-note sender change address. Change from one note never funds another note in the same invoice.
Transaction formation and broadcast. Each note is a standard P2PKH transaction, fully signed and valid in isolation. Either party may broadcast any subset; duplicate submission is benign. Broadcast may be all-at-once, paced, or in bursts; confirmation depth chosen by the recipient defines finality per note.
Receipts and selective disclosure. After notes exist, each side can compute a per-note leaf that commits to the index, txid, amount, and address payload, and then a Merkle root over the set of leaves. The root and a manifest of indices and txids provide a commitment that later supports selective proofs for any subset.
Failure handling. Deterministic behaviours cover insufficient inputs, pre-broadcast fee changes, external conflicts, reorgs, and expiry. Reissue preserves indices and addresses; older raw bytes are marked “superseded” off-chain and are not broadcast.
How it does it (implementation narrative)
Module 1 — Canonical JSON and signing. Provide byte-identical encodings of policy, invoice, logs, and receipts. Every signed artefact includes a detached signature made by the relevant identity key over the canonical bytes (signature fields omitted from the preimage). This guarantees both sides hash the same content when computing the invoice fingerprint and policy hash.
Module 2 — Scope and derivations. Given {Z, H_I}, derive deterministically: (a) per-note recipient keys (label “recv”), (b) per-note sender change keys (label “snd”), (c) the split of amounts (label “split”), and (d) any pacing schedule (label “pace”). Output: reproducible addresses, labels, and amounts that both wallets compute locally without sharing per-note data.
Module 3 — Coin selection with reservation. Take a snapshot of the payer’s spendable UTXOs and allocate disjoint input sets to each note. Preference order: exact matches; then single-input near-over with valid change; then few-input combinations with minimal overshoot, always respecting dust and fee floors. If inputs are too coarse, perform one payer→payer fan-out and restart reservations. Reservations are tagged by the note identifier so rebuilding from logs yields the same table.
Module 4 — Transaction builder. For each index, combine reserved inputs with the payee output (and optional change), order inputs and outputs deterministically, compute the fee at the floor, and sign every input in the standard way. Output: raw transaction bytes and txid per note. Log the per-note metadata (index, note id, invoice hash, recipient address, amount, txid) with signatures.
Module 5 — Broadcast manager. Compute a nominal plan (all-at-once, paced, or bursts) using seeds derived from {Z, H_I}. Either side may submit; duplicate submissions yield the same txid. Periodic rebroadcast continues until the recipient’s depth is reached or a note is cancelled or reissued. Hold-time limits trigger automatic reissue or cancel actions; all transitions are signed in the log.
Module 6 — Receipts and proofs. Compute the Merkle root over per-note leaves and store a manifest of indices and txids. Later, produce compact proofs for any subset by disclosing only those leaves and paths; a verifier recomputes the root without learning undisclosed notes.
Module 7 — Logging and audit. Every state transition—reservation, signing, broadcast, reissue, cancel, orphan—is recorded as canonical JSON with timestamps and identity signatures and chained by a “prev_hash” field. Given these logs plus the public chain, an auditor can reconstruct the intended settlement set and verify that exactly one transaction per index was meant to settle.
The need and its consequences
Operational fit. Many recipients want steady, bounded inflows rather than sporadic large hits. By decomposing a purchase into bounded notes, intake risk and internal accounting become simpler, while the payer gains a clear, deterministic procedure that never leaks identity keys on-chain.
Robustness. Notes are independent and inputs are disjoint, so partial progress is meaningful: a subset can confirm while the rest are queued or reissued. Reorg handling is straightforward: rebroadcast the same bytes.
Privacy by construction. Identity keys stay off-chain. Per-invoice and per-note derivations require the off-chain scope to reproduce, so outsiders cannot link the recipient’s addresses across the set. Change addresses are unique per note, defeating shared-change clustering. Pacing reduces simple time-based clustering. Selective receipts allow proving exactly what is needed—no more.
Determinism and interoperability. Independent implementations that follow the rules produce the same addresses, the same split, the same reservations, and the same receipts. Disputes are resolvable by recomputation from signed logs.
Scope discipline. Binding all derivations to {Z, H_I} prevents cross-invoice reuse. Index i is meaningful only within the invoice that defined it. This keeps the address space clean and prevents accidental collisions when many invoices are active.
Limitations and boundaries
The design uses standard transaction forms and well-understood cryptographic primitives. It does not compress notes into nonstandard constructs, introduce new script paths, or rely on external relays. Fees and confirmation policies remain subject to network conditions. The payer’s own inputs are clustered within each note by necessity; the privacy goal here is to avoid linking recipient notes to each other, not to conceal that a single payer funded each note.
What it means
This recentres Bitcoin settlement on two authenticated endpoints who deterministically derive everything needed for a payment and act independently to bring it to finality. The outcome is not merely “many small transactions”; it is a protocol for invoice-scoped, symmetric, auditable settlement:
Invoice-scoped: every artefact—addresses, amounts, labels, receipts—derives from the invoice fingerprint and shared element. • Symmetric: either party can complete settlement of any note at any time. • Auditable: small, signed JSON records and a single Merkle root suffice to reconstruct and prove the payment’s history.
An engineer following the formal sections can implement these modules with no new cryptography, no changes to standard transaction formats, and no special network behaviour. A reader evaluating the design can see what it is, why it exists, how it works, and what it achieves, while staying strictly within Bitcoin’s established toolset.
Objective and model
Objective. Specify a direct, invoice-scoped payment workflow in Bitcoin in which two principals — Alice (payer) and Bob (payee) — agree explicit bounds for the value of each note and settle a total as many independent on-chain transactions. Each note is a standard transaction paying a bounded amount to a unique recipient address computable by the payer yet spendable only by the payee. Identity keys have a single role: authenticate the off-chain session and yield shared material that scopes all deterministic derivations; identity keys never appear in locking scripts, never fund or receive outputs, and never enter the on-chain graph. “IP-to-IP” denotes a direct, mutually authenticated message channel adequate to exchange compact UTF-8 JSON documents and raw transaction serialisations; transport mechanics are outside scope and irrelevant to correctness. Either party may submit any fully formed note at any time; settlement finality is defined solely by the confirmation depth the payee requires for that note. The design enforces three global guarantees: note independence, strict non-overlap of inputs, and determinism sufficient for audit and replay.
Actors and keys. Alice maintains a long-lived identity pair Kₐ = (kₐ, Pₐ) and a sender anchor A = a·G used only to derive per-note change addresses. Bob maintains a long-lived identity pair Kᵦ = (kᵦ, Pᵦ) and a recipient anchor B = b·G used only to derive per-note receiving keys. Identity keys authenticate and scope; anchors settle on-chain. For each invoice the parties compute a shared secret Z := ECDH(kₐ, Pᵦ) = ECDH(kᵦ, Pₐ), and an invoice fingerprint Hᴵ := SHA-256(canonical-JSON(invoice)), which together bind all subsequent derivations to that invoice.
Scope and channel model. The off-chain state for a payment is a finite, authenticated transcript: policy → invoice → acknowledgements → (optional) per-note metadata → (optional) raw transactions → receipts. The only required capability of the channel is confidential, integrity-protected exchange of these small artefacts; routing, addressability, and link maintenance are orthogonal and out of scope.
Payment decomposition. Let T be the invoice total (in the smallest unit) and let the payee’s bounds be [vₘᵢₙ, vₘₐₓ] with 0 < vₘᵢₙ ≤ vₘₐₓ. The payment is decomposed into N ≥ 2 notes with amounts a = (a₀, a₁, …, aₙ₋₁), such that for every index i: vₘᵢₙ ≤ aᵢ ≤ vₘₐₓ and Σ aᵢ = T. Feasibility requires ⌈T ÷ vₘₐₓ⌉ ≤ N ≤ ⌊T ÷ vₘᵢₙ⌋. The pair (N, a) and all per-note labels are deterministically derived from (Z, Hᴵ) and the accepted policy so that both parties — given the same inputs — arrive at the identical target set without exchanging per-note values in the clear.
Recipient addressing (model-level). For each index i ∈ {0, …, N−1} a unique recipient public key Pᴮ,ᵢ is derived so that: (1) Alice can compute Pᴮ,ᵢ and therefore the standard address Addrᴮ,ᵢ for the note; (2) only Bob can compute the corresponding private key kᴮ,ᵢ that spends from Addrᴮ,ᵢ; (3) no two notes of the invoice share a recipient key; and (4) observers lacking Z and B cannot link these addresses to identities or to one another.
Funding and change (model-level). Let U be Alice’s snapshot of available unspent outputs at construction time. A reservation mapping R assigns to each index i a finite, exclusive input set Sᵢ ⊆ U with pairwise disjointness Sᵢ ∩ Sⱼ = ∅ for all i ≠ j. Each note transaction Tᵢ is funded only by Sᵢ. Where change is required, it is paid to a per-note sender address Addrᴬ,ᵢ derived deterministically from (Z, Hᴵ) and the sender anchor A. Change from one note never overlaps with change from any other note in the same invoice and is never selected to fund a different note of that invoice; intra-invoice reuse is prohibited by construction.
Broadcast and finality. Because every Tᵢ is fully formed and independent, either party may broadcast any subset in any order or schedule (all-at-once, paced, or opportunistic). Settlement finality for a note is established when its transaction to Addrᴮ,ᵢ reaches the payee’s required confirmation depth d. Duplicate submission is benign. Conflicts cannot arise within the invoice because input sets are disjoint.
Determinism and auditability. Determinism is a first-class requirement: given the same policy, invoice fingerprint, identities, and input snapshot U, both parties compute the same (N, a), the same recipient and change address sets (Addrᴮ,ᵢ, Addrᴬ,ᵢ), and the same per-note labels. A complete audit is reconstructible from persisted canonical JSON logs and the chain: the invoice and policy, Hᴵ, the mapping i ↦ txidᵢ, and an optional Merkle root over note receipts. No external oracle is required to re-derive or verify the set.
Definitions (normative)
D1 — Policy. A signed statement by the payee declaring [vₘᵢₙ, vₘₐₓ], any per-address cap (≤ vₘₐₓ), a fee-rate floor, and an expiry; hashed and referenced by the invoice.
D2 — Invoice. A signed statement by the payer declaring T, unit, terms, invoice number, and the hash of the accepted policy; its canonical hash Hᴵ scopes all derivations.
D3 — Note. A single standard on-chain transaction Tᵢ that pays aᵢ to Addrᴮ,ᵢ and, if necessary, returns change to Addrᴬ,ᵢ; valid, complete, and broadcastable in isolation.
D4 — Reservation. An exclusive assignment R(i) = Sᵢ of inputs to index i with Sᵢ ∩ Sⱼ = ∅ for i ≠ j.
D5 — Either-side broadcast. The right of both principals to announce any subset of {Tᵢ}; correctness and finality depend only on confirmation depth.
Invariants (safety and liveness)
I1 — Identity/settlement separation: identity keys authenticate and scope derivations; anchors alone appear in locking scripts. I2 — Per-invoice scoping: all per-note derivations and labels are functions of (Z, Hᴵ); nothing is reused across invoices. I3 — Note independence: each Tᵢ is valid without reference to any Tⱼ; no chained dependence within the invoice. I4 — Disjoint inputs: Sᵢ ∩ Sⱼ = ∅ for all i ≠ j; input reuse within an invoice is impossible by construction. I5 — Determinism: for fixed inputs (policy, invoice, Kₐ, Kᵦ, U) both parties derive the same (N, a), address sets, and labels. I6 — Non-overlapping change: for any i ≠ j, change outputs of Tᵢ and Tⱼ pay to distinct Addrᴬ,ᵢ and Addrᴬ,ⱼ and are never selected to fund another note of the same invoice. I7 — Finality by confirmation: a note is settled when its transaction has ≥ d confirmations to Addrᴮ,ᵢ; other notes are unaffected. I8 — Multiplicity: the cardinality of the note set satisfies |{Tᵢ}| = N ≥ 2; aggregation into a single multi-output transaction is out of model.
Primitives, notation, and encodings
Keys and curve (secp256k1). Private keys are scalars k in the range 1 ≤ k ≤ n−1 over the prime field 𝔽ₚ, with p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F (decimal p = 2²⁵⁶ − 2³² − 977). The base point (generator) G has order n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141. Public keys are elliptic-curve points P = k·G on the curve y² = x³ + 7 (mod p). Private scalars are represented as fixed-length 32-byte big-endian values when serialised; public keys are represented in compressed SEC1 form (see serP).
Generator G. G is the unique base point defined by secp256k1 with affine coordinates (x_G, y_G) on 𝔽ₚ. All public keys and derivations are computed by scalar multiplication on this generator, using constant-time algorithms. No alternative generator is permitted; all parties MUST compute on the canonical G to guarantee interoperation and reproducibility.
Group order n. The order n is the prime 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141. All scalar arithmetic (addition, subtraction, multiplication) is carried out modulo n. Any derived scalar equal to 0 is invalid and MUST be skipped deterministically (e.g., by advancing the index) to maintain a one-to-one mapping between indices and usable keys.
ECDH shared element Z. ECDH(k, P′) is defined as the 32-byte big-endian x-coordinate of the point k·P′, where k is the caller’s private scalar and P′ is the counterparty’s public point. The raw ECDH output used in this specification is Z, a 32-byte array (left-padded with zeros if necessary). Only the x-coordinate is used; no further key-derivation primitive is introduced in this section.
Hash functions H and H160. H(x) denotes SHA-256 over the exact byte sequence x, returning 32 bytes. H160(x) denotes RIPEMD-160(SHA-256(x)), returning 20 bytes. All inputs to H and H160 are byte strings formed by the concatenation conventions defined below; strings such as literal labels (“recv”, “snd”, “split”) are 7-bit ASCII bytes in the concatenation, not hex text.
Generator-based serialisation serP(P). serP(P) denotes the compressed SEC1 encoding of the public point P: a single prefix byte 0x02 if y(P) is even or 0x03 if y(P) is odd, followed by the 32-byte big-endian x-coordinate. This yields 33 bytes. Uncompressed encodings (0x04 + x + y) are not used in this specification.
Base58Check(version ∥ payload ∥ checksum). Addresses are Base58Check encodings of a 1-byte version, a payload, and a 4-byte checksum. For pay-to-public-key-hash (P2PKH), version = 0x00, payload = H160(serP(P)), checksum = first four bytes of SHA-256(SHA-256(version ∥ payload)). The encoded address is the Base58 string of version ∥ payload ∥ checksum with no whitespace, no separators, and no leading “0x”.
Concatenation operator “∥”. The operator “∥” denotes byte-level concatenation: if x and y are byte strings, then x ∥ y is the byte string consisting of x immediately followed by y. When concatenating integers (other than LE32 below) they MUST first be encoded as fixed-length big-endian byte strings of their canonical size (e.g., 32 bytes for secp256k1 scalars). When concatenating literal labels, the labels are included as their raw ASCII bytes. No implicit conversions are permitted.
Little-endian index LE32(i). LE32(i) is the 4-byte little-endian encoding of the non-negative integer i modulo 2³². If i ≥ 2³², then LE32(i) = LE32(i mod 2³²). The index domain for this specification is 0 ≤ i ≤ 2³²−1; indices outside this range are invalid. LE32 is used only where explicitly stated; all other integers are big-endian.
Invoice fingerprint H_I. The invoice fingerprint H_I is a 32-byte value defined as H(canonical_json(invoice)). The function canonical_json(·) produces an unambiguous UTF-8 byte sequence for the invoice object according to the encoding rules below (key ordering, whitespace, numeric form, and normalisation are fixed). Any party recomputing H_I over the same invoice MUST obtain the same 32-byte value.
Note identifier NoteID. For a given invoice fingerprint H_I and note index i, the note identifier is NoteID = H(H_I ∥ LE32(i)), a 32-byte value. NoteID uniquely labels a note within an invoice scope; it MUST be used in off-chain logs, reservation locks, and receipts to reference the note without revealing addresses or amounts. For fixed H_I, distinct indices yield distinct NoteIDs up to the collision resistance of SHA-256.
Encoding rules for canonical JSON (normative).
Character encoding. All JSON text is encoded as UTF-8. No byte order mark (BOM) is permitted.
Unicode normalisation. All JSON string values MUST be normalised to NFC prior to byte-level serialisation. Field names are ASCII and need no normalisation.
Object key order. Within every JSON object, keys MUST be sorted in strict lexicographic order by Unicode code point of the key strings (e.g., “addr” < “amount” < “invoice_hash” < “txid”). No deviations per locale are allowed.
Whitespace. No insignificant whitespace is permitted in canonical JSON. Specifically: no spaces or tabs before or after “:”; no spaces after “,”; no trailing commas; no leading or trailing spaces around values. Arrays and objects are compact (e.g., {"a":1,"b":2}).
Numbers. Integers are encoded in base-10 without leading zeros (except zero itself, which is “0”). No “+” sign. No fractional or scientific notation unless the field is explicitly defined as a float; in that case, use the shortest round-trip decimal form that parses identically (IEEE-754 round-trip), with a dot as decimal separator.
Booleans and null. Encode as the lowercase literals “true”, “false”, and “null”.
Byte strings. Fields that carry bytes (keys, hashes, signatures) are encoded as lowercase hexadecimal strings without “0x” prefix, unless a field is explicitly defined to contain Base58 (addresses) or base64url. For public keys, use hex of serP(P). For hashes (H, H160), use full-length lowercase hex.
Time. Timestamp fields use ISO-8601 basic or extended form with UTC “Z” (e.g., “2025-08-26T00:00:00Z”). Offsets other than “Z” are not permitted in canonical form.
Field presence. All required fields MUST appear exactly once. Optional fields MUST be omitted (not null) when absent.
Determinism. canonical_json(x) is the byte sequence obtained after applying all the above rules; H_I is computed over those exact bytes. Any semantically equivalent but differently formatted JSON MUST NOT be used for hashing.
Unambiguous definitions (summary).
H_I := H(canonical_json(invoice)) ∈ {0,1}²⁵⁶. This binds all derivations to a single, byte-exact invoice representation. Any alteration of any invoice field (including reordering or whitespace) changes H_I.
NoteID := H(H_I ∥ LE32(i)) ∈ {0,1}²⁵⁶. This labels note i within the scope of H_I. NoteIDs are independent of addresses, amounts, and inputs; they exist to coordinate construction, logging, and receipts without exposing settlement details.
Implementation notes (conformance).
All scalar reductions are mod n; all field reductions are mod p. Any derived scalar equal to 0 MUST trigger deterministic skip logic to preserve a total, collision-free mapping from indices to usable keys.
All cryptographic hashes operate on the exact concatenated byte strings as defined; mixing hex text with raw bytes is an error. Literal labels used for domain separation are 7-bit ASCII and MUST be included as their byte values.
Base58Check outputs are case-sensitive and MUST match the standard Bitcoin alphabet; no whitespace or line breaks are permitted in encoded addresses.
When serialising or parsing serP(P), only 33-byte compressed encodings (0x02/0x03 + x) are valid in this specification; 65-byte uncompressed form MUST be rejected.
3.. Identities, policy, invoice, and scope
3.1 Roles and key material Alice (payer) holds a long-lived identity pair Kₐ = (kₐ, Pₐ) on secp256k1. Bob (payee) holds a long-lived identity pair Kᵦ = (kᵦ, Pᵦ) and a distinct settlement anchor b/B with B = b·G. Identity keys authenticate and sign off-chain artefacts; they do not appear in locking scripts. The anchor B is the sole on-chain base from which Bob’s per-note recipient keys are derived. Identity and anchor domains are strictly separated: kₐ, kᵦ are never used to authorise on-chain spends; b is never used to sign off-chain identity artefacts.
3.2 Shared secret and invoice scope For each invoice, both parties compute a shared element Z := ECDH(kₐ, Pᵦ) = ECDH(kᵦ, Pₐ) (32-byte x-coordinate, big-endian). Define the invoice fingerprint Hᴵ := H(canonical_json(invoice)) (SHA-256 over the exact UTF-8 bytes). All derivations and labels for this invoice are functions of the pair {Z, Hᴵ}. This scoping is normative: any key, address, amount split, label, reservation lock, or receipt root that does not include {Z, Hᴵ} in its preimage is invalid for this specification. Cross-invoice reuse is forbidden by construction because Hᴵ is invoice-unique and Z is recomputed per counterparty pair.
3.3 Policy (payee → payer): JSON schema and signature Bob issues a signed policy describing bounds and operational parameters that the payer must satisfy when constructing notes. The policy is a single JSON object encoded canonically (see §2), then signed by Bob’s identity key kᵦ.
Canonical key order and field types (normative):
"pk_anchor": string — hex(serP(B)), 66 chars (0x02/0x03 + 64 hex).
"vmin": integer — minimum per-note amount (smallest unit), vmin > 0.
"vmax": integer — maximum per-note amount (smallest unit), vmax ≥ vmin.
"per_address_cap": integer — cap per derived recipient address; MUST satisfy vmin ≤ per_address_cap ≤ vmax.
Constraints and verification (normative): • pk_anchor MUST equal the compressed SEC1 encoding of B; the private b MUST be distinct from kᵦ. • vmin MUST be ≥ the current dust threshold; vmax MUST be ≥ vmin; per_address_cap MUST be within [vmin, vmax]. • feerate_floor MUST be a positive integer measured per byte; both parties compute size estimates identically. • expiry MUST be strictly in the future at the time of acceptance. • sig MUST verify under sig_key over the canonical bytes of the policy with "sig", "sig_key", "sig_alg" omitted from the preimage. • H_policy := H(canonical_json(policy)) is the stable identifier referenced by the invoice.
Canonical skeleton (bytes hashed exactly as shown, no whitespace other than mandated): {"pk_anchor":"…","vmin":…,"vmax":…,"per_address_cap":…,"feerate_floor":…,"expiry":"…","sig_key":"…","sig_alg":"secp256k1-sha256","sig":"…"}
3.4 Invoice (payer → payee): JSON schema and signature Alice issues a signed invoice binding the total to the accepted policy and establishing the scope Hᴵ. The invoice is a single JSON object encoded canonically and signed by Alice’s identity key kₐ.
Canonical key order and field types (normative):
"invoice_number": string — UTF-8 identifier under the payer’s namespace.
"terms": string — UTF-8 human-readable terms or reference thereto.
"unit": string — unit of account label (e.g., "sat", "USD" when quoting; settlement remains on-chain).
"total": integer — total amount in the smallest settlement unit.
Constraints and verification (normative): • policy_hash MUST equal H_policy of the policy accepted for this invoice. • total MUST satisfy feasibility with the accepted bounds: ⌈total ÷ vmax⌉ ≤ N ≤ ⌊total ÷ vmin⌋ for some integer N ≥ 2 (checked later during split). • sig MUST verify under sig_key over the canonical bytes of the invoice with "sig", "sig_key", "sig_alg" omitted from the preimage. • Hᴵ MUST be computed as H(canonical_json(invoice)) after the signature is attached; the same Hᴵ MUST be used in all subsequent derivations for this invoice.
Canonical skeleton: {"invoice_number":"…","terms":"…","unit":"…","total":…,"policy_hash":"…","expiry":"…","sig_key":"…","sig_alg":"secp256k1-sha256","sig":"…"}
3.5 Authenticity and scoping procedure (end-to-end)
Bob constructs the policy object with fields 1–6, sets sig_key = hex(serP(Pᵦ)), sig_alg = "secp256k1-sha256", signs the canonical bytes of the object excluding the signature triplet, and attaches sig. He sends the exact UTF-8 bytes.
Alice verifies pk_anchor structure, bounds, feerate_floor, expiry, and the signature under Pᵦ; she computes H_policy = H(canonical_json(policy)).
Alice constructs the invoice object with fields 1–6, sets sig_key = hex(serP(Pₐ)), sig_alg = "secp256k1-sha256", signs the canonical bytes excluding the signature triplet, attaches sig, and sends the exact UTF-8 bytes.
Bob verifies policy_hash matches his H_policy, checks total and (optional) expiry, verifies Alice’s signature under Pₐ, and computes Hᴵ = H(canonical_json(invoice)).
3.6 Security properties (normative) • Domain separation by {Z, Hᴵ} prevents cross-invoice linkage and replay: the same index i under a different invoice produces unrelated recipient and change keys. • Identity/anchor separation prevents misuse of long-term identity material on-chain and constrains blast radius of compromise: kₐ or kᵦ compromise does not expose b; b compromise affects only settlement keys, not identity assertions. • Signatures bind human-readable terms to cryptographic scope: any alteration of policy or invoice (including key order or whitespace) changes the hash and invalidates the signature.
3.7 Rejection conditions (must fail) • Missing or malformed "pk_anchor", "sig_key", or signatures in either artefact. • vmin ≤ 0, vmax < vmin, per_address_cap outside [vmin, vmax], feerate_floor ≤ 0, or expired artefacts. • policy_hash mismatch between invoice and policy. • Failure to compute Z (invalid public keys). • Any attempt to reuse an existing Hᴵ for a different set of invoice fields.
This section defines the precise identities, artefacts, and scoping required so that the remainder of the construction (per-note derivations, bounded splitting, disjoint funding, and receipts) operates deterministically and remains auditable without revealing identity keys on-chain.
Deterministic per-note recipient keys (sender can address; recipient alone can spend)
4.1 Inputs and domain separation Inputs: the recipient anchor B = b·G (public), the per-invoice shared element Z (32 bytes), and the invoice fingerprint Hᴵ (32 bytes). Domain separation: the literal ASCII label “recv” is included in the preimage; indices use LE32(i). All byte concatenations are with the operator “∥”. Identity keys never appear on-chain.
4.2 Scalar derivation per note For each note index i ≥ 0 derive a scalar tᵢ as a function of {Z, Hᴵ, i}: tᵢ := int( SHA-256( Z ∥ Hᴵ ∥ "recv" ∥ LE32(i) ) ) mod n. Reject-zero rule: if tᵢ = 0, re-derive with a counter appended until non-zero, leaving the index stable: tᵢ := int( SHA-256( Z ∥ Hᴵ ∥ "recv" ∥ LE32(i) ∥ LE32(ctr) ) ) mod n, where ctr = 1,2,… until tᵢ ≠ 0. This “counter bump” preserves a one-to-one mapping from index i to a usable scalar without shifting indices and without introducing a new primitive.
4.3 Recipient public key and address (sender view) The sender computes the per-note recipient public key by a single public-key tweak anchored at B: Pᴮ,ᵢ := B + tᵢ·G. Encode the recipient address as a standard P2PKH address: Addrᴮ,ᵢ := Base58Check( 0x00 ∥ H160( serP( Pᴮ,ᵢ ) ) ). Properties: (a) Pᴮ,ᵢ ≠ B because tᵢ ≠ 0; (b) for fixed B, different i give independent points with overwhelming probability; (c) the sender never learns any private scalar corresponding to Pᴮ,ᵢ.
4.4 Recipient private key (recipient view) Only the recipient, who knows b, computes the spending scalar: kᴮ,ᵢ := ( b + tᵢ ) mod n, with corresponding public key Pᴮ,ᵢ = kᴮ,ᵢ·G by linearity. The recipient uses kᴮ,ᵢ to spend outputs paid to Addrᴮ,ᵢ. The base scalar b is never revealed and never used to sign off-chain artefacts.
4.5 Collision resistance and non-reuse Per-invoice scope: because tᵢ is a function of (Z, Hᴵ, "recv", i), two different invoices (different Hᴵ and/or Z) produce unrelated tᵢ values even at the same index. Per-note uniqueness: for a fixed invoice and anchor B, equality Pᴮ,ᵢ = Pᴮ,ⱼ with i ≠ j would require tᵢ ≡ tⱼ (mod n), which has negligible probability under SHA-256. Cross-role separation: the label “recv” fixes the derivation to recipient keys and prevents accidental overlap with other derivation namespaces (e.g., sender change which uses a different label).
4.6 One-wayness (proof sketch) Sender cannot recover kᴮ,ᵢ. The sender knows tᵢ and the public keys B and Pᴮ,ᵢ = B + tᵢ·G. Suppose the sender could compute kᴮ,ᵢ from these. Then b ≡ kᴮ,ᵢ − tᵢ (mod n) would be recoverable, yielding the discrete logarithm of B to the base G. This contradicts the hardness of the elliptic-curve discrete logarithm problem on secp256k1. Therefore, learning kᴮ,ᵢ without b is infeasible. Outsiders cannot link Pᴮ,ᵢ across notes or invoices. An observer sees addresses derived from Pᴮ,ᵢ but lacks Z and typically lacks Hᴵ. Without Z the observer cannot reproduce tᵢ, hence cannot predict or recognise the set {Pᴮ,ᵢ}. Because Pᴮ,ᵢ = B + tᵢ·G with tᵢ pseudorandom in [1, n−1], the distribution of Pᴮ,ᵢ is computationally indistinguishable from uniform over the subgroup generated by G given B, and linkage reduces to breaking the preimage resistance of SHA-256 or the ECDLP.
4.7 Side-channel and constant-time requirements Scalar and point operations MUST be executed in constant time with respect to secret values (b and kᴮ,ᵢ). Implementations MUST avoid secret-dependent branches and table lookups during scalar multiplication and addition. The reduction “mod n” MUST be constant time. The counter-bump loop executes at most a negligible expected number of iterations (usually zero); its decision is data-independent with respect to b and only depends on the public hash tᵢ = 0 test, which is uniform at 1/n.
4.8 Derivation pseudocode (Unicode, canonical, no LaTeX)
Inputs • Z: 32-byte ECDH element (x-coordinate). • Hᴵ: 32-byte invoice fingerprint. • B: recipient anchor public key (compressed or internal point). • i: note index (0 ≤ i ≤ 2³²−1).
Functions • H(x) := SHA-256(x). • H160(x) := RIPEMD-160(SHA-256(x)). • serP(P) := SEC1 compressed encoding of point P (33 bytes). • LE32(u) := 4-byte little-endian of integer u. • Base58Check(v ∥ p) := Base58Check with version v and payload p (checksum = first 4 bytes of double-SHA-256).
Procedure (sender view)
tᵢ ← int( H( Z ∥ Hᴵ ∥ "recv" ∥ LE32(i) ) ) mod n.
if tᵢ = 0 then ctr ← 1; repeat tᵢ ← int( H( Z ∥ Hᴵ ∥ "recv" ∥ LE32(i) ∥ LE32(ctr) ) ) mod n; ctr ← ctr + 1; until tᵢ ≠ 0.
Pᴮ,ᵢ ← point_add( B, scalar_mul( tᵢ, G ) ).
addrᴮ,ᵢ ← Base58Check( 0x00 ∥ H160( serP( Pᴮ,ᵢ ) ) ).
Procedure (recipient view)
Recompute tᵢ from {Z, Hᴵ, i} with the same counter-bump if necessary.
kᴮ,ᵢ ← ( b + tᵢ ) mod n.
Verify kᴮ,ᵢ·G equals Pᴮ,ᵢ (optional local check).
Use kᴮ,ᵢ to spend outputs paying to addrᴮ,ᵢ.
4.9 Conformance and rejection rules • If serP(B) is not a valid compressed SEC1 encoding or not on curve, reject the invoice before derivation. • If any tᵢ = 0 after counter-bump exhaustion (theoretically impossible for practical counters), reject index i and halt with an error state. • Implementations MUST record the counter value used for each i (if any) in off-chain logs to guarantee reproducibility. • Any per-note artefact (address, label, receipt) that cannot be recomputed from {Z, Hᴵ, i, B} MUST be considered invalid for this specification.
Deterministic bounded note-splitting (exact sum; indices independent of sizes)
5.1 Inputs and feasibility
Input parameters (all in the smallest settlement unit): total T ≥ 1; bounds [v_min, v_max] with 1 ≤ v_min ≤ v_max; per-invoice scope {Z, H_I}. Define the feasible note-count interval N_min := ⌈T ÷ v_max⌉ and N_max := ⌊T ÷ v_min⌋. Validity requires N_min ≤ N_max (otherwise the invoice is infeasible). A valid split consists of an integer N with N_min ≤ N ≤ N_max and an amount vector a[0…N−1] such that for all i: v_min ≤ a[i] ≤ v_max and Σ a[i] = T.
5.2 Deterministic seeding
Define a seed S := H( Z ∥ H_I ∥ "split" ) (32 bytes, SHA-256 of the exact bytes). All randomness below is deterministically derived from S. A counter-based PRNG is used: for j = 0,1,2,… define R_j := H( S ∥ LE32(j) ) and let u_j be the 64-bit unsigned integer formed from the first 8 bytes of R_j (big-endian). The function next_u64() returns u_j and increments j. This yields a reproducible, stateless stream for both parties.
5.3 Choosing N (reproducible, interior-biased) If N_min = N_max, set N := N_min. Otherwise set span := N_max − N_min, mid := ⌊(N_min + N_max)/2⌋, and Δ := ⌊span/4⌋. Draw u := next_u64(). Map u to a symmetric jitter J in [−Δ, +Δ] by J := (u mod (2Δ+1)) − Δ. Set N₀ := mid + J, then clamp to the feasible interval: N := min( max(N₀, N_min), N_max ). This rule is deterministic, prefers interior counts when unconstrained, and never violates feasibility.
5.4 Range-safe uniform integer draws To draw an integer r uniformly from [0, R−1] using next_u64() without modulo bias, use rejection sampling: let M := 2⁶⁴, lim := ⌊M/R⌋·R. Repeatedly draw u := next_u64() until u < lim, then return r := u mod R. This is used below for bounded choices.
5.5 Prefix-clamped construction (exact sum, bounds preserved) Initialise rem := T. For i from 0 to N−2 do:
Compute the feasible interval for a[i] given the remaining slots: slots := N−1−i low := max( v_min, rem − v_max·slots ) high := min( v_max, rem − v_min·slots ) (low ≤ high must hold by feasibility; see §5.7.)
Draw r ∈ [0, (high−low)] uniformly using the range-safe method and set a[i] := low + r.
Set rem := rem − a[i]. After the loop set a[N−1] := rem. By construction v_min ≤ a[N−1] ≤ v_max (proof in §5.7). This produces v_min ≤ a[i] ≤ v_max for all i and Σ a[i] = T exactly.
5.6 Index/size de-correlation by permutation To ensure indices are independent of sizes, apply a deterministic Fisher–Yates shuffle to the completed vector a using a seed derived from S but disjoint from the draw sequence above. Define S_perm := H( S ∥ "permute" ). Instantiate a second counter-based PRNG with S_perm and perform Fisher–Yates on positions 0…N−1, using range-safe draws for each swap index. The permutation is thus fixed by {Z, H_I} and independent of the prefix-clamping choices, so no structural correlation between index and size remains.
5.7 Correctness and termination Feasibility of each step. Assume at step i (0 ≤ i ≤ N−2) that rem satisfies v_min·(N−i) ≤ rem ≤ v_max·(N−i). Then: • Lower bound low = max( v_min, rem − v_max·(N−1−i) ) ensures that after choosing a[i] ≥ low the remaining rem′ = rem − a[i] can still be paid using at most (N−1−i) notes each of size ≤ v_max, because rem′ ≤ rem − (rem − v_max·(N−1−i)) = v_max·(N−1−i). • Upper bound high = min( v_max, rem − v_min·(N−1−i) ) ensures that after choosing a[i] ≤ high the remaining rem′ can still be paid using at least (N−1−i) notes each of size ≥ v_min, because rem′ ≥ rem − (rem − v_min·(N−1−i)) = v_min·(N−1−i). Hence low ≤ a[i] ≤ high implies v_min·(N−1−i) ≤ rem′ ≤ v_max·(N−1−i), maintaining the invariant. The base case at i = 0 holds by the choice of N (N_min ≤ N ≤ N_max). By induction the invariant holds for all i ≤ N−2. Termination occurs after exactly N steps. At i = N−1 we have rem′ = a[N−1] and v_min ≤ a[N−1] ≤ v_max by the invariant, and Σ a[i] = T by construction.
5.8 Determinism and replay Every choice is a pure function of {Z, H_I}, the accepted bounds, and the deterministic PRNG streams from S and S_perm. Given identical inputs, independent implementations compute identical N, identical pre-permutation a, and an identical permutation. No per-note amounts need to be exchanged off-chain; both parties recompute the same vector.
5.9 Edge cases and rejection rules • Infeasible invoice: if N_min > N_max (e.g., T < v_min or T > v_max·N_max under external constraints), reject the invoice before splitting. • Tight bounds: if v_min = v_max then N is forced to N_min = N_max and the vector is constant a[i] = v_min for all i. • Degenerate last step: if N = 1 the construction degenerates to a[0] := T, which must equal v_min = v_max = T to be feasible; otherwise reject at §5.1. • Deterministic streams: the PRNG counters MUST NOT be shared between “split” and “permute”; S_perm isolates the shuffle. Counters MUST be reset to zero for each new seed.
5.10 Pseudocode (Unicode; canonical; no bias)
Seed and PRNG seed_split := H( Z ∥ H_I ∥ "split" ) seed_perm := H( seed_split ∥ "permute" )
function next_u64(seed, counter): R := H( seed ∥ LE32(counter) ) counter := counter + 1 return (first 8 bytes of R as uint64 big-endian), counter
function draw_uniform(R, range): # range ≥ 1 M := 2⁶⁴ lim := (M ÷ range) × range loop: (u, ctr) := next_u64(R.seed, R.ctr) if u < lim: return (u mod range), (R.seed, ctr) else: continue
Choosing N span := N_max − N_min if span = 0: N := N_min else: mid := ⌊(N_min + N_max)/2⌋ Δ := ⌊span/4⌋ (r, R_split) := draw_uniform( R_split, 2Δ + 1 ) J := r − Δ N := clamp( mid + J, N_min, N_max )
Constructing a rem := T for i in 0 … N−2: slots := N−1−i low := max( v_min, rem − v_max × slots ) high := min( v_max, rem − v_min × slots ) (r, R_split) := draw_uniform( R_split, high − low + 1 ) a[i] := low + r rem := rem − a[i] a[N−1] := rem
Permutation (Fisher–Yates using seed_perm) for j in (N−1) down to 1: (r, R_perm) := draw_uniform( R_perm, j+1 ) swap a[j] ↔ a[r]
5.11 Rationale for the permutation step Without permutation, prefix-clamping tends to bias early indices toward interior values of [v_min, v_max] when T is near a boundary, creating a weak but systematic index→size correlation. A seeded Fisher–Yates shuffle removes positional information while preserving multiset equality and the exact sum. Using S_perm derived from S ensures determinism tied to {Z, H_I} and prevents stream re-use, so the shuffle cannot be predicted or recomputed by third parties lacking the invoice scope.
5.12 Security and leakage considerations The split leaks only that all amounts lie in [v_min, v_max]; no per-note sizes are revealed off-chain because both wallets compute a independently. On-chain, an external observer learns the multiset of paid amounts if all notes are broadcast promptly; timing diversity in broadcast is handled elsewhere. The permutation ensures indices carry no information about sizes in off-chain logs or receipts. Deterministic seeding binds all outcomes to the invoice; cross-invoice linkage by split structure is prevented by H_I.
Disjoint coin selection and strict non-overlap across notes
6.1 Snapshot and reservation model Input pool U is the payer’s spendable UTXOs at invoice start, filtered for script type (payer’s own keys), maturity, and policy (confirmations, timelocks). U is a set of outpoints ⟨txid, vout, value, scriptPubKey, keyref⟩. At construction time the wallet takes a read-only snapshot U₀ and builds a reservation table R mapping each note index i to an exclusive input set Sᵢ ⊂ U₀. Exclusivity is strict: Sᵢ ∩ Sⱼ = ∅ for all i ≠ j. Each outpoint carries a reservation state: free → reserved(i) → committed(i) or free (on cancel). Reservations are keyed by NoteID to ensure stable recovery from logs.
6.2 Deterministic ordering To maximise success and ensure reproducibility, notes are funded in a fixed order: sort indices by descending a[i]; ties break by ascending i. The UTXO pool is iterated in a fixed order: sort by (value ascending, then txid lexicographic ascending, then vout ascending). No random tie-breakers appear in selection; given U₀ and a[·], the same R is produced.
6.3 Fee and dust parameters Let feerate_floor be the minimum units-per-byte. For a candidate with m inputs and n outputs (n ∈ {1,2}), estimate bytes as size ≈ 10 + 148·m + 34·n. Required fee = feerate_floor × size (integer, round up). Dust threshold δ_dust is the minimum output value permitted by local policy; any output < δ_dust is invalid. Change outputs must be either ≥ δ_dust or omitted (surplus folded into the fee only when explicitly permitted by policy).
6.4 Selection policy (bounded-knapsack with exact-match preference) Goal for note i: choose Sᵢ such that Σ value(Sᵢ) = a[i] + fee + change, with change ∈ {0} ∪ [δ_dust, ∞). Preference order: (1) exact match with zero change; (2) single-input just-over target with valid change; (3) fewest inputs subject to minimal overshoot and valid change. Any candidate that would produce an output < δ_dust or violate feerate_floor is rejected.
6.5 Algorithm for building R (normative)
Inputs: U₀ (ordered), vector a[0…N−1], feerate_floor, δ_dust. Outputs: reservation table R or failure.
Pre-pass: remove from U₀ any outpoint < δ_dust or not controlled by the payer. Initialise R := ∅. Let Used := ∅.
For i in sort_descending_by a[i] then ascending i:
Target initialisation target := a[i] best := ⊥
Stage A — exact single-input For each u ∈ U₀ \ Used: fee₁ := feerate_floor × (10 + 148·1 + 34·1) if value(u) = target + fee₁: best := {u}; goto Commit
Stage B — exact few-inputs (bounded subset) Search over combinations of up to K_max inputs (K_max default 4) from U₀ \ Used in ascending cardinality. For each candidate set C: m := |C|; fee_m := feerate_floor × (10 + 148·m + 34·1) if Σ value(C) = target + fee_m: best := C; goto Commit (Prune by value sums exceeding target + fee_m + δ_dust unless Stage C.)
Stage C — single-input near-over For each u ∈ U₀ \ Used: fee₂ := feerate_floor × (10 + 148·1 + 34·2) change := value(u) − target − fee₂ if change ≥ δ_dust and change is minimal among examined: best := {u}
FailureForI: Attempt optional fan-out (once per invoice). If fan-out succeeds and confirms per policy, refresh U₀ := U_fanout ⊎ (U₀ \ Used) and restart from i = 0. If fan-out is disabled or fails, abort with “insufficient granularity”.
Return R upon success for all i.
6.6 Definition of “locked” UTXO A locked UTXO is an outpoint in state reserved(i) with metadata {NoteID, timestamp, size_estimate, fee_rate_used}. While reserved, it MUST NOT be considered by selection for any j ≠ i within the same invoice. A reserved UTXO transitions to committed(i) once Tᵢ is fully signed. If a note is cancelled before broadcast, all Sᵢ return to free; if reissued, Sᵢ remain reserved until the new Tᵢ is committed.
6.7 Failure modes and outcomes • Insufficient value: Σ value(U₀) < Σ a[i] + fees_min → abort invoice (fan-out cannot create value). • Insufficient granularity: value(U₀) is concentrated in large outputs so that every candidate either violates δ_dust for change or exceeds K_max/M_max → attempt fan-out. • Conflicting external spend: if a reserved outpoint is spent externally (wallet mutation), invalidate R and rebuild from a fresh U snapshot; log the conflict under the offending outpoint. • Policy violation: any candidate producing outputs below δ_dust or underpaying fee is rejected; if no candidate remains, treat as insufficient granularity.
6.8 Optional preparatory fan-out (payer-only, outside the note set) Purpose: reshape coarse inputs into a set of smaller payer-owned outputs suitable for the target a[·]. Rules: • Destination: strictly to the payer’s own addresses derived from the sender anchor under a distinct namespace label “fund” (not “snd”), e.g., Addr_fund,j; never to the payee; never counted as a note. • Granularity: choose output sizes to cover the histogram of a[·] and expected change values, typically near v_max and mid-range values; all fan-out outputs ≥ max(δ_dust, v_min). • Count: minimise number of fan-out outputs while ensuring coverage; a practical target is ⌈Σ a[i] ÷ v_max⌉ plus a small buffer. • Fee and confirmation: construct with feerate ≥ feerate_floor and, by default, require at least one confirmation before using the resulting outputs in R (deterministic policy); if unconfirmed chaining is allowed by local policy, mark chained notes as risk-accepted in logs. • Scope: fan-out transactions are labelled funding-only with a distinct manifest and are excluded from receipt accounting. • Idempotence: perform at most one fan-out attempt per invoice; repeated fan-outs can re-fragment and harm determinism.
6.9 Determinism and auditability Given U₀, a[·], feerate_floor, δ_dust, K_max, M_max, and the fixed iteration orders, the algorithm produces a unique R. The wallet MUST persist U₀ snapshot metadata, the reservation table R, size/fee calculations, and any fan-out manifest to allow exact recomputation. Change outputs created by notes MUST NOT be admitted into U₀ for funding other notes within the same invoice; they become eligible only after the invoice is closed (completed or aborted).
6.10 Pseudocode (Unicode; canonical)
function build_reservations(U₀, a[0…N−1], feerate_floor, δ_dust): order_notes := sort_desc( (a[i], −i) ) # by amount desc, then i asc order_utxo := sort_asc( (value, txid, vout) ) # deterministic pool order Used := ∅; R := {} fanout_done := false repeat: for i in order_notes: best := select_inputs_disjoint(order_utxo \ Used, a[i], feerate_floor, δ_dust) if best = ⊥: if not fanout_done and policy_allows_fanout(): F := build_fanout(order_utxo \ Used, histogram(a), feerate_floor, δ_dust) if F.success and F.confirmed: U₀ := (U₀ \ F.inputs) ⊎ F.outputs Used := ∅; R := {}; fanout_done := true; goto repeat return ⊥ R[i] := best; Used := Used ∪ best return R
6.11 Guarantees • Strict non-overlap: by construction Sᵢ ∩ Sⱼ = ∅ for i ≠ j, and change from Tᵢ is excluded from funding Tⱼ. • Independence: each Tᵢ can be signed and broadcast without reference to any Tⱼ. • Compliance: every output is ≥ δ_dust; every transaction meets or exceeds feerate_floor. • Reproducibility: with the persisted U₀ and logs, the same R is reconstructed exactly.
Per-note change addresses and change calculation (no overlap; deterministic; auditable)
7.1 Scope and requirements Change outputs are per-note, invoice-scoped artefacts. For each index i, the payer derives exactly one sender-side change address Addrᴬ,ᵢ deterministically from {Z, Hᴵ} and a sender anchor A = a·G. Change produced by note i MUST pay to Addrᴬ,ᵢ. No change output from note i is eligible to fund any other note j ≠ i within the same invoice. Reissues before broadcast preserve the index i and Addrᴮ,ᵢ; Addrᴬ,ᵢ remains stable for i.
7.2 Sender change derivation (deterministic, per note) Inputs: sender anchor A = a·G (public), per-invoice scope {Z, Hᴵ}. Domain separation uses the ASCII label “snd”.
Define the per-note tweak scalar: sᵢ := int( SHA-256( Z ∥ Hᴵ ∥ "snd" ∥ LE32(i) ) ) mod n.
Reject-zero rule: if sᵢ = 0, deterministically bump a counter until non-zero: sᵢ := int( SHA-256( Z ∥ Hᴵ ∥ "snd" ∥ LE32(i) ∥ LE32(ctr) ) ) mod n, ctr = 1,2,...
Define the per-note sender public key and address: Pᴬ,ᵢ := A + sᵢ·G. Addrᴬ,ᵢ := Base58Check( 0x00 ∥ H160( serP(Pᴬ,ᵢ) ) ).
Uniqueness and separation: for fixed A and invoice scope, indices map to distinct Pᴬ,ᵢ with overwhelming probability; “snd” prevents namespace collision with recipient derivations (“recv”).
7.3 Fee and change arithmetic (standard P2PKH) Parameters: • m = number of inputs for note i. • n ∈ {1, 2} = number of outputs (1 = pay-only, 2 = pay + change). • size_bytes ≈ 10 + 148·m + 34·n (P2PKH approximation). • fee_rate_floor = minimum units-per-byte (smallest unit per byte). • fee := ceil( fee_rate_floor × size_bytes ). • δ_dust = dust threshold (policy parameter, smallest unit). • sum_inputs := Σ value(inputⱼ) over the reserved set Sᵢ. • target := a[i] (the note’s recipient amount).
Deterministic evaluation (no circularity): Step A — assume n = 1 (no change). Compute fee₁ := ceil( fee_rate_floor × (10 + 148·m + 34·1) ). • If sum_inputs = target + fee₁ → construct a 1-output transaction: pay target to Addrᴮ,ᵢ; no change. • Else proceed to Step B.
Step B — assume n = 2 (pay + change). Compute fee₂ := ceil( fee_rate_floor × (10 + 148·m + 34·2) ). • change := sum_inputs − target − fee₂. • If change ≥ δ_dust → construct a 2-output transaction: pay target to Addrᴮ,ᵢ and change to Addrᴬ,ᵢ. • If change ∈ [1, δ_dust−1] → invalid (dust). Either add one more input (recompute m, fee₂, change) or reselect Sᵢ per §6; if no valid candidate exists, fail funding for i. • If change ≤ 0 → underfunded; add inputs or reselect Sᵢ.
Rounding rule: fees are rounded up to the nearest integer unit to avoid underpayment. All implementations MUST use identical size estimates and rounding to preserve determinism.
7.4 No-overlap and pool eligibility No intra-invoice reuse: change created by Tᵢ MUST NOT be inserted into the funding pool U for any j ≠ i while the invoice is open (building, broadcasting, or awaiting confirmations). Enforcement is by NoteID tagging: every change UTXO carries {invoice_hash = Hᴵ, index = i} and a state “locked(change,i)” until closure.
7.5 Reissue semantics (pre-broadcast) If a different fee rate is desired before broadcast, reissue note i by constructing a new transaction with possibly different inputs but the same index i, the same recipient address Addrᴮ,ᵢ, and the same sender change address Addrᴬ,ᵢ. Update fee, change, and txid in logs; mark the prior, unbroadcast serialisation as superseded. Addrᴬ,ᵢ stability guarantees that all change for i remains isolated to that index across reissues.
7.6 Security and leakage Identity keys are not used on-chain. Sender change keys are invoice-scoped and per-note; without {Z, Hᴵ, A} an observer cannot regenerate Addrᴬ,ᵢ. Because change pays to unique addresses per index, clustering by shared change across notes of the same invoice is prevented by construction.
7.7 Conformance and rejection rules • Reject if serP(A) is invalid or not on curve. • Reject if sᵢ = 0 after counter-bump exhaustion (theoretical). • Reject any candidate where change ∈ (0, δ_dust). • Reject any construction whose fee < fee_rate_floor × estimated size (post-rounding). • Persist {i, sum_inputs, m, n, size_bytes, fee, change, Addrᴮ,ᵢ, Addrᴬ,ᵢ, txid} for audit.
7.8 Pseudocode (Unicode; canonical)
Inputs • a[i], Sᵢ (reserved inputs for i), fee_rate_floor, δ_dust, A, Z, Hᴵ. • serP, H, H160, Base58Check as defined earlier.
Derive change address sᵢ := int( H( Z ∥ Hᴵ ∥ "snd" ∥ LE32(i) ) ) mod n if sᵢ = 0: ctr := 1 repeat: sᵢ := int( H( Z ∥ Hᴵ ∥ "snd" ∥ LE32(i) ∥ LE32(ctr) ) ) mod n ctr := ctr + 1 until sᵢ ≠ 0 Pᴬ,ᵢ := point_add( A, scalar_mul( sᵢ, G ) ) Addrᴬ,ᵢ := Base58Check( 0x00 ∥ H160( serP(Pᴬ,ᵢ) ) )
Compute fee and change m := |Sᵢ| size₁ := 10 + 148×m + 34×1 fee₁ := ceil( fee_rate_floor × size₁ ) if Σ value(Sᵢ) = a[i] + fee₁: outputs := [ (Addrᴮ,ᵢ, a[i]) ] # n = 1, no change else: size₂ := 10 + 148×m + 34×2 fee₂ := ceil( fee_rate_floor × size₂ ) change := Σ value(Sᵢ) − a[i] − fee₂ if change ≥ δ_dust: outputs := [ (Addrᴮ,ᵢ, a[i]), (Addrᴬ,ᵢ, change) ] # n = 2 else if 0 < change < δ_dust: fail "dust-change" # add input or reselect Sᵢ else: fail "underfunded" # add input or reselect Sᵢ
Construct transaction Tᵢ := make_tx( inputs = Sᵢ, outputs = outputs ) sign_all_inputs(Tᵢ) record_log( i, Hᴵ, Sᵢ, m, outputs, fee, change, txid(Tᵢ), Addrᴬ,ᵢ, Addrᴮ,ᵢ )
Policy enforcement mark_change_utxo(Tᵢ, i, Hᴵ, state="locked(change,i)") forbid_selection_of_locked_change_until_invoice_closed(Hᴵ)
7.9 Bytesize estimator and parameters (normative defaults) • Input (P2PKH): 148 bytes. Output (P2PKH): 34 bytes. Overhead: 10 bytes. • Implementations MAY use precise varint-aware sizing; if so, the same sizing method MUST be used deterministically by both parties. • δ_dust is a fixed policy parameter for the implementation; it MUST be agreed implicitly by equal software or explicitly encoded in policy to avoid disagreements.
Transaction formation per note (independent, standard, fully signed)
8.1 Build order (normative)
For each index i:
Inputs (funding). Take the reserved input set Sᵢ from the reservation table (pairwise disjoint across i). Let m = |Sᵢ| and sum_inputs = Σ value(Sᵢ).
Outputs (payee and optional change). • Primary output: (Addrᴮ,ᵢ, a[i]). • Change output (if required by §7): (Addrᴬ,ᵢ, change[i]). Output ordering is deterministic: primary first, change second if present.
Transaction fields. • nVersion = 1 (32-bit little-endian). • vin count = m (varint). • For each input j in Sᵢ in deterministic order (value ascending, then txid lexicographic ascending, then vout ascending): – prevout.txid (32-byte little-endian), prevout.vout (4-byte little-endian). – scriptSig = empty during preimage construction. – nSequence = 0xFFFFFFFF unless explicitly negotiated otherwise. • vout count = 1 or 2 (varint). • For each output in the order specified: – value (8-byte little-endian). – scriptPubKey = standard P2PKH: OP_DUP OP_HASH160 <20-byte H160(serP(P))> OP_EQUALVERIFY OP_CHECKSIG. • nLockTime = 0 unless explicitly negotiated otherwise.
8.2 Independence and validity (normative)
Tᵢ references only inputs from Sᵢ and pays only to Addrᴮ,ᵢ and (if present) Addrᴬ,ᵢ. • Tᵢ contains no pointers to any Tⱼ, j ≠ i; no input or change overlap is permitted within the invoice. • Any subset of {Tᵢ} may be broadcast in any order without invalidating or starving the remainder.
8.3 NoteMeta schema (canonical JSON; log-only)
Minimal, required fields:
{"i": , "note_id": "<hex 32-byte NoteIDᵢ>", "invoice_hash": "<hex 32-byte Hᴵ>", "addr": "<base58 Addrᴮ,ᵢ>", "amount": <int a[i]>, "txid": "<hex 32-byte txidᵢ>"}
Recommended extensions for audit (all integers in smallest unit; strings lowercase hex unless noted):
{"size_bytes": , "fee": , "feerate_used": , "change_addr": "<base58 Addrᴬ,ᵢ or empty>", "change_amount": <int or 0>, "inputs": [ {"txid":"","vout":,"value":,"scriptPubKey":""} ], "outputs": [ {"addr":"","value":}, {"addr":"","value":}? ], "sig_alg": "secp256k1-sha256", "created_at": "", "status": "unsigned|signed|broadcast|confirmed|reissued|cancelled"}
All NoteMeta objects MUST be encoded canonically (UTF-8, sorted keys, no extraneous whitespace) when hashed or signed. The tuple (i, NoteIDᵢ, txidᵢ) is the stable handle for the note within an invoice.
8.4 Deterministic ordering and tie-breaking (normative)
Inputs inside Tᵢ are ordered by (value ascending, txid lexicographic ascending, vout ascending). • Outputs are ordered: primary primary first, change second if present. • scriptSig contains exactly one DER-encoded ECDSA signature with appended 0x01 hash-type byte and one compressed public key; no additional pushes are permitted.
8.5 Conformance and rejection rules
Reject Tᵢ if any output value < 0 or > 2⁶³−1, or if Σ outputs + fee ≠ Σ inputs. • Reject if any output < δ_dust. • Reject if fee < fee-rate floor × size_bytes (post-rounding). • Reject if any scriptSig is empty or fails standard verification under SIGHASH_ALL. • Reject if NoteIDᵢ mismatches {Hᴵ, i}, or if addr ≠ Addrᴮ,ᵢ derived from {Z, Hᴵ, i, B}. • On reissue prior to broadcast, the new transaction replaces txidᵢ in logs for the same i and NoteIDᵢ; Addrᴮ,ᵢ and Addrᴬ,ᵢ remain unchanged.
Ordering, pacing, and broadcast authority (either side may settle)
9.1 Authority and symmetry Either party may broadcast any subset of the note transactions at any time. The payer (Alice) and the recipient (Bob) hold identical authority to submit a fully signed note TiTᵢ to the network. Duplicate broadcast of identical bytes is benign: identical transactions share the same txid and are deduplicated by nodes. Settlement for a note is defined solely by confirmation depth dd selected by the recipient in policy; once TiTᵢ achieves ≥ dd confirmations to Addrᴮ,ᵢ, that note is settled irrespective of the status of other notes in the invoice.
9.2 Broadcast strategies (permissible) • All-at-once — submit all TiTᵢ immediately. • Paced — submit TiTᵢ according to a deterministic schedule over a window [t0,t1][t₀, t₁] with minimum spacing. • Grouped bursts — partition notes into batches of size β; submit batches with inter-batch gaps.
A strategy is advisory; either party may accelerate or decelerate within the agreed bounds.
9.3 Policy fields (broadcast) — payee → payer (canonical JSON) Augment the policy (§3.3) with:
"broadcast": { "authority": "either", // fixed literal "strategy_default": "paced|all_at_once|bursts", "min_spacing_ms": <int ≥ 0>, // minimum inter-note gap "max_spacing_ms": <int ≥ min_spacing>, // upper bound for paced jitter "burst_size": <int ≥ 1>, // used when strategy_default = "bursts" "burst_gap_ms": <int ≥ 0>, // inter-burst gap "window_start": "|"" ", "window_end": "|"" ", "rebroadcast_interval_s": <int ≥ 60>, // periodic re-announce until confirmed "hold_time_max_s": <int ≥ 0>, // max age for unbroadcast notes before action "confirm_depth": <int ≥ 0> // d, settlement depth }
Normative constraints: • If "window_start" and "window_end" are both present, require window_start < window_end. • If "strategy_default" = "bursts", "burst_size" ≥ 2; otherwise "burst_size" is ignored. • "confirm_depth" defines finality dd for all notes unless overridden in the invoice.
9.4 Invoice overrides — payer → payee (canonical JSON) An invoice MAY propose narrower pacing within policy bounds:
"broadcast_overrides": { "strategy": "paced|all_at_once|bursts", // optional; must be allowed by policy "min_spacing_ms": , // ≤ policy.max_spacing_ms "max_spacing_ms": , // ≥ min_spacing_ms and ≤ policy.max_spacing_ms "burst_size": , // if strategy = "bursts" and ≤ policy.burst_size "burst_gap_ms": , // ≤ policy.burst_gap_ms or equal "window_start": "|"" ", "window_end": "|"" ", "confirm_depth": // ≥ policy.confirm_depth }
Validation: every override must satisfy policy bounds; otherwise the invoice is invalid.
9.5 Deterministic pacing schedule (sender/recipient can compute identically) Seed for schedule: S_pace := H( Z ∥ Hᴵ ∥ "pace" ). Derive a reproducible jitter stream from S_pace (as in §5.2). Let order of notes for scheduling be the stable index order 0…N−1.
All-at-once: for all i, schedule_time[i] := max(now, window_start). Paced: for i = 0…N−1, draw δᵢ uniformly from [min_spacing_ms, max_spacing_ms] via deterministic PRNG; set schedule_time[0] := max(now, window_start); schedule_time[i] := schedule_time[i−1] + δᵢ ms; if schedule_time[i] > window_end (when set), cap at window_end. Bursts: partition indices into batches of size β (β = burst_size). For batch k, set batch_time[k] := (k = 0 ? max(now, window_start) : batch_time[k−1] + burst_gap_ms). All notes in batch k share batch_time[k]; within a batch, apply a tiny deterministic intra-batch offset εᵢ ∈ [0, min_spacing_ms] to break ties.
Either party may use the same schedule; divergence does not affect correctness. The schedule is an off-chain convenience that does not appear on-chain.
9.6 Hold-time and lifecycle timers Define τ_hold_max := hold_time_max_s (policy). A note ii moves through states:
constructed → signed → queued → broadcast → seen → confirmed | orphaned ↘ reissued → signed → queued … ↘ cancelled
Timer rules (normative): • If queued and now − created_at(i) ≥ τ_hold_max, choose exactly one: (a) reissue ii with new inputs Sᵢ′ (same i, same Addrᴮ,ᵢ, same Addrᴬ,ᵢ), reset created_at; or (b) cancel ii explicitly (see §9.8). • If broadcast but not seen by the counterparty within 2 × rebroadcast_interval_s, rebroadcast. • If seen but unconfirmed for more than κ × rebroadcast_interval_s (κ ≥ 3), continue periodic rebroadcast until confirmed or cancelled.
9.7 Reissue procedure (pre-broadcast; deterministic labels preserved) Preconditions: note ii is not confirmed; the previous candidate Tᵢ has not been announced or is to be superseded; reasons include fee adjustment or input conflict.
Steps:
Release prior reservation Sᵢ to free (unless already conflicted on-chain).
Select new Sᵢ′ disjoint from every Sⱼ, j ≠ i (see §6).
Recompute fee and change at the negotiated floor (§7); derive the same Addrᴮ,ᵢ and Addrᴬ,ᵢ.
Build and sign new transaction Tᵢ′.
Only the latest version for index i is eligible for broadcast. Earlier versions MUST NOT be re-broadcast once superseded.
9.8 Cancellation procedure (explicit, off-chain) If a note will not be used:
Set NoteMeta[i].status := "cancelled".
Clear reservation Sᵢ to free; do not derive a new candidate unless re-enabled.
Record a signed cancellation entry by the party effecting the cancel: {"i": i, "note_id": "<NoteIDᵢ>", "invoice_hash": "<Hᴵ>", "action": "cancel", "by": "<sig_key>", "at": "", "sig": ""}.
9.9 Duplicate broadcast semantics • Identical bytes: if Alice and Bob both submit the same Tᵢ, the txid is identical; network deduplicates; state becomes "seen" then "confirmed". • Divergent bytes (only possible after reissue): the policy forbids broadcasting superseded versions. If an older Tᵢ is propagated accidentally, whichever transaction confirms first defines settlement for i; the other is a double-spend loser and MUST be marked "obsolete" in logs. Implementations SHOULD guard by refusing to broadcast any version where NoteMeta[i].version < current_version.
9.10 Logging fields (canonical JSON; per note) Extend NoteMeta (§8.3) with broadcast management:
{ "i": , "note_id": "", "invoice_hash": "", "txid": "", "version": <int ≥ 1>, "status": "queued|broadcast|seen|confirmed|reissued|cancelled|obsolete|orphaned", "scheduled_at": "", "broadcast_at": "|""", "last_rebroadcast_at": "|""", "confirm_depth": , // effective d "supersedes": "|""", "superseded_by": "|""" }
All entries are UTF-8 canonical JSON (sorted keys). Signatures over log events MAY be added using identity keys to bind audit records.
9.11 Conformance and rejection rules • If "broadcast_overrides" conflicts with policy bounds, reject the invoice. • If a party attempts to broadcast a superseded version (version < current_version), reject locally and log an error; do not transmit. • If a queued note exceeds τ_hold_max without reissue or cancel, mark "stale" and require explicit operator action before any broadcast. • Confirmation acceptance: mark a note "confirmed" only after ≥ d confirmations for its output to Addrᴮ,ᵢ; d is taken from invoice override if present, else from policy.confirm_depth.
9.12 Determinism and auditability The schedule derived from S_pace ensures both parties can compute the same nominal broadcast plan without coordination. Regardless of who submits, finality is defined by on-chain confirmations. The “supersedes” chain, version counter, and timestamped log records provide an append-only audit trail proving that, for each index i, exactly one transaction version was intended to settle, and any earlier candidates were explicitly voided off-chain before settlement.
Receipts and selective disclosure
10.1 Purpose Receipts bind a complete set of notes for an invoice into a single commitment that can later be opened selectively. The commitment is a Merkle root M computed over per-note leaves Lᵢ. To acknowledge any subset, the prover discloses those leaves and their Merkle paths; all undisclosed leaves remain hidden. Domain separation by the invoice fingerprint Hᴵ prevents cross-invoice linkage.
10.2 Exact leaf structure (bytes, canonical) Each leaf is the SHA-256 of a byte-exact concatenation of fixed-width fields. The recipient address is represented as its binary payload (version byte and 20-byte public-key hash), not as Base58 text.
Definitions
label := ASCII bytes of the literal string "leaf". • i := note index (0 ≤ i ≤ 2³²−1). • LE32(i) := 4-byte little-endian encoding of i. • txidᵢ := 32-byte transaction identifier in big-endian display order (exact bytes of the hex string). • amountᵢ := 8-byte little-endian unsigned integer (smallest unit). • Pᴮ,ᵢ := recipient public key for note i. • h160ᵢ := RIPEMD-160(SHA-256(serP(Pᴮ,ᵢ))) (20 bytes). • ver := 0x00 (1 byte; P2PKH version). • addr_payloadᵢ := ver ∥ h160ᵢ (21 bytes). (Checksum and Base58 encoding are excluded.)
Leaf preimage and hash preimageᵢ := label ∥ LE32(i) ∥ txidᵢ ∥ amountᵢ ∥ addr_payloadᵢ Lᵢ := SHA-256(preimageᵢ) (32 bytes)
Notes • All concatenations are byte-level. • All numeric encodings and endianness are as stated; no alternative formats are permitted. • If the implementation wishes to include additional fields (e.g., change_amount), they MUST be placed after addr_payloadᵢ and MUST also be fixed-width and identically encoded by both parties; otherwise omit them entirely.
10.3 Merkle tree construction (binary, deterministic) • Leaves: the sequence [L₀, L₁, …, L_{N−1}] in ascending index order. • Internal node hash: H(x ∥ y) := SHA-256(x ∥ y) with x,y each 32 bytes. • Layering: pair adjacent elements left-to-right; if a layer has odd cardinality, duplicate the final element (Bitcoin-style padding) so every parent has two children. Repeat until a single 32-byte value remains; that value is the Merkle root M. • Empty set: N ≥ 1 is required; for N = 1, M = L₀.
10.4 Stored commitment and manifest (canonical JSON) Persist a compact manifest for the invoice. Canonical JSON rules from §2 apply (UTF-8, NFC, sorted keys, no extraneous whitespace).
Minimal manifest { "invoice_hash": "<hex 32-byte H_I>", "merkle_root": "<hex 32-byte M>", "count": , "entries": [ {"i": , "txid": "<hex 32-byte>"}, … ] }
Recommendations • Do not store amounts or addresses in the manifest; keep them private to the payer and payee. • Optionally include "created_at" (ISO-8601 UTC) and detached signatures by identity keys to authenticate the record.
10.5 Selective disclosure proof (single leaf) A proof for index i consists of: • The disclosed leaf data (i, txidᵢ, amountᵢ, addr_payloadᵢ). • The computed leaf hash Lᵢ. • A Merkle path πᵢ = [(pos₀, s₀), (pos₁, s₁), …, (pos_{h−1}, s_{h−1})], where each sⱼ is a 32-byte sibling hash and posⱼ ∈ {"L","R"} indicates whether L (or the running hash) is on the left or right at that layer. • The root M being claimed.
Verification procedure
Recompute preimageᵢ := "leaf" ∥ LE32(i) ∥ txidᵢ ∥ amountᵢ ∥ addr_payloadᵢ.
Compute L ← SHA-256(preimageᵢ).
For j = 0…h−1: • If posⱼ = "L": L ← SHA-256( L ∥ sⱼ ). • If posⱼ = "R": L ← SHA-256( sⱼ ∥ L ).
Accept if and only if L equals the claimed merkle_root M (as 32-byte value). Otherwise reject.
10.6 Selective disclosure proof (multiple leaves) For a subset S ⊆ {0,…,N−1}, provide independent single-leaf proofs (as above) for each i ∈ S, or provide a multi-proof that minimises duplicate siblings by sharing path segments. A multi-proof may be represented as: { "invoice_hash": "", "merkle_root": "", "leaves": [ {"i": , "txid": "", "amount": , "addr_payload": "<hex 21-byte>"}, … ], "sibling_hashes": ["<hex 32-byte>", …], "structure": "" // e.g., a bitstring specifying merge order } Verification recomputes the same frontier using the structure bitstring; if unfamiliar with multi-proofs, verify each leaf independently.
10.7 Privacy properties • For any disclosed leaf i, the verifier learns (i, txidᵢ, amountᵢ, addr_payloadᵢ). • For any undisclosed leaf j ∉ S, no information beyond the length N and the set of indices S is revealed; sibling hashes are computationally indistinguishable from random without preimages. • The manifest stores only (i, txidᵢ) pairs, avoiding publication of amounts and addresses. • Domain separation by H_I ensures that identical (i, txidᵢ, amountᵢ, addr_payloadᵢ) under a different invoice yields a different leaf and root.
10.8 Examples of partial proofs (formats)
Example A — single-leaf proof (index 7) { "invoice_hash": "…h_i…", "merkle_root": "…m…", "leaf": { "i": 7, "txid": "…32-byte-hex…", "amount": 1420, "addr_payload": "00…20-byte-h160…" }, "path": [ {"pos": "L", "hash": "…32-byte-hex…"}, {"pos": "R", "hash": "…32-byte-hex…"}, {"pos": "R", "hash": "…32-byte-hex…"} ] }
Example B — two-leaf independent proofs (indices 3 and 11) [ { "invoice_hash": "…h_i…", "merkle_root": "…m…", "leaf": {"i": 3, "txid": "…", "amount": 600, "addr_payload": "00…"}, "path": [ … ] }, { "invoice_hash": "…h_i…", "merkle_root": "…m…", "leaf": {"i": 11, "txid": "…", "amount": 785, "addr_payload": "00…"}, "path": [ … ] } ]
Example C — redacted manifest with selective disclosure Manifest (public): { "invoice_hash": "…h_i…", "merkle_root": "…m…", "count": 72, "entries": [ {"i": 0, "txid": "…"}, {"i": 1, "txid": "…"}, … ] } Disclosure: provide Example A for i = 7 only. Verifier checks that the txid in the proof matches the manifest entry for i = 7 and that the recomputed root equals merkle_root.
10.9 Edge conditions and rejection rules • Reject any proof where the byte encodings (LE32(i), amountᵢ, addr_payloadᵢ) do not match the normative widths and endianness. • Reject if txidᵢ is not a 32-byte value (invalid hex length or characters). • Reject if any path element has an invalid "pos" or a non-32-byte sibling. • Reject if the recomputed root ≠ merkle_root M. • Reject if invoice_hash in the proof ≠ H_I for the invoice under consideration.
10.10 Construction and logging (normative) • Both parties compute the same M from the same ordered leaf list. • Store merkle_root M, count N, and entries [(i, txidᵢ)] in the manifest. • Optionally sign the manifest with identity keys to authenticate receipt creation: {"sig_key": "<hex serP(Pₐ or Pᵦ)>", "sig_alg": "secp256k1-sha256", "sig": ""} • For each disclosed proof later, store the presented leaf and path alongside the manifest for audit.
10.11 Deterministic recomputation Given H_I, the derivation rules, and full knowledge of the note set, either party can recompute every preimage and thus M exactly. Given only the manifest and a subset proof, a third party verifies membership of the disclosed leaves without learning the undisclosed ones.
10.12 Interoperability notes • The use of addr_payload (ver ∥ h160) avoids ambiguity and normalisation issues inherent in Base58 strings and checksums, while remaining consistent with P2PKH addressing. • If an implementation wishes to future-proof for alternative script types, include an extra 1-byte "script_tag" before addr_payloadᵢ to denote the payload format; for this specification, script_tag = 0x00 (P2PKH) and MUST be present if the extension is adopted by both parties.
Failure handling and exact behaviours
11.1 Overview (normative) Failures are handled deterministically at two levels: per-note and per-invoice. Every transition is recorded in canonical JSON with timestamps and signatures by the acting party’s identity key. All timers are measured in seconds since the Unix epoch; all timestamps are ISO-8601 UTC.
— Global timers and flags — • τ_hold_max := policy.broadcast.hold_time_max_s (maximum queued time before action). • τ_rebroadcast := policy.broadcast.rebroadcast_interval_s (periodic re-announce). • d_confirm := effective confirmation depth (invoice override or policy default). • expires_at_policy := policy.expiry. • expires_at_invoice := invoice.expiry (if present). • fanout_allowed := implementation/policy toggle; at most one fan-out per invoice. • supersedes flag := indicates a newer transaction version exists for the same index i. • voided_offchain := indicates older raw bytes must not be broadcast.
11.2 Per-note state machine (textual diagram)
States: S0 Constructed → S1 Signed → S2 Queued → S3 Broadcast → S4 Seen → S5 Confirmed ↘ R Reissued (loops back to S1) ↘ C Cancelled (terminal) ↘ O Orphaned (from S5; loops to S3 on rebroadcast) ↘ X Obsolete (terminal; older superseded bytes) ↘ F Conflict (terminal; external spend of reserved inputs)
Transitions (events → new state): • construct(i) → S0 • sign(i) → S1 • enqueue(i) → S2 • t ≥ scheduled_at(i) ∧ authority_either → broadcast(i) → S3 • mempool_accept(i) → S4 • conf_depth(i) ≥ d_confirm → S5 • t − created_at(i) ≥ τ_hold_max ∧ S2 → reissue(i) → R → S1 • explicit_cancel(i) at S0/S1/S2/S3/S4 → C • superseded(i) (new Tᵢ′ built) → old version → X; new version → S1 • external_conflict(Sᵢ) (reserved input spent elsewhere) → F • reorg_orphan(i) at S5 → O → rebroadcast(i) → S3
Determinism: given identical logs and timers, the same sequence of transitions occurs.
11.3 Invoice-level state machine (textual diagram)
I0 Open → I1 Fan-out-Pending (optional) → I2 Building → I3 Ready → I4 Broadcasting → I5 Closing ↘ IF Insufficient-UTXO (terminal failure) ↘ IE Expired (terminal failure) ↘ IS Stopped (operator abort) ↘ IC Completed (terminal success: all i ∈ [0…N−1] in S5)
Triggers: • accept(policy, invoice) → I0 • fanout_start (if required) → I1 → fanout_confirmed → I2 • reservations_built(R) → I3 • any_broadcast → I4 • all notes S5 → IC • t ≥ expires_at_invoice or t ≥ expires_at_policy → IE • Σ value(U₀) insufficient or granularity impossible after one fan-out → IF
11.4 Deterministic behaviours by failure class (normative)
A) Insufficient UTXOs Condition: Σ value(U₀) < Σ a[i] + fees_min, or granularity failure after bounded-knapsack (§6). Action sequence:
If fanout_allowed = true and no prior fan-out: perform exactly one preparatory fan-out (payer→payer) per §6.8; wait per policy (confirm or risk-accepted) deterministically.
Rebuild U₀ from fan-out result; rebuild R.
If still insufficient, set invoice.state := IF and emit failure record; no notes are issued or all outstanding notes are cancelled (see 11.6). Audit: record {"event":"insufficient_utxo","at":…,"fanout_attempted":true|false}.
B) Fee change before broadcast Condition: either party requires a new fee rate > fee_rate_floor while a note i is S0/S1/S2 and no broadcast has occurred for i. Action sequence:
Reissue i: select new Sᵢ′ (disjoint), recompute fee at requested rate (must be ≥ floor), preserve Addrᴮ,ᵢ and Addrᴬ,ᵢ.
Set NoteMeta[i].supersedes := txid_old; version := version + 1.
Mark old bytes voided_offchain := true and status := "reissued".
New candidate enters S1 (Signed). Audit: append {"i":i,"event":"reissue","supersedes":"<txid_old>","version":v}.
C) Conflicting spend detected Condition: any u ∈ Sᵢ appears spent on-chain or reserved contradictorily by external wallet mutation while note i ∈ {S0,S1,S2}. Action sequence:
Abort invoice build immediately: invoice.state := I2 (rebuild) or I5 (closing) depending on operator policy.
Invalidate R; take a fresh snapshot U′; rebuild reservations per §6 or fail IF.
For the conflicted note i, mark status := "conflict" and record the offending outpoint. Audit: record {"i":i,"event":"conflict_external","outpoint":{"txid":…,"vout":…},"at":…}. Determinism: any detected conflict forces a full reservation rebuild; partial salvage is not permitted.
D) Reorg Condition: a confirmed note i (S5) is orphaned by reorganisation (conf_depth falls below d_confirm). Action sequence:
Transition i → O (Orphaned).
Rebroadcast the same raw bytes of Tᵢ (no reconstruction, no re-sign).
Return to S3 (Broadcast) → S4 (Seen) → S5 (Confirmed) as usual. Audit: record {"i":i,"event":"orphaned","rebroadcast":true,"txid":"","at":…}. Constraint: reissue is not permitted for orphaned-but-valid transactions unless they later double-spend fail; first action is always rebroadcast.
E) Expiry Condition: t ≥ expires_at_invoice or t ≥ expires_at_policy before all notes reach S5. Action sequence:
For every i ∈ {S0,S1,S2}: set status := "cancelled"; release reservations.
For i ∈ {S3,S4}: continue passive monitoring until either confirmed (counted) or timeout set by operator; no new broadcasts after expiry.
Set invoice.state := IE and emit summary record with counts by status. Audit: record {"event":"invoice_expired","at":…,"counts":{"cancelled":…,"broadcast":…,"confirmed":…}}.
11.5 Timers and automated actions (per note)
Scheduler tick (every τ_rebroadcast seconds): • If status = "broadcast" ∨ "seen" and conf_depth < d_confirm → rebroadcast raw bytes. • If status = "queued" and t − created_at >= τ_hold_max → reissue(i) (preferred) or cancel(i) per policy. • If status = "signed" and scheduled_at reached → broadcast(i).
All actions are idempotent: rebroadcasting identical bytes preserves txid; reissuing increments version and voids older bytes.
11.6 Precise reissue and cancel records (canonical JSON)
Reissue record (append-only): { "invoice_hash":"", "i":, "note_id":"<hex NoteIDᵢ>", "event":"reissue", "version":, // new version "supersedes":"", "txid_new":"", "addr_recv":"<base58 Addrᴮ,ᵢ>", "addr_change":"<base58 Addrᴬ,ᵢ>", "fee":, "feerate_used":, "at":"", "by":"<hex serP(Pₐ or Pᵦ)>", "sig_alg":"secp256k1-sha256", "sig":"" }
Cancel record (append-only): { "invoice_hash":"", "i":, "note_id":"<hex NoteIDᵢ>", "event":"cancel", "reason":"hold_timeout|operator|expiry|insufficient_utxo", "version":, "at":"", "by":"<hex serP(Pₐ or Pᵦ)>", "sig_alg":"secp256k1-sha256", "sig":"" }
Conflict record (append-only): { "invoice_hash":"", "i":, "note_id":"<hex NoteIDᵢ>", "event":"conflict_external", "outpoint":{"txid":"","vout":}, "at":"", "by":"<hex serP(Pₐ or Pᵦ)>", "sig_alg":"secp256k1-sha256", "sig":"" }
Orphaned record (append-only): { "invoice_hash":"", "i":, "note_id":"<hex NoteIDᵢ>", "event":"orphaned", "txid":"", "rebroadcast":true, "at":"", "by":"<hex serP(Pₐ or Pᵦ)>", "sig_alg":"secp256k1-sha256", "sig":"" }
11.7 Rejection conditions (must fail immediately) • Attempt to broadcast a superseded version (version < current_version). • Attempt to broadcast after invoice expiry (except passive rebroadcasts of already-broadcast notes until final state is reached). • Attempt to reuse change from one note to fund another note in the same invoice. • Attempt to continue with reservations after detecting external conflict; a full rebuild is mandatory.
11.8 Determinism and auditability guarantees • Given identical inputs (policy, invoice, {Z,Hᴵ}, U₀) and identical timer parameters, two independent implementations will produce identical reservation tables, note schedules, and failure-handling transitions. • The append-only reissue/cancel logs, combined with NoteMeta and the receipt manifest (§10), are sufficient to reconstruct the exact intended settlement set and to demonstrate that exactly one transaction per index i was designated to settle, with earlier byte serialisations voided before settlement.
Privacy and linkage analysis (bounded disclosures; role separation)
12.1 Adversary model (explicit) • A₁ — Passive chain analyst: observes full blocks and mempools, parses scripts, amounts, timings, and addresses; cannot break SHA-256, RIPEMD-160, or the secp256k1 discrete-log problem. • A₂ — Passive network observer: sees when and from which IP a transaction is first announced; cannot tamper with payloads. • A₃ — Inquisitive counterparty: one party attempts to learn more than the protocol reveals; has its own keys and transcript but lacks the other party’s secret scalars. • A₄ — Data-broker adversary: correlates public off-chain artefacts (e.g., leaked policies, screenshots, manifests) with on-chain activity. Assumptions: preimage resistance of SHA-256, collision resistance where invoked, and hardness of ECDLP on secp256k1.
12.2 Role separation and cryptographic domains Identity keys never appear in locking scripts; only settlement keys derived from anchors do. All recipient keys are derived in the “recv” domain: Pᴮ,ᵢ = B + tᵢ·G, tᵢ := int(SHA-256(Z ∥ Hᴵ ∥ "recv" ∥ LE32(i))) mod n, tᵢ ≠ 0. All sender-change keys are derived in the “snd” domain: Pᴬ,ᵢ = A + sᵢ·G, sᵢ := int(SHA-256(Z ∥ Hᴵ ∥ "snd" ∥ LE32(i))) mod n, sᵢ ≠ 0. Domain labels (“recv”, “snd”) and the invoice fingerprint Hᴵ ensure that the same index i across invoices yields unrelated points; identity/settlement separation prevents anchor leakage via off-chain signatures.
12.3 What an outsider can and cannot infer
Linking recipient addresses across notes (same invoice). Cannot: Without Z and Hᴵ, an outsider cannot recompute tᵢ and so cannot test membership of any address in the set {B + tᵢ·G}. Observing two spends reveals serP(Pᴮ,ᵢ) and serP(Pᴮ,ⱼ), but the relation Pᴮ,ᵢ − Pᴮ,ⱼ = (tᵢ − tⱼ)·G holds for all random points; without tᵢ, tⱼ it gives no anchor linkage. Limit: If the anchor B itself is leaked off-chain and the adversary also learns Z or Hᴵ (both should remain secret), they could regenerate addresses; by design, neither Z nor Hᴵ is published.
Linking across invoices (same payee). Cannot: Hᴵ changes per invoice; tᵢ depends on Hᴵ. Even with B known, without Z the adversary cannot bind addresses to a particular invoice scope. Limit: Long-term address-reuse does not occur; accidental reuse would require tᵢ collision or (b + tᵢ) mod n repeating—negligible under SHA-256 and modular arithmetic.
Change-based clustering within an invoice. Prevented: Change pays to per-note Addrᴬ,ᵢ under “snd”; selection forbids using change from one note to fund another of the same invoice. The common “shared-change” heuristic therefore does not tie notes i and j together. Limit: Each individual note still clusters its own inputs (multi-input heuristic) to the payer, which is acceptable: the payer’s identity is not a privacy target here; recipient unlinkability is.
(6) Off-chain artefact correlation. Possible: If a policy or manifest leaks publicly (A₄), an adversary learns B and (i, txid) pairs. Limit: txid alone does not reveal amounts or change; B does not reveal kᴮ,ᵢ or tᵢ; receipts commit to data but selective proofs reveal only chosen leaves.
12.4 Recommended mitigations (no new primitives)
M₁ — Either-side broadcast. Let the recipient submit a random subset first. This breaks any single-origin heuristic; first-seen IP attribution no longer correlates the whole set to the payer.
M₂ — Paced or burst scheduling with deterministic jitter. Use the S_pace-derived schedule (§9) to spread announcements over a window. Choose “bursts” with β ≪ N and non-uniform burst gaps to defeat simple time-window clustering.
M₃ — Wider published bounds than actually used. Bob may publish [v_min_pub, v_max_pub] with v_min ≤ v_min_pub and v_max_pub ≤ v_max; enforce the true internal bounds off-chain. Observers only learn the loose public interval.
M₄ — Interleave multiple invoices. When operationally feasible, the payer constructs two or more invoices concurrently and interleaves broadcasts. This raises ambiguity without changing any primitive.
M₅ — Strict no-reuse within invoice. Enforce “no intra-invoice change reuse” and “no cross-note input reuse” (already normative) to preclude standard clustering hooks.
M₆ — Avoid deterministic time-of-day fingerprints. Do not always start at the top of the hour; salt schedule start with H(Z ∥ Hᴵ ∥ "pace") to vary timing patterns.
M₇ — Minimal public metadata. Manifests published externally should contain only (i, txid) and the Merkle root M; keep amounts, addresses, and H160 payloads private. Sign manifests with identity keys to prove authorship without revealing anchors.
12.5 Specific inferences and their limits (tabular)
Inference: “These outputs belong to the same payer.” Basis: multi-input heuristic per note. Limit: Does not link different notes together; does not link recipient addresses across notes.
Inference: “These outputs belong to the same payee.” Basis: proximity, equal denominations in [v_min, v_max]. Limit: Without Z and Hᴵ, cannot prove common derivation from B; pacing and interleaving degrade confidence.
Inference: “This set is one invoice of size T.” Basis: near-simultaneous group of bounded outputs summing to ≈ T. Limit: Pacing plus concurrent invoices makes partitioning NP-hard in practice; receipts reveal only when the payee consents.
Inference: “These two payee public keys share an anchor B.” Basis: Pᴮ,ᵢ − Pᴮ,ⱼ is some scalar times G. Limit: Trivial in any cyclic group; provides no test for a shared anchor without tᵢ, tⱼ; computing b from B is ECDLP-hard.
12.6 Residual risks and operator guidance
R₁ — Broadcast-pattern fingerprinting. Residual risk: sophisticated a₂ correlates bursts over long windows. Guidance: vary β and gaps per invoice; occasionally switch who broadcasts first.
R₂ — Anchor disclosure. Residual risk: if B is widely published and Z or Hᴵ leaks, address sets become derivable. Guidance: treat Z and Hᴵ as confidential; never publish invoice JSON or transcripts; rotate anchors periodically (operational) without changing identity keys.
R₃ — Wallet hygiene. Residual risk: accidental reuse if software ignores reject-zero logic or mis-scopes derivations. Guidance: enforce reject-zero; unit tests that re-derive all Pᴮ,ᵢ and Pᴬ,ᵢ from logs; fail closed on any mismatch.
Summary. Outsiders cannot link recipient addresses across notes or invoices because identity keys never appear on-chain and per-note keys require {Z, H_I} with domain separation. Amount bounds leak a coarse interval; pacing, interleaving, and wider published bounds blunt inference. Change addresses are per-note under “snd”, eliminating shared-change clustering. All recommendations require no new primitives—only deterministic use of the ones already defined.
Security considerations (keys, transcripts, and logs)
13.1 Key domains and separation (normative) • Identity (off-chain): Kₐ = (kₐ, Pₐ), Kᵦ = (kᵦ, Pᵦ). Purpose: authenticate messages; sign policy, invoice, and logs. Identity keys MUST NEVER appear in locking scripts or be used to spend on-chain funds. • Anchors (on-chain bases): A = a·G (sender change domain), B = b·G (recipient receive domain). Purpose: derive settlement keys per invoice and per note. Anchor private scalars (a, b) MUST NEVER sign off-chain identity artefacts. • Scope tuple: {Z, Hᴵ}. Z := ECDH(kₐ, Pᵦ) = ECDH(kᵦ, Pₐ) (32-byte x-coordinate). Hᴵ := H(canonical_json(invoice)). Hᴵ MUST be unique per invoice and MUST NOT be reused. Z SHOULD be recomputed on demand and SHOULD NOT be stored at rest unless encrypted under an operator-controlled mechanism (implementation policy).
13.2 Key-handling checklist (operational) ✓ Generate identity and anchor keys on distinct cryptographic modules or profiles. ✓ Store kₐ, kᵦ, a, b in separate sealed locations; never co-reside identity and anchor secrets for the same principal on the same soft-keystore without OS isolation. ✓ Export only compressed public keys (serP) to peers; never export private material. ✓ Enforce constant-time scalar use; disable debug traces of secret scalars. ✓ Back up identity keys (kₐ, kᵦ) with out-of-band recovery; back up anchors (a, b) with the same or stronger controls; record public anchors (A, B) separately for verification. ✓ Rotate anchors periodically (operational policy) and immediately on suspected compromise; old anchors remain valid only for historical settlement and verification. ✓ Bind every signed artefact to the exact canonical JSON bytes; do not sign ad hoc serialisations. ✓ Treat transcripts and logs as confidential; encrypt at rest per environment policy; access is least-privilege. ✓ Never reuse Hᴵ; never derive keys or amounts without {Z, Hᴵ} and the correct domain label (“recv”, “snd”). ✓ Refuse to proceed if any signature verification or canonicalisation check fails.
13.3 Compromise impact summary • kₐ or kᵦ compromised → attacker can forge off-chain messages/logs for that identity; on-chain funds unaffected; rotate identity key; rebind future policies/invoices; mark prior logs with rotation event. • a (sender anchor) compromised → attacker can spend sender change if they control UTXOs addressed to Pᴬ,ᵢ; recipient funds unaffected; rotate A, quarantine outstanding change, rebuild change policy. • b (recipient anchor) compromised → attacker may spend future notes to Addrᴮ,ᵢ; rotate B immediately; cease using the compromised anchor; existing receipts remain valid.
13.4 Log and transcript principles • Canonical JSON only: UTF-8, NFC, sorted keys, no extraneous whitespace (see §2). • Detached signatures: every signed record includes {"sig_key","sig_alg","sig"} where the signature is computed over the canonical bytes of the record with the signature triplet omitted from the preimage. • Append-only: logs are monotone sequences with sequence numbers and a hash-chain (“prev_hash”) for tamper evidence. • Append-only: logs are monotone sequences with sequence numbers and a hash-chain (“prev_hash”) for tamper evidence. • Append-only: logs are monotone sequences with sequence numbers and a hash-chain (“prev_hash”) for tamper evidence. • Append-only: logs are monotone sequences with sequence numbers and a hash-chain (“prev_hash”) for tamper evidence.
13.5 Signature placement (normative) • Policy (Bob) — signed by kᵦ. • Invoice (Alice) — signed by kₐ. • Reservation table R and per-note NoteMeta updates — signed by the party performing the action (payer when constructing/funding; either when broadcasting). • Receipt manifest (Merkle root M) — at least signed by the recipient; co-signature by the payer is RECOMMENDED for bilateral acknowledgement. • Reissue / cancel / conflict / orphan records — signed by the actor identity key initiating the state change.
13.6 Core log schemas (canonical JSON; required fields; sorted keys)
A) Key registry (local, not exchanged) { "anchors": { "payer": {"pub":"<hex serP(A)>"}, "payee": {"pub":"<hex serP(B)>"} }, "identities": { "payer": {"pub":"<hex serP(Pₐ)>"}, "payee": {"pub":"<hex serP(Pᵦ)>"} }, "created_at":"", "sig_alg":"secp256k1-sha256", "sig_key":"<hex serP(Pₐ or Pᵦ)>", "sig":"" }
B) Policy record (Bob → Alice) — as §3.3 plus envelope { "policy": { …canonical policy object per §3.3… }, "hash":"", "record_type":"policy_record", "prev_hash":"<hex|"">", "seq":, "created_at":"", "sig_key":"<hex serP(Pᵦ)>", "sig_alg":"secp256k1-sha256", "sig":"" }
C) Invoice record (Alice → Bob) — as §3.4 plus envelope { "invoice": { …canonical invoice object per §3.4… }, "invoice_hash":"<hex Hᴵ>", "record_type":"invoice_record", "prev_hash":"", "seq":, "created_at":"", "sig_key":"<hex serP(Pₐ)>", "sig_alg":"secp256k1-sha256", "sig":"" }
D) Reservation table (payer, deterministic R) — summary plus per-note entries { "invoice_hash":"<hex Hᴵ>", "record_type":"reservations", "entries":[ { "i":, "note_id":"<hex NoteIDᵢ>", "inputs":[{"txid":"","vout":,"value":}], "sum_inputs":, "feerate_used":, "created_at":"" }, … ], "prev_hash":"", "seq":, "sig_key":"<hex serP(Pₐ)>", "sig_alg":"secp256k1-sha256", "sig":"" }
E) NoteMeta (per note; extends §8.3 with lifecycle) { "i":, "note_id":"", "invoice_hash":"<hex Hᴵ>", "addr":"<base58 Addrᴮ,ᵢ>", "amount":, "txid":"<hex|"">", "change_addr":"<base58 Addrᴬ,ᵢ|"">", "change_amount":, "size_bytes":, "fee":, "feerate_used":, "inputs": [ {"txid":"","vout":,"value":,"scriptPubKey":""} ], "outputs": [ {"addr":"","value":}, {"addr":"","value":}? ], "sig_alg": "secp256k1-sha256", "created_at": "", "updated_at": "", "prev_hash":"", "seq":, "sig_key":"<hex serP(Pₐ or Pᵦ)>", "sig_alg":"secp256k1-sha256", "sig":"" }
F) Receipt manifest (Merkle root M) — see §10; repeated here with envelope { "invoice_hash":"<hex Hᴵ>", "merkle_root":"", "count":, "entries":[{"i":,"txid":""}…], "record_type":"receipt_manifest", "prev_hash":"", "seq":, "created_at":"", "sig_key":"<hex serP(Pᵦ)>", "sig_alg":"secp256k1-sha256", "sig":"" }
G) Event records (reissue / cancel / conflict / orphan) — see §11.6; all include {invoice_hash, i, note_id, prev_hash, seq, created_at, sig_key, sig_alg, sig}.
13.7 Transcript chaining (tamper evidence) Each persisted record Rₖ includes "prev_hash" = H(canonical_bytes(Rₖ₋₁ without its signature triplet)) and "seq" = seqₖ₋₁ + 1. A daily (or per-invoice) journal root J := MerkleRoot( H(canonical_bytes(Rₖ)) for all k in window ) MAY be published or anchored externally to provide a timestamped, tamper-evident commitment to the log without revealing contents.
13.8 Retention and export • Retain logs and manifests for the statutory/accounting horizon; redact only where policy permits. • Export is the ordered canonical JSON stream with signatures; verification replays the hash-chain and signatures, recomputes R, NoteMeta, and M, and checks that exactly one final transaction per i reached "confirmed".
13.9 Conformance (must-pass checks) • Identity/anchor separation enforced at API boundaries (no cross-domain signing). • Hᴵ uniqueness per invoice; refusal to load or write logs if an Hᴵ collision is detected. • All signatures verify over canonical bytes; any failure halts processing. • Hash-chain continuity: each "prev_hash" matches the prior record; gaps or forks are rejected unless explicitly marked as a rotated journal with a signed boundary record.
This section specifies the mandatory operational controls for key isolation and the precise, signed, append-only logging required to reconstruct, verify, and audit every invoice, note, and receipt without exposing identity keys on-chain.
Minimal message frames (UTF-8 JSON; canonical where hashed)
14.1 Policy (Bob → Alice)
Wire frame (compact):
{"pk_anchor":"", "vmin":, "vmax":, "feerate_floor":, "expiry":"", "sig":""}
Canonical field order (for signature preimage): expiry, feerate_floor, pk_anchor, vmax, vmin (“sig” is excluded from the preimage)
Signature input (bytes to sign): canonical_json({pk_anchor, vmin, vmax, feerate_floor, expiry}) → SHA-256 → ECDSA sign with K_B.
Verification rules (normative): • pk_anchor is 33-byte SEC1 compressed hex (0x02/0x03 + 32-byte x), on-curve. • vmin ≥ 1, vmax ≥ vmin, feerate_floor ≥ 1; expiry is future UTC. • Verify sig with P_B over SHA-256(canonical_json(policy_without_sig)). • H_policy := SHA-256(canonical_json(policy_with_sig)) is the policy hash used by the invoice. • Canonical JSON per §2 (UTF-8, NFC strings, sorted keys, no extra whitespace).
14.2 Invoice (Alice → Bob)
Wire frame (compact):
{"invoice_number":"", "terms":"", "unit":"", "total":, "policy_hash":"<hex 32-byte H_policy>", "expiry":"", "sig":""}
Canonical field order (for signature preimage): expiry, invoice_number, policy_hash, terms, total, unit (“sig” is excluded from the preimage)
Signature input (bytes to sign): canonical_json({invoice_number, terms, unit, total, policy_hash, expiry}) → SHA-256 → ECDSA sign with K_A.
Verification rules (normative): • total ≥ 1; policy_hash equals H_policy from the accepted policy (14.1). • Verify sig with P_A over SHA-256(canonical_json(invoice_without_sig)). • H_I := SHA-256(canonical_json(invoice_with_sig)) is the invoice fingerprint for all derivations. • If expiry is present, it MUST be future UTC at acceptance.
14.3 NoteMeta (optional to exchange; log/audit)
Wire frame (compact):
{"i":"", "addr":"", "amount":"", "invoice_hash":"<hex 32-byte H_I>", "txid":"<hex 32-byte>"}
Canonical field order (when hashing/recording): addr, amount, i, invoice_hash, txid
Verification rules (normative): • i ∈ [0, 2³²−1]; amount ≥ 1. • addr decodes to version 0x00 and a 20-byte payload. • txid is 32-byte hex (big-endian textual form). • invoice_hash equals H_I for this invoice. • addr must equal Addr_B,i derived from {Z, H_I, i, B} (sender/recipient can recompute). • Optional signatures over NoteMeta (if used) are made with the actor’s identity key over SHA-256(canonical_json(NoteMeta_without_sig)).
14.4 Canonical JSON recap (applies wherever “canonical_json” is referenced)
UTF-8 encoding; no BOM; all strings NFC-normalised. • Keys sorted lexicographically by Unicode code point. • No insignificant whitespace (compact objects/arrays). • Integers in base-10 without leading zeros (except “0”). • Hex fields are lowercase, no “0x”. • Timestamps are ISO-8601 with “Z”.
14.5 Cross-checks (receiver MUST perform)
Policy:
Parse and validate fields; verify ECDSA(sig, SHA-256(canonical_json(policy_without_sig)), P_B).
Compute H_policy = SHA-256(canonical_json(policy_with_sig)).
Invoice:
Verify policy_hash = H_policy.
Verify ECDSA(sig, SHA-256(canonical_json(invoice_without_sig)), P_A).
Compute H_I = SHA-256(canonical_json(invoice_with_sig)) and cache {Z, H_I} for derivations.
NoteMeta (if exchanged):
Verify invoice_hash = H_I.
Recompute Addr_B,i; check equality.
Optionally verify txid exists or matches a provided raw transaction.
All frames are minimal by design; anything not explicitly listed is out of scope and MUST be omitted.
Reference pseudocode
Per-note recipient key (sender view)
Z := ECDH(k_A, K_B) # 32-byte x-coordinate H_I := SHA256(canonical_json(invoice)) # invoice fingerprint
t_i := int( SHA256( Z ∥ H_I ∥ "recv" ∥ LE32(i) ) ) mod n if t_i = 0: ctr := 1 repeat: t_i := int( SHA256( Z ∥ H_I ∥ "recv" ∥ LE32(i) ∥ LE32(ctr) ) ) mod n ctr := ctr + 1 until t_i ≠ 0
P_Bi := point_add( B, scalar_mul(t_i, G) ) addrB_i := Base58Check( 0x00 ∥ RIPEMD160( SHA256( serP(P_Bi) ) ) )
Per-note sender change (payer view)
s_i := int( SHA256( Z ∥ H_I ∥ "snd" ∥ LE32(i) ) ) mod n if s_i = 0: ctr := 1 repeat: s_i := int( SHA256( Z ∥ H_I ∥ "snd" ∥ LE32(i) ∥ LE32(ctr) ) ) mod n ctr := ctr + 1 until s_i ≠ 0
P_Ai := point_add( A, scalar_mul(s_i, G) ) addrA_i := Base58Check( 0x00 ∥ RIPEMD160( SHA256( serP(P_Ai) ) ) )
Bounded split (exact sum)
S := SHA256( Z ∥ H_I ∥ "split" ) S_perm := SHA256( S ∥ "permute" )
function next_u64(seed, ctr): R := SHA256( seed ∥ LE32(ctr) ) ctr := ctr + 1 return (first 8 bytes of R as uint64), ctr
function draw_uniform(seed, ctr, range): # unbiased draw in [0, range−1] M := 2^64 lim := (M ÷ range) × range loop: (u, ctr) := next_u64(seed, ctr) if u < lim: return (u mod range), ctr
Nmin := ceil(T ÷ v_max) Nmax := floor(T ÷ v_min) N := choose_count(S, Nmin, Nmax) # deterministic, interior-biased
rem := T ctr := 0 for i in 0 … N−2: slots := N−1−i low := max( v_min, rem − v_max × slots ) high := min( v_max, rem − v_min × slots ) (r, ctr) := draw_uniform(S, ctr, high − low + 1) a[i] := low + r rem := rem − a[i] a[N−1] := rem
Fisher–Yates permutation to decorrelate indices from sizes
ctrp := 0 for j in (N−1) down to 1: (r, ctrp) := draw_uniform(S_perm, ctrp, j + 1) swap a[j] ↔ a[r]
Coin selection with reservation
U := snapshot_available_utxos() payer-owned, spendable, filtered R := {} reservation table: i ↦ S_i
for i in note_indices_in_fixed_order(): e.g., by descending a[i], then i target := a[i] (S_i, fee, change) := select_inputs_disjoint(U, target, feerate_floor, dust) R[i] := S_i U := U \ S_i remove reserved inputs (strict non-overlap)
Change arithmetic and size estimate
m inputs, n outputs
(n ∈ {1,2}); P2PKH sizes size_bytes(m, n) := 10 + 148×m + 34×n fee(m, n) := ceil( feerate_floor × size_bytes(m, n) )
sum_in := Σ value(input ∈ S_i) target := a[i]
Try no-change first
fee1 := fee(m=|S_i|, n=1) if sum_in = target + fee1: outputs := [ (addrB_i, target) ] # exact match, n = 1 else: fee2 := fee(m=|S_i|, n=2) change := sum_in − target − fee2 if change ≥ dust: outputs := [ (addrB_i, target), (addrA_i, change) ] # n = 2 elif 0 < change < dust: reseat_inputs() # add/select different inputs; recompute else: # change ≤ 0 reseat_inputs() # underfunded; add/select different inputs
Worked-through flow (illustrative only)
Step 0 — Preconditions • Alice (payer): identity K_A = (k_A, P_A); sender anchor A = a·G. • Bob (payee): identity K_B = (k_B, P_B); recipient anchor B = b·G. • All amounts are in the smallest settlement unit. This slice uses N = 7 notes for readability; amounts remain symbolic (a₀…a₆) and are illustrative only.
Step 1 — Policy exchange (Bob → Alice) Bob sends a canonical Policy JSON; Alice verifies signature and computes H_policy. Example (placeholders only):
{"pk_anchor":"<hex serP(B)>", "vmin":<v_min>, "vmax":<v_max>, "feerate_floor":, "expiry":"", "sig":""}
Alice verifies pk_anchor structure, bounds, feerate_floor, expiry, and sig(K_B).
Step 2 — Invoice issuance (Alice → Bob) Alice issues a canonical Invoice JSON referencing H_policy; Bob verifies and computes H_I:
{"invoice_number":"", "terms":"", "unit":"", "total":, "policy_hash":"", "expiry":"", "sig":""}
Both parties compute the per-invoice scope: • Z := ECDH(k_A, K_B) = ECDH(k_B, K_A) (32-byte x-coordinate). • H_I := SHA-256(canonical_json(invoice)). All subsequent derivations are functions of {Z, H_I}.
Step 3 — Bounded split (deterministic; exact sum) Seed S := SHA-256( Z ∥ H_I ∥ "split" ). Compute N_min := ⌈T ÷ v_max⌉, N_max := ⌊T ÷ v_min⌋; choose N ∈ [N_min, N_max] deterministically (interior-biased). For this slice, N = 7. Compute a vector a = (a₀, a₁, …, a₆) with v_min ≤ aᵢ ≤ v_max and Σ aᵢ = T using prefix clamping; then apply a seeded Fisher–Yates permutation with S_perm := H(S ∥ "permute"). Both parties obtain identical (N, a) without exchanging per-note amounts.
Step 4 — Reservation build (disjoint funding) Alice snapshots her UTXO pool U₀ and constructs a reservation table R with pairwise disjoint input sets Sᵢ. Deterministic ordering: fund larger aᵢ first; UTXOs iterated in fixed (value↑, txid↑, vout↑) order. For illustration: • S₀ = {u_3, u_9} • S₁ = {u_1} • S₂ = {u_5, u_7, u_12} • S₃ = {u_8} • S₄ = {u_2, u_10} • S₅ = {u_4} • S₆ = {u_6, u_11} with Sᵢ ∩ Sⱼ = ∅ for all i ≠ j. Exact members and values are determined by the bounded-knapsack policy (§6). Size/fee are computed at feerate_floor; candidates producing dust change are rejected or reseated.
Step 5 — Per-note addresses (recipient and change) For each index i = 0…6:
Recipient (sender view): • tᵢ := int( SHA-256( Z ∥ H_I ∥ "recv" ∥ LE32(i) ) ) mod n; if tᵢ = 0, bump ctr deterministically until non-zero. • P_B,i := B + tᵢ·G; Addr_B,i := Base58Check( 0x00 ∥ H160( serP(P_B,i) ) ).
Sender change (payer view): • sᵢ := int( SHA-256( Z ∥ H_I ∥ "snd" ∥ LE32(i) ) ) mod n; if sᵢ = 0, bump ctr deterministically until non-zero. • P_A,i := A + sᵢ·G; Addr_A,i := Base58Check( 0x00 ∥ H160( serP(P_A,i) ) ).
All Addr_B,i are unique per note; all Addr_A,i are unique per note; identity keys never appear on-chain.
Step 6 — Transaction construction (one per note) For each i:
Inputs: Sᵢ (reserved). Let m = |Sᵢ|, sum_in = Σ value(Sᵢ). Outputs: primary (Addr_B,i, aᵢ); optional change (Addr_A,i, changeᵢ).
Fee/change arithmetic (deterministic at floor): • size1 ≈ 10 + 148·m + 34; fee1 := ceil(floor × size1). If sum_in = aᵢ + fee1 → 1-output note (no change). • Else size2 ≈ 10 + 148·m + 68; fee2 := ceil(floor × size2). changeᵢ := sum_in − aᵢ − fee2; require changeᵢ = 0 or changeᵢ ≥ dust. If 0 < changeᵢ < dust or changeᵢ ≤ 0 → reseat inputs.
Build legacy P2PKH transaction Tᵢ with deterministic input order; sign every input under SIGHASH_ALL. Compute txidᵢ = dSHA256(serialisation). Record NoteMetaᵢ := {"i": i, "note_id": H(H_I ∥ LE32(i)), "invoice_hash": H_I, "addr": Addr_B,i, "amount": aᵢ, "txid": txidᵢ, …}.
Result: seven independent, fully valid transactions {T₀…T₆}, each paying aᵢ to Addr_B,i and (if present) change to Addr_A,i; no input overlaps; no change overlaps within the invoice.
Step 7 — Either-side broadcast and pacing Broadcast authority is “either”. Seed S_pace := H( Z ∥ H_I ∥ "pace" ); compute a paced schedule within policy bounds (min/max spacing or bursts). Either party may submit any subset in any order; duplicate submission of identical bytes is benign. Confirmation depth d from policy defines settlement for each note independently.
Step 8 — Receipts and commitment (Merkle root) For i = 0…6, construct leaves: • preimageᵢ := "leaf" ∥ LE32(i) ∥ txidᵢ (32 bytes) ∥ amountᵢ (8-byte LE) ∥ addr_payloadᵢ (0x00 ∥ 20-byte H160). • Lᵢ := SHA-256(preimageᵢ). Compute M := MerkleRoot([L₀, L₁, …, L₆]) with Bitcoin-style odd-leaf duplication. Persist a manifest:
{"invoice_hash":"", "merkle_root":"", "count":7, "entries":[ {"i":0,"txid":""}, {"i":1,"txid":""}, {"i":2,"txid":""}, {"i":3,"txid":""}, {"i":4,"txid":""}, {"i":5,"txid":""}, {"i":6,"txid":""} ]}
Selective proof example (index 3 only; placeholders):
{"invoice_hash":"", "merkle_root":"", "leaf":{"i":3, "txid":"", "amount":<a_3>, "addr_payload":"00<20-byte-h160-hex>"}, "path":[ {"pos":"L","hash":""}, {"pos":"R","hash":""}, {"pos":"R","hash":""} ]}
A verifier recomputes L₃ and folds the path to M; no information about i ≠ 3 is revealed.
Step 9 — Reissue before broadcast (fee update example) Suppose, prior to any broadcast, a higher effective fee is desired for i = 5. Procedure:
Release reservation S₅; select a new disjoint S₅′; recompute fee at the new rate (≥ floor) (preserve Addr_B,5, Addr_A,5).
Build and sign T₅′; compute txid₅′.
Update NoteMeta₅:
{"i":5, "note_id":"", "invoice_hash":"", "version":2, "supersedes":"", "txid":"", "status":"signed"}
Only the latest version is eligible for broadcast; older txid₅ is not propagated.
Step 10 — Settlement and closure As notes are announced (by either party) and reach ≥ d confirmations to their respective Addr_B,i, statuses become “confirmed”. Orphaned confirmations (reorg) trigger rebroadcast of the same raw bytes until reconfirmed. When all seven notes are confirmed (or when the invoice expires, cancelling any remaining queued notes), the invoice is closed. The audit trail consists of: the signed Policy and Invoice, H_I, the deterministic split a[·], the reservation table R, per-note NoteMeta with txidᵢ, the Merkle root M and manifest, and any reissue/cancel records. Every note in the slice has a disjoint input set Sᵢ, a unique recipient address Addr_B,i, and—where applicable—a unique sender change address Addr_A,i.
Testing and verification
17.1 Scope Verify correctness, determinism, and auditability for a complete invoice lifecycle. All tests operate on canonical UTF-8 JSON, secp256k1 over compressed keys, and the invoice scope {Z, Hᴵ}.
17.2 Required fixtures (deterministic) • Keys – Identity: Kₐ = (kₐ, Pₐ), Kᵦ = (kᵦ, Pᵦ). – Anchors: A = a·G, B = b·G (a ≠ kₐ, b ≠ kᵦ). • Policy (canonical JSON) with fields per §3.3 and valid ECDSA by kᵦ. • Invoice (canonical JSON) referencing H_policy, valid ECDSA by kₐ; compute Hᴵ := H(canonical_json(invoice)). • UTXO snapshots U₀ covering edge cases: – exact-match inputs; – “just-over” inputs that yield valid ≥ dust change; – coarse inputs forcing fan-out; – conflicting external spend (simulated) on a reserved outpoint on a reserved outpoint. • Parameters: v_min, v_max, fee_rate_floor, dust, expiry, d (confirm depth). • Golden PRNG seeds are implicit: derived from {Z, Hᴵ} per spec; no external RNG.
17.3 Property tests (must hold)
P1 — Sums and bounds (split). For hundreds of randomly chosen feasible triples (T, v_min, v_max): • Compute N_min := ⌈T/v_max⌉, N_max := ⌊T/v_min⌋; run the split (§5). • Assert: N_min ≤ N ≤ N_max; ∑ᵢ a[i] = T; ∀i: v_min ≤ a[i] ≤ v_max. • Re-run: identical a (before permutation) and identical permuted a.
P2 — Address agreement (recipient). For i = 0…N−1: • Sender computes Pᴮ,ᵢ, Addrᴮ,ᵢ from {Z, Hᴵ, B, i}. • Recipient computes kᴮ,ᵢ := (b + tᵢ) mod n; check kᴮ,ᵢ·G = Pᴮ,ᵢ. • Assert identical Addrᴮ,ᵢ at both ends.
P3 — Disjoint reservations. Build R from U₀ and a[·] (§6). • Assert Sᵢ ∩ Sⱼ = ∅ for all i ≠ j; • Assert every input used appears in exactly one Sᵢ; • Assert deterministic reproducibility: rebuild(R) equals prior R byte-for-byte.
P4 — Non-overlapping change. For all notes that produce change: • Derive Addrᴬ,ᵢ (§7); assert Addrᴬ,ᵢ ≠ Addrᴬ,ⱼ for i ≠ j; • Assert no change UTXO from note i is admitted to the funding pool while invoice open. • Assert transactions meet dust and fee-floor rules; otherwise reseat inputs.
P5 — Reissue invariants. For a pre-broadcast fee update on index i: • Build Tᵢ, then reissue Tᵢ′ at higher fee. • Assert: i unchanged; NoteIDᵢ unchanged; Addrᴮ,ᵢ unchanged; Addrᴬ,ᵢ unchanged; txidᵢ ≠ txidᵢ′; prior bytes flagged superseded and never broadcast.
P6 — Receipts. Construct leaves Lᵢ and root M (§10). • For random S ⊂ {0…N−1}, produce single-leaf proofs and verify to M. • If multi-proof implemented, verify shared-sibling structure; otherwise independent proofs suffice. • Manifest { (i, txidᵢ) } + M must match recomputation.
P7 — Rebuild from logs. Given only signed logs (policy, invoice, reservations, NoteMeta, receipt manifest): • Verify signatures, canonical JSON, and hash-chain “prev_hash”. • Recompute H_policy, Hᴵ, Z. • Recompute a[·], Addrᴮ,ᵢ, Addrᴬ,ᵢ; rebuild R from the logged U₀ snapshot and parameters; • Reconstruct or fetch raw Tᵢ, recompute txid_i. • Check lifecycle: supersedes chain, statuses, timers consistent with §9–§11. All asserts must pass without external oracles.
17.4 Negative / failure tests (must reject)
N1 — Infeasible invoice. Choose T, v_min, v_max with ⌈T/v_max⌉ > ⌊T/v_min⌋ → split must fail early.
N2 — Canonicalisation break. Permute JSON key order or add whitespace → signatures fail; H_policy/Hᴵ differ; reject.
N3 — Scope misuse. Attempt derivation without {Z, Hᴵ} or with wrong domain label (“recv” vs “snd”) → derived addresses mismatch; reject.
N4 — Zero-scalar forcing. Artificially search i such that tᵢ = 0 or sᵢ = 0; verify deterministic counter-bump produces non-zero and stable i mapping; log counter used.
N5 — Reservation overlap. Mutate R to place same outpoint in two Sᵢ → builder must detect and fail.
N6 — Dust/fee violations. Construct candidates with change ∈ (0, dust) or fee < fee_floor × size → must reseat or fail.
N7 — Conflicting external spend. Mark a reserved input as spent elsewhere → builder must abort and rebuild R (§11).
N8 — Expiry and stale notes. Advance clock beyond invoice/policy expiry → queued notes become “cancelled”; no new broadcasts permitted.
17.5 Rebuild procedure from logs (normative)
Inputs: append-only, signed, canonical JSON records per §13 (policy_record, invoice_record, reservations, NoteMeta*, receipt_manifest, event records).
Procedure:
Verify hash-chain: for each record R_k, check prev_hash = H(bytes(R_{k−1} \ sigtriplet)).
Verify signatures by the stated sig_key over canonical bytes (without sigtriplet).
Extract policy → compute H_policy; extract invoice → verify policy_hash and compute H_I.
Recompute Z from K_A, K_B.
17.6 Golden vectors and cross-impl determinism Publish at least two golden invoices (small and large N) with: policy, invoice, H_policy, H_I, Z (hex), a[·], R (outpoints), (Addr_B,i, Addr_A,i), txidᵢ, M, and selective proofs. Independent implementations must reproduce these byte-for-byte.
17.7 Fuzzing and coverage (recommended) • Fuzz (T, v_min, v_max) under feasibility; run P1–P6. • Fuzz U₀ distributions (heavy-tail, uniform, clustered). • Randomly trigger reissue, conflict, expiry. • Target ≥ 95 % branch coverage across split, reservation, fee/change, and receipt code paths.
17.8 CI gating (pass/fail) A build is releasable only if: P1–P7 pass; N1–N8 reject as specified; golden vectors match; rebuild-from-logs produces identical outputs including R, addresses, txids, and M.
"feerate_floor": integer — minimum fee-rate in units-per-byte (smallest unit per virtual byte), feerate_floor ≥ 1.
"expiry": string — ISO-8601 UTC, e.g., "2025-08-26T00:00:00Z".
"sig_key": string — hex(serP(Pᵦ)), Bob’s identity public key in compressed SEC1 hex.
"sig_alg": string — "secp256k1-sha256".
"sig": string — hex(ECDSAₖᵦ(SHA-256(canonical_json(policy_without_sig_fields)))).
"policy_hash": string — hex(H_policy) computed from the accepted policy in §3.3.
"expiry": string — ISO-8601 UTC, optional but recommended; if present, MUST be in the future.
"sig_key": string — hex(serP(Pₐ)), Alice’s identity public key in compressed SEC1 hex.
"sig_alg": string — "secp256k1-sha256".
"sig": string — hex(ECDSAₖₐ(SHA-256(canonical_json(invoice_without_sig_fields)))).
Both compute Z := ECDH(kₐ, Pᵦ) = ECDH(kᵦ, Pₐ). The tuple {Z, Hᴵ} is recorded as the sole cryptographic scope for this invoice. Any per-note recipient key, sender change key, amount split, label, reservation lock, or receipt root that does not include {Z, Hᴵ} in its preimage MUST be rejected.
Output (Pᴮ,ᵢ, addrᴮ,ᵢ) for index i.
Stage D — fewest-inputs minimal-overshoot Increase m from 2 to M_max (M_max default 6). For each m, run a greedy bounded-knapsack over U₀ \ Used (largest-first or meet-in-the-middle for m≤4) to find C with Σ value(C) ≥ target + fee_m₂ where fee_m₂ := feerate_floor × (10 + 148·m + 34·2). Among feasible C, minimise overshoot := Σ value(C) − (target + fee_m₂) subject to overshoot = 0 or overshoot ≥ δ_dust. Choose the C with smallest m then smallest overshoot. Set best := C if found.
Commit if best = ⊥: goto FailureForI m := |best| n := 1 if Σ value(best) = target + feerate_floor×(10 + 148·m + 34·1) else 2 fee := feerate_floor × (10 + 148·m + 34·n) sum_in := Σ value(best) if n = 1: change := 0 else: change := sum_in − target − fee if change < δ_dust: // attempt to repair by adding one more input once pick the smallest u′ ∈ U₀ \ Used \ best if u′ exists: recompute m, n=2, fee, change; if change ≥ δ_dust accept; else discard u′ and continue Stage D if no repair possible: goto FailureForI R[i] := best mark all u ∈ best as reserved(i); Used := Used ∪ best continue with next i
Fee and dust validation. Verify the size estimate and fee satisfy the fee-rate floor; verify no output < δ_dust (§7).
Signing scope. For each input j = 0…m−1, construct the legacy SIGHASH preimage for SIGHASH_ALL: – Start from the transaction template with all scriptSig empty. – Replace the scriptSig of input j with the exact previous locking script (the P2PKH scriptPubKey of the UTXO referenced by Sᵢ[j]). – Append the 4-byte SIGHASH type 0x00000001 (little-endian). – Double-hash with SHA-256 to obtain zⱼ. – Produce a deterministic ECDSA signature σⱼ over zⱼ using the private key that controls Sᵢ[j], with low-s normalisation; append one-byte hash type 0x01 to σⱼ. – Set scriptSigⱼ := <PUSH σⱼ> <PUSH serP(Pⱼ)>, where Pⱼ is the corresponding public key.
Final serialisation and txid. Serialise the fully signed transaction using Bitcoin legacy (non-SegWit) encoding. Define txidᵢ := SHA-256(SHA-256(serialised_bytes)), expressed as 32-byte hash (displayed big-endian; stored and relayed as little-endian in prevouts). The note transaction Tᵢ is now complete and valid in isolation.
Deterministic labelling. Compute NoteIDᵢ := H(Hᴵ ∥ LE32(i)). Associate NoteIDᵢ with Tᵢ in logs and manifests.
Update NoteMeta[i] with fields: "supersedes": "", "version": <prev_version + 1>, "txid": "", "status": "signed".
Update the old raw bytes as voided_offchain = true and "status": "reissued".
Persist an append-only audit record of the transition.
Amount and bounds leakage. Possible: When many notes are broadcast close in time, an observer may infer that outputs lie in [v_min, v_max] and approximate N by histogramming. Limit: Exact partition a[·] remains hidden off-chain; permutation decouples indices from sizes; paced broadcast blurs simultaneity.
Timing linkage. Possible: If all notes are broadcast at once from a single IP, a₁/a₂ can cluster them temporally and by origin peer. Mitigation below: either-side broadcast; paced or bursty schedules; diverse first-announcers.
Recompute split a[·] from {Z, H_I, v_min, v_max} (and permutation).
Rebuild R deterministically from logged U₀ snapshot and parameters; compare to logged reservations.
For each i: a. Derive Addr_B,i and Addr_A,i; compare to NoteMeta (if present). b. Reconstruct tx template from NoteMeta.inputs/outputs or fetch raw tx; recompute txid_i. c. Check lifecycle: supersedes chain, statuses, timers consistent with §9–§11.
Recompute leaves L_i and Merkle root M; match receipt_manifest.merkle_root.
Emit a verification report listing all assertions; fail on first discrepancy.
Ty Everett ([email protected])
Tone Engel ([email protected])
Brayden Langley ([email protected])
We define the BSV Blockchain's standard wallet-to-application interface. This interface defines a robust and secure communication protocol between BSV wallets and applications. This protocol, built on the MetaNet architectural principles, aims to standardize the interaction between wallets and decentralized applications in the BSV ecosystem. The interface is designed to be vendor-neutral, supporting a wide range of implementations, ensuring interoperability, and promoting openness across different wallet and app vendors.
Computing has long been subject to shortfalls in the areas of information centralization and architectural cross-compatibility. Users on the internet struggle with complex and insecure authentication systems which leave them vulnerable and leak their data. Websites rely on advertising to monetize their offerings, but ads warp the incentives of platforms and creators in ways that ultimately harm everyone involved. By defining a standard interface by which users can identify themselves, protect their data and engage in e-commerce with Bitcoin, this standard offers a solution to problems that have long plagued the existing model.
Standardization: The interface provides a consistent and standardized API that ensures that any application can integrate with any compliant wallet without needing custom adaptations.
Vendor-Neutrality: The interface abstracts the underlying wallet implementation, allowing applications to work seamlessly with wallets from different vendors.
Open Specification: The interface is openly documented, encouraging community adoption, and providing a bedrock foundation atop which anyone can build with confidence, knowing the interface will remain constant.
SECTION TL;DR: We use the BSV Blockchain. We use the secp256k1 elliptic curve. We use compressed, DER-formatted public keys. We use BKDS key derivation. We use for security levels, protocol IDs, key IDs, and counterparties (with protocols reserved for internal wallet use). We subscribe to the idea that "Outputs are tokens". We use output baskets for tracking tokens. For categorization and filtering purposes, we allow transactions to be given a set of labels, and outputs to be given a set of tags. We utilize the rules for SPV validation. We use the BEEF standard outlined in for representing transactions. For encryption and decryption, we use the methods described in . For creating digital signatures, we use the methods described in . For HMACs , we derive symmetric keys as in , but then use them for HMAC operations instead of AES-GCM encryption. For the digital certificate structure and field encryption scheme, we use . For internalizing payment outputs that increase the user's wallet balance, we employ the key derivation protocol described within . For revealing key linkages, we employ the two methods described within , and we protect this information as described in . We incorporate for defining flexible proof-type fields to support emerging zero-knowledge proof (ZKP) schemes in specific key linkage revelations. We reserve specific protocol identifiers as described in to ensure forward compatibility with future permissioned protocols. Similarly, we reserve basket identifiers as outlined in to accommodate evolving wallet-managed asset permission schemes. These foundational prerequisites allow us to fully define and specify the behavior and functionality of the digital wallet system, ensuring future extensibility and compatibility across the BSV ecosystem.
The Wallet Interface is built upon a set of essential foundational standards and protocols that define the underlying architecture, cryptographic operations, and key management systems required for a robust and coherent wallet-to-application interface within the BSV ecosystem. This section thoroughly explains these foundational requirements, incorporating relevant content from the preceding BRCs to provide a holistic understanding of the interface's structure.
At the heart of the Wallet Interface lies the : BSV Key Derivation Scheme. This scheme defines how keys are derived between two interacting parties. BKDS leverages the secp256k1 elliptic curve and enables participants to derive multiple unique public-private key pairs from a shared master key. All cryptographic operations, including key derivation and identity handling, adhere to the BKDS as defined in . This specification does not support legacy key derivation schemes like BIP32 due to the foundational nature of BKDS in ensuring compatibility, privacy, and security across the BSV ecosystem. Wallets implementing this interface must migrate to BKDS and cannot rely on older derivation schemes.
Identity Keys:
Each wallet has an everyday master private key and a corresponding master public key derived from the secp256k1 elliptic curve. The public key is known as the "identity key".
Additionally, there's a whole secondary "privileged mode" keyring for sensitive operations, allowing these privileged keys to be treated with higher security than the user's everyday keyring.
Key Derivation Process:
When deriving a key for a payment or exchange, the sender computes an elliptic curve Diffie-Hellman (ECDH) shared secret using their private key and the recipient's public key.
The shared secret is then used to generate a scalar through HMAC and convert it to a point on the elliptic curve.
This point is combined with the recipient's master public key to produce a child public key.
Key Privacy:
No information about the derived private key is exposed until actually used. This ensures that only the recipient can derive the private key corresponding to the public key provided by the sender.
Expanding Key Universes:
With BKDS, wallets can create a virtually unlimited number of unique key pairs through simple modifications of the derivation inputs (e.g., invoice numbers). This open-ended invoice numbering scheme is baked into the Wallet Interface, facilitating custom, flexible key derivation for transactions, signatures, and encryption.
plays a vital role in organizing how keys are used and accessed in the wallet. This standard introduces Security Levels, Protocol IDs, Key IDs, and Counterparties rules that govern key derivation, permissions, and data access within standard wallets.
Security Levels:
Security levels determine the required user permissions and access controls for a derived key:
Level 0: No restrictions, open access.
Level 1: Requires a level of user authorization that applies across all counterparties who use the same protocol.
Level 2: Restricts key usage to specific counterparties and requires individual permission for each.
Protocol IDs & Key IDs:
Protocol IDs further define the usage context for a derived key, such as "Document Signing" or "Encryption".
A Key ID is a unique identifier that differentiates specific keys under the same Protocol ID, allowing numerous derived keys under the same context but with different purposes.
Counterparties:
Counterparties are entities with whom keys are shared (sender, receiver, etc.). For instance, the interface allows single-party self-derivations where a sender and receiver are the same (useful for internal key operations) and anyone-derivations denoted by the private key 1 (for public operations).
Permission System:
Permission grants are managed transparently by the wallet and include expiration times, user notifications, and granularity based on the security level. This ensures that applications receive only the delineated keys they need and no more.
discusses Admin-reserved and Prohibited Key Derivation Protocols that are exclusive to administrative use by the wallet. These reserved protocols prevent application access to key derivation operations that are inherently internal and crucial to wallet security and integrity.
Internal Protocol Guidelines:
Any protocol ID that begins with admin is off-limits for external applications.
The Wallet Interface requires that this reserved protocol space is never exposed to or invoked by third-party applications, maintaining the separation between user-facing operations and internal wallet functionalities.
defines UTXOs as Tokens, asserting that Unspent Transaction Outputs (UTXOs) are the base units of tokenization within Bitcoin. This principle is integral to the interface, where UTXOs serve as tokens that can be managed within wallets using custom baskets, as discussed below.
Transaction Outputs as Tokens:
UTXOs represent discrete token units that can be transferred directly from sender to recipient. Their validity and legitimacy can be independently verified by anyone receiving the transaction.
The Wallet Interface relies on the UTXO model to enhance scalability, ensure decentralization, and promote trustlessness by enabling transparent and verifiable transactions without reliance on intermediaries.
Simplified Payment Verification (SPV):
The wallet interface integrates transaction validation protocols that follow 's SPV method. By validating UTXOs through matching txid and proof inclusion within the blockchain, wallets can quickly confirm that tokens are genuine without needing the entire chain. This eliminates the reliance on tracing tokens back to their genesis, addressing the Back To Genesis (BTG) problem. Instead, the Wallet Interface ensures validity and provenance by leveraging SPV proofs and state-based validation, allowing efficient token verification while maintaining scalability, reducing computational overhead, and preserving user privacy. As a result, tokens can be securely and rapidly transacted without the inefficiencies and complexities historically associated with the BTG issue.
To facilitate complex tracking and interaction with UTXOs, establishes Wallet Transaction Output Tracking (Output Baskets) within the interface.
Baskets:
Baskets are conceptual containers for grouping UTXOs, creating an easy-to-manage structure for tracking specific outputs used across applications or protocols.
A wallet must support basket management, including returning transaction outputs from a given basket, customizing outputs with relevant instructions, and spending or relinquishing them.
Permissions:
Like with key derivation, permissioning is enforced for applications executing operations involving baskets. The security model here follows from , ensuring consistency across the system.
Wallets must ensure that users have given consent before listing the outputs from a given basket, creating transactions that insert outputs into a basket, or internalizing transactions that facilitate output insertion into baskets.
Authenticated transaction verification is critical in wallet operations, and this builds, in part, on : Simplified Payment Verification and : Background Evaluation Extended Format (BEEF) Transactions.
BEEF Data Structure:
The BEEF format specified in is optimized for SPV and designed for efficient data transmission, focusing on economy of information while retaining verification integrity.
Wallets should utilize BEEF when constructing, communicating, and validating transactions. BEEF supports streaming validation, enabling the wallet to initiate transaction verification as soon as it starts receiving the data.
Verification Steps:
outlines the steps to be undertaken for verifying a transaction, including script validation, fee checking, sequence, and locktime examination. These checks ensure that transactions processed through the wallet interface are legitimate and contextually accurate.
SPV Empowerment:
The SPV model allows lightweight wallet clients to verify the chain, making them resistant to fraud while not requiring them to store or access all blockchain data.
Security is paramount in the Wallet Interface, and defines Encryption and Decryption operations encapsulated within the interface's cryptographic functionality.
AES-256-GCM Encryption:
Wallets employ AES-256-GCM for symmetric encryption, where the derived shared secret between the sender's and recipient's child keys becomes the basis for the encryption key.
Encryption & Decryption Process:
Wallets use their private keys and their counterparty's public keys (derived through BKDS) to encrypt data under a given protocol ID and key ID. Similarly, upon receipt, the recipient decrypts the data using their private key combined with the sender's public key.
Confidentiality Assurance:
Encryption through this interface ensures the confidentiality of transmitted data and can be applied for user-specific actions like private document exchange (shielded with encryption keys derived from BKDS).
The Wallet Interface supports digital signing functionalities defined in : Digital Signature Creation and Verification.
Digital Signature Process:
mandates use of ECDSA over secp256k1 keys.
Derived child keys (from BKDS) are used to sign data based on the security levels, protocol IDs, and key IDs defined in .
Private & Public Signatures:
Wallets can create private digital signatures intended for a specified receiver, by naming them as a counterparty.
It's also possible to create publicly verifiable signatures, simply by naming anyone as the counterparty.
Verification:
The signature is verifiable by the recipient using derived public keys. The recipient uses mechanics to derive the corresponding public key and validates the signature using this key over the provided data.
defines Identity Certificates, which incorporate selective revelation protocols that wallets must support.
Certificate Structure:
Identity certificates encapsulate a subject's identity information, certified by a trusted entity, with fields selectively encryptable to preserve user privacy.
Selective Revelation:
Wallets must facilitate keyring management for applications to reveal or withhold certificate fields selectively. The wallet keeps a copy of the master keyring, transmitting only the necessary revelation keys to authorized parties in an encrypted form.
Revocation Mechanism:
Identity certificate revocation is implemented via UTXO tracking. If the UTXO tied to a revocation outpoint is spent, everyone considers the certificate invalid—this adds an additional layer of trust and decentralized authority, depending on the constraints placed upon the UTXO.
The Wallet Interface incorporates support for : Simple Authenticated BSV P2PKH Payment Protocol, which standardizes how payments are derived, handled, and internalized within the wallet.
Payment Key Derivation:
BKDS Integration: Payments utilize keys derived through BKDS. They are based on a combination of transaction-specific data (such as a unique derivation prefix) and counterparty public keys. The derived keys are used to generate P2PKH scripts, ensuring that only the intended recipient can derive the corresponding private keys for spending.
Internalization Process:
Decoding and Deriving Keys: Upon receiving a payment message, the wallet decodes the message, derives the necessary private keys using the provided derivationPrefix and derivationSuffix, checks the scripts match, and processes the UTXOs.
Baskets and Custom Instructions: Non-P2PKH outputs with custom scripts can be directed into specific baskets, enabling organized tracking within the wallet. Custom instructions attached to UTXOs are also stored and can be used by applications for token history tracking or future spending.
SPV Verification: Regardless of whether a payment increments a user's wallet balance via
Flexible Payment Handling:
Multiple Outputs and Transactions: supports handling multiple outputs and even transactions within a single "payment." Each transaction output can be individually indexed with a unique derivation suffix, allowing the wallet to differentiate and manage multiple outputs efficiently, while all outputs within one given payment share a common derivation prefix.
The Wallet Interface incorporates methods to enhance transparency and auditability through : Revealing Key Linkages, while ensuring the protection of sensitive data during transit with : Protecting BRC-69 Key Linkage Information in Transit.
Key Linkage Revelations:
BKDS Based Key Linkage: outlines two methods for revealing key linkages from BKDS derived keys. The first method allows wallets to reveal a root ECDH shared secret between a user's identity key and another counterparty's key, enabling anyone to link all interactions between them. The second method reveals the specific key offset for individual derived child keys, enabling audit trails while preserving privacy in other contexts.
Protection of Linkage Information:
BRC-72 Integration: Protecting sensitive linkage data is paramount. specifies mechanisms for encrypting key linkage revelations when they are in transit. This encryption is done using the AES-256-GCM method, ensuring that only authorized verifiers (recipients) can decrypt and access the linkage data. This preserves privacy and security, even when key linkages must be revealed for audit or verification purposes.
Additionally, proofs help overcome the limitations described in . allows for future zero-knowledge proof types to be used in the context of specific key linkage revelation as technology develops.
The foundational BRCs integrated into this Wallet Interface ensure security, scalability, and flexibility across operations performed within the BSV ecosystem. These frameworks from key derivation, rigorous cryptographic principles, selective identity verification, tokenization, secure payments, and audit trails through linkage revelations all combine to form a robust and future-proof digital wallet architecture. The next section defines the high-level structure of the wallet interface.
The interface comprises numerous methods that cater to different functional areas related to wallet operations and application needs. The methods are grouped for easier understanding:
Creation: The createAction method creates a new Action, which is effectively a Bitcoin transaction augmented with descriptive metadata and optional categorization (labels). This method can either fully construct and sign a transaction or return a "signableTransaction" reference if some inputs must be signed or processed later. The method always requires at least one input or one output to produce a valid transaction; otherwise, it must return an error. The minimum requirements is that at least one input or one output must be specified. If only description is provided and no inputs and no outputs are given, the wallet must return an error, since a transaction cannot be constructed. If inputs are provided, the wallet requires inputBEEF to supply context and validation data for these inputs. If both inputBEEF and a fully prepared set of inputs are provided, inputBEEF should provide SPV and contextual information about these inputs. The two are complementary:
Key Retrieval: getPublicKey facilitates the retrieval of public keys, be they derived keys based on protocols or the user's main identity keys.
Key Linkage: Methods revealCounterpartyKeyLinkage and revealSpecificKeyLinkage disclose key relationships, as specified in , with additional support for future zero-knowledge proof schemes outlined in . These are essential for identity verification and the auditing of interactions between parties.
Encryption/Decryption: encrypt and decrypt methods implement secure encryption and decryption of data using derived keys and consistent protocol definitions, enabling private exchanges of information between counterparties.
HMAC Operations: createHmac and verifyHmac allow for the creation and verification of Hash-based Message Authentication Codes (HMAC) to ensure data integrity.
Certificate Acquisition: acquireCertificate allows the wallet to obtain identity certificates, either by directly saving them or through a standardized issuance protocol. Conversely, relinquishCertificate allows an old certificate to be removed.
Certificate Listing and Discovery: listCertificates, discoverByIdentityKey, and discoverByAttributes enable querying of identity certificates owned by the user or others based on identity keys or specific attributes.
Blockchain Height: getHeight retrieves the current height of the blockchain.
Merkle Root Retrieval: getMerkleRootForHeight retrieves the Merkle root at a specific block height.
Network and Version Information: getNetwork and getVersion
User Authentication: isAuthenticated checks the user's authentication status, ensuring they've set up their wallet before operations are attempted.
Authentication Wait: waitForAuthentication waits for the user to complete authentication and returns once the wallet has been fully set up.
To ensure consistency and prevent errors, the interface defines various data types and associated constraints. A few key examples include:
BooleanDefaultFalse: Defaults to false if not provided.
BooleanDefaultTrue: Defaults to true if not provided.
Byte: An integer between 0 and 255.
PositiveIntegerOrZero: A non-negative integer with an upper bound of 2^32 - 1.
PositiveIntegerMax10: A positive integer between 1 and 10.
ISOTimestampString: Represents an ISO 8601 format timestamp.
HexString: A string containing hexadecimal characters.
Base64String: A string in standard base64 encoded format.
status: Denotes the presence of a failure (always "error").
code: A short machine-readable string representing the specific error or fault.
description: A human-readable explanation of the error.
When errors occur, they must be communicated and thrown such that they preserve these structural elements. Specific instantiations or realizations of this interface, comprising APIs or transport mechanisms for messages between wallets and applications, must specify how these errors are communicated.
Wallets must apply all specified rules and logical validation procedures to all methods within the specification. For example, in the case of createAction, wallets should:
Verify that the required fields (description plus at least one input or one output) are present.
Check that if inputs are provided, the inputDescription field is also provided for each input.
Validate that if inputBEEF is provided, it corresponds logically to the inputs
If parameters are missing, malformed, or conflict with each other, the wallet should return an error with an appropriate error code and a human-readable description. For instance:
Missing required fields: Return an error code like ERR_MISSING_PARAMETER and a description stating which parameter is missing.
Invalid numeric ranges or string lengths: Return ERR_INVALID_PARAMETER_VALUE.
Conflicts between inputs and inputBEEF: Return ERR_CONFLICTING_PARAMETERS
These are examples; wallets and implementations are free to use their own naming conventions for error codes as long as they provide a clear description field.
Interoperability: Since the interface is vendor-neutral, developers should ensure they comply fully with the defined types, constraints, and method contracts, allowing their wallets and applications to interface smoothly with others.
Use of Privileged Mode: Methods related to the use of keys include options that allow an alternative "privileged access" mode to be used. When implemented, a secondary and more secure set of keys is used instead of the primary ones. This should only be invoked when necessary, and requires proper justification to be provided.
Request Originators and Permissions: The interface ensures that operations like key derivation, signing, encryption, certificate field revelation, and transaction creation are conducted with proper authorization by incorporating the request's originator. The wallet can then authenticate the originator and seek user permission if necessary.
Protocol IDs and basket names are used to control access to data and assets, respectively. In order to ensure that consistent rules apply across wallet implementations, and to ensure that appropriate reservations are made for future permissions architectures (see and ), we specify the rules that apply to these namespaces here:
Protocol IDs:
Must be at least 5 characters.
Generally must not exceed 400 characters (except for the specific linkage revelation protocol, which is allowed to be up to 430 characters, since it's the only protocol that encapsulates anotherr full protocol name within itself).
Must not contain multiple consecutive spaces (e.g., " ").
Must only contain lowercase letters, numbers, and spaces.
Key IDs:
Must be at least one byte in length.
Must not exceed 800 bytes in length.
Basket names:
Must be at least 5 characters.
Must be no more than 400 characters.
Must only contain lowercase letters, numbers, and spaces.
Must not end with basket (this is redundant).
This interface is specified in TypeScript as follows:
This section defines the Application Binary Interface (ABI) specification for the Wallet Interface, detailing the binary communication protocol used between applications and wallets over a Wallet Wire. It provides a comprehensive description of how method calls are structured, how data is serialized and deserialized, and how errors and origins are handled within the protocol. This specification ensures that all implementations conform to a standardized binary protocol, enabling interoperability across different platforms and vendors.
The Wallet ABI defines a binary protocol for communication between an application and the user's digital wallet. Each message transmitted over the Wallet Wire consists of a structured binary frame that includes the method call code, originator information, parameters, and return values. The protocol is designed to be efficient, minimizing the data transmitted while ensuring all necessary information is accurately conveyed.
Every message sent to the wallet follows this general structure:
Call Code (1 byte): An unsigned integer representing the method being invoked.
Originator Length (1 byte): The length of the originator domain name in bytes.
Originator (variable length): The UTF-8 encoded fully qualified domain name (FQDN) of the application originating the request.
Parameters (variable length): Method-specific parameters serialized according to the rules defined in this specification.
Responses from the wallet consist of:
Error Code (1 byte): A byte indicating success (0) or an error code (1-255).
Response Data (variable length): If Error Code is 0, this contains the serialized return values. If an error occurred, it contains the serialized error message and optional stack trace.
Each method in the Wallet Interface is assigned a unique call code. The call codes are defined as follows:
The originator is the fully qualified domain name (FQDN) of the application making the request. It is included in each message to allow the wallet to:
Identify the requesting application.
Apply appropriate permissions and access controls.
Record audit logs for security and compliance.
The originator is serialized as follows:
Originator Length (1 byte): The length of the originator string in bytes.
Originator (variable length): UTF-8 encoded bytes representing the originator's FQDN.
Errors are communicated using a structured format, ensuring consistent and detailed information about any issues that occur during method execution. This format enables precise error reporting, seamless debugging, and interoperability across implementations:
Error Code (1 byte):
0: Indicates success; the method executed without errors.
1-255: Indicates an error occurred; the specific code may correspond to predefined error types or be used for custom error categorization.
If an error occurs (Error Code is non-zero), the following fields are included in the response:
Error Message Length (VarInt): The length of the error message in bytes.
Error Message (variable length): UTF-8 encoded string describing the error.
Stack Trace Length (VarInt): The length of the stack trace in bytes (optional, -1 if absent).
The following data types are used in the binary protocol:
Byte: An unsigned 8-bit integer (0 to 255).
Int8: A signed 8-bit integer (-128 to 127).
UInt8: An unsigned 8-bit integer (0
Variable-length integers (VarInt) are used to efficiently represent integer values. The encoding follows the Bitcoin protocol's VarInt format:
For values from 0 to 0xFC (inclusive), a single byte represents the value.
For larger values, a marker byte indicates the length:
0xFD: Next two bytes are the value as a little-endian unsigned integer.
Each method call has specific parameter and return value formats. The following sections detail the serialization and deserialization process for each method.
createActionCall Code: 1
Parameters
Inputs Array:
For each input:
Outputs Array:
For each output:
For each tag:
UTF-8 String
Options Struct:
For each TXID:
Byte Array (32 bytes)
For each outpoint:
Byte Array (32 bytes + VarInt)
Return Values
Error Code (1 byte): 0 on success.
Response Data:
Depending on the outcome, the response may include:
For each outpoint:
Byte Array (32 bytes + VarInt)
For each sendWithResults entry:
Signable Transaction Struct:
signActionCall Code: 2
Parameters
For each spend:
inputIndex: VarInt
unlockingScript: VarInt Length + Byte Array
Options Struct:
Same as in the createAction method, but only applicable fields:
For each TXID:
Byte Array (32 bytes)
Error Code (1 byte): 0 on success.
Response Data:
abortActionCall Code: 3
Parameters
Return Values
Error Code (1 byte): 0 on success.
Response Data: None.
listActionsCall Code: 4
Parameters
Return Values
Error Code (1 byte): 0 on success.
Response Data:
Action Object:
For each action:
Status Codes:
1: 'completed'
2: 'unprocessed'
3: 'sending'
Input Object:
For each input:
Output Object:
For each output:
internalizeActionCall Code: 5
Parameters
Output Object:
For each output:
Payment Remittance Struct:
Insertion Remittance Struct:
Return Values
Error Code (1 byte): 0 on success.
Response Data: None.
listOutputsCall Code: 6
Parameters
Return Values
Error Code (1 byte): 0 on success.
Response Data:
Output Object:
For each output:
relinquishOutputCall Code: 7
Parameters
Return Values
Error Code (1 byte): 0 on success.
Response Data: None.
getPublicKeyCall Code: 8
Parameters
Protocol ID Struct:
Return Values
Error Code (1 byte): 0 on success.
Response Data:
revealCounterpartyKeyLinkageCall Code: 9
Parameters
Return Values
Error Code (1 byte): 0 on success, non-zero error code otherwise.
Response Data (on success):
revealSpecificKeyLinkageCall Code: 10
Parameters
Key-Related Parameters:
Return Values
Error Code (1 byte): 0 on success, non-zero error code otherwise.
Response Data (on success):
encryptCall Code: 11
Parameters
Return Values
Error Code (1 byte): 0 on success, non-zero error code otherwise.
Response Data (on success):
decryptCall Code: 12
Parameters
Return Values
Error Code (1 byte): 0 on success, non-zero error code otherwise.
Response Data (on success):
createHmacCall Code: 13
Parameters
Return Values
Error Code (1 byte): 0 on success, non-zero error code otherwise.
Response Data (on success):
verifyHmacCall Code: 14
Parameters
Return Values
Error Code (1 byte): 0 if the HMAC is valid, non-zero error code otherwise.
Response Data: Nothing extra on successful verification.
createSignatureCall Code: 15
Parameters
Return Values
Error Code (1 byte): 0 on success, non-zero error code otherwise.
Response Data (on success):
verifySignatureCall Code: 16
Parameters
Return Values
Error Code (1 byte): 0 if the signature is valid, non-zero error code otherwise.
Response Data: Nothing extra if successfully verified.
acquireCertificateCall Code: 17
Parameters
Depending on acquisitionProtocol, include additional fields:
If acquisitionProtocol is 'direct' (1):
If acquisitionProtocol is 'issuance' (2):
Return Values
Error Code (1 byte): 0 on success, non-zero error code otherwise.
Response Data (on success):
listCertificatesCall Code: 18
Parameters
Return Values
Error Code (1 byte): 0 on success, non-zero error code otherwise.
Response Data (on success):
Each certificate in the certificates array is serialized as:
VarInt Length + Byte Array representing the certificate binary data (the certificate binary format as described in proveCertificate method).
proveCertificateCall Code: 19
Parameters
Certificate Struct:
Return Values
Error Code (1 byte): 0 on success, non-zero error code otherwise.
Response Data (on success):
relinquishCertificateCall Code: 20
Parameters
Return Values
Error Code (1 byte): 0 on success, non-zero error code otherwise.
Response Data: None.
discoverByIdentityKeyCall Code: 21
Parameters
Return Values
Error Code (1 byte): 0 on success, non-zero error code otherwise.
Response Data (on success):
This method returns a list of certificates with additional certifier information and decrypted fields.
Each certificate includes:
Certificate Binary Data: Serialized certificate as in proveCertificate method.
Certifier Info Struct: Contains the following fields:
discoverByAttributesCall Code: 22
Parameters
Return Values
Same as discoverByIdentityKey method.
isAuthenticatedCall Code: 23
Parameters
None.
Return Values
Error Code (1 byte): 0 on success.
Response Data (on success):
waitForAuthenticationCall Code: 24
Parameters
None.
Return Values
Error Code (1 byte): 0 once the user is authenticated.
Response Data: None.
getHeightCall Code: 25
Parameters
None.
Return Values
Error Code (1 byte): 0 on success.
Response Data (on success):
getHeaderForHeightCall Code: 26
Parameters
Return Values
Error Code (1 byte): 0 on success.
Response Data (on success):
getNetworkCall Code: 27
Parameters
None.
Return Values
Error Code (1 byte): 0 on success.
Response Data (on success):
getVersionCall Code: 28
Parameters
None.
Return Values
Error Code (1 byte): 0 on success.
Response Data (on success):
Absence of Optional Fields: Optional fields are often indicated by a special value (e.g., -1 as a VarInt for length fields). Implementations should handle these values appropriately to determine the presence or absence of data.
Encoding of Strings: All strings are UTF-8 encoded and prefixed with their length as a VarInt. The -1 value indicates absence, notably different from 0 which indicates an empty string ("").
This ABI specification provides a complete and detailed description of the binary protocol used for communication between wallets and applications. By adhering to this specification, developers can ensure compatibility and interoperability across different implementations, enabling a robust and secure ecosystem for wallet interactions within the BSV blockchain ecosystem.
Implementers should carefully follow the serialization and deserialization rules outlined for each method to ensure correct functionality.
AbortAction: A method in the Wallet Interface that allows the cancellation of a transaction that is in progress and has not yet been finalized or sent to the network.
Action: An Action is a Bitcoin transaction plus metadata. It is a foundational concept that aligns with "Action Oriented Programming" (see: https://projectbabbage.com/docs/babbage-sdk/concepts/actions-aop).
Actions: In the context of this specification, an Action is a Bitcoin transaction (as defined within the BSV blockchain context) that is enriched with additional metadata such as descriptions, labels, and other optional data. This concept follows the "Action Oriented Programming" paradigm, where each Action represents a business-level event or operation captured as a blockchain transaction. Actions are created using the createAction method, managed and categorized using labels, and can incorporate outputs tagged for easier retrieval.
AES-256-GCM: Advanced Encryption Standard (AES) cipher with a 256-bit key size using Galois/Counter Mode (GCM); used for symmetric encryption and decryption operations within the Wallet Interface (as per ). By convention, 32-byte initialization vectors are prepended to the beginning of the ciphertext.
Application Binary Interface (ABI): A specification detailing the binary communication protocol between applications and wallets over a Wallet Wire, ensuring consistent method call structures, data serialization, and error handling as defined in the Wallet Interface.
Background Evaluation Extended Format (BEEF): A compact data format specified in for representing Bitcoin transactions optimized for Simplified Payment Verification (SPV) and efficient data transmission within the Wallet Interface.
Baskets: Conceptual containers within a wallet used to group and manage specific Unspent Transaction Outputs (UTXOs) as per , enabling organized tracking and handling across applications or protocols.
Bitcoin Request for Comment (BRC): An informal proposal or standard within the BSV ecosystem that outlines protocols, methods, or guidelines for functionalities such as transactions, key derivation, network architecture, and wallet interfaces.
BKDS (BSV Key Derivation Scheme): A key derivation scheme defined in that allows wallets to derive multiple unique public-private key pairs from a shared master key using the secp256k1 elliptic curve and a counterparty.
Blockchain Height: The number of blocks in the longest valid chain of the blockchain, representing the latest block's height within the BSV network.
BooleanDefaultFalse: A data type representing an optional boolean parameter that defaults to false if not provided in method arguments.
BooleanDefaultTrue: A data type representing an optional boolean parameter that defaults to true if not provided in method arguments.
BSV Blockchain: A network emphasizing stability, scalability, and adherence to Satoshi Nakamoto's original vision for Bitcoin as a token system, a micropayment system, and a peer-to-peer electronic cash system.
Call Code: An unsigned integer used in the ABI specification to represent the method being invoked over the Wallet Wire.
Certificate: In the context of , a digital identity document that encapsulates a subject's identity information, certified by a trusted entity, with support for selective field encryption.
Certificate Field Name: The name of a specific attribute or piece of data within an identity certificate, used for identification and selective revelation.
Certificate Revocation: The process by which an identity certificate is invalidated, often implemented via spending a specific UTXO tied to a revocation outpoint; used to indicate the certificate is no longer valid.
Compressed DER-formatted Public Key: A public key formatted according to the Distinguished Encoding Rules (DER), compressed to represent a point on the secp256k1 elliptic curve using 33 bytes (66 hexadecimal characters). The first byte denotes whether Y is odd, and the remaining 32 bytes comprise the X coordinate on the curve.
Counterparty: An entity (e.g., sender, receiver, verifier) involved in a transaction or key derivation process, identified by their public key; used in BKDS and defined in .
createAction: A method that constructs a new transaction based on provided inputs, outputs, and options. It can return a completed transaction, or a signableTransaction if the transaction is not finalized.
Custom Instructions: Data attached to UTXOs that provide contextual information or necessary unlocking context within application logic, represented as a string in the Wallet Interface.
Derivation Prefix/Suffix: Values used during the internalization of payment outputs, as described within to generate different key pairs for each output across the same payment.
DescriptionString5to50Characters: A string data type used for descriptions within the Wallet Interface, constrained to a length between 5 and 50 characters.
Digital Signature: A cryptographic value generated using a private key that verifies the authenticity and integrity of data, as outlined in with ECDSA.
ECDH (Elliptic Curve Diffie-Hellman): A key agreement protocol using elliptic curve cryptography that allows two parties to establish a shared secret over an insecure channel.
ECDSA (Elliptic Curve Digital Signature Algorithm): A cryptographic algorithm used for creating digital signatures using elliptic curve cryptography, specifically over the secp256k1 curve.
Encryption/Decryption: The processes of scrambling data to prevent unauthorized access (encryption) and restoring it to its original form (decryption), as specified in with AES-256-GCM and used within the Wallet Interface.
Entity Icon URL: A URL pointing to an icon representing a trusted entity or certifier in identity certificates, used for display and identification purposes.
Entity Name: The name of a trusted entity or certifier associated with an identity certificate, providing a human-readable identifier for the certifier.
Error Handling: The standardized method by which errors are communicated within the Wallet Interface, using structures containing status, code, description, and optional context.
ErrorCodeString10To40Characters: A data type representing a machine-readable error code string with a length between 10 and 40 characters, used in error responses.
ErrorDescriptionString20To200Characters: A data type representing a human-readable error description string with a length between 20 and 200 characters, providing details about an error.
Fully Qualified Domain Name (FQDN): The complete domain name of a specific computer or host on the internet, used as the OriginatorDomainNameString to identify the originator of a request in the Wallet Interface.
HMAC (Hash-based Message Authentication Code): A specific type of message authentication code involving a cryptographic hash function and a secret key, used for data integrity checks within the Wallet Interface.
Identity Key: The master public key of a wallet derived from the master private key using the secp256k1 elliptic curve; used to identify the wallet owner and derive child keys.
Input (Transaction Input): A reference in a transaction to a previous UTXO that is being spent, containing details such as the outpoint and unlocking script.
Key Derivation: The process of generating child keys from a master key using a specified algorithm like BKDS, providing unique keys for different purposes or interactions.
Key ID: A unique identifier differentiating specific keys under the same Protocol ID, allowing for multiple derived keys with different purposes within the same context as defined in .
Key Linkage: Information that reveals the relationship between derived keys, used for transparency, auditability, and verification, particularly as per and protected during transit by .
Labels (Transaction Labels): Strings used to categorize transactions within a wallet for organizational and filtering purposes.
Locking Script: A script associated with a transaction output that specifies the conditions under which the output can later be spent (also known as an output script or scriptPubKey, though scriptPubKey should no longer be used).
Lock Time: A parameter in a Bitcoin transaction that specifies the earliest time or block height at which the transaction can be included in the blockchain.
Master Private Key: The primary private key of a wallet from which other keys are derived, forming the root of the wallet's key structure.
Merkle Root: The root hash of a Merkle tree, summarizing all transactions in a block; used in SPV to verify transaction inclusion without downloading the entire block.
Originator Domain Name String: The fully qualified domain name (FQDN) of the application that originated the request, used to authenticate and authorize requests in the Wallet Interface.
Outpoint: A reference to a specific output in a previous transaction, identified by the transaction ID (txid) and output index (vout), used when spending UTXOs.
Output (Transaction Output): A component of a transaction that specifies the recipient of funds or data, including the amount (in satoshis) and a locking script.
Output Tags: Strings assigned to outputs within wallets to categorize or indicate attributes, facilitating searching, sorting, and filtering of UTXOs.
Payment Internalization: The process by which a wallet accepts and manages incoming transactions by parsing, tagging, and organizing outputs, as per .
Privileged Mode: A mode of operation within the wallet where sensitive or high-security operations are performed using a secondary, more secure set of keys; requires additional authorization.
Private Digital Signature: A signature intended for a specified receiver, created using a private key derived via BKDS, ensuring that only the intended recipient can verify it using the corresponding public key.
Proof-Type: A one-byte unsigned integer (0-255) defined in to specify the type of proof included in specific key linkage revelations. Proof Type 0 indicates no proof is provided, while Proof Types 1-255 are reserved for various zero-knowledge proof (ZKP) schemes, such as STARKs, Bulletproofs or SNARKs. This extensible format supports future advancements in ZKP technologies, enabling flexible and verifiable interactions while maintaining backward compatibility.
Protocol ID: An identifier used in to define the context or usage of a derived key, combining a security level and a protocol string (e.g., [1, "document signing"]).
PubKeyHex: A hexadecimal string representing a compressed DER-formatted secp256k1 public key, 66 characters long (33 bytes).
Satoshi: The smallest unit of Bitcoin, equal to 0.00000001; used as the unit for transaction amounts within the Wallet Interface.
Script Validation: The process of evaluating and verifying the correctness of scripts (locking and unlocking scripts) within transactions, ensuring they adhere to Bitcoin's scripting rules.
Security Level: In , a classification that determines the required permissions and access controls for using a derived key (Level 0: open access, Level 1: requires cross-counterparty authorization, Level 2: requires individual permission for each counterparty).
Shared Secret: In ECDH key exchange, a secret value derived by both parties using their own private key and the other party’s public key, used as the basis for deriving child keys or symmetric encryption keys.
Signable Transaction: A Bitcoin transaction that has been created but is not yet fully signed or finalized. This transaction is in a preparatory state where it requires one or more digital signatures before it can be considered complete and potentially broadcast to the Bitcoin network. Returned from createAction as a partially constructed transaction in BEEF (Background Evaluation Extended Format), which includes a reference number for later signing or aborting. This appears when an input does not have its unlocking script yet. The transaction is partially constructed and returned with a reference. The caller can use this reference with signAction to supply the necessary unlocking scripts later. This mechanism decouples the initial transaction construction from the final signing steps.
signAction: A method that signs a previously created transaction (a Signable Action) using the unlocking scripts provided, allowing it to be finalized and potentially broadcast.
Signature: Data that proves the authenticity and integrity of a message or transaction, created using a private key and verifiable with the corresponding public key.
Simplified Payment Verification (SPV): A method for verifying that a transaction is included in the blockchain without downloading the entire chain, by verifying the transaction's inclusion in a block (or its ancestors), checking the scripts that transfer coins, and validating that block via its Merkle root and proof-of-work.
TXID (Transaction ID): A unique identifier for a transaction, calculated as a double SHA-256 hash of the transaction data.
TagQueryMode: A parameter determining how tags are matched when listing outputs within a wallet; can be 'any' (matches if any tag matches) or 'all' (matches only if all queried tags are present).
Transaction: An instruction sent to the Bitcoin network to transfer Bitcoin from one or more inputs to one or more outputs; forms the fundamental operation within the blockchain. Also referred to as Actions when used within applications.
Unlocking Script: A script that satisfies the conditions specified by the locking script of an output, allowing the output to be spent (also known as an input script or scriptSig, though scriptSig should no longer be used).
Unspent Transaction Output (UTXO): An output from a prior transaction that has not yet been spent; represents an amount of Bitcoin controlled by a script that can be used as an input in a new transaction.
Vendor Neutrality: A design principle ensuring that an interface or protocol can be implemented by any vendor without proprietary constraints, promoting interoperability and standardization.
VersionString7To30Characters: A data type representing a version string of the wallet, constrained to be between 7 and 30 characters, in the format [vendor]-[major].[minor].[patch].
Wallet Interface: The standardized set of methods and protocols defined in for communication between wallets and applications in the BSV ecosystem, designed to be unified, vendor-neutral, and open.
Wallet Wire: The communication channel or protocol over which applications and wallets exchange messages using the defined ABI specification.
This specification provides a comprehensive and standardized framework for Wallet-to-Application interactions within the BSV ecosystem. By defining a clear and secure ABI, it fosters interoperability, scalability, and vendor-neutral implementation. The type constraints, structured error handling, and robust serialization methods enable developers to build reliable and secure applications across diverse environments.
Through its open and stable design, this specification ensures that wallets and applications can interoperate seamlessly, driving innovation while preserving stability. The reserved ranges for future call codes and adherence to backward compatibility principles also ensure long-term adaptability to new use cases and evolving technologies.
Developers and implementers are encouraged to align with this standard to enable a unified ecosystem, empowering applications and wallets to interact efficiently and securely, ultimately advancing the adoption and utility of the BSV blockchain.
At long last.
Comprehensive Functionality: The interface covers a broad range of functionalities, from transaction creation, signing, and broadcasting, to identity management, encryption, and digital signatures.
inputsinputBEEFinputDescriptionbasketlistOutputsSigning: signAction allows for signing and processing previously created transactions from the createAction method.
Aborting: abortAction facilitates the cancellation of transactions that have not yet been completed.
Internalization: internalizeAction enables wallets to accept and manage incoming transactions by parsing, tagging, and organizing outputs.
Listing: listActions and listOutputs allow querying transactions and outputs based on specific criteria like labels, baskets, and tags. Labels can be attached to transactions to facilitate discovery via listActions and tags can be attached to outputs to similarly facilitate discovery via listOutputs. They are purely organizational tools and cannot be used as triggers or conditional hooks for other wallet operations. They serve only for later searching and categorizing actions.
Relinquishment: relinquishOutput releases an output from a basket tracked by the wallet, even if it has yet to be spent.
Pre-Built Transactions: If you already have a fully constructed transaction in BEEF format and simply need to internalize it into the wallet, internalizeAction is the appropriate method. createAction is meant for constructing new transactions within the wallet, not for just adding an existing transaction.
Tags vs. Labels: Transaction-level labels categorize entire transactions (Actions) and are used with listActions. Output-level tags categorize individual outputs and are used with listOutputs. Both are organizational metadata fields and do not trigger special wallet logic or external processes. They exist solely for searching, filtering, and organizing data.
createSignature and verifySignature enable the creation and verification of digital signatures, both public and private, essential for validating the authenticity of transactions, documents, and data.Proving Identity Certificates: proveCertificate leverages selective revelation protocols defined in BRC-52 while integrating future-proof proof schemes from BRC-97. This provides enhanced flexibility and enables users to securely prove their identity or certified attributes to third parties when required.
PositiveIntegerDefault10Max10000: A positive integer that defaults to 10, and has an upper bound of 10000.SatoshiValue: Represents a value in Satoshis, ranging between 1 and 2.1 * 10^15.
context: (Optional) Additional contextual data relevant to the error---often binary or debug information.Ensure that labels, tags, and basket names comply with the defined format and size constraints.
If any constraints are not met, or if conflicting parameters are detected (e.g., invalid noSendChange usage), the wallet should return a structured error as previously defined.
Must not end with protocol (this is redundant).
Must not start with p (allows for future "specially permissioned" protocols). Specified in BRC-98.
Must not contain consecutive spaces.
Must not start with admin (allows the wallet to manage assets internal to its operations).
Must not be default (some wallets have historically used this for internal operations).
Must not start with p (allows for future "specially permissioned" baskets). Specified in BRC-99.
7
relinquishOutput
8
getPublicKey
9
revealCounterpartyKeyLinkage
10
revealSpecificKeyLinkage
11
encrypt
12
decrypt
13
createHmac
14
verifyHmac
15
createSignature
16
verifySignature
17
acquireCertificate
18
listCertificates
19
proveCertificate
20
relinquishCertificate
21
discoverByIdentityKey
22
discoverByAttributes
23
isAuthenticated
24
waitForAuthentication
25
getHeight
26
getHeaderForHeight
27
getNetwork
28
getVersion
255VarInt: A variable-length integer used for lengths and counts.
UTF-8 String: A string prefixed with its length (as a VarInt), followed by UTF-8 encoded bytes.
Byte Array: A sequence of bytes prefixed with its length (as a VarInt).
0xFE: Next four bytes are the value as a little-endian unsigned integer.
0xFF: Next eight bytes are the value as a little-endian unsigned integer.
lockTime
VarInt
Transaction lock time (optional). Use -1 to indicate absence.
version
VarInt
Transaction version (optional). Use -1 to indicate absence.
labels
VarInt Length + Array
An array of labels (optional). Use VarInt -1 if absent.
options
Int8 Flag + Options Struct
Options object (optional). If present, 1; else, 0.
sequenceNumber
VarInt
Sequence number (optional). Use -1 to indicate absence.
customInstructions
UTF-8 String
Custom instructions (optional). If absent, VarInt -1.
tags
VarInt Length + Array
Array of tags (optional). If absent, VarInt -1.
returnTXIDOnly
Int8
1 for true, 0 for false, -1 if not provided.
noSend
Int8
1 for true, 0 for false, -1 if not provided.
noSendChange
VarInt Length + Array
Array of outpoints (optional). If absent, VarInt -1.
sendWith
VarInt Length + Array
Array of TXIDs (optional). If absent, VarInt -1.
randomizeOutputs
Int8
1 for true, 0 for false, -1 if not provided.
signableTransaction
Int8 Flag + Struct
If present, 1 followed by signable transaction data. Else, 0.
txid: Byte Array (32 bytes)status: Int8 (1 for 'unproven', 2 for 'sending', 3 for 'failed')
sequenceNumber: VarInt (optional). Use -1 to indicate absence.
includeInputSourceLockingScripts
Int8
Same as above.
includeInputUnlockingScripts
Int8
Same as above.
includeOutputs
Int8
Same as above.
includeOutputLockingScripts
Int8
Same as above.
limit
VarInt
Maximum number of actions to return. Use -1 if not provided.
offset
VarInt
Number of actions to skip. Use -1 if not provided.
seekPermission
Int8
1 for true (default), 0 for false, -1 if not provided.
description
UTF-8 String
Description of the action.
labels
VarInt Length + Array
Array of labels (if includeLabels is true, zero-length otherwise).
version
VarInt
Transaction version.
lockTime
VarInt
Transaction lock time.
inputs
VarInt Length + Array
Array of input objects (if includeInputs is true, zero-length otherwise).
outputs
VarInt Length + Array
Array of output objects (if includeOutputs is true, zero-length otherwise).
4: 'unproven'
5: 'unsigned'
6: 'nosend'
7: 'nonfinal'
inputDescription
UTF-8 String
Description of the input.
sequenceNumber
VarInt
Sequence number.
outputDescription
UTF-8 String
Description of the output.
basket
UTF-8 String
Basket name (if present, else -1).
tags
VarInt Length + Array
Array of tags (if present, else -1).
customInstructions
UTF-8 String
Custom instructions (if present, else -1).
seekPermission
Int8
1 for true (default), 0 for false, -1 if not provided.
includeCustomInstructions
Int8
1 for true, 0 for false, -1 if not provided.
includeTags
Int8
Same as above.
includeLabels
Int8
Same as above.
limit
VarInt
Maximum number of outputs to return. Use -1 if not provided.
offset
VarInt
Number of outputs to skip. Use -1 if not provided.
seekPermission
Int8
1 for true (default), 0 for false, -1 if not provided.
spendable
Int8
Always 1 (indicates the output is spendable).
customInstructions
UTF-8 String
Custom instructions (if included).
tags
VarInt Length + Array
Array of tags (if included).
labels
VarInt Length + Array
Array of labels (if included).
privileged
Int8
1 for true, 0 for false, -1 if not provided.
privilegedReason
Int8 Length + UTF-8 String
Reason for privileged access (optional).
forSelf
Int8
1 for true, 0 for false, -1 if not provided.
seekPermission
Int8
1 for true (default), 0 for false, -1 if not provided.
encryptedLinkage
VarInt Length + Byte Array
The encrypted linkage data.
encryptedLinkageProof
VarInt Length + Byte Array
The encrypted linkage proof data.
privilegedReason
UTF-8 String
The privileged reason string (Optional).
keyID
UTF-8 String
The key ID used for key derivation.
encryptedLinkage
VarInt Length + Byte Array
The encrypted linkage data.
encryptedLinkageProof
VarInt Length + Byte Array
The encrypted linkage proof data.
proofType
UInt8 Number
The type of proof generated
seekPermission
Int8
1 for true (default), 0 for false, -1 if not provided.
data
VarInt Length + Byte Array
The data over which the signature was computed (if dataTypeFlag is 1).
hashToDirectlyVerify
Byte Array (32 bytes)
The hash over which the signature was computed (if dataTypeFlag is 2).
seekPermission
Int8
1 for true (default), 0 for false, -1 if not provided.
privilegedReason
Int8 Length + UTF-8 String
The reason for the privileged request (Optional).
acquisitionProtocol
UInt8
1 for 'direct', 2 for 'issuance'.
keyringForSubject
VarInt number of entries + Map
Map of fieldName to keyring values.
privileged
Int8
1 for true, 0 for false, -1 if not provided.
privilegedReason
Int8 Length + UTF-8 String
The privileged reason string (Optional)
privilegedReason
Int8 Length + UTF-8 String
The privileged reason (Optional).
revocationOutpoint
Byte Array
Revocation outpoint (TXID + output index).
signature
VarInt Length + Byte Array
Certificate signature.
fields
VarInt Length + Map
Map of fieldName to encrypted fieldValue.
Certifier's name.
iconUrl
UTF-8 String
Certifier's icon URL.
description
UTF-8 String
Certifier's description.
trust
UInt8
User trust level of this certifier (1-10).
Publicly Revealed Keyring: Map of fieldName to keyring values.
Decrypted Fields: Map of fieldName to decrypted fieldValue strings (UTF-8).
Error Codes: While only 0 (success) and 1 (generic error) are defined here, implementations may use additional error codes for error classification as needed.
Serialization of Nested Structures: Arrays and maps are prefixed with their lengths as VarInts. Nested fields must follow the same serialization rules as their top-level counterparts.
1
createAction
2
signAction
3
abortAction
4
listActions
5
internalizeAction
6
listOutputs
description
UTF-8 String
A human-readable description of the action represented by this transaction.
inputBEEF
VarInt Length + Byte Array
BEEF data associated with inputs (optional). Use VarInt -1 if absent.
inputs
VarInt Length + Array
An array of input objects (optional). Use VarInt -1 if absent.
outputs
VarInt Length + Array
An array of output objects (optional). Use VarInt -1 if absent.
outpoint
Byte Array (32 bytes + VarInt)
32-byte TXID followed by VarInt output index.
unlockingScript
VarInt Length + Byte Array
Unlocking script (optional). If present, provide length and data. If absent, use VarInt -1 and then provide unlockingScriptLength (VarInt).
unlockingScriptLength
VarInt
Length of the unlocking script if unlockingScript is absent, VarInt -1 otherwise.
inputDescription
UTF-8 String
Description of this input.
lockingScript
VarInt Length + Byte Array
Locking script.
satoshis
VarInt
Amount in satoshis.
outputDescription
UTF-8 String
Description of this output.
basket
UTF-8 String
Basket name (optional). If absent, VarInt -1.
signAndProcess
Int8
1 for true, 0 for false, -1 if not provided.
acceptDelayedBroadcast
Int8
1 for true, 0 for false, -1 if not provided.
trustSelf
Int8
1 if 'known', -1 if not provided.
knownTxids
VarInt Length + Array
Array of TXIDs (optional). If absent, VarInt -1.
txid
Int8 Flag + Byte Array (32 bytes)
If present, 1 followed by 32-byte TXID. Else, 0.
tx
Int8 Flag + VarInt Length + Byte Array
If present, 1 followed by transaction data (AtomicBEEF). Else, 0.
noSendChange
VarInt Length + Array
Array of outpoints (optional). If absent, VarInt -1.
sendWithResults
VarInt Length + Array
Array of structures containing txid and status. If absent, VarInt -1.
tx
VarInt Length + Byte Array
Transaction data in AtomicBEEF format.
reference
VarInt Length + Byte Array
Reference identifier as a Base64-encoded string.
spends
VarInt Count + Map
Map of input indexes to spend information.
reference
VarInt Length + Byte Array
Reference number as a Base64-encoded string.
options
Int8 Flag + Options Struct
Options object (optional). If present, 1; else, 0.
acceptDelayedBroadcast
Int8
1 for true, 0 for false, -1 if not provided.
returnTXIDOnly
Int8
1 for true, 0 for false, -1 if not provided.
noSend
Int8
1 for true, 0 for false, -1 if not provided.
sendWith
VarInt Length + Array
Array of TXIDs (optional). If absent, VarInt -1.
txid
Int8 Flag + Byte Array (32 bytes)
If present, 1 followed by 32-byte TXID. Else, 0.
tx
Int8 Flag + VarInt Length + Byte Array
If present, 1 followed by transaction data (AtomicBEEF). Else, 0.
noSendChange
VarInt Length + Array
Array of outpoints (optional). If absent, VarInt -1.
sendWithResults
VarInt Length + Array
Array of structures containing txid and status. If absent, VarInt -1.
reference
VarInt Length + Byte Array
Reference identifier (Base64-encoded).
labels
VarInt number + Array
Array of labels to filter actions.
labelQueryMode
Int8
1 for 'any', 2 for 'all', -1 if not provided.
includeLabels
Int8
1 for true, 0 for false, -1 if not provided.
includeInputs
Int8
Same as above.
totalActions
VarInt
Total number of actions matching the query.
actions
Array
Serialized array of action objects (details follow).
txid
Byte Array (32 bytes)
Transaction ID.
satoshis
VarInt
Amount in satoshis.
status
Int8
Status code (see below).
isOutgoing
Int8
1 for true, 0 for false.
sourceOutpoint
Byte Array
Outpoint (TXID + output index).
sourceSatoshis
VarInt
Amount in satoshis of the source output.
sourceLockingScript
VarInt Length + Byte Array
Locking script of the source output (if included).
unlockingScript
VarInt Length + Byte Array
Unlocking script (if included).
outputIndex
VarInt
Index of the output within the transaction.
satoshis
VarInt
Amount in satoshis.
lockingScript
VarInt Length + Byte Array
Locking script (if included).
spendable
Int8
1 for true, 0 for false.
tx
VarInt Length + Byte Array
BEEF-formatted transaction.
outputs
VarInt Length + Array
Array of output objects to internalize.
labels
VarInt Length + Array
Array of labels (optional). If absent, -1.
description
UTF-8 String
Description of the action.
outputIndex
VarInt
Index of the output within the transaction.
protocol
Int8
1 for 'wallet payment', 2 for 'basket insertion'.
paymentRemittance
Struct
Remittance data for payments (if protocol is 1).
insertionRemittance
Struct
Remittance data for insertions (if protocol is 2).
senderIdentityKey
Byte Array (33 bytes)
Sender's compressed public key.
derivationPrefix
VarInt Length + Byte Array
Base64-encoded derivation prefix.
derivationSuffix
VarInt Length + Byte Array
Base64-encoded derivation suffix.
basket
UTF-8 String
Basket name.
customInstructions
UTF-8 String
Custom instructions (optional). If absent, -1.
tags
VarInt Length + Array
Array of tags. If absent, length 0.
basket
UTF-8 String
Basket name.
tags
VarInt Length + Array
Array of tags to filter by (optional). If absent, length 0.
tagQueryMode
Int8
1 for 'all', 2 for 'any', -1 if not provided.
include
Int8
1 for 'locking scripts', 2 for 'entire transactions', -1 if not provided.
totalOutputs
VarInt
Total number of outputs matching the query.
outputs
Array
Serialized array of output objects (details follow).
outpoint
Byte Array
Outpoint (TXID + output index).
satoshis
VarInt
Amount in satoshis.
lockingScript
VarInt Length + Byte Array
Locking script (if included).
tx
VarInt Length + Byte Array
Transaction data (if included).
basket
UTF-8 String
Basket name.
output
Byte Array
Outpoint (TXID + output index) to relinquish.
identityKey
UInt8
1 to retrieve the identity key, 0 otherwise.
protocolID
Struct
Protocol ID (if identityKey is 0).
keyID
UTF-8 String
Key ID (if identityKey is 0).
counterparty
Byte Array or UInt8
Counterparty public key, or 11 for 'self', 12 for 'anyone'.
securityLevel
UInt8
Security level (0, 1, or 2).
protocolString
UTF-8 String
Protocol identifier string.
publicKey
Byte Array (33 bytes)
Compressed DER-formatted public key.
privileged
Int8
1 for true, 0 for false, -1 if not provided.
privilegedReason
UTF-8 String
The privileged reason string (Optional).
counterparty
Byte Array (33 bytes)
The counterparty's compressed secp256k1 public key.
verifier
Byte Array (33 bytes)
The verifier's compressed secp256k1 public key.
prover
Byte Array (33 bytes)
The prover's (user's) compressed public key.
verifier
Byte Array (33 bytes)
The verifier's compressed public key.
counterparty
Byte Array (33 bytes)
The counterparty's compressed public key.
revelationTime
UTF-8 String
ISO 8601 timestamp string indicating the time of revelation.
Key-Related Parameters
See Key-Related Parameters as defined in call code 10.
verifier
Byte Array (33 bytes)
The verifier's compressed public key.
protocolID
Protocol ID Struct
Security level and protocol string (see Protocol ID Struct from call code 8).
keyID
UTF-8 String
Key ID used for key derivation.
counterparty
Byte Array (33 bytes) or UInt8
Counterparty's compressed public key, 11 for 'self', 12 for 'anyone', or 0 if not provided.
privileged
Int8
1 for true, 0 for false, -1 if not provided.
prover
Byte Array (33 bytes)
The prover's compressed public key.
verifier
Byte Array (33 bytes)
The verifier's compressed public key.
counterparty
Byte Array (33 bytes)
The counterparty's compressed public key.
protocolID
Protocol ID Struct
The security level and protocol string used for key derivation, first described in call code 8).
Key-Related Parameters
See Key-Related Parameters as defined in call code 10.
plaintext
VarInt Length + Byte Array
The plaintext data to be encrypted.
seekPermission
Int8
1 for true (default), 0 for false, -1 if not provided.
ciphertext
Byte Array
The encrypted data (AES-256-GCM).
Key-Related Parameters
See Key-Related Parameters as defined in call code 10.
ciphertext
VarInt Length + Byte Array
The ciphertext data to be decrypted.
seekPermission
Int8
1 for true (default), 0 for false, -1 if not provided.
plaintext
Byte Array
The decrypted data.
Key-Related Parameters
See Key-Related Parameters as defined in call code 10.
data
VarInt Length + Byte Array
The data over which to compute the HMAC.
seekPermission
Int8
1 for true (default), 0 for false, -1 if not provided.
hmac
Byte Array
The computed HMAC value.
Key-Related Parameters
See Key-Related Parameters as defined in call code 10.
hmac
Byte Array
The HMAC value to verify.
data
VarInt Length + Byte Array
The data over which the HMAC was computed.
seekPermission
Int8
1 for true (default), 0 for false, -1 if not provided.
Key-Related Parameters
See Key-Related Parameters as defined in call code 10.
dataTypeFlag
UInt8
1 if signing data, 2 if signing hashToDirectlySign.
data
VarInt Length + Byte Array
The data to be signed (if dataTypeFlag is 1).
hashToDirectlySign
Byte Array (32 bytes)
The hash to directly sign (if dataTypeFlag is 2).
signature
Byte Array
The DER-encoded ECDSA signature.
Key-Related Parameters
See Key-Related Parameters as defined in call code 10.
forSelf
Int8
1 for verifying own signature, 0 otherwise, -1 if not provided.
signature
VarInt Length + Byte Array
The DER-encoded ECDSA signature to verify.
dataTypeFlag
UInt8
1 if verifying over data, 2 if over hashToDirectlyVerify.
type
Byte Array (Base64 encoded)
The certificate type identifier.
certifier
Byte Array (33 bytes)
The certifier's compressed public key.
fields
VarInt number + Map
Map of fieldName to fieldValue (both UTF-8 strings).
privileged
Int8
1 for true, 0 for false, -1 if not provided.
serialNumber
Byte Array (Base64 encoded)
The certificate serial number.
revocationOutpoint
Byte Array (32 bytes) + VarInt
The revocation outpoint (TXID + output index).
signature
VarInt Length + Byte Array
The certifier's signature over the certificate data.
keyringRevealer
Byte Array (33 bytes) or UInt8
The revealer's compressed public key, or 11 for 'certifier'.
certifierUrl
UTF-8 String
The certifier's URL for issuance.
certificate
Byte Array
The serialized certificate binary data (format described in call code 19).
certifiers
VarInt Length + Array
Array of certifier public keys (each 33 bytes).
types
VarInt Length + Array
Array of certificate types (where each entry is a byte array of 32 bytes).
limit
VarInt
Maximum number of certificates to return, -1 if not provided.
offset
VarInt
Number of certificates to skip, -1 if not provided.
totalCertificates
VarInt
Total number of certificates matching the criteria.
certificates
Array
Array of certificate binary data (see below).
certificate
Certificate Struct
The certificate data (see Certificate Struct below).
fieldsToReveal
VarInt Length + Array
Array of fieldName strings (UTF-8) to reveal.
verifier
Byte Array (33 bytes)
The verifier's compressed public key.
privileged
Int8
1 for true, 0 for false, -1 if not provided.
type
Byte Array (Base64 encoded)
Certificate type identifier.
subject
Byte Array (33 bytes)
Subject's compressed public key.
serialNumber
Byte Array (Base64 encoded)
Certificate serial number.
certifier
Byte Array (33 bytes)
Certifier's compressed public key.
keyringForVerifier
VarInt number of fields + Map
Map of fieldName to keyring values.
type
Byte Array (Base64 encoded)
Certificate type identifier.
serialNumber
Byte Array (Base64 encoded)
Certificate serial number.
certifier
Byte Array (33 bytes)
Certifier's compressed public key.
identityKey
Byte Array (33 bytes)
The identity key to search for certificates.
limit
VarInt
Maximum number of certificates to return, -1 if not provided.
offset
VarInt
Number of certificates to skip, -1 if not provided.
seekPermission
Int8
1 for true (default), 0 for false, -1 if not provided.
totalCertificates
VarInt
Total certificates matching the identity key.
certificates
Array
Array of extended certificate structs (see below).
name
attributes
VarInt Length + Map
Map of fieldName to fieldValue strings (UTF-8).
limit
VarInt
Maximum number of certificates to return, -1 if not provided.
offset
VarInt
Number of certificates to skip, -1 if not provided.
seekPermission
Int8
1 for true (default), 0 for false, -1 if not provided.
authenticated
UInt8
1 if authenticated, 0 otherwise.
height
VarInt
Current block height.
height
VarInt
The block height for which to retrieve the header.
header
Byte Array (80 bytes)
The serialized block header (80 bytes).
network
UInt8
0 for 'mainnet', 1 for 'testnet'.
version
UTF-8 String
The wallet's version string (e.g., vendor-1.0.0).
UTF-8 String
/**
* @typedef {boolean} BooleanDefaultFalse
* Represents an optional boolean parameter, which defaults to `false` if not provided.
* @remarks
* Default values are not enforced at the type level. Ensure that implementations explicitly assign the default value.
*/
export type BooleanDefaultFalse = boolean
/**
* @typedef {boolean} BooleanDefaultTrue
* Represents an optional boolean parameter, which defaults to `true` if not provided.
* @remarks
* Default values are not enforced at the type level. Ensure that implementations explicitly assign the default value.
*/
export type BooleanDefaultTrue = boolean
/**
* @typedef {number} Byte
* Represents an integer from 0 to 255 (inclusive).
* @minimum 0
* @maximum 255
*/
export type Byte = number
/**
* @typedef {number} PositiveIntegerOrZero
* A positive integer, includes zero and has an upper bound of `2^32 - 1`.
* @minimum 0
* @maximum 4294967295
* @remarks
* TypeScript cannot enforce numeric ranges. Validate at runtime if the value is within the specified range.
*/
export type PositiveIntegerOrZero = number
/**
* @typedef {number} PositiveInteger
* A positive integer that excludes zero, and has an upper bound of `2^32 - 1`.
* @minimum 1
* @maximum 4294967295
* @remarks
* TypeScript cannot enforce numeric ranges. Validate at runtime if the value is within the specified range.
*/
export type PositiveInteger = number
/**
* @typedef {number} PositiveIntegerMax10
* A positive integer that excludes zero, and has an upper bound of 10.
* @minimum 1
* @maximum 10
* @remarks
* TypeScript cannot enforce numeric ranges. Validate at runtime if the value is within the specified range.
*/
export type PositiveIntegerMax10 = number
/**
* @typedef {number} PositiveIntegerDefault10Max10000
* A positive integer that defaults to 10, and has an upper bound of 10000.
* @minimum 1
* @default 10
* @maximum 10000
* @remarks
* Default values are not enforced at the type level. Validate at runtime if the value is within the specified range and assign the default if omitted.
*/
export type PositiveIntegerDefault10Max10000 = number
/**
* @typedef {number} SatoshiValue
* Represents a value in Satoshis, constrained by the max supply of Bitcoin (2.1 * 10^15 Satoshis).
* @minimum 1
* @maximum 2100000000000000
* @remarks
* TypeScript cannot enforce numeric ranges. Validate at runtime if the value is within the specified range.
*/
export type SatoshiValue = number
/**
* @typedef {string} ISOTimestampString
* Represents an ISO timestamp string.
* @remarks
* Ensure runtime validation to confirm the string adheres to the ISO 8601 standard.
*/
export type ISOTimestampString = string
/**
* @typedef {string} HexString
* A string containing only hexadecimal characters (0-9, a-f).
* @remarks
* TypeScript does not enforce format or case. Validate at runtime to ensure the string is properly formatted.
*/
export type HexString = string
/**
* @typedef {HexString} TXIDHexString
* Represents a transaction ID, enforced to be exactly 64 characters in length and in hexadecimal format.
* @length 64
* @remarks
* TypeScript cannot enforce string length. Validate at runtime for length and format compliance.
*/
export type TXIDHexString = HexString
/**
* @typedef {string} OutpointString
* Represents a transaction ID and output index pair. The TXID is given as a hex string followed by a period "." and then the output index is given as a decimal integer.
* @remarks
* Validate at runtime to ensure the correct format: `<TXID>.<index>`.
*/
export type OutpointString = string
/**
* @typedef {HexString} PubKeyHex
* Represents a compressed DER secp256k1 public key, exactly 66 hex characters (33 bytes) in length.
* @length 66
* @remarks
* TypeScript does not enforce length or format. Validate at runtime to ensure the string is 66 characters long and valid hexadecimal.
*/
export type PubKeyHex = HexString
/**
* @typedef {string} Base64String
* A standard base64 encoded string.
* @remarks
* Validate at runtime to ensure the string adheres to the Base64 format.
*/
export type Base64String = string
/**
* @typedef {string} OriginatorDomainNameString
* Represents the fully qualified domain name (FQDN) of the application that originates the request.
* @remarks
* Validate at runtime to ensure the string conforms to FQDN formatting rules.
*/
export type OriginatorDomainNameString = string
/**
* @typedef {string & { minLength: 5, maxLength: 50 }} DescriptionString5to50Characters
* A string used for descriptions, with a length between 5 and 50 characters.
* @remarks
* TypeScript cannot enforce length constraints. Validate at runtime for length compliance.
*/
export type DescriptionString5to50Characters = string
/**
* @typedef {string & { maxLength: 300 }} BasketStringUnder300Characters
* A string for naming baskets, with a maximum length of 300 characters.
* @remarks
* TypeScript cannot enforce length constraints. Validate at runtime for length compliance.
*/
export type BasketStringUnder300Characters = string
/**
* @typedef {string & { maxLength: 300 }} OutputTagStringUnder300Characters
* A string for tagging outputs, with a maximum length of 300 characters.
* @remarks
* TypeScript cannot enforce length constraints. Validate at runtime for length compliance.
*/
export type OutputTagStringUnder300Characters = string
/**
* @typedef {string & { maxLength: 300 }} LabelStringUnder300Characters
* A string for labeling transactions, with a maximum length of 300 characters.
* @remarks
* TypeScript cannot enforce length constraints. Validate at runtime for length compliance.
*/
export type LabelStringUnder300Characters = string
/**
* @typedef {Byte[]} BEEF
* An array of integers, each ranging from 0 to 255, indicating transaction data in BEEF(BRC-62) format.
* @remarks
* Validate at runtime to ensure each element is within the specified range.
*/
export type BEEF = Byte[]
/**
* @typedef {Byte[]} AtomicBEEF
* An array of integers, each ranging from 0 to 255, indicating transaction data in Atomic BEEF(BRC-95) format.
* @remarks
* Validate at runtime to ensure each element is within the specified range.
*/
export type AtomicBEEF = Byte[]
/**
* @typedef {string & { minLength: 5, maxLength: 400 }} ProtocolString5To400Characters
* A protocol identifier with a length between 5 and 400 characters.
* @remarks
* TypeScript cannot enforce length constraints. Validate at runtime for length compliance.
*/
export type ProtocolString5To400Characters = string
/**
* @typedef {string & { maxLength: 800 }} KeyIDStringUnder800Characters
* Represents a key identifier string, with a maximum length of 800 characters.
* @remarks
* TypeScript cannot enforce length constraints. Validate at runtime for length compliance.
*/
export type KeyIDStringUnder800Characters = string
/**
* @typedef {string & { maxLength: 50 }} CertificateFieldNameUnder50Characters
* Represents a certificate field name with a maximum length of 50 characters.
* @remarks
* TypeScript cannot enforce length constraints. Validate at runtime for length compliance.
*/
export type CertificateFieldNameUnder50Characters = string
/**
* @typedef {string & { maxLength: 100 }} EntityNameStringMax100Characters
* Represents a trusted entity name with a maximum length of 100 characters.
* @remarks
* TypeScript cannot enforce length constraints. Validate at runtime for length compliance.
*/
export type EntityNameStringMax100Characters = string
/**
* @typedef {string & { maxLength: 500 }} EntityIconURLStringMax500Characters
* Represents a trusted entity icon URL with a maximum length of 500 characters.
* @remarks
* TypeScript cannot enforce length constraints. Validate at runtime for length compliance.
*/
export type EntityIconURLStringMax500Characters = string
/**
* @typedef {string & { minLength: 7, maxLength: 30 }} VersionString7To30Characters
* Represents a version string, with a length between 7 and 30 characters.
*
* The format is [vendor]-[major].[minor].[patch]
* @remarks
* Validate at runtime for format and length compliance.
*/
export type VersionString7To30Characters = string
/**
* @typedef {string & { minLength: 10, maxLength: 40 }} ErrorCodeString10To40Characters
* Represents a machine-readable error code string, with a length between 10 and 40 characters.
* @remarks
* TypeScript cannot enforce length constraints. Validate at runtime for length compliance.
*/
export type ErrorCodeString10To40Characters = string
/**
* @typedef {string & { minLength: 20, maxLength: 200 }} ErrorDescriptionString20To200Characters
* Represents a human-readable error description string, with a length between 20 and 200 characters.
* @remarks
* TypeScript cannot enforce length constraints. Validate at runtime for length compliance.
*/
export type ErrorDescriptionString20To200Characters = string
/**
* The Wallet interface defines a wallet capable of various tasks including transaction creation and signing,
* encryption, decryption, identity certificate management, identity verification, and communication
* with applications as per the BRC standards. This interface allows applications to interact with
* the wallet for a range of functionalities aligned with the Babbage architectural principles.
*/
export interface Wallet {
/**
* Creates a new Bitcoin transaction based on the provided inputs, outputs, labels, locks, and other options.
*
* @param {Object} args - The arguments required to create the transaction.
* @param {DescriptionString5to50Characters} args.description - A human-readable description of the action represented by this transaction.
* @param {BEEF} [args.inputBEEF] - BEEF data associated with the set of input transactions from which UTXOs will be consumed.
* @param {Array<Object>} [args.inputs] - An optional array of input objects used in the transaction.
* @param {OutpointString} args.inputs[].outpoint - The outpoint being consumed.
* @param {HexString} args.inputs[].unlockingScript - The unlocking script needed to release the specified UTXO.
* @param {DescriptionString5to50Characters} args.inputs[].inputDescription - A description of this input for contextual understanding of what it consumes.
* @param {PositiveIntegerOrZero} [args.inputs[].sequenceNumber] - An optional sequence number applied to the input.
* @param {PositiveInteger} [args.inputs[].unlockingScriptLength] - Length of the unlocking script, in case it will be provided later using `signAction`.
* @param {Array<Object>} [args.outputs] - An optional array of output objects for the transaction.
* @param {HexString} args.outputs[].lockingScript - The locking script that dictates how the output can later be spent.
* @param {SatoshiValue} args.outputs[].satoshis - Number of Satoshis that constitute this output.
* @param {DescriptionString5to50Characters} args.outputs[].outputDescription - Description of what this output represents.
* @param {BasketStringUnder300Characters} [args.outputs[].basket] - Name of the basket where this UTXO will be held, if tracking is desired.
* @param {string} [args.outputs[].customInstructions] - Custom instructions attached onto this UTXO, often utilized within application logic to provide necessary unlocking context or track token histories.
* @param {OutputTagStringUnder300Characters[]} [args.outputs[].tags] - Tags assigned to the output for sorting or filtering.
* @param {PositiveIntegerOrZero} [args.lockTime] - Optional lock time for the transaction.
* @param {PositiveInteger} [args.version] - Optional transaction version specifier.
* @param {LabelStringUnder300Characters[]} [args.labels] - Optional labels providing additional categorization for the transaction.
* @param {Object} [args.options] - Optional settings modifying transaction processing behavior.
* @param {BooleanDefaultTrue} [args.options.signAndProcess] - Optional. If true and all inputs have unlockingScripts, the new transaction will be signed and handed off for processing by the network; result `txid` and `tx` are valid and `signableTransaciton` is undefined. If false or an input has an unlockingScriptLength, result `txid` and `tx` are undefined and `signableTransaction` is valid.
* @param {BooleanDefaultTrue} [args.options.acceptDelayedBroadcast] - Optional. If true, the transaction will be sent to the network by a background process; use `noSend` and `sendWith` options to batch chained transactions. If false, the transaction will be broadcast to the network and any errors returned in result; note that rapidly sent chained transactions may still fail due to network propagation delays.
* @param {'known'} [args.options.trustSelf] - Optional. If `known`, input transactions may omit supporting validity proof data for TXIDs known to this wallet or included in `knownTxids`.
* @param {TXIDHexString[]} [args.options.knownTxids] - Optional. When working with large chained transactions using `noSend` and `sendWith` options, include TXIDs of inputs that may be assumed to be valid even if not already known by this wallet.
* @param {BooleanDefaultFalse} [args.options.returnTXIDOnly] - Optional. If true, only a TXID will be returned instead of a transaction.
* @param {BooleanDefaultFalse} [args.options.noSend] - Optional. If true, the transaction will be constructed but not sent to the network. Supports the creation of chained batches of transactions using the `sendWith` option.
* @param {Array<OutPoint>} [args.options.noSendChange] - Optional. Valid when `noSend` is true. May contain `noSendChange` outpoints previously returned by prior `noSend` actions in the same batch of chained actions.
* @param {Array<TXIDHexString>} [args.options.sendWith] - Optional. Sends a batch of actions previously created as `noSend` actions to the network; either synchronously if `acceptDelayedBroadcast` is true or by a background process.
* @param {BooleanDefaultTrue} [args.options.randomizeOutputs] — optional. When set to false, the wallet will avoid randomizing the order of outputs within the transaction.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} The promise returns different structures based on the outcome: error response, response with TXID, response with transaction, or info about signable transaction (partial BEEF and reference number).
*/
createAction: (
args: {
description: DescriptionString5to50Characters
inputBEEF?: BEEF
inputs?: Array<{
outpoint: OutpointString
unlockingScript?: HexString
unlockingScriptLength?: PositiveInteger
inputDescription: DescriptionString5to50Characters
sequenceNumber?: PositiveIntegerOrZero
}>
outputs?: Array<{
lockingScript: HexString
satoshis: SatoshiValue
outputDescription: DescriptionString5to50Characters
basket?: BasketStringUnder300Characters
customInstructions?: string
tags?: OutputTagStringUnder300Characters[]
}>
lockTime?: PositiveIntegerOrZero
version?: PositiveIntegerOrZero
labels?: LabelStringUnder300Characters[]
options?: {
signAndProcess?: BooleanDefaultTrue
acceptDelayedBroadcast?: BooleanDefaultTrue
trustSelf?: 'known'
knownTxids?: TXIDHexString[]
returnTXIDOnly?: BooleanDefaultFalse
noSend?: BooleanDefaultFalse
noSendChange?: OutpointString[]
sendWith?: TXIDHexString[]
randomizeOutputs?: BooleanDefaultTrue
}
},
originator?: OriginatorDomainNameString
) => Promise<{
txid?: TXIDHexString
tx?: AtomicBEEF
noSendChange?: OutpointString[]
sendWithResults?: Array<{
txid: TXIDHexString
status: 'unproven' | 'sending' | 'failed'
}>
signableTransaction?: {
tx: AtomicBEEF
reference: Base64String
}
}>
/**
* Signs a transaction previously created using `createAction`.
*
* @param {Object} args - Arguments to sign the transaction.
* @param {Record<PositiveIntegerOrZero, Object>} args.spends - Map of input indexes to the corresponding unlocking script and optional sequence number.
* @param {HexString} args.spends[].unlockingScript - The unlocking script for the corresponding input.
* @param {PositiveIntegerOrZero} [args.spends[].sequenceNumber] - The sequence number of the input.
* @param {Base64String} args.reference - Reference number returned from the call to `createAction`.
* @param {Object} [args.options] - Optional settings modifying transaction processing behavior.
* @param {BooleanDefaultTrue} [args.options.acceptDelayedBroadcast] - Optional. If true, transaction will be sent to the network by a background process; use `noSend` and `sendWith` options to batch chained transactions. If false, transaction will be broadcast to the network and any errors returned in result; note that rapidly sent chained transactions may still fail due to network propagation delays.
* @param {'known'} [args.options.trustSelf] - Optional. If `known`, input transactions may omit supporting validity proof data for TXIDs known to this wallet or included in `knownTxids`.
* @param {TXIDHexString[]} [args.options.knownTxids] - Optional. When working with large chained transactions using `noSend` and `sendWith` options, include TXIDs of inputs that may be assumed to be valid even if not already known by this wallet.
* @param {BooleanDefaultFalse} [args.options.returnTXIDOnly] - Optional. If true, only a TXID will be returned instead of a transaction.
* @param {BooleanDefaultFalse} [args.options.noSend] - Optional. If true, the transaction will be constructed but not sent to the network. Supports the creation of chained batches of transactions using the `sendWith` option.
* @param {Array<TXIDHexString>} [args.options.sendWith] - Optional. Sends a batch of actions previously created as `noSend` actions to the network; either synchronously if `acceptDelayedBroadcast` is true or by a background process.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} The promise returns an error response or a response with either the completed transaction or TXID.
*/
signAction: (
args: {
spends: Record<
PositiveIntegerOrZero,
{
unlockingScript: HexString
sequenceNumber?: PositiveIntegerOrZero
}
>
reference: Base64String
options?: {
acceptDelayedBroadcast?: BooleanDefaultTrue
returnTXIDOnly?: BooleanDefaultFalse
noSend?: BooleanDefaultFalse
sendWith?: TXIDHexString[]
}
},
originator?: OriginatorDomainNameString
) => Promise<{
txid?: TXIDHexString
tx?: AtomicBEEF
sendWithResults?: Array<{
txid: TXIDHexString
status: 'unproven' | 'sending' | 'failed'
}>
}>
/**
* Aborts a transaction that is in progress and has not yet been finalized or sent to the network.
*
* @param {Object} args - Arguments to identify the transaction that needs to be aborted.
* @param {Base64String} args.reference - Reference number for the transaction to abort.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} The promise resolves to an object indicating the abortion result (either success or error).
*/
abortAction: (
args: {
reference: Base64String
},
originator?: OriginatorDomainNameString
) => Promise<{ aborted: true }>
/**
* Lists all transactions matching the specified labels.
*
* @param {Object} args - Arguments to specify how to filter or retrieve transactions.
* @param {LabelStringUnder300Characters[]} args.labels - An array of labels used to filter actions.
* @param {'any' | 'all'} [args.labelQueryMode] - Specifies how to match labels (default is any which matches any of the labels).
* @param {BooleanDefaultFalse} [args.includeLabels] - Whether to include transaction labels in the result set.
* @param {boolean} [args.includeInputs] - Whether to include input details in the result set.
* @param {boolean} [args.includeInputSourceLockingScripts] - Whether to include input source locking scripts in the result set.
* @param {boolean} [args.includeInputUnlockingScripts] - Whether to include input unlocking scripts in the result set.
* @param {boolean} [args.includeOutputs] - Whether to include output details in the result set.
* @param {boolean} [args.includeOutputLockingScripts] - Whether to include output locking scripts in the result set.
* @param {PositiveIntegerDefault10Max10000} [args.limit] - The maximum number of transactions to retrieve.
* @param {PositiveIntegerOrZero} [args.offset] - Number of transactions to skip before starting to return the results.
* @param {BooleanDefaultTrue} [args.seekPermission] — Whether to seek permission from the user for this operation if required. Default true, will return an error rather than proceed if set to false.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} The promise resolves to an object containing actions, their metadata, inputs, and outputs if applicable, or an error object.
*/
listActions: (
args: {
labels: LabelStringUnder300Characters[]
labelQueryMode?: 'any' | 'all'
includeLabels?: BooleanDefaultFalse
includeInputs?: BooleanDefaultFalse
includeInputSourceLockingScripts?: BooleanDefaultFalse
includeInputUnlockingScripts?: BooleanDefaultFalse
includeOutputs?: BooleanDefaultFalse
includeOutputLockingScripts?: BooleanDefaultFalse
limit?: PositiveIntegerDefault10Max10000
offset?: PositiveIntegerOrZero
seekPermission?: BooleanDefaultTrue
},
originator?: OriginatorDomainNameString
) => Promise<{
totalActions: PositiveIntegerOrZero
actions: Array<{
txid: TXIDHexString
satoshis: SatoshiValue
status:
| 'completed'
| 'unprocessed'
| 'sending'
| 'unproven'
| 'unsigned'
| 'nosend'
| 'nonfinal'
isOutgoing: boolean
description: DescriptionString5to50Characters
labels?: LabelStringUnder300Characters[]
version: PositiveIntegerOrZero
lockTime: PositiveIntegerOrZero
inputs?: Array<{
sourceOutpoint: OutpointString
sourceSatoshis: SatoshiValue
sourceLockingScript?: HexString
unlockingScript?: HexString
inputDescription: DescriptionString5to50Characters
sequenceNumber: PositiveIntegerOrZero
}>
outputs?: Array<{
outputIndex: PositiveIntegerOrZero
satoshis: SatoshiValue
lockingScript?: HexString
spendable: boolean
outputDescription: DescriptionString5to50Characters
basket: BasketStringUnder300Characters
tags: OutputTagStringUnder300Characters[]
customInstructions?: string
}>
}>
}>
/**
* Submits a transaction to be internalized and optionally labeled, outputs paid to the wallet balance, inserted into baskets, and/or tagged.
*
* @param {Object} args - Arguments required to internalize the transaction.
* @param {BEEF} args.tx - Atomic BEEF-formatted transaction to internalize.
* @param {Array<Object>} args.outputs - Metadata about outputs, processed differently based on payment or insertion types.
* @param {PositiveIntegerOrZero} args.outputs[].outputIndex - Index of the output within the transaction.
* @param {'payment' | 'insert'} args.outputs[].protocol - Specifies whether the output is a payment (to be received into the wallet balance) or an insert operation (into a particular basket).
* @param {Object} [args.outputs[].paymentRemittance] - Remittance data, structured accordingly for the payment operation.
* @param {Base64String} args.outputs[].paymentRemittance.derivationPrefix - Payment-level derivation prefix used by the sender for key derivation (for payments).
* @param {Base64String} args.outputs[].paymentRemittance.derivationSuffix - Specific output-level derivation suffix used by the sender for key derivation (for payments).
* @param {PubKeyHex} args.outputs[].paymentRemittance.senderIdentityKey - Public identity key of the sender (for payments).
* @param {Object} [args.outputs[].insertionRemittance] - Remittance data, structured accordingly for the insertion operation.
* @param {BasketStringUnder300Characters} args.outputs[].insertionRemittance.basket - Basket in which to place the output (for insertions).
* @param {string} [args.outputs[].insertionRemittance.customInstructions] - Optionally provided custom instructions attached to the output (for insertions).
* @param {OutputTagStringUnder300Characters[]} [args.outputs[].insertionRemittance.tags] - Tags attached to the output (for insertions).
* @param {DescriptionString5to50Characters} args.description - Human-readable description of the transaction being internalized.
* @param {LabelStringUnder300Characters[]} [args.labels] - Optional labels associated with this transaction.
* @param {BooleanDefaultTrue} [args.seekPermission] — Whether to seek permission from the user for this operation if required. Default true, will return an error rather than proceed if set to false.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} The promise resolves to an object indicating the success of the operation or an error object.
*/
internalizeAction: (
args: {
tx: AtomicBEEF
outputs: Array<{
outputIndex: PositiveIntegerOrZero
protocol: 'wallet payment' | 'basket insertion'
paymentRemittance?: {
derivationPrefix: Base64String
derivationSuffix: Base64String
senderIdentityKey: PubKeyHex
}
insertionRemittance?: {
basket: BasketStringUnder300Characters
customInstructions?: string
tags?: OutputTagStringUnder300Characters[]
}
}>
description: DescriptionString5to50Characters
labels?: LabelStringUnder300Characters[]
seekPermission?: BooleanDefaultTrue
},
originator?: OriginatorDomainNameString
) => Promise<{ accepted: true }>
/**
* Lists the spendable outputs kept within a specific basket, optionally tagged with specific labels.
*
* @param {Object} args - Arguments detailing the query for listing spendable outputs.
* @param {BasketStringUnder300Characters} args.basket - The associated basket name whose outputs should be listed.
* @param {OutputTagStringUnder300Characters[]} [args.tags] - Filter outputs based on these tags.
* @param {'all' | 'any'} [args.tagQueryMode] - Filter mode, defining whether all or any of the tags must match. By default, any tag can match.
* @param {'locking scripts' | 'entire transactions'} [args.include] - Whether to include locking scripts (with each output) or entire transactions (as aggregated BEEF, at the top level) in the result. By default, unless specified, neither are returned.
* @param {BooleanDefaultFalse} [args.includeEntireTransactions] - Whether to include the entire transaction(s) in the result.
* @param {BooleanDefaultFalse} [args.includeCustomInstructions] - Whether custom instructions should be returned in the result.
* @param {BooleanDefaultFalse} [args.includeTags] - Whether the tags associated with the output should be returned.
* @param {BooleanDefaultFalse} [args.includeLabels] - Whether the labels associated with the transaction containing the output should be returned.
* @param {PositiveIntegerDefault10Max10000} [args.limit] - Optional limit on the number of outputs to return.
* @param {PositiveIntegerOrZero} [args.offset] - Number of outputs to skip before starting to return results.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @param {BooleanDefaultTrue} [args.seekPermission] — Whether to seek permission from the user for this operation if required. Default true, will return an error rather than proceed if set to false.
* @returns {Promise<Object>} The promise returns an output listing or an error object.
*/
listOutputs: (
args: {
basket: BasketStringUnder300Characters
tags?: OutputTagStringUnder300Characters[]
tagQueryMode?: 'all' | 'any'
include?: 'locking scripts' | 'entire transactions'
includeCustomInstructions?: BooleanDefaultFalse
includeTags?: BooleanDefaultFalse
includeLabels?: BooleanDefaultFalse
limit?: PositiveIntegerDefault10Max10000
offset?: PositiveIntegerOrZero
seekPermission?: BooleanDefaultTrue
},
originator?: OriginatorDomainNameString
) => Promise<{
totalOutputs: PositiveIntegerOrZero
BEEF?: BEEF
outputs: Array<{
outpoint: OutpointString
satoshis: SatoshiValue
lockingScript?: HexString
spendable: true
customInstructions?: string
tags?: OutputTagStringUnder300Characters[]
labels?: LabelStringUnder300Characters[]
}>
}>
/**
* Relinquish an output out of a basket, removing it from tracking without spending it.
*
* @param {Object} args - Arguments identifying the output in the basket.
* @param {BasketStringUnder300Characters} args.basket - The associated basket name where the output should be removed.
* @param {OutpointString} args.outpoint - The output that should be removed from the basket.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} The promise returns an indication of successful removal or an error object.
*/
relinquishOutput: (
args: {
basket: BasketStringUnder300Characters
output: OutpointString
},
originator?: OriginatorDomainNameString
) => Promise<{ relinquished: true }>
/**
* Retrieves a derived or identity public key based on the requested protocol, key ID, counterparty, and other factors.
*
* @param {Object} args - Arguments to specify which public key to retrieve.
* @param {BooleanDefaultFalse|true} [args.identityKey] - Use true to retrieve the current user's own identity key, overriding any protocol ID, key ID, or counterparty specified.
* @param {[0 | 1 | 2, ProtocolString5To400Characters]} args.protocolID - The security level and protocol string used for key derivation.
* @param {KeyIDStringUnder800Characters} args.keyID - The key ID used for key derivation.
* @param {BooleanDefaultFalse} [args.privileged] - Whether this is a privileged request.
* @param {DescriptionString5to50Characters} [args.privilegedReason] - Reason provided for privileged access, required if this is a privileged operation.
* @param {PubKeyHex | 'self' | 'anyone'} [args.counterparty] - The public key of the counterparty involved in the key derivation process.
* @param {BooleanDefaultFalse} [args.forSelf] - Whether to return the public key derived from the current user's own identity (as opposed to the counterparty's identity).
* @param {BooleanDefaultTrue} [args.seekPermission] — Whether to seek permission from the user for this operation if required. Default true, will return an error rather than proceed if set to false.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} Resolves to an object containing the public key, or an error response.
*/
getPublicKey: (
args: {
identityKey?: true
protocolID?: [0 | 1 | 2, ProtocolString5To400Characters]
keyID?: KeyIDStringUnder800Characters
privileged?: BooleanDefaultFalse
privilegedReason?: DescriptionString5to50Characters
counterparty?: PubKeyHex | 'self' | 'anyone'
forSelf?: BooleanDefaultFalse
seekPermission?: BooleanDefaultTrue
},
originator?: OriginatorDomainNameString
) => Promise<{ publicKey: PubKeyHex }>
/**
* Reveals the key linkage between ourselves and a counterparty, to a particular verifier, across all interactions with the counterparty.
*
* @param {Object} args - Contains information about counterparty, verifier, and whether the operation is privileged.
* @param {PubKeyHex} args.counterparty - The public key of the counterparty involved in the linkage.
* @param {PubKeyHex} args.verifier - The public key of the verifier requesting the linkage information.
* @param {DescriptionString5to50Characters} [args.privilegedReason] - Reason provided for privileged access, required if this is a privileged operation.
* @param {BooleanDefaultFalse} [args.privileged] - Whether this is a privileged request.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} Resolves to the key linkage, or an error response.
*/
revealCounterpartyKeyLinkage: (
args: {
counterparty: PubKeyHex
verifier: PubKeyHex
privilegedReason?: DescriptionString5to50Characters
privileged?: BooleanDefaultFalse
},
originator?: OriginatorDomainNameString
) => Promise<{
prover: PubKeyHex
verifier: PubKeyHex
counterparty: PubKeyHex
revelationTime: ISOTimestampString
encryptedLinkage: Byte[]
encryptedLinkageProof: Byte[]
}>
/**
* Reveals the key linkage between ourselves and a counterparty, to a particular verifier, with respect to a specific interaction.
*
* @param {Object} args - The object defining the counterparty, verifier, protocol, and keyID for which linkage should be revealed.
* @param {PubKeyHex} args.counterparty - The public key of the counterparty involved in the linkage.
* @param {PubKeyHex} args.verifier - The public key of the verifier requesting the linkage information.
* @param {[0 | 1 | 2, ProtocolString5To400Characters]} args.protocolID - The security level and protocol string associated with the linkage information to reveal.
* @param {KeyIDStringUnder800Characters} args.keyID - The key ID associated with the linkage information to reveal.
* @param {DescriptionString5to50Characters} [args.privilegedReason] - Reason provided for privileged access, required if this is a privileged operation.
* @param {BooleanDefaultFalse} [args.privileged] - Whether this is a privileged request.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} The promise returns the requested linkage information, or an error object.
*/
revealSpecificKeyLinkage: (
args: {
counterparty: PubKeyHex
verifier: PubKeyHex
protocolID: [0 | 1 | 2, ProtocolString5To400Characters]
keyID: KeyIDStringUnder800Characters
privilegedReason?: DescriptionString5to50Characters
privileged?: BooleanDefaultFalse
},
originator?: OriginatorDomainNameString
) => Promise<{
prover: PubKeyHex
verifier: PubKeyHex
counterparty: PubKeyHex
protocolID: [0 | 1 | 2, ProtocolString5To400Characters]
keyID: KeyIDStringUnder800Characters
encryptedLinkage: Byte[]
encryptedLinkageProof: Byte[]
proofType: Byte
}>
/**
* Encrypts the provided plaintext data using derived keys, based on the protocol ID, key ID, counterparty, and other factors.
*
* @param {Object} args - Information needed for encryption, including the plaintext, protocol ID, and key ID.
* @param {Byte[]} args.plaintext - Array of bytes constituting the plaintext data to be encrypted.
* @param {[0 | 1 | 2, ProtocolString5To400Characters]} args.protocolID - The security level and protocol string under which the data should be encrypted.
* @param {KeyIDStringUnder800Characters} args.keyID - Key ID under which the encryption will be performed.
* @param {DescriptionString5to50Characters} [args.privilegedReason] - Reason provided for privileged access, required if this is a privileged operation.
* @param {PubKeyHex | 'self' | 'anyone'} [args.counterparty] - Public key of the counterparty (if two-party encryption is desired).
* @param {BooleanDefaultFalse} [args.privileged] - Whether this is a privileged request.
* @param {BooleanDefaultTrue} [args.seekPermission] — Whether to seek permission from the user for this operation if required. Default true, will return an error rather than proceed if set to false.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} Resolves to the encrypted ciphertext bytes or an error if encryption fails.
*/
encrypt: (
args: {
plaintext: Byte[]
protocolID: [0 | 1 | 2, ProtocolString5To400Characters]
keyID: KeyIDStringUnder800Characters
privilegedReason?: DescriptionString5to50Characters
counterparty?: PubKeyHex | 'self' | 'anyone'
privileged?: BooleanDefaultFalse
seekPermission?: BooleanDefaultTrue
},
originator?: OriginatorDomainNameString
) => Promise<{ ciphertext: Byte[] }>
/**
* Decrypts the provided ciphertext using derived keys, based on the protocol ID, key ID, counterparty, and other factors.
*
* @param {Object} args - Contains the ciphertext, protocol ID, and key ID required to decrypt the data.
* @param {Byte[]} args.ciphertext - Encrypted bytes, including the initialization vector, for decryption.
* @param {[0 | 1 | 2, ProtocolString5To400Characters]} args.protocolID - Security level and protocol string that were used during the encryption of the ciphertext.
* @param {KeyIDStringUnder800Characters} args.keyID - Key ID used during the encryption of the ciphertext.
* @param {DescriptionString5to50Characters} [args.privilegedReason] - Reason provided for privileged access, required if this is a privileged operation.
* @param {PubKeyHex | 'self' | 'anyone'} [args.counterparty] - Public identity key of the counterparty for the encryption operation.
* @param {BooleanDefaultFalse} [args.privileged] - Whether this is a privileged request.
* @param {BooleanDefaultTrue} [args.seekPermission] — Whether to seek permission from the user for this operation if required. Default true, will return an error rather than proceed if set to false.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} Resolves to the decryption result, containing the plaintext data or an error.
*/
decrypt: (
args: {
ciphertext: Byte[]
protocolID: [0 | 1 | 2, ProtocolString5To400Characters]
keyID: KeyIDStringUnder800Characters
privilegedReason?: DescriptionString5to50Characters
counterparty?: PubKeyHex | 'self' | 'anyone'
privileged?: BooleanDefaultFalse
seekPermission?: BooleanDefaultTrue
},
originator?: OriginatorDomainNameString
) => Promise<{ plaintext: Byte[] }>
/**
* Creates an HMAC (Hash-based Message Authentication Code) based on the provided data, protocol, key ID, counterparty, and other factors.
*
* @param {Object} args - Arguments containing the data, protocol ID, and key ID to generate the HMAC from.
* @param {Byte[]} args.data - Input data (in bytes) for which the HMAC needs to be created.
* @param {[0 | 1 | 2, ProtocolString5To400Characters]} args.protocolID - Security level and protocol string to be used during the HMAC operation.
* @param {KeyIDStringUnder800Characters} args.keyID - Key ID to be used in the HMAC operation.
* @param {DescriptionString5to50Characters} [args.privilegedReason] - Reason provided for privileged access, required if this is a privileged operation.
* @param {PubKeyHex | 'self' | 'anyone'} [args.counterparty] - Public identity key of the counterparty if the operation encompasses a two-party interaction.
* @param {BooleanDefaultFalse} [args.privileged] - Whether this is a privileged request.
* @param {BooleanDefaultTrue} [args.seekPermission] — Whether to seek permission from the user for this operation if required. Default true, will return an error rather than proceed if set to false.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} Resolves to an object containing the generated HMAC bytes, or an error if the creation fails.
*/
createHmac: (
args: {
data: Byte[]
protocolID: [0 | 1 | 2, ProtocolString5To400Characters]
keyID: KeyIDStringUnder800Characters
privilegedReason?: DescriptionString5to50Characters
counterparty?: PubKeyHex | 'self' | 'anyone'
privileged?: BooleanDefaultFalse
seekPermission?: BooleanDefaultTrue
},
originator?: OriginatorDomainNameString
) => Promise<{ hmac: Byte[] }>
/**
* Verifies an HMAC (Hash-based Message Authentication Code) based on the provided data, protocol, key ID, counterparty, and other factors.
*
* @param {Object} args - Arguments containing the HMAC data, protocol ID, and key ID needed for verification.
* @param {Byte[]} args.data - The input data whose HMAC is to be verified.
* @param {Byte[]} args.hmac - Byte array representing the HMAC value to be verified.
* @param {[0 | 1 | 2, ProtocolString5To400Characters]} args.protocolID - Security level and protocol string to be used during the HMAC operation.
* @param {KeyIDStringUnder800Characters} args.keyID - Key ID to be used during the HMAC operation.
* @param {DescriptionString5to50Characters} [args.privilegedReason] - Reason provided for privileged access, required if this is a privileged operation.
* @param {PubKeyHex | 'self' | 'anyone'} [args.counterparty] - Public identity key of the counterparty if the operation encompasses a two-party interaction.
* @param {BooleanDefaultFalse} [args.privileged] - Whether this is a privileged request.
* @param {BooleanDefaultTrue} [args.seekPermission] — Whether to seek permission from the user for this operation if required. Default true, will return an error rather than proceed if set to false.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} Resolves to an object confirming whether the HMAC was valid or an error.
*/
verifyHmac: (
args: {
data: Byte[]
hmac: Byte[]
protocolID: [0 | 1 | 2, ProtocolString5To400Characters]
keyID: KeyIDStringUnder800Characters
privilegedReason?: DescriptionString5to50Characters
counterparty?: PubKeyHex | 'self' | 'anyone'
privileged?: BooleanDefaultFalse
seekPermission?: BooleanDefaultTrue
},
originator?: OriginatorDomainNameString
) => Promise<{ valid: true }>
/**
* Creates a digital signature for the provided data or hash using a specific protocol, key, and optionally considering privilege and counterparty.
*
* @param {Object} args - Arguments to specify data, protocol, key ID, and privilege for creating the signature.
* @param {Byte[]} [args.data] - Data to be signed using the derived private key with ECDSA. Required unless directly signing a hash.
* @param {[0 | 1 | 2, ProtocolString5To400Characters]} args.protocolID - Security level and protocol string to be used during the signing operation.
* @param {KeyIDStringUnder800Characters} args.keyID - Key ID to be used during the signing operation.
* @param {DescriptionString5to50Characters} [args.privilegedReason] - Reason provided for privileged access, required if this is a privileged operation.
* @param {PubKeyHex | 'self' | 'anyone'} [args.counterparty] - Public identity key of the counterparty if the operation encompasses a two-party interaction.
* @param {BooleanDefaultFalse} [args.privileged] - Whether this is a privileged request.
* @param {Byte[]} [args.hashToDirectlySign] - Sign a pre-hashed value in situations where data can't or shouldn't be revealed, whether due to its size or for privacy.
* @param {BooleanDefaultTrue} [args.seekPermission] — Whether to seek permission from the user for this operation if required. Default true, will return an error rather than proceed if set to false.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} The promise will resolve to an object containing the DER-encoded ECDSA signature, or an error on failure.
*/
createSignature: (
args: {
data?: Byte[]
hashToDirectlySign?: Byte[]
protocolID: [0 | 1 | 2, ProtocolString5To400Characters]
keyID: KeyIDStringUnder800Characters
privilegedReason?: DescriptionString5to50Characters
counterparty?: PubKeyHex | 'self' | 'anyone'
privileged?: BooleanDefaultFalse
seekPermission?: BooleanDefaultTrue
},
originator?: OriginatorDomainNameString
) => Promise<{ signature: Byte[] }>
/**
* Verifies a digital signature for the provided data or hash using a specific protocol, key, and optionally considering privilege and counterparty.
*
* @param {Object} args - Arguments specifying the data, signature, protocol, and key ID.
* @param {Byte[]} [args.data] - The data originally signed, which is required for verification unless directly verifying a hash.
* @param {Byte[]} args.signature - The DER-encoded ECDSA signature to validate.
* @param {[0 | 1 | 2, ProtocolString5To400Characters]} args.protocolID - Security level and protocol string to be used during signature verification.
* @param {KeyIDStringUnder800Characters} args.keyID - Key ID to be used during signature verification.
* @param {DescriptionString5to50Characters} [args.privilegedReason] - Reason provided for privileged access, required if this is a privileged operation.
* @param {PubKeyHex | 'self' | 'anyone'} [args.counterparty] - Public identity key of the counterparty if the operation encompasses a two-party interaction.
* @param {BooleanDefaultFalse} [args.forSelf] - Whether the signature to be verified was created by this user rather than the counterparty.
* @param {BooleanDefaultFalse} [args.privileged] - Whether this is a privileged request.
* @param {Byte[]} [args.hashToDirectlyVerify] - Optional field to verify the signature against a precomputed hash instead of data.
* @param {BooleanDefaultTrue} [args.seekPermission] — Whether to seek permission from the user for this operation if required. Default true, will return an error rather than proceed if set to false.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} The promise resolves to a boolean object indicating whether the signature was valid or an error message.
*/
verifySignature: (
args: {
data?: Byte[]
hashToDirectlyVerify?: Byte[]
signature: Byte[]
protocolID: [0 | 1 | 2, ProtocolString5To400Characters]
keyID: KeyIDStringUnder800Characters
privilegedReason?: DescriptionString5to50Characters
counterparty?: PubKeyHex | 'self' | 'anyone'
forSelf?: BooleanDefaultFalse
privileged?: BooleanDefaultFalse
seekPermission?: BooleanDefaultTrue
},
originator?: OriginatorDomainNameString
) => Promise<{ valid: true }>
/**
* Acquires an identity certificate, whether by acquiring one from the certifier or by directly receiving it.
*
* @param {Object} args - Contains the type of certificate, certifier information, and fields of the certificate to be provided, among other details.
* @param {Base64String} args.type - Type identifier for the certificate.
* @param {PubKeyHex} args.certifier - The public identity key of the certifier.
* @param {'issuance' | 'direct'} args.acquisitionProtocol - Specifies the acquisition process, set to either 'issuance' or 'direct'.
* * @param {Record<CertificateFieldNameUnder50Characters, string>} args.fields - The fields included within the certificate.
* @param {Base64String} [args.serialNumber] - Serial number of the certificate to acquire (required when the acquisition protocol is direct).
* @param {string} [args.revocationOutpoint] - Reference for an outpoint comprising a Bitcoin token that, if ever spent, marks the certificate as invalid (required when the acquisition protocol is direct).
* @param {HexString} [args.signature] - Signature over the certificate (required when the acquisition protocol is direct).
* @param {string} [args.certifierUrl] - URL of the certifier where certificate acquisition requests will be sent (required when the acquisition protocol is issuance).
* @param {PubKeyHex | 'certifier'} [args.keyringRevealer] - The public identity key of the entity revealing the keyring to the user, if different from the certifier (required when the acquisition protocol is direct).
* @param {Record<CertificateFieldNameUnder50Characters, Base64String>} [args.keyringForSubject] - Keyring revealing all certificate fields to the subject (required when the acquisition protocol is direct).
* @param {BooleanDefaultFalse} [args.privileged] - Whether this is a privileged request.
* @param {DescriptionString5to50Characters} [args.privilegedReason] - Reason provided for privileged access, required if this is a privileged operation.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} The promise resolves to an object containing the acquired certificate, or an error object.
*/
acquireCertificate: (
args: {
type: Base64String
certifier: PubKeyHex
acquisitionProtocol: 'direct' | 'issuance'
fields: Record<CertificateFieldNameUnder50Characters, string>
serialNumber?: Base64String
revocationOutpoint?: OutpointString
signature?: HexString
certifierUrl?: string
keyringRevealer?: PubKeyHex | 'certifier'
keyringForSubject?: Record<
CertificateFieldNameUnder50Characters,
Base64String
>
privileged?: BooleanDefaultFalse
privilegedReason?: DescriptionString5to50Characters
},
originator?: OriginatorDomainNameString
) => Promise<{
type: Base64String
subject: PubKeyHex
serialNumber: Base64String
certifier: PubKeyHex
revocationOutpoint: OutpointString
signature: HexString
fields: Record<CertificateFieldNameUnder50Characters, string>
}>
/**
* Lists identity certificates belonging to the user, filtered by certifier(s) and type(s).
*
* @param {Object} args - Arguments used to filter or limit the list of certificates returned by the request.
* @param {PubKeyHex[]} args.certifiers - An array of public keys for specific certifiers (filters by these certifiers).
* @param {Base64String[]} args.types - An array of certificate types issued by any of the specified certifiers, which should be returned.
* @param {PositiveIntegerDefault10Max10000} [args.limit] - Maximum number of certificates to return.
* @param {PositiveIntegerOrZero} [args.offset] - Number of records to skip before starting to return results.
* @param {BooleanDefaultFalse} [args.privileged] - Whether this is a privileged request.
* @param {DescriptionString5to50Characters} [args.privilegedReason] - Reason provided for privileged access, required if this is a privileged operation.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} The promise resolves to an object containing certificates or an error response.
*/
listCertificates: (
args: {
certifiers: PubKeyHex[]
types: Base64String[]
limit?: PositiveIntegerDefault10Max10000
offset?: PositiveIntegerOrZero
privileged?: BooleanDefaultFalse
privilegedReason?: DescriptionString5to50Characters
},
originator?: OriginatorDomainNameString
) => Promise<{
totalCertificates: PositiveIntegerOrZero
certificates: Array<{
type: Base64String
subject: PubKeyHex
serialNumber: Base64String
certifier: PubKeyHex
revocationOutpoint: OutpointString
signature: HexString
fields: Record<CertificateFieldNameUnder50Characters, string>
}>
}>
/**
* Proves select fields of an identity certificate, as specified, when requested by a verifier.
*
* @param {Object} args - Arguments including the certificate, fields to reveal, and verifier's public key.
* @param {Object} args.certificate - The specific identity certificate being proven.
* @param {Base64String} args.certificate.type - The type of the certificate to be proven.
* @param {PubKeyHex} args.certificate.subject - Public key belonging to the certificate's subject.
* @param {Base64String} args.certificate.serialNumber - Unique serial number of the certificate.
* @param {PubKeyHex} args.certificate.certifier - Public key of the certifier who issued the certificate.
* @param {OutpointString} args.certificate.revocationOutpoint - The outpoint used to confirm that the certificate has not been revoked.
* @param {HexString} args.certificate.signature - Certificate signature by the certifier's private key.
* @param {Record<CertificateFieldNameUnder50Characters, string>} args.certificate.fields - All the encrypted fields present in the certificate.
* @param {CertificateFieldNameUnder50Characters[]} args.fieldsToReveal - Array of field names that need to be revealed to the verifier.
* @param {PubKeyHex} args.verifier - Public key of the verifier, to whom the key revelations will be made.
* @param {BooleanDefaultFalse} [args.privileged] - Whether this is a privileged request.
* @param {DescriptionString5to50Characters} [args.privilegedReason] - Reason provided for privileged access, required if this is a privileged operation.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} Resolves to a keyring for the verifier or an error object.
*/
proveCertificate: (
args: {
certificate: {
type: Base64String
subject: PubKeyHex
serialNumber: Base64String
certifier: PubKeyHex
revocationOutpoint: OutpointString
signature: HexString
fields: Record<CertificateFieldNameUnder50Characters, string>
}
fieldsToReveal: CertificateFieldNameUnder50Characters[]
verifier: PubKeyHex
privileged?: BooleanDefaultFalse
privilegedReason?: DescriptionString5to50Characters
},
originator?: OriginatorDomainNameString
) => Promise<{
keyringForVerifier: Record<
CertificateFieldNameUnder50Characters,
Base64String
>
}>
/**
* Relinquishes an identity certificate, removing it from the wallet regardless of whether the revocation outpoint has become spent.
*
* @param {Object} args - Contains the type of certificate, certifier, and serial number for relinquishment.
* @param {Base64String} args.type - Type identifier for the certificate.
* @param {PubKeyHex} args.certifier - The public identity key of the certifier.
* @param {Base64String} args.serialNumber - Serial number of the certificate to relinquish.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} The promise resolves to an indication of successful relinquishment or an error object.
*/
relinquishCertificate: (
args: {
type: Base64String
serialNumber: Base64String
certifier: PubKeyHex
},
originator?: OriginatorDomainNameString
) => Promise<{ relinquished: true }>
/**
* Discovers identity certificates, issued to a given identity key by a trusted entity.
*
* @param {Object} args - Arguments for requesting the discovery based on the identity key.
* @param {PubKeyHex} args.identityKey - Identity key used to filter and discover certificates.
* @param {PositiveIntegerDefault10Max10000} [args.limit] - Maximum number of certificates to return in the response.
* @param {PositiveIntegerOrZero} [args.offset] - Skip this number of records before starting to provide results.
* @param {BooleanDefaultTrue} [args.seekPermission] — Whether to seek permission from the user for this operation if required. Default true, will return an error rather than proceed if set to false.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} The promise resolves to the list of certificates discovered or an error object.
*/
discoverByIdentityKey: (
args: {
identityKey: PubKeyHex
limit?: PositiveIntegerDefault10Max10000
offset?: PositiveIntegerOrZero
seekPermission?: BooleanDefaultTrue
},
originator?: OriginatorDomainNameString
) => Promise<{
totalCertificates: PositiveIntegerOrZero
certificates: Array<{
type: Base64String
subject: PubKeyHex
serialNumber: Base64String
certifier: PubKeyHex
revocationOutpoint: OutpointString
signature: HexString
fields: Record<CertificateFieldNameUnder50Characters, Base64String>
certifierInfo: {
name: EntityNameStringMax100Characters
iconUrl: EntityIconURLStringMax500Characters
description: DescriptionString5to50Characters
trust: PositiveIntegerMax10
}
publiclyRevealedKeyring: Record<
CertificateFieldNameUnder50Characters,
Base64String
>
decryptedFields: Record<CertificateFieldNameUnder50Characters, string>
}>
}>
/**
* Discovers identity certificates belonging to other users, where the documents contain specific attributes, issued by a trusted entity.
*
* @param {Object} args - Attributes and optional parameters used to discover certificates.
* @param {Record<CertificateFieldNameUnder50Characters, string>} args.attributes - The attributes used to discover the certificates.
* @param {PositiveIntegerDefault10Max10000} [args.limit] - Optional limit on the number of results returned.
* @param {PositiveIntegerOrZero} [args.offset] - Starts retrieval of results after the specified number of records.
* @param {BooleanDefaultTrue} [args.seekPermission] — Whether to seek permission from the user for this operation if required. Default true, will return an error rather than proceed if set to false.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} The promise resolves to a list of matching certificates or an error object.
*/
discoverByAttributes: (
args: {
attributes: Record<CertificateFieldNameUnder50Characters, string>
limit?: PositiveIntegerDefault10Max10000
offset?: PositiveIntegerOrZero
seekPermission?: BooleanDefaultTrue
},
originator?: OriginatorDomainNameString
) => Promise<{
totalCertificates: PositiveIntegerOrZero
certificates: Array<{
type: Base64String
subject: PubKeyHex
serialNumber: Base64String
certifier: PubKeyHex
revocationOutpoint: OutpointString
signature: HexString
fields: Record<CertificateFieldNameUnder50Characters, Base64String>
certifierInfo: {
name: EntityNameStringMax100Characters
iconUrl: EntityIconURLStringMax500Characters
description: DescriptionString5to50Characters
trust: PositiveIntegerMax10
}
publiclyRevealedKeyring: Record<
CertificateFieldNameUnder50Characters,
Base64String
>
decryptedFields: Record<CertificateFieldNameUnder50Characters, string>
}>
}>
/**
* Checks the authentication status of the user.
*
* @param {Object} args - Empty object, as no parameters are needed.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} The promise resolves to an object indicating whether the user is authenticated or an error response.
*/
isAuthenticated: (
args: {},
originator?: OriginatorDomainNameString
) => Promise<{ authenticated: boolean }>
/**
* Continuously waits until the user is authenticated, returning the result once confirmed.
*
* @param {Object} args - Not used, pass an empty object.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} The final result indicating that the user is authenticated or an error object.
*/
waitForAuthentication: (
args: {},
originator?: OriginatorDomainNameString
) => Promise<{ authenticated: true }>
/**
* Retrieves the current height of the blockchain.
*
* @param {Object} args - Empty object as no other parameters are necessary.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} Resolves to an object indicating the current height or an error on failure.
*/
getHeight: (
args: {},
originator?: OriginatorDomainNameString
) => Promise<{ height: PositiveInteger }>
/**
* Retrieves the block header of a block at a specified height.
*
* @param {Object} args - Contains the height parameter needed to retrieve the block header.
* @param {PositiveInteger} args.height - Specifies the height at which the block header needs to be retrieved.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} The promise resolves to an 80-byte block header or an error if it cannot be retrieved.
*/
getHeaderForHeight: (
args: { height: PositiveInteger },
originator?: OriginatorDomainNameString
) => Promise<{ header: HexString }>
/**
* Retrieves the Bitcoin network the client is using (mainnet or testnet).
*
* @param {Object} args - No arguments required, pass an empty object.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} The promise resolves to an object indicating whether the client is using the mainnet or testnet.
*/
getNetwork: (
args: {},
originator?: OriginatorDomainNameString
) => Promise<{ network: 'mainnet' | 'testnet' }>
/**
* Retrieves the current version string of the wallet.
*
* @param {Object} args - Empty argument object.
* @param {OriginatorDomainNameString} [originator] - Fully-qualified domain name (FQDN) of the application that originated the request.
* @returns {Promise<Object>} Resolves to an object containing the version string of the wallet, or an error.
*/
getVersion: (
args: {},
originator?: OriginatorDomainNameString
) => Promise<{ version: VersionString7To30Characters }>
}