diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..7585238 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +book diff --git a/docs/book.toml b/docs/book.toml new file mode 100644 index 0000000..ded7671 --- /dev/null +++ b/docs/book.toml @@ -0,0 +1,5 @@ +[book] +authors = ["Nyksdevs"] +language = "en" +src = "src" +title = "Nyks Docs" diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md new file mode 100644 index 0000000..ca73287 --- /dev/null +++ b/docs/src/SUMMARY.md @@ -0,0 +1,7 @@ +# Summary + +- [ZK](zk.md) +- [Block](block.md) +- [Transaction](transaction.md) +- [Mining](mining.md) +- [RPC](rpc.md) \ No newline at end of file diff --git a/docs/src/block.md b/docs/src/block.md new file mode 100644 index 0000000..254fcfd --- /dev/null +++ b/docs/src/block.md @@ -0,0 +1,29 @@ +# Block + +On Nyks, a block consists of a kernel and a proof. The kernel is made up of a header, body, and appendix. + +## Header + +The header includes: + +* Version +* Height +* Previous block digest, linking it to its parent +* Timestamp +* Proof-of-work data, consisting of a root, two authentication paths (`path_a`, `path_b`), and a nonce +* Cumulative proof-of-work, the running total of work done across the chain +* Difficulty +* Guesser receiver data, describing where the guesser's portion of the block reward goes + +## Body + +The body includes: + +* The transaction kernel, aka the block's inputs and outputs. At this level there's no notion of individual transactions, just the merged set of inputs and outputs that make up the block (its scheme is similar to a single transaction kernel, as explained in [transactions.md](transactions.md)) +* The mutator set accumulator, reflecting the mutator set state after this block without guesser UTXOs +* The lock-free MMR accumulator (will be removed) +* The block MMR accumulator, a Merkle mountain range over all block digests in the chain up to and including this block. It lets anyone produce a compact membership proof that a given block is part of the chain's history, without needing the full chain + +## Appendix + +Contains a block's claims, which are recursively proven on the block's proof. \ No newline at end of file diff --git a/docs/src/mining.md b/docs/src/mining.md new file mode 100644 index 0000000..9d4fc10 --- /dev/null +++ b/docs/src/mining.md @@ -0,0 +1,28 @@ +# Mining + +On Nyks, mining is a 2-step process (plus transaction upgrading, but that's not a "requirement" for consensus, so it doesn't count as one of the steps). + +## Composing + +The composer's role is to select transactions for inclusion, merge them if needed, or create an empty transaction if none are found. Two transactions are always required for a block transaction, so an empty one gets used as a filler when there's nothing else to include. The composer also generates a coinbase transaction for themselves and finishes computing the zk block proof. This requires beefy machinery, like 128+ GB RAM, 96 cores. + +A composer always has to merge two transactions for generating a block transaction, to discourage skipping transactions just to get faster proof generation. + +Distribution of the block reward is determined by the composer. + +## Mining + +Typical PoW mining: a memory-heavy variant of Tip5 is used, and you search for a nonce that satisfies the block's target. + +Tip5's research paper can be found here: https://eprint.iacr.org/2023/107.pdf. +Tip5 is also a crucial part of computing ZK proofs [zk.md](zk.md)), so giving miners an incentive to make it faster will probably be worth it in the long term. + +Miners choose whichever block template from a composer favors them most. + +## Upgraders + +There's one extra role for upgrading transactions, called upgraders. + +In parallel to composers, upgraders can raise transaction proofs to a quality suitable for on-chain inclusion, merge them for easier inclusion (turning them into a single transaction so the composer doesn't waste time merging), and collect fees from transactions through a process called "gobbling." + +Also requires beefy machinery for competition. \ No newline at end of file diff --git a/docs/src/rpc.md b/docs/src/rpc.md new file mode 100644 index 0000000..49034ab --- /dev/null +++ b/docs/src/rpc.md @@ -0,0 +1,46 @@ +## RPC + +The Nyks node has a built-in RPC module for whatever purposes you need it for. + +It's designed to support multiple transports, but only HTTP is supported for now. + +### Enabling + +RPC can be enabled with `--rpc-listen :`. + +Methods are grouped into namespaces, which can be individually exposed with `rpc-modules` using the same flag style. This lets you isolate which parts of the node are reachable over RPC, so you can expose only what you need instead of opening up everything. + +Available namespaces: + +* `Node` - endpoints for general node info +* `Network` - endpoints for peer and networking info +* `Chain` - endpoints for querying blockchain tip state +* `Mining` - endpoints for mining/composing processes +* `Archival` - endpoints for historical data which won't be needed for consensus itself in future +* `Mempool` - endpoints for inspecting mempool status +* `Wallet` - endpoints for serving external wallets + +### Request Format + +Requests are sent as a JSON POST body: + +```json +{ + "jsonrpc": "2.0", + "id": 0, + "method": "node_network", + "params": [] +} +``` + +### Response Format + +```json +{ + "jsonrpc": "2.0", + "id": 0, + "result": "testnet-0" +} +``` + +For the full list of RPC methods, see [`rpc/core/src/api/ops.rs`](https://github.com/Nyksnet/node/blob/master/rpc/core/src/api/ops.rs). diff --git a/docs/src/transaction.md b/docs/src/transaction.md new file mode 100644 index 0000000..e5f70ff --- /dev/null +++ b/docs/src/transaction.md @@ -0,0 +1,38 @@ +# Transaction + +On Nyks, transactions consist of 2 parts: the kernel and the proof. + +The kernel includes: + +* Inputs, which represent removal records from the mutator set +* Outputs, which represent addition records to the mutator set. Each output is made up of a UTXO, a sender randomness, and a receiver digest, which are hashed together with Tip5 to produce the commitment +* Announcements, which allow users to put data on-chain (primarily used for announcing data a receiver needs in order to receive UTXOs today *note*) +* The fee the transaction is paying +* If it's a coinbase transaction, a field declaring the "asked" coinbase reward amount +* A timestamp for when the transaction was created +* The mutator set hash the transaction is synced to +* A merge bit, which can only be true when the transaction has been merged with another transaction + +## Proof + +Nyks consensus and P2P support 2 proof types. + +`ProofCollection` is a collection of proofs for the transaction's validity. This proof kind cannot be included on-chain (it's considered low quality, but fast and easy to compute). + +`SingleProof` is generated by recursively proving `ProofCollection` proofs. It's high quality and can be included on-chain, but takes a lot of computation power to produce. + +`ProofCollection`s can be upgraded to `SingleProof` by off-P2P services, or by on-P2P upgrader services for a fee (or a different cost, depending on the service). This proving process is why transactions can be expensive. + +### Wallet SDK + +The Wallet SDK provides a **primitive witness**, a general witness that makes generating these proofs easy. + +Transactions are put together with a `TransactionBuilder`, which takes inputs, outputs, and other kernel data, and builds a transaction from them. + +Each input is made up of: + +* a UTXO +* its mutator set membership proof - proving the UTXO is actually part of the current mutator set, and thus spendable +* the lock script and witness needed to unlock it - proving the caller has the right to spend it + +If an output has notification enabled, the `TransactionBuilder` includes its *note* announcement in the kernel's announcements field. \ No newline at end of file diff --git a/docs/src/zk.md b/docs/src/zk.md new file mode 100644 index 0000000..eccbf96 --- /dev/null +++ b/docs/src/zk.md @@ -0,0 +1,9 @@ +# ZK + +Nyks uses Triton VM as the underlying VM for its STARK proofs. It is a stack-based virtual machine with efficient recursive verification, making it suitable for an indefinitely growing blockchain. + +Because each block can verify the proof from the previous block, the latest block can represent the validity of the entire chain and its current state. This keeps verification succinct without requiring the whole blockchain to be verified from scratch. + +Succinctness is not implemented yet. + +Triton VM's repository can be found here: https://github.com/TritonVM/triton-vm. \ No newline at end of file diff --git a/miner/src/core/args.rs b/miner/src/core/args.rs index 8f7ee7a..190c7ad 100644 --- a/miner/src/core/args.rs +++ b/miner/src/core/args.rs @@ -49,8 +49,7 @@ impl Args { .await .expect("Failed to connect to RPC node") .network - .parse::() - .unwrap(); + .into(); assert_eq!( self.network, remote_network, diff --git a/node/src/application/rpc/service.rs b/node/src/application/rpc/service.rs index 899654e..53c6210 100644 --- a/node/src/application/rpc/service.rs +++ b/node/src/application/rpc/service.rs @@ -52,7 +52,7 @@ async fn find_block_digest(selector: BlockSelector, state: &GlobalState) -> Opti impl RpcApi for RpcServer { async fn network_call(&self, _: NetworkRequest) -> RpcResult { Ok(NetworkResponse { - network: self.state.cli().network.to_string(), + network: self.state.cli().network.into(), }) } diff --git a/rpc/core/src/api/ops.rs b/rpc/core/src/api/ops.rs index ec3d025..e114e68 100644 --- a/rpc/core/src/api/ops.rs +++ b/rpc/core/src/api/ops.rs @@ -15,7 +15,7 @@ use crate::model::message::*; /// Must be bumped every time a breaking change to the RPC API is made. Adding /// a new endpoint is not a breaking change. Neither is adding a new field to /// a type that is returned by this API. -pub const RPC_API_VERSION: u16 = 2; +pub const RPC_API_VERSION: u16 = 1; #[derive( Clone, diff --git a/rpc/core/src/api/rpc.rs b/rpc/core/src/api/rpc.rs index 0f5c3b2..23f0668 100644 --- a/rpc/core/src/api/rpc.rs +++ b/rpc/core/src/api/rpc.rs @@ -12,7 +12,7 @@ use crate::model::block::header::RpcBlockPow; use crate::model::block::transaction_kernel::RpcAbsoluteIndexSet; use crate::model::block::transaction_kernel::RpcAdditionRecord; use crate::model::block::transaction_kernel::RpcTransactionKernelId; -use crate::model::common::RpcBlockSelector; +use crate::model::common::BlockSelector; use crate::model::json::JsonError; use crate::model::message::*; use crate::model::wallet::transaction::RpcTransaction; @@ -159,10 +159,7 @@ pub trait RpcApi: Sync + Send { request: GetBlockDigestsRequest, ) -> RpcResult; - async fn get_block_digest( - &self, - selector: RpcBlockSelector, - ) -> RpcResult { + async fn get_block_digest(&self, selector: BlockSelector) -> RpcResult { self.get_block_digest_call(GetBlockDigestRequest { selector }) .await } @@ -171,15 +168,12 @@ pub trait RpcApi: Sync + Send { request: GetBlockDigestRequest, ) -> RpcResult; - async fn get_block(&self, selector: RpcBlockSelector) -> RpcResult { + async fn get_block(&self, selector: BlockSelector) -> RpcResult { self.get_block_call(GetBlockRequest { selector }).await } async fn get_block_call(&self, request: GetBlockRequest) -> RpcResult; - async fn get_block_proof( - &self, - selector: RpcBlockSelector, - ) -> RpcResult { + async fn get_block_proof(&self, selector: BlockSelector) -> RpcResult { self.get_block_proof_call(GetBlockProofRequest { selector }) .await } @@ -188,10 +182,7 @@ pub trait RpcApi: Sync + Send { request: GetBlockProofRequest, ) -> RpcResult; - async fn get_block_kernel( - &self, - selector: RpcBlockSelector, - ) -> RpcResult { + async fn get_block_kernel(&self, selector: BlockSelector) -> RpcResult { self.get_block_kernel_call(GetBlockKernelRequest { selector }) .await } @@ -200,10 +191,7 @@ pub trait RpcApi: Sync + Send { request: GetBlockKernelRequest, ) -> RpcResult; - async fn get_block_header( - &self, - selector: RpcBlockSelector, - ) -> RpcResult { + async fn get_block_header(&self, selector: BlockSelector) -> RpcResult { self.get_block_header_call(GetBlockHeaderRequest { selector }) .await } @@ -212,7 +200,7 @@ pub trait RpcApi: Sync + Send { request: GetBlockHeaderRequest, ) -> RpcResult; - async fn get_block_body(&self, selector: RpcBlockSelector) -> RpcResult { + async fn get_block_body(&self, selector: BlockSelector) -> RpcResult { self.get_block_body_call(GetBlockBodyRequest { selector }) .await } @@ -223,7 +211,7 @@ pub trait RpcApi: Sync + Send { async fn get_block_transaction_kernel( &self, - selector: RpcBlockSelector, + selector: BlockSelector, ) -> RpcResult { self.get_block_transaction_kernel_call(GetBlockTransactionKernelRequest { selector }) .await @@ -235,7 +223,7 @@ pub trait RpcApi: Sync + Send { async fn get_block_announcements( &self, - selector: RpcBlockSelector, + selector: BlockSelector, ) -> RpcResult { self.get_block_announcements_call(GetBlockAnnouncementsRequest { selector }) .await diff --git a/rpc/core/src/model/common.rs b/rpc/core/src/model/common.rs index b55e3fc..2e94828 100644 --- a/rpc/core/src/model/common.rs +++ b/rpc/core/src/model/common.rs @@ -3,6 +3,7 @@ use std::fmt::LowerHex; use std::str::FromStr; use nyks_consensus::block::block_height::BlockHeight; +use nyks_consensus::network::Network; use serde::Deserialize; use serde::Deserializer; use serde::Serialize; @@ -207,8 +208,35 @@ impl FromStr for BlockSelector { } } -// TODO: cleanup... -pub type RpcBlockSelector = BlockSelector; +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct RpcNetwork(pub Network); + +impl From for RpcNetwork { + fn from(network: Network) -> Self { + RpcNetwork(network) + } +} + +impl From for Network { + fn from(network: RpcNetwork) -> Self { + network.0 + } +} + +impl Serialize for RpcNetwork { + fn serialize(&self, s: S) -> Result { + s.collect_str(&self.0) + } +} + +impl<'de> Deserialize<'de> for RpcNetwork { + fn deserialize>(d: D) -> Result { + let s = String::deserialize(d)?; + Network::from_str(&s) + .map(RpcNetwork) + .map_err(serde::de::Error::custom) + } +} #[cfg(test)] #[cfg_attr(coverage_nightly, coverage(off))] diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index 1c6ff80..bb7c7c5 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -25,7 +25,7 @@ pub struct NetworkRequest {} #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NetworkResponse { - pub network: String, + pub network: RpcNetwork, } #[derive(Clone, Copy, Debug, Serialize_tuple, Deserialize_tuple)] @@ -121,7 +121,7 @@ pub struct TipAnnouncementsResponse { #[derive(Clone, Copy, Debug, Serialize_tuple, Deserialize_tuple)] #[serde(rename_all = "camelCase")] pub struct GetBlockDigestRequest { - pub selector: RpcBlockSelector, + pub selector: BlockSelector, } #[derive(Clone, Copy, Debug, Serialize, Deserialize)] @@ -145,7 +145,7 @@ pub struct GetBlockDigestsResponse { #[derive(Clone, Copy, Debug, Serialize_tuple, Deserialize_tuple)] #[serde(rename_all = "camelCase")] pub struct GetBlockRequest { - pub selector: RpcBlockSelector, + pub selector: BlockSelector, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -157,7 +157,7 @@ pub struct GetBlockResponse { #[derive(Clone, Copy, Debug, Serialize_tuple, Deserialize_tuple)] #[serde(rename_all = "camelCase")] pub struct GetBlockProofRequest { - pub selector: RpcBlockSelector, + pub selector: BlockSelector, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -169,7 +169,7 @@ pub struct GetBlockProofResponse { #[derive(Clone, Copy, Debug, Serialize_tuple, Deserialize_tuple)] #[serde(rename_all = "camelCase")] pub struct GetBlockKernelRequest { - pub selector: RpcBlockSelector, + pub selector: BlockSelector, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -181,7 +181,7 @@ pub struct GetBlockKernelResponse { #[derive(Clone, Copy, Debug, Serialize_tuple, Deserialize_tuple)] #[serde(rename_all = "camelCase")] pub struct GetBlockHeaderRequest { - pub selector: RpcBlockSelector, + pub selector: BlockSelector, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -193,7 +193,7 @@ pub struct GetBlockHeaderResponse { #[derive(Clone, Copy, Debug, Serialize_tuple, Deserialize_tuple)] #[serde(rename_all = "camelCase")] pub struct GetBlockBodyRequest { - pub selector: RpcBlockSelector, + pub selector: BlockSelector, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -205,7 +205,7 @@ pub struct GetBlockBodyResponse { #[derive(Clone, Copy, Debug, Serialize_tuple, Deserialize_tuple)] #[serde(rename_all = "camelCase")] pub struct GetBlockTransactionKernelRequest { - pub selector: RpcBlockSelector, + pub selector: BlockSelector, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -217,7 +217,7 @@ pub struct GetBlockTransactionKernelResponse { #[derive(Clone, Copy, Debug, Serialize_tuple, Deserialize_tuple)] #[serde(rename_all = "camelCase")] pub struct GetBlockAnnouncementsRequest { - pub selector: RpcBlockSelector, + pub selector: BlockSelector, } #[derive(Clone, Debug, Serialize, Deserialize)]