BMAPjs is a transaction parser for Bitcoin data protocols like B, MAP, BAP, METANET and AIP/HAIP. Supports multiple outputs, signature verification, and multiple instances in a single OP_RETURN. It also has support for some Script based protocols like Boost POW and 21e8
It processes BOB formatted transactions for B | MAP
OP_RETURN protocols. It processes transaction outputs and transforms them into self
descriptive js objects based on the OP_RETURN protocols it discovers in the data.
It supports B, MAP, AIP, METANET and a list of other popular protocols It ingests structured JSON objects in both BOB and MOM formats.
It is written in typescript and can be used both as an esm module or in the browser as commonjs via script tag. See the dist
folder for compiled outputs.
BOB format is a great way to express Bitcoin transaction outputs, especially those containing data protocols, but there are some problems. For example, each field has multiple options to choose from, be it a base64 encoded binary representation in the b field, a string value in the s field, or a hex value in the h field. Depending on the protocol and the specific field you might choose one or another. This means you have to have a full understanding of the protocol you're trying to use. That's where bmapjs comes in. It can recognize many protocols, structure the data according to their individual protocols, and provide an easy to use BMAP transaction object with no mysteries about the data.
- A BOB formatted transaction. If you only have a raw transaction you can convert to BOB using BPU. More information here
- npm / yarn
npm install bmapjs
or
yarn add bmapjs
Yopu can import bmap using require
var { BMAP } = require('bmapjs')
or using esm module import
import { BMAP, TransformTx } from 'bmapjs'
You can also use bmapjs in the browser by pointing to the .cjs file in the dist fodler.
<script src="dist/bmap.cjs"></script>
The CJS is by far the largest package since it includes dependencies. It is also possible to import in the browser using the module syntax.
<script src="dist/bmap" type="module">
import { TransformTx } from 'bmapjs'
... more code here
</script>
Turn a BOB or MOM formatted transaction into a BMAP tx. It will throw an error if the transaction is malformed.
in node:
import { TransformTx } from 'bmapjs'
try {
const bmapTx = await TransformTx(bob_or_mom_tx_object)
} catch (e) {
console.error(e)
}
or in the browser:
<script src="dist/bmap.cjs"></script>
bmap
will be available on the window object
const bmapTx = await bmap.TransformTx(bob_or_mom_tx_object)
After transforming the object will contain a key for each protocol found within the transaction. Each value will be an array. Most of the time the array will have only one value indicating the protocol was detected only once. However, in some cases where protocols will be used multiple times in the same transaction, the array will contain one object for each protocol instance detected.
{
"tx": {
"h": [TRANSACTION HASH],
"r": [RAW TRANSACTION]
},
"blk" {
"i": [BLOCK INDEX],
"h": [BLOCK HASH],
"t": [BLOCK TIME]
},
"in": [
INPUT1,
INPUT2,
INPUT3,
...
],
"out": [
OUTPUT1,
OUTPUT2,
OUTPUT3,
...
],
"coinbase": [COINBASE]
// array of transaction inputs
in: [{
// index
i: 0,
e: {
// transaction
}
}],
// transaction outputs
out: [ ...],
//
"AIP": [{
...
}],
"B": [{
// B protocol - output 1
// ...
}, {
// B protocol - output 2
// ...
}],
"BAP": [{
// Bitcoin Attestation Protocol
// ...
}],
"MAP": [{
// Magic Attribute Protocol - output 1
// ...
}, {
// Magic Attribute Protocol - output 2
// ...
}],
"1MAEepzgWei6zKmbsdQSy8wAYL5ySDizKo": [{
...
}]
}
If you want to use a raw transaction as your input, first transform it using BPU, then use bmapjs on the output. More information here.
Not all protocols available in bmap.js
are active by default. These are less used or older protocols, but they can be easily added at runtime.
import { BMAP, RON } from 'bmapjs'
const bmap = new BMAP()
bmap.addProtocolHandler(RON)
The protocols that are available, but not active by default are BITCOM
, BITKEY
, BITPIC
, RON
and SYMRE
.
You can also easily add new handlers for processing any type of bitcom output.
import { BMAP } from 'bmapjs'
const bmap = new BMAP()
const opReturnSchema = [{}] // optional
const handler = function (dataObj, cell, tape, tx) {
// dataObj is the object that all data is added to
// cell is the current cell being processed
// tape is the tape the cell is in
// tx is the total transaction
}
// addProtocolHandler(name, address, opReturnSchema, handler);
bmap.addProtocolHandler({
name: 'TEST',
address: '1FJrobAYoQ6qSVJH7yiawfaUmZ3G13q9iJ',
opReturnSchema,
handler,
})
bmap.transformTx(bob_or_mom_tx)
You can also use the default protocol handler, with a well defined query schema to make it even easier:
In this example the OP_RETURN has 4 fields (the first is the bitcom address and is not included in the definition).
OP_FALSE OP_RETURN
1FJrobAYoQ6qSVJH7yiawfaUmZ3G13q9iJ
<type>
<hash>
<sequence>
import { BMAP } from 'bmapjs';
import { bmapOpReturnSchemaHandler } from './utils';
const bmap = new BMAP();
const opReturnSchema = [{
{ type: 'string' },
{ hash: 'string' },
{ sequence: 'string' },
}];
const handler = bmapOpReturnSchemaHandler.bind(this, 'TEST', opReturnSchema);
// or
const handler2 = function(dataObj, cell, tape, tx) {
bmapOpReturnSchemaHandler('TEST', opReturnSchema, dataObj, cell, tape, tx);
}
// addProtocolHandler(name, address, opReturnSchema, handler);
bmap.addProtocolHandler({
name: 'TEST',
address: '1FJrobAYoQ6qSVJH7yiawfaUmZ3G13q9iJ',
opReturnSchema,
handler,
});
bmap.transformTx(bob_or_mom_tx);
See the current protocol handlers in src/protocols/
for examples on how to create your own handler.
There are several big changes in 0.4
-
Library is now typescript based
-
Using Parcel to build for multiple targets. bmap.cjs is a commonjs library for script tag imports. bmap.js is an esm module.
-
Breaking Change: Protocol data is now always in an array. For example bmapTx.MAP used to be an array only if more than one MAP instance was found in the transaction. Now it will always be an array and have only one element if there is only one match.
-
Exports have changed. Update your import statements.
Import syntax has changed from:
import BMAP from 'bmapjs'
to
import { BMAP } from 'bmapjs'
- BOB
- BMAP (a public BMAPjs pre-formatted planaria indexing MAP, BITPIC, BITKEY, transactions)
- MOM (enables additional fields for MetaNet)
example:
{
"B": {
"content": "{\"name\":\"myname\",\"bio\":\"<p>bio</p>\\n\",\"logo\":\"\"}",
"content-type": "application/json",
"encoding": "utf-8",
"filename": "matter.profile.json"
}
}
example:
{
"BAP": [
{
"type": "ATTEST",
"hash": "cf39fc55da24dc23eff1809e6e6cf32a0fe6aecc81296543e9ac84b8c501bac5",
"sequence": "0"
}
]
}
example:
{
"MAP": [
{
"cmd": "SET",
"app": "metalens",
"type": "comment",
"url": "https://twitter.com/",
"user": "Satchmo"
}
]
}
Response will include metanet relavent keys from MOM Planaria when available. When not available (BOB data source), bmap will provide the "parent" and "node" keys only. These will be provided in the same data structure as MOM Planaria.
{
"METANET": [
{
"node": {
"a": "15ZCvDUJ6wG1hoiSyw1ftfiRhpKTVGLMnn",
"tx": "70bcbe4dc1ff796389e3de4f5f151cff7eb4a172142468a79677c703afd930b9",
"id": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"parent": {
"a": "15ZCvDUJ6wG1hoiSyw1ftfiRhpKTVGLMnn",
"tx": "577cc5de372f65e33045745129699139568eb46b2ef09d2ca5bf44a9bcb07c71",
"id": "59f2e83ac0607d44d764b9040aaa8dd8741e6169444739464f97422055ad001c"
}
}
]
}
{
"METANET": [
{
"node": {
"a": "15ZCvDUJ6wG1hoiSyw1ftfiRhpKTVGLMnn",
"tx": "70bcbe4dc1ff796389e3de4f5f151cff7eb4a172142468a79677c703afd930b9",
"id": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"parent": {
"a": "15ZCvDUJ6wG1hoiSyw1ftfiRhpKTVGLMnn",
"tx": "577cc5de372f65e33045745129699139568eb46b2ef09d2ca5bf44a9bcb07c71",
"id": "59f2e83ac0607d44d764b9040aaa8dd8741e6169444739464f97422055ad001c"
},
"ancestor": [
{
"tx": "06ea0de45680b790d25372bc12b52c7e740e3b10f36d8aabd8b8a31e858a79c2",
"id": "9d9fee655e15decf639cf13617cadaf285ff15c5e5c593e1ff24c38c3c6edbcc",
"a": "1NXHduuvxtXVgsTyXjm9VrbV7Zy8BZ1JHr"
},
{
"tx": "59f2e83ac0607d44d764b9040aaa8dd8741e6169444739464f97422055ad001c",
"id": "44807057a5235e022477d7c75425132d31b0a53f86b9a98cd21dd681c42945f5",
"a": "1j161kyQh6jxWxFySch9Kk6YRt6ZK31jA"
}
],
"child": [],
"head": true
}
]
}
bitkey_signature
and user_signature
are in base64 encoded binary format
{
"BITKEY": [
{
"bitkey_signature": "SDQwdkEyVnN0emtIY2VnYXJVTm1WUm1wQ3ZLUVBSdXR4KzczdG9Jcm4vMWxRWU9aQ1lRQ0cyaFhBdHRQRFl0L0h2KzE0dWtUZ25MWVh1UUNsTFp6blBnPQ==",
"user_signature": "SUxzZWpEWXVwMlBEYjltdnJET1dSaWxMSy9Xd1BtVlRiazFOWnZnUHZiczRWVzYyenM1MFY5c3E0akdrQm8yeDlLOG9jSE5acTlLd1hRMkREV0V2OGNjPQ==",
"paymail": "[email protected]",
"pubkey": "0210fdec2372cb65dd9d6adb982101d9cdbb407d9f2e2d5be31cd9d59a561ccacf"
}
]
}
BITCOM commands
useradd, echo, su, route
{
"BITCOM": ["$", "echo", "delphe_test2", "to", "name"]
}
pubkey
and sig
fields are returned in base64 encoded binary format
{
"BITPIC": [
{
"paymail": "[email protected]",
"pubkey": "AoAgqoMucQcdi7kyLHhN4y1HVCPMyVpcPrj75AAoFo/6",
"sig": "SVBJVzU3NnplSnUzODlKNTVPT0RSNjVvSlhDdldYTDY0SWtEa1dOQzNkZ0xBdGZGVUx0MlYzWW1OWkNUQTBsUlV1M2dJMlIrRkswT1JlUnl1Vm9SQjVZPQ=="
}
]
}
When an unknown protocol is encountered, bmap will keep the incoming format and use the protocol prefix as the key name on the response object:
{
"1MAEepzgWei6zKmbsdQSy8wAYL5ySDizKo": [
[
{
"b": "MU1BRWVwemdXZWk2ekttYnNkUVN5OHdBWUw1eVNEaXpLbw==",
"s": "1MAEepzgWei6zKmbsdQSy8wAYL5ySDizKo",
"ii": 7,
"i": 0
}
],
[
{
"b": "bWF0dGVyLWNyZWF0ZS1wb3N0",
"s": "matter-create-post",
"ii": 8,
"i": 1
}
],
[
{
"b": "djE=",
"s": "v1",
"ii": 9,
"i": 2
}
],
[
{
"b": "aGVsbG8td29ybGQtcG9zdA==",
"s": "hello-world-post",
"ii": 10,
"i": 3
}
]
]
}
- 21E8
- AIP
- AIP validation
- B
- BAP
- BCAT
- Bitcom
- BITCOM-HASHED (Relay Paymail Signatures)
- Bitkey
- Bitpic
- Bitpic validation
- BOOST
- D
- HAIP
- HAIP validation
- MAP v1
- MAP v2
- MetaNet
- ORDER LOCK
- PSP
- RON
- SymRe
- Issue validating Twetch signatures with the AIP package (they use a slightly different signing scheme)
- HAIP validation is failing as of v0.4
Beginning with v0.2.0, bmapjs uses BOB as the source format for transaction processing. The previous versions of bmap used TXO formatted transactions. To use bmapjs with TXO data, use v0.1.5.
You can also use BPU to get a BOB format tx from a raw tx, and then parse it with bmapjs v0.2.0 or higher:
const BPU = require('bpu')
const BMAP = require('bmapjs')
// 'rawtx' is a raw transaction string
(async function () {
let bob = await BPU.parse({
tx: { r: rawtx }
});
const bmapjs = new BMAP();
let bmap = bmapjs.TransformTx(bob);
...
})()
bmapjs is a BOB formatted transaction processor. If you need to convert from a raw transaction, you can use the BPU package and pass the output in to bmap.TransformTx.
Raw Tx to Bob example:
const bobFromRawTx = async (rawtx) => {
return await BPU.parse({
tx: { r: rawtx },
split: [
{
token: { op: 106 },
include: 'l',
},
{
token: { op: 0 },
include: 'l',
},
{
token: { s: '|' },
},
],
})
}
and use it like this
const bobTx = await bobFromRawTx(ctx.transaction)
const bmapTx = await TransformTx(bobTx)