Your first transaction
In this section we will walk through a complete payment without touching the network. We need a key, an Output that pays to that key, a Transaction that spends the Output, and a check that the spend is valid. The Developer Guide’s walkthrough explains what happens inside a Transaction. Here we focus on the code.
-
Creating the keys and Addresses. We load a fixed testnet Private Key from its WIF string, so that every run gives us the same result, and derive its Address. Our recipient, Bob, is known to us only by his Address.
PrivateKey alice = PrivateKey.fromWIF("cVVvUsNHhbrgd7aW3gnuGo2qJM45LhHhTCVXrDSJDDcNGE6qmyCs");Address aliceAddress = Address.fromKey(NetworkAddressType.TEST_PKH, alice.getPublicKey());Address bobAddress = Address.fromString(NetworkType.TEST, "mrU9pEmAx26HcbKVrABvgL7AwA5fjNFoDc"); -
Receiving some coins. Before we can spend any coins, we need to receive some first. In a real wallet the coins arrive in someone else’s Transaction, and we fetch it (or only its Output) from a node or an API. For a test, we build that Transaction ourselves. The following helper, which we use throughout these pages, makes a Transaction with one Output that pays
satoshisto a given Locking Script./*** A stand-in for a Transaction we received, with one Output of {@code satoshis} locked by {@code locker}.* Its single Input points at nothing real, which does not matter when we build and verify a spend offline.*/public static Transaction fundingTransaction(LockingScriptBuilder locker, long satoshis) {Transaction tx = new Transaction();tx.addInput(new TransactionInput(new byte[32], 0, TransactionInput.MAX_SEQ_NUMBER, new DefaultUnlockBuilder()));tx.addOutput(new TransactionOutput(BigInteger.valueOf(satoshis), locker));return tx;}Let’s lock 100,000 satoshis to Alice’s Address with a P2PKH Locking Script.
// 100,000 satoshis paid to Alice's Address.Transaction fundingTx = Fixtures.fundingTransaction(new P2PKHLockBuilder(aliceAddress), 100_000); -
Spending the coins. A
TransactionBuilderspends Output 0 of the funding Transaction, pays 50,000 satoshis to Bob, and returns the rest, less the fee, to Alice.TransactionSigner signer = new TransactionSigner(SigHashType.ALL.value | SigHashType.FORKID.value, alice);P2PKHUnlockBuilder unlocker = new P2PKHUnlockBuilder(alice.getPublicKey());Transaction spendingTx = new TransactionBuilder().spendFromTransaction(signer, fundingTx, 0, TransactionInput.MAX_SEQ_NUMBER, unlocker).spendTo(new P2PKHLockBuilder(bobAddress), BigInteger.valueOf(50_000)).sendChangeTo(aliceAddress).build(true);In the preceding example:
- the
TransactionSignerholds the key and the Sighash type we sign with, - the
P2PKHUnlockBuilderbuilds the P2PKH Unlocking Script (the Signature and the Public Key) once the signer has signed, build(true)runs the builder’s checks on amounts, dust and fees, and then signs every Input that has a signer.
- the
-
Checking the spend. The interpreter runs the Unlocking Script followed by the Locking Script, as a node would. This helper wraps the call, with the rules that a BSV node applies today.
/** The rules a BSV node applies to a spend of an output created after the Genesis upgrade. */public static final Set<Script.VerifyFlag> FLAGS = EnumSet.of(Script.VerifyFlag.SIGHASH_FORKID,Script.VerifyFlag.UTXO_AFTER_GENESIS,Script.VerifyFlag.STRICTENC,Script.VerifyFlag.DERSIG,Script.VerifyFlag.LOW_S,Script.VerifyFlag.NULLFAIL);/** Runs Input {@code inputIndex} of {@code spendingTx} against the Output it spends, and throws ScriptException if it fails. */public static void verifySpend(Transaction spendingTx, int inputIndex, TransactionOutput spentOutput) {Script unlockingScript = spendingTx.getInputs().get(inputIndex).getScriptSig();new Interpreter().correctlySpends(unlockingScript,spentOutput.getScript(),spendingTx,inputIndex,FLAGS,Coin.valueOf(spentOutput.getAmount().longValue()));}It returns normally if the spend is valid, and throws a
ScriptExceptionif it is not.// Input 0 of spendingTx correctly spends Output 0 of fundingTx.assertDoesNotThrow(() -> Fixtures.verifySpend(spendingTx, 0, fundingTx.getOutputs().get(0))); -
Serialising the Transaction. The raw hex is what we hand to a node or a transaction-processing API to broadcast.
String rawHex = Utils.HEX.encode(spendingTx.serialize()); // what you hand to a node or broadcast APIString txid = spendingTx.getTransactionId();
Our Transaction ends up with two Outputs. Output 0 is the payment to Bob, and the builder puts the Change
Output last, so Output 1 holds the 49,744 satoshis of change. The fee is 256 satoshis, which is the builder’s
minimum (the page on fees and change explains how it is worked
out). Signatures are deterministic (RFC 6979), so this example always produces the Transaction id
f8bb7d4c…ed578f9c.
Broadcasting is the one step we cannot do offline. We send the raw hex to a node’s
sendrawtransactionRPC or to a transaction-processing API. The node runs the same checks as our interpreter did, plus the ones that need the network, i.e. that the funding Output exists and is unspent. It should be noted that a Transaction built on our locally madefundingTxwould fail that check, sincefundingTxwas never broadcast.
Where to go next
Section titled “Where to go next”- The Transaction builder covers the other ways of adding Inputs, and signing later.
- Verifying a spend looks at the interpreter call in detail.
- Using Bitcoin4J from Kotlin builds the same Transaction in Kotlin.

