How to create a Bitcoin raw transaction using bitcoind


A Bitcoin raw transaction is a chunk of bytes that contains the info about a Bitcoin transaction.

That raw transaction will become part of the blockchain when a miner adds it to a block.


We are going to create a Raw Transaction step by step using bitcoind (Bitcoin Core server).


Recomendation: Do not store private keys in an online system.

Better create an online wallet with addresses and public keys only:
bitcoind: How to create a wallet with no private keys

And run another instance of bitcoind offline with the private keys.


The online bitcoind will validate current blockchain, but will not be able to sign transactions.

Offline bitcoind has the private keys but does not store the whole copy of the blockchain to validate it.


This tutorial also works if you have one online machine with public and private keys, but it is not safe to store
private keys in an online machine.



Get current balance


To get the total balance tracked by our online wallet:

(online)$ bitcoin-cli getbalance "*" 0 true
0.03311140

We have a total of 0.03311140 bitcoins!

That is the maximum amount we could transfer.



Get unspent UTXOS


Bitcoin is an UTXO based system. You actually do not send Bitcoin to an address, but spent some existing UTXOs and create new ones.

We need a list of unspent UTXOs in our wallet to create the input part of our transaction.

(online)$ bitcoin-cli listunspent
  {
    "txid": "old_transaction",
    "vout": 0,
    "address": "my_old_address",
    "scriptPubKey": "my_old_address_scriptpubkey",
	...
    "amount": 0.0098,
	...
  },


listunspent command will show you the list of UTXOs you can use to create a transaction.

Then you select the ones you want to spend.


To determine an UTXO you will need its txid and vout.

You will also need the scriptPubKey when signing the transaction.

Total amount spent needs to be lower than the sum of all amount of UTXOs we are using.


In the offline computer we can check if we actually own the bitcoin address: "my_old_address"

(offline) $ bitcoin-cli getaddressinfo "my_old_address"
{
  "address": "my_old_address",
  "scriptPubKey": "my_old_address_scriptpubkey",
  ...
  "ismine": true,
  "iswatchonly": false,
  "isscript": false,
  "iswitness": true,
  ...
  "pubkey": "my_old_address_public_key",
}


ismine true shows that you own that witness address and you will be able to spend it.

public key in "pubkey" is important to create the transaction. We import the public key in the online bitcoind:

(online)$ bitcoin-cli importpubkey "my_old_address_public_key" false


Note down the scriptPubKey because we will use it when signing the transaction.



Create the Raw Transaction


First we need the destination address where we are going to send the funds to.

In this example we are going to use only one destination address. i.e: "bc1q_my_destination_address"

Help about createrawtransaction command:
(online)$ bitcoin-cli help createrawtransaction
createrawtransaction [{"txid":"id","vout":n},...] [{"address":amount},{"data":"hex"},...] ( locktime ) ( replaceable )


Now we create the raw transaction:

NOTE: We send 0.004 bitcoin to bc1_my_destination_address, because we do not spend the whole UTXO amount we will need a change address for fundrawtransaction command.

(online)$ bitcoin-cli createrawtransaction '[{"txid":"old_transaction","vout":0}]' '[{"bc1q_my_destination_address":0.004}]' 0 true
020000000_first_raw_transaction_XXXXX


We can decode the created raw transaction to examine it:
(online)$ bitcoin-cli decoderawtransaction "020000000_first_raw_transaction_XXXXX"
{
  "txid": "XXXXX"
  "hash": "XXXXX"
  "version": 2,
  "size": 83,
  "vsize": 83,
  "weight": 332,
  "locktime": 0,
  "vin": [
    {
      "txid": "old_transaction",
      "vout": 0,
	  ...
    }
  ],
  "vout": [
    {
      "value": 0.004,
      "n": 0,
      "scriptPubKey": {
	    ...
        "addresses": [
          "bc1q_my_destination_address"
        ]
      }
    }
  ]
}




Fund the Raw Transaction


UTXO contains 0.0098 btc, we send 0.004 btc to bc1_my_destination_address and we want to receive 0.0058 change in our change address bc1q_my_change_address.

We set feerate to 0.00005.

Because we created the transaction with bc1_my_destination_address at vout: 0 we tell fundrawtransaction command that change address comes at position 1.

More info at $ bitcoin-cli help fundrawtransaction

(online)$ bitcoin-cli fundrawtransaction "020000000_first_raw_transaction_XXXXX" '{"changeAddress":"bc1q_my_change_address" ,"includeWatching":false, "feeRate":0.00005, "replaceable":true, "changePosition":1, "subtractFeeFromOutputs":[0]}' true
{
  "hex": "020000000_second_raw_transaction_XXXXX",
  "fee": 0.00000701,
  "changepos": 1
}


NOTE: Even if we send the whole UTXO amount we will need a fake change address for fundrawtransaction command.


We decode the raw transaction and check the change address appears:
(online)$ bitcoin-cli decoderawtransaction "020000000_second_raw_transaction_XXXXX"
{
  "txid": "XXXXX",
  "hash": "XXXXX",
  "version": 2,
  "size": 113,
  "vsize": 113,
  "weight": 452,
  "locktime": 0,
  "vin": [
    {
      "txid": "old_transaction",
      "vout": 0,
	  ...
    }
  ],
  "vout": [
    {
      "value": 0.004,
      "n": 0,
      "scriptPubKey": {
	    ...
        "addresses": [
		  "bc1q_my_destination_address"
        ]
      }
    },
    {
      "value": 0.0058,
      "n": 1,
      "scriptPubKey": {
	    ...
        "addresses": [
		  "bc1q_my_change_address"
        ]
      }
    }
  ]
}




Sign the Raw Transaction


Signing process is performed in the offline machine.

We unlock the wallet if it is secured by a passphrasse (recommended):
(offline)$ bitcoin-cli walletpassphrase "my_passphrase" 300


We pass the scriptPubKey and the exact amount of the UTXO:
(offline)$ bitcoin-cli signrawtransactionwithwallet "020000000_second_raw_transaction_XXXXX" '[{"txid":"old_transaction","vout":0,"scriptPubKey":"my_old_address_scriptpubkey", "amount":0.0098}]'
{
  "hex": "020000000_signed_raw_transaction_XXXXX",
  "complete": true
}




Send the Raw Transaction to other nodes


First in the online machine we test the new transaction:

(online)$ bitcoin-cli testmempoolaccept '["020000000_signed_raw_transaction_XXXXX"]'
[
  {
    "txid": "new_raw_transaction_txid",
    "allowed": true
  }
]



Then we send the raw transaction:

(online)$ bitcoin-cli sendrawtransaction "020000000_signed_raw_transaction_XXXXX"
"new_raw_transaction_txid"


We can check state of the sent transaction:

$ bitcoin-cli gettransaction "new_raw_transaction_txid"
{ 
  "amount": 0.00000000,
  "confirmations": 0,
  "trusted": false,
  "txid": "new_raw_transaction_txid",
  "walletconflicts": [
  ],
  "hex": "020000000_signed_raw_transaction_XXXXX"
}


After some time, when transaction is accepted in a block, confirmations will rise.

6 confirmations or more is usually enough to consider the transaction as immutable in the blockchain.


We could also check the transaction in a block explorer:
https://blockstream.info/nojs/tx/new_raw_transaction_txid