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.
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 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 hbarsTinybars 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.
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.
Rust – can be downloaded using these instructions.
rustup
is highly recommended as it facilitates version and dependency management.cargo --version
You should see version 1.32.0
or higher. If this command fails, the most likely reason relates to the PATH
environment variable as explained in the instructions.
rust-toolchain
file in this repo currently makes this project dependent on a nightly build of Rust. Users who choose manage this project using rustup
should not notice this, as the correct version of Rust will be installed transparently as required. This dependency will be removed when the asynchronous I/O packages used by this SDK are deemed stable, which is expected to happen in March 2019.protoc
, must be installed and available to the system. To confirm that protobuf compiler is available: execute the following command from a terminal window:protoc --version
You should see version 3.3.x
or later. If the command fails, it is likely that the protoc
executable is not included in the PATH
environment variable, or perhaps absent from your system. It can be installed as explained in the readme.
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.
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.
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.
For those who have already cloned this github repo, running the following command from the root folder of the SDK in a terminal window should be sufficient to retrieve all required packages:
cargo build
If you're having trouble, please review the Prerequisites section.
You can use the Hedera SDK for Rust from your own project without the need to clone the github repo.
To set up a new skeletal project in a new folder, run the following commands from a terminal window, replacing hello-future
with the name of the project (folder) you want to create:
cargo install cargo-edit
cargo new hello-future
cargo add hedera
cargo-edit
is a tool that extends cargo
to allow command-line manipulation of the Cargo.toml
file. See here for more information.
Copy the rust-toolchain
file from this repository into the new project folder. This ensures that the latest nightly build of Rust is used by your project. Explanation of this requirement can be found in the Prerequisites section.
Add your own code to the /src/main.rs
file in your new project folder.
The examples in this document use the failure
crate (see details here) for error handling. To add this dependency to your project from a terminal window, run the following command:
cargo add failure
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>
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.
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
.
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
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 theuse 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.
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:
- Create a local environment variable OPERATOR_SECRET that contains your private key.
- Make sure that the node account ID matches the ID you see in the portal.
- Update the
testnet.hedera.com:...
testnet address to the correct one.- Change the
operator
variable value to your own testnet account ID.- Change the
initial_balance
to an acceptable quantity of testnet tinybars.
For the purposes of this example, an Account ID of 0.0.1235
will be used for that second account. Don't forget to amend 1235
to the account number of your friend's account. If you forget to do this will you will probably see a transaction failed the pre-check: InvalidAccount
message.
To continue with this example, add the next code code block into your existing main.rs
file, just before the Ok(())
statement.
As mentioned above, we will add a small delay to ensure that we do not exceed testnet throttling limits. For brevity, this statement will be included without further comment in all subsequent examples. If you get an error here, you need to remove the //
comment symbols before the use std:{....},
statement near the beginning of the program.
Before executing any transfers, you can initialise a second variable friend_account
representing the second account, query its balance and output the result.
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);
Run the program again by executing cargo run main.rs
from terminal.
You should see your balance followed by your friend's balance.
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.
transfer_amount
variable can be used to make the next steps more readable. In this case, we'll transfer 10 ℏ and output details of the intended transaction.let transfer_amount: i64 = 10 * ONE_HBAR;
println!("Starting transfer of {} tinybars from Account {} to Account {}", transfer_amount, my_account, friend_account);
It is worth re-stating that a secret key (also known as private key) is required in order to transfer hbars from an account. Since the operator private key has already been set for this client session, it is not necessary to sign this transaction explicitly.
The next statement is a little more complex so each line is explained individually below the code.
let transaction_id = client
.transfer_crypto()
.transfer(operator, -1 * transfer_amount)
.transfer(friend, transfer_amount)
.memo("My first transfer of hbars! w00t!")
.execute()?;
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 atransfer_crypto
transaction must add up to zero.
The transaction_id_
variable should now contain a reference to this transfer transaction. A transaction ID is made up of the account ID and the transaction timestamp – right down to nanoseconds.
It makes sense to wait a little longer (2 seconds) after sending the transaction, so that the Hedera network can reach consensus on the transaction.
println!("Transaction sent. Transaction ID is {}", transaction_id);
sleep(Duration::from_secs(2));
receipt
can be requested, so we can define a corresponding variable. Although this is not a mandatory step, it does verify that this transaction successfully reached network consensus. 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);
Run the program again by executing cargo run main.rs
from terminal.
You should now see both balances prior to the transfer followed by details of the transfer including success/failure. You should then be able to see the balances of both accounts after the transfer, demonstrating that 10 ℏ has been transferred from your account to you friend's account. Hopefully it looks something like this:
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
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.
We welcome participation from all developers! For instructions on how to contribute to this repo, please review the Contributing Guide.
Licensed under Apache License, Version 2.0 – see LICENSE in this repo or apache.org/licenses/LICENSE-2.0