Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/lib/psbt.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class Psbt {
throw new Error('unknownKeyVals must be an Array');
}
addKeyVals.forEach(keyVal =>
this.addUnknownKeyValToInput(outputIndex, keyVal),
this.addUnknownKeyValToOutput(outputIndex, keyVal),
);
utils_1.addOutputAttributes(this.outputs, outputData);
return this;
Expand Down
79 changes: 79 additions & 0 deletions src/tests/addInputOutput.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,85 @@ Object.defineProperty(exports, '__esModule', { value: true });
const tape = require('tape');
const psbt_1 = require('../lib/psbt');
const txTools_1 = require('./utils/txTools');
const SCRIPT = Buffer.from(
'a914e18870f2c297fbfca54c5c6f645c7745a5b66eda87',
'hex',
);
// Proprietary key: 0xfc prefix + identifier + subtype (type byte 0xfc is
// above both InputTypes (16 entries) and OutputTypes (6 entries), so it is
// always an unknown key for either map).
const PROPRIETARY_KEY = Buffer.from('fc0a626974676f2f6d7573696700', 'hex');
const PROPRIETARY_VAL = Buffer.from('deadbeef', 'hex');
tape(
'Test: addOutput routes unknownKeyVals to output map, not input map',
t => {
const psbt = new psbt_1.Psbt(txTools_1.getDefaultTx());
psbt.addInput({
hash: '865dce988413971fd812d0e81a3395ed916a87ea533e1a16c0f4e15df96fa7d4',
index: 0,
});
psbt.addOutput({
script: SCRIPT,
value: 1000000,
unknownKeyVals: [{ key: PROPRIETARY_KEY, value: PROPRIETARY_VAL }],
});
// Key must be on the output, not the input
t.deepEqual(
psbt.outputs[0].unknownKeyVals,
[{ key: PROPRIETARY_KEY, value: PROPRIETARY_VAL }],
'output[0].unknownKeyVals contains the proprietary key',
);
t.deepEqual(
psbt.inputs[0].unknownKeyVals,
[],
'input[0].unknownKeyVals is empty',
);
// Placement must survive a serialize/parse round-trip
const psbt2 = psbt_1.Psbt.fromHex(
psbt.toHex(),
txTools_1.transactionFromBuffer,
);
t.deepEqual(
psbt2.outputs[0].unknownKeyVals,
[{ key: PROPRIETARY_KEY, value: PROPRIETARY_VAL }],
'output[0].unknownKeyVals survives round-trip',
);
// fromBuffer leaves unknownKeyVals undefined when there are none;
// either undefined or [] is acceptable here.
t.ok(
!psbt2.inputs[0].unknownKeyVals ||
psbt2.inputs[0].unknownKeyVals.length === 0,
'input[0].unknownKeyVals is empty after round-trip',
);
t.end();
},
);
tape(
'Test: addOutput with unknownKeyVals does not throw when outputs > inputs',
t => {
// Common case: 1 input, 2 outputs — the second addOutput formerly threw
// "No input #1" because it called addUnknownKeyValToInput(1, ...).
const psbt = new psbt_1.Psbt(txTools_1.getDefaultTx());
psbt.addInput({
hash: '865dce988413971fd812d0e81a3395ed916a87ea533e1a16c0f4e15df96fa7d4',
index: 0,
});
psbt.addOutput({ script: SCRIPT, value: 1000000 });
t.doesNotThrow(() => {
psbt.addOutput({
script: SCRIPT,
value: 500000,
unknownKeyVals: [{ key: PROPRIETARY_KEY, value: PROPRIETARY_VAL }],
});
}, 'second addOutput with unknownKeyVals must not throw');
t.deepEqual(
psbt.outputs[1].unknownKeyVals,
[{ key: PROPRIETARY_KEY, value: PROPRIETARY_VAL }],
'output[1] holds the proprietary key',
);
t.end();
},
);
tape('Test: add Input Output', t => {
const psbt = new psbt_1.Psbt(txTools_1.getDefaultTx());
psbt.addInput({
Expand Down
2 changes: 1 addition & 1 deletion ts_src/lib/psbt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ export class Psbt {
throw new Error('unknownKeyVals must be an Array');
}
addKeyVals.forEach((keyVal: KeyValue) =>
this.addUnknownKeyValToInput(outputIndex, keyVal),
this.addUnknownKeyValToOutput(outputIndex, keyVal),
);
addOutputAttributes(this.outputs, outputData);
return this;
Expand Down
86 changes: 86 additions & 0 deletions ts_src/tests/addInputOutput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,92 @@ import * as tape from 'tape';
import { Psbt } from '../lib/psbt';
import { getDefaultTx, transactionFromBuffer } from './utils/txTools';

const SCRIPT = Buffer.from(
'a914e18870f2c297fbfca54c5c6f645c7745a5b66eda87',
'hex',
);

// Proprietary key: 0xfc prefix + identifier + subtype (type byte 0xfc is
// above both InputTypes (16 entries) and OutputTypes (6 entries), so it is
// always an unknown key for either map).
const PROPRIETARY_KEY = Buffer.from('fc0a626974676f2f6d7573696700', 'hex');
const PROPRIETARY_VAL = Buffer.from('deadbeef', 'hex');

tape(
'Test: addOutput routes unknownKeyVals to output map, not input map',
t => {
const psbt = new Psbt(getDefaultTx());
psbt.addInput({
hash: '865dce988413971fd812d0e81a3395ed916a87ea533e1a16c0f4e15df96fa7d4',
index: 0,
});
psbt.addOutput({
script: SCRIPT,
value: 1000000,
unknownKeyVals: [{ key: PROPRIETARY_KEY, value: PROPRIETARY_VAL }],
});

// Key must be on the output, not the input
t.deepEqual(
psbt.outputs[0].unknownKeyVals,
[{ key: PROPRIETARY_KEY, value: PROPRIETARY_VAL }],
'output[0].unknownKeyVals contains the proprietary key',
);
t.deepEqual(
psbt.inputs[0].unknownKeyVals,
[],
'input[0].unknownKeyVals is empty',
);

// Placement must survive a serialize/parse round-trip
const psbt2 = Psbt.fromHex(psbt.toHex(), transactionFromBuffer);
t.deepEqual(
psbt2.outputs[0].unknownKeyVals,
[{ key: PROPRIETARY_KEY, value: PROPRIETARY_VAL }],
'output[0].unknownKeyVals survives round-trip',
);
// fromBuffer leaves unknownKeyVals undefined when there are none;
// either undefined or [] is acceptable here.
t.ok(
!psbt2.inputs[0].unknownKeyVals ||
psbt2.inputs[0].unknownKeyVals.length === 0,
'input[0].unknownKeyVals is empty after round-trip',
);

t.end();
},
);

tape(
'Test: addOutput with unknownKeyVals does not throw when outputs > inputs',
t => {
// Common case: 1 input, 2 outputs — the second addOutput formerly threw
// "No input #1" because it called addUnknownKeyValToInput(1, ...).
const psbt = new Psbt(getDefaultTx());
psbt.addInput({
hash: '865dce988413971fd812d0e81a3395ed916a87ea533e1a16c0f4e15df96fa7d4',
index: 0,
});
psbt.addOutput({ script: SCRIPT, value: 1000000 });

t.doesNotThrow(() => {
psbt.addOutput({
script: SCRIPT,
value: 500000,
unknownKeyVals: [{ key: PROPRIETARY_KEY, value: PROPRIETARY_VAL }],
});
}, 'second addOutput with unknownKeyVals must not throw');

t.deepEqual(
psbt.outputs[1].unknownKeyVals,
[{ key: PROPRIETARY_KEY, value: PROPRIETARY_VAL }],
'output[1] holds the proprietary key',
);

t.end();
},
);

tape('Test: add Input Output', t => {
const psbt = new Psbt(getDefaultTx());
psbt.addInput({
Expand Down
Loading