JavaScript Bitcoin data multiformats codecs and utilities for IPLD
This codec is intended to be used with multiformats and @ipld/block. It provides decode and encode functionality for the Bitcoin native format to and from IPLD.
The primary usage of this library is as a codec added to a multiformats
object:
const multiformats = require('multiformats')()
multiformats.add(require('@ipld/bitcoin'))
The following multicodecs are registered:
bitcoin-block
/0xb0
: The Bitcoin block header, commonly identified by "Bitcoin block identifiers" (hashes with leading zeros).bitcoin-tx
/0xb1
: Bitcoin transactions and nodes in a binary merkle tree, the tip of which is referenced by the Bitcoin block header.bitcoin-witness-commitment
/0xb2
: The Bitcoin witness commitment that is used to reference transactions with intact witness data (a complication introduced by SegWit).
These multicodecs support encode()
and decode()
functionality through multiformats
.
The following multihash is registered:
dbl-sha2-256
/0x56
: 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.
In addition to the multiformats codecs and hash, 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 library.
See the API section below for details on the additional utility functions.
The previous incarnation of the Bitcoin codec for IPLD can be found at https://github.com/ipld/js-ipld-bitcoin.
const multiformats = require('multiformats/basics')
multiformats.add(require('@ipld/bitcoin'))
const CarDatastore = require('datastore-car')(multiformats)
const carDs = await CarDatastore.readFileComplete('/path/to/bundle/of/blocks.car')
const headerCid = ipldBitcoin.blockHashToCID(multiformats, hash)
const header = multiformats.decode(await carDs.get(headerCid), 'bitcoin-block')
// navigate the transaction binary merkle tree to the first transaction, the coinbase
let txCid = header.tx
let tx
while (true) {
tx = multiformats.decode(await carDs.get(txCid), 'bitcoin-tx')
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'))
deserializeFullBitcoinBinary(binary)
serializeFullBitcoinBinary(obj)
async blockToCar(multiformats, carWriter, obj)
cidToHash(multiformats, cid)
async assemble(multiformats, loader, blockCID)
blockHashToCID(multiformats)
txHashToCID(multiformats)
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 <identifier> 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
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 <identifier> 2
command (minus some chain-context elements that are not possible to derive without the full blockchain).
Parameters:
binary
(Uint8Array|Buffer
): a binary form of a Bitcoin block graph
Return value (object
): an object representation of the full Bitcoin block graph
Encode a full object form of a Bitcoin block graph into its binary equivalent. This is the inverse of deserializeFullBitcoinBinary
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 bitcoin/bitcoin#18826 for more information. Without this nonce, the exact binary form cannot be fully generated.
Parameters:
obj
(object
): a full JavaScript object form of a Bitcoin block graph
Return value (Buffer
): a binary form of the Bitcoin block graph
Extract all IPLD blocks from a full Bitcoin block graph and write them to a CAR archive.
This operation requires a full deserialized Bitcoin block graph, where the transactions in their full form (with witness data intact post-segwit), as typically presented in JSON form with the Bitcoin Core bitcoin-cli
command getblock <identifier> 2
or using one of the utilities here to instantiate a full object form.
The CAR archive should be created using datastore-car and should be capable of write operations.
Parameters:
multiformats
(object
): a multiformats object withdbl-sha2-256
multihash,bitcoin-block
,bitcoin-tx
andbitcoin-witness-commitment
multicodecs as well as thedag-cbor
multicodec which is required for writing the CAR header.carWriter
(object
): an initialized and writableCarDatastore
instance.obj
(object
): a full Bitcoin block graph.
Return value (object
): a CID for the root block (the header bitcoin-block
).
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.
Parameters:
multiformats
(object
): a multiformats objectcid
(object
): a CID (multiformats.CID
)
Return value (string
): a hexadecimal big-endian representation of the identifier.
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.
The loader should be able to return the binary form for bitcoin-block
, bitcoin-tx
and bitcoin-witness-commitment
CIDs.
Parameters:
multiformats
(object
): a multiformats object with the Bitcoin multicodec and multihash installedloader
(function
): an IPLD block loader function that takes a CID argument and returns aBuffer
orUint8Array
containing the binary block data for that CIDblockCID
(CID
): a CID of typebitcoin-block
pointing to the Bitcoin block header for the block to be assembled
Return value (object
): an object containing two properties, deserialized
and binary
where deserialized
contains a full JavaScript instantiation of the Bitcoin block graph and binary
contains a Buffer
with the binary representation of the graph.
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
.
Parameters:
multiformats
(object
): a multiformats object withdbl-sha2-256
multihash andbitcoin-block
multicodec registered
Return value (object
): a CID (multiformats.CID
) object representing this block 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
.
Parameters:
multiformats
(object
): a multiformats object withdbl-sha2-256
multihash andbitcoin-tx
multicodec registered
Return value (object
): A CID (multiformats.CID
) object representing this transaction identifier.
Licensed under either of
- Apache 2.0, (LICENSE-APACHE / http://www.apache.org/licenses/LICENSE-2.0)
- MIT (LICENSE-MIT / http://opensource.org/licenses/MIT)
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.