Merge PR #941 from 'nodech/batch-refactor'
This commit is contained in:
commit
a812f0bc8d
5 changed files with 519 additions and 234 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -3862,13 +3862,45 @@ class Wallet extends EventEmitter {
|
|||
}
|
||||
|
||||
/**
|
||||
* Make a batch transaction with multiple actions.
|
||||
* @param {Array} actions
|
||||
* @param {Object} options
|
||||
* @returns {Promise<MTX>}
|
||||
* @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<BatchResult>}
|
||||
*/
|
||||
|
||||
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<MTX>}
|
||||
* @returns {Promise<BatchResult>}
|
||||
*/
|
||||
|
||||
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<MTX>}
|
||||
* @returns {Promise<BatchResult>}
|
||||
*/
|
||||
|
||||
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<TX>}
|
||||
* @returns {Promise<BatchTXResult>}
|
||||
*/
|
||||
|
||||
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<TX>}
|
||||
* @returns {Promise<BatchTXResult>}
|
||||
*/
|
||||
|
||||
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<Address>}
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -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.'}
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue