Skip to content

Commit

Permalink
introduce strict length consumption during decode
Browse files Browse the repository at this point in the history
  • Loading branch information
rvagg committed May 19, 2020
1 parent edafa9a commit da3bb14
Show file tree
Hide file tree
Showing 6 changed files with 31 additions and 12 deletions.
16 changes: 12 additions & 4 deletions README.md
Expand Up @@ -135,7 +135,7 @@ Only the non-redundant parts of a porcelain form of the objects are required to
* [`BitcoinBlock#encode(args)`](#BitcoinBlock_encode)
* [`BitcoinBlock.HASH_NO_WITNESS`](#BitcoinBlock__HASH_NO_WITNESS)
* [`BitcoinBlock.fromPorcelain(porcelain)`](#BitcoinBlock__fromPorcelain)
* [`BitcoinBlock.decode(bytes)`](#BitcoinBlock__decode)
* [`BitcoinBlock.decode(bytes, strictLengthUsage)`](#BitcoinBlock__decode)
* [`BitcoinBlock.decodeBlockHeaderOnly(bytes)`](#BitcoinBlock__decodeBlockHeaderOnly)
* [`class BitcoinTransaction`](#BitcoinTransaction)
* [Constructor: `BitcoinTransaction()`](#BitcoinTransaction_new)
Expand All @@ -149,7 +149,7 @@ Only the non-redundant parts of a porcelain form of the objects are required to
* [`BitcoinTransaction#encode(args)`](#BitcoinTransaction_encode)
* [`BitcoinTransaction.isPorcelainSegWit(porcelain)`](#BitcoinTransaction__isPorcelainSegWit)
* [`BitcoinTransaction.fromPorcelain(porcelain)`](#BitcoinTransaction__fromPorcelain)
* [`BitcoinTransaction.decode(bytes)`](#BitcoinTransaction__decode)
* [`BitcoinTransaction.decode(bytes, strictLengthUsage)`](#BitcoinTransaction__decode)
* [`class BitcoinTransactionIn`](#BitcoinTransactionIn)
* [Constructor: `BitcoinTransactionIn(prevout, scriptSig, sequence)`](#BitcoinTransactionIn_new)
* [`BitcoinTransactionIn#toPorcelain()`](#BitcoinTransactionIn_toPorcelain)
Expand Down Expand Up @@ -437,7 +437,7 @@ structure.
**Return value** _(`BitcoinBlock`)_

<a name="BitcoinBlock__decode"></a>
### `BitcoinBlock.decode(bytes)`
### `BitcoinBlock.decode(bytes, strictLengthUsage)`

Decode a [`BitcoinBlock`](#BitcoinBlock) from the raw bytes of the block. Such data
in hex form is available directly from the bitcoin cli:
Expand All @@ -449,6 +449,10 @@ to parse just the 80-byte header data.
**Parameters:**

* **`bytes`** _(`Uint8Array|Buffer`)_: the raw bytes of the block to be decoded.
* **`strictLengthUsage`** _(`boolean`)_: ensure that all bytes were consumed during decode.
This is useful when ensuring that bytes have been properly decoded where there is
uncertainty about whether the bytes represent a Block or not. Switch to `true` to be
sure.

**Return value** _(`BitcoinBlock`)_

Expand Down Expand Up @@ -644,7 +648,7 @@ a correct BitcoinTransaction. This could be one of:
**Return value** _(`BitcoinTransaction`)_

<a name="BitcoinTransaction__decode"></a>
### `BitcoinTransaction.decode(bytes)`
### `BitcoinTransaction.decode(bytes, strictLengthUsage)`

Decode a [`BitcoinTransaction`](#BitcoinTransaction) from the raw bytes of the transaction.
Normally raw transaction data isn't available in detached form, although the
Expand All @@ -655,6 +659,10 @@ each element of the `tx` array. It may also come from the
**Parameters:**

* **`bytes`** _(`Uint8Array|Buffer`)_: the raw bytes of the transaction to be decoded.
* **`strictLengthUsage`** _(`boolean`)_: ensure that all bytes were consumed during decode.
This is useful when ensuring that bytes have been properly decoded where there is
uncertainty about whether the bytes represent a Transaction or not. Switch to `true`
to be sure.

**Return value** _(`BitcoinTransaction`)_

Expand Down
12 changes: 6 additions & 6 deletions bitcoin-block.js
Expand Up @@ -6,12 +6,12 @@ const BitcoinOutPoint = require('./classes/OutPoint')
const coding = require('./coding')(require('./classes/'))
const { toHashHex, fromHashHex, COIN, dblSha2256, merkle, merkleRoot } = require('./classes/class-utils')

BitcoinBlock.decode = function decodeBlock (buf) {
return coding.decodeType(buf, 'CBlockHeader')
BitcoinBlock.decode = function decodeBlock (buf, strictLengthUsage) {
return coding.decodeType(buf, 'CBlockHeader', strictLengthUsage)
}

BitcoinBlock.decodeHeaderOnly = function decodeBlockHeaderOnly (buf) {
return coding.decodeType(buf, 'CBlockHeader__Only')
BitcoinBlock.decodeHeaderOnly = function decodeBlockHeaderOnly (buf, strictLengthUsage) {
return coding.decodeType(buf, 'CBlockHeader__Only', strictLengthUsage)
}

BitcoinBlock.prototype.encode = function (...args) {
Expand All @@ -22,8 +22,8 @@ BitcoinBlock.BitcoinBlockHeaderOnly.prototype.encode = function (...args) {
return Buffer.concat([...coding.encodeType(this, args)])
}

BitcoinTransaction.decode = function decodeTransaction (buf) {
return coding.decodeType(buf, 'CTransaction')
BitcoinTransaction.decode = function decodeTransaction (buf, strictLengthUsage) {
return coding.decodeType(buf, 'CTransaction', strictLengthUsage)
}

BitcoinTransaction.prototype.encode = function (...args) {
Expand Down
4 changes: 4 additions & 0 deletions classes/Block.js
Expand Up @@ -480,6 +480,10 @@ module.exports.BitcoinBlockHeaderOnly = BitcoinBlockHeaderOnly
* to parse just the 80-byte header data.
*
* @param {Uint8Array|Buffer} bytes - the raw bytes of the block to be decoded.
* @param {boolean} strictLengthUsage - ensure that all bytes were consumed during decode.
* This is useful when ensuring that bytes have been properly decoded where there is
* uncertainty about whether the bytes represent a Block or not. Switch to `true` to be
* sure.
* @name BitcoinBlock.decode
* @function
* @returns {BitcoinBlock}
Expand Down
4 changes: 4 additions & 0 deletions classes/Transaction.js
Expand Up @@ -339,6 +339,10 @@ BitcoinTransaction.HASH_NO_WITNESS = HASH_NO_WITNESS
* {@link BitcoinTransaction#encode} method.
*
* @param {Uint8Array|Buffer} bytes - the raw bytes of the transaction to be decoded.
* @param {boolean} strictLengthUsage - ensure that all bytes were consumed during decode.
* This is useful when ensuring that bytes have been properly decoded where there is
* uncertainty about whether the bytes represent a Transaction or not. Switch to `true`
* to be sure.
* @name BitcoinTransaction.decode
* @returns {BitcoinTransaction}
* @function
Expand Down
5 changes: 4 additions & 1 deletion coding.js
Expand Up @@ -206,7 +206,7 @@ function isArrayType (typ) {
return arrayDesc
}

function decodeType (buf, type) {
function decodeType (buf, type, strictLengthUsage) {
let pos = 0
const state = {}

Expand Down Expand Up @@ -413,6 +413,9 @@ function decodeType (buf, type) {
}

const block = decoder.readType(type)
if (strictLengthUsage && pos !== buf.length) {
throw new Error('decode did not consume all available bytes as expected')
}
return block
}

Expand Down
2 changes: 1 addition & 1 deletion test/test.js
Expand Up @@ -197,7 +197,7 @@ function test (hash, block, expected) {
// verify _only_ the first 80 bytes and that we can parse basic data
verifyHeader(block, expected)

const decoded = BitcoinBlock.decode(block) // decode full block
const decoded = BitcoinBlock.decode(block, true) // decode full block, strict length consumption

// ---------------------------------------------------------------------------
// test the serialized minimum form, where the `tx` array is just the txids
Expand Down

0 comments on commit da3bb14

Please sign in to comment.