The Tari RFCs
Tari is a community-driven project. The documents presented in this RFC collection have typically gone through several iterations before reaching this point:
- Ideas and questions are posted in #tari-dev on #FreeNode IRC. This is typically short-form content with rapid feedback. Often, these conversations will lead to someone posting an issue or RFC pull request.
- RFCs are "Requests for Comment", so although the proposals in these documents are usually well-thought out, they are not cast in stone. RFCs can, and should, undergo further evaluation and discussion by the community. RFC comments are best made using Github issues.
New RFC's should follow the format given in the RFC template.
Lifecycle
RFCs go through the following lifecycle, which roughly corresponds to the COSS:
Status | Description | |
---|---|---|
Draft | Changes, additions and revisions can be expected. | |
Stable | Typographical and cosmetic changes aside, no further changes should be made. Changes to the Tari code base w.r.t. a stable RFC will lead to the RFC becoming out of date, deprecated, or retired. | |
Out of date | This RFC has become stale due to changes in the code base. Contributions will be accepted to make it stable again if the changes are relatively minor, otherwise it should eventually become deprecated or retired. | |
Deprecated | This RFC has been replaced by a newer RFC document, but is still is use in some places and/or versions of Tari. | |
Retired | The RFC is no longer in use on the Tari network. |
RFC-0001/Overview
Overview of Tari Network
Maintainer(s): Cayle Sharrock
Licence
Copyright 2018 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this proposal is to provide a very high-level perspective of the moving parts of the Tari protocol.
Related Requests for Comment
Description
Abstract
The Tari network comprises two layers:
- A base layer that deals with Tari coin transactions. It governed by a proof-of-work (PoW) blockchain that is merged-mined with Monero. The base layer is highly secure, decentralized and relatively slow.
- A digital assets network (DAN), consisting of multiple independent sidechains, that manage the state of the native digital assets. It is built for liveness, speed and scalability at the expense of decentralization.
Currency Tokens and Digital Assets
There are two major digital entities on the Tari network: the coins that are the unit of transfer for the Tari cryptocurrency, and the digital assets that could represent anything from tickets to in-game items.
Tari coins are the fuel that drives the entire Tari ecosystem. They share many of the properties of money, so security is a non-negotiable requirement. In a cryptocurrency context, this is usually achieved by employing a decentralized network running a censorship-resistant protocol such as Nakamoto consensus over a proof-of-work blockchain. As we know, PoW blockchains are not scalable or very fast.
On the other hand, the Tari network will be used to create and manage digital assets.
In Tari parlance, a digital asset is defined as a finite set of digital stateful tokens that are governed by predefined rules. A single digital asset may define anything from one to thousands of tokens within its scope.
For example, in a ticketing context, an event will be an asset. The asset definition will allocate tokens representing the tickets for that event. The ticket tokens will have state, such as its current owner and whether or not it has been redeemed. Users might be interacting with digital assets hundreds of times a second, and state updates need to be propagated and agreed upon by the network very quickly. A blockchain-enabled ticketing system is practically useless if a user has to wait for "three block confirmations" before the bouncer will let her into a venue. Users expect near-instant state updates because centralized solutions offer them that today.
Therefore the Tari DAN must offer speed and scalability.
Multiple Layers
The distributed system trilemma tells us that these requirements are mutually exclusive.
We can't have fast, cheap digital assets and also highly secure and decentralized currency tokens on a single system.
Tari overcomes this constraint by building two layers:
- A base layer that provides a public ledger of Tari coin transactions, secured by PoW to maximize security.
- A DAN consisting of multiple independent sidechains that each manage the state of a digital asset. It is very fast and cheap, at the expense of decentralization.
If required, the digital assets layer can refer back to the base layer to temporarily give up speed in exchange for increased security. These commitments allow token owners to make attestations based on their asset state without relying completely on the sidechain infrastructure. Furthermore, this link to the base layer can be used to resolve consensus issues on the digital assets layer that may crop up from time to time as a result of the lower degree of decentralization.
Base Layer
Refer to RFC-0100/BaseLayer for more detail.
The Tari base layer has the following primary features:
- PoW-based blockchain using Nakamoto consensus
- Transactions and blocks based on the Mimblewimble protocol
Mimblewimble is an exciting new blockchain protocol that offers some key advantages over other UTXO-based cryptocurrencies such as Bitcoin:
- Transactions are private. This means that casual observers cannot ascertain the amounts being transferred or the identities of the parties involved.
- Mimblewimble employs a novel blockchain "compression" method called cut-through, which dramatically reduces the storage requirements for blockchain nodes.
- Multi-signature transactions can be easily aggregated, making such transactions very compact, and completely hiding the parties involved, or the fact that there were multiple parties involved at all.
"Mimblewimble is the most sound, scalable 'base layer' protocol we know" -- @fluffypony
Proof of Work
There are a few options for the PoW mechanism for Tari:
- Implement an existing PoW mechanism. This is a bad idea, because a nascent cryptocurrency that uses a non-unique mining algorithm is incredibly vulnerable to a 51% attack from miners from other currencies using the same algorithm. Bitcoin Gold and Verge have already experienced this, and it's a matter of time before it happens to others.
- Implement a unique PoW algorithm. This is a risky approach and comes close to breaking the number one rule of cryptocurrency design: never roll your own crypto.
- Merged mining. This approach is not without its own risks, but offers the best trade-offs in terms of bootstrapping the network. It typically provides high levels of hash rate from day one, along with 51% attack resistance, assuming mining pools are well-distributed.
- A hybrid approach, utilizing two or more of the above mechanisms.
Given Tari's relationship with Monero, a merged-mining strategy with Monero makes the most sense. However, the PoW mechanism SHOULD be written in a way that makes it relatively easy to code, implement and switch to a different strategy in the future. More information on Tari's current approach can be found in RFC-0131_Mining.
The mining strategy is explained more thoroughly in RFC-130.
Digital Assets Network
A more detailed proposal for the DAN is presented in RFC-0300/DAN. Digital assets are discussed in more detail in RFC-0310/Assets.
The DAN is focused on achieving high speed and scalability, without compromising on security. To achieve this we make the explicit trade-off of sacrificing decentralization. Generally, the primary parties that have a stake in the security of a given digital asset are the Asset Issuer and Token owners. This fact points to a natural centralization of control of an asset by the Asset Issuer.
Digital Assets consist of a set of tokens and their associated state. The state of an asset's tokens will be managed on a sidechain that will run in parallel to the Tari base layer. The consensus mechanism, ledger style and other characteristics of the sidechain will be chosen and managed by the Asset Issuer.
There are many options for the nature of these sidechains. These are still under discussion but it will be possible to run multiple types of sidechains in the DAN and an Asset Issuer can choose which best suits the asset type it is supporting.
Please refer to Tari Labs University (TLU) for detailed discussions on layer 2 scaling solutions and consensus mechanisms.
Interaction between Base Layer and Digital Assets Network
The base layer provides supporting services to the DAN. In general, the base layer only knows about Tari coin transactions. It knows nothing about the details of any digital assets and their state.
This is by design: the network cannot scale if details of digital asset contracts have to be tracked on the base layer. We envisage that there could be tens of thousands of contracts deployed on Tari. Some of those contracts may be enormous; imagine controlling every piece of inventory and their live statistics for a massively multiplayer online role-playing game (MMORPG). The base layer is also too slow. If any state relies on base layer transactions being confirmed, there is an immediate lag before that state change can be considered final, which kills the liveness properties we seek for the DAN.
It is better to keep the two networks almost totally decoupled from the outset, and allow each network to play to its strength.
That said, there are key interactions between the two layers. The base layer is a ledger and can be used as a source of truth for the DAN. Asset sidechains will periodically commit to their state on the base layer. These commitments make it possible for token owners to make attestations about their tokens at certain points in time without relying on the sidechain. These commitments can also be used as a final court of appeal in the case of consensus disputes.
The interplay between base layer and DAN is what incentivizes every actor in the system to maintain an efficient and well-functioning network, even while acting in their own self-interest.
Summary
The following table summarizes the defining characteristics of the Tari network layers:
Base Layer | Digital Assets Network | |
---|---|---|
Speed | Slow | Fast |
Scalability | Moderate | Very high |
Security | High | Moderate |
Decentralization | High | Low - Med |
Processes digital asset instructions | Only checkpoints | Yes |
RFC-0010/CodeStructure
Tari Code Structure and Organization
Maintainer(s): Cayle Sharrock and Byron Hambly
Licence
Copyright 2018 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to describe and explain the Tari codebase layout.
Related Requests for Comment
None.
Description
The code follows a Domain-driven Design (DDD) layout, with top-level directories falling into infrastructure, domain and application layers.
Infrastructure Layer
The infrastructure layer provides a set of crates that have general infrastructural utility. The rest of the Tari codebase can make use of these crates to obtain persistence, communication and cryptographic services. The infrastructure layer doesn't know anything about blockchains, transactions or digital assets.
We recommend that code in this layer generalizes infrastructure services behind abstraction layers as much as is reasonable, so that specific implementations can be swapped out with relative ease.
Domain Layer
The domain layer houses the Tari "business logic". All protocol-related concepts and procedures are defined and implemented here.
This means that any and all terms defined in the Glossary will have a software implementation here, and only here. They can be used in the application layer, but must be implemented in the domain layer.
The domain layer can make use of crates in the infrastructure layer to achieve its goals.
Application Layer
In the application layer, applications build on top of the domain layer to produce the executable software that is deployed as part of the Tari network.
As an example, the following base layer applications may be developed as part of the Tari protocol release:
- A base node executable (tari_base_node)
- A Command Line Interface (CLI) wallet for the Tari cryptocurrency (tari_console_wallet)
- A standalone miner (tari_mining_node)
- A mining proxy to enable merge mining Monero (tari_merge_mining_proxy)
- An Application Programming Interface (API) server for the base node (REST, gRPC, etc.)
Code Layout
- Tari Protocol
Github: tari-project/tari
The Tari Protocol code repository is a Rust workspace consisting of multiple packages. A package is a set of crates, which is the source code of a binary or library.
The source code is organized into the following directories.
-
applications
contains crates for all the application-layer executables that form part of the Tari codebase.tari_base_node
- the Base Node applicationtari_console_wallet
- the CLI Wallet applicationtari_mining_node
- the SHA3 Mining Node (CPU)tari_merge_mining_proxy
- the Merge Mining Proxytari_explorer
- a local web based block explorer
-
base_layer
is the fundamental domain-layer directory and contains multiple packages and crates.core
- core classes and traits, such as Transactions, Blocks, and consensus, mempool, and blockchain database code;key_manager
- construction and storage of key derivations and mnemonic seed phrases;mmr
- an independent implementation of a Merkle Mountain Range;p2p
- the block and transaction propagation module;service_framework
- asynchronous service stack builder;wallet
- a wallet library including services and storage classes to create Tari wallets;wallet_ffi
- a Foreign Function Interface (FFI) library to create Tari wallets in other programming languages;
-
comms
is the networking and messaging subsystem, used across the base layer and applications; -
infrastructure
contains application-layer code and is not Tari-specific. It holds the following crates:derive
- a crate to containderive(...)
macros;shutdown
- a convenient way for threads to let each other know to stop working;storage
- data persistence services, including a Lightning Memory-mapped Database (LMDB) persistence implementation;
-
other utility and test libraries.
- Tari Cryptography
Github: tari-project/tari-crypto
Tari Crypto was refactored into its own crate, for ease of use and integration across different projects. It includes all cryptographic services, including a Curve25519 implementation.
RFC-0100/BaseLayer
The Tari Base Layer
Maintainer(s): Cayle Sharrock
Licence
Copyright 2018 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to describe the major software components of the Tari Base Layer network.
Related Requests for Comment
- RFC-0001: Overview
- RFC-0110: Base Nodes
- RFC-0120: Consensus rules
- RFC-0130: Mining
- RFC-0150: Wallets
Description
The Tari Base Layer network comprises the following major pieces of software:
- Base Layer full node implementation. The base layer full nodes are the consensus-critical pieces of software for the Tari base layer and cryptocurrency. The base nodes validate and transmit transactions and blocks, and maintain consensus about the longest valid proof-of-work blockchain.
- Mining software. Mining nodes perform proof-of-work to secure the base layer and compete to submit the
next valid block into the Tari blockchain. Tari uses two Proof of Work (PoW) algorithms, the first is merge-mined with Monero and a second native SHA3 PoW.
The Tari source provides three alternatives for Tari miners:
- A standalone miner for SHA3 mining
- A merge-mining proxy to be used with XMRig to merge mine Tari with Monero
- A stratum-compatible pool miner.
- Wallet software. Client software and Application Programming Interfaces (APIs) offering means to construct transactions, query nodes for information and maintain personal private keys.
These three major pieces of software make use of common functionality provided by the following libraries within the Tari project source code:
- Local data storage
- Cryptography services
- Peer-to-peer networking and messaging services
RFC-0010 provides more detail on how the source code is structured within the Tari codebase.
RFC-0110/BaseNodes
Base Layer Full Nodes (Base Nodes)
Maintainer(s): Cayle Sharrock, S W van heerden and Stanley Bondi
Licence
Copyright 2019 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to describe the roles that base nodes play in the Tari network as well as their general approach for doing so.
Related Requests for Comment
$$ \newcommand{\so}{\gamma} % script offset $$
Description
Broad Requirements
Tari Base Nodes form a peer-to-peer network for a proof-of-work based blockchain running the Mimblewimble protocol. The proof-of-work is performed via hybrid mining, that is merge mining with Monero and stand-alone SHA 3. Arguments for this design are presented in the overview.
Tari Base Nodes MUST carry out the following tasks:
- validate all Tari coin transactions;
- propagate valid transactions to peer nodes;
- validate all new blocks received;
- propagate validated new blocks to peer nodes;
- connect to peer nodes to catch up (sync) with their blockchain state;
- provide historical block information to peers that are syncing.
Once the Digital Assets Network (DAN) goes live, Base Nodes will also need to support the tasks described in RFC-0300_DAN. These requirements are omitted for the moment.
To carry out these tasks effectively, Base Nodes SHOULD:
- save the blockchain into an indexed local database;
- maintain an index of all Unspent Transaction Outputs (UTXOs);
- maintain a list of all pending, valid transactions that have not yet been mined (the mempool);
- manage a list of Base Node peers present on the network.
Tari Base Nodes MAY implement chain pruning strategies that are features of Mimblewimble, including transaction cut-through and block compaction techniques.
Tari Base Nodes MAY also implement the following services via an Application Programming Interface (API) to clients:
- Block queries
- Kernel data queries
- Transaction queries
- Submission of new transactions
Such clients may include "light" clients, block explorers, wallets and Tari applications.
Transaction Validation and Propagation
Base nodes can be notified of new transactions by:
- connected peers;
- clients via APIs.
When a new transaction has been received, it is then passed to the mempool service where it will be validated and either stored or rejected.
The transaction is validated as follows:
- All inputs to the transaction are valid UTXOs in the UTXO set or are outputs in the current block.
- No inputs are duplicated.
- All inputs are able to be spent (they are not time-locked).
- All inputs are signed by their owners.
- All outputs have valid range proofs.
- No outputs currently exist in the current UTXO set.
- The transaction does not have timelocks applied, limiting it from being mined and added to the blockchain before a specified block height or timestamp has been reached.
- The transaction excess has a valid signature.
- The transaction weight does not exceed the maximum permitted in a single block as defined by consensus.
- The transaction excess is a valid public key. This proves that: $$ \Sigma \left( \mathrm{inputs} - \mathrm{outputs} - \mathrm{fees} \right) = 0 $$.
- The transaction excess has a unique value across the whole chain.
- The Tari script of each input must execute successfully and return the public key that signs the script signature.
- The script offset \( \so\) is calculated and verified as per RFC-0201_TariScript.
Rejected transactions are dropped silently.
Timelocked transactions are rejected by the mempool. The onus is on the client to submit transactions once they are able to be spent.
Note: More detailed information is available in the timelocks RFC document.
Valid transactions are:
- added to the mempool;
- forwarded to peers using the transaction BroadcastStrategy.
Block/Transaction Weight
The weight of a transaction / block measured in "grams". Input, output and kernel weights reflect their respective relative storage and computation cost. Transaction fees are typically proportional to a transaction body's total weight, creating incentive to reduce the size of the UTXO set.
Given the target block size of S
and the choice for 1 gram to represent N
bytes, we end up with
a maximum block weight of S/N
grams.
To illustrate (these values should not be considered authoritative), with an S
of 1MiB and N
of 16, the block and
transaction body weights are as follows:
| | Byte size | Natural Weight | Adjust | Final | |------------------- |----------- |------------------------ |-------- |------------------------ | | Output | | | | | | - Per output | 832 | 52 | 0 | 52 | | - Tari Script | variable | size_of(script) / 16 | 0 | size_of(script) / 16 | | - Output Features | variable | size_of(features) / 16 | 0 | size_of(features) / 16 | | Input | 169 | 11 | -2 | 9 | | Kernel size | 113 | 8 | 2 | 10 |
Pseudocode:
output_weight = num_outputs * PER_OUTPUT_GRAMS(53)
foreach output in outputs:
output_weight += serialize(output.script) / BYTES_PER_GRAM
output_weight += serialize(output.features) / BYTES_PER_GRAM
input_weight = num_inputs * PER_INPUT_GRAMS(9)
kernel_weight = num_kernels * PER_KERNEL_GRAMS(10)
weight = output_weight + input_weight + kernel_weight
where the capitalized values are hard-coded constants.
Block Validation and Propagation
The block validation and propagation process is analogous to that of transactions. New blocks are received from the peer-to-peer network, or from an API call if the Base Node is connected to a Miner.
When a new block is received, it is passed to the block validation service. The validation service checks that:
- The block has not been processed before.
- Every transaction in the block is valid.
- The proof-of-work is valid.
- The block header is well-formed.
- The block is being added to the chain with the highest accumulated proof-of-work.
- It is possible for the chain to temporarily fork; Base Nodes SHOULD store orphaned forks up to some configured depth.
- It is possible that blocks may be received out of order. Base Nodes SHOULD keep blocks that have block heights greater than the current chain tip for some preconfigured period.
- The sum of all excesses is a valid public key. This proves that: $$ \Sigma \left( \mathrm{inputs} - \mathrm{outputs} - \mathrm{fees} \right) = 0$$.
- That all kernel excess values are unique for that block and the entire chain.
- Check if a block contains already spent outputs, reject that block.
- The Tari script of every input must execute successfully and return the public key that signs the script signature.
- The script offset \( \so\) is calculated and verified as per RFC-0201_TariScript. This prevents cut-through from being applied.
Because Mimblewimble blocks can simply be seen as large transactions with multiple inputs and outputs, the block validation service checks all transaction verification on the block as well.
Rejected blocks are dropped silently.
Base Nodes are not obliged to accept connections from any peer node on the network. In particular:
- Base Nodes MAY refuse connections from peers that have been added to a denylist.
- Base Nodes MAY be configured to exclusively connect to a given set of peer nodes.
Validated blocks are
- added to the blockchain;
- forwarded to peers using the block BroadcastStrategy.
In addition, when a block has been validated and added to the blockchain:
- The mempool MUST also remove all transactions that are present in the newly validated block.
- The UTXO set MUST be updated by removing all inputs in the block, and adding all the new outputs into it.
Synchronizing and Pruning of the Chain
Syncing, pruning and cut-through are discussed in detail in RFC-0140.
Archival Nodes
Archival nodes are used to keep a complete history of the blockchain since genesis block. They do not employ pruning at all. These nodes will allow full syncing of the blockchain, because normal nodes will not keep the full history to enable this. These nodes must sync from another archival node.
Pruned Nodes
[Pruned nodes] take advantage of the cryptography of mimblewimble to allow them to prune spent inputs and outputs beyond the pruning horizon and still validate the integrity of the blockchain i.e. no coins were destroyed or created beyond what is allowed by consensus rules. A sufficient number of blocks back from the tip should be configured because reorgs are no longer possible beyond that horizon. These nodes can sync from any other base node (archival and pruned).
RFC-0111/BaseNodesArchitecture
Base Node Architecture
Maintainer(s): Cayle Sharrock, Philip Robinson
Licence
Copyright 2021 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to describe the high-level Base Node architecture.
Architectural Layout
The Base Node architecture is designed to be modular, robust and performant.
The major components are separated into separate modules. Each module exposes a public Application Programming Interface (API), which communicates with other modules using asynchronous messages via futures.
Base Node Service
The Base Node Service fields requests for the local nodes chain state and also accepts newly mined blocks that are propagating across the network. The service subscribes to NewBlock and BaseNodeRequest messages via the P2P comms interface. These messages are propagated across the P2P network and can also be received directly from other nodes. The service also provides a local interface to its functionality via an asynchronous Request-Response API.
The P2P message types this service subscribes to are:
-
NewBlock: A newly mined block is being propagated over the network. If the node has not seen the block before, the node will validate it. Its action depends on the validation outcome:
- Invalid block - drop the block.
- Valid block appending to the longest chain - add the block to the local state; propagate the block to peers.
- Valid block forking off main chain - add the block to the local state; propagate the block to peers.
- Valid block building off unknown block - add the orphan block to the local state.
-
BaseNodeServiceRequest: A collection of requests for chain data from the node.
Base Node State Machine Service
This service is essentially a finite state machine that synchronises its blockchain state with its peers. When the state machine decides it needs to synchronize its chain state with a peer it uses the Base Node Sync RPC service to do so. The RPC service allows for streaming of headers and blocks in a far more efficient manner than using the P2P messaging.
This service does not provide a local API but does provide an event stream and Status Info watch channel for other modules to subscribe to.
Mempool and Mempool Sync Services
The mempool service tracks valid transactions that the node knows about, but that have not yet been included in a block. The mempool is ephemeral and non-consensus critical, and as such may be a memory-only data structure. Maintaining a large mempool is far more important for Base Nodes serving miners than those serving wallets. The mempool structure itself is a set of hash maps as described in RFC-0190
When the node reboots the Mempool sync service will contact peers and sync valid mempool transactions from them. After it has synced this service runs to field such requests from other peers.
The Mempool service handles Mempool Service Requests which it can receive from the P2P comms stack via its subscriptions, via the Mempool RPC service and via an internal Request-Response API. All these interfaces provide the following calls:
- SubmitTransaction: Submit a transaction to be validated and included in the mempool. If the transaction is invalid it will be rejected with a reason.
- GetTxStateByExcess: Request the state of a transaction if it exists in the mempool using its excess signature
- GetStats and getState: Request information about the current status of the mempool.
Liveness Service
The Liveness service can be used by other modules to test the liveness of a specific peer and also periodically tests a
set of its connected peers for liveness. This service subscribes to Ping
P2P messages and responds with Pong
s. The
service gathers data about the monitored peer's liveness such as its latency. The Ping
and Pong` messages also contain
a copy of this nodes current Chain Metadata for use by the receiving nodes Chain Metadata Service.
Chain Metadata Service
The Chain Metadata Service maintains this nodes current Chain Metadata state to be sent out via Ping
and Pong
messages by the Liveness service. This node also monitors the Chain Metadata received from other peers in the Ping
and
Pong
messages received by the Liveness service. Once a full round of Pong
messages are received this service will
emit this data as an event which the Base Node State Machine monitors.
Distributed Hash Table (DHT) Service
Peer discovery is a key service that blockchain nodes provide so that the peer mesh network can be navigated by the full nodes making up the network.
In Tari, the peer-to-peer network is not only used by full nodes (Base Nodes), but also by Validator Nodes, and
Tari and Digital Assets Network (DAN) clients.
For this reason, peer management is handled internally by the Comms layer. If a Base Node wants to propagate a message,
new block or transaction, for example, it simply selects a BROADCAST
strategy for the message and the Comms layer
will do the rest.
When a node wishes to query a peer for its peer list, this request will be handled by the DHTService
. It will
communicate with its Comms module's Peer Manager, and provide that information to the peer.
Blockchain Database
The blockchain database module is responsible for providing a persistent storage solution for blockchain state data. This module is used by the Base Node Service, Base Node State Machine, Mempool Service and the RPC servers. For Tari, this is delivered using the Lightning Memory-mapped Database (LMDB). LMDB is highly performant, intelligent and straightforward to use. An LMDB is essentially treated as a hash map data structure that transparently handles memory caching, disk Input/Output (I/O) and multi-threaded access. This module is shared by many services and so must be thread-safe.
Communication Interfaces
P2P communications
The Tari Peer to Peer messaging protocol is defined in RFC-0172. It is a fire-and-forget style protocol. Messages can be sent directly to a known peer, sent indirectly to an offline or unknown peer and broadcast to a set of peers. When a message is sent to specific peer it is propagated to the peers local neighbourhood and stored by those peers until it comes online to receive the message. Messages that are broadcast will be propagated around the network until the whole network has received them, they are not stored.
RPC Services
Fire-and-forget messaging is not efficient for point to point communications between online peers. For these applications the Base Node provides RPC services that present an API for clients to interact with. These RPC services provide a Request-Response interface defined by Profobuf for clients to use. RPC also allows for streaming of data which is much more efficient when transferring large amounts of data.
Examples of RPC services running in Base Node are:
- Wallet RPC service: An RPC interface containing methods used by wallets to submit and query transactions on a Base Node
- Base Node Sync RPC Service: Used by the Base Node State Machine Service to synchronize blocks
- Mempool RPC Service: Provides the Mempool Service API via RPC
gRPC Interface
Base Nodes need to provide a local communication interface in addition to the P2P and RPC communication interface. This is best achieved using gRPC. The Base Node gRPC interface provides access to the public API methods of the Base Node Service, the mempool module and the blockchain state module, as discussed above.
gRPC access is useful for tools such as local User Interfaces (UIs) to a running Base Node; client wallets running on the same machine as the Base Node that want a more direct communication interface to the node than the P2P network provides; third-party applications such as block explorers; and, of course, miners.
A non-exhaustive list of methods the base node module API will expose includes:
- Blockchain state calls, including:
- checking whether a given Unspent Transaction Output (UTXO) is in the current UTXO set;
- requesting the latest block height;
- requesting the total accumulated work on the longest chain;
- requesting a specific block at a given height;
- requesting the Merklish root commitment of the current UTXO set;
- requesting a block header for a given height;
- requesting the block header for the chain tip;
- validating signatures for a given transaction kernel;
- validating a new block without adding it to the state tree;
- validating and adding a (validated) new block to the state, and informing of the result (orphaned, fork, reorg, etc.).
- Mempool calls
- The number of unconfirmed transactions
- Returning a list of transaction ranked by some criterion (of interest to miners)
- The current size of the mempool (in transaction weight)
- Block and transaction validation calls
- Block synchronisation calls
RFC-0120/Consensus
Base Layer Consensus
Maintainer(s): Cayle Sharrock, S W van heerden and Stanley Bondi
Licence
Copyright 2019 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to describe the fields that a block should contain as well as all consensus rules that will determine the validity of a block.
Related Requests for Comment
Description
Blockchain consensus is a set of rules that a majority of nodes agree on that determines the state of the blockchain.
This RFC details the consensus rules for the Tari network.
Blocks
Every block MUST:
-
have exactly one valid block header, as per the Block Headers section
-
have exactly one coinbase transaction
-
have a total transaction weight less than the consensus maximum
-
be able to calculate matching Merkle roots (kernel_mr, output_mr, witness_mr, and input_mr)
-
each transaction input MUST:
- spend an existing valid UTXO
- have a maturity greater than the current block height
- be in a canonical order (see Transaction ordering)
-
each transaction output MUST:
- have a unique hash (
features || commitment || script
) - have a unique commitment in the current UTXO set
- be in a canonical order (see Transaction ordering)
- have a valid range proof
- have a valid metadata signature
- have a valid script offset (\gamma), as per RFC-0201_TariScript.
- have a unique hash (
-
each [transaction kernel] MUST
- have a valid kernel signature
- have a unique excess
-
the transaction commitments and kernels MUST balance, as follows:
$$ \begin{align} &\sum_i\mathrm{Cout_{i}} - \sum_j\mathrm{Cin_{j}} + \text{fees} \cdot H \stackrel{?}{=} \sum_k\mathrm{K_k} + \text{offset} \\ & \text{for each output}, i, \\ & \text{for each input}, j, \\ & \text{for each kernel excess}, k \\ & \text{and }\textit{offset }\text{is the total kernel offset} \\ \end{align} \tag{1} $$
If a block does not conform to the above, the block SHOULD be discarded and MAY ban the peer that sent it.
Coinbase
A coinbase transaction contained in a block MUST:
- be the only transaction in the block with the coinbase flag
- consist of exactly one output and one kernel (no input)
- have a valid kernel signature
- have a value exactly equal to the emission at the block height it was minted (see emission schedule) plus the total transaction fees within the block
- have a lock-height as per consensus
Block Headers
Every block header MUST contain the following fields:
- version;
- height;
- prev_hash;
- timestamp;
- output_mr;
- output_mmr_size;
- input_mr;
- witness_mr;
- kernel_mr;
- kernel_mmr_size;
- total_kernel_offset;
- script_kernel_offset;
- nonce;
- pow.
The block header MUST conform to the following:
- The nonce and PoW must be valid for the block header.
- The achieved difficulty MUST be greater than or equal to the target difficulty.
- The FTL and MTP rules, detailed below.
The Merkle roots are validated as part of the full block validation, detailed in Blocks.
If the block header does not conform to any of the above, the block SHOULD be rejected and MAY ban the peer that sent it.
Version
This is the version currently running on the chain.
The version MUST conform to the following:
- It is represented as an unsigned 16-bit integer.
- Version numbers MUST be incremented whenever there is a change in the blockchain schema starting from 1.
Height
A counter indicating how many blocks have passed since the genesis block (inclusive).
The height MUST conform to the following:
- Represented as an unsigned 64-bit integer.
- The height MUST be exactly one more than the block referenced in the
prev_hash
block header field. - The genesis block MUST have a height of 0.
Prev_hash
This is the hash of the previous block's header.
The prev_hash MUST conform to the following:
- represented as an array of unsigned 8-bit integers (bytes) in little-endian format.
- MUST be a hash of the entire contents of the previous block's header.
Timestamp
This is the timestamp at which the block was mined.
The timestamp MUST conform to the following:
Output_mr
The output_mr
MUST be calculated as follows: Hash (TXO MMR root
|| Hash(spent TXO bitmap
)).
The TXO MMR root
is the MMR root that commits to every transaction output that has ever existed since
the genesis block.
The spent TXO bitmap
is a compact serialized roaring bitmap containing all the output MMR leaf indexes
of all the outputs that have ever been spent.
The output_mr MUST conform to the following:
- Represented as an array of unsigned 8-bit integers (bytes) in little-endian format.
- The hashing function used MUST be blake2b with a 256-bit digest.
Output_mmr_size
This is the total size of the leaves in the output Merkle mountain range.
The Output_mmr_size MUST conform to the following:
- Represented as a single unsigned 64-bit integer.
Input_mr
This is the Merkle root of all the inputs in the block, which consists of the hashed inputs. It is used to prove that all inputs are correct and not changed after mining. This MUST be constructed by adding, in order, the hash of every input contained in the block.
The input_mr MUST conform to the following:
- Represented as an array of unsigned 8-bit integers (bytes) in little-endian format.
- The hashing function must be blake2b with a 256-bit digest.
Witness_mr
This is the Merkle root of the output witness data, specifically all created outputs’ range proofs and
metadata signatures. This MUST be constructed by
Hash ( RangeProof
|| metadata commitment signature
), in order, for every output contained in the block.
The witness_mr MUST conform to the following:
- Represented as an array of unsigned 8-bit integers (bytes) in little-endian format.
- The hashing function used must be blake2b with a 256-bit digest.
Kernel_mr
This is the Merkle root of the outputs.
The kernel_mr MUST conform to the following:
- Must be transmitted as an array of unsigned 8-bit integers (bytes) in little-endian format.
- The hashing function used must be blake2b with a 256-bit digest.
Kernel_mmr_size
This is the total size of the leaves in the kernel Merkle mountain range.
The Kernel_mmr_size MUST conform to the following:
- Represented as a single unsigned 64-bit integer.
Total_kernel_offset
This is the total summed offset of all the transactions in this block.
The total_kernel_offset MUST conform to the following:
- Must be transmitted as an array of unsigned 8-bit integers (bytes) in little-endian format
Total_script_offset
This is the total summed script offset of all the transactions in this block.
The total_script_offset MUST conform to the following:
- Must be transmitted as an array of unsigned 8-bit integers (bytes) in little-endian format
Total_difficulty
This is the total accumulated difficulty of the mined chained.
The total_difficulty MUST conform to the following:
- Must be transmitted as an unsigned 64-bit integer.
- MUST be larger than the previous block's
total_difficulty
. - meet the difficulty target for the block as determined by the consensus difficulty algorithm.
Nonce
This is the nonce used in solving the Proof of Work.
The nonce MUST conform to the following:
- Must be transmitted as an unsigned 64-bit integer;
PoW
This is the Proof of Work algorithm used to solve the Proof of Work. This is used in conjunction with the Nonce.
The [PoW] MUST contain the following:
- accumulated_monero_difficulty as an unsigned 64-bit integer.
- accumulated_blake_difficulty as an unsigned 64-bit integer.
- pow_algo as an enum (0 for Monero, 1 for Sha3).
- pow_data as an array of unsigned 8-bit integers (bytes) in little-endian format.
Difficulty Calculation
The target difficulty represents how difficult it is to mine a given block. This difficulty is not fixed and needs to constantly adjust to changing network hash rates.
The difficulty adjustment MUST be calculated using a linear-weighted moving average (LWMA) algorithm (2) $$ \newcommand{\solvetime}{ \mathrm{ST_i} } \newcommand{\solvetimemax}{ \mathrm{ST_{max}} } $$
Symbol | Value | Description |
---|---|---|
N | 90 | Target difficulty block window |
T | SHA3: 300 Monero: 200 | Target block time in seconds. The value used depends on the PoW algorithm being used. |
\( \solvetimemax \) | SHA3: 1800 Monero: 1200 | Maximum solve time. This is six times the target time of the current PoW algorithm. |
\( \solvetime \) | variable | The timestamp difference in seconds between block i and i - 1 where \( 1 \le \solvetime \le \solvetimemax \) |
\( \mathrm{D_{avg}} \) | variable | The average difficulty of the last N blocks |
$$ \begin{align} & \textit{weighted_solve_time} = \sum\limits_{i=1}^N(\solvetime*i) \\ & \textit{weighted_target_time} = (\sum\limits_{i=1}^Ni) * \mathrm{T} \\ & \textit{difficulty} = \mathrm{D_{avg}} * \frac{\textit{weighted_target_time}}{\textit{weighted_solve_time}}\\ \end{align} \tag{2} $$
It is important to note that the two proof of work algorithms are calculated independently. i.e., if the current block uses SHA3 proof of work, the block window and solve times only include SHA3 blocks and vice versa.
FTL
The Future Time Limit. This is how far into the future a time is accepted as a valid time. Any time that is more than the FTL is rejected until such a time that it is not less than the FTL. The FTL is calculated as (T*N)/20 with T and N defined as: T: Target time - This is the ideal time that should pass between blocks that have been mined. N: Block window - This is the number of blocks used when calculating difficulty adjustments.
MTP
The Median Time Passed (MTP) is the lower bound calculated by taking the median average timestamp of the last N blocks. Any block with a timestamp that is less than MTP will be rejected.
Total accumulated proof of work
This is defined as the total accumulated proof of work done on the blockchain. Tari uses two independent proof of work algorithms rated at different difficulties. To compare them, we simply multiply them together into one number: $$ \begin{align} \textit{accumulated_monero_difficulty} * \textit{accumulated_sha_difficulty} \end{align} \tag{3} $$ This value is used to compare chain tips to determine the strongest chain.
Transaction Ordering
The order in which transaction inputs, outputs, and kernels are added to the Merkle mountain range completely changes the final Merkle root. Input, output, and kernel ordering within a block is, therefore, part of the consensus.
The block MUST be transmitted in canonical ordering. The advantage of this approach is that sorting does not need to be done by the whole network, and verification of sorting is exceptionally cheap.
Transaction outputs are sorted lexicographically by the byte representation of their Pedersen commitment i.e. \\(k \cdot G + v \cdot H\\)
.
Transaction inputs are sorted lexicographically by the hash of the output that is spent by the input.
RFC-0121/Consensus Encoding
Consensus Encoding
Maintainer(s): Stanley Bondi
Licence
Copyright 2022 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to describe the encoding used for various consensus-critical data types, as well as the construction of hash pre-images and signature challenges used in base-layer consensus.
Related Requests for Comment
Description
A Tari base node must validate each block containing a block header as well as set of transaction inputs, transaction outputs and transaction kernels, each containing a number of fields pertinent to their function within the [base layer]. The data contained within these structures needs to be consistently encoded (represented as bytes) across platforms and implementations so that the network can agree on a single correct state.
This RFC defines the low-level specification for how these data types MUST be encoded to construct a valid hash and signature on the Tari network.
Consensus Encoding
The primary goal of consensus encoding is to provide a consistent data format that is committed to in hashes and signatures.
Consensus encoding defines what "raw" data is included in the encoding, the order in which it should appear and the length for variable length elements. To keep encoding as simple as possible, no type information, field names etc. are catered for in the format as this is always statically known. This is particularly appropriate for hashes and signatures where many fields must be consistently represented and concatenated together.
The rest of this section defines some encodings for common primitives used in the Tari codebase.
Unsigned integer encoding
Varint encoding is used for integer fields greater than 1 byte. Describing varint is out of scope for this RFC but there are many resources online to understand this fairly basic encoding. The only rule we apply is that the encoding has a limit of 10 bytes, a little more than what is required to store a 64-bit integer.
Dynamically-sized vec encoding
This type refers to a contiguous block of data of any length. Because the size is dynamic, the size is included in the encoding.
|len(data)| data for type | data for type | ...
Fixed size arrays
If the size of the array is constant (static). The length is omitted and the data is encoded.
| data for type | ...
Optional or nullable encoding
An optional field starts with a 0x00 byte to indicate the value is not provided (None
, null
, nil
etc) or a 0x01 byte
to indicate that the value is provided followed by the encoding of the value.
| 0 or 1 | encoding for type |
Ristretto Keys
RistrettoPublicKey
and RistrettoPrivateKey
types defined in the tari_crypto
crate both have 32-byte canonical formats
and are encoded as a 32-byte fixed array.
The tari_crypto
Rust crate provides an FFI interface that allows
generating of the canonical byte formats in any language that supports FFI.
Commitment
A commitment is a RistrettoPublicKey and so has identical encoding.
Schnorr Signature
See the TLU on Schnorr Signatures
A Schnorr signature tuple is <R, s>
where R
is a RistrettoPublicKey and s
is a the signature scalar wrapped in RistrettoPrivateKey.
The encoding is fixed at 64-bytes:
| 32-byte public key | 32-byte scalar |
Commitment Signature
A commitment signature tuple consists of a <R, u, v>
where R
is the Pederson commitment \(r_u.G + r_v.H\)
for the signature scalars u
and v
.
The encoding is fixed at 96-bytes:
| 32-byte commitment (R) | 32-byte scalar (u) | 32-byte scalar (v) |
Example
Given the following data and types:
{
// Type: Fixed array of 5 bytes
short_id: [1,2,3,4,5],
// Type: variable length bytes
name: Buffer.from("Case"),
// Type: unsigned integer
age: 40,
// Type: struct
details: {
// Type: variable length bytes
kind: Buffer.from("Hacker"),
},
// Type: nullable varint
dob: null
}
Encoded (hex) as follows:
short id | len | name | age | len | kind | null? | dob |
---|---|---|---|---|---|---|---|
0102030405 | 04 | 43617365 | 28 | 05 | 4861636b6572 | 00 |
Note that nested structs are flattened and the order must be preserved to allow decoding.
The 00
null byte is important so that for e.g. the kind
bytes cannot be manipulated to
produce the same encoding as non-null dob
.
Block Header
The block hash pre-image is constructed by first constructing the merge mining hash. Each encoding is concatenated in order as follows:
version
- 1 byteheight
- varintprev_hash
- fixed 32-bytestimestamp
- varintinput_mr
- fixed 32-bytesoutput_mmr_size
- varintwitness_mr
- fixed 32-byteskernel_mr
- fixed 32-byteskernel_mmr_size
- `varinttotal_kernel_offset
- 32-byte Scalar, see RistrettoPrivateKeytotal_script_offset
- 32-byte Scalar, see RistrettoPrivateKey
This pre-image is hashed and block hash is constructed, in order, as follows:
merge_mining_hash
- As abovepow_algo
- enumeration of types of PoW as a single unsigned byte, whereMonero = 0x00
andSha3 = 0x01
pow_data
- raw variable bytes (no length varint)nonce
- the PoW nonce,u64
converted to a fixed 8-byte array (little endian)
Output Features
pub struct OutputFeatures {
pub version: OutputFeaturesVersion,
pub maturity: u64,
pub flags: OutputFlags,
pub metadata: Vec<u8>,
pub unique_id: Option<Vec<u8>>,
pub parent_public_key: Option<PublicKey>,
pub asset: Option<AssetOutputFeatures>,
pub mint_non_fungible: Option<MintNonFungibleFeatures>,
pub sidechain_checkpoint: Option<SideChainCheckpointFeatures>,
}
Output features consensus encoding is defined as follows (in order):
version
- 1 unsigned byte. This should always be0x00
but is reserved for future proofing.maturity
- varintflags
- 1 unsigned bytemetadata
- dynamic vectorunique_id
- nullable + dynamic vectorparent_public_key
- nullable + 32-byte compressed public keyasset
- nullable + AssetOutputFeaturesmint_non_fungible
- nullable + MintNonFungibleFeaturessidechain_checkpoint
- nullable + SideChainCheckpointFeatures
AssetOutputFeatures
public_key
- RistrettoPublicKeytemplate_ids
- dynamic vector + varinttemplate_parameters
- dynamic vector
MintNonFungibleFeatures
asset_public_key
- RistrettoPublicKeyasset_owner_commitment
- RistrettoPublicKey
SideChainCheckpointFeatures
merkle_root
- fixed sized arraycommittee
- dynamic vector + RistrettoPublicKey
Transaction Output
pub struct TransactionOutput {
pub version: u8,
pub features: OutputFeatures,
pub commitment: Commitment,
pub proof: RangeProof,
pub script: TariScript,
pub sender_offset_public_key: PublicKey,
pub metadata_signature: ComSignature,
pub covenant: Covenant,
}
The canonical output hash is appended to the output Merkle tree and commits to the common data between an output
and the input spending that output i.e. output_hash = Hash(version | features | commitment | script | covenant)
.
The encoding is defined as follows:
version
- 1 bytefeatures
- OutputFeaturescommitment
- RistrettoPublicKeyscript
- byte length as varint + TariScriptcovenant
- byte length as varint + Covenant
Witness hash
The witness hash is appended to the witness Merkle tree.
proof
- Raw proof bytes encoded using dynamic vector encodingmetadata_signature
- [CommitementSiganture]
Metadata signature challenge
See Metadata Signature for details.
public_commitment_nonce
- RistrettoPublicKeyscript
- byte length as varint + TariScriptfeatures
- OutputFeaturessender_offset_public_key
- RistrettoPublicKeycommitment
- RistrettoPublicKeycovenant
- byte length as varint + Covenant
Transaction Input
The following struct represents the full transaction input data for reference. The actual input struct does not duplicate the output data to optimise storage and transmission of the input.
pub struct TransactionInput {
pub version: u8,
pub input_data: ExecutionStack,
pub script_signature: ComSignature,
// Output data
pub output_version: TransactionOutputVersion,
pub features: OutputFeatures,
pub commitment: Commitment,
pub script: TariScript,
pub sender_offset_public_key: PublicKey,
pub covenant: Covenant,
}
The transaction input canonical hash pre-image is constructed as follows:
input_version
- 1 byteoutput_hash
- See [TransactionOutput]sender_offset_public_key
- RistrettoPublicKeyinput_data
- TariScript Stackscript_signature
- [CommitementSiganture]
Script Challenge
For details see RFC-0201_TariScript.md.
The script challenge is constructed as follows:
nonce_commitment
- Commitmentscript
- TariScriptinput_data
- TariScript Stackscript_public_key
- RistrettoPublicKeycommitment
- Commitment
RFC-0130/Mining
Full-node Mining on Tari Base Layer
Maintainer(s): Hansie Odendaal, Philip Robinson
Licence
Copyright 2020 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
This document describes the final proof-of-work strategy proposal for Tari main net.
Related Requests for Comment
Description
The following proposal draws from many of the key points of debate from the Tari community on the topic of Tari’s main chain proof of work strategy. The early working assumption was that Tari would be 100% merged mined by Monero.
It would be nice to have a single, merge mined Proof of Work (PoW) algorithm, but the risks of hash rate attacks are real and meaningful. Double-spends and altering history can happen with >50% hash power, while selfish mining and eclipse attacks can happen with >33% hash power for a poorly connected attacker and >25% for a well-connected attacker (see Merged Mining: Analysis of Effects and Implications). Any non-merge mined PoW algorithm that is currently employed is even more vulnerable, especially if one can simply buy hash rate on platforms like NiceHash.
Hybrid mining is a strategy that apportions blocks across multiple PoW algorithms. If the hybrid algorithms are independent, then one can get at most x% of the total hash rate, where x is the fraction of blocks apportioned to that algorithm. As a result, the threat of a double-spend or selfish mining attack is mitigated, and in some cases eliminated.
This proposal puts forward Hybrid mining as the Tari PoW algorithm. However, some details still needed to be decided:
- the number of algorithms;
- the choice of algorithms;
- the block distribution;
- the difficulty adjustment strategy.
The number of algorithms
In hybrid mining, "independence" of algorithms is key. If the same mining hardware can be used on multiple PoW algorithms in the hybrid mining scheme, you may as well not bother with hybrid mining, because miners can simply switch between them.
In practice, no set of algorithms are truly independent. The best we can do is try to choose algorithms that work best on CPUs, GPUs, and ASICs. In truth, the distinction between GPUs and ASICs is only a matter of time. Any "GPU-friendly" algorithm is ASIC-friendly too; it's just a case of whether the capital outlay for fabricating them is worth it; and this will eventually become true for any algorithm that supplies PoW for a growing market cap. Employing merged mining with major players that use independent hardware introduces another degree of freedom, as long as those are independent, like RandomX with Monero, SHA-256 with Bitcoin and Scrypt with Litecoin.
Note: Merge mining does not add security per se, but it does add plenty of hash rate and continuity of the blockchain.
So really the answer to how many algorithms is: More than one, as independent as possible.
The choice of algorithms
A good technical choice would be merge mining with Monero, Bitcoin and Litecoin, if enough interest could be attracted from those mining communities. However, that would rule out any participation from Tari supporters and enthusiasts, at least in the early stages. So, to be inclusive of Tari supporters and enthusiasts, merge mining RandomX with Monero and another GPU/ASIC-friendly algorithm, like SHA3 also known as Keccak, is proposed. Using a custom configuration of such a simple and well understood algorithm means there is a low likelihood of unforeseen optimizations that will give a single miner a huge advantage. It also means that it stands a good chance of being "commoditized" when ASICs are eventually manufactured. This means that SHA3 ASICs will be widely available and not available from only a single supplier.
Edit: Handshake, which launched a few months ago, selected a Hashcash PoW algorithm (see #Consensus) using SHA3 and Blake2B for many of the same reasons: SHA3 is currently under-represented in PoW; SHA3 usage in combination with Blake2B in PoW creates a more level playing field for hardware manufacturers.
The block distribution
To reduce the chance of hash rate attacks, an even 50/50 distribution is needed, as discussed earlier. However, sufficient buy-in is needed, especially with regards to merge mining RandomX with Monero. To make it worthwhile for a Monero pool operator to merge mine Tari, but still guard against hash rate attacks and to be inclusive of independent Tari supporters and enthusiasts, a 60/40 split is proposed in favour of merge mining RandomX with Monero. The approaching Monero tail emission at the end of May 2022 should also make this a worthwhile proposal for Monero pool operators.
The difficulty adjustment strategy
The choice of difficulty adjustment algorithm is important. In typical hybrid mining strategies, each algorithm operates completely independently with a scaled-up target block time and is the most likely approach that any blockchain will take. Tari testnet has been running very successfully on Linear Weighted Moving Average (LWMA) from Bitcoin & Zcash Clones version 2018-11-27. This LWMA difficulty adjustment algorithm has also been tested in simulations and it proved to be a good choice in the multi-PoW scene as well.
Final proposal, hybrid mining details
The final proposal is summarized below:
- 2x mining algorithms, with average combined target block time at 120 s, to match Monero's block interval
- LWMA version 2018-11-27 difficulty algorithm adjustment for both with difficulty algo window of 90 blocks
- Algorithm 1: Monero merged mining
- at ~60% blocks distribution, based on block time setting of 192.0
- using RandomX, with
seed_hash
as arbitrary data, re-use restricted by age measured in Tari blocks
- Algorithm 2: Independent mining
- at ~40% blocks distribution, based on block time setting of 288.0 s
- SHA3-based algorithm, details to be fleshed out
RFC-0132/MergeMiningMonero
Tari protocol for Merge Mining with Monero
Maintainer(s): Stanley Bondi
Licence
Copyright 2020 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
This document describes the specific protocol Tari uses to merge mine with Monero.
Related Requests for Comment
Introduction
Tari employs a hybrid mining strategy, accepting 2 mining algorithms whose difficulties are independent of each other as discussed in RFC-0131_Mining.html. This RFC details a protocol to enable Tari to accept Monero proof of work, enabling participating miners a chance to produce a valid block for either or both chain without additional mining effort.
The protocol must enable a Tari base node to make the following assertions:
REQ 1. The achieved mining difficulty exceeds target difficulty as dictated by Tari consensus,
REQ 2. The Monero block was constructed after the current Tari tip block. This is to prevent a miner from submitting blocks from the parent chain that satisfy the auxiliary chain's difficulty without doing new work.
It's worth noting that a Tari base node never has to contact or download data from Monero to make these assertions.
Merge Mining on Tari
A new Tari block template is obtained from a Tari base node by calling the get_new_block_template
gRPC method, setting Monero
as the chosen PoW algorithm.
The Monero
algorithm must be selected so that the correct mining difficulty for the Monero algorithm is returned. Remember, that Monero and SHA difficulties
are independent (See RFC-0131_Mining.html). Next, a coinbase transaction is requested from a Tari Wallet for a give height by calling
the get_coinbase
gRPC function.
Next, the coinbase transaction is added to the new block template and passed back to the base node for the new MMR roots to be calculated.
Furthermore, the base node constructs a Blake256 hash of some of the Tari header fields. We'll call this hash the merge mining hash \( h_m \) that commits to
the following header fields in order: version
, height
,prev_hash
,timestamp
,output_mr
,range_proof_mr
,output_mmr_size
,kernel_mr
,
kernel_mmr_size
,total_kernel_offset
,total_script_offset
. Note, this hash does not include the pow
and nonce
fields, as these fields are set as part of mining.
To have the chance of mining a Monero block as well as a Tari block, we must obtain a new valid monero block template, by calling get_block_template.
This returns a blocktemplate_blob
, that is, a serialized Monero block containing the Monero block header, coinbase and a list of hashes referencing the
transactions included in the block. Additionally, a blockhashing_blob
is a fixed size blob containing serialized_monero_header
, merkle_tree_root
and
txn_count
concatenated together. The merkle_tree_root
is a merkle root of the coinbase + the transaction hashes contained in the block.
pub struct Block {
/// The block header
pub header: BlockHeader,
/// Coinbase transaction a.k.a miner transaction
pub miner_tx: Transaction,
/// References to the transactions included in this block
pub tx_hashes: Vec<hash::Hash>,
}
fig 1. The Monero block struct
Next, modify the Monero block template by including the merge mining hash \( h_m \) in the extra fields of the coinbase transaction. Monero has a merge mining subfield
to accommodate this data. Importantly, the extra field data part of the coinbase transaction hash and therefore the merkle_tree_root
, the blockhashing_blob
must be
reconstructed. A rust port of Monero's tree hash algorithm is needed to achieve this. The coinbase hash MUST be the first element to be hashed when constructing the merkle_tree_root
.
This satisfies REQ 2, proving that the proof-of-work was performed for the Tari block.
The block may now be mined. Once a solution is found that satisfies the Tari difficulty, the miner must include enough data to allow the Tari blockchain to assert REQ 1 and REQ 2.
Concretely, A miner must serialize MoneroPowData
using Monero consensus encoding and add it to the pow_data
field in the Tari header.
pub struct MoneroPowData {
/// Monero header fields
header: MoneroBlockHeader,
/// randomX vm key
randomx_key: FixedByteArray, // Fixed 64 bytes
/// transaction count
transaction_count: u16,
/// transaction root
transaction_root: Hash,
/// Coinbase merkle proof hashes
coinbase_merkle_proof: MerkleProof,
/// Coinbase tx from Monero
coinbase_tx: MoneroTransaction,
}
fig 2. Monero PoW data struct serialized in Tari blocks
pub struct MerkleProof {
branch: Vec<Hash>,
depth: u16,
path: u32,
}
fig 3. Merkle proof struct
A verifier may now check that the coinbase_tx
contains the merge mining hash \( h_m \), and validate the coinbase_merkle_proof
against the transaction_root
.
The coinbase_merkle_proof
contains the minimal proof required to construct the transaction_root
.
For example, a proof for a merkle tree of 4 hashes will require 2 hashes (h_1, h_23) of 32 bytes each, 4 bytes for the path bitmap and 2 bytes for the depth.
Root*
/ \
h_c1* h_23
/ \
h_c* h_1
* Not included in proof
Serialisation
For Monero proof-of-work, Monero consensus encoding MUST be used to serialize the MoneroPowData
struct. Given the same inputs,
this encoding will byte-for-byte the same. The encoding uses VarInt for all integer types, allowing byte-savings, in particular
for fields that typically contain small values. Importantly, extra bytes that a miner could tack onto the end of the pow_data
field
are expressly disallowed.
Merge Mining Proxy
The Tari merge mining proxy proxies the Monero daemon RPC interface. It behaves as a middleware that implements the merge mining protocol detailed above. This allows existing Monero miners to merge mine with Tari without having to make changes to mining software.
The proxy must be configured to connect to a monerod
instance, a Tari base node, and a Tari console wallet. Most requests
are forwarded "as is" to monerod
, however some are intercepted and augmented before being returned to the miner.
get_block_template
Once monerod
has provided the block template response, the proxy retrieves a Tari block template and coinbase,
and assembles the Tari block. The merge mining hash \( h_m \) is generated and added to the Monero coinbase. The modified
blockhashing_blob
and blocktemplate_blob
are returned to the miner. The difficulty is set to min(monero_difficulty, tari_difficulty)
so that the miner submits the found block at either chain's difficulty. The Tari block template is cached for later submission.
submit_block
The miner submits a solved Monero block (at a difficulty of min(monero_difficulty, tari_difficulty)
) to the proxy. The cached
Tari block is retrieved, enriched with the MoneroPowData
struct and submitted to the Tari base node.
RFC-0140/SyncAndSeeding
Syncing Strategies and Objectives
Maintainer(s): S W van Heerden, Philip Robinson
Licence
Copyright 2018 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to describe the syncing, seeding and pruning process.
Related Requests for Comment
Descriptions
Syncing
When a new node comes online, loses connection or encounters a chain reorganization that is longer than it can tolerate, it must enter syncing mode. This will allow it to recover its state to the newest up-to-date state. Syncing can be divided into two SynchronizationStrategys: complete sync and horizon sync. Complete sync means that the node communicates with an archive node to get the complete history of every single block from genesis block. Horizon Sync involves the node getting every block from its pruning horizon to current head, as well as every block header up to the genesis block.
To determine if the node needs to synchronise the node will monitor the broadcast chain_metadata provided by its neighbours.
Complete Sync
Complete sync is only available from archival nodes, as these will be the only nodes that will be able to supply the complete history required to sync every block with every transaction from genesis block up onto current head.
Complete Sync Process
Once the base node has determined that it is lagging behind the network tip it will start to synchronise with the peer it determines to have all the data required to synchronise.
The syncing process MUST be done in the following steps:
- Set SynchronizationState to
header_sync
. - Sync all missing headers from the genesis block to the current chain tip. The initial header sync allows the node to confirm that the syncing peer does indeed have a fully intact chain from which to sync that adheres to this node's consensus rules and has a valid proof-of-work that is higher than any competing chains.
- Set SynchronizationState to
block_sync
. - Start downloading blocks from sync peer starting with the oldest block in our database. A fresh node will start from the genesis block.
- Download all block up to current head, validating and adding the blocks to the local chain storage as we go.
- Once all blocks have been downloaded up and including the current network tip set the SynchronizationState to
listening
.
After this process, the node will be in sync, and will be able to process blocks and transactions normally as they arrive.
Horizon Sync Process
The horizon sync process MUST be done in the following steps:
- Set SynchronizationState to
header_sync
. - Sync all missing headers from the genesis block to the current chain tip. The initial header sync allows the node to confirm that the syncing peer does indeed have a fully intact chain from which to sync that adheres to this nodes consensus rules and has a valid proof-of-work that is higher than any competing chains.
- Set SynchronizationState to
horizon_sync
. - Download all kernels from the current network tip back to this node's pruning horizon.
- Validate kernel MMR root against headers.
- Download all utxo's from the current network tip back to this node's pruning horizon.
- Validate outputs and utxo MMR.
- Validate the chain balances with the expect total emission that the final sync height.
- Once all kernels and utxos have been downloaded from the network tip back to this node's pruning horizon set
the SynchronizationState to
block_sync
. This hands over further syncing to the standard sync protocol which should return to thelistening
state if no further data has been received from peers.
After this process, the node will be in sync, and will be able to process blocks and transactions normally as they arrive.
Keeping in Sync
The node that is in the listening
state SHOULD periodically test a subset of its peers with ping messages to ensure
that they are alive. When a node sends a ping message, it MUST include the height of the current longest chain, current
accumulated PoW difficulty, hash of the current head, it's pruning horizon and it's current pruned height. The
receiving node MUST reply with a pong message, which should include it's version of the information contained within the
ping message.
When a node receives pong replies from the current ping round, or the timeout expires, the collected chain_metadata
replies will be examined to determine what the current best chain is, i.e. the chain with the most accumulated work.
If the best chain is longer than out chain data the node will set SynchronizationState to header_sync
and catch up
with the network.
Chain Forks
Chain forks occur in all decentralized proof-of-work blockchains. When the local node is in the listening
state it
will detect that it has fallen behind other nodes in the network. It will then perform a header sync and during the
header sync process will be able to detect that a chain fork has occurred. The header sync process will then determine
which chain is the correct chain with the highest accumulated work. If required this node will switch the best chain
and proceed to sync the new blocks required to catch up to the correct chain. This process is called a chain
reorganization or reorg.
Pruning
In Mimblewimble, the state can be completely verified using the current UTXO set (which contains the output commitments and range proofs), the set of excess signatures (contained in the transaction kernels) and the PoW. The full block and transaction history is not required. This allows base layer nodes to remove old spent inputs from the blockchain storage.
Pruning is only for the benefit of the local Base Node, as it reduces the local blockchain size. Pruning only happens after the block is older than the pruning horizon height. A Base Node will either run in archival mode or pruned mode. If the Base Node is running in archive mode, it MUST NOT prune.
When running in pruning mode, Base Nodes MUST remove all spent outputs that are older than the pruning horizon in their current stored UTXO set when a new block is received from another Base Node.
RFC-0150/Wallets
Base Layer Wallet Module
Maintainer(s): Yuko Roodt, Cayle Sharrock
Licence
Copyright 2019 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to propose the functionality and techniques required by the Base Layer Tari wallet module. The module exposes the core wallet functionality on which user-facing wallet applications may be built.
Related Requests for Comment
This RFC is derived from a proposal first made in this issue.
Description
Key Responsibilities
The wallet software is responsible for constructing and negotiating transactions for transferring and receiving Tari coins on the Base Layer. It should also provide functionality to generate, store and recover a master seed key and derived cryptographic key pairs that can be used for Base Layer addresses and signing of transactions.
Details of Functionality
A detailed description of the required functionality of the Tari software wallet is provided in three parts:
- basic transaction functionality;
- key management features; and
- the different methods for recovering the wallet state of the Tari software wallet.
Basic Transaction Functionality
- It MUST be able to send and receive Tari coins using Mimblewimble transactions.
- It SHOULD be able to establish a connection between different user wallets to negotiate:
- the construction of a transaction; and
- the signing of multi-signature transactions.
- The Tari software wallet SHOULD be implemented as a library or Application Programming Interface (API) so that Graphic User Interface (GUI) or Command Line Interface (CLI) applications can be developed on top of it.
- It MUST be able to establish a connection to a Base Node to submit transactions and monitor the Tari blockchain.
- It SHOULD maintain an internal ledger to keep track of the Tari coin balance of the wallet.
- It MAY offer transaction fee estimation, taking into account:
- transaction byte size;
- network congestion; and
- desired transaction priority.
- It SHOULD be able to monitor and return the states (Spent, Unspent or Unconfirmed) of previously submitted transactions by querying information from the connected Base Node.
- It SHOULD present the total Spent, Unspent or Unconfirmed transactions in summarized form.
- It SHOULD be able to update its software to patch potential security vulnerabilities. Automatic updating SHOULD be selected by default, but users can decide to opt out.
- Wallet features requiring querying a base node for information SHOULD have caching capabilities to reduce bandwidth consumption.
Key Management Features
- It MUST be able to generate a master seed key for the wallet by using:
- input from a user (e.g. when restoring a wallet, or in testing); or
- a user-defined set of mnemonic word sequences using known word lists; or
- a cryptographically secure random number generator.
- It SHOULD be able to generate derived transactional cryptographic key pairs from the master seed key using deterministic key pair generation.
- It SHOULD store the wallet state using a password or passphrase encrypted persistent key-value database.
- It SHOULD provide the ability to back up the wallet state to a single encrypted file to simplify wallet recovery and reconstruction at a later stage.
- It MAY provide the ability to export the master seed key or wallet state as a printable paper wallet, using coded markers.
Different Methods for Recovering Wallet State of Tari Software Wallet
- It MUST be able to reconstruct the wallet state from a manually entered master seed key.
- It MUST have a mechanism to systematically search through the Tari blockchain and mempool for unspent and unconfirmed transactions, using the keys derived from the master key.
- The master seed key SHOULD be derivable from a specific set of mnemonic word sequences using known word lists.
- It MAY enable the reconstruction of the master seed key by scanning a coded marker of a paper wallet.
TransactionProtocol
Transaction Protocol
Maintainer(s): Cayle Sharrock
Licence
Copyright 2019 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
This Request for Comment (RFC) describes the transaction protocol for peer-to-peer Tari payments using the Mimblewimble protocol. It also considers some attacks that may be launched against the protocol and offers some discussion around those attacks and potential alternatives to the protocol.
The goal is to describe a transaction protocol that:
- permits multiple recipients;
- preserves privacy regarding how many parties are involved in the transaction; and
- is secure against all reasonable attacks.
Related Requests for Comment
Description
The Tari base layer is built using Mimblewimble, which requires that all parties involved in a Tari transfer must interact to construct a valid Mimblewimble transaction.
A valid transaction involves:
- a set of one or more inputs being spent by the Sender;
- a set of zero or more outputs being sent to the Sender;
- a set of recipients, each of whom MUST construct exactly one output; and
- a set of partial Schnorr signatures which, when aggregated, validates the transaction construction and indicates every party's satisfaction with the terms.
The Issue with Multiple Recipients
Each party involved in a Tari transaction must produce a partial signature, signing the same challenge. This challenge is defined as
$$ e = H(\Sigma R_i \Vert \Sigma P_i \Vert m) $$
where \(R_i\) are public nonces generated by each party for this signature; \(P_i\) are the public Spending Keys; and m is the additional metadata for the transaction. \(\Sigma P_i\) is the value of the (pre-offset) excess that is stored in the transaction kernel.
Notice that every signing party needs to know the sum of all the nonces and public spending keys. This suggests that every party knows how many parties are involved in the transaction, which is not an ideal privacy scenario. It would be preferable if a secure scheme could be found where each recipient interacts only with the sender and does not need to calculate these sums themselves.
Unfortunately, as discussed below, it seems that using any known scheme, it's not possible to satisfy this privacy goal while achieving the desired security level.
The Issue with Multiple Senders
To increase privacy, the public excess values are offset by a constant random value. The choice of this value, as well as fee selection, can only be set once per transaction. The privilege of selecting these values is generally bestowed on the sender, since the sender pays the fee. Allowing multiple sending parties (or equivalently, allowing recipients to provide inputs) would require a negotiation round to set the fee and offset before the transaction could be constructed. This is a complication we don't want to deal with, and so all schemes presented here allow exactly one sender.
Two-party Transactions
Two-party transactions are fairly straightforward and are described in detail by Tari Labs University (TLU). (Refer to Mimblewimble Transaction.)
It is proposed that Tari implement this single-round two-party transaction scheme as a special case to support both online two-party transactions as well as "offline" transactions such as via email, text message and carrier pigeon.
Multiple-recipient Transaction Scheme
** Legend **
Symbol | Meaning |
---|---|
tx_id | Transaction identifier |
amt_i | Amount sent to i-th recipient |
Rs, Ri | Public nonce |
Xs, Pi | Public excess/key |
m | Message metadata |
C_i | Commitment |
RP_i | Range proof |
[..] | Vector of data |
Transaction ID
The scheme above makes use of a tx_id
field in every peer-to-peer message. Since all messages are stateless and
asynchronous, peers need some way of figuring out which message refers to which transaction. The transaction ID fulfils
this role.
The ID does not appear on the blockchain in any manner; is purely used to disambiguate Tari transaction messages and can be discarded after the transaction is broadcast to the network.
The tx_id
is unique for every receiver so that any observers of the communication will not be able to group receivers
together (however, the communication should be over secure channels in general).
The format of the transaction ID is a four-byte, little-endian integer (u64) and is calculated as
H(Rs||i)[0..4]
where i
is the i-th recipient in the transaction. The sender can use the tx_id
as a hash map key to identify and
differentiate recipients.
Replay Attacks
If any party can be convinced to sign a different message with the same nonce, its private keys will be lost. One way of achieving this would be if a virtual machine could be "snapshotted" or otherwise cloned at any point between sharing the public nonce and signing the message. Both copies of the victim's machine will now continue, unaware that there's a copy participating in a signature round. What then happens is:
$$ \begin{align} &\text{Clone A} & &\text{Clone B} \\ e_1 &= H(r_1 \Vert r_s \Vert \dots) & e_2 &= H(r_1 \Vert r_s^* \Vert \dots) \\ s_1 &= r_1 + e_1 \cdot k_1 & s_2 &= r_1 + e_2 \cdot k_1 \\ \end{align} $$
The attacker receives both signatures and trivially calculates the secret key:
$$ \begin{align} \Delta s &= s_1 - s_2 \\ &= k_1(e_1 - e_2) = k_1\Delta e \\ \Rightarrow k_1 &= \frac{\Delta s}{\Delta e} \end{align} $$
We've demonstrated this with the attacker changing their nonce, but literally any alteration to the challenge will provide a new challenge \(e_2\), enabling the attack.
What can we do about this? In fact, it's not possible to eliminate this attack at all! The reason sits with the proof that the Schnorr scheme works as a zero-knowledge protocol; the demonstration of this proof is precisely the attack we're trying to avoid [GOL19]. If we could eliminate this attack, we'd need to come up with a completely different way of proving the zero-knowledge property.
So we can't stop it, but we can make it as tricky as possible for the attacker to trick the receiver into replaying the signature. MuSig does this by requiring parties to share the hash of their nonces beforehand. At its extreme: in the two-party, single-round scheme, for example, the attacker would need to be able to control the victim's machine code execution (like running a debugger), at which point one might think the attacker could read the private key directly from memory anyway.
Rogue Key Attacks
Rogue Key attacks are another type of attack that can occur in multi-signature schemes.
In this case, the attacker has the freedom to choose a key or nonce after the victim has already disclosed theirs. This may allow the attacker to forge a valid signature on behalf of the victim. A recent paper, [DRI19], suggests that any Schnorr-based, two-round multi-signature scheme is vulnerable to a rogue key attack.
How this might apply in an insecure two-round Tari multi-signature scheme is as follows: A receiver sends their public nonce; output commitment and range proof; and public spending key to the sender, but then decides to cancel the transaction by refusing to provide a signature and sending an "Abort" message to the sender instead. The sender could, if they wanted, forge the 2-of-2 signature using this rogue key attack and broadcast the transaction anyway.
Note: This attack is not applicable in the one-round, two-party scheme, since the receiver returns their information in an all-or-nothing manner. However, the receiver could attempt to forge a signature, since they have the Sender's public nonce, but there's nothing they can really do with this signature; they certainly cannot broadcast a transaction with it because they don't have any of the transaction data at this stage.
We avoid rogue-key attacks in the Tari multi-recipient scheme by employing three rounds. In the first round, parties share a hash of their public nonces, which each party can later use to verify that no nonces were changed after the actual public nonces were shared.
RFC-0152/EmojiId
Emoji Id specification
Maintainer(s):Cayle Sharrock
Licence
Copyright 2020. The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community regarding the technological merits of the potential system outlined herein.
Goals
This document describes the specification for Emoji Ids. Emoji Ids are encoded node ids used for humans to easily verify peer node addresses.
Related Requests for Comment
None
Description
Tari Communication Nodes are identified on the network via their Node ID; which in turn are derived from the node's public key. Both the node id and public key are simple large integer numbers.
The most common practice for human beings to copy large numbers in cryptocurrency software is to scan a QR code or copy and paste a value from one application to another. These numbers are typically encoded using hexadecimal or Base58 encoding. The user will then typically scan (parts) of the string by eye to ensure that the value was transferred correctly.
For Tari, we propose encoding values, the node ID in particular, using emoji. The advantages of this approach are:
- Emoji are more easily identifiable; and if selected carefully, less prone to identification errors (e.g. mistaking an O for a 0).
- The alphabet can be considerably larger than hexadecimal (16) or Base58 (58), resulting in shorter character sequences in the encoding.
The specification
The emoji character map
An emoji alphabet of 1,024 characters is selected. Each emoji is assigned a unique index from 0 to 1023 inclusive. This list is the emoji map. For example,
- 😀 => 0
- 😘 => 1
- ...
- 🦊 => 1023
The emoji SHOULD be selected such that
- Similar looking emoji are excluded from the map. e.g. Neither 😁 or 😄 should be included. Similarly the Irish and Côte d'Ivoirean flags look very similar, and both should be excluded.
- Modified emoji (skin tones, gender modifiers) are excluded. Only the "base" emoji is considered.
Encoding
The essential strategy in the encoding process is to map a sequence of 8-bit values onto a 10-bit alphabet. The general encoding procedure is as follows:
Given a large integer value, represented as a byte array, S
, in little-endian format (most significant digit last).
Assume the string is addressable, i.e. S[i]
is the i
th byte in the array.
- Set
CURSOR
to 0, SetL
to a multiple of 10 that is<= len(S)
. - Set
IDX
to[]
(an empty array) - While
CURSOR < L
:- Set
L <= S[CURSOR/8 + 1]
, the current low byte; if the index would overflow, setL
to zero. - Set
H <= S[CURSOR/8]
, the current high byte - Set
n <= CURSOR % 8
, the position of the cursor in the current high byte - Set
i <= ((H as u8) << n) << 2 + (L >> (6 - n))
, where the first shift left (H as u8 <<n
) is on a one-byte width (effectively losing the first n bits) and the second shift left is on a 8-byte width (u64). - Push
i
ontoIDX
CURSOR <= CURSOR + 10
- Set
- Return
IDX
The emoji string is created by mapping the IDX
array to the emoji map.
Emoji ID definition
The emoji ID is an emoji string of 12 characters. Each character encodes 10 bits according to the bitmap:
+---------------------+------------------+-------------------+
| Node Id (104 bits) | Version (6 bits) | Checksum (10 bits)|
+---------------------+------------------+-------------------+
The emoji ID is calculated from a 104-bit node id represented as 13 bytes (B
) as follows:
- Take the current emoji ID version number,
v
and addv << 2
as an additional byte toB
. This "right-pads" the version in the last byte. This is necessary since we have a 14 byte (112 bit) sequence, which is not divisible by 10. This padding sets the last 2 bits, which will be discarded, to zero. - Encode B into an emoji string with
L
= 11. - Calculate a 12th emoji using the Luhn mod 1024 checksum algorithm.
Decoding
One can extract the node id from an emoji ID as follows:
- Calculate the checksum of the first 11 emoji using the Luhn mod 1024 algorithm. If it does not match the 12th emoji, return with an error. if any emoji character is not in the emoji map, return an error.
- Extract the version number:
- Do a reverse lookup of emoji
[10]
to find its index. Store this u64 value inI
. - The Version number is
(I && 0x3F) >> 2
. This can be used to set the Emoji map accordingly (and may have to be done iteratively, since the version is encoded into the emoji string).
- Do a reverse lookup of emoji
- Set
CURSOR = 0
. - Set
B = []
, and empty byte array - While
CURSOR <= 11
:- Set
k <= CURSOR * 2
- Do a reverse lookup of the emoji
[CURSOR]
to find its index. Store this u64 value inL
. - If
k > 0
, setH
to the reverse lookup index of emoji[CURSOR-1]
as u8 (first 2 bits are discarded), elseH=0
. - Set
v = ((H as u8) << (8-k)) + (L >> (2+k))
. Push v onto thoB
. - Set
CURSOR <= CURSOR + 1
- Set
If the algorithm completes, B
holds the node ID.
Versioning
The current emoji ID version number is 1. If the emoji alphabet changes, the version number MUST be incremented. This will usually cause incompatible versions of the emoji ID to be detected. However, this is not fail-safe.
The last 6 bits of the 11th emoji encodes the version; this means that the first 4 bits are part of the node ID. On a reverse mapping, there is a chance that the reverse mapping would offer a valid, but incorrect version number if the new mapping are not chosen carefully.
Example.
In version 1, 😘 => 0b0000_000001
= 1 in the map. Seeing 😘 as the 11th emoji in a string would result in a version
code of 1, which is consistent and expected.
However, in unlucky version 13, if 😘 moves in the map to number 13 (0b0000_001101
), the version decoding would also
be valid and thus we wouldn't be able to unambiguously identify the version.
RFC-0153/StagedWalletSecurity
Staged Wallet Security
Maintainer(s): Yuko Roodt, Cayle Sharrock
Licence
Copyright 2021 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
This Request for Comment (RFC) aims to describe Tari's ergonomic approach to securing funds in a hot wallet. The focus is on mobile wallets, but the strategy described here is equally applicable to console or desktop wallets.
Related Requests for Comment
Description
Rationale
A major UX hurdle when users first interact with a crypto wallet is the friction they experience with the first user experience.
A common theme: I want to play with some new wallet X that I saw advertised somewhere, so I download it and run it. But first I get several screens that
- ask me to review my seed phrase,
- ask me to write down my seed phrase,
- prevent typical "skip this" tricks like taking a screenshot,
- ask to confirm if I've written down my seed phrase,
- force me to write a test, either by supplying a random sample of my seed phrase, or by getting me to type in the whole thing.
After all this, I play with the wallet a bit, and then typically, I uninstall it.
The goal of this RFC is to get the user playing with the wallet as quickly as possible. Without sacrificing security whatsoever.
A staged approach
This RFC proposes a smart, staged approach to wallet security. One that maximises user experience without compromising safety.
Each step enforces more stringent security protocols on the user than the previous step.
The user moves from one step to another based on criteria that
- the user configures based on her preferences, or
- uses sane predefined defaults.
The criteria are generally based on the value of the wallet balance.
Once a user moves to a stage, the wallet does not move to a lower stage if the requirements for the stage are no longer met.
Users may also jump to any more advanced stage from their wallet settings / configuration at any time.
Stage zero - zero balance
When the user has a zero balance, there's no risk in letting them skip securing their wallet.
Therefore, Tari wallets SHOULD just skip the whole seed phrase ritual and let the user jump right into the action.
Stage 1a - a reminder to write down your seed phrase
Once the user's balance exceeds the MINIMUM_STAGE_ONE_BALANCE
, they will be prompted to review and write down their
seed phrase. The MINIMUM_STAGE_ONE_BALANCE
is any non-zero balance by default.
After the transaction that causes the balance to exceed MINIMUM_STAGE_ONE_BALANCE
is confirmed, the user is presented
with a friendly message: "You now have real money in your wallet. If you accidentally delete your wallet app or lose
your device, your funds are lost, and there is no way to recover them unless you have safely kept a copy of your
seed phrase
safe somewhere. Click 'Ok' to review and save the phrase now, or 'Do it later' to do it at a more
convenient time".
If the user elects not to save the phrase, the message pops up again periodically. Once per day, or when the balance increases -- whichever is less frequent -- is sufficient without being too intrusive.
Stage 1b - simple wallet backups
Users are used to storing their data in the cloud. Although this practice is frowned upon by crypto purists, for small balances (the type you often keep in a hot wallet), using secure cloud storage for wallet backups is a fair compromise between keeping the keys safe from attackers and protecting users from themselves.
The simple wallet backup saves the spending keys and values of the user's wallet to a personal cloud space (e.g. Google Drive, Apple iCloud, Dropbox).
This solution does not require any additional input from the user besides providing authorisation to store in the cloud. This can be done using the standard APIs and Authentication flows that each cloud provider publishes for their platform.
In particular, we do not ask for a password to encrypt the commitment data. The consequence is that anyone who gains access to this data -- by stealing the user's cloud credentials -- could steal the user's funds.
Therefore, the threshold for moving from this stage to Stage 3, STAGE_TWO_THRESHOLD_BALANCE
is relatively low;
somewhere in the region of $10 to $50.
The seed phrase MUST NOT be stored on the cloud in Stage 1b. Doing so would result in all future funds of the user being lost if the backup were ever compromised. Since the backup is unencrypted in Stage 1b, we store the minimum amount of data needed to recover the funds and limit the potential loss of funds in case of a breach to just that found in the commitments in the backup, which should not be more than $50.
Backups MUST be authorised by the user when the first cloud backup is made and SHOULD be automatically updated after each transaction is confirmed.
Restoring a wallet from Stage 1b entails importing the UTXO commitments into the user's current wallet.
Wallet authors MAY choose to exclude Stage 1b from the staged security protocol.
As usual, the user MUST be able to configure STAGE_TWO_THRESHOLD_BALANCE
to suit their particular needs.
Stage 2 - full wallet backups
Once a user has a significant balance (over STAGE_TWO_THRESHOLD_BALANCE
), Stage 2 is active. Stage 2 entails a full,
encrypted backup of the user's wallet to the cloud. The user needs to provide a password to perform and secure the encryption.
This makes the user's fund safer while at rest in the cloud. It also introduces an additional point of failure: the user can forget their wallet's encryption password.
Stage 1b and 2 are similar in functionality but different in scope (Stage 2 allows us to store all the wallet metadata, rather than just the commitments). For this reason, Stage 1b is optional.
Backups MUST be authorised by the user when the first cloud backup is made and SHOULD be automatically updated after each transaction.
When migrating from Stage 1 to Stage 2, the Stage 1b backups SHOULD be deleted.
Stage 3 - Sweep to cold wallet
Above a given limit -- user-defined, or the default MAX_HOT_WALLET_BALANCE
, the user should be prompted to transfer
funds into a cold wallet. The amount to sweep can be calculated as MAX_HOT_WALLET_BALANCE
- SAFE_HOT_WALLET_BALANCE
.
If the user ignores the prompt, they SHOULD be reminded one week later. From the second prompt onward, users SHOULD be given
an option to re-configure the values for MAX_HOT_WALLET_BALANCE
and SAFE_HOT_WALLET_BALANCE
.
Assuming one-sided payments are live, the user SHOULD be able to configure a COLD_WALLET_ADDRESS
in the wallet.
For security reasons, a user SHOULD be asked for their 2FA confirmation, if it is configured, before broadcasting the sweep transaction to the blockchain.
Security hygiene
- From stage 1 onwards Users should be asked periodically whether they still have their seed phrase written down. Once every two months is sufficient.
RFC-0154/DeepLinksConvention
Deep links structure convention.
Maintainer(s): Adrian Truszczyński
Licence
Copyright 2022 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community regarding the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to specify the deep links structure used in the Tari Aurora project. The primary motivation is to create a simple, human-readable, and scalable way to structure deep links used by the Tari Aurora clients.
Description
Deep links are the URIs with hierarchical components sequence. We can use this sequence to pass and handle data in a standardized and predictable way. To do that, we need to pass three components to the target client: the scheme, command, and data.
Scheme
The scheme is used to address the client, which will handle the command and data components. In the Tari Aurora project, we're using the tari
scheme to open the wallet app and execute the command.
Command
The command is a path string used to pass information about the action that should be performed by the client. The handler uses this command to determine how to deserialize the data, before passing it to the command function. To support multiple networks the first path component should be the name of corresponding network. Before proceeding with the command, the client should first check that the active account is pointed to the correct network, or show an error if there is a mismatch.
For simple actions, the command can be defined as a single phrase. For more complex actions, the command should be defined as a multi-path where the second path component should be the name of the action group.
Examples:
Simple Actions:
mainnet/user_profile
testnet/login
Complex Actions:
mainnet/payments/send
testnet/payments/request
Data
The data component is an optional string of key-value pairs used by the parser/decoder to deserialize the data, which the client passes to the command function. The data component should be formatted in the same way as a URL query. The sub-component should have a ?
prefix, the key-value pairs should be separated by &
, and every key should be separated from the value by =
character.
The Structure
Combining all three components, they will form a deep link with a structure presented below:
{scheme}://{command}?{data}
Examples:
tari://mainnet/profile
tari://mainnet/profile/username
tari://testnet/payments/send?amount=1.23&pubKey=01234556789abcde
Deeplinks in use:
/transactions/send
The data contains transaction information used in the send tokens process.
Value Name | Value Type | Note |
---|---|---|
publicKey | String | Receiver's public key |
amount | UInt64? | The amount in micro Tari |
note | String? | Note passed with transaction |
/base_nodes/add
The data contains a custom base node configuration. This deep link adds a new base node configuration to the pool and switches to the added base node.
Value Name | Value Type | Note |
---|---|---|
name | String | The name of the base node |
peer | String | Base node's public link and onion address combined together |
RFC-0160/BlockSerialization
Tari Block Binary Serialization
Maintainer(s): Byron Hambly
Licence
Copyright 2021 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to specify the binary serialization of:
- a mined Tari block
- a Tari block mining template
This is to facilitate interoperability of mining software and hardware.
Related Requests for Comment
Specification
By reviewing the block and mining template fields below, we have the following underlying data types for serialization:
bool
u8
u16
u32
Vec<u8>
For 1. to 4. and all numbers, Base 128 Varint encoding MUST be used.
From the Protocol Buffers documentation:
Varints are a method of serializing integers using one or more bytes. Smaller numbers take a smaller number of bytes. Each byte in a varint, except the last byte, has the most significant bit (msb) set – this indicates that there are further bytes to come. The lower 7 bits of each byte are used to store the two's complement representation of the number in groups of 7 bits, least significant group first.
For 5., the dynamically sized Vec
array type, the encoded array MUST be preceded by a number indicating the length of the array. This length MUST also be encoded as a varint. By prepending the length of the array, the decoder knows how many elements to decode as part of the sequence.
Block field ordering
Using this varint encoding, all fields of the complete block MUST be encoded in the following order:
- Version
- Height
- Previous block hash
- Timestamp
- Output Merkle root
- Witness Merkle root
- Output Merkle mountain range size
- Kernel Merkle root
- Kernel Merkle mountain range size
- Input Merkle root
- Total kernel offset
- Total script offset
- Nonce
- Proof of work algorithm
- Proof of work supplemental data
- Transaction inputs - for each input:
- Flags
- Maturity
- Commitment
- Script
- Input data
- Script signature
- Sender Offset
- Transaction outputs - for each output:
- Flags
- Maturity
- Commitment
- Range proof
- Script
- Sender Offset
- Signature
- Transaction kernels - for each kernel:
- Features
- Fee
- Lock height
- Excess
- Excess signature public nonce
- Excess signature
Mining template field ordering
The new block template is provided to miners to complete. Its fields MUST also be encoded using varints, in the following order:
- Version
- Height
- Previous block hash
- Total kernel offset
- Total script offset
- Proof of work algorithm
- Proof of work supplemental data
- Target difficulty
- Reward
- Total fees
- Transaction inputs - for each input:
- Flags
- Maturity
- Commitment
- Script
- Input data
- Script signature
- Sender Offset
- Transaction outputs - for each output:
- Flags
- Maturity
- Commitment
- Range proof
- Script
- Sender Offset
- Signature
- Transaction kernels - for each kernel:
- Features
- Fee
- Lock height
- Excess
- Excess signature public nonce
- Excess signature
Tari Block and Mining Template - Data Types
A Tari block is comprised of the block header and aggregate body.
Here we describe the respective Rust types of these fields in the tari codebase, and their underlying data types:
Block Header
Field | Abstract Type | Data Type | Description |
---|---|---|---|
Version | u16 | u16 | The Tari protocol version number, used for soft/hard forks |
Height | u64 | u64 | Height of this block since the genesis block |
Previous Block Hash | BlockHash | [u8;32] | Hash of the previous block in the chain |
Timestamp | EpochTime | u64 | Timestamp at which the block was built (number of seconds since Unix epoch) |
Output Merkle Root | BlockHash | [u8;32] | Merkle Root of the unspent transaction ouputs |
Witness Merkle Root | BlockHash | [u8;32] | MMR root of the witness proofs |
Output MMR Size | u64 | u64 | The size (number of leaves) of the output and range proof MMRs at the time of this header |
Kernel Merkle Root | BlockHash | [u8;32] | MMR root of the transaction kernels |
Kernel MMR Size | u64 | u64 | Number of leaves in the kernel MMR |
Input Merkle Root | BlockHash | [u8;32] | Merkle Root of the transaction inputs in this block |
Total Kernel Offset | BlindingFactor | [u8;32] | Sum of kernel offsets for all transaction kernels in this block |
Total Script Offset | BlindingFactor | [u8;32] | Sum of script offsets for all transaction kernels in this block |
Nonce | u64 | u64 | Nonce increment used to mine this block |
Proof of Work Algorithm | PowAlgorithm | u8 | Proof of Work Algorithm used to mine this block (Monero or SHA3 ) |
Proof of Work Data | Vec<u8> | Vec<u8> | Supplemental proof of work data. For Sha3 this would be empty, but for a Monero block we need the Monero header and RandomX seed hash. |
[u8;32]
indicates an array of 32 unsigned 8-bit integers
Block Body
Field | Abstract Type | Data Type | Description |
---|---|---|---|
Transaction Inputs | Vec<TransactionInput> | TransactionInput | List of inputs spent |
Transaction Outputs | Vec<TransactionOutput> | TransactionOutput | List of outputs produced |
Transaction Kernels | Vec<TransactionKernel> | TransactionKernel | Kernels contain the excesses and their signatures for the transactions |
A further breakdown of the body fields is described below:
TransactionInput
Field | Abstract Type | Data Type | Description |
---|---|---|---|
Features | OutputFeatures | See OutputFeatures | The features of the output being spent. We will check maturity for all outputs. |
Commitment | PedersenCommitment | [u8;32] | The commitment referencing the output being spent. |
Script | TariScript | Vec<u8> | The serialised script, maximum size is 512 |
Input Data | ExecutionStack | Vec<u8> | The script input data, maximum size is 512 |
Script Signature | ComSignature | See ComSignature | A signature with $k_s$, signing the script, input data, and mined height |
Sender Offset | PublicKey | [u8;32] | The offset public key, $K_O$ |
OutputFeatures
Field | Abstract Type | Data Type | Description |
---|---|---|---|
Flags | OutputFlags | u8 | Feature flags that differentiate the output, for example to specify a coinbase output |
Maturity | u64 | u64 | The block height at which the output can be spent |
ComSignature
Field | Abstract Type | Data Type | Description |
---|---|---|---|
Public Nonce | PedersenCommitment | [u8;32] | public (Pedersen) commitment nonce created with the two random nonces |
u | SecretKey | [u8;32] | the first publicly known private key of the signature signing with the value |
v | SecretKey | [u8;32] | the second publicly known private key of the signature signing with the blinding factor |
Find out more about Commitment signatures:
- Simple Schnorr Signature with Pedersen Commitment as Key
- A New and Efficient Signature on Commitment Values.
TransactionOutput
Field | Abstract Type | Data Type | Description |
---|---|---|---|
Features | OutputFeatures | See OutputFeatures | Options for the output's structure or use |
Commitment | PedersenCommitment | [u8;32] | The homomorphic commitment representing the output amount |
Range Proof | RangeProof | Vec<u8> | A proof that the commitment is in the right range |
Script | TariScript | Vec<u8> | The script that will be executed when spending this output |
Sender Offset | PublicKey | [u8;32] | Tari script offset pubkey, K_O |
Signature | ComSignature | See ComSignature | UTXO signature with the script offset private key, k_O |
TransactionKernel
Field | Abstract Type | Data Type | Description |
---|---|---|---|
Features | KernelFeatures | u8 | Options for a kernel's structure or use |
Fee | MicroTari | u64 | Fee originally included in the transaction this proof is for. |
Lock Height | u64 | u64 | This kernel is not valid earlier than this height. The max maturity of all inputs to this transaction |
Excess | PedersenCommitment | [u8;32] | Remainder of the sum of all transaction commitments (minus an offset). If the transaction is well-formed, amounts plus fee will sum to zero, and the excess is a valid public key. |
Excess Signature | RistrettoSchnorr | See RistrettoSchnorr | An aggregated signature of the metadata in this kernel, signed by the individual excess values and the offset excess of the sender. |
RistrettoSchnorr
Field | Abstract Type | Data Type | Description |
---|---|---|---|
Public nonce | PublicKey | [u8;32] | The public nonce of the Schnorr signature |
Signature | SecretKey | [u8;32] | The signature of the Schnorr signature |
Mining Template Header
Field | Abstract Type | Data Type | Description |
---|---|---|---|
Version | u16 | u16 | The Tari protocol version number, used for soft/hard forks |
Height | u64 | u64 | Height of this block since the genesis block |
Previous Hash | BlockHash | [u8;32] | Hash of the previous block in the chain |
Total Kernel Offset | BlindingFactor | [u8;32] | Sum of kernel offsets for all transaction kernels in this block |
Total Script Offset | BlindingFactor | [u8;32] | Sum of script offsets for all transaction kernels in this block |
Proof of Work Algorithm | PowAlgorithm | u8 | Proof of Work Algorithm used to mine this block (Monero or SHA3 ) |
Proof of Work Data | Vec<u8> | Vec<u8> | Supplemental proof of work data. For Sha3 this would be empty, but for a Monero block we need the Monero header and RandomX seed hash. |
Target Difficulty | Difficulty | u64 | The minimum difficulty required to satisfy the Proof of Work for the block |
Reward | MicroTari | u64 | The value of the emission for the coinbase output for the block |
Total Fees | MicroTari | u64 | The sum of all transaction fees in this block |
Mining Template Body
Field | Abstract Type | Data Type | Description |
---|---|---|---|
Transaction Inputs | Vec<TransactionInput> | TransactionInput | List of inputs spent |
Transaction Outputs | Vec<TransactionOutput> | TransactionOutput | List of outputs produced |
Transaction Kernels | Vec<TransactionKernel> | TransactionKernel | Kernels contain the excesses and their signatures for the transactions |
RFC-0170/NetworkCommunicationProtocol
The Tari Communication Network and Network Communication Protocol
Maintainer(s): Yuko Roodt
License
Copyright 2018 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
The purpose of this document and its content is for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community regarding the technological merits of the potential system outlined herein.
Goals
This document will introduce the Tari communication network and the communication protocol used to select, establish and maintain connections between peers on the network. Communication Nodes and Communication Clients will be introduced and their required functionality will be proposed.
Related RFCs
Description
Assumptions
- A communication channel can be established between two peers once their online communication addresses are known to each other.
- A Validator Node is able to derive a node ID from the Validator Node registration transaction on the Base Layer.
Abstract
The backbone of the Tari communication network consists of a large number of nodes that maintain peer connections between each other. These nodes forward and propagate encrypted and unencrypted data messages through the network such as joining requests, discovery requests, transactions and completed blocks. Network clients, not responsible for maintaining the network, are able to create ad hoc connections with nodes on the network to perform joining and discovery requests. The majority of communication between clients and nodes will be performed using direct Peer-to-peer (P2P) communication once the discovery process was used to obtain the online communication addresses of peers. Where possible the efficient Kademlia based directed forwarding of encrypted data messages can be used to perform quick node discovery and joining of clients and nodes on the Tari communication network. Where messages are of importance to a wide variety of entities on the network, Gossip protocol based message propagation can be performed to distribute the message to the entire network.
Overview
The Tari communication network is a variant of a Kademlia network that allows for fast discovery of nodes, with an added ability to perform Gossip protocol based broadcasting of data messages to the entire network. The majority of the communication required on the Base Layer and Digital Asset Network (DAN) will be performed via direct P2P communication between known clients and nodes. Alternatively, the Tari communication network can be used for broadcasting joining requests, discovery requests and propagating data messages such as completed blocks, transactions and data messages that are of interest to a large part of the Tari communication network.
The Tari communication network consists of a number of different entities that need to communicate in a distributed and ad-hoc manner. The primary entities that need to communicate are Validator Nodes (VN), Base Nodes (BN), Wallets (W) and Token Wallets (TW). Here are some examples of different communication tasks that need to be performed by these entities on the Tari Communication network:
- Base Nodes on the Base Layer need to propagate completed blocks and transactions to other Base Nodes using Gossip protocol based broadcasting.
- Validator Nodes need to efficiently discover other Validator Nodes in the process of forming Validator Node committees.
- Wallets need to communicate and negotiate with other Wallets to create transactions. They also need the ability to submit transactions to the mempool of Base Nodes.
- Token Wallets need to communicate with Validator Node committees and other Token Wallets to construct and send DAN instructions.
Here is an overview communication matrix that show which source entities SHOULD initiate communication with destination entities on the Tari Communication network:
\Destination Source | Validator Node | Base Node | Wallet | Token Wallet |
---|---|---|---|---|
Validator Node | Yes | Yes | Yes | Yes |
Base Node | No | Yes | No | No |
Wallet | No | Yes | Yes | No |
Token Wallet | Yes | No | Yes | Yes |
Communication Nodes and Communication Clients
To simplify the description of the Tari communication network, the different entities with similar behaviour were grouped into two groups: Communication Nodes and Communication Clients. Validator Nodes and Base Nodes are Communication Nodes (CN). Wallets and Token Wallets are Communication Clients (CC). CNs form the core communication infrastructure of the Tari communication network and are responsible for maintaining the Tari communication network by receiving, forwarding and distributing joining requests, discovery requests, data messages and routing information. CCs are different from CNs in that they do not maintain the network and they are not responsible for propagating any joining requests, discovery requests, data messages and routing information. They do make use of the network to submit their own joining requests and perform discovery request of other specific CNs and CCs when they need to communicate with them. Once a CC has discovered the CC or CN they want to communicate with, they will establish a direct P2P channel with them. The Tari communication network is unaware of this direct P2P communication once discovery is completed.
The different entity types MUST be grouped into the different communication node types as follows:
Entity Type | Communication Node Type |
---|---|
Validator Node | Communication Node |
Base Node | Communication Node |
Wallet | Communication Client |
Token Wallet | Communication Client |
Unique identification of Communication Nodes and Communication Clients
In the Tari communication network, each CN or CC makes use of a node ID to determine their position in the network. This node ID is either assigned based on registration on the Base Layer or can be derived from the CNs or CCs identification public key. The method used to obtain a node ID will either enhance or limit the trustworthiness of that entity when propagating messages through them on the Tari communication network. When performing the broadcasting of data messages or propagating discovery requests, nodes with registration assigned node IDs are considered more trustworthy compared to nodes with derived node IDs.
Obtaining a node ID from registration on the Base Layer is important as it will limit the potential of some parties performing Eclipse attacks on the network. Registration makes it more difficult for Bad Actors to position themselves in ideal patterns on the network to perform disruptive operations and actions. In sensitive situations or situations where the Kademlia-style directed propagation of messages are vulnerable, gossip protocol-based broadcasting of messages can be performed as a less efficient, but safer alternative to ensure that the message will successfully reach the rest of the network.
The similarity or distance between different node IDs can be calculated by performing the Hamming distance between the bits of the two node ID numbers. The Hamming distance can be implemented as an Exclusive OR (XOR) between the bits of the numbers and the summation of the resulting true bits. CCs and/or CNs that have similar node IDs, that produce a small Hamming distance, are located in similar regions of the Tari communication network. This does not mean that their geographic locations are near each other, but rather that their location in the network is similar. A thresholding scheme can be applied to the Hamming distance to ensure that only neighboring CNs with similar node IDs are allowed to share and propagate specific information. As an example, only routing table information that contains similar node IDs to the requesting CCs or CNs node ID should be shared with them. Limiting the sharing of routing table information makes it more difficult to map the entire Tari communication network.
The recommended method of node ID assignment for each Tari communication network entity type MUST be implemented as follows:
Entity Type | Communication Node Type | Node ID Assignment |
---|---|---|
Validator Node | Communication Node | Registration |
Base Node | Communication Node | Derived |
Wallet | Communication Client | Derived |
Token Wallet | Communication Client | Derived |
Note that Mining Servers and Mining Workers are excluded from the Tari communication network. A Mining Worker will have a local or remote P2P connection with a Mining Server. A Mining Server will have a local or remote P2P connection with a Base Node. They do not need to make use of the communication network and they are not responsible for propagating any messages on the network. The parent Base Node will perform any communication tasks on the Tari communication network on their behalf.
Online Communication Address, Peer Address and Routing Table
Each CC and CN on the Tari communication network will have identification cryptographic keys, a node ID and an online communication address. The online communication address SHOULD be either an IPv4, IPv6, URL, Tor (Base32) or I2P (Base32) address and can be stored using the network address type as follows:
Description | Data type | Comments |
---|---|---|
address type | uint4 | Specify if IPv4/IPv6/Url/Tor/I2P |
address | char array | IPv4, IPv6, URL, Tor (Base32), I2P (Base32) address |
port | uint16 | port number |
The address type is used to determine how to interpret the address characters. An I2P address can be interpreted as "{52 address characters}.b32.i2p". The Tor address should be interpreted as "http://{16_or_52_address_chars}.onion/". The IPv4 and IPv6 address can be stored in the address field without modification. URL addresses can be used for nodes with dynamic IP addresses.
A Tor or I2P address can be used when anonymity is important for a CC or CN. The IPv4, IPv6 and URL address types do not provide any privacy features but do provide increased bandwidth.
Each CC or CN has a local routing table that contains the online communication addresses of all CCs and CNs on the Tari communication network known to that CC or CN. When a CC or CN wants to join the Tari communication network, the online communication address of at least one other CN that is part of the network needs to be known. The online communication address of the initial CN can either be manually provided or a bootstrapped list of "reliable" and persistent CNs can be provided with the Validator Node, Base Node, Wallet or Token Wallet software. The new CC or CN can then request additional peer contact information of other CNs from the initial peers to extend their own routing table.
The routing table consists of a list of peer addresses that link node IDs, public identification keys and online communication addresses of each known CC and CN.
The Peer Address stored in the routing table MAY be implemented as follows:
Description | Data type | Comments |
---|---|---|
network address | network_address | The online communication address of the CC or CN |
node_ID | node_ID | Registration Assigned for VN, Self selected for BN, W and TW |
public_key | public_key | The public key of the identification cryptographic key of the CC or CN |
node_type | node_type | VN, BN, W or TW |
linked asset IDs | list of asset IDs | Asset IDs can be used as an address on Tari network similar to a node ID |
last_connection | timestamp | Time of last successful connection with peer |
update_timestamp | timestamp | A timestamp for the last peer address update |
When a new CC or CN wants to join the Tari communication network they need to submit a joining request to the rest of the network. The joining request contains the peer address of the new CC or CN. Each CN that receives the joining request can decide if they want to add the new CCs or CNs contact information to their local routing table. When a CN, that received the joining request, has a similar node ID to the new CC or CN then that node must add the peer address to their routing table. All CNs with similar node IDs to the new CC or CN should have a copy of the new peer address in their routing tables.
To limit potential attacks, only one registration for a specific node type with the same online communication address can be stored in the routing table of a CN. This restriction will limit Bad Actors from spinning up multiple CNs on a single computer.
Joining the Network using a Joining Request
A new CC or CN needs to register their peer address on the Tari communication network. This is achieved by submitting a network joining request to a subset of CNs selected from the routing table of the new CC or CN. These peers will forward the joining request, with the peer address, to the rest of the network until CNs with similar node IDs have been reached. CNs with similar node IDs will then add the new peer address of the new node to their routing table, allowing for fast discovery of the new CC or CN.
Other CCs and CNs will then be able to retrieve the new CCs or CNs peer address by submitting discovery requests. Once the peer address of the desired CC or CN has been discovered then a direct P2P communication channel can be established between the two parties for any future communication. After discovery, the rest of the Tari communication network will be unaware of any further communication between the two parties.
Sending Data Messages and Discovery Requests
The majority of all communication on the Tari communication network will be performed using direct P2P channels established between different CCs and CNs once they are aware of the peer addresses of each other that contain their online communication addresses. Message propagation on the network will typically consist only of joining and discovery requests where a CC or CN wants to join the network or retrieve the peer address of another CC or CN so that a direct P2P channel can be established.
Messages can be transmitted in this network in either an unencrypted or encrypted form. Typically messages that have been sent in unencrypted form are of interest to a number of CNs on the network and should be propagated so that every CN that is interested in that data message obtains a copy. Block and Transaction propagation are examples of data messages where multiple entities on the Tari communication network are interested in that data message; this requires propagation through the entire Tari communication network in unencrypted form.
Encrypted data messages make use of the source and destinations identification cryptographic keys to construct a shared secret with which the message can be encoded and decoded. This ensures that only the two parties are able to decode the data message as it is propagated through the communication network. This mechanism can be used to perform private discovery requests, where the online communication address of the source node is encrypted and propagated through the network until it reached the destination node. Private discovery requests can only be performed if both parties are online at the same time. Encryption of the data message ensures that only the destination node is able to view the online address of the source node as the data message moves through the network. Once the destination node receives and decrypts the data message, that node is then able to establish a P2P communication channel with the source node for any further communication.
Propagation of completely private discovery request, hidden as an encrypted data message, can be performed as a broadcast through the entire network using the Gossip protocol. Propagation of public discovery requests can be performed using more efficient directed propagation using the Kademlia protocol. As encrypted message with visible destinations tend to not be of interest to the rest of the network, directed propagation using the Kademlia protocol to forward these messages to the correct parties are preferred. Privacy of a CCs online address, whom is sending a transaction to a Base Node, may be enhanced if the transaction is encrypted and sent to a Base Node with a node ID that is not the closest to the CCs node ID. This should prevent linking a transaction to the originating online address.
This same encryption technique can be used to send encrypted messages to a single node or a group of nodes, where the group of nodes have shared identification keys. A Validation Committee is an example of a group of CNs that have shared identification keys for the committee. The shared identification keys ensure that all members of that committee are able to receive and decrypt data messages that were sent to the committee.
Maintaining connections with peers
CCs and CNs establish and maintain connections with peers differently. CCs only create a few short-lived ad hoc channels and CNs create and maintain long-lived channels with a number of peers.
If a CC is unaware of a destination CNs or CCs online communication address then the address first needs to be obtained using a discovery request. When a CC already knows the communication address of the CC or CN that he wants to communicate with, then a direct P2P channel can be established between the two peers for the duration of the communication task. The communication channel can then be closed as soon as the communication task has been completed.
CNs consisting of VNs and BNs typically attempt to maintain communication channels with a large number of peers. The distribution of peers (VNs vs BNs) that a single CN keeps communication channels open with can change depending on the type of node. A CN that is also a BN should maintain more peer connections with other BNs, but should also have some connections with other VNs.
Having some connections with VNs are important as BNs have derived node IDs and not registered node IDs such as VNs, making it possible for the CN to be separated from the main network and become victim to an Eclipse attack. Having some connections with VNs will make it more difficult to separate the CN from the network and will ensure successful propagation of transactions and completed blocks from that CN.
A CN that is also a VN should maintain more peer connections with other VNs, but also have some connections with BNs. CNs that are part of Validator Node committees should attempt to maintain permanent connections with the other members of the committee to ensure that quick consensus can be achieved.
To maintain connections with peers, the following process can be performed. Discover peers using discovery requests, and add their details to the local routing table. The CN can decide how the peer connections should be selected from the routing table by either:
- manually selecting a subset,
- automatically selecting a random subset or
- selecting a subset of neighbouring nodes with similar node IDs.
Maintaining communication channels is important and the following process can be followed in an attempt to keep peer connections alive: For an existing peer connection. When more than 30 minutes have passed since the last communication with that peer, then a heartbeat message should be sent to the peer in an attempt to keep the connection with the peer alive. If the peer connection was not established for a specific purpose, such as with the connections between committee members, then a new replacement peer can be selected from the local routing table. A new connection can then be established and maintained between the current node and the newly selected node. If that specific connection is important, such as with the connections between committee members, then the current CN or CC must wait and attempt to create a new connection with that same peer. If more than 90 minutes have passed since the last successful communication with the peer node, a new discovery request can be sent on the Tari communication network in an attempt to locate that peer again. Losing of a peer might happen in cases where the CN or CC went temporarily offline and their dynamic communication address changed, requiring the discovery process to be performed again before a direct P2P communication channel can be established.
Functionality Required of Communication Nodes
- It MUST select a cryptographic key pair used for identification on the Tari Communication network.
- A CN MAY request the peer addresses of CNs with similar node IDs from other CNs to extend their local routing table.
- If a CN is a VN, then a node ID MUST be obtained by registering on the Base layer.
- If a CN is a BN, then a node ID MUST be derived from the nodes identification public key.
- A new CN MUST submit a joining request to the Tari communication network so that the nodes peer address can be added to the routing table of neighbouring peers in the network.
- If a CN receives a new joining request with a similar node ID (within a network selected threshold), then the peer address specified in the joining request MUST be added to its local routing table.
- When a CN receives an encrypted message, the node MUST attempt to open the message.
- When a CN receives an encrypted message that the node is unable to open, and the destination node ID is known then the CN MUST forward it to all connected peers that have node IDs that are closer to the destination.
- When a CN receives an encrypted message that the node is unable to open and the destination node is unknown then the CN MUST forward the message to all connected peers.
- A CN MUST have the ability to verify the content of unencrypted messages to limit the propagation of spam messages.
- If an unencrypted message is received by the CN with a unspecified destination node ID, then the node MUST verify the content of the message and forward the message to all connected peers.
- If an unencrypted message is received by the CN with an specified destination node ID, then the node MUST verify the content of the message and forward the message to all connected peers that have closer node IDs.
- A CN MUST have the ability to select a set of peer connections from its routing table.
- Connections with the selected set of peers MUST be maintained by the CN.
- A CN MUST have a mechanism to construct encrypted and unencrypted joining requests, discovery requests or data messages.
- A CN MUST construct and provide a list of peer addresses from its routing table that is similar to a requested node ID so that other CCs and CNs can extend their routing tables.
- A CN MUST keep its routing table up to date by removing unreachable peer addresses and adding newly received addresses.
- It MUST have a mechanism to determine if a node ID was obtained through registration or was derived from an identification public key.
- A CN MUST calculate the similarity between different node IDs by calculating the Hamming distance between the bits of the two node ID numbers.
Functionality Required of Communication Clients
- It MUST select a cryptographic key pair used for identification on the Tari Communication network.
- It MUST have a mechanism to derive a node ID from the self-selected identification public key.
- A CC must have the ability to construct a peer address that links its identification public key, node ID and an online communication address.
- A new CC MUST broadcast a joining request with its peer address to the Tari communication network so that CNs with similar node IDs can add the peer address of the new CC to their routing tables.
- A CC MAY request the peer addresses of CNs with similar node IDs from other CNs to extend their local routing table.
- A CC MUST have a mechanism to construct encrypted and unencrypted joining and discovery requests.
- A CC MUST maintain a small persistent routing table of Tari Communication network peers with which ad hoc connections can be established.
- As the CC becomes aware of other CNs and CCs on the communication network, the CC SHOULD extend its local routing table by including the newly discovered CCs or CNs contact information.
- Peers from the CCs routing table that have been unreachable for a number of attempts SHOULD be removed from the its routing table.
- A CC MUST calculate the similarity between different node IDs by calculating the Hamming distance between the bits of the two node ID numbers.
RFC-0171/MessageSerialization
Message Serialization
Maintainer(s): Cayle Sharrock
Licence
Copyright 2019 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to describe the message serialization formats for message payloads used in the Tari network.
Related Requests for Comment
RFC-0710: Tari Communication Network and Network Communication Protocol
Description
One way of interpreting the Tari network is that it is a large peer-to-peer messaging application. The entities chatting on the network include:
- Users
- Wallets
- Base nodes
- Validator nodes
The types of messages that these entities send might include:
- Text messages
- Transaction messages
- Block propagation messages
- Asset creation instructions
- Asset state change instructions
- State Checkpoint messages
For successful communication to occur, the following needs to happen:
- The message is translated from its memory storage format into a standard payload format that will be transported over the wire.
- The communication module wraps the payload into a message format, which may entail any/all of
- adding a message header to describe the type of payload;
- encrypting the message;
- signing the message;
- adding destination/recipient metadata.
- The communication module then sends the message over the wire.
- The recipient receives the message and unwraps it, possibly performing any/all of the following:
- decryption;
- verifying signatures;
- extracting the payload;
- passing the serialized payload to modules that are interesting in that particular message type.
- The message is deserialized into the correct data structure for use by the receiving software
This document only covers the first and last steps, i.e. serializing data from in-memory objects to a format that can be transmitted over the wire. The other steps are handled by the Tari communication protocol.
In addition to machine-to-machine communication, we also standardize on human-to-machine communication. Use cases for this include:
- Handcrafting instructions or transactions. The ideal format here is a very human-readable format.
- Copying transactions or instructions from cold wallets. The ideal format here is a compact but easy-to-copy format.
- Peer-to-peer text messaging. This is just a special case of what has already been described, with the message
structure containing a unicode
message_text
field.
When sending a message from a human to the network, the following happens:
- The message is deserialized into the native structure.
- The deserialization acts as an automatic validation step.
- Additional validation can be performed.
- The usual machine-to-machine process is followed, as described above.
Binary Serialization Formats
The ideal properties for binary serialization formats are:
- widely used across multiple platforms and languages, but with excellent Rust support;
- compact binary representation; and
- serialization "Just Works"(TM) with little or no additional coding overhead.
Several candidates fulfill these properties to some degree.
ASN.1
- Pros:
- Very mature (was developed in the 1980s)
- Large number of implementations
- Dovetails nicely into ZMQ
- Cons:
- Limited Rust/Serde support
- Requires schema (additional coding overhead if no automated tools for this exist)
Message Pack
- Pros:
- Very compact
- Fast
- Multiple language support
- Good Rust/Serde support
- Dovetails nicely into ZMQ
- Cons:
- No metadata support
Protobuf
Similar to Message Pack, but also requires schemas to be written and compiled. Serialization performance and size are similar to Message Pack. It Can work with ZMQ, but is better designed to be used with gRPC.
Cap'n Proto
Similar to Protobuf, but claims to be much faster. Rust is supported.
Hand-rolled Serialization
Hintjens recommends using hand-rolled serialization for bulk messaging. While Pieter usually offers sage advice, I'm going to argue against using custom serializers at this stage for the following reasons:
- We're unlikely to improve hugely over MessagePack.
- Since Serde does 95% of our work for us with MessagePack, there's a significant development overhead (and new bugs) involved with a hand-rolled solution.
- We'd have to write de/serializers for every language that wants Tari bindings; whereas every major language has a MessagePack implementation.
Serialization in Tari
Deciding between these protocols is largely a matter of preference, since there isn't that much difference between them. Given that ZMQ is used in other places in the Tari network, MessagePack looks to be a good fit while offering a compact data structure and highly performant de/serialization. In Rust, in particular, there's first-class support for MessagePack in Serde.
For human-readable formats, it makes little sense to deviate from JSON. For copy-paste semantics, the extra compression that Base64 offers over raw hex or Base58 makes it attractive.
Many Tari data types' binary representation will be the straightforward MessagePack version of each field in the related
struct
. In these cases, a straightforward #[derive(Deserialize, Serialize)]
is all that is required to enable the data
structure to be sent over the wire.
However, other structures might need fine-tuning, or hand-written serialization procedures. To capture both use cases,
it is proposed that a MessageFormat
trait be defined:
#![allow(unused)] fn main() { pub trait MessageFormat: Sized { fn to_binary(&self) -> Result<Vec<u8>, MessageFormatError>; fn to_json(&self) -> Result<String, MessageFormatError>; fn to_base64(&self) -> Result<String, MessageFormatError>; fn from_binary(msg: &[u8]) -> Result<Self, MessageFormatError>; fn from_json(msg: &str) -> Result<Self, MessageFormatError>; fn from_base64(msg: &str) -> Result<Self, MessageFormatError>; } }
This trait will have default implementations to cover most use cases (e.g. a simple call through to serde_json
). Serde
also offers significant ability to tweak how a given struct will be serialized through the use of
attributes.
RFC-0172/PeerToPeerMessaging
Peer to Peer Messaging Protocol
Maintainer(s): Stanley Bondi, Cayle Sharrock and Yuko Roodt
Licence
Copyright 2019 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to describe the peer-to-peer messaging protocol for communication nodes and communication clients on the Tari network.
Related Requests for Comment
Description
Assumptions
- Either every communication node or communication client has access to a Tor/I2P proxy, or a native Tor/I2P implementation exists, which allows communication across the Tor network.
- All messages are de/serialized as per RFC-0171: Message Serialisation.
Broad Requirements
Tari network peer communication must facilitate secure, private and efficient communication between peers. Broadly, a communication node or communication client MUST be capable of:
- bidirectional communication between multiple connected peers;
- private and secure over-the-wire communication;
- understanding and constructing Tari messages;
- encrypting and decrypting message payloads;
- gracefully reestablishing dropped connections; and (optionally)
- communicating to a SOCKS5 proxy (for connections over Tor and I2P).
Additionally, communication nodes MUST be capable of performing the following tasks:
- opening a control port for establishing secure peer channels;
- maintaining a list of known peers in the form of a routing table;
- forwarding directed messages to neighbouring peers; and
- broadcasting messages to neighbouring peers.
Overall Architectural Design
The Tari communication layer has a modular design to allow for the various communicating nodes and clients to use the same infrastructure code.
The design is influenced by an open-source library called ZeroMQ and the ZeroMQ C bindings are a dependency of the project. ZeroMQ's over-the-wire protocol is relatively simple, and replicating ZeroMQ framing in a custom implementation should not be prohibitively difficult. However, ZeroMQ offers many valuable features, which would be a significantly larger undertaking to reproduce. Fortunately, bindings or native ports are available in numerous languages.
To learn more about ZeroMQ, read the guide. It's an enjoyable and worthwhile read.
A quick overview of what ZeroMQ provides:
- A simple socket Application Programming Interface (API).
- Some well-defined patterns to connect sockets together.
- Sockets that are tiny asynchronous message queues, which:
- abstract away complexity around the underlying socket;
- are transport agnostic, meaning you can choose between Transmission Control Protocol (TCP), Pragmatic General Multicast (PGM), Inter-process Communication (IPC) and in-process (inproc) transports with little or no changes to code; and
- transparently reconnect when connections are dropped.
- The
inproc
transport for message passing between threads without mutex locks. - Built-in protocol for asymmetric encryption over the wire using Curve25519.
- Ability to send and receive multipart messages using a simple framing scheme. More info here.
- Support for Secure Socket (SOCKS) proxies.
This document will refer to several ZeroMQ sockets. These are referred to by prepending ZMQ_
and the name
of the socket in capitals. For example, ZMQ_ROUTER
.
Note about ZeroMQ frames and multipart messages:
ZeroMQ frames are length-specified blocks of binary data and can be strung together to make multipart messages.
|5|H|E|L|L|O|*|0|*|3|F|O|O|+|
* = more flag
+ = no more flag
A multipart message consisting of three frames "HELLO", a zero-length frame and "FOO"
When this RFC mentions 'multipart messages', this is what it's referring to.
Establishing a Connection
Every participating communication node SHOULD open a control socket (refer to ControlService) to allow peers to negotiate and establish a peer connection. The NetAddress of the control socket is what is stored in peers' routing tables and will be used to establish new ephemeral PeerConnections. Any peer that wants to connect MUST establish a connection to the control socket of the destination peer to negotiate a new encrypted PeerConnection.
Once a connection is established, messages can be sent and received directly to or from the Peer.
Incoming messages are validated, deserialized and handled as appropriate.
Encryption
Two forms of encryption are used:
- Over-the-wire encryption, in which traffic between nodes is encrypted using ZMQ's CURVE implementation.
- Payload encryption, in which the MessageEnvelopeBody is encrypted in such a way that it can only be decrypted by the destination recipient.
Components
The following components are proposed:
NetAddress
Represents:
- IP address;
- Onion address; or
- I2P address.
#![allow(unused)] fn main() { #[derive(Clone, PartialEq, Eq, Debug)] /// Represents an address which can be used to reach a node on the network pub enum NetAddress { /// IPv4 and IPv6 IP(SocketAddress), Tor(OnionAddress), I2P(I2PAddress), } }
Messaging Structure
The following illustrates the structure of a Tari message:
+----------------------------------------+
| MessageEnvelope |
| +----------------------------------+ |
| | MessageEnvelopeHeader | |
| +----------------------------------+ |
| +----------------------------------+ |
| | MessageEnvelopeBody | |
| | (optionally encrypted) | |
| | +------------------------------+ | |
| | | Message | | |
| | | +-----------------------+ | | |
| | | | MessageHeader | | | |
| | | +-----------------------+ | | |
| | | | | |
| | | +-----------------------+ | | |
| | | | MessageBody | | | |
| | | +-----------------------+ | | |
| | +------------------------------+ | |
| +----------------------------------+ |
+----------------------------------------+
MessageEnvelope Wire format
Every Tari message MUST use the MessageEnvelope format. This format consists of four frames of a multipart message.
A MessageEnvelope represents a message that has either just come off or is about to go on to the wire. and consists of the following:
Name | Frame | Length (Octets) | Type | Description |
---|---|---|---|---|
identity | 0 | 8 | [u8;8] | The identifier that a ZMQ_ROUTER socket expects so that it knows the intended destination of the message. This can be thought of as a session token. |
version | 1 | 1 | u8 | The wire protocol version. |
header | 2 | Varies | Vec<u8> | Serialized bytes of data containing an unencrypted MessageEnvelopeHeader. |
body | 3 | Varies | Vec<u8> | Serialized bytes of data containing an unencrypted or encrypted MessageEnvelopeBody. |
The header and decrypted body MUST be deserializable as per RFC-0171: MessageSerialization.
MessageEnvelopeHeader
Every MessageEnvelope MUST have an unencrypted header containing the following fields:
Name | Type | Description |
---|---|---|
version | u8 | Message protocol version. |
source | PublicKey | Source public key. |
dest | Option<NodeDestination> | Destination node ID or public key. A destination is optional. |
signature | [u8] | Signature of the message header and body, signed with the private key of the source. |
flags | u8 |
|
A communication node and communication client:
- MUST validate the signature of the message using the source's public key.
- MUST reject the message if the signature verification fails.
- If the encryption bit flag is set:
- MUST attempt to decrypt the MessageEnvelopeBody; or failing that
- MUST forward the message to a subset of peers using the
Closest
BroadcastStrategy. - MUST discard the message if the body is not encrypted.
MessageEnvelopeBody
A MessageEnvelopeBody is the payload of the [MessageEnvelope]. A MessageEnvelopeBody may be encrypted as required.
It consists of a MessageHeader and Message of a particular predefined MessageType.
MessageType
An enumeration of the messages that are part of the Tari network. MessageTypes are represented as an unsigned eight-bit integer and each value must be mapped to a corresponding Message struct.
All MessageTypes fall within a particular numerical range according to the message's concern:
Category | Range | # Message Types | Description |
---|---|---|---|
reserved | 0 | 1 | Reserved for control messages such as Ack . |
net | 1-32 | 32 | Network-related messages such as join and discover . |
peer | 33-64 | 32 | Peer connection messages, such as establish connection . |
blockchain | 65-96 | 32 | Messages related to the blockchain, such as add block . |
vn | 97-224 | 128 | Messages related to the validator nodes, such as execute instruction . |
extended | 225-255 | 30 | Reserved for future use. |
In documentation, MessageTypes can be referred to by the category and name. For example, peer::EstablishConnection
and
net::Discover
.
MessageHeader
Every Tari message MUST have a header containing the following fields:
Name | Type | Description |
---|---|---|
version | u8 | The message version. |
message_type | u8 | An enumeration of the message type of the body. Refer to MessageType. |
As this is part of the MessageEnvelopeBody, it can be encrypted along with the rest of the message, which keeps the type of message private.
MessageBody
Messages are an intention to perform a task. MessageType names should thus be a verb such as net::Join
or blockchain::AddBlock
.
All messages can be categorized as follows; each categorization has rules for how they should be handled:
- A propagation message
- SHOULD NOT have a destination in the MessageHeader;
- MUST be forwarded;
- SHOULD use the
Random
BroadcastStrategy; - SHOULD discard a message that it has seen within the DuplicateMessageWindow.
- A direct message
- MUST have a destination in the MessageHeader;
- SHOULD be discarded if it does not have a destination;
- SHOULD discard a message that it has seen before;
- MUST use the
Direct
BroadcastStrategy if a destination peer is known; - SHOULD use the
Closest
BroadcastStrategy if a destination peer is not known.
- An encrypted message
- MUST undergo an attempt to be decrypted by all recipients;
- MUST be forwarded by recipients if it cannot be decrypted;
- SHOULD discard a message that it has seen before.
The MessageType in the header MUST be used to determine the type of the message deserialized. If the deserialization fails, the message SHOULD be discarded.
DuplicateMessageWindow
A configurable length of time for which message signatures should be tracked in order to eliminate duplicate messages. This should be long enough to make it highly unlikely that a particular message will be processed again and short enough to not be a burden on the node.
InboundConnection
A thin wrapper around a ZMQ_ROUTER
socket, which binds to a NetAddress and accepts incoming multipart messages.
This connection blocks until there is data to read, or a timeout is reached. In both cases, the receive
method
can be called again (i.e. in a loop) to continue listening for messages. Client code should run this loop in its own thread.
send
is only called (if at all) in response to an incoming message.
Fields may include
- a NetAddress;
- a timeout;
- underlying ZeroMQ socket.
Methods may include:
receive()
send(data)
set_encryption(secret_key)
set_socks_proxy(address)
set_hwm(v)
An InboundConnection:
- MUST perform the "server-side" CurveZMQ encryption protocol if encryption is set.
- Using ZeroMQ. this means setting the socketopts
ZMQ_CURVE_SERVER
to 1 andZMQ_CURVE_SECRETKEY
to the secret key before binding.
- Using ZeroMQ. this means setting the socketopts
- MUST listen for and accept TCP connections.
- For an IP NetAddress, bind on the given host IP and port.
- For an Onion NetAddress, bind on 127.0.0.1 and the given port.
- For an I2P NetAddress, as yet undetermined.
- MUST read multipart messages and return them to the caller.
- If the timeout is reached, return an error to be handled by the calling code.
OutboundConnection
A thin wrapper around a ZMQ_DEALER
socket, which connects to a NetAddress and sends outbound multipart messages.
This connection blocks until data can be written, or a timeout is reached. The timeout should never be reached, as
ZeroMQ internally queues messages to be sent.
Fields may include:
- a NetAddress;
- underlying ZeroMQ socket.
Methods may include:
send(msg)
receive()
disconnect()
set_encryption(server_pk, client_pk, client_sk)
set_socks_proxy(address)
set_hwm(v)
An OutboundConnection:
- MUST perform the "client-side" CurveZMQ encryption protocol if encryption is set.
- Using ZeroMQ, this means setting the socketopts
ZMQ_CURVE_SERVERKEY
,ZMQ_CURVE_SECRETKEY
andZMQ_CURVE_PUBLICKEY
.
- Using ZeroMQ, this means setting the socketopts
- MUST connect to a TCP endpoint.
- For an IP NetAddress, connect to the given host IP and port.
- For an Onion NetAddress, connect to the onion address using the TCP, e.g.
tcp://xyz...123.onion:1234
. - For an I2P NetAddress, as yet undetermined.
- MUST write the parts of the given MessageEnvelope to the socket as a multipart message consisting of, in order:
- identity;
- version;
- header;
- body.
- If specified, MUST set a High Water Mark (HWM) on the underlying ZeroMQ socket.
- If the HWM is reached, a call to
send
MUST return an error and any messages received SHOULD be discarded.
Peer
A single peer that can communicate on the Tari network.
Fields may include:
addresses
- a list of NetAddresses associated with the peer, perhaps accompanied by some bookkeeping metadata, such as preferred address;node_type
- the type of node or client, i.e. BaseNode, ValidatorNode, Wallet or TokenWallet);last_seen
- a timestamp of the last time a message has been sent/received from this peer;flags
- 8-bit flag;- bit 0: is_banned,
- bit 1-7: reserved.
A peer may also contain reputation metrics (e.g. rejected_message_count, avg_latency) to be used to decide if a peer should be banned. This mechanism is yet to be decided.
PeerConnection
Represents direct bidirectional connection to another node or client. As connections are bidirectional, the PeerConnection need only hold a single InboundConnection or OutboundConnection, depending on if the node requested a peer connect to it or if it is connecting to a peer.
PeerConnection will send messages to the peer in a non-blocking, asynchronous manner as long as the connection is maintained.
It has a few important functions:
- managing the underlying network connections, with automatic reconnection if necessary;
- forwarding incoming messages onto the given handler socket; and
- sending outgoing messages.
Unlike InboundConnection and OutboundConnection, which are essentially stateless,
PeerConnection
maintains a particular ConnectionState
.
Idle
- the connection has not been established.Connecting
- the connection is in progress.Connected
- the connection has been established.Suspended
- the connection has been suspended. Incoming messages will be discarded, calls tosend()
will error.Dead
- the connection is no longer active because the connection was dropped.Shutdown
- the connection is no longer active because it was shut down.
Fields may include:
- a connection state;
- a control socket;
- a peer connection
NetAddress
; - a direction (either
Inbound
orOutbound
); - a public key obtained from the connection negotiation;
- (optional) SOCKS proxy.
Methods may include:
establish()
shutdown()
suspend()
resume()
send(msg)
A PeerConnection
:
- MUST listen for data on the given NetAddress using an InboundConnection;
- MUST sequentially try to connect to one of the peer's NetAddresses until one succeeds or all fail using an OutboundConnection;
- MUST immediately reject and dispose of a multipart message not consisting of four parts, as detailed in MessageEnvelope;
- MUST construct a MessageEnvelope from the multiple parts;
- MUST pass the constructed MessageEnvelope to the message handler;
- MUST transition to
Connecting
state and retry the connection, should a connection drop; - MUST send a
net::Disconnect
message and drop the connection when a shutdown signal is received.
ConnectionManager
The ConnectionManager manages a set of live PeerConnections. It provides an abstraction for other components to initiate and use PeerConnections without having to worry about attaching the new PeerConnection to message handlers.
It consists of a list of active peer connections and an inproc
message handler socket. This socket is 'written to' whenever
a message is received from any active PeerConnection for other components to act on.
Methods may include:
establish_connection(Peer)
- create and return a new PeerConnection;disconnect(peer)
- disconnect a particular peer;suspend()
- temporarily suspend connections;resume()
- temporarily suspend connections;shutdown
- cleanly shut down all PeerConnections.
The ConnectionManager
:
- MUST call
suspend
on every PeerConnection if itssuspend
method is called; - MUST call
resume
on every PeerConnection if itsresume
method is called; - MUST call
shutdown
on every PeerConnection if itsshutdown
method is called - MUST create a new PeerConnection with the given Peer and NetAddress, when
establish_connection
is called; - MUST call
shutdown
on the PeerConnection and remove the connection for the given peer, whendisconnect(peer)
is called; - MAY disconnect peers if the connection has not been used for an extended period;
- SHOULD disconnect the least recently used peer if the connection pool is greater than
max connections
ControlService
The purpose of this service is to negotiate a new secure PeerConnection.
The control service accepts a single message:
peer::EstablishConnection(pk, curve_pk, net_address)
.
A ControlService:
- MUST listen for connections on a predefined CONTROL PORT;
- SHOULD deny connections from banned peers.
The steps to establish a peer connection are as follows:
Alice wants to connect to Bob
- Alice creates a
PeerConnection
to which Bob can connect.- A new CURVE key pair is generated.
- Alice connects to Bob's control server and Bob accepts the connection.
- Alice sends a
peer::establish_connection
message, with:- the CURVE public key for the socket connection;
- the node's public key corresponding to its Node ID; and
- the NetAddress of the new PeerConnection.
- Bob accepts this request and opens a new
PeerConnnection
socket using Alice's CURVE public key. - Bob connects to the given NetAddress and sends a
peer::establish_connection
message. - If Alice accepts the connection, they can begin sending messages. If not, both sides terminate the connection.
PeerManager
The PeerManager is responsible for managing the list of peers with which the node has previously interacted. This list is called a routing table and is made up of Peers.
The PeerManager can
- add a peer to the routing table;
- search for a peer given a node ID, public key or NetAddress;
- delete a peer from the list;
- persist the peer list using a storage backend;
- restore the peer list from the storage backend;
- maintain lightweight views of peers, using a filter criterion, e.g. a list of peers that have been banned, i.e. a denylist; and
- prune the routing table based on a filter criterion, e.g. last date seen.
MessageDispatcher
The MessageContext contains:
- the requesting PeerConnection;
- the MessageHeader;
- the deserialized message;
- the OutboundMessageService.
Basically, all the tools the handler needs to interact with the network.
A MessageDispatcher is responsible for:
- constructing the MessageContext;
- finding the message handler that is associated with the MessageType;
- passing the MessageContext to the handler; and
- ignoring the message if the handler cannot be found.
An example API may be:
#![allow(unused)] fn main() { let dispatcher = MessageDispatcher::<MessageType>::new() .middleware(logger) .route(BlockchainMessageType::NewBlock, BlockHandlers::store_and_broadcast) ... .route(NetMessageType::Ping, send_pong); inbound_msg_service.set_handler(dispatcher.handler); }
InboundMessageService
InboundMessageService is a service that receives messages over a non-blocking asynchronous socket and determines what to do with it. There are three options: handle, forward and discard.
A pool of worker threads (with a configurable size) is started and each one listens for messages on its $1:n$ inproc
message
socket. A ZMQ_DEALER
socket is suggested for fair-queueing work amongst workers, who listen for work with a ZMQ_REP
.
The workers read off this socket and process the messages.
An InboundMessageService:
- MUST receive messages from all PeerConnections; and
- MUST write the message to the worker socket.
A worker:
- MUST deserialize the MessageHeader.
- If unable to deserialize, MUST discard the message.
- MUST check the message signature.
- MUST discard the message if the signature is invalid.
- MUST discard the message if the signature has been processed within the DuplicateMessageWindow.
- If the encryption flag is set:
- MUST attempt to decrypt the message.
- If successful, process and handle the message.
- Otherwise, MUST forward the message using the
Random
BroadcastStrategy. - If the message is not encrypted, MUST discard it.
- MUST attempt to decrypt the message.
- If the destination node ID is set:
- If the destination matches this node's ID - process and handle the message.
- If the destination does not match this node's ID - MUST forward the message using the
Closest
BroadcastStrategy.
- If the destination is not set
- If the MessageType is a kind of propagation message:
- MUST handle the message;
- MUST forward the message using the
Random
BroadcastStrategy.
- If the MessageType is a kind of encrypted message:
- MUST attempt to decrypt and handle the message;
- if successful, MUST handle the message;
- if unsuccessful, MUST forward the message using the
Random
orFlood
BroadcastStrategy,
- If the MessageType is a kind of propagation message:
OutboundMessageService
OutboundMessageService is responsible for using the connection and peer infrastructure to send messages to the rest of the network.
In particular, it is responsible for:
- serializing the message body;
- constructing the MessageEnvelope;
- executing the required BroadcastStrategy; and
- sending messages using the [ConnectionManager].
The actual sending of messages can be requested via the public send_message
method, which takes a
MessageHeader, MessageBody and BroadcastStrategy as parameters.
send_message
then selects an appropriate peer(s) from the ConnectionManager according to the
BroadcastStrategy and sends the message to each of the selected peers.
BroadcastStrategy determines how a set of peer nodes will be selected and can be:
Direct
- send to a particular peer matching the given node ID;Flood
- send to all known peers who are not [communication clients];Closest
- send to $n$ closest peers who are not [communication clients]; orRandom
- send to a random set of peers of size $n$ who are not [communication clients].
Privacy Features
The following privacy features are proposed:
- A communication node or communication client MAY communicate solely over the Tor/I2P networks.
- All traffic (with the exception of the control service) MUST be encrypted.
- Messages MAY encrypt the body of a MessageEnvelope, which only a particular recipient can decrypt.
- The
destination
header field can be omitted when used in conjunction with body encryption; the destination is completely unknown to the rest of the network.
Store and Forward Strategy
Sometimes it may be desirable for messages to be sent without a destination node/client being online. This is especially important for a modern chat/messaging application.
The mechanism for this is proposed as follows:
Each communication node MUST allocate some disk space for storage of messages for offline recipients. Only some allowlisted MessageTypes are permitted to be stored. A sender sends a message destined for a particular node ID to its closest peers, which forward the message to their closest peers, and so on.
Eventually, the message will reach nodes that either know the destination or are very close to the destination. These nodes MUST store the message in some pending message bucket for the destination. The maximum number of buckets and the size of each bucket SHOULD be sufficiently large as to be unlikely to overflow, but not so large as to approach disk space problems. Individual messages should be small and responsibilities for storage spread over the entire network.
A communication node
- MUST store messages for later retransmission, if all of the following conditions are true:
- the MessageType is permitted to be stored;
- there are fewer than $n$ closer online peers to the destination.
- MUST retransmit pending messages when a closer peer comes online or is added to the routing table.
- MAY remove a bucket, in any of the following conditions:
- The bucket is empty;
- A configured maximum number of buckets has been reached. Discard the bucket with the earliest creation timestamp.
- The number of closer online peers to the destination is equal to or has exceeded $n$.
- MAY expire individual messages after a sufficiently long Time to Live (TTL)
This approach has the following benefits:
- When a destination comes online, it will receive pending messages without having to query them.
- The "closer within a threshold" metric is simple.
- Messages are stored on multiple peers, which makes it less likely for messages to disappear as nodes come and go (depending on threshold $n$).
Queue Overflow Strategy
Inbound/OutboundConnections (and therefore PeerConnection) have an HWM set.
If the HWM is hit:
- any call to
send()
should return an error; and - incoming messages should be silently discarded.
Outstanding Items
- A PeerConnection will probably need to implement a heartbeat to detect if a peer has gone offline.
- InboundConnection(Service) may want to send small replies (such as OK, ERR) when the message has been accepted or rejected.
- OutboundConnection(Service) may want to receive and handle small replies.
- Encrypted communication for the ControlService would be better privacy, but since ZMQ requires a CURVE public key before the connection is bound, a dedicated "secure connection negotiation socket" would be needed.
- Details of distributed message storage.
- Which NetAddress to use if a peer has many.
RFC-0173/Versioning
Versioning
Maintainer(s): Philip Robinson
Licence
Copyright 2021 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to describe the various types of versioning that nodes on the Tari network will use during interaction with other nodes.
Related Requests for Comment
- RFC-0710: Tari Communication Network and Network Communication Protocol
- RFC-0171: MessageSerialization
Description
In a decentralized system the set of nodes on the network will run a variety of software versions as time goes on. Some of these versions will be compatible and others not. For example, if a crucial consensus change is added during a hard fork event. Furthermore, there will be multiple networks running Tari code, i.e. Mainnet vs Testnet. Versioning refers to the strategies we will use for nodes to determine if they can communicate.
Tari will contain three different versioning schemes:
- WireMode is the first byte a peer sends when connecting to another peer, used to identify the network and/or protocol bytes that follow
- P2P message versions that will accompany every P2P message,
- Consensus rules versions that will be exchanged on connection and are included in each block header.
WireMode byte
In the Bitcoin P2P protocol messages are preceded by 4 magic values or bytes. These values are used to delimit when a new message starts in a byte stream and also are used to indicate which of the Bitcoin networks the node is speaking on, such as TestNet or Mainnet.
Tari message packets are encapsulated using the Noise protocol so we do not need the delimiting functionality of these bytes but Tari will include a single WireMode byte at the beginning of every connection session. This byte will indicate which network a node is communicating on, so that if the counterparty is on a different network it can reject this connection cheaply without having to perform any further operations, like completing the Noise protocol handshake.
The following is a proposed mapping of the WireMode byte. Space is left between Mainnet, Stagenet and Testnet bytes for future use.
#[repr(u8)]
enum Network {
Mainnet = 0x00,
Stagenet1 = 0x51,
Testnet1 = 0xa1,
Testnet2 = 0xa2
}
P2P message version
Peer to Peer messages on the Tari network are encapsulated into message envelopes. The body of message envelopes are defined, serialized and deserialized using Protobuf. These messages will only be updated by adding new fields to the Protobuf definitions, never removing fields. This is done in order to preserve backwards compatibility where newer nodes can still communicate with older nodes.
The P2P messaging protocol will see many changes in its lifetime. Some will be minor changes that are fully backwards
compatible and some changes will be breaking where older nodes will not be able to communicate with newer nodes. In
order to document these two classes of changes each P2P message header will contain a version
field that will use
a two-part semantic versioning scheme with the format of major.minor
integer versions. The minor
version will be
incremented whenever there is any change. The major
version be incremented when there is a breaking change made to
the P2P protocol. Each integer can be stored separately.
Consensus version
The final aspect of the Tari system that will be versioned are the Consensus rules. These rules will change as the network matures. Changes to consensus rules can be achieved using either a Soft fork or Hard fork. Soft forks are where new consensus rules are added that older nodes will see as valid (thus backwards compatible) but newer nodes will reject blocks from older nodes that are not aware of the new consensus rules. A hard fork means that the new consensus rules are not backwards compatible and so only nodes that know about the new rules will be able to produce and validate new transactions and blocks.
The consensus version will be used by a node to determine if it can interact with another node successfully or not. A
list of fork versions will be maintained within the code. When a connection is started with a new node the two nodes
will exchange Version
messages detailing the consensus version they are each running and the blockheight at which they
are currently operating. Both nodes will need to reply with a Version Acknowledge
message to confirm that they are
compatible with the counterparty's version. It is possible for a newer node to downgrade its protocol to speak to an
older node so this must be decided during this handshake process. Only once the acknowledgments have been exchanged can
further messages be exchanged by the parties. This is the method currently employed on the
Bitcoin network
For example, if we have two nodes, Node A and Node B, where Node A is ahead of Node B in version and block height. During the handshake Node B will not recognize Node A's version but should wait for Node A to reject or confirm the connection because Node A could potentially downgrade their version to match Node B's. Node A will speak to Node B if and only if Node A recognizes Node B's version and Node B's block height is in the correct range for its advertised version according to Node A's fork version list.
Tari Block Headers contain a version
field which will be used to indicate the version of consensus rules that are
used in the construction and validation of this block. Consensus rules versions will only consist of breaking changes
and as such will be represented with a single incremented integer. This coupled with the internal list of fork versions,
that includes the height at which they came into effect, will be used to validate whether the consensus rules specified
in the header are valid for that block's height.
RFC-0180: Bulletproof range proof rewinding
Bulletproof range proof rewinding
Maintainer(s): Hansie Odendaal
Licence
Copyright 2020 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
This Request for Comment (RFC) presents a proposal for Bulletproof range proof rewinding in the Tari blockchain to enable advanced usages like wallet recovery and one‑sided payments.
Related Requests for Comment
Introduction
We use dalek-cryptography/bulletproofs
in the Tari project and have a need to do wallet recovery from seed values and
also to recover the value in the value commitment from the Unspent Transaction Output (UTXO). Pull requests
PR#340 for the dalek-cryptography/bulletproofs
crate
and PR#6 for the zkcrypto/bulletproofs
crate were submitted to add
Bulletproofs rewinding functionality to the Bulletproofs crate as a user option.
The methodology presented here is closely modelled on Grin's solution as discussed here, but using two private keys instead of one.
Rewind Scheme
Bulletproofs per say are not be discussed in this RFC, only how the rewinding scheme works. Readers who require background information on Bulletproofs can read the excellent documentation created by the Dalek team here. Important to note is that Dalek only implemented the aggregated Multiparty Computation Protocol (MCP) for range proofs and that proving a single range proof is handled a special case.
Constructing a rewindable Bulletproof range proof
Our scheme is discussed with reference to the Party and Dealer's algorithm and using notation defined here.
In this scheme three additional parameters are introduced when creating a range proof for a Pedersen commitment (termed value commitment by Dalek because it is a commitment to the value of the token):
- Private rewind key: $ r_{key} $
- Private blinding key: $ b_{key} $
- Twenty three (23) bytes proof message: $ p_{msg} $.
The 23 bytes worth of proof message can be any message a user wants to embed within the proof. Internally the two private keys, in combination with the value commitment, are converted into two rewind nonces and two blinding nonces:
- Rewind nonce 1: $ r_{n1} = \text{H}( \ \text{H}(r_{key} \cdot \widetilde{B}) \ || \ V_{(j)} \ ) $
- Rewind nonce 2: $ r_{n2} = \text{H}( \ \text{H}(b_{key} \cdot \widetilde{B}) \ || \ V_{(j)} \ ) $
- Blinding nonce 1: $ b_{n1} = \text{H}( \ \text{H}(r_{key}) \ || \ V_{(j)} \ ) $
- Blinding nonce 2: $ b_{n2} = \text{H}( \ \text{H}(b_{key}) \ || \ V_{(j)} \ ) $
These four values are seen as nonces due to the fact that each value commitment is unique, whereas the $ r_{key} $ and $ b_{key} $ can be used over and over without leaking any information.
The value $ v_{(j)} $ is an 8 byte word, and $ p_{msg} $ is a 23 byte word. The bytes of these two words can be concatenated to form a 32 byte word and when XORed with $ r_{n2} $ , it can be used to embed the value and proof message. $ r_{n2} $ is modified as follows:
$$ \begin{aligned} r^\backprime_{n2} = r_{n2} \ \mathbin{\oplus} \ (v_{(j)_{\ bytes \ 1..8}} \ || \ p_{msg_{\ bytes \ 9..31}} ) \end{aligned} \tag{1} $$
Consider the start of the protocol where each party $ j $ computes three commitments: to the value $ v_{(j)} $, to the bits of that value $ \mathbf{a}_{L, (j)}, \mathbf{a}_{R, (j)} $, and to the per-bit blinding factors $ \mathbf{s}_{L, (j)}, \mathbf{s}_{R, (j)} $:
$$ \begin{aligned} V_{(j)} &\gets \operatorname{Com}(v_{(j)}, {\widetilde{v}_{(j)}}) && = v_{(j)} \cdot B + {\widetilde{v}_{(j)}} \cdot {\widetilde{B}} \\ A_{(j)} &\gets \operatorname{Com}({\mathbf{a}}_{L, (j)}, {\mathbf{a}}_{R, (j)}) && = {\langle {\mathbf{a}}_{L, (j)}, {\mathbf{G}_{(j)}} \rangle} + {\langle {\mathbf{a}}_{R, (j)}, {\mathbf{H}_{(j)}} \rangle} + {\widetilde{a}_{(j)}} {\widetilde{B}} \\ S_{(j)} &\gets \operatorname{Com}({\mathbf{s}}_{L, (j)}, {\mathbf{s}}_{R, (j)}) && = {\langle {\mathbf{s}}_{L, (j)}, {\mathbf{G}_{(j)}} \rangle} + {\langle {\mathbf{s}}_{R, (j)}, {\mathbf{H}_{(j)}} \rangle} + {\widetilde{s}_{(j)}} {\widetilde{B}} \\ \end{aligned} \tag{2} $$
where $ \widetilde{v}_{(j)}, \widetilde{a}_{(j)}, \widetilde{s}_{(j)} $ are sampled randomly from $ {\mathbb Z_p} $. (Note that $ \widetilde{v}_{(j)} $ is the blinding factor of the value commitment.)
In our scheme:
- blinding factor $ {\widetilde{a}_{(j)}} $ is replaced by $ r_{n1} $
- blinding factor $ {\widetilde{s}_{(j)}} $ is replaced by $ r^\backprime_{n2} $
Consider where the party commits to the terms $ t_{1, (j)}, t_{2, (j)} $:
$$ \begin{aligned} T_{1, (j)} &\gets \operatorname{Com}(t_{1, (j)}, {\tilde{t}_{(j1}}) && = t_{1, (j)} \cdot B + {\tilde{t}_{1, (j)}} \cdot {\widetilde{B}} \\ T_{2, (j)} &\gets \operatorname{Com}(t_{2, (j)}, {\tilde{t}_{2, (j)}}) && = t_{2, (j)} \cdot B + {\tilde{t}_{2, (j)}} \cdot {\widetilde{B}} \end{aligned} \tag{3} $$
where $ \tilde{t}_{1, (j)}, \tilde{t}_{2, (j)} $ are sampled randomly from $ {\mathbb Z_p} $.
In our scheme:
- blinding factor $ \tilde{t}_{1, (j)} $ is replaced by $ b_{n1} $
- blinding factor $ \tilde{t}_{2, (j)} $ is replaced by $ b_{n2} $
The synthetic blinding factors calculation below is key, as it will be used to extract the data when playing the Bulletproof in reverse:
$$ \begin{aligned} {\tilde{t}}_{(j)}(x) &\gets z^{2} {\tilde{v}}_{(j)} + x {\tilde{t}}_{1, (j)} + x^{2} {\tilde{t}}_{2, (j)} \\ \end{aligned} \tag{4} $$
$$ \begin{aligned} \tilde{e}_{(j)} &\gets {\widetilde{a}}_{(j)} + x {\widetilde{s}}_{(j)} \end{aligned} \tag{5} $$
In the end, the complete range proof consists of these elements:
$$ \begin{aligned} \lbrace A, S, T_1, T_2, t(x), {\tilde{t}}(x), \tilde{e}, L_k, R_k, \dots, L_1, R_1, a, b \rbrace \end{aligned} \tag{6} $$
Note: This scheme has been improved in what has been presented in by Grin after being commented on by Dalek, by not using the same rewind nonce for $ {\widetilde{a}_{(j)}} $ and $ {\widetilde{s}_{(j)}} $ nor the same blinding nonce for $ \tilde{t}_{1, (j)} $ and $ \tilde{t}_{2, (j)} $.
Extracting data
Note the presence of $ {\tilde{t}}_{(j)} $ and $ \tilde{e} $ in (6). The Dalek Bulletproofs are constructed using Merlin Transcripts to automate the Fiat-Shamir transform, so that non-interactive protocols can be implemented as if they were interactive. The prover adds each step of the Bulletproof range proof creation to the protocol transcript, so the verifier has to do the same.
The extraction procress starts by adding the values $ A $ and $ S $ are to the protocol transcript to obtain challenge scalars $ z $ and $ x $ from the transcript.
There after, $ {\widetilde{s}_{(j)}} $ is extracted from (5) by replacing $ {\widetilde{a}_{(j)}} $ with $ r_{n1} $ :
$$ \begin{aligned} {\widetilde{s}}_{(j)} = ( \tilde{e}_{(j)} - r_{n1} ) \cdot \frac{1}x \end{aligned} \tag{7} $$
Next, the value and proof message are extracted from $ {\widetilde{s}_{(j)}} $ when XORed with $ r_{n2} $ :
$$ \begin{aligned} v_{(j)} &= ( r_{n2} \ \mathbin{\oplus} \ {\widetilde{s}}_{(j)} ) | _{\ bytes \ 1..8} \\ p_{msg} &= ( r_{n2} \ \mathbin{\oplus} \ {\widetilde{s}}_{(j)} ) | _{\ bytes \ 9..31} \end{aligned} \tag{8} $$
Finally, the blinding factor is extracted from (4):
$$ \begin{aligned} \widetilde{v} = \frac{1}{z^2} \cdot ( {\tilde{t}}(x) - x \cdot \tilde{t}_{1, (j)} - x^2 \cdot \tilde{t}_{2, (j)} ) \end{aligned} \tag{9} $$
Some notes on usage and use cases
Rewinding a Bulletproof can take place according to one or both of these steps:
- Peak value only: Using this step returns the value and proof message only, but returning garbage data if the wrong rewind nonces are provided, or,
- Rewind fully: Using this step returns the value, blinding factor and proof message, returning an error if the wrong rewind and blinding nonces are provided. Note that this step is independent from peaking the value only, thus do not have ot be preceded by it. If many range proofs need to be scanned to uncover those that belong to a particuler wallet, peaking the value only before fully rewinfing it will provide a performance benefit.
The main use case has to do with wallet recovery. A user would normally have a backup of their unique wallet seed words somewhere, but could more easily lose their entire wallet without having made any backups or only having old backups. If a wallet can derive one or more sets of private keys from the seed words and use them in every UTXO construction as proposed, it can enable wallet recovery using Bulletproof rewinding.
A secondary use case would be for trusted 3rd parties to identify spending, by only having access to the public rewind key and the embedded proof message. The public rewind keys can be shared with a 3rd party out of band. The owner and/or delegated 3rd party can then use these keys in conjunction with a specific value commitment to calculate candidate rewind nonces for its proof. The returned proof message from the peak value only rewind step can be used to narrow down the probability that the particular proof belongs to a specific collection. In this mode the owner alone will be able to use both sets of pub-pvt key pairs in conjunction with a specific value commitment to calculate candidate rewind and blinding nonces for its proof. The rewind fully step will reveal the details of the value commitment and proof message if successful.
The use for this protocol, as opposed to simply revealing the original value along with the blinding factor to whoever wants the plain value, is to protect the UTXO. In Mimblewimble, if the value commitment can be opened, it can be spent without the owners knowledge.
The proof message is private or can be shared with a trusted 3rd party in the same way one would share the public rewind keys, but not common public knowledge. It is totally arbitrary, but known data, to enable identifying beyond a doubt if the returned value $ v_{(j)} $ is from a specific collection of value commitments $ V_{(j)} $.
Implementation
Using the Application Programmers Interface (API) it is possible to:
- create a rewindable Zero-knowledge (ZK) proof with up to 23 bytes of additional embedded proof message $ p_{msg} $ ;
- extract the value $ v_{(j)} $ and 23 bytes proof messsage $ p_{msg} $ only;
- extract the value $ v_{(j)} $ , blinding factor $ \widetilde{v} $ and 23 bytes proof messsage $ p_{msg} $ .
Credits
- @jaspervdm for his improved bulletproof rewind scheme, used as precurser.
- @cathieyun for provifing valuable feedback to improve this scheme.
Mempool
The Mempool for Unconfirmed Transactions on the Tari Base Layer
Maintainer(s): Yuko Roodt
License
Copyright 2018 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
The purpose of this document and its content is for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community regarding the technological merits of the potential system outlined herein.
Goals
This document will introduce the Tari base layer Mempool that consists of a Transaction Pool, Pending Pool, Orphan Pool and Reorg Pool. The Mempool is used for storing and managing unconfirmed and time-lock restricted transactions.
Related RFCs
Description
Assumptions
- Each base node is connected to a number of peers that maintain their own copies of the Mempool.
Abstract
The Mempool is responsible for managing, verifying and maintaining all unconfirmed transactions that have not yet been included in a block and added to the Tari blockchain. It consists of a Transaction Pool, Pending Pool, Orphan Pool and Reorg Pool to achieve these tasks. It is also responsible for propagating valid transactions and sharing the Mempool state with connected peers. An overview of the required functionality for the Mempool and each of its component pools will be provided.
Overview
Every base node maintains a Mempool that consists of four separate pools: the Transaction Pool, Pending Pool, Orphan Pool and Reorg Pool. These four pools have different tasks and work together to form the Mempool used for maintaining unconfirmed transactions.
This is the role descriptions for each component pool:
- Transaction Pool: contains all unconfirmed transactions that have been verified, have passed all checks, that only spend valid UTXOs and don't have any time-lock restrictions.
- Pending Pool: contains unconfirmed transactions that have time-lock restrictions. The transactions in this pool either attempt to spend UTXOs with time-locks or the transactions themselves have time-locks limiting them from being included in new blocks until a specified future time or block height has been reached.
- Orphan Pool: out of order unconfirmed transactions are managed by this pool, these transactions attempt to spend non-existent UTXOs.
- Reorg Pool: stores a backup of all transactions that have recently been included into blocks, in case a blockchain reorganization occurs and these transactions have to be restored to the Transaction Pool so that they can be included in future blocks.
Prioritizing Unconfirmed Transactions
The maximum storage capacity used for storing unconfirmed transactions by the Mempool and each of its component pools can be configured. If a new transaction is received and the storage capacity limits have been reached, then transactions are prioritized according to the transaction priority metric. The transaction priority metric consist of the fees and the maturity of the UTXOs being spent by the transaction and should be applied to determine the priority of each transaction in the Mempool. As the allocated storage space of the Mempool becomes limited, the transactions with the highest priority are kept in the pool. The lowest priority transactions are discarded to make room for higher priority incoming transactions.
The transaction priority metric has the following behavior:
- Transactions spending UTXOs with higher block height maturity SHOULD be prioritized over transactions spending UTXOs with lower block height maturity.
- Transactions with higher fees per transaction message size SHOULD be prioritized over lower fee transactions.
Syncing and Updating of the Memory Pool State
On the initial startup of the Mempool, the complete state of the Mempool that consists of the Transaction Pool, Pending Pool, Orphan Pool and Reorg Pool can be requested and downloaded from the connected peers. A memory pool typically doesn't make use of persistent storage but could be configured to keep a backup of the last known state. If an existing Mempool state is locally available then a more efficient update process can be performed by requesting only the unconfirmed transactions missing from the current Mempool state. When no state is available then the entire Mempool state must be downloaded from the connected peers. During downloading or updating of the Mempool state, the validity of all transaction in the pool must be verified and the priority of each transaction must be calculated.
Functional behavior required for sharing and updating of the Mempool state, and propagation of transactions between peers:
- All verified transaction MUST be propagated to neighboring peers.
- Duplicate transactions MUST NOT be propagated to peers.
- Unvalidated or invalid transactions MUST NOT be propagated to peers.
- Verified transactions that were discarded due to low priority levels MUST be propagated to peers.
- The Mempool MUST have an interface that can be used by neighboring peers to query the content and state of the memory pool.
- It MUST have a mechanism that enables peers to download the memory pool state in full or in part.
- It MUST accept all transactions received from peers but MAY decide to discard low priority transactions.
- It MUST allow wallets to track payments by monitoring that a particular transaction has been added to the Mempool.
- A Mempool MAY choose:
- to discard the Mempool state on restarts and then download the full state from its peers or
- to store the state of the Mempool using persistent storage to reduce communication bandwidth required when reinitializing the Mempool after a restart.
Transaction Pool
The Transaction Pool consists of all unconfirmed transactions that have been received, verified and have passed all checks. These unconfirmed transactions in the Transaction Pool are ready to be included and can be used to construct new blocks for the Tari blockchain.
Functional behavior required of the Transaction Pool:
- It MUST verify that incoming transactions only spend existing UTXOs.
- It MUST ensure that incoming transactions don't have a processing time-lock or has a time-lock that has expired.
- It MUST ensure that all time-locks of the UTXOs that will be spent by the transaction have expired.
- Transactions that have been used to construct new blocks MUST be removed from the Transaction Pool and added to the Reorg Pool.
Pending Pool
The Pending Pool contains all transactions that are restricted by time-locks. A transaction could have a time-lock limiting it from being processed or it can attempt to spend UTXOs with time-locks. These transactions require their time-locks or the time-locks of the input UTXOs to expire before they can be processed and included into new blocks. All transactions in the Pending Pool have been verified and passed all checks except their own time-lock has not yet expired or some of the UTXOs that will be spent have time-lock restrictions that are not yet valid. Once the transactions time-lock or the time-locks on the UTXOs have expired then the Pending Pool transactions can be moved to the Transaction Pool for inclusion in future blocks.
Functional behavior required of the Pending Pool:
- Once the transaction time-lock or UTXO time-lock restricting the processing of a transaction has expired then the pending transaction MUST be moved to the Transaction Pool.
Orphan Pool
The Orphan Pool contains all the received transactions that have passed all the verification steps and checks, except they attempt to spend UTXOs that don't exist. A possible reason these UTXOs do not yet exist is that they may not yet have been created and might exist in the future. Typically, these orphaned transactions are from a series or bundle of transactions that need to be processed in a specific order but
- have been either received out of order,
- or the order of processing the bundled transactions might not have been known or specified.
As transactions are processed and the missing UTXOs have been created, then the orphaned transactions can be moved to the Transaction Pool for possible inclusion in future blocks. Another possibility why the input UTXOs might not be available is that the UTXOs were double spent by other transactions. In time, the double spent transactions will be discarded from the Orphan Pool once they have reached the appropriate maturity threshold.
Functional behavior required of the Orphan Pool:
- Each newly received transaction MUST be verified and pass all checks except the UTXO validity check before it is placed in the Orphan Pool.
- Orphaned transactions must be upgraded and moved to the Transaction Pool once the previously unavailable UTXOs become available.
- Orphaned transactions that have surpassed the expiration time threshold MUST be removed from the Orphan Pool.
Reorg Pool
The Reorg Pool consists of all unconfirmed transaction that have recently been added to blocks, resulting in their removal from the Transaction Pool. When a potential blockchain reorganization occurs that invalidates previously assembled blocks, the transactions used to construct these discarded blocks can be recovered from the Reorg Pool and can be added back into the Transaction Pool. This will ensure that high priority transactions are not lost during blockchain reorganization but can be added into future blocks without retransmission of these transactions.
Functional behavior required of the Reorg Pool:
- Copies of the verified transactions removed from the Transaction pool that were placed in blocks MUST be stored in the Reorg Pool.
- Transactions in the Reorg pool MAY be removed after the threshold expiration time has been reached.
- When a blockchain reorganization is detected, all affected transactions from the Reorg Pool MUST be moved to the Transaction Pool.
Mempool
The Mempool manages the four component pools and interacts with peers to share and retrieve transactions and the Mempool state. During the operation of the Mempool it will distribute incoming transactions to the appropriate component pools. When a new incoming transaction is received a number of checks and verification steps need to be performed to determine if the transaction can be added to the Mempool and determine which of the component pools should be responsible for that particular transaction. Only when the new transaction has passed these checks can it be added to the Mempool and should it be propagated to the connected peers.
Functional behavior required of the Mempool:
- If a duplicate transaction is received, that already exist in the Mempool, then the duplicate copy MUST be discarded.
- When considering transactions that attempt to double spend UTXOs, the highest priority transaction MUST be kept and any other transactions that spend the same UTXO MUST be discarded.
- When the storage limit of the Mempool has been reached, new incoming transactions SHOULD be prioritized according to the Priority metric.
- Lower priority Mempool transactions MUST be discarded to make room for higher priority incoming transactions.
- Incoming transactions with lower priorities than the minimum transaction priority in the Mempool MUST be discarded.
- The Mempool MUST verify that incoming transactions do not have duplicate outputs.
- It MUST check that all coinbase outputs that will be spent have matured sufficiently.
- The distribution of storage space allocated to each component pool in the memory pool MAY be configured and adjusted.
- The memory pool SHOULD have a mechanism to estimate fee categories from the current Mempool state. As an example, a priority fee can be estimated that will ensure that a new transactions will have the appropriate priority to be added into a new block in a timely manner.
Functional behavior required for distributing incoming transactions to the component pools:
- Verified transactions that have passed all checks such as spending of valid UTXOs and expired time-locks MUST be placed in the Transaction Pool
- All transactions that attempt to spend UTXOs with valid time-locks MUST be added to the Pending Pool.
- Incoming transactions with time-locks prohibiting them from being included into new blocks should be added to the Pending Pool.
- Newly received verified transaction attempting to spend a UTXO that does not yet exist MUST be added to the Orphan pool.
- Transactions that have been added to blocks and were removed from the Transaction Pool should be added to the Reorg Pool.
Tari-specific Base Layer Extensions
This section covers RFCs that describe extensions to the Mimblewimble protocol that enable key functionality for the Tari Base layer token and Digital Asset network.
- RFC-0220: Asset Checkpoints
- RFC-0230: Time related transactions
- RFC-0201: TariScript
- RFC-0250: Covenants
- RFC-0322: Validator Node Registration
- RFC-0341: Asset Registration
RFC-0201/TariScript
TariScript
Maintainer(s): Cayle Sharrock
Licence
Copyright 2020 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
This Request for Comment (RFC) presents a proposal for introducing TariScript into the Tari base layer protocol. Tari Script aims to provide a general mechanism for enabling further extensions such as side chains, the DAN, one-sided payments and atomic swaps.
Related Requests for Comment
- RFC-0200: Base Layer Extensions
- RFC-0202: TariScript Opcodes
- RFC-0300: The Tari Digital Assets Network
$$ \newcommand{\script}{\alpha} % utxo script \newcommand{\input}{ \theta } \newcommand{\cat}{\Vert} \newcommand{\so}{\gamma} % script offset \newcommand{\hash}[1]{\mathrm{H}\bigl({#1}\bigr)} $$
Introduction
It is hopefully clear to anyone reading these RFCs that the ambitions of the Tari project extend beyond a Mimblewimble-clone-coin. It should also be fairly clear that vanilla Mimblewimble does not have the feature set to provide functionality such as:
- One-sided payments
- Multiparty side-chain peg outs and peg-ins
- Generalised smart contracts
Extensions to Mimblewimble have been proposed for most of these features, for example, David Burkett's one-sided payment proposal for LiteCoin (LIP-004), this project's HTLC RFC and the pegging proposals for the Clacks side-chain.
Some smart contract features are possible, or partly possible in vanilla [Mimblewimble] using Scriptless script, such as
- Atomic swaps
- Hash time-locked contracts
This RFC makes the case that if Tari were to implement a scripting language similar to Bitcoin script, then all of these use cases will collapse and can be achieved under a single set of (relatively minor) modifications and additions to the current Tari and Mimblewimble protocol.
Scripting on Mimblewimble
To the author's knowledge, none of existing [Mimblewimble] projects have employed a scripting language, nor are there ambitions to do so.
Grin styles itself as a "Minimal implementation of the Mimblewimble protocol", so one might infer that this status is unlikely to change soon.
Beam recently announced the inclusion of a smart contract protocol, which allows users to execute arbitrary code (shaders) in a sandboxed Beam VM and have the results of that code interact with transactions.
Mimblewimble coin is a fork of Grin and "considers the protocol ossified".
Litecoin is in the process of adding Mimblewimble as a side-chain. As of this writing, there appear to be no plans to include general scripting into the protocol.
Scriptless scripts
Scriptless script is a wonderfully elegant technology and inclusion of TariScript does not preclude the use of Scriptless script in Tari. However, scriptless scripts have some disadvantages:
- They are often difficult to reason about, with the result that the development of features based on scriptless scripts is essentially in the hands of a very select group of cryptographers and developers.
- The use case set is impressive considering that the "scripts" are essentially signature wrangling, but is still somewhat limited.
- Every feature must be written and implemented separately using the specific and specialised protocol designed for that feature. That is, it cannot be used as a dynamic scripting framework on a running blockchain.
TariScript - a brief motivation
The essential idea of TariScript is as follows:
Given a standard Tari UTXO, we add additional restrictions on whether that UTXO can be included as a valid input in a transaction.
As long as those conditions are suitably committed to, are not malleable throughout the existence of the UTXO, and one can prove that the script came from the UTXO owner, then these conditions are not that different to the requirement of having range proofs attached to UTXOs, which require that the value of Tari commitments is non-negative.
This argument is independent of the nature of the additional restrictions. Specifically, if these restrictions are manifested as a script that provides additional constraints over whether a UTXO may be spent, the same arguments apply.
This means that in a very hand-wavy sort of way, there ought to be no reason that TariScript is not workable.
Note that range proofs can be discarded after a UTXO is spent. This entails that the global security guarantees of Mimblewimble are not that every transaction in history was valid from an inflation perspective, but that the net effect of all transactions lead to zero spurious inflation. This sounds worse than it is, since locally, every individual transaction is checked for validity at the time of inclusion in the blockchain.
If it somehow happened that two illegal transactions made it into the blockchain (perhaps due to a bug), and the two cancelled each other out such that the global coin supply was still correct, one would never know this when doing a chain synchronisation in pruned mode.
But if there was a steady inflation bug due to invalid range proofs making it into the blockchain, a pruned mode sync would still detect that something was awry, because the global coin supply balance acts as another check.
With TariScript, once the script has been pruned away, and then there is a re-org to an earlier point on the chain, then there's no way to ensure that the script was honoured unless you run an archival node.
This is broadly in keeping with the Mimblewimble security guarantees that, in pruned-mode synchronisation, individual transactions are not necessarily verified during chain synchronisation.
However, the guarantee that no additional coins are created or destroyed remains intact.
Put another way, the blockchain relies on the network at the time to enforce the TariScript spending rules. This means that the scheme may be susceptible to certain horizon attacks.
Incidentally, a single honest archival node would be able to detect any fraud on the same chain and provide a simple proof that a transaction did not honour the redeem script.
Additional requirements
The assumptions that broadly equate scripting with range proofs in the above argument are:
- The script must be committed to the blockchain.
- The script must not be malleable in any way without invalidating the transaction. This restriction extends to all participants, including the UTXO owner.
- We must be able to prove that the UTXO originator provides the script and no-one else.
- The scripts and their redeeming inputs must be stored on the block chain. In particular, the input data must not be malleable.
The next section discusses the specific proposals for achieving these requirements.
Protocol modifications
Please refer to Notation, which provides important pre-knowledge for the remainder of the report.
At a high level, TariScript works as follows:
- The spending script \((\script)\) is recorded in the transaction UTXO.
- UTXOs also define a new, sender offset public key \((K_{O})\).
- After the script \((\script)\) is executed, the execution stack must contain exactly one value that will be interpreted as the script public key \((K_{S})\). One can prove ownership of a UTXO by demonstrating knowledge of both the commitment blinding factor \((k\)), and the script private key \((k_{S})\).
- The script private key \((k_{S})\), commitment blinding factor \((k)\) and commitment value \((v)\) signs the script input data \((\input)\).
- The sender offset private keys \((k_{O})\) and script private keys \((k_{S})\) are used in conjunction to create a script offset \((\so)\), which are used in the consensus balance to prevent a number of attacks.
UTXO data commitments
The script, as well as other UTXO metadata, such as the output features are signed for with the sender offset private key to prevent malleability. As we will describe later, the notion of a script offset is introduced to prevent cut-through and forces the preservation of these commitments until they are recorded into the blockchain.
There are two changes to the protocol data structures that must be made to allow this scheme to work.
The first is a relatively minor adjustment to the transaction output definition. The second is the inclusion of script input data and an additional public key in the transaction input field.
Transaction output changes
The current definition of a Tari UTXO is:
pub struct TransactionOutput {
/// Options for an output's structure or use
features: OutputFeatures,
/// The homomorphic commitment representing the output amount
commitment: Commitment,
/// A proof that the commitment is in the right range
proof: RangeProof,
}
Note: Currently, the output features are actually malleable. TariScript fixes this.
Under TariScript, this definition changes to accommodate the script and the sender offset public keys:
pub struct TransactionOutput {
/// Options for an output's structure or use
features: OutputFeatures,
/// The homomorphic commitment representing the output amount
commitment: Commitment,
/// A proof that the commitment is in the right range
proof: RangeProof,
/// The serialised script
script: Vec<u8>,
/// The sender offset pubkey, K_O
sender_offset_public_key: PublicKey
/// UTXO signature signing the transaction output data and the homomorphic commitment with a combination
/// of the homomorphic commitment private values (amount and blinding factor) and the sender offset private key.
metadata_signature: CommitmentSignature,
}
The commitment definition is unchanged:
$$ \begin{aligned} C_i = v_i \cdot H + k_i \cdot G \end{aligned} \tag{1} $$
The metadata signature is an aggregated Commitment Signature signed with a combination of the homomorphic commitment private values \( (v_i \, , \, k_i )\), with the spending key only known by the receiver, and sender offset private key \(k_{Oi}\), only known by the sender. The signature challenge consists of all the transaction output metadata, effectively forming a contract between the sender and receiver, making all those values non-malleable and ensuring only the sender and receiver can enter into this contract. (See Signature on Commitment values by F. Zhang et. al. and Commitment Signature by G. Yu. for details about this signature.)
Note that the Commitment Signature is an aggregated signature between the sender and receiver, which is constructed as follows.
The sender portion of the public nonce is:
$$ \begin{aligned} R_{MSi} &= r_{MSi_a} \cdot H + r_{MSi_b} \cdot G \end{aligned} \tag{2} $$
The sender sends \(K_{Oi}, R_{MSi}\) to the receiver, who now has all the required information to calculate the final challenge. The receiver portion of the public nonce is:
$$ \begin{aligned} R_{MRi} &= r_{MRi_a} \cdot H + r_{MRi_b} \cdot G \end{aligned} \tag{3} $$
The final challenge is:
$$ \begin{aligned} e &= \hash{ (R_{MSi} + R_{MRi}) \cat \script_i \cat F_i \cat K_{Oi} \cat C_i} \\ \end{aligned} \tag{4} $$
The receiver can now calculate their portion of the aggregated Commitment Signature as:
$$ \begin{aligned} R_{MRi} &= r_{MRi_a} \cdot H + r_{MRi_b} \cdot G \\ a_{MRi} &= r_{MRi_a} + e(v_{i}) \\ b_{MRi} &= r_{MRi_b} + e(k_i) \end{aligned} \tag{5} $$
The receiver sends \( s_{MRi} = (a_{MRi}, b_{MRi}, R_{MRi} ) \) along with the other partial transaction information to the sender. The sender starts by calculating the final challenge (16) and then completes their part of the aggregated Commitment Signature.
$$ \begin{aligned} a_{MSi} &= r_{MSi_a} \\ b_{MSi} &= r_{MSi_b} + e(k_{Oi}) \end{aligned} \tag{6} $$
Note that (6) is a slight deviation to the Commitment Signature due to the fact that we do not have a private value for \( a_{MSi} \). This is equivalent to having a commitment to the value of zero (i.e. \( C_i = 0 \cdot H + k_{Oi} \cdot G \)).
The final aggregated Commitment Signature is combined a follows:
$$ \begin{aligned} s_{Mi} &= (a_{Mi}, b_{Mi}, R_{Mi} ) \\ &= ((a_{MSi} + a_{MRi}), (b_{MSi} + b_{MRi}), (R_{MSi} + R_{MRi}) ) \end{aligned} \tag{7} $$
This is verified by the following:
$$ \begin{aligned} a_{Mi} \cdot H + b_{Mi} \cdot G = R_{Mi} + (C_i + K_{Oi})e \end{aligned} \tag{8} $$
However, when evaluating (8) it is evident that the receiver can calculate \( r_{MSi_a} \) as follows:
$$ \begin{aligned} a_{MSi} &= a_{Mi} - a_{MRi} \\ r_{MSi_a} &= a_{MSi} \end{aligned} \tag{9} $$
To not leak any private nonces from the sender to the receiver, \( r_{MSi_a} \) can be set to zero, effectively turning the sender portion of the aggregated signature into a normal Schnorr signature. Therefore, (2), (6) and (7) can be rewritten as follows:
$$ \begin{aligned} R_{MSi} &= r_{MSi_b} \cdot G \end{aligned} \tag{10} $$
$$ \begin{aligned} a_{MSi} &= 0 \\ b_{MSi} &= r_{MSi_b} + e(k_{Oi}) \end{aligned} \tag{11} $$
$$ \begin{aligned} s_{Mi} &= (a_{Mi}, b_{Mi}, R_{Mi} ) \\ &= (a_{MRi}, (b_{MSi} + b_{MRi}), (R_{MSi} + R_{MRi}) ) \end{aligned} \tag{12} $$
Note that:
- The UTXO has a positive value
v
like any normal UTXO. - The script and the output features can no longer be changed by the miner or any other party. This includes the sender and receiver; they would need to cooperate to enter into a new contract to change any metadata, otherwise the metadata signature will be invalidated.
- We provide the complete script on the output.
Transaction input changes
The current definition of an input is
pub struct TransactionInput {
/// The features of the output being spent. We will check maturity for all outputs.
pub features: OutputFeatures,
/// The commitment referencing the output being spent.
pub commitment: Commitment,
}
In standard Mimblewimble, an input is the same as an output sans range proof. The range proof doesn't need to be checked again when spending inputs, so it is dropped.
The updated input definition is:
pub struct TransactionInput {
/// Options for an output's structure or use
features: OutputFeatures,
/// The homomorphic Pedersen commitment representing the output amount
commitment: Commitment,
/// The serialised script
script: Vec<u8>,
/// The script input data, if any
input_data: Vec<u8>,
/// Signature signing the script, input data, [script public key] and the homomorphic commitment with a combination
/// of the homomorphic commitment private values (amount and blinding factor) and the [script private key].
script_signature: CommitmentSignature,
/// The sender offset pubkey, K_O
sender_offset_public_key: PublicKey
}
The script_signature
is an aggregated Commitment Signature signed with a combination of the homomorphic commitment
private values \( (v_i \, , \, k_i )\) and script private key \(k_{Si}\) to prove ownership thereof. It
signs the script, the script input, script public key and the commitment:
$$ \begin{aligned} s_{Si} = (a_{Si}, b_{Si}, R_{Si} ) \end{aligned} \tag{13} $$
Where
$$ \begin{aligned} R_{Si} &= r_{Si_a} \cdot H + r_{Si_b} \cdot G \\ a_{Si} &= r_{Si_a} + e(v_{i}) \\ b_{Si} &= r_{Si_b} + e(k_{Si}+k_i) \\ e &= \hash{ R_{Si} \cat \alpha_i \cat \input_i \cat K_{Si} \cat C_i} \\ \end{aligned} \tag{14} $$
This is verified by the following:
$$ \begin{aligned} a_{Si} \cdot H + b_{Si} \cdot G = R_{Si} + (C_i+K_{Si})e \end{aligned} \tag{15} $$
The script public key \(K_{Si}\) needed for the script signature verification is not stored with the TransactionInput, but obtained by executing the script with the provided input data. Because this signature is signed with the script private key \(k_{Si}\), it ensures that only the owner can provide the input data \(\input_i\) to the TransactionInput.
Script Offset
For every transaction an accompanying script offset \( \so \) needs to be provided. This is there to prove that every
script public key \( K_{Sj} \) and every sender offset public key \( K_{Oi} \) supplied with the UTXOs are the
correct ones. The sender will know and provide sender offset private keys \(k_{Oi} \) and script private keys
\(k_{Si} \); these are combined to create the script offset \( \so \), which is calculated as follows:
$$ \begin{aligned} \so = \sum_j\mathrm{k_{Sj}} - \sum_i\mathrm{k_{Oi}} \; \text{for each input}, j,\, \text{and each output}, i \end{aligned} \tag{16} $$
Verification of (16) will entail:
$$ \begin{aligned} \so \cdot G = \sum_j\mathrm{K_{Sj}} - \sum_i\mathrm{K_{Oi}} \; \text{for each input}, j,\, \text{and each output}, i \end{aligned} \tag{17} $$
We modify the transactions to be:
pub struct Transaction {
...
/// A scalar offset that links outputs and inputs to prevent cut-through, enforcing the correct application of
/// the output script.
pub script_offset: BlindingFactor,
}
All script offsets (\(\so\)) from (16) contained in a block is summed together to create a total script offset (18) so that algorithm (16) still holds for a block.
$$ \begin{aligned} \so_{total} = \sum_k\mathrm{\so_{k}}\; \text{for every transaction}, k \end{aligned} \tag{18} $$
Verification of (18) will entail:
$$ \begin{aligned} \so_{total} \cdot G = \sum_j\mathrm{K_{Sj}} - \sum_i\mathrm{K_{Oi}} \; \text{for each input}, j,\, \text{and each output}, i \end{aligned} \tag{19} $$
As can be seen all information required to verify (18) is contained in a block's inputs and outputs. One important distinction to make is that the Coinbase output in a coinbase transaction does not count towards the script offset. This is because the Coinbase UTXO already has special rules accompanying it and it has no input, thus we cannot generate a script offset \( \so \). The coinbase output can allow any script \(\script_i\) and sender offset public key \( K_{Oi} \) as long as it does not break any of the rules in RFC 120 and the script is honored at spend. If the coinbase is used as in input, it is treated exactly the same as any other input.
We modify Blockheaders to be:
pub struct BlockHeader {
...
/// Sum of script offsets for all kernels in this block.
pub total_script_offset: BlindingFactor,
}
This notion of the script offset \(\so\) means that the no third party can remove any input or output from a
transaction or the block, as that will invalidate the script offset balance equation, either (17) or (19) depending on
whether the scope is a transaction or block. It is important to know that this also stops
cut‑through so that we can verify all spent UTXO scripts. Because the script private key and
sender offset private key is not publicly known, its impossible to create a new script offset.
Certain scripts may allow more than one valid set of input data. Users might be led to believe that this will allow a third party to change the script keypair \((k_{Si}\),\(K_{Si})\). If an attacker can change the \(K_{Si}\) keys of the input then he can take control of the \(K_{Oi}\) as well, allowing the attacker to change the metadata of the UTXO including the script. But as shown in Script offset security, this is not possible.
If equation (17) or (19) balances then we know that every included input and output in the transaction or block has its correct script public key and sender offset public key. Signatures (2) & (13) are checked independently from script offset verification (17) and (19), and looked at in isolation those could verify correctly but can still be signed by fake keys. When doing verification in (17) and (19) you know that the signatures and the message/metadata signed by the private keys can be trusted.
Consensus changes
The Mimblewimble balance for blocks and transactions stays the same.
In addition to the changes given above, there are consensus rule changes for transaction and block validation.
Verify that for every valid transaction or block:
- The metadata signature \( s_{Mi} \) is valid for every output.
- The script executes successfully using the given input script data.
- The result of the script is a valid script public key, \( K_S \).
- The script signature, \( s_{Si} \), is valid for every input.
- The script offset is valid for every transaction and block.
Examples
Let's cover a few examples to illustrate the new scheme and provide justification for the additional requirements and validation steps.
Standard MW transaction
For this use case we have Alice who sends Bob some Tari. Bob's wallet is online and is able to countersign the transaction.
Alice creates a new transaction spending \( C_a \) to a new output containing the commitment \( C_b \) (ignoring fees for now).
To spend \( C_a \), she provides:
- An input that contains \( C_a \).
- The script input, \( \input_a \).
- A valid script signature, \( s_{Si} \) as per (13),(14) proving that she owns the commitment \( C_a \), knows the private key, \( k_{Sa} \), corresponding to \( K_{Sa} \), the public key left on the stack after executing \( \script_a \) with \( \input_a \).
- A sender offset public key, \( K_{Ob} \).
- The sender portion of the public nonce, \( R_{MSi} )\, as per (10).
- The script offset, \( \so\) with: $$ \begin{aligned} \so = k_{Sa} - k_{Ob} \end{aligned} \tag{20} $$
Alice sends the usual first round data to Bob, but now because of TariScript also includes \( K_{Ob} \) and
\( R_{MSi} \). Bob can then complete his side of the transaction as per the standard Mimblewimble protocol
providing the commitment \(C_b\), its public blinding factor, its rangeproof and the partial transaction signature.
In addition, Bob also needs to provide a partial metadata signature as per (5) where he commits to all the transaction
output metadata with a commitment signature. Because Alice is creating the transaction, she can suggest the script
\( \script_b \) to use for Bob's output, similar to a bitcoin transaction, but Bob can choose a different script
\(\script_b\). However, in most cases the parties will agree on using something akin to a NOP
script
\(\script_b\). Bob has to return this consistent set of information back to Alice.
Alice verifies the information received back from Bob, check if she agrees with the script \( \script_b \) Bob signed, and calculates her portion of the metadata signature \( s_{Mb} \) with:
$$ \begin{aligned} s_{Mb} = r_{mb} + k_{Ob} \hash{ \script_b \cat F_b \cat R_{Mb} } \end{aligned} \tag{21} $$
Alice then constructs the final aggregated metadata signature \(s_{Mb}\) as per (12) and replaces Bob's partial metadata signature in Bob's TransactionOutput.
She completes the transaction as per standard Mimblewimble protocol and also adds the script offset \( \so \), after which she sends the final transaction to Bob and broadcasts it to the network.
Transaction validation
Base nodes validate the transaction as follows:
- They check that the usual Mimblewimble balance holds by summing inputs and outputs and validating against the excess signature. This check does not change nor do the other validation rules, such as confirming that all inputs are in the UTXO set etc.
- The metadata signature \(s_{Ma}\) on Bob's output,
- The input script must execute successfully using the provided input data; and the script result must be a valid public key,
- The script signature on Alice's input is valid by checking:
$$ \begin{aligned} a_{Sa} \cdot H + b_{Sa} \cdot G = R_{Sa} + (C_a + K_{Sa})* \hash{ R_{Sa} \cat \alpha_a \cat \input_a \cat K_{Sa} \cat C_a} \end{aligned} \tag{22} $$
- The script offset is verified by checking that the balance holds:
$$ \begin{aligned} \so \cdot{G} = K_{Sa} - K_{Ob} \end{aligned} \tag{23} $$
Finally, when Bob spends this output, he will use \( K_{Sb} \) as his script input and sign it with his script private key \( k_{Sb} \). He will choose a new sender offset public key \( K_{Oc} \) to give to the recipient, and he will construct the script offset, \( \so_b \) as follows:
$$ \begin{aligned} \so_b = k_{Sb} - k_{Oc} \end{aligned} \tag{24} $$
One sided payment
In this example, Alice pays Bob, who is not available to countersign the transaction, so Alice initiates a one-sided payment,
$$ C_a \Rightarrow C_b $$
Once again, transaction fees are ignored to simplify the illustration.
Alice owns \( C_a \) and provides the required script to spend the UTXO as was described in the previous cases.
Alice needs a public key from Bob, \( K_{Sb} \) to complete the one-sided transaction. This key can be obtained out-of-band, and might typically be Bob's wallet public key on the Tari network.
Bob requires the value \( v_b \) and blinding factor \( k_b \) to claim his payment, but he needs to be able to claim it without asking Alice for them.
This information can be obtained by using Diffie-Hellman and Bulletproof rewinding. If the blinding factor \( k_b \) was calculated with Diffie-Hellman using the sender offset keypair, (\( k_{Ob} \),\( K_{Ob} \)) as the sender keypair and the script keypair, \( (k_{Sb} \),\( K_{Sb}) \) as the receiver keypair, the blinding factor \( k_b \) can be securely calculated without communication.
Alice uses Bob's public key to create a shared secret, \( k_b \) for the output commitment, \( C_b \), using Diffie-Hellman key exchange.
Alice calculates \( k_b \) as
$$ \begin{aligned} k_b = k_{Ob} * K_{Sb} \end{aligned} \tag{25} $$
Next Alice uses Bulletproof rewinding, see RFC 180, to encrypt the value \( v_b \) into the the Bulletproof for the commitment \( C_b \). For this she uses \( k_{rewind} = \hash{k_{b}} \) as the rewind_key and \( k_{blinding} = \hash{\hash{k_{b}}} \) as the blinding key.
Alice knows the script-redeeming private key \( k_{Sa}\) for the transaction input.
Alice will create the entire transaction, including generating a new sender offset keypair and calculating the script offset,
$$ \begin{aligned} \so = k_{Sa} - k_{Ob} \end{aligned} \tag{26} $$
She also provides a script that locks the output to Bob's public key, PushPubkey(K_Sb)
.
This will only be spendable if the spender can provide a valid signature as input that demonstrates proof
of knowledge of \( k_{Sb}\) as well as the value and blinding factor of the output \(C_b\). Although Alice knowns
the value and blinding factor of the output \(C_b\) only Bob knows \( k_{Sb}\).
Any base node can now verify that the transaction is complete, verify the signature on the script, and verify the script offset.
For Bob to claim his commitment he will scan the blockchain for a known script because he knowns that the script will
be PushPubkey(K_Sb)
. In this case, the script is analogous to an address in Bitcoin or Monero. Bob's wallet can scan
the blockchain looking for scripts that he would know how to resolve.
When Bob's wallet spots a known script, he requires the blinding factor, \( k_b \) and the value \( v_b \). First he uses Diffie-Hellman to calculate \( k_b \).
Bob calculates \( k_b \) as
$$ \begin{aligned} k_b = K_{Ob} * k_{Sb} \end{aligned} \tag{27} $$
Next Bob's wallet calculates \( k_{rewind} \), using \( k_{rewind} = \hash{k_{b}}\) and (\( k_{blinding} = \hash{\hash{k_{b}}} \), using those to rewind the Bulletproof to get the value \( v_b \).
Because Bob's wallet already knowns the script private key \( k_{Sb} \), he now knows all the values required to spend the commitment \( C_b \)
For Bob's part, when he discovers one-sided payments to himself, he should spend them to new outputs using a traditional transaction to thwart any potential horizon attacks in the future.
To summarise, the information required for one-sided transactions are as follows:
Transaction input | Symbols | Knowledge |
---|---|---|
commitment | \( C_a = k_a \cdot G + v \cdot H \) | Alice knows the blinding factor and value. |
features | \( F_a \) | Public |
script | \( \alpha_a \) | Public |
script input | \( \input_a \) | Public |
script signature | \( s_{Sa} \) | Alice knows \( k_{Sa},\, r_{Sa} \) and \( k_{a},\, v_{a} \) of the commitment \(C_a\). |
sender offset public key | \( K_{Oa} \) | Not used in this transaction. |
Transaction output | Symbols | Knowledge |
---|---|---|
commitment | \( C_b = k_b \cdot G + v \cdot H \) | Alice and Bob know the blinding factor and value. |
features | \( F_b \) | Public |
script | \( \script_b \) | Script is public; only Bob knows the correct script input. |
range proof | Alice and Bob know opening parameters. | |
sender offset public key | \( K_{Ob} \) | Alice knows \( k_{Ob} \). |
metadata signature | \( s_{Mb} \) | Alice knows \( k_{Ob} \), \( (k_{b},\, v) \) and the metadata. |
HTLC-like script
In this use case we have a script that controls where it can be spent. The script is out of scope for this example, but has the following rules:
- Alice can spend the UTXO unilaterally after block n, or
- Alice and Bob can spend it together.
This would be typically what a lightning-type channel requires.
Alice owns the commitment \( C_a \). She and Bob work together to create \( C_s\). But we don't yet know who can spend the newly created \( C_s\) and under what conditions this will be.
$$ C_a \Rightarrow C_s \Rightarrow C_x $$
Alice owns \( C_a\), so she knows the blinding factor \( k_a\) and the correct input for the script's spending conditions. Alice also generates the sender offset keypair, \( (k_{Os}, K_{Os} )\).
Now Alice and Bob proceed with the standard transaction flow.
Alice ensures that the sender offset public key \( K_{Os}\) is part of the output metadata that contains commitment \( C_s\). Alice will fill in the script with her \( k_{Sa}\) to unlock the commitment \( C_a\). Because Alice owns \( C_a\) she needs to construct \( \so\) with:
$$ \begin{aligned} \so = k_{Sa} - k_{Os} \end{aligned} \tag{28} $$
The blinding factor, \( k_s\) can be generated using a Diffie-Hellman construction. The commitment \( C_s\) needs to be constructed with the script that Bob agrees on. Until it is mined, Alice could modify the script via double-spend and thus Bob must wait until the transaction is confirmed before accepting the conditions of the smart contract between Alice and himself.
Once the UTXO is mined, both Alice and Bob possess all the knowledge required to spend the \( C_s \) UTXO. It's only the conditions of the script that will discriminate between the two.
The spending case of either Alice or Bob claiming the commitment \( C_s\) follows the same flow described in the previous examples, with the sender proving knowledge of \( k_{Ss}\) and "unlocking" the spending script.
The case of Alice and Bob spending \( C_s \) together to a new multiparty commitment requires some elaboration.
Assume that Alice and Bob want to spend \( C_s \) co-operatively. This involves the script being executed in such a way that the resulting public key on the stack is the sum of Alice and Bob's individual script keys, \( k_{SsA} \) and \( k_{SaB} \).
The script input needs to be signed by this aggregate key, and so Alice and Bob must each supply a partial signature following the usual Schnorr aggregate mechanics, but one person needs to add in the signature of the blinding factor and value.
In an analogous fashion, Alice and Bob also generate an aggregate sender offset private key \( k_{Ox}\), each using their own \( k_{OxA} \) and \( k_{OxB}\).
To be specific, Alice calculates her portion from
$$ \begin{aligned} \so_A = k_{SsA} - k_{OxA} \end{aligned} \tag{29} $$
Bob will construct his part of the \( \so\) with:
$$ \begin{aligned} \so_B = k_{SsB} - k_{OxB} \end{aligned} \tag{30} $$
And the aggregate \( \so\) is then:
$$ \begin{aligned} \so = \so_A + \so_B \end{aligned} \tag{31} $$
Notice that in this case, both \( K_{Ss} \) and \( K_{Ox}\) are aggregate keys.
Notice also that because the script resolves to an aggregate key \( K_s\) neither Alice nor Bob can claim the commitment \( C_s\) without the other party's key. If either party tries to cheat by editing the input, the script validation will fail.
If either party tries to cheat by creating a new output, the script offset will not validate correctly as it locks the output of the transaction.
A base node validating the transaction will also not be able to tell this is an aggregate transaction as all keys are aggregated Schnorr signatures. But it will be able to validate that the script input is correctly signed, thus the output public key is correct and that the \( \so\) is correctly calculated, meaning that the commitment \( C_x\) is the correct UTXO for the transaction.
To summarise, the information required for creating a multiparty UTXO is as follows:
Transaction input | Symbols | Knowledge |
---|---|---|
commitment | \( C_a = k_a \cdot G + v \cdot H \) | Alice knows the blinding factor and value. |
features | \( F_a \) | Public |
script | \( \alpha_a \) | Public |
script input | \( \input_a \) | Public |
script signature | \( s_{Sa} \) | Alice knows \( k_{Sa},\, r_{Sa} \) and \( k_{a},\, v_{a} \) of the commitment \(C_a\). |
sender offset public key | \( K_{Oa} \) | Not used in this transaction. |
Transaction output | Symbols | Knowledge |
---|---|---|
commitment | \( C_s = k_s \cdot G + v \cdot H \) | Alice and Bob know the blinding factor and value. |
features | \( F_s \) | Public |
script | \( \script_s \) | Script is public; Alice and Bob only knows their part of the correct script input. |
range proof | Alice and Bob know opening parameters. | |
sender offset public key | \( K_{Os} = K_{OsA} + K_{OsB}\) | Alice knows \( k_{OsA} \), Bob knows \( k_{OsB} \), neither party knows \( k_{Os} \). |
metadata signature | \( (a_{Ms} , b_{Ms} , R_{Ms}) \) | Alice knows \( k_{OsA} \), Bob knows \( k_{OsB} \), both parties know \( (k_{s},\, v) \). Neither party knows \( k_{Os}\). |
When spending the multi-party input:
Transaction input | Symbols | Knowledge |
---|---|---|
commitment | \( C_s = k_s \cdot G + v_s \cdot H \) | Alice and Bob know the blinding factor and value. |
features | \( F_s \) | Public |
script | \( \alpha_s \) | Public |
script input | \( \input_s \) | Public |
script signature | \( (a_{Ss} ,b_{Ss} , R_{Ss}) \) | Alice knows \( (k_{SsA},\, r_{SsA}) \), Bob knows \( (k_{SsB},\, r_{SsB}) \), both parties know \( (k_{s},\, v_{s}) \), neither party knows \( k_{Ss}\). |
sender offset public key | \( K_{Os} \) | As above, Alice and Bob each know part of the sender offset key. |
Cut-through
A major issue with many Mimblewimble extension schemes is that miners are able to cut-through UTXOs if an output is spent in the same block it was created. This makes it so that the intervening UTXO never existed; along with any checks and balances carried in that UTXO. It's also impossible to prove without additional information that cut-through even occurred (though one may suspect, since the "one" transaction would contribute two kernels to the block).
In particular, cut-through is devastating for an idea like TariScript which relies on conditions present in the UTXO being enforced.
This is a reason for the presence of the script offset in the TariScript proposal. It mathematically links all inputs and outputs of all the transactions in a block and that tallied up to create the script offset. Providing the script offset requires knowledge of keys that miners do not possess; thus they are unable to produce the necessary script offset when attempting to perform cut-through on a pair of transactions.
Lets show by example how the script offset stops cut-through, where Alice spends to Bob who spends to Carol. Ignoring fees, we have:
$$ C_a \Rightarrow C_b \Rightarrow C_c $$
For these two transactions, the total script offset is calculated as follows:
$$ \begin{aligned} \so_1 = k_{Sa} - k_{Ob}\\ \so_2 = k_{Sb} - k_{Oc}\\ \end{aligned} \tag{32} $$
$$ \begin{aligned} \so_t = \so_1 + \so_2 = (k_{Sa} + k_{Sb}) - (k_{Ob} + k_{Oc})\\ \end{aligned} \tag{33} $$
In standard Mimblewimble cut-through can be applied to get:
$$ C_a \Rightarrow C_c $$
After cut-through the total script offset becomes:
$$ \begin{aligned} \so'_t = k_{Sa} - k_{Oc}\\ \end{aligned} \tag{34} $$
As we can see:
$$ \begin{aligned} \so_t\ \neq \so'_t \\ \end{aligned} \tag{35} $$
A third party cannot generate a new script offset as only the original owner can provide the script private key \(k_{Sa}\) to create a new script offset.
Script offset security
If all the inputs in a transaction or a block contain scripts such as just NOP
or CompareHeight
commands, then the
hypothesis is that it is possible to recreate a false script offset. Lets show by example why this is not possible. In
this Example we have Alice who pays Bob with no change output:
$$ C_a \Rightarrow C_b $$
Alice has an output \(C_{a}\) which contains a script that only has a NOP
command in it. This means that the
script \( \script_a \) will immediately exit on execution leaving the entire input data \( \input_a \)on the
stack. She sends all the required information to Bob as per the standard mw transaction, who
creates an output \(C_{b}\). Because of the NOP
script \( \script_a \), Bob can change the script public key
\( K_{Sa}\) contained in the input data. Bob can now use his own \(k'_{Sa}\) as the script private key. He
replaces the sender offset public key with his own \(K'_{Ob}\) allowing him to change the script
\( \script_b \) and generate a new signature as in (2). Bob can now generate a new script offset with
\(\so' = k'_{Sa} - k'_{Ob} \). Up to this point, it all seems valid. No one can detect that Bob changed the script
to \( \script_b \).
But what Bob also needs to do is generate the signature in (13). For this signature Bob needs to know \(k_{Sa}, k_a, v_a\). Because Bob created a fake script private key, and there is no change in this transaction, he does know the script private key and the value. But Bob does not know the blinding factor \(k_a\) of Alice's commitment and thus cannot complete the signature in (13). Only the rightful owner of the commitment, which in Mimblewimble terms is the person who knows \( k_a, v_a\), can generate the signature in (13).
Script lock key generation
At face value, it looks like the burden for wallets has tripled, since each UTXO owner has to remember three private keys, the spend key, \( k_i \), the sender offset key \( k_{O} \) and the script key \( k_{S} \). In practice, the script key will often be a static key associated with the user's node or wallet. Even if it is not, the script and sender offset keys can be deterministically derived from the spend key. For example, \( k_{S} \) could be \( \hash{ k_i \cat \alpha} \).
Blockchain bloat
The most obvious drawback to TariScript is the effect it will have on blockchain size. UTXOs are substantially larger, with the addition of the script, script signature, and a public key to every output.
These can eventually be pruned, but will increase storage and bandwidth requirements.
Input size of a block will now be much bigger as each input was previously just a commitment and output features. Each input now includes a script, input_data, the script signature and an extra public key. This could be compacted by just broadcasting input hashes along with the missing script input data and signature, instead of the full input in transaction messages, but this will still be larger than inputs are currently.
Every header will also be bigger as it includes an extra blinding factor that will not be pruned away.
Fodder for chain analysis
Another potential drawback of TariScript is the additional information that is handed to entities wishing to perform chain analysis. Having scripts attached to outputs will often clearly mark the purpose of that UTXO. Users may wish to re-spend outputs into vanilla, default UTXOs in a mixing transaction to disassociate Tari funds from a particular script.
Notation
Where possible, the "usual" notation is used to denote terms commonly found in cryptocurrency literature. Lower case characters are used as private keys, while uppercase characters are used as public keys. New terms introduced by TariScript are assigned greek lowercase letters in most cases. The capital letter subscripts, R and S refer to a UTXO receiver and script respectively.
Symbol | Definition |
---|---|
\( \script_i \) | An output script for output i, serialised to binary. |
\( F_i \) | Output features for UTXO i. |
\( f_t \) | Transaction fee for transaction t. |
\( (k_{Oi}, K_{Oi}) \) | The private - public keypair for the UTXO sender offset key. |
\( (k_{Si}, K_{Si}) \) | The private - public keypair for the script key. The script, \( \script_i \) resolves to \( K_S \) after completing execution. |
\( \so_t \) | The script offset for transaction t, see (16) |
\( C_i \) | A Pedersen commitment to a value \( v_i \), see (1) |
\( \input_i \) | The serialised input for script \( \script_i \) |
\( s_{Si} \) | A script signature for output \( i \), see (13 - 15) |
\( s_{Mi} \) | A metadata signature for output \( i \), see (2 - 12) |
Extensions
Covenants
TariScript places restrictions on who can spend UTXOs. It will also be useful for Tari digital asset applications to restrict how or where UTXOs may be spent in some cases. The general term for these sorts of restrictions are termed covenants. The Handshake white paper has a fairly good description of how covenants work.
It is beyond the scope of this RFC, but it's anticipated that TariScript would play a key role in the introduction of generalised covenant support into Tari.
Lock-time malleability
The current Tari protocol has an issue with Transaction Output Maturity malleability. This output feature is enforced in the consensus rules, but it is actually possible for a miner to change the value without invalidating the transaction.
With TariScript, output features are properly committed to in the transaction and verified as part of the script offset validation.
Credits
Thanks to David Burkett for proposing a method to prevent cut-through and willingness to discuss ideas.
RFC-0202/TariScriptOpcodes
TariScript Opcodes
Maintainer(s): Cayle Sharrock
Licence
Copyright 2020 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
This Request for Comment (RFC) defines the opcodes that make up the TariScript scripting language and provides some examples and applicaitons.
Related Requests for Comment
Introduction
TariScript semantics
The proposal for TariScript is straightforward. It is based on Bitcoin script and inherits most of its ideas.
The main properties of TariScript are
- The scripting language is stack-based. At redeem time, the UTXO spender must supply an input stack. The script runs by operating on the stack contents.
- If an error occurs during execution, the script fails.
- After the script completes, it is successful if and only if it has not aborted, and there is exactly a single element on the stack. The script fails if the stack is empty, or contains more than one element, or aborts early.
- It is not Turing complete, so there are no loops or timing functions.
- The opcodes enforce type safety. e.g. A public key cannot be added to an integer scalar. Errors of this kind MUST cause the script to fail. The Rust implementation of TariScript automatically applies the type safety rules.
Failure modes
Bitcoin transactions can "fail" in two main ways: Either there is a genuine error in the locking or unlocking script; or a wallet broadcasts a Non-standard transaction to a non-mining node. To be more precise, Bitcoin core nodes only accept a small subset of valid transaction scripts that are deemed Standard transactions. To succesfully produce a non-standard Bitcoin transaction, one has to submit it directly to a miner that accepts non-standard transactions.
It's interesting to note that only 0.02% of transactions mined in Bitcoin before block 550,000 were non-standard, and it appears that the vast majority of these were in fact unintentional, leading to loss of funds (1).
The present RFC proposes that TariScript not identify a subset of transactions as "standard". However, some transactions might be invalid in and of- themselves (e.g. invalid signature, invalid script), while others may be invalid because of the execution context (e.g. a lock time has not expired).
All Tari nodes MUST reject the former type of invalid transaction; and SHOULD reject the latter. In these instances, it is the wallets' responsibility to wait until transactions are valid before broadcasting them.
Rejected transactions are simply silently dropped.
This policy discourages spamming on the network and promotes responsible behaviour by wallets.
The full list of Error codes is given below.
Constraints
- The maximum length of a script when serialised is 1,024 bytes.
- The maximum length of a script's input is 1,024 bytes.
- The maximum stack height is 255.
Opcodes
TariScript opcodes range from 0 to 255 and are represented as a single unsigned byte. The opcode set is limited to allow for the applications specified in this RFC, but can be expanded in the future.
Block height checks
All these opcodes test the current block height (or, if running the script as part of a transaction validation, the next earliest block height) against a given value.
CheckHeightVerify(height)
Compare the current block height to height
.
Fails with VERIFY_FAILED
if the block height < height
.
CheckHeight(height)
Pushes the value of (the current height - height
) to the stack. In other words,
the top of the stack will hold the height difference between height
and the current height. If the chain has
progressed beyond height, the value is positive; and negative if the chain has yet to reach height
.
Fails with STACK_OVERFLOW
if the stack would exceed the max stack height.
CompareHeightVerify
The same as CheckHeightVerify
, except that the height to compare against the block height
is popped off the stack instead of being hardcoded in the script. Pops the top of the stack as height
and compares
it to the current block height.
- Fails with
INVALID_INPUT
if there is not a valid integer value on top of the stack. - Fails with
EMPTY_STACK
if the stack is empty. - Fails with
VERIFY_FAILED
if the block height <height
.
CompareHeight
The same as CheckHeight
except that the height to compare against the block height
is popped off the stack instead of being hardcoded in the script.
Pops the top of the stack as height
, then pushes the value of (height
- the current height) to the stack.
In other words, this opcode replaces the top of the stack with the difference between that value and the current height.
- Fails with
INVALID_INPUT
if there is not a valid integer value on top of the stack. - Fails with
EMPTY_STACK
if the stack is empty.
Stack manipulation
NoOp
Does nothing. Never fails.
PushZero
Pushes a zero onto the stack. This is a very common opcode and has the same effect as PushInt(0)
but is more compact.
PushZero
can also be interpreted as PushFalse
(although no such opcode exists).
- Fails with
STACK_OVERFLOW
if the stack would exceed the max stack height.
PushOne
Pushes a one onto the stack. This is a very common opcode and has the same effect as PushInt(1)
but is more compact.
PushOne
can also be interpreted as PushTrue
, although no such opcode exists.
- Fails with
STACK_OVERFLOW
if the stack would exceed the max stack height.
PushHash(HashValue)
Push the associated 32-byte value onto the stack.
- Fails with
INVALID_SCRIPT_DATA
if HashValue is not a valid 32 byte sequence - Fails with
STACK_OVERFLOW
if the stack would exceed the max stack height.
PushInt(i64)
Push the associated 64-bit signed integer onto the stack
- Fails with
INVALID_SCRIPT_DATA
ifi64
is not a valid integer. - Fails with
STACK_OVERFLOW
if the stack would exceed the max stack height.
PushPubKey(PublicKey)
Push the associated 32-byte value onto the stack. It will be interpreted as a public key or a commitment.
- Fails with
INVALID_SCRIPT_DATA
if HashValue is not a valid 32 byte sequence - Fails with
STACK_OVERFLOW
if the stack would exceed the max stack height.
Drop
Drops the top stack item.
- Fails with
EMPTY_STACK
if the stack is empty.
Dup
Duplicates the top stack item.
- Fails with
EMPTY_STACK
if the stack is empty. - Fails with
STACK_OVERFLOW
if the stack would exceed the max stack height.
RevRot
Reverse rotation. The top stack item moves into 3rd place, e.g. abc => bca
.
- Fails with
EMPTY_STACK
if the stack has fewer than three items.
Math operations
GeZero
Pops the top stack element as val
. If val
is greater than or equal to zero, push a 1 to the stack, otherwise push 0.
- Fails with
EMPTY_STACK
if the stack is empty. - Fails with
INVALID_INPUT
ifval
is not an integer.
GtZero
Pops the top stack element as val
. If val
is strictly greater than zero, push a 1 to the stack, otherwise push 0.
- Fails with
EMPTY_STACK
if the stack is empty. - Fails with
INVALID_INPUT
if the item is not an integer.
LeZero
Pops the top stack element as val
. If val
is less than or equal to zero, push a 1 to the stack, otherwise push 0.
- Fails with
EMPTY_STACK
if the stack is empty. - Fails with
INVALID_INPUT
if the item is not an integer.
LtZero
Pops the top stack element as val
. If val
is strictly less than zero, push a 1 to the stack, otherwise push 0.
- Fails with
EMPTY_STACK
if the stack is empty. - Fails with
INVALID_INPUT
if the items is not an integer.
Add
Pop two items and push their sum
- Fails with
EMPTY_STACK
if the stack has fewer than two items. - Fails with
INVALID_INPUT
if the items cannot be added to each other (e.g. an integer and public key).
Sub
Pop two items and push the second minus the top
- Fails with
EMPTY_STACK
if the stack has fewer than two items. - Fails with
INVALID_INPUT
if the items cannot be subtracted from each other (e.g. an integer and public key).
Equal
Pops the top two items, and pushes 1 to the stack if the inputs are exactly equal, 0 otherwise. 0 is also pushed if the values cannot be compared (e.g. integer and pubkey).
- Fails with
EMPTY_STACK
if the stack has fewer than two items.
EqualVerify
Pops the top two items, and compares their values.
- Fails with
EMPTY_STACK
if the stack has fewer than two items. - Fails with
VERIFY_FAILED
if the top two stack elements are not equal.
Boolean logic
Or(n)
n
+ 1 items are popped from the stack. If the last item popped matches at least one of the first n
items popped,
push 1 onto the stack. Push 0 otherwise.
- Fails with
EMPTY_STACK
if the stack has fewer thann
+ 1 items.
OrVerify(n)
n
+ 1 items are popped from the stack. If the last item popped matches at least one of the first n
items popped,
continue. Fail with VERIFY_FAILED
otherwise.
- Fails with
EMPTY_STACK
if the stack has fewer thann
+ 1 items.
Cryptographic operations
HashBlake256
Pop the top element, hash it with the Blake256 hash function and push the result to the stack.
- Fails with
EMPTY_STACK
if the stack is empty.
HashSha256
Pop the top element, hash it with the SHA256 hash function and push the result to the stack.
- Fails with
EMPTY_STACK
if the stack is empty.
HashSha3
Pop the top element, hash it with the SHA-3 hash function and push the result to the stack.
- Fails with
EMPTY_STACK
if the stack is empty.
CheckSig(Msg)
Pop the public key and then the signature. If the signature signs the 32-byte message, push 1 to the stack, otherwise push 0.
- Fails with
INVALID_SCRIPT_DATA
if theMsg
is not a valid 32-byte value. - Fails with
EMPTY_STACK
if the stack has fewer than 2 items. - Fails with
INVALID_INPUT
if the top stack element is not a PublicKey or Commitment - Fails with
INVALID_INPUT
if the second stack element is not a Signature
CheckSigVerify(Msg),
Identical to CheckSig
, except that nothing is pushed to the stack if the signature is valid, and the
operation fails with VERIFY_FAILED
if the signature is invalid.
ToRistrettoPoint,
Pops the top element which must be a valid Ristretto scalar, calculates the corresponding Ristretto point, and pushes this to the stack.
- Fails with
EMPTY_STACK
if the stack is empty. - Fails with
INVALID_INPUT
if the top stack element is not a scalar.
Miscellaneous
Return
Always fails with VERIFY_FAILED
.
If-then-else
The if-then-else clause is marked with the IFTHEN
opcode.
When the IFTHEN
opcode is reached, the top element of the stack is popped into pred
.
If pred
is 1, the instructions between IFTHEN
and ELSE
are executed. The instructions from ELSE
to ENDIF
are
then popped without being executed.
If pred
is 0, instructions are popped until ELSE
or ENDIF
is encountered.
If ELSE
is encountered, instructions are executed until ENDIF
is reached.
ENDIF
is a marker opcode and a no-op.
- Fails with
EMPTY_STACK
if the stack is empty. - If
pred
is anything other than 0 or 1, the script fails withINVALID_INPUT
. - If any instruction during execution of the clause causes a failure, the script fails with that failure code.
Serialisation
TariScript and the execution stack are serialised into byte strings using a simple linear parser. Since all opcodes are
a single byte, it's very easy to read and write script byte strings. If an opcode has a parameter associated with it,
e.g. PushHash
then it is equally known how many bytes following the opcode will contain the parameter.
The script input data is serialised in an analogous manner. The first byte in a stream indicates the type of data in the bytes that follow. The length of each type is fixed and known a priori. The next n bytes read represent the data type.
As input data elements are read in, they are pushed onto the stack. This means that the last input element will typically be operated on first!
The types of input parameters that are accepted are:
Type | Range / Value |
---|---|
Integer | 64-bit signed integer |
Hash | 32-byte hash value |
PublicKey | 32-byte Ristretto public key |
Signature | 32-byte nonce + 32-byte signature |
Data | single byte, n, indicating length of data, followed by n bytes of data |
RistrettoScalar | 32-byte Ristretto secret key |
Example scripts
Anyone can spend
The simplest script is an empty script, or a script with a single NoOp
opcode. When faced with this script, the spender
can supply any pubkey in her script input for which she knows the private key. The script will execute, leaving that
public key as the result, and the transaction script validation will pass.
One-sided transactions
One-sided transactions lock the input to a predetermined public key provided by the recipient; essentially the same method that Bitcoin uses. The simplest form of this is to simply post the new owner's public key as the script:
PushPubkey(P_B)
To spend this output, Bob provides an empty input stack. After execution, the stack contains his public key.
An equivalent script to Bitcoin's P2PKH would be:
Dup HashBlake256 PushHash(PKH) EqualVerify
To spend this, Bob provides his public key as script input. To illustrate the execution process, we show the script running on the left, and resulting stack on the right:
Initial script | Initial Stack |
---|---|
Dup | Bob's Pubkey |
HashBlake256 | |
PushHash(PKH) | |
EqualVerify |
Copy Bob's pubkey:
Dup | |
---|---|
HashBlake256 | Bob's Pubkey |
PushHash(PKH) | Bob's Pubkey |
EqualVerify |
Hash the public key:
HashBlake256 | |
---|---|
PushHash(PKH) | H(Bob's Pubkey) |
EqualVerify | Bob's Pubkey |
Push the expected hash to the stack:
PushHash(PKH) | |
---|---|
EqualVerify | PKH |
H(Bob's Pubkey) | |
Bob's Pubkey |
Is PKH
equal to the hash of Bob's public key?
EqualVerify | |
---|---|
Bob's Pubkey |
The script has completed without errors, and Bob's public key remains on the stack.
Time-locked contract
Alice sends some Tari to Bob. If he doesn't spend it within a certain timeframe (up till block 4000), then she is also able to spend it back to herself.
The spender provides their public key as input to the script.
Dup PushPubkey(P_b) CheckHeight(4000) GeZero IFTHEN PushPubkey(P_a) OrVerify(2) ELSE EqualVerify ENDIF
Let's run through this script assuming it's block 3990 and Bob is spending the UTXO. We'll only print the stack this time:
Initial Stack |
---|
Bob's pubkey |
Dup
:
Stack |
---|
Bob's pubkey |
Bob's pubkey |
PushPubkey(P_b)
:
Stack |
---|
P_b |
Bob's pubkey |
Bob's pubkey |
CheckHeight(4000)
. The block height is 3990, so 3990 - 4000
is pushed to the stack:
Stack |
---|
-10 |
P_b |
Bob's pubkey |
Bob's pubkey |
GeZero
pushes a 1 if the top stack element is positive or zero:
Stack |
---|
0 |
P_b |
Bob's pubkey |
Bob's pubkey |
IFTHEN
compares the top of the stack to 1. It is not a match, so it will execute the ELSE
branch:
Stack |
---|
P_b |
Bob's pubkey |
Bob's pubkey |
EqualVerify
checks that P_b
is equal to Bob's pubkey:
Stack |
---|
Bob's pubkey |
The ENDIF
is a no-op, so the stack contains Bob's public key, meaning Bob must sign to spend this transaction.
Similarly, if it is after block 4000, say block 4005, and Alice or Bob tries to spend the UTXO, the sequence is:
Initial Stack |
---|
Alice or Bob's pubkey |
Dup
and PushPubkey(P_b)
as before:
Stack |
---|
P_b |
Alice or Bob's pubkey |
Alice or Bob's pubkey |
CheckHeight(4000)
calculates 4005 - 4000)
and pushes 5 to the stack:
Stack |
---|
5 |
P_b |
Alice or Bob's pubkey |
Alice or Bob's pubkey |
GeZero
pops the 5 and pushes a 1 to the stack:
Stack |
---|
1 |
P_b |
Alice or Bob's pubkey |
Alice or Bob's pubkey |
The top of the stack is 1, so IFTHEN
executes the first branch, PushPubkey(P_a)
:
Stack |
---|
P_a |
P_b |
Alice or Bob's pubkey |
Alice or Bob's pubkey |
OrVerify(2)
compares the 3rd element, Alice's pubkey, with the 2 top items that were popped. There is a match, so the script
continues.
Stack |
---|
Alice or Bob's pubkey |
If the script executes successfully, then either Alice's or Bob's public key is left on the stack, meaning only Alice or Bob can spend the output.
Error codes
Code | Description |
---|---|
SCRIPT_TOO_LONG | The serialised script exceeds 1024 bytes. |
SCRIPT_INPUT_TOO_LONG | The serialised script input exceeds 1024 bytes. |
STACK_OVERFLOW | The stack exceeded 255 elements during script execution |
EMPTY_STACK | There was an attempt to pop an item off an empty stack |
INVALID_OPCODE | The script cannot be deserialised due to an invalid opcode |
INVALID_SCRIPT_DATA | An opcode parameter is invalid or of the wrong type |
INVALID_INPUT | Invalid or incompatible data was popped off the stack as input into an operation |
VERIFY_FAILED | A script condition (typically a nnnVerify opcode) failed |
Credits
Thanks to @philipr-za and @SWvheerden for their input and contributions to this RFC.
RFC-0203/Stealth addresses
Stealth addresses
Maintainer(s): Philip Robinson
Licence
Copyright 2022 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
This Request for Comment (RFC) presents the implementation of Dual-Key Stealth Addresses in One-Sided payments to improve privacy for receivers of these payments on the Tari base layer.
Related Requests for Comment
Introduction
The Tari protocol extends the Mimblewimble protocol to include scripting in the form of TariScript. One of the first features implemented using TariScript was one-sided payments. These are payments to a recipient that do not require an interactive negotiation in the same way a standard Mimblewimble transaction does. One of the main downsides of the current implementation of one-sided payments is that the script key used is the Public Key of the recipient's wallet. This public key is embedded in the TariScript of the UTXO created by the sender. The issue is that it becomes very easy for a third party to scan the blockchain to look for one-sided transaction outputs being sent to a given wallet. In order to alleviate this privacy leak, this RFC proposes the use of Dual-Key Stealth Addresses to be used as the script key when sending a one-sided payment.
Brief background on the development of Stealth Addresses
Stealth addresses were first proposed on the Bitcoin Talk forum by user Bytecoin. The concept was further refined in the Cryptonote whitepaper and by Peter Todd which went on to be used in Monero. These formulations were very similar to the BIP-32 style of address generation. Later in 2014 a developer called rynomster/sdcoin proposed a further improvement to the scheme that he called the Dual-Key Stealth Address Protocol (DKSAP) that allowed for a separate scanning key and spending key. Since then there have been many variations of DKSAP proposed, but generally they only offer performance optimizations for certain scenarios or add an application-specific feature. For our application, DKSAP will do the job.
Dual-key Stealth Addresses
The Dual-key Stealth Address Protocol (DKSAP) uses two key-pairs for the recipient of a one-sided payment, \( A = a \cdot G \) and \( B = b \cdot G \). Where \( a \) is called the scan key and \( b \) is the spend key. A recipient will distribute the public keys out of band to receive one-sided payments.
The protocol that a sender will use to make a payment to the recipient is as follows:
- Sender generates a random nonce key-pair \( R = r \cdot G \).
- Sender calculates a ECDH shared secret \(c = H( r \cdot a \cdot G ) = H( a \cdot R) = H( r \cdot A) \), where \( H( \cdot ) \) is a cryptographic hash function.
- The sender will then use \( K_S = c \cdot G + B \) as the last public key in the one-sided payment script.
- The sender includes \( R \) for the receiver but dropping it as it is not required during script execution.
This changes the script for a one-sided payment from
PushPubkey(K_S)
toPushPubkey(R) Drop PushPubkey(K_S)
.
The recipient will need to scan the blockchain for outputs that contain scripts of the one-sided payment form, and when one is found they will need to do the following:
- Extract the nonce \( R \) from the script.
- Use the public nonce to calculate the shared secret \(c = H( a \cdot R) \)
- Calculate \( K_S \) and check if it exists in the script.
- If it exists, the recipient can produce the script signature required using the private key calculated by \( c + b \). This private key can only be computed by the recipient using \( b \).
One of the benefits of the DKSAP is that the key used for scanning the blockchain, \( a \), does not enable one to calculate the private key required for spending the output. This means that a recipient can potentially outsource the scanning of the blockchain to a less trusted third-party by giving them just the scanning key \( a \) but retaining the secrecy of the spend key \( b \).
RFC-0230/Time-related Transactions
Time-related Transactions
Maintainer(s): S W van Heerden and Philip Robinson
Licence
Copyright 2019 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to describe a few extensions to Mimblewimble to allow time-related transactions.
Related Requests for Comment
Description
Time-locked UTXOs
Time-locked Unspent Transaction Outputs (UTXOs) can be accomplished by adding a feature flag to a UTXO and a lock height, also referred to as the output's maturity. This allows a consensus limit on after which height the output can be spent.
This requires that users constructing a transaction:
- MUST include a feature flag of their UTXO; and
- MUST include a lock height in their UTXO.
This adds the following requirement for a base node:
- A base node MUST NOT allow a UTXO to be spent if the current head has not already exceeded the UTXO's lock height.
This also adds the following requirement for a base node:
- A base node MUST reject any block that contains a UTXO with a lock height not already past the current head.
Time-locked Contracts
In standard Mimblewimble, time-locked contracts can be accomplished by modifying the kernel of each transaction to include a lock height. This limits how early in the blockchain lifetime the specific transaction can be included in a block. This approach is used in a traditional Mimblewimble construction that does not implement any kind of scripting. This has two disadvantages. Firstly, the spending condition is very primitive and cannot be linked to other conditions. Secondly, it bloats the kernel, which is a component of the transaction that cannot be pruned.
However, with TariScript it becomes possible to express spending conditions like a time-lock as part of a UTXO's script.
The CheckHeightVerify(height)
TariScript Op code allows a time-lock check to be incorporated into a script. The following is a simple
example of a plain time-lock script that prevents an output from being spent before the chain reaches height 4000:
CheckHeightVerify(4000)
Hashed Time-locked Contract
Hashed time-locked contracts (HTLC) are a way of reserving funds that can only be spent if a hash pre-image can be provided or if a specified amount of time has passed. The hash pre-image is a secret that can be revealed under the right conditions to enable spending of the UTXO before the time-lock is reached. The secret can be directly exchanged between the parties or revealed to the other party by spending an output that makes use of an adaptor signature.
HTLCs enable a number of interesting transaction constructions. For example, Atomic Swaps and Payment Channels like those in the Lightning Network.
The following is an example of an HTLC script. In this script, Alice sends some Tari to Bob that he can spend using the
private key of P_b
if he can provide the pre-image to the SHA256 hash output (HASH256{pre_image}
) specified in the script
by Alice. If Bob has not spent this UTXO before the chain reaches height 5000 then Alice will be able to spend the output
using the private key of P_a
.
HashSha256
PushHash(HASH256{pre_image})
Equal
IFTHEN
PushPubkey(P_b)
ELSE
CheckHeightVerify(5000)
PushPubkey(P_a)
ENDIF
A more detailed analysis of the execution of this kind of script can be found at Time-locked Contact
RFC-0240/Atomic Swap
Maintainer(s): S W van Heerden
Licence
Copyright 2021 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
This Request for Comment (RFC) aims to describe how Atomic swaps will be created between two parties on different blockchains.
Related Requests for Comment
$$ \newcommand{\preimage}{\phi} % pre image \newcommand{\hash}[1]{\mathrm{H}\bigl({#1}\bigr)} $$
Description
Atomic swaps are atomic transactions that allow users to exchange different crypto assets and or coins without using a central exchange and or trusting each other. Trading coins or assets this way makes it much more private and secure to do and swap because no third party is required to be secure. Atomic swaps work on the principle of using Hashed Time Lock Contracts(HTLC). In short, it requires some hash pre-image to unlock the contract, or the time-lock can be used to reclaim the funds.
In a cross-chain Atomic swap, both users lock up the funds to be exchanged on their respective chains in an HTLC-type contract. However, the two contracts’ pre-image, or spending secret, is the same, but only one party knows the correct pre-image. When the first HTLC contract is spent, this publicly reveals the pre-image for the other party to spend the second HTLC. If the first HTLC is never spent, the second transaction's time-lock will allow the user to respend the funds back to themselves after the time lock has passed.
BTC - XTR AtomicSwap
Overview
BTC uses a scripting language for smart contracts on transactions which enables atomic swaps on the BTC chain. Traditionally Mimblewimble coins do not implement scripts, which makes Atomic swaps harder to implement but not impossible. Grin has implemented atomic swaps using a version of a 2-of-2 multi-signature transaction mimblewimble atomic swaps. Fortunately, Tari does have scripting with TariScript, which works a lot like BTC scripts, making the implementation simpler. Because of the scripting similarities, the scripts to both HTLCs will look very similar, and we only need to ensure that we use the same hash function in both.
To do an Atomic swap from BTC to XTR, we need four wallets, two BTC wallets, and two XTR wallets, one wallet per person, per coin.
As an example, Alice wants to trade some of her XTR for Bob's BTC. Alice and Bob need to agree on an amount of XTR and BTC to swap. Once an agreement is reached, the swap is executed in the following steps:
-
Alice chooses a set of random bytes, \( \preimage \), as the pre-image and hashes it with SHA256. She then sends
-
the hash of the pre-image, \( \hash{\preimage} \), to Bob along with her BTC address.
-
Bob sends her a public version of his script key, \( K_{Sb} \), for use in the XTR transaction, which we can refer to as Bob's script address.
-
Alice creates a one-sided XTR transaction with an HTLC contract requiring \( \preimage \) as the input, which will either payout to Bob's script address or her script address, \( K_{Sa} \), after a particular "time" has elapsed (block height has been reached).
-
Bob waits for this transaction to be mined. When it is mined, he verifies that the UTXO spending script expects a comparison of \( \hash{\preimage} \) as the first instruction, and that his public script key, \( K_{Sb} \), will be the final value remaining after executing the script. He has the private script key, \( k_{Sb} \), to enable him to produce a signature to claim the funds if he can get hold of the expected pre-image input value, \( \preimage \). He also verifies that the UTXO has a sufficiently long time-lock to give him time to claim the transaction.
-
Upon verification, Bob creates a Segwit HTLC BTC transaction with the same \( \hash{\preimage} \), which will spend
-
to Alice's BTC address she gave him. It is essential to note that the time lock for this HTLC has to expire before
-
the time lock of the XTR HTLC that Alice created.
-
Alice checks the Bitcoin blockchain, and upon seeing that the transaction is mined, she claims the transaction, but,
-
for her to do so, she has to make public what \( \preimage \) is as she has to use it as the witness of the claiming transaction.
-
Bob sees that his BTC is spent, and looks at the witness to get \( \preimage \). Bob can then use \( \preimage \) to claim the XTR transaction.
BTC - HTLC script
Here is the required BTC script that Bob publishes:
OP_IF
OP_SHA256 <HASH256{pre_image}> OP_EQUALVERIFY
<Alice BTC address> OP_CHECKSIG
OP_ELSE
<relative locktime>
OP_CHECKSEQUENCEVERIFY
OP_DROP
<Bob BTC address> OP_CHECKSIG
OP_ENDIF
relative locktime is a time sequence in which Alice chooses to lock up the funds to give Bob time to claim this.
XTR - HTLC script
Here is the required XTR script that Alice publishes:
HashSha256 PushHash(HASH256{pre_image}) Equal
IFTHEN
PushPubkey(K_{Sb})
ELSE
CheckHeightVerify(height)
PushPubkey(K_{Sa})
ENDIF
(\( K_{Sb} \)) is the public key of the script key pair that Bob chooses to claim this transaction if Alice backs out. height is an absolute block height that Bob chooses to lock up the funds to give Alice time to claim the funds.
XTR - XMR swap
The Tari - Monero atomic swap involved a bit more detail than just a simple script and is explained in RFC-0241: XTR - XMR swap
Notation
Where possible, the "usual" notation is used to denote terms commonly found in cryptocurrency literature. Lower case characters are used as private keys, while uppercase characters are used as public keys. New terms introduced here are assigned greek lowercase letters in most cases. Some terms used here are noted down in TariScript.
Name | Symbol | Definition |
---|---|---|
Pre-image | \( \preimage \) | The random byte data used for the pre-image of the hash |
RFC-0241/XMR Atomic Swap
Maintainer(s): S W van Heerden
Licence
Copyright 2021 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
This Request for Comment (RFC) aims to describe how an Atomic swap between Tari and Monero will be created.
Related Requests for Comment
$$ \newcommand{\script}{\alpha} % utxo script \newcommand{\input}{ \theta } \newcommand{\cat}{\Vert} \newcommand{\so}{\gamma} % script offset \newcommand{\hash}[1]{\mathrm{H}\bigl({#1}\bigr)} $$
Comments
Any comments, changes or questions to this PR can be made in one of the following ways:
- Join the discussion on the Github discussion page.
- Create a new PR on the Tari project Github pull requests.
- Create a new issue on the Tari project Github issues.
Description
Doing atomic swaps with Monero is more complicated and requires a cryptographic dance to complete as Monero does not implement any form of HTLC's or the like. This means that when doing an atomic swap with Monero, most of the logic will have to be implemented on the Tari side. Atomic swaps between Monero and Bitcoin have been implemented by the Farcaster project and the Comit team. Due to the how TariScript works, we have a few advantages over Bitcoin script regarding adaptor signatures, as the script key was explicitly designed with scriptless scripts in mind.
Method
The primary, happy path outline of a Tari - Monero atomic swap is described here, and more detail will follow. We assume that Alice wants to trade her XTR for Bob's XMR.
- Negotiation - Both parties negotiate the value and other details of the Monero and Tari UTXO's.
- Commitment - Both parties commit to the keys, nonces, inputs, and outputs to use for the transaction.
- XTR payment - Alice makes the XTR payment to a UTXO containing a "special" script described below.
- XMR Payment - The Monero payment is made to a multiparty scriptless script UTXO.
- Claim XTR - Bob redeems the XTR, and in doing so, reveals the XMR private key to Alice only.
- Claim XMR - Alice may claim the XMR using the revealed key.
Please take note of the notation used in TariScript and specifically notation used on the signatures on the transaction inputs and on the signatures on the transaction outputs. We will note other notations in the Notation section.
TL;DR
The scheme revolves around Alice, who wants to exchange her Tari for Bob's Monero. Because they don't trust each other, they have to commit some information to do the exchange. And if something goes wrong here, we want to ensure that we can refund both parties either in Monero or Tari.
How this works is that Alice and Bob create a shared output on both chains. The Monero output is a simple aggregate key to unlock the UTXO, while multiple keys are needed to unlock the Tari UTXO. An aggregate key locks this Monero UTXO that neither Alice nor Bob knows, but they both know half of the key. The current Tari block height determines the unlocking key for the Tari UTXO.
The process is started by Alice and Bob exchanging and committing to some information. Alice is the first to publish a transaction, which creates the Tari UTXO. If Bob is happy that the Tari UTXO has been mined and verifies all the information, he will publish a transaction to create the Monero UTXO.
The TariScript script on the UTXO ensures that they will have to reveal their portion of the Monero key when either Alice or Bob spends this. This disclosure allows the other party to claim the Monero by being the only one to own the complete Monero aggregate key.
We can visualize the happy path flow with the image below.
The script will ensure that at any point in time, at least someone can claim the Tari UTXO, and if that person does so, the other party can claim the Monero UTXO by looking at the spending data. It has two lock heights, determining who can claim the Tari UTXO if the happy path fails. Before the first lock height, only Bob can claim the Tari; we call this the swap transaction.
If Bob disappears after Alice has posted the Tari UTXO, Alice can claim the Tari after the first lock height and before the second lock height; we call this the refund transaction. It ensures that Alice can reclaim her Tari if Bob disappears, and if Bob reappears, he can reclaim his Monero.
That leaves us with the scenario where Alice disappears after Bob posts the Monero transaction, in which case we need to protect Bob. After the second lock height, only Bob can claim the Tari; we call this the lapse transaction. The lapse transaction will reveal Bob's Monero key so that if Alice reappears, she can claim the Monero.
The image below details the time flow of the Tari transactions spending the Tari UTXO.
Heights, Security, and other considerations
We need to consider a few things for this to be secure, as there are possible scenarios that can reduce the security in the atomic swap.
When looking at the two lock heights, the first lock height should be sufficiently large enough to give ample time for Alice to post the Tari UTXO transaction and for it to be mined with a safe number of confirmations, and for Bob to post the Monero transaction and for it to be mined with a safe number of confirmations. The second lock height should give ample time for Alice after the first lock height to re-claim her Tari. Larger heights here might make refunds slower, but it should be safer in giving more time to finalize this.
Allowing both to claim the Tari after the second lock height is, on face value, a safer option. This can be done by enabling either party to claim the script with the lapse transaction. The counterparty can then claim the Monero. However, this will open up an attack vector to enable either party to claim the Monero while claiming the Tari. Either party could trivially pull off such a scheme by performing a front-running attack and having a bit of luck. The counterparty monitors all broadcast transactions to base nodes. Upon identifying the lapse transaction, they do two things; in quick succession, broadcast their lapse transaction and the transaction to claim the Monero, both with sufficiently high fees. Base nodes will prefer to mine transactions with the higher fees, and thus the counterparty can walk away with both the Tari and the Monero.
It is also possible to prevent the transaction from being mined after being submitted to the mempool. This can be caused by a combination of a too busy network, not enough fees, or a too-small period in the time locks. When one of these atomic swap transactions gets published to a mempool, we effectively already have all the details exposed. For the atomic swaps, it means we already revealed part of the Monero key, although the actual Tari transaction has not been mined. But this is true for any HTLC or like script on any blockchain. But in the odd chance that this does happen whereby the fees are too little and time locks not enough, it should be possible to do a child-pays-for-parent transaction to bump up the fees on the transaction to get it mined and confirmed.
Key construction
Using multi-signatures with Schnorr signatures, we need to ensure that the keys are constructed so that key cancellation attacks are not possible. To do this, we create new keys from the chosen public keys \(K_a'\) and \(K_b'\)
$$ \begin{aligned} K_a &= \hash{\hash{K_a' \cat K_b'} \cat K_a' } * K_a' \\ k_a &= \hash{\hash{K_a' \cat K_b'} \cat K_a' } * k_a' \\ K_b &= \hash{\hash{K_a' \cat K_b'} \cat K_b' } * K_b' \\ k_b &= \hash{\hash{K_a' \cat K_b'} \cat K_b' } * k_b' \\ \end{aligned} \tag{1} $$
Key equivalence
Monero uses Ed25519, while Tari uses Ristretto as its curve of choice. While Ristretto and Ed25519 both works on Curve25519 and the Edward points are the same, they differ when the points are encoded. In practice, this means the following:
$$ \begin{aligned} Xm &= x \cdot G_m \\ X &= x \cdot G \\ X &\neq X_m \\ \end{aligned} \tag{2} $$
To use public keys across different implementations or curves, we must prove that the same private key created the "different" public keys. Because we exchange encoded points, we need some way of proving they are the same. Jepsen designed a proof to prove Discrete Logarithm Equality (DLEQ) between two generator groups. But the proof here is not created for Elliptic Curve Cryptography (ECC) but can be adapted to ECC by:
$$ \begin{aligned} e &= \hash{K_1 \cat K_2 \cat R_{1} \cat R_{2}} \\ s &= r + e(k) \\ R_{1} &= r \cdot G \\ R_{2} &= r \cdot G_m \\ K_{1} &= k \cdot G \\ K_{2} &= k \cdot G_m \\ \end{aligned} \tag{3} $$
The verification is then: $$ \begin{aligned} e &= \hash{K_1 \cat K_2 \cat R_{1} \cat R_{2}} \\ s \cdot G &= R_{1} + e(K_1) \\ s \cdot G_m &= R_{2} + e(K_2) \\ \end{aligned} \tag{4} $$
But this method has a problem with ECC keys as they are always used as \(mod(n)\) where n is the group size. With ECC, we cannot always use the method; it can only be used when the two curves are of the same group size \(n\), or \(s < n\). If this is not the case, a bit comparison needs to be created to prove this. But luckily, we use Ristretto and Ed25519, both on Curve25519, because it’s the same curve, same generator point, and just different encodings. We can use this proof to know even though the encoded public keys do not match, they still share a private key
Key security
The risk of publicly exposing part of the Monero private key is still secure because of how ECC works. We can add two secret keys together and share the public version of both. And at the same time, we know that no one can calculate the secret key with just one part.
$$ \begin{aligned} (k_a + k_b) \cdot G &= k_a \cdot G + k_b \cdot G\\ (k_a + k_b) \cdot G &= K_a + K_b \\ (k_a + k_b) \cdot G &= K \\ \end{aligned} \tag{5} $$
We know that \(K\), \(K_a\), \(K_b\) are public. While \(k\), \(k_a\), \(k_b\) are all private.
But if we expose \(k_b\), we can try to do the following: $$ \begin{aligned} (k_a + k_b) \cdot G &= K_a + K_b\\ k_a \cdot G &= (K_a + K_b - k_b \cdot G) \\ k_a \cdot G &= K_a \\ \end{aligned} \tag{6} $$
However, this is the Elliptic-Curve Discrete Logarithm Problem, and there is no easy solution to solve this on current computer hardware. Thus this is still secure even though we leaked part of the secret key \(k\).
Method 1
Detail
This method relies purely on TariScript to enforce the exposure of the private Monero aggregate keys. Based on Point Time Lock Contracts, the script forces the spending party to supply their Monero
private key part as input data to the script, evaluated via the operation ToRistrettoPoint
. This TariScript operation will
publicly reveal part of the aggregated Monero private key, but this is still secure: see Key security.
The simplicity of this method lies therein that the spending party creates all transactions on their own. Bob requires a pre-image from Alice to complete the swap transaction; Alice needs to verify that Bob published the Monero transaction and that everything is complete as they have agreed. If she is happy, she will provide Bob with the pre-image to claim the Tari UTXO.
TariScript
The Script used for the Tari UTXO is as follows:
ToRistrettoPoint
CheckHeight(height_1)
LtZero
IFTHEN
PushPubkey(X_b)
EqualVerify
HashSha256
PushHash(HASH256{pre_image})
EqualVerify
PushPubkey(K_{Sb})
Else
CheckHeight(height_2)
LtZero
IFTHEN
PushPubkey(X_a)
EqualVerify
PushPubkey(K_{Sa})
Else
PushPubkey(X_b)
EqualVerify
PushPubkey(K_{Sb})
ENDIF
ENDIF
Before height_1
, Bob can claim the Tari UTXO by supplying pre_image
and his private Monero key part x_b
. After
height_1
but before height_2
, Alice can claim the Tari UTXO by supplying her private Monero key part x_a
. After
height_2
, Bob can claim the Tari UTXO by providing his private Monero key part x_b
.
Negotiation
Alice and Bob have to negotiate the exchange rate and the amount exchanged in the atomic swap. They also need to decide how the two UTXO's will look on the blockchain. To accomplish this, the following needs to be finalized:
- Amount of Tari to swap for the amount of Monero
- Monero public key parts \(Xm_a\), \(Xm_b\) ,and its aggregate form \(Xm\)
- DLEQ proof of \(Xm_a\) and \(X_a\)
- Tari script key parts \(K_{Sa}\), \(K_{Sb}\)
- The TariScript to be used in the Tari UTXO
- The blinding factor \(k_i\) for the Tari UTXO, which can be a Diffie-Hellman between their Tari addresses.
Key selection
Using (1), we create the Monero keys as they are multi-party aggregate keys. The Monero key parts for Alice and Bob is constructed as follows:
$$ \begin{aligned} Xm_a' &= xm_a' \cdot G_m \\ Xm_b' &= xm_b' \cdot G_m \\ xm_a &= \hash{\hash{Xm_a' \cat Xm_b'} \cat Xm_a' } * xm_a' \\ xm_b &= \hash{\hash{Xm_a' \cat Xm_b'} \cat Xm_b' } * xm_b' \\ xm_a &= x_a \\ xm_b &= x_b \\ Xm_a &= \hash{\hash{Xm_a' \cat Xm_b'} \cat Xm_a' } * Xm_a' \\ Xm_b &= \hash{\hash{Xm_a' \cat Xm_b'} \cat Xm_b' } * Xm_b' \\ xm &= xm_a + xm_b + k_i \\ Xm &= Xm_a + Xm_b + k_i \cdot G_m\\ x &= x_a + x_b + k_i \\ X &= X_a + X_b + k_i \cdot G\\ \end{aligned} \tag{7} $$
Commitment phase
This phase allows Alice and Bob to commit to using their keys.
Starting values
Alice needs to provide Bob with the following:
- Script public key: \( K_{Sa}\)
- Monero public key \( Xm_a'\) with Ristretto encoding
Bob needs to provide Alice with the following:
- Script public key: \( K_{Sb}\)
- Monero public key \( Xm_b'\) with Ristretto encoding
Using the above equations in (7), Alice and Bob can calculate \(Xm\), \(Xm_a\), \(Xm_b\)
DLEQ proof
Alice needs to provide Bob with:
- Monero public key \(X_a\) encoding on Ristretto
- DLEQ proof for \(Xm_a\) and \(X_a\): \((R_{ZTa}, R_{ZMa}, s_{Za})\)
$$ \begin{aligned} e &= \hash{X_a \cat Xm_a \cat R_{ZTa} \cat R_{ZMa}} \\ s_{Za} &= r + e(x_a) \\ R_{ZTa} &= r \cdot G \\ R_{ZMa} &= r \cdot G_m \\ \end{aligned} \tag{8} $$
Bob needs to provide Alice with:
- Monero public key \(X_b\) encoding on Ristretto
- DLEQ proof for \(Xm_b\) and \(X_b\): \((R_{ZTb}, R_{ZMb}, s_{Zb})\)
$$ \begin{aligned} e &= \hash{X_b \cat Xm_b \cat R_{ZTb} \cat R_{ZMb}} \\ s_{Za} &= r + e(x_b) \\ R_{ZTa} &= r \cdot G \\ R_{ZMa} &= r \cdot G_m \\ \end{aligned} \tag{9} $$
Alice needs to verify Bob's DLEQ proof with:
$$ \begin{aligned} e &= \hash{X_b \cat Xm_b \cat R_{ZTb} \cat R_{ZMb}} \\ s_{Zb} \cdot G &= R_{ZTb} + e(X_b) \\ s_{Zb} \cdot G_m &= R_{ZMb} + e(Xm_b) \\ \end{aligned} \tag{10} $$
Bob needs to verify Alice's DLEQ proof with:
$$ \begin{aligned} e &= \hash{X_a \cat Xm_a \cat R_{ZTa} \cat R_{ZMa}} \\ s_{Za} \cdot G &= R_{ZTa} + e(X_a) \\ s_{Za} \cdot G_m &= R_{ZMa} + e(Xm_a) \\ \end{aligned} \tag{11} $$
XTR payment
Alice will construct the Tari UTXO with the correct script and publish the containing transaction to the blockchain, knowing that she can reclaim her Tari if Bob vanishes or tries to break the agreement. This is done with standard Mimblewimble rules and signatures.
XMR payment
When Bob sees that the Tari UTXO that Alice created is mined on the Tari blockchain with the correct script, Bob can publish the Monero transaction containing the Monero UTXO with the aggregate key \(Xm = Xm_a + Xm_b + k_i \cdot G_m \).
Claim XTR
When Alice sees that the Monero UTXO that Bob created is mined on the Monero blockchain containing the correct aggregate
key \(Xm\), she can provide Bob with the required pre_image
to spend the Tari UTXO. She does not have the
missing key \(xm_b \) to claim the Monero yet, but it will be revealed when Bob claims the Tari.
Bob can now supply the pre_image
and his Monero private key as transaction input to unlock the script.
Claim XMR
Alice can now see that Bob spent the Tari UTXO, and by examining the input_data
required to satisfy the script, she
can learn Bob's secret Monero key. Although this private key \( xm_b \) is now public knowledge, her part of the Monero spend key
is still private, and thus only she knows the complete Monero spend key. She can use this knowledge to claim the Monero
UTXO.
The refund
If something goes wrong and Bob never publishes the Monero or disappears, Alice needs to wait for the lock height
height_1
to pass. This will allow her to reclaim her Tari, but in doing so, she needs to publish her Monero secret key
as input to the script to unlock the Tari. When Bob comes back online, he can use this public knowledge to reclaim his
Monero, as only he knows both parts of the Monero UTXO spend key.
The lapse transaction
If something goes wrong and Alice never gives Bob the required pre_image
, Bob needs to wait for the lock height
height_2
to pass. This will allow him to claim the Tari he wanted all along, but in doing so, he needs to publish
his Monero secret key as input to the script to unlock the Tari. When Alice comes back online, she can use this public
knowledge to claim the Monero she wanted all along as only she now knows both parts of the Monero UTXO spend key.
Method 2
Detail
This method is based on work by the Farcaster project and the Comit team. It utilizes adapter signatures and multi-party commitment signatures to ensure that the spending party leaks their private Monero key part. Because all keys are aggregates keys, we need to ensure that the refund and lapse transactions are negotiated and signed before Alice publishes the Tari UTXO. This will allow either Alice or Bob to claim the refund and lapse transactions, respectively, without the other party being online.
TariScript
The Script used for the Tari UTXO is as follows:
CheckHeight(height_1)
LtZero
IFTHEN
PushPubkey(K_{Ss})
Else
CheckHeight(height_2)
LtZero
IFTHEN
PushPubkey(K_{Sr})
Else
PushPubkey(K_{Sl})
ENDIF
ENDIF
Before height_1
, Bob can claim the Tari UTXO if Alice gives him the correct signature to complete the transaction.
After height_1
but before height_2
, Alice can claim the Tari UTXO. After height_2,
Bob can claim the Tari UTXO.
Negotiation
Alice and Bob have to negotiate the exchange rate and the amount exchanged in the atomic swap. They also need to decide how the two UTXO's will look on the blockchain. To accomplish this, the following needs to be finalized:
- Amount of Tari to swap for the amount of Monero
- Monero public key parts \(Xm_a\), \(Xm_b\), and its aggregate form \(X\)
- Tari script key parts \(K_{Ssa}\), \(K_{Ssb}\), and its aggregate form \(K_{Ss}\) for the swap transaction
- Tari script key parts \(K_{Sra}\), \(K_{Srb}\), and its aggregate form \(K_{Sr}\) for the refund transaction
- Tari script key parts \(K_{Sla}\), \(K_{Slb}\), and its aggregate form \(K_{Sl}\) for the lapse transaction
- Tari sender offset key parts \(K_{Osa}\), \(K_{Osb}\), and its aggregate form \(K_{Os}\) for the swap transaction
- Tari sender offset key parts \(K_{Ora}\), \(K_{Orb}\), and its aggregate form \(K_{Or}\) for the refund transaction
- Tari sender offset key parts \(K_{Ola}\), \(K_{Olb}\), and its aggregate form \(K_{Ol}\) for the lapse transaction
- All of the nonces used in the script signature creation and Metadata signature for the swap, refund, and lapse transactions
- The script offset used in both the swap, refund, and lapse transactions
- The TariScript to be used in the Tari UTXO
- The blinding factor \(k_i\) for the Tari UTXO, which can be a Diffie-Hellman between their addresses.
Key selection
Using (1), we create the Monero keys as they are multi-party aggregate keys.
The script key parts for Alice and Bob is constructed as follows:
$$ \begin{aligned} k_{Ssa} &= \hash{\hash{K_{Ssa}' \cat K_{Ssb}'} \cat K_{Ssa}' } * k_{Ssa}' \\ k_{Ssb} &= \hash{\hash{K_{Ssa}' \cat K_{Ssb}'} \cat K_{Ssb}' } * k_{Ssb}' \\ k_{Ss} &= k_{Ssa} + k_{sb} \\ k_{Sra} &= \hash{\hash{K_{Sra}' \cat K_{Srb}'} \cat K_{Sra}' } * k_{Sra}' \\ k_{Srb} &= \hash{\hash{K_{Sra}' \cat K_{Srb}'} \cat K_{Srb}' } * k_{Srb}' \\ k_{Sr} &= k_{Sra} + k_{Srb} \\ k_{Sla} &= \hash{\hash{K_{Sla}' \cat K_{Slb}'} \cat K_{Sla}' } * k_{Sla}' \\ k_{Slb} &= \hash{\hash{K_{Sla}' \cat K_{Slb}'} \cat K_{Slb}' } * k_{Slb}' \\ k_{Sl} &= k_{Sla} + k_{Slb} \\ \end{aligned} \tag{12} $$
The sender offset key parts for Alice and Bob is constructed as follows:
$$ \begin{aligned} k_{Osa} &= \hash{\hash{K_{Osa}' \cat K_{Osb}'} \cat K_{Osa}' } * k_{Osa}' \\ k_{Osb} &= \hash{\hash{K_{Osa}' \cat K_{Osb}'} \cat K_{Osb}' } * k_{Osb}' \\ k_{Os} &= k_{Osa} + k_{Ssb} \\ k_{Ora} &= \hash{\hash{K_{Ora}' \cat K_{Orb}'} \cat K_{Ora}' } * k_{Ora}' \\ k_{Orb} &= \hash{\hash{K_{Ora}' \cat K_{Orb}'} \cat K_{Orb}' } * k_{Orb}' \\ k_{Or} &= k_{Ora} + k_{Srb} \\ k_{Ola} &= \hash{\hash{K_{Ola}' \cat K_{Olb}'} \cat K_{Ola}' } * k_{Ola}' \\ k_{Olb} &= \hash{\hash{K_{Ola}' \cat K_{Olb}'} \cat K_{Olb}' } * k_{Olb}' \\ k_{Ol} &= k_{Ola} + k_{Slb} \\ \end{aligned} \tag{13} $$
The Monero key parts for Alice and Bob is constructed as follows:
$$ \begin{aligned} xm_a' &= x_a' \\ xm_b' &= x_b' \\ Xm_a' &= x_a' \cdot G_m \\ Xm_b' &= x_b' \cdot G_m \\ X_a' &= x_a' \cdot G \\ X_b' &= x_b' \cdot G \\ x_a &= \hash{\hash{X_a' \cat X_b'} \cat X_a' } * x_a' \\ x_b &= \hash{\hash{X_a' \cat X_b'} \cat X_b' } * x_b' \\ x &= x_a + x_b + k_i \\ Xm &= Xm_a + Xm_b + k_i \cdot G_m \\ X &= X_a + X_b + k_i \cdot G\\ \end{aligned} \tag{14} $$
Commitment phase
Similar to method 1, this phase allows Alice and Bob to commit to using their keys and requires more than one round to complete. Some of the information that needs to be committed depends on previous knowledge.
Starting values
Alice needs to provide Bob with the following:
- Output commitment \(C_r\) of the refund transaction's output
- Output features \( F_r\) of the refund transaction's output
- Output script \( \script_r\) of the refund transaction's output
- Output commitment \(C_l\) of the lapse transaction's output
- Output features \( F_l\) of the lapse transaction's output
- Output script \( \script_l\) of the lapse transaction's output
- Public keys: \( K_{Ssa}'\), \( K_{Sra}'\), \( K_{Sla}'\), \( K_{Osa}'\), \( K_{Ora}'\), \( K_{Ola}'\), \( X_a'\)
- Nonces: \( R_{Ssa}\), \( R_{Sra}\), \( R_{Sla}\), \( R_{Msa}\), \( R_{Mra}\), \( R_{Mla}\)
Bob needs to provide Alice with the following:
- Output commitment \(C_s\) of the swap transaction's output
- Output features \( F_s\) of the swap transaction's output
- Output script \( \script_s\) of the swap transaction's output
- Public keys: \( K_{Ssb}'\), \( K_{Srb}'\), \( K_{Slb}'\), \( K_{Osb}'\), \( K_{Orb}'\), \( K_{Olb}'\), \( X_b'\)
- Nonces: \( R_{Ssb}\), \( R_{Srb}\), \( R_{Slb}\), \( R_{Msb}\), \( R_{Mrb}\), \( R_{Mlb}\)
After Alice and Bob have exchanged the variables, they start trading calculated values.
Construct adaptor signatures and DLEQ proofs
Alice needs to provide Bob with the following values:
- Adaptor signature part \(b_{Sra}'\) for \(b_{Sra}\)
- Signature part \(a_{Sra}\)
- Monero public key \(X_a\) encoded with Ristretto
- Monero public key \(Xm_a\) encoded with ed25519
- DLEQ proof for \(Xm_a\) and \(X_a\): \((R_{ZTa}, R_{ZMa}, s_{Za})\)
Alice constructs the adaptor signature of the input script signature parts for the refund transaction ( \(a_{Sra}\) and \(b_{Sra}'\) ) with:
$$ \begin{aligned} a_{Sra} &= r_{Sra_a} + e_r(v_{i}) \\ b_{Sra}' &= r_{Sra_b} + e_r(k_{Sra}+k_i) \\ e_r &= \hash{ (R_{Sr} + (X_a)) \cat \alpha_r \cat \input_r \cat (K_{Sra} + K_{Srb}) \cat C_i} \\ R_{Sr} &= r_{Sra_a} \cdot H + r_{Sra_b} \cdot G + R_{Srb} \\ X_a &= x_a \cdot G \\ \end{aligned} \tag{15} $$
Alice constructs the DLEQ proof:
$$ \begin{aligned} e &= \hash{X_a \cat Xm_a \cat R_{ZTa} \cat R_{ZMa}} \\ s_{Za} &= r + e(x_a) \\ R_{ZTa} &= r \cdot G \\ R_{ZMa} &= r \cdot G_m \\ \end{aligned} \tag{16} $$
Bob needs to provide Alice with the following values:
- Adaptor signature part \(b_{Ssb}'\) for \(b_{Ssb}\)
- Signature part \(a_{Ssb}\)
- Adaptor signature part \(b_{Slb}'\) for \(b_{Slb}\)
- Signature part \(a_{Slb}\)
- Monero public key \(X_b\) encoded with Ristretto
- Monero public key \(Xm_b\) encoded with ed25519
- DLEQ proof for \(Xm_b\) and \(X_b\): \((R_{ZTb}, R_{ZMb}, s_{Zb})\)
Bob constructs the adaptor signatures of the input script signature parts for the swap transaction ( \(a_{Ssb}\), \(b_{Ssb}'\) ) and the lapse transaction ( \(a_{Slb}\) and \(b_{Slb}'\) ) with
$$ \begin{aligned} a_{Ssb} &= r_{Ssb_a} + e_s(v_{i}) \\ b_{Ssb}' &= r_{Ssb_b} + e_s(k_{Ssb}+k_i) \\ e_s &= \hash{ (R_{Sr} + (X_b)) \cat \alpha_i \cat \input_i \cat (K_{Ssa} + K_{Ssb}) \cat C_i} \\ R_{Ss} &= r_{Ssb_a} \cdot H + r_{Ssb_b} \cdot G + R_{Ssa} \\ a_{Slb} &= r_{Slb_a} + e_l(v_{i}) \\ b_{Slb}' &= r_{Slb_b} + e_l(k_{Slb}+k_i) \\ e_l &= \hash{ (R_{Sl} + (X_b)) \cat \alpha_i \cat \input_i \cat (K_{Sla} + K_{Slb}) \cat C_i} \\ R_{Sl} &= r_{Slb_a} \cdot H + r_{Slb_b} \cdot G + R_{Sla} \\ X_b &= x_b \cdot G \\ \end{aligned} \tag{17} $$
Bob constructs the DLEQ proof:
$$ \begin{aligned} e &= \hash{X_b \cat Xm_b \cat R_{ZTb} \cat R_{ZMb}} \\ s_{Zb} &= r + e(x_b) \\ R_{ZTb} &= r \cdot G \\ R_{ZMb} &= r \cdot G_m \\ \end{aligned} \tag{18} $$
Verify adaptor signatures and DLEQ proofs
Alice needs to verify Bob's adaptor signatures with:
$$ \begin{aligned} a_{Ssb} \cdot H + b_{Ssb}' \cdot G &= R_{Ssb} + (C_i+K_{Ssb})*e_s \\ a_{Slb} \cdot H + b_{Slb}' \cdot G &= R_{Slb} + (C_i+K_{Slb})*e_l \\ \end{aligned} \tag{19} $$
Alice needs to verify Bob's Monero public keys using the zero-knowledge proof:
$$ \begin{aligned} e &= \hash{X_b \cat Xm_b \cat R_{ZTb} \cat R_{ZMb}} \\ s_{Zb} \cdot G &= R_{ZTb} + e(X_b) \\ s_{Zb} \cdot G_m &= R_{ZMb} + e(Xm_b) \\ \end{aligned} \tag{20} $$
Bob needs to verify Alice's adaptor signature with:
$$ \begin{aligned} a_{Sra} \cdot H + b_{Sra}' \cdot G &= R_{Sra} + (C_i+K_{Sra})*e_r \\ \end{aligned} \tag{21} $$
Bob needs to verify Alice's Monero public keys using the zero-knowledge proof:
$$ \begin{aligned} e &= \hash{X_a \cat XM_a \cat R_{ZTa} \cat R_{ZMa}} \\ s_{Za} \cdot G &= R_{ZTa} + e(X_a) \\ s_{Za} \cdot G_m &= R_{ZMa} + e(Xm_a) \\ \end{aligned} \tag{22} $$
Swap out refund and lapse transactions
If Alice and Bob are happy with the verification, they need to swap out refund and lapse transactions.
Alice needs to provide Bob with the following:
- Input script signature for lapse transaction (\( (a_{Sla}, b_{Sla}), R_{Sla}\) )
- Output metadata signature for lapse transaction (\( b_{Mla}, R_{Mla}\) )
- Script offset for lapse transaction \( \so_{la} \)
Alice constructs for the lapse transaction signatures. $$ \begin{aligned} a_{Sla} &= r_{Sla_a} + e_l(v_{i}) \\ b_{Sla} &= r_{Sla_b} + e_l(k_{Sla}) \\ e_l &= \hash{ (R_{Sl} + (X_b)) \cat \alpha_i \cat \input_i \cat (K_{Sla} + K_{Slb}) \cat C_i} \\ R_{Sl} &= r_{Sla_a} \cdot H + r_{Sla_b} \cdot G + R_{Slb}\\ b_{Mla} &= r_{Mla_b} + e(k_{Ola}) \\ R_{Mla} &= b_{Mla} \cdot G \\ e &= \hash{ (R_{Mla} + R_{Mlb}) \cat \script_l \cat F_l \cat (K_{Ola} + K_{Olb}) \cat C_l} \\ \so_{la} &= k_{Sla} - k_{Ola} \\ \end{aligned} \tag{23} $$
Bob needs to provide Alice with the following:
- Input script signature for refund transaction (\( (a_{Srb}, b_{Srb}), R_{Srb}\) )
- Output metadata signature for refund transaction (\( b_{Mrb}, R_{Mrb}\) )
- Script offset for refund transaction \( \so_{rb} \)
Bob constructs for the refund transaction signatures. $$ \begin{aligned} a_{Srb} &= r_{Srb_a} + e_r(v_{i}) \\ b_{Srb} &= r_{Srb_b} + e_r(k_{Srb}) \\ e_r &= \hash{ (R_{Sr} + (X_a)) \cat \alpha_i \cat \input_i \cat (K_{Sra} + K_{Srb}) \cat C_i} \\ R_{Sl} &= r_{Srb_a} \cdot H + r_{Srb_b} \cdot G + R_{Sra}\\ b_{Mrb} &= r_{Mrb_b} + e(k_{Orb}) \\ R_{Mrb} &= b_{Mrb} \cdot G \\ e &= \hash{ (R_{Mra} + R_{Mrb}) \cat \script_r \cat F_r \cat (K_{Ora} + K_{Orb}) \cat C_r} \\ \so_{rb} &= k_{Srb} - k_{Orb} \\ \end{aligned} \tag{24} $$
Although the script validation on output \(C_i\) will not pass due to the lock height, both Alice and Bob need to verify that the total aggregated signatures and script offset for the refund and lapse transaction are valid should they need to publish them at a future date without the other party’s presence.
XTR payment
If Alice and Bob are happy with all the committed values, Alice will construct the Tari UTXO with the correct script and publish the containing transaction to the blockchain. Because Bob already gave her the required signatures for his part of the refund transaction, Alice can easily compute the required aggregated signatures by adding the parts together. She has all the knowledge to spend this after the lock expires.
XMR Payment
When Bob sees that the Tari UTXO that Alice created is mined on the Tari blockchain with the correct script, he can go ahead and publish the Monero UTXO with the aggregate key \(Xm = Xm_a + Xm_b \).
Claim XTR
When Alice sees that the Monero UTXO that Bob created is mined on the Monero blockchain containing the correct aggregate key \(Xm\), she can provide Bob with the following allowing him to spend the Tari UTXO:
- Script signature for the swap transaction \((a_{Ssa}\, b_{Ssa}), R_{Ssa}\)
- Metadata signature for swap transaction \((b_{Msa}, R_{Msa})\)
- Script offset for swap transaction \( \so_{sa} \)
She does not have the missing key \(x_b \) to claim the Monero yet, but it will be revealed when Bob claims the Tari.
Alice constructs for the swap transaction. $$ \begin{aligned} a_{Ssa} &= r_{Ssa_a} + e_s(v_{i}) \\ b_{Ssa} &= r_{Ssa_b} + e_s(k_{Ssa}) \\ e_s &= \hash{ (R_{Ss} + (X_b)) \cat \alpha_i \cat \input_i \cat (K_{Ssa} + K_{Ssb}) \cat C_i} \\ R_{Ss} &= r_{Ssa_a} \cdot H + r_{Ssa_b} \cdot G + R_{Ssb}\\ b_{Msa} &= r_{Msa_b} + e(k_{Osa}) \\ R_{Msa} &= b_{Msa} \cdot G \\ e &= \hash{ (R_{Msa} + R_{Msb}) \cat \script_s \cat F_s \cat (K_{Osa} + K_{Osb}) \cat C_s} \\ \so_{sa} &= k_{Ssa} - k_{Osa} \\ \end{aligned} \tag{25} $$
Bob constructs the swap transaction. $$ \begin{aligned} a_{Ssb} &= r_{Ssb_a} + e_s(v_{i}) \\ b_{Ssb} &= r_{Ssb_b} + x_b + e_s(k_{Ssb} + k_i) \\ e_s &= \hash{ (R_{Ss} + (X_b)) \cat \alpha_i \cat \input_i \cat (K_{Ssa} + K_{Ssb}) \cat C_i} \\ a_{Ss} &= a_{Ssa} + a_{Ssb} \\ b_{Ss} &= b_{Ssa} + b_{Ssb} \\ R_{Ss} &= r_{Ssa_b} \cdot H + r_{Ssb_b} \cdot G + R_{Ssa}\\ a_{Msb} &= r_{Msb_a} + e(v_{s}) \\ b_{Msb} &= r_{Msb_b} + e(k_{Osb}+k_s) \\ R_{Msb} &= a_{Msb} \cdot H + b_{Msb} \cdot G \\ e &= \hash{ (R_{Msa} + R_{Msb}) \cat \script_s \cat F_s \cat (K_{Osa} + K_{Osb}) \cat C_s} \\ R_{Ms} &= R_{Msa} + R_{Msb} \\ \so_{sb} &= k_{Ssb} - k_{Osb} \\ \so_{s} &= \so_{sa} +\so_{sb} \\ \end{aligned} \tag{26} $$
Bob's transaction now has all the required signatures to complete the transaction. He will then publish the transaction.
Claim XMR
Because Bob has now published the transaction on the Tari blockchain, Alice can calculate the missing Monero key \(x_b\) as follows:
$$ \begin{aligned} b_{Ss} &= b_{Ssa} + b_{Ssb} \\ b_{Ss} - b_{Ssa} &= b_{Ssb} \\ b_{Ssb} &= r_{Ssb_b} + x_b + e_s(k_{Ssb} + k_i) \\ b_{Ssb} - b_{Ssb}' &= r_{Ssb_b} + x_b + e_s(k_{Ssb} + k_i) -(r_{Ssb_b} + e_s(k_{Ssb}+k_i))\\ b_{Ssb} - b_{Ssb}' &= x_b \\ \end{aligned} \tag{27} $$
With \(x_b\) in hand, she can calculate \(X = x_a + x_b\), and with this, she claims the Monero.
The refund
If something goes wrong and Bob never publishes the Monero, Alice needs to wait for the lock height
height_1
to pass. This will allow her to create the refund transaction to reclaim her Tari.
Alice constructs the refund transaction with
$$ \begin{aligned} a_{Sra} &= r_{Sra_a} + e_s(v_{i}) \\ b_{Sra} &= r_{Sra_b} + x_a + e_s(k_{Sra} + k_i) \\ e_r &= \hash{ (R_{Sr} + (X_a)) \cat \alpha_i \cat \input_i \cat (K_{Sra} + K_{Srb}) \cat C_i} \\ a_{Sr} &= a_{Sra} + a_{Srb} \\ b_{Sr} &= b_{Sra} + b_{Srb} \\ R_{Sr} &= r_{Sra_a} \cdot H + r_{Sra_b} \cdot G + R_{Srb}\\ a_{Mra} &= r_{Mra_a} + e(v_{r}) \\ b_{Mra} &= r_{Mra_b} + e(k_{Ora}+k_r) \\ R_{Mra} &= a_{Mra} \cdot H + b_{Mra} \cdot G \\ e &= \hash{ (R_{Mra} + R_{Mrb}) \cat \script_s \cat F_s \cat (K_{Ora} + K_{Orb}) \cat C_r} \\ R_{Mr} &= R_{Mra} + R_{Mrb} \\ \so_{ra} &= k_{Sra} - k_{Ora} \\ \so_{r} &= \so_{ra} +\so_{rb} \\ \end{aligned} \tag{28} $$
This allows Alice to claim back her Tari, but it also exposes her Monero key \(x_a\) This means if Bob did publish the Monero UTXO, he could calculate \(X\) using: $$ \begin{aligned} b_{Sr} &= b_{Sra} + b_{Srb} \\ b_{Sr} - b_{Sra} &= b_{Sra} \\ b_{Sra} &= r_{Sra_b} + x_a + e_r(k_{Sra} + k_i) \\ b_{Sra} - b_{Sra}' &= r_{Sra_b} + x_a + e_r(k_{Sra} + k_i) -(r_{Sra_b} + e_r(k_{Sra}+k_i))\\ b_{Sra} - b_{Sra}' &= x_a \\ \end{aligned} \tag{29} $$
The lapse transaction
If something goes wrong and Alice never publishes her refund transaction, Bob needs to wait for the
lock height height_2
to pass. This will allow him to create the lapse transaction to claim the Tari.
Bob constructs the lapse transaction with $$ \begin{aligned} a_{Slb} &= r_{Slb_a} + e_l(v_{i}) \\ b_{Slb} &= r_{Slb_b} + x_b + e_l(k_{Slb} + k_i) \\ e_l &= \hash{ (R_{Sl} + (X_b)) \cat \alpha_i \cat \input_i \cat (K_{Sla} + K_{Slb}) \cat C_i} \\ a_{Sl} &= a_{Sla} + a_{Slb} \\ b_{Sl} &= b_{Sla} + b_{Slb} \\ R_{Sl} &= r_{Slb_a} \cdot H + r_{Slb_b} \cdot G + R_{Sla}\\ a_{Mlb} &= r_{Mlb_a} + e(v_{l}) \\ b_{Mlb} &= r_{Mlb_b} + e(k_{Olb}+k_l) \\ R_{Mlb} &= a_{Mlb} \cdot H + b_{Mlb} \cdot G \\ e &= \hash{ (R_{Mla} + R_{Mlb}) \cat \script_l \cat F_l \cat (K_{Ola} + K_{Olb}) \cat C_l} \\ R_{Ml} &= R_{Mla} + R_{Mlb} \\ \so_{lb} &= k_{Slb} - k_{Olb} \\ \so_{r} &= \so_{la} +\so_{lb} \\ \end{aligned} \tag{30} $$
This allows Bob to claim the Tari he originally wanted, but it also exposes his Monero key \(x_b\) This means if Alice ever comes back online, she can calculate \(X\) and claim the Monero she wanted all along using: $$ \begin{aligned} b_{Sl} &= b_{Slb} + b_{Slb} \\ b_{Sl} - b_{Slb} &= b_{Slb} \\ b_{Slb} &= r_{Slb_b} + x_a + e_r(k_{Slb} + k_i) \\ b_{Slb} - b_{Slb}' &= r_{Slb_b} + x_b + e_r(k_{Slb} + k_i) -(r_{Slb_b} + e_r(k_{Slb}+k_i))\\ b_{Slb} - b_{Slb}' &= x_b \\ \end{aligned} \tag{31} $$
Notation
Where possible, the "usual" notation is used to denote terms commonly found in cryptocurrency literature. Lower case characters are used as private keys, while uppercase characters are used as public keys. New terms introduced here are assigned greek lowercase letters in most cases. Some terms used here are noted down in TariScript.
Name | Symbol | Definition |
---|---|---|
subscript s | \( _s \) | The swap transaction |
subscript r | \( _r \) | The refund transaction |
subscript l | \( _l \) | The lapse transaction |
subscript a | \( _a \) | Belongs to Alice |
subscript b | \( _b \) | Belongs to Bob |
Monero key | \( X \) | Aggregate Monero public key encoded with Ristretto |
Alice's Monero key | \( X_a \) | Alice's partial Monero public key encoded with Ristretto |
Bob's Monero key | \( X_b \) | Bob's partial Monero public key encoded with Ristretto |
Monero key | \( Xm \) | Aggregate Monero public key encoded with Ed25519 |
Alice's Monero key | \( Xm_a \) | Alice's partial Monero public key encoded with Ed25519 |
Bob's Monero key | \( Xm_b \) | Bob's partial Monero public key encoded with Ed25519 |
Script key | \( K_s \) | The script key of the utxo |
Alice's Script key | \( K_sa \) | Alice's partial script key |
Bob's Script key | \( K_sb \) | Bob's partial script key |
Alice's adaptor signature | \( b'_{Sa} \) | Alice's adaptor signature for the signature \( b_{Sa} \) of the script_signature of the utxo |
Bob's adaptor signature | \( b'_{Sb} \) | Bob's adaptor signature for the \( b_{Sb} \) of the script_signature of the utxo |
Ristretto G generator | \(k \cdot G \) | Value k over Curve25519 G generator encoded with Ristretto |
Ristretto H generator | \(k \cdot H \) | Value k over Tari H generator encoded with Ristretto |
ed25519 G generator | \(k \cdot G_m \) | Value k over Curve25519 G generator encoded with Ed25519 |
RFC-0250/Covenants
Covenants
Maintainer(s): Stanley Bondi
Licence
Copyright 2021 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
This Request for Comment (RFC) presents a proposal for introducing covenants into the Tari base layer protocol. Tari Covenants aims to provide restrictions on the future spending of subsequent transactions to enable a number of powerful use-cases, such as
- vaults
- side-chain checkpointing transactions,
- commission on NFT transfers, and
- many others not thought of here.
Related Requests for Comment
Introduction
The Tari protocol already provides programmable consensus, through TariScript, that restricts whether a UTXO may be included as an input to a transaction (a.k.a spent). The scope of information TariScript is inherently limited, by the [TariScript Opcodes] and the input data provided by a spender. Once the requirements of the script are met, a spender may generate UTXOs of their choosing, within the constraints of MimbleWimble.
This RFC aims to expand the capabilities of Tari protocol by adding additional requirements, called covenants that allow the owner(s) of a UTXO to control the composition of a subsequent transaction.
Covenants are not a new idea and have been proposed and implemented in various forms by others.
For example,
- Bitcoin-NG covenants put forward the
CheckOutputVerify
script opcode. - Handshake has implemented covenants to add the UTXO state of their auctioning process.
- Elements Covenants
Covenants in MimbleWimble
In blockchains like Bitcoin, a block contains discrete transactions containing inputs and outputs. A covenant in Bitcoin would be able to interrogate those outputs belonging to the input to ensure that they adhere to rules.
In MimbleWimble, the body of a block and transaction can be expressed in an identical data structure. This
is indeed the case in the Tari codebase, which defines a structure called AggregateBody
containing inputs
and outputs (and kernels) for transactions and blocks. This is innate to MimbleWimble, so even if we were
to put a "box" around these inputs/outputs there is nothing to stop someone from including inputs and
outputs from other boxes as long as balance is maintained.
This results in an interesting dilemma: how do we allow rules that dictate how future outputs look only armed with the knowledge that the rule must apply to one or more outputs?
In this RFC, we propose a covenant scheme that allows the UTXO originator to express a filter that must be satisfied for a subsequent spending transaction to be considered valid.
Assumptions
The following assumptions are made:
- Duplicate commitments within a block are disallowed by consensus prior to covenant execution,
- all outputs in the output set are valid, and
- all inputs are valid spends, save for covenant checks.
Protocol modifications
Modifications to the existing protocol and consensus are as follows:
- the covenant is recorded in the transaction UTXO,
- the covenant is committed to in the output and input hashes to prevent malleability,
- transactions with covenants entering the mempool MUST be validated, and
- each covenant in a block must be validated before being included in the block chain.
Transaction input and output changes
A covenant
field would need to be added to the TransactionOutput
and TransactionInput
structs
and committed to in their hashes.
Covenant definition
We define a clear notation for covenants that mirrors the miniscript project.
Execution Context and Scope
Covenants execute within a limited read-only context and scope. This is both to reduce complexity (and therefore the possibility of bugs) and maintain reasonable performance.
A covenant's context is limited to:
- an immutable reference to the current input,
- a vector of immutable references to outputs in the current block/transaction (called the output set),
- the current input's mined height, and
- the current block height.
Each output's covenant is executed with this context, filtering on the output set and returning the result. The output set given to each covenant at execution MUST be the same set for all covenants and MUST never be influenced by other covenants. The stateless and immutable nature of this scheme has the benefit of being able to execute covenants in parallel.
A covenant passes if at least one output in the set is matched. Allowing more than one output to match allows for covenants that restrict the characteristics of multiple outputs. A covenant that matches zero outputs fails which invalidates the transaction/block.
If a covenant is empty (zero bytes) the identity
operation is implied and therefore, no actual execution need occur.
Argument types
enum CovenantArg {
// byte code: 0x01
// data size: 32 bytes
Hash([u8; 32]),
// byte code: 0x02
// data size: 32 bytes
PublicKey(RistrettoPublicKey),
// byte code: 0x03
// data size: 32 bytes
Commitment(PedersonCommitment),
// byte code: 0x04
// data size: 64 bytes
Signature(Signature),
// byte code: 0x05
// data size: variable
Script(TariScript),
// byte code: 0x06
// data size: variable
Covenant(Covenant),
// byte code: 0x07
// data size: variable
VarInt(VarInt),
// byte code: 0x08
// data size: 1 byte
Field(FieldKey),
// byte code: 0x09
// data size: variable
Fields(Vec<FieldKey>),
}
Output field tags
Fields from each output in the output set may be brought into a covenant filter. The available fields are defined as follows:
Tag Name | Byte Code | Returns |
---|---|---|
field::commitment | 0x00 | output.commitment |
field::script | 0x01 | output.script |
field::sender_offset_public_key | 0x02 | output.sender_offset_public_key |
field::covenant | 0x03 | output.covenant |
field::features | 0x04 | output.features |
field::features_flags | 0x05 | output.features.flags |
field::features_maturity | 0x06 | output.features.maturity |
field::features_unique_id | 0x07 | output.features.unique_id |
field::features_parent_public_key | 0x08 | output.features.parent_public_key |
field::features_metadata | 0x09 | output.features.metadata |
Each field tag returns a consensus encoded byte representation of the value contained in the field.
How those bytes are interpreted depends on the covenant. For instance, filter_fields_hashed_eq
will
concatenate the bytes and hash the result whereas filter_field_int_eq
will interpret the bytes as a
little-endian 64-bit unsigned integer.
Set operations
identity()
The output set is returned unaltered. This rule is implicit for an empty (0 byte) covenant.
op_byte: 0x20
args: []
and(A, B)
The intersection (\(A \cap B\)) of the resulting output set for covenant rules \(A\) and \(B\).
op_byte: 0x21
args: [Covenant, Covenant]
or(A, B)
The union (\(A \cup B\)) of the resulting output set for covenant rules \(A\) and \(B\).
op_byte: 0x22
args: [Covenant, Covenant]
xor(A, B)
The symmetric difference (\(A \triangle B\)) of the resulting output set for covenant rules \(A\) and \(B\). This is, outputs that match either \(A\) or \(B\) but not both.
op_byte: 0x23
args: [Covenant, Covenant]
not(A)
Returns the compliment of A
. That is, all the elements of A
are removed from the
resultant output set.
op_byte: 0x24
args: [Covenant]
empty()
Returns an empty set. This will always fail and, if used alone, prevents the UTXO from ever being spent.
A more useful reason to use empty
is in conjunction a conditional e.g. if_else(Condition(older_rel(10)), A, empty)
op_byte: 0x25
args: []
Filters
filter_output_hash_eq(hash)
Filters for a single output that matches the hash. This filter only returns zero or one outputs.
op_byte: 0x30
args: [Hash]
filter_fields_preserved(fields)
Filter for outputs where all given fields in the input are preserved in the output.
op_byte: 0x31
args: [Fields]
filter_field_int_eq(field, int)
Filters for outputs whose field value matches the given integer value. If the given field cannot be cast to an unsigned 64-bit integer, the transaction/block is rejected.
op_byte: 0x32
args: [Field, VarInt]
filter_fields_hashed_eq(fields, hash)
op_byte: 0x33
args: [Fields, VarInt]
filter_relative_height(height)
Checks the block height that the current UTXO (i.e. the current input) was mined plus height
is greater than or
equal to the current block height. If so, the identity()
is returned, otherwise empty()
.
op_byte: 0x34
args: [VarInt]
Encoding / Decoding
Covenants can be encoded to/decoded from bytes as a token stream. Each token is consumed and interpreted serially before being executed.
For instance,
xor(
filter_output_hash_eq(Hash(0e0411c70df0ea4243a363fcbf161ebe6e2c1f074faf1c6a316a386823c3753c)),
filter_relative_height(10),
)
is represented in hex bytes as 23 30 01 a8b3f48e39449e89f7ff699b3eb2b080a2479b09a600a19d8ba48d765fe5d47d 35 07 0a
.
Let's unpack that as follows:
23 // xor - consume two covenant args
30 // filter_output_hash_eq - consume a hash arg
01 // 32-byte hash
a8b3f48e39449e89f7ff699b3eb2b080a2479b09a600a19d8ba48d765fe5d47d // data
// end filter_output_hash_eq
35 // 2nd covenant - filter_relative_height
07 // varint
0A // 10
// end varint, filter_relative_height, xor
Some functions can take any number of arguments, such as filter_fields_hashed_eq
which defines the Fields
type.
This type is encoded first by its byte code 34
followed by a varint encoded number that indicates the number
of field identifiers to consume. To mitigate misuse, the maximum allowed arguments are limited.
Covenant Validation
A covenant and therefore the block/transaction MUST be regarded as invalid if:
- an unrecognised bytecode is encountered
- the end of the byte stream is reached unexpectedly
- there are bytes remaining on the stream after interpreting
- an invalid argument type is encountered
- the
Fields
type encounters more than 9 arguments (i.e. the number of fields tags available) - the depth of the calls exceeds 16.
Consensus changes
The covenant is executed once all other validations, including TariScript, are complete. This ensures that invalid transactions in a block cannot influence the results.
Considerations
Complexity
This introduces additional validation complexity. We avoid stacks, loops, and conditionals (covenants are basically one conditional), there are overheads both in terms of complexity and performance as a trade-off for the power given by covenants.
The worst case complexity for covenant validation is O(num_inputs*num_outputs)
, although as mentioned above
validation for each input can be executed in parallel. To compensate for the additional workload the network
encounters, use of covenants should incur heavily-weighted fees to discourage needlessly using them.
Cut-through
The same arguments made in the TariScript RFC for the need to prevent cut-through apply to covenants.
Chain analysis
The same arguments made in the TariScript RFC apply.
Security
As all outputs in a block are in the scope of an input to be checked, any unrelated/malicious output in a block could pass an unrelated covenant rule if given the chance. A secure covenant is one that uniquely identifies one or more outputs.
Examples
Now or never
Spend within 10 blocks or burn
not(filter_relative_height(10))
Note, this covenant may be valid when submitted to the mempool, but invalid by the time it is put in a block for the miner.
NFT transfer
Output features as detailed in [RFC-310-AssetImplementation] (early draft stages, still to be finalised) contain the NFT details. This covenant preserves both the covenant protecting the token, and the token itself.
filter_fields_preserved([field::features, field::covenant])
Side-chain checkpointing
and(
filter_field_int_eq(field::feature_flags, 16) // SIDECHAIN CHECKPOINT = 16
filter_fields_preserved([field::features, field::covenant, field::script])
)
Restrict spending to a particular commitment if not spent within 100 blocks
or(
not(filter_relative_height(100)),
filter_fields_hashed_eq([field::commmitment], Hash(xxxx))
)
Output must preserve covenant, features and script or be burnt
xor(
filter_fields_preserved([field::features, field::covenant, field::script]),
and(
filter_field_int_eq(field::features_flags, 128), // FLAG_BURN = 128
filter_fields_hashed_eq([field::commitment, field::script], Hash(...)),
),
)
Commission for NFT transfer
// Must be different outputs
xor(
and(
// Relavant input fields preserved in subsequent output
filter_fields_preserved([fields::features, fields::covenant, fields::script]),
// The spender must obtain the covenent for the subsequent output
filter_fields_hashed_eq([fields::covenant], Hash(xxxx)),
),
// The spender must obtain and submit the output that matches this hash
filter_output_hash_eq(Hash(xxxx)),
)
Other potential covenants
filter_script_eq(script)
filter_covenant_eq(covenant)
filter_script_match(<pattern>)
filter_covenant_match(<pattern>)
RFC-0322/VNRegistration
Validator Node Registration
Maintainer(s): Cayle Sharrock
Licence
Copyright 2019 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to describe Validator Node (VN) registration. Registration accomplishes two goals:
- Provides a register of Validator Nodes with an authority (the Tari base layer).
- Offers Sybil resistance against gaining probabilistic majority of any given publicly nominated VN committee.
Related Requests for Comment
- RFC-0302: Validator Nodes
- RFC-0304: Validator Node Committee Selection
- RFC-0170: The Tari Communication Network and Network Communication Protocol
- RFC-0341: Asset Registration
- RFC-0200: Base Layer Extensions
Description
VNs register themselves on the Base layer using a special transaction type. The registration transaction type requires the spending of a certain minimum amount of Tari coin, the (Registration Deposit), which has a time lock on the output for a minimum amount of time (Registration Term), as well as some metadata, such as the VN's public key.
The Node ID is calculated after registration to prevent mining of VN public keys that can be used to manipulate routing on the Distributed Hash Table (DHT).
Once a VN's Registration Term has expired, so will this specific VN registration. The Unspent Transaction Output (UTXO) time lock will have elapsed so the Registration Deposit can be reclaimed and a new VN registration needs to be performed. This automatic registration expiry will ensure that the VN registry stays up to date with active VN registrations, and inactive registrations will naturally be removed.
Requiring nodes to register themselves serves two purposes:
- makes VN Sybil attacks expensive; and
- provides an authoritative "central-but-not-centralized" registry of VNs from the base layer.
Node ID
The VN ID can be calculated deterministically after the VN registration transaction is mined. This ensures that VNs are randomly distributed over the DHT network.
VN IDs MUST be calculated as follows:
NodeId = Hash( pk || h || kh )
Where
Field | Description |
---|---|
pk | The VN's DHT public key |
h | The block height of the block in which the registration transaction was mined |
kh | The hash of the registration transaction's kernel |
Base Nodes SHOULD maintain a cached list of VNs and MUST return the Node ID in response to a
get_validator_node_id
request.
Validator Node Registration
A VN MUST register on the base layer before it can join any Distributed Area Network (DAN) committees. Registration happens by virtue of a VN registration transaction.
VN registrations are valid for the Registration Term.
The registration term is set at SIX months.
A VN registration transaction is a special transaction.
- The transaction MUST have EXACTLY ONE UTXO with the
VN_Deposit
flag set. - This UTXO MUST also:
- set a time lock for AT LEAST the Registration Term (or equivalent block periods);
- provide the value of the UTXO in the signature metadata; and
- provide the public key for the spending key for the output in the signature metadata.
- The value of this output MUST be equal to or greater than the Registration Deposit.
- The UTXO MUST store:
- the value of the VN deposit UTXO as a u64; and
- the value of the public key for the spending key for the output as 32 bytes in little-endian order.
- The
KernelFeatures
bit flag MUST have theVN_Registration
flag set. - The kernel MUST also store the VN's DHT public key as 32 bytes in little-endian order.
Validator Node Registration Renewal
If a VN owner does not renew the registration before the Registration Term has expired, the registration will lapse and the VN will no longer be allowed to participate in any committees.
The number of consecutive renewals MAY increase the VN's reputation score.
A VN may only renew a registration in the TWO-WEEK period prior to the current term expiring.
A VN renewal transaction is a special transaction:
- The transaction MUST have EXACTLY ONE UTXO with the
VN_Deposit
flag set. - The transaction MUST spend the previous VN deposit UTXO for this VN.
- This UTXO MUST also:
- set a time lock for AT LEAST six months (or equivalent block periods);
- provide the value of the transaction in the signature metadata; and
- provide the public key for the spending key for the output in the signature metadata.
- This UTXO MUST also store:
- The value of the VN deposit UTXO as a u64.
- The value of the public key for the spending key for the output as 32 bytes in little-endian order;
- The VN's Node ID. This can be validated by following the Renewal transaction kernel chain.
- The kernel hash of this transaction's kernel.
- A counter indicating that this is the n-th consecutive renewal. This counter will be confirmed by nodes and miners. The first renewal will have a counter value of one.
- The previous VN deposit UTXO MUST NOT be spendable in a standard transaction (i.e. its time lock has not expired).
- The previous VN deposit UTXO MUST expire within the next TWO WEEKS.
- The transaction MAY provide additional inputs to cover transaction fees and increases in the Registration Deposit.
- The transaction kernel MUST have the
VN_Renewal
bit flag set. - The transaction kernel MUST also store the hash of the previous renewal transaction kernel, or the registration kernel, if this is the first renewal.
One will notice that a VN's Node ID does not change as a result of a renewal transaction. Rather, every renewal adds to a chain linking back to the original registration transaction. It may be desirable to establish a long chain of renewals, in order to offer evidence of longevity and improve a VN's reputation.
RFC-0341: Asset Registration
Asset Registration Process
Maintainer(s): Philip Robinson
Licence
Copyright 2019 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to describe the process in which an Asset Issuer (AI) will need to engage to register a Digital Asset (DA) and commence its operation on the Digital Asset Network (DAN).
Related Requests for Comment
- RFC-0311: Digital Asset Templates
- RFC-0302: Validator Nodes
- RFC-0304: Validator Node Committee Selection
- RFC-0220: Asset Checkpoints
Description
Abstract
This document will describe the process through which an AI will go in order to:
- register a DA on the base layer;
- assemble a committee of Validator Nodes (VNs); and
- commence operation of the DA on the DAN.
Asset Creation Instruction
The first step in registering and commencing the operation of an asset is that the AI MUST issue an asset creation transaction to the base layer.
This transaction will be time-locked for the length of the desired nomination period. This ensures that this transaction
cannot be spent until the nomination period has elapsed so that it is present during the entire nomination process. The
value of the transaction will be the asset_creation_fee
described in RFC-0311. The AI
will spend the transaction back to themselves, but locking this fee up at this stage achieves two goals:
-
Firstly, it makes it expensive to spam the network with asset creation transactions that a malicious AI does not intend to complete.
-
Secondly, it proves to the VNs that participate in the nomination process that the AI does indeed have the funds required to commence operation of the asset once the committee has been selected.
If the asset registration process fails, e.g. if there are not enough available VNs for the committee, then the AI can refund the fee to themselves after the time lock expires.
The transaction will contain the following extra metadata to facilitate the registration process:
-
The value of the transaction in clear text and the public spending key of the commitment so that it can be verified by third parties. A third party can verify the value of the commitment by using the information in (1) and (2) below, to calculate (3):
- The output commitment is $ C = k \cdot G + v \cdot H $.
- $ v $ and $ k \cdot G $ are provided in the metadata.
- A verifier can calculate $ C - k \cdot G = v \cdot H $ and verify this value by multiplying the clear text $ v $ by $ H $ themselves.
-
A commitment (hash) to the asset parameters as defined by a DigitalAssetTemplate described in RFC-0311. This template will define all the parameters of the asset that the AI intends to register, including information the VNs need to know, such as what AssetCollateral is required to be part of the committee.
Once this transaction has been confirmed to the required depth on the blockchain, the nomination phase can begin.
Nomination Phase
The next step in registering an asset is for the AI to select a committee of VNs to manage the asset. The process to do
this is described in RFC-0304. This process lasts as long as the time lock on the asset
creation transaction described above. The VNs have until that time lock elapses to nominate themselves (in the case of
an asset being registered using the committee_mode::PUBLIC_NOMINATION
parameter in the DigitalAssetTemplate).
Asset Commencement
Once the nomination phase is complete and the AI has selected a committee as described in RFC-0304,
the chosen committee and AI are ready to commit their asset_creation_fee
and AssetCollaterals to commence the
operation of the asset. This is done by the AI and the committee members collaborating to build the initial Checkpoint
of the asset. When this Checkpoint transaction is published to the base layer, the digital asset will be live on
the DAN. The Checkpoint transaction is described in RFC_0220.
RFC-0300/DAN
Digital Assets Network
Maintainer(s): Cayle Sharrock and Philip Robinson
Licence
Copyright 2018 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to describe the key features of the Tari second layer, also known as the Digital Assets Network (DAN)
Related Requests for Comment
- RFC-0100: Base Layer
- RFC-0311: Digital Assets
- RFC-0340: VN Consensus Overview
- RFC-0302: Validator Nodes
Description
Abstract
Digital Assets (DAs) are managed by committees of special nodes called Validator Nodes (VNs):
- VNs manage digital asset state change and ensure that the rules of the asset contracts are enforced.
- VNs form a peer-to-peer communication network that together defines the Tari DAN.
- VNs register themselves on the base layer and commit collateral to prevent Sybil attacks.
- Scalability is achieved by sacrificing decentralization. Not all VNs manage every asset. Assets are managed by subsets of the DAN, called VN committees. These committees reach consensus on DA state amongst themselves.
- VNs earn fees for their efforts.
- DA contracts are not Turing complete, but are instantiated by Asset Issuers (AIs) using DigitalAssetTemplates that are defined in the DAN protocol code.
Digital Assets
- DA contracts are not Turing complete, but are selected from a set of DigitalAssetTemplates that govern the behaviour of each contract type. For example, there could be a Single-use Token template for simple ticketing systems, a Coupon template for loyalty programmes, and so on.
- The template system is intended to be highly flexible and additional templates can be added to the protocol periodically.
- Asset issuers can link a Registered Asset Issuer Domain (RAID) ID in an OpenAlias TXT public Domain Name System (DNS) record to a Fully Qualified Domain Name (FQDN) that they own. This is to help disambiguate similar contracts and improve the signal-to-noise ratio from scam or copycat contracts.
An AI will issue a DA by constructing a contract from one of the supported set of DigitalAssetTemplates. The AI will choose how large the committee of VNs will be for this DA, and have the option to nominate Trusted Nodes to be part of the VN committee for the DA. Any remaining spots on the committee will be filled by permissionless VNs that are selected according to a CommitteeSelectionStrategy. This is a strategy that an AI will use to select from the set of potential candidate VNs that nominated themselves for a position on the committee when the AI broadcast a public call for VNs during the asset creation process. For the VNs to accept the appointment to the committee, they will need to put up the specified collateral.
RFC-0301/NamespaceRegistration
Namespace Registration on Base Layer
Maintainer(s): Hansie Odendaal
Licence
Copyright 2019 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to describe and specify the process for creating and linking an asset issuer specified domain name with a digital asset on the Digital Assets Network (DAN).
Related Requests for Comment
Description
Background
Alternative Approaches
In order to easily differentiate digital assets in the DAN, apart from some unique unpronounceable character string, a human-readable identifier (domain name) is required. It is perceived that shorter names will have higher value due to branding and marketability. The question is, how this can be managed elegantly? It is also undesirable if, for example, the real Disney is forced to use the long versioned "disney.com-goofy-is-my-asset-yes" because some fake Disneys claimed "goofy" and "disney.com-goofy" and everything in-between.
One method to curb name space squatting is to register names on the base layer layer with a domain name registration transaction. Let us call such a name a Registered Asset Issuer Name (RAIN). To make registering RAINs difficult enough to prevent spamming the network, a certain amount of Tari coins must be committed in a burn (permanently destroy) or a time-locked pay-to-self type transaction. Lots of management overhead will be associated with such a scheme, even if domainless assets are allowed. However, it would be impossible to stop someone from registering, say, a "disney.com" RAIN if they did not own the real "disney.com" Fully Qualified Domain Name (FQDN).
Another approach would be to make use of the public Domain Name System (DNS) and to link the FQDNs that are already registered, to the digital assets in the DAN, making use of OpenAlias text (TXT) DNS records on an FQDN. Let us call this a Registered Asset Issuer Domain (RAID) TXT record. If we hash a public key and FQDN pair, it will provide us with a unique RAID_ID (RAID Identification). The RAID_ID will serve a similar purpose in the DAN as a Top Level Domain (TLD) in a DNS, as all digital assets belonging to a specific asset issuer could then be grouped under the FQDN by inference. To make this scheme more elaborate, but potentially unnecessary, all such RAID_IDs could also be registered on the base layer, similar to the RAIN scheme.
If the standard Mimblewimble protocol is followed, a new output feature can be defined to cater for a RAID tuple
(RAID_ID, PubKey)
that is linked to a specific Unspent Transaction Output (UTXO). The RAID_ID
could be based on
a Base58Check variant applied to Hash256(PubKey || FQDN)
. If the
amount of Tari coins associated with the RAID tuple transaction is burned, (RAID_ID, PubKey)
will forever be present
on the blockchain and can increase blockchain bloat. On the other hand, if those Tari coins are spent back to their
owner with a specific time lock, it will be possible to spend and prune that UTXO later on. While that UTXO remains
unspent, the RAID tuple will be valid, but when all or part of it is spent, the RAID tuple will disappear from the
blockchain. Such a UTXO will thus be "coloured" while unspent, as it will have different properties to a normal UTXO. It
will also be possible to recreate the original RAID tuple by registering it using the original Hash256(PubKey || FQDN)
.
Thinking about the make-up of the RAID_ID
, it is evident that it can easily be calculated on the fly using the public
key and FQDN, the values of which will always be known. The biggest advantage of having the RAID tuple on the base
layer is that of embedded consensus, where it will be validated (as no duplicates can be allowed) and mined before it
can be used. However, this comes at the cost of more complex code, a more elaborate asset registration process and
higher asset registration fees.
This Request for Comment
This RFC explores the creation and use of RAID TXT records to link asset issuer-specified domain names with digital assets on the DAN, without RAID_IDs being registered on the base layer.
Requirements
OpenAlias TXT DNS Records
An OpenAlias TXT DNS record [1] on an FQDN is a single string and starts with "oa1:<name>" field followed by a number of key-value pairs. Standard (optional) key values are: "recipient_address"; "recipient_name"; "tx_description"; "tx_amount"; "tx_payment_id"; "address_signature"; and "checksum". Additional key values may also be defined. Only entities with write access to a specific DNS record will be able to create the required TXT DNS record entries.
TXT DNS records are limited to multiple strings of size 255, and as the User Datagram Protocol (UDP) size is 512 bytes, a TXT DNS record that exceeds that limit is less optimal [2, Sections 3.3 and 3.4]. Some hosting sites also place limitations on TXT DNS record string lengths and concatenation of multiple strings as per [2]. The basic idea of this specification is to make the implementation robust and flexible at the same time.
Req - Integration with public DNS records MUST be used to ensure valid ownership of an FQDN that needs to be linked to a digital asset on the DAN.
Req - The total size of the OpenAlias TXT DNS record SHOULD NOT exceed 255 characters.
Req - The total size of the OpenAlias TXT DNS record MUST NOT exceed 512 characters.
Req - The OpenAlias TXT DNS record implementation MUST make provision to interpret entries that are made up of more than one string as defined in [2].
Req - The OpenAlias TXT DNS record SHOULD adhere to the formatting requirements as specified in [1].
Req - The OpenAlias TXT DNS record MUST be constructed as follows:
OpenAlias TXT DNS Record Field | OpenAlias TXT DNS Record Data |
---|---|
oa1:<name> | "oa1:tari" |
pk | <256 bit public key, in hexadecimal format (64 characters), that is converted into a Base58 encoded string (44 characters)> |
raid_id | <RAID_ID (refer to RAID_ID) (15 characters)> |
nonce | <256 bit public nonce, in hexadecimal format (64 characters), that is converted into a Base58 encoded string (44 characters)> |
sig | <Asset issuer's 256 bit Schnorr signature for the RAID_ID (refer to RAID_ID), in hexadecimal format (64 characters), that is converted into a Base58 encoded string (44 characters)> |
desc | <Optional RAID description>; ASCII String; Up to 48 characters for the condensed version (using only one string) and up to 235 characters (when spanning two strings). |
crc | <CRC-32 checksum of the entire record, up to but excluding the checksum key-value pair (starting at "oa1:tari" and ending at the last ";" before the checksum key-value pair) in hexadecimal format (eight characters)> |
Examples: Two example OpenAlias TXT DNS records are shown; the first is a condensed version and the second spans two strings:
RAID_ID:
public key = ca469346d7643336c19155fdf5c6500a5232525ce4eba7e4db757639159e9861
FQDN = disney.com
-> id = RYqMMuSmBZFQkgp
base58 encodings:
public key = ca469346d7643336c19155fdf5c6500a5232525ce4eba7e4db757639159e9861
-> base58 = EcbmnM6PLosBzpyCcBz1TikpNXRKcucpm73ez6xYfLtg
public nonce = fc2c5fce596338f43f70dc0ce14659fdfea1ba3e588a7c6fa79957fc70aa1b4b
-> base58 = 5ctFNnCfBrP99rT1AFmj1WPyMD8uAdNUTESHhLoV3KBZ
signature = 7dc54ec98da2350b0c8ed0561537517ac6f93a37f08a34482824e7df3514ce0d
-> base58 = 9TxTorviyTJAaVJ4eY4AQPixwLb6SDL4dieHff6MFUha
OpenAlias TXT DNS record (condensed: 212 characters):
IN TXT = "oa1:tari pk=EcbmnM6PLosBzpyCcBz1TikpNXRKcucpm73ez6xYfLtg;id=RYqMMuSmBZFQkgp;
nonce=5ctFNnCfBrP99rT1AFmj1WPyMD8uAdNUTESHhLoV3KBZ;sig=9TxTorviyTJAaVJ4eY4AQPixwLb6SDL4dieHff6MFUha;
desc=Cartoon characters;crc=176BE80C"
OpenAlias TXT DNS record (spanning two strings: string 1 = 179 characters and string 2 = 250 characters):
IN TXT = "oa1:tari pk=EcbmnM6PLosBzpyCcBz1TikpNXRKcucpm73ez6xYfLtg; id=RYqMMuSmBZFQkgp;
nonce=5ctFNnCfBrP99rT1AFmj1WPyMD8uAdNUTESHhLoV3KBZ; sig=9TxTorviyTJAaVJ4eY4AQPixwLb6SDL4dieHff6MFUha; "
"desc=Cartoon characters: Mickey Mouse\; Minnie Mouse\; Goofy\; Donald Duck\; Pluto\; Daisy Duck\;
Scrooge McDuck\; Launchpad McQuack\; Huey, Dewey and Louie\; Bambi\; Thumper\; Flower\; Faline\;
Tinker Bell\; Peter Pan and Captain Hook.; crc=54465902"
RAID_ID
Because the RAID_ID
does not exist as an entity on the base layer or in the DAN, it cannot be owned or
transferred, but can only be verified as part of the OpenAlias TXT DNS record [1] verification. If an asset creator
chooses not to link a RAID_ID
and FQDN, a default network-assigned RAID_ID
will be used in the digital asset
registration process.
Req - A default RAID_ID
MUST be used where it will not be linked to an FQDN, e.g. it MAY be derived
from a default input string "No FQDN"
.
Req - An FQDN-linked (non-default) RAID_ID
MUST be derived from the concatenation PubKey || <FQDN>
.
Req - All concatenations of inputs for any hash algorithm MUST be done without adding any spaces.
Req - The hash algorithm MUST be Blake2b
using a 32 byte digest size, unless otherwise specified.
Req - Deriving a RAID_ID
MUST be calculated as follows:
- Inputs for all hashing algorithms used to calculate the
RAID_ID
MUST be lower-case characters. - Stage 1 - MUST select the input string to use (either
"No FQDN"
orPubKey || <FQDN>
).- Example: Mimblewimble public key
ca469346d7643336c19155fdf5c6500a5232525ce4eba7e4db757639159e9861
and FQDNdisney.com
are used here, resulting inca469346d7643336c19155fdf5c6500a5232525ce4eba7e4db757639159e9861disney.com
.
- Example: Mimblewimble public key
- Stage 2 - MUST perform
Blake2b
hashing on the result of stage 1 using a 10 byte digest size.- Example: In hexadecimal representation
ff517a1387153cc38009
or binary representation\xffQz\x13\x87\x15<\xc3\x80\t
.
- Example: In hexadecimal representation
- Stage 3 - MUST concatenate the
RAID_ID
identifier byte,0x62
, with the result of stage 2.- Example:
62ff517a1387153cc38009
`.
- Example:
- Stage 4 - MUST convert the result of stage 3 from a byte string into a
Base58
encoded string. This will result in a 15 character string starting withR
.- Example: The resulting
RAID_ID
will beRYqMMuSmBZFQkgp
.
- Example: The resulting
Req - A valid RAID_ID
signature MUST be a 256 bit Schnorr signature defined as s = PvtNonce + e·PvtKey
with
the challenge e
being e = Blake2b(PubNonce || PubKey || RAID_ID)
.
Sequence of Events
The sequence of events leading up to digital asset registration is perceived as follows:
-
The asset issuer will decide if the default
RAID_ID
or aRAID_ID
that is linked to an FQDN must be used for asset registration. (Note: A single linked (RAID_ID
, FQDN) tuple may be associated with multiple digital assets from the same asset issuer.) -
Req - If a default
RAID_ID
is required:- The asset issuer MUST use the default
RAID_ID
(refer to RAID_ID). - The asset issuer MUST NOT sign the
RAID_ID
.
- The asset issuer MUST use the default
-
Req - If a linked (
RAID_ID
, FQDN) tuple is required:- The asset issuer MUST create a
RAID_ID
. - The asset issuer MUST sign the
RAID_ID
as specified (refer to RAID_ID). - The asset issuer MUST create a valid TXT DNS record (refer to OpenAlias TXT DNS Records).
- The asset issuer MUST create a
-
Req - Validator Nodes (VNs) MUST only allow a valid
RAID_ID
to be used in the digital asset registration process. -
Req - VNs MUST verify the OpenAlias TXT DNS record if a linked (
RAID_ID
, FQDN) tuple is used:- Verify that all fields have been completed as per the specification (refer to OpenAlias TXT DNS Records).
- Verify that the
RAID_ID
can be calculated from information provided in the TXT DNS record and the FQDN of the public DNS record it is in. - Verify that the asset issuer's
RAID_ID
signature is valid. - Verify the checksum.
Confidentiality and Security
To prevent client lookups from leaking, OpenAlias recommends making use of DNSCrypt, resolution via DNSCrypt-compatible resolvers that support Domain Name System Security Extensions (DNSSEC) and without DNS requests being logged.
Req - Token Wallets (TWs) and VNs SHOULD implement the following confidentiality and security measures when dealing with OpenAlias TXT DNS records:
- All queries SHOULD make use of the DNSCrypt protocol.
- Resolution SHOULD be forced via DNSCrypt-compatible resolvers that:
- support DNSSEC;
- do not log DNS requests.
- The DNSSEC trust chain validity:
- SHOULD be verified;
- SHOULD be reported back to the asset issuer.
References
[1] Crate Openalias [online]. Available: https://docs.rs/openalias/0.2.0/openalias/index.html. Date accessed: 2019-03-05.
[2] RFC 7208: Sender Policy Framework (SPF) for Authorizing Use of Domains in Email, Version 1 [online]. Available: https://tools.ietf.org/html/rfc7208. Date accessed: 2019-03-06.
RFC-0302/ValidatorNode
Validator Nodes
Maintainer(s): Cayle Sharrock and Philip Robinson
Licence
Copyright 2018 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to describe the responsibilities of Validator Nodes (VNs) on the Digital Asset Network (DAN).
Related Requests for Comment
- RFC-0322: Validator Node Registration
- RFC-0304: Validator Node Committee Selection
- RFC-0340: VN Consensus Overview
Description
Abstract
Validator Nodes form the basis of the second-layer DAN. All actions on this network take place by interacting with VNs. Some examples of actions that VNs will facilitate are:
- issuing a Digital Asset (DA);
- querying the state of a DA and its constituent tokens; and
- issuing an instruction to change the state of a DA or tokens.
VNs will also perform archival functions for the assets they manage. The lifetime of these archives and the fee structure for this function are still being discussed.
Registration
VNs register themselves on the Base Layer using a special transaction type.
Validator node registration is described in RFC-0322.
Execution of Instructions
VNs are expected to manage the state of DAs on behalf of DA issuers. They receive fees as reward for doing this.
- DAs consist of an initial state plus a set of state transition rules. These rules are set by the Tari protocol, but will usually provide parameters that must be specified by the Asset Issuer.
- The set of VNs that participate in managing state of a specific DA is called a Committee. A committee is selected during the asset issuance process and membership of the committee can be updated at Checkpoints.
- The VN is responsible for ensuring that every state change in a DA conforms to the contract's rules.
- VNs accept DA Instructions from clients and peers. Instructions allow for creating, updating, expiring and archiving DAs on the DAN.
- VNs provide additional collateral, called AssetCollateral, when accepting an offer to manage an asset, which is stored in a multi-signature (multi-sig) Unspent Transaction Output (UTXO) on the base layer. This collateral can be taken from the VN if it is proven that the VN engaged in malicious behaviour.
- VNs participate in fraud-proof validations in the event of consensus disputes (which could result in the malicious VN's collateral being slashed).
- DA metadata (e.g. large images) is managed by VNs. The large data itself will not be stored on the VNs, but in an external location, and a hash of the data can be stored. Whether the data is considered part of the state (and thus checkpointed) or out of state depends on the type of DA contract employed.
Fees
Fees will be paid to VNs based on the amount of work they did during a checkpoint period. The fees will be paid from a fee pool, which will be collected and reside in a UTXO that is accessible by the committee. The exact mechanism for the payment of the fees by the committee and who pays the various types of fees is still under discussion.
Checkpoints
VNs periodically post checkpoint summaries to the base layer for each asset that they are managing. The checkpoints will form an immutable record of the DA state on the base layer. There will be two types of checkpoints:
-
An Opening Checkpoint (OC) will:
- specify the members of the VN committee;
- lock up the collateral for the committee members for this checkpoint period; and
- collect the fee pool for this checkpoint period from the Asset Issuer into a multi-sig UTXO under the control of the committee. This can be a top-up of the fees or a whole new fee pool.
-
A Closing Checkpoint (CC) will:
- summarize the DA state on the base layer;
- release the fee payouts;
- release the collateral for the committee members for this checkpoint period; and
- allow for committee members to resign from the committee.
After a DA is issued, there will immediately be an OC. After a checkpoint period there will then be a CC, followed immediately by an OC for the next period. We will call this set of checkpoints an Intermediate checkpoint, which could be a compressed combination of an OC and CC. This will continue until the end of the asset's lifetime, when there will be a final CC that will be followed by the retirement of the asset.
Consensus
Committees of VNs will use a ConsensusStrategy to manage the process of:
- propagating signed instructions between members of the committee; and
- determining when the threshold has been reached for an instruction to be considered valid.
Part of the Consensus Strategy will be mechanisms for detecting actions by Bad Actors. The nature of the enforcement actions that can be taken against bad actors is still to be decided.
Network Communication
The VNs will communicate using a Peer-to-Peer (P2P) network. To facilitate this, the VNs must perform the following functions:
- VNs MUST maintain a list of peers, including which assets each peer is managing.
- VNs MUST relay instructions to members of the committee that are managing the relevant asset.
- VNs MUST respond to requests for information about DAs that they manage on the DAN.
- VNs and clients can advertise public keys to facilitate P2P communication encryption.
RFC-0304/VNCommittees
Validator Node Committee Selection
Maintainer(s): Philip Robinson
Licence
Copyright 2019 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to describe the process an Asset Issuer (AI) will go through in order to select the committee of Validator Nodes (VNs) that will serve a given Digital Asset (DA).
Related Requests for Comment
Description
Abstract
Digital Assets (DAs) are managed by committees of nodes called Validator Nodes (VNs), as described in RFC-0300 and RFC-0302. During the asset creation process, described in RFC-0341, the Asset Issuer (AI) MUST select a committee of VNs to manage their asset. This process consists of the following steps:
- Candidate VNs MUST be nominated to be considered for selection by the AI.
- The AI MUST employ a CommitteeSelectionStrategy to select VNs from the set of nominated candidates.
- The AI MUST make an offer of committee membership to the selected VNs.
- Selected VNs MAY accept the offer to become part of the committee by posting the required AssetCollateral.
Nomination
The first step in assembling a committee is to nominate candidate VNs. As described in
RFC-0311, an asset can be created with two possible committee_modes
- CREATOR_NOMINATION
or PUBLIC_NOMINATION
:
- In
CREATOR_NOMINATION
mode, the AI nominates candidate committee members directly. The AI will have a list of permissioned Trusted Nodes that they want to act as the committee. The AI will contact the candidate VNs directly to inform them of their nomination. - In
PUBLIC_NOMINATION
mode, the AI does not have a list of Trusted Nodes and wants to source unknown VNs from the network. In this case, the AI broadcasts a public call for nomination to the Tari network using the peer-to-peer messaging protocol described in RFC-0172. This call for nomination contains all the details of the asset. VNs that want to participate will then nominate themselves by contacting the AI.
Selection
Once the AI has received a list of nominated VNs, it must make a selection, assuming enough VNs were nominated to populate the committee. The AI will employ some CommitteeSelectionStrategy in order to select the committee from the candidate VNs that have been nominated. This strategy might aim for a perfectly random selection, or perhaps it will consider some metrics about the candidate VNs, such as the length of their VN registrations. These metrics might indicate that they are reliable and have not been denylisted for poor or malicious performance.
A consideration when selecting a committee in PUBLIC_NOMINATION
mode will be the size of the pool of nominated VNs.
The size of this pool relative to the size of the committee to be selected will be linked to a risk profile. If the pool
contains very few candidates, then it will be much easier for an attacker to have nominated their own nodes in order to
obtain a majority membership of the committee. For example, if the AI is selecting a committee of 10 members using a uniformly
random selection strategy and only 12 public nominations are received, an attacker only requires control of six VNs to
achieve a majority position in the committee. In contrast, if 100 nominations are received and the AI performs a
uniformly random selection, an attacker would need to control more than 50 of the nominated nodes in order to achieve a
majority position in the committee.
Offer Acceptance
Once the selection has been made by the AI, the selected VNs will be informed and they will be made an offer of membership. If the VNs are still inclined to join the committee, they will accept the offer by posting the AssetCollateral required by the asset to the base layer during the initial Checkpoint transaction built to commence the operation of the asset.
Asset Management
This section contains RFCs that describe various aspects of asset management in the Tari network.
RFC-0311/AssetTemplates
Digital Asset Templates
Maintainer(s): Cayle Sharrock
Licence
Copyright 2019 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to describe the Tari Digital Asset templating system for smart contract definition.
The term “smart contracts” in this document is used to refer to a set of rules enforced by computers. These smart contracts are not Turing complete, such as those executed by the Ethereum Virtual Machine (VM).
Related Requests for Comment
- RFC-0300: The Digital Assets Network
- RFC-0301: Namespace Registration
- RFC-0340: Validator Node Consensus
- RFC-0345: Asset Life cycle
Description
Motivation
The reasons for issuing assets on Tari under a templating system, rather than a scripting language (whether Turing complete or not), are manifold:
- A scripting language, irrespective of how simple it is, limits the target market for asset issuers to developers, or people who pay developers.
- The market doesn’t want general smart contracts. This is evidenced by the fact that the vast majority of Ethereum transactions go through ERC-20 or ERC-721 contracts, which are literally contract templates.
- The attack surface for smart contracts is reduced considerably, to the node software itself.
- Bugs can be fixed for all contracts simultaneously by using a template versioning system. Existing assets can opt in to fixes by migrating assets to a new version of the contract.
- Contracts will have better Quality Assurance (QA), since more eyes are looking at fewer contract code sets.
- Transmission, storage and processing of contracts will be more efficient, as one only has to deal with the parameters, and not the logic of the contract. Furthermore, the cost for users is usually lower, since there's no need to add friction or extra costs to contract execution (e.g. Ethereum gas) to work around the halting problem.
Implementation
Assets are created on the Tari network by issuing a create_asset
instruction from a wallet or client, and broadcasting
it to the Tari Digital Assets Network (DAN).
The instruction is in JSON format and MUST contain the following fields:
Name | Type | Description |
---|---|---|
Asset Description | ||
issuer | PubKey | The public key of the creator of the asset. Refer to issuer. |
name | string[64] | The name or identifier for the asset. Refer to Name and Description. |
description | string[216] | A short description of the asset - with name, fits in a tweet. Refer to Name and Description. |
raid_id | string[15] | The Registered Asset Issuer Domain (RAID_ID) for the asset. |
fqdn | string[*] | The Fully Qualified Domain Name (FQDN) corresponding to the raid_id . Up to 255 characters in length; or "No_FQDN" to use the default. |
public_nonce | PubKey | Public nonce part of the creator signature. |
template_id | u64 | The template descriptor. Refer to Template ID. |
asset_expiry | u64 | A timestamp or block height after which the asset will automatically expire. Zero for arbitrarily long-lived assets. |
Validation Committee Selection | ||
committee_mode | u8 | The validation committee nomination mode, either CREATOR_NOMINATION (0) or PUBLIC_NOMINATION (1). |
committee_parameters | Object | Refer to Committee Parameters. |
asset_creation_fee | u64 | The fee the issuer is paying, in microTari, for the asset creation process. |
commitment | u256 | A time-locked commitment for the asset creation fee. |
initial_state_hash | u256 | The hash of the canonical serialization of the initial template state (of the template-specific data). |
initial_state_length | u64 | Size in bytes of initial state. |
Template-specific Data | Object | Template-specific metadata can be defined in this section. |
Signatures | ||
metadata_hash | u256 | A hash of the previous three sections' data, in canonical format (m ). |
creator_sig | u256 | A digital signature of the message H(R ‖ P ‖ RAID_ID ‖ m) , using the asset creator’s private key corresponding to the issuer Public Key Hash (PKH). |
commitment_sig | u256 | A signature proving the issuer is able to spend the commitment to the asset fee. |
Committee Parameters
If committee_mode
is CREATOR_NOMINATION
, the committee_parameters
object is:
Name | Type | Description |
---|---|---|
trusted_node_set | Array of PKH | See below. |
Only the nodes in the trusted node set will be allowed to execute instructions for this asset.
If committee_mode
is PUBLIC_NOMINATION
, the committee_parameters
object is:
Name | Type | Description |
---|---|---|
node_threshold | u32 | The required number of Validator Nodes (VNs) that must register to execute instructions for this asset. |
minimum_collateral | u64 | The minimum amount of Tari a VN must put up in collateral in order to execute instructions for this asset. |
node_selection_strategy | u32 | The selection strategy to employ allowing nodes to register to manage this asset. |
Issuer
Anyone can create new assets on the Tari network from their Tari Collections client. The client will provide the Public Key Hash (PKH) and sign the instruction. The client needn’t use the same private key each time.
Name and Description
These fields are purely for information purposes. They do not need to be unique and do not act as an asset ID.
RAID ID
The RAID_ID is a 15-character string that associates the asset issuer with a registered Internet domain name on the Domain Name System (DNS).
If it is likely that a digital asset issuer will be issuing many assets on the Tari Network (hundreds or thousands),
the issuer should strongly consider using a registered domain (e.g. acme.com
). This is
done via OpenAlias on the domain owner's DNS record, as described in RFC-0301. A RAID prevents spoofing of assets from
copycats or other malicious actors. It also simplifies asset discovery.
Assets from issuers that do not have a RAID are all grouped under the default RAID.
RAID owners must provide a valid signature proving that they own the given domain when creating assets.
Fully Qualified Domain Name
The Fully Qualified Domain Name (FQDN) that corresponds to the raid_id
or the string "NO FQDN"
to use the default RAID ID.
Validator Nodes (VNs) will calculate and check that the RAID ID is valid when
validating the instruction signature.
Public Nonce
A single-use public nonce to be used in the asset signature.
Asset Identification
Assets are identified by a 64-character string that uniquely identifies an asset on the network:
Bytes | Description |
---|---|
8 | Template type (hex) |
4 | Template version (hex) |
4 | Feature flags (hex) |
15 | RAID identifier (Base58) |
1 | A period character, . |
32 | Hex representation of the metadata_hash field |
This allows assets to be deterministically identified from their initial state. Two different creation instructions
leading to the same hash refer to the same single asset, by definition. VNs maintain an index of assets and
their committees, and so can determine whether a given asset already exists; and MUST reject any create_asset
instruction for an existing asset.
Template ID
Tari uses templates to define the behaviour for its smart contracts. The template ID refers to the type of digital asset being created.
Note: Integer values are given in little-endian format, i.e. the least significant bit is first.
The template number is a 64-bit unsigned integer and has the following format, with 0 representing the least significant bit:
Bit Range | Description |
---|---|
0 - 31 | Template type (0 - 4,294,967,295) |
32 - 47 | Template version (0 - 65,535) |
48 | Beta Mode flag |
49 | Confidentiality flag |
50 - 63 | Reserved (must be 0) |
The lowest 32 bits refer to the canonical smart contract type, i.e. the qualitative types of contracts the network supports. Many assets can be issued from a single template.
Template types below 65,536 (216) are public, community-developed templates. All VNs MUST implement and be able to interpret instructions related to these templates.
Template types 65,536 and above are opt-in or proprietary templates. There is no guarantee that any given VN will be able to manage assets on these templates. Part of the committee selection and confirmation process for new assets will be an attestation by VNs that they are willing and able to manage the asset under the designated template rules.
A global registry of opt-in template types will be necessary to prevent collisions (public templates existence will be evident from the Validator Node source code), possibly implemented as a special transaction type on the base layer, which is perfectly suited for hosting such a registry. The details of this will be covered in a separate proposal.
Examples of template types may be:
Template Type | Asset |
---|---|
1 | Simple single-use tokens |
2 | Simple coupons |
20 | ERC-20-compatible |
... | ... |
120 | Collectible cards |
144 | In-game items |
721 | ERC-721-compatible |
... | ... |
65,537 | Acme In game items |
723,342 | CryptoKitties v8 |
The template ID may also set one or more feature flags to indicate that the contract is:
- Experimental, or in testing phase (bit 48).
- Confidential. The definition of confidential assets and their implementation had not been finalized at the time of writing.
Wallets/client apps SHOULD have settings to allow, or otherwise completely ignore, asset types on the network that have certain feature flags enabled. For instance, most consumer wallets should never interact with templates that have the “Beta mode” bit set. Only developers' wallets should ever even see that such assets exist.
Asset Expiry
Asset issuers can set a future expiry date or block height, after which the asset will expire and nodes will be free to expunge any/all state relating to the asset from memory after a fixed grace period. The grace period is to allow interested parties (e.g. the issuer) to take a snapshot of the final state of the contract if they wish (e.g. proving that you had a ticket for that epic World Cup final game, even after the asset no longer exists on the DAN).
Nodes will publish a final checkpoint on the base layer soon after expiry and before purging an asset.
The expiry_date is a Unix epoch, representing the number of seconds since 1 January 1970 00:00:00 UTC if the value is greater than 1,500,000,000; or a block height if it is less than that value (with 1 min blocks this scheme is valid until the year 4870).
Expiry times should not be considered exact, since nodes don’t share the same clocks and block heights, and time proxies become more inaccurate the further out you go (since height in the future is dependent on hash rate).
Signature Validation
Validator nodes will verify the creator_sig
for every create_asset
instruction before propagating the instruction to
the network. The process is as follows:
-
The VN MUST calculate the metadata hash by hashing the canonical representation of all the data in the first three sections of the
create_asset
instruction. -
The VN MUST compare this calculated value to the value given in the
metadata_hash
field. If they do not match, the VN MUST drop the instruction and STOP. -
The VN MUST calculate the RAID ID from the
fqdn
andissuer
fields as specified in RFC-0301. -
The VN MUST compare the calculated RAID ID with the value given in the
raid_id
field. If they do not match, the VN MUST drop the instruction and STOP. -
If the
fqdn
is `"No FQDN", then skip to step 9. -
The VN MUST Look up the OpenAlias TXT record at the domain given in
fqdn
. If the record does not exist, then the VN MUST drop the instruction and STOP. -
The VN MUST check that each of the public key and RAID ID in the TXT record match the values in the
create_asset
instruction. If any values do not match, the VN MUST then drop the instruction and STOP. -
The VN MUST validate the registration signature in the TXT record, using the TXT record's nonce, the issuer's public key and the RAID ID. If the signature does not verify, the VN MUST drop the instruction and STOP.
-
The VN MUST validate the signature in the
creator_sig
field against the challenge built up from the issuer's public key, the nonce given inpublic_nonce
field, theraid_id
field and themetadata_hash
field.
If step 9 passes, then the VN has proven that the create_asset
contains a valid RAID ID, and that if a non-default
FQDN was provided, the owner of that domain provided the create_asset
instruction. In this case, the VN SHOULD
propagate the instruction to the network.
RFC-0345/AssetLifeCycle
Asset Life Cycle
Maintainer(s): Cayle Sharrock
Licence
Copyright 2019. The Tari Development Community.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community regarding the technological merits of the potential system outlined herein.
Goals
Related Requests for Comment
- RFC-0300: The Digital Assets Network
- RFC-0301: Namespace Registration
- RFC-0311 Asset templates
- RFC-0340: Validator Node Consensus
Description
Introduction
Tari digital assets are created on the Digital Assets Network, managed by a Validator Node (VN) committee, see RFC-0340, and are operated under the rules of the template that governs the asset.
A given version of a template provides an immutable definition of the type of information (the state) that is managed during the lifetime of the asset. All the rules that govern reading and updating of the asset state, and rules regarding any transfers of tokens that may be defined as part of the asset are governed by the template code.
This immutability is what allows VN committees to reach rapid consensus about what an asset’s state looks
like after instructions are executed.
However, immutability is a major problem when faced with typical software and business development challenges such as software bugs, or changes in legal and operational requirements.
The Tari network is designed to accommodate these requirements by offering a migration path for assets from one version of a template to another version of a template.
Asset migration
To carry out a successful migration, the following requirements must be met:
- The validator node committee for the asset must support the migration path. This entails that every VN in the
committee has a
MigrateAsset
class from thetemplate_type.version
combination of the existing asset to thetemplate_type.version
of the new asset. - The original asset issuer provides a valid
migrate_asset
instruction to the DAN.- The asset issuer MUST provide any additional state that exists in the new template version and not the original.
- The original asset SHOULD be marked as
retired
. If so, thesuperseded_by
field in the old asset will carry the new asset id once the new asset has been confirmed. We recommend retiring the old asset because all the keys that indicate ownership of tokens will be copied over; effectively re-using them; which can damage privacy. - A policy is provided to determine the course of action to follow if any state from the old asset is illegal under
the new template rules (e.g. If a new rule requires
token.age
to be > 21; what happens to any tokens where this condition fails?)
As part of the migration,
- An entirely new asset is created with the full state of the old asset copied over into the new asset; supplemented with any additional state required in the new template.
- A state validation run is performed; and any invalid state from the old asset is modified according to the migration policy.
- Step 2 is repeated until a valid initial state is produced.
- If Step 2 has run
STATE_VALIDATION_LIMIT
times and the initial state is still not valid, the migration instruction is rejected; the migrate_asset instruction will advise what should be done with the original asset in this case: either allow the original asset to continue as before, or retire it. - Once a valid initial state is produced, a new
create_asset
instruction is generated from the initial state and themigrate_asset
instruction. Typically the same VN committee will be used for the new asset, but this needn’t be the case.
Once a successful migration has completed, any instructions to the old asset can return a simple asset_migrated
response with the new asset ID, which will allow clients and wallets to seamlessly update their records.
Retiring Assets
Retiring an asset follows the same procedure as when as asset reaches its natural end-of-life: A final checkpoint is posted to the base layer and a grace period is given to allow DAN nodes and clients to take a snapshot of the final state if desired.
Resurrecting assets
It’s unreasonable to expect VNs to hold onto large chunks of state for assets that are effectively dead (e.g. ticket stubs long after the event is over). For this reason, assets are allowed to expire after which VNs can forget about the state and use that storage for something else.
However, it may be that interest in an asset resurfaces long after the asset expires (nostalgia being the multi-billion
dollar industry it is today). The resurrect_asset
instruction provides a mechanism to bring an asset back to life.
To resurrect an asset, the following conditions must be met:
- The asset must have expired.
- It must not be currently active (i.e. it hasn’t already been resurrected).
- An asset issuer (not necessarily the original asset issuer) must provide funding for the new lifetime of the asset.
- The asset issuer needs to have a copy of the state corresponding to the final asset checkpoint of the original asset.
- The new asset issuer transmits a
resurrect_asset
instruction to the network. This instruction is identical to the originalcreate_asset
instruction with the following exceptions:- The “initial state” merkle root must be equal to the final state checkpoint merkle root.
- The asset owner public key will be provided by the new asset issuer.
- Third parties can interrogate the new committee asking them to provide a Merkle Proof for pieces of state that the third party (perhaps former asset owners) knows about. This can mitigate fraud attempts where parties can attempt to resurrect assets without actually having a copy of the smart contract state. If enough random state proofs are requested, or a single proof of enough random pieces of state, we can be confident that the asset resurrection is legitimate.
The VN committee for the resurrected asset need not bear any relation to the original VN committee. Once confirmed, the resurrected asset operates exactly like any other asset. An asset can expire and be resurrected multiple times (sequentially).
RFC-0340/VNConsensusOverview
Validator node consensus algorithm
Maintainer(s): Cayle Sharrock
License
Copyright 2019. The Tari Development Community
Validator node consensus algorithm
Maintainer(s): Cayle Sharrock
License
Copyright 2019. The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
The purpose of this document and its content is for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community regarding the technological merits of the potential system outlined herein.
Goals
This document describes at a high level how smart contract state is managed on the Tari Digital Assets Network.
Related RFCs
- RFC-0300: The Tari Digital Assets Network
- RFC-0302: Validator Nodes
- RFC-0304: Validator Node committee selection
- RFC-0341: Asset Registration
Description
Overview
The primary problem under consideration here is for multiple machines running the same program (in the form of a Tari smart contract) to maintain agreement on what the state of the program is, often under adverse conditions, including unreliable network communication, malicious third parties, or even malicious peers running the smart contract.
In computer science terms, the problem is referred to as State Machine Replication, or SMR. If we want our honest machines (referred to as replicas in SMR parlance) to reach agreement in the face of arbitrary failures, then we talk about our system being Byzantine Fault Tolerant.
Tari Asset committees are chosen by the asset issuer according to RFC-0304. The committees form a fixed set of replicas, at the very least from checkpoint to checkpoint, and will typically be limited in size, usually less than ten, and almost always under 100. Note: These numbers are highly speculative based on an intuitive guess about the main use cases for Tari DAs, where we have
- many 1-3-sized committees where the asset issuer and the VN committee are the same entity,
- semi-decentralised assets of ±4-10 where speed trumps censorship-resistance,
- a small number of 50-100 VNs where censorship-resistance trumps speed.
Because nodes cannot join and leave the committees at will, robust yet slow and expensive consensus approaches such as Nakamoto consensus can be dropped in favour of something more performant.
There is a good survey of consensus mechanisms on Tari Labs University.
From the point of view of a DAN committee, the ideal consensus algorithm is one that
- Allows a high number of transactions per second, and doesn't have unnecessary pauses (i.e. a partially synchronous or asynchronous model).
- Is Byzantine Fault tolerant.
- Is relatively efficient from a network communication point of view (number of messages passed per state agreement).
- Is relatively simple to implement (to reduce the bug and vulnerability surface in implementations).
A summary of some of the most well-known BFT algorithms is presented in this table.
A close reading of the algorithms presented suggest that LinBFT, which is based on HotStuff BFT provide the best trade-offs for the goals that a DAN committee is trying to achieve:
- The algorithm is optimistic, i.e. as soon as quorum is reached on a particular state, the committee can move onto the next one. There is no need to wait for the "timeout" period as we do in e.g. Tendermint. This allows instructions to be executed almost as quickly as they are received.
- The algorithm is efficient in communication, requiring O(n) messages per state agreement in most practical cases. This is compared to e.g. PBFT which requires O(n4) messages.
- The algorithm is modular and relatively simple to implement.
Potential drawbacks to using HotStuff include:
- Each round required the election of a leader. Having a leader dramatically simplifies the consensus algorithm; it allows a linear number of messages to be sent between the leader and the other replicas in order to agree on the current state; and it allows a strict ordering to be established on instructions without having to resort to e.g. proof of work. However, if the choice of leader is deterministic, attackers can identify and potentially DDOS the leader for a given round, causing the algorithm to time out. There are ways to mitigate this attack for a specific round, as suggested in the LinBFT paper, such as using Verifiable Random Functions, but DDOSing a single replica means that, on average, the algorithm will time out every 1/n rounds.
- The attack described above only pauses progress in Hotstuff for the timeout period. In similar protocols, e.g. Tendermint it can be shown to delay progress indefinitely.
Given these trade-offs, there is strong evidence to suggest that HotStuff BFT, when implemented on the Tari DAN will provide BFT security guarantees with liveness performance in the sub-second scale and throughput on the order of thousands of instructions per second, if the benchmarks presented in the HotStuff paper are representative.
Implementation
The HotStuff BFT algorithm provides a detailed description of the consensus algorithm. Only a summary of it is presented here. To reduce confusion, we adopt the HotStuff nomenclature to describe state changes, rounds and function names where appropriate.
Every proposed state change, as a result of replicas receiving instructions from clients is called a view. There is a
function that every node can call that will tell it which replica will be the leader for a given
view. Every view goes through three phases (Prepare
, PreCommit
, Commit
) before final consensus is reached. Once a
view reaches the Commit
phase, it is finalised and will never be reversed.
As part of their normal operation, every replica broadcasts instructions it receives for its contract to its peers. These instructions are stored in a replica's instruction mempool.
When the leader selection function designates a replica as leader for the next view, it will try
and execute all the instructions it currently has in its mempool to update the state for the next view. Following this
it compiles a tuple of <valid-instructions, rejected-instructions, new-state>. This tuple represents the CMD
structure described in HotStuff.
In parallel with this, the leader expects a set of NewView
messages from the other replicas, indicating that the other
replicas know that this replica is the leader for the next view.
Once a super-majority of these messages have been received, the leader composes a proposal for the next state by adding a new node to the state history graph (I'm calling it a state history graph to avoid naming confusion, but it's really a blockchain). It composes a message containing the new proposal, and broadcasts it to the other replicas.
Replicas, on receipt of the proposal, decide whether the proposal is valid, both from a protocol point of view (i.e. did the leader provide a well-formed proposal) as well as whether they agree on the new state (e.g. by executing the instructions as given and comparing the resulting state with that of the proposal). If there is agreement, they vote on the proposal by signing it, and sending their partial signature back to the leader.
When the leader has received a super-majority of votes, it sends a message back to the replicas with the (aggregated) set of signatures.
Replicas can validate this signature and provide another partial signature indicating that they've received the first aggregated signature for the proposal.
At this point, all replicas know that enough other replicas have received the proposal and are in agreement that it is valid.
In Tendermint, replicas would now wait for the timeout period to make sure that the proposal wasn't going to be superseded before finalising the proposal. But there is an attack described in the HotStuff paper that could stall progress at this point.
The HotStuff protocol prevents this by having a final round of confirmations with the leader. This prevents the stalling attack and also lets replicas finalise the proposal immediately on receipt of the final confirmation from the leader. This lets HotStuff proceed at "network" speed, rather than with a heartbeat dictated by the BFT synchronicity parameter.
Although there are 4 round trips of communication between replicas and the leader, the number of messages sent are O(n). It's also possible to stagger and layer these rounds on top of each other, so that there are always four voting rounds happening simultaneously, rather than waiting for one to complete in its entirety before moving onto the next one. Further details are given in the HotStuff paper.
Forks and byzantine failures
The summary of the HotStuff protocol given above describes the "Happy Path", when there are no breakdowns in communication, or when the leader is an honest node. In cases where the leader is unavailable, the protocol will time out, the current view will be abandoned, and all replicas will move onto the next view.
If a leader is not honest, replicas will reject its proposal, and move onto the next view.
If there is a temporary network partition, the chain may fork (up to a depth of three), but the protocol guarantees safety via the voting mechanism, and the chain will reconcile once the partition resolves.
Leader selection
HotStuff leaves the leader selection algorithm to the application. Usually, a round-robin approach is suggested for its simplicity. However, this requires the replicas to reliably self-order themselves before starting with SMR, which is a non-trivial exercise in byzantine conditions.
For Tari DAN committees, the following algorithm is proposed:
- Every replica knows the Node ID of every other replica in the committee.
- For a given view number, the Node ID with the closest XOR distance to the hash of the view number will be the leader for that view, where the hash function provides a uniformly random value of the same length as the Node ID.
Quorum Certificate
A Quorum certificate, or QC is proof that a super-majority of replicas have agreed on a given state. In particular, a QC consists of
- The type of QC (depending on the phase in which the HotStuff pipeline the QC was signed),
- The view number for the QC
- A reference to the node in the state tree being ratified,
- A signature from a super-majority of replicas.
Tari-specific considerations
As soon as a state is finalised, replicas can inform clients as to the result of instructions they have submitted (in the affirmative or negative). Given that HotStuff proceeds optimistically, and finalisation happens after 4 rounds of communication, it's anticipated that clients can receive a final response from the validator committee in under 500 ms for reasonably-sized committees (this value is speculation at present and will be updated once exploratory experiments have been carried out).
The Tari communication platform was designed to handle peer-to-peer messaging of the type described in HotStuff, and therefore the protocol implementation should be relatively straightforward.
The "state" agreed upon by the VN committee will not only include the smart-contract state, but instruction fee allocations and periodic checkpoints onto the base layer.
Checkpoints onto the base layer achieve several goals:
- Offers a proof-of-work backstop against "evil committees". Without proof of work, there's nothing stopping an evil committee (one that controls a super-majority of replicas) from rewriting history. Proof-of-work is the only reliable and practical method that currently exists to make it expensive to change the history of a chain of records. Tari gives us a "best of both worlds" scenario wherein an evil committee would have to rewrite the base layer history (which does use proof-of-work) before they could rewrite the digital asset history (which does not).
- They allow the asset issuer to authorise changes in the VN committee replica set.
- It allows asset owners to have an immutable proof of asset ownership long after the VN committee has dissolved after the useful end-of-life of a smart contract.
- Provides a means for an asset issuer to resurrect a smart contract long after the original contract has terminated.
When Validator Nodes run smart contracts, they should be run in a separate thread so that if a smart contract crashes, it does not bring the consensus algorithm down with it.
Furthermore, VNs should be able to quickly revert state to at least four views back in order to handle temporary forks. Nodes should also be able to initialise/resume a smart contract (e.g. from a crash) given a state, view number, and view history.
This implies that VNs, in addition to passing around HotStuff BFT messages, will expose additional APIs in order to
- allow lagging replicas to catch up in the execution state.
- Provide information to (authorised) clients regarding the most recent finalised state of the smart contract via a read-only API.
- Accept smart-contract instructions from clients and forward these onto the other replicas in the VN committee.
RFC-0360/NFTInvoices
NFT sale via Mimblwimble Invoice
Maintainer(s): Philip Robinson
Licence
Copyright 2021 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to describe a formulation of a Mimblewimble transaction negotiation process to allow for the sale of an Non Fungible Token (NFT) by a Seller to a Buyer. The Seller will initiate the transaction negotiation by specifying which [NFT] containing UTXO they are offering for sale and how much Tari they expect in return. Having the party that will receive the funds specify the funds they wish to receive is termed an Invoice. The Buyer will then provide their payment input and resulting change output to the transaction and provide a partial signature. The final step will be the Seller completing the final signature to produce a complete transaction.
The process must be completely atomic. At no point should the data included in the negotiation be malleable by the other party or allow for one party to change the elements provided by the counterparty without making the transaction invalid.
Related Requests for Comment
$$ \newcommand{\hash}[1]{\mathrm{H}\bigl({#1}\bigr)} $$
Description
The standard interactive Mimblewimble transaction is usually initiated by a party, the Sender, that is going to send funds to a Receiver. The Sender selects a number of input UTXOs from which the sent funds and fees will come from, they build a change UTXO to receive any remaining funds and calculate the excess of these inputs and change output. The Receiver is then told how many Tari they will receive and provides the aggregated public excess and public nonce of the Sender's portion of the transaction signature. The Receiver builds a commitment with the specified value and their private spending key and then provides their partial signature for their UTXO and the excess. This gets sent back to the Sender who then completes the transaction by providing their partial signature and aggregating it with the Receiver's partial signature.
In the standard Mimblewimble negotiation there are two main elements that make the process trustless for the Sender and Receiver. Firstly, for the Sender, whose inputs are being spent, has the final say in whether to complete the transaction by producing the final signature. For the Receiver there is little risk because they have not provided any inputs at all and are just receiving funds, so they have nothing to lose if the process is compromised. Furthermore, their partial signature means that the Sender cannot change anything about the Receiver's output in the final stage of negotiation.
When it comes to the sale of a token using a Mimblewimble style transaction the risk profile for both the Seller and the Buyer is a bit more complicated.
- The Seller has to prove that they own a given token UTXO and provide some partial signature committing to the terms of the sale without the Buyer being able to use that data to build a new transaction transferring ownership of the token with different terms to what the Seller specified.
- The Buyer has to provide inputs into the transaction before the finalization step in such a way that those inputs are committed but not at risk of the Seller being able to claim the inputs without completing the agreed upon transaction in full.
So the ultimate goal must be a negotiation process where the Seller and Buyer can interact completely trustlessly to build this transaction to the agreed terms. This must be done while each party's contributions to the negotiation are not malleable by the other party and the entire process must be atomic. It either results in a valid transaction built as both parties expect or not at all.
Transaction Negotiation
Assumptions:
- The UTXOs containing the token data have a value of zero (This has not been decided yet in the RFCs regarding token UTXOs).
- The Buyer is going to pay the fees.
- The Buyer will select a single input for payment purposes whose value will cover the requested payment and fees.
- In the following notation a Transaction Input/Output will be represented by \( C_x \) and when used in equations will represent a Pederson Commitment but when sent to the counterparty will include all the addition metadata as described in RFC-0201: TariScript
In the following equations the capital letter subscripts, S and B refer to a token Seller and Buyer respectively.
The transaction that is constructed by this process will need to produce an output metadata signature for the Transaction Output the Buyer will receive the token in and also the Transaction Output the Seller will receive their payment in. These signatures will be aggregated Commitment Signatures that are described in detail in RFC-0201: TariScript.
RFC-0201: TariScript is written from the perspective of a standard Mimblewimble transaction where only the Sender has Transaction Inputs and the Receiver only has a single Transaction Output. The case this RFC discusses both parties receive a Transaction Output and both parties supply a Transaction Input. This results in the requirement of an extra round of communication between the parties. However, the negotiation described below allows for a transaction to be built that can be validated exactly the same was as is described in RFC-0201: TariScript.
Negotiation
Round 1
The Seller initiates the negotiation by collecting and calculating the following:
Terms | Description |
---|---|
\( C_{St} = 0 \cdot H + k_{St} \cdot G \) | Select the Seller's token Transaction Input to be offered |
\( C_{Sp} = v_p \cdot H + k_{Sp} \cdot G \) | Choose a value to be requested from Buyer and select a spending key for the payment Transaction Output to be received |
\( R_{Sk} = r_{Sk} \cdot G \) | Choose a excess signature nonce |
\( R_{SMSt} = r_{SMSt_a} \cdot H + r_{SMSt_b} \cdot G \) | Choose a Seller commitment signature nonce for the Buyer's token output |
\( K_{SO} = k_{SO} \cdot G \) | Choose a Seller Offset to be used in the Buyer's received token output metadata signature |
\( x_S = k_{Sp} - k_{St} \) | Seller's private Excess |
\( X_S = x_S \cdot G \) | Seller's public Excess |
\( s_{St} \) | A Commitment Signature using \( C_{St} \) signing the message \( e = (X_S \Vert v_p \Vert R_S) \) |
The Seller now sends the following to the Buyer
Items | Description |
---|---|
\( C_{St} \) | The Seller's token Transaction Input to be offered |
\( C_{Sp} \) | Seller's payment Transaction Output to be received |
\( v_p \) | The value the Seller is requesting as part of this offer |
\( R_{Sk} \) | Seller's public excess signature nonce |
\( R_{SMSt}\) | Seller's public metadata signature nonce for the Buyer's token UTXO |
\( K_{SO}\) | Seller Offset public key |
\( s_{St} \) | A Commitment Signature using \( C_{St} \) signing the message \( e = (X_S \Vert v_p \Vert R_s) \) |
Round 2
The commitment signature is provided to the Buyer to show that the Seller does indeed own the token UTXO \( C_{St} \). The Buyer's wallet should verify on the base layer that this UTXO is for the token they are being offered.
The Buyer will construct the Seller's public Excess, \( X_s \) from the provided components: \( X_s = C_{Sp} - C_{St} - v_p \cdot H \) This operation confirms that the only commitments that form part of the Seller's excess are the token input and a commitment with the value of the requested payment. To ensure that there is no malleability in the Seller's payment output the Buyer will also provide the Seller with an offset public key so that the Seller can produce an output metadata signature.
The Buyer will now calculate/choose the following:
Terms | Description |
---|---|
\( C_{Bp} = v_{Bp} \cdot H + k_{Bp} \cdot G \) | Buyer's selected Transaction Input from which the payment is drawn |
\( C_{Bc} = (v_{Bp} - v_p - \text{fees}) \cdot H + k_{Bc} \cdot G \) | Buyer's change Transaction Output accounting for the amount to pay to seller and the fees |
\( C_{Bt} = 0 \cdot H + k_{Bt} \cdot G \) | Buyer's token Transaction Output that he will own if the transaction is completed |
\( x_B = k_{Bt} + k_{Bc} - k_{Bp} \) | Buyers's private Excess |
\( X_B = x_B \cdot G \) | Buyers's public Excess |
\( R_B = r_B \cdot G \) | Choose an excess signature nonce |
\( s_{B} = r_B + e_k x_B \) | A partial excess signature signing the message \( e_k = (R_S + R_B \Vert X_S + X_B \Vert \text{metadata} \Vert \text{fees} ) \) |
\( R_{BMSt} = r_{BMSt_a} \cdot H + r_{BMSt_b} \cdot G \) | Choose a Buyer commitment signature nonce for the Buyer's token output |
\( e_{Bt} = \hash{ (R_{SMSt} + R_{BMSt}) \Vert \alpha_{Bt} \Vert F_{Bt} \Vert K_{SO} \Vert C_{Bt}} \) | Buyer's token output metadata signature challenge where \( \alpha_{Bt} \) is the Buyer's token UTXO script and \( F_{Bt} \) is the Buyer's token UTXO output features |
\( s_{BMSt} = (a_{BMSt}, b_{BMSt}, R_{BMSt} ) \) | A partial metadata commitment signature for \( C_{Bt} \) signing message \( e_{Bt} \) |
\( R_{BMSp} = r_{BMSp_a} \cdot H + r_{BMSp_b} \cdot G \) | Buyer's public metadata signature nonce for use by the Seller to calculate their payment output metadata signature |
\( K_{BO} = k_{BO} \cdot G \) | Choose a Buyer Offset to be used in the Sellers's received payment output metadata signature |
The Buyer will then return the following to the Seller:
Items | Description |
---|---|
\( C_{Bp} \) | Buyer's Transaction Input that the payment will come from |
\( C_{Bc} \) | Buyer's change Transaction Output |
\( C_{Bt} \) | Buyer's token Transaction Input that will be received if the sale is completed |
\( R_B \) | Buyers's public Excess |
\( \text{fees & metadata} \) | The fees and Mimblewimble transaction metadata |
\( s_{B} \) | The Buyer's partial excess signature |
\( R_{SMSt}\) | Buyer's public metadata signature nonce for use in the Seller's payment output metadata signature |
\( K_{BO}\) | Buyer Offset to be used in the Sellers's received payment output metadata signature |
\( s_{BMSt} \) | A partial metadata commitment signature for \( C_{Bt} \) |
Round 3
The Seller can calculate the Buyer's excess as follows:
\( X_B = C_{Bt} + C_{Bc} - C_{Bp} + (v_p + \text{fees}) \cdot H \)
The Seller will now calculate/choose the following:
Terms | Description |
---|---|
\( s_{S} = r_S + e x_S \) | Sellers's partial excess signature |
\( s = s_S + s_B, R = R_S + R_B \) | Seller's aggregates the excess signatures to produce the final excess signature |
\( b_{SMSt} = r_{SMSt_b} + e_{Bt}(k_SO) \) | Seller's partial metadata signature for the Buyer's token output |
\( s_{MSt} = (a_{BMSt}, b_{SMSt} + b_{BMSt}, R_{SMSt} + R_{BMSt} \) | Aggregated metadata signature for Buyer's token Transaction Output |
\( R_{SMS} = r_{SMSp_a} \cdot H + r_{SMSp_b} \cdot G \) | Choose a Seller commitment signature nonce for the Seller's payment output |
\( e_{Sp} = \hash{ (R_{SMSpt} + R_{BMSp}) \Vert \alpha_{Sp} \Vert F_{Sp} \Vert K_{BO} \Vert C_{Sp}} \) | where \( \alpha_{Sp} \) is the Sellers's payment UTXO script and \( F_{Sp} \) is the Seller's payment UTXO output features |
\( s_{SMSp} = (a_{SMSp}, b_{SMSp}, R_{SMSp} ) \) | A partial metadata commitment signature for \( C_{Sp} \) signing message \( e_{Sp} \) |
\( \gamma_S = k_Sst - k_SO \) | The Seller's portion of the Script Offset constructed using the script key from \(C_St\), \( k_Sst \), and the private Seller Offset |
The Seller can almost fully complete the transaction construction now. However, while the Seller can complete their portion of the final script offset they cannot complete the entire offset because the Buyer also has input's in the transaction. This means we need one extra round of communication where the Seller returns the almost complete transaction to the Buyer who will complete there portion of the final script offset to complete the transaction.
The Seller sends the following back to the Buyer:
Items | Description |
---|---|
\( C_{St} \) | Seller's token Transaction Input |
\( C_{Sp} \) | Seller's payment Transaction Output |
\( C_{Bp} \) | Buyer's Transaction Input that the payment will come from |
\( C_{Bc} \) | Buyer's change Transaction Output |
\( C_{Bt} \) | Buyer's token Transaction Output that will be received if the sale is completed |
\( X_S + X_B \) | Public Excess |
\( s \) | Aggregated Excess Signature |
\( s_{MSt} \) | Aggregated metadata signature for Buyer's token Transaction Output |
\( s_{SMSp} \) | Partial metadata commitment signature for \( C_{Sp} \) |
\( \gamma_S \) | The Seller's portion of the Script Offset |
Round 4
The final step will be for the Buyer to complete their portion of the metadata signature for the Seller's payment Transaction Output and complete their portion of the Script Offset.
Terms | Description |
---|---|
\( b_{BMSp} = r_{BMSp_b} + e_{Sp}(k_BO) \) | Buyers's partial metadata signature for the Seller's payment output |
\( s_{MSp} = (a_{SMSp}, b_{SMSp} + b_{BMSp}, R_{SMSp} + R_{BMSp} \) | Aggregated metadata signature for Seller's payment Transaction Output |
\( \gamma_B = k_Bsp - k_BO \) | The Seller's portion of the Script Offset constructed using the script key from \(C_Bp\), \( k_Bsp \), and the private Buyer Offset |
\( \gamma = \gamma_B + \gamma_S \) | The final script offset for the completed transaction. |
The Buyer's change Transaction Output will be constructed fully by them including the metadata signature which will be included in the final \( \gamma \) value.
Validation
A Base Node will validate this transaction in the exact same way as described in RFC-0201: TariScript.
In addition to the standard Mimblewimble and TariScript validation operations the consensus rules required for assets and
tokens must also be applied. The most important is to confirm that the unique_id
of the token being sold in this transaction
exists only once as an input and once as an output and the metadata is correctly transfered.
Security concerns
For this negotiation to be secure it must not be possible for the following to occur:
- For the Buyer to use the information provided by the Seller during the first round to construct a valid transaction spending the Seller's token UTXO without the requested payment being fulfilled.
- For the Seller to take the information provided by the Buyer in the second round and construct a valid transaction where the ownership of the token is not transferred even if the Mimblewimble arithmetic balances out.
These points should both be taken care of by the aggregated excess signature and the respective output [metadata signatures]. These signatures prevent the counterparty from changing anything about the Transaction Outputs without the cooperation of the other party.
RFC-0500/PaymentChannels
Payment channels
Maintainer(s): Cayle Sharrock
Licence
Copyright 2019. The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community regarding the technological merits of the potential system outlined herein.
Goals
Related Requests for Comment
Description
Introduction
The base layer is slow. The DAN is fast. However, every DAN instruction that results in a state change has a fee (i.e.
base layer transaction) associated with it.
To bridge the speed gap between the two layers, a payment channel solution is required.
This document provides the high-level overview of how this is done.
The Clacks
The Clacks is a multi-party side-channel off-chain scalability proposal for Tari. The essential idea behind the Clacks is:
- Users give control of some Tari to a Clacks Committee.
- The committee creates an off-chain UTXO for the user(s). This is called the peg-in transaction.
- Users can transact amongst each other without those transactions touching the base layer. This allows a very high throughput of transactions and instant finality. However, the locus of trust move significantly towards the Clacks committee. In other words, base layer transactions are slow, but do not require users to trust anyone. Clacks transactions are fast, but requires some level of trust in the entity or entities controlling the user's funds.
- Users can send off-chain Tari to any other users, including users that have not made deposits into the Clacks committee.
- Users can request a withdrawal for their Tari at any time. At predefined intervals, the Clacks committee will process those withdrawal requests and give the Tari UTXO control back to the user.
Users also have a pre-signed, time-locked transaction that returns the user's fund back to them. This refund transaction can be used in case the Clacks committee stops processing withdrawals and provides insurance for users against having their funds locked up forever.
The peg-in, peg-out cycle are represented by standard Tari transactions. This means that full nodes verify that no funds have been created or destroyed over the course of the cycle. They do not check that the "balances" associated with users are what those users would expect in an honestly run side chain.
However, if the side-chain is run as a standard mimblewimble process, any third party running a full node and that access to the opening and closing channel balances, can in fact verify that the committee has operated honestly.
RFC-1000/TariUseCases
Digital Asset Framework
Maintainer(s): Leland Lee
Licence
Copyright 2019 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
There will be many types of digital assets that can be issued on Tari. The aim of this Request for Comment (RFC) is to help potential asset issuers identify use cases for Tari that lead to the design of new types of digital assets that may or may not exist in their ecosystems today.
Related Requests for Comment
Description
Tari digital assets may exist on the Tari Digital Assets Network (DAN), are perceived to have value and can be owned, with the associated digital data being classified as intangible, personal property.
Types
Digital assets may have the following high-level classification scheme:
- Symbolic
- Insignia
- Mascots
- Event-driven or historical, e.g. a rivalry or a highly temporal event
- Artefacts or objects
- Legendary
- Rare
- High demand
- Artistic
- Historical
- Utility
- Tickets
- In-game items
- Points
- Currency
- Full or fractional representation
- Bearer instruments/access token
- Chat stickers
- Persona
- Personality trait(s)
- Emotion(s)
- Statistics
- Superpower(s)
- Storyline(s)
- Relationship(s)
- Avatar
Behaviours
Digital asset tokens may influence the following behavioural types:
- Advance purchase
- Experience enhancement
- Sharing
- Loyalty
- Reward for performance
- Tipping
- Donating to a charity
- Collecting
- Trading
- Building/combining
Attributes
Digital assets have many different properties, which may be one or more of the following:
- Digital assets can be interactive:
- Easter eggs;
- media
- dynamic, e.g. imagine if assets were similar to sounds, visualizations or other kinds of "demos" or "gifs")
- Game mechanics;
- Evolutionary.
- Easter eggs;
- Digital assets can be combined to create super assets.
- Digital assets can be attribute(s) of another digital asset, e.g. wheels of a vehicle or a VIP ticket has two drink cards.
- Digital assets can have contingencies, e.g. ownership of a digital asset is contingent on ownership of a different digital asset. Using this digital asset is contingent on holding it for a particular duration, etc.
- Digital assets can have utility, e.g. be useful.
- Digital assets can be used across platforms, e.g. a digital asset for a game could be used as avatars in a social network.
- Digital assets can have history.
- Digital assets can have user-generated tags and/or metadata.
Interactions
Digital asset owners may have the following interactions with the DAN and/or other people:
- Digital asset owners can attest that they have ownership over their assets at time
t
. - Digital asset owners may attest ownership to an individual, to a group of friends or to the entire world.
Rules
Rules are the governance of how digital assets may be used or transferred, as defined by the asset issuer:
- Royalty fees. Digital asset issuers can set a royalty that charges a fee every time the digital asset is transferred between parties. The fee as defined by the issuer can be fixed or dynamic, or follow a complex formula, and value is granted to the issuer(s) and/or other entities.
- Contingency. Digital asset ownership/interaction may be contingent/dependent on another asset.
- Timing controls. Digital assets can only be transferred or used at particular times.
- Sharing. Digital assets can be shared with others or even co-owned.
- Privacy. Ownership of a digital asset can be changed from private to public.
- Upgradability/versioning. Digital assets can be upgraded and/or versioned.
- Redeemability. Digital assets can be used once or multiple times.
Examples
Some examples of how different types of digital assets with different attributes, rules and interactions may be manifested are provided here:
Crystal Skull of Akador
- Is rare, it is 1 of 5.
- Is legendary:
- https://indianajones.fandom.com/wiki/Crystal_Skull_of_Akator;
- press reports that this artefact could be worth $X.
- Drives collectability.
- Drives advance purchase, e.g. if you are one of the first 100,000 people to buy tickets to Indiana Jones World, you have a chance of winning this 1 of 5 artefact.
- Has superpowers and utility, e.g. if you have this item while visiting Indiana Jones World, you get to skip the line three times.
- Is a contingency for another asset, e.g. if you collect this item, two Sankara stones and the Cross of Coronado, you
can buy the ark of the covenant:
- Ark of the covenant is rare. It is 1 of 1.
- Ark of the covenant is legendary.
- Ark of the covenant gives you lifetime access to Indiana Jones World.
- Ark of the covenant has rules; 20% of the resale price goes to Indiana Jones World.
AB de Villiers' bat
- Is not rare, it is 1 of 100,000.
- Is legendary - https://www.youtube.com/watch?v=HK6B2da3DPA.
- Drives collectability, it is part of a series of bats from famous batsmen.
- Can be combined with other assets, e.g. be one of the first 10 people to combine six bats to turn this asset into a
One Day International (ODI) century bat:
- ODI century bats are rare, they are 1 of 10;
- ODI century bats are legendary.
- Has no superpowers.
- Has no utility.
- Has no rules.
OVO Owl x Supreme
- Is rare, it is 1 of 200.
- Is legendary:
- https://www.supremenewyork.com;
- https://us.octobersveryown.com.
- Has a game mechanic:
- Every time it’s transferred, it may become a golden ticket that grants you access to any Drake show.
- If it becomes a golden ticket and is transferred, it loses its golden ticket superpower.
- Has utility:
- Unlocks exclusive media content feat. Drake hosted by OVO SOUND.
- May become a golden ticket that grants you access to any Drake show.
- Has rules - every time its transferred Supreme and OVO SOUND receive 25% of the transaction value.
Deprecated RFC
The following documents are either obsolete, or have been superceded by newer RFC documents.
RFC-0130/Mining
Full-node Mining on Tari Base Layer
Maintainer(s): [Yuko Roodt] (https://github.com/neonknight64)
Licence
Copyright 2018 The Tari Development Community
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community of the technological merits of the potential system outlined herein.
Goals
The aim of this Request for Comment (RFC) is to provide a brief overview of the Tari merged mining process and introduce the primary functionality required of the Mining Server and Mining Worker.
Related Requests for Comment
Description
Assumptions
- The Tari blockchain will be merged mined with Monero.
- The Tari Base Layer has a network of Base Nodes that verify and propagate valid transactions and blocks.
Abstract
The process of merged mining Tari with Monero on the Tari Base Layer is performed by Mining Servers and Mining Workers. Mining Servers are responsible for constructing new blocks by bundling transactions from the mempool of a connected Base Node. They then distribute Proof-of-Work (PoW) tasks to Mining Workers in an attempt to solve newly created blocks. Solved solutions and shares are sent by the Mining Workers to the Mining Server, which in turn verifies the solution and distributes the newly created blocks to the Base Node and Monero Node for inclusion in their respective blockchains.
Merged Mining on Tari Base Layer
This document is divided into three parts:
- A brief overview of the merged mining process and the interactions between the Base Node, Mining Server and Mining Worker.
- The primary functionality required of the Mining Server.
- The primary functionality required of the Mining Worker.
Overview of Tari Merged Mining Process using Mining Servers and Mining Workers
Mining on the Tari Base Layer consists of three primary entities: the Base Nodes, Mining Servers and Mining Workers. A description of the Base Node is provided in RFC-0110/Base Nodes. A Mining Server is connected locally or remotely to a Tari Base Node and a Monero Node, and is responsible for constructing Tari and Monero Blocks from their respective mempools. The Mining Server should retrieve transactions from the mempool of the connected Base Node and assemble a new Tari block by bundling transactions together.
Mining servers may re-verify transactions before including them in a new Tari block, but this enforcement of verification and transaction rules such as signatures and timelocks is the responsibility of the connected Base Node. Mining Servers are responsible for cut-through, as this is required for scalability and privacy.
To enable merged mining of Tari with Monero, both a Tari and a Monero block need to be created and linked. First, a new Tari block is created and then the block header hash of the new Tari block is included in the coinbase transaction of the new Monero block. Once a new merged mined Monero block has been constructed, PoW tasks can be sent to the connected Mining Workers, which will attempt to solve the block by performing the latest released version of the PoW algorithm selected by Monero.
Assuming the Tari difficulty is less than the Monero difficulty, miners get rewarded for solving the PoW at any difficulty above the Tari difficulty. If the block is solved above the Tari difficulty, a new Tari block is mined. If the difficulty is also greater than the Monero difficulty, a new Monero block is mined as well. In either event, the header for the candidate Monero block is included in the Tari block header.
If the PoW solution was sufficient to meet the difficult level of both the Tari and Monero blockchains, then the individual blocks for each blockchain can be sent from the Mining Server to the Base Node and Monero Node to be added to the respective blockchains.
Every Tari block must include the solved Monero block's information (block header hash, Merkle tree branch and hash of the coinbase transaction) in the PoW summary section of the Tari block header. If the PoW solution found by the Mining Workers only solved the problem at the Tari difficulty, the Monero block can be discarded.
This process will ensure that the Tari difficulty remains independent. Adjusting the difficulty will ensure that the Tari block times are preserved. Also, the Tari block time can be less than, equal to or greater than the Monero block times. A more detailed description of the merged mining process between a Primary and Auxiliary blockchain is provided in the Merged Mining TLU report.
Functionality Required by Tari Mining Server
- The Tari blockchain MUST have the ability to be merged mined with Monero.
- The Tari Mining Server:
- MUST maintain a local or remote connection with a Base Node and a Monero Node.
- MUST have a mechanism to construct a new Tari and Monero block by selecting transactions from the different Tari and Monero mempools that need to be included in the different blocks.
- MUST apply cut-through when mining Tari transactions from the mempool and only add the excess to the list of new Tari block transactions.
- MAY have a configurable transaction selection mechanism for the block construction process.
- MAY have the ability to re-verify transactions before including them in a new Tari block.
- MUST have the ability to include the block header hash of the new Tari block in the coinbase section of a newly created Monero block to enable merged mining.
- MUST be able to include the Monero block header hash, Merkle tree branch and hash of the coinbase transaction of the Monero block into the PoW summary field of the new Tari block header.
- MUST have the ability to transmit and distribute PoW tasks for the newly created Monero block, which contains the Tari block information, to connected Mining Workers.
- MUST verify PoW solutions received from Mining Workers and MUST reject and discard invalid solutions or solutions that do not meet the minimum required difficulty.
- MAY keep track of mining share contributions of the connected Mining Workers.
- MUST submit completed Tari blocks to the Tari Base Node.
- MUST submit completed Monero blocks to the Monero Network.
Functionality Required by Tari Mining Worker
The Tari Mining Worker:
- MUST maintain a local or remote connection to a Mining Server.
- MUST have the ability to receive PoW tasks from the connected Mining Server.
- MUST have the ability to perform the latest released version of Monero's PoW algorithm on the received PoW tasks.
- MUST attempt to solve the PoW task at the difficulty specified by the Mining Server.
- MUST submit completed shares to the connected Mining Server.
-
Maintainer(s):
Licence
Copyright
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of this document must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as shown here.
Disclaimer
This document and its content are intended for information purposes only and may be subject to change or update without notice.
This document may include preliminary concepts that may or may not be in the process of being developed by the Tari community. The release of this document is intended solely for review and discussion by the community regarding the technological merits of the potential system outlined herein.
Goals
Related Requests for Comment
Description
Tari Network Terminology
Below are a list of terms and their definitions that are used throughout the Tari code and documentation. Let's use this glossary to disambiguate ideas, and work towards a ubiquitous language for this project.
Archive node
This is a full history base node. It will keep a complete history of every transaction ever received and it will not implement pruning.
AssetCollateral
The amount of tari coin that a Validator Node must put up on the base layer in order to become part of an asset committee.
Asset Issuer
An entity that creates digital assets on the Tari DAN. The Asset Issuer will specify the parameters of the contract template that defines the rules that govern the asset and the number and nature of its constituent tokens on issuance. The Asset Issuer will, generally, be the initial owner of the tokens.
Bad Actor
A participant that acts maliciously or negligently to the detriment of the network or another participant.
Base layer
The Tari Base layer is a merge-mined blockchain secured by proof-of-work. The base layer is primarily responsible for the emission of new Tari, for securing and managing Tari coin transfers.
Base Node
A full Tari node running on the base layer. It's primary role is validating and propagating Tari coin transactions and blocks to the rest of the network.
Block
A collection of transactions and associated metadata recorded as a single entity in the Tari blockchain. The ordering of Tari transactions is set purely by the block height of the block they are recorded in.
Block Header
A data structure that validates the information contained in a block.
Block Body
A data structure containing the transaction inputs, outputs, and kernels that make up the block.
Block reward
The amount of Tari created by the coinbase transaction in every block. The block reward is set by the emission schedule.
Blockchain
A sequence of tari blocks. Each block contains a hash of the previous valid block. Thus the blocks form a chain with the property that changing anything in a block other than the head block requires rewriting the entire blockchain from that point on.
Blockchain state
The complete state of the blockchain at a specific block height. This means a pruned utxo set, a complete set of kernels and headers up to that block height from the genesis block.
BroadcastStrategy
A strategy for propagating messages amongst nodes in a peer-to-peer network. Example implementations of
BroadcastStrategy
include the Gossip protocol and flood fill.
Chain Reorganization
A chain reorganization occurs after a chain split occurs on the network, which commonly occurs due to network latency and connectivity issues. When a chain split occurs one chain will have the higher accumulated proof-of-work, this chain is considered the best chain. Nodes on the poorer chain will need to rewind and resync their chains to best chain. In this process transaction in the mempool could become orphaned or invalid.
Checkpoint
A hash of the state of a Digital Asset that is recorded on the base layer.
Coinbase transaction
The first transaction in every Tari block yields a Block Reward according to the Tari emission Schedule and is awarded to the miner that performed the Proof of Work for the block.
Committee
A group of Validator Nodes that are responsible for managing the state of a specific Digital Asset. A committee is selected during asset issuance and can be updated at Checkpoints.
CommitteeSelectionStrategy
A strategy for an Asset Issuer to select candidates for the committee from the available registered Validator Nodes who responded to the nomination call for that asset.
ConsensusStrategy
The approach that will be taken for a committee to reach consensus on the validity of instructions that are performed on a given Digital Asset.
Commitment
A commitment is a cryptographic primitive that allows one to commit to a chosen value while keeping it hidden from others, with the ability to reveal the committed value later. Commitments are designed so that one cannot change the value or statement after they have committed to it.
Communication Node
A Communication Node is either a Validator Node or Base Node that is part of the Tari communication network. It maintains the network and is responsible for forwarding and propagating joining requests, discovery requests and data messages on the communication network.
Communication Client
A Communication Client is a Wallet or Asset Manager that makes use of the Tari communication network to send joining and discovery requests. A Communication Client does not maintain the communication network and is not responsible for forwarding or propagating any requests or data messages.
Creator Nomination Mode
An asset runs in creator nomination mode when every validator node in a validator committee is a Trusted Node that was directly nominated by the Asset Issuer.
Current head
The last block of the base layer that represents the latest valid block. This block must be from the longest proof-of-work chain to be the current head.
Cut-Through
Cut-through is the process where outputs spent within a single block may be removed without breaking the standard MimbleWimble
validation rules. Simplistically, Alice -> Bob -> Carol
may be "cut-through" to Alice -> Carol
. Bob's commitments may be removed.
On Tari, for reasons described in RFC-0201_TariScript, cut-through is prevented from ever happening.
Digital asset
Digital assets (DAs) are the sets or collections of native digital tokens (both fungible and non-fungible) that are created by asset issuers on the Tari 2nd layer. For example, a promoter might create a DA for a music concert event. The event is the digital asset, and the tickets for the event are digital asset tokens.
Digital Asset Network
The Tari second layer. All digital asset interactions are managed on the Tari Digital Assets Network (DAN). These interactions (defined in instructions) are processed and validated by Validator Nodes.
DigitalAssetTemplate
A DigitalAssetTemplate is one of a set of contract types supported by the DAN. These contracts are non-turing complete and consist of rigid rule-sets with parameters that can be set by Asset Issuers.
Digital asset tokens
Digital asset tokens (or often, just "tokens") are the finite set of digital entities associated with a given digital asset. Depending on the DA created, tokens can represent tickets, in-game items, collectibles or loyalty points. They are bound to the digital asset that created them.
Hashed Time Locked Contract
A time locked contract that only pays out after a certain criteria has been met or refunds the originator if a certain period has expired.
Emission schedule
An explicit formula as a function of the block height, h, that determines the block reward for the hth block.
Instructions
Instructions are the digital asset network equivalent of transactions. Instructions are issued by asset issuers and client applications and are relayed by the DAN to the validator nodes that are managing the associated digital asset.
Mempool
The mempool consists of the transaction pool, pending pool, orphan pool and reorg pool, and is responsible for managing unconfirmed transactions that have not yet been included in the longest proof-of-work chain. Miners usually draw verified transactions from the mempool to build up transaction blocks.
Metadata Signature
The metadata signature is an aggregated Commitment Signature ("ComSig") signature, attached to a transaction output and signed with a combination of the homomorphic commitment private values \( (v_i \, , \, k_i )\), the spending key known only to the receiver, and sender offset private key \(k_{Oi}\) known only to the sender. This prevents malleability of the UTXO metadata.
Mimblewimble
Mimblewimble is a privacy-centric cryptocurrency protocol. It was dropped in the Bitcoin Developers chatroom by an anonymous author and has since been refined by several authors, including Andrew Poelstra.
Mining Server
A Mining Server is responsible for constructing new blocks by bundling transactions from the mempool of a connected Base Node. It also distributes Proof-of-Work tasks to Mining Workers and verifies PoW solutions.
Mining Worker
A Mining Worker is responsible for performing Proof-of-Work tasks received from its parent Mining Server.
Multisig
Multi-signatures (Multisigs) are also known as N-of-M signatures, this means that a minimum of N number of the M peers need to agree before a transaction can be spent. N and M can be equal; which is a special case and is often referred to as an N-of-N Multisig.
Node ID
A node ID is a unique identifier that specifies the location of a communication node or communication client in the Tari communication network. The node ID can either be obtained from registration on the Base Layer or can be derived from the public identification key of a communication node or communication client.
Non-fungible Token (NFT)
A Non-fungible token is a specific instance of a token issued as part of a digital asset. It is another name for a [digital asset token]. NFTs are contained within specially marked UTXOs on the Tari Base Layer.
Orphan Pool
The orphan pool is part of the mempool and manages all transactions that have been verified but attempt to spend UTXOs that do not exist or haven't been created yet.
Pending Pool
The pending pool is part of the mempool and manages all transactions that have a time-lock restriction on when it can be processed or attempts to spend UTXOs with time-locks.
This is a local setting for each node to help reduce syncing time and bandwidth. This is the number of blocks from the chain tip beyond which a chain will be pruned.
Public Nomination Mode
An asset runs in public nomination mode when the Asset Issuer broadcasts a call for nominations to the network and VNs from the network nominate themselves as candidates to become members of the committee for the asset. The Asset Issuer will then employ the CommitteeSelectionStrategy to select the committee from the list of available candidates.
Range proof
A mathematical demonstration that a value inside a commitment (i.e. it is hidden) lies within a certain range. For Mimblewimble, range proofs are used to prove that outputs are positive values.
Registration Deposit
An amount of tari coin that is locked up on the base layer when a Validator Node is registered. In order to make Sybil attacks expensive and to provide an authorative base layer registry of validator nodes they will need to lock up a amount of Tari Coin on the Base Layer using a registration transaction to begin acting as a VN on the DAN.
Registration Term
The minimum amount of time that a VN registration lasts, the Registration Deposit can only be released after this minimum period has elapsed.
Reorg Pool
The reorg pool is part of the mempool and stores all transactions that have recently been included in blocks in case a blockchain reorganization occurs and the transactions need to be restored to the transaction pool.
Script Keypair
The script private - public keypair, \((k_{Si}\),\(K_{Si})\), is used in TariScript to unlock and execute the script associated with an output. Afterwards the execution stack must contain exactly one value that must be equal to the script public key.
Script Offset
The script offset provides a proof that every script public key \( K_{Si} \) and sender offset public key \( K_{Oi} \) provided for the a transaction's inputs and outputs are correct.
Sender Offset Keypair
The sender offset private - public keypair, (\( k_{Oi} \),\( K_{Oi} \)), is used by the sender of an output to lock all its metadata by virtue of a [sender metadata signature].
Spending Key
A private spending key is a private key that permits spending of a UTXO. It is also sometimes referred to as a Blinding Factor, since is Tari (and Mimblewimble) outputs, the value of a UTXO is blinded by the spending key:
$$ C = v.H + k.G $$
The public key, \(P = k.G\) is known as the public spending key.
SynchronisationState
The current synchronisation state of a Base Node. This can either be
starting
- The node has freshly started up and is still waiting for first round of chain_metadata responses from its neighbours on which to base its next state change.header_sync
- The node is in the process of synchronising headers with chosen sync peer.horizon_sync
- The node is in the process of syncing blocks from the tip to its [pruning horizon]block_sync
- The node is in the process of syncing all blocks back to the genesis blocklistening
- The node has completed its syncing strategy and will continue to listen for new blocks and monitor its neighbours to detect if it falls behind.
SynchronisationStrategy
The generalised approach for a Base Node to obtain the current state of the blockchain from the peer-to-peer network. Specific implementations may differ based on different trade-offs and constraints with respect to bandwidth, local network conditions etc.
Tari Coin
The base layer token. Tari coins are released according to the emission schedule on the Tari base layer blockchain in coinbase transactions.
TariScript
Tari uses a scripting system for transactions, not unlike Bitcoin's scripting system, called TariScript. It is also simple, stack-based, processed from left to right, not Turing-complete, with no loops. It is a list of instructions linked in a non malleable way to each output, specifying its conditions of spending.
Transaction
Transactions are activities recorded on the Tari blockchain running on the base layer. Transactions always involve a transfer of Tari coins. A mimblewimble transaction body consists of one or more blinded inputs and outputs.
Transaction Pool
The transaction pool is part of the mempool and manages all transactions that have been verified, that spend valid UTXOs and don't have any time-lock restrictions.
Trusted Node
A permissioned Validator Node nominated by an Asset Issuer that will form part of the committee for that Digital Asset.
Token Wallet
A Tari Token Wallet is responsible for managing Digital assets and Tokens, and for constructing and negotiating instructions for transferring and receiving Assets and Tokens on the Digital Asset Network.
Transaction Weight
The weight of a transaction / block measured in "grams". See Block / Transaction weight for more details.
Unspent transaction outputs
An unspent transaction output (UTXO) is a discrete number of Tari that are available to be spent. The sum of all UTXOs represents all the Tari currently in circulation. In addition, the sum of all UTXO values equals the sum of the block rewards for all blocks up to the current block height.
UTXO values are hidden by their commitments. Only the owner of the UTXO and (presumably) the creator of the UTXO (either a Coinbase transaction or previous spender) know the value of the UTXO.
Validator Node
Validator nodes (VNs) make up the Tari second layer, or Digital Asset Network. VNs are responsible for creating and updating digital assets living on the Tari network.
Wallet
A Tari Wallet is responsible for managing key pairs, and for constructing and negotiating transactions for transferring and receiving tari coins on the Base Layer.
Disclaimer
This document is subject to the disclaimer.