Data in Script
Bitcoin Script provides several OpCodes specifically targeted at pushing data onto the stack, to be part of any computation one might want to perform. There exists a very specific edge case wherein one can essentially use the bitcoin ledger for arbitrary data storage. It should be noted that to the extent that one has to pay the Transaction Processors (miners) a fee, and to the extent that this fee is calculated in terms of satoshis-per-byte, pushing large amounts of data into a Transaction gets expensive. There are however plenty of use-cases wherein one might push small amounts of data which has some monetisation strategy tied to it. The general model for pushing data onto the bitcoin ledger involves:
- making a Transaction Output unspendable by prepending OP_FALSE OP_RETURN as the first two OpCodes of the scriptPubkey (Locking Script),
- adding arbitrary data elements to the script, in accordance with some data model, using OP_PUSHDATA operations,
- setting the Output amount of satoshis to ZERO. Failing to take this step will permanently lock up funds.
Locking script (scriptPubkey)
Section titled “Locking script (scriptPubkey)”The scriptPubkey still needs to be a valid script. It therefore follows that after making the Output unspendable by prepending OP_FALSE OP_RETURN to the start of the script, one can add any arbitrary, valid bitcoin script operators and operands after that. For the purposes of using this method for the storage of data however, one might want to replicate a domain model as a series of OP_PUSHDATA operations, or even one OP_PUSHDATA operation which e.g. contains a serialised Protocol Buffer.
The general form of the script is therefore:
OP_FALSE OP_RETURN <data 1> <data 2> <data n>Unlocking script (scriptSig)
Section titled “Unlocking script (scriptSig)”Data-only Transaction Outputs do not have corresponding Unlocking Scripts. They are by definition unspendable.
A code example
Section titled “A code example”DartSV contains a very simple UnspendableDataLockBuilder, which takes a byte buffer and creates a data Output script of the following form:
OP_FALSE OP_RETURN <data>We can now take the Spending Transaction from the P2PKH section, and add a data Output to it as follows:
final dataLocker = UnspendableDataLockBuilder(utf8.encode('Hello, onchain data world!'));
final dataTx = TransactionBuilder() .spendFromTxnWithSigner(DefaultTransactionSigner(sighash, privateKey), previousTx, 0, TransactionInput.MAX_SEQ_NUMBER, P2PKHUnlockBuilder(privateKey.publicKey)) .spendToLockBuilder(dataLocker, BigInt.zero) // ZERO satoshis: nobody can ever spend this Output .sendChangeToPKH(myAddress) .withFeePerKb(100) .withOption(TransactionOption.DISABLE_DUST_OUTPUTS) // a 0-satoshi Output counts as dust to the checks .build(true);
dataTx.outputs[1].script.toString(type: 'asm'); // 0 OP_RETURN 48656c6c6f2c...In the preceding example:
- the data Output carries zero satoshis,
- our change goes back to our own Address, as before, and pays for the extra bytes in the fee,
withOption(TransactionOption.DISABLE_DUST_OUTPUTS)tells the builder’s checks that a 0-satoshi Output is intended. Without it,build(true)refuses the Transaction as having a dust Output.
Send it! As in Build a Transaction, ./scripts/rpc.sh sendrawtransaction <hex> puts it on our RegTest Teranode. Its data is on the ledger from the moment it is mined.
The DartSV data Outputs page shows several data pushes in one Output, reading the data back, and a spendable alternative that puts the data in front of an ordinary P2PKH script.
Data Serialisation Protocols
Section titled “Data Serialisation Protocols”Within the BitcoinSV ecosystem there are essentially two major methods of serialising Data-only Transaction Outputs. Both of these methods use a “protocol prefix” as the first data push following OP_FALSE OP_RETURN:
- _unwriter’s B:// protocol employs a Bitcoin Address as the “protocol prefix”,
- individuals and organisations claim an arbitrary constant value as their “protocol prefix”.
Bitcoin Address Prefix
Section titled “Bitcoin Address Prefix”- B:// protocol by _unwriter: https://github.com/unwriter/B
- Author Identity Protocol (a B:// protocol implementation): https://github.com/BitcoinFiles/AUTHOR_IDENTITY_PROTOCOL
Arbitrary Constant Prefix
Section titled “Arbitrary Constant Prefix”- Metanet Protocol (PDF): https://nchain.com/app/uploads/2019/06/The-Metanet-Technical-Summary-v1.0.pdf
- Metanet Blog: https://medium.com/nchain/edge-cases-the-metanet-blog-92f43c48490d
- Tokenized Envelope (employs Protocol Buffers): https://github.com/tokenized/envelope

