@sodot/sodot-node-sdk • Docs
@sodot/sodot-node-sdk / BIP340
Class: BIP340
Class providing the functionality for the BIP340 protocol the FROST MPC protocol.
Example
// Your server side creates a room for 3 parties using its API_KEY
// Creating a room uuid should always happen on the server side using your API_KEY
const N = 3;
const T = 2;
const bip340 = new BIP340();
const API_KEY = 'MY_API_KEY';
const keygenRoomUuid = await bip340.createRoom(N, API_KEY);
// All parties call initKeygen to get an BIP340InitKeygenResult, that contains a keygenId
const keygenInitResult = await bip340.initKeygen();
// All parties receive the keygenIds from all other parties
const keygenIds = [keygenId1, keygenId2];
// All parties join the keygen room
const keygenResult = await bip340.keygen(keygenRoomUuid, N, T, keygenInitResult, keygenIds);
// Pick the derivation path of the public key you want to sign for
const derivationPath = new Uint32Array([44,60,0,0,0]);
// Get the public key for the derivation path
const pubkey = await bip340.deriveTweakPubkey(keygenResult, derivationPath);
// To sign a message, create a signing room on the server side, using your API_KEY
const signingRoomUuid = await bip340.createRoom(T, API_KEY);
// your message in hex
const message = 'deadbeef';
// 2 parties join the signing room
const signature = await bip340.sign(signingRoomUuid, keygenResult, message, derivationPath);
// This signature can now be verified against pubkey
// Refreshing the secret key material
// Your server creates a room for 3 parties
const refreshRoomUuid = await bip340.createRoom(N, API_KEY);
// All parties join the refresh room
const refreshResult = await bip340.refresh(refreshRoomUuid, keygenResult);
// Note: refreshResult.pubkey == keygenResult.pubkey
// Signing using the new secret key material
// The room is again created by the server
const signingRoomUuid2 = await bip340.createRoom(T, API_KEY);
const message2 = 'deadbeefcafebabe';
const signature2 = await bip340.sign(signingRoomUuid2, refreshResult, message2, derivationPath);
// This signature can now be verified against pubkey
Extends
BIP340
Constructors
new BIP340()
new BIP340(
hostUrl
?):BIP340
Constructs a new Ecdsa instance.
Parameters
• hostUrl?: string
Returns
Overrides
BIP340Internal.constructor
Methods
createRoom()
createRoom(
numParties
,apiKey
):Promise
<string
>
Creates a room for the given number of parties. A room is a one time instance used to perform a single MPC operation(keygen/signing/refresh etc.) between parties
This function should be called in the backend so to not embed the API key in code that is distributed to the users. After the backend calls this function, the other parties can join the room by calling the relevant keygen/signing/refresh/etc. operation.
Parameters
• numParties: number
The number of parties that will join the room. (an integer in range 1..65_535)
• apiKey: string
An API key is required to create a room
Returns
Promise
<string
>
The UUID of the created room.
Inherited from
BIP340Internal.createRoom
derivePrivateKeyFromXpriv()
derivePrivateKeyFromXpriv(
xpriv
,derivationPath
?):Promise
<string
>
Parses an xpriv
string according to BIP-32 non-hardened, and returns the derived private key for a given BIP-32 non-hardened derivation path
Parameters
• xpriv: string
A valid secp256k1 xpriv string.
• derivationPath?: Uint32Array
The BIP-32 non-hardened derivation path to use for computing the private key.
Returns
Promise
<string
>
The derived private key of the xpriv.
Inherited from
BIP340Internal.derivePrivateKeyFromXpriv
deriveTweakPubkey()
deriveTweakPubkey(
keygenResult
,derivationPath
?,tweak
?):Promise
<Uint8Array
>
Returns the (optionally)derived and (optionally)tweaked public key for a keygenResult
for a given BIP-32 non-hardened derivation path and an optional BIP-341 tweak
The tweak
is used to tweak a public key with a BIP-341 Taproot tweaking, by supplying the T
value from the BIP-341 Taproot specification(https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#script-validation-rules):
Let t = hash_<TapTweak
>(p || km).
If t ≥ 0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141 (order of secp256k1), fail.
Let Q = P + int(t)G.
Parameters
• keygenResult: string
| BIP340KeygenResult
An BIP340KeygenResult
that contains a secret share, or the secret share as a string.
• derivationPath?: Uint32Array
The BIP-32 non-hardened derivation path to use for computing the public key.
• tweak?: string
| Uint8Array
The tweak to be used for signing. Must be 32 bytes long, either as a Uint8Array or as a hex string.
Returns
Promise
<Uint8Array
>
The derived public key of the keypair, you can sign for it by passing the same derivation_path
and tweak
to the sign
method.
Inherited from
BIP340Internal.deriveTweakPubkey
deriveTweakPubkeyFromXpub()
deriveTweakPubkeyFromXpub(
Xpub
,derivationPath
?,tweak
?):Promise
<Uint8Array
>
Returns the derived public key for a given BIP-32 non-hardened derivation path and an extended public key (xpub).
Parameters
• Xpub: string
The extended public key (xpub) of the keypair.
• derivationPath?: Uint32Array
The BIP-32 non-hardened derivation path to use for computing the public key.
• tweak?: string
| Uint8Array
The tweak to be used for signing. Must be 32 bytes long, either as a Uint8Array or as a hex string.
Returns
Promise
<Uint8Array
>
The extended public key (xpub) of the keypair as a hex string.
Inherited from
BIP340Internal.deriveTweakPubkeyFromXpub
exportFullPrivateKey()
exportFullPrivateKey(
roomUuid
,keygenResult
,toExportID
):Promise
<undefined
|string
>
Combine all secret shares and export the full private key to a single party. Requires a threshold amount of parties to participate.
Parameters
• roomUuid: string
The UUID of the export room.
• keygenResult: string
| BIP340KeygenResult
The BIP340KeygenResult
that contains the secret share of the private key to be extracted, or the secret key as a string.
• toExportID: string
The exportID outputs from exportID()
of the party that will receive the private key, this must match between all parties, and the receiving party must participate.
Returns
Promise
<undefined
| string
>
The party being exported to will receive a string
containing the full xpriv, while the rest will receive undefined
.
Inherited from
BIP340Internal.exportFullPrivateKey
exportID()
exportID(
keygenResult
):Promise
<string
>
The party that expects to receive the private key needs to call this function before exportFullPrivatekey
.
It must then transmit the ID to threshold-1 parties, this can be done in an untrusted channel (as if the parties use different keys this will break)
Once the parties have the exportID of the party they want to export the private key to, the party and the threshold-1 participants need to call exportFullPrivatekey
with that ID.
Parameters
• keygenResult: string
| BIP340KeygenResult
The BIP340KeygenResult that contains the secret share to be used for key extraction, or the secret share as a string.
Returns
Promise
<string
>
A string that contains a base58 string exportID.
Inherited from
BIP340Internal.exportID
getXpub()
getXpub(
keygenResult
):Promise
<string
>
Returns returns a base58 encoded extended public key (Xpub) derived from a BIP340KeygenResult. See BIP-32: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#serialization-format The Xpub can be used either via third party libraries or via BIP340.deriveTweakPubkeyFromXpub
Parameters
• keygenResult: string
| BIP340KeygenResult
An BIP340KeygenResult that contains the secret share of the private key, or the secret share as a string.
Returns
Promise
<string
>
base58 encoded extended public key (Xpub).
Inherited from
BIP340Internal.getXpub
importPrivateKeyImporter()
importPrivateKeyImporter(
roomUuid
,threshold
,privateKey
,keygenInit
,keygenIds
):Promise
<BIP340KeygenResult
>
WARNING: Private key import is an advanced feature of the SDK. We strongly advise consulting with the Sodot team before using it, due to a full private key being imported from a different system. Secret shares generated from imported private keys will always have the risk of the private key having been compromised in the past or in the future in case the private key is not deleted after the import operation.
Importing a full private key, and sharing into a T-of-N
sharing, the resultant key shares will be of the exact same public key as the full private key.
This is the method that an importing party (meaning one the party in possesion of the private key) should use for receiving a key share in the new T-of-N
quorum.
Parameters
• roomUuid: string
The UUID of the import room.
• threshold: number
The threshold of the keypair of the new quorum. (an integer in range 1..65_535)
• privateKey: string
The private key to be imported. (a hex string of length 64)
• keygenInit: BIP340InitKeygenResult
This must be the same BIP340InitKeygenResult the keygenId of was sent to the other parties.
• keygenIds: string
[]
The keygenId outputs from initKeygen()
, of all other parties we wish to be part of the new quorum with, keygenId
s from all parties must be received through an authenticated communication channel.
Returns
Promise
<BIP340KeygenResult
>
An BIP340KeygenResult that contains the public key as well as the secret data that can be used for signing.
Example
const N = 3;
const T = 2;
// A private key is created in some external system.
// const privateKey = "b3ac...0d71"; // hex string with 64 hex chars
// Some time passes...
// Now this party wishes to be part of a new quorum of `2-of-3` sharing of the private key.
// The app server creates a room for N(= 3) parties.
const importRoomUuid = await bip340.createRoom(N, API_KEY);
// The other parties must join the import room using the `importPrivateKeyRecipient` method.
const keygenInitResult = await bip340.initKeygen(); // This the importing party
// This importing party will send its `keygenInitResult.keygenId` to all other parties.
// This party will also receive the `keygenId`s of all other parties of the new quorum.
const keygenIds = [keygenId1, keygenId2, keygenId3]; // Note that here we must include our own `keygenId` as well, the order of the ids doesn't matter.
const importKeygenResult = await bip340.importPrivateKeyImporter(importRoomUuid, T, privateKey, keygenInitResult, keygenIds);
// importKeygenResult can now be used for signing under the T(= 2) threshold with the same public key
Inherited from
BIP340Internal.importPrivateKeyImporter
importPrivateKeyRecipient()
importPrivateKeyRecipient(
roomUuid
,threshold
,keygenInit
,keygenIds
):Promise
<BIP340KeygenResult
>
WARNING: Private key import is an advanced feature of the SDK. We strongly advise consulting with the Sodot team before using it, due to a full private key being imported from a different system. Secret shares generated from imported private keys will always have the risk of the private key having been compromised in the past or in the future in case the private key is not deleted after the import operation.
Importing a full private key, and sharing into a T-of-N
sharing, the resultant key shares will be of the exact same public key as the full private key.
This is the method that a new party (meaning one that does not currently have the private key) should use for receiving a key share in the new T-of-N
quorum.
The method takes the same input parameters as keygen
since for a new party joining the quorum the import
operation is very similar to a keygen
operation.
Parameters
• roomUuid: string
The UUID of the import room.
• threshold: number
The threshold of the keypair of the new quorum. (an integer in range 1..65_535)
• keygenInit: BIP340InitKeygenResult
This must be the same BIP340InitKeygenResult the keygenId of was sent to the other parties.
• keygenIds: string
[]
The keygenId outputs from initKeygen()
, of all other parties we wish to be part of the new quorum with, keygenId
s from all parties must be received through an authenticated communication channel.
Returns
Promise
<BIP340KeygenResult
>
An BIP340KeygenResult that contains the public key as well as the secret data that can be used for signing.
Example
const N = 3;
const T = 2;
// A private key is created in some external system.
// Some time passes...
// Now this party wishes to be part of a new quorum of `2-of-3` sharing of the private key.
// The app server creates a room for N(= 3) parties.
const importRoomUuid = await bip340.createRoom(N, API_KEY);
// The party with the private key must join the import room using the `importPrivateKeyImporter` method.
const keygenInitResult = await bip340.initKeygen(); // This is a new party
// This new party will send its `keygenInitResult.keygenId` to all other parties.
// This party will also receive the `keygenId`s of all other parties of the new quorum.
const keygenIds = [keygenId1, keygenId2, keygenId3]; // Note that here we must include our own `keygenId` as well, the order of the ids doesn't matter.
const importKeygenResult = await bip340.importPrivateKeyRecipient(importRoomUuid, T, keygenInitResult, keygenIds);
// importKeygenResult can now be used for signing under the T(= 2) threshold with the same public key
Inherited from
BIP340Internal.importPrivateKeyRecipient
initKeygen()
initKeygen():
Promise
<BIP340InitKeygenResult
>
All parties must call this function before calling keygen. All parties receive an BIP340InitKeygenResult as an output from this function. The BIP340InitKeygenResult.keygenId must be sent through an authenticated communication channel to all other devices we wish to perform keygen with. Once we have the keygenId-s of all parties, then keygen can be called with the same BIP340InitKeygenResult.keygenSecret as was given here.
Returns
Promise
<BIP340InitKeygenResult
>
An BIP340InitKeygenResult that contains a base58 string keygenId and keygenSecret.
Inherited from
BIP340Internal.initKeygen
keygen()
keygen(
roomUuid
,numParties
,threshold
,keygenInit
,keygenIds
):Promise
<BIP340KeygenResult
>
Generate a keypair for the given number of parties and threshold.
Parameters
• roomUuid: string
The UUID of the keygen room.
• numParties: number
The number of parties that will join the keygen room. (an integer in range 1..65_535)
• threshold: number
The threshold of the keypair that will be generated. (an integer in range 1..65_535)
• keygenInit: BIP340InitKeygenResult
This must be the same BIP340InitKeygenResult the keygenId of was sent to the other parties.
• keygenIds: string
[]
The keygenId outputs from initKeygen of all other parties we wish to perform keygen with, these must have been received through an authenticated communication channel.
Returns
Promise
<BIP340KeygenResult
>
An BIP340 KeygenResult that includes the public key of the generated keypair.
Example
// Your server side creates a room for 3 parties using its API_KEY
const N = 5;
const T = 3;
const bip340 = new BIP340();
const API_KEY = 'MY_API_KEY';
const keygenRoomUuid = await bip340.createRoom(N, API_KEY);
// All parties call initKeygen to get an BIP340InitKeygenResult, that contains a keygenId
const keygenInitResult = await bip340.initKeygen();
// All parties receive the keygenIds from all other parties
const keygenIds = [keygenId1, keygenId2, keygenId3, keygenId4];
// All parties join the keygen room
const keygenResult = await bip340.keygen(keygenRoomUuid, N, T, keygenInitResult, keygenIds);
// keygenResult.pubkey is now distributed between all 5 parties, such that each 3 parties can sign a message
Inherited from
BIP340Internal.keygen
offlineExportFullPrivateKey()
offlineExportFullPrivateKey(
keygenResults
):Promise
<string
>
Receives as input an array of threshold
BIP340KeygenResult
s and locally computes the full private key (spriv).
The main use case for this function is in an offline recovery setting where keygen results are collected manually and used to recover the full private key on an air-gapped server/device.