如上所述,普通的ETH交易并不能让用户不需要气费,所以需要在交易中嵌套一个交易,即元交易,来实现气费免除。
本文将分析开源库open zeppelin/open zeppelin-contracts中元事务契约的实现,让你快速了解元事务实现的细节,以便你在后续探索更多的相关技术。
前知识概述元博览会涉及ECDSA、EIP712等知识。如果熟悉,可以跳过这一节,直接浏览具体的实现分析部分。
hash也叫Hash、Hash、digital digest。通过哈希函数,不同长度的信息可以转换成任意长度的可预测(确定性)结果。这种神奇的功能可以将大量信息转化为一串可以作为摘要的简短数据指纹。对于给定的输入,生成的“指纹”总是一致的。如果您的原始数据有任何细微的变化,生成的哈希值将会有很大的不同。以太坊采用Keccak-256算法。
ECDSA在密码学中,ECDSA(椭圆曲线数字签名算法)是使用椭圆曲线密码的数字签名算法(DSA)的变种。
主要用于为数据(如文件)创建数字签名,以便您可以在不破坏其安全性的情况下验证其真实性。把它想象成一个真实的签名。你可以认出一些人的签名,但不能在别人不知道的情况下伪造。
不要将ECDSA与用于加密数据的AES(高级加密标准)混淆。ECDSA不会加密数据或阻止他人查看或访问您的数据。它可以防止的是确保数据没有被篡改。
如图所示,在以太坊中,ECDSA用于签名和恢复原始数据的哈希值。
用户A通过hash函数获得原始数据的hash值后,用自己的私钥对hash值进行签名,获得签名。有了这个签名和哈希值,任何人都可以恢复签名人的钱包地址,而在这里,用户B已经恢复了用户a的钱包地址
EIP712以太坊改进提案(EIPs)。您可以在此查看所有EIP。Eip712(以太网类型结构化数据哈希和签名)以太网方形类型结构化数据哈希和签名。
如果我们只关心字节串,那么签署数据就是一个已经解决的问题。不幸的是,在现实世界中,我们关心的是复杂而有意义的信息。哈希结构化数据非常重要,出错会导致系统安全属性的丢失。
此EIP旨在提高链上使用的链外消息签名的可用性。我们看到越来越多的人采用离线消息签名,因为它节省了油费,减少了区块链上的交易次数。当前签名的消息是显示给用户的不透明的十六进制字符串。几乎没上述文章内容就是于组成消息的项目的上下文。
EIP712概述了对数据及其结构进行编码的方案,该方案允许在签名时向用户显示数据以供验证。以下是用户在签署EIP712消息时显示的示例。
元交易契约的实现此分析针对open zeppelin-contracts v 4 . 3 . 2 .
契约极小化Forwarder是EIP712 {使用ECDSA for bytes32结构ForwardRequest { address from地址:uint256值;uint256气体;uint256 nonce字节数据;} Constructor()EIP 712(' Minimal Forwarder ',' 0.0.1') {}ECDSA是openzeppelin实现的solidity库,实现了从哈希值恢复钱包地址的方法。如果将recover方法应用于bytes32,则可以直接对bytes32调用该方法。recover函数签名:函数recover (bytes s32hash,bytes memory signature)内部纯返回(address)。
ForwardRequest结构定义了事务中用于签名的基本组件。 不像以太坊交易,没有气价,因为智能合约的执行只关心气的消耗。ForwardRequest中的nonce的概念类似于以太坊,都是为了避免双花攻击,但是这里的nonce只由智能合约维护,与普通以太坊交易中的nonce无关。
EIP 712的构造函数直接用于初始化。EIP712的构造函数的签名是:constructor (string memory name,string memory version),其中name是合约名称,version是合约版本。这将作为EIP712签名验证的一部分。部署时会自动获取契约的地址、chainId等信息。这意味着即使有相同的ForwardRequest结构数据,但合同地址或区块链网络不同,签名也将无效。
mapping(address=uint 256)private _ nonces;函数getNonce(address from)public view returns(uint 256){ return _ nonces[from];}为了避免双花攻击,有必要在智能合约中维护nonce。
bytes 32 private constant _ type hash=ke ccak 256(' forward request(address from,address to,uint256 value,uint256 gas,uint256 nonce,bytes data)');函数verify(forward request call data req,bytes call data signature)public view returns(bool){ address signer=_ hashtypeddatav 4(keccak 256(ABI . encode(_ type hash,req.from,req.to,req.value,req.gas,req.nonce,keccak256(req.data)))。收回(签名);return _ nonces[req . from]==req . nonce signer==req . from;}看到verify函数,我们知道要恢复钱包地址,至少需要ECDSA的签名和用于签名的原始数据,而这里ECDSA签名的原始数据是Keccak256 (abi.encode (_ type hash,req.from,req.to,req.value,The req.gas,req.nonce,kekcak256 (req.data))转发请求结构数据的哈希值。然后调用ECDSA库中的recover函数,传入签名,就可以恢复签名人的钱包地址。
确保事务的调用是顺序的,不会遭受双花攻击by _ nonces[req . from]==req . nonce . Signer==req . from避免元事务的签名者和实际发送者不匹配。
接下来,看看如何执行元事务。
function execute(forward request calldata req,bytes calldata signature)publicpayablereturns(bool,bytes memory){ require(req,signature),' MinimalForwarder:签名与请求不匹配');_ nonces[req . from]=req . nonce 1;(bool success,bytes memory return data)=req . to . call { gas:req . gas,value:req . value }(ABI . encode packed(req . data,req . from));//验证继电器已经为呼叫发送了足够的气体。//参见https://Ronan . eth . link/blog/ether eum-gas-dangeries/assert(gas left()req . gas/63);return(成功,return data);}使用Address.call方法时,根据元事务参数指定call的gas和value值。需要注意的是,这里元事务的数据字段不是直接作为调用操作的数据,而是将data和from一起abi编码作为调用操作的参数尚力财经小编2022,会在目标契约(即req.to)中解析得到事务的发送方,接下来会详细说明。
Assert (gas left () req。gas/63)简单理解为防止重复者(执行元交易的人)恶意或无意使用足够低的gas使交易成功,而元交易失败。你可以在以太坊毒气危险中了解详情。
ERC2771要支持元事务,只实现元事务智能契约是不够的,因为目标契约无法知道实际的元事务来自谁。在没有额外措施的情况下,只能从msg.sender中获取由于是通过Address.call调用的,在元事务契约的实现中,将要获取的发送方是元事务契约的地址。ERC2771解决了这个问题。
抽象契约erc2771Context是Contexterc2771context继承了Context,Context只是封装了从属的msg.sender和msg.data,从而规范了这两个函数的使用,使它们能够修改自己在子契约中的行为。 要求使用语尚力财经小编2022境合约获取味精相关的数据,而不是直接使用msg.sender等。
抽象契约上下文{ function _msgSender()内部视图虚拟返回(地址){ return msg . sender } function _ msg data()内部视图虚拟返回(字节调用数据){返回消息。数据;} }ERC 2771上下文就修改了语境合约的方法。function _msgSender()内部视图虚拟重写返回(地址发送方){ if(isTrustedForwarder(msg。发件人)){//汇编代码比固态版本使用` abi.decode '更直接。assembly {sender :=shr(96,calldataload(sub(calldatasize(),20))} } else { return super ._ msg sender();}}
先通过isTrustedForwarder(msg.sender)验证元交易的调用方是期望的元交易合约地址100 .装配代码将上文的元交易合约中请求到调用{.}(abi.encodePacked(req.data,req.from))编码进的数据部分内容的请求自获取到,然后再返回该值十.元交易使用概览让我们来尝试简单使用元交易合约,要支持元交易,你所编写的合约必须继承ERC 2771上下文。在这里简单实现一个神经原纤维紊乱合约,在部署它之前,你必须先部署元交易合约,将元交易合约地址作为参数传递给神经原纤维紊乱合约构造函数。
//spdx-license-identifier:GPL 3.0 pragma solidity ^0.8.0;导入“@打开zeppelin/contracts/utils/math/safe math。sol”;导入“@打开zeppelin/contracts/meta tx/ERC 2771上下文。sol”;导入@开放齐柏林/合同/令牌/ERC 721/ERC 721。sol ';合同神经原纤维紊乱是ERC 2771上下文,ERC 721 {对uint 256使用安全数学;uint 256 private _ currentTokenId=0;构造函数(字符串内存名称,字符串内存符号,地址trustedForwarder ) ERC721(名称,符号)ERC 2771上下文(trustedForwarder){ }函数safe mint()公共虚拟{ safe mint(');} function safeMint(bytes memory _data) internal virtual { uint256 tokenId = _getNextTokenId(); _incrementTokenId(); _safeMint(_msgSender(), tokenId, _data); } function getCurrTokenId() public virtual view returns (uint256) { return _currentTokenId; } /** * @dev calculates the next token ID based on value of _currentTokenId * @return uint256 for the next token ID */ function _getNextTokenId() internal virtual view returns (uint256) { return _currentTokenId.add(1); } /** * @dev increments the value of _currentTokenId */ function _incrementTokenId() internal virtual { _currentTokenId++; } function _msgSender() internal view virtual override(Context, ERC2771Context) returns (address) { return ERC2771Context._msgSender(); } function _msgData() internal view virtual override(Context, ERC2771Context) returns (bytes calldata) { return ERC2771Context._msgData(); }}在这个示例中,如果 Alice 没有足够的 ETH 支付 gas 费,来铸造一个 NFT,她可以签署一个元交易,元交易的 data 是由 abi.encodeWithSignature(functionSelector, parmas...) 得到的,将该元交易递交给具有足够 ETH 的 Bob,Bob 调用元交易合约 MinimalForwarder.execute(req, signature),从而让 Alice 的元交易成功执行。
以上就是元交易合约如何实现?智能合约开发实战:元交易(Metatransaction)系列二的详细内容,更多关于元交易合约实现的资料请关注尚力财经其它相关文章!