Data outputs
Bitcoin SV Transactions can carry data in their Outputs. The guide’s Data in script page compares the ways of doing it. DartSV has builders for the two common ones.
The examples use the keys and the fundingTransaction helper described in About the examples. This page’s examples share the following setup:
final myAddress = aliceKey.toAddress(networkType: NetworkType.TEST);final fundingTx = fundingTransaction(P2PKHLockBuilder.fromAddress(myAddress).getScriptPubkey(), BigInt.from(100000));final signer = DefaultTransactionSigner(sighashAll, aliceKey);Unspendable data: OP_FALSE OP_RETURN
Section titled “Unspendable data: OP_FALSE OP_RETURN”UnspendableDataLockBuilder writes OP_FALSE OP_RETURN <data>. The Output can never be spent, so we give it
zero satoshis.
final dataLocker = UnspendableDataLockBuilder(utf8.encode('hello from dartsv'));
final tx = TransactionBuilder() .spendFromTxnWithSigner(signer, fundingTx, 0, TransactionInput.MAX_SEQ_NUMBER, P2PKHUnlockBuilder(aliceKey.publicKey)) .spendToLockBuilder(dataLocker, BigInt.zero) // zero satoshis, since this Output can never be spent .sendChangeToPKH(myAddress) .withOption(TransactionOption.DISABLE_DUST_OUTPUTS) // a 0-satoshi output is "dust" to the checks .build(true);
tx.outputs[1].script.toString(type: 'asm'); // 0 OP_RETURN 68656c6c6f2066726f6d20646172747376Do note that a zero-satoshi Output is below the builder’s dust limit, so
build(true)throws aTransactionAmountExceptionunless we addTransactionOption.DISABLE_DUST_OUTPUTS.
UnspendableDataLockBuilder takes a single push. For protocols with several fields, we build the script with
ScriptBuilder and pass it through DefaultLockBuilder, which wraps any script as a Locking Script builder:
// For protocols that expect several fields, we build the script ourselves.final script = ScriptBuilder() .opFalse() .opCode(OpCodes.OP_RETURN) .addData(utf8.encode('19HxigV4QyBv3tHpQVcUEQyq1pzZVdoAut')) // e.g. a protocol prefix .addData(utf8.encode('Hello')) .addData(utf8.encode('text/plain')) .build();
final tx = TransactionBuilder() .spendFromTxnWithSigner(signer, fundingTx, 0, TransactionInput.MAX_SEQ_NUMBER, P2PKHUnlockBuilder(aliceKey.publicKey)) .spendToLockBuilder(DefaultLockBuilder.fromScript(script), BigInt.zero) .sendChangeToPKH(myAddress) .withOption(TransactionOption.DISABLE_DUST_OUTPUTS) .build(true);Pushes of any size work, since the script builder picks OP_PUSHDATA1, 2 or 4 as needed.
To read the data from an Output we received:
// script: an Output's Locking Scriptfinal reader = UnspendableDataLockBuilder.fromScript(script);utf8.decode(reader.dataBuffer!); // hello from dartsvFor several pushes, we walk the script’s chunks. See Building scripts.
Spendable data: P2PKH with data
Section titled “Spendable data: P2PKH with data”P2PKHDataLockBuilder puts <data> OP_DROP in front of an ordinary P2PKH script. The Output holds coins, and
spending it signs over the data.
// <data> OP_DROP followed by an ordinary P2PKH script, so the data rides along on a spendable Output.final dataLocker = P2PKHDataLockBuilder.fromAddress(myAddress, utf8.encode('invoice 1042'));
final tx = TransactionBuilder() .spendFromTxnWithSigner(signer, fundingTx, 0, TransactionInput.MAX_SEQ_NUMBER, P2PKHUnlockBuilder(aliceKey.publicKey)) .spendToLockBuilder(dataLocker, BigInt.from(1000)) .sendChangeToPKH(myAddress) .build(true);It is spent like any other P2PKH Output:
// It unlocks like any P2PKH Output, with a Signature and Public Key.final spend = TransactionBuilder() .spendFromTxnWithSigner(signer, tx, 1, TransactionInput.MAX_SEQ_NUMBER, P2PKHUnlockBuilder(aliceKey.publicKey)) .spendToPKH(myAddress, BigInt.from(900)) .build(false);And it is read back with fromScript:
final reader = P2PKHDataLockBuilder.fromScript(tx.outputs[1].script);utf8.decode(reader.dataBuffer!); // invoice 1042
