Solidity & Web3js:EIP712 签名 uint256 可以签名 uint256[] 不工作
Solidity & Web3js: EIP712 signing uint256 works signing uint256[] does not
我正在用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[]
并提供数组时,它会恢复到错误的地址。
Edit/Added
使用 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 在客户端实现 encoding/hashing 的方式,但我仍然无法在 solidity 合约中重现它以正确恢复它。
来自https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#definition-of-encodedata:
The array values are encoded as the keccak256
hash of the concatenated encodeData
of their contents (i.e. the encoding of SomeType[5]
is identical to that of a struct containing five members of type 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
))
));
}
要添加到所选答案,
如果你有一个任意大小的数据数组,你必须先 keccack256 那些,即下面的数据是 bytes[]
bytes32[] memory keccakData = new bytes32[](data.length);
for (uint256 i = 0; i < data.length; i++) {
keccakData[i] = keccak256(data[i]);
}
然后你可以用剩下的编码执行keccak256(abi.encodePacked(keccakData))
。
我正在用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[]
并提供数组时,它会恢复到错误的地址。
Edit/Added
使用 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 在客户端实现 encoding/hashing 的方式,但我仍然无法在 solidity 合约中重现它以正确恢复它。
来自https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#definition-of-encodedata:
The array values are encoded as the
keccak256
hash of the concatenatedencodeData
of their contents (i.e. the encoding ofSomeType[5]
is identical to that of a struct containing five members of typeSomeType
).
所以我相信这应该有效(完全未经测试):
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
))
));
}
要添加到所选答案,
如果你有一个任意大小的数据数组,你必须先 keccack256 那些,即下面的数据是 bytes[]
bytes32[] memory keccakData = new bytes32[](data.length);
for (uint256 i = 0; i < data.length; i++) {
keccakData[i] = keccak256(data[i]);
}
然后你可以用剩下的编码执行keccak256(abi.encodePacked(keccakData))
。