欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

當(dāng)前位置:主頁 > 區(qū)塊鏈 > 區(qū)塊鏈技術(shù) > 元交易合約如何實(shí)現(xiàn)

元交易合約如何實(shí)現(xiàn)?智能合約開發(fā)實(shí)戰(zhàn):元交易(Metatransaction)系列二

2021-12-15 15:59:09 | 來源: | 作者:佚名
這篇文章主要介紹了元交易合約如何實(shí)現(xiàn)?智能合約開發(fā)實(shí)戰(zhàn):元交易(Metatransaction)系列二的相關(guān)資料,希望小編的這篇關(guān)于元交易合約如何實(shí)現(xiàn)的文章,能夠幫助各位投資者對元交易有一個更加全面透徹的了解。

引言

上文中提到,普通的 ETH 交易并不能夠做到讓用戶無需 gas 費(fèi),需要交易中嵌套一個交易,即元交易,來實(shí)現(xiàn)免 gas 費(fèi)。

本文將分析開源庫 OpenZeppelin/openzeppelin-contracts 中的元交易合約的實(shí)現(xiàn),讓你能夠快速入門元交易實(shí)現(xiàn)細(xì)節(jié),從而能夠自己對后續(xù)更多的相關(guān)技術(shù)深入探索。

前置知識概述

元交易會涉及到 ECDSA 與 EIP712 等知識,如果你是熟手,可以跳過此節(jié)內(nèi)容,直接瀏覽具體實(shí)現(xiàn)分析部分。

Hash

也稱哈希、散列、數(shù)字摘要。通過哈希函數(shù),可以將長短不一的信息轉(zhuǎn)化為一段長度任意但可預(yù)測的(確定性的)結(jié)果。這是一類神奇的函數(shù),可以將一大堆信息轉(zhuǎn)變成一串短的,可作為摘要的數(shù)據(jù) “指紋”。對于一個給定的輸入而言,生成的 “指紋” 始終一致。如果你的原始數(shù)據(jù)中有任何細(xì)微的改動,生成的哈希值將大不相同。以太坊中采用的是 Keccak-256 算法。

ECDSA

在密碼學(xué)中,ECDSA(Elliptic Curve Digital Signature Algorithm,橢圓曲線數(shù)字簽名算法)是使用橢圓曲線密碼學(xué)的數(shù)字簽名算法(DSA)的一個變種。

主要用于對數(shù)據(jù)(比如一個文件)創(chuàng)建數(shù)字簽名,以便于你在不破壞它的安全性的前提下對它的真實(shí)性進(jìn)行驗證??梢詫⑺胂蟪梢粋€實(shí)際的簽名,你可以識別部分人的簽名,但是你無法在別人不知道的情況下偽造它。

你不應(yīng)該將ECDSA與用來對數(shù)據(jù)進(jìn)行加密的AES(高級加密標(biāo)準(zhǔn))相混淆。ECDSA不會對數(shù)據(jù)進(jìn)行加密、或阻止別人看到或訪問你的數(shù)據(jù),它可以防止的是確保數(shù)據(jù)沒有被篡改。

如圖所示,在以太坊中,ECDSA 用于對原始數(shù)據(jù)的 hash 值進(jìn)行簽名及恢復(fù)。
 

元交易合約如何實(shí)現(xiàn)?智能合約開發(fā)實(shí)戰(zhàn):元交易(Metatransaction)系列二

將原始數(shù)據(jù)通過 hash 函數(shù)得到它的 hash 值后,用戶 A 用自己的私鑰對該 hash 值進(jìn)行簽名,得到 Signature(簽名)。有了該簽名與 hash 值,任何人都能夠從中恢復(fù)出簽名人的錢包地址,在這里用戶 B 則恢復(fù)得到了用戶 A 的錢包地址。

EIP712
Ethereum Improvement Proposals (EIPs),你可以在這里查看所有的 EIPs。EIP712 (Ethereum typed structured data hashing and signing)以太坊類型的結(jié)構(gòu)化數(shù)據(jù)哈希與簽名。

如果我們只關(guān)心字節(jié)字符串的話,簽名數(shù)據(jù)是一個已經(jīng)解決了的問題。但不幸的是,在現(xiàn)實(shí)世界中,我們關(guān)心的是復(fù)雜而有意義的信息,對結(jié)構(gòu)化數(shù)據(jù)進(jìn)行哈希是非常重要的,錯誤會導(dǎo)致系統(tǒng)安全屬性的丟失。

此 EIP 旨在提高鏈上使用的鏈下消息簽名的可用性。我們看到越來越多的人采用鏈下消息簽名,因為它節(jié)省了 gas 費(fèi),減少了區(qū)塊鏈上的交易數(shù)量。當(dāng)前簽名消息是一個不透明的十六進(jìn)制字符串,顯示給用戶,關(guān)于組成消息的項目的上下文很少。
 

元交易合約如何實(shí)現(xiàn)?智能合約開發(fā)實(shí)戰(zhàn):元交易(Metatransaction)系列二

EIP712 概述了一個編碼數(shù)據(jù)及其結(jié)構(gòu)的方案,該方案允許在簽名時將數(shù)據(jù)顯示給用戶進(jìn)行驗證。下面是一個用戶在簽署 EIP712 消息時顯示的示例。

元交易合約如何實(shí)現(xiàn)?智能合約開發(fā)實(shí)戰(zhàn):元交易(Metatransaction)系列二

元交易合約的實(shí)現(xiàn)

此分析針對 openzeppelin-contracts v4.3.2 版本。

contract MinimalForwarder is EIP712 {
    using ECDSA for bytes32;

    struct ForwardRequest {
        address from;
        address to;
        uint256 value;
        uint256 gas;
        uint256 nonce;
        bytes data;
    }

	constructor() EIP712("MinimalForwarder", "0.0.1") {}
}

ECDSA 是 openzeppelin 實(shí)現(xiàn)的一個 solidity 庫,它實(shí)現(xiàn)了從 hash 值中恢復(fù)錢包地址的方法,將它應(yīng)用在 bytes32 上,就可以直接在 bytes32 上調(diào)用 recover 方法。recover 函數(shù)簽名:function recover(bytes32 hash, bytes memory signature) internal pure returns (address) 。

ForwardRequest 結(jié)構(gòu)體定義了一個交易中用于簽名的基本組成成分。與以太坊交易不同的是沒有 gasPrice,因為智能合約的執(zhí)行只關(guān)心 gas 的消耗。ForwardRequest 中 的 nonce 概念與以太坊類似,都是為了避免雙花攻擊,但這里的 nonce 僅由智能合約維護(hù),跟普通的以太坊交易中的 nonce 無關(guān)。

構(gòu)造函數(shù)中直接使用 EIP712 的構(gòu)造函數(shù)進(jìn)行初始化,EIP712 的構(gòu)造函數(shù)簽名為:constructor(string memory name, string memory version) ,其中 name 是合約名稱,version 是合約版本,這將作為 EIP712 簽名驗證的一部分,它在部署時,將自動獲取合約的地址、chainId 等信息。意味著,即便有相同的 ForwardRequest 結(jié)構(gòu)體數(shù)據(jù),但合約地址或區(qū)塊鏈網(wǎng)絡(luò)不同,也會導(dǎo)致簽名無效。
 

mapping(address => uint256) private _nonces;

function getNonce(address from) public view returns (uint256) {
	return _nonces[from];
}

為了避免雙花攻擊,在智能合約中維護(hù) nonce 是必要的。

bytes32 private constant _TYPEHASH =
	keccak256("ForwardRequest(address from,address to,uint256 value,uint256 gas,uint256 nonce,bytes data)");

function verify(ForwardRequest calldata req, bytes calldata signature) public view returns (bool) {
	address signer = _hashTypedDataV4(
            keccak256(abi.encode(_TYPEHASH, req.from, req.to, req.value, req.gas, req.nonce, keccak256(req.data)))
    ).recover(signature);
	return _nonces[req.from] == req.nonce && signer == req.from;
}

看到 verify 函數(shù),我們知道,要將錢包地址恢復(fù),至少需要經(jīng)過 ECDSA 的簽名以及用于簽名的原始數(shù)據(jù),而此處,ECDSA 簽名的原始數(shù)據(jù)就是經(jīng)過 abi 編碼的 keccak256(abi.encode(_TYPEHASH, req.from, req.to, req.value, req.gas, req.nonce, keccak256(req.data))) ForwardRequest 結(jié)構(gòu)體數(shù)據(jù)的哈希值。再通過調(diào)用 ECDSA 庫中的 recover 函數(shù),傳入簽名,就能夠恢復(fù)得到簽名者的錢包地址。

通過 _nonces[req.from] == req.nonce 來確保交易的調(diào)用是順序的,且不會遭受雙花攻擊。signer == req.from 避免簽名者與實(shí)際元交易發(fā)送者不匹配。

接下來看,如何執(zhí)行元交易。
 

function execute(ForwardRequest calldata req, bytes calldata signature)
	public
	payable
	returns (bool, bytes memory)
{
	require(verify(req, signature), "MinimalForwarder: signature does not match request");
	_nonces[req.from] = req.nonce + 1;

	(bool success, bytes memory returndata) = req.to.call{gas: req.gas, value: req.value}(
		abi.encodePacked(req.data, req.from)
	);
	// Validate that the relayer has sent enough gas for the call.
	// See https://ronan.eth.link/blog/ethereum-gas-dangers/
	assert(gasleft() > req.gas / 63);

	return (success, returndata);
}

在使用 Address.call 方法的時候,根據(jù)元交易參數(shù),指定了 call 的 gas 與 value 值。需要注意的是,這里并不直接將元交易的 data 字段當(dāng)作 call 操作的 data,而是將 data 與 from 進(jìn)行 abi 編碼后一起作為 call 操作的參數(shù),這在目標(biāo)合約(也就是 req.to)中會被解析,從而得到交易的發(fā)送者,在下面會詳細(xì)講解。

assert(gasleft() > req.gas / 63) 簡單理解為避免中繼器(代為執(zhí)行元交易的人)惡意地或無意地使用足夠低的 gas 使得交易執(zhí)行成功,而元交易執(zhí)行失敗。詳情可以在 ethereum gas dangers 中學(xué)習(xí)。

ERC2771

要支持元交易,僅實(shí)現(xiàn)元交易智能合約是不夠的,因為目標(biāo)合約無法知道實(shí)際的元交易 from 是誰。如果沒有額外的措施,它將只能夠從 msg.sender 中獲取,由于在元交易合約實(shí)現(xiàn)中,是通過 Address.call 調(diào)用的,因此將得到的發(fā)送者是元交易合約的地址。ERC2771 則解決了該問題。
 

abstract contract ERC2771Context is Context

ERC2771Context 繼承了 Context,而 Context 中簡單封裝了從 msg.sender 與 msg.data ,以便規(guī)范這兩個功能的使用,且能夠讓其在子合約中修改其行為。要求使用 Context 合約獲取 msg 相關(guān)的數(shù)據(jù),而不是直接使用 msg.sender 等。
 

abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

ERC2771Context 就修改了 Context 合約的方法。

function _msgSender() internal view virtual override returns (address sender) {
	if (isTrustedForwarder(msg.sender)) {
		// The assembly code is more direct than the Solidity version using `abi.decode`.
		assembly {
		sender := shr(96, calldataload(sub(calldatasize(), 20)))
		}
	} else {
		return super._msgSender();
	}
}

先通過 isTrustedForwarder(msg.sender) 驗證元交易的調(diào)用方是期望的元交易合約地址。assembly 代碼將上文的元交易合約中 req.to.call{...}(abi.encodePacked(req.data, req.from)) 編碼進(jìn)的 data 部分內(nèi)容的 req.from 獲取到,然后再返回該值。

元交易使用概覽

讓我們來嘗試簡單使用元交易合約,要支持元交易,你所編寫的合約必須繼承 ERC2771Context。在這里簡單實(shí)現(xiàn)一個 NFT 合約,在部署它之前,你必須先部署元交易合約,將元交易合約地址作為參數(shù)傳遞給 NFT 合約構(gòu)造函數(shù)。
 

// SPDX-License-Identifier: GPL3.0
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/metatx/ERC2771Context.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract NFT is ERC2771Context, ERC721 {
    using SafeMath for uint256;

    uint256 private _currentTokenId = 0;

    constructor(
        string memory name,
        string memory symbol,
        address trustedForwarder
    ) ERC721(name, symbol) ERC2771Context(trustedForwarder) {}

    function safeMint() public virtual {
        safeMint("");
    }

    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 費(fèi),來鑄造一個 NFT,她可以簽署一個元交易,元交易的 data 是由 abi.encodeWithSignature(functionSelector, parmas...) 得到的,將該元交易遞交給具有足夠 ETH 的 Bob,Bob 調(diào)用元交易合約 MinimalForwarder.execute(req, signature),從而讓 Alice 的元交易成功執(zhí)行。

以上就是元交易合約如何實(shí)現(xiàn)?智能合約開發(fā)實(shí)戰(zhàn):元交易(Metatransaction)系列二的詳細(xì)內(nèi)容,更多關(guān)于元交易合約實(shí)現(xiàn)的資料請關(guān)注腳本之家其它相關(guān)文章!

聲明:文章內(nèi)容不代表本站觀點(diǎn)及立場,不構(gòu)成本平臺任何投資建議。本文內(nèi)容僅供參考,風(fēng)險自擔(dān)!

你可能感興趣的文章

幣圈快訊

  • 美股收盤:三大股指漲跌不一

    2025-06-10 04:05
    金色財經(jīng)報道,美股周一收盤,道指初步收跌1點(diǎn),標(biāo)普500指數(shù)漲0.09%,納指漲0.3%。蘋果(AAPL.O)跌1%,英偉達(dá)(NVDA.O)漲0.6%,特斯拉(TSLA.O)漲4.5%,Circle(CRCL.K)漲7%。
  • 特朗普:不會放棄Starlink項目

    2025-06-10 03:57
    金色財經(jīng)報道,美國總統(tǒng)特朗普表示,不會放棄Starlink項目;Starlink提供的服務(wù)很好。
  • 特朗普:我認(rèn)為預(yù)算法案會很快通過

    2025-06-10 03:55
    金色財經(jīng)報道,美國總統(tǒng)特朗普表示,我認(rèn)為預(yù)算法案會很快通過。如果預(yù)算法案花的時間比目標(biāo)長一點(diǎn),也沒關(guān)系。
  • 特朗普:如有必要,將在洛杉磯部署更多國民警衛(wèi)隊,不希望發(fā)生內(nèi)戰(zhàn)

    2025-06-10 03:54
    金色財經(jīng)報道,美國總統(tǒng)特朗普表示,如果他們(抗議者)吐口水,我們就會還擊,我向你們保證,他們將受到比以往任何時候都更嚴(yán)厲的打擊。如有必要,將在洛杉磯部署更多國民警衛(wèi)隊。加州州長不稱職。關(guān)于國民警衛(wèi)隊的問題,我們別無選擇。我們采取了正確的行動。我不希望發(fā)生內(nèi)戰(zhàn)。
  • 美國財政部:外國投資者購買了價值65.02億美元的7年期美債

    2025-06-10 03:54
    金色財經(jīng)報道,美國財政部數(shù)據(jù)顯示,在最新的雙周報告期內(nèi),外國投資者購買了價值65.02億美元的7年期美債,上月為46.70億美元;投資基金購買了334.91億美元的7年期美債,上個月為31.438十億美元。外國投資者購買了73.34億美元的5年期美債,上月為78.04億美元;投資基金購買了545.15億美元的5年期美債,上月為501.10億美元。外國投資者購買了59.38億美元2年期美債,上月為66.18億美元;投資基金購買了535.64億美元2年期美債,上月為509.64億美元。
  • 查看更多