Skip to content

Commit 613e23f

Browse files
committed
Merge PR #825 from 'rithvikvibhu/makebatch-addr-fix'
1 parent a7145dc commit 613e23f

File tree

2 files changed

+145
-11
lines changed

2 files changed

+145
-11
lines changed

lib/wallet/wallet.js

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1716,14 +1716,16 @@ class Wallet extends EventEmitter {
17161716
* @param {Number} lockup
17171717
* @param {Number|String} acct
17181718
* @param {MTX?} mtx
1719-
* @returns {MTX}
1719+
* @param {Address?} addr
1720+
* @returns {Promise<MTX>}
17201721
*/
17211722

1722-
async makeBid(name, value, lockup, acct, mtx) {
1723+
async makeBid(name, value, lockup, acct, mtx, addr) {
17231724
assert(typeof name === 'string');
17241725
assert(Number.isSafeInteger(value) && value >= 0);
17251726
assert(Number.isSafeInteger(lockup) && lockup >= 0);
17261727
assert((acct >>> 0) === acct || typeof acct === 'string');
1728+
assert(addr == null || addr instanceof Address);
17271729

17281730
if (!rules.verifyName(name))
17291731
throw new Error(`Invalid name: ${name}.`);
@@ -1759,7 +1761,9 @@ class Wallet extends EventEmitter {
17591761
`Bid (${value}) exceeds lockup value (${lockup}): ${name}.`
17601762
);
17611763

1762-
const addr = await this.receiveAddress(acct);
1764+
if (!addr)
1765+
addr = await this.receiveAddress(acct);
1766+
17631767
const blind = await this.generateBlind(nameHash, addr, value);
17641768

17651769
const output = new Output();
@@ -3713,6 +3717,11 @@ class Wallet extends EventEmitter {
37133717
}
37143718
});
37153719

3720+
// Some actions accept output addresses to avoid address reuse.
3721+
// We track that by bumping receiveIndex.
3722+
const account = await this.getAccount(acct);
3723+
let receiveIndex = account.receiveDepth - 1;
3724+
37163725
// "actions" are arrays that start with a covenant type (or meta-type)
37173726
// followed by the arguments expected by the corresponding "make" function.
37183727
for (const action of actions) {
@@ -3735,10 +3744,12 @@ class Wallet extends EventEmitter {
37353744
assert(action.length === 1, 'Bad arguments for OPEN.');
37363745
await this.makeOpen(...action, force, acct, mtx);
37373746
break;
3738-
case 'BID':
3747+
case 'BID': {
37393748
assert(action.length === 3, 'Bad arguments for BID.');
3740-
await this.makeBid(...action, acct, mtx);
3749+
const address = account.deriveReceive(receiveIndex++).getAddress();
3750+
await this.makeBid(...action, acct, mtx, address);
37413751
break;
3752+
}
37423753
case 'REVEAL':
37433754
if (action.length === 1) {
37443755
await this.makeReveal(...action, acct, mtx);
@@ -3807,10 +3818,6 @@ class Wallet extends EventEmitter {
38073818
// Clean up.
38083819
// 1. Some actions MUST be the ONLY action for a name.
38093820
// i.e. no duplicate OPENs or REVOKE/FINALIZE for same name in one tx.
3810-
// 2. Some outputs may reuse same address from this.receieveAddress(acct)
3811-
// We can bump those to the next receive address,
3812-
const account = await this.getAccount(acct);
3813-
let receiveIndex = account.receiveDepth - 1;
38143821
const set = new BufferSet();
38153822
for (const output of mtx.outputs) {
38163823
const {covenant} = output;
@@ -3823,12 +3830,10 @@ class Wallet extends EventEmitter {
38233830
case types.CLAIM:
38243831
case types.OPEN:
38253832
output.address = account.deriveReceive(receiveIndex++).getAddress();
3826-
38273833
assert(!set.has(nameHash), 'Duplicate name with exclusive action.');
38283834
set.add(nameHash);
38293835
break;
38303836
case types.BID:
3831-
output.address = account.deriveReceive(receiveIndex++).getAddress();
38323837
case types.REVEAL:
38333838
case types.REDEEM:
38343839
break;

test/wallet-importnonce-test.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
'use strict';
2+
3+
const assert = require('bsert');
4+
const FullNode = require('../lib/node/fullnode');
5+
const Network = require('../lib/protocol/network');
6+
const Address = require('../lib/primitives/address');
7+
const rules = require('../lib/covenants/rules');
8+
9+
/** @typedef {import('../lib/wallet/wallet')} Wallet */
10+
11+
const network = Network.get('regtest');
12+
13+
const node = new FullNode({
14+
memory: true,
15+
network: network.type,
16+
plugins: [require('../lib/wallet/plugin')]
17+
});
18+
19+
const { wdb } = node.require('walletdb');
20+
21+
async function mineBlocks(n, addr) {
22+
addr = addr ? addr : new Address().toString(network);
23+
for (let i = 0; i < n; i++) {
24+
const block = await node.miner.mineBlock(null, addr);
25+
await node.chain.add(block);
26+
}
27+
}
28+
29+
describe('Wallet Import Nonce', function () {
30+
/** @type {Wallet} */
31+
let walletA;
32+
33+
/** @type {Wallet} */
34+
let walletB;
35+
36+
const NAME = rules.grindName(10, 1, network);
37+
const NAMEHASH = rules.hashName(NAME);
38+
const BIDS = [
39+
{ value: 1e6, lockup: 2e6, addr: undefined }, // sendbid
40+
{ value: 2e6, lockup: 4e6, addr: undefined }, // -|sendbatch
41+
{ value: 4e6, lockup: 8e6, addr: undefined } // -|sendbatch
42+
];
43+
44+
before(async () => {
45+
await node.ensure();
46+
await node.open();
47+
48+
// Both wallets have the same seed
49+
walletA = await wdb.create();
50+
walletB = await wdb.create({ mnemonic: walletA.master.mnemonic });
51+
assert.bufferEqual(walletA.master.writeKey(), walletB.master.writeKey());
52+
});
53+
54+
after(async () => {
55+
await node.close();
56+
});
57+
58+
it('should fund wallet', async () => {
59+
await mineBlocks(2, await walletA.receiveAddress());
60+
});
61+
62+
it('should open an auction and advance to bidding period', async () => {
63+
await walletA.sendOpen(NAME, false);
64+
await mineBlocks(network.names.treeInterval + 1);
65+
});
66+
67+
it('should bid with sendbid', async () => {
68+
const bid = BIDS[0];
69+
70+
const bidTx = await walletA.sendBid(NAME, bid.value, bid.lockup);
71+
72+
// Save address for importnonce later
73+
bid.addr = bidTx.outputs[0].address;
74+
});
75+
76+
it('should bid with sendbatch', async () => {
77+
const batch = [
78+
['BID', NAME, BIDS[1].value, BIDS[1].lockup],
79+
['BID', NAME, BIDS[2].value, BIDS[2].lockup]
80+
];
81+
82+
const bidTx = await walletA.sendBatch(batch);
83+
84+
// Save address for importnonce later
85+
for (const output of bidTx.outputs) {
86+
if (!output.covenant.isBid())
87+
continue;
88+
89+
const index = BIDS.findIndex(bid => bid.lockup === output.value);
90+
BIDS[index].addr = output.address;
91+
}
92+
});
93+
94+
it('should verify bids were placed', async () => {
95+
await mineBlocks(1);
96+
const bidsA = await walletA.getBidsByName(NAME);
97+
assert.strictEqual(bidsA.length, BIDS.length);
98+
});
99+
100+
it('should not be known by other wallet', async () => {
101+
const bidsB = await walletB.getBidsByName(NAME);
102+
assert.strictEqual(bidsB.length, BIDS.length);
103+
104+
for (const bid of bidsB)
105+
assert.strictEqual(bid.value, -1);
106+
});
107+
108+
it('should be imported by other wallet', async () => {
109+
for (const bid of BIDS)
110+
await walletB.generateBlinds(NAMEHASH, bid.addr, bid.value);
111+
112+
const bidsB = await walletB.getBidsByName(NAME);
113+
assert.strictEqual(bidsB.length, BIDS.length);
114+
115+
// Ensure bids have correct true bid values
116+
for (const bid of bidsB) {
117+
const index = BIDS.findIndex(x => x.lockup === bid.lockup);
118+
assert.strictEqual(BIDS[index].value, bid.value);
119+
}
120+
});
121+
122+
it('should reaveal all bids from other wallet', async () => {
123+
await mineBlocks(network.names.biddingPeriod);
124+
125+
const revealTx = await walletB.sendRevealAll();
126+
const revealOutputs = revealTx.outputs.filter(out => out.covenant.isReveal());
127+
assert.strictEqual(revealOutputs.length, BIDS.length);
128+
});
129+
});

0 commit comments

Comments
 (0)