本教程將指導(dǎo)如何使用rust實現(xiàn)發(fā)送以太坊交易所需的代碼。
先決條件
我們假設(shè)您已經(jīng)擁有Rust IDE,并且具有Rust編程的合理知識。我們還假設(shè)一些關(guān)于以太坊的基本知識,并且不涉及以太坊事務(wù)的內(nèi)容等概念。
· Rust入門
· 以太坊101
庫的使用
本教程使用MIT許可的rust-web3庫。要在您的應(yīng)用程序中使用此庫,請將其添加到Cargo.toml文件中:
[dependencies]
web3 = { git = “https://github.com/tomusdrw/rust-web3” }
然后,您可以將庫添加到您的包中:
extern crate web3;
啟動以太坊節(jié)點
我們需要訪問我們可以發(fā)送事務(wù)的節(jié)點。在本教程中,我們使用ganache-cli,它允許您啟動個人以太坊網(wǎng)絡(luò),其中有許多未鎖定的和已資助的帳戶。
從ganache-cli安裝文檔中獲取,要使用npm進行安裝,請使用以下命令:
npm install -g ganache-cli
或者如果你喜歡用yarn命令
yarn global add ganache-cli
安裝后,運行下面的命令以啟動專用以太坊測試網(wǎng)絡(luò):
ganache-cli -d
注意,-d參數(shù)指示ganache cli始終以預(yù)先填充eth的相同帳戶開始。這在本教程的原始事務(wù)部分很有用,因為我們將知道這些帳戶的私鑰。
從節(jié)點管理帳戶發(fā)送事務(wù)
發(fā)送事務(wù)的最簡單方法是依靠連接的以太坊節(jié)點執(zhí)行事務(wù)簽名。這通常是一種不太安全的方法,因為它依賴于在節(jié)點上“unlock”帳戶。
use聲明
use web3::futures::Future;
use web3::types::{TransactionRequest, U256};
節(jié)點連接
let (_eloop, transport) = web3::transports::Http::new(
“http://localhost:8545”).unwrap();
let web3 = web3::Web3::new(transport);
首先,我們創(chuàng)建一個用于連接節(jié)點的傳輸對象。在這個例子中,我們通過http連接到端口8545上的localhost,這是Ganache的默認端口,以及大多數(shù)(如果不是全部)以太坊客戶端。
注意:還會返回EventLoop,但這超出了本指南的范圍。
接下來,我們構(gòu)造一個web3對象,傳入先前創(chuàng)建的傳輸變量,就是這樣!我們現(xiàn)在已連接到以太坊節(jié)點!
獲取帳戶詳細信息
GANACHE CLI自動解鎖多個賬戶,并使用100ETH為其提供資金,這對測試很有用。每次重新啟動時帳戶都不同,因此我們需要一種以編程方式獲取帳戶信息的方法:
let accounts = web3.eth().accounts().wait().unwrap();
通過web3.eth()獲得的Eth命名空間包含許多用于與以太坊節(jié)點交互的有用函數(shù)。通過accounts()獲取管理帳戶列表就是其中之一。它返回異步的未來,所以我們等待任務(wù)完成(wait()),并獲得結(jié)果(unwrap())。
發(fā)送交易
我們定義要通過TransactionRequest結(jié)構(gòu)發(fā)送的事務(wù)的參數(shù):
let tx = TransactionRequest {
from: accounts[0],
to: Some(accounts[1]),
gas: None,
gas_price: None,
value: Some(U256::from(10000)),
data: None,
nonce: None,
condition: None
};
此結(jié)構(gòu)中的大多數(shù)字段都是可選的,如果不手動指定,則使用合理的默認值。當我們發(fā)送簡單的ETH轉(zhuǎn)移事務(wù)時,數(shù)據(jù)字段為空,在此示例中,我們使用默認的gas和gas_price值。我們也沒有指定nonce,因為rust-web3庫默認情況下會向以太坊客戶端查詢最新的nonce值。該條件是rust-web3特定字段,允許您延遲發(fā)送事務(wù)直到滿足某個條件,例如達到特定的塊編號。
一旦啟動TransactionRequest,它就是一個發(fā)送交易的單行:
let tx_hash = web3.eth().send_transaction(tx).wait().unwrap();
TransactionRequest傳遞給Eth命名空間中的send_transaction(。.)函數(shù),該函數(shù)返回一個在廣播到網(wǎng)絡(luò)后完成的Future。完成后,Promise返回事務(wù)哈希Result,然后我們可以unwrap。
全部放在一起。..。..
extern crate web3;
use web3::futures::Future;
use web3::types::{TransactionRequest, U256};
fn main() {
let (_eloop, transport) = web3::transports::Http::new(“http://localhost:8545”).unwrap();
let web3 = web3::Web3::new(transport);
let accounts = web3.eth().accounts().wait().unwrap();
let balance_before = web3.eth().balance(accounts[1], None).wait().unwrap();
let tx = TransactionRequest {
from: accounts[0],
to: Some(accounts[1]),
gas: None,
gas_price: None,
value: Some(U256::from(10000)),
data: None,
nonce: None,
condition: None
};
let tx_hash = web3.eth().send_transaction(tx).wait().unwrap();
let balance_after = web3.eth().balance(accounts[1], None).wait().unwrap();
println!(“TX Hash: {:?}”, tx_hash);
println!(“Balance before: {}”, balance_before);
println!(“Balance after: {}”, balance_after);
}
我們使用web3.eth()。balance(。.)函數(shù)來獲取轉(zhuǎn)移前后收件人帳戶的余額,以證明轉(zhuǎn)移發(fā)生。運行此代碼,您應(yīng)該看到在事務(wù)發(fā)送后帳戶[1]余額超過10000 wei 。..成功的以太轉(zhuǎn)移!
發(fā)送原始交易
發(fā)送原始事務(wù)意味著在Rust端而不是在節(jié)點上使用私鑰對事務(wù)進行簽名。然后,該節(jié)點將此事務(wù)轉(zhuǎn)發(fā)到以太坊網(wǎng)絡(luò)。
ethereum-tx-sign庫可以幫助我們進行這種脫鏈簽名,但由于缺少共享結(jié)構(gòu),因此不容易與rust-web3一起使用。在本指南的這一部分中,我將解釋如何讓這些庫很好地協(xié)同工作。
使用的其他庫
在構(gòu)造RawTransaction時,ethereum-tx-sign庫依賴于以太它類型庫。我們還使用十六進制庫將十六進制私鑰轉(zhuǎn)換為字節(jié)。
將這些條目添加到cargo.toml文件中:
ethereum-tx-sign = “0.0.2”
ethereum-types = “0.4”
hex = “0.3.1”
然后,您可以將它們添加到您的包中:
extern crate ethereum_tx_sign;
extern crate ethereum_types;
extern crate hex;
簽署交易
ethereum_tx_sign庫包含一個RawTransaction結(jié)構(gòu),我們可以在初始化后用它來簽署以太坊事務(wù)。初始化是棘手的部分,因為我們需要在rust-web3和ethereum_types結(jié)構(gòu)之間進行轉(zhuǎn)換。
一些轉(zhuǎn)換函數(shù)可以將由rust-web3函數(shù)返回的web3 ::類型的H160(對于以太坊帳戶地址)和U256(對于nonce值)結(jié)構(gòu)轉(zhuǎn)換為由ethereum-tx-sign預(yù)期的theherehere_types:
fn convert_u256(value: web3::types::U256) -》 U256 {
let web3::types::U256(ref arr) = value;
let mut ret = [0; 4];
ret[0] = arr[0];
ret[1] = arr[1];
U256(ret)
}
fn convert_account(value: web3::types::H160) -》 H160 {
let ret = H160::from(value.0);
ret
}
我們現(xiàn)在可以構(gòu)造一個RawTransaction對象(替換下面的代碼,讓balance_before):
let nonce = web3.eth().transaction_count(accounts[0], None).wait().unwrap();
let tx = RawTransaction {
nonce: convert_u256(nonce),
to: Some(convert_account(accounts[1])),
value: U256::from(10000),
gas_price: U256::from(1000000000),
gas: U256::from(21000),
data: Vec::new()
};
請注意,構(gòu)造RawTransaction時不會自動計算nonce。我們需要通過調(diào)用Eth命名空間中的transaction_count函數(shù)來獲取發(fā)送帳戶的nonce。隨后需要將此值轉(zhuǎn)換為RawTransaction期望的格式。
與TransactionRequest結(jié)構(gòu)不同,我們還必須手動提供一些合理的gas和gas_price值。
獲取私鑰
簽名之前,我們需要訪問用于簽名的私鑰。在這個例子中,我們硬編碼ganache中第一個ETH填充帳戶的私鑰(記得以-d參數(shù)開頭)。這可以用于測試,但是您不應(yīng)該在生產(chǎn)環(huán)境中公開私鑰!
fn get_private_key() -》 H256 {
// Remember to change the below
let private_key = hex::decode(
“4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7
d21715b23b1d”).unwrap();
return H256(to_array(private_key.as_slice()));
}
fn to_array(bytes: &[u8]) -》 [u8; 32] {
let mut array = [0; 32];
let bytes = &bytes[。.array.len()];
array.copy_from_slice(bytes);
array
}
hex:decode函數(shù)將十六進制字符串(確保刪除0x前綴)轉(zhuǎn)換為Vec 《u8》,但RawTransction的sign函數(shù)采用ethereum_types :: H256格式的私鑰。不幸的是,h256在構(gòu)建期間采用的是[u8;32]而不是vec《t》,因此我們需要進行另一個轉(zhuǎn)換!
私鑰作為切片傳遞給to_array,然后將此切片轉(zhuǎn)換為[u8:32]。
簽名
既然我們有了一個以正確格式返回私鑰的函數(shù),那么我們可以通過調(diào)用以下命令來對事務(wù)進行簽名:
let signed_tx = tx.sign(&get_private_key());
發(fā)送交易
簽署后,向以太坊網(wǎng)絡(luò)廣播交易也是一條一行程序:
let tx_hash = web3.eth().send_raw_transaction(Bytes::from(signed_tx)).wait().unwrap()
注意,我們必須在這里進行另一次轉(zhuǎn)換!send_raw_transaction將Bytes值作為參數(shù),而RawTransaction的sign函數(shù)返回Vec 《u8》。 幸運的是,這種轉(zhuǎn)換很容易,因為bytes結(jié)構(gòu)有一個現(xiàn)成的from特性,可以從vec《u8》轉(zhuǎn)換。
與send_transaction等效項一樣,此函數(shù)返回Future,后者又返回一個Result對象,該對象包含完成時廣播事務(wù)的事務(wù)哈希。
把它們放在一起
extern crate web3;
extern crate ethereum_tx_sign;
extern crate ethereum_types;
extern crate hex;
use web3::futures::Future;
use web3::types::Bytes;
use ethereum_tx_sign::RawTransaction;
use ethereum_types::{H160,H256,U256};
fn main() {
let (_eloop, transport) = web3::transports::Http::new(“http://localhost:8545”).unwrap();
let web3 = web3::Web3::new(transport);
let accounts = web3.eth().accounts().wait().unwrap();
let balance_before = web3.eth().balance(accounts[1], None).wait().unwrap();
let nonce = web3.eth().transaction_count(accounts[0], None).wait().unwrap();
let tx = RawTransaction {
nonce: convert_u256(nonce),
to: Some(convert_account(accounts[1])),
value: U256::from(10000),
gas_price: U256::from(1000000000),
gas: U256::from(21000),
data: Vec::new()
};
let signed_tx = tx.sign(&get_private_key());
let tx_hash = web3.eth().send_raw_transaction(Bytes::from(signed_tx)).wait().unwrap();
let balance_after = web3.eth().balance(accounts[1], None).wait().unwrap();
println!(“TX Hash: {:?}”, tx_hash);
println!(“Balance before: {}”, balance_before);
println!(“Balance after: {}”, balance_after);
}
fn get_private_key() -》 H256 {
let private_key = hex::decode(
“4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d”).unwrap();
return H256(to_array(private_key.as_slice()));
}
fn convert_u256(value: web3::types::U256) -》 U256 {
let web3::types::U256(ref arr) = value;
let mut ret = [0; 4];
ret[0] = arr[0];
ret[1] = arr[1];
U256(ret)
}
fn convert_account(value: web3::types::H160) -》 H160 {
let ret = H160::from(value.0);
ret
}
fn to_array(bytes: &[u8]) -》 [u8; 32] {
let mut array = [0; 32];
let bytes = &bytes[。.array.len()];
array.copy_from_slice(bytes);
array
}
總結(jié)
在本教程中,我們學(xué)習(xí)了如何使用Rust將基本以太網(wǎng)值轉(zhuǎn)移事務(wù)從一個帳戶發(fā)送到另一個帳戶。我們解釋了兩種簽名方法:通過解鎖帳戶在節(jié)點上簽名,以及在Rust端簽署一個事務(wù)。
評論
查看更多