diff --git a/.gitignore b/.gitignore index 0a75c5e5a..579191a6b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ docker_data/ docs/reference/ node_modules/ npm-debug.log +coverage/ +.nyc_output/ diff --git a/.npmignore b/.npmignore index 30ec5f154..d0c680d75 100644 --- a/.npmignore +++ b/.npmignore @@ -20,3 +20,4 @@ package-lock.json test/ webpack.*.js yarn.lock +eslint.config.cjs diff --git a/docs/release-notes/release-notes-8.x.md b/docs/release-notes/release-notes-8.x.md new file mode 100644 index 000000000..6f898cf2d --- /dev/null +++ b/docs/release-notes/release-notes-8.x.md @@ -0,0 +1,80 @@ +v8.x Release notes +================== + + + +- [v8.0.0](#v800) + * [How to Upgrade](#how-to-upgrade) + * [Notable Changes](#notable-changes) + + [Network](#network) + + [Wallet Changes](#wallet-changes) + - [Wallet HTTP API](#wallet-http-api) + - [Wallet/WalletDB API](#walletwalletdb-api) + + [hs-client Wallet](#hs-client-wallet) + * [Changelog](#changelog) + + + +# v8.0.0 +## How to Upgrade +**When upgrading to this version of hsd, you must pass `--chain-migrate=4` +and `--wallet-migrate=7` when you run it for the first time. +It is strongly recommended to back up your wallet before proceeding with the upgrade +([#925](https://github.com/handshake-org/hsd/pull/925), + [#928](https://github.com/handshake-org/hsd/pull/928))**. + +## Notable Changes + +### Network + +**End Airdrop soft fork has been included. ([#927](https://github.com/handshake-org/hsd/pull/927)) +Miners who want to support the soft-fork need to start signalling with `airstop` bit.** + +### Wallet Changes + +#### Wallet HTTP API + - `POST /wallet/:id/zap` response object has a new property: `zapped: number`, + indicating the number of transactions that were zapped + ([#920](https://github.com/handshake-org/hsd/pull/920)). + - `GET /wallet/:id/name/:name` now accepts an `own` parameter and only returns + the namestate when the wallet owns the name + ([#922](https://github.com/handshake-org/hsd/pull/922)). + - Introduce admin `POST /recalculate-balances`, useful if the post-migration + recalculation was not triggered and wallet balances are not correct + ([#926](https://github.com/handshake-org/hsd/pull/926)). + - The TX creation HTTP Endpoints now supports new values for the `selection` + property. These new strategies use database iterators instead of loading all + coins into RAM ([#928](https://github.com/handshake-org/hsd/pull/928)). + - `db-value` - This is a database alternative to `value` and new default. + - `db-age` - A database alternative `age`. + - `db-all` - A database alternative `all`. + - `db-sweepdust` - Select smallest coins first. + - Add `sweepdustMinValue` option for TX creation endpoints, default 1. + +#### Wallet/WalletDB API + - `Wallet.zap` now returns the number of transactions zapped instead of their hashes + ([#920](https://github.com/handshake-org/hsd/pull/920)). + +### hs-client Wallet + - `getName` now accepts an `options` object with an `own` property + ([#922](https://github.com/handshake-org/hsd/pull/922)). + +## Changelog + + - \[[`82cacf38`](https://github.com/handshake-org/hsd/commit/82cacf38)] - [#933](https://github.com/handshake-org/hsd/pull/933) - **SEMVER-MAJOR net/pkg**: Update network seeds, checkpoints and deps - (@nodech - Nodari Chkuaselidze) + - \[[`dee79a3c`](https://github.com/handshake-org/hsd/commit/dee79a3c)] - [#927](https://github.com/handshake-org/hsd/pull/927) - **SEMVER-MAJOR chain**: add airstop soft fork - (@rithvikvibhu - Rithvik Vibhu) + - \[[`f0a81dac`](https://github.com/handshake-org/hsd/commit/f0a81dac)] - [#928](https://github.com/handshake-org/hsd/pull/928) - **SEMVER-MAJOR wallet**: - Wallet coinselection - (@nodech - Nodari Chkuaselidze) + - \[[`5f11d622`](https://github.com/handshake-org/hsd/commit/5f11d622)] - [#925](https://github.com/handshake-org/hsd/pull/925) - **SEMVER-MAJOR migrations**: Add in progress data to migration - (@nodech - Nodari Chkuaselidze) + - \[[`e19f9fb4`](https://github.com/handshake-org/hsd/commit/e19f9fb4)] - [#930](https://github.com/handshake-org/hsd/pull/930) - **SEMVER-MINOR seeder**: allow passing custom prefix dir - (@rithvikvibhu - Rithvik Vibhu) + - \[[`18dcc5e1`](https://github.com/handshake-org/hsd/commit/18dcc5e1)] - [#926](https://github.com/handshake-org/hsd/pull/926) - **SEMVER-MINOR wallet**: recalculate-balances endpoint. - (@nodech - Nodari Chkuaselidze) + - \[[`31009340`](https://github.com/handshake-org/hsd/commit/31009340)] - [#922](https://github.com/handshake-org/hsd/pull/922) - **SEMVER-MINOR wallet-http**: Add own parameter to the getName. - (@nodech - Nodari Chkuaselidze) + - \[[`77e22dae`](https://github.com/handshake-org/hsd/commit/77e22dae)] - [#920](https://github.com/handshake-org/hsd/pull/920) - **SEMVER-MINOR wallet-http**: Return total number of transactions zapped. - (@nodech - Nodari Chkuaselidze) + - \[[`343525aa`](https://github.com/handshake-org/hsd/commit/343525aa)] - [#916](https://github.com/handshake-org/hsd/pull/916) - **SEMVER-MINOR wallet-http:** Wallet http fixes clean - (@nodech - Nodari Chkuaselidze) + - \[[`827769d4`](https://github.com/handshake-org/hsd/commit/827769d4)] - [#932](https://github.com/handshake-org/hsd/pull/932) - **wallet-http**: validate timeouts and make default explicit. - (@nodech - Nodari Chkuaselidze) + - \[[`85a1ada0`](https://github.com/handshake-org/hsd/commit/85a1ada0)] - [#929](https://github.com/handshake-org/hsd/pull/929) - **ci**: add nodejs v24 to the matrix. - (@nodech - Nodari Chkuaselidze) + - \[[`8df0724a`](https://github.com/handshake-org/hsd/commit/8df0724a)] - [#924](https://github.com/handshake-org/hsd/pull/924) - **net**: remove easyhandshake from mainnet seed nodes - (@pinheadmz - Matthew Zipkin) + - \[[`73533cdf`](https://github.com/handshake-org/hsd/commit/73533cdf)] - [#923](https://github.com/handshake-org/hsd/pull/923) - **misc**: update chain types - (@nodech - Nodari Chkuaselidze) + - \[[`ab2f5f84`](https://github.com/handshake-org/hsd/commit/ab2f5f84)] - [#921](https://github.com/handshake-org/hsd/pull/921) - **misc**: don't use public class fields usage. - (@nodech - Nodari Chkuaselidze) + - \[[`dd7249f6`](https://github.com/handshake-org/hsd/commit/dd7249f6)] - [#919](https://github.com/handshake-org/hsd/pull/919) - **docs**: backport release notes. - (@nodech - Nodari Chkuaselidze) + - \[[`886f6515`](https://github.com/handshake-org/hsd/commit/886f6515)] - [#918](https://github.com/handshake-org/hsd/pull/918) - **wallet-misc**: clean up types - (@nodech - Nodari Chkuaselidze) + - \[[`11bd81f5`](https://github.com/handshake-org/hsd/commit/11bd81f5)] - [#917](https://github.com/handshake-org/hsd/pull/917) - **ci**: Update ci and add node v22 - (@nodech - Nodari Chkuaselidze) diff --git a/lib/blockchain/chain.js b/lib/blockchain/chain.js index bb96ac36a..02768a7d6 100644 --- a/lib/blockchain/chain.js +++ b/lib/blockchain/chain.js @@ -38,7 +38,7 @@ const { /** @typedef {import('../types').Hash} Hash */ /** @typedef {import('../types').LockFlags} LockFlags */ -/** @typedef {import('@handshake-org/bfilter').BloomFilter} BloomFilter */ +/** @typedef {import('bfilter').BloomFilter} BloomFilter */ /** @typedef {import('../primitives/block')} Block */ /** @typedef {import('../primitives/tx')} TX */ /** @typedef {import('../primitives/txmeta')} TXMeta */ diff --git a/lib/blockchain/chaindb.js b/lib/blockchain/chaindb.js index 9e70ec784..6c0f07cda 100644 --- a/lib/blockchain/chaindb.js +++ b/lib/blockchain/chaindb.js @@ -39,7 +39,7 @@ const { /** @typedef {import('urkel').Proof} Proof */ /** @typedef {ReturnType} Batch */ -/** @typedef {import('@handshake-org/bfilter').BloomFilter} BloomFilter */ +/** @typedef {import('bfilter').BloomFilter} BloomFilter */ /** @typedef {import('../types').Hash} Hash */ /** @typedef {import('./chain').ChainOptions} ChainOptions */ /** @typedef {import('../primitives/tx')} TX */ diff --git a/lib/blockchain/common.js b/lib/blockchain/common.js index fae1ed3d4..7c3464373 100644 --- a/lib/blockchain/common.js +++ b/lib/blockchain/common.js @@ -6,7 +6,7 @@ 'use strict'; -/** @typedef {import('@handshake-org/bfilter').BloomFilter} BloomFilter */ +/** @typedef {import('bfilter').BloomFilter} BloomFilter */ /** @typedef {import('../types').LockFlags} LockFlags */ /** diff --git a/lib/mempool/mempool.js b/lib/mempool/mempool.js index b698c4f97..0e9446f15 100644 --- a/lib/mempool/mempool.js +++ b/lib/mempool/mempool.js @@ -10,7 +10,7 @@ const assert = require('bsert'); const path = require('path'); const EventEmitter = require('events'); const bdb = require('bdb'); -const {RollingFilter} = require('@handshake-org/bfilter'); +const {RollingFilter} = require('bfilter'); const Heap = require('bheep'); const {BufferMap, BufferSet} = require('buffer-map'); const random = require('bcrypto/lib/random'); diff --git a/lib/net/packets.js b/lib/net/packets.js index 0876e09fa..7efc4bcb9 100644 --- a/lib/net/packets.js +++ b/lib/net/packets.js @@ -14,7 +14,7 @@ const assert = require('bsert'); const bio = require('bufio'); const blake2b = require('bcrypto/lib/blake2b'); const UrkelProof = require('urkel').Proof; -const {BloomFilter} = require('@handshake-org/bfilter'); +const {BloomFilter} = require('bfilter'); const common = require('./common'); const util = require('../utils/util'); const bip152 = require('./bip152'); diff --git a/lib/net/peer.js b/lib/net/peer.js index f3eb28a41..7a154baf9 100644 --- a/lib/net/peer.js +++ b/lib/net/peer.js @@ -13,7 +13,7 @@ const {format} = require('util'); const tcp = require('btcp'); const dns = require('bdns'); const Logger = require('blgr'); -const {RollingFilter} = require('@handshake-org/bfilter'); +const {RollingFilter} = require('bfilter'); const {BufferMap} = require('buffer-map'); const Parser = require('./parser'); const Framer = require('./framer'); diff --git a/lib/net/pool.js b/lib/net/pool.js index fc1b64bc9..b0bd6ee1a 100644 --- a/lib/net/pool.js +++ b/lib/net/pool.js @@ -17,7 +17,7 @@ const List = require('blst'); const base32 = require('bcrypto/lib/encoding/base32'); const {BufferMap, BufferSet} = require('buffer-map'); const blake2b = require('bcrypto/lib/blake2b'); -const {BloomFilter, RollingFilter} = require('@handshake-org/bfilter'); +const {BloomFilter, RollingFilter} = require('bfilter'); const rng = require('bcrypto/lib/random'); const secp256k1 = require('bcrypto/lib/secp256k1'); const {siphash} = require('bcrypto/lib/siphash'); diff --git a/lib/node/fullnode.js b/lib/node/fullnode.js index 0e8bbdcb8..801fe7537 100644 --- a/lib/node/fullnode.js +++ b/lib/node/fullnode.js @@ -162,13 +162,15 @@ class FullNode extends Node { }); if (!this.config.bool('no-dns')) { + const publicHost = this.config.str('public-host'); + this.ns = new RootServer({ logger: this.logger, key: this.identityKey, host: this.config.str('ns-host'), port: this.config.uint('ns-port', this.network.nsPort), lookup: key => this.chain.db.tree.get(key), - publicHost: this.config.str('public-host'), + publicHost: this.config.str('ns-public-host', publicHost), noSig0: this.config.bool('no-sig0') }); diff --git a/lib/node/http.js b/lib/node/http.js index 754a09b8c..2d613bc6d 100644 --- a/lib/node/http.js +++ b/lib/node/http.js @@ -11,7 +11,7 @@ const path = require('path'); const {Server} = require('bweb'); const Validator = require('bval'); const base58 = require('bcrypto/lib/encoding/base58'); -const {BloomFilter} = require('@handshake-org/bfilter'); +const {BloomFilter} = require('bfilter'); const sha256 = require('bcrypto/lib/sha256'); const random = require('bcrypto/lib/random'); const {safeEqual} = require('bcrypto/lib/safe'); diff --git a/lib/primitives/block.js b/lib/primitives/block.js index ab6102c1a..54bdd377c 100644 --- a/lib/primitives/block.js +++ b/lib/primitives/block.js @@ -20,7 +20,7 @@ const Network = require('../protocol/network'); const util = require('../utils/util'); const {encoding} = bio; -/** @typedef {import('@handshake-org/bfilter').BloomFilter} BloomFilter */ +/** @typedef {import('bfilter').BloomFilter} BloomFilter */ /** @typedef {import('../types').BufioWriter} BufioWriter */ /** @typedef {import('../types').Hash} Hash */ /** @typedef {import('../types').Amount} AmountValue */ diff --git a/lib/primitives/covenant.js b/lib/primitives/covenant.js index 3ac9869d3..1d7deae70 100644 --- a/lib/primitives/covenant.js +++ b/lib/primitives/covenant.js @@ -14,7 +14,7 @@ const consensus = require('../protocol/consensus'); const {encoding} = bio; const {types, typesByVal} = rules; -/** @typedef {import('@handshake-org/bfilter').BloomFilter} BloomFilter */ +/** @typedef {import('bfilter').BloomFilter} BloomFilter */ /** @typedef {import('../types').Hash} Hash */ /** @typedef {import('../types').BufioWriter} BufioWriter */ /** @typedef {import('./address')} Address */ diff --git a/lib/primitives/merkleblock.js b/lib/primitives/merkleblock.js index e1ff939a1..e2a512031 100644 --- a/lib/primitives/merkleblock.js +++ b/lib/primitives/merkleblock.js @@ -18,7 +18,7 @@ const Headers = require('./headers'); const DUMMY = Buffer.from([0]); const {encoding} = bio; -/** @typedef {import('@handshake-org/bfilter').BloomFilter} BloomFilter */ +/** @typedef {import('bfilter').BloomFilter} BloomFilter */ /** @typedef {import('../types').Hash} Hash */ /** @typedef {import('../types').BufioWriter} BufioWriter */ /** @typedef {import('../coins/coinview')} CoinView */ diff --git a/lib/primitives/tx.js b/lib/primitives/tx.js index 49dca5e86..35b967448 100644 --- a/lib/primitives/tx.js +++ b/lib/primitives/tx.js @@ -28,7 +28,7 @@ const AirdropProof = require('../primitives/airdropproof'); const {encoding} = bio; const {hashType} = Script; -/** @typedef {import('@handshake-org/bfilter').BloomFilter} BloomFilter */ +/** @typedef {import('bfilter').BloomFilter} BloomFilter */ /** @typedef {import('../types').SighashType} SighashType */ /** @typedef {import('../types').Hash} Hash */ /** @typedef {import('../types').Amount} AmountValue */ diff --git a/lib/script/script.js b/lib/script/script.js index 6ce9148b6..1e9edbb3d 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -27,7 +27,7 @@ const opcodes = common.opcodes; const scriptTypes = common.types; const {encoding} = bio; -/** @typedef {import('@handshake-org/bfilter').BloomFilter} BloomFilter */ +/** @typedef {import('bfilter').BloomFilter} BloomFilter */ /** @typedef {import('../types').BufioWriter} BufioWriter */ /** @typedef {import('../types').SighashType} SighashType */ /** @typedef {import('../types').VerifyFlags} VerifyFlags */ diff --git a/lib/script/witness.js b/lib/script/witness.js index cff666bc3..7a4475775 100644 --- a/lib/script/witness.js +++ b/lib/script/witness.js @@ -17,7 +17,7 @@ const Stack = require('./stack'); const {encoding} = bio; const scriptTypes = common.types; -/** @typedef {import('@handshake-org/bfilter').BloomFilter} BloomFilter */ +/** @typedef {import('bfilter').BloomFilter} BloomFilter */ /** @typedef {import('../types').ScriptType} ScriptType */ /** @typedef {import('../types').BufioWriter} BufioWriter */ diff --git a/lib/wallet/http.js b/lib/wallet/http.js index 747f2f88a..87102e88b 100644 --- a/lib/wallet/http.js +++ b/lib/wallet/http.js @@ -1860,8 +1860,26 @@ class TransactionOptions { */ constructor(valid, network) { + this.rate = null; + this.maxFee = null; + this.selection = null; + this.sweepdustMinValue = null; + this.smart = null; + this.account = null; + this.locktime = null; + this.sort = null; + this.subtractFee = null; + this.subtractIndex = null; + this.depth = null; + this.paths = null; + this.passphrase = null; + this.hardFee = null; + this.blocks = null; + this.network = network || Network.primary; + this.outputs = []; + if (valid) - return this.fromValidator(valid, network); + this.fromValidator(valid, network); } /** @@ -1875,22 +1893,53 @@ class TransactionOptions { fromValidator(valid, network) { assert(valid); - this.rate = valid.u64('rate'); - this.maxFee = valid.u64('maxFee'); - this.selection = valid.str('selection'); - this.sweepdustMinValue = valid.u64('sweepdustMinValue'); - this.smart = valid.bool('smart'); - this.account = valid.str('account'); - this.locktime = valid.u64('locktime'); - this.sort = valid.bool('sort'); - this.subtractFee = valid.bool('subtractFee'); - this.subtractIndex = valid.i32('subtractIndex'); - this.depth = valid.u32(['confirmations', 'depth']); - this.paths = valid.bool('paths'); - this.passphrase = valid.str('passphrase'); - this.hardFee = valid.u64('hardFee'), - this.blocks = valid.u32('blocks'); - this.outputs = []; + if (network) + this.network = network; + + if (valid.has('rate')) + this.rate = valid.u64('rate'); + + if (valid.has('maxFee')) + this.maxFee = valid.u64('maxFee'); + + if (valid.has('selection')) + this.selection = valid.str('selection'); + + if (valid.has('sweepdustMinValue')) + this.sweepdustMinValue = valid.u64('sweepdustMinValue'); + + if (valid.has('smart')) + this.smart = valid.bool('smart'); + + if (valid.has('account')) + this.account = valid.str('account'); + + if (valid.has('locktime')) + this.locktime = valid.u64('locktime'); + + if (valid.has('sort')) + this.sort = valid.bool('sort'); + + if (valid.has('subtractFee')) + this.subtractFee = valid.bool('subtractFee'); + + if (valid.has('subtractIndex')) + this.subtractIndex = valid.i32('subtractIndex'); + + if (valid.has('confirmations') || valid.has('depth')) + this.depth = valid.u32(['confirmations', 'depth']); + + if (valid.has('paths')) + this.paths = valid.bool('paths'); + + if (valid.has('passphrase')) + this.passphrase = valid.str('passphrase'); + + if (valid.has('hardFee')) + this.hardFee = valid.u64('hardFee'); + + if (valid.has('blocks')) + this.blocks = valid.u32('blocks'); if (valid.has('outputs')) { const outputs = valid.array('outputs'); @@ -1929,7 +1978,7 @@ class TransactionOptions { */ static fromValidator(valid, network) { - return new this().fromValidator(valid, network); + return new this(valid, network); } } diff --git a/lib/wallet/nodeclient.js b/lib/wallet/nodeclient.js index 87ac153d8..b174975b2 100644 --- a/lib/wallet/nodeclient.js +++ b/lib/wallet/nodeclient.js @@ -10,7 +10,7 @@ const assert = require('bsert'); const blacklist = require('bsock/lib/blacklist'); const AsyncEmitter = require('bevent'); -/** @typedef {import('@handshake-org/bfilter').BloomFilter} BloomFilter */ +/** @typedef {import('bfilter').BloomFilter} BloomFilter */ /** @typedef {import('../types').Hash} Hash */ /** @typedef {import('../primitives/tx')} TX */ /** @typedef {import('../primitives/claim')} Claim */ diff --git a/lib/wallet/rpc.js b/lib/wallet/rpc.js index a73830b29..61ae0004d 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 40ea10c0a..af9e5c607 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -1770,8 +1770,10 @@ class Wallet extends EventEmitter { mtx.outputs.push(output); - if (await this.txdb.isDoubleOpen(mtx)) + if (await this.txdb.isDoubleOpen(mtx)) { + mtx.outputs.pop(); throw new Error(`Already sent an open for: ${name}.`); + } return mtx; } @@ -3861,14 +3863,46 @@ class Wallet extends EventEmitter { } } + /** + * @typedef {Object} BatchAction + * @property {String} type - Action type. + * @property {String} [id] - Action ID. + * @property {Array} [args] - Arguments for the action. + */ + + /** + * @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 {Array} actions + * @param {BatchAction[]} actions * @param {Object} options - * @returns {Promise} + * @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) { + async makeBatch(actions, options = {}) { assert(Array.isArray(actions)); assert(actions.length, 'Batches require at least one action.'); @@ -3885,12 +3919,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.type === b.type) + return 0; - switch (b[0]) { + switch (b.type) { case 'REVEAL': case 'REDEEM': case 'UPDATE': @@ -3909,110 +3944,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) @@ -4065,27 +4132,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) { @@ -4101,20 +4171,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) { @@ -5459,7 +5532,7 @@ class Wallet extends EventEmitter { /** * Get current change address. - * @param {Number} [acct=0] + * @param {Number|String} [acct=0] * @returns {Promise
} */ diff --git a/lib/wallet/walletdb.js b/lib/wallet/walletdb.js index 1ac57569a..bd160d9d9 100644 --- a/lib/wallet/walletdb.js +++ b/lib/wallet/walletdb.js @@ -10,7 +10,7 @@ const assert = require('bsert'); const path = require('path'); const EventEmitter = require('events'); const bio = require('bufio'); -const {BloomFilter} = require('@handshake-org/bfilter'); +const {BloomFilter} = require('bfilter'); const {Lock, MapLock} = require('bmutex'); const bdb = require('bdb'); const Logger = require('blgr'); diff --git a/package-lock.json b/package-lock.json index 3a83e9978..3195be916 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,21 +9,21 @@ "version": "8.99.0", "license": "MIT", "dependencies": { - "@handshake-org/bfilter": "~2.3.0", "bcfg": "~0.2.2", - "bcrypto": "~5.4.0", + "bcrypto": "~5.5.2", "bcurl": "~0.2.1", "bdb": "~1.6.2", "bdns": "~0.1.5", "bevent": "~0.1.6", "bfile": "~0.2.3", + "bfilter": "~2.4.0", "bheep": "~0.1.6", "binet": "~0.3.9", "blgr": "~0.2.1", "blru": "~0.1.8", "blst": "~0.1.6", "bmutex": "~0.1.8", - "bns": "~0.15.0", + "bns": "~0.16.0", "bsert": "~0.0.13", "bsock": "~0.1.11", "bsocks": "~0.2.6", @@ -32,8 +32,8 @@ "bufio": "~1.2.3", "bupnp": "~0.2.6", "bval": "~0.1.8", - "bweb": "~0.2.0", - "goosig": "~0.10.0", + "bweb": "~0.3.0", + "goosig": "~0.11.0", "n64": "~0.2.10", "urkel": "~1.0.3" }, @@ -56,19 +56,6 @@ "node": ">=14.0.0" } }, - "node_modules/@handshake-org/bfilter": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@handshake-org/bfilter/-/bfilter-2.3.0.tgz", - "integrity": "sha512-vTKTVJvLHz2knpdnYMT0idb6R+HlOCbYKlw2L9Bk9oKOAXwjOIFUp6hnZKIVb87rYW8eEfUROrFG3+DcYwxm7w==", - "dependencies": { - "bcrypto": "~5.4.0", - "bsert": "~0.0.12", - "bufio": "~1.2.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/bcfg": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/bcfg/-/bcfg-0.2.2.tgz", @@ -81,9 +68,9 @@ } }, "node_modules/bcrypto": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/bcrypto/-/bcrypto-5.4.0.tgz", - "integrity": "sha512-KDX2CR29o6ZoqpQndcCxFZAtYA1jDMnXU3jmCfzP44g++Cu7AHHtZN/JbrN/MXAg9SLvtQ8XISG+eVD9zH1+Jg==", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/bcrypto/-/bcrypto-5.5.2.tgz", + "integrity": "sha512-k3PF755oJM0+25iOVuraNedF5XneykxRwl+oBoMeQPfYee4qX8hHQhKCsNZWLthNYgi41GH2ysopd/8sDQDhEw==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -159,6 +146,20 @@ "node": ">=8.0.0" } }, + "node_modules/bfilter": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/bfilter/-/bfilter-2.4.0.tgz", + "integrity": "sha512-ZimmJkrNPQlVgTTNUIz0ghXfMRBZbO2fadiO5Z/21a3cktIKHgPQmdN3Crh38VRBkzVUT0pWak6GapUwkTUjsA==", + "license": "MIT", + "dependencies": { + "bcrypto": "~5.5.2", + "bsert": "~0.0.13", + "bufio": "~1.2.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/bheep": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/bheep/-/bheep-0.1.6.tgz", @@ -243,19 +244,20 @@ } }, "node_modules/bns": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/bns/-/bns-0.15.0.tgz", - "integrity": "sha512-iJWQVE399vQzPfhalFMJGEQ7k5Ot2D6Mz8dkoPeLO8huWAMOiJNJ1tHzOu5j+ZyNNew6ITgG/LsSyaRPxvkXuw==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/bns/-/bns-0.16.0.tgz", + "integrity": "sha512-DYltZ95Kt7IvbaAP2PVgKJalDWovCsxQqbsiHdlUVzvQmYHZodTbw8OWtgzrRu4B1zG3HDrQ4FzRPNPuzgnHrQ==", + "license": "MIT", "dependencies": { - "bcrypto": "~5.4.0", - "bfile": "~0.2.2", - "bheep": "~0.1.5", - "binet": "~0.3.6", - "bs32": "~0.1.6", - "bsert": "~0.0.10", + "bcrypto": "~5.5.2", + "bfile": "~0.2.3", + "bheep": "~0.1.6", + "binet": "~0.3.9", + "bs32": "~0.1.7", + "bsert": "~0.0.13", "btcp": "~0.1.5", "budp": "~0.1.6", - "bufio": "~1.0.7" + "bufio": "~1.2.3" }, "bin": { "bns-keygen": "bin/bns-keygen", @@ -277,14 +279,6 @@ "unbound": "~0.4.3" } }, - "node_modules/bns/node_modules/bufio": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/bufio/-/bufio-1.0.7.tgz", - "integrity": "sha512-bd1dDQhiC+bEbEfg56IdBv7faWa6OipMs/AFFFvtFnB3wAYjlwQpQRZ0pm6ZkgtfL0pILRXhKxOiQj6UzoMR7A==", - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/brq": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/brq/-/brq-0.1.10.tgz", @@ -407,12 +401,13 @@ } }, "node_modules/bweb": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bweb/-/bweb-0.2.0.tgz", - "integrity": "sha512-JfpXemYqylNySwrhR7b4HZTrxnDhbOzNiIXCPBVQU6O8rTZ1wFDLFDr/7uQqkwzjyNZ4ZWTp5wP/pJY2IizfDA==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/bweb/-/bweb-0.3.0.tgz", + "integrity": "sha512-FOBSCnNj+l2j+LfQXt1Rnsx+ZuTGaRjLELT+wo+KXFNsW0ZSJmrJnd29HjEOG9ZJZnmIjAjtTGzvpGxcFume7g==", + "license": "MIT", "dependencies": { - "bsert": "~0.0.10", - "bsock": "~0.1.9" + "bsert": "~0.0.12", + "bsock": "~0.1.11" }, "bin": { "bweb": "bin/bweb" @@ -422,13 +417,14 @@ } }, "node_modules/goosig": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/goosig/-/goosig-0.10.0.tgz", - "integrity": "sha512-+BVVLfxmawAmGVjjJpXzu5LNcFIOfgXgP7kWEyc3qu/xn9RMqbPbNfYDdHBZKfZkDMIO7Q4vD790iNYQAXhoFA==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/goosig/-/goosig-0.11.0.tgz", + "integrity": "sha512-Bk3gMuk1odsF3+Z7Ir9KZwRHfbisIYxqShh4eMW1fKkVhP1MGG7b0bn1FK9SmFZkQrqvYVr4dbV5+TZwNTQfyQ==", "hasInstallScript": true, + "license": "MIT", "dependencies": { - "bcrypto": "~5.4.0", - "bsert": "~0.0.10", + "bcrypto": "~5.5.2", + "bsert": "~0.0.13", "loady": "~0.0.5" }, "engines": { diff --git a/package.json b/package.json index 7701a8e07..f07d3bcad 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,9 @@ "node": ">=14.0.0" }, "dependencies": { - "@handshake-org/bfilter": "~2.3.0", + "bfilter": "~2.4.0", "bcfg": "~0.2.2", - "bcrypto": "~5.4.0", + "bcrypto": "~5.5.2", "bcurl": "~0.2.1", "bdb": "~1.6.2", "bdns": "~0.1.5", @@ -34,7 +34,7 @@ "blru": "~0.1.8", "blst": "~0.1.6", "bmutex": "~0.1.8", - "bns": "~0.15.0", + "bns": "~0.16.0", "bsert": "~0.0.13", "bsock": "~0.1.11", "bsocks": "~0.2.6", @@ -43,8 +43,8 @@ "bufio": "~1.2.3", "bupnp": "~0.2.6", "bval": "~0.1.8", - "bweb": "~0.2.0", - "goosig": "~0.10.0", + "bweb": "~0.3.0", + "goosig": "~0.11.0", "n64": "~0.2.10", "urkel": "~1.0.3" }, diff --git a/test/chain-blockstore-test.js b/test/chain-blockstore-test.js index 67ae85a76..4cd0960d6 100644 --- a/test/chain-blockstore-test.js +++ b/test/chain-blockstore-test.js @@ -1,7 +1,7 @@ 'use strict'; const assert = require('bsert'); -const {BloomFilter} = require('@handshake-org/bfilter'); +const {BloomFilter} = require('bfilter'); const Network = require('../lib/protocol/network'); const {FileBlockStore, LevelBlockStore} = require('../lib/blockstore'); const Chain = require('../lib/blockchain/chain'); diff --git a/test/net-test.js b/test/net-test.js index e3c8a60fd..af74a9104 100644 --- a/test/net-test.js +++ b/test/net-test.js @@ -3,7 +3,7 @@ const assert = require('bsert'); const {resolve} = require('path'); const fs = require('fs'); -const {BloomFilter} = require('@handshake-org/bfilter'); +const {BloomFilter} = require('bfilter'); const {nonce} = require('../lib/net/common'); const consensus = require('../lib/protocol/consensus'); const Parser = require('../lib/net/parser'); diff --git a/test/node-rescan-test.js b/test/node-rescan-test.js index a3868872f..f6f40e3e8 100644 --- a/test/node-rescan-test.js +++ b/test/node-rescan-test.js @@ -2,7 +2,7 @@ const assert = require('bsert'); const {BufferSet} = require('buffer-map'); -const {BloomFilter} = require('@handshake-org/bfilter'); +const {BloomFilter} = require('bfilter'); const TX = require('../lib/primitives/tx'); const nodeCommon = require('../lib/blockchain/common'); const {scanActions} = nodeCommon; diff --git a/test/util/memwallet.js b/test/util/memwallet.js index 8604493fd..31d1be11a 100644 --- a/test/util/memwallet.js +++ b/test/util/memwallet.js @@ -14,7 +14,7 @@ const rules = require('../../lib/covenants/rules'); const Network = require('../../lib/protocol/network'); const MTX = require('../../lib/primitives/mtx'); const HD = require('../../lib/hd/hd'); -const {BloomFilter} = require('@handshake-org/bfilter'); +const {BloomFilter} = require('bfilter'); const KeyRing = require('../../lib/primitives/keyring'); const Outpoint = require('../../lib/primitives/outpoint'); const CoinView = require('../../lib/coins/coinview'); diff --git a/test/wallet-auction-test.js b/test/wallet-auction-test.js index e5281fe48..33ae3de32 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 065f8c99c..664a2c603 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 85c6d2b0c..183f77e50 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) {