Merge pull request #557 from nodar-chkuaselidze/no-template

Wallet option to skip templates.
This commit is contained in:
Braydon Fuller 2019-03-05 09:55:07 -08:00
commit 1a18040ad4
No known key found for this signature in database
GPG Key ID: F24F232D108B3AD4
5 changed files with 116 additions and 2 deletions

View File

@ -9,8 +9,13 @@
- `PUT /wallet/:id` Creating a watch-only wallet now requires an `accountKey` - `PUT /wallet/:id` Creating a watch-only wallet now requires an `accountKey`
argument. This is to prevent bcoin from generating keys and addresses the argument. This is to prevent bcoin from generating keys and addresses the
user can not spend from. user can not spend from.
- `POST /wallet/:id/create` Now has a `sign` argument for optional signing - `POST /wallet/:id/create`
of transactions. - Now has a `sign` argument for optional signing of transactions.
- Now has a `template` option, that will skip templating inputs when
`sign = false`, but you can enable it if necessary. It does not have an
effect when `sign = true`.
- Exposes `blocks`, which can will be used if there is no `rate` option.
- Exposes `sort` (Default `true`), that can be used to disable BIP69 sorting.
#### RPC #### RPC

View File

@ -468,13 +468,16 @@ class HTTP extends Server {
const options = { const options = {
rate: valid.u64('rate'), rate: valid.u64('rate'),
blocks: valid.u32('blocks'),
maxFee: valid.u64('maxFee'), maxFee: valid.u64('maxFee'),
selection: valid.str('selection'), selection: valid.str('selection'),
smart: valid.bool('smart'), smart: valid.bool('smart'),
account: valid.str('account'), account: valid.str('account'),
sort: valid.bool('sort'),
subtractFee: valid.bool('subtractFee'), subtractFee: valid.bool('subtractFee'),
subtractIndex: valid.i32('subtractIndex'), subtractIndex: valid.i32('subtractIndex'),
depth: valid.u32(['confirmations', 'depth']), depth: valid.u32(['confirmations', 'depth']),
template: valid.bool('template', sign),
outputs: [] outputs: []
}; };

View File

@ -1236,6 +1236,9 @@ class Wallet extends EventEmitter {
* to avoid sorting), set locktime, and template it. * to avoid sorting), set locktime, and template it.
* @param {Object} options - See {@link Wallet#fund options}. * @param {Object} options - See {@link Wallet#fund options}.
* @param {Object[]} options.outputs - See {@link MTX#addOutput}. * @param {Object[]} options.outputs - See {@link MTX#addOutput}.
* @param {Boolean} options.sort - Sort inputs and outputs (BIP69).
* @param {Boolean} options.template - Build scripts for inputs.
* @param {Number} options.locktime - TX locktime
* @returns {Promise} - Returns {@link MTX}. * @returns {Promise} - Returns {@link MTX}.
*/ */
@ -1281,6 +1284,9 @@ class Wallet extends EventEmitter {
assert(mtx.verifyInputs(this.wdb.state.height + 1), assert(mtx.verifyInputs(this.wdb.state.height + 1),
'TX failed context check.'); 'TX failed context check.');
if (options.template === false)
return mtx;
const total = await this.template(mtx); const total = await this.template(mtx);
if (total === 0) if (total === 0)

View File

@ -264,6 +264,72 @@ describe('HTTP', function() {
}); });
}); });
for (const template of [true, false]) {
const suffix = template ? 'with template' : 'without template';
it(`should create and sign transaction ${suffix}`, async () => {
const change = await wallet.createChange('default');
const tx = await wallet.createTX({
template: template, // should not matter, sign = true
sign: true,
outputs: [{
address: change.address,
value: 50000
}]
});
const mtx = MTX.fromJSON(tx);
for (const input of tx.inputs) {
const script = input.script;
assert.notStrictEqual(script, '',
'Input must be signed.');
}
assert.strictEqual(mtx.verify(), true,
'Transaction must be signed.');
});
}
it('should create transaction without template', async () => {
const change = await wallet.createChange('default');
const tx = await wallet.createTX({
sign: false,
outputs: [{
address: change.address,
value: 50000
}]
});
for (const input of tx.inputs) {
const script = input.script;
assert.strictEqual(script.length, 0,
'Input must not be templated.');
}
});
it('should create transaction with template', async () => {
const change = await wallet.createChange('default');
const tx = await wallet.createTX({
sign: false,
template: true,
outputs: [{
address: change.address,
value: 20000
}]
});
for (const input of tx.inputs) {
const script = Buffer.from(input.script, 'hex');
// p2pkh
// 1 (OP_0 placeholder) + 1 (length) + 33 (pubkey)
assert.strictEqual(script.length, 35);
assert.strictEqual(script[0], 0x00,
'First item in stack must be a placeholder OP_0');
}
});
it('should cleanup', async () => { it('should cleanup', async () => {
consensus.COINBASE_MATURITY = 100; consensus.COINBASE_MATURITY = 100;
await wallet.close(); await wallet.close();

View File

@ -1220,6 +1220,40 @@ describe('Wallet', function() {
assert(t3.verify()); assert(t3.verify());
}); });
for (const witness of [true, false]) {
it(`should create non-templated tx (witness=${witness})`, async () => {
const wallet = await wdb.create({ witness });
// Fund wallet
const t1 = new MTX();
t1.addInput(dummyInput());
t1.addOutput(await wallet.receiveAddress(), 500000);
await wdb.addTX(t1.toTX());
const options = {
rate: 10000,
round: true,
outputs: [{
address: await wallet.receiveAddress(),
value: 7000
}],
template: false
};
const t2 = await wallet.createTX(options);
assert(t2, 'Could not create tx.');
for (const input of t2.inputs) {
const {script, witness} = input;
assert.strictEqual(script.length, 0, 'Input is templated.');
assert.strictEqual(witness.length, 0, 'Input is templated.');
}
});
}
it('should get range of txs', async () => { it('should get range of txs', async () => {
const wallet = currentWallet; const wallet = currentWallet;
const txs = await wallet.getRange(null, { const txs = await wallet.getRange(null, {