diff --git a/README.md b/README.md index 8a0885c..23dce36 100644 --- a/README.md +++ b/README.md @@ -140,10 +140,6 @@ Some examples interact (via HTTPS) with a 3rd Party Blockchain Provider (3PBP). - [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L88) - [Create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L144) - [Create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L190) -- [Generate a single-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L72) -- [Generate a single-key stealth address (randomly)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L91) -- [Generate a dual-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L124) -- [Generate a dual-key stealth address (randomly)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L147) If you have a use case that you feel could be listed here, please [ask for it](https://github.com/bitcoinjs/bitcoinjs-lib/issues/new)! diff --git a/test/integration/stealth.js b/test/integration/stealth.js deleted file mode 100644 index dd99d63..0000000 --- a/test/integration/stealth.js +++ /dev/null @@ -1,167 +0,0 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const bitcoin = require('../../') -const ecc = require('tiny-secp256k1') - -function getAddress (node, network) { - return bitcoin.payments.p2pkh({ pubkey: node.publicKey, network }).address -} - -// vG = (dG \+ sha256(e * dG)G) -function stealthSend (e, Q) { - const eQ = ecc.pointMultiply(Q, e, true) // shared secret - const c = bitcoin.crypto.sha256(eQ) - const Qc = ecc.pointAddScalar(Q, c) - const vG = bitcoin.ECPair.fromPublicKey(Qc) - - return vG -} - -// v = (d + sha256(eG * d)) -function stealthReceive (d, eG) { - const eQ = ecc.pointMultiply(eG, d) // shared secret - const c = bitcoin.crypto.sha256(eQ) - const dc = ecc.privateAdd(d, c) - const v = bitcoin.ECPair.fromPrivateKey(dc) - - return v -} - -// d = (v - sha256(e * dG)) -function stealthRecoverLeaked (v, e, Q) { - const eQ = ecc.pointMultiply(Q, e) // shared secret - const c = bitcoin.crypto.sha256(eQ) - const vc = ecc.privateSub(v, c) - const d = bitcoin.ECPair.fromPrivateKey(vc) - - return d -} - -// vG = (rG \+ sha256(e * dG)G) -function stealthDualSend (e, R, Q) { - const eQ = ecc.pointMultiply(Q, e) // shared secret - const c = bitcoin.crypto.sha256(eQ) - const Rc = ecc.pointAddScalar(R, c) - const vG = bitcoin.ECPair.fromPublicKey(Rc) - - return vG -} - -// vG = (rG \+ sha256(eG * d)G) -function stealthDualScan (d, R, eG) { - const eQ = ecc.pointMultiply(eG, d) // shared secret - const c = bitcoin.crypto.sha256(eQ) - const Rc = ecc.pointAddScalar(R, c) - const vG = bitcoin.ECPair.fromPublicKey(Rc) - - return vG -} - -// v = (r + sha256(eG * d)) -function stealthDualReceive (d, r, eG) { - const eQ = ecc.pointMultiply(eG, d) // shared secret - const c = bitcoin.crypto.sha256(eQ) - const rc = ecc.privateAdd(r, c) - const v = bitcoin.ECPair.fromPrivateKey(rc) - - return v -} - -describe('bitcoinjs-lib (crypto)', function () { - it('can generate a single-key stealth address', function () { - // XXX: should be randomly generated, see next test for example - const recipient = bitcoin.ECPair.fromWIF('5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss') // private to recipient - const nonce = bitcoin.ECPair.fromWIF('KxVqB96pxbw1pokzQrZkQbLfVBjjHFfp2mFfEp8wuEyGenLFJhM9') // private to sender - - // ... recipient reveals public key (recipient.Q) to sender - const forSender = stealthSend(nonce.privateKey, recipient.publicKey) - assert.equal(getAddress(forSender), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE') - assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) - - // ... sender reveals nonce public key (nonce.Q) to recipient - const forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey) - assert.equal(getAddress(forRecipient), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE') - assert.equal(forRecipient.toWIF(), 'L1yjUN3oYyCXV3LcsBrmxCNTa62bZKWCybxVJMvqjMmmfDE8yk7n') - - // sender and recipient, both derived same address - assert.equal(getAddress(forSender), getAddress(forRecipient)) - }) - - it('can generate a single-key stealth address (randomly)', function () { - const recipient = bitcoin.ECPair.makeRandom() // private to recipient - const nonce = bitcoin.ECPair.makeRandom() // private to sender - - // ... recipient reveals public key (recipient.Q) to sender - const forSender = stealthSend(nonce.privateKey, recipient.publicKey) - assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) - - // ... sender reveals nonce public key (nonce.Q) to recipient - const forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey) - assert.doesNotThrow(function () { forRecipient.toWIF() }) - - // sender and recipient, both derived same address - assert.equal(getAddress(forSender), getAddress(forRecipient)) - }) - - it('can recover parent recipient.d, if a derived private key is leaked [and nonce was revealed]', function () { - const recipient = bitcoin.ECPair.makeRandom() // private to recipient - const nonce = bitcoin.ECPair.makeRandom() // private to sender - - // ... recipient reveals public key (recipient.Q) to sender - const forSender = stealthSend(nonce.privateKey, recipient.publicKey) - assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) - - // ... sender reveals nonce public key (nonce.Q) to recipient - const forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey) - assert.doesNotThrow(function () { forRecipient.toWIF() }) - - // ... recipient accidentally leaks forRecipient.d on the blockchain - const leaked = stealthRecoverLeaked(forRecipient.privateKey, nonce.privateKey, recipient.publicKey) - assert.equal(leaked.toWIF(), recipient.toWIF()) - }) - - it('can generate a dual-key stealth address', function () { - // XXX: should be randomly generated, see next test for example - const recipient = bitcoin.ECPair.fromWIF('5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss') // private to recipient - const scan = bitcoin.ECPair.fromWIF('L5DkCk3xLLoGKncqKsWQTdaPSR4V8gzc14WVghysQGkdryRudjBM') // private to scanner/recipient - const nonce = bitcoin.ECPair.fromWIF('KxVqB96pxbw1pokzQrZkQbLfVBjjHFfp2mFfEp8wuEyGenLFJhM9') // private to sender - - // ... recipient reveals public key(s) (recipient.Q, scan.Q) to sender - const forSender = stealthDualSend(nonce.privateKey, recipient.publicKey, scan.publicKey) - assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) - - // ... sender reveals nonce public key (nonce.Q) to scanner - const forScanner = stealthDualScan(scan.privateKey, recipient.publicKey, nonce.publicKey) - assert.throws(function () { forScanner.toWIF() }, /Error: Missing private key/) - - // ... scanner reveals relevant transaction + nonce public key (nonce.Q) to recipient - const forRecipient = stealthDualReceive(scan.privateKey, recipient.privateKey, nonce.publicKey) - assert.doesNotThrow(function () { forRecipient.toWIF() }) - - // scanner, sender and recipient, all derived same address - assert.equal(getAddress(forSender), getAddress(forScanner)) - assert.equal(getAddress(forSender), getAddress(forRecipient)) - }) - - it('can generate a dual-key stealth address (randomly)', function () { - const recipient = bitcoin.ECPair.makeRandom() // private to recipient - const scan = bitcoin.ECPair.makeRandom() // private to scanner/recipient - const nonce = bitcoin.ECPair.makeRandom() // private to sender - - // ... recipient reveals public key(s) (recipient.Q, scan.Q) to sender - const forSender = stealthDualSend(nonce.privateKey, recipient.publicKey, scan.publicKey) - assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) - - // ... sender reveals nonce public key (nonce.Q) to scanner - const forScanner = stealthDualScan(scan.privateKey, recipient.publicKey, nonce.publicKey) - assert.throws(function () { forScanner.toWIF() }, /Error: Missing private key/) - - // ... scanner reveals relevant transaction + nonce public key (nonce.Q) to recipient - const forRecipient = stealthDualReceive(scan.privateKey, recipient.privateKey, nonce.publicKey) - assert.doesNotThrow(function () { forRecipient.toWIF() }) - - // scanner, sender and recipient, all derived same address - assert.equal(getAddress(forSender), getAddress(forScanner)) - assert.equal(getAddress(forSender), getAddress(forRecipient)) - }) -})