Skip to content

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

BIP-352 Silent Payments

A simple JavaScript library for deriving BIP-352 Silent Payment addresses, scan keys, and spend keys. It also implements the spscan/spspend key expressions from BIP-392. It is designed to work well as a library module alongside other bitcoinjs suite of libraries.

Silent Payments is a privacy-enhancing protocol that allows users to receive Bitcoin payments to a static, reusable address without revealing it on-chain. Each payment generates a unique output address, improving receiver privacy.

Installation

npm install bip352-js

Usage

Basic Example

const bip39 = require('bip39');
const bip32 = require('bip32');
const ecc = require('tiny-secp256k1');
const bip352 = require('bip352-js');

const BIP32 = bip32.BIP32Factory(ecc);

// Generate or import a mnemonic
const mnemonic = bip39.generateMnemonic();
const seed = bip39.mnemonicToSeedSync(mnemonic);
const root = BIP32.fromSeed(seed);

// Derive Silent Payment address for account 0
const { address, scanKey, spendKey } = bip352.deriveAddress(
  root,
  0,
  bip352.COIN_TYPE_BTC_MAINNET
);

console.log('Silent Payment Address:', address);
// => sp1qq...

Derive Keys Only

const { scanKey, spendKey } = bip352.deriveKeys(
  root,
  0,
  bip352.COIN_TYPE_BTC_MAINNET
);

console.log('Scan key path:', bip352.getPath('scan', 0, bip352.COIN_TYPE_BTC_MAINNET));
// => m/352'/0'/0'/1'/0

console.log('Spend key path:', bip352.getPath('spend', 0, bip352.COIN_TYPE_BTC_MAINNET));
// => m/352'/0'/0'/0'/0

Encode Address from Public Keys

const address = bip352.toAddress(
  scanKey.publicKey,
  spendKey.publicKey,
  bip352.COIN_TYPE_BTC_MAINNET
);

console.log('Address:', address);

Decode Address

const decoded = bip352.fromAddress(address);

console.log('Version:', decoded.version);
console.log('Scan pubkey:', Buffer.from(decoded.scanPub).toString('hex'));
console.log('Spend pubkey:', Buffer.from(decoded.spendPub).toString('hex'));
console.log('Testnet:', decoded.testnet);

Testnet Support

const { address } = bip352.deriveAddress(
  root,
  0,
  bip352.COIN_TYPE_BTC_TESTNET
);

console.log('Testnet Address:', address);
// => tsp1qq...

Key Expressions (BIP-392)

BIP-392 defines two key expressions for use in the sp() output script descriptor. An spscan expression carries the scan private key with the spend public key, which lets a watch-only wallet detect incoming payments without being able to spend them. An spspend expression carries both private keys, for a wallet that can do both.

const { scanKey, spendKey } = bip352.deriveKeys(root, 0, bip352.COIN_TYPE_BTC_MAINNET);

// Watch-only: can scan, cannot spend
const scanExpr = bip352.toScanKeyExpression(
  scanKey.privateKey,
  spendKey.publicKey,
  bip352.COIN_TYPE_BTC_MAINNET
);
// => spscan1q0rnl6lft0gkpg4nsn528qgdpytfdej40atdqgrxpqqsg8c5r8vys97...

// Full wallet: can scan and spend
const spendExpr = bip352.toSpendKeyExpression(
  scanKey.privateKey,
  spendKey.privateKey,
  bip352.COIN_TYPE_BTC_MAINNET
);
// => spspend1q0rnl6lft0gkpg4nsn528qgdpytfdej40atdqgrxpqqsg8c5r8vyu3p...

const { scanPriv, spendPub, testnet } = bip352.fromScanKeyExpression(scanExpr);

Note: Both expressions encode private key material. Treat them like a seed — an spscan expression leaks full transaction history to anyone holding it, and an spspend expression allows spending.

API Reference

deriveKeys(rootKey, account, coinType)

Derives scan and spend keys according to BIP-352.

Parameters:

  • rootKey (BIP32Interface): Master BIP32 key
  • account (number, optional): Account index (default: 0)
  • coinType (number, optional): BIP44 coin type - 0 for mainnet, 1 for testnet (default: 0)

Returns: { scanKey, spendKey } - Both are BIP32Interface objects

Derivation paths:

  • Scan: m/352'/coin_type'/account'/1'/0
  • Spend: m/352'/coin_type'/account'/0'/0

toAddress(scanPub, spendPub, coinType)

Encodes a Silent Payment address from public keys.

Parameters:

  • scanPub (Uint8Array): 33-byte compressed scan public key
  • spendPub (Uint8Array): 33-byte compressed spend public key
  • coinType (number, optional): 0 for mainnet, 1 for testnet (default: 0)

Returns: Silent Payment address string (bech32m encoded)

fromAddress(address)

Decodes a Silent Payment address.

Parameters:

  • address (string): Silent Payment address

Returns: { version, scanPub, spendPub, testnet }

  • version (number): Protocol version
  • scanPub (Uint8Array): 33-byte scan public key
  • spendPub (Uint8Array): 33-byte spend public key
  • testnet (boolean): True if testnet address

deriveAddress(rootKey, account, coinType)

Convenience function that combines key derivation and address encoding.

Parameters:

  • rootKey (BIP32Interface): Master BIP32 key
  • account (number, optional): Account index (default: 0)
  • coinType (number, optional): 0 for mainnet, 1 for testnet (default: 0)

Returns: { address, scanKey, spendKey }

getPath(type, account, coinType)

Returns the BIP-32 derivation path string.

Parameters:

  • type (string): 'scan' or 'spend'
  • account (number, optional): Account index (default: 0)
  • coinType (number, optional): 0 for mainnet, 1 for testnet (default: 0)

Returns: Derivation path string (e.g., m/352'/0'/0'/1'/0)

toScanKeyExpression(scanPriv, spendPub, coinType)

Encodes a BIP-392 spscan key expression, for watch-only wallets that can scan but not spend.

Parameters:

  • scanPriv (Uint8Array): 32-byte scan private key
  • spendPub (Uint8Array): 33-byte compressed spend public key
  • coinType (number, optional): 0 for mainnet, 1 for testnet (default: 0)

Returns: spscan key expression string (bech32m encoded)

fromScanKeyExpression(expression)

Decodes an spscan key expression.

Parameters:

  • expression (string): spscan key expression

Returns: { version, scanPriv, spendPub, testnet }

toSpendKeyExpression(scanPriv, spendPriv, coinType)

Encodes a BIP-392 spspend key expression, for full wallets that can both scan and spend.

Parameters:

  • scanPriv (Uint8Array): 32-byte scan private key
  • spendPriv (Uint8Array): 32-byte spend private key
  • coinType (number, optional): 0 for mainnet, 1 for testnet (default: 0)

Returns: spspend key expression string (bech32m encoded)

fromSpendKeyExpression(expression)

Decodes an spspend key expression.

Parameters:

  • expression (string): spspend key expression

Returns: { version, scanPriv, spendPriv, testnet }

Constants

  • PURPOSE_BIP352: 352
  • COIN_TYPE_BTC_MAINNET: 0
  • COIN_TYPE_BTC_TESTNET: 1
  • VERSION_SP: 0
  • HRP_SP_MAINNET: 'sp'
  • HRP_SP_TESTNET: 'tsp'
  • HRP_SPSCAN_MAINNET: 'spscan'
  • HRP_SPSCAN_TESTNET: 'tspscan'
  • HRP_SPSPEND_MAINNET: 'spspend'
  • HRP_SPSPEND_TESTNET: 'tspspend'

Testing

npm test

The test suite uses deterministic test vectors to verify:

  • Correct key derivation paths
  • Expected xpub/xprv values
  • Address encoding/decoding
  • Network type detection
  • Invalid address rejection
  • Key expression round-trips, HRP selection, and malformed input rejection

Address Format

Silent Payment addresses use bech32m encoding (not bech32):

  • HRP: sp (mainnet) or tsp (testnet)
  • Version: 0
  • Payload: 66 bytes (33-byte scan pubkey + 33-byte spend pubkey)
  • Example: sp1qqw3hksrgxfk0p7lf2h6hatk0n206a59l4wc354rxurvrjnzudfjx6q7dda0gtxsj4n0rp3u7782w8334tp5yt8y4uarpulqf05ca8gecj5p7ke3y

Key Expression Format

Key expressions also use bech32m, with the same version byte as addresses:

HRP (mainnet / testnet) Payload Contents
spscan spscan / tspscan 65 bytes 32-byte scan privkey + 33-byte spend pubkey
spspend spspend / tspspend 64 bytes 32-byte scan privkey + 32-byte spend privkey

Dependencies

  • bech32: Bech32m encoding/decoding — the only module src/ imports
  • bip32: HD key derivation
  • tiny-secp256k1: Elliptic curve cryptography
  • bip39: Mnemonic generation (for examples/testing)
  • mocha: Test framework (dev dependency)

This library takes an already-derived BIP32 node as input rather than importing bip32 itself, so you supply your own bip32/tiny-secp256k1 instances as shown in the usage examples.

License

GPL-3.0

Resources

About

Simple js bip352 library for bitcoin silent payment address derivation

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages