EIP712以太坊签名和验签

EIP712旨在提高链下消息签名对链上的可用性。我们可以看到,因为节省gas以及减少链上交易的原因,采用链下消息签名的需求日益增长。现在已经被签名的消息,展示给用户的是一串难以理解的16进制的字符串,附带一些组成这个消息的项目的上下文。

EIP-712: Ethereum typed structured data hashing and signinghttps://eips.ethereum.org/EIPS/eip-712Signing Data | MetaMask DocsDeveloper documentation for the MetaMask Ethereum wallethttps://docs.metamask.io/guide/signing-data.htmlDraft EIPs - OpenZeppelin Docshttps://docs.openzeppelin.com/contracts/3.x/api/draftsV4签名的js示例:

//创建web3对象
var Web3 = require('web3');
var sigUtil = require("eth-sig-util")
var provider = new Web3.providers.HttpProvider("http://localhost:7545");
var web3 = new Web3(provider);

var json = require("../build/contracts/Demo.json");
var contractAddr = '';

var account = "";
var account_to = "";
//签名人的私钥
var privateKey = "";
var privateKeyHex = Buffer.from(privateKey, 'hex')

var demoContract = new web3.eth.Contract(json['abi'], contractAddr);

//获取链ID
demoContract.methods.getChainId().call({from: account}, function(error, result){
  if (error) {
    console.log(error);
  }
  console.log("getChainId:", result);
});

//V4签名
const typedData = {
  types: {
    EIP712Domain: [
      { name: 'name', type: 'string' },
      { name: 'version', type: 'string' },
      { name: 'chainId', type: 'uint256' },
      { name: 'verifyingContract', type: 'address' },
    ],
    Mail: [
      { name: 'from', type: 'address' },
      { name: 'to', type: 'address' },
      { name: 'value', type: 'uint256' },
    ],
  },
  domain: {
    name: 'Demo',
    version: '1.0',
    chainId: 1,
    verifyingContract: contractAddr,
  },
  primaryType: 'Mail',
  message: {
    from: account,
    to: account_to,
    value: 12345,
  },
}

//V4签名
var signature = sigUtil.signTypedData_v4(privateKeyHex, { data: typedData })
console.log("signature:", signature)

//V4验签
const recovered = sigUtil.recoverTypedSignature_v4({
  data: typedData,
  sig: signature,
});
console.log("recovered:", recovered)

//合约V4验签
demoContract.methods.verify(typedData.message.from, typedData.message.to, typedData.message.value, signature).call({from: account}, function(error, result){
  if (error) {
    console.log(error);
  }
  console.log("verify:", result);
});

V4验签的sol示例:

pragma solidity >=0.6.0 <0.9.0;

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract Demo is EIP712, Ownable {
    //签名人
    address private _signer;

    constructor(string memory name, string memory version) EIP712(name, version) {
    }

    //设置签名人
    function setSigner(address signer) public onlyOwner {
        _signer = signer;
    }
    //获取签名人(V4)
    function recoverV4(
        address from,
        address to,
        uint256 value,
        bytes memory signature
    ) public view returns (address) {
        bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
        keccak256("Mail(address from,address to,uint256 value)"),
            from,
            to,
            value
        )));
        return ECDSA.recover(digest, signature);
    }

    //验签
    function verify(
        address from,
        address to,
        uint256 value,
        bytes memory signature
    ) public view returns (bool) {
        address signer = recoverV4(from, to, value, signature);
        return signer == _signer;
    }

    function getChainId() public view returns(uint256) {
        return block.chainid;
    }
}
在 Rust 中,可以使用 `ethers-rs` crate 来实现 EIP712 签名。以下是一个示例代码: 首先,你需要在 `Cargo.toml` 文件中添加 `ethers-rs` crate 的依赖: ``` [dependencies] ethers = "0.3.0-alpha.5" ``` 接下来,你可以使用以下代码实现 EIP712 签名: ```rust use ethers::types::{Address, Name, TypedData}; use ethers::utils::{keccak256, hash_message, bytes_to_hex_str}; use ethers::signers::{LocalWallet, Signer}; fn main() { // 创建钱包 let wallet = LocalWallet::new(&mut rand::thread_rng()); // EIP712 消息 let message = TypedData { types: Default::default(), domain: Default::default(), message: vec![ ( "name".to_string(), "Hello".to_string(), ), ( "value".to_string(), 42u64.into(), ), ], }; // 计算消息哈希 let message_hash = keccak256(&message.to_bytes()).to_fixed_bytes(); // 签名 let signature = wallet.sign_message(&message_hash).unwrap(); // 打印签名结果 println!("Signature: {}", bytes_to_hex_str(&signature)); // 验证签名 let signer_address = Address::from(wallet.address()); let recovered_address = hash_message(&message.to_bytes(), &signature).unwrap(); assert_eq!(signer_address, recovered_address); } ``` 在这个示例中,我们首先创建了一个本地钱包。然后,我们定义了一个 EIP712 消息,包含一个名为 `name` 的字符串和一个名为 `value` 的整数。我们计算了消息哈希,并使用钱包的 `sign_message` 方法对其进行签名。最后,我们打印签名结果,并验证签名。 请注意,这个示例中的 EIP712 消息是一个自定义的示例消息。如果你要使用真实的 EIP712 消息,请参考对应协议的文档,以获取正确的类型定义和域名设置。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值