From 8aa4c8fdd44f3db4bb7ced71a9dd191322f7b452 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Tue, 3 Jul 2018 22:33:04 +1000 Subject: [PATCH 1/5] payments: prepare tests for null data --- test/payments.js | 5 +++-- test/payments.utils.js | 18 +++++++++++------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/test/payments.js b/test/payments.js index 5722d88..6a4e733 100644 --- a/test/payments.js +++ b/test/payments.js @@ -9,14 +9,15 @@ const u = require('./payments.utils') const fixtures = require('./fixtures/' + p) fixtures.valid.forEach(function (f, i) { - const args = u.preform(f.arguments) - it(f.description + ' as expected', function () { + const args = u.preform(f.arguments) const actual = fn(args, f.options) + u.equate(actual, f.expected, f.arguments) }) it(f.description + ' as expected (no validation)', function () { + const args = u.preform(f.arguments) const actual = fn(args, Object.assign({}, f.options, { validate: false })) diff --git a/test/payments.utils.js b/test/payments.utils.js index 22001e9..2dbed3d 100644 --- a/test/payments.utils.js +++ b/test/payments.utils.js @@ -7,6 +7,12 @@ function tryHex (x) { if (Array.isArray(x)) return x.map(tryHex) return x } + +function fromHex (x) { + if (typeof x === 'string') return Buffer.from(x, 'hex') + if (Array.isArray(x)) return x.map(fromHex) + return x +} function tryASM (x) { if (Buffer.isBuffer(x)) return bscript.toASM(x) return x @@ -64,6 +70,7 @@ function equate (a, b, args) { if ('n' in b) t.strictEqual(a.n, b.n, 'Inequal *.n') if ('pubkeys' in b) t.deepEqual(tryHex(a.pubkeys), tryHex(b.pubkeys), 'Inequal *.pubkeys') if ('signatures' in b) t.deepEqual(tryHex(a.signatures), tryHex(b.signatures), 'Inequal *.signatures') + if ('data' in b) t.deepEqual(tryHex(a.data), tryHex(b.data), 'Inequal *.data') } function preform (x) { @@ -80,21 +87,18 @@ function preform (x) { } if (typeof x.output === 'string') x.output = asmToBuffer(x.output) if (typeof x.input === 'string') x.input = asmToBuffer(x.input) - if (Array.isArray(x.witness)) { - x.witness = x.witness.map(function (y) { - return Buffer.from(y, 'hex') - }) - } + if (Array.isArray(x.witness)) x.witness = x.witness.map(fromHex) + if (x.data) x.data = x.data.map(fromHex) if (x.hash) x.hash = Buffer.from(x.hash, 'hex') if (x.pubkey) x.pubkey = Buffer.from(x.pubkey, 'hex') if (x.signature) x.signature = Buffer.from(x.signature, 'hex') - if (x.pubkeys) x.pubkeys = x.pubkeys.map(function (y) { return Buffer.from(y, 'hex') }) + if (x.pubkeys) x.pubkeys = x.pubkeys.map(fromHex) if (x.signatures) x.signatures = x.signatures.map(function (y) { return Number.isFinite(y) ? y : Buffer.from(y, 'hex') }) if (x.redeem) { if (typeof x.redeem.input === 'string') x.redeem.input = asmToBuffer(x.redeem.input) if (typeof x.redeem.output === 'string') x.redeem.output = asmToBuffer(x.redeem.output) - if (Array.isArray(x.redeem.witness)) x.redeem.witness = x.redeem.witness.map(function (y) { return Buffer.from(y, 'hex') }) + if (Array.isArray(x.redeem.witness)) x.redeem.witness = x.redeem.witness.map(fromHex) x.redeem.network = bnetworks[x.redeem.network] || x.network || bnetworks.bitcoin } From d886e76abd74c7133a2fab93426cfc93e87098a4 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Tue, 3 Jul 2018 22:33:22 +1000 Subject: [PATCH 2/5] tests/payments: add null data tests --- test/fixtures/p2data.json | 63 +++++++++++++++++++++++++++++++++++++++ test/payments.js | 2 +- 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/p2data.json diff --git a/test/fixtures/p2data.json b/test/fixtures/p2data.json new file mode 100644 index 0000000..b0486cd --- /dev/null +++ b/test/fixtures/p2data.json @@ -0,0 +1,63 @@ +{ + "valid": [ + { + "description": "output from output", + "arguments": { + "output": "OP_RETURN a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4" + }, + "expected": { + "data": [ + "a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4" + ], + "output": "OP_RETURN a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4", + "input": null, + "witness": null + } + }, + { + "description": "output from data", + "arguments": { + "data": [ + "a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4" + ] + }, + "expected": { + "data": [ + "a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4" + ], + "output": "OP_RETURN a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4", + "input": null, + "witness": null + } + } + ], + "invalid": [ + { + "exception": "Not enough data", + "arguments": {} + } + ], + "dynamic": { + "depends": { + "data": [ "data", "output" ], + "output": [ "output", "data" ] + }, + "details": [ + { + "description": "p2data", + "data": [ + "a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4" + ], + "output": "OP_RETURN a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4" + }, + { + "description": "p2data", + "data": [ + "a3b147dbe4a85579fc4b5a1811e76620560e0726", + "7e62b9a0d6858f9127735cadd82f67e06c24dbc4" + ], + "output": "OP_RETURN a3b147dbe4a85579fc4b5a1811e76620560e0726 7e62b9a0d6858f9127735cadd82f67e06c24dbc4" + } + ] + } +} diff --git a/test/payments.js b/test/payments.js index 6a4e733..3645ea7 100644 --- a/test/payments.js +++ b/test/payments.js @@ -3,7 +3,7 @@ const assert = require('assert') const u = require('./payments.utils') -;['p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(function (p) { +;['p2data', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(function (p) { describe(p, function () { const fn = require('../src/payments/' + p) const fixtures = require('./fixtures/' + p) From 44c13665c8f436c32d6a9036c585acfff75829fb Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Tue, 3 Jul 2018 22:33:35 +1000 Subject: [PATCH 3/5] payments: add p2data --- src/payments/index.js | 11 ++----- src/payments/p2data.js | 56 ++++++++++++++++++++++++++++++++ test/integration/transactions.js | 5 ++- 3 files changed, 60 insertions(+), 12 deletions(-) create mode 100644 src/payments/p2data.js diff --git a/src/payments/index.js b/src/payments/index.js index 9e869f5..de3bec1 100644 --- a/src/payments/index.js +++ b/src/payments/index.js @@ -1,3 +1,4 @@ +const p2data = require('./p2data') const p2ms = require('./p2ms') const p2pk = require('./p2pk') const p2pkh = require('./p2pkh') @@ -5,15 +6,7 @@ const p2sh = require('./p2sh') const p2wpkh = require('./p2wpkh') const p2wsh = require('./p2wsh') -module.exports = { - p2ms: p2ms, - p2pk: p2pk, - p2pkh: p2pkh, - p2sh: p2sh, - p2wpkh: p2wpkh, - p2wsh: p2wsh -} +module.exports = { p2data, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh } // TODO -// OP_RETURN // witness commitment diff --git a/src/payments/p2data.js b/src/payments/p2data.js new file mode 100644 index 0000000..c636c80 --- /dev/null +++ b/src/payments/p2data.js @@ -0,0 +1,56 @@ +const lazy = require('./lazy') +const typef = require('typeforce') +const OPS = require('bitcoin-ops') + +const bscript = require('../script') +const BITCOIN_NETWORK = require('../networks').bitcoin + +function stacksEqual (a, b) { + if (a.length !== b.length) return false + + return a.every(function (x, i) { + return x.equals(b[i]) + }) +} + +// output: OP_RETURN ... +function p2data (a, opts) { + if ( + !a.data && + !a.output + ) throw new TypeError('Not enough data') + opts = opts || { validate: true } + + typef({ + network: typef.maybe(typef.Object), + output: typef.maybe(typef.Buffer), + data: typef.maybe(typef.arrayOf(typef.Buffer)) + }, a) + + const network = a.network || BITCOIN_NETWORK + const o = { network } + + lazy.prop(o, 'output', function () { + if (!a.data) return + return bscript.compile([OPS.OP_RETURN].concat(a.data)) + }) + lazy.prop(o, 'data', function () { + if (!a.output) return + return bscript.decompile(a.output).slice(1) + }) + + // extended validation + if (opts.validate) { + if (a.output) { + const chunks = bscript.decompile(a.output) + if (chunks[0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid') + if (!chunks.slice(1).every(typef.Buffer)) throw new TypeError('Output is invalid') + + if (a.data && !stacksEqual(a.data, o.data)) throw new TypeError('Data mismatch') + } + } + + return Object.assign(o, a) +} + +module.exports = p2data diff --git a/test/integration/transactions.js b/test/integration/transactions.js index 5bb1342..cec0839 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -91,10 +91,9 @@ describe('bitcoinjs-lib (transactions)', function () { const txb = new bitcoin.TransactionBuilder(regtest) const data = Buffer.from('bitcoinjs-lib', 'utf8') - const dataScript = require('../../src/templates/nulldata').output.encode([data]) - + const p2data = bitcoin.payments.p2data({ data: [data] }) txb.addInput(unspent.txId, unspent.vout) - txb.addOutput(dataScript, 1000) + txb.addOutput(p2data.output, 1000) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e5) txb.sign(0, keyPair) From de90fea0ac0e21c9a00b8bbfff2c334cc3521649 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Wed, 11 Jul 2018 17:27:44 +1000 Subject: [PATCH 4/5] payments: rename p2data to embed --- src/payments/embed.js | 56 +++++++++++++++++++++++ src/payments/index.js | 4 +- src/payments/p2data.js | 56 ----------------------- test/fixtures/{p2data.json => embed.json} | 4 +- test/integration/transactions.js | 4 +- test/payments.js | 2 +- 6 files changed, 63 insertions(+), 63 deletions(-) create mode 100644 src/payments/embed.js rename test/fixtures/{p2data.json => embed.json} (96%) diff --git a/src/payments/embed.js b/src/payments/embed.js new file mode 100644 index 0000000..c636c80 --- /dev/null +++ b/src/payments/embed.js @@ -0,0 +1,56 @@ +const lazy = require('./lazy') +const typef = require('typeforce') +const OPS = require('bitcoin-ops') + +const bscript = require('../script') +const BITCOIN_NETWORK = require('../networks').bitcoin + +function stacksEqual (a, b) { + if (a.length !== b.length) return false + + return a.every(function (x, i) { + return x.equals(b[i]) + }) +} + +// output: OP_RETURN ... +function p2data (a, opts) { + if ( + !a.data && + !a.output + ) throw new TypeError('Not enough data') + opts = opts || { validate: true } + + typef({ + network: typef.maybe(typef.Object), + output: typef.maybe(typef.Buffer), + data: typef.maybe(typef.arrayOf(typef.Buffer)) + }, a) + + const network = a.network || BITCOIN_NETWORK + const o = { network } + + lazy.prop(o, 'output', function () { + if (!a.data) return + return bscript.compile([OPS.OP_RETURN].concat(a.data)) + }) + lazy.prop(o, 'data', function () { + if (!a.output) return + return bscript.decompile(a.output).slice(1) + }) + + // extended validation + if (opts.validate) { + if (a.output) { + const chunks = bscript.decompile(a.output) + if (chunks[0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid') + if (!chunks.slice(1).every(typef.Buffer)) throw new TypeError('Output is invalid') + + if (a.data && !stacksEqual(a.data, o.data)) throw new TypeError('Data mismatch') + } + } + + return Object.assign(o, a) +} + +module.exports = p2data diff --git a/src/payments/index.js b/src/payments/index.js index de3bec1..d445466 100644 --- a/src/payments/index.js +++ b/src/payments/index.js @@ -1,4 +1,4 @@ -const p2data = require('./p2data') +const embed = require('./embed') const p2ms = require('./p2ms') const p2pk = require('./p2pk') const p2pkh = require('./p2pkh') @@ -6,7 +6,7 @@ const p2sh = require('./p2sh') const p2wpkh = require('./p2wpkh') const p2wsh = require('./p2wsh') -module.exports = { p2data, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh } +module.exports = { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh } // TODO // witness commitment diff --git a/src/payments/p2data.js b/src/payments/p2data.js index c636c80..e69de29 100644 --- a/src/payments/p2data.js +++ b/src/payments/p2data.js @@ -1,56 +0,0 @@ -const lazy = require('./lazy') -const typef = require('typeforce') -const OPS = require('bitcoin-ops') - -const bscript = require('../script') -const BITCOIN_NETWORK = require('../networks').bitcoin - -function stacksEqual (a, b) { - if (a.length !== b.length) return false - - return a.every(function (x, i) { - return x.equals(b[i]) - }) -} - -// output: OP_RETURN ... -function p2data (a, opts) { - if ( - !a.data && - !a.output - ) throw new TypeError('Not enough data') - opts = opts || { validate: true } - - typef({ - network: typef.maybe(typef.Object), - output: typef.maybe(typef.Buffer), - data: typef.maybe(typef.arrayOf(typef.Buffer)) - }, a) - - const network = a.network || BITCOIN_NETWORK - const o = { network } - - lazy.prop(o, 'output', function () { - if (!a.data) return - return bscript.compile([OPS.OP_RETURN].concat(a.data)) - }) - lazy.prop(o, 'data', function () { - if (!a.output) return - return bscript.decompile(a.output).slice(1) - }) - - // extended validation - if (opts.validate) { - if (a.output) { - const chunks = bscript.decompile(a.output) - if (chunks[0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid') - if (!chunks.slice(1).every(typef.Buffer)) throw new TypeError('Output is invalid') - - if (a.data && !stacksEqual(a.data, o.data)) throw new TypeError('Data mismatch') - } - } - - return Object.assign(o, a) -} - -module.exports = p2data diff --git a/test/fixtures/p2data.json b/test/fixtures/embed.json similarity index 96% rename from test/fixtures/p2data.json rename to test/fixtures/embed.json index b0486cd..ccc0e70 100644 --- a/test/fixtures/p2data.json +++ b/test/fixtures/embed.json @@ -44,14 +44,14 @@ }, "details": [ { - "description": "p2data", + "description": "embed", "data": [ "a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4" ], "output": "OP_RETURN a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4" }, { - "description": "p2data", + "description": "embed", "data": [ "a3b147dbe4a85579fc4b5a1811e76620560e0726", "7e62b9a0d6858f9127735cadd82f67e06c24dbc4" diff --git a/test/integration/transactions.js b/test/integration/transactions.js index cec0839..5358461 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -91,9 +91,9 @@ describe('bitcoinjs-lib (transactions)', function () { const txb = new bitcoin.TransactionBuilder(regtest) const data = Buffer.from('bitcoinjs-lib', 'utf8') - const p2data = bitcoin.payments.p2data({ data: [data] }) + const embed = bitcoin.payments.embed({ data: [data] }) txb.addInput(unspent.txId, unspent.vout) - txb.addOutput(p2data.output, 1000) + txb.addOutput(embed.output, 1000) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e5) txb.sign(0, keyPair) diff --git a/test/payments.js b/test/payments.js index 3645ea7..3af6699 100644 --- a/test/payments.js +++ b/test/payments.js @@ -3,7 +3,7 @@ const assert = require('assert') const u = require('./payments.utils') -;['p2data', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(function (p) { +;['embed', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(function (p) { describe(p, function () { const fn = require('../src/payments/' + p) const fixtures = require('./fixtures/' + p) From 5481bde49c3e45c1a6302e94d3c7964bc04660b1 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Fri, 13 Jul 2018 13:09:41 +1000 Subject: [PATCH 5/5] templates: rm unused nulldata code --- src/templates/nulldata.js | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/templates/nulldata.js b/src/templates/nulldata.js index 2ad09d7..d42fd71 100644 --- a/src/templates/nulldata.js +++ b/src/templates/nulldata.js @@ -1,8 +1,6 @@ // OP_RETURN {data} const bscript = require('../script') -const types = require('../types') -const typeforce = require('typeforce') const OPS = require('bitcoin-ops') function check (script) { @@ -13,22 +11,4 @@ function check (script) { } check.toJSON = function () { return 'null data output' } -function encode (data) { - typeforce([types.Buffer], data) - - return bscript.compile([OPS.OP_RETURN].concat(data)) -} - -function decode (buffer) { - typeforce(check, buffer) - - return bscript.decompile(buffer).slice(1) -} - -module.exports = { - output: { - check: check, - decode: decode, - encode: encode - } -} +module.exports = { output: { check: check } }