An open API service indexing awesome lists of open source software.

https://github.com/ipld/js-bitcoin

JavaScript Bitcoin data multiformats codecs and utilities for IPLD
https://github.com/ipld/js-bitcoin

Last synced: 11 months ago
JSON representation

JavaScript Bitcoin data multiformats codecs and utilities for IPLD

Awesome Lists containing this project

README

          

# IPLD for Bitcoin

**JavaScript Bitcoin data multiformats codecs and utilities for IPLD**

## About

This codec is intended to be used with **[multiformats](https://github.com/multiformats/js-multiformats)**. It provides decode and encode functionality for the Bitcoin native format to and from IPLD.

The following IPLD codecs are available; they each support `encode()` and `decode()` functionality compatible with the multiformats `BlockCodec` type.

## Codecs

### `bitcoin-block`

`bitcoin-block` / `0xb0` is the Bitcoin block **header**, commonly identified by "Bitcoin block identifiers" (hashes with leading zeros).

```js
import * as bitcoinBlock from '@ipld/bitcoin/block'
```

### `bitcoin-tx`

`bitcoin-tx` / `0xb1` are Bitcoin transactions _and_ nodes in a binary merkle tree, the tip of which is referenced by the Bitcoin block header.

```js
import * as bitcoinTx from '@ipld/bitcoin/tx'
```

### `bitcoin-witness-commitment`

`bitcoin-witness-commitment` / `0xb2` is the Bitcoin witness commitment that is used to reference transactions with intact witness data (a complication introduced by [SegWit](https://en.wikipedia.org/wiki/SegWit)).

```js
import * as bitcoinWitnessCommitment from '@ipld/bitcoin/witness-commitment'
```

## Hasher

The following multihash is available, compatible with the multiformats `MultihashHasher` type.

### `dbl-sha2-256`

`dbl-sha2-256` / `0x56` is a double SHA2-256 hash: `SHA2-256(SHA2-256(bytes))`, used natively across all Bitcoin blocks, forming block identifiers, transaction identifiers and hashes and binary merkle tree nodes.

```js
import * as dblSha2256 from '@ipld/bitcoin/dbl-sha2-256'
```

## Utilities

In addition to the multiformats codecs and hasher, utilities are also provided to convert between Bitcoin hash identifiers and CIDs and to convert to and from full Bitcoin raw block data to a full collection of IPLD blocks. Additional conversion functionality for bitcoin raw data and the `bitcoin-cli` JSON format is provided by the **[bitcoin-block](https://github.com/rvagg/js-bitcoin-block)** library.

See the **API** section below for details on the additional utility functions.

## Example

This example reads Bitcoin IPLD blocks from a CAR file; assuming that CAR contains a complete (enough) graph representing a Bitcoin block (whose identifier is supplied as the second argument) and its transactions, it navigates to the first transaction (the Coinbase) and prints the `scriptSig` as UTF-8. This is often used to store arbitrary messages and other "graffiti".

Running this example on the Genesis block (CAR provided in this project: example-genesis.car) produces the following output:

```
$ node example.js ./example-genesis.car 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
��EThe Times 03/Jan/2009 Chancellor on brink of second bailout for banks
```

```js
import fs from 'fs'
import * as bitcoinBlock from '@ipld/bitcoin/block'
import * as bitcoinTx from '@ipld/bitcoin/tx'
import { CarReader } from '@ipld/car'

// Assumes a CAR with at least one full Bitcoin block represented as IPLD blocks
// and a "blockId" which is the commonly used Bitcoin block identifier (32-byte
// digest in hexadecimal, with leading zeros).
async function run (pathToCar, blockId) {
const reader = await CarReader.fromIterable(fs.createReadStream(pathToCar))
const headerCid = bitcoinBlock.blockHashToCID(blockId)
const header = bitcoinBlock.decode((await reader.get(headerCid)).bytes)

// navigate the transaction binary merkle tree to the first transaction, the coinbase,
// which will be at the leftmost side of the tree.
let txCid = header.tx
let tx
while (true) {
tx = bitcoinTx.decode((await reader.get(txCid)).bytes)
if (!Array.isArray(tx)) { // is not an inner merkle tree node
break
}
txCid = tx[0] // leftmost side of the tx binary merkle
}

// convert the scriptSig to UTF-8 and cross our fingers that there's something
// interesting in there
console.log(Buffer.from(tx.vin[0].coinbase, 'hex').toString('utf8'))
}

run(process.argv[2], process.argv[3]).catch((err) => {
console.error(err.stack)
process.exit(1)
})
```

## Usage

In the API docs below, the names denote the export locations, such that they may be obtained by the following:

```js
// The whole bundle (note this object also includes additional properties that
// can be used to access all the others)
import * as Bitcoin from '@ipld/bitcoin'
// the `bitcoin-block` / `0xb0` codec
import * as BitcoinBlock from '@ipld/bitcoin/block'
// the `bitcoin-tx` / `0xb1` codec
import * as BitcoinTransaction from '@ipld/bitcoin/tx'
// the `bitcoin-witness-commitment` / `0xb2` codec
import * as BitcoinWitnessCommitment from '@ipld/bitcoin/witness-commitment'
// the `dbl-sha2-256` / `0x56` multihasher
import * as DblSha2256 from '@ipld/bitcoin/dbl-sha2-256'
```

## API

### Contents

* [`Bitcoin.deserializeFullBitcoinBytes()(bytes)`](#Bitcoin__deserializeFullBitcoinBytes____)
* [`Bitcoin.serializeFullBitcoinBytes()(obj)`](#Bitcoin__serializeFullBitcoinBytes____)
* [`Bitcoin.cidToHash()(cid)`](#Bitcoin__cidToHash____)
* [`Bitcoin.encodeAll()`](#Bitcoin__encodeAll____)
* [`Bitcoin.assemble()`](#Bitcoin__assemble____)
* [`BitcoinBlock.encode()`](#BitcoinBlock__encode____)
* [`BitcoinBlock.decode()`](#BitcoinBlock__decode____)
* [`BitcoinBlock.name`](#BitcoinBlock__name)
* [`BitcoinBlock.code`](#BitcoinBlock__code)
* [`BitcoinBlock.blockHashToCID()`](#BitcoinBlock__blockHashToCID____)
* [`BitcoinTransaction.encode()`](#BitcoinTransaction__encode____)
* [`BitcoinTransaction.encodeNoWitness()`](#BitcoinTransaction__encodeNoWitness____)
* [`BitcoinTransaction.encodeAll()`](#BitcoinTransaction__encodeAll____)
* [`BitcoinTransaction.encodeAllNoWitness()`](#BitcoinTransaction__encodeAllNoWitness____)
* [`BitcoinTransaction.decode()`](#BitcoinTransaction__decode____)
* [`BitcoinTransaction.name`](#BitcoinTransaction__name)
* [`BitcoinTransaction.name`](#BitcoinTransaction__name)
* [`BitcoinTransaction.txHashToCID()`](#BitcoinTransaction__txHashToCID____)
* [`BitcoinWitnessCommitment.encode()`](#BitcoinWitnessCommitment__encode____)
* [`BitcoinWitnessCommitment.decode()`](#BitcoinWitnessCommitment__decode____)
* [`BitcoinWitnessCommitment.name`](#BitcoinWitnessCommitment__name)
* [`BitcoinWitnessCommitment.code`](#BitcoinWitnessCommitment__code)
* [`DblSha2256.name`](#DblSha2256__name)
* [`DblSha2256.code`](#DblSha2256__code)
* [`DblSha2256.encode()`](#DblSha2256__encode____)
* [`DblSha2256.digest()`](#DblSha2256__digest____)


### `Bitcoin.deserializeFullBitcoinBytes()(bytes)`

* `bytes` `(Uint8Array)`: a binary form of a Bitcoin block graph

* Returns: `BlockPorcelain`: an object representation of the full Bitcoin block graph

Instantiate a full object form from a full Bitcoin block graph binary representation. This binary form is typically extracted from a Bitcoin network node, such as with the Bitcoin Core `bitcoin-cli` `getblock 0` command (which outputs hexadecimal form and therefore needs to be decoded prior to handing to this function). This full binary form can also be obtained from the utility [`assemble`](#assemble) function which can construct the full graph form of a Bitcoin block from the full IPLD block graph.

The object returned, if passed through `JSON.stringify()` should be identical to the JSON form provided by the Bitcoin Core `bitcoin-cli` `getblock 2` command (minus some chain-context elements that are not possible to derive without the full blockchain).


### `Bitcoin.serializeFullBitcoinBytes()(obj)`

* `obj` `(BlockPorcelain)`: a full JavaScript object form of a Bitcoin block graph

* Returns: `Uint8Array`: a binary form of the Bitcoin block graph

Encode a full object form of a Bitcoin block graph into its binary
equivalent. This is the inverse of
[`Bitcoin.deserializeFullBitcoinBytes()`](#Bitcoin__deserializeFullBitcoinBytes____) and should produce the exact
binary representation of a Bitcoin block graph given the complete input.

The object form must include both the header and full transaction (including
witness data) data for it to be properly serialized.

As of writing, the witness merkle nonce is not currently present in the JSON
output from Bitcoin Core's `bitcoin-cli`. See
https://github.com/bitcoin/bitcoin/pull/18826 for more information. Without
this nonce, the exact binary form cannot be fully generated.


### `Bitcoin.cidToHash()(cid)`

* `cid` `(CID|string)`: a CID

* Returns: `string`: a hexadecimal big-endian representation of the identifier.

Convert a CID to a Bitcoin block or transaction identifier. This process is
the reverse of `blockHashToCID()` and `txHashToCID()` and involves extracting
and decoding the multihash from the CID, reversing the bytes and presenting
it as a big-endian hexadecimal string.

Works for both block identifiers and transaction identifiers.


### `Bitcoin.encodeAll()`

* `block` `(BlockPorcelain)`

* Returns: `IterableIterator<{cid: CID, bytes: Uint8Array}>`

Encodes a full Bitcoin block, as presented in `BlockPorcelain` form (which is
available as JSON output from the `bitcoin-cli` tool—see the `bitcoin-block`
npm package for more information) into its constituent IPLD blocks. This
includes the header, the transaction merkle intermediate nodes, the
transactions and SegWit forms of the transaction merkle and nodes if present
along with the witness commitment block if required.


### `Bitcoin.assemble()`

* `loader` `(IPLDLoader)`: an IPLD block loader function that takes a CID argument and returns a `Uint8Array` containing the binary block data for that CID
* `blockCid` `(CID)`: a CID of type `bitcoin-block` pointing to the Bitcoin block header for the block to be assembled

* Returns: `Promise<{deserialized:BlockPorcelain, bytes:Uint8Array}>`: an object containing two properties, `deserialized` and `bytes` where `deserialized` contains a full JavaScript instantiation of the Bitcoin block graph and `bytes` contains a `Uint8Array` with the binary representation of the graph.

Given a CID for a `bitcoin-block` Bitcoin block header and an IPLD block
loader that can retrieve Bitcoin IPLD blocks by CID, re-assemble a full
Bitcoin block graph into both object and binary forms. This is the inverse
of the [`Bitcoin.encodeAll()`](#Bitcoin__encodeAll____) function in that it puts the
`BitcoinPorcelain` back together. A JSON form of this output should match
the output provided by `bitcoin-cli` (with some possible minor differences).

The loader should be able to return the binary form for `bitcoin-block`,
`bitcoin-tx` and `bitcoin-witness-commitment` CIDs.


### `BitcoinBlock.encode()`

* `node` `(BitcoinHeader)`

* Returns: `ByteView`

**`bitcoin-block` / `0xb0` codec**: Encodes an IPLD node representing a
Bitcoin header object into byte form.


### `BitcoinBlock.decode()`

* `data` `(ByteView)`

* Returns: `BitcoinHeader`

**`bitcoin-block` / `0xb0` codec**: Decodes a bytes form of a Bitcoin header
into an IPLD node representation.


### `BitcoinBlock.name`

**`bitcoin-block` / `0xb0` codec**: the codec name


### `BitcoinBlock.code`

**`bitcoin-block` / `0xb0` codec**: the codec code


### `BitcoinBlock.blockHashToCID()`

* `blockHash` `(string)`: a string form of a block hash

* Returns: `CID`: a CID object representing this block identifier.

Convert a Bitcoin block identifier (hash) to a CID. The identifier should be in big-endian form, i.e. with leading zeros.

The process of converting to a CID involves reversing the hash (to little-endian form), encoding as a `dbl-sha2-256` multihash and encoding as a `bitcoin-block` multicodec. This process is reversable, see [`cidToHash`](#cidToHash).


### `BitcoinTransaction.encode()`

* `node` `(BitcoinTransaction|BitcoinTransactionMerkleNode)`

* Returns: `ByteView<(BitcoinTransaction|BitcoinTransactionMerkleNode)>`

**`bitcoin-tx` / `0xb1` codec**: Encodes an IPLD node representing a
Bitcoin transaction object into byte form.

Note that a `bitcoin-tx` IPLD node can either be a full transaction with or
without SegWit data, or an intermediate transaction Merkle tree node; in
which case it is simply an array of two CIDs.


### `BitcoinTransaction.encodeNoWitness()`

* `node` `(BitcoinTransaction)`

* Returns: `ByteView`

Same as [`BitcoinTransaction.encode()`](#BitcoinTransaction__encode____) but will explictly exclude any
witness (SegWit) data from the output. This is necessary for encoding SegWit
blocks since transactions must be stored both with and without witness data
to correctly represent the full content addressed structure.


### `BitcoinTransaction.encodeAll()`

* `obj` `(BlockPorcelain)`

* Returns:

Encodes all transactions in a complete `BlockPorcelain` (see the
`bitcoin-block` npm package for details on this type) representation of an
entire Bitcoin transaction; including intermediate Merkle tree nodes.

Intermediate Merkle tree nodes won't have the `transaction` property on the
output as they aren't full transactions and their `bytes` will have a length
of 64.


### `BitcoinTransaction.encodeAllNoWitness()`

* `obj` `(BlockPorcelain)`

* Returns:

Same as [`BitcoinTransaction.encodeAll()`](#BitcoinTransaction__encodeAll____) but only encodes non-SegWit
transaction data, that is, transactions without witness data and no secondary
SegWit transactions Merkle tree.


### `BitcoinTransaction.decode()`

* `data` `(ByteView<(BitcoinTransaction|BitcoinTransactionMerkleNode)>)`

* Returns: `BitcoinTransaction|BitcoinTransactionMerkleNode`

**`bitcoin-block` / `0xb0` codec**: Decodes a bytes form of a Bitcoin
transaction into an IPLD node representation.

Note that a `bitcoin-tx` IPLD node can either be a full transaction with or
without SegWit data, or an intermediate transaction Merkle tree node; in
which case it is simply an array of two CIDs. As byte form, an intermediate
Merkle tree node is a fixed 64-bytes.


### `BitcoinTransaction.name`

**`bitcoin-tx` / `0xb1` codec**: the codec name


### `BitcoinTransaction.name`

**`bitcoin-tx` / `0xb1` codec**: the codec name


### `BitcoinTransaction.txHashToCID()`

* `txHash` `(string)`: a string form of a transaction hash

* Returns: `CID`: A CID (`multiformats.CID`) object representing this transaction identifier.

Convert a Bitcoin transaction identifier (hash) to a CID. The identifier should be in big-endian form as typically understood by Bitcoin applications.

The process of converting to a CID involves reversing the hash (to little-endian form), encoding as a `dbl-sha2-256` multihash and encoding as a `bitcoin-tx` multicodec. This process is reversable, see [`cidToHash`](#cidToHash).


### `BitcoinWitnessCommitment.encode()`

* `node` `(BitcoinWitnessCommitment)`

* Returns: `ByteView`

**`bitcoin-witness-commitment` / `0xb2` codec**: Encodes an IPLD node
representing a Bitcoin witness commitment object into byte form.

The object is expected to be in the form
`{witnessMerkleRoot:CID, nonce:Uint8Array}` where the `witnessMerkleRoot`
may be null.


### `BitcoinWitnessCommitment.decode()`

* `data` `(ByteView)`

* Returns: `BitcoinWitnessCommitment`

**`bitcoin-witness-commitment` / `0xb2` codec**: Decodes a bytes form of a
Bitcoin witness commitment into an IPLD node representation.
.

The returned object will be in the form
`{witnessMerkleRoot:CID, nonce:Uint8Array}` where the `witnessMerkleRoot`
may be null.


### `BitcoinWitnessCommitment.name`

**`bitcoin-witness-commitment` / `0xb2` codec**: the codec name


### `BitcoinWitnessCommitment.code`

**`bitcoin-witness-commitment` / `0xb2` codec**: the codec code


### `DblSha2256.name`

**`dbl-sha2-256` / `0x56` multihash**: the multihash name


### `DblSha2256.code`

**`dbl-sha2-256` / `0x56` multihash**: the multihash code


### `DblSha2256.encode()`

* `bytes` `(Uint8Array)`: a Uint8Array

* Returns: `Uint8Array`: a 32-byte digest

**`dbl-sha2-256` / `0x56` multihash**: Encode bytes using the multihash
algorithm, creating raw 32-byte digest _without_ multihash prefix.


### `DblSha2256.digest()`

* `input` `(Uint8Array)`

* Returns:

**`dbl-sha2-256` / `0x56` multihash**: Encode bytes using the multihash
algorithm, creating multihash `Digest` (i.e. with multihash prefix).

## License

Licensed under either of

* Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / http://www.apache.org/licenses/LICENSE-2.0)
* MIT ([LICENSE-MIT](LICENSE-MIT) / http://opensource.org/licenses/MIT)

### Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.