launchbadge / hedera-sdk-rust-archived

Hedera SDK for Rust
https://www.hedera.com
Apache License 2.0
41 stars 14 forks source link
hashgraph hedera rust sdk sdk-rust

Hedera SDK for Rust

Crates.io License

This repo contains the Rust SDK for interacting with the Hedera platform. Hedera is the only public distributed ledger licensed to use the Hashgraph consensus algorithm for fast, fair and secure transactions. By using any of the Hedera SDKs, developers will be empowered to build an entirely new class of decentralized applications.

Following the instructions below should help you to reach the position where you can send transactions and queries to a Hedera testnet or the Hedera mainnet.

Table of Contents


Developer Rewards

Developers who create Hedera accounts and test their applications on our testnet will be offered the opportunity to earn real mainnet (hbars) based upon that testing activity. The SDKs are intended to facilitate application development and encourage such testing. See the Hedera Account section for further details.

Hbars:

Hbars are the native cryptocurrency that is used to pay for transactions on the Hedera platform and to secure the network from certain types of cyberattacks. They are the native platform coin needed to interact with and exchange value on Hedera.

The symbol for hbars is "" so 5 ℏ means 5 hbars

Tinybars:

Tinybars are (not surprisingly) smaller than hbars. They are used to divide hbars into smaller amounts. One hbar is equivalent to one hundred million tinybars.

The symbol for tinybars is "tℏ" so it is correct to say 1 ℏ = 100,000,000 tℏ

Important Note: The values of all fees and transfers throughout the Hedera SDKs are represented in tinybars, though the term hbars may be used for the purposes of brevity.

Architectural Overview

All Hedera SDKs are intended to provide a developer-friendly means to leverage the Hedera API, which is based on Google Protobuf. Protobuf supports code generation for a growing number of languages and is highly optimised for efficient communications. For those interesting in viewing the underlying protobuf message definitions, see the Hedera Protobuf Message Definitions repo.

Developers who wish to work in other languages are at liberty to do so, but should be aware that implementation of cryptographic key generation and manipulation is not a trivial undertaking. The source code for the cryptography libraries used by all other Hedera SDKs (except Java) use C libraries generated from the Rust code in this repository. We would recommend use of these same libraries for developers interested in adding support for other languages.

Prerequisites

Software

Hedera Account

The Hedera Portal allows people to create a Hedera Account facilitating access to the Hedera mainnet and Hedera testnets. A Hedera Account allows entry of a testnet access code, in order to add a number of testnet (hbars) to a testnet account created (as can be seen below) using Hedera SDKs. The public key associated with a testnet account must also be associated with your Hedera account. A detailed explanation of this whole process is contained within this document.

A full explanation of the Portal, Hedera accounts, identity verification and many other topics can be found at Hedera Help. New users should head for the Getting Started section.

Hedera testnet access

A Hedera testnet provides a test environment for testing your code without having to spend "real" mainnet (hbars). Testnet hbars are akin to "monopoly money" and have no intrinsic value, but testing against testnets will help you earn real mainnet ℏ (hbars). It is worth noting that the virtual infrastructure used to provide testnets is not intended for performance testing, as the specification of nodes is not in any way equivalent to that of mainnet nodes. Further information on this topic is included within the "Testnet Performance and Throttling" section further on in these instructions.

Installing the Hedera SDK for Rust

This SDK can be run be cloning the SDK or by creating a new project folder that includes a dependency on the Hedera SDK for Rust. The examples in the "Building your first Hedera application" section later in this document the latter (new project) approach will be used.

From an existing clone of this SDK repo

Building a new project for your Hedera Rust app

You can use the Hedera SDK for Rust from your own project without the need to clone the github repo.

Running the examples

After running cargo build it should be possible to run the examples contained within this repo. In most cases, doing so will require changes to those examples to make them work with your account on your testnet. Detailed explanations of such changes can be seen in subsequent sections of this document.

If you have already modified the examples accordingly, running the following command from a terminal window will execute the example. Make sure you replace <filename> with the name (e.g. generate_key) of one of the example files. The .rs suffix is not required.

cargo run --example <filename>

Creating a public/private keypair for testnet use

As a general principle, it is bad practice to use your mainnet keys on a testnet. The code below shows the content of the generate_key example file. This shows how you can create new public and private keys using the Hedera SDK for Rust:

use hedera::SecretKey;

fn main() {
    let (secret, mnemonic) = SecretKey::generate("");
    let public = secret.public();

    println!("secret   = {}", secret);
    println!("mnemonic = {}", mnemonic);
    println!("public   = {}", public);
}
cargo run --example generate_key

Development Environment Note

All of the commands described in these instructions can also be executed using an IDE – such as VSCode or IntelliJ IDEA (with Rust plugin), or editors such as Atom – according to each developer's preferences and budget. Hedera has no affiliation with the companies providing these or other equivalent tools.

Throughout these instructions you'll find the phrase "Run the following command from a terminal window." Feel free to use your IDE whenever you see this – if that's how you prefer to work. Terminal is used in this document to avoid ambiguity.

Associating your public key with you Hedera tesnet account

Once you have generated a public/private keypair as described above, you need to link the public key to your Hedera testnet account. To do this, return to the Hedera portal and ensure that you have the testnet selected in the drop-down at the top of the page.

You should see the box asking you to Enter your Public Key. Copy the long hex value of the public key and paste it into that textbox in the portal. Do make sure you that you select all of the characters. Click Submit.

You should briefly see an "Account Pending" message, which will be replaced with an Account ID box. You should make a note of your account ID - perhaps in the same text file where you have stored your public and private testnet keys.

All Hedera IDs consist of three numbers (int64s) separated by the "." symbol. The three numbers represent Shard number, Realm number and Account number respectively. Shards and Realms are not yet in use so expect the first two numbers to be zeros.

You should also scroll down until you see a box labelled "Network" and make a note of the Address, which will be something like testnet.hedera.com:50222. You should also take note of the Node, which represents the account ID of a node on the testnet; it will be something like 0.0.3.

Your First Hedera Application

Checking your account balance

A more complete example, which includes code fragments in this section can be found in the get_account example file located in the examples folder of this repo. This simplified example is broken into bite-sized pieces here so that accompanying explanations can be seen in context alongside each fragment.

This explanation assumes that a "hello_future" (or equivalent) project has been created as explained in the "Building a new project for your Hedera Rust app" instructions earlier in this document. The following code samples are intended to represent the contents of the src/main.rs file therein.

Firstly the failure::Error and hedera::Client crates are imported.

The std::thread:sleep and std::time::Duration are also imported but commented out for now. These crates will be needed later in this example and can be un-commented when required by removing the preceding //. Uncommenting this import before the crates are used will result in an "unused import" warning when the code is run.

It's also useful to create a constant ONE_HBAR to represent the number of tinybars in one hbar:

use failure::Error;
use hedera::Client;
//use std::{thread::sleep, time::Duration};

fn main() -> Result<(), Error> {

  const ONE_HBAR: i64 = 100_000_000;

Create and set a my_account variable, replacing 1234 with your own Account ID from the portal. This is the account for which we will retrieve the balance.

  let my_account: String = "0.0.1234".parse()?;

All Hedera transactions and queries must be sent to a Hedera node. This can be specified using a node_account variable. Testnets provide each developer with a single node's account ID (labelled node in the portal) in order to simplify this and limit testnet infrastructure burden. On mainnet it will be the responsibility of the application to choose a node - usually at random.

The node account ID should look something like 0.0.3.You should use the ID you see in the portal in the following code:

  let node_account: String = "0.0.3".parse()?;

Node Account Defaults: When using testnets, it is worth noting that the SDK uses node account 0.0.3 by default – even when no node account is specified.

It is also important to specify which account is initiating this query – known as the operator account. In this case the operator account is the same account for which the balance is to be checked, so the same my_account variable can be used.

  let operator = my_account.parse()?;

Next, you need to establish a connection to the Hedera testnet using the Address you noted earlier from the network section shown on the Hedera portal. Make sure that you replace testnet.hedera.com:50222 with the equivalent address you copied from the portal.

Whilst establishing the connection to Hedera the private key of the operator account – in this case your account, so your private key – can be specified. This is required in order to authorise the payment of a small fee for the execution of this query. Be sure to replace <my-private-key> with the private key you generated near the start of these instructions.

  let client = Client::builder("testnet.hedera.com:50222")
    .node(node_account.parse()?)
    .operator(operator, || "<my-private-key>")
    .build()?;

Security Tip: In the get_account_balance example file located in the examples folder, the env::var("OPERATOR_SECRET") function is used. This retrieves the private key from an environment variable called OPERATOR_SECRET. It is good practice to use this technique, as it avoids accidental publication of private keys when storing code in public repos or sharing them accidentally via collaboration tools.

Don't forget: If someone else knows your private key, they effectively own your account! Although the impact of this is low when using testnets, it could be a very expensive mistake on mainnet. For purposes of clarity, the example code above has been simplified and does not use an environment variable.

At this point, you're ready to query your account balance. The client.account(operator).balance() constructs the request; adding .get() executes that request.

You can the output the balance using println! macro and end the program indicating success with Ok(()) and then closing the braces for fn main.

For illustrative purposes, we're showing the balance in tinybars and hbars. The Hedera SDKs represent all quantities for transfers and fees as integers using tinybars. There are one hundred million (100,000,000) tinybars in one hbar.

  let my_tinybars = client.account(operator).balance().get()?;
  let my_hbars: f64 = my_tinybars as f64 / ONE_HBAR as f64;
  println!("Account {} balance = {} tinybars", my_account, my_tinybars);
  println!("Account {} balance = {} hbars", my_account, my_hbars);

  Ok(())
}

You should now be able to run your first Hedera program by executing cargo run main.rs from terminal.

If everything went according to plan you should see something like this:

Account 1234 balance = 100500005000 tinybars
Account 1234 balance = 1005.00005 hbars

Testnet performance and throttling

For the present, our testnets have been throttled, allowing a limited number of Hedera transactions per account per second. We're using virtual infrastructure to support the huge demand we have had for testnet access, and prefer to foster innovative use of these resources and discourage folks from trying to generate metrics using underspecified hardware.

If you see error messages like transaction failed the pre-check: Busy, it is likely that you are exceeding these throttling thresholds. To avoid such errors, short delays can be added. To add a one second delay, for example, use the following code between transactions or queries:

 sleep(Duration::from_secs(1));

Don't forget to remove the // comment symbols before the use std:{....}, statement near the beginning of the program. This was disabled to prevent the "unused import" warning caused when the code is run before those crates are used in the code.

Enhance your application to check a friend's account balance

If you know the account ID of another account on your testnet – perhaps a friend or colleague – you can also check their balance.

Creating an additional testnet account

If your friends won’t share their accounts, or if you don’t have any friends, see the Create Account Example included in this repo.

If you do choose to create an account using that example, don't forget to do the following:

  1. Create a local environment variable OPERATOR_SECRET that contains your private key.
  2. Make sure that the node account ID matches the ID you see in the portal.
  3. Update the testnet.hedera.com:... testnet address to the correct one.
  4. Change the operator variable value to your own testnet account ID.
  5. Change the initial_balance to an acceptable quantity of testnet tinybars.
  sleep(Duration::from_secs(1));

  let friend_account: String = "0.0.1235".parse()?;

  let friend = friend_account.parse()?;

  let friend_tinybars = client.account(friend).balance().get()?;
  let friend_hbars: f64 = friend_tinybars as f64 / ONE_HBAR as f64;
  println!("Account {} balance = {} tinybars", friend_account, friend_tinybars);
  println!("Account {} balance = {} hbars", friend_account, friend_hbars);

Transaction and Query Fees

Note that your balance will decrease slightly each time you execute your code. This is due to the small fees associated with each query or transaction on the Hedera platform. On a testnet, this is not all that important, but it's worth keeping on mind when using mainnet.

Next step: Transferring hbars to a friend's account

let transfer_amount: i64 = 10 * ONE_HBAR;
  println!("Starting transfer of {} tinybars from Account {} to Account {}", transfer_amount, my_account, friend_account);
  let transaction_id = client
    .transfer_crypto()
    .transfer(operator, -1 * transfer_amount)
    .transfer(friend, transfer_amount)
    .memo("My first transfer of hbars! w00t!")
    .execute()?;

Explanation of the above code block by line number

1. let transaction_id = client declares a transaction_id variable and populates it with the result of this transaction.

2. transfer_crypto() specifies that the transaction will transfer hbars between accounts.

3. transfer(operator, -1 * transfer_amount) sets up part of the transfer. In this case from your account. Note that the * -1 makes the amount negative, denoting that hbars will be deducted from this account.

4. transfer(friend, transfer_amount) sets up the second part of this transfer. In this case to your friend's account. A positive number indicates that this account will be incremented by the specified amount.

5. memo("My first transfer of hbars! w00t!") assigns a label to the transaction of up to 100 bytes. Use of this field is at the developer's discretion and does not affect the behaviour of the plaform.

6. execute()?; executes the transaction.

Multi-party transfers

It is possible to create a transfer transaction containing multiple to and multiple _from_accounts within that same transaction. In a case where multiple accounts were to be debited, signatures would be required for each one, and additional .sign(...) lines would have to be added.

Important: the sum of all amounts in .transfer(...) lines contained within in a transfer_crypto transaction must add up to zero.

  println!("Transaction sent. Transaction ID is {}", transaction_id);

  sleep(Duration::from_secs(2));
  let receipt = client.transaction(transaction_id).receipt().get()?;
  if receipt.status == Status::Success {
    println!("Transaction Successful. Consensus confirmed.");
  } else {
    Err(format_err!(
      "Transaction unsuccessful. Status: {:?}",
      receipt.status
    ))?;
  }
  let my_tinybars = client.account(operator).balance().get()?;
  let my_hbars: f64 = my_tinybars as f64 / ONE_HBAR as f64;
  println!("Account {} balance = {} tinybars", my_account, my_tinybars);
  println!("Account {} balance = {} hbars", my_account, my_hbars);

  let friend_tinybars = client.account(friend).balance().get()?;
  let friend_hbars: f64 = friend_tinybars as f64 / ONE_HBAR as f64;
  println!("Account {} balance = {} tinybars", friend_account, friend_tinybars);
  println!("Account {} balance = {} hbars", friend_account, friend_hbars);
Account 1234 balance = 96495305000 tinybars
Account 1234 balance = 964.95305 hbars
Account 1235 balance = 4000000000 tinybars
Account 1235 balance = 40.00000 hbars
Transfering 1000000000 tinybars from Account 1234 to Account 1235
Transfer Sent. Transaction ID is 0:0:1234@1548679850.429332000
Transaction Successful. Consensus confirmed.
Account 1234 balance = 95494805000 tinybars
Account 1234 balance = 954.94805 hbars
Account 1235 balance = 5000000000 tinybars
Account 1235 balance = 50.00000 hbars

Other resources

Getting in touch

Please reach out to us on the Hedera discord channels. We're fortunate to have an active community of over 5000 like-minded devs, who are passionate about our tech. The Hedera Developer Advocacy team also participates actively.

Contributing to this Project

We welcome participation from all developers! For instructions on how to contribute to this repo, please review the Contributing Guide.

License Information

Licensed under Apache License, Version 2.0 – see LICENSE in this repo or apache.org/licenses/LICENSE-2.0