區塊鏈研究實驗室|使用Rust實現傳送以太坊交易

買賣虛擬貨幣


本教程將指導如何使用rust實現傳送以太坊交易所需的程式碼。

先決條件

我們假設您已經擁有Rust IDE,並且具有Rust程式設計的合理知識。我們還假設一些關於以太坊的基本知識,並且不涉及以太坊事務的內容等概念。

  • Rust入門

  • 以太坊101

庫的使用

本教程使用MIT許可的rust-web3庫。要在您的應用程式中使用此庫,請將其新增到Cargo.toml檔案中:

[dependencies]
web3 = { git = "https://github.com/tomusdrw/rust-web3" }

然後,您可以將庫新增到您的包中:

extern crate web3;

啟動以太坊節點

我們需要訪問我們可以傳送事務的節點。在本教程中,我們使用ganache-cli,它允許您啟動個人以太坊網路,其中有許多未鎖定的和已資助的帳戶。

從ganache-cli安裝文件中獲取,要使用npm進行安裝,請使用以下命令:

npm install -g ganache-cli

或者如果你喜歡用yarn命令

yarn globaladd ganache-cli

安裝後,執行下面的命令以啟動專用以太坊測試網路:

ganache-cli -d

注意,-d引數指示ganache cli始終以預先填充eth的相同帳戶開始。這在本教程的原始事務部分很有用,因為我們將知道這些帳戶的私鑰。

從節點管理帳戶傳送事務

傳送事務的最簡單方法是依靠連線的以太坊節點執行事務簽名。這通常是一種不太安全的方法,因為它依賴於在節點上“unlock”帳戶。

use宣告

use web3::futures::Future;
use web3::types::{TransactionRequest, U256};

節點連線

let (_eloop, transport) = web3::transports::Http::new(  "http://localhost:8545").unwrap();

let web3 = web3::Web3::new(transport);

首先,我們建立一個用於連線節點的傳輸物件。在這個例子中,我們透過http連線到埠8545上的localhost,這是Ganache的預設埠,以及大多數(如果不是全部)以太坊客戶端。

注意:還會返回EventLoop,但這超出了本指南的範圍。

接下來,我們構造一個web3物件,傳入先前建立的傳輸變數,就是這樣!我們現在已連線到以太坊節點!

獲取帳戶詳細資訊

GANACHE CLI自動解鎖多個賬戶,並使用100ETH為其提供資金,這對測試很有用。每次重新啟動時帳戶都不同,因此我們需要一種以程式設計方式獲取帳戶資訊的方法:

let accounts = web3.eth().accounts().wait().unwrap();

透過web3.eth()獲得的Eth名稱空間包含許多用於與以太坊節點互動的有用函式。透過accounts()獲取管理帳戶列表就是其中之一。它返回非同步的未來,所以我們等待任務完成(wait()),並獲得結果(unwrap())。

傳送交易

我們定義要透過TransactionRequest結構傳送的事務的引數:

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
    };

此結構中的大多數字段都是可選的,如果不手動指定,則使用合理的預設值。當我們傳送簡單的ETH轉移事務時,資料欄位為空,在此示例中,我們使用預設的gas和gas_price值。我們也沒有指定nonce,因為rust-web3庫預設情況下會向以太坊客戶端查詢最新的nonce值。該條件是rust-web3特定欄位,允許您延遲傳送事務直到滿足某個條件,例如達到特定的塊編號。

一旦啟動TransactionRequest,它就是一個傳送交易的單行:

let tx_hash = web3.eth().send_transaction(tx).wait().unwrap();

TransactionRequest傳遞給Eth名稱空間中的send_transaction(..)函式,該函式返回一個在廣播到網路後完成的Future。完成後,Promise返回事務雜湊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(..)函式來獲取轉移前後收件人帳戶的餘額,以證明轉移發生。執行此程式碼,您應該看到在事務傳送後帳戶[1]餘額超過10000 wei ...成功的以太轉移!

傳送原始交易

傳送原始事務意味著在Rust端而不是在節點上使用私鑰對事務進行簽名。然後,該節點將此事務轉發到以太坊網路。

ethereum-tx-sign庫可以幫助我們進行這種脫鏈簽名,但由於缺少共享結構,因此不容易與rust-web3一起使用。在本指南的這一部分中,我將解釋如何讓這些庫很好地協同工作。

使用的其他庫

在構造RawTransaction時,ethereum-tx-sign庫依賴於以太它型別庫。我們還使用十六進位制庫將十六進位制私鑰轉換為位元組。

將這些條目新增到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結構,我們可以在初始化後用它來簽署以太坊事務。初始化是棘手的部分,因為我們需要在rust-web3和ethereum_types結構之間進行轉換。

一些轉換函式可以將由rust-web3函式返回的web3 ::型別的h260(對於以太坊帳戶地址)和U256(對於nonce值)結構轉換為由ethereum-tx-sign預期的theherehere_types:

fn convert_u256(value: web3::types::U256) -> U256 {
    let web3::types::U256(ref arr) = value;
    let mut ret = [04];
    ret[0] = arr[0];
    ret[1] = arr[1];
    U256(ret)
}

fn convert_account(value: web3::types::h260) -> h260 {
    let ret = h260::from(value.0);
    ret
}

我們現在可以構造一個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()
};

請注意,構造RawTransaction時不會自動計算nonce。我們需要透過呼叫Eth名稱空間中的transaction_count函式來獲取傳送帳戶的nonce。隨後需要將此值轉換為RawTransaction期望的格式。

與TransactionRequest結構不同,我們還必須手動提供一些合理的gas和gas_price值。

獲取私鑰

簽名之前,我們需要訪問用於簽名的私鑰。在這個例子中,我們硬編碼ganache中第一個ETH填充帳戶的私鑰(記得以-d引數開頭)。這可以用於測試,但是您不應該在生產環境中公開私鑰!

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 = [032];
    let bytes = &bytes[..array.len()];
array.copy_from_slice(bytes);
array
}

hex:decode函式將十六進位制字串(確保刪除0x字首)轉換為Vec <u8>,但RawTransction的sign函式採用ethereum_types :: H256格式的私鑰。不幸的是,h256在構建期間採用的是[u8;32]而不是vec<t>,因此我們需要進行另一個轉換!

私鑰作為切片傳遞給to_array,然後將此切片轉換為[u8:32]。

簽名

既然我們有了一個以正確格式返回私鑰的函式,那麼我們可以透過呼叫以下命令來對事務進行簽名:

let signed_tx = tx.sign(&get_private_key());

傳送交易

簽署後,向以太坊網路廣播交易也是一條一行程式:

let tx_hash = web3.eth().send_raw_transaction(Bytes::from(signed_tx)).wait().unwrap()

注意,我們必須在這裡進行另一次轉換!send_raw_transaction將Bytes值作為引數,而RawTransaction的sign函式返回Vec <u8>。 幸運的是,這種轉換很容易,因為bytes結構有一個現成的from特性,可以從vec<u8>轉換。

與send_transaction等效項一樣,此函式返回Future,後者又返回一個Result物件,該物件包含完成時廣播事務的事務雜湊。

把它們放在一起

extern crate web3;
extern crate ethereum_tx_sign;
extern crate ethereum_types;
extern crate hex;

useweb3::futures::Future;
useweb3::types::Bytes;
useethereum_tx_sign::RawTransaction;
useethereum_types::{h260,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 = [04];
    ret[0] = arr[0];
    ret[1] = arr[1];
    U256(ret)
}

fn convert_account(value: web3::types::h260) -> h260 {
    let ret = h260::from(value.0);
    ret
}

fn to_array(bytes: &[u8]) -> [u8; 32] {
    let mut array = [032];
    let bytes = &bytes[..array.len()];
array.copy_from_slice(bytes);
array
}

總結

在本教程中,我們學習瞭如何使用Rust將基本乙太網值轉移事務從一個帳戶傳送到另一個帳戶。我們解釋了兩種簽名方法:透過解鎖帳戶在節點上簽名,以及在Rust端簽署一個事務。

描下方二維碼新增我,拉您進入技術交流群

關注一下,精彩不停

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;