fcoin/test/wallet-rpc-test.js
2019-06-05 12:11:57 -07:00

282 lines
9.2 KiB
JavaScript

/* eslint-env mocha */
'use strict';
const {NodeClient, WalletClient} = require('bclient');
const assert = require('bsert');
const FullNode = require('../lib/node/fullnode');
const Network = require('../lib/protocol/network');
const Mnemonic = require('../lib/hd/mnemonic');
const HDPrivateKey = require('../lib/hd/private');
const Script = require('../lib/script/script');
const Address = require('../lib/primitives/address');
const network = Network.get('regtest');
const mnemonics = require('./data/mnemonic-english.json');
// Commonly used test mnemonic
const phrase = mnemonics[0][1];
const ports = {
p2p: 49331,
node: 49332,
wallet: 49333
};
const node = new FullNode({
network: network.type,
apiKey: 'bar',
walletAuth: true,
memory: true,
workers: true,
plugins: [require('../lib/wallet/plugin')],
port: ports.p2p,
httpPort: ports.node,
env: {
'BCOIN_WALLET_HTTP_PORT': ports.wallet.toString()
}
});
const nclient = new NodeClient({
port: ports.node,
apiKey: 'bar'
});
const wclient = new WalletClient({
port: ports.wallet,
apiKey: 'bar'
});
describe('Wallet RPC Methods', function() {
this.timeout(15000);
// Define an account level hd extended public key to be
// used to derive addresses throughout the test suite
let xpub;
before(async () => {
await node.open();
await nclient.open();
await wclient.open();
// Derive the xpub using the well known
// mnemonic and network's coin type
const mnemonic = Mnemonic.fromPhrase(phrase);
const priv = HDPrivateKey.fromMnemonic(mnemonic);
const type = network.keyPrefix.coinType;
const key = priv.derive(44, true).derive(type, true).derive(0, true);
xpub = key.toPublic();
// Assert that the expected test phrase was
// read from disk
assert.equal(phrase, [
'abandon', 'abandon', 'abandon', 'abandon',
'abandon', 'abandon', 'abandon', 'abandon',
'abandon', 'abandon', 'abandon', 'about'
].join(' '));
});
after(async () => {
await nclient.close();
await wclient.close();
await node.close();
});
describe('getaddressinfo', () => {
const watchOnlyWalletId = 'foo';
const standardWalletId = 'bar';
// m/44'/1'/0'/0/{0,1}
const pubkeys = [
Buffer.from('02a7451395735369f2ecdfc829c0f'
+ '774e88ef1303dfe5b2f04dbaab30a535dfdd6', 'hex'),
Buffer.from('03589ae7c835ce76e23cf8feb32f1a'
+ 'df4a7f2ba0ed2ad70801802b0bcd70e99c1c', 'hex')
];
// set up the initial testing state
before(async () => {
{
// Set up the testing environment
// by creating a wallet and a watch
// only wallet
const info = await nclient.getInfo();
assert.equal(info.chain.height, 0);
}
{
// Create a watch only wallet using the path
// m/44'/1'/0' and assert that the wallet
// was properly created
const accountKey = xpub.xpubkey(network.type);
const response = await wclient.createWallet(watchOnlyWalletId, {
watchOnly: true,
accountKey: accountKey
});
assert.equal(response.id, watchOnlyWalletId);
const wallet = wclient.wallet(watchOnlyWalletId);
const info = await wallet.getAccount('default');
assert.equal(info.accountKey, accountKey);
assert.equal(info.watchOnly, true);
}
{
// Create a wallet that manages the private keys itself
const response = await wclient.createWallet(standardWalletId);
assert.equal(response.id, standardWalletId);
const info = await wclient.getAccount(standardWalletId, 'default');
assert.equal(info.watchOnly, false);
};
});
// The rpc interface requires the wallet to be selected first
it('should return iswatchonly correctly', async () => {
// m/44'/1'/0'/0/0
const receive = 'mkpZhYtJu2r87Js3pDiWJDmPte2NRZ8bJV';
{
await wclient.execute('selectwallet', [standardWalletId]);
const response = await wclient.execute('getaddressinfo', [receive]);
assert.equal(response.iswatchonly, false);
}
{
await wclient.execute('selectwallet', [watchOnlyWalletId]);
const response = await wclient.execute('getaddressinfo', [receive]);
assert.equal(response.iswatchonly, true);
}
});
it('should return the correct address', async () => {
// m/44'/1'/0'/0/0
const receive = 'mkpZhYtJu2r87Js3pDiWJDmPte2NRZ8bJV';
await wclient.execute('selectwallet', [watchOnlyWalletId]);
const response = await wclient.execute('getaddressinfo', [receive]);
assert.equal(response.address, receive);
});
it('should detect owned address', async () => {
// m/44'/1'/0'/0/0
const receive = 'mkpZhYtJu2r87Js3pDiWJDmPte2NRZ8bJV';
{
await wclient.execute('selectwallet', [watchOnlyWalletId]);
const response = await wclient.execute('getaddressinfo', [receive]);
assert.equal(response.ismine, true);
}
{
await wclient.execute('selectwallet', [standardWalletId]);
const response = await wclient.execute('getaddressinfo', [receive]);
assert.equal(response.ismine, false);
}
});
it('should detect a p2sh address', async () => {
const script = Script.fromMultisig(2, 2, pubkeys);
const address = Address.fromScript(script);
const addr = address.toString(network);
const response = await wclient.execute('getaddressinfo', [addr]);
assert.equal(response.isscript, true);
assert.equal(response.iswitness, false);
assert.equal(response.witness_program, undefined);
});
it('should return the correct program for a p2wpkh address', async () => {
// m/44'/1'/0'/0/5
const receive = 'bcrt1q53724q6cywuzsvq5e3nvdeuwrepu69jsc6ulmx';
const addr = Address.fromString(receive);
await wclient.execute('selectwallet', [watchOnlyWalletId]);
const str = addr.toString(network);
const response = await wclient.execute('getaddressinfo', [str]);
assert.equal(response.witness_program, addr.hash.toString('hex'));
});
it('should detect p2wsh program', async () => {
const script = Script.fromMultisig(2, 2, pubkeys);
const address = Address.fromWitnessScripthash(script.sha256());
const addr = address.toString(network);
const response = await wclient.execute('getaddressinfo', [addr]);
assert.equal(response.isscript, true);
assert.equal(response.iswitness, true);
assert.equal(response.witness_program, address.hash.toString('hex'));
});
it('should detect ismine up to the lookahead', async () => {
const info = await wclient.getAccount(watchOnlyWalletId, 'default');
await wclient.execute('selectwallet', [watchOnlyWalletId]);
// m/44'/1'/0'
const addresses = [
'mkpZhYtJu2r87Js3pDiWJDmPte2NRZ8bJV', // /0/0
'mzpbWabUQm1w8ijuJnAof5eiSTep27deVH', // /0/1
'mnTkxhNkgx7TsZrEdRcPti564yQTzynGJp', // /0/2
'mpW3iVi2Td1vqDK8Nfie29ddZXf9spmZkX', // /0/3
'n2BMo5arHDyAK2CM8c56eoEd18uEkKnRLC', // /0/4
'mvWgTTtQqZohUPnykucneWNXzM5PLj83an', // /0/5
'muTU2Av1EwnsyhieQhyPL7hgEf883LR4xg', // /0/6
'mwduZ8Ksa563v7rWdSPmqyKR4y2FeB5g8p', // /0/7
'miyBE85ro5zt9RseSzYVEbB3TfzkxgSm8C', // /0/8
'mnYwW7mU3jajB11vrpDZwZDrXwVfE5Jc31', // /0/9
'mx3YNRT8Vg8QwFq5Z5MAVDDVHp4ihHsffn' // /0/10
];
// Assert that the lookahead is configured as expected
// subtract one from addresses.length, it is 0 indexed
assert.equal(addresses.length - 1, info.lookahead);
// Each address through the lookahead number should
// be recognized as an owned address
for (let i = 0; i <= info.lookahead; i++) {
const address = addresses[i];
const response = await wclient.execute('getaddressinfo', [address]);
assert.equal(response.ismine, true);
}
// m/44'/1'/0'/0/11
// This address is outside of the lookahead range
const failed = 'myHL2QuECVYkx9Y94gyC6RSweLNnteETsB';
const response = await wclient.execute('getaddressinfo', [failed]);
assert.equal(response.ismine, false);
});
it('should detect change addresses', async () => {
// m/44'/1'/0'/1/0
const address = 'mi8nhzZgGZQthq6DQHbru9crMDerUdTKva';
const info = await wclient.execute('getaddressinfo', [address]);
assert.equal(info.ischange, true);
});
it('should throw for the wrong network', async () => {
// m/44'/1'/0'/0/0
const failed = '16JcQVoL61QsLCPS6ek8UJZ52eRfaFqLJt';
// Match the bitcoind response when sending the incorrect
// network. Expect an RPC error
const fn = async () => await wclient.execute('getaddressinfo', [failed]);
await assert.rejects(fn, {
name: 'Error',
message: 'Invalid address.'
});
});
it('should fail for invalid address', async () => {
// m/44'/1'/0'/0/0
let failed = '16JcQVoL61QsLCPS6ek8UJZ52eRfaFqLJt';
// remove the first character
failed = failed.slice(1, failed.length);
const fn = async () => await wclient.execute('getaddressinfo', [failed]);
await assert.rejects(fn, {
name: 'Error',
message: 'Invalid address.'
});
});
});
});