-
Notifications
You must be signed in to change notification settings - Fork 574
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
adds napi methods to support Ledger multisig (#5376)
adds typescript version of test_dkg_signing example adds multisig.test.slow.ts that replicates the logic of test_dkg_signing from ironfish-rust - adds method to retrieve frost signing package from deserialized signing package - adds signingPackageFromRaw method - allows construction of signing package from identities and raw commitments (from frost, not ironfish) - adds method to NativeSigningCommitment to get raw_commitments - defines NativeSignatureShare to support deserializing ironfish SignatureShares and accessing the underlying identity and frost signature share - adds from_frost factor method to reconstruct SignatureShare from parts
- Loading branch information
Showing
4 changed files
with
279 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ | ||
|
||
import { Asset, multisig, Note as NativeNote, verifyTransactions } from '@ironfish/rust-nodejs' | ||
import { Note, RawTransaction } from './primitives' | ||
import { Transaction, TransactionVersion } from './primitives/transaction' | ||
import { makeFakeWitness } from './testUtilities' | ||
|
||
describe('multisig', () => { | ||
describe('dkg', () => { | ||
it('should create multisig accounts and sign transactions', () => { | ||
const participantSecrets = [ | ||
multisig.ParticipantSecret.random(), | ||
multisig.ParticipantSecret.random(), | ||
multisig.ParticipantSecret.random(), | ||
] | ||
|
||
const secrets = participantSecrets.map((secret) => secret.serialize().toString('hex')) | ||
const identities = participantSecrets.map((secret) => | ||
secret.toIdentity().serialize().toString('hex'), | ||
) | ||
|
||
const minSigners = 2 | ||
|
||
const round1Packages = secrets.map((_, index) => | ||
multisig.dkgRound1(identities[index], minSigners, identities), | ||
) | ||
|
||
const round1PublicPackages = round1Packages.map( | ||
(packages) => packages.round1PublicPackage, | ||
) | ||
|
||
const round2Packages = secrets.map((secret, index) => | ||
multisig.dkgRound2( | ||
secret, | ||
round1Packages[index].round1SecretPackage, | ||
round1PublicPackages, | ||
), | ||
) | ||
|
||
const round2PublicPackages = round2Packages.map( | ||
(packages) => packages.round2PublicPackage, | ||
) | ||
|
||
const round3Packages = participantSecrets.map((participantSecret, index) => | ||
multisig.dkgRound3( | ||
participantSecret, | ||
round2Packages[index].round2SecretPackage, | ||
round1PublicPackages, | ||
round2PublicPackages, | ||
), | ||
) | ||
|
||
const publicAddress = round3Packages[0].publicAddress | ||
|
||
const raw = new RawTransaction(TransactionVersion.V1) | ||
|
||
const inNote = new NativeNote( | ||
publicAddress, | ||
42n, | ||
Buffer.from(''), | ||
Asset.nativeId(), | ||
publicAddress, | ||
) | ||
const outNote = new NativeNote( | ||
publicAddress, | ||
40n, | ||
Buffer.from(''), | ||
Asset.nativeId(), | ||
publicAddress, | ||
) | ||
const asset = new Asset(publicAddress, 'Testcoin', 'A really cool coin') | ||
const mintOutNote = new NativeNote( | ||
publicAddress, | ||
5n, | ||
Buffer.from(''), | ||
asset.id(), | ||
publicAddress, | ||
) | ||
|
||
const witness = makeFakeWitness(new Note(inNote.serialize())) | ||
|
||
raw.spends.push({ note: new Note(inNote.serialize()), witness }) | ||
raw.outputs.push({ note: new Note(outNote.serialize()) }) | ||
raw.outputs.push({ note: new Note(mintOutNote.serialize()) }) | ||
raw.mints.push({ | ||
creator: asset.creator().toString('hex'), | ||
name: asset.name().toString(), | ||
metadata: asset.metadata().toString(), | ||
value: mintOutNote.value(), | ||
}) | ||
raw.fee = 1n | ||
|
||
const proofAuthorizingKey = round3Packages[0].proofAuthorizingKey | ||
const viewKey = round3Packages[0].viewKey | ||
const outgoingViewKey = round3Packages[0].outgoingViewKey | ||
|
||
const unsignedTransaction = raw.build(proofAuthorizingKey, viewKey, outgoingViewKey) | ||
const transactionHash = unsignedTransaction.hash() | ||
|
||
const commitments = secrets.map((secret, index) => | ||
multisig.createSigningCommitment( | ||
secret, | ||
round3Packages[index].keyPackage, | ||
transactionHash, | ||
identities, | ||
), | ||
) | ||
|
||
// Simulates receiving raw commitments from Ledger | ||
// Ledger app generates raw commitments, not wrapped SigningCommitment | ||
const commitmentIdentities: string[] = [] | ||
const rawCommitments: string[] = [] | ||
for (const commitment of commitments) { | ||
const signingCommitment = new multisig.SigningCommitment(Buffer.from(commitment, 'hex')) | ||
commitmentIdentities.push(signingCommitment.identity().toString('hex')) | ||
rawCommitments.push(signingCommitment.rawCommitments().toString('hex')) | ||
} | ||
|
||
const signingPackage = unsignedTransaction.signingPackageFromRaw( | ||
commitmentIdentities, | ||
rawCommitments, | ||
) | ||
|
||
// Ensure that we can extract deserialize and extract frost signing package | ||
// Ledger app needs frost signing package to generate signature shares | ||
const frostSigningPackage = new multisig.SigningPackage( | ||
Buffer.from(signingPackage, 'hex'), | ||
).frostSigningPackage() | ||
expect(frostSigningPackage).not.toBeUndefined() | ||
|
||
const signatureShares = secrets.map((secret, index) => | ||
multisig.createSignatureShare(secret, round3Packages[index].keyPackage, signingPackage), | ||
) | ||
|
||
// Ensure we can construct SignatureShare from parts | ||
// Ledger app returns raw frost signature shares | ||
for (const share of signatureShares) { | ||
const signatureShare = new multisig.SignatureShare(Buffer.from(share, 'hex')) | ||
const reconstructed = multisig.SignatureShare.fromFrost( | ||
signatureShare.frostSignatureShare(), | ||
signatureShare.identity(), | ||
) | ||
expect(reconstructed.frostSignatureShare()).toEqual( | ||
signatureShare.frostSignatureShare(), | ||
) | ||
expect(reconstructed.identity()).toEqual(signatureShare.identity()) | ||
} | ||
|
||
const serializedTransaction = multisig.aggregateSignatureShares( | ||
round3Packages[0].publicKeyPackage, | ||
signingPackage, | ||
signatureShares, | ||
) | ||
const transaction = new Transaction(serializedTransaction) | ||
|
||
expect(verifyTransactions([serializedTransaction])).toBeTruthy() | ||
|
||
expect(transaction.unsignedHash().equals(transactionHash)).toBeTruthy() | ||
}) | ||
}) | ||
}) |