mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-01-02 13:23:10 +00:00
Merge branch 'main' into schouhy/implement-privacy-preserving-tail-calls
This commit is contained in:
commit
ffe2ae4e0b
468
README.md
468
README.md
@ -1,469 +1,33 @@
|
||||
# nescience-testnet
|
||||
This repo serves for Nescience Node testnet
|
||||
This repo serves for Nescience testnet
|
||||
|
||||
For more details you can read [blogpost](https://vac.dev/rlog/Nescience-state-separation-architecture/)
|
||||
For more details you can read [here](https://notes.status.im/Ya2wDpIyQquoiRiuEIM8hQ?view).
|
||||
|
||||
For more details on node functionality [here](https://www.notion.so/5-Testnet-initial-results-analysis-18e8f96fb65c808a835cc43b7a84cddf)
|
||||
# Install dependencies
|
||||
|
||||
# How to run
|
||||
Node and sequecer require Rust installation to build. Preferable latest stable version.
|
||||
|
||||
Rust can be installed as
|
||||
Install build dependencies
|
||||
- On Linux
|
||||
```sh
|
||||
apt install build-essential clang libssl-dev pkg-config
|
||||
```
|
||||
- On Mac
|
||||
```sh
|
||||
xcode-select --install
|
||||
brew install pkg-config openssl
|
||||
```
|
||||
|
||||
Install Rust
|
||||
```sh
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
```
|
||||
|
||||
Node needs RISC0 toolchain to run.
|
||||
|
||||
It can be installed as
|
||||
Install Risc0
|
||||
|
||||
```sh
|
||||
curl -L https://risczero.com/install | bash
|
||||
```
|
||||
|
||||
After that, before next step, you may need to restart your console, as script updates PATH variable. Next:
|
||||
|
||||
Then restart your shell and run
|
||||
```sh
|
||||
rzup install
|
||||
```
|
||||
|
||||
After cloning this repository the following actions need to be done:
|
||||
|
||||
Entrypoints to node and sequencer are `node_runner` and `sequencer_runner`. Both of them have a configuration of similar manner. Path to configs need to be given into runner binaries as first arguent. No other arguments have to be given. We search given directory for files "node_config.json" for node and "sequencer_config.json" for sequencer.
|
||||
|
||||
With repository debug configs at `node_runner/configs/debug` and `sequencer_runner/configs/debug` are provided, you can use them, or modify as you wish.
|
||||
|
||||
For sequencer:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"home": ".",
|
||||
"override_rust_log": null,
|
||||
"genesis_id": 1,
|
||||
"is_genesis_random": true,
|
||||
"max_num_tx_in_block": 20,
|
||||
"block_create_timeout_millis": 10000,
|
||||
"port": 3040
|
||||
}
|
||||
```
|
||||
|
||||
* "home" shows relative path to directory with datebase.
|
||||
* "override_rust_log" sets env var "RUST_LOG" to achieve different log levels(if null, using present "RUST_LOG" value).
|
||||
* "genesis_id" is id of genesis block.
|
||||
* "is_genesis_random" - flag to randomise forst block.
|
||||
* "max_num_tx_in_block" - transaction mempool limit.
|
||||
* "block_create_timeout_millis" - block timeout.
|
||||
* "port" - port, which sequencer will listen.
|
||||
|
||||
For node:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"home": ".",
|
||||
"override_rust_log": null,
|
||||
"sequencer_addr": "http://127.0.0.1:3040",
|
||||
"seq_poll_timeout_secs": 10,
|
||||
"port": 3041
|
||||
}
|
||||
```
|
||||
|
||||
* "home" shows relative path to directory with datebase.
|
||||
* "override_rust_log" sets env var "RUST_LOG" to achieve different log levels(if null, using present "RUST_LOG" value).
|
||||
* "sequencer_addr" - address of sequencer.
|
||||
* "seq_poll_timeout_secs" - polling interval on sequencer, in seconds.
|
||||
* "port" - port, which sequencer will listen.
|
||||
|
||||
To run:
|
||||
|
||||
_FIRSTLY_ in sequencer_runner directory:
|
||||
|
||||
```sh
|
||||
RUST_LOG=info cargo run <path-to-configs>
|
||||
```
|
||||
|
||||
_SECONDLY_ in node_runner directory
|
||||
|
||||
```sh
|
||||
RUST_LOG=info cargo run <path-to-configs>
|
||||
```
|
||||
|
||||
# Node Public API
|
||||
|
||||
Node exposes public API with mutable and immutable methods to create and send transactions.
|
||||
|
||||
## Standards
|
||||
|
||||
Node supports JSON RPC 2.0 standard, details can be seen [there](https://www.jsonrpc.org/specification).
|
||||
|
||||
## API Structure
|
||||
|
||||
Right now API has only one endpoint for every request('/'), and JSON RPC 2.0 standard request structure is fairly simple
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": $number_or_dontcare,
|
||||
"method": $string,
|
||||
"params": $object
|
||||
}
|
||||
```
|
||||
|
||||
Response strucuture will look as follows:
|
||||
|
||||
Success:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"result": $object,
|
||||
"id": "dontcare"
|
||||
}
|
||||
```
|
||||
|
||||
There $number - integer or string "dontcare", $string - string and $object - is some JSON object.
|
||||
|
||||
## Methods
|
||||
|
||||
* get_block
|
||||
|
||||
Get block data for specific block number.
|
||||
|
||||
Request:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": $number_or_dontcare,
|
||||
"method": "get_block",
|
||||
"params": {
|
||||
"block_id": $number
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"block": $block
|
||||
},
|
||||
"id": $number_or_dontcare
|
||||
}
|
||||
```
|
||||
|
||||
There "block" field returns block for requested block id
|
||||
|
||||
* get_last_block
|
||||
|
||||
Get last block number.
|
||||
|
||||
Request:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": $number_or_dontcare,
|
||||
"method": "get_last_block",
|
||||
"params": {}
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"last_block": $number
|
||||
},
|
||||
"id": $number_or_dontcare
|
||||
}
|
||||
```
|
||||
|
||||
There "last_block" field returns number of last block
|
||||
|
||||
* write_register_account
|
||||
|
||||
Create new acccount with 0 public balance and no private UTXO.
|
||||
|
||||
Request:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": $number_or_dontcare,
|
||||
"method": "write_register_account",
|
||||
"params": {}
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"status": $string
|
||||
},
|
||||
"id": $number_or_dontcare
|
||||
}
|
||||
```
|
||||
|
||||
There "status" field shows address of generated account
|
||||
|
||||
* show_account_public_balance
|
||||
|
||||
Show account public balance, field "account_addr" can be taken from response in "write_register_account" request.
|
||||
|
||||
Request:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": $number_or_dontcare,
|
||||
"method": "show_account_public_balance",
|
||||
"params": {
|
||||
"account_addr": $string
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"addr": $string,
|
||||
"balance": $number
|
||||
},
|
||||
"id": $number_or_dontcare
|
||||
}
|
||||
```
|
||||
|
||||
Fields in response is self-explanatory.
|
||||
|
||||
* write_deposit_public_balance
|
||||
|
||||
Deposit public balance into account. Any amount under u64::MAX can be deposited, can overflow.
|
||||
Due to hashing process(transactions currently does not have randomization factor), we can not send two deposits with same amount to one account.
|
||||
|
||||
Request:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": $number_or_dontcare,
|
||||
"method": "write_deposit_public_balance",
|
||||
"params": {
|
||||
"account_addr": $string,
|
||||
"amount": $number
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"status": "success"
|
||||
},
|
||||
"id": $number_or_dontcare
|
||||
}
|
||||
```
|
||||
|
||||
Fields in response is self-explanatory.
|
||||
|
||||
* write_mint_utxo
|
||||
|
||||
Mint private UTXO for account.
|
||||
Due to hashing process(transactions currently does not have randomization factor), we can not send two mints with same amount to one account.
|
||||
|
||||
Request:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": $number_or_dontcare,
|
||||
"method": "write_mint_utxo",
|
||||
"params": {
|
||||
"account_addr": $string,
|
||||
"amount": $number
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"status": "success",
|
||||
"utxo": {
|
||||
"asset": [$number],
|
||||
"commitment_hash": $string,
|
||||
"hash": $string
|
||||
}
|
||||
},
|
||||
"id": $number_or_dontcare
|
||||
}
|
||||
```
|
||||
|
||||
There in "utxo" field "hash" is used for viewing purposes, field "commitment_hash" is used for sending purposes.
|
||||
|
||||
* show_account_utxo
|
||||
|
||||
Show UTXO data for account. "utxo_hash" there can be taken from "hash" field in response for "write_mint_utxo" request
|
||||
|
||||
Request:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": $number_or_dontcare,
|
||||
"method": "show_account_utxo",
|
||||
"params": {
|
||||
"account_addr": $string,
|
||||
"utxo_hash": $string
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"amount": $number,
|
||||
"asset": [$number],
|
||||
"hash": $string
|
||||
},
|
||||
"id": $number_or_dontcare
|
||||
}
|
||||
```
|
||||
|
||||
Fields in response is self-explanatory.
|
||||
|
||||
* write_send_utxo_private
|
||||
|
||||
Send utxo from one account private balance into another(need to be different) private balance.
|
||||
|
||||
Both parties are is hidden.
|
||||
|
||||
Request:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": $number_or_dontcare,
|
||||
"method": "write_send_utxo_private",
|
||||
"params": {
|
||||
"account_addr_sender": $string,
|
||||
"account_addr_receiver": $string,
|
||||
"utxo_hash": $string,
|
||||
"utxo_commitment": $string
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"status": "success",
|
||||
"utxo_result": {
|
||||
"asset": [$number],
|
||||
"commitment_hash": $string,
|
||||
"hash": $string
|
||||
}
|
||||
},
|
||||
"id": $number_or_dontcare
|
||||
}
|
||||
```
|
||||
|
||||
Be aware, that during this action old UTXO is nullified, hence can not be used anymore, even if present in owner private state.
|
||||
|
||||
* write_send_utxo_deshielded
|
||||
|
||||
Send utxo from one account private balance into another(not neccesary different account) public balance.
|
||||
|
||||
Sender is hidden.
|
||||
|
||||
Request:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": $number_or_dontcare,
|
||||
"method": "write_send_utxo_deshielded",
|
||||
"params": {
|
||||
"account_addr_sender": $string,
|
||||
"account_addr_receiver": $string,
|
||||
"utxo_hash": $string,
|
||||
"utxo_commitment": $string
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"status": "success"
|
||||
},
|
||||
"id": $number_or_dontcare
|
||||
}
|
||||
```
|
||||
|
||||
Fields in response is self-explanatory.
|
||||
|
||||
* write_send_utxo_shielded
|
||||
|
||||
Send amount from one account public balance into another(not neccesary different account) private balance.
|
||||
|
||||
Receiver is hidden.
|
||||
|
||||
Request:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": $number_or_dontcare,
|
||||
"method": "write_send_utxo_shielded",
|
||||
"params": {
|
||||
"account_addr_sender": $string,
|
||||
"account_addr_receiver": $string,
|
||||
"amount": $number
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```yaml
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"status": "success",
|
||||
"utxo_result": {
|
||||
"asset": [$number],
|
||||
"commitment_hash": $string,
|
||||
"hash": $string
|
||||
}
|
||||
},
|
||||
"id": $number_or_dontcare
|
||||
}
|
||||
```
|
||||
|
||||
Fields in response is self-explanatory.
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::account::{Account, AccountWithMetadata};
|
||||
use risc0_zkvm::serde::Deserializer;
|
||||
use risc0_zkvm::{DeserializeOwned, guest::env};
|
||||
@ -71,30 +73,35 @@ pub fn validate_execution(
|
||||
post_states: &[Account],
|
||||
executing_program_id: ProgramId,
|
||||
) -> bool {
|
||||
// 1. Lengths must match
|
||||
// 1. Check account ids are all different
|
||||
if !validate_uniqueness_of_account_ids(pre_states) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. Lengths must match
|
||||
if pre_states.len() != post_states.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (pre, post) in pre_states.iter().zip(post_states) {
|
||||
// 2. Nonce must remain unchanged
|
||||
// 3. Nonce must remain unchanged
|
||||
if pre.account.nonce != post.nonce {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. Program ownership changes are not allowed
|
||||
// 4. Program ownership changes are not allowed
|
||||
if pre.account.program_owner != post.program_owner {
|
||||
return false;
|
||||
}
|
||||
|
||||
let account_program_owner = pre.account.program_owner;
|
||||
|
||||
// 4. Decreasing balance only allowed if owned by executing program
|
||||
// 5. Decreasing balance only allowed if owned by executing program
|
||||
if post.balance < pre.account.balance && account_program_owner != executing_program_id {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 5. Data changes only allowed if owned by executing program or if account pre state has
|
||||
// 6. Data changes only allowed if owned by executing program or if account pre state has
|
||||
// default values
|
||||
if pre.account.data != post.data
|
||||
&& pre.account != Account::default()
|
||||
@ -103,13 +110,13 @@ pub fn validate_execution(
|
||||
return false;
|
||||
}
|
||||
|
||||
// 6. If a post state has default program owner, the pre state must have been a default account
|
||||
// 7. If a post state has default program owner, the pre state must have been a default account
|
||||
if post.program_owner == DEFAULT_PROGRAM_ID && pre.account != Account::default() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Total balance is preserved
|
||||
// 8. Total balance is preserved
|
||||
let total_balance_pre_states: u128 = pre_states.iter().map(|pre| pre.account.balance).sum();
|
||||
let total_balance_post_states: u128 = post_states.iter().map(|post| post.balance).sum();
|
||||
if total_balance_pre_states != total_balance_post_states {
|
||||
@ -118,3 +125,14 @@ pub fn validate_execution(
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn validate_uniqueness_of_account_ids(pre_states: &[AccountWithMetadata]) -> bool {
|
||||
let number_of_accounts = pre_states.len();
|
||||
let number_of_account_ids = pre_states
|
||||
.iter()
|
||||
.map(|account| account.account_id.clone())
|
||||
.collect::<HashSet<_>>()
|
||||
.len();
|
||||
|
||||
number_of_accounts == number_of_account_ids
|
||||
}
|
||||
|
||||
@ -3,7 +3,12 @@ use std::collections::HashSet;
|
||||
use risc0_zkvm::{guest::env, serde::to_vec};
|
||||
|
||||
use nssa_core::{
|
||||
account::{Account, AccountId, AccountWithMetadata}, compute_digest_for_path, encryption::Ciphertext, program::{validate_execution, ProgramId, ProgramOutput, DEFAULT_PROGRAM_ID}, Commitment, CommitmentSetDigest, EncryptionScheme, Nullifier, NullifierPublicKey, PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput, DUMMY_COMMITMENT_HASH
|
||||
Commitment, CommitmentSetDigest, DUMMY_COMMITMENT_HASH, EncryptionScheme,
|
||||
Nullifier, NullifierPublicKey, PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput,
|
||||
account::{Account, AccountId, AccountWithMetadata},
|
||||
compute_digest_for_path,
|
||||
encryption::Ciphertext,
|
||||
program::{DEFAULT_PROGRAM_ID, ProgramOutput, validate_execution},
|
||||
};
|
||||
|
||||
fn main() {
|
||||
@ -16,43 +21,12 @@ fn main() {
|
||||
program_id,
|
||||
} = env::read();
|
||||
|
||||
// These lists will be the public outputs of this circuit
|
||||
// and will be populated next.
|
||||
let mut public_pre_states: Vec<AccountWithMetadata> = Vec::new();
|
||||
let mut public_post_states: Vec<Account> = Vec::new();
|
||||
let mut ciphertexts: Vec<Ciphertext> = Vec::new();
|
||||
let mut new_commitments: Vec<Commitment> = Vec::new();
|
||||
let mut new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)> = Vec::new();
|
||||
// TODO: WIP
|
||||
let program_output = program_outputs[0].clone();
|
||||
|
||||
for program_output in program_outputs.iter() {
|
||||
// Check that `program_output` is consistent with the execution of the corresponding program.
|
||||
env::verify(program_id, &to_vec(program_output).unwrap()).unwrap();
|
||||
}
|
||||
// Check that `program_output` is consistent with the execution of the corresponding program.
|
||||
env::verify(program_id, &to_vec(&program_output).unwrap()).unwrap();
|
||||
|
||||
if private_nonces_iter.next().is_some() {
|
||||
panic!("Too many nonces.");
|
||||
}
|
||||
|
||||
if private_keys_iter.next().is_some() {
|
||||
panic!("Too many private account keys.");
|
||||
}
|
||||
|
||||
if private_auth_iter.next().is_some() {
|
||||
panic!("Too many private account authentication keys.");
|
||||
}
|
||||
|
||||
let output = PrivacyPreservingCircuitOutput {
|
||||
public_pre_states,
|
||||
public_post_states,
|
||||
ciphertexts,
|
||||
new_commitments,
|
||||
new_nullifiers,
|
||||
};
|
||||
|
||||
env::commit(&output);
|
||||
}
|
||||
|
||||
fn validate_program_execution(program_output: &ProgramOutput, program_id: ProgramId) {
|
||||
let ProgramOutput {
|
||||
pre_states,
|
||||
post_states,
|
||||
@ -64,11 +38,6 @@ fn validate_program_execution(program_output: &ProgramOutput, program_id: Progra
|
||||
panic!("Privacy preserving transactions do not support yet chained calls.")
|
||||
}
|
||||
|
||||
// Check that there are no repeated account ids
|
||||
if !validate_uniqueness_of_account_ids(&pre_states) {
|
||||
panic!("Repeated account ids found")
|
||||
}
|
||||
|
||||
// Check that the program is well behaved.
|
||||
// See the # Programs section for the definition of the `validate_execution` method.
|
||||
if !validate_execution(&pre_states, &post_states, program_id) {
|
||||
@ -80,6 +49,14 @@ fn validate_program_execution(program_output: &ProgramOutput, program_id: Progra
|
||||
panic!("Invalid visibility mask length");
|
||||
}
|
||||
|
||||
// These lists will be the public outputs of this circuit
|
||||
// and will be populated next.
|
||||
let mut public_pre_states: Vec<AccountWithMetadata> = Vec::new();
|
||||
let mut public_post_states: Vec<Account> = Vec::new();
|
||||
let mut ciphertexts: Vec<Ciphertext> = Vec::new();
|
||||
let mut new_commitments: Vec<Commitment> = Vec::new();
|
||||
let mut new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)> = Vec::new();
|
||||
|
||||
let mut private_nonces_iter = private_account_nonces.iter();
|
||||
let mut private_keys_iter = private_account_keys.iter();
|
||||
let mut private_auth_iter = private_account_auth.iter();
|
||||
@ -173,15 +150,26 @@ fn validate_program_execution(program_output: &ProgramOutput, program_id: Progra
|
||||
_ => panic!("Invalid visibility mask value"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_uniqueness_of_account_ids(pre_states: &[AccountWithMetadata]) -> bool {
|
||||
let number_of_accounts = pre_states.len();
|
||||
let number_of_account_ids = pre_states
|
||||
.iter()
|
||||
.map(|account| account.account_id.clone())
|
||||
.collect::<HashSet<_>>()
|
||||
.len();
|
||||
if private_nonces_iter.next().is_some() {
|
||||
panic!("Too many nonces.");
|
||||
}
|
||||
|
||||
number_of_accounts == number_of_account_ids
|
||||
if private_keys_iter.next().is_some() {
|
||||
panic!("Too many private account keys.");
|
||||
}
|
||||
|
||||
if private_auth_iter.next().is_some() {
|
||||
panic!("Too many private account authentication keys.");
|
||||
}
|
||||
|
||||
let output = PrivacyPreservingCircuitOutput {
|
||||
public_pre_states,
|
||||
public_post_states,
|
||||
ciphertexts,
|
||||
new_commitments,
|
||||
new_nullifiers,
|
||||
};
|
||||
|
||||
env::commit(&output);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user