Schnor 签名
一个javaScript库,用于签名和验证施诺尔签名.
它可用于单签名和多签名.
通过ecrecover进行区块链验证也得到了支持.
字体支持
从2.0版本开始,我们完全转到了Typescript.
2.0 版本数 中断更改
sign()财务报告和审定财务报表multiSigSign()返回实例SignatureOutput中每个元素都有缓冲属性- 改为
e我们回来了challenge为施诺尔挑战赛。 要达到它的价值,使用challenge.buffer - 改为
s我们回来了signature为施诺尔签名。 要达到它的价值,使用signature.buffer - 改为
R我们回来了publicNonce为非斯. 要达到它的价值,使用publicNonce.buffer
- 改为
getCombinedPublicKey()返回 aKey课。 要得到真正的钥匙,使用key.buffer- 许多方法变得静止 因为他们没有保持任何状态:
verifysignsumSigsgetCombinedPublicKeygetCombinedAddress
版本 3.0 中断更改
finalPublicNonce, (中文(简体) ),FinalPublicNonce在所有地方替换为publicNonce, (中文(简体) ),PublicNonce旧名只是没有道理.sign()是第一个signHash()。接受纯文本消息作为参数的符号函数已不存在.multiSigSign()是第一个multiSigSignHash()。接受纯文本消息作为参数的符号函数已不存在.verify()是第一个verifyHash()。接受纯文本消息作为参数的校验功能已不复存在.
在第二版中,我们有许多方法可以签署一个信息。 对于在各种情况下使用何种职能是恰当的,这种广泛的混淆。 这使我们相信,使事情简单化并迫使散列成为方法是前进的道路.
所需经费:
- 节点:QQ16.0.0, <20.0.0
- npm (Node.js软件包管理器) v9.x.x
安装
git clone https://github.com/borislav-itskov/schnorrkel.js
cd schnorrkel.js
npm i
测试
npm run test
使用量
单一签名
我们称单一签字为单一签字.
签名 :
import { SchnorrSigner } from "@borislav.itskov/schnorrkel.js";
import { hexlify, randomBytes, hashMessage } from "ethers/lib/utils";
const privateKey = hexlify(randomBytes(32));
const signer = new SchnorrSigner(pk1);
const msg = "test message";
const commitment = hashMessage(msg);
const signature = signer.sign(commitment);
离链核查:
我们拿 signature 财务报告和审定财务报表 hash 从上文的例子来看
const result = signer.verify(hash, signature);
链上核查:
首先,你需要一个合同 核实Schnorr。 我们把它藏在仓库里 叫做 SchnorrAccountAbstraction。 。 。 .
但总之,你需要这个链子:
function verifySchnorr(bytes32 hash, bytes memory sig) internal pure returns (bool) {
// px := public key x-coord
// e := schnorr signature challenge
// s := schnorr signature
// parity := public key y-coord parity (27 or 28)
(bytes32 px, bytes32 e, bytes32 s, uint8 parity) = abi.decode(sig, (bytes32, bytes32, bytes32, uint8));
// ecrecover = (m, v, r, s);
bytes32 sp = bytes32(Q - mulmod(uint256(s), uint256(px), Q));
bytes32 ep = bytes32(Q - mulmod(uint256(e), uint256(px), Q));
require(sp != Q);
// the ecrecover precompile implementation checks that the `r` and `s`
// inputs are non-zero (in this case, `px` and `ep`), thus we don't need to
// check if they're zero.
address R = ecrecover(sp, parity, px, ep);
require(R != address(0), "ecrecover failed");
return e == keccak256(abi.encodePacked(R, uint8(parity), px, hash));
}
我们来解释神秘感是如何起作用的 为什么以后需要它 在本文档中。 。 。 .
让我们向当地的硬帽节点发出一个请求. 终点站首跑:
npx 硬帽子节点
然后,这里是代码的一部分:
import { SchnorrSigner } from "@borislav.itskov/schnorrkel.js";
import { hexlify, randomBytes, hashMessage } from "ethers/lib/utils";
import { ContractFactory } from "ethers";
const privateKey = hexlify(randomBytes(32));
const signer = new SchnorrSigner(privateKey);
const factory = new ContractFactory(
SchnorrAccountAbstraction.abi,
SchnorrAccountAbstraction.bytecode,
wallet
);
const contract: any = await factory.deploy([signer.getSchnorrAddress()]);
const msg = "just a test message";
const commitment = hashMessage(msg);
const sig = signer.sign(commitment);
const result = await contract.isValidSignature(
commitment,
signer.getEcrecoverSignature(sig)
);
您可以在 tests/schnorrkel/onchainSingleSign.test.ts 在此仓库中.
多边形
Schnorr 的多签名基于 n/n - 所有签名者都需要签名才能使签名有效.
下面是制定成功的多步骤.
多图像
为了让多西格更容易,a SchnorrMultisigProvider 类被创建。 它期待所有 SchnorrProvider 参加多西格的对象。 这个 SchnorrProvider 可以自己通过 或者一个人可以使用 SchnorrSigner。最刻薄的一点是,你不需要拥有私钥即可使用 SchnorrMultisigProvider。目标是为获取正确的链上 schnor 地址提供辅助功能,即所有签名者的综合公钥,并为获取预期的链上结构进行验证提供方便的方法
import {
SchnorrSigner,
SchnorrMultisigProvider,
} from "@borislav.itskov/schnorrkel.js";
const signerOne = new SchnorrSigner(pk1);
const signerTwo = new SchnorrSigner(pk2);
const multisigProvider = new SchnorrMultisigProvider([signerOne, signerTwo]);
公众非
公开的非ce需要在签字人之间进行交换后签字. 你可以用两种方式做到这一点.
使用多相图
const publicNonces = multisigProvider.getPublicNonces();
或手动给每个签名人单独打电话:
const publicNonces = [signerOne.getPublicNonces(), signerOne.getPublicNonces()];
在签署开始之前,非斯需要被交换. 还有.., getPublicNonces 在所有签署人完成签署程序之前,不应再次打电话。 或者至少一个人应该注意不要将公共非ces与新产生的非ces相混. 对于混血儿,签名不会奏效.
签名
以下是签字过程的例子。 公用密钥和公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用非公用.
import { solidityKeccak256 } from "ethers/lib/utils";
const msg = "just a test message";
const msgHash = solidityKeccak256(["string"], [msg]);
const publicKeys = multisigProvider.getPublicKeys();
const publicNonces = multisigProvider.getPublicNonces();
const signature = signerOne.sign(msgHash, publicKeys, publicNonces);
const signatureTwo = signerTwo.sign(msgHash, publicKeys, publicNonces);
校验链上
用于链上核查的编码数据的生成有些复杂,因此,它隐藏在 multisigProvider。 。 。 .
以下是如何使用提供者进行链上核查的例子:
const ecRecoverSchnorr = multisigProvider.getEcrecoverSignature([
signature,
signatureTwo,
]);
const result = await contract.isValidSignature(msgHash, ecRecoverSchnorr);
这里也有一个例子,说明没有多相传输器,你如何做到这一点:
import Schnorrkel from "@borislav.itskov/schnorrkel.js";
import { defaultAbiCoder } from "ethers/lib/utils";
const publicKeys = [signerOne.publicKey, signerTwo.publicKey];
const publicKey = arrayify(Schnorrkel.getCombinedPublicKey(publicKeys).buffer);
const sigOutputs = [signature, signatureTwo];
const sSummed = Schnorrkel.sumSigs(
sigOutputs.map((output) => output.signature)
);
const challenge = sigOutputs[0].challenge;
const px = publicKey.slice(1, 33);
const parity = publicKey[0] - 2 + 27;
const ecRecoverSchnorr = defaultAbiCoder.encode(
["bytes32", "bytes32", "bytes32", "uint8"],
[px, challenge.buffer, sSummed.buffer, parity]
);
const result = await contract.isValidSignature(msgHash, ecRecoverSchnorr);
校验下链
与多西格提供商:
const result = multisigProvider.verify(msgHash, [signature, signatureTwo]);
没有它:
import Schnorrkel from "@borislav.itskov/schnorrkel.js";
const publicKeys = [signerOne.publicKey, signerTwo.publicKey];
const sigOutputs = [signature, signatureTwo];
const sSummed = Schnorrkel.sumSigs(
sigOutputs.map((output) => output.signature)
);
return _verify(
sSummed.buffer,
msgHash,
sigOutputs[0].publicNonce.buffer,
Schnorrkel.getCombinedPublicKey(publicKeys).buffer
);
您可以在 tests/schnorrkel/onchainMultiSign.test.ts 在此仓库中.
隐形
对于链上核查工作而言,我们受到以下工作的启发: 点数没有他的工作,RND需要更多的时间才能到达这个点. 你可以看看他的仓库 这里
我们利用Ethereum的隐秘 来验证签名。 这就是它的工作方式:
Ethereum ecrecover 返回一个地址( 公共密钥的屏蔽) 给定了 ECDSA 签名 .
给定 m 和 ECDSA 签名 (v, r, s), 其中 v 表示 x 坐标点的 Y 坐标的等同 r
ecrecover(m, v, r, s):
R = point derived from r and v
a = -G*m
b = R*s
Qr = a + b
Q = Qr * (1/r)
Q = (1/r) * (R*s - G*m) //recovered pubkey
Ethereum的ecrecover返回了64字节公用钥匙的keccak256散列的最后20字节. 鉴于签名(R, s),消息m和公钥P,我们可以将值输入密钥,这样返回的地址就可以与挑战作一比较.
calculate e = H(address(R) || m) and P_x = x-coordinate of P
过:
m = -s*P_x
v = parity of P
r = x-coordinate of P
s = -e*P_x
然后:
ecrecover(m=-s*P_x, v=0/1, r=P_x, s=-e*P_x):
P = point derived from r and v (public key)
a = -G*(-s*P_x) = G*s*P_x
b = P*(-m*P_x) = -P*e*P_x
Q = (1/P_x) (a+b)
Q = (1/P_x)(G*s*P_x - P*e*P_x)
Q = G*s - P*e // same as schnorr verify above
返回的值是地址(Q).
- 计算 e' = h( 地址( Q) ++ m)
- 检查 e' == 以验证签名.