你好,
我正在研究一个简单的EIP712白名单成员钱包注册/验证计划。简单地说,(符号类型的数据->传递到链,->提取签名地址->,而不是存储在链上的签名地址)。
我已经有一段时间把头撞在上面了。,我无法让在线链接提取的地址与签名的公共地址相匹配。,我的眼睛离这个问题太近了,我需要帮助寻找一些我可能错过的东西。以我最好的能力,我似乎是在坚持标准,但显然我做错了什么。
我指的是EIP712标准、'Mail‘EIP参考实现这里(溶胶) + 这里(js)和msfeldstein参考实现这里(溶胶) + 这里(ts)。
约束
Notes
v和打印到控制台来生成r、s和签名公共地址。然后,我将.sol部署到Remix,并手动输入生成的值。如果您有时间和知识,我将感谢您对我的EIP712标准的实施情况的审查如下。
客户端:
// using ethereumjs-util 7.1.3
const ethUtil = require('ethereumjs-util');
// using ethereumjs-abi 0.6.9
const abi = require('ethereumjs-abi');
// The purpose of this script is to be painfully explicit for the sake
// of showing work, to ask for help.
// generate keys
prikey = ethUtil.keccakFromString('cow', 256);
signingAddress = ethUtil.privateToAddress(prikey);
// 0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826
// data
const typedData = {
types: {
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' },
],
Validation: [
{ name: 'wallet', type: 'address' },
{ name: 'share', type: 'uint256' },
{ name: 'pool', type: 'uint8' }
],
},
primaryType: 'Validation',
domain: {
name: 'Validator',
version: '1',
chainId: 1,
verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
},
message: {
wallet: '0xeeBA65D9C7E5832918d1F4277DE0a78b78efEC43',
share: 1000,
pool: 5,
},
};
// create domain struct hash
const encodedDomainType = 'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)';
const domainTypeHash = ethUtil.keccakFromString(encodedDomainType, 256);
var encTypes = [];
var encValues = [];
// add typehash
encTypes.push('bytes32');
encValues.push(domainTypeHash);
// add name
encTypes.push('bytes32');
encValues.push(ethUtil.keccakFromString(typedData.domain.name, 256));
// add version
encTypes.push('bytes32');
encValues.push(ethUtil.keccakFromString(typedData.domain.version, 256));
// add chainId
encTypes.push('uint256');
encValues.push(typedData.domain.chainId);
// add chainId
encTypes.push('address');
encValues.push(typedData.domain.verifyingContract);
// computer final hash
domainStructHash = abi.rawEncode(encTypes, encValues);
// create validation struct hash
const encodedValidationType = 'Validation(address wallet,uint256 share,uint256 pool)';
const validationTypeHash = ethUtil.keccakFromString(encodedValidationType, 256);
encTypes = [];
encValues = [];
// add typehash
encTypes.push('bytes32');
encValues.push(validationTypeHash);
// add wallet address
encTypes.push('address');
encValues.push(typedData.message.wallet);
// add share
encTypes.push('uint256');
encValues.push(typedData.message.share);
// add pool
encTypes.push('uint256');
encValues.push(typedData.message.pool);
// computer final hash
validationStructHash = abi.rawEncode(encTypes, encValues);
// now finally create final signature hash
signatureHash = ethUtil.keccak256(
Buffer.concat([
Buffer.from('1901', 'hex'),
domainStructHash,
validationStructHash,
]),
);
// and finally, sign
signature = ethUtil.ecsign(signatureHash, prikey);
// convert r, s, and signingAddress into hex strings to pass to remix
console.log(signature.v);
var r = ''
function pad2(s) {return s.length < 2 ? "0" + s : s};
for(i = 0; i < signature.r.length; i++) {
r += pad2(signature.r[i].toString(16)); }
console.log('0x' + r); // r bytes
var s = ''
function pad2(s) {return s.length < 2 ? "0" + s : s};
for(i = 0; i < signature.s.length; i++) {
s += pad2(signature.s[i].toString(16)); }
console.log('0x' + s); // s bytes
var str = '';
function pad2(s) {return s.length < 2 ? "0" + s : s};
for(i = 0; i < signingAddress.length; i++) {
str += pad2(signingAddress[i].toString(16)); }
console.log('0x' + str); // signingAddress bytes链上的:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract validateData {
address _validationKey = 0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826;
struct EIP712Domain {
string name;
string version;
uint256 chainId;
address verifyingContract;
}
struct Validation {
address wallet;
uint256 share;
uint256 pool;
}
bytes32 constant EIP712DOMAIN_TYPEHASH = keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
bytes32 constant VALIDATION_TYPEHASH = keccak256(
"Validation(address wallet,uint256 share,uint256 pool)"
);
bytes32 DOMAIN_SEPARATOR;
constructor () {
DOMAIN_SEPARATOR = hash(EIP712Domain({
name: "Validator",
version: '1',
chainId: 1,
verifyingContract: 0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC
}));
}
function hash(EIP712Domain memory eip712Domain) internal pure returns (bytes32) {
return keccak256(abi.encode(
EIP712DOMAIN_TYPEHASH,
keccak256(bytes(eip712Domain.name)),
keccak256(bytes(eip712Domain.version)),
eip712Domain.chainId,
eip712Domain.verifyingContract
));
}
function hash(Validation calldata validation) internal pure returns (bytes32) {
return keccak256(abi.encode(
VALIDATION_TYPEHASH,
validation.wallet,
validation.share,
validation.pool
));
}
event compare(address sig, address key);
function verify(Validation calldata validation, uint8 v, bytes32 r, bytes32 s) public {
bytes32 digest = keccak256(abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
hash(validation)
));
emit compare(ecrecover(digest, v, r, s), _validationKey);
}
}谢谢你的时间和考虑!
发布于 2022-07-25 12:59:13
当您成功地检索到地址时,请使用此契约模板首先恢复您的地址,然后将其应用到您的合同中。我也会解释一下合同:
pragma solidity ^0.8.0;
contract SignTest {
address owner = msg.sender;
mapping(uint256 => bool) usedNonces;
function test(uint256 amount, uint256 nonce, bytes memory sig, uint tV, bytes32 tR, bytes32 tS, bytes32 tMsg) public view returns(address) {
bytes32 message = prefixed(keccak256(abi.encodePacked(amount, nonce)));
bytes32 messageWithoutPrefix = keccak256(abi.encodePacked(amount, nonce));
address signer = recoverSigner(messageWithoutPrefix, sig, tV, tR,tS);
return signer;
}
// Signature methods
function splitSignature(bytes memory sig)
public
view
returns (uint8, bytes32, bytes32)
{
require(sig.length == 65, "B");
bytes32 r;
bytes32 s;
uint8 v;
assembly {
// first 32 bytes, after the length prefix
r := mload(add(sig, 32))
// second 32 bytes
s := mload(add(sig, 64))
// final byte (first byte of the next 32 bytes)
v := byte(0, mload(add(sig, 96)))
}
return (v, r, s);
}
function recoverSigner(bytes32 message, bytes memory sig, uint tV, bytes32 tR, bytes32 tS)
public
view
returns (address)
{
uint8 v;
bytes32 r;
bytes32 s;
(v, r, s) = splitSignature(sig);
require(v==tV, "V is not correct");
require(r==tR, "R is not correct");
require(s==tS, "S is not correct");
return ecrecover(message, v, r, s);
}
// Builds a prefixed hash to mimic the behavior of eth_sign.
function prefixed(bytes32 inputHash) public pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", inputHash));
}
}我使用web3来恢复,因此,让我提供一个简单的示例:
let fnSignature = web3.utils.keccak256("setApprovalForAll(address,bool").substr(0,10)
// encode the function parameters and add them to the call data
let fnParams = web3.eth.abi.encodeParameters(
["address","bool"],
[toAddr,permit]
)
calldata = fnSignature + fnParams.substr(2)
console.log(calldata)首先,对函数签名的前4个字节进行substr,然后将它们编码到fnParams + fnSignature中。
第二步是签署您的数据:
const data = calldata //Retrieved from above code
const NFTAddress = 'Contract address where you sign'
const newSigner = web3.eth.accounts.privateKeyToAccount("Your Priv Key");
const myAccount = web3.eth.accounts.wallet.add(newSigner);
const signer = myAccount.address;
console.log(signer) // display the target address in console ( for better verification )使用第一个函数中的回调数据并将其添加到(您希望在其中签名散列),然后按照以下步骤操作:
let rawData = web3.eth.abi.encodeParameters(
['address','bytes'],
[NFTAddress,data]
);
// hash the data.
let hash = web3.utils.soliditySha3(rawData);
console.log(hash)
// sign the hash.
let signature = web3.eth.sign(hash, signer);
console.log(signature)使用“散列”,转到实体契约(由我发布),并使用“签名”将检索到的字节(散列)添加到函数前缀(bytes32散列)中,转到实体契约并检索v,r,s。使用函数splitSignature(bytes32签名)使用由函数前缀生成的字节( bytes32散列),使用函数recoverSigner(消息(前缀字节(Bytes32hash)),签名(来自web3 javascript),v( uint8 from splitSignature),r(Bytes32 from splitSignature),s(bytes32,splitSignature) )。
你也可以玩我提供的合同和脚本,但是,我更喜欢一步一步的发布指南,让每个人都能理解。
发展愉快!:)
https://stackoverflow.com/questions/72381687
复制相似问题