Solidity & Web3js:EIP712 签名 uint256 工作签名 uint256[] 不

Posted

技术标签:

【中文标题】Solidity & Web3js:EIP712 签名 uint256 工作签名 uint256[] 不【英文标题】:Solidity & Web3js: EIP712 signing uint256 works signing uint256[] does not 【发布时间】:2020-02-04 00:50:49 【问题描述】:

我正在用EIP712签署订单,并且我在签署uint256时实现了恢复数据,一旦我使用uint256[],它就会恢复到错误的地址。

有人可以帮忙吗?

这是创建签名 (web3js) 的 javascript 方面:

var domain = [
   name: "name", type: "string" ,
   name: "version", type: "string" ,
   name: "chainId", type: "uint256" ,
   name: "verifyingContract", type: "address" ,
   name: "salt", type: "bytes32" 
];
var sellOrders = [
   name: "id", type: "uint256[]" ,
   name: "tokenId", type: "uint256[]" ,
   name: "price", type: "uint256[]" ,
   name: "proto", type: "uint256[]" ,
   name: "purity", type: "uint256[]" ,
   name: "seller", type: "address" 
];
const domainData = 
  name: "app",
  version: "1",
  chainId: 3,
  verifyingContract: cardExchangeContract,
  salt: "0xa222082684812afae4e093416fff16bc218b569abe4db590b6a058e1f2c1cd3e"
;
var message = 
  id: [1],
  tokenId: [1],
  price: [1],
  proto: [1],
  purity: [1],
  seller: address
;
var data = JSON.stringify(
  types: 
      EIP712Domain: domain,
      SellOrders: sellOrders,
  ,
  domain: domainData,
  primaryType: "SellOrders",
  message: message
);
window.web3.currentProvider.sendAsync(
  method: "eth_signTypedData_v4",
  params: [address, data],
  from: address
, function(error, result) 
  if (error)  
    errorCallback(); 
   else 
    const signature = result.result.substring(2);
    const r = "0x" + signature.substring(0, 64);
    const s = "0x" + signature.substring(64, 128);
    const v = parseInt(signature.substring(128, 130), 16);
    successCallback(signature, r, s, v);
  
);

这里是坚固的一面:

string private constant domain = "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)";
bytes32 public constant domainTypeHash = keccak256(abi.encodePacked(domain));
string private constant sellOrdersType = "SellOrders(uint256[] id,uint256[] tokenId,uint256[] price,uint256[] proto,uint256[] purity,address seller)";
bytes32 public constant sellOrdersTypeHash = keccak256(abi.encodePacked(sellOrdersType));
uint256 constant chainId = 3;
bytes32 constant salt = 0xa222082684812afae4e093416fff16bc218b569abe4db590b6a058e1f2c1cd3e;
bytes32 private domainSeparator;

struct SellOrders 
    uint256[] id;
    uint256[] tokenId;
    uint256[] price;
    uint256[] proto;
    uint256[] purity;
    address payable seller;


constructor() public 
    domainSeparator = keccak256(abi.encode(
        domainTypeHash,
        keccak256("app"),
        keccak256("1"), // version
        chainId,
        this,
        salt
    ));


function recover(uint256[] calldata id, uint256[] calldata tokenId, uint256[] calldata price, uint256[] calldata proto, uint256[] calldata purity, address seller, uint8 v, bytes32 r, bytes32 s) external view returns (address) 
    return _recover(id, tokenId, price, proto, purity, seller, v, r, s);

function _recover(uint256[] memory id, uint256[] memory tokenId, uint256[] memory price, uint256[] memory proto, uint256[] memory purity, address seller, uint8 v, bytes32 r, bytes32 s) private view returns (address) 
    return ecrecover(hashSellOrders(id, tokenId, price, proto, purity, seller), v, r, s);

function hashSellOrders(uint256[] memory id, uint256[] memory tokenId, uint256[] memory price, uint256[] memory proto, uint256[] memory purity, address seller) private view returns (bytes32)
    return keccak256(abi.encodePacked(
       "\x19\x01",
       domainSeparator,
       keccak256(abi.encode(
            sellOrdersTypeHash,
            id,
            tokenId,
            price,
            proto,
            purity,
            seller
        ))
    ));

当我将 sellOrders 属性更改为 uint256 而不是 uint256[] 并提供 uint256s 而不是数组时,它会按预期工作,并恢复正确的地址。当我设置它uint256[] 并提供数组时,它会恢复到错误的地址。

编辑/添加

实际工作的代码sn-ps,当使用uint256(无数组)时:

Javascript (web3js/Metamask):

var sellOrders = [
   name: "id", type: "uint256" ,
   name: "tokenId", type: "uint256" ,
   name: "price", type: "uint256" ,
   name: "proto", type: "uint256" ,
   name: "purity", type: "uint256" ,
   name: "seller", type: "address" 
];

var message = 
  id: 1,
  tokenId: 1,
  price: 1,
  proto: 1,
  purity: 1,
  seller: address
;

坚固性:

string private constant sellOrdersType = "SellOrders(uint256 id,uint256 tokenId,uint256 price,uint256 proto,uint256 purity,address seller)";

struct SellOrders 
    uint256 id;
    uint256 tokenId;
    uint256 price;
    uint256 proto;
    uint256 purity;
    address payable seller;


function recover(uint256 id, uint256 tokenId, uint256 price, uint256 proto, uint256 purity, address seller, uint8 v, bytes32 r, bytes32 s) external view returns (address) 
    return _recover(id, tokenId, price, proto, purity, seller, v, r, s);

function _recover(uint256 id, uint256 tokenId, uint256 price, uint256 proto, uint256 purity, address seller, uint8 v, bytes32 r, bytes32 s) private view returns (address) 
    return ecrecover(hashSellOrders(id, tokenId, price, proto, purity, seller), v, r, s);

function hashSellOrders(uint256 id, uint256 tokenId, uint256 price, uint256 proto, uint256 purity, address seller) private view returns (bytes32)
    return keccak256(abi.encodePacked(
       "\x19\x01",
       domainSeparator,
       keccak256(abi.encode(
            sellOrdersTypeHash,
            id,
            tokenId,
            price,
            proto,
            purity,
            seller
        ))
    ));

添加了 2 个 我的最后一项研究将我引向https://github.com/MetaMask/eth-sig-util/blob/master/index.js#L70-L78 Metamask 在客户端实现编码/散列的方式,但我仍然无法在solidity 合约中重现这一点以正确恢复它。

【问题讨论】:

我想我看到了问题,但只是为了确定你能发布两个版本的代码(工作和不工作)吗?谢谢 将代码添加到原始问题中。 【参考方案1】:

来自https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#definition-of-encodedata:

数组值被编码为其内容的串联encodeDatakeccak256 哈希(即SomeType[5] 的编码与包含五个SomeType 类型成员的结构的编码相同)。

所以我相信这应该可行(完全未经测试):

function hashSellOrders(uint256[] memory id, uint256[] memory tokenId, uint256[] memory price, uint256[] memory proto, uint256[] memory purity, address seller) private view returns (bytes32)
    return keccak256(abi.encodePacked(
       "\x19\x01",
       domainSeparator,
       keccak256(abi.encode(
            sellOrdersTypeHash,
            keccak256(abi.encodePacked(id)),
            keccak256(abi.encodePacked(tokenId)),
            keccak256(abi.encodePacked(price)),
            keccak256(abi.encodePacked(proto)),
            keccak256(abi.encodePacked(purity)),
            seller
        ))
    ));

【讨论】:

以上是关于Solidity & Web3js:EIP712 签名 uint256 工作签名 uint256[] 不的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 web3js 在solidity 中建立一个大的字符串数组

如何将 java 或其他语言与 ethereum 或 solidity 或 web3js 集成?

Web3JS 发送事务

Truffle testing - 尝试使用 web3 测试 EIP712,有没有其他选择?

内部数据库和 Solidity 之间的数据重复

在没有元掩码的情况下自动进行solidity传输