Skip to content

Commit

Permalink
Improve the CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
xevisalle authored Nov 16, 2023
1 parent bffb209 commit 82b9a27
Show file tree
Hide file tree
Showing 59 changed files with 224 additions and 409 deletions.
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 specifying the testing Rusk node address in `integration-tests/tests/config/config.toml`, and executing the following (note that the wallet needs to use `password` as password for the tests to succeed):

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 onchain to LPs, and being able to use the licenses onchain as well.

### 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"
2 changes: 1 addition & 1 deletion integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ moat-core={ path = "../moat-core" }
license-provider = { path = "../license-provider" }

[dev-dependencies]
dusk-wallet = "0.20.0-rc.0"
dusk-wallet = "0.20.1-rc.0"
zk-citadel = "0.5"
phoenix-core = { version = "0.21", features = ["alloc"] }
poseidon-merkle = { version = "0.3", features = ["rkyv-impl"] }
Expand Down
6 changes: 4 additions & 2 deletions integration-tests/tests/blockchain/stake_add_owner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ async fn stake_add_owner() -> Result<(), Error> {
let blockchain_config =
BlockchainAccessConfig::load_path(blockchain_config_path)?;

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

let request_json: RequestJson = RequestJson::from_file(request_path)?;

Expand Down
24 changes: 16 additions & 8 deletions integration-tests/tests/citadel/int_test_lp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ use wallet_accessor::BlockchainAccessConfig;
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");
let lp_config_path = concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/config/test_keys_lp.json"
);

let blockchain_config =
BlockchainAccessConfig::load_path(blockchain_config_path)?;
Expand All @@ -31,8 +33,10 @@ async fn lp_scan() -> Result<(), Error> {
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");
let lp_config_path = concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/config/test_keys_lp_2.json"
);

let blockchain_config =
BlockchainAccessConfig::load_path(blockchain_config_path)?;
Expand All @@ -50,10 +54,14 @@ async fn lp_scan_last_blocks() -> Result<(), Error> {
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");
let lp2_config_path =
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/lp2.json");
let lp1_config_path = concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/config/test_keys_lp.json"
);
let lp2_config_path = concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/config/test_keys_lp_2.json"
);

let blockchain_config =
BlockchainAccessConfig::load_path(blockchain_config_path)?;
Expand Down
12 changes: 8 additions & 4 deletions integration-tests/tests/citadel/int_test_user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,15 @@ async fn user_round_trip() -> Result<(), Error> {
let (prover, verifier) = Compiler::compile::<LicenseCircuit>(&pp, LABEL)
.expect("Compiling circuit should succeed");

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

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

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

Expand Down
12 changes: 8 additions & 4 deletions integration-tests/tests/citadel/issue_license.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,17 @@ const GAS_PRICE: u64 = 1;
#[tokio::test(flavor = "multi_thread")]
#[cfg_attr(not(feature = "int_tests"), ignore)]
async fn issue_license() -> Result<(), Error> {
let request_path =
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/request/request.json");
let request_path = concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/request/test_request.json"
);
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");
let lp_config_path = concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/config/test_keys_lp_2.json"
);

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

Expand Down
6 changes: 4 additions & 2 deletions integration-tests/tests/citadel/send_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ async fn send_request() -> Result<(), Error> {
tracing::subscriber::set_global_default(subscriber.finish())
.expect("Setting tracing default should work");

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

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"
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion license-provider/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2021"

[dependencies]
dusk-wallet = "0.20.0-rc.0"
dusk-wallet = "0.20.1-rc.0"
zk-citadel = "0.5"
moat-core={ path = "../moat-core" }
wallet-accessor = { path = "../wallet-accessor" }
Expand Down
18 changes: 8 additions & 10 deletions license-provider/src/reference_lp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,17 @@ 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_with_ssk_psk<S>(ssk_lp: S, psk_lp: S) -> Result<Self, Error>
where
S: AsRef<str>,
{
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(lp_config.ssk_lp)?;
let ssk_lp = SecretSpendKey::from_slice(ssk_bytes.as_slice())?;

Self::create_with_ssk(&ssk_lp)
}

pub fn create_with_ssk(ssk_lp: &SecretSpendKey) -> Result<Self, Error> {
let psk_lp = ssk_lp.public_spend_key();
let vk_lp = ssk_lp.view_key();
Ok(Self::new(psk_lp, ssk_lp, vk_lp))
Ok(Self::new(psk_lp, *ssk_lp, vk_lp))
}

/// scans the entire blockchain for the requests to process
Expand Down
4 changes: 0 additions & 4 deletions license-provider/tests/config/lp.json

This file was deleted.

5 changes: 5 additions & 0 deletions license-provider/tests/config/test_keys_lp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"psk_lp": "29c4336ef24e585f4506e32e269c5363a71f7dcd74586b210c56e569ad2644e832c785f102dd3c985c705008ec188be819bac85b65c9f70decb9adcf4a72cc43",
"ssk_lp": "530046f569d26740eaa703b7f88bfb82a31aae1cef96732609e4b8f1e59802039f5afb7c1d5576e51c71d5afe8f6e06977c68641fae11abd3ce0b2196d1f3608"
}

Loading

0 comments on commit 82b9a27

Please sign in to comment.