If we want to search for the occurrence of “1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5” as the first item of its own cell, we simply need to filter for cells that simultaneously match i of 0 and s of 1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5, using $elemMatch .

{

"v": 3,

"q": {

"find": {

"out.tape.cell": {

"$elemMatch": {

"i": 0,

"s": "1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5"

}

}

},

"project": {

"out.tape.cell.s": 1

},

"limit": 5

},

"r": {

"f": "[ .[] | .out[0].tape[] | { cell1: .cell } ]"

}

}

3. OP_RETURN vs. OP_FALSE OP_RETURN Becomes a Non-problem.

This local scoping approach gives us a cool side effect. If you use BOB, the OP_RETURN vs. OP_FALSE OP_RETURN problem becomes a non-problem.

In case you weren’t aware of this, one thing all OP_RETURN protocol developers need to keep in mind is that in 2020 the OP_RETURN goes from a meaningless opcode used mainly for attaching arbitrary data, to an actual programming language construct.

It goes back to Bitcoin’s original design, where it is now an actual “return” statement. This will open doors to many interesting features, because now functions can have return values.

But this also means there may be vulnerabilities if you don’t fully understand the implication. This is why it is recommended that developers start using OP_FALSE OP_RETURN instead of just OP_RETURN . You can read more about this here:

Anyway, if we wanted to support both the legacy OP_RETURN and the new OP_FALSE OP_RETURN options using the existing Planaria nodes such as Genesis and Babel, we would have to write an $or query that takes into account both cases (because now OP_RETURN can appear as the first opcode but also the second after OP_FALSE )

But because BOB chunks the tape out into multiple cells and keeps local scope positional index, you no longer need to worry about this, because your queries will be against local scope, instead of global scope. Also because you can query without even worrying about positional index.

To clarify, the following query will return exactly the same result no matter whether your output starts with OP_RETURN or OP_FALSE OP_RETURN because in both cases they will belong to their own cell and don’t affect the local position of “1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5” within its own cell scope.

{

"v": 3,

"q": {

"find": {

"out.tape.cell": {

"$elemMatch": {

"i": 0,

"s": "1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5"

}

}

}

}

}

4. Global positional index query

While the local positional index query is very useful, this doesn’t mean the global positional index is gone, you can still query by global index.

If you used to use the following query (look for index 1):

{

"v": 3,

"q": {

"find": {

"out.s1": "19HxigV4QyBv3tHpQVcUEQyq1pzZVdoAut"

},

"limit": 10

}

}

Now you can do the following, because each push data in a cell has an additional attribute named i which keeps the global positional index:

{

"v": 3,

"q": {

"find": {

"out.tape.cell":{

"$elemMatch": {

"ii": 1,

"s": "19HxigV4QyBv3tHpQVcUEQyq1pzZVdoAut"

}

}

},

"limit": 10

}

}

5. A dedicated human-readable OPCODE attribute

In 2020, all script limits will be gone and we will be able to finally use Bitcoin script as an actual programming language.

I am sure there will be lots of need to query and filter these NON-OP_RETURN Opcodes as well. Until now, the opcodes were stored under b attributes. This was not the cleanest design decision because this meant b variables could be used for both base64 encoded pushdata, but also to store opcodes. For example an OP_RETURN opcode may have looked like this:

{

b0: {

"op": 106

},

..

}

but at the same time, it was being used to store base64, like this:

{

b0: {

"op": 106

},

"b1": "MTlIeGlnVjRReUJ2M3RIcFFWY1VFUXlxMXB6WlZkb0F1dA",

"s1": "19HxigV4QyBv3tHpQVcUEQyq1pzZVdoAut",

..

}

This is confusing, so with this new schema, there are two additional attributes: op (for opcode number) and ops (for opcode string). Here’s an example:

[

{

"cell": [

{

"op": 0,

"ops": "OP_0",

"ii": 0,

"i": 0

},

{

"op": 106,

"ops": "OP_RETURN",

"ii": 1,

"i": 1

}

]

},

..

]

This makes it much more human readable, and also the rule is much more consistent. Now the attribute naming rule is straight forward:

b : base64 encoded push data

: base64 encoded push data s : UTF8 encoded push data

: UTF8 encoded push data op : opcode number

: opcode number ops : opcode string

Today we don’t have access to many different types of Opcodes, but it’s exciting to just imagine being able to make queries like this in the near future:

{

"v": 3,

"q": {

"find": {

"out.tape.cell.ops": "OP_CODESEPARATOR"

}

}

}

7. Conclusion

Because BOB is a serialization format, and is simply too convenient for dealing with OP_RETURN scripts, you will probably start to see this show up in other future Planaria endpoints as well as other related systems such as Bitbus, etc.

This is the first version of BOB and it will keep evolving based on usage patterns and feedback, so please don’t hesitate to send your questions and feedback.

Try Bob: