Commit 07179d0d authored by kenshin-samourai's avatar kenshin-samourai
Browse files

add derivation of payment addresses

parent bb95a471
...@@ -9,10 +9,14 @@ A set of utilities for working with BIP47 and bitcoinjs-lib. ...@@ -9,10 +9,14 @@ A set of utilities for working with BIP47 and bitcoinjs-lib.
``` ```
const bip47 = require('bip47-js'); const bip47 = require('bip47-js');
const b58PCode = 'PM8TJTLJbPRGxSbc8EJi42Wrr6QbNSaSSVJ5Y3E4pbCYiTHUskHg13935Ubb7q8tx9GVbh2UuRnBc3WSyJHhUrw8KhprKnn9eDznYGieTzFcwQRya4GA'; const b58PCode = 'PM8TJS2JxQ5ztXUpBBRnpTbcUXbUHy2T1abfrb3KkAAtMEGNbey4oumH7Hc578WgQJhPjBxteQ5GHHToTYHE3A1w6p7tU6KSoFmWBVbFGjKPisZDbP97';
const pcode = bip47.fromBase58(b58PCode); const pcode = bip47.fromBase58(b58PCode);
const notifAddr = pcode.getNotificationAddress(); const notifAddr = pcode.getNotificationAddress();
const myPrivKey0 = Buffer.from('8d6a8ecd8ee5e0042ad0cb56e3a971c760b5145c3917a8e7beaf0ed92d7a520c', 'hex');
const paymentAddr1 = pcode.getPaymentAddress(myPrivKey0, 1);
const paymentAddr2 = pcode.getPaymentAddress(myPrivKey0, 2);
``` ```
### Testnet ### Testnet
...@@ -22,7 +26,7 @@ const bip47 = require('bip47-js'); ...@@ -22,7 +26,7 @@ const bip47 = require('bip47-js');
const networks = bip47.utils.networks; const networks = bip47.utils.networks;
const b58PCode = 'PM8TJTLJbPRGxSbc8EJi42Wrr6QbNSaSSVJ5Y3E4pbCYiTHUskHg13935Ubb7q8tx9GVbh2UuRnBc3WSyJHhUrw8KhprKnn9eDznYGieTzFcwQRya4GA'; const b58PCode = 'PM8TJS2JxQ5ztXUpBBRnpTbcUXbUHy2T1abfrb3KkAAtMEGNbey4oumH7Hc578WgQJhPjBxteQ5GHHToTYHE3A1w6p7tU6KSoFmWBVbFGjKPisZDbP97';
const pcode = bip47.fromBase58(b58PCode, networks.testnet); const pcode = bip47.fromBase58(b58PCode, networks.testnet);
const notifAddr = pcode.getNotificationAddress(); const notifAddr = pcode.getNotificationAddress();
...@@ -41,7 +45,7 @@ The recommended method of using this library and bitcoinjs-lib in your browser i ...@@ -41,7 +45,7 @@ The recommended method of using this library and bitcoinjs-lib in your browser i
</head> </head>
<body> <body>
<script type="text/javascript"> <script type="text/javascript">
const pcBase58 = 'PM8TJTLJbPRGxSbc8EJi42Wrr6QbNSaSSVJ5Y3E4pbCYiTHUskHg13935Ubb7q8tx9GVbh2UuRnBc3WSyJHhUrw8KhprKnn9eDznYGieTzFcwQRya4GA'; const pcBase58 = 'PM8TJS2JxQ5ztXUpBBRnpTbcUXbUHy2T1abfrb3KkAAtMEGNbey4oumH7Hc578WgQJhPjBxteQ5GHHToTYHE3A1w6p7tU6KSoFmWBVbFGjKPisZDbP97';
const pcode = bip47.fromBase58(pcBase58); const pcode = bip47.fromBase58(pcBase58);
const notifAddr = pcode.getNotificationAddress(); const notifAddr = pcode.getNotificationAddress();
console.log(notifAddr); console.log(notifAddr);
......
...@@ -17,7 +17,8 @@ ...@@ -17,7 +17,8 @@
"bip32": "2.0.6", "bip32": "2.0.6",
"bs58check": "2.1.2", "bs58check": "2.1.2",
"create-hash": "1.2.0", "create-hash": "1.2.0",
"safe-buffer": "5.2.0" "safe-buffer": "5.2.0",
"tiny-secp256k1": "1.1.6"
}, },
"devDependencies": { "devDependencies": {
"mocha": "^7.1.1" "mocha": "^7.1.1"
......
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
const { networks, getP2pkhAddress } = require('./utils'); const ecc = require('tiny-secp256k1');
const { networks, getP2pkhAddress, sha256 } = require('./utils');
const { fromPublicKey } = require('bip32'); const { fromPublicKey } = require('bip32');
const { encode, decode } = require('bs58check'); const { encode, decode } = require('bs58check');
...@@ -58,6 +59,30 @@ class PaymentCode { ...@@ -58,6 +59,30 @@ class PaymentCode {
const child = this.derive(0); const child = this.derive(0);
return getP2pkhAddress(child.publicKey, this.network); return getP2pkhAddress(child.publicKey, this.network);
} }
derivePaymentPublicKey(a, idx) {
if (!ecc.isPrivate(a))
throw new TypeError('Invalid private key');
const B = this.derive(idx).publicKey;
if (!ecc.isPoint(B))
throw new TypeError('Invalid derived public key');
const S = ecc.pointMultiply(B, a);
const Sx = S.slice(1, 33);
const s = sha256(Sx);
if (!ecc.isPrivate(s))
throw new TypeError('Invalid shared secret');
return ecc.pointAdd(B, ecc.pointFromScalar(s));
}
getPaymentAddress(a, idx) {
const pubkey = this.derivePaymentPublicKey(a, idx);
return getP2pkhAddress(pubkey, this.network);
}
} }
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
const assert = require('assert') const assert = require('assert')
const bip47 = require('../src') const bip47 = require('../src')
const utils = bip47.utils
/** /**
* Test vectors * Test vectors
...@@ -9,15 +11,30 @@ const bip47 = require('../src') ...@@ -9,15 +11,30 @@ const bip47 = require('../src')
const PC_1 = { const PC_1 = {
pcBase58: 'PM8TJTLJbPRGxSbc8EJi42Wrr6QbNSaSSVJ5Y3E4pbCYiTHUskHg13935Ubb7q8tx9GVbh2UuRnBc3WSyJHhUrw8KhprKnn9eDznYGieTzFcwQRya4GA', pcBase58: 'PM8TJTLJbPRGxSbc8EJi42Wrr6QbNSaSSVJ5Y3E4pbCYiTHUskHg13935Ubb7q8tx9GVbh2UuRnBc3WSyJHhUrw8KhprKnn9eDznYGieTzFcwQRya4GA',
notifAddr: '1JDdmqFLhpzcUwPeinhJbUPw4Co3aWLyzW', notifAddr: '1JDdmqFLhpzcUwPeinhJbUPw4Co3aWLyzW',
notifPrivkey: '8d6a8ecd8ee5e0042ad0cb56e3a971c760b5145c3917a8e7beaf0ed92d7a520c',
notifPubKey: '0353883a146a23f988e0f381a9507cbdb3e3130cd81b3ce26daf2af088724ce683' notifPubKey: '0353883a146a23f988e0f381a9507cbdb3e3130cd81b3ce26daf2af088724ce683'
}; };
const PC_2 = { const PC_2 = {
pcBase58: 'PM8TJS2JxQ5ztXUpBBRnpTbcUXbUHy2T1abfrb3KkAAtMEGNbey4oumH7Hc578WgQJhPjBxteQ5GHHToTYHE3A1w6p7tU6KSoFmWBVbFGjKPisZDbP97', pcBase58: 'PM8TJS2JxQ5ztXUpBBRnpTbcUXbUHy2T1abfrb3KkAAtMEGNbey4oumH7Hc578WgQJhPjBxteQ5GHHToTYHE3A1w6p7tU6KSoFmWBVbFGjKPisZDbP97',
notifAddr: '1ChvUUvht2hUQufHBXF8NgLhW8SwE2ecGV', notifAddr: '1ChvUUvht2hUQufHBXF8NgLhW8SwE2ecGV',
notifPrivkey: '04448fd1be0c9c13a5ca0b530e464b619dc091b299b98c5cab9978b32b4a1b8b',
notifPubKey: '024ce8e3b04ea205ff49f529950616c3db615b1e37753858cc60c1ce64d17e2ad8' notifPubKey: '024ce8e3b04ea205ff49f529950616c3db615b1e37753858cc60c1ce64d17e2ad8'
}; };
const PC_2_PAYMENT_ADDR = [
'141fi7TY3h936vRUKh1qfUZr8rSBuYbVBK',
'12u3Uued2fuko2nY4SoSFGCoGLCBUGPkk6',
'1FsBVhT5dQutGwaPePTYMe5qvYqqjxyftc',
'1CZAmrbKL6fJ7wUxb99aETwXhcGeG3CpeA',
'1KQvRShk6NqPfpr4Ehd53XUhpemBXtJPTL',
'1KsLV2F47JAe6f8RtwzfqhjVa8mZEnTM7t',
'1DdK9TknVwvBrJe7urqFmaxEtGF2TMWxzD',
'16DpovNuhQJH7JUSZQFLBQgQYS4QB9Wy8e',
'17qK2RPGZMDcci2BLQ6Ry2PDGJErrNojT5',
'1GxfdfP286uE24qLZ9YRP3EWk2urqXgC4s'
]
const PC_3 = { const PC_3 = {
pcBase58: 'QM8TJTLJbPRGxSbc8EJi42Wrr6QbNSaSSVJ5Y3E4pbCYiTHUskHg13935Ubb7q8tx9GVbh2UuRnBc3WSyJHhUrw8KhprKnn9eDznYGieTzFcwQRya4GA', pcBase58: 'QM8TJTLJbPRGxSbc8EJi42Wrr6QbNSaSSVJ5Y3E4pbCYiTHUskHg13935Ubb7q8tx9GVbh2UuRnBc3WSyJHhUrw8KhprKnn9eDznYGieTzFcwQRya4GA',
}; };
...@@ -89,4 +106,39 @@ describe('payment-code', function() { ...@@ -89,4 +106,39 @@ describe('payment-code', function() {
}); });
}); });
describe('PaymentCode.derivePaymentPublicKey()', function() {
it('should successfully derive public keys for payments', function() {
try {
const privkey1 = Buffer.from(PC_1.notifPrivkey, 'hex');
const pc2 = bip47.fromBase58(PC_2.pcBase58);
for (let i=0; i<10; i++) {
const pubkeyPayment = pc2.derivePaymentPublicKey(privkey1, i);
const addrPayment = utils.getP2pkhAddress(pubkeyPayment, utils.networks.bitcoin);
if (addrPayment !== PC_2_PAYMENT_ADDR[i])
assert(false);
}
assert(true);
} catch(e) {
assert(false);
}
});
});
describe('PaymentCode.getPaymentAddress()', function() {
it('should successfully derive P2PKH addresses for payments', function() {
try {
const privkey1 = Buffer.from(PC_1.notifPrivkey, 'hex');
const pc2 = bip47.fromBase58(PC_2.pcBase58);
for (let i=0; i<10; i++) {
const addrPayment = pc2.getPaymentAddress(privkey1, i);
if (addrPayment !== PC_2_PAYMENT_ADDR[i])
assert(false);
}
assert(true);
} catch(e) {
assert(false);
}
});
});
}); });
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment