• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Rust 和 Elixir 读取以太坊和其他 EVM 智能合约函数式编程和区块链

武飞扬头像
chinadefi
帮助2

Rust 和 Elixir 如何读取以太坊和其他 EVM 智能合约:函数式编程和区块链

学新通

学新通

本系列将重点介绍两种函数式编程语言:Rust&Elixir。本篇分享函数式编程的思想和实践。

在这篇文章中将展示Elixir&Rust读取以太坊智能合约的功能。重要的是,该程序不仅在以太坊上工作,而且还在任何支持EVM的区块链上工作,例如,Polkadot上的Moonbeam !@_@

0x01 Ethereumex & ExABI

我更喜欢 Elixir 的两个存储库是 Ethereumex:用于以太坊区块链的 Elixir JSON-RPC 客户端。

ExABI:Solidity的应用程序二进制接口(ABI)描述了如何将二进制数据转换为Solidity编程语言能够理解的类型。

ABI 小贴士:

ABI(应用程序二进制接口)是计算机科学中两个程序模块之间的接口。

它与API(应用程序接口)非常相似,API是代码接口的可读表示形式。ABI定义了用于与二进制合约交互的方法和结构,就像API那样,只是在更低的层次上。

.abi文件包含了json格式的函数接口和事件的描述。

这是 HelloWorld.sol 的示例 ABI:

[{
 "constant": true,
 "inputs": [],
 "name": "get",
 "outputs": [{
     "name": "",
     "type": "string"
   }
 ],
 "payable": false,
 "stateMutability": "view",
 "type": "function"
}]

Ethereumex 的配置

首先,让我们将 Ethereumex 添加到 mix.exs 中的 depsand 应用领域!

# mix.exs:
def application do
  [
    mod: {TaiShang.Application, []},
    extra_applications: [:logger, :runtime_tools, :ethereumex]
  ]
end
……
defp deps do
	[
		 {:ethereumex, "~> 0.7.0"}
	]
end

然后,在config/config.exs中。将以太坊协议主机参数添加到配置文件中:

# config.exs
config :ethereumex,
  url: "http://localhost:8545" # node url

Tx结构

在 Elixir 中显示

通过代码很容易理解Elixir中的Struct。

以太坊的tx在Elixir中显示:

Transaction{
  nonce: nonce, # counter to ensure the sequence of txs
  gas_price: @gas.price, # gas fee
  gas_limit: @gas.limit, # gas gas limit
  to: bin_to, # addr in binary
  value: 0, # the eth u are going to send
  init: <<>>, # bytecode
  data: data # the data u are going to send
}

我们刚刚读取了以太坊中的数据,因此随机数是没有用的。只有在我们把数据写进合约的时候才需要更改碎随机数。

eth_call

立即执行一个新的消息调用,而不需要在区块链上创建交易。

参数

Object -交易调用对象

from: DATA, 20 Bytes -(可选)交易发送的地址

to: DATA, 20 Bytes -交易被指向到的地址

gas: QUANTITY -(可选)为交易执行提供的gas整数。eth_call消耗零gas,但某些执行可能需要这个参数

gasPrice: QUANTITY -(可选)每一种付费gas使用的gasPrice的整数

value: QUANTITY -(可选)与该交易一起发送的值的整数

data: DATA -(可选)方法签名和编码参数的哈希值

QUANTITY|TAG -整数区块号,或字符串"latest", “earliest"或"pending”,参见默认区块参数

返回

DATA -已执行合约的返回值。

例子

// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_call","params":[{see above}],"id":1}'

// Result
{
  "id":1,
  "jsonrpc": "2.0",
  "result": "0x"
}

gas的机制对新人不友好,所以我们现在可以设置gas_price和gas_limit为一个特定的数字:

@gas %{price: 0, limit: 300_000}

在 Rust 中显示

它是 Rust 中的一个类似结构:

/// from: https://kauri.io/#collections/A Hackathon Survival Guide/sending-ethereum-transactions-with-rust/
let tx = TransactionRequest {
        from: accounts[0],
        to: Some(accounts[1]),
        gas: None, // gaslimit
        gas_price: None,
        value: Some(U256::from(10000)),
        data: None,
        nonce: None,
        condition: None
    };

现在我们应该处理tx的两个参数:

to & data。

地址的字符串到二进制

区块链中使用的地址(如0x769699506f972A992fc8950C766F0C7256Df601f)可以在Elixir程序中转换为二进制:

@spec addr_to_bin(String.t()) :: Binary.t()
def addr_to_bin(addr_str) do
  addr_str
  |> String.replace("0x", "")
  |> Base.decode16!(case: :mixed)
end

智能合约功能到数据

我们希望通过以太坊函数和参数列表的字符串样式生成数据:

@spec get_data(String.t(), List.t()) :: String.t()
def get_data(func_str, params) do
  payload =
  func_str
  |> ABI.encode(params)
  |> Base.encode16(case: :lower)

  "0x" <> payload

“以太坊函数的字符串样式”示例:

@func %{
    balance_of: "balanceOf(address)",
    token_of_owner_by_index: "tokenOfOwnerByIndex(address, uint256)",
    token_uri: "tokenURI(uint256)",
    get_evidence_by_key: "getEvidenceByKey(string)",
    new_evidence_by_key: "newEvidenceByKey(string, string)",
    mint_nft: "mintNft(address, string)",
    owner_of: "ownerOf(uint256)"
    }

eth函数的字符串样式抽象为"function_name(param_type1, param_type2,…)"

深入了解encode函数的实现是很好的!

def encode(function_signature, data, data_type \\ :input)

# string type of function to function_selector
# then call encode function again with function_selector
def encode(function_signature, data, data_type) when is_binary(function_signature) do
  function_signature
  |> Parser.parse!()
  |> encode(data, data_type)
end

def encode(%FunctionSelector{} = function_selector, data, data_type) do
  TypeEncoder.encode(data, function_selector, data_type)
end

FunctionSelector的结构:

iex(5)> ABI.Parser.parse!("baz(uint8)")
I.FunctionSelector{
  function: "baz",
  input_names: [],
  inputs_indexed: nil,
  method_id: nil,
  returns: [],
  type: nil,
  types: [uint: 8]
}

TypeEncoder.encode 的工作是编译数据,function_selector 和 data_type 转换为数据。

智能合约响应的翻译器

在 Elixir 中编写一个 TypeTransalator 将十六进制数据更改为普通数据用于智能合约的响应是好的:

defmodule Utils.TypeTranslator do
  ……

  def data_to_int(raw) do
    raw
    |> hex_to_bin()
    |> ABI.TypeDecoder.decode_raw([{:uint, 256}])
    |> List.first()
  end

  def data_to_str(raw) do
    raw
    |> hex_to_bin()
    |> ABI.TypeDecoder.decode_raw([:string])
    |> List.first()
  end

  def data_to_addr(raw) do
    addr_bin =
      raw
      |> hex_to_bin()
      |> ABI.TypeDecoder.decode_raw([:address])
      |> List.first()

    "0x" <> Base.encode16(addr_bin, case: :lower)
  end

……
end
学新通

我们要选择的函数是基于响应的类型,我们可以在ABI中获取它:

{
    "constant": true,
    "inputs": [],
    "name": "get",
    "outputs": [{
        "name": "",
        "type": "string"  # The response is string!
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
}

Elixir中的调用者

这是最后一步!只要把上面的功能混合在一起,智能合约的数据读取就可以工作了!

例如:读取ERC20代币的余额:

@spec balance_of(String.t(), String.t()) :: Integer.t()
def balance_of(contract_addr, addr_str) do
  {:ok, addr_bytes} = TypeTranslator.hex_to_bytes(addr_str)
  data = get_data("balanceOf(address)", [addr_bytes])

  {:ok, balance_hex} =
    Ethereumex.HttpClient.eth_call(%{ # the tx is encapsulated by ethereumex.
    data: data,
    to: contract_addr
  })

	TypeTranslator.data_to_int(balance_hex)
end

Rust 中的调用者

最后一个是调用以太坊的例子

extern crate hex;
use hex_literal::hex;

use web3::{
    contract::{Contract, Options},
    types::{U256, H160, Bytes},
};

#[tokio::main]
async fn main() -> web3::contract::Result<()> {
    let _ = env_logger::try_init();
    let http = web3::transports::Http::new("https://ropsten.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161")?;
    let web3 = web3::Web3::new(http);

    let addr_u8 = hex::decode("7Ad11de6d4C3DA366BC929377EE2CaFEcC412A10").expect("Decoding failed");
    let addr_h160 = H160::from_slice(&addr_u8);

    let contra = Contract::from_json(
        web3.eth(),
        addr_h160,
        include_bytes!("../contracts/hello_world.json"),
    )?;

    // let acct:[u8; 20] = hex!("f24ff3a9cf04c71dbc94d0b566f7a27b94566cac").into();
    
    let result = contra.query::<String, _, _,_>("get", (), None, Options::default(), None).await?;
    println!("{}", result);

    Ok(())
}
学新通

Source:https://hackernoon.com/how-rust-and-elixir-read-ethereum-and-other-evm-smart-contracts-functional-programming-and-blockchain

关于

ChinaDeFi - ChinaDeFi.com 是一个研究驱动的DeFi创新组织,同时我们也是区块链开发团队。每天从全球超过500个优质信息源的近900篇内容中,寻找思考更具深度、梳理更为系统的内容,以最快的速度同步到中国市场提供决策辅助材料。如果您是从业者希望获得更多海外最新技术方案及项目信息,可以查看Gavin的“每日文章”笔记 https://day.chinadefi.com(内容可能比较生涩,但是相信可以带来帮助)。

学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgbhiba
系列文章
更多 icon
同类精品
更多 icon
继续加载