From 5cc65bb7241856d503d2a57d6263de8a1ff17721 Mon Sep 17 00:00:00 2001 From: Roy Ben Abraham Date: Fri, 12 Nov 2021 15:50:57 +0700 Subject: [PATCH] Updated docs for node start-up, tokens, and transactions --- docs/README.md | 65 +++++++++++++++++++++++-------- docs/tokens.md | 93 +++++++++++++++++++++++---------------------- docs/transaction.md | 30 +++++++-------- 3 files changed, 113 insertions(+), 75 deletions(-) diff --git a/docs/README.md b/docs/README.md index dc2b9b1..1afc7fc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -3,9 +3,21 @@ ## Quick start Binaries can be found [here](https://github.com/mintlayer/core/releases). + +Running a node also requires as input a chain specification. +Currently a single chain specification for the testnet is provided, and can be downloaded using curl: + +``` +curl --proto '=https' -sSf \ + https://raw.githubusercontent.com/mintlayer/core/master/assets/Testnet1Spec.json \ + --output Testnet1Spec.json +``` Download and run: ``` -mintlayer-core --base-path data/my_first_ml_node --validator --rpc-external --rpc-methods Unsafe --chain=Testnet1Spec.json +mintlayer-core \ + --base-path data/my_first_ml_node \ + --validator \ + --chain=Testnet1Spec.json ``` to start a node. It will automatically connect to the Mintlayer bootnodes. @@ -80,7 +92,7 @@ See [Mintlayer installation on Windows](windows_installation.md) ## Running a node Clone the repository: -```bash +``` git clone https://github.com/mintlayer/core.git ``` @@ -94,25 +106,47 @@ to build the project. Finally, to run a node: ``` -RUST_LOG=info ./target/release/mintlayer-core --base-path [PATH_TO_DB] --name [NODE_NAME] --port [P2P_PORT] --ws-port [WEB_SOCKET_PORT] --rpc-port [RPC_PORT] --validator --rpc-methods Unsafe --chain=[CHAIN_SPEC] +RUST_LOG=info ./target/release/mintlayer-core \ + --base-path [PATH_TO_DB] \ + --name [NODE_NAME] \ + --port [P2P_PORT] \ + --ws-port [WEB_SOCKET_PORT] \ + --rpc-port [RPC_PORT] \ + --validator \ + --chain=[CHAIN_SPEC] ``` + For example, ``` -RUST_LOG=info ./target/release/mintlayer-core --base-path data/node1 --name brian --port 30333 --ws-port 9945 --rpc-port 9933 --validator --rpc-methods Unsafe --chain=Testnet1Spec.json +RUST_LOG=info ./target/release/mintlayer-core \ + --base-path data/node1 \ + --name brian \ + --port 30333 \ + --ws-port 9945 \ + --rpc-port 9933 \ + --validator \ + --chain=Testnet1Spec.json ``` + Let's look at these flags in detail: -|
Flags
| Descriptions | -| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `--base-path` | Specifies a directory where Mintlayer should store all the data related to this chain. If the directory does not exist, it will be created for you. If other blockchain data already exists there you will get an error. Either clear the directory or choose a different one. | -| `--chain local` | Specifies which chain specification to use. There are a few prepackaged options including `local`, `development`, and `staging` but generally one specifies their own chain spec file. We'll specify our own file in a later step. | -| `--alice` | Puts the predefined Alice keys (both for block production and finalization) in the node's keystore. Generally one should generate their own keys and insert them with an RPC call. We'll generate our own keys in a later step. This flag also makes Alice a validator. | -| `--port 30333` | Specifies the port that your node will listen for p2p traffic on. `30333` is the default and this flag can be omitted if you're happy with the default. If Bob's node will run on the same physical system, you will need to explicitly specify a different port for it. | -| `--ws-port 9945` | Specifies the port that your node will listen for incoming WebSocket traffic on. The default value is `9944`. This example uses a custom web socket port number (`9945`). | -| `--rpc-port 9933` | Specifies the port that your node will listen for incoming RPC traffic on. `9933` is the default, so this parameter may be omitted. | -| `--node-key ` | The Ed25519 secret key to use for `libp2p` networking. The value is parsed as a hex-encoded Ed25519 32 byte secret key, i.e. 64 hex characters. WARNING: Secrets provided as command-line arguments are easily exposed. Use of this option should be limited to development and testing. | -| `--telemetry-url` | Tells the node to send telemetry data to a particular server. The one we've chosen here is hosted by Parity and is available for anyone to use. You may also host your own (beyond the scope of this article) or omit this flag entirely. | -| `--validator` | Means that we want to participate in block production and finalization rather than just sync the network. | +|
Flags
| Descriptions | +| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `--base-path` | Specifies a directory where Mintlayer should store all the data related to this chain. If the directory does not exist, it will be created for you. If other blockchain data already exists there you will get an error. Either clear the directory or choose a different one. | +| `--chain=[CHAIN_SPEC_FILE]` | Specifies which chain specification to use. A chain specification, or "chain spec", is a collection of configuration information that dictates which network a blockchain node will connect to, which entities it will initially communicate with, and what consensus-critical state it must have at genesis. | +| `--alice` | Puts the predefined Alice keys (both for block production and finalization) in the node's keystore. Generally one should generate their own keys and insert them with an RPC call. We'll generate our own keys in a later step. This flag also makes Alice a validator. | +| `--port 30333` | Specifies the port that your node will listen for p2p traffic on. `30333` is the default and this flag can be omitted if you're happy with the default. If Bob's node will run on the same physical system, you will need to explicitly specify a different port for it. | +| `--ws-port 9945` | Specifies the port that your node will listen for incoming WebSocket traffic on. The default value is `9944`. This example uses a custom web socket port number (`9945`). | +| `--rpc-port 9933` | Specifies the port that your node will listen for incoming RPC traffic on. `9933` is the default, so this parameter may be omitted. | +| `--telemetry-url` | Tells the node to send telemetry data to a particular server. The one we've chosen here is hosted by Parity and is available for anyone to use. You may also host your own (beyond the scope of this article) or omit this flag entirely. | +| `--validator` | Means that we want to participate in block production and finalization rather than just sync the network. | + +*Note*: As a safety precaution, the node will listen to RPC interfaces on localhost only. +It is possible expose the node's RPC port publicly `--rpc-external`, but only do this if you understand the risks involved. As a safer alternative to enable RPC calls from outside the node, consider using tunnels. + +**TODO** Provide an explanation/examples of methods that are unsafe to call. Or link to a list, together with an explanation of why each method is unsafe. + +*Note*: Some RPC calls can be used to control the node's behavior and should never (or rarely) be exposed. We call such methods _Unsafe_, and they are disabled by default. It is possible to enable them using `--rpc-methods Unsafe`. ## Docker setup @@ -128,6 +162,7 @@ If you want to save the blockchain to host, run: docker run -v ~/ml-blockchain:/tmp/ml-core -t mintlayer-core ``` + ## Create a chain specification In the preceding example, we used `--chain local` which is a predefined "chain spec" that has Alice and Bob specified as validators along with many other useful defaults. diff --git a/docs/tokens.md b/docs/tokens.md index a0d349b..b66a396 100644 --- a/docs/tokens.md +++ b/docs/tokens.md @@ -1,25 +1,23 @@ # Mintlayer Tokens -**TODO Do we want Rust code in this doc?** -Each transaction output must carry a data field. This field describes the purpose of the transaction. +This document describes the structure of transactions involving tokens on Mintlayer. Currently, two types of tokens are supported: -We must highlight the following at the moment: +1. *MLS-01 tokens*: MLS-01 is the "basic" Mintlayer token standard, analogous to, say, ERC-20 tokens on Ethereum. -- Transfer Tokens or NFT -- Token Issuance -- Burning tokens -- NFT creation +2. NFTs + +A transaction involving transaction output carries a (possibly empty) `data` field specifying the purpose of the transaction. -All transactions must be signed on the client side. This means we cannot sign transactions on the node side. -**TODO what is the connection between these two sentences?** -In the future, there will be changes to the structures of transactions and we will be required to track them. -**TODO by structures above to we mean rust structs? Why is this interesting to the user?** +The data field can be any of the following: + +- Transfer of MLS-01 tokens or NFTs +- Token issuance +- Token burning +- NFT creation -## Transfer Tokens +## Transferring Tokens -**TODO sentence fragment, I don't understand...** -**TODO When do we NOT use the TxData field?** -For transfering funds to another person in a given UTXO. To send MLT we will use the MLT token ID, which is equal to 0. If the token ID is equal to the ID of the MLS-01 (**TODO what is MLS-01**) token, then the amount of token is transferred to the recipient. The commission is taken only in MLT (**TODO what is this commission**. If the token ID is equal to the ID of any NFT, then the data of this NFT is transferred to the recipient without changing the creator field. The UTXO model itself allows to determine the owner of the NFT. +To send MLT we use the MLT token ID, which is equal to 0. If the token ID is equal to the ID of an MLS-01 token, then the amount of the token is transferred to the recipient. The transaction fee is taken only in MLT. If the token ID is equal to the ID of any NFT, then the data of this NFT is transferred to the recipient without changing the creator field. The UTXO model itself allows to determine the owner of the NFT. ```rust TxData { @@ -30,27 +28,32 @@ TxData { } ``` -## Issue Tokens -When issuing a new token, we specify the data for creating a new token in the transaction input, where the `token_id` is a hash of the inputs. **TODO which inputs?** -**TODO explain remaining fields** +## Issuing Tokens -**TODO understand the comment** -```rust +To issue a new token, we specify the data for creating the token in the transaction output's `data` field: + + ```rust TxData { TokenIssuanceV1 { - token_id: TokenID, - token_ticker: Vec, - amount_to_issue: Value, - // Should be not more than 18 numbers - number_of_decimals: u8, - metadata_URI: Vec, - } + token_ticker: Vec, + amount_to_issue: Value, + // Should not be more than 18 + number_of_decimals: u8, + metadata_URI: Vec, + } } -``` + ``` + +Here, `token_ticker` is a short name given to the token (up to 5 chararcters long). + +The `metatdata_URI` is a web link to a JSON file where we can store additional information about the token + +The _token ID_ is defined as a hash of the _first input_ of the issuance transaction. + + +### Burning Tokens -### Burn Tokens -**TODO verify - the input should be a utxo that contains tokens, the output should contain the TokenBurn arm** -A token burning - as an input is used by UTXO that contains tokens. As an output, the data field should contain the TokenBurn arm. If the amount in burning the output is less than in the input then there should exist at least one output for returning the funds change. In this case, you can burn any existing number of tokens. After this operation, you can use UTXO for the remaining amount of tokens. +The input for a token-burning transaction should be a UTXO containing tokens. In the output, the data field should contain the _TokenBurn_ variant. If the `amount_to_burn` in the output is less than the amount in the input, then there should exist at least one output for returning the difference. In this way, any existing number of tokens can be burned. ```rust TxData { @@ -61,18 +64,17 @@ TxData { } ``` ### NFT -TO DO +**TODO** ## Wallet +**TODO** -TO DO -## Issue and Transfer Tokens +## Issuing and Transferring Tokens -**TODO who are these examples meant for?** ```rust /* Transfer and Issuance in one Tx */ -// Alice issues 1_000_000_000 MLS-01, and send them to Karl +// Alice issues 1_000_000_000 MLS-01 tokens, and sends them to Karl let (utxo0, input0) = tx_input_gen_no_signature(); let tx = Transaction { inputs: vec![input0], @@ -96,13 +98,17 @@ let tx = Transaction { } .sign_unchecked(&[utxo0.clone()], 0, &alice_pub_key); -let first_issuance_token_id = TokenId::new(&tx.inputs[0]); assert_ok!(Utxo::spend(Origin::signed(H256::zero()), tx.clone())); + +let first_issuance_token_id = TokenId::new(&tx.inputs[0]); + +// The newly issued token is represented by the TransactionOutput at index 1 +// "Outoint" here refers to the hash of the TransactionOutput struct. let token_utxo_hash = tx.outpoint(1); let token_utxo = tx.outputs[1].clone(); -// Let's send 300_000_000 and rest back and create another token +// Let's send alice 300_000_000 and the rest back to Karl, andalso create another token, "KarlToken" let tx = Transaction { inputs: vec![TransactionInput::new_empty(token_utxo_hash)], outputs: vec![ @@ -126,7 +132,7 @@ let tx = Transaction { 0, H256::from(karl_pub_key), OutputData::TokenIssuanceV1 { - token_ticker: "Token".as_bytes().to_vec(), + token_ticker: "KarlToken".as_bytes().to_vec(), amount_to_issue: 5_000_000_000, // Should be not more than 18 numbers number_of_decimals: 12, @@ -137,10 +143,13 @@ let tx = Transaction { time_lock: Default::default(), } .sign_unchecked(&[token_utxo.clone()], 0, &karl_pub_key); + assert_ok!(Utxo::spend(Origin::signed(H256::zero()), tx.clone())); + let alice_transfer_utxo_hash = tx.outpoint(0); let karl_transfer_utxo_hash = tx.outpoint(1); let karl_issuance_utxo_hash = tx.outpoint(2); + assert!(!UtxoStore::::contains_key(H256::from( token_utxo_hash ))); @@ -148,8 +157,6 @@ assert!(UtxoStore::::contains_key(alice_transfer_utxo_hash)); assert!(UtxoStore::::contains_key(karl_transfer_utxo_hash)); assert!(UtxoStore::::contains_key(karl_issuance_utxo_hash)); - - // Let's check token transfer UtxoStore::::get(alice_transfer_utxo_hash) .unwrap() @@ -165,8 +172,6 @@ UtxoStore::::get(alice_transfer_utxo_hash) }) .unwrap(); - - UtxoStore::::get(karl_transfer_utxo_hash) .unwrap() .data @@ -181,8 +186,6 @@ UtxoStore::::get(karl_transfer_utxo_hash) }) .unwrap(); - - // Let's check token issuance UtxoStore::::get(karl_issuance_utxo_hash) .unwrap() diff --git a/docs/transaction.md b/docs/transaction.md index 164b0af..1da057d 100644 --- a/docs/transaction.md +++ b/docs/transaction.md @@ -2,17 +2,11 @@ ## UTXO overview -**TODO maybe system/model instead of structure?** -Mintlayer uses the Bitcoin UTXO structure instead of the account-based models of Ethereum, Ripple, Stellar, and others. -**TODO need explanation of this sentence, it is not clear to me** -Since each transaction's output is stored separately (even when sent to a single address), it is only possible to spend the entire transaction’s output. +Mintlayer uses the a UTXO system similar to Bitcoin's, instead of the account-based models of Ethereum, Ripple, Stellar, and others. There are three essential reasons for this: -There are three essential reasons for choosing the UTXO model: - -- It is compatible with technologies already implemented in Bitcoin, such atomic swaps and the Lightning Network. +- The utxo model is compatible with technologies already implemented in Bitcoin, such atomic swaps and the Lightning Network. -**TODO - why does this improve privacy?** -- It is more privacy-oriented: a single wallet usually utilizes multiple addresses, making it difficult and sometimes impossible to determine which addresses belong to whichs user. +- The utxo model is more privacy-oriented: a single wallet can utilize multiple addresses, making it difficult and sometimes impossible to determine which addresses belong to which user. - Payments can be batched together (aggregated) in a single transaction, saving a considerable amount of the space otherwise required for making a single transaction per payment. @@ -24,10 +18,10 @@ There are three destination types for transaction outputs : A general Mintlayer transaction looks something like this: -**TODO Not sure we want this in Rust code. Too developer specific. Not clear what H256 is, witness, lock** -**TODO possibly add a link to information about the utxo system** -**TODO if we go for the rust struct, then we need the data field. Also, what is this field?** +**TODO if we go for the rust struct, then we need the data field in output** + **TODO timelock is not a string..."** + ```rust Transaction { inputs: [ @@ -66,17 +60,23 @@ In Mintlayer, as Substrate, transanctions need to be signed before being submitt - The timelock **TODO Explain what we are showing here** + **TODO We need to document the python mintlayer crate** + **TODO what is utxos[0][0]? Utxos is a two-dimentsional array?** + **TODO I want to see the Transaction python class. What is the utxo[0][1] in the signature?** -**In the second transaction's signature, outpoints instead of outputs** + +**TODO In the second transaction's signature, outpoints instead of outputs** ### Python ```python from substrateinterface import Keypair import mintlayer.utxo as utxo -client = self.nodes[0].rpc_client +#... + +account = Account(args) alice = Keypair.create_from_uri('//Alice') bob = Keypair.create_from_uri('//Bob') @@ -85,7 +85,7 @@ bob = Keypair.create_from_uri('//Bob') utxos = list(client.utxos_for(alice)) tx1 = utxo.Transaction( - client, + account.client, inputs=[ utxo.Input(utxos[0][0]), ],