diff --git a/test/node-rpc-test.js b/test/node-rpc-test.js new file mode 100644 index 000000000..ba8e02cbc --- /dev/null +++ b/test/node-rpc-test.js @@ -0,0 +1,63 @@ +/* eslint-env mocha */ +/* eslint prefer-arrow-callback: "off" */ + +'use strict'; + +const assert = require('bsert'); +const FullNode = require('../lib/node/fullnode'); + +const ports = { + p2p: 49331, + node: 49332, + wallet: 49333 +}; + +const node = new FullNode({ + network: 'regtest', + apiKey: 'foo', + walletAuth: true, + memory: true, + workers: true, + workersSize: 2, + plugins: [require('../lib/wallet/plugin')], + port: ports.p2p, + httpPort: ports.node, + env: { + 'BCOIN_WALLET_HTTP_PORT': ports.wallet.toString() + }}); + +const {NodeClient} = require('bclient'); + +const nclient = new NodeClient({ + port: ports.node, + apiKey: 'foo', + timeout: 15000 +}); + +describe('RPC', function() { + this.timeout(15000); + + before(async () => { + await node.open(); + }); + + after(async () => { + await node.close(); + }); + + it('should rpc help', async () => { + assert(await nclient.execute('help', [])); + + await assert.rejects(async () => { + await nclient.execute('help', ['getinfo']); + }, { + name: 'Error', + message: /^getinfo/ + }); + }); + + it('should rpc getinfo', async () => { + const info = await nclient.execute('getinfo', []); + assert.strictEqual(info.blocks, 0); + }); +}); diff --git a/test/rpc-test.js b/test/rpc-test.js deleted file mode 100644 index afad91ce0..000000000 --- a/test/rpc-test.js +++ /dev/null @@ -1,280 +0,0 @@ -/* eslint-env mocha */ -/* eslint prefer-arrow-callback: "off" */ - -'use strict'; - -const assert = require('bsert'); -const consensus = require('../lib/protocol/consensus'); -const Address = require('../lib/primitives/address'); -const FullNode = require('../lib/node/fullnode'); - -const ports = { - p2p: 49331, - node: 49332, - wallet: 49333 -}; - -const node = new FullNode({ - network: 'regtest', - apiKey: 'foo', - walletAuth: true, - memory: true, - workers: true, - workersSize: 2, - plugins: [require('../lib/wallet/plugin')], - port: ports.p2p, - httpPort: ports.node, - env: { - 'BCOIN_WALLET_HTTP_PORT': ports.wallet.toString() - }}); - -const {NodeClient, WalletClient} = require('bclient'); - -const nclient = new NodeClient({ - port: ports.node, - apiKey: 'foo', - timeout: 15000 -}); - -const wclient = new WalletClient({ - port: ports.wallet, - apiKey: 'foo' -}); - -const {wdb} = node.require('walletdb'); -const defaultCoinbaseMaturity = consensus.COINBASE_MATURITY; - -let addressHot = null; -let addressMiner = null; -let walletHot = null; -let walletMiner = null; -let blocks = null; -let txid = null; -let utxo = null; - -describe('RPC', function() { - this.timeout(15000); - - before(() => { - consensus.COINBASE_MATURITY = 0; - }); - - after(() => { - consensus.COINBASE_MATURITY = defaultCoinbaseMaturity; - }); - - it('should open node and create wallets', async () => { - await node.open(); - await nclient.open(); - await wclient.open(); - - const walletHotInfo = await wclient.createWallet('hot'); - walletHot = wclient.wallet('hot', walletHotInfo.token); - const walletMinerInfo = await wclient.createWallet('miner'); - walletMiner = wclient.wallet('miner', walletMinerInfo.token); - await walletHot.open(); - await walletMiner.open(); - }); - - it('should rpc help', async () => { - assert(await nclient.execute('help', [])); - assert(await wclient.execute('help', [])); - - await assert.rejects(async () => { - await nclient.execute('help', ['getinfo']); - }, { - name: 'Error', - message: /^getinfo/ - }); - - await assert.rejects(async () => { - await wclient.execute('help', ['getbalance']); - }, { - name: 'Error', - message: /^getbalance/ - }); - }); - - it('should rpc getinfo', async () => { - const info = await nclient.execute('getinfo', []); - assert.strictEqual(info.blocks, 0); - }); - - it('should rpc selectwallet', async () => { - const response = await wclient.execute('selectwallet', ['miner']); - assert.strictEqual(response, null); - }); - - it('should rpc getnewaddress from default account', async () => { - const acctAddr = await wclient.execute('getnewaddress', []); - assert(Address.fromString(acctAddr.toString())); - }); - - it('should fail rpc getnewaddress from nonexistent account', async () => { - await assert.rejects(async () => { - await wclient.execute('getnewaddress', ['bad-account-name']); - }, { - name: 'Error', - message: 'Account not found.' - }); - }); - - it('should rpc getaccountaddress', async () => { - addressMiner = await wclient.execute('getaccountaddress', ['default']); - assert(Address.fromString(addressMiner.toString())); - }); - - it('should rpc generatetoaddress', async () => { - blocks = await nclient.execute('generatetoaddress', - [10, addressMiner]); - assert.strictEqual(blocks.length, 10); - }); - - it('should rpc sendtoaddress', async () => { - const acctHotDefault = await walletHot.getAccount('default'); - addressHot = acctHotDefault.receiveAddress; - - txid = await wclient.execute('sendtoaddress', [addressHot, 0.1234]); - assert.strictEqual(txid.length, 64); - }); - - it('should rpc sendmany', async () => { - const sendTo = {}; - sendTo[addressHot] = 1.0; - sendTo[addressMiner] = 0.1111; - txid = await wclient.execute('sendmany', ['default', sendTo]); - assert.strictEqual(txid.length, 64); - }); - - it('should fail malformed rpc sendmany', async () => { - await assert.rejects(async () => { - await wclient.execute('sendmany', ['default', null]); - }, { - name: 'Error', - message: 'Invalid send-to address.' - }); - - const sendTo = {}; - sendTo[addressHot] = null; - await assert.rejects(async () => { - await wclient.execute('sendmany', ['default', sendTo]); - }, { - name: 'Error', - message: 'Invalid amount.' - }); - }); - - it('should rpc listreceivedbyaddress', async () => { - await wclient.execute('selectwallet', ['hot']); - - const listZeroConf = await wclient.execute('listreceivedbyaddress', - [0, false, false]); - assert.deepStrictEqual(listZeroConf, [{ - 'involvesWatchonly': false, - 'address': addressHot, - 'account': 'default', - 'amount': 1.1234, - 'confirmations': 0, - 'label': '' - }]); - - blocks.push(await nclient.execute('generatetoaddress', [1, addressMiner])); - await wdb.syncChain(); - - const listSomeConf = await wclient.execute('listreceivedbyaddress', - [1, false, false]); - assert.deepStrictEqual(listSomeConf, [{ - 'involvesWatchonly': false, - 'address': addressHot, - 'account': 'default', - 'amount': 1.1234, - 'confirmations': 1, - 'label': '' - }]); - - const listTooManyConf = await wclient.execute('listreceivedbyaddress', - [100, false, false]); - assert.deepStrictEqual(listTooManyConf, []); - }); - - it('should rpc listtransactions with no args', async () => { - const txs = await wclient.execute('listtransactions', []); - assert.strictEqual(txs.length, 2); - assert.strictEqual(txs[0].amount + txs[1].amount, 1.1234); - assert.strictEqual(txs[0].account, 'default'); - }); - - it('should rpc listtransactions from specified account', async () => { - const wallet = await wclient.wallet('hot'); - await wallet.createAccount('foo'); - - const txs = await wclient.execute('listtransactions', ['foo']); - assert.strictEqual(txs.length, 0); - }); - - it('should fail rpc listtransactions from nonexistent account', async () => { - assert.rejects(async () => { - await wclient.execute('listtransactions', ['nonexistent']); - }, { - name: 'Error', - message: 'Account not found.' - }); - }); - - it('should rpc listunspent', async () => { - utxo = await wclient.execute('listunspent', []); - assert.strictEqual(utxo.length, 2); - }); - - it('should rpc lockunspent and listlockunspent', async () => { - let result = await wclient.execute('listlockunspent', []); - assert.deepStrictEqual(result, []); - - // lock one utxo - const output = utxo[0]; - const outputsToLock = [{'txid': output.txid, 'vout': output.vout}]; - result = await wclient.execute('lockunspent', [false, outputsToLock]); - assert(result); - - result = await wclient.execute('listlockunspent', []); - assert.deepStrictEqual(result, outputsToLock); - - // unlock all - result = await wclient.execute('lockunspent', [true]); - assert(result); - - result = await wclient.execute('listlockunspent', []); - assert.deepStrictEqual(result, []); - }); - - it('should rpc listsinceblock', async () => { - const listNoBlock = await wclient.execute('listsinceblock', []); - assert.strictEqual(listNoBlock.transactions.length, 2); - // txs returned in unpredictable order - const txids = [ - listNoBlock.transactions[0].txid, - listNoBlock.transactions[1].txid - ]; - assert(txids.includes(txid)); - - const block5 = blocks[5]; - const listOldBlock = await wclient.execute('listsinceblock', [block5]); - assert.strictEqual(listOldBlock.transactions.length, 2); - - const nonexistentBlock = consensus.ZERO_HASH.toString('hex'); - await assert.rejects(async () => { - await wclient.execute('listsinceblock', [nonexistentBlock]); - }, { - name: 'Error', - message: 'Block not found.' - }); - }); - - it('should cleanup', async () => { - await walletHot.close(); - await walletMiner.close(); - await wclient.close(); - await nclient.close(); - await node.close(); - }); -}); diff --git a/test/wallet-rpc-test.js b/test/wallet-rpc-test.js index de6a45538..c8e83f0ef 100644 --- a/test/wallet-rpc-test.js +++ b/test/wallet-rpc-test.js @@ -4,6 +4,7 @@ const {NodeClient, WalletClient} = require('bclient'); const assert = require('bsert'); +const consensus = require('../lib/protocol/consensus'); const FullNode = require('../lib/node/fullnode'); const Network = require('../lib/protocol/network'); const Mnemonic = require('../lib/hd/mnemonic'); @@ -12,6 +13,7 @@ const Script = require('../lib/script/script'); const Address = require('../lib/primitives/address'); const network = Network.get('regtest'); const mnemonics = require('./data/mnemonic-english.json'); +const {forValue} = require('./util/common'); // Commonly used test mnemonic const phrase = mnemonics[0][1]; @@ -27,6 +29,7 @@ const node = new FullNode({ walletAuth: true, memory: true, workers: true, + workersSize: 2, plugins: [require('../lib/wallet/plugin')], port: ports.p2p, httpPort: ports.node, @@ -35,9 +38,12 @@ const node = new FullNode({ } }); +const {wdb} = node.require('walletdb'); + const nclient = new NodeClient({ port: ports.node, - apiKey: 'bar' + apiKey: 'bar', + timeout: 15000 }); const wclient = new WalletClient({ @@ -52,10 +58,14 @@ describe('Wallet RPC Methods', function() { // used to derive addresses throughout the test suite let xpub; + let walletHot = null; + let walletMiner = null; + let addressHot = null; + let addressMiner = null; + let utxo = null; + before(async () => { await node.open(); - await nclient.open(); - await wclient.open(); // Derive the xpub using the well known // mnemonic and network's coin type @@ -73,17 +83,214 @@ describe('Wallet RPC Methods', function() { 'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'about' ].join(' ')); + + // Create wallets. + { + const walletInfo = await wclient.createWallet('hot'); + walletHot = wclient.wallet('hot', walletInfo.token); + + const account = await walletHot.getAccount('default'); + addressHot = account.receiveAddress; + } + + { + const walletInfo = await wclient.createWallet('miner'); + walletMiner = wclient.wallet('miner', walletInfo.token); + + const account = await walletMiner.getAccount('default'); + addressMiner = account.receiveAddress; + } + + await nclient.execute('generatetoaddress', [102, addressMiner]); + await forValue(wdb, 'height', 102); }); after(async () => { - await nclient.close(); - await wclient.close(); await node.close(); }); + it('should rpc help', async () => { + assert(await wclient.execute('help', [])); + + await assert.rejects(async () => { + await wclient.execute('help', ['getbalance']); + }, { + name: 'Error', + message: /^getbalance/ + }); + }); + + it('should rpc selectwallet', async () => { + for (const wname of ['hot', 'miner']) { + const response = await wclient.execute('selectwallet', [wname]); + assert.strictEqual(response, null); + + const info = await wclient.execute('getwalletinfo'); + assert.strictEqual(info.walletid, wname); + } + }); + + it('should rpc getnewaddress from default account', async () => { + const acctAddr = await wclient.execute('getnewaddress', []); + assert(Address.fromString(acctAddr.toString())); + }); + + it('should rpc sendtoaddress', async () => { + await wclient.execute('selectwallet', ['miner']); + + const txid = await wclient.execute('sendtoaddress', [addressHot, 0.1234]); + assert.strictEqual(txid.length, 64); + }); + + it('should rpc getaccountaddress', async () => { + addressMiner = await wclient.execute('getaccountaddress', ['default']); + assert(Address.fromString(addressMiner.toString())); + }); + + it('should fail rpc getnewaddress from nonexistent account', async () => { + await assert.rejects(async () => { + await wclient.execute('getnewaddress', ['bad-account-name']); + }, { + name: 'Error', + message: 'Account not found.' + }); + }); + + it('should rpc sendmany', async () => { + const sendTo = {}; + sendTo[addressHot] = 1.0; + sendTo[addressMiner] = 0.1111; + const txid = await wclient.execute('sendmany', ['default', sendTo]); + assert.strictEqual(txid.length, 64); + }); + + it('should fail malformed rpc sendmany', async () => { + await assert.rejects(async () => { + await wclient.execute('sendmany', ['default', null]); + }, { + name: 'Error', + message: 'Invalid send-to address.' + }); + + const sendTo = {}; + sendTo[addressHot] = null; + await assert.rejects(async () => { + await wclient.execute('sendmany', ['default', sendTo]); + }, { + name: 'Error', + message: 'Invalid amount.' + }); + }); + + it('should rpc listreceivedbyaddress', async () => { + await wclient.execute('selectwallet', ['hot']); + + const listZeroConf = await wclient.execute('listreceivedbyaddress', + [0, false, false]); + assert.deepStrictEqual(listZeroConf, [{ + 'involvesWatchonly': false, + 'address': addressHot, + 'account': 'default', + 'amount': 1.1234, + 'confirmations': 0, + 'label': '' + }]); + + await nclient.execute('generatetoaddress', [1, addressMiner]); + await wdb.syncChain(); + + const listSomeConf = await wclient.execute('listreceivedbyaddress', + [1, false, false]); + assert.deepStrictEqual(listSomeConf, [{ + 'involvesWatchonly': false, + 'address': addressHot, + 'account': 'default', + 'amount': 1.1234, + 'confirmations': 1, + 'label': '' + }]); + + const listTooManyConf = await wclient.execute('listreceivedbyaddress', + [100, false, false]); + assert.deepStrictEqual(listTooManyConf, []); + }); + + it('should rpc listtransactions with no args', async () => { + const txs = await wclient.execute('listtransactions', []); + assert.strictEqual(txs.length, 2); + assert.strictEqual(txs[0].amount + txs[1].amount, 1.1234); + assert.strictEqual(txs[0].account, 'default'); + }); + + it('should rpc listtransactions from specified account', async () => { + const wallet = await wclient.wallet('hot'); + await wallet.createAccount('foo'); + + const txs = await wclient.execute('listtransactions', ['foo']); + assert.strictEqual(txs.length, 0); + }); + + it('should fail rpc listtransactions from nonexistent account', async () => { + assert.rejects(async () => { + await wclient.execute('listtransactions', ['nonexistent']); + }, { + name: 'Error', + message: 'Account not found.' + }); + }); + + it('should rpc listunspent', async () => { + utxo = await wclient.execute('listunspent', []); + assert.strictEqual(utxo.length, 2); + }); + + it('should rpc lockunspent and listlockunspent', async () => { + let result = await wclient.execute('listlockunspent', []); + assert.deepStrictEqual(result, []); + + // lock one utxo + const output = utxo[0]; + const outputsToLock = [{'txid': output.txid, 'vout': output.vout}]; + result = await wclient.execute('lockunspent', [false, outputsToLock]); + assert(result); + + result = await wclient.execute('listlockunspent', []); + assert.deepStrictEqual(result, outputsToLock); + + // unlock all + result = await wclient.execute('lockunspent', [true]); + assert(result); + + result = await wclient.execute('listlockunspent', []); + assert.deepStrictEqual(result, []); + }); + + it('should rpc listsinceblock', async () => { + const listNoBlock = await wclient.execute('listsinceblock', []); + assert.strictEqual(listNoBlock.transactions.length, 2); + + const txs = listNoBlock.transactions; + + // Sort transactions by blockheight + txs.sort((a, b) => a.blockheight - b.blockheight); + + // get lowest block hash. + const bhash = txs[0].blockhash; + const listOldBlock = await wclient.execute('listsinceblock', [bhash]); + assert.strictEqual(listOldBlock.transactions.length, 2); + + const nonexistentBlock = consensus.ZERO_HASH.toString('hex'); + await assert.rejects(async () => { + await wclient.execute('listsinceblock', [nonexistentBlock]); + }, { + name: 'Error', + message: 'Block not found.' + }); + }); + describe('getaddressinfo', () => { - const watchOnlyWalletId = 'foo'; - const standardWalletId = 'bar'; + const watchOnlyWalletId = 'getaddressinfo-foo'; + const standardWalletId = 'getaddressinfo-bar'; // m/44'/1'/0'/0/{0,1} const pubkeys = [ @@ -95,14 +302,6 @@ describe('Wallet RPC Methods', function() { // 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