How to Create and Broadcast a P2WPKH (Segwit Native) Bitcoin Transaction

Continuing our series of posts on how to make different types of Bitcoin transactions (click here to read the first of the series), in this post we are going to learn how to make a P2WPKH (Pay To Witness Public Key Hash, aka Segwit Native) Bitcoin transaction using the bitcoin-java library.

This time, we are going to use our Bitcoin Core node on the testnet to search for and broadcast transactions. If you do not have a Bitcoin node, you can use the Blockstream Explorer to search for and broadcast transactions, as we explained in our previous post.

If you are going to use a Bitcoin Core node, make sure that its configuration matches the following bitcoin.conf file.

First, let us create a private key and use it to generate a Segwit Native (Bech32) address. For mainnet transactions, use safer ways to derive your private keys.
import io.github.bitcoineducation.bitcoinjava.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

import static io.github.bitcoineducation.bitcoinjava.AddressConstants.TESTNET_P2WPKH_ADDRESS_PREFIX;

public class Example {
    public static void main(String[] args) throws IOException {
        Security.addProvider(new BouncyCastleProvider()); // Bitcoin java uses the library Bouncy Castle to provide cryptography functions, so we add its provider to Java Security

        String secret = "4321fafafefeefef12";
        System.out.println("Private Key for input: " + secret);

        PrivateKey privateKey = new PrivateKey(new BigInteger(1, Hex.decode(secret))); // We convert the hexadecimal secret to a BigInteger before passing it to the PrivateKey's constructor
        String address = privateKey.getPublicKey().segwitAddressFromCompressedPublicKey(TESTNET_P2WPKH_ADDRESS_PREFIX);
        System.out.println("Address for input: " + address);

        // Output:
        // Private Key for input: 4321fafafefeefef12
        // Address for input: tb1q6q3s0qk90avg5ndt9qem5yng2ee084wxj6d6pp

Let us get test bitcoins. For that, we are going to use a testnet faucet. Access this link, or another faucet, and follow the instructions. Use the address generated above to receive test bitcoins. Take note of the ID generated by the faucet’s transaction.
					String inputTransactionId = "d0f96eef2a82acb17a289c52d004013c822e367261a5ad0d2acf1bb6f226c255";

Now, let us create another private key and Segwit Native address to be used as the receiving address.

					String secret2 = "123afefafef23312341234432112343412";
    System.out.println("Private Key for output: " + secret2);

    PrivateKey privateKey2 = new PrivateKey(new BigInteger(1, Hex.decode(secret2)));
    String address2 = privateKey2.getPublicKey().segwitAddressFromCompressedPublicKey(TESTNET_P2WPKH_ADDRESS_PREFIX);
    System.out.println("Address for output: " + address2);

    // Output:
    // Private Key for output: 123afefafef23312341234432112343412
    // Address for output: tb1qhnwf864rna2qk72kl7p5tnvcxemyvxllteyrc3

Let now us create the input of our transaction. For that, we are going to need the ID of the transaction that funded our address and the output index of the same transaction associated with our address. We have assigned the former in the inputTransactionId variable. We will discover the latter by sending the following commands to our Bitcoin Core node.

					$ bitcoin-cli getrawtransaction "d0f96eef2a82acb17a289c52d004013c822e367261a5ad0d2acf1bb6f226c255"
$ bitcoin-cli decoderawtransaction "02000000000101b728a0c70ab5089c47ab9ed473c99c99d587f28522e7fcfefdd394b86279ad5d0000000000feffffff02a086010000000000160014d0230782c57f588a4dab2833ba12685672f3d5c670361a0000000000160014d964768d6b4c3263367325fe4fcdfd20bff4973c024730440220144e3e3cd516bcb4640c882af8832cda7f265d68a6ddde7263c09a8bdafa16ab02204e768793cc3b3e489c832e81441d80bf0a3d47b3905194fbe531cf1f0a35315b012103713898bd370313a0190a869f3253445481a8c7a1e5f027d7ac139699464a202bdd022000"
  "txid": "d0f96eef2a82acb17a289c52d004013c822e367261a5ad0d2acf1bb6f226c255",
  "hash": "34b6501817f3e4afdfab83a9dbfa60eed2f94a689449acbf20a20b7c7af9dee7",
  "version": 2,
  "size": 222,
  "vsize": 141,
  "weight": 561,
  "locktime": 2097885,
  "vin": [
      "txid": "5dad7962b894d3fdfefce72285f287d5999cc973d49eab479c08b50ac7a028b7",
      "vout": 0,
      "scriptSig": {
        "asm": "",
        "hex": ""
      "txinwitness": [
      "sequence": 4294967294
  "vout": [
      "value": 0.00100000,
      "n": 0,
      "scriptPubKey": {
        "asm": "0 d0230782c57f588a4dab2833ba12685672f3d5c6",
        "hex": "0014d0230782c57f588a4dab2833ba12685672f3d5c6",
        "address": "tb1q6q3s0qk90avg5ndt9qem5yng2ee084wxj6d6pp",
        "type": "witness_v0_keyhash"
      "value": 0.01717872,
      "n": 1,
      "scriptPubKey": {
        "asm": "0 d964768d6b4c3263367325fe4fcdfd20bff4973c",
        "hex": "0014d964768d6b4c3263367325fe4fcdfd20bff4973c",
        "address": "tb1qm9j8drttfsexxdnnyhlyln0ayzllf9euva4pwg",
        "type": "witness_v0_keyhash"

The first command above passes the transaction ID of the transaction that funded our address as the parameter and receives the hex-encoded transaction as result. The second command above is used to decode the result of the first. We see by its result that the output index corresponding to our address (tb1q6q3s0qk90avg5ndt9qem5yng2ee084wxj6d6pp) is zero.

					BigInteger inputTransactionIndex = BigInteger.ZERO;

We can now build our transaction input.

					TransactionInput transactionInput = new TransactionInput(
        new Script(List.of()),
        new BigInteger(1, Hex.decode("ffffffff"))
    ArrayList<TransactionInput> transactionInputArrayList = new ArrayList<>();

Let us build our transaction output. For simplicity’s sake, this transaction will not have change. Since I received 100,000 satoshis, I will send 90,000 satoshis, and the difference (10,000 satoshis) will be the transaction fee.

					BigInteger amount = BigInteger.valueOf(90000);
    Script script = Script.p2wpkhScript(Bech32.decode("tb", address2)[1]);
    TransactionOutput transactionOutput = new TransactionOutput(amount, script);
    ArrayList<TransactionOutput> transactionOutputArrayList = new ArrayList<>();

Now that we have the transaction’s inputs and outputs, we can create our new transaction.

					        Transaction transaction = new Transaction(
            BigInteger.ONE, // Transaction version, we set it equal to one for this type of transaction
            BigInteger.ZERO, // Locktime, we set it equal to zero for this type of transaction
            true // True, since it is a Segwit transaction
        System.out.println("unsigned transaction: " + transaction.serialize());

        // Output:
        // unsigned transaction: 0100000000010155c226f2b61bcf2a0dada56172362e823c0104d0529c287ab1ac822aef6ef9d00000000000ffffffff01905f010000000000160014bcdc93eaa39f540b7956ff8345cd983676461bff0000000000


Now, let us sign the transaction. We must sign each transaction input. Since our transaction has only one input, we just have to sign it.

            BigInteger.valueOf(100000), // Since it is a segwit transaction, we have to pass the amount in satoshis locked by the input we are signing
            true // True, since we are signing a Segwit input
        System.out.println("signed transaction: " + transaction.serialize());

        // Output:
        // signed transaction: 0100000000010155c226f2b61bcf2a0dada56172362e823c0104d0529c287ab1ac822aef6ef9d00000000000ffffffff01905f010000000000160014bcdc93eaa39f540b7956ff8345cd983676461bff02483045022100c2f43fa6d45f3f16800b571ab89660ec018a613b5152a0fcba45c026d476b95d022065caec14bc94a3dfc10edcf04939d0149f9bcbbd040d0e50bd64f3abcb3ddccd01210293ede0facc9e7383dbde043ef04635e9c93b6e051b84fbb5976bd5671afe714c00000000


Great, we are ready to broadcast our transaction. Let us copy the serialized signed transaction generated above and use it in the following command to our node.

					$ bitcoin-cli sendrawtransaction "0100000000010155c226f2b61bcf2a0dada56172362e823c0104d0529c287ab1ac822aef6ef9d00000000000ffffffff01905f010000000000160014bcdc93eaa39f540b7956ff8345cd983676461bff02483045022100c2f43fa6d45f3f16800b571ab89660ec018a613b5152a0fcba45c026d476b95d022065caec14bc94a3dfc10edcf04939d0149f9bcbbd040d0e50bd64f3abcb3ddccd01210293ede0facc9e7383dbde043ef04635e9c93b6e051b84fbb5976bd5671afe714c00000000"

The resulting string is the transaction ID. We can also obtain the transaction ID with the following code.

					        System.out.println("transaction id: " +;
        // Output:
        // transaction id: 1550dbcf034eea35232b43ca4b27a3c54f1281d201538fce3f9c6f3e4b70a5f2

Finally, to check if your transaction was confirmed, send the following command to your bitcoin core node.

					$ bitcoin-cli gettxout "1550dbcf034eea35232b43ca4b27a3c54f1281d201538fce3f9c6f3e4b70a5f2" 0
  "bestblock": "000000000000003a537d8cd096530ac4dee2d5fe826a9de3f9d01d1af016bd5b",
  "confirmations": 110,
  "value": 0.00090000,
  "scriptPubKey": {
    "asm": "0 bcdc93eaa39f540b7956ff8345cd983676461bff",
    "hex": "0014bcdc93eaa39f540b7956ff8345cd983676461bff",
    "address": "tb1qhnwf864rna2qk72kl7p5tnvcxemyvxllteyrc3",
    "type": "witness_v0_keyhash"
  "coinbase": false

The first parameter of the command above is the transaction ID, and the second is the output index of our transaction, which we know is zero since it has only one output.