Written by: Camilo Soto & Shoaib Ahmed, IN3 Software Developers
Share on:
The popularity of Rust as a reliable and efficient programming language continues to rise. It has now become one of the most popular languages (including its use in blockchain programming), due to its focus on explicitness and correctness. As passionate technologists and geeks, the IN3 (Incubed) team at Blockchains wants to give the growing Rust community the opportunity to try it out for IN3!
IN3 is the first blockchain client of its kind. Also known as a “minimal verification client,” IN3 allows individuals to simultaneously connect their internet of things (IoT) devices to multiple blockchain networks, supporting the decentralization of application programming interfaces (APIs). At its core, the IN3 library implements a JSON-RPC client which delivers verifiable responses by leveraging the power of cryptographic proofs that are provided by IN3 nodes as defined by the IN3 protocol.
According to the 2020 Stack Overflow developer survey, Rust is…”The Beloved.”
Rust held onto its spot as the most beloved language among the professional developers we surveyed.
2020 Stack Overflow Developer Survey
In short, Rust is amazing and there are plenty of reasons for that claim to be true among the community:
With these features, Rust has also consolidated its position in the blockchain space. It is no mere coincidence that two of the most performant clients for Ethereum 1.0 and 2.0 are written in Rust.
Last but not least, Rust has recently become the first formal (and machine-checked) safety proof for a language representing a realistic subset of Rust as part of the project RustBelt: Logical Foundations for the Future of Safe Systems Programming from the Max Planck Institute for Software Systems (MPI-SWS) in Saarbruecken, Germany. This is a great milestone for programming languages and one of the hardest topics that is also part of the state of the art research in the blockchain community.
Yes, the first IN3 client was written in C, so we decided to support Rust and still keep our reliable and rigorously tested C source code.
Top-notch quality assurance is very important to us, the IN3 Rust API features these core milestones achieved:
We followed best practices and patterns for writing idiomatic Rust to create low-level bindings and expose the greatness of our C client with an easy to use API. To achieve this we created two crates:
Just follow the next few steps and you are good to go!
Add IN3 and futures_executor (or just any executor of your choice) to your cargo manifest. The in3-rs API is asynchronous and Rust doesn’t have any built-in executors so we need to choose one, and we decided futures_executor is a very good option as it is lightweight and practical to use.
[package]
name = "in3-examples"
version = "0.1.0"
authors = ["
BlockchainsLLC
"]
edition = "2018"
[dependencies]
in3 = "0.1.7"
futures-executor = "0.3.5"
Let’s begin with the ‘hello-world’ equivalent of the Ethereum JSON-RPC API – eth_blockNumber. This call returns the number of the most recent block in the blockchain. Here’s the complete program:
use in3::eth1::Api;
use in3::prelude::*;
fn main() -> In3Result<()> {
let client = Client::new(chain::MAINNET);
let mut eth_api = Api::new(client);
let number = futures_executor::block_on(eth_api.block_number())?;
println!("Latest block number => {:?}", number);
Ok(())
}
Now, let’s go through this program line-by-line. We start by creating a JSON-RPC capable Incubed Client instance for the Ethereum mainnet chain.
let client = Client::new(chain::MAINNET);
This client is then used to instantiate an Ethereum Api instance which implements the Ethereum JSON-RPC API spec.
let mut eth_api = Api::new(client);
From here, getting the latest block number is as simple as calling the block_number()
function on the Ethereum API instance. As specified before, we need to use futures_executor::block_on
to run the future returned by block_number()
to completion on the current thread.
let number = futures_executor::block_on(eth_api.block_number())?;
A complete list of supported functions can be found on the in3-rs crate documentation page at docs.rs.
- eth_getBlockByNumber:
use in3::eth1::{Api, BlockNumber};
use in3::prelude::*;
fn main()-> In3Result<()> {
// configure client and API
let mut eth_api = Api::new(Client::new(chain::MAINNET));
// get latest block
let block: futures executor::block_on(eth_api.get_block_by_number(BlockNumber::Latest, false))?;
println!("Block => {:?}", block);
Ok(())
}
In this case, we are reading the number of nodes that are registered in the IN3 network deployed on the Ethereum Mainnet at 0x2736D225f85740f42D17987100dc8d58e9e16252.
use in3::eth1::{abi, abi::*, Api, BlockNumber, CallTransaction};
use in3::json_rpc::json;
use in3::prelude::*;
fn main() {
// configure client and API
let mut eth_api = Api::new(Client::new(chain::MAINNET));
// setup Incubed contract address
let contract: Address =
json::from_str(r#""0x2736D225f85740f42D17987100dc8d58e9e16252""#).unwrap(); // cannot fail
// instantiate an abi encoder for the contract call
let mut abi = abi::In3EthAbi::new();
// setup the signature to call in this case we are calling totalServers():uint256 from in3-nodes contract
let params = futures_executor::block_on(abi.encode("totalServers():uint256", json::json!([])))
.expect("failed to ABI encode params");
// setup the transaction with contract and signature data
let txn = CallTransaction {
to: Some(contract),
data: Some(params),
..Default::default()
};
// execute asynchronous api call
let output: Bytes =
futures_executor::block_on(eth_api.call(txn, BlockNumber::Latest))
.expect("ETH call failed");
// decode the Bytes output and get the result
let output =
futures_executor::block_on(abi.decode("uint256", output))
.expect("failed to ABI decode output");
let total_servers: U256 = json::from_value(output).unwrap(); // cannot fail if ABI decode succeeds
println!("{:?}", total_servers);
}
IPFS is a protocol and peer-to-peer network for storing and sharing data in a distributed file system.
use in3::ipfs::Api;
use in3::prelude::*;
fn main() {
let mut ipfs_api = Api::new(Client::new(chain::IPFS));
match futures_executor::block_on(ipfs_api.put("incubed meets rust".as_bytes().into())) {
Ok(res) => println!("The hash is {:?}", res),
Err(err) => println!("Failed with error: {:?}", err),
}
}
Head over to our sample project on GitHub or simply run:
$ git clone
https://github.com/blockchainsllc/in3-example-rust.git
$ cd in3-example-rust
$ cargo run
Head over to the Rust directory in the IN3-C repository on GitHub, you will find more examples to explore there.
Thank you for your interest in using IN3 for Rust. We hope you enjoy writing Rust IN3 applications and we cannot wait to hear from you. For questions and feedback, feel free to reach out to our Gitter IN3 channel.
If you are interested, we also provide IN3 clients for Wasm, Java, Android, .net, and Python.
For the complete IN3 technical documentation, visit ReadTheDocs.