How to Send a Bitcoin Transaction Using a Bitcoin Node

·

Sending Bitcoin transactions directly through a full node offers unparalleled control, privacy, and security. Unlike relying on third-party wallets or services, using your own Bitcoin node means you validate and broadcast transactions independently—trusting no one. This guide walks you through the complete process of initiating a raw Bitcoin transaction using a local node, from setup to broadcast, based on Bitcoin Core 0.18 in a Ubuntu environment.

While this article assumes prior knowledge of compiling and configuring a Bitcoin node with RPC access, it consolidates key steps into a clear, actionable workflow: UTXO extraction, transaction creation, signing, and broadcasting. We'll also address common pitfalls and provide real command-line examples for clarity.


Step 1: Extracting UTXOs

The foundation of any Bitcoin transaction is the Unspent Transaction Output (UTXO). Before sending funds, your node must identify which UTXOs are available for spending.

Use the listunspent RPC command:

bitcoin-cli -regtest listunspent

However, you may encounter an empty result—even if the address holds funds. Why? Your node only tracks UTXOs linked to its internal wallet.

Understanding Wallet "Association"

Bitcoin Core organizes addresses under wallet labels (replacing older account systems). If an address isn't part of your wallet—i.e., not generated by getnewaddress—it's considered "unassociated," and its UTXOs won't appear in listunspent.

👉 Learn how to securely manage private keys and connect them to your node wallet.

How to Associate an External Address

To gain visibility over external UTXOs, import the corresponding private key:

bitcoin-cli -regtest importprivkey "your-private-key-here" "" true
⚠️ Set the third parameter (rescan) to true. This triggers a full blockchain scan to index all transactions related to the key. Depending on chain size, this can take minutes to hours.

Once complete, listunspent will include UTXOs from the imported address.

Alternatives Without Importing Private Keys

You can query UTXOs without importing keys:

  1. Enable Transaction Indexing: Start bitcoind with txindex=1 in bitcoin.conf. This allows querying any address’s history but increases disk usage and sync time.
  2. Import Only the Address: Use importaddress for read-only monitoring. Note: You cannot sign transactions without the private key.

Still, for full transaction control, importing the private key remains the most practical method.

🔍 Pro Tip: Many tutorials avoid this complexity by using getnewaddress, which generates wallet-associated addresses by default—ensuring immediate UTXO visibility.

Step 2: Creating a Raw Transaction

Now that you have accessible UTXOs, create a raw transaction using createrawtransaction.

Example:

bitcoin-cli -regtest createrawtransaction '''
[
  {
    "txid": "bea69fa6b4d2395f340f8b57ab5c6b0afd3de451b63cf335848e3f11885e89b4",
    "vout": 0
  }
]''' '''
{
  "2N9KXa3WdbbGRYM4XgNWqnQemDRSj5fqgDh": 20
}
'''

This returns a hexadecimal string representing an unsigned transaction.

Important: No Change Address = High Fees

createrawtransaction does not automatically create a change output. The difference between input and output values becomes the mining fee.

For example:

Such fees will likely prevent confirmation unless explicitly allowed during broadcast.

To avoid this, use fundrawtransaction, which auto-generates change addresses and estimates appropriate fees.

👉 Discover tools that help estimate optimal Bitcoin transaction fees.


Step 3: Signing the Transaction

After creating the raw hex, sign it. Bitcoin Core offers two methods:

Option 1: Sign with Wallet (signrawtransactionwithwallet)

Requires the private key to be imported into the wallet:

bitcoin-cli -regtest signrawtransactionwithwallet "rawhex-here"

Option 2: Sign with Private Key (signrawtransactionwithkey)

Pass the private key directly:

bitcoin-cli -regtest signrawtransactionwithkey "rawhex" '["private-key"]'

Both return a JSON object containing:

{
  "hex": "signed-transaction-hex",
  "complete": true
}
✅ A "complete": true response indicates successful signature formatting—but does not guarantee validity. Errors like incorrect PrevTx details or mismatched scripts can still cause broadcast failure.

Even slight parameter changes produce different signed hex strings due to how signatures are serialized. Always double-check inputs against confirmed transaction data.


Step 4: Broadcasting the Transaction

With a properly signed transaction, broadcast it:

bitcoin-cli -regtest sendrawtransaction "signed-hex"

If using createrawtransaction with large implicit fees, add the allowhighfees flag:

bitcoin-cli -regtest sendrawtransaction "signed-hex" true

Common errors include:

🛠️ Debugging Tip: If broadcasting fails, check the signature first. Most issues originate here.

Practical Example: Full Regtest Workflow

Below is a complete sequence tested on a local regtest chain:

# 1. Start bitcoind in regtest mode
bitcoind -printtoconsole -regtest &

# 2. Generate initial address
ADDR1=$(bitcoin-cli -regtest getnewaddress)

# 3. Mine 101 blocks to activate coinbase
bitcoin-cli -regtest generatetoaddress 101 $ADDR1

# 4. Check balance (should be 50 BTC)
bitcoin-cli -regtest getbalance

# 5. Create recipient address
ADDR2=$(bitcoin-cli -regtest getnewaddress)

# 6. List UTXOs
bitcoin-cli -regtest listunspent

# 7. Create raw transaction sending 20 BTC
RAW_TX=$(bitcoin-cli -regtest createrawtransaction '''[{"txid":"...", "vout":0}]''' '''{"'$ADDR2'":20}''')

# 8. Sign with wallet
SIGNED_TX=$(bitcoin-cli -regtest signrawtransactionwithwallet "$RAW_TX")

# 9. Broadcast (allow high fee)
bitcoin-cli -regtest sendrawtransaction "$(echo $SIGNED_TX | jq -r .hex)" true

# Output: Transaction ID (e.g., 3b3c86...a5ae)

Wait for miners to include your transaction—or mine another block manually:

bitcoin-cli -regtest generate 1

Frequently Asked Questions (FAQ)

Q: Can I send transactions without importing private keys?
A: Yes, but only if you use txindex=1 or import addresses via importaddress. However, signing still requires access to the private key—either imported into the wallet or passed directly during signing.

Q: Why does my listunspent return nothing even though the address has funds?
A: Your node only indexes wallet-managed addresses by default. To track external addresses, import their private keys or enable full transaction indexing (txindex=1).

Q: What causes “Missing Input” when broadcasting?
A: This usually means the specified UTXO doesn’t exist in your node’s view—either it’s already spent, mistyped, or belongs to an unindexed address. Verify the txid and vout values match confirmed transactions.

Q: Is it safe to use allowhighfees?
A: Only in testing or deliberate cases. On mainnet, this could result in losing significant funds. Always verify fee amounts before enabling this flag.

Q: How do I reduce transaction fees?
A: Use fundrawtransaction instead of createrawtransaction. It automatically calculates change outputs and applies fee estimation based on current network conditions.

Q: Can I automate these steps in code?
A: Absolutely. Bitcoin Core uses JSON-RPC over HTTP. You can interact with it programmatically using tools like curl, Python’s requests, or dedicated libraries—authenticating via .cookie files or basic auth.


Final Tips for Node-Based Transactions

  1. Prefer Regtest for Testing: Use isolated regtest environments to experiment safely—no risk of losing real BTC or dealing with slow confirmations.
  2. Avoid Configuration Conflicts: Ensure CLI arguments and bitcoin.conf settings don’t clash (e.g., conflicting ports or modes).
  3. Monitor Rescan Progress: After importing keys, wait for rescan completion before expecting accurate UTXO data.
  4. Use JSON-RPC Securely: When building applications, protect RPC endpoints with firewalls and strong authentication.

👉 Start building secure Bitcoin applications with reliable infrastructure support.

By mastering these fundamentals, you gain deep insight into Bitcoin’s inner workings—empowering you to transact with full sovereignty and technical confidence.