Skip to content

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.

  1. 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");
  2. 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 satoshis to 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);
  3. Spending the coins. A TransactionBuilder spends 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 TransactionSigner holds the key and the Sighash type we sign with,
    • the P2PKHUnlockBuilder builds 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.
  4. 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 ScriptException if it is not.

    // Input 0 of spendingTx correctly spends Output 0 of fundingTx.
    assertDoesNotThrow(() -> Fixtures.verifySpend(spendingTx, 0, fundingTx.getOutputs().get(0)));
  5. 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 API
    String 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 sendrawtransaction RPC 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 made fundingTx would fail that check, since fundingTx was never broadcast.