BMAPjs
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.
Why this exists
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.
Pre-requisites
- A BOB formatted transaction. If you only have a raw transaction you can convert to BOB using BPU. More information here
- npm / yarn
Install
npm install bmapjs
or
yarn add bmapjs
Importing
Yopu can import bmap using require
var { BMAP } = require('bmapjs')
or using esm module import
import { BMAP, TransformTx } from 'bmapjs'
Using in the browser
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>
Other languages
Go
Demo
Examples
Usage
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)
BMAP (Transaction 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]
in: [{
i: 0,
e: {
}
}],
out: [ ...],
"AIP": [{
...
}],
"B": [{
}, {
}],
"BAP": [{
}],
"MAP": [{
}, {
}],
"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.
Adding other protocols
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
.
Extending the BMAP class
You can also easily add new handlers for processing any type of bitcom output.
import { BMAP } from 'bmapjs'
const bmap = new BMAP()
const opReturnSchema = [{}]
const handler = function (dataObj, cell, tape, tx) {
}
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);
const handler2 = function(dataObj, cell, tape, tx) {
bmapOpReturnSchemaHandler('TEST', opReturnSchema, dataObj, cell, tape, tx);
}
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.
Change log
v0.4
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'
Protocols
Planarias
- BOB
- BMAP (a public BMAPjs pre-formatted planaria indexing MAP, BITPIC, BITKEY, transactions)
- MOM (enables additional fields for MetaNet)
Example Responses
B
example:
{
"B": {
"content": "{\"name\":\"myname\",\"bio\":\"<p>bio</p>\\n\",\"logo\":\"\"}",
"content-type": "application/json",
"encoding": "utf-8",
"filename": "matter.profile.json"
}
}
BAP
example:
{
"BAP": [
{
"type": "ATTEST",
"hash": "cf39fc55da24dc23eff1809e6e6cf32a0fe6aecc81296543e9ac84b8c501bac5",
"sequence": "0"
}
]
}
MAP
example:
{
"MAP": [
{
"cmd": "SET",
"app": "metalens",
"type": "comment",
"url": "https://twitter.com/",
"user": "Satchmo"
}
]
}
MetaNet
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.
BOB Data Source
{
"METANET": [
{
"node": {
"a": "15ZCvDUJ6wG1hoiSyw1ftfiRhpKTVGLMnn",
"tx": "70bcbe4dc1ff796389e3de4f5f151cff7eb4a172142468a79677c703afd930b9",
"id": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"parent": {
"a": "15ZCvDUJ6wG1hoiSyw1ftfiRhpKTVGLMnn",
"tx": "577cc5de372f65e33045745129699139568eb46b2ef09d2ca5bf44a9bcb07c71",
"id": "59f2e83ac0607d44d764b9040aaa8dd8741e6169444739464f97422055ad001c"
}
}
]
}
MOM Data Source
{
"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
bitkey_signature
and user_signature
are in base64 encoded binary format
{
"BITKEY": [
{
"bitkey_signature": "SDQwdkEyVnN0emtIY2VnYXJVTm1WUm1wQ3ZLUVBSdXR4KzczdG9Jcm4vMWxRWU9aQ1lRQ0cyaFhBdHRQRFl0L0h2KzE0dWtUZ25MWVh1UUNsTFp6blBnPQ==",
"user_signature": "SUxzZWpEWXVwMlBEYjltdnJET1dSaWxMSy9Xd1BtVlRiazFOWnZnUHZiczRWVzYyenM1MFY5c3E0akdrQm8yeDlLOG9jSE5acTlLd1hRMkREV0V2OGNjPQ==",
"paymail": "oktets@moneybutton.com",
"pubkey": "0210fdec2372cb65dd9d6adb982101d9cdbb407d9f2e2d5be31cd9d59a561ccacf"
}
]
}
Bitcom
BITCOM commands
useradd, echo, su, route
{
"BITCOM": ["$", "echo", "delphe_test2", "to", "name"]
}
Bitpic
pubkey
and sig
fields are returned in base64 encoded binary format
{
"BITPIC": [
{
"paymail": "stockrt@moneybutton.com",
"pubkey": "AoAgqoMucQcdi7kyLHhN4y1HVCPMyVpcPrj75AAoFo/6",
"sig": "SVBJVzU3NnplSnUzODlKNTVPT0RSNjVvSlhDdldYTDY0SWtEa1dOQzNkZ0xBdGZGVUx0MlYzWW1OWkNUQTBsUlV1M2dJMlIrRkswT1JlUnl1Vm9SQjVZPQ=="
}
]
}
Unknown Protocols
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
}
]
]
}
Support Checklist
Known Issues
- Issue validating Twetch signatures with the AIP package (they use a slightly different signing scheme)
- HAIP validation is failing as of v0.4
Note: TXO Format Deprecation
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')
(async function () {
let bob = await BPU.parse({
tx: { r: rawtx }
});
const bmapjs = new BMAP();
let bmap = bmapjs.TransformTx(bob);
...
})()
Note: Convert raw tx to BOB using BPU
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)