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

Add Rust examples #22

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 7 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
5,668 changes: 0 additions & 5,668 deletions Rust/Cargo.lock

This file was deleted.

22 changes: 6 additions & 16 deletions Rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[package]
name = "iroha_2_examples"
name = "iroha_examples"
version = "0.1.0"
edition = "2021"

authors = ["Iroha 2 team <https://github.com/orgs/soramitsu/teams/iroha2>"]
description = "Example repository for Iroha 2 ledger"
description = "Hyperledger Iroha 2 code examples"
repository = "https://github.com/hyperledger/iroha-2-examples"
documentation = "https://hyperledger.github.io/iroha-2-docs"
homepage = "https://iroha.tech"
Expand All @@ -14,17 +14,7 @@ keywords = ["blockchain", "crypto", "iroha", "ledger"]
categories = ["cryptography::cryptocurrencies"]

[dependencies]
iroha = { "git" = "https://github.com/hyperledger/iroha.git", branch = "iroha2-dev" }
iroha_crypto = { "git" = "https://github.com/hyperledger/iroha.git", branch = "iroha2-dev" }
iroha_client = { "git" = "https://github.com/hyperledger/iroha.git", branch = "iroha2-dev" }
iroha_config = { "git" = "https://github.com/hyperledger/iroha.git", branch = "iroha2-dev" }
iroha_data_model = { "git" = "https://github.com/hyperledger/iroha.git", branch = "iroha2-dev" }
iroha_genesis = { "git" = "https://github.com/hyperledger/iroha.git", branch = "iroha2-dev" }
test_network = { "git" = "https://github.com/hyperledger/iroha.git", branch = "iroha2-dev" }

eyre = "0.6.8"

serde = { version = "1.0.151", default-features = false }
serde_json = { version = "1.0.91", default-features = false }

tokio = "1.23.0"
iroha = { git = "https://github.com/hyperledger/iroha.git" }
iroha_executor_data_model = { git = "https://github.com/hyperledger/iroha.git" }
dima74 marked this conversation as resolved.
Show resolved Hide resolved
eyre = "0.6.12"
serde_json = "1.0.128"
25 changes: 11 additions & 14 deletions Rust/README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
# Rust examples for Iroha 2
# Rust examples for Hyperledger Iroha

This directory contains the examples from the [Rust tutorial](https://hyperledger.github.io/iroha-2-docs/guide/rust.html#_2-configuring-iroha-2).
This project contains code examples to help you get started using the Rust SDK for Hyperledger Iroha, as well as understand Iroha conceptually.

## Running the examples

To run the examples, you need to install [`cargo-nextest`](https://nexte.st/) first.
To run the examples, you should have a running Iroha network with the default genesis block configuration. Some examples depend on other examples being executed. Examples without dependencies are good starting points:

```bash
cargo install cargo-nextest
```
- [`domain_register`](examples/domain_register.rs)

After it is installed, type:
## Helper library

```bash
cargo nextest run
```
A small helper library is included to facilitate defining actors and props involved in the examples while avoiding repetition of the parsing logic, etc.

You'll Cargo install the packages that are needed for the tests and the test code will run.
### Usage

## Extending the example set

Simply add a file with Rust code to the [`examples`](./examples/) directory. It will be launched by `cargo-nextest` on its next run.
* Define primitives like domains (`Wonderland`, `Chess`), signatories (`Alice`, `Bob`, `Magnus`), assets (`Money`, `Roses`, `Clothes`), and more using traits like `ExampleDomain`, `ExampleSignatory` `ExampleAssetName` and others.
* Combine primitives into compound types for accounts (`AliceInWonderland`, `BobInChess`), asset definitions (`WonderlandRoses`, `ChessClothes`) and more using `ExampleAccount`, `ExampleAssetDefinition`, etc.
* Easily construct identifiers (`BobInWonderland::account_id()`, `ClothesOfBobInChess::asset_id()`) as needed.
* Construct clients acting on behalf of various accounts using `AliceInWonderland::client()`, `BobInChess::client()`.
13 changes: 13 additions & 0 deletions Rust/configs/alice_chess.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
chain = "00000000-0000-0000-0000-000000000000"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we emphasize somewhere in the docs that this value must be generated as UUID (and why). This here is just a dummy value

torii_url = "http://127.0.0.1:8080/"

[basic_auth]
web_login = "mad_hatter"
password = "ilovetea"

[account]
# Domain in which Alice has an authority
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does "has an authority" mean here? I'd rephrase

domain = "chess"
# Alice's key pair
public_key = "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03"
private_key = "802620CCF31D85E3B32A4BEA59987CE0C78E3B8E2DB93881468AB2435FE45D5C9DCD53"
13 changes: 13 additions & 0 deletions Rust/configs/alice_wonderland.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
chain = "00000000-0000-0000-0000-000000000000"
torii_url = "http://127.0.0.1:8080/"

[basic_auth]
web_login = "mad_hatter"
password = "ilovetea"

[account]
# Domain in which Alice has an authority
domain = "wonderland"
# Alice's key pair
public_key = "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03"
private_key = "802620CCF31D85E3B32A4BEA59987CE0C78E3B8E2DB93881468AB2435FE45D5C9DCD53"
13 changes: 13 additions & 0 deletions Rust/configs/bob_chess.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
chain = "00000000-0000-0000-0000-000000000000"
torii_url = "http://127.0.0.1:8080/"

[basic_auth]
web_login = "mad_hatter"
password = "ilovetea"

[account]
# Domain in which Bob has an authority
domain = "chess"
# Bob's key pair
public_key = "ed012004FF5B81046DDCCF19E2E451C45DFB6F53759D4EB30FA2EFA807284D1CC33016"
private_key = "802620AF3F96DEEF44348FEB516C057558972CEC4C75C4DB9C5B3AAC843668854BF828"
13 changes: 13 additions & 0 deletions Rust/configs/bob_wonderland.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
chain = "00000000-0000-0000-0000-000000000000"
torii_url = "http://127.0.0.1:8080/"

[basic_auth]
web_login = "mad_hatter"
password = "ilovetea"

[account]
# Domain in which Bob has an authority
domain = "wonderland"
# Bob's key pair
public_key = "ed012004FF5B81046DDCCF19E2E451C45DFB6F53759D4EB30FA2EFA807284D1CC33016"
private_key = "802620AF3F96DEEF44348FEB516C057558972CEC4C75C4DB9C5B3AAC843668854BF828"
56 changes: 56 additions & 0 deletions Rust/examples/account_register.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//! Shows how to register an account.
//!
//! Depends on the `domain_register` example.

use iroha::client::Client;
use iroha::data_model::prelude::{
Account, AccountId, FindAccounts, Grant, QueryBuilderExt, Register, Revoke,
};
use iroha_examples::{
AliceInChess, AliceInWonderland, BobInChess, BobInWonderland, Chess, ExampleDomain,
MagnusInChess,
};
use iroha_executor_data_model::permission::domain::CanRegisterAccountInDomain;

fn main() -> iroha_examples::Result<()> {
// An account is created for a signatory in a domain.
// By default, only the owner of the domain can register accounts in it.
let as_alice_in_wland = AliceInWonderland::client();
// The same signatory can have an account in different domains.
register(&as_alice_in_wland, AliceInChess::id())?;

// The domain owner can also grant a permission to register accounts in the domain.
let can_register_accounts_in_chess = CanRegisterAccountInDomain {
dima74 marked this conversation as resolved.
Show resolved Hide resolved
domain: Chess::id(),
};
// Grant the permission to Bob from Wonderland.
let bob_in_wland = BobInWonderland::id();
as_alice_in_wland.submit_blocking(Grant::account_permission(
can_register_accounts_in_chess.clone(),
bob_in_wland.clone(),
))?;
// Bob in Wonderland can now register accounts in Chess.
let as_bob_in_wland = BobInWonderland::client();
register(&as_bob_in_wland, BobInChess::id())?;
register(&as_bob_in_wland, MagnusInChess::id())?;
// Revoke the permission from Bob in Wonderland.
as_alice_in_wland.submit_blocking(Revoke::account_permission(
can_register_accounts_in_chess,
bob_in_wland,
))?;
Ok(())
}

fn register(as_who: &Client, account: AccountId) -> iroha_examples::Result<()> {
let register_account = Register::account(Account::new(account.clone()));
as_who.submit_blocking(register_account)?;
// Observe that the account has really been registered.
let account = as_who
.query(FindAccounts)
.filter_with(|acc| acc.id.eq(account))
.execute_single()?;
println!("Account: {}\nRegistered by: {}", account.id, as_who.account);
// Account: ed12...41@wonderland
// Registered by: ed01...12@wonderland
Ok(())
}
35 changes: 35 additions & 0 deletions Rust/examples/account_unregister.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//! Shows how to unregister an account.
//!
//! Depends on the `account_register` example.

use iroha::client::Client;
use iroha::data_model::account::AccountId;
use iroha::data_model::prelude::{FindAccounts, QueryBuilderExt, Unregister};

use iroha_examples::{AliceInChess, AliceInWonderland, BobInChess, MagnusInChess};

fn main() -> iroha_examples::Result<()> {
// An account's owner can unregister that account.
let as_bob_in_chess = BobInChess::client();
unregister(&as_bob_in_chess, BobInChess::id())?;
// A domain owner can unregister any account in that domain.
let as_alice_in_wland = AliceInWonderland::client();
unregister(&as_alice_in_wland, AliceInChess::id())?;
unregister(&as_alice_in_wland, MagnusInChess::id())?;
Ok(())
}

fn unregister(as_who: &Client, account: AccountId) -> iroha_examples::Result<()> {
let unregister_account = Unregister::account(account.clone());
as_who.submit_blocking(unregister_account)?;
// Observe that the account has really been unregistered.
as_who
.query(FindAccounts)
.filter_with(|acc| acc.id.eq(account.clone()))
.execute_single()
.expect_err("account should not be found");
println!("Account: {}\nUnregistered by: {}", account, as_who.account);
dima74 marked this conversation as resolved.
Show resolved Hide resolved
// Account: ed12...41@wonderland
// Unregistered by: ed01...12@wonderland
Ok(())
}
95 changes: 95 additions & 0 deletions Rust/examples/asset_definition_register.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//! Shows how to register asset definitions.
//!
//! Depends on `account_register`.

use iroha::client::Client;
use iroha::data_model::asset::{AssetDefinition, AssetType};
use iroha::data_model::ipfs::IpfsPath;
use iroha::data_model::isi::Grant;
use iroha::data_model::prelude::{
FindAssetsDefinitions, Metadata, NewAssetDefinition, NumericSpec, QueryBuilderExt, Register,
Revoke,
};
use iroha_examples::{
AliceInWonderland, BobInChess, Chess, ChessBook, ChessPawns, ExampleDomain, WonderlandMoney,
WonderlandRoses,
};
use iroha_executor_data_model::permission::domain::CanRegisterAssetDefinitionInDomain;

fn main() -> iroha_examples::Result<()> {
let as_alice_in_wland = AliceInWonderland::client();
// `rose#wonderland` are defined in the default genesis block.
println!(
"Wonderland Roses:\n{:#?}",
as_alice_in_wland
.query(FindAssetsDefinitions)
.filter_with(|asset_def| asset_def.id.eq(WonderlandRoses::id()))
.execute_single()?
);
// Assets can be defined as either numeric or store.
// Numeric assets can be minted (increased) or burned (decreased).
// `money#wonderland` is a numeric asset with fractional values up to 2 decimal places.
register(
&as_alice_in_wland,
AssetDefinition::new(
WonderlandMoney::id(),
AssetType::Numeric(NumericSpec::fractional(2)),
),
)?;
// Since `bob@chess` is not the owner of `chess`, `alice@wonderland`
// has to grant `bob@chess` permission to define assets in `chess`.
let bob_in_chess = BobInChess::id();
let can_define_assets_in_chess = CanRegisterAssetDefinitionInDomain {
domain: Chess::id(),
};
// Grant the permission to `bob@chess`.
as_alice_in_wland.submit_blocking(Grant::account_permission(
can_define_assets_in_chess.clone(),
bob_in_chess.clone(),
))?;
// `pawn#chess` is a numeric asset with integer values that can only be minted once,
// meaning that the asset has a globally fixed supply.
//
// `bob@chess` will be the owner of the definition of `pawn#chess`,
// meaning he will have the default right to mint/burn `pawn#chess`.
// Since `alice@wonderland` owns `chess`, she will also have that right.
register(
&BobInChess::client(),
AssetDefinition::new(ChessPawns::id(), AssetType::Numeric(NumericSpec::integer()))
.mintable_once(),
)?;
// Revoke the permission.
as_alice_in_wland.submit_blocking(Revoke::account_permission(
can_define_assets_in_chess,
bob_in_chess,
))?;
// `book#chess` is a store asset. Store assets are not minted or burned.
// Instead, key-value pairs are set or removed for them.
//
// Here we also provide an optional IPFS path to the asset logo,
// and some metadata. Metadata is covered in detail in TODO(`metadata`)
register(
&as_alice_in_wland,
AssetDefinition::store(ChessBook::id())
.with_logo("QmQqzMTavQgT4f4T5v6PWBp7XNKtoPmC9jvn12WPT3gkSE".parse::<IpfsPath>()?)
.with_metadata(Metadata::default()),
)?;
Ok(())
}

fn register(as_who: &Client, asset_definition: NewAssetDefinition) -> iroha_examples::Result<()> {
let asset_definition_id = asset_definition.id.clone();
let define_asset = Register::asset_definition(asset_definition);
as_who.submit_blocking(define_asset)?;
let asset_definition = as_who
.query(FindAssetsDefinitions)
.filter_with(|asset_def| asset_def.id.eq(asset_definition_id))
.execute_single()?;
println!(
"Asset definition: {}\nRegistered by: {}",
asset_definition.id, as_who.account
);
// Asset definition: pawn#chess
// Registered by: ed01...12@wonderland
Ok(())
}
57 changes: 57 additions & 0 deletions Rust/examples/asset_definition_unregister.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//! Shows how to unregister asset definitions.
//!
//! Depends on `asset_definition_register`.

use iroha::client::Client;
use iroha::data_model::isi::Grant;
use iroha::data_model::prelude::{
AssetDefinitionId, FindAssetsDefinitions, QueryBuilderExt, Revoke, Unregister,
};
use iroha_executor_data_model::permission::asset_definition::CanUnregisterAssetDefinition;

use iroha_examples::{AliceInWonderland, BobInChess, ChessBook, ChessPawns, WonderlandMoney};

fn main() -> iroha_examples::Result<()> {
let as_alice_in_wland = AliceInWonderland::client();
// `alice@wonderland` owns the definition of `book#chess`
unregister(&as_alice_in_wland, ChessBook::id())?;
let as_bob_in_chess = BobInChess::client();
// `bob@chess` owns the definition of `pawn#chess`
unregister(&as_bob_in_chess, ChessPawns::id())?;
// Since `bob@chess` is not the owner of `money#wonderland`, `alice@wonderland`
// has to grant `bob@chess` permission to unregister its definition.
let bob_in_chess = BobInChess::id();
let wonderland_money = WonderlandMoney::id();
let can_undefine_wonderland_money = CanUnregisterAssetDefinition {
asset_definition: wonderland_money.clone(),
};
// Grant the permission to `bob@chess`.
as_alice_in_wland.submit_blocking(Grant::account_permission(
can_undefine_wonderland_money.clone(),
bob_in_chess.clone(),
))?;
unregister(&as_bob_in_chess, wonderland_money)?;
// Revoke the permission.
as_alice_in_wland.submit_blocking(Revoke::account_permission(
can_undefine_wonderland_money,
bob_in_chess,
))?;
Ok(())
}

fn unregister(as_who: &Client, asset_definition: AssetDefinitionId) -> iroha_examples::Result<()> {
let undefine_asset = Unregister::asset_definition(asset_definition.clone());
as_who.submit_blocking(undefine_asset)?;
as_who
.query(FindAssetsDefinitions)
.filter_with(|asset_def| asset_def.id.eq(asset_definition.clone()))
.execute_single()
.expect_err("asset definition should not be found");
println!(
"Asset definition: {}\nUnregistered by: {}",
asset_definition, as_who.account
);
// Asset definition: pawn#chess
// Unregistered by: ed01...12@wonderland
Ok(())
}
Loading