new Non_Custodial_Wallet()
Non-custodial wallet implementation using Threshold Signature Scheme (TSS) for distributed key management. Enables multi-party control without a trusted party.
This class implements advanced threshold cryptography where any subset of participants meeting the threshold requirement can collaboratively generate valid signatures without ever reconstructing the private key. It's ideal for scenarios requiring distributed control, enhanced security, and elimination of single points of failure.
Key Features:
- Distributed key generation using Joint Verifiable Random Secret Sharing (JVRSS)
- Threshold signature generation compatible with standard ECDSA verification
- No trusted dealer required for key setup
- Configurable t-of-n threshold schemes (e.g., 2-of-3, 3-of-5, 5-of-7)
- Secret shares can be distributed across different entities or devices
- Compatible with Bitcoin transaction signing and verification
Security Model:
- Requires exactly t participants to generate signatures
- Information-theoretic security: < t participants learn nothing about private key
- No single point of failure: up to n-t participants can be compromised safely
- Private key never exists in complete form anywhere
- Forward secrecy: compromising future shares doesn't reveal past signatures
Use Cases:
- Corporate treasury management with executive approval
- Cryptocurrency exchanges with operator separation
- Escrow services with dispute resolution
- Multi-signature wallets for shared accounts
- Compliance requirements for multi-party authorization
- High-value asset protection with distributed control
- Since:
- 1.0.0
- Source:
Examples
// Create a 2-of-3 escrow wallet
const escrowWallet = Non_Custodial_Wallet.fromRandom("main", 3, 2);
const [buyerShare, sellerShare, arbiterShare] = escrowWallet._shares;
// Normal release: buyer + seller
const releaseWallet = Non_Custodial_Wallet.fromShares("main", [buyerShare, sellerShare], 2);
const releaseSignature = releaseWallet.sign("Release funds to seller");
// Dispute resolution: buyer + arbiter or seller + arbiter
const disputeWallet = Non_Custodial_Wallet.fromShares("main", [buyerShare, arbiterShare], 2);
const disputeSignature = disputeWallet.sign("Refund to buyer after dispute");
// Corporate treasury with 3-of-5 executive approval
const corporateWallet = Non_Custodial_Wallet.fromRandom("main", 5, 3);
const executiveShares = corporateWallet._shares;
// Distribute shares to 5 executives
const executives = [
{ name: "CEO", share: executiveShares[0] },
{ name: "CFO", share: executiveShares[1] },
{ name: "COO", share: executiveShares[2] },
{ name: "CTO", share: executiveShares[3] },
{ name: "Board Rep", share: executiveShares[4] }
];
// Any 3 executives can authorize payments
const paymentAuth = Non_Custodial_Wallet.fromShares("main",
[executives[0].share, executives[1].share, executives[2].share], 3);
const authSignature = paymentAuth.sign("Q4 dividend payment: $1M");
// Cryptocurrency exchange cold storage
const exchangeWallet = Non_Custodial_Wallet.fromRandom("main", 7, 4);
const operatorShares = exchangeWallet._shares;
// Distribute shares across geographic locations and roles
const distribution = [
{ location: "US-East", role: "Security Officer", share: operatorShares[0] },
{ location: "US-West", role: "Operations Lead", share: operatorShares[1] },
{ location: "EU", role: "Compliance Officer", share: operatorShares[2] },
{ location: "Asia", role: "Technical Lead", share: operatorShares[3] },
{ location: "Backup-1", role: "Emergency Access", share: operatorShares[4] },
{ location: "Backup-2", role: "Emergency Access", share: operatorShares[5] },
{ location: "Audit", role: "External Auditor", share: operatorShares[6] }
];
// Requires 4 of 7 operators to authorize large withdrawals
// Provides redundancy and prevents single operator compromise
Extends
Members
_privateKey
Gets the reconstructed private key in WIF (Wallet Import Format).
This getter reconstructs the complete private key from the distributed shares and returns it in standard WIF format. This operation defeats the purpose of the threshold scheme by centralizing the private key, so it should be used with extreme caution and only when absolutely necessary.
Security Warning:
- Reconstructing the private key eliminates the security benefits of threshold signatures
- The complete private key provides full control over the wallet
- Should only be used for emergency recovery or migration scenarios
- Consider using threshold signatures instead of key reconstruction when possible
- Ensure secure deletion of the reconstructed key after use
Use Cases:
- Emergency wallet recovery when threshold scheme is no longer viable
- Migration to different wallet software that doesn't support threshold signatures
- Compliance requirements that mandate private key export
- Integration with legacy systems that require WIF private keys
- Source:
Examples
// Emergency private key extraction (use with caution!)
const wallet = Non_Custodial_Wallet.fromRandom("main", 3, 2);
// Only use in emergency situations
console.warn('Reconstructing private key - this defeats threshold security!');
const privateKey = wallet._privateKey;
console.log('WIF Private Key:', privateKey);
// "L5HgWvFghocq1FmxSjKNaGhVN8f67p6xYg5pY7M8FE77HXwHtGGu"
// Secure private key extraction with cleanup
function emergencyKeyExtraction(thresholdWallet) {
console.warn('SECURITY WARNING: Extracting private key from threshold wallet');
try {
// Extract private key
const privateKey = thresholdWallet._privateKey;
// Use private key for emergency operation
const emergencyOperation = performEmergencyTransfer(privateKey);
// Clear private key from memory (best effort)
privateKey.fill('\0'); // Overwrite string content
return emergencyOperation;
} catch (error) {
console.error('Private key extraction failed:', error);
throw error;
}
}
// Migration to single-key wallet
const thresholdWallet = Non_Custodial_Wallet.fromShares("main", shares, 2);
// Extract private key for migration
const migratedPrivateKey = thresholdWallet._privateKey;
// Create equivalent single-key wallet
const singleKeyWallet = Custodial_Wallet.fromSeed("main",
privateKeyToSeed(migratedPrivateKey));
// Verify address consistency
console.log('Address match:',
thresholdWallet.address === singleKeyWallet.address); // true
_shares
Gets the secret shares as hex-encoded strings for secure distribution to participants.
This getter provides access to the distributed secret shares in a format suitable for secure transmission and storage. Each share is a hex-encoded string representing a point on the secret-sharing polynomial. These shares should be distributed to different participants and stored securely.
Share Properties:
- Each share is cryptographically independent
- Shares are information-theoretically secure (< threshold reveals nothing)
- Hex encoding ensures safe transmission over text-based channels
- Each share is typically 64 hex characters (32 bytes)
- Shares should be transmitted over secure, authenticated channels
Distribution Best Practices:
- Use secure communication channels (encrypted email, secure messaging)
- Verify recipient identity before share distribution
- Consider using QR codes for offline share transfer
- Implement share backup and recovery procedures
- Document which participant holds which share index
- Source:
Examples
// Basic share distribution
const wallet = Non_Custodial_Wallet.fromRandom("main", 3, 2);
const shares = wallet._shares;
console.log('Number of shares:', shares.length); // 3
console.log('Share format:', shares[0]); // "79479395a59a8e9d..."
// Secure share distribution to participants
const corporateWallet = Non_Custodial_Wallet.fromRandom("main", 5, 3);
const executiveShares = corporateWallet._shares;
const executives = [
{ name: "Alice Johnson", email: "alice@company.com", share: executiveShares[0] },
{ name: "Bob Smith", email: "bob@company.com", share: executiveShares[1] },
{ name: "Carol Davis", email: "carol@company.com", share: executiveShares[2] },
{ name: "Dave Wilson", email: "dave@company.com", share: executiveShares[3] },
{ name: "Eve Brown", email: "eve@company.com", share: executiveShares[4] }
];
// Distribute shares securely
executives.forEach(exec => {
sendSecureEmail(exec.email, `Your wallet share: ${exec.share}`);
console.log(`Share distributed to ${exec.name}`);
});
// QR code generation for offline distribution
const wallet = Non_Custodial_Wallet.fromRandom("main", 3, 2);
const shares = wallet._shares;
shares.forEach((share, index) => {
const qrCode = generateQRCode(share);
saveQRCode(qrCode, `share_${index + 1}.png`);
console.log(`QR code generated for share ${index + 1}`);
});
// Backup and recovery documentation
const wallet = Non_Custodial_Wallet.fromRandom("main", 5, 3);
const shares = wallet._shares;
const backupDocument = {
walletAddress: wallet.address,
threshold: wallet.threshold,
totalShares: wallet.group_size,
creationDate: new Date().toISOString(),
shares: shares.map((share, index) => ({
index: index + 1,
share: share,
holder: `Participant ${index + 1}`,
status: 'Active'
}))
};
// Store backup document securely
storeSecureBackup(JSON.stringify(backupDocument, null, 2));
(readonly) group_size :number
Total number of participants in the threshold scheme
Type:
- number
- Inherited From:
- Source:
(readonly) polynomial_order :number
Polynomial degree (threshold - 1) for secret sharing
Type:
- number
- Inherited From:
- Source:
(readonly) threshold :number
Minimum number of participants needed for cryptographic operations
Type:
- number
- Inherited From:
- Source:
(static, readonly) this.net :string
Network type for this threshold wallet instance. Determines address formats, version bytes, and network-specific parameters.
Type:
- string
- Source:
Example
console.log(wallet.net); // "main" or "test"
Methods
addss(a_sharesopt, b_sharesopt) → {BN}
Additive Secret Sharing (ADDSS) - combines two sets of shares additively
ADDSS enables secure addition of two shared secrets without revealing the individual secrets. Each participant adds their corresponding shares, and the result can be reconstructed to obtain the sum of the original secrets.
Mathematical Foundation:
- If secret A is shared as (a₁, a₂, ..., aₙ)
- And secret B is shared as (b₁, b₂, ..., bₙ)
- Then A + B is shared as (a₁+b₁, a₂+b₂, ..., aₙ+bₙ)
Applications:
- Combining multiple randomness sources
- Adding constants to shared secrets
- Building complex cryptographic protocols
- Secure multi-party computation primitives
Parameters:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
a_shares |
Array.<BN> |
<optional> |
[] | First set of secret shares |
b_shares |
Array.<BN> |
<optional> |
[] | Second set of secret shares |
- Inherited From:
- Source:
Throws:
-
If share arrays have different lengths
- Type
- Error
Returns:
The sum of the two original secrets
- Type
- BN
Examples
const tss = new ThresholdSignature(3, 2);
// Generate two sets of shares
const [shares1, _] = tss.jvrss();
const [shares2, __] = tss.jvrss();
// Add the shared secrets
const sum = tss.addss(shares1, shares2);
// Verify: sum should equal individual secret sum
const secret1 = tss.privite_key(shares1);
const secret2 = tss.privite_key(shares2);
const expectedSum = secret1.add(secret2).umod(N);
console.log(sum.eq(expectedSum)); // true
// Adding a constant to a shared secret
const constant = new BN(42);
const constantShares = Array(tss.group_size).fill(new BN(0));
constantShares[0] = constant; // Only first share gets the constant
const result = tss.addss(tss.shares, constantShares);
// result = original_secret + 42
invss(a_sharesopt) → {Array.<BN>}
Inverse Secret Sharing (INVSS) - computes modular inverse of shared secret
INVSS computes the modular inverse of a shared secret without revealing the secret. This is crucial for threshold ECDSA signatures where we need to compute k⁻¹ (inverse of the nonce) as part of the signature generation process.
Algorithm:
- Generate a fresh random secret b using JVRSS
- Compute c = a × b using PROSS (where a is the input secret)
- Reconstruct c (this reveals c but not a or b individually)
- Compute c⁻¹ using standard modular inverse
- Multiply b shares by c⁻¹ to get shares of a⁻¹
Security:
- The intermediate value c is revealed but provides no information about a
- The randomness b masks the original secret a
- Final result is properly shared according to the threshold scheme
Parameters:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
a_shares |
Array.<BN> |
<optional> |
[] | Shares of the secret to invert |
- Inherited From:
- Source:
Returns:
Shares of the modular inverse of the original secret
- Type
- Array.<BN>
Examples
const tss = new ThresholdSignature(5, 3);
// Compute inverse of shared secret
const inverseShares = tss.invss(tss.shares);
// Verify: secret × inverse = 1 (mod N)
const product = tss.pross(tss.shares, inverseShares);
console.log(product.eq(new BN(1))); // true
// Use in threshold signature (simplified)
const message = Buffer.from("Hello World!");
const msgHash = new BN(createHash('sha256').update(message).digest());
// Generate nonce shares
const [kShares, kPubKey] = tss.jvrss();
// Compute inverse of nonce
const kInvShares = tss.invss(kShares);
// This would be used in signature computation
// s = k⁻¹(hash + r × private_key)
jvrss() → {Array|Array.<BN>|Object}
Joint Verifiable Random Secret Sharing (JVRSS) protocol implementation
JVRSS is the core protocol for distributed key generation without a trusted dealer. It combines multiple random polynomials from all participants to create a shared secret that no single party knows or can control.
Protocol Steps:
- Polynomial Generation: Each participant conceptually generates a random polynomial
- Share Distribution: Each polynomial contributes to every participant's final share
- Linear Combination: Shares are combined additively to create the final distribution
- Public Key Derivation: The aggregate public key is computed from polynomial constants
Security Properties:
- No single participant controls the final secret
- The secret is uniformly random over the field
- Shares are properly distributed according to Shamir's scheme
- Public key is verifiable and corresponds to the shared secret
- Inherited From:
- Source:
Returns:
-
Tuple containing shares array and aggregate public key
- Type
- Array
-
returns.0 - Array of secret shares for each participant
- Type
- Array.<BN>
-
returns.1 - Aggregate elliptic curve public key point
- Type
- Object
Examples
const tss = new ThresholdSignature(5, 3);
const [shares, publicKey] = tss.jvrss();
console.log(shares.length); // 5 shares
console.log(publicKey.constructor.name); // ProjectivePoint
// Shares are properly distributed
const points = tss.shares_to_points(shares);
const secret = Polynomial.interpolate_evaluate(points, 0);
const derivedPubKey = secp256k1.ProjectivePoint.fromPrivateKey(secret.toBuffer());
// Public keys should match
console.log(publicKey.equals(derivedPubKey)); // true
// Manual JVRSS execution for understanding
const groupSize = 3, threshold = 2;
const polynomials = Array(groupSize).fill(null)
.map(() => Polynomial.fromRandom(threshold - 1));
// Each participant's share is sum of evaluations
let manualShares = Array(groupSize).fill(new BN(0));
for (let i = 0; i < groupSize; i++) {
for (let j = 0; j < groupSize; j++) {
manualShares[j] = manualShares[j].add(polynomials[i].evaluate(j + 1));
}
}
// This produces the same result as jvrss()
privite_key(a_sharesopt) → {BN}
Reconstructs the private key from secret shares using polynomial interpolation
This method recovers the original private key from the distributed shares. It should be used with caution as it reconstructs the full private key, defeating the purpose of the threshold scheme. Typically used only for specific operations like computing WIF format or for emergency recovery.
Security Warning:
- Reconstructing the private key centralizes control
- Should only be done when absolutely necessary
- Consider using threshold operations instead when possible
- Ensure secure deletion of reconstructed key after use
Parameters:
| Name | Type | Attributes | Description |
|---|---|---|---|
a_shares |
Array.<BN> |
<optional> |
Secret shares to reconstruct from (defaults to this.shares) |
- Inherited From:
- Source:
Returns:
The reconstructed private key as a BigNumber
- Type
- BN
Examples
const tss = new ThresholdSignature(3, 2);
// Reconstruct private key (use with caution!)
const privateKey = tss.privite_key();
// Verify it corresponds to the public key
const derivedPubKey = secp256k1.ProjectivePoint.fromPrivateKey(privateKey.toBuffer());
console.log(derivedPubKey.equals(tss.public_key)); // true
// Partial reconstruction with threshold shares only
const thresholdShares = tss.shares.slice(0, tss.threshold);
const partialKey = tss.privite_key(thresholdShares);
// Should equal full reconstruction
const fullKey = tss.privite_key();
console.log(partialKey.eq(fullKey)); // true
// Emergency recovery scenario
function emergencyRecovery(shareHolders) {
if (shareHolders.length < tss.threshold) {
throw new Error("Insufficient shares for recovery");
}
const recoveredKey = tss.privite_key(shareHolders.slice(0, tss.threshold));
// Use recovered key for emergency operations
// ... perform emergency actions ...
// Securely delete the key
recoveredKey.fill(0);
}
pross(a_sharesopt, b_sharesopt) → {BN}
Polynomial Reconstruction Secret Sharing (PROSS) - computes product of shared secrets
PROSS enables secure multiplication of two shared secrets. This is more complex than addition because the product of two degree-t polynomials yields a degree-2t polynomial, requiring more shares for reconstruction.
Mathematical Foundation:
- Product of degree-t polynomials has degree 2t
- Requires 2t+1 shares for reconstruction (vs t+1 for addition)
- Uses polynomial interpolation on the product values
- Result is the product of the original secrets
Security Note:
- Requires more participants for security than addition
- Product shares reveal more information than additive shares
- Should be used carefully in cryptographic protocols
Applications:
- Computing multiplicative inverses (used in INVSS)
- Secure polynomial evaluation
- Advanced threshold cryptographic protocols
- Zero-knowledge proof systems
Parameters:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
a_shares |
Array.<BN> |
<optional> |
[] | First set of secret shares |
b_shares |
Array.<BN> |
<optional> |
[] | Second set of secret shares |
- Inherited From:
- Source:
Returns:
The product of the two original secrets
- Type
- BN
Examples
const tss = new ThresholdSignature(5, 2); // Need larger group for PROSS
// Generate two secrets
const [shares1, _] = tss.jvrss();
const [shares2, __] = tss.jvrss();
// Compute product
const product = tss.pross(shares1, shares2);
// Verify result
const secret1 = tss.privite_key(shares1);
const secret2 = tss.privite_key(shares2);
const expectedProduct = secret1.mul(secret2).umod(N);
console.log(product.eq(expectedProduct)); // true
// Squaring a shared secret
const squared = tss.pross(tss.shares, tss.shares);
const originalSecret = tss.privite_key();
const expectedSquare = originalSecret.mul(originalSecret).umod(N);
console.log(squared.eq(expectedSquare)); // true
shares_to_points(sharesopt) → {SharePoints}
Converts share values to coordinate points for polynomial interpolation
Transforms an array of share values into the coordinate format required for Lagrange interpolation. Each share at index i becomes a point (i+1, share) since polynomial evaluation uses 1-based indexing (x=0 is reserved for secrets).
Parameters:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
shares |
Array.<BN> |
<optional> |
[] | Array of BigNumber share values to convert |
- Inherited From:
- Source:
Returns:
Array of [x, y] coordinate pairs for interpolation
- Type
- SharePoints
Examples
const tss = new ThresholdSignature(3, 2);
const points = tss.shares_to_points(tss.shares);
console.log(points);
// [[1, share1_value], [2, share2_value], [3, share3_value]]
// Use for secret reconstruction
const secret = Polynomial.interpolate_evaluate(points, 0);
// Partial reconstruction with threshold shares
const partialShares = tss.shares.slice(0, tss.threshold);
const thresholdPoints = tss.shares_to_points(partialShares);
const reconstructed = Polynomial.interpolate_evaluate(thresholdPoints, 0);
sign(message) → {ThresholdSignatureResult}
Generates a threshold signature for a given message
This is the core method that produces threshold ECDSA signatures. The signature is generated collaboratively using the threshold scheme without reconstructing the private key. The process follows the threshold ECDSA protocol:
Threshold ECDSA Algorithm:
- Nonce Generation: Create shared random nonce k using JVRSS
- R Value Computation: Compute R = k×G and extract r = R.x mod n
- Inverse Computation: Compute k⁻¹ using INVSS without revealing k
- Signature Shares: Each party computes their share of s = k⁻¹(hash + r×private)
- Reconstruction: Combine shares to get final signature (r, s)
- Validation: Ensure signature is valid and non-zero
Security Properties:
- Private key never reconstructed during signing
- Nonce is generated distributively and remains secret
- Resulting signature is indistinguishable from single-party ECDSA
- Compatible with standard ECDSA verification
Parameters:
| Name | Type | Description |
|---|---|---|
message |
string | Message to sign (will be SHA256 hashed) |
- Inherited From:
- Source:
Returns:
Complete signature with metadata
Examples
const tss = new ThresholdSignature(3, 2);
// Generate threshold signature
const signature = tss.sign("Transfer $1000 to Alice");
console.log(signature.sig.r); // BigInt r value
console.log(signature.sig.s); // BigInt s value
console.log(signature.serialized_sig); // Base64 compact format
console.log(signature.recovery_id); // 0-3 for public key recovery
// Verify signature
const isValid = ThresholdSignature.verify_threshold_signature(
tss.public_key,
signature.msgHash,
signature.sig
);
console.log(isValid); // true
// Corporate authorization workflow
const corporateTSS = new ThresholdSignature(5, 3);
const authMessage = JSON.stringify({
action: "wire_transfer",
amount: 1000000,
recipient: "operations_account",
timestamp: Date.now()
});
const authorization = corporateTSS.sign(authMessage);
console.log("Authorization signature:", authorization.serialized_sig);
// Escrow release with buyer + seller
const escrowTSS = new ThresholdSignature(3, 2);
const releaseMessage = "Release escrow funds to seller";
const escrowSignature = escrowTSS.sign(releaseMessage);
// This signature can be verified by anyone
const verified = ThresholdSignature.verify_threshold_signature(
escrowTSS.public_key,
escrowSignature.msgHash,
escrowSignature.sig
);
verify(sig, msgHash) → {boolean}
Verifies a threshold signature against the original message hash.
This method performs cryptographic verification of threshold signatures using standard ECDSA verification. Threshold signatures are mathematically equivalent to single-party ECDSA signatures, so they can be verified using standard verification algorithms without knowledge of the threshold scheme.
Verification Process:
- Parse signature into r and s components
- Validate signature components are within valid ranges
- Compute verification equation using aggregate public key
- Check that computed point matches signature r value
- Return boolean result of verification
Compatibility:
- Compatible with standard ECDSA verification
- Can be verified by any Bitcoin-compatible software
- Third parties don't need knowledge of threshold scheme
- Signatures are indistinguishable from single-party signatures
Parameters:
| Name | Type | Description |
|---|---|---|
sig |
Object | Signature object with r and s properties (BigInt values) |
msgHash |
Buffer | SHA256 hash of the original message (32 bytes) |
- Source:
Throws:
-
-
If signature format is invalid
- Type
- Error
-
-
-
If message hash is not 32 bytes
- Type
- Error
-
Returns:
True if signature is valid for this wallet's public key, false otherwise
- Type
- boolean
Examples
// Basic threshold signature verification
const wallet = Non_Custodial_Wallet.fromRandom("main", 3, 2);
const message = "Multi-party authorization required";
// Generate threshold signature
const signature = wallet.sign(message);
// Verify signature
const isValid = wallet.verify(signature.sig, signature.msgHash);
console.log('Threshold signature valid:', isValid); // true
// Cross-verification with different wallet instances
const originalWallet = Non_Custodial_Wallet.fromRandom("main", 5, 3);
const shares = originalWallet._shares;
// Create signing wallet with threshold shares
const signingWallet = Non_Custodial_Wallet.fromShares("main", shares.slice(0, 3), 3);
// Create verification wallet with different shares
const verifyingWallet = Non_Custodial_Wallet.fromShares("main", shares.slice(2, 5), 3);
const message = "Cross-wallet verification test";
const signature = signingWallet.sign(message);
// Both wallets should verify the same signature
const valid1 = signingWallet.verify(signature.sig, signature.msgHash);
const valid2 = verifyingWallet.verify(signature.sig, signature.msgHash);
console.log('Both verify same:', valid1 === valid2 && valid1 === true); // true
// Third-party verification without threshold knowledge
function verifyPaymentAuthorization(publicKeyHex, messageHash, signature) {
// This function doesn't know about threshold signatures
// It just uses standard ECDSA verification
const publicKey = secp256k1.ProjectivePoint.fromHex(publicKeyHex);
return ThresholdSignature.verify_threshold_signature(publicKey, messageHash, signature);
}
const wallet = Non_Custodial_Wallet.fromRandom("main", 3, 2);
const authorization = wallet.sign("Payment approved: $10,000");
// Third party can verify without knowing about threshold scheme
const thirdPartyVerification = verifyPaymentAuthorization(
wallet.publicKey,
authorization.msgHash,
authorization.sig
);
console.log('Third party verification:', thirdPartyVerification); // true
// Batch verification for audit trail
const wallet = Non_Custodial_Wallet.fromRandom("main", 5, 3);
const transactions = [
"Transfer $1000 to Account A",
"Transfer $2000 to Account B",
"Transfer $3000 to Account C"
];
// Generate signatures for all transactions
const signedTransactions = transactions.map(tx => {
const signature = wallet.sign(tx);
return {
transaction: tx,
signature: signature.sig,
messageHash: signature.msgHash
};
});
// Verify all signatures
const allValid = signedTransactions.every(item =>
wallet.verify(item.signature, item.messageHash)
);
console.log('All transactions valid:', allValid); // true
(static) fromRandom(netopt, group_sizeopt, thresholdopt) → {Non_Custodial_Wallet}
Generates a new random threshold wallet with specified parameters.
This static factory method creates a fresh threshold signature scheme using cryptographically secure randomness. It initializes the distributed key generation protocol and produces a complete threshold wallet ready for multi-party operations.
Generation Process:
- Create new threshold signature instance with specified parameters
- Execute JVRSS for distributed key generation
- Generate secret shares for all participants
- Compute aggregate public key and Bitcoin address
- Return initialized wallet instance
Security Properties:
- Uses cryptographically secure random number generation
- No participant has knowledge of the complete private key
- Secret shares are information-theoretically secure
- Aggregate public key is verifiable and deterministic
Parameters:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
net |
string |
<optional> |
"main" | Network type ('main' for mainnet, 'test' for testnet) |
group_size |
number |
<optional> |
3 | Total number of participants in the scheme |
threshold |
number |
<optional> |
2 | Minimum participants needed for signature generation |
- Source:
Throws:
-
-
"Threshold is too high or low" if constraints are violated
- Type
- Error
-
-
-
If network parameter is invalid
- Type
- Error
-
Returns:
New threshold wallet instance
- Type
- Non_Custodial_Wallet
Examples
// Standard 2-of-3 multi-signature wallet
const multiSigWallet = Non_Custodial_Wallet.fromRandom("main", 3, 2);
console.log('Multi-sig address:', multiSigWallet.address);
// Get shares for distribution
const [share1, share2, share3] = multiSigWallet._shares;
console.log('Share 1:', share1); // Hex-encoded secret share
// Corporate treasury wallet (3-of-5)
const treasuryWallet = Non_Custodial_Wallet.fromRandom("main", 5, 3);
const executiveShares = treasuryWallet._shares;
// Distribute shares to executives
const shareDistribution = [
{ executive: "CEO", share: executiveShares[0] },
{ executive: "CFO", share: executiveShares[1] },
{ executive: "COO", share: executiveShares[2] },
{ executive: "CTO", share: executiveShares[3] },
{ executive: "Board Rep", share: executiveShares[4] }
];
// High-security vault (5-of-9)
const vaultWallet = Non_Custodial_Wallet.fromRandom("main", 9, 5);
console.log(`Vault requires ${vaultWallet.threshold} of ${vaultWallet.group_size} participants`);
// Example distribution across different security zones
const vaultShares = vaultWallet._shares;
const securityZones = [
{ zone: "Primary Datacenter", shares: vaultShares.slice(0, 3) },
{ zone: "Secondary Datacenter", shares: vaultShares.slice(3, 6) },
{ zone: "Offline Storage", shares: vaultShares.slice(6, 9) }
];
(static) fromShares(netopt, shares, thresholdopt) → {Non_Custodial_Wallet}
Reconstructs a threshold wallet from existing secret shares.
This static factory method rebuilds a threshold wallet from previously distributed secret shares. It's used when participants want to reconstruct the wallet for signature generation or when migrating shares between systems. The method validates share consistency and reconstructs the public key and address.
Reconstruction Process:
- Create new threshold instance with matching parameters
- Convert hex-encoded shares to BigNumber format
- Reconstruct the aggregate public key from shares
- Derive Bitcoin address from reconstructed public key
- Validate share consistency and threshold requirements
Security Considerations:
- Only provided shares are used; missing shares remain unknown
- Threshold requirement still applies for signature generation
- Share authenticity should be verified through secure channels
- Reconstructed wallet has same capabilities as original
Parameters:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
net |
string |
<optional> |
"main" | Network type ('main' for mainnet, 'test' for testnet) |
shares |
Array.<string> | Array of hex-encoded secret shares |
||
threshold |
number |
<optional> |
2 | Minimum participants required for operations |
- Source:
Throws:
-
-
If threshold is greater than number of provided shares
- Type
- Error
-
-
-
If reconstructed public key is invalid
- Type
- Error
-
Returns:
Reconstructed threshold wallet instance
- Type
- Non_Custodial_Wallet
Examples
// Reconstruct 2-of-3 wallet from shares
const originalShares = [
"79479395a59a8e9d930f2b10ccd5ac3671b0ff0bf8a66aaa1d74978c5353694b",
"98510126c920e18b148130ac1145686cb299d21f0e010b98ede44169a7bb1c13",
"b7428d37e5847f9a8b3d4c2f9a1e5c8d7b4f2a8e9c1d5b7a3f8e2c9d4b6a1f5"
];
const reconstructedWallet = Non_Custodial_Wallet.fromShares("main", originalShares, 2);
console.log('Reconstructed address:', reconstructedWallet.address);
// Partial reconstruction for signing (only threshold shares needed)
const originalWallet = Non_Custodial_Wallet.fromRandom("main", 5, 3);
const allShares = originalWallet._shares;
// Use only 3 shares (minimum threshold)
const signingShares = [allShares[0], allShares[2], allShares[4]];
const signingWallet = Non_Custodial_Wallet.fromShares("main", signingShares, 3);
// Can generate signatures with just threshold shares
const signature = signingWallet.sign("Authorized payment");
// Corporate recovery scenario
function recoverCorporateWallet(executiveShares) {
if (executiveShares.length < 3) {
throw new Error("Insufficient executives present for recovery");
}
// Reconstruct wallet from available executive shares
const recoveredWallet = Non_Custodial_Wallet.fromShares(
"main",
executiveShares.slice(0, 3), // Use first 3 available shares
3
);
return recoveredWallet;
}
// Cross-platform wallet migration
// Export shares from mobile app
const mobileShares = mobileWallet._shares;
// Import to desktop application
const desktopWallet = Non_Custodial_Wallet.fromShares("main", mobileShares, 2);
// Desktop wallet has identical functionality
console.log('Same address:', mobileWallet.address === desktopWallet.address); // true