首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

区块链应用与以太坊的交互

编程知识
2024年09月11日 16:21

我们要谈的交互

首先要明确一点,以太坊是一个去中心化的平台,他不可能为了某个项目而新增交互接口。
我这里说的交互是指应用是链上合约的交互,更明确的说,是chainlink,arbitrum,cosmos这些链下应用与链上合约的交互。
所以这里我不是想说以下交互方式:

  • 通过钱包交互:将应用打包成一个网页,连接类似与小狐狸 这样的钱包,与链发生交互。
  • 手动构造交易:构建交易tx并使用私钥对交易进行签名,然后直接发送到链给定的接口上。

对于一些简单的调用,通过上面两种方式是可行的,例如我们只是做一些nft的构造,通过钱包是最合适的。但是对于向arbitrum这类
链上的应用,如果要做一个交互式单步证明,在这个过程中我需要监控链上合约抛出的event,分析event并构造出相应结果。
这个时候钱包就很难插手,而如果主动构造交易并签名,那么过程太繁琐。实际上以太坊上已经提供了相关的工具链。

我们要谈的交互方式就是,通过以太坊支持的工具链实现与以太坊的交互

工具链

工具链

简单而言是使用以太坊工具将sol的合约代码转换成go的类文件,并对调用细节进行封装。
而在应用层(arbitrum,chainlink这一层)可以直接将对应参数传过去就可以.

以arbitrum为例:

生成go代码

生成工具在: https://github.com/OffchainLabs/nitro/blob/master/solgen/gen.go
由于arbitrum用到链makefile,所我们没法通过运行这个文件(go run gen.go)的方式,去生成合约文件。
不过实际上这是一个路径的问题,在代码的第71行:

    filePaths, err := filepath.Glob(filepath.Join(parent, "contracts", "build", "contracts", "src", "*", "*.sol", "*.json"))
	if err != nil {
		log.Fatal(err)
	}

	filePathsSafeSmartAccount, err := filepath.Glob(filepath.Join(parent, "safe-smart-account", "build", "artifacts", "contracts", "*", "*.sol", "*.json"))
	if err != nil {
		log.Fatal(err)
	}
	filePathsSafeSmartAccountOuter, err := filepath.Glob(filepath.Join(parent, "safe-smart-account", "build", "artifacts", "contracts", "*.sol", "*.json"))
	if err != nil {
		log.Fatal(err)
	}

这里实际上就指定了合约代码的路径,当然如果只是初次下载合约文件应该是看不到build目录的,需要在合约所在项目构建一下,才能生成这个build文件

构造方法:

yarn --cwd contracts build
yarn --cwd contracts build:forge:yul
# 其实就是hardhat compile的产物

然后就会在solgen这个目录下生成对应的go文件
solgen

我们具体看一下这个生成的代码怎么用

使用生成的代码

首先可以看到在生成的代码中,每一个合约都有一个对应的类
eg:

// ChallengeLibTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
type ChallengeLibTransactorRaw struct {
	Contract *ChallengeLibTransactor // Generic write-only contract binding to access the raw methods on
}

合约中的方法则对应到类的方法

eg:

// Solidity: function oneStepProveExecution(uint64 challengeIndex, (uint256,uint256,bytes32[],uint256) selection, bytes proof) returns()
func (_ChallengeManager *ChallengeManagerTransactor) OneStepProveExecution(opts *bind.TransactOpts, challengeIndex uint64, selection ChallengeLibSegmentSelection, proof []byte) (*types.Transaction, error) {
	return _ChallengeManager.contract.Transact(opts, "oneStepProveExecution", challengeIndex, selection, proof)
}

其中关键在与这里的opts,如果我们继续进到这个Transact方法里面会发现,链上的信息都是由这里的opts获取的,用户签名接口,用户信息等

那么对于一个合约调用就分为两部分,一是调用参数,也就是这里的opts和合约参数,一是对接一台的的client

参数

进入到这里的opts,这是以太坊里面的数据结构

// valid Ethereum transaction.
type TransactOpts struct {
	From   common.Address // Ethereum account to send the transaction from
	Nonce  *big.Int       // Nonce to use for the transaction execution (nil = use pending state)
	Signer SignerFn       // Method to use for signing the transaction (mandatory)

	Value     *big.Int // Funds to transfer along the transaction (nil = 0 = no funds)
	GasPrice  *big.Int // Gas price to use for the transaction execution (nil = gas price oracle)
	GasFeeCap *big.Int // Gas fee cap to use for the 1559 transaction execution (nil = gas price oracle)
	GasTipCap *big.Int // Gas priority fee cap to use for the 1559 transaction execution (nil = gas price oracle)
	GasLimit  uint64   // Gas limit to set for the transaction execution (0 = estimate)
	GasMargin uint64   // Arbitrum: adjusts gas estimate by this many basis points (0 = no adjustment)

	Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)

	NoSend bool // Do all transact steps but do not send the transaction
}

我们可以看到,这里包含用户信息的签名数据

对于链下的开发者,我们需要构造这个结构,来调用方法

client

我们有链合约的调用参数,那么就需要有一个client来为我们发送交易,(虽然构造交易的时候也用到链client,但这都是已经被工具封装好的,开发者没必要细究它是怎么构建的)

实际上client是在我们构建合约对象时构建的

// NewChallengeManager creates a new instance of ChallengeManager, bound to a specific deployed contract.
func NewChallengeManager(address common.Address, backend bind.ContractBackend) (*ChallengeManager, error) {
	contract, err := bindChallengeManager(address, backend, backend, backend)
	if err != nil {
		return nil, err
	}
	return &ChallengeManager{ChallengeManagerCaller: ChallengeManagerCaller{contract: contract}, ChallengeManagerTransactor: ChallengeManagerTransactor{contract: contract}, ChallengeManagerFilterer: ChallengeManagerFilterer{contract: contract}}, nil
}

在构建ChallengeManger这个合约对象时,我们需要给他一个backend,这里的backend就是链的client,地址也就是链上的合约地址

可以看到backend也是bind这个包里面的,实际上它也是以太坊源码里面的包

type ContractBackend interface {
	ContractCaller
	ContractTransactor
	ContractFilterer
}

type ContractCaller interface {
	// CodeAt returns the code of the given account. This is needed to differentiate
	// between contract internal errors and the local chain being out of sync.
	CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error)

	// CallContract executes an Ethereum contract call with the specified data as the
	// input.
	CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error)
}
type ContractTransactor interface {
	ethereum.GasEstimator
	ethereum.GasPricer
	ethereum.GasPricer1559
	ethereum.TransactionSender

	// HeaderByNumber returns a block header from the current canonical chain. If
	// number is nil, the latest known header is returned.
	HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)

	// PendingCodeAt returns the code of the given account in the pending state.
	PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error)

	// PendingNonceAt retrieves the current pending nonce associated with an account.
	PendingNonceAt(ctx context.Context, account common.Address) (uint64, error)
}

type ContractFilterer interface {
	ethereum.LogFilterer
}

这个client看起来构造很麻烦,实际上也是有迹可循的,这里面都是以太坊里的数据结构,所以理论上以太坊里面已经有对象实现了这些接口

type Client struct {
	c rpc.ClientInterface
}
type ClientInterface interface {
	CallContext(ctx_in context.Context, result interface{}, method string, args ...interface{}) error
	EthSubscribe(ctx context.Context, channel interface{}, args ...interface{}) (*ClientSubscription, error)
	BatchCallContext(ctx context.Context, b []BatchElem) error
	Close()
}

// Client represents a connection to an RPC server.
type Client struct {
	idgen    func() ID // for subscriptions
	isHTTP   bool      // connection type: http, ws or ipc
	services *serviceRegistry

	idCounter atomic.Uint32

	// This function, if non-nil, is called when the connection is lost.
	reconnectFunc reconnectFunc

	// config fields
	batchItemLimit       int
	batchResponseMaxSize int

	// writeConn is used for writing to the connection on the caller's goroutine. It should
	// only be accessed outside of dispatch, with the write lock held. The write lock is
	// taken by sending on reqInit and released by sending on reqSent.
	writeConn jsonWriter

	// for dispatch
	close       chan struct{}
	closing     chan struct{}    // closed when client is quitting
	didClose    chan struct{}    // closed when client quits
	reconnected chan ServerCodec // where write/reconnect sends the new connection
	readOp      chan readOp      // read messages
	readErr     chan error       // errors from read
	reqInit     chan *requestOp  // register response IDs, takes write lock
	reqSent     chan error       // signals write completion, releases write lock
	reqTimeout  chan *requestOp  // removes response IDs when call timeout expires
}
From:https://www.cnblogs.com/blogforeverything/p/18408576
本文地址: http://shuzixingkong.net/article/1942
0评论
提交 加载更多评论
其他文章 .NET 6.0 + WPF 使用 Prism 框架实现导航
前言 Prism 一个开源的框架,专门用于开发可扩展、模块化和可测试的企业级 XAML 应用程序,适用于 WPF(Windows Presentation Foundation)和 Xamarin Forms 等平台。 Prism 基于 MVVM(Model-View-ViewModel)设计模式,
.NET 6.0 + WPF 使用 Prism 框架实现导航 .NET 6.0 + WPF 使用 Prism 框架实现导航 .NET 6.0 + WPF 使用 Prism 框架实现导航
verilog vscode 与AI 插件
Verilog 轻量化开发环境 背景 笔者常用的开发环境 VIAVDO, 体积巨大,自带编辑器除了linting 能用,编辑器几乎不能用,仿真界面很友好,但是速度比较慢。 Sublime Text, 非常好用的编辑器,各种插件使用verilog 非常方便,可以自动补全、生成调用、linting等;
verilog vscode 与AI 插件 verilog vscode 与AI 插件 verilog vscode 与AI 插件
一文快速上手-Vue CLI脚手架
安装Vue CLI (1) 全局安装Vue CLI 方式一(推荐方式):在终端安装指定版本 npm i @vue/cli@5.0.8 -g 注:目前5.0.8应该是最新的版本了。 方式二:在终端通过命令安装最新版本 npm i @vue/cli -g (2) 升级Vue CLI到最新版本(可选) n
牛逼!在Vue3.5中仅仅2分钟就能封装一个自动cancel的fetch函数
前言 在欧阳的上一篇 这应该是全网最详细的Vue3.5版本解读文章中有不少同学对Vue3.5新增的onWatcherCleanup有点疑惑,这个新增的API好像和watch API回调的第三个参数onCleanup功能好像重复了。今天这篇文章来讲讲新增的onWatcherCleanup函数的使用场景
牛逼!在Vue3.5中仅仅2分钟就能封装一个自动cancel的fetch函数 牛逼!在Vue3.5中仅仅2分钟就能封装一个自动cancel的fetch函数 牛逼!在Vue3.5中仅仅2分钟就能封装一个自动cancel的fetch函数
关于 Splay 树
前置芝士 $\LARGE {关于二叉搜索树及平衡树无聊的一大串子定义}$ 二叉搜索树(BST树) 定义 二叉搜索树是一种二叉树的树形数据结构,其定义如下: 空树是二叉搜索树。 若二叉搜索树的左子树不为空,则其左子树上所有点的附加权值均小于其根节点的值。 若二叉搜索树的右子树不为空,则其右子树上所有点
关于 Splay 树 关于 Splay 树 关于 Splay 树
SPiT:超像素驱动的非规则ViT标记化,实现更真实的图像理解 | ECCV 2024
Vision Transformer(ViT) 架构传统上采用基于网格的方法进行标记化,而不考虑图像的语义内容。论文提出了一种模块化的超像素非规则标记化策略,该策略将标记化和特征提取解耦,与当前将两者视为不可分割整体的方法形成了对比。通过使用在线内容感知标记化以及尺度和形状不变的位置嵌入,与基于图像
SPiT:超像素驱动的非规则ViT标记化,实现更真实的图像理解 | ECCV 2024 SPiT:超像素驱动的非规则ViT标记化,实现更真实的图像理解 | ECCV 2024 SPiT:超像素驱动的非规则ViT标记化,实现更真实的图像理解 | ECCV 2024
006.MinIO基础使用
图形界面基础使用 bucket bucket创建 图形界面创建bucket。 特性: Versioning 开启版本控制,开启版本控制则允许在同一键下保持同一对象的多个版本。 Object Locking 对象锁定防止对象被删除,需要支持保留和合法持有,只能在创建桶时启用。 Quita 配额限制bu
006.MinIO基础使用 006.MinIO基础使用 006.MinIO基础使用
AI实战 | 领克汽车线上营销助手:全面功能展示与效果分析
本篇文章的主要目的是为大家提供实现思路,以及如何更好地开发一个助手,而不仅仅是简单地进行拆解。如果采取拆解的方式,一篇文章可能会长达2万+字,还需要配以数十张图片,这将会非常繁琐。因此,针对拆解的详细内容,我计划单独制作一期视频,以帮助大家更清晰地理解。感谢大家对小雨的关注与支持。
AI实战 | 领克汽车线上营销助手:全面功能展示与效果分析 AI实战 | 领克汽车线上营销助手:全面功能展示与效果分析 AI实战 | 领克汽车线上营销助手:全面功能展示与效果分析