@samouraiwallet/bip47
Advanced tools
Comparing version 0.6.3 to 0.7.0
# Changelog | ||
## v0.7.0 | ||
### Features | ||
- allow usage of custom ecc library | ||
- added new `getNotificationPrivateKey` method + tests | ||
### Chores | ||
- updated devDependencies | ||
- updated README | ||
## v0.6.2 | ||
@@ -4,0 +13,0 @@ ### Bugfixes |
@@ -1,3 +0,3 @@ | ||
export { PaymentCode } from './payment-code.js'; | ||
export { BIP47Factory } from './payment-code.js'; | ||
export * as utils from './utils.js'; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -1,3 +0,3 @@ | ||
export { PaymentCode } from './payment-code.js'; | ||
export { BIP47Factory } from './payment-code.js'; | ||
export * as utils from './utils.js'; | ||
//# sourceMappingURL=index.js.map |
@@ -1,28 +0,62 @@ | ||
/// <reference types="node" /> | ||
import type { Network } from 'bitcoinjs-lib'; | ||
import { BIP32Interface } from 'bip32'; | ||
declare type AddressType = 'p2pkh' | 'p2sh' | 'p2wpkh'; | ||
export declare class PaymentCode { | ||
version: Buffer; | ||
buf: Buffer; | ||
network: Network; | ||
root: BIP32Interface; | ||
constructor(buf: Buffer, network?: Network); | ||
static fromSeed(bSeed: Buffer, id: number | string, network?: Network): PaymentCode; | ||
static fromBase58(inString: string, network?: Network): PaymentCode; | ||
static fromBuffer(buf: Buffer, network?: Network): PaymentCode; | ||
get features(): Buffer; | ||
get pubKey(): Buffer; | ||
get chainCode(): Buffer; | ||
get paymentCode(): Buffer; | ||
toBase58(): string; | ||
_hasPrivKeys(): boolean; | ||
derive(index: number): BIP32Interface; | ||
deriveHardened(index: number): BIP32Interface; | ||
getNotificationAddress(): string; | ||
derivePaymentPrivateKey(A: Buffer, idx: number): Buffer; | ||
derivePaymentPublicKey(a: Buffer, idx: number): Buffer; | ||
getPaymentAddress(a: Buffer, idx: number, type: AddressType): string; | ||
} | ||
export {}; | ||
import { Network, TinySecp256k1Interface, AddressType } from './types'; | ||
export declare const BIP47Factory: (ecc: TinySecp256k1Interface) => { | ||
fromSeed: (bSeed: Buffer, id: number | string, network?: Network) => { | ||
version: Buffer; | ||
buf: Buffer; | ||
network: Network; | ||
root: BIP32Interface; | ||
readonly features: Buffer; | ||
readonly pubKey: Buffer; | ||
readonly chainCode: Buffer; | ||
readonly paymentCode: Buffer; | ||
toBase58(): string; | ||
_hasPrivKeys(): boolean; | ||
derive(index: number): BIP32Interface; | ||
deriveHardened(index: number): BIP32Interface; | ||
getNotificationAddress(): string; | ||
getNotificationPrivateKey(): Buffer; | ||
derivePaymentPrivateKey(A: Buffer, idx: number): Buffer; | ||
derivePaymentPublicKey(a: Buffer, idx: number): Buffer; | ||
getPaymentAddress(a: Buffer, idx: number, type?: AddressType): string; | ||
}; | ||
fromBase58: (inString: string, network?: Network) => { | ||
version: Buffer; | ||
buf: Buffer; | ||
network: Network; | ||
root: BIP32Interface; | ||
readonly features: Buffer; | ||
readonly pubKey: Buffer; | ||
readonly chainCode: Buffer; | ||
readonly paymentCode: Buffer; | ||
toBase58(): string; | ||
_hasPrivKeys(): boolean; | ||
derive(index: number): BIP32Interface; | ||
deriveHardened(index: number): BIP32Interface; | ||
getNotificationAddress(): string; | ||
getNotificationPrivateKey(): Buffer; | ||
derivePaymentPrivateKey(A: Buffer, idx: number): Buffer; | ||
derivePaymentPublicKey(a: Buffer, idx: number): Buffer; | ||
getPaymentAddress(a: Buffer, idx: number, type?: AddressType): string; | ||
}; | ||
fromBuffer: (buf: Buffer, network?: Network) => { | ||
version: Buffer; | ||
buf: Buffer; | ||
network: Network; | ||
root: BIP32Interface; | ||
readonly features: Buffer; | ||
readonly pubKey: Buffer; | ||
readonly chainCode: Buffer; | ||
readonly paymentCode: Buffer; | ||
toBase58(): string; | ||
_hasPrivKeys(): boolean; | ||
derive(index: number): BIP32Interface; | ||
deriveHardened(index: number): BIP32Interface; | ||
getNotificationAddress(): string; | ||
getNotificationPrivateKey(): Buffer; | ||
derivePaymentPrivateKey(A: Buffer, idx: number): Buffer; | ||
derivePaymentPublicKey(a: Buffer, idx: number): Buffer; | ||
getPaymentAddress(a: Buffer, idx: number, type?: AddressType): string; | ||
}; | ||
}; | ||
//# sourceMappingURL=payment-code.d.ts.map |
@@ -1,2 +0,1 @@ | ||
import * as ecc from 'tiny-secp256k1'; | ||
import { BIP32Factory } from 'bip32'; | ||
@@ -7,17 +6,129 @@ import bs58check from 'bs58check'; | ||
const PC_VERSION = 0x47; | ||
const { fromPublicKey, fromSeed } = BIP32Factory(ecc); | ||
export class PaymentCode { | ||
constructor(buf, network = utils.networks.bitcoin) { | ||
if (buf.length !== 80) | ||
throw new TypeError('Invalid buffer length'); | ||
this.version = buf.slice(0, 1); | ||
if (this.version[0] !== 1) | ||
throw new TypeError('Only payment codes version 1 are supported'); | ||
this.buf = buf; | ||
this.network = network; | ||
this.root = fromPublicKey(this.pubKey, this.chainCode, this.network); | ||
export const BIP47Factory = (ecc) => { | ||
const bip32 = BIP32Factory(ecc); | ||
class PaymentCode { | ||
constructor(buf, network = utils.networks.bitcoin) { | ||
if (buf.length !== 80) | ||
throw new TypeError('Invalid buffer length'); | ||
this.version = buf.slice(0, 1); | ||
if (this.version[0] !== 1) | ||
throw new TypeError('Only payment codes version 1 are supported'); | ||
this.buf = buf; | ||
this.network = network; | ||
this.root = bip32.fromPublicKey(this.pubKey, this.chainCode, this.network); | ||
} | ||
get features() { | ||
return this.buf.slice(1, 1); | ||
} | ||
get pubKey() { | ||
return this.buf.slice(2, 2 + 33); | ||
} | ||
get chainCode() { | ||
return this.buf.slice(35, 35 + 32); | ||
} | ||
get paymentCode() { | ||
return this.buf; | ||
} | ||
toBase58() { | ||
const version = Buffer.from([PC_VERSION]); | ||
const buf = Buffer.concat([version, this.buf]); | ||
return encode(buf); | ||
} | ||
_hasPrivKeys() { | ||
return this.root.privateKey != null; | ||
} | ||
derive(index) { | ||
return this.root.derive(index); | ||
} | ||
deriveHardened(index) { | ||
return this.root.deriveHardened(index); | ||
} | ||
getNotificationAddress() { | ||
const child = this.derive(0); | ||
return utils.getP2pkhAddress(child.publicKey, this.network); | ||
} | ||
getNotificationPrivateKey() { | ||
if (!this._hasPrivKeys()) | ||
throw new Error('This payment code does not have private keys'); | ||
const child = this.derive(0); | ||
return child.privateKey; | ||
} | ||
derivePaymentPrivateKey(A, idx) { | ||
if (!ecc.isPoint(A)) | ||
throw new TypeError('Argument is not a valid public key'); | ||
const b_node = this.derive(idx); | ||
if (!b_node.privateKey) | ||
throw new Error('Unable to derive node with private key'); | ||
const b = b_node.privateKey; | ||
const S = ecc.pointMultiply(A, b); | ||
if (!S) | ||
throw new Error('Unable to compute resulting point'); | ||
const Sx = S.slice(1, 33); | ||
const s = utils.sha256(Buffer.from(Sx)); | ||
if (!ecc.isPrivate(s)) | ||
throw new TypeError('Invalid shared secret'); | ||
const paymentPrivateKey = ecc.privateAdd(b, s); | ||
if (!paymentPrivateKey) | ||
throw new TypeError('Unable to compute payment private key'); | ||
return Buffer.from(paymentPrivateKey); | ||
} | ||
derivePaymentPublicKey(a, idx) { | ||
if (!ecc.isPrivate(a) && !ecc.isPoint(a)) | ||
throw new TypeError('Argument is neither a valid private key or public key'); | ||
let B = null; | ||
let S = null; | ||
if (ecc.isPrivate(a)) { | ||
B = this.derive(idx).publicKey; | ||
S = ecc.pointMultiply(B, a); | ||
} | ||
else if (ecc.isPoint(a)) { | ||
if (!this._hasPrivKeys()) | ||
throw new Error('Unable to compute the derivation with a public key provided as argument'); | ||
const A = a; | ||
const b_node = this.derive(idx); | ||
if (!b_node.privateKey) | ||
throw new Error('Unable to derive node with private key'); | ||
const b = b_node.privateKey; | ||
B = b_node.publicKey; | ||
S = ecc.pointMultiply(A, b); | ||
} | ||
if (!B || !ecc.isPoint(B)) | ||
throw new TypeError('Invalid derived public key'); | ||
if (!S) | ||
throw new Error('Unable to compute resulting point'); | ||
const Sx = S.slice(1, 33); | ||
const s = utils.sha256(Buffer.from(Sx)); | ||
if (!ecc.isPrivate(s)) | ||
throw new TypeError('Invalid shared secret'); | ||
const EccPoint = ecc.pointFromScalar(s); | ||
if (!EccPoint) | ||
throw new Error('Unable to compute point'); | ||
const paymentPublicKey = ecc.pointAdd(B, EccPoint); | ||
if (!paymentPublicKey) | ||
throw new TypeError('Unable to compute payment public key'); | ||
return Buffer.from(paymentPublicKey); | ||
} | ||
getPaymentAddress(a, idx, type = 'p2pkh') { | ||
const pubkey = this.derivePaymentPublicKey(a, idx); | ||
if (!pubkey) | ||
throw new TypeError('Unable to derive public key'); | ||
switch (type) { | ||
case 'p2pkh': { | ||
return utils.getP2pkhAddress(pubkey, this.network); | ||
} | ||
case 'p2sh': { | ||
return utils.getP2shAddress(pubkey, this.network); | ||
} | ||
case 'p2wpkh': { | ||
return utils.getP2wpkhAddress(pubkey, this.network); | ||
} | ||
default: { | ||
throw new Error('Unknown address type. Expected: p2pkh | p2sh | p2wpkh'); | ||
} | ||
} | ||
} | ||
} | ||
static fromSeed(bSeed, id, network = utils.networks.bitcoin) { | ||
const fromSeed = (bSeed, id, network = utils.networks.bitcoin) => { | ||
const reserved = Buffer.alloc(13, 0); | ||
const root = fromSeed(bSeed); | ||
const root = bip32.fromSeed(bSeed); | ||
const coinType = (network.pubKeyHash === utils.networks.bitcoin.pubKeyHash) ? '0' : '1'; | ||
@@ -34,4 +145,4 @@ const root_bip47 = root.derivePath(`m/47'/${coinType}'/${id}'`); | ||
return pcode; | ||
} | ||
static fromBase58(inString, network) { | ||
}; | ||
const fromBase58 = (inString, network) => { | ||
const buf = decode(inString); | ||
@@ -42,111 +153,12 @@ const version = buf.slice(0, 1); | ||
return new PaymentCode(buf.slice(1), network); | ||
} | ||
static fromBuffer(buf, network) { | ||
}; | ||
const fromBuffer = (buf, network) => { | ||
return new PaymentCode(buf, network); | ||
} | ||
get features() { | ||
return this.buf.slice(1, 1); | ||
} | ||
get pubKey() { | ||
return this.buf.slice(2, 2 + 33); | ||
} | ||
get chainCode() { | ||
return this.buf.slice(35, 35 + 32); | ||
} | ||
get paymentCode() { | ||
return this.buf; | ||
} | ||
toBase58() { | ||
const version = Buffer.from([PC_VERSION]); | ||
const buf = Buffer.concat([version, this.buf]); | ||
return encode(buf); | ||
} | ||
_hasPrivKeys() { | ||
return this.root.privateKey != null; | ||
} | ||
derive(index) { | ||
return this.root.derive(index); | ||
} | ||
deriveHardened(index) { | ||
return this.root.deriveHardened(index); | ||
} | ||
getNotificationAddress() { | ||
const child = this.derive(0); | ||
return utils.getP2pkhAddress(child.publicKey, this.network); | ||
} | ||
derivePaymentPrivateKey(A, idx) { | ||
if (!ecc.isPoint(A)) | ||
throw new TypeError('Argument is not a valid public key'); | ||
const b_node = this.derive(idx); | ||
if (!b_node.privateKey) | ||
throw new Error('Unable to derive node with private key'); | ||
const b = b_node.privateKey; | ||
const S = ecc.pointMultiply(A, b); | ||
if (!S) | ||
throw new Error('Unable to compute resulting point'); | ||
const Sx = S.slice(1, 33); | ||
const s = utils.sha256(Buffer.from(Sx)); | ||
if (!ecc.isPrivate(s)) | ||
throw new TypeError('Invalid shared secret'); | ||
const paymentPrivateKey = ecc.privateAdd(b, s); | ||
if (!paymentPrivateKey) | ||
throw new TypeError('Unable to compute payment private key'); | ||
return Buffer.from(paymentPrivateKey); | ||
} | ||
derivePaymentPublicKey(a, idx) { | ||
if (!ecc.isPrivate(a) && !ecc.isPoint(a)) | ||
throw new TypeError('Argument is neither a valid private key or public key'); | ||
let B = null; | ||
let S = null; | ||
if (ecc.isPrivate(a)) { | ||
B = this.derive(idx).publicKey; | ||
S = ecc.pointMultiply(B, a); | ||
} | ||
else if (ecc.isPoint(a)) { | ||
if (!this._hasPrivKeys()) | ||
throw new Error('Unable to compute the derivation with a public key provided as argument'); | ||
const A = a; | ||
const b_node = this.derive(idx); | ||
if (!b_node.privateKey) | ||
throw new Error('Unable to derive node with private key'); | ||
const b = b_node.privateKey; | ||
B = b_node.publicKey; | ||
S = ecc.pointMultiply(A, b); | ||
} | ||
if (!B || !ecc.isPoint(B)) | ||
throw new TypeError('Invalid derived public key'); | ||
if (!S) | ||
throw new Error('Unable to compute resulting point'); | ||
const Sx = S.slice(1, 33); | ||
const s = utils.sha256(Buffer.from(Sx)); | ||
if (!ecc.isPrivate(s)) | ||
throw new TypeError('Invalid shared secret'); | ||
const EccPoint = ecc.pointFromScalar(s); | ||
if (!EccPoint) | ||
throw new Error('Unable to compute point'); | ||
const paymentPublicKey = ecc.pointAdd(B, EccPoint); | ||
if (!paymentPublicKey) | ||
throw new TypeError('Unable to compute payment public key'); | ||
return Buffer.from(paymentPublicKey); | ||
} | ||
getPaymentAddress(a, idx, type) { | ||
const pubkey = this.derivePaymentPublicKey(a, idx); | ||
if (!pubkey) | ||
throw new TypeError('Unable to derive public key'); | ||
switch (type) { | ||
case 'p2pkh': { | ||
return utils.getP2pkhAddress(pubkey, this.network); | ||
} | ||
case 'p2sh': { | ||
return utils.getP2shAddress(pubkey, this.network); | ||
} | ||
case 'p2wpkh': { | ||
return utils.getP2wpkhAddress(pubkey, this.network); | ||
} | ||
default: { | ||
throw new Error('Address type has not been defined: p2pkh | p2sh | p2wpkh'); | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
return { | ||
fromSeed, | ||
fromBase58, | ||
fromBuffer | ||
}; | ||
}; | ||
//# sourceMappingURL=payment-code.js.map |
/// <reference types="node" /> | ||
import type { Network } from 'bitcoinjs-lib'; | ||
import { Network } from './types'; | ||
export declare const networks: Record<'bitcoin' | 'regtest' | 'testnet', Network>; | ||
@@ -4,0 +4,0 @@ export declare function ripemd160(buffer: Buffer): Buffer; |
{ | ||
"name": "@samouraiwallet/bip47", | ||
"version": "0.6.3", | ||
"version": "0.7.0", | ||
"engines": { | ||
@@ -46,4 +46,3 @@ "node": ">=14.0.0" | ||
"bs58check": "2.1.2", | ||
"create-hash": "1.2.0", | ||
"tiny-secp256k1": "2.2.1" | ||
"create-hash": "1.2.0" | ||
}, | ||
@@ -56,10 +55,10 @@ "devDependencies": { | ||
"@types/tiny-secp256k1": "^2.0.1", | ||
"@typescript-eslint/eslint-plugin": "^5.38.1", | ||
"@typescript-eslint/parser": "^5.38.1", | ||
"bitcoinjs-lib": "^6.0.2", | ||
"eslint": "^8.24.0", | ||
"@typescript-eslint/eslint-plugin": "^5.40.0", | ||
"@typescript-eslint/parser": "^5.40.0", | ||
"eslint": "^8.25.0", | ||
"eslint-import-resolver-typescript": "^3.5.1", | ||
"eslint-plugin-import": "^2.26.0", | ||
"eslint-plugin-unicorn": "^44.0.0", | ||
"mocha": "^10.0.0", | ||
"eslint-plugin-unicorn": "^44.0.2", | ||
"mocha": "^10.1.0", | ||
"tiny-secp256k1": "^2.2.1", | ||
"ts-node": "^10.9.1", | ||
@@ -66,0 +65,0 @@ "typescript": "^4.8.4" |
@@ -11,5 +11,8 @@ # @samouraiwallet/bip47 | ||
You need to provide your own implementation of `secp256k1` elliptic curve. | ||
[tiny-secp256k1](https://github.com/bitcoinjs/tiny-secp256k1) is recommended but you can use any other which has the same API. | ||
All necessary documentation of usage is provided via test files. | ||
## Using in browser | ||
### Using in browser | ||
@@ -23,15 +26,22 @@ You will need webpack/browserify to bundle this library and it's dependencies to be able to use it in browser | ||
## Examples | ||
### Mainnet | ||
```js | ||
import {PaymentCode} from '@samouraiwallet/bip47'; | ||
import * as ecc from 'tiny-secp256k1'; | ||
import {BIP47Factory} from '@samouraiwallet/bip47'; | ||
const b58PCode = 'PM8TJS2JxQ5ztXUpBBRnpTbcUXbUHy2T1abfrb3KkAAtMEGNbey4oumH7Hc578WgQJhPjBxteQ5GHHToTYHE3A1w6p7tU6KSoFmWBVbFGjKPisZDbP97'; | ||
const bip47 = BIP47Factory(ecc); | ||
const pcode = PaymentCode.fromBase58(b58PCode); | ||
const notifAddr = pcode.getNotificationAddress(); | ||
const aliceB58PCode = 'PM8TJTLJbPRGxSbc8EJi42Wrr6QbNSaSSVJ5Y3E4pbCYiTHUskHg13935Ubb7q8tx9GVbh2UuRnBc3WSyJHhUrw8KhprKnn9eDznYGieTzFcwQRya4GA'; | ||
const bobSeed = '87eaaac5a539ab028df44d9110defbef3797ddb805ca309f61a69ff96dbaa7ab5b24038cf029edec5235d933110f0aea8aeecf939ed14fc20730bba71e4b1110'; | ||
const myPrivKey0 = Buffer.from('8d6a8ecd8ee5e0042ad0cb56e3a971c760b5145c3917a8e7beaf0ed92d7a520c', 'hex'); | ||
const paymentAddr1 = pcode.getPaymentAddress(myPrivKey0, 1); | ||
const paymentAddr2 = pcode.getPaymentAddress(myPrivKey0, 2); | ||
const alicePcode = bip47.fromBase58(aliceB58PCode); | ||
const bobPcode = bip47.fromSeed(Buffer.from(bobSeed, 'hex'), 0); | ||
const bobNotifPrivKey = bobPcode.getNotificationPrivateKey(); | ||
const alicePaymentAddr1 = alicePcode.getPaymentAddress(bobNotifPrivKey, 0, 'p2pkh'); // derive P2PKH payment address | ||
const alicePaymentAddr2 = alicePcode.getPaymentAddress(bobNotifPrivKey, 1, 'p2wpkh'); // derive P2WPKH payment address | ||
``` | ||
@@ -42,10 +52,13 @@ | ||
```js | ||
import {PaymentCode, utils} from '@samouraiwallet/bip47'; | ||
import * as ecc from 'tiny-secp256k1'; | ||
import {BIP47Factory, utils} from '@samouraiwallet/bip47'; | ||
const networks = utils.networks; | ||
const bip47 = BIP47Factory(ecc); | ||
const b58PCode = 'PM8TJS2JxQ5ztXUpBBRnpTbcUXbUHy2T1abfrb3KkAAtMEGNbey4oumH7Hc578WgQJhPjBxteQ5GHHToTYHE3A1w6p7tU6KSoFmWBVbFGjKPisZDbP97'; | ||
const pcode = PaymentCode.fromBase58(b58PCode, networks.testnet); | ||
const notifAddr = pcode.getNotificationAddress(); | ||
const alicePcode = bip47.fromBase58(b58PCode, networks.testnet); | ||
const aliceNotifAddr = alicePcode.getNotificationAddress(); | ||
``` |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
4
20
324
62
0
36373
+ Addedcipher-base@1.0.5(transitive)
- Removedtiny-secp256k1@2.2.1
- Removedcipher-base@1.0.6(transitive)
- Removedtiny-secp256k1@2.2.1(transitive)
- Removeduint8array-tools@0.0.7(transitive)