diff --git a/lib/wallet/rpc.js b/lib/wallet/rpc.js index a73830b2..61ae0004 100644 --- a/lib/wallet/rpc.js +++ b/lib/wallet/rpc.js @@ -2717,7 +2717,7 @@ class RPC extends RPCBase { async sendBatch(args, help) { const [actions, options] = this._validateBatch(args, help, 'sendbatch'); const wallet = this.wallet; - const tx = await wallet.sendBatch(actions, options); + const {tx} = await wallet.sendBatch(actions, options); return tx.getJSON(this.network); } @@ -2727,7 +2727,7 @@ class RPC extends RPCBase { options.paths = true; const wallet = this.wallet; - const mtx = await wallet.createBatch(actions, options); + const {mtx} = await wallet.createBatch(actions, options); return mtx.getJSON(this.network); } @@ -2762,7 +2762,7 @@ class RPC extends RPCBase { 'NONE action requires 2 arguments: address, value' ); const {addr, value} = this._validateSendToAddress(action); - actions.push([type, addr, value]); + actions.push({ type: type, args: [addr, value] }); break; } case 'OPEN': { @@ -2771,7 +2771,7 @@ class RPC extends RPCBase { 'OPEN action requires 1 argument: name' ); const {name} = this._validateOpen(action); - actions.push([type, name]); + actions.push({ type: type, args: [name] }); break; } case 'BID': { @@ -2780,7 +2780,7 @@ class RPC extends RPCBase { 'BID action requires 3 arguments: name, bid, value' ); const {name, bid, value} = this._validateBid(action); - actions.push([type, name, bid, value]); + actions.push({ type: type, args: [name, bid, value] }); break; } case 'REVEAL': { @@ -2790,9 +2790,9 @@ class RPC extends RPCBase { ); const {name} = this._validateReveal(action); if (name) - actions.push([type, name]); + actions.push({ type: type, args: [name] }); else - actions.push([type]); + actions.push({ type: type }); break; } case 'REDEEM': { @@ -2802,9 +2802,9 @@ class RPC extends RPCBase { ); const {name} = this._validateRedeem(action); if (name) - actions.push([type, name]); + actions.push({ type: type, args: [name] }); else - actions.push([type]); + actions.push({ type: type }); break; } case 'UPDATE': { @@ -2813,7 +2813,7 @@ class RPC extends RPCBase { 'UPDATE action requires 2 arguments: name, data' ); const {name, resource} = this._validateUpdate(action); - actions.push([type, name, resource]); + actions.push({ type: type, args: [name, resource] }); break; } case 'RENEW': { @@ -2823,9 +2823,9 @@ class RPC extends RPCBase { ); if (action.length === 1) { const {name} = this._validateRenewal(action); - actions.push([type, name]); + actions.push({ type: type, args: [name] }); } else { - actions.push([type]); + actions.push({ type: type }); } break; } @@ -2835,7 +2835,7 @@ class RPC extends RPCBase { 'TRANSFER action requires 2 arguments: name, address' ); const {name, address} = this._validateTransfer(action); - actions.push([type, name, address]); + actions.push({ type: type, args: [name, address] }); break; } case 'FINALIZE': { @@ -2845,9 +2845,9 @@ class RPC extends RPCBase { ); if (action.length === 1) { const {name} = this._validateFinalize(action); - actions.push([type, name]); + actions.push({ type: type, args: [name] }); } else { - actions.push([type]); + actions.push({ type: type }); } break; } @@ -2857,7 +2857,7 @@ class RPC extends RPCBase { 'CANCEL action requires 1 argument: name' ); const {name} = this._validateCancel(action); - actions.push([type, name]); + actions.push({ type: type, args: [name] }); break; } case 'REVOKE': { @@ -2866,7 +2866,7 @@ class RPC extends RPCBase { 'REVOKE action requires 1 argument: name' ); const {name} = this._validateRevoke(action); - actions.push([type, name]); + actions.push({ type: type, args: [name] }); break; } default: diff --git a/lib/wallet/wallet.js b/lib/wallet/wallet.js index f7674ec7..f5920c22 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -3862,13 +3862,45 @@ class Wallet extends EventEmitter { } /** - * Make a batch transaction with multiple actions. - * @param {Array} actions - * @param {Object} options - * @returns {Promise} + * @typedef {Object} BatchAction + * @property {String} type - Action type. + * @property {String} [id] - Action ID. + * @property {Array} [args] - Arguments for the action. */ - async makeBatch(actions, options) { + /** + * @typedef {Object} BatchError + * @property {String} message - Error message. + * @property {String} type - Type of the action that caused the error. + * @property {Error} error - Original error object. + * @property {String} [id] - Optional ID for the action. + */ + + /** + * @typedef {Object} BatchResult + * @property {MTX} mtx - The resulting transaction + * @property {BatchError[]} errors - List of errors encountered during + * processing. + */ + + /** + * @typedef {Object} BatchTXResult + * @property {TX} tx - The resulting transaction + * @property {BatchError[]} errors - List of errors encountered during + * processing. + */ + + /** + * Make a batch transaction with multiple actions. + * @param {BatchAction[]} actions + * @param {Object} options + * @param {Number|String} [options.account=0] - Account index or name. + * @param {Boolean} [options.partialFailure=false] - Allow partial failure. + * @throws {Error} - general validations. + * @returns {Promise} + */ + + async makeBatch(actions, options = {}) { assert(Array.isArray(actions)); assert(actions.length, 'Batches require at least one action.'); @@ -3885,15 +3917,13 @@ class Wallet extends EventEmitter { // covenant like NONE, OPEN, or BID could shift the // output array out of sync with their corresponding inputs. actions.sort((a, b) => { - assert(Array.isArray(a)); - assert(Array.isArray(b)); - assert(a.length); - assert(b.length); + assert(a.type); + assert(b.type); - if (a[0] === b[0]) + if (a.type === b.type) return 0; - switch (b[0]) { + switch (b.type) { case 'REVEAL': case 'REDEEM': case 'UPDATE': @@ -3912,110 +3942,142 @@ class Wallet extends EventEmitter { // We track that by bumping receiveIndex. const account = await this.getAccount(acct); let receiveIndex = account.receiveDepth - 1; + /** @type {BatchError[]} */ + const errors = []; - // "actions" are arrays that start with a covenant type (or meta-type) - // followed by the arguments expected by the corresponding "make" function. - for (const action of actions) { - const type = action.shift(); - assert(typeof type === 'string'); + /** + * @param {BatchAction} action + * @param {Array} args + */ - switch (type) { + const handleAction = async (action, args) => { + switch (action.type) { case 'NONE': { - assert(action.length === 2); + assert(args.length === 2); this.makeTX([{ - address: action[0], - value: action[1] + address: args[0], + value: args[1] }], mtx); break; } case 'OPEN': { - assert(action.length === 1, 'Bad arguments for OPEN.'); - const name = action[0]; + assert(args.length === 1, 'Bad arguments for OPEN.'); + const name = args[0]; await this.makeOpen(name, acct, mtx); break; } case 'BID': { - assert(action.length === 3, 'Bad arguments for BID.'); - const address = account.deriveReceive(receiveIndex++).getAddress(); - const name = action[0]; - const value = action[1]; - const lockup = action[2]; + const bidIndex = receiveIndex++; + + assert(args.length === 3, 'Bad arguments for BID.'); + const address = account.deriveReceive(bidIndex).getAddress(); + const name = args[0]; + const value = args[1]; + const lockup = args[2]; await this.makeBid(name, value, lockup, acct, mtx, address); break; } case 'REVEAL': { - if (action.length === 1) { - const name = action[0]; + if (args.length === 1) { + const name = args[0]; await this.makeReveal(name, acct, mtx); break; } - assert(action.length === 0, 'Bad arguments for REVEAL.'); + assert(args.length === 0, 'Bad arguments for REVEAL.'); await this.makeRevealAll(mtx, witnessSize); break; } case 'REDEEM': { - if (action.length === 1) { - const name = action[0]; + if (args.length === 1) { + const name = args[0]; await this.makeRedeem(name, acct, mtx); break; } - assert(action.length === 0, 'Bad arguments for REDEEM.'); + assert(args.length === 0, 'Bad arguments for REDEEM.'); await this.makeRedeemAll(mtx, witnessSize); break; } case 'UPDATE': { - assert(action.length === 2, 'Bad arguments for UPDATE.'); - const name = action[0]; - const resource = action[1]; + assert(args.length === 2, 'Bad arguments for UPDATE.'); + const name = args[0]; + const resource = args[1]; await this.makeUpdate(name, resource, acct, mtx); break; } case 'RENEW': { - if (action.length === 1) { - const name = action[0]; + if (args.length === 1) { + const name = args[0]; await this.makeRenewal(name, acct, mtx); break; } - assert(action.length === 0, 'Bad arguments for RENEW.'); + assert(args.length === 0, 'Bad arguments for RENEW.'); await this.makeRenewalAll(mtx, witnessSize); break; } case 'TRANSFER': { - assert(action.length === 2, 'Bad arguments for TRANSFER.'); - const name = action[0]; - const address = action[1]; + assert(args.length === 2, 'Bad arguments for TRANSFER.'); + const name = args[0]; + const address = args[1]; await this.makeTransfer(name, address, acct, mtx); break; } case 'FINALIZE': { - if (action.length === 1) { - const name = action[0]; + if (args.length === 1) { + const name = args[0]; await this.makeFinalize(name, acct, mtx); break; } - assert(action.length === 0, 'Bad arguments for FINALIZE.'); + assert(args.length === 0, 'Bad arguments for FINALIZE.'); await this.makeFinalizeAll(mtx, witnessSize); break; } case 'CANCEL': { - assert(action.length === 1, 'Bad arguments for CANCEL.'); - const name = action[0]; + assert(args.length === 1, 'Bad arguments for CANCEL.'); + const name = args[0]; await this.makeCancel(name, acct, mtx); break; } case 'REVOKE': { - assert(action.length === 1, 'Bad arguments for REVOKE.'); - const name = action[0]; + assert(args.length === 1, 'Bad arguments for REVOKE.'); + const name = args[0]; await this.makeRevoke(name, acct, mtx); break; } default: - throw new Error(`Unknown action type: ${type}`); + throw new Error(`Unknown action type: ${action.type}`); + } + }; + + // "actions" are arrays that start with a covenant type (or meta-type) + // followed by the arguments expected by the corresponding "make" function. + for (const action of actions) { + assert(action); + assert(typeof action.type === 'string'); + + const args = action.args || []; + + assert(Array.isArray(args), 'Action args must be an array.'); + + try { + await handleAction(action, args); + } catch (err) { + if (!options.partialFailure) + throw err; + + if (action.type === 'BID') + receiveIndex--; + + errors.push({ + message: err.message, + type: action.type, + error: err, + id: action.id || null + }); } if (rules.countOpens(mtx) > consensus.MAX_BLOCK_OPENS) @@ -4068,27 +4130,30 @@ class Wallet extends EventEmitter { throw new Error('Batch output addresses would exceed lookahead.'); } - return mtx; + return {mtx, errors}; } /** * Make a batch transaction with multiple actions. * @param {Array} actions * @param {Object} options - * @returns {Promise} + * @returns {Promise} */ async _createBatch(actions, options) { - const mtx = await this.makeBatch(actions, options); + const {mtx, errors} = await this.makeBatch(actions, options); await this.fill(mtx, options); - return this.finalize(mtx, options); + return { + mtx: await this.finalize(mtx, options), + errors + }; } /** * Make a batch transaction with multiple actions. * @param {Array} actions * @param {Object} options - * @returns {Promise} + * @returns {Promise} */ async createBatch(actions, options) { @@ -4104,20 +4169,23 @@ class Wallet extends EventEmitter { * Create and send a batch transaction with multiple actions. * @param {Array} actions * @param {Object} options - * @returns {Promise} + * @returns {Promise} */ async _sendBatch(actions, options) { const passphrase = options ? options.passphrase : null; - const mtx = await this._createBatch(actions, options); - return this.sendMTX(mtx, passphrase); + const {mtx, errors} = await this._createBatch(actions, options); + return { + tx: await this.sendMTX(mtx, passphrase), + errors + }; } /** * Create and send a batch transaction with multiple actions. * @param {Array} actions * @param {Object} options - * @returns {Promise} + * @returns {Promise} */ async sendBatch(actions, options) { @@ -5462,7 +5530,7 @@ class Wallet extends EventEmitter { /** * Get current change address. - * @param {Number} [acct=0] + * @param {Number|String} [acct=0] * @returns {Promise
} */ diff --git a/test/wallet-auction-test.js b/test/wallet-auction-test.js index e5281fe4..33ae3de3 100644 --- a/test/wallet-auction-test.js +++ b/test/wallet-auction-test.js @@ -17,6 +17,8 @@ const Covenant = require('../lib/primitives/covenant'); const {Resource} = require('../lib/dns/resource'); const {forEvent} = require('./util/common'); +/** @typedef {import('../lib/wallet/wallet')} Wallet */ + const network = Network.get('regtest'); const { treeInterval, @@ -637,13 +639,16 @@ describe('Wallet Auction', function() { }); describe('Batch TXs', function() { - let wallet, receive; + /** @type {Wallet} */ + let wallet; + let receive; const hardFee = 12345; const name1 = rules.grindName(3, 0, network); const name2 = rules.grindName(4, 0, network); const name3 = rules.grindName(5, 0, network); const name4 = rules.grindName(6, 0, network); + const name5 = rules.grindName(7, 0, network); const res1 = Resource.fromJSON({records: [{type: 'TXT', txt: ['one']}]}); const res2 = Resource.fromJSON({records: [{type: 'TXT', txt: ['two']}]}); @@ -696,11 +701,11 @@ describe('Wallet Auction', function() { }); it('should create multiple OPENs with options', async () => { - const mtx = await wallet.createBatch( + const {mtx, errors} = await wallet.createBatch( [ - ['OPEN', name1], - ['OPEN', name2], - ['OPEN', name3] + { type: 'OPEN', args: [name1] }, + { type: 'OPEN', args: [name2] }, + { type: 'OPEN', args: [name3] } ], { hardFee @@ -708,8 +713,9 @@ describe('Wallet Auction', function() { ); assert(uniqueAddrs(mtx)); - + assert.strictEqual(errors.length, 0); assert.strictEqual(mtx.outputs.length, 4); + let opens = 0; for (const output of mtx.outputs) { if (output.covenant.type === rules.types.OPEN) @@ -730,72 +736,176 @@ describe('Wallet Auction', function() { await assert.rejects( wallet.sendBatch( [ - ['OPEN', 'google'], - ['OPEN', name2], - ['OPEN', name3] + { type: 'OPEN', args: ['google'] }, + { type: 'OPEN', args: [name2] }, + { type: 'OPEN', args: [name3] } ] ), {message: 'Name is reserved: google.'} ); }); + it('should fail if one action is invalid: OPEN reserved (partial)', async () => { + const {mtx, errors} = await wallet.createBatch([ + { type: 'OPEN', args: ['google'], id: 'google-id' }, + { type: 'OPEN', args: [name2] }, + { type: 'OPEN', args: [name3] } + ], { + partialFailure: true + }); + + assert.strictEqual(errors.length, 1); + assert.deepStrictEqual(errors[0], { + id: 'google-id', + type: 'OPEN', + message: 'Name is reserved: google.', + error: new Error('Name is reserved: google.') + }); + + assert.strictEqual(mtx.outputs.length, 3); + }); + it('should fail if one action is invalid: OPEN duplicated', async () => { await assert.rejects( wallet.sendBatch( [ - ['OPEN', name1], - ['OPEN', name1], - ['OPEN', name3] + { type: 'OPEN', args: [name1] }, + { type: 'OPEN', args: [name1] }, + { type: 'OPEN', args: [name3] } ] ), {message: 'Duplicate name with exclusive action.'} ); + + await assert.rejects( + wallet.sendBatch( + [ + { type: 'OPEN', args: [name1] }, + { type: 'OPEN', args: [name1] }, + { type: 'OPEN', args: [name3] } + ], { + partialFailure: true + } + ), + {message: 'Duplicate name with exclusive action.'} + ); + }); + + it('should fail if one action is invalid: OPEN duplicated (partial)', async () => { + await assert.rejects( + wallet.sendBatch( + [ + { type: 'OPEN', args: [name1] }, + { type: 'OPEN', args: [name1] }, + { type: 'OPEN', args: [name3] } + ], { + partialFailure: true + } + ), + {message: 'Duplicate name with exclusive action.'} + ); }); it('should fail if one action is invalid: REVEAL before bid', async () => { await assert.rejects( wallet.sendBatch( [ - ['OPEN', name1], - ['OPEN', name1], - ['OPEN', name3] + { type: 'OPEN', args: [name1] }, + { type: 'OPEN', args: [name1] }, + { type: 'OPEN', args: [name3] } ] ), {message: 'Duplicate name with exclusive action.'} ); }); + it('should fail if one action is invalid: REVEAL before bid (partial)', async () => { + await assert.rejects( + wallet.sendBatch( + [ + { type: 'OPEN', args: [name1] }, + { type: 'OPEN', args: [name1] }, + { type: 'OPEN', args: [name3] } + ], { + partialFailure: true + } + ), + {message: 'Duplicate name with exclusive action.'} + ); + }); + it('should fail if one action is invalid: BID early', async () => { await assert.rejects( wallet.sendBatch( [ - ['BID', name1, 1, 1], - ['OPEN', name2], - ['OPEN', name3] + { type: 'BID', args: [name1, 1, 1] }, + { type: 'OPEN', args: [name2] }, + { type: 'OPEN', args: [name3] } ] ), {message: `Name has not reached the bidding phase yet: ${name1}.`} ); }); + it('should fail if one action is invalid: BID early (partial)', async () => { + const {mtx, errors} = await wallet.createBatch( [ + { type: 'BID', args: [name1, 1, 1], id: name1 }, + { type: 'OPEN', args: [name2] }, + { type: 'OPEN', args: [name3] } + ], { + partialFailure: true + }); + + assert.strictEqual(errors.length, 1); + assert.deepStrictEqual(errors[0], { + id: name1, + type: 'BID', + message: `Name has not reached the bidding phase yet: ${name1}.`, + error: new Error(`Name has not reached the bidding phase yet: ${name1}.`) + }); + + assert.strictEqual(mtx.outputs.length, 3); + }); + it('should fail if one action is invalid: wrong arguments', async () => { await assert.rejects( wallet.sendBatch( [ - ['BID', name1, 21000000], - ['OPEN', name2], - ['OPEN', name3] + { type: 'BID', args: [name1, 21000000] }, + { type: 'OPEN', args: [name2] }, + { type: 'OPEN', args: [name3] } ] ), {message: 'Bad arguments for BID.'} ); }); + it('should fail if one action is invalid: wrong arguments (partial)', async () => { + const {mtx, errors} = await wallet.createBatch([ + { type: 'BID', args: [name1, 21000000] }, + { type: 'OPEN', args: [name2] }, + { type: 'OPEN', args: [name3] } + ], { + partialFailure: true + }); + + assert.strictEqual(errors.length, 1); + assert.strictEqual(errors[0].error.message, 'Bad arguments for BID.'); + delete errors[0].error; + + assert.deepStrictEqual(errors[0], { + id: null, + type: 'BID', + message: 'Bad arguments for BID.' + }); + assert.strictEqual(mtx.outputs.length, 3); + }); + it('should fail if one action is invalid: REVEAL all before bid', async () => { await assert.rejects( wallet.sendBatch( [ - ['REVEAL'] + { type: 'REVEAL' } ] ), {message: 'Nothing to do.'} @@ -806,7 +916,7 @@ describe('Wallet Auction', function() { await assert.rejects( wallet.sendBatch( [ - ['REDEEM'] + { type: 'REDEEM' } ] ), {message: 'Nothing to do.'} @@ -818,69 +928,125 @@ describe('Wallet Auction', function() { await assert.rejects( wallet.sendBatch( [ - ['OPEN', name1], - ['OPEN', name1], - ['OPEN', name3], - ['NONE', addr, 1] + { type: 'OPEN', args: [name1] }, + { type: 'OPEN', args: [name1] }, + { type: 'OPEN', args: [name3] }, + { type: 'NONE', args: [addr, 1] } ] ), {message: 'Output is dust.'} ); }); + it('should fail if one action is invalid: NONE below dust (partial)', async () => { + const addr = Address.fromProgram(0, Buffer.alloc(20, 0x01)).toString('regtest'); + const {mtx, errors} = await wallet.createBatch( + [ + { type: 'OPEN', args: [name1] }, + { type: 'OPEN', args: [name3] }, + { type: 'NONE', args: [addr, 1] } + ], + { + partialFailure: true + } + ); + + assert.strictEqual(errors.length, 1); + assert.deepStrictEqual(errors[0], { + id: null, + type: 'NONE', + message: 'Output is dust.', + error: new Error('Output is dust.') + }); + + assert.strictEqual(mtx.outputs.length, 3); + }); + it('should fail if one action is invalid: unknown action', async () => { await assert.rejects( wallet.sendBatch( [ - ['OPEN', name1], - ['OPEN', name1], - ['OPEN', name3], - ['open', name4] + { type: 'OPEN', args: [name1] }, + { type: 'OPEN', args: [name1] }, + { type: 'OPEN', args: [name3] }, + { type: 'open', args: [name4] } ] ), {message: 'Unknown action type: open'} ); }); + it('should fail if one action is invalid: unknown action (partial)', async () => { + const {mtx, errors} = await wallet.createBatch([ + { type: 'OPEN', args: [name1] }, + { type: 'OPEN', args: [name3] }, + { type: 'open', args: [name4] } + ], { + partialFailure: true + }); + + assert.strictEqual(errors.length, 1); + assert.deepStrictEqual(errors[0], { + id: null, + type: 'open', + message: 'Unknown action type: open', + error: new Error('Unknown action type: open') + }); + + assert.strictEqual(mtx.outputs.length, 3); + }); + describe('Complete auction and diverse-action batches', function() { const addr = Address.fromProgram(0, Buffer.alloc(20, 0x01)).toString('regtest'); + it('3 OPENs and 1 NONE', async () => { - const tx = await wallet.sendBatch( + const {tx, errors} = await wallet.sendBatch( [ - ['OPEN', name1], - ['OPEN', name2], - ['OPEN', name3], - ['NONE', addr, 10000] + { type: 'OPEN', args: [name1] }, + { type: 'OPEN', args: [name2] }, + { type: 'OPEN', args: [name3] }, + { type: 'NONE', args: [addr, 10000] } ] ); assert(uniqueAddrs(tx)); + assert.strictEqual(errors.length, 0); await mineBlocks(treeInterval + 1); }); it('4 BIDs', async () => { - const tx = await wallet.sendBatch( + const {tx, errors} = await wallet.sendBatch( [ - ['BID', name1, 10000, 20000], - ['BID', name1, 10001, 20000], // self-snipe! - ['BID', name2, 30000, 40000], - ['BID', name3, 50000, 60000] - ] + { type: 'BID', args: [name1, 10000, 20000] }, + { type: 'BID', args: [name1, 10001, 20000] }, // self-snipe! + { type: 'BID', args: [name2, 30000, 40000] }, + { type: 'BID', args: [name3, 50000, 60000] }, + // Handle failed index increments. + { type: 'BID', args: ['random', -100, -1]}, + { type: 'BID', args: ['random', -100, -1]}, + { type: 'BID', args: ['random', -100]}, + { type: 'OPEN', args: [name5]} + ], { + partialFailure: true + } ); assert(uniqueAddrs(tx)); + assert.strictEqual(errors.length, 3); await mineBlocks(biddingPeriod); }); it('REVEAL all', async () => { // Don't send this one - const revealAll = await wallet.createBatch( + const {mtx: revealAll, errors} = await wallet.createBatch( [ - ['REVEAL'] + { type: 'REVEAL' } ] ); assert.strictEqual(revealAll.outputs.length, 5); + assert.strictEqual(errors.length, 0); + let reveals = 0; for (const output of revealAll.outputs) { if (output.covenant.type === rules.types.REVEAL) @@ -890,20 +1056,21 @@ describe('Wallet Auction', function() { }); it('2 REVEALs then 1 REVEAL', async () => { - const tx = await wallet.sendBatch( + const {tx, errors} = await wallet.sendBatch( [ - ['REVEAL', name1], - ['REVEAL', name2] + { type: 'REVEAL', args: [name1] }, + { type: 'REVEAL', args: [name2] } ] ); assert(uniqueAddrs(tx)); + assert.strictEqual(errors.length, 0); // No "could not resolve preferred inputs" error // because names are being revealed individually. await wallet.sendBatch( [ - ['REVEAL', name3] + { type: 'REVEAL', args: [name3] } ] ); await mineBlocks(revealPeriod); @@ -911,13 +1078,15 @@ describe('Wallet Auction', function() { it('REDEEM all', async () => { // Don't send this one - const redeemAll = await wallet.createBatch( + const {mtx: redeemAll, errors} = await wallet.createBatch( [ - ['REDEEM'] + { type: 'REDEEM' } ] ); assert.strictEqual(redeemAll.outputs.length, 2); + assert.strictEqual(errors.length, 0); + let redeems = 0; for (const output of redeemAll.outputs) { if (output.covenant.type === rules.types.REDEEM) @@ -928,17 +1097,18 @@ describe('Wallet Auction', function() { it('3 REGISTERs, 1 REDEEM and 1 OPEN', async () => { // Complete all 4 bids win and/or lose in one TX - const batch1 = await wallet.sendBatch( + const {tx: batch1, errors} = await wallet.sendBatch( [ - ['OPEN', name4], - ['REDEEM', name1], - ['UPDATE', name1, res1], - ['UPDATE', name2, res2], - ['UPDATE', name3, res3] + { type: 'OPEN', args: [name4] }, + { type: 'REDEEM', args: [name1] }, + { type: 'UPDATE', args: [name1, res1] }, + { type: 'UPDATE', args: [name2, res2] }, + { type: 'UPDATE', args: [name3, res3] } ] ); assert(uniqueAddrs(batch1)); + assert.strictEqual(errors.length, 0); // Unlinked covenant (OPEN) was listed first but // should be sorted last with the change output (NONE). @@ -953,16 +1123,18 @@ describe('Wallet Auction', function() { version: 31, hash: Buffer.from([1, 2, 3]) }); - const tx = await wallet.sendBatch( + + const {tx, errors} = await wallet.sendBatch( [ - ['TRANSFER', name1, nullAddr], - ['TRANSFER', name2, nullAddr], - ['TRANSFER', name3, nullAddr], - ['BID', name4, 70000, 80000] + { type: 'TRANSFER', args: [name1, nullAddr] }, + { type: 'TRANSFER', args: [name2, nullAddr] }, + { type: 'TRANSFER', args: [name3, nullAddr] }, + { type: 'BID', args: [name4, 70000, 80000] } ] ); assert(uniqueAddrs(tx)); + assert.strictEqual(errors.length, 0); // True for regtest but not mainnet, // should allow both REVEAL and FINALIZE @@ -972,16 +1144,17 @@ describe('Wallet Auction', function() { }); it('1 FINALIZE, 1 CANCEL, 1 REVOKE and 1 REVEAL', async () => { - const tx = await wallet.sendBatch( + const {tx, errors} = await wallet.sendBatch( [ - ['FINALIZE', name1], - ['CANCEL', name2], - ['REVOKE', name3], - ['REVEAL', name4] + { type: 'FINALIZE', args: [name1] }, + { type: 'CANCEL', args: [name2] }, + { type: 'REVOKE', args: [name3] }, + { type: 'REVEAL', args: [name4] } ] ); assert(uniqueAddrs(tx)); + assert.strictEqual(errors.length, 0); // Should allow for both REGISTER and re-open revoked name assert(auctionMaturity > revealPeriod); @@ -990,14 +1163,15 @@ describe('Wallet Auction', function() { }); it('1 revoked name re-OPEN and 1 REGISTER', async () => { - const batch2 = await wallet.sendBatch( + const {tx: batch2, errors} = await wallet.sendBatch( [ - ['OPEN', name3], // and the cycle begins again... - ['UPDATE', name4, res4] + { type: 'OPEN', args: [name3] }, // and the cycle begins again... + { type: 'UPDATE', args: [name4, res4] } ] ); assert(uniqueAddrs(batch2)); + assert.strictEqual(errors.length, 0); // Linked covenant (UPDATE) was listed last but should be sorted first. assert.strictEqual(batch2.outputs[0].covenant.type, rules.types.REGISTER); @@ -1010,12 +1184,13 @@ describe('Wallet Auction', function() { const actions = []; for (let i = 0; i < 10; i++) { const addr = Address.fromProgram(0, Buffer.alloc(20, i + 1)); - actions.push(['NONE', addr, 10000]); + actions.push({ type: 'NONE', args: [addr, 10000] }); } - const batch = await wallet.sendBatch(actions, {hardFee: 1000}); + const {tx: batch, errors} = await wallet.sendBatch(actions, {hardFee: 1000}); assert.strictEqual(batch.outputs.length, 11); + assert.strictEqual(errors.length, 0); // Mine to some other wallet so reward doesn't affect our balance receive = new Address(); @@ -1056,13 +1231,15 @@ describe('Wallet Auction', function() { }); it('2 RENEW', async () => { - await wallet.sendBatch( + const {errors} = await wallet.sendBatch( [ - ['RENEW', name2], - ['RENEW', name4] + { type: 'RENEW', args: [name2] }, + { type: 'RENEW', args: [name4] } ] ); + assert.strictEqual(errors.length, 0); + await mineBlocks(1); const ns1 = await chain.db.getNameStateByName(name1); const ns2 = await chain.db.getNameStateByName(name2); @@ -1094,14 +1271,16 @@ describe('Wallet Auction', function() { const wtxs = await wallet.toDetails(txs); for (const wtx of wtxs) { - for (const output of wtx.outputs) + for (const output of wtx.outputs) { if ( output.path && output.path.account === 0 && output.path.branch === 0 // receive ) { addrIndexes[output.path.index]++; } + } } + // Ensure every receive address was used at least once assert(addrIndexes.indexOf(0) === -1); }); @@ -1130,14 +1309,16 @@ describe('Wallet Auction', function() { it('should OPEN', async () => { name = rules.grindName(4, chain.height, network); - await wallet.sendBatch([['OPEN', name]]); + await wallet.sendBatch([ + { type: 'OPEN', args: [name] } + ]); await mineBlocks(treeInterval + 1); }); it('should not batch too many BIDs', async () => { const batch = []; for (let i = 201; i > 0; i--) - batch.push(['BID', name, i * 1000, i * 1000]); + batch.push({ type: 'BID', args: [name, i * 1000, i * 1000] }); await assert.rejects( wallet.sendBatch(batch), @@ -1148,19 +1329,19 @@ describe('Wallet Auction', function() { it('should batch BIDs', async () => { let batch = []; for (let i = 200; i > 0; i--) - batch.push(['BID', name, i * 1000, i * 1000]); + batch.push({ type: 'BID', args: [name, i * 1000, i * 1000] }); await wallet.sendBatch(batch); batch = []; for (let i = 200; i > 0; i--) - batch.push(['BID', name, i * 1001, i * 1001]); + batch.push({ type: 'BID', args: [name, i * 1001, i * 1001] }); await wallet.sendBatch(batch); batch = []; for (let i = 200; i > 0; i--) - batch.push(['BID', name, i * 1002, i * 1002]); + batch.push({ type: 'BID', args: [name, i * 1002, i * 1002] }); await wallet.sendBatch(batch); batch = []; for (let i = 150; i > 0; i--) - batch.push(['BID', name, i * 1003, i * 1003]); + batch.push({ type: 'BID', args: [name, i * 1003, i * 1003] }); await wallet.sendBatch(batch); await mineBlocks(biddingPeriod); @@ -1175,7 +1356,7 @@ describe('Wallet Auction', function() { it('should create batch just under weight limit', async () => { // Start with the batch we would normally make - const mtx = await wallet.createBatch([['REVEAL']]); + const {mtx} = await wallet.createBatch([{ type: 'REVEAL' }]); // Find a spendable coin const coins = await wallet.getCoins(); @@ -1213,12 +1394,12 @@ describe('Wallet Auction', function() { it('should REVEAL all in several batches', async () => { let reveals = 0; - const mtx1 = await wallet.createBatch([['REVEAL']]); + const {mtx: mtx1} = await wallet.createBatch([{ type: 'REVEAL' }]); assert(mtx1.changeIndex >= 0); reveals += mtx1.outputs.length - 1; await wdb.addTX(mtx1.toTX()); - const mtx2 = await wallet.createBatch([['REVEAL']]); + const {mtx: mtx2} = await wallet.createBatch([{ type: 'REVEAL' }]); assert(mtx2.changeIndex >= 0); reveals += mtx2.outputs.length - 1; await wdb.addTX(mtx2.toTX()); @@ -1239,12 +1420,12 @@ describe('Wallet Auction', function() { it('should REDEEM all in several batches', async () => { let reveals = 0; - const mtx1 = await wallet.createBatch([['REDEEM']]); + const {mtx: mtx1} = await wallet.createBatch([{ type: 'REDEEM' }]); assert(mtx1.changeIndex >= 0); reveals += mtx1.outputs.length - 1; await wdb.addTX(mtx1.toTX()); - const mtx2 = await wallet.createBatch([['REDEEM']]); + const {mtx: mtx2} = await wallet.createBatch([{ type: 'REDEEM' }]); assert(mtx2.changeIndex >= 0); reveals += mtx2.outputs.length - 1; await wdb.addTX(mtx2.toTX()); @@ -1292,7 +1473,7 @@ describe('Wallet Auction', function() { it('should not batch too many OPENs', async () => { const batch = []; for (let i = 0; i < consensus.MAX_BLOCK_OPENS + 1; i++) - batch.push(['OPEN', names[i]]); + batch.push({ type: 'OPEN', args: [names[i]] }); await assert.rejects( wallet.createBatch(batch), @@ -1305,7 +1486,7 @@ describe('Wallet Auction', function() { for (let i = 1; i <= 8; i++) { const batch = []; for (let j = 1; j <= 100; j++) { - batch.push(['OPEN', names[count++]]); + batch.push({ type: 'OPEN', args: [names[count++]] }); } await wallet.sendBatch(batch); await mineBlocks(1); @@ -1319,8 +1500,8 @@ describe('Wallet Auction', function() { const batch = []; for (let j = 1; j <= 100; j++) { batch.push( - ['BID', names[count], 10000, 10000], - ['BID', names[count++], 10000, 10000] + { type: 'BID', args: [names[count], 10000, 10000]}, + { type: 'BID', args: [names[count++], 10000, 10000]} ); } await wallet.sendBatch(batch); @@ -1334,7 +1515,12 @@ describe('Wallet Auction', function() { let reveals = 0; for (;;) { try { - const tx = await wallet.sendBatch([['REVEAL'], ['REDEEM'], ['RENEW'], ['FINALIZE']]); + const {tx} = await wallet.sendBatch([ + { type: 'REVEAL' }, + { type: 'REDEEM' }, + { type: 'RENEW' }, + { type: 'FINALIZE' } + ]); reveals += tx.outputs.length - 1; // Don't count change output } catch (e) { assert.strictEqual(e.message, 'Nothing to do.'); @@ -1351,7 +1537,13 @@ describe('Wallet Auction', function() { let redeems = 0; for (;;) { try { - const tx = await wallet.sendBatch([['REVEAL'], ['REDEEM'], ['RENEW'], ['FINALIZE']]); + const {tx} = await wallet.sendBatch([ + { type: 'REVEAL' }, + { type: 'REDEEM' }, + { type: 'RENEW' }, + { type: 'FINALIZE' } + ]); + redeems += tx.outputs.length - 1; // Don't count change output } catch (e) { assert.strictEqual(e.message, 'Nothing to do.'); @@ -1367,7 +1559,7 @@ describe('Wallet Auction', function() { for (let i = 1; i <= 8; i++) { const batch = []; for (let j = 1; j <= 100; j++) { - batch.push(['UPDATE', names[count++], new Resource()]); + batch.push({ type: 'UPDATE', args: [names[count++], new Resource()]}); } await wallet.sendBatch(batch); await mineBlocks(1); @@ -1390,7 +1582,7 @@ describe('Wallet Auction', function() { it('should not batch too many UPDATEs', async () => { const batch = []; for (let i = 0; i < consensus.MAX_BLOCK_UPDATES + 1; i++) - batch.push(['UPDATE', names[i], new Resource()]); + batch.push({ type: 'UPDATE', args: [names[i], new Resource()]}); await assert.rejects( wallet.createBatch(batch), @@ -1406,7 +1598,12 @@ describe('Wallet Auction', function() { ); await assert.rejects( - wallet.sendBatch([['REVEAL'], ['REDEEM'], ['RENEW'], ['FINALIZE']]), + wallet.sendBatch([ + { type: 'REVEAL' }, + { type: 'REDEEM' }, + { type: 'RENEW' }, + { type: 'FINALIZE' } + ]), {message: 'Nothing to do.'} ); }); @@ -1414,7 +1611,7 @@ describe('Wallet Auction', function() { it('should not batch too many RENEWs', async () => { const batch = []; for (let i = 0; i < consensus.MAX_BLOCK_RENEWALS + 1; i++) - batch.push(['RENEW', names[i]]); + batch.push({ type: 'RENEW', args: [names[i]]}); await assert.rejects( wallet.createBatch(batch), @@ -1427,7 +1624,12 @@ describe('Wallet Auction', function() { let renewals = 0; for (;;) { - const tx = await wallet.sendBatch([['REVEAL'], ['REDEEM'], ['RENEW'], ['FINALIZE']]); + const {tx} = await wallet.sendBatch([ + { type: 'REVEAL' }, + { type: 'REDEEM' }, + { type: 'RENEW' }, + { type: 'FINALIZE' } + ]); await mineBlocks(1); if (!renewals) { @@ -1446,7 +1648,7 @@ describe('Wallet Auction', function() { it('should not batch too many TRANSFERs', async () => { const batch = []; for (const name of names) - batch.push(['TRANSFER', name, new Address()]); + batch.push({ type: 'TRANSFER', args: [name, new Address()]}); await assert.rejects( wallet.createBatch(batch), @@ -1460,7 +1662,7 @@ describe('Wallet Auction', function() { for (let i = 1; i <= 8; i++) { const batch = []; for (let j = 1; j <= 100; j++) { - batch.push(['TRANSFER', names[count++], addr]); + batch.push({ type: 'TRANSFER', args: [names[count++], addr]}); } await wallet.sendBatch(batch); await mineBlocks(1); @@ -1471,7 +1673,12 @@ describe('Wallet Auction', function() { await mineBlocks(network.names.lockupPeriod - 9); await assert.rejects( - wallet.sendBatch([['REVEAL'], ['REDEEM'], ['RENEW'], ['FINALIZE']]), + wallet.sendBatch([ + { type: 'REVEAL' }, + { type: 'REDEEM' }, + { type: 'RENEW' }, + { type: 'FINALIZE' } + ]), {message: 'Nothing to do.'} ); }); @@ -1481,7 +1688,12 @@ describe('Wallet Auction', function() { let finalizes = 0; for (;;) { - const tx = await wallet.sendBatch([['REVEAL'], ['REDEEM'], ['RENEW'], ['FINALIZE']]); + const {tx} = await wallet.sendBatch([ + { type: 'REVEAL' }, + { type: 'REDEEM' }, + { type: 'RENEW' }, + { type: 'FINALIZE' } + ]); await mineBlocks(1); finalizes += tx.outputs.length - 1; // Don't count change output @@ -1493,7 +1705,12 @@ describe('Wallet Auction', function() { it('should have nothing to do', async () => { await assert.rejects( - wallet.sendBatch([['REVEAL'], ['REDEEM'], ['RENEW'], ['FINALIZE']]), + wallet.sendBatch([ + { type: 'REVEAL' }, + { type: 'REDEEM' }, + { type: 'RENEW' }, + { type: 'FINALIZE' } + ]), {message: 'Nothing to do.'} ); }); diff --git a/test/wallet-balance-test.js b/test/wallet-balance-test.js index 065f8c99..664a2c60 100644 --- a/test/wallet-balance-test.js +++ b/test/wallet-balance-test.js @@ -565,17 +565,17 @@ describe('Wallet Balance', function() { const {nextAddr} = getAheadAddr(account, ahead); await primary.sendBatch([ - ['OPEN', name1], - ['OPEN', name2] + { type: 'OPEN', args: [name1] }, + { type: 'OPEN', args: [name2] } ]); await mineBlocks(openingPeriod); const txOpts = { hardFee: HARD_FEE }; // all three bids are there. - const bidMTX = await wallet.createBatch([ - ['BID', name1, BID_AMOUNT_1, BLIND_AMOUNT_1], - ['BID', name2, BID_AMOUNT_2, BLIND_AMOUNT_2] + const {mtx: bidMTX} = await wallet.createBatch([ + { type: 'BID', args: [name1, BID_AMOUNT_1, BLIND_AMOUNT_1] }, + { type: 'BID', args: [name2, BID_AMOUNT_2, BLIND_AMOUNT_2] } ], txOpts); assert.strictEqual(bidMTX.outputs[0].covenant.type, types.BID); @@ -604,16 +604,16 @@ describe('Wallet Balance', function() { await primary.sendReveal(name2); await wallet.sendBatch([ - ['REVEAL', name1], - ['REVEAL', name2] + { type: 'REVEAL', args: [name1] }, + { type: 'REVEAL', args: [name2] } ], txOpts); await mineBlocks(revealPeriod); if (register !== false) { await wallet.sendBatch([ - ['UPDATE', name1, EMPTY_RS], - ['UPDATE', name2, EMPTY_RS] + { type: 'UPDATE', args: [name1, EMPTY_RS] }, + { type: 'UPDATE', args: [name2, EMPTY_RS] } ], { hardFee: HARD_FEE }); @@ -1018,9 +1018,9 @@ describe('Wallet Balance', function() { const {nextAddr} = getAheadAddr(account, ahead); const txOpts = { hardFee: HARD_FEE }; - const bidMTX = await wallet.createBatch([ - ['BID', name, BID_AMOUNT_1, BLIND_AMOUNT_1], - ['BID', name, BID_AMOUNT_2, BLIND_AMOUNT_2] + const {mtx: bidMTX} = await wallet.createBatch([ + { type: 'BID', args: [name, BID_AMOUNT_1, BLIND_AMOUNT_1] }, + { type: 'BID', args: [name, BID_AMOUNT_2, BLIND_AMOUNT_2] } ], txOpts); assert.strictEqual(bidMTX.outputs[0].covenant.type, types.BID); @@ -1111,9 +1111,9 @@ describe('Wallet Balance', function() { const {nextAddr} = getAheadAddr(account, ahead); const txOpts = { hardFee: HARD_FEE }; - const bidMTX = await primary.createBatch([ - ['BID', name, BID_AMOUNT_1, BLIND_AMOUNT_1], - ['BID', name, BID_AMOUNT_2, BLIND_AMOUNT_2] + const {mtx: bidMTX} = await primary.createBatch([ + { type: 'BID', args: [name, BID_AMOUNT_1, BLIND_AMOUNT_1] }, + { type: 'BID', args: [name, BID_AMOUNT_2, BLIND_AMOUNT_2] } ], txOpts); assert.strictEqual(bidMTX.outputs[0].covenant.type, types.BID); @@ -1208,9 +1208,9 @@ describe('Wallet Balance', function() { const addr1 = getAheadAddr(altAccount, -altAccount.lookahead); const addr2 = getAheadAddr(altAccount, ahead); - const bidMTX = await wallet.createBatch([ - ['BID', name, BID_AMOUNT_1, BLIND_AMOUNT_1], - ['BID', name, BID_AMOUNT_2, BLIND_AMOUNT_2] + const {mtx: bidMTX} = await wallet.createBatch([ + { type: 'BID', args: [name, BID_AMOUNT_1, BLIND_AMOUNT_1] }, + { type: 'BID', args: [name, BID_AMOUNT_2, BLIND_AMOUNT_2] } ], txOpts); assert.strictEqual(bidMTX.outputs[0].covenant.type, types.BID); @@ -1393,9 +1393,9 @@ describe('Wallet Balance', function() { await primary.sendOpen(name, false); await mineBlocks(openingPeriod); - const bidMTX = await clone.createBatch([ - ['BID', name, BID_AMOUNT_1, BLIND_AMOUNT_1], - ['BID', name, BID_AMOUNT_2, BLIND_AMOUNT_2] + const {mtx: bidMTX} = await clone.createBatch([ + { type: 'BID', args: [name, BID_AMOUNT_1, BLIND_AMOUNT_1] }, + { type: 'BID', args: [name, BID_AMOUNT_2, BLIND_AMOUNT_2] } ], txOpts); assert.strictEqual(bidMTX.outputs[0].covenant.type, types.BID); @@ -1511,8 +1511,8 @@ describe('Wallet Balance', function() { await mineBlocks(openingPeriod); await wallet.sendBatch([ - ['BID', name, BID_AMOUNT_1, BLIND_AMOUNT_1], - ['BID', name, BID_AMOUNT_2, BLIND_AMOUNT_2] + { type: 'BID', args: [name, BID_AMOUNT_1, BLIND_AMOUNT_1] }, + { type: 'BID', args: [name, BID_AMOUNT_2, BLIND_AMOUNT_2] } ], txOpts); await mineBlocks(biddingPeriod); }; @@ -1738,8 +1738,8 @@ describe('Wallet Balance', function() { await primary.sendOpen(name, false); await mineBlocks(openingPeriod); await primary.sendBatch([ - ['BID', name, BID_AMOUNT_1, BLIND_AMOUNT_1], - ['BID', name, BID_AMOUNT_2, BLIND_AMOUNT_2] + { type: 'BID', args: [name, BID_AMOUNT_1, BLIND_AMOUNT_1] }, + { type: 'BID', args: [name, BID_AMOUNT_2, BLIND_AMOUNT_2] } ]); await mineBlocks(biddingPeriod); }; @@ -1840,8 +1840,8 @@ describe('Wallet Balance', function() { const addr1 = getAheadAddr(cloneAccount, ahead); await primary.sendBatch([ - ['OPEN', name1], - ['OPEN', name2] + { type: 'OPEN', args: [name1] }, + { type: 'OPEN', args: [name2] } ]); await mineBlocks(openingPeriod); @@ -1852,9 +1852,9 @@ describe('Wallet Balance', function() { const txOpts = { hardFee: HARD_FEE }; // all three bids are there. - const bidMTX = await clone.createBatch([ - ['BID', name1, BID_AMOUNT_1, BLIND_AMOUNT_1], - ['BID', name2, BID_AMOUNT_2, BLIND_AMOUNT_2] + const {mtx: bidMTX} = await clone.createBatch([ + { type: 'BID', args: [name1, BID_AMOUNT_1, BLIND_AMOUNT_1] }, + { type: 'BID', args: [name2, BID_AMOUNT_2, BLIND_AMOUNT_2] } ], txOpts); assert.strictEqual(bidMTX.outputs[0].covenant.type, types.BID); @@ -1873,8 +1873,8 @@ describe('Wallet Balance', function() { await primary.sendReveal(name2); await clone.sendBatch([ - ['REVEAL', name1], - ['REVEAL', name2] + { type: 'REVEAL', args: [name1] }, + { type: 'REVEAL', args: [name2] } ], txOpts); await mineBlocks(revealPeriod + 1); @@ -1882,8 +1882,8 @@ describe('Wallet Balance', function() { const sendRedeems = async (wallet, clone, ahead) => { await clone.sendBatch([ - ['REDEEM', name1], - ['REDEEM', name2] + { type: 'REDEEM', args: [name1] }, + { type: 'REDEEM', args: [name2] } ], { hardFee: HARD_FEE }); @@ -1984,8 +1984,8 @@ describe('Wallet Balance', function() { const sendRedeems = async (wallet, clone, ahead) => { await clone.sendBatch([ - ['UPDATE', name1, EMPTY_RS], - ['UPDATE', name2, EMPTY_RS] + { type: 'UPDATE', args: [name1, EMPTY_RS] }, + { type: 'UPDATE', args: [name2, EMPTY_RS] } ], { hardFee: HARD_FEE }); @@ -2124,8 +2124,8 @@ describe('Wallet Balance', function() { const sendUpdates = async (wallet, clone) => { await clone.sendBatch([ - ['UPDATE', name1, EMPTY_RS], - ['UPDATE', name2, EMPTY_RS] + { type: 'UPDATE', args: [name1, EMPTY_RS] }, + { type: 'UPDATE', args: [name2, EMPTY_RS] } ], { hardFee: HARD_FEE }); @@ -2165,8 +2165,8 @@ describe('Wallet Balance', function() { const sendRevokes = async (wallet, clone) => { await clone.sendBatch([ - ['REVOKE', name1], - ['REVOKE', name2] + { type: 'REVOKE', args: [name1] }, + { type: 'REVOKE', args: [name2] } ], { hardFee: HARD_FEE }); @@ -2205,8 +2205,8 @@ describe('Wallet Balance', function() { const sendRenews = async (wallet, clone) => { await mineBlocks(treeInterval); await clone.sendBatch([ - ['RENEW', name1], - ['RENEW', name2] + { type: 'RENEW', args: [name1] }, + { type: 'RENEW', args: [name2] } ], { hardFee: HARD_FEE }); @@ -2244,8 +2244,8 @@ describe('Wallet Balance', function() { const sendTransfers = async (wallet, clone) => { await clone.sendBatch([ - ['TRANSFER', name1, await primary.receiveAddress()], - ['TRANSFER', name2, await primary.receiveAddress()] + { type: 'TRANSFER', args: [name1, await primary.receiveAddress()] }, + { type: 'TRANSFER', args: [name2, await primary.receiveAddress()] } ], { hardFee: HARD_FEE }); @@ -2281,8 +2281,8 @@ describe('Wallet Balance', function() { name2 = names[1]; await clone.sendBatch([ - ['TRANSFER', name1, await primary.receiveAddress()], - ['TRANSFER', name2, await primary.receiveAddress()] + { type: 'TRANSFER', args: [name1, await primary.receiveAddress()] }, + { type: 'TRANSFER', args: [name2, await primary.receiveAddress()] } ], { hardFee: HARD_FEE }); @@ -2292,8 +2292,8 @@ describe('Wallet Balance', function() { const sendFinalizes = async (wallet, clone) => { await clone.sendBatch([ - ['FINALIZE', name1], - ['FINALIZE', name2] + { type: 'FINALIZE', args: [name1] }, + { type: 'FINALIZE', args: [name2] } ], { hardFee: HARD_FEE }); @@ -2383,8 +2383,8 @@ describe('Wallet Balance', function() { name2 = names[1]; await clone.sendBatch([ - ['TRANSFER', name1, recv], - ['TRANSFER', name2, nextAddr] + { type: 'TRANSFER', args: [name1, recv] }, + { type: 'TRANSFER', args: [name2, nextAddr] } ], { hardFee: HARD_FEE }); @@ -2394,8 +2394,8 @@ describe('Wallet Balance', function() { const sendFinalizes = async (wallet, clone) => { await clone.sendBatch([ - ['FINALIZE', name1], - ['FINALIZE', name2] + { type: 'FINALIZE', args: [name1] }, + { type: 'FINALIZE', args: [name2] } ], { hardFee: HARD_FEE }); diff --git a/test/wallet-importnonce-test.js b/test/wallet-importnonce-test.js index 85c6d2b0..183f77e5 100644 --- a/test/wallet-importnonce-test.js +++ b/test/wallet-importnonce-test.js @@ -75,11 +75,11 @@ describe('Wallet Import Nonce', function () { it('should bid with sendbatch', async () => { const batch = [ - ['BID', NAME, BIDS[1].value, BIDS[1].lockup], - ['BID', NAME, BIDS[2].value, BIDS[2].lockup] + { type: 'BID', args: [NAME, BIDS[1].value, BIDS[1].lockup]}, + { type: 'BID', args: [NAME, BIDS[2].value, BIDS[2].lockup]} ]; - const bidTx = await walletA.sendBatch(batch); + const {tx: bidTx} = await walletA.sendBatch(batch); // Save address for importnonce later for (const output of bidTx.outputs) {