Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI improvements #40

Merged
merged 8 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[workspace]
members = ["moat-cli", "moat-cli-user", "moat-cli-lp", "moat-cli-sp", "moat-cli-request", "moat-core", "wallet-accessor", "integration-tests", "license-provider", "macros/code-hasher"]
members = ["testing-utils/test-moat-cli", "moat-cli-user", "moat-cli-lp", "moat-cli-sp", "testing-utils/test-moat-request", "moat-core", "wallet-accessor", "integration-tests", "license-provider", "macros/code-hasher"]
201 changes: 30 additions & 171 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,182 +1,41 @@
# dusk moat
# Moat: The Citadel SDK

[![Repository](https://img.shields.io/badge/github-moat-blueviolet?logo=github)](https://github.com/dusk-network/moat)
![Build Status](https://github.com/dusk-network/moat/workflows/dusk_ci/badge.svg)
[![Documentation](https://img.shields.io/badge/docs-moat-blue?logo=rust)](https://docs.rs/moat/)

`moat` is a Rust workspace containing the following crates:
**Moat** (a.k.a. the Citadel SDK) contains all the required tools for using and implementing self-sovereign identity systems using the Citadel protocol integrated into the Blockchain of Dusk.

- `moat-cli`: Command line interface (CLI) for submitting license requests to the Dusk blockchain.
- `moat-core`: Library providing an SDK required for implementing Citadel scenarios with the Dusk blockchain.
- `license-provider`: Reference implementation of a license provider.
- `integration-tests`: Moat library integration tests (test which require access to Dusk Wallet and live Dusk cluster).
- `wallet-accessor`: Library for submitting contract-call transactions to the Dusk blockchain.
## Prerequisites

**Moat** requires a reachable Rusk node installed and running, or selecting a trusted one. You can set up a node as explained [here](https://wiki.dusk.network/en/setting-up-node). It also requires an installed wallet connected to the given Rusk node, as explained [here](https://github.com/dusk-network/wallet-cli/blob/main/src/bin/README.md). Then you should specify the Rusk node address in `config.toml`.

## moat-cli
## Testing the environment

moat-cli utility can be used to submit license request to the Dusk blockchain.
Any license provider can then scan the blockchain for requests, filter out relevant requests and process them accordingly.
You can test if the environment you set up and the library are working properly by executing the following:

Example usage of the moat-cli utility:
```sh
cargo r --release --bin moat-cli -- --wallet-path ~/.dusk/rusk-wallet --config-path ./moat-cli/config.toml --password password ./moat-cli/request.json
```
where:
- `wallet-path`: path to your installed wallet directory
- `config-path`: path to your blockchain access configuration (refer to the configuration section)
- `password`: password for your wallet
- `...request.json`: path to your request json file (as explained in the request section)

Request sending can also be performed programmatically, using moat-core, described below:
```rust
//...
let request_json: RequestJson = RequestJson::from_file(json_path)?;
let rng = &mut StdRng::seed_from_u64(0xcafe);
let request = RequestCreator::create_from_hex_args(
request_json.user_ssk,
request_json.provider_psk,
rng,
)?;
//...
PayloadSender::send_to_contract_method(
request,
&blockchain_access_config,
&wallet_path,
&psw,
gas_limit,
gas_price,
LICENSE_CONTRACT_ID,
NOOP_METHOD_NAME,
)
.await?;
//...
cargo t --release --features="exp_tests" -- --test-threads=1
cargo t --release --features="int_tests" -- --test-threads=1
```
In the above example, the user is expected to have a request json file available
at some filesystem path. The code loads contents of the file, extracts request arguments from it and
instantiates a request object. Subsequently, request is being sent to blockchain via
a provided wallet (utilizing wallet path and password, as well as a blockchain access configuration file).

As the `moat-cli` requires a path to a json request file. An example request json file looks as follows:
```json
{
"user_ssk": "c6afd78c8b3902b474d4c0972b62888e4b880dccf8da68e86266fefa45ee7505926f06ab82ac200995f1239d518fdb74903f225f4460d8db62f2449f6d4dc402",
"provider_psk": "29c4336ef24e585f4506e32e269c5363a71f7dcd74586b210c56e569ad2644e832c785f102dd3c985c705008ec188be819bac85b65c9f70decb9adcf4a72cc43"
}

## Usage

The moat-cli utility can be used from the POV of any of the parties involved in the Citadel protocol, let them be:
- **License Provider (LP):** A party receiving onchain requests from users to issue licenses onchain addressed to them.
- **User:** A party requesting licenses to LPs.

### License Provider

LPs can then scan the Blockchain for requests and issue licenses if the requests are valid. To run the LP CLI, simply run:

```sh
cargo r --release --bin moat-cli-lp -- --wallet-path ~/.dusk/rusk-wallet --wallet-pass <PASSWORD>
```
It contains the user secret spend key (user_ssk) and provider public spend key (provider_psk), both in a form of a hexadecimal string.

## moat-core
Provides an SDK for writing user, LP, and SP applications based on the Dusk blockchain.
It provides the following functionality, related to license contract:
- creating requests
- sending license requests
- scanning blockchain for requests
- performing license contract queries: _get_licenses, get_merkle_opening, get_session, get_info_

In addition, it provides the following generic Dusk blockchain functionality, not necessarily related to license contract:
- sending payloads to any method of any contract (e.g., can be used for _issue_license_ and _use_license_)
- retrieving payloads of any type from the blockchain
- performing queries on any method of any contract (with return values passed directly or via a feeder/stream)
- retrieving transactions from blockchain (e.g., by block range)

In addition, websocket functionality for the queries is also provided.

## license-provider
Provides functionality needed for implementors of license provider, including:
- license issuer
- blockchain scanner for relevant request
The crate allows for implementation of a license provider, whose task is to periodically check for license requests in the blockchain, and the to process the request and issue licenses.

## integration-tests
As most of the functionality provided by Moat deals with a blockchain, integration tests play critical role.
As in the case of moat-core functionality, tests include both Citadel-specific tests and blockchain generic test.

## wallet accessor
This is a low-level crate which provides wallet (Blockchain) connectivity for functions of moat-core.
Users of moat-core do not need to be aware of this crate, yet for maintainers and extenders, the crate
provides a convenient low level interface between the higher-level moat-core library and the blockchain.
Note that this crate deals only with contract method calling, it does not deal with contract queries.

## moat-core

### citadel requests

Class: RequestCreator
Methods:
create,
create_from_hex_args
Both methods allow for creation of a request, given user's secret spend key and license provider's public spend key.
The request can then be sent to license provider, off-chain or on-chain.

Class: RequestSender
Methods: send_request
Submits the request into blockchain.
It does so by calling a dummy contract method with request as an argument.

Class: RequestScanner
Methods:
scan_transactions,
scan_last_blocks,
scan_block_range
Scan requests in a given collection of transactions,
contained in a given range of blocks or in a given number of most recent blocks.

### citadel queries

Class: CitadelInquirer
Methods:
get_licenses,
get_merkle_opening,
get_session,
get_info
Execute citadel-specific query methods of the license contract method.

### blockchain payloads

Class: PayloadExtractor
Methods: payload_from_tx
Extracts a payload from the given transaction,
errors if payload of a given type is not present or the transaction is not a contract calling transaction.

Class: PayloadRetriever
Methods: retrieve_payload
Retrieves payload of a given transaction id,
errors if transaction is not found, or it does not contain a payload
(for example, given transaction is not a contract calling transaction)

Class: PayloadSender
Methods: execute_contract_method
Executes given method of a given contract (identified by a contract id), passing to it the payload as an argument.

### contract queries

Class: ContractInquirer
Methods:
query_contract,
query_contract_with_feeder
query_contract - accepts a generic argument, contract id and contract query method name, returns a generic value result
query_contract_with_feeder - accepts a generic argument, contract id and method name, returns result as a Stream of bytes

### blockchain queries

Class: BcInquirer
Methods:
gql_query,
block_height
gql_query - executes a GQL query and returns result as a vector of bytes
block_height - returns the current block height as u64

Class: TxAwaiter
Methods:
wait_for,
wait_for_tx
Waits for a transaction identified by transaction id to be confirmed on the blockchain.

Class: TxInquirer
Methods:
txs_from_block,
txs_from_block_range,
txs_from_last_n_blocks,
retrieve_tx
Retrieve transaction identified by transaction id, or transactions contained in a given block, or a collection of blocks.

### User

Users can request licenses and use them. To run the user CLI, simply run:

```sh
cargo r --release --bin moat-cli-user -- --wallet-path ~/.dusk/rusk-wallet --wallet-pass <PASSWORD>
```

2 changes: 2 additions & 0 deletions config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
rusk_address = "http://127.0.0.1:8080"
prover_address = "http://127.0.0.1:8080"
1 change: 1 addition & 0 deletions data/secret_key_lp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fd611dc2cfe15488e3cb94b410fadd3a5e77057be64574eb9b6acaf967a37d0514d0ce88727a24d3756a08bb8ae072d8aaaa88f88768c8a9487fb50678ba5204
1 change: 1 addition & 0 deletions data/secret_key_user
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
c6afd78c8b3902b474d4c0972b62888e4b880dccf8da68e86266fefa45ee7505926f06ab82ac200995f1239d518fdb74903f225f4460d8db62f2449f6d4dc402
2 changes: 1 addition & 1 deletion integration-tests/tests/blockchain/retrieve_txs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ async fn retrieve_tx_by_id() -> Result<(), Error> {
const TXID: &str =
"44fe2c6407fc400a2dee6e30c62a02b82f3980da18d3b6306e80f9f83730520d";

let config_path =
let config_path =
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/config.toml");

let config = BlockchainAccessConfig::load_path(config_path)?;
Expand Down
8 changes: 4 additions & 4 deletions integration-tests/tests/citadel/int_test_lp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ async fn lp_scan() -> Result<(), Error> {
let blockchain_config_path =
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/config.toml");
let lp_config_path =
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/lp.json");
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/test_secret_key_lp");

let blockchain_config =
BlockchainAccessConfig::load_path(blockchain_config_path)?;
Expand All @@ -32,7 +32,7 @@ async fn lp_scan_last_blocks() -> Result<(), Error> {
let blockchain_config_path =
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/config.toml");
let lp_config_path =
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/lp2.json");
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/test_secret_key_lp_2");

let blockchain_config =
BlockchainAccessConfig::load_path(blockchain_config_path)?;
Expand All @@ -51,9 +51,9 @@ async fn lp_scan_2_lps() -> Result<(), Error> {
let blockchain_config_path =
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/config.toml");
let lp1_config_path =
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/lp.json");
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/test_secret_key_lp");
let lp2_config_path =
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/lp2.json");
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/test_secret_key_lp_2");

let blockchain_config =
BlockchainAccessConfig::load_path(blockchain_config_path)?;
Expand Down
2 changes: 1 addition & 1 deletion integration-tests/tests/citadel/int_test_user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ async fn user_round_trip() -> Result<(), Error> {
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/request/request.json");

let lp_config_path =
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/lp2.json");
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/test_secret_key_lp_2");

let reference_lp = ReferenceLP::create(&lp_config_path)?;

Expand Down
4 changes: 2 additions & 2 deletions integration-tests/tests/citadel/issue_license.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ const GAS_PRICE: u64 = 1;
async fn issue_license() -> Result<(), Error> {
let request_path =
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/request/request.json");
let blockchain_config_path =
let blockchain_config_path =
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/config.toml");

let lp_config_path =
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/lp2.json");
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/test_secret_key_lp_2");

let reference_lp = ReferenceLP::create(&lp_config_path)?;

Expand Down
2 changes: 1 addition & 1 deletion integration-tests/tests/citadel/retrieve_requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ async fn retrieve_payload() -> Result<(), Error> {
const TXID: &str =
"8d45a9fb7196f322282d522ff4bb2d2e926ddd96b858b91d59f228b27250ef03";

let config_path =
let config_path =
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/config.toml");

let config = BlockchainAccessConfig::load_path(config_path)?;
Expand Down
2 changes: 1 addition & 1 deletion integration-tests/tests/citadel/send_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ async fn send_request() -> Result<(), Error> {

let request_path =
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/request/request.json");
let config_path =
let config_path =
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/config.toml");

let request_json: RequestJson = RequestJson::from_file(request_path)?;
Expand Down
2 changes: 0 additions & 2 deletions integration-tests/tests/config/config.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
rusk_address = "http://127.0.0.1:8080"
prover_address = "http://127.0.0.1:8080"
#rusk_address = "http://nodes.dusk.network:8585"
#prover_address = "http://provers.dusk.network:8686"
4 changes: 0 additions & 4 deletions integration-tests/tests/config/lp.json

This file was deleted.

4 changes: 0 additions & 4 deletions integration-tests/tests/config/lp2.json

This file was deleted.

1 change: 1 addition & 0 deletions integration-tests/tests/config/test_secret_key_lp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
530046f569d26740eaa703b7f88bfb82a31aae1cef96732609e4b8f1e59802039f5afb7c1d5576e51c71d5afe8f6e06977c68641fae11abd3ce0b2196d1f3608
1 change: 1 addition & 0 deletions integration-tests/tests/config/test_secret_key_lp_2
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fd611dc2cfe15488e3cb94b410fadd3a5e77057be64574eb9b6acaf967a37d0514d0ce88727a24d3756a08bb8ae072d8aaaa88f88768c8a9487fb50678ba5204
16 changes: 7 additions & 9 deletions license-provider/src/reference_lp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use std::collections::BTreeSet;
use std::path::Path;
use wallet_accessor::BlockchainAccessConfig;
use zk_citadel::license::Request;
use std::fs;

#[derive(Debug, Default, serde::Deserialize, serde::Serialize)]
pub struct LPConfig {
Expand Down Expand Up @@ -47,19 +48,16 @@ impl ReferenceLP {
}
}

pub fn create<P: AsRef<Path>>(lp_config_path: P) -> Result<Self, Error> {
let lp_config: LPConfig = LPConfig::from_file(lp_config_path)?;
Self::create_with_ssk_psk(lp_config.ssk_lp, lp_config.psk_lp)
pub fn create<P: AsRef<Path>>(ssk_path: P) -> Result<Self, Error> {
let ssk_bytes = fs::read(ssk_path).expect("Unable to read file");
Self::create_with_ssk(&ssk_bytes)
}

pub fn create_with_ssk_psk<S>(ssk_lp: S, psk_lp: S) -> Result<Self, Error>
where
S: AsRef<str>,
pub fn create_with_ssk(ssk_lp: &Vec<u8>) -> Result<Self, Error>
xevisalle marked this conversation as resolved.
Show resolved Hide resolved
{
let psk_bytes = hex::decode(psk_lp.as_ref())?;
let ssk_bytes = hex::decode(ssk_lp.as_ref())?;
let psk_lp = PublicSpendKey::from_slice(psk_bytes.as_slice())?;
let ssk_bytes = hex::decode(ssk_lp)?;
let ssk_lp = SecretSpendKey::from_slice(ssk_bytes.as_slice())?;
let psk_lp = ssk_lp.public_spend_key();
let vk_lp = ssk_lp.view_key();
Ok(Self::new(psk_lp, ssk_lp, vk_lp))
}
Expand Down
4 changes: 0 additions & 4 deletions license-provider/tests/config/lp.json

This file was deleted.

1 change: 1 addition & 0 deletions license-provider/tests/config/test_secret_key_lp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
530046f569d26740eaa703b7f88bfb82a31aae1cef96732609e4b8f1e59802039f5afb7c1d5576e51c71d5afe8f6e06977c68641fae11abd3ce0b2196d1f3608
xevisalle marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion license-provider/tests/test_lp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use moat_core::{Error, JsonLoader, RequestScanner, Transactions};
#[test]
fn lp_filter_requests() -> Result<(), Error> {
let lp_config_path =
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/lp.json");
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/test_secret_key_lp");
let reference_lp = ReferenceLP::create(&lp_config_path)?;

let txs_path =
Expand Down
6 changes: 0 additions & 6 deletions moat-cli-lp/config.toml

This file was deleted.

4 changes: 2 additions & 2 deletions moat-cli-lp/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ pub struct Args {
pub wallet_path: PathBuf,

/// Blockchain access config directory
#[clap(short, long)]
#[clap(short, long, default_value = "./config.toml")]
pub config_path: PathBuf,

/// Password for the wallet
#[clap(long, default_value_t = String::from(""), env = "RUSK_WALLET_PWD")]
pub password: String,
pub wallet_pass: String,

/// Hash of the password for the wallet [default: ``]
#[clap(short, long, default_value_t = String::from(""))]
Expand Down
Loading