Skip to main content

ECDSA

ECDSA模块可以提供恢复以太坊账号ECDSA签名的功能,对应签名web.eth.sign

使用场景

可以用于处理: 链上无法做校验,链下可以校验的场景 比如: 链验证节点账户做认证。链上不清楚该账户是否为链的验证节点,但是链下中心化服务可以知道哪些账户是链验证节点。这样就可以由中心化服务签名。合约只要校验是中心化管理员的签名,即可认为该签名信息是权威有效的。流程如下

使用如下

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

contract ECDSAContract {
using ECDSA for bytes32;

function checkSignature(
bytes memory signature,
uint256 signatureExpTimestamp,
uint256 roundId
) external view returns (address) {
bytes32 hash = keccak256(
abi.encodePacked(
signatureExpTimestamp,
msg.sender,
roundId,
address(this)
)
);
bytes32 messageHash = hash.toEthSignedMessageHash();
return messageHash.recover(signature);
}
}

签名部分封装

import { ethers } from "hardhat";
import * as ethUtil from "ethereumjs-util";

export function ECDSASign(
types: Array<string>,
values: Array<any>,
privateKey: string
): Buffer {
const digest = ethers.utils.keccak256(
ethers.utils.solidityPack(types, values)
);
console.log({ digest });
const prefixedHash = ethUtil.hashPersonalMessage(ethUtil.toBuffer(digest));
const { v, r, s } = ethUtil.ecsign(
prefixedHash,
Buffer.from(privateKey, "hex")
);
const vb = Buffer.from([v]);
const signature = Buffer.concat([r, s, vb]);
return signature;
}

测试

import * as ethUtil from "ethereumjs-util";
import { ethers } from "hardhat";
import { expect } from "chai";

async function testCheckSignature() {
// admin signer private key
const privateKey =
"ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
const accounts = await ethers.getSigners();
const signer = accounts[0];
const alice = accounts[1];

const ECDSAContract = await ethers.getContractFactory("ECDSAContract");
const ecdsa = await ECDSAContract.deploy();
await ecdsa.deployed();

const roundId = 1;
const signatureExpTimestamp = Date.now() + 20 * 60;
const digest = ethers.utils.keccak256(
ethers.utils.solidityPack(
["uint256", "address", "uint256", "address"],
[signatureExpTimestamp, alice.address, roundId, ecdsa.address]
)
);
const prefixedHash = ethUtil.hashPersonalMessage(ethUtil.toBuffer(digest));
const { v, r, s } = ethUtil.ecsign(
prefixedHash,
Buffer.from(privateKey, "hex")
);
const vb = Buffer.from([v]);
const signature = Buffer.concat([r, s, vb]);
const addr = await ecdsa
.connect(alice)
.checkSignature(signature, signatureExpTimestamp, roundId);
expect(addr).to.eql(signer.address);
}