diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 3f0c7964a..56948d372 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -35,6 +35,379 @@ function nav(): DefaultTheme.NavItem[] { link: '/reference/torii-endpoints', activeMatch: '/reference/', }, + { + text: 'Cookbook', + link: '/cookbook/', + activeMatch: '/cookbook/', + }, + ] +} + +function sidebarCookbook(): DefaultTheme.SidebarItem[] { + return [ + { + text: 'Access Control', + collapsed: true, + items: [ + { + text: 'Permission Tokens', + collapsed: true, + items: [ + { + text: 'Grant Permissions', + link: '/cookbook/grant-permissions', + }, + { + text: 'Revoke Permissions', + link: '/cookbook/revoke-permissions', + }, + ], + }, + { + text: 'Roles', + collapsed: true, + items: [ + { + text: 'Register a Role', + link: '/cookbook/register-roles', + }, + { + text: 'Grant a Role', + link: '/cookbook/grant-roles', + }, + { + text: 'Revoke a Role', + link: '/cookbook/revoke-roles', + }, + ], + }, + ], + }, + { + text: 'Accounts', + collapsed: true, + items: [ + { + text: 'Register Accounts', + link: '/cookbook/register-accounts', + }, + { + text: 'Unregister Accounts', + link: '/cookbook/unregister-accounts', + }, + ], + }, + { + text: 'Asset Definitions', + collapsed: true, + items: [ + { + text: 'Register Asset Definitions', + link: '/cookbook/register-asset-definitions', + }, + { + text: 'Unregister Asset Definitions', + link: '/cookbook/unregister-asset-definitions', + }, + { + text: 'Transfer Asset Definitions', + link: '/cookbook/transfer-asset-definitions', + }, + ], + }, + { + text: 'Assets', + collapsed: true, + items: [ + { + text: 'Assets', + collapsed: true, + items: [ + { + text: 'Register Assets', + link: '/cookbook/register-assets', + }, + { + text: 'Unregister Assets', + link: '/cookbook/unregister-assets', + }, + { + text: 'Transfer Assets Between Accounts', + link: '/cookbook/transfer-assets', + }, + { + text: 'Transfer Groups of Assets', + link: '/cookbook/transfer-group-assets', + }, + ], + }, + { + text: 'Numeric Assets', + collapsed: true, + items: [ + { + text: 'Work with Numeric Assets', + link: '/cookbook/work-with-numeric-assets', + }, + ], + }, + { + text: 'Store Assets', + collapsed: true, + items: [ + { + text: 'Work with Store Assets', + link: '/cookbook/work-with-store-assets', + }, + ], + }, + { + text: 'Mintable Assets', + collapsed: true, + items: [ + { + text: 'Mint Assets', + link: '/cookbook/mint-assets', + }, + { + text: 'Mint More of a Mintable Asset', + link: '/cookbook/mint-more-assets', + }, + { + text: 'Burn Assets', + link: '/cookbook/burn-assets', + }, + ], + }, + { + text: 'Non-Mintable Assets', + collapsed: true, + items: [ + { + text: 'Work with Non-Mintable Assets', + link: '/cookbook/work-with-non-mintable-assets', + }, + ], + }, + { + text: 'Tokens', + collapsed: true, + items: [ + { + text: 'Create Asset-backed Tokens', + link: '/cookbook/create-asset-backed-tokens', + }, + { + text: 'Create Non-Fungible Tokens (NFTs)', + link: '/cookbook/create-nfts', + }, + ], + }, + ], + }, + { + text: 'Domains', + collapsed: true, + items: [ + { + text: 'Register Domains', + link: '/cookbook/register-domains', + }, + { + text: 'Unregister Domains', + link: '/cookbook/unregister-domains', + }, + { + text: 'Transfer Domain Owner', + link: '/cookbook/transfer-domain-owner', + }, + ], + }, + { + text: 'Events and Filters', // for this sections no files were created + collapsed: true, + items: [ + { + text: 'Pipeline Events', + collapsed: true, + items: [], + }, + { + text: 'Data Events', + collapsed: true, + items: [], + }, + { + text: 'Trigger Events', + collapsed: true, + items: [], + }, + { + text: 'Advanced Filtering', + collapsed: true, + items: [], + }, + { + text: 'Block Stream', + collapsed: true, + items: [ + { + text: 'Subscribe to Block Stream', + }, + { + text: 'View output', + }, + ], + }, + ], + }, + { + text: 'Executors', + collapsed: true, + items: [ + { + text: 'Write Executor', + link: '/cookbook/write-executor', + }, + { + text: 'Update Executor', + link: '/cookbook/update-executor', + }, + { + text: 'Define Custom Permission Tokens', + link: '/cookbook/define-custom-permission-tokens', + }, + ], + }, + { + text: 'Instructions', + collapsed: true, + items: [ + { + text: 'Use Instructions', + link: '/cookbook/use-instructions', + }, + { + text: 'Combine Instructions via Expressions', + link: '/cookbook/combine-instructions', + }, + ], + }, + { + text: 'Metadata', + collapsed: true, + items: [ + { + text: 'Set Key Value', + link: '/cookbook/set-key-value', + }, + { + text: 'Remove Key Value', + link: '/cookbook/remove-key-value', + }, + { + text: 'Access Metadata', + link: '/cookbook/access-metadata', + }, + ], + }, + { + text: 'Peers', + collapsed: true, + items: [ + { + text: 'Register Peers', + link: '/cookbook/register-peers', + }, + { + text: 'Unregister Peers', + link: '/cookbook/unregister-peers', + }, + { + text: "Check Peer's Load", + link: '/cookbook/check-peer-load', + }, + { + text: 'Find the Leader Among Running Peers', + link: '/cookbook/find-leader-among-running-peers', + }, + { + text: 'Query Connected Peers', + link: '/cookbook/query-connected-peers', + }, + ], + }, + { + text: 'Queries', + collapsed: true, + items: [{ text: 'Use queries' }, { text: 'Filter query results' }, { text: 'Use Sorting and Pagination' }], + }, + { + text: 'Telemetry', + collapsed: true, + items: [ + { + text: 'Check Status', + link: '/cookbook/check-status', + }, + { + text: 'Get Metrics', + link: '/cookbook/get-metrics', + }, + { + text: 'Monitor Iroha Performance', + link: '/cookbook/monitor-iroha-performance', + }, + { + text: 'Check Health', + link: '/cookbook/check-health', + }, + ], + }, + { + text: 'Transactions', + collapsed: true, + items: [ + { + text: 'Create Transactions', + link: '/cookbook/create-transactions', + }, + { + text: 'Submit Transactions', + link: '/cookbook/submit-transactions', + }, + { + text: 'Use Multi-Signature Transactions', + link: '/cookbook/use-multi-signature-transactions', + }, + ], + }, + { + text: 'Triggers', + collapsed: true, + items: [ + { + text: 'Register a Data Trigger', + link: '/cookbook/register-data-triggers', + }, + { + text: 'Register a Scheduled Trigger', + link: '/cookbook/register-scheduled-triggers', + }, + { + text: 'Register a Pre-commit Trigger', + link: '/cookbook/register-pre-commit-triggers', + }, + { + text: 'Register a By-call Trigger', + link: '/cookbook/register-by-call-triggers', + }, + { + text: 'Unregister a Trigger', + link: '/cookbook/unregister-triggers', + }, + ], + }, ] } @@ -405,7 +778,7 @@ export default defineConfig({ base: BASE, srcDir: 'src', srcExclude: ['snippets/*.md'], - title: 'Hyperledger Iroha 2 Tutorial', + title: 'Hyperledger Iroha 2 Documentation', description: 'Documentation for Hyperledger Iroha 2 offering step-by-step guides for SDKs and outlining the main differences between Iroha versions.', lang: 'en-US', @@ -481,6 +854,7 @@ export default defineConfig({ sidebar: { '/guide/': sidebarGuide(), '/reference/': sidebarAPI(), + '/cookbook/': sidebarCookbook(), }, search: { diff --git a/.vscode/settings.json b/.vscode/settings.json index d4a90eccc..a352328cc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { - "eslint.workingDirectories": [".vitepress", "."] + "eslint.workingDirectories": [".vitepress", "."], + "cSpell.words": ["Iroha"] } diff --git a/src/cookbook/access-metadata.md b/src/cookbook/access-metadata.md new file mode 100644 index 000000000..50fd15f65 --- /dev/null +++ b/src/cookbook/access-metadata.md @@ -0,0 +1,39 @@ +--- +title: "Access Metadata | Cookbook" +head: + - - meta + - name: description + content: "Learn how to access metadata of Iroha objects such as domains, accounts, assets, and so on." + - - meta + - name: keywords + content: "Iroha metadata" +--- + +# How to Access an Object's Metadata + +In the Iroha 2 almost all basic objects like Account, Asset, Domain, etc. +have a Metadata field, which is a struct containing a BTreeMap. +By default the basic objects are created with 0 metadata capacity so if you get empty result, +be sure that metadata has been added into the object. + +The current example describes how to get metadata from an Account. + +Precondition: The object has been created with a metadata. + +```rust +fn access_metadata( + iroha: &Client, +) { + //Define the target account and make a request to get this account's object + let account_id: AccountId = "alice@wonderland".parse().unwrap(); + let account: Account = iroha.request(FindAccountById::new(account_id)).unwrap(); + + //Bind metadata struct to a variable + let account_metadata = account.metadata(); + + //Iterate for the metadata in the account's object + for metadata in account_metadata.iter() { + println!("{:?}", metadata) + } +} +``` \ No newline at end of file diff --git a/src/cookbook/burn-assets.md b/src/cookbook/burn-assets.md new file mode 100644 index 000000000..3399c2578 --- /dev/null +++ b/src/cookbook/burn-assets.md @@ -0,0 +1,25 @@ +--- +title: "Burn Assets | Cookbook" +head: + - - meta + - name: description + content: "Learn how to burn assets." + - - meta + - name: keywords + content: "mintable assets, burning assets" +--- + +# How to Burn an Asset + +Only numeric assets can be burned. + +```rust +fn burn_numeric_asset(iroha: &Client) { + let roses = "rose#wonderland".parse::().unwrap(); + let alice = "alice@wonderland".parse::().unwrap(); + let roses_of_alice = AssetId::new(roses, alice); + let quantity = numeric!(42); + let burn_roses_of_alice = Burn::asset_numeric(quantity, roses_of_alice); + iroha.submit(burn_roses_of_alice).unwrap(); +} +``` \ No newline at end of file diff --git a/src/cookbook/check-health.md b/src/cookbook/check-health.md new file mode 100644 index 000000000..712dc1950 --- /dev/null +++ b/src/cookbook/check-health.md @@ -0,0 +1,14 @@ +--- +title: "Check Health | Cookbook" +head: + - - meta + - name: description + content: "Learn how to monitor Iroha performance by checking health." + - - meta + - name: keywords + content: "telemetry, health, Iroha performance" +--- + +# How to Check Health + +TODO \ No newline at end of file diff --git a/src/cookbook/check-peer-load.md b/src/cookbook/check-peer-load.md new file mode 100644 index 000000000..ecdfd4a05 --- /dev/null +++ b/src/cookbook/check-peer-load.md @@ -0,0 +1,14 @@ +--- +title: "Check Peer's Load | Cookbook" +head: + - - meta + - name: description + content: "Learn how to check an Iroha peer's load." + - - meta + - name: keywords + content: "peer load" +--- + +# How to Check a Peer's Load + +TODO \ No newline at end of file diff --git a/src/cookbook/check-status.md b/src/cookbook/check-status.md new file mode 100644 index 000000000..3ce8b7b80 --- /dev/null +++ b/src/cookbook/check-status.md @@ -0,0 +1,33 @@ +--- +title: "Check Status | Cookbook" +head: + - - meta + - name: description + content: "Learn how to check the Iroha status." + - - meta + - name: keywords + content: "telemetry, Iroha status" +--- + +# How to Check Iroha Status + +```rust +fn check_status(iroha: &Client) { + let status = iroha.get_status().unwrap(); + println!("{:#?}", status); +} +``` + +Sample output: + +``` +Status { + peers: 4, + blocks: 5, + txs_accepted: 31, + txs_rejected: 3, + uptime: Uptime(5.937s), + view_changes: 2, + queue_size: 18, +} +``` \ No newline at end of file diff --git a/src/cookbook/combine-instructions.md b/src/cookbook/combine-instructions.md new file mode 100644 index 000000000..d6de9a186 --- /dev/null +++ b/src/cookbook/combine-instructions.md @@ -0,0 +1,52 @@ +--- +title: "Combine Instructions | Cookbook" +head: + - - meta + - name: description + content: "Learn how to combine different Iroha Special Instructions (ISI) via expressions." + - - meta + - name: keywords + content: "Iroha Special Instructions, expressions" +--- + +# How to Combine Iroha Special Instructions + +In order to combine instructions of different types in one collection, +you need some sort of polymorphism. We provide this via enum wrappers +for each instruction type (e.g. `RegisterBox`, `MintBox`, and others), +as well as the most general `InstructionBox` type. + +Any instruction can be converted to the needed wrapper if compatible, +no allocations involved (despite what the `*Box` suffix might suggest). + +```rust +fn combine_isi(iroha: &Client) { + let alice = "alice@wonderland".parse::().unwrap(); + let register_alice = { + let (public_key, _private_key) = KeyPair::random().into_parts(); + Register::account(Account::new(alice.clone(), public_key)) + }; + let roses = "rose#wonderland".parse::().unwrap(); + let define_roses = Register::asset_definition( + AssetDefinition::numeric(roses.clone()) + ); + let roses_of_alice = AssetId::new(roses.clone(), alice.clone()); + let mint_roses_for_alice = Mint::asset_numeric( + numeric!(20), + roses_of_alice.clone() + ); + let mouse = "mouse@wonderland".parse::().unwrap(); + let transfer_roses_from_alice_to_mouse = Transfer::asset_numeric( + roses_of_alice, + numeric!(10), + mouse, + ); + let instructions: [InstructionBox; _] = [ + register_alice.into(), + define_roses.into(), + mint_roses_for_alice.into(), + transfer_roses_from_alice_to_mouse.into(), + ]; + iroha.submit_all(instructions).unwrap(); +} +``` \ No newline at end of file diff --git a/src/cookbook/create-asset-backed-tokens.md b/src/cookbook/create-asset-backed-tokens.md new file mode 100644 index 000000000..0d5e4af75 --- /dev/null +++ b/src/cookbook/create-asset-backed-tokens.md @@ -0,0 +1,14 @@ +--- +title: "Create Asset-backed Tokens | Cookbook" +head: + - - meta + - name: description + content: "Learn how to create asset-backed tokens in Iroha." + - - meta + - name: keywords + content: "asset-backed tokens" +--- + +# How to Create an Asset-backed Token + +TODO \ No newline at end of file diff --git a/src/cookbook/create-nfts.md b/src/cookbook/create-nfts.md new file mode 100644 index 000000000..7a5f1d52e --- /dev/null +++ b/src/cookbook/create-nfts.md @@ -0,0 +1,14 @@ +--- +title: "Create NFTs | Cookbook" +head: + - - meta + - name: description + content: "Learn how to create non-fungible tokens in Iroha." + - - meta + - name: keywords + content: "non-fungible tokens, NFT" +--- + +# How to Create a Non-Fungible Token (NFT) + +TODO \ No newline at end of file diff --git a/src/cookbook/create-transactions.md b/src/cookbook/create-transactions.md new file mode 100644 index 000000000..d3bdf6c57 --- /dev/null +++ b/src/cookbook/create-transactions.md @@ -0,0 +1,48 @@ +--- +title: "Create Transactions | Cookbook" +head: + - - meta + - name: description + content: "Learn how to create transactions in Iroha." + - - meta + - name: keywords + content: "transactions" +--- + +# How to Create a Transaction + +```rust +fn create_transaction(iroha: &Client) -> SignedTransaction { + // Prepare the instructions you want to execute + let alice = "alice@wonderland".parse::().unwrap(); + let register_alice = { + let (public_key, _private_key) = KeyPair::random().into_parts(); + Register::account(Account::new(alice.clone(), public_key)) + }; + let roses = "rose#wonderland".parse::().unwrap(); + let define_roses = Register::asset_definition( + AssetDefinition::numeric(roses.clone()) + ); + let roses_of_alice = AssetId::new(roses.clone(), alice.clone()); + let register_roses_of_alice = Register::asset( + Asset::new(roses_of_alice, numeric!(100)) + ); + + // Combine the instructions + let instructions: [InstructionBox; _] = [ + register_alice.into(), + define_roses.into(), + register_roses_of_alice.into(), + ]; + + // Build a transaction with the prepared instructions and empty metadata + // on behalf of the current account configured with the client + iroha.build_transaction( + instructions, + UnlimitedMetadata::default() + ) +} +``` + +See [Submit Transactions](submit-transactions.md) to learn how to submit +the resulting `SignedTransaction`. \ No newline at end of file diff --git a/src/cookbook/define-custom-permission-tokens.md b/src/cookbook/define-custom-permission-tokens.md new file mode 100644 index 000000000..ffb3ef80a --- /dev/null +++ b/src/cookbook/define-custom-permission-tokens.md @@ -0,0 +1,14 @@ +--- +title: "Define Custom Permission Tokens | Cookbook" +head: + - - meta + - name: description + content: "Learn how to define custom permission tokens in Iroha." + - - meta + - name: keywords + content: "permission tokens, executors" +--- + +# How to Define a Custom Permission Token + +TODO \ No newline at end of file diff --git a/src/cookbook/find-leader-among-running-peers.md b/src/cookbook/find-leader-among-running-peers.md new file mode 100644 index 000000000..60a4b16b8 --- /dev/null +++ b/src/cookbook/find-leader-among-running-peers.md @@ -0,0 +1,14 @@ +--- +title: "Find Leader Among Running Peers | Cookbook" +head: + - - meta + - name: description + content: "Learn how to find the leader among running Iroha peers." + - - meta + - name: keywords + content: "peers, leader" +--- + +# How to Find the Leader Among Running Peers + +TODO \ No newline at end of file diff --git a/src/cookbook/get-metrics.md b/src/cookbook/get-metrics.md new file mode 100644 index 000000000..93dfd4154 --- /dev/null +++ b/src/cookbook/get-metrics.md @@ -0,0 +1,14 @@ +--- +title: "Get Metrics | Cookbook" +head: + - - meta + - name: description + content: "Learn how to get metrics to monitor Iroha performance." + - - meta + - name: keywords + content: "telemetry, metrics, Iroha performance" +--- + +# Get Metrics + +TODO \ No newline at end of file diff --git a/src/cookbook/grant-permissions.md b/src/cookbook/grant-permissions.md new file mode 100644 index 000000000..11edaa801 --- /dev/null +++ b/src/cookbook/grant-permissions.md @@ -0,0 +1,49 @@ +--- +title: "Grant Permissions | Cookbook" +head: + - - meta + - name: description + content: "Learn how to grant permissions in Iroha." + - - meta + - name: keywords + content: "granting permissions, permission tokens" +--- + +# How to Grant Permissions + +Granting a permission to an account: + +```rust +fn grant_permission_to_account( + iroha: &Client, +) { + // Alice will be given permission to unregister the kingdom domain + let grant_permission_to_unregister_kingdom = Grant::permission( + PermissionToken::new( + "CanUnregisterDomain".parse::().unwrap(), + &serde_json::json!({ "domain_id": "kingdom" }), + ), + "alice@wonderland".parse::().unwrap() + ); + iroha.submit(grant_permission_to_unregister_kingdom).unwrap(); +} +``` + +Granting a permission to a role: + +```rust +fn grant_permission_to_role( + iroha: &Client, +) { + // all accounts with the DOMAIN_DESTROYER role + // will be able to unregister the kingdom domain + let grant_permission_to_unregister_kingdom = Grant::role_permission( + PermissionToken::new( + "CanUnregisterDomain".parse::().unwrap(), + &serde_json::json!({ "domain_id": "kingdom" }), + ), + "DOMAIN_DESTROYER".parse::().unwrap(), + ); + iroha.submit(grant_permission_to_unregister_kingdom).unwrap(); +} +``` diff --git a/src/cookbook/grant-roles.md b/src/cookbook/grant-roles.md new file mode 100644 index 000000000..3a241bd5b --- /dev/null +++ b/src/cookbook/grant-roles.md @@ -0,0 +1,26 @@ +--- +title: "Grant Roles | Cookbook" +head: + - - meta + - name: description + content: "Learn how to grant roles to accounts in Iroha." + - - meta + - name: keywords + content: "granting roles, roles, permission groups" +--- + +# How to Grant a Role + +Roles are granted to accounts: + +```rust +fn grant_role( + iroha: &Client, +) { + let grant_role = Grant::role( + "DOMAIN_DESTROYER".parse::().unwrap(), + "alice@wonderland".parse::().unwrap() + ); + iroha.submit(grant_role).unwrap(); +} +``` diff --git a/src/cookbook/index.md b/src/cookbook/index.md new file mode 100644 index 000000000..6cb8213d5 --- /dev/null +++ b/src/cookbook/index.md @@ -0,0 +1,3 @@ +# Cookbook + +TODO \ No newline at end of file diff --git a/src/cookbook/mint-assets.md b/src/cookbook/mint-assets.md new file mode 100644 index 000000000..b6448aaf3 --- /dev/null +++ b/src/cookbook/mint-assets.md @@ -0,0 +1,33 @@ +--- +title: "Mint Assets | Cookbook" +head: + - - meta + - name: description + content: "Learn how to mint assets in Iroha." + - - meta + - name: keywords + content: "minting assets, mintable assets" +--- + +# How to Mint an Asset + +Only numeric assets can be minted – infinitely, or only once. + +```rust +fn mint_numeric_asset(iroha: &Client) { + let roses = "rose#wonderland".parse::().unwrap(); + let roses_definition = iroha + .request(FindAssetDefinitionById::new(roses.clone())) + .unwrap(); + match roses_definition.mintable { + Mintable::Infinitely => println!("This code will succeed indefinitely"), + Mintable::Once => println!("This code will succeed only once"), + Mintable::Not => println!("This code will fail"), + } + let alice = "alice@wonderland".parse::().unwrap(); + let roses_of_alice = AssetId::new(roses, alice); + let quantity = numeric!(42); + let mint_roses_of_alice = Mint::asset_numeric(quantity, roses_of_alice); + iroha.submit(mint_roses_of_alice).unwrap(); +} +``` \ No newline at end of file diff --git a/src/cookbook/mint-more-assets.md b/src/cookbook/mint-more-assets.md new file mode 100644 index 000000000..6155171a5 --- /dev/null +++ b/src/cookbook/mint-more-assets.md @@ -0,0 +1,14 @@ +--- +title: "Mint More of a Mintable Asset | Cookbook" +head: + - - meta + - name: description + content: "Learn how to mint more of a mintable asset." + - - meta + - name: keywords + content: "minting assets, mintable assets" +--- + +# How to Mint More of a Mintable Asset + +TODO \ No newline at end of file diff --git a/src/cookbook/monitor-iroha-performance.md b/src/cookbook/monitor-iroha-performance.md new file mode 100644 index 000000000..3432e3a76 --- /dev/null +++ b/src/cookbook/monitor-iroha-performance.md @@ -0,0 +1,14 @@ +--- +title: "Monitor Iroha Performance | Cookbook" +head: + - - meta + - name: description + content: "Learn how to monitor Iroha performance." + - - meta + - name: keywords + content: "telemetry, Iroha performance, monitoring performance" +--- + +# How to Monitor Iroha Performance + +TODO \ No newline at end of file diff --git a/src/cookbook/query-connected-peers.md b/src/cookbook/query-connected-peers.md new file mode 100644 index 000000000..c4859bc98 --- /dev/null +++ b/src/cookbook/query-connected-peers.md @@ -0,0 +1,14 @@ +--- +title: "Query Connected Peers | Cookbook" +head: + - - meta + - name: description + content: "Learn how to query connected peers." + - - meta + - name: keywords + content: "connected peers, Iroha queries" +--- + +# How to Query Connected Peers + +TODO \ No newline at end of file diff --git a/src/cookbook/register-accounts.md b/src/cookbook/register-accounts.md new file mode 100644 index 000000000..4d37a89ff --- /dev/null +++ b/src/cookbook/register-accounts.md @@ -0,0 +1,25 @@ +--- +title: "Register Accounts | Cookbook" +head: + - - meta + - name: description + content: "Learn how to register accounts in Iroha." + - - meta + - name: keywords + content: "registering accounts" +--- + +# How to Register an Account + +```rust +fn register_account(iroha: &Client) { + let alice = "alice@wonderland".parse::().unwrap(); + let (public_key, _private_key) = KeyPair::random().into_parts(); + // Keep your private key secret, + // and use the public key to create an account + let register_alice = Register::account( + Account::new(alice, public_key) + ); + iroha.submit(register_alice).unwrap(); +} +``` \ No newline at end of file diff --git a/src/cookbook/register-asset-definitions.md b/src/cookbook/register-asset-definitions.md new file mode 100644 index 000000000..e07985f2e --- /dev/null +++ b/src/cookbook/register-asset-definitions.md @@ -0,0 +1,51 @@ +--- +title: "Register Asset Definitions | Cookbook" +head: + - - meta + - name: description + content: "Learn how to register asset definitions in Iroha." + - - meta + - name: keywords + content: "asset definitions, registering asset definitions" +--- + +# How to Register an Asset Definition + +```rust +fn define_store_asset( + iroha: &Client, +) { + let hats = "hat#outfit".parse::().unwrap(); + let hats_as_a_concept = AssetDefinition::store(hats); + iroha.submit(Register::asset_definition(hats_as_a_concept)).unwrap(); +} +``` + +```rust +fn define_numeric_asset(iroha: &Client) { + let define_roses = Register::asset_definition( + AssetDefinition::new( + "rose#wonderland".parse().unwrap(), + // allow only whole values + AssetValueType::Numeric(NumericSpec::integer()), + ) + ); + let define_coins = Register::asset_definition( + AssetDefinition::new( + "coin#wonderland".parse().unwrap(), + // allow fractional values with two decimal places + AssetValueType::Numeric(NumericSpec::fractional(2)), + ) + ); + let define_gold = Register::asset_definition( + // allow arbitrary numeric values + AssetDefinition::numeric("gold#wonderland".parse().unwrap()) + ); + let instructions: [RegisterBox; _] = [ + define_roses.into(), + define_coins.into(), + define_gold.into(), + ]; + iroha.submit_all(instructions).unwrap(); +} +``` \ No newline at end of file diff --git a/src/cookbook/register-assets.md b/src/cookbook/register-assets.md new file mode 100644 index 000000000..16f06f915 --- /dev/null +++ b/src/cookbook/register-assets.md @@ -0,0 +1,49 @@ +--- +title: "Register Assets | Cookbook" +head: + - - meta + - name: description + content: "Learn how to register assets in Iroha." + - - meta + - name: keywords + content: "registering assets" +--- + +# How to Register an Asset + +```rust +fn register_store_asset( + iroha: &Client, +) { + let hat_of_alice = "hat##alice@wonderland".parse::().unwrap(); + let hat_data = { + // hat has at most 3 keys, each value must fit into 100 bytes + let (data, limits) = (Metadata::new(), MetadataLimits::new(3, 100)); + data.insert_with_limits( + "color".parse::().unwrap(), + "yellow".to_owned(), + limits, + ) + .unwrap(); + data + }; + // register a hat as Alice's asset + let register_hat_of_alice = Register::asset( + Asset::new(hat_of_alice, AssetValue::Store(hat_data)) + ); + iroha.submit(register_hat_of_alice).unwrap(); +} +``` + +```rust +fn register_numeric_asset( + iroha: &Client, +) { + let roses_of_alice = "rose##alice@wonderland".parse::().unwrap(); + // register 123 roses as Alice's asset + let register_roses_of_alice = Register::asset( + Asset::new(roses_of_alice, numeric!(123)) + ); + iroha.submit(register_roses_of_alice).unwrap(); +} +``` \ No newline at end of file diff --git a/src/cookbook/register-by-call-triggers.md b/src/cookbook/register-by-call-triggers.md new file mode 100644 index 000000000..ee3d032c2 --- /dev/null +++ b/src/cookbook/register-by-call-triggers.md @@ -0,0 +1,14 @@ +--- +title: "Register By-call Triggers | Cookbook" +head: + - - meta + - name: description + content: "Learn how to register a by-call trigger in Iroha." + - - meta + - name: keywords + content: "Iroha triggers, by-call triggers" +--- + +# How to Register a By-call Trigger + +TODO \ No newline at end of file diff --git a/src/cookbook/register-data-triggers.md b/src/cookbook/register-data-triggers.md new file mode 100644 index 000000000..a39cd27ed --- /dev/null +++ b/src/cookbook/register-data-triggers.md @@ -0,0 +1,14 @@ +--- +title: "Register Data Triggers | Cookbook" +head: + - - meta + - name: description + content: "Learn how to register a data trigger in Iroha." + - - meta + - name: keywords + content: "Iroha triggers, data triggers" +--- + +# How to Register a Data Trigger + +TODO \ No newline at end of file diff --git a/src/cookbook/register-domains.md b/src/cookbook/register-domains.md new file mode 100644 index 000000000..e3e30a452 --- /dev/null +++ b/src/cookbook/register-domains.md @@ -0,0 +1,22 @@ +--- +title: "Register Domains | Cookbook" +head: + - - meta + - name: description + content: "Learn how to register domains in Iroha." + - - meta + - name: keywords + content: "Iroha domains, registering domains" +--- + +# How to Register a Domain + +```rust +fn register_domain(iroha: &Client) { + let wonderland = "wonderland".parse::().unwrap(); + let register_wonderland = Register::domain( + Domain::new(wonderland) + ); + iroha.submit(register_wonderland).unwrap(); +} +``` \ No newline at end of file diff --git a/src/cookbook/register-peers.md b/src/cookbook/register-peers.md new file mode 100644 index 000000000..eaec5ba58 --- /dev/null +++ b/src/cookbook/register-peers.md @@ -0,0 +1,14 @@ +--- +title: "Register Peers | Cookbook" +head: + - - meta + - name: description + content: "Learn how to register peers in Iroha." + - - meta + - name: keywords + content: "registering peers, Iroha peers" +--- + +# How to Register a Peer + +TODO \ No newline at end of file diff --git a/src/cookbook/register-pre-commit-triggers.md b/src/cookbook/register-pre-commit-triggers.md new file mode 100644 index 000000000..9001c0ef3 --- /dev/null +++ b/src/cookbook/register-pre-commit-triggers.md @@ -0,0 +1,14 @@ +--- +title: "Register Pre-commit Triggers | Cookbook" +head: + - - meta + - name: description + content: "Learn how to register pre-commit triggers in Iroha." + - - meta + - name: keywords + content: "Iroha triggers, pre-commit triggers" +--- + +# How to Register a Pre-commit Trigger + +TODO \ No newline at end of file diff --git a/src/cookbook/register-roles.md b/src/cookbook/register-roles.md new file mode 100644 index 000000000..239de27af --- /dev/null +++ b/src/cookbook/register-roles.md @@ -0,0 +1,53 @@ +--- +title: "Register Roles | Cookbook" +head: + - - meta + - name: description + content: "Learn how to register roles in Iroha." + - - meta + - name: keywords + content: "registering roles, permissions" +--- + +# How to Register a Role + +The minimal case is an empty role (without any permission tokens): + +```rust +fn register_new_role( + iroha: &Client +) { + let role_id = "MY_EMPTY_ROLE".parse::().unwrap(); + let role = Role::new(role_id); + let register_role = Register::role(role); + iroha.submit(register_role).unwrap(); +} +``` + +Permission tokens may be added to a role. In the following example, +a predefined `CanUnregisterDomain` permission token is used. + +You can also define your own permission tokens, +see [Define Custom Permission Tokens](define-custom-permission-tokens.md). + +```rust +fn register_new_role_with_permission( + iroha: &Client, +) { + let roses_of_alice = "rose##alice@wonderland".parse::().unwrap(); + let roses_of_mouse = "rose##mouse@wonderland".parse::().unwrap(); + let can_burn_roses_of_alice = PermissionToken::new( + "CanBurnUserAsset".parse::().unwrap(), + &serde_json::json!({ "asset_id": roses_of_alice }), + ); + let can_burn_roses_of_mouse = PermissionToken::new( + "CanBurnUserAsset".parse().unwrap(), + &serde_json::json!({ "asset_id": roses_of_mouse }), + ); + let rose_burner = Role::new("ROSE_BURNER".parse().unwrap()) + .add_permission(can_burn_roses_of_alice) + .add_permission(can_burn_roses_of_mouse); + let register_rose_burner = Register::role(rose_burner); + iroha.submit(register_rose_burner).unwrap(); +} +``` diff --git a/src/cookbook/register-scheduled-triggers.md b/src/cookbook/register-scheduled-triggers.md new file mode 100644 index 000000000..abd331270 --- /dev/null +++ b/src/cookbook/register-scheduled-triggers.md @@ -0,0 +1,14 @@ +--- +title: "Register Scheduled Triggers | Cookbook" +head: + - - meta + - name: description + content: "Learn how to register scheduled triggers in Iroha." + - - meta + - name: keywords + content: "Iroha triggers, scheduled triggers" +--- + +# How to Register a Scheduled Trigger + +TODO \ No newline at end of file diff --git a/src/cookbook/remove-key-value.md b/src/cookbook/remove-key-value.md new file mode 100644 index 000000000..ddffe4703 --- /dev/null +++ b/src/cookbook/remove-key-value.md @@ -0,0 +1,14 @@ +--- +title: "Remove Key Value | Cookbook" +head: + - - meta + - name: description + content: "Learn how to use the RemoveKeyValue instruction in Iroha." + - - meta + - name: keywords + content: "Iroha metadata, Iroha Special Instructions" +--- + +# How to Remove Key Value + +TODO \ No newline at end of file diff --git a/src/cookbook/revoke-permissions.md b/src/cookbook/revoke-permissions.md new file mode 100644 index 000000000..e46f70f89 --- /dev/null +++ b/src/cookbook/revoke-permissions.md @@ -0,0 +1,42 @@ +--- +title: "Revoke Permissions | Cookbook" +head: + - - meta + - name: description + content: "Learn how to revoke permissions in Iroha." + - - meta + - name: keywords + content: "revoking permissions, permission tokens" +--- + +# How to Revoke Permissions + +```rust +fn revoke_permission_from_account( + iroha: &Client, +) { + let revoke_permission_to_unregister_kingdom = Revoke::permission( + PermissionToken::new( + "CanUnregisterDomain".parse().unwrap(), + &serde_json::json!({ "domain_id": "kingdom" }), + ), + "alice@wonderland".parse::().unwrap() + ); + iroha.submit(revoke_permission_to_unregister_kingdom).unwrap(); +} +``` + +```rust +fn revoke_permission_from_role( + iroha: &Client, +) { + let revoke_permission_to_unregister_kingdom = Revoke::role_permission( + PermissionToken::new( + "CanUnregisterDomain".parse().unwrap(), + &serde_json::json!({ "domain_id": "kingdom" }), + ), + "DOMAIN_DESTROYER".parse::().unwrap(), + ); + iroha.submit(revoke_permission_to_unregister_kingdom).unwrap(); +} +``` diff --git a/src/cookbook/revoke-roles.md b/src/cookbook/revoke-roles.md new file mode 100644 index 000000000..aeece0d7a --- /dev/null +++ b/src/cookbook/revoke-roles.md @@ -0,0 +1,25 @@ +--- +title: "Revoke Roles | Cookbook" +head: + - - meta + - name: description + content: "Learn how to revoke roles from accounts in Iroha." + - - meta + - name: keywords + content: "revoking roles, roles, permission groups" +--- + +# How to Revoke Roles + +```rust +fn revoke_role( + iroha: &Client, +) { + // given that Alice has the role, revoke it + let revoke_role = Revoke::role( + "DOMAIN_DESTROYER".parse::().unwrap(), + "alice@wonderland".parse::().unwrap() + ); + iroha.submit(revoke_role).unwrap(); +} +``` diff --git a/src/cookbook/set-key-value.md b/src/cookbook/set-key-value.md new file mode 100644 index 000000000..624e70919 --- /dev/null +++ b/src/cookbook/set-key-value.md @@ -0,0 +1,14 @@ +--- +title: "Set Key Value | Cookbook" +head: + - - meta + - name: description + content: "Learn how to use the SetKeyValue instruction in Iroha." + - - meta + - name: keywords + content: "Iroha metadata, Iroha Special Instructions" +--- + +# How to Set Key Value + +TODO \ No newline at end of file diff --git a/src/cookbook/submit-transactions.md b/src/cookbook/submit-transactions.md new file mode 100644 index 000000000..6fc0cadae --- /dev/null +++ b/src/cookbook/submit-transactions.md @@ -0,0 +1,35 @@ +--- +title: "Submit Transactions | Cookbook" +head: + - - meta + - name: description + content: "Learn how to submit transactions in Iroha." + - - meta + - name: keywords + content: "submitting transactions, Iroha transactions" +--- + +# How to Submit a Transaction + +See [Create Transactions](create-transactions.md) to learn how to create +a `SignedTransaction` used in the examples below. + +```rust +fn submit_transaction_do_not_wait_for_approval( + iroha: &Client, + transaction: &SignedTransaction +) { + // panics if the transaction is invalid (cannot be submitted) + let _hash = iroha.submit_transaction(transaction).unwrap(); + // transaction may or may not have been committed or rejected +} + +fn submit_transaction_and_wait_for_approval( + iroha: &Client, + transaction: &SignedTransaction +) { + // panics if the transaction is invalid or rejected + let _hash = iroha.submit_transaction_blocking(transaction).unwrap(); + // transaction has been committed +} +``` \ No newline at end of file diff --git a/src/cookbook/transfer-asset-definitions.md b/src/cookbook/transfer-asset-definitions.md new file mode 100644 index 000000000..b7bb6ef4b --- /dev/null +++ b/src/cookbook/transfer-asset-definitions.md @@ -0,0 +1,14 @@ +--- +title: "Transfer Asset Definitions | Cookbook" +head: + - - meta + - name: description + content: "Learn how to transfer asset definitions." + - - meta + - name: keywords + content: "asset definitions, transferring asset definitions" +--- + +# How to Transfer Asset Definitions + +TODO \ No newline at end of file diff --git a/src/cookbook/transfer-assets.md b/src/cookbook/transfer-assets.md new file mode 100644 index 000000000..73efcf359 --- /dev/null +++ b/src/cookbook/transfer-assets.md @@ -0,0 +1,43 @@ +--- +title: "Transfer Assets | Cookbook" +head: + - - meta + - name: description + content: "Learn how to transfer assets between accounts in Iroha." + - - meta + - name: keywords + content: "transferring assets" +--- + +# How to Transfer Assets Between Accounts + +```rust +fn transfer_numeric_asset( + iroha: &Client, +) { + let roses = "rose#wonderland".parse::().unwrap(); + let alice = "alice@wonderland".parse::().unwrap(); + let mouse = "mouse@wonderland".parse::().unwrap(); + let transfer_roses_from_alice_to_mouse = Transfer::asset_numeric( + AssetId::new(roses, alice), + 13_u32, + mouse, + ); + iroha.submit(transfer_roses_from_alice_to_mouse).unwrap(); +} +``` + +```rust +fn transfer_store_asset( + iroha: &Client, +) { + let hats = "hat#outfit".parse::().unwrap(); + let alice = "alice@outfit".parse::().unwrap(); + let mouse = "mouse@outfit".parse::().unwrap(); + let transfer_hat_from_alice_to_mouse = Transfer::asset_store( + AssetId::new(hats, alice), + mouse, + ); + iroha.submit(transfer_hat_from_alice_to_mouse).unwrap(); +} +``` diff --git a/src/cookbook/transfer-domain-owner.md b/src/cookbook/transfer-domain-owner.md new file mode 100644 index 000000000..555821611 --- /dev/null +++ b/src/cookbook/transfer-domain-owner.md @@ -0,0 +1,14 @@ +--- +title: "Transfer Domain Owner | Cookbook" +head: + - - meta + - name: description + content: "Learn how to transfer a domain owner." + - - meta + - name: keywords + content: "Iroha domains, domain owners" +--- + +# How to Transfer a Domain Owner + +TODO \ No newline at end of file diff --git a/src/cookbook/transfer-group-assets.md b/src/cookbook/transfer-group-assets.md new file mode 100644 index 000000000..7294b496f --- /dev/null +++ b/src/cookbook/transfer-group-assets.md @@ -0,0 +1,43 @@ +--- +title: "Transfer Groups of Assets | Cookbook" +head: + - - meta + - name: description + content: "Learn how to transfer a group of assets." + - - meta + - name: keywords + content: "transferring assets" +--- + +# How to Transfer a Group of Assets + +Transferring multiple assets atomically involves combining multiple +Transfer instructions in a single transaction. + +```rust +fn transfer_group_of_assets(iroha: &Client) { + let alice = "alice@wonderland".parse().unwrap(); + let mouse = "mouse@wonderland".parse().unwrap(); + let transfer_roses_from_alice_to_mouse = Transfer::asset_numeric( + AssetId::new("rose#wonderland".parse().unwrap(), alice.clone()), + numeric!(1), + mouse.clone(), + ); + let transfer_coins_from_alice_to_mouse = Transfer::asset_numeric( + AssetId::new("coin#wonderland".parse().unwrap(), alice.clone()), + numeric!(0.99), + mouse.clone(), + ); + let transfer_hat_from_alice_to_mouse = Transfer::asset_store( + AssetId::new("hat#wonderland".parse().unwrap(), alice), + mouse, + ); + let transfers: [TransferBox; _] = [ + transfer_roses_from_alice_to_mouse.into(), + transfer_coins_from_alice_to_mouse.into(), + transfer_hat_from_alice_to_mouse.into(), + ]; + iroha.submit_all(transfers).unwrap(); +} +``` + \ No newline at end of file diff --git a/src/cookbook/unregister-accounts.md b/src/cookbook/unregister-accounts.md new file mode 100644 index 000000000..a98cb8108 --- /dev/null +++ b/src/cookbook/unregister-accounts.md @@ -0,0 +1,23 @@ +--- +title: "Unregister Accounts | Cookbook" +head: + - - meta + - name: description + content: "Learn how to unregister accounts in Iroha." + - - meta + - name: keywords + content: "Iroha accounts, unregister instruction" +--- + +# How to Unregister Accounts + +Only domain owners and accounts with an appropriate permission token +can unregister another account. + +```rust +fn unregister_account(iroha: &Client) { + let alice = "alice@wonderland".parse::().unwrap(); + let unregister_alice = Unregister::account(alice); + iroha.submit(unregister_alice).unwrap(); +} +``` \ No newline at end of file diff --git a/src/cookbook/unregister-asset-definitions.md b/src/cookbook/unregister-asset-definitions.md new file mode 100644 index 000000000..9df88f900 --- /dev/null +++ b/src/cookbook/unregister-asset-definitions.md @@ -0,0 +1,21 @@ +--- +title: "Unregister Asset Definitions | Cookbook" +head: + - - meta + - name: description + content: "Learn how to unregister asset definitions in Iroha." + - - meta + - name: keywords + content: "Iroha asset definition, unregister instruction" +--- + +# How to Unregister an Asset Definition + +```rust +fn undefine_asset( + iroha: &Client, +) { + let hats = "hat#outfit".parse::().unwrap(); + iroha.submit(Unregister::asset_definition(hats)).unwrap(); +} +``` \ No newline at end of file diff --git a/src/cookbook/unregister-assets.md b/src/cookbook/unregister-assets.md new file mode 100644 index 000000000..df55f963a --- /dev/null +++ b/src/cookbook/unregister-assets.md @@ -0,0 +1,22 @@ +--- +title: "Unregister Assets | Cookbook" +head: + - - meta + - name: description + content: "Learn how to unregister assets in Iroha." + - - meta + - name: keywords + content: "Iroha assets, unregister instruction" +--- + +# How to Unregister an Asset + +```rust +fn unregister_asset( + iroha: &Client, +) { + let roses_of_alice = "rose##alice@wonderland".parse::().unwrap(); + let unregister_roses_of_alice = Unregister::asset(roses_of_alice); + iroha.submit(unregister_roses_of_alice).unwrap(); +} +``` \ No newline at end of file diff --git a/src/cookbook/unregister-domains.md b/src/cookbook/unregister-domains.md new file mode 100644 index 000000000..2d5587beb --- /dev/null +++ b/src/cookbook/unregister-domains.md @@ -0,0 +1,20 @@ +--- +title: "Unregister Domains | Cookbook" +head: + - - meta + - name: description + content: "Learn how to unregister domains in Iroha." + - - meta + - name: keywords + content: "Iroha domains, unregister instruction" +--- + +# How to Unregister a Domain + +```rust +fn unregister_domain(iroha: &Client) { + let wonderland = "wonderland".parse::().unwrap(); + let unregister_wonderland = Unregister::domain(wonderland); + iroha.submit(unregister_wonderland).unwrap(); +} +``` \ No newline at end of file diff --git a/src/cookbook/unregister-peers.md b/src/cookbook/unregister-peers.md new file mode 100644 index 000000000..1b0635165 --- /dev/null +++ b/src/cookbook/unregister-peers.md @@ -0,0 +1,14 @@ +--- +title: "Unregister Peers | Cookbook" +head: + - - meta + - name: description + content: "Learn how to unregister peers in Iroha." + - - meta + - name: keywords + content: "Iroha peers, unregister instruction" +--- + +# How to Unregister a Peer + +TODO \ No newline at end of file diff --git a/src/cookbook/unregister-triggers.md b/src/cookbook/unregister-triggers.md new file mode 100644 index 000000000..a9044076f --- /dev/null +++ b/src/cookbook/unregister-triggers.md @@ -0,0 +1,14 @@ +--- +title: "Unregister Triggers | Cookbook" +head: + - - meta + - name: description + content: "Learn how to unregister triggers in Iroha." + - - meta + - name: keywords + content: "Iroha triggers, unregister instruction" +--- + +# How to Unregister a Trigger + +TODO \ No newline at end of file diff --git a/src/cookbook/update-executor.md b/src/cookbook/update-executor.md new file mode 100644 index 000000000..ff0ac8062 --- /dev/null +++ b/src/cookbook/update-executor.md @@ -0,0 +1,14 @@ +--- +title: "Update Executor | Cookbook" +head: + - - meta + - name: description + content: "Learn how to update an executor in Iroha." + - - meta + - name: keywords + content: "Iroha executor" +--- + +# How to Update an Executor + +TODO \ No newline at end of file diff --git a/src/cookbook/use-instructions.md b/src/cookbook/use-instructions.md new file mode 100644 index 000000000..dc9b4d5da --- /dev/null +++ b/src/cookbook/use-instructions.md @@ -0,0 +1,30 @@ +--- +title: "Use Instructions | Cookbook" +head: + - - meta + - name: description + content: "Learn how to use Iroha Special Instructions (ISI)." + - - meta + - name: keywords + content: "Iroha Special Instructions" +--- + +# How to Use Iroha Special Instructions + +Building and submitting an instruction: + +```rust +fn use_instruction( + iroha: &Client, +) { + let roses = "rose#wonderland".parse::().unwrap(); + let alice = "alice@wonderland".parse::().unwrap(); + // build an instruction + let mint_roses_for_alice = Mint::asset_numeric( + 42_u32, + AssetId::new(roses, alice) + ); + // submit the instruction + iroha.submit(mint_roses_for_alice).unwrap(); +} +``` \ No newline at end of file diff --git a/src/cookbook/use-multi-signature-transactions.md b/src/cookbook/use-multi-signature-transactions.md new file mode 100644 index 000000000..8c0d9e3db --- /dev/null +++ b/src/cookbook/use-multi-signature-transactions.md @@ -0,0 +1,261 @@ +--- +title: 'Use Multi-Signature Transactions | Cookbook' +head: + - - meta + - name: description + content: 'Learn how to use multi-signature transactions in Iroha.' + - - meta + - name: keywords + content: 'multi-signature transactions' +--- + +# How to Use Multi-Signature Transactions + +The following example shows how to set up a rule that all transactions made +by a specific account should also be signed by an additional key. + +## Overview + + + +1. Create an account, set up a second signature rule for it, and register a + new asset. +2. Submit the transaction signed by the main account key, minting the + asset. +3. Check that the transaction is not committed by observing the asset. +4. Submit the same transaction signed by the second additional key. +5. Check that the transaction is committed, and the asset is minted. + +## Example + +1. Let's create an account that would need to have an additional signature + for its transactions. Let's also create an asset that this account will + later mint in order to check that the rule about second signature works + as expected. + + The code below covers the following steps: + + - Create account `mad_hatter` in `wonderland` domain with `key_pair_1`. + - Assign `SignatureCheckCondition` to the created account. This will + enforce transactions from `mad_hatter@wonderland` to have an + additional signature by `key_pair_2`. + - Register a numeric asset `casomile#wonderland` of type `Quantity` and + `Infinite` mintability. + +::: code-group + +```rust [Rust] +let key_pair_1 = KeyPair::generate()?; +let key_pair_2 = KeyPair::generate()?; + +let account_id = AccountId::from_str("mad_hatter@wonderland")?; +let asset_definition_id = AssetDefinitionId::from_str("camomile#wonderland")?; + +let register_account = RegisterExpr::new(Account::new( + account_id.clone(), + [key_pair_1.public_key().clone()], +)); +let set_signature_condition = MintExpr::new( + SignatureCheckCondition::AllAccountSignaturesAnd( + vec![key_pair_2.public_key().clone()].into(), + ), + IdBox::AccountId(account_id.clone()), +); +let register_asset_definition = + RegisterExpr::new(AssetDefinition::quantity(asset_definition_id.clone())); + +let _hash = admin_client.submit_all_blocking({ + let isi: [InstructionExpr; 3] = [ + register_account.into(), + set_signature_condition.into(), + register_asset_definition.into(), + ]; + isi +})?; +``` + +```ts [TypeScript] +declare const adminClient: Client +declare const torii: ToriiRequirementsForApiHttp + +const keyPair1 = crypto.KeyPair.generate() +const keyPair2 = crypto.KeyPair.generate() + +const accountId = sugar.accountId('mad_hatter', 'wonderland') +const assetDefinitionId = sugar.assetDefinitionId('camomile', 'wonderland') + +const registerAccount = sugar.instruction.register( + sugar.identifiable.newAccount(accountId, [ + freeScope(() => keyPair1.publicKey().toDataModel()), + ]), +) +const setSignatureCondition = sugar.instruction.mint( + datamodel.Value( + 'SignatureCheckCondition', + datamodel.SignatureCheckCondition( + 'AllAccountSignaturesAnd', + datamodel.VecPublicKey([ + freeScope(() => keyPair2.publicKey().toDataModel()), + ]), + ), + ), + datamodel.IdBox('AccountId', accountId), +) +const registerAssetDefinition = sugar.instruction.register( + sugar.identifiable.newAssetDefinition( + assetDefinitionId, + datamodel.AssetValueType('Quantity'), + { mintable: datamodel.Mintable('Infinitely') }, + ), +) + +await adminClient.submitExecutable( + torii, + sugar.executable.instructions([ + registerAccount, + setSignatureCondition, + registerAssetDefinition, + ]), +) +``` + +::: + +2. Submit the transaction signed by the main account key, minting the + asset: + +::: code-group + +```rust [Rust] +let mut mad_hatter_client = admin_client.clone(); +mad_hatter_client.key_pair = key_pair_1; +mad_hatter_client.account_id = account_id.clone(); + +let quantity: u32 = 42; +let asset_id = AssetId::new(asset_definition_id, account_id.clone()); +let mint_asset = MintExpr::new(quantity.to_value(), IdBox::AssetId(asset_id.clone())); + +let transaction_1 = { + let tx = + mad_hatter_client.build_transaction([mint_asset.clone()], UnlimitedMetadata::new())?; + mad_hatter_client.sign_transaction(tx)? +}; +mad_hatter_client.submit_transaction(&transaction_1)?; +``` + +```ts [TypeScript] +const madHatterClient = new Client({ + signer: new Signer(accountId, keyPair1), +}) + +const quantity = 42 +const assetId = sugar.assetId(accountId, assetDefinitionId) +const mintAsset = sugar.instruction.mint( + sugar.value.numericU32(quantity), + datamodel.IdBox('AssetId', assetId), +) + +const transaction = makeSignedTransaction( + makeTransactionPayload({ + executable: sugar.executable.instructions(mintAsset), + accountId, + }), + madHatterClient.signer, +) + +await Torii.submit(torii, transaction) +``` + +::: + +3. Let's query the asset to ensure that the transaction was not committed + yet, without the second signature: + +::: code-group + +```rust [Rust] +let error = mad_hatter_client + .request(client::asset::by_id(asset_id.clone())) + .expect_err("Asset should not be found"); + +assert!(matches!( + error, + ClientQueryError::Validation(ValidationFail::QueryFailed(QueryExecutionFail::Find( + FindError::Asset(_) + ))) +)); +``` + +```ts [TypeScript] +const asset = await madHatterClient.requestWithQueryBox( + torii, + sugar.find.assetById(assetId), +) + +expect(() => + asset.as('Err').enum.as('QueryFailed').enum.as('Find').enum.as('Asset'), +).not.toThrow() +``` + +::: + +4. Now let's submit the same transaction, but signed with the second key + pair: + +::: code-group + +```rust [Rust] +mad_hatter_client.key_pair = key_pair_2; + +// FIXME: not sign tx1, but get original tx from Iroha and sign it +let transaction_2 = mad_hatter_client.sign_transaction(transaction_1)?; +mad_hatter_client.submit_transaction(&transaction_2)?; +``` + +```ts [TypeScript] +const newSigner = new Signer(accountId, keyPair2) +transaction.enum + .as('V1') + .signatures.push( + signTransaction(transaction.enum.as('V1').payload, newSigner), + ) +await Torii.submit(torii, transaction) +``` + +::: + +5. Let's check the asset to ensure that now the transaction is committed: + +::: code-group + +```rust [Rust] +let asset: Asset = mad_hatter_client + .request(client::asset::by_id(asset_id)) + .expect("Asset should be found") + .try_into() + .expect("Value should be Asset"); + +assert_eq!(asset.value, quantity.into()); +``` + +```ts [TypeScript] +const asset = await madHatterClient.requestWithQueryBox( + torii, + sugar.find.assetById(assetId), +) + +expect( + asset + .as('Ok') + .batch.enum.as('Identifiable') + .enum.as('Asset') + .value.enum.as('Quantity'), +).toEqual(quantity) +``` + +::: + +## Find More + +You could find full code here: [Rust](https://www.example.com/), +[TypeScript](https://www.example.com/). diff --git a/src/cookbook/work-with-non-mintable-assets.md b/src/cookbook/work-with-non-mintable-assets.md new file mode 100644 index 000000000..d686c6278 --- /dev/null +++ b/src/cookbook/work-with-non-mintable-assets.md @@ -0,0 +1,62 @@ +--- +title: "Work with Non-Mintable Assets | Cookbook" +head: + - - meta + - name: description + content: "Learn how to work with non-mintable assets in Iroha." + - - meta + - name: keywords + content: "non-mintable assets, Iroha assets" +--- + +# How to Work with Non-Mintable Assets + +```rust +fn register_non_mintable_asset( + iroha: &Client, +) { + let magical_keys = "magical_key#wonderland" + .parse::() + .unwrap(); + // register keys as an asset definition + let register_keys_as_a_concept = Register::asset_definition( + AssetDefinition::new( + magical_keys.clone(), + AssetValueType::Numeric(NumericSpec::integer()), + ).mintable_once() + ); + let alice = "alice@wonderland".parse().unwrap(); + // Alice owns ten keys and cannot mint more + let initial_keys_of_alice = Asset::new( + AssetId::new(magical_keys, alice), + 10_u32 + ); + let register_keys_of_alice = Register::asset(initial_keys_of_alice); + let instructions: [RegisterBox; _] = [ + register_keys_as_a_concept.into(), + register_keys_of_alice.into(), + ]; + iroha.submit_all(instructions).unwrap(); +} +``` + +```rust +fn mint_non_mintable_asset( + iroha: &Client, +) { + // Alice owns zero keys and can mint once + let keys_of_alice = "magical_key##alice@wonderland".parse().unwrap(); + let zero_keys_of_alice = Asset::new( + keys_of_alice.clone(), + 0_u32 + ); + let register_keys_of_alice = Register::asset(zero_keys_of_alice); + let mint_keys_for_alice = Mint::asset_numeric(10_u32, keys_of_alice); + let instructions: [InstructionBox; _] = [ + register_keys_as_a_concept.into(), + register_keys_of_alice.into(), + mint_keys_for_alice.into(), + ]; + iroha.submit_all(instructions).unwrap(); +} +``` \ No newline at end of file diff --git a/src/cookbook/work-with-numeric-assets.md b/src/cookbook/work-with-numeric-assets.md new file mode 100644 index 000000000..a1461bedf --- /dev/null +++ b/src/cookbook/work-with-numeric-assets.md @@ -0,0 +1,109 @@ +--- +title: "Work with Numeric Assets | Cookbook" +head: + - - meta + - name: description + content: "Learn how to work with numeric assets in Iroha." + - - meta + - name: keywords + content: "Iroha assets, numeric assets" +--- + +# How to Work with Numeric Assets + +Registering Alice's roses: + +```rust +fn register_numeric_asset( + iroha: &Client, +) { + let roses = "rose#wonderland".parse::().unwrap(); + // register roses as an asset definition + let register_roses_as_a_concept = Register::asset_definition( + AssetDefinition::new( + roses.clone(), + // for the sake of the example, + // allow dividing roses into tenths + AssetValueType::Numeric(NumericSpec::fractional(1)), + ) + ); + let alice = "alice@wonderland".parse().unwrap(); + let roses_of_alice = AssetId::new(roses, alice); + let initial_roses_of_alice = Asset::new(roses_of_alice, Numeric::ZERO); + // register zero roses as Alice's asset + let register_roses_of_alice = Register::asset(initial_roses_of_alice); + let instructions: [RegisterBox; _] = [ + register_roses_as_a_concept.into(), + register_roses_of_alice.into(), + ]; + iroha.submit_all(instructions).unwrap(); +} +``` + +Minting roses for Alice: + +```rust +fn mint_numeric_asset( + iroha: &Client, +) { + // mint twelve and a half roses + let mint_roses_for_alice = Mint::asset_numeric( + numeric!(12.5), + "rose##alice@wonderland".parse().unwrap() + ); + iroha.submit(mint_roses_for_alice).unwrap(); +} +``` + +Burning Alice's roses: + +```rust +fn burn_numeric_asset( + iroha: &Client, +) { + // burn three roses + let burn_roses_of_alice = Burn::asset_numeric( + numeric!(3), + "rose##alice@wonderland".parse::().unwrap() + ); + iroha.submit(burn_roses_of_alice).unwrap(); +} +``` + +Transferring Alice's roses to Mouse: + +```rust +fn transfer_numeric_asset( + iroha: &Client, +) { + let roses = "rose#wonderland".parse::().unwrap(); + let alice = "alice@wonderland".parse::().unwrap(); + let mouse = "mouse@wonderland".parse::().unwrap(); + // transfer ten roses and a tenth of a rose + let transfer_roses_from_alice_to_mouse = Transfer::asset_numeric( + AssetId::new(roses, alice), + numeric!(10.1), + mouse, + ); + iroha.submit(transfer_roses_from_alice_to_mouse).unwrap(); +} +``` + +Check that Alice has a whole number of roses: + +```rust +fn query_numeric_asset( + iroha: &Client, +) { + let roses = "rose#wonderland".parse::().unwrap(); + let alice = "alice@wonderland".parse::().unwrap(); + let roses_of_alice = AssetId::new(roses, alice); + let total_roses_of_alice = iroha + .request(FindAssetQuantityById::new(roses_of_alice)) + .unwrap(); + match NumericSpec::integer().check(total_roses_of_alice) { + Ok(_) => println!("Alice has a whole number of roses"), + Err(_) => println!("Alice has a fractional number of roses"), + } +} +``` \ No newline at end of file diff --git a/src/cookbook/work-with-store-assets.md b/src/cookbook/work-with-store-assets.md new file mode 100644 index 000000000..e26ff6c4a --- /dev/null +++ b/src/cookbook/work-with-store-assets.md @@ -0,0 +1,64 @@ +--- +title: "Work with Store Assets | Cookbook" +head: + - - meta + - name: description + content: "Learn how to work with Store assets in Iroha." + - - meta + - name: keywords + content: "Iroha assets, Store assets" +--- + +# How to Work with Store Assets + +While numeric assets represent quantities, store assets represent +arbitrary key-value tables. + +```rust +fn define_store_asset( + iroha: &Client, +) { + let hats = "hat#outfit".parse::().unwrap(); + let hats_as_a_concept = AssetDefinition::store(hats); + iroha.submit(Register::asset_definition(hats_as_a_concept)).unwrap(); +} +``` + +```rust +fn set_key_value_pair( + iroha: &Client, +) { + let hat_of_alice = "hat##alice@outfit".parse::().unwrap(); + let color = "color".parse::().unwrap(); + iroha.submit(SetKeyValue::asset( + hat_of_alice, + color, + "red".to_owned() + )).unwrap(); +} +``` + +```rust +fn read_key_value_pair( + iroha: &Client, +) { + let hat_of_alice = "hat##alice@outfit".parse::().unwrap(); + let color = "color".parse::().unwrap(); + // assume the color has been set to "red" + let red = iroha.request(FindAssetKeyValueByIdAndKey::new( + hat_of_alice, + color + )).unwrap(); + assert_eq!(red, MetadataValueBox::String("red".to_owned())); +} +``` + +```rust +fn unset_key_value_pair( + iroha: &Client, +) { + let hat_of_alice = "hat##alice@outfit".parse::().unwrap(); + let color = "color".parse::().unwrap(); + iroha.submit(RemoveKeyValue::asset(hats, color)).unwrap(); +} +``` \ No newline at end of file diff --git a/src/cookbook/write-executor.md b/src/cookbook/write-executor.md new file mode 100644 index 000000000..3d7bbe80b --- /dev/null +++ b/src/cookbook/write-executor.md @@ -0,0 +1,14 @@ +--- +title: "Write Executor | Cookbook" +head: + - - meta + - name: description + content: "Learn how to write an executor in Iroha." + - - meta + - name: keywords + content: "Iroha executor" +--- + +# How to Write an Executor + +TODO \ No newline at end of file diff --git a/src/index.md b/src/index.md index c3b667e86..0ffd1f8a3 100644 --- a/src/index.md +++ b/src/index.md @@ -23,4 +23,7 @@ features: title: Reference details: Consult reference documentation for extensive information about available functionality link: /reference/torii-endpoints - # - title: Cookbook # (TBA) + - icon: 📓 + title: Cookbook + details: Find code samples and example showcasing Iroha functionality + link: /cookbook/