mirror of
https://github.com/ChristopherA/Learning-Bitcoin-from-the-Command-Line.git
synced 2025-06-07 16:06:26 +00:00
Merge branch 'master' into master
This commit is contained in:
commit
579de0e7e7
@ -4,12 +4,24 @@ The ways that we make payments for goods and services has been changing dramatic
|
|||||||
|
|
||||||
These centralization risks were some of the prime catalysts behind the creation of cryptocurrencies, the first and most successful of which is Bitcoin. Bitcoin offers pseudonymity, it makes it difficult to correlate transactions, and it makes censorship by individual entities all but impossible. These advantages have made it one of the quickest growing currencies in the world. That growth in turn has made Bitcoin into a going concern among entrepreneurs and developers, eager to create new services for the Bitcoin community.
|
These centralization risks were some of the prime catalysts behind the creation of cryptocurrencies, the first and most successful of which is Bitcoin. Bitcoin offers pseudonymity, it makes it difficult to correlate transactions, and it makes censorship by individual entities all but impossible. These advantages have made it one of the quickest growing currencies in the world. That growth in turn has made Bitcoin into a going concern among entrepreneurs and developers, eager to create new services for the Bitcoin community.
|
||||||
|
|
||||||
If you're one of those entrepreneurs or developers, then this book is for you, because it's all about learning to program Bitcoin. It's an introductory book, which explains all the nuances and features of Bitcoin as it goes. It also takes a very specific tack, by offering lessons in how to work directly with Bitcoin Core and with the c-lightning server.
|
If you're one of those entrepreneurs or developers, then this book is for you, because it's all about learning to program Bitcoin. It's an introductory book that explains all the nuances and features of Bitcoin as it goes. It also takes a very specific tack, by offering lessons in how to work _directly_ with Bitcoin Core and with the c-lightning server using their RPG interfaces.
|
||||||
|
|
||||||
Why not use some of the more fully featured libraries found in various programming languages? Why not create your own from scratch? It's because working with cryptocurrency is dangerous. There are no safety nets. If you accidentally overpay your fees or lose a signing key or create an invalid transaction or make any number of potential mistakes, then your cryptocurrency will been gone forever. Much of that responsibility will, of course, lie with you as a cryptocurrency programmer, but it can be minimized by working with the most robust, secure, and safe cryptocurrency interfaces around, the ones created by the cryptocurrency programming teams themselves: ``bitcoind`` and ``lightningd``.
|
Why not use some of the more fully featured libraries found in various programming languages? Why not create your own from scratch? It's because working with cryptocurrency is dangerous. There are no safety nets. If you accidentally overpay your fees or lose a signing key or create an invalid transaction or make any number of potential mistakes, then your cryptocurrency will been gone forever. Much of that responsibility will, of course, lie with you as a cryptocurrency programmer, but it can be minimized by working with the most robust, secure, and safe cryptocurrency interfaces around, the ones created by the cryptocurrency programming teams themselves: ``bitcoind`` and ``lightningd``.
|
||||||
|
|
||||||
Much of this book thus discusses how to script Bitcoin (and Lightning) directly from the command line. Some later chapters deals with more sophisticated programming languages, but again they continue to interact directly with the ``bitcoind`` and ``lightningd`` daemons by using RPC or by interacting with the files they create. This allows you to stand on the shoulders of giants and use their trusted technology to learn how to create your own trusted systems.
|
Much of this book thus discusses how to script Bitcoin (and Lightning) directly from the command line. Some later chapters deals with more sophisticated programming languages, but again they continue to interact directly with the ``bitcoind`` and ``lightningd`` daemons by using RPC or by interacting with the files they create. This allows you to stand on the shoulders of giants and use their trusted technology to learn how to create your own trusted systems.
|
||||||
|
|
||||||
|
## Overview of Topics
|
||||||
|
|
||||||
|
This book is broadly divided into the following sections:
|
||||||
|
|
||||||
|
* **Part One: Preparing for Bitcoin** - Understanding the basics of Bitcoin and setting up a server for use.
|
||||||
|
* **Part Two: Using Bitcoin-CLI** - Using the Bitcoin-CLI for creating transactions.
|
||||||
|
* **Part Three: Bitcoin Scripting** - Expanding your Bitcoin work with scripts.
|
||||||
|
* **Part Four: Using Tor** - Improving your node security with Tor
|
||||||
|
* **Part Five: Using Lightning-CLI** - Using the Lightning-CLI for creating transactions.
|
||||||
|
* **Part Six: Programming with RPC** - Accessing RPC from C and other languages.
|
||||||
|
* **Appendices** - Utilizing less common Bitcoin setups.
|
||||||
|
|
||||||
## What's Next?
|
## What's Next?
|
||||||
|
|
||||||
Learn the basics of Bitcoin in [Chapter One: Introducing Bitcoin](01_0_Introducing_Bitcoin.md).
|
Learn the basics of Bitcoin in [Chapter One: Introducing Bitcoin](01_0_Introducing_Bitcoin.md).
|
||||||
|
@ -26,13 +26,13 @@ Obviously, Bitcoin is the heart of this book, but it's also the originator of ma
|
|||||||
|
|
||||||
**_Who Can You Send Coins To?_** The vast majority of bitcoin transactions involve coins being sent to individual people (or at least to individual Bitcoin addresses). However, more complex methodologies can be used to send bitcoins to groups of people or to scripts. These various methodologies have names like P2PKH, multisig, and P2SH.
|
**_Who Can You Send Coins To?_** The vast majority of bitcoin transactions involve coins being sent to individual people (or at least to individual Bitcoin addresses). However, more complex methodologies can be used to send bitcoins to groups of people or to scripts. These various methodologies have names like P2PKH, multisig, and P2SH.
|
||||||
|
|
||||||
**_How Are Transactions Stored?_** Transactions are combined into larger blocks of data, which are then written to the blockchain ledger. A block is built in such a way that it cannot be replaced or rewritten once several blocks have been built after it. This is what makes bitcoins non-repudiable: the decentralized global ledger where everything is recorded is effectively a permanent and unchangeable database.
|
**_How Are Transactions Stored?_** Transactions are combined into larger blocks of data, which are then written to the blockchain ledger. A block is built in such a way that it cannot be replaced or rewritten once several blocks have been built atop (following) it. This is what makes bitcoins non-repudiable: the decentralized global ledger where everything is recorded is effectively a permanent and unchangeable database.
|
||||||
|
|
||||||
However, the process of building these blocks is stochastic: it's somewhat random, so you can never be assured that a transaction will be placed in a specific block. There can also be changes in blocks if they're very recent, but only if they're _very_ recent. So, things become non-repudiable (and permanent and unchangeable) after a little bit of time.
|
However, the process of building these blocks is stochastic: it's somewhat random, so you can never be assured that a transaction will be placed in a specific block. There can also be changes in blocks if they're very recent, but only if they're _very_ recent. So, things become non-repudiable (and permanent and unchangeable) after a little bit of time.
|
||||||
|
|
||||||
**_How Are Transactions Protected?_** The funds contained in a Bitcoin transaction are locked with a cryptographic puzzle. These puzzles are designed so that they can be easily solved by the person who the funds were sent to. This is done using the power of public-key cryptography.
|
**_How Are Transactions Protected?_** The funds contained in a Bitcoin transaction are locked with a cryptographic puzzle. These puzzles are designed so that they can be easily solved by the person who the funds were sent to. This is done using the power of public-key cryptography. Technically, a transaction is protected by a signature that proves you're the owner of the public key that a transaction was sent to: this proof of ownership is the puzzle that's being solved.
|
||||||
|
|
||||||
Funds are further protected by the use of hashes. Public keys aren't actually stored in the blockchain, only public-key hashes are. This means that even if quantum computer were to come along, Bitcoin transactions would remain protected by this second level of cryptography.
|
Funds are further protected by the use of hashes. Public keys aren't actually stored in the blockchain until the funds are spent: only public-key hashes are. This means that even if quantum computer were to come along, Bitcoin transactions would remain protected by this second level of cryptography.
|
||||||
|
|
||||||
**_How Are Transactions Created?_** The heart of each Bitcoin transaction is a FORTH-like scripting language that is used to lock the transaction. To respend the money, the recipient provides specific information to the script that proves he's the intended recipient.
|
**_How Are Transactions Created?_** The heart of each Bitcoin transaction is a FORTH-like scripting language that is used to lock the transaction. To respend the money, the recipient provides specific information to the script that proves he's the intended recipient.
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ However, these Bitcoin scripts are the lowest level of Bitcoin functionality. Mu
|
|||||||
|
|
||||||
### Bitcoin — In Short
|
### Bitcoin — In Short
|
||||||
|
|
||||||
One way to think of Bitcoin is as _a sequence of atomic transactions_. Each transaction is authenticated by a sender with the solution to a previous cryptographic puzzle that was stored as a script. The new transaction is locked for the recipient with a new cryptographic puzzle that is stored as a script. Every transaction is recorded in an immutable global ledger.
|
One way to think of Bitcoin is as _a sequence of atomic transactions_. Each transaction is authenticated by a sender with the solution to a previous cryptographic puzzle that was stored as a script. The new transaction is locked for the recipient with a new cryptographic puzzle that is also stored as a script. Every transaction is recorded in an immutable global ledger.
|
||||||
|
|
||||||
## About Public-Key Cryptography
|
## About Public-Key Cryptography
|
||||||
|
|
||||||
@ -50,9 +50,9 @@ It's important to Bitcoin (and to most blockchain systems) because it's the basi
|
|||||||
|
|
||||||
**_What Is a Public Key?_** A public key is the key given out to other people. In a typical public-key system, a user generates a public key and a private key, then he gives the public key to all and sundry. Those recipients can encrypt information with the public key, but it can't be decrypted with the same public key because of the asymmetry of the key pair.
|
**_What Is a Public Key?_** A public key is the key given out to other people. In a typical public-key system, a user generates a public key and a private key, then he gives the public key to all and sundry. Those recipients can encrypt information with the public key, but it can't be decrypted with the same public key because of the asymmetry of the key pair.
|
||||||
|
|
||||||
**_What Is a Private Key?_** A private key is linked to a public key in a key pair. In a typical public-key system, a user keeps his private key secure and uses it to decrypt messages that were encrypted with his public key in the process of being sent to him..
|
**_What Is a Private Key?_** A private key is linked to a public key in a key pair. In a typical public-key system, a user keeps his private key secure and uses it to decrypt messages that were encrypted with his public key before being sent to him.
|
||||||
|
|
||||||
**_What Is a Signature?_** A message (or more commonly, a hash of a message) can be signed with a private key, creating a signature. Anyone with the corresponding public key can then validate the signature, which verifies that the signer owns the private key associated with the public key in question.
|
**_What Is a Signature?_** A message (or more commonly, a hash of a message) can be signed with a private key, creating a signature. Anyone with the corresponding public key can then validate the signature, which verifies that the signer owns the private key associated with the public key in question. _SegWit_ is a specific format for storing a signature on the Bitcoin network that we'll meet down the line.
|
||||||
|
|
||||||
**_What Is a Hash Function?_** A hash function is an algorithm frequently used with cryptography. It's a way to map a large, arbitrary amount of data to a small, fixed amount of data. Hash functions used in cryptography are one-way and collision-resistant, meaning that a hash can reliably be linked to the original data, but the original data can not be regenerated from the hash. Hashes thus allow the transmission of small amounts of data to represent large amounts of data, which can be important for efficiency and storage requirements.
|
**_What Is a Hash Function?_** A hash function is an algorithm frequently used with cryptography. It's a way to map a large, arbitrary amount of data to a small, fixed amount of data. Hash functions used in cryptography are one-way and collision-resistant, meaning that a hash can reliably be linked to the original data, but the original data can not be regenerated from the hash. Hashes thus allow the transmission of small amounts of data to represent large amounts of data, which can be important for efficiency and storage requirements.
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ One way to think of public-key cryptography is: _a way to enable public-key cryp
|
|||||||
|
|
||||||
## About Blockchains
|
## About Blockchains
|
||||||
|
|
||||||
Blockchain is the generalization of the methodology used by Bitcoin to create a distributed global ledger. Bitcoin is a blockchain as are any number of alt-coins, each of which lives in its networks and writes to its own chain. Sidechains like Liquid are blockchains too. Blockchains don't even need to have anything to do with finances. For example, there have been many discussions of using blockchains to protect self-sovereign identities.
|
Blockchain is the generalization of the methodology used by Bitcoin to create a distributed global ledger. Bitcoin is a blockchain as are any number of alt-coins, each of which lives on its own networks and writes to its own chain. Sidechains like Liquid are blockchains too. Blockchains don't even need to have anything to do with finances. For example, there have been many discussions of using blockchains to protect self-sovereign identities.
|
||||||
|
|
||||||
Though you need to understand the basics of how a blockchain works to understand how transactions work in Bitcoin, you won't need to go any further than that. Because blockchains have become a wide category of technology, those basic concepts are likely to be applicable to many other projects in this growing technology sector. The specific programming commands learned in this book will not be, however, as they're fairly specific to Bitcoin (and Lightning).
|
Though you need to understand the basics of how a blockchain works to understand how transactions work in Bitcoin, you won't need to go any further than that. Because blockchains have become a wide category of technology, those basic concepts are likely to be applicable to many other projects in this growing technology sector. The specific programming commands learned in this book will not be, however, as they're fairly specific to Bitcoin (and Lightning).
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Chapter Two: Creating a Bitcoin-Core VPS
|
# Chapter Two: Creating a Bitcoin-Core VPS
|
||||||
|
|
||||||
To get started with Bitcoin, you first need to set up a machine running Bitcoin. The articles in this chapter describe how to do so using a VPS (Virtual Private Server).
|
To get started with Bitcoin, you first need to set up a machine running Bitcoin. The articles in this chapter describe how to do so, primarily by using a VPS (Virtual Private Server).
|
||||||
|
|
||||||
## Objectives for this Chapter
|
## Objectives for this Chapter
|
||||||
|
|
||||||
@ -20,8 +20,8 @@ Supporting objectives include the ability to:
|
|||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
You can either create your VPS by hand or you can use a simple StackScript at Linode.com to do so.
|
You don't actually need to read this entire chapter. Decide if you want to set up your VPS by hand, typing in every command (§2.1); if you want to run a StackScript to set it up on a Linode VPS (§2.2); or you want to set up on a different environment, such as on an AWS machine or a Mac (§2.3). Then, jump to the appropriate section.
|
||||||
|
|
||||||
* [Section One: Setting Up a Bitcoin Core VPS by Hand](02_1_Setting_Up_a_Bitcoin-Core_VPS_by_Hand.md)
|
* [Section One: Setting Up a Bitcoin Core VPS by Hand](02_1_Setting_Up_a_Bitcoin-Core_VPS_by_Hand.md)
|
||||||
* [Section Two: Setting Up a Bitcoin Core VPS with StackScript](02_2_Setting_Up_a_Bitcoin-Core_VPS_with_StackScript.md)
|
* [Section Two: Setting Up a Bitcoin Core VPS with Bitcoin Standup](02_2_Setting_Up_a_Bitcoin-Core_VPS_with_StackScript.md)
|
||||||
* [Section Three: Setting Up a Bitcoin Core Machine via Other Means](02_3_Setting_Up_Bitcoin_Core_Other.md)
|
* [Section Three: Setting Up a Bitcoin Core Machine via Other Means](02_3_Setting_Up_Bitcoin_Core_Other.md)
|
||||||
|
@ -2,11 +2,13 @@
|
|||||||
|
|
||||||
> :information_source: **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
> :information_source: **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
||||||
|
|
||||||
|
> :information_source: **WARNING:** This chapter is currently out of sync with the Bitcoin Standup script, and won't be updated until we finish some changes to Standup this summer. We very much suggest [using the Script](https://github.com/BlockchainCommons/Learning-Bitcoin-from-the-Command-Line/blob/master/02_2_Setting_Up_a_Bitcoin-Core_VPS_with_StackScript.md) instead of this by-hand description, because it includes Tor and other security functions.
|
||||||
|
|
||||||
This document explains how to set up a VPS (Virtual Private Server) by hand to run a Bitcoin node on a cloud computer. You'll need to set up your computer yourself, but then this document will provide you with important commands to secure your VPS and to get it running Bitcoin. The setup should all be done in my session, so you don't lose the variables `export`ed at the start.
|
This document explains how to set up a VPS (Virtual Private Server) by hand to run a Bitcoin node on a cloud computer. You'll need to set up your computer yourself, but then this document will provide you with important commands to secure your VPS and to get it running Bitcoin. The setup should all be done in my session, so you don't lose the variables `export`ed at the start.
|
||||||
|
|
||||||
> :warning: **WARNING:** Don’t use a VPS for a bitcoin wallet with significant real funds; see http://blog.thestateofme.com/2012/03/03/lessons-to-be-learned-from-the-linode-bitcoin-incident/ . It is very nice to be able experiment with real bitcoin transactions on a live node without tying up a self-hosted server on a local network. I’ve also found it useful to be able to use an iPhone or iPad to communicate via SSH to my VPS to do some simple bitcoin tasks. But a higher level of safety is required for significant funds.
|
> :warning: **WARNING:** Don’t use a VPS for a bitcoin wallet with significant real funds; see http://blog.thestateofme.com/2012/03/03/lessons-to-be-learned-from-the-linode-bitcoin-incident/ . It is very nice to be able experiment with real bitcoin transactions on a live node without tying up a self-hosted server on a local network. I’ve also found it useful to be able to use an iPhone or iPad to communicate via SSH to my VPS to do some simple bitcoin tasks. But a higher level of safety is required for significant funds.
|
||||||
|
|
||||||
If you want to instead have a script do the setup for you, specifically at linode.com, read the parallel HOWTO file, [§2.2: Setting up a Bitcoin-Core VPS with StackScript](02_2_Setting_Up_a_Bitcoin-Core_VPS_with_StackScript.md).
|
If you want to instead have a script do the setup for you, specifically at linode.com, read the parallel HOWTO file, [§2.2: Setting up a Bitcoin-Core VPS with StackScript](02_2_Setting_Up_a_Bitcoin-Core_VPS_with_StackScript.md). This
|
||||||
|
|
||||||
If you already have a Bitcoin node running, instead read the next HOWTO file, [Chapter Three: Understanding Your Bitcoin Setup](03_0_Understanding_Your_Bitcoin_Setup.md).
|
If you already have a Bitcoin node running, instead read the next HOWTO file, [Chapter Three: Understanding Your Bitcoin Setup](03_0_Understanding_Your_Bitcoin_Setup.md).
|
||||||
|
|
||||||
@ -258,7 +260,7 @@ You're now ready to get to the bitcoin-specific part of this tutorial!
|
|||||||
|
|
||||||
We find a number of Bash aliases helpful to make it easier to use Bitcoin.
|
We find a number of Bash aliases helpful to make it easier to use Bitcoin.
|
||||||
```
|
```
|
||||||
$ sudo -u user1 cat >> ~user1/.bash_profile <<EOF
|
$ sudo -u user1 cat >> ~user1/.bash_aliases <<EOF
|
||||||
alias btcdir="cd ~/.bitcoin/" #linux default bitcoind path
|
alias btcdir="cd ~/.bitcoin/" #linux default bitcoind path
|
||||||
alias bc="bitcoin-cli"
|
alias bc="bitcoin-cli"
|
||||||
alias bd="bitcoind"
|
alias bd="bitcoind"
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
# 2.2: Setting Up a Bitcoin-Core VPS with StackScript
|
# 2.2: Setting Up a Bitcoin-Core VPS with Bitcoin Standup
|
||||||
|
|
||||||
> :information_source: **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
> :information_source: **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
||||||
|
|
||||||
This document explains how to set up a VPS (Virtual Private Sever) to run a Bitcoin node on Linode.com, installed using an automated StackScript. You just need to enter a few commands and boot your VPS. Then, after you go and get yourself an espresso, you'll be able to come back and find your new Bitcoin node happily downloading blocks.
|
This document explains how to set up a VPS (Virtual Private Sever) to run a Bitcoin node on Linode.com, installed using an automated StackScript from the [Bitcoin Standup project](https://github.com/BlockchainCommons/Bitcoin-Standup-Scripts). You just need to enter a few commands and boot your VPS. Almost immediately after you boot, you'll find find your new Bitcoin node happily downloading blocks.
|
||||||
|
|
||||||
> :warning: **WARNING:** Don’t use a VPS for a bitcoin wallet with significant real funds; see http://blog.thestateofme.com/2012/03/03/lessons-to-be-learned-from-the-linode-bitcoin-incident/ . It is very nice to be able experiment with real bitcoin transactions on a live node without tying up a self-hosted server on a local network. I’ve also found it useful to be able to use an iPhone or iPad to communicate via SSH to my VPS to do some simple bitcoin tasks. But a higher level of safety is required for significant funds.
|
> :warning: **WARNING:** Don’t use a VPS for a bitcoin wallet with significant real funds; see http://blog.thestateofme.com/2012/03/03/lessons-to-be-learned-from-the-linode-bitcoin-incident/ . It is very nice to be able experiment with real bitcoin transactions on a live node without tying up a self-hosted server on a local network. It's also useful to be able to use an iPhone or iPad to communicate via SSH to your VPS to do some simple bitcoin tasks. But a higher level of safety is required for significant funds.
|
||||||
|
|
||||||
If you want to instead do all the setup by hand, please read the parallel HOWTO file, [§2.1: Setting up a Bitcoin-Core VPS by Hand](02_1_Setting_Up_a_Bitcoin-Core_VPS_by_Hand.md).
|
* If you want to instead do all the setup by hand, goto [§2.1: Setting up a Bitcoin-Core VPS by Hand](02_1_Setting_Up_a_Bitcoin-Core_VPS_by_Hand.md).
|
||||||
|
* If you want to instead setup on a machine other than a Linode VPS, such as an AWS machine or a Mac, goto [§2.3: Setting Up a Bitcoin-Core via Other Means](02_3_Setting_Up_Bitcoin_Core_Other.md)
|
||||||
If you already have a Bitcoin node running, instead read the next HOWTO file, [Chapter Three: Understanding Your Bitcoin Setup](03_0_Understanding_Your_Bitcoin_Setup.md).
|
* If you already have a Bitcoin node running, goto [Chapter Three: Understanding Your Bitcoin Setup](03_0_Understanding_Your_Bitcoin_Setup.md).
|
||||||
|
|
||||||
## Getting Started with Linode
|
## Getting Started with Linode
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ https://www.linode.com/?r=23211828bc517e2cb36e0ca81b91cc8c0e1b2d96
|
|||||||
|
|
||||||
You'll need to provide an email address and later preload money from a credit card or PayPal for future costs.
|
You'll need to provide an email address and later preload money from a credit card or PayPal for future costs.
|
||||||
|
|
||||||
When you're done, you should land on https://manager.linode.com
|
When you're done, you should land on [https://cloud.linode.com/dashboard](https://cloud.linode.com/dashboard).
|
||||||
|
|
||||||
### Consider Two-Factor Authentication
|
### Consider Two-Factor Authentication
|
||||||
|
|
||||||
@ -40,119 +40,107 @@ Your server security won't be complete if people can break into your Linode acco
|
|||||||
|
|
||||||
### Load the StackScript
|
### Load the StackScript
|
||||||
|
|
||||||
There's a copy of our Bitcoin VPS Setup StackScript in [the repo here](02_2__Script_Linode_Setup.stackscript). This script basically automates the Bitcoin VPS setup instructions from [§2.1](02_1_Setting_Up_a_Bitcoin-Core_VPS_by_Hand.md). If you want to be particulary prudent, read it over carefully. If you are satisfied, you can copy that StackScript into your own account by going to [Manage StackScripts](https://manager.linode.com/stackscripts/index), or clicking the link under your list of Linodes. Click "Add a new StackScript", give it a good name (we use `Bitcoin VPS Setup`), then copy and paste the script.
|
Download the [Linode Standup Script](https://github.com/BlockchainCommons/Bitcoin-Standup-Scripts/blob/master/Scripts/LinodeStandUp.sh) from the [Bitcoin Standup Scripts repo](https://github.com/BlockchainCommons/Bitcoin-Standup-Scripts). This script basically automates the Bitcoin VPS setup instructions from [§2.1](02_1_Setting_Up_a_Bitcoin-Core_VPS_by_Hand.md). If you want to be particulary prudent, read it over carefully. If you are satisfied, you can copy that StackScript into your own account by going to the [Stackscripts page](https://cloud.linode.com/stackscripts?type=account) on your Linode account and selecting to [Create New Stackscript](https://cloud.linode.com/stackscripts/create). Give it a good name (we use `Bitcoin Standup`), then copy and paste the script. Choose Debian 10 for your target image and "Save" it.
|
||||||
|
|
||||||
### Add a Linode
|
### Do the Initial Setup
|
||||||
|
|
||||||
You'll next need to click to your Linodes tab and select "Add a Linode".
|
You're now ready to create a node based on the Stackscript.
|
||||||
|
|
||||||
A Linode 2G will suffice for most setups, including: Pruned Mainnet, Pruned Testnet, and even non-Pruned Testnet. They all use less than 50G of storage and they can barely get by with 2G of memory. This is the setup we suggest.
|
1. On the [Stackscripts page](https://cloud.linode.com/stackscripts?type=account), click on the "..." to the right of your new script and choose "Deploy New Linode".
|
||||||
|
2. Fill in a short and a fully qualified hostname
|
||||||
If you want to instead have a non-Pruned Mainnet in a VPS, you'll need to install a Linode with a disk in excess of 120G, which is currently the Linode 12288, which has 192G of storage and 12G of memory and costs approximately $80 per month. We do _not_ suggest this.
|
* **Short Hostname.** Pick a name for your VPS. For example, "mybtctest".
|
||||||
|
* **Fully Qualified Hostname.** If you're going to include this VPS as part of a network with full DNS records, type in the hostname with its domain. For example, "mybtctest.mydomain.com". Otherwise, just repeat the short hostname and add ".local", for example "mybtctest.local".
|
||||||
The following chart shows minimum requirements
|
3. Enter the password for the "standup" user.
|
||||||
|
4. Choose an Installation Type in the advanced options.
|
||||||
| Setup | Memory | Storage | Linnode |
|
* **Installation Type.** This is likely "Mainnet" or "Pruned Mainnet" if you are setting up a node for usage and "Pruned Testnet" if you're just playing around. See the [Appendix](#appendix-bitcoin-installation-types) for more information on these options.
|
||||||
|-------|--------|---------|---------|
|
5. Fill in any other appropriate advanced options.
|
||||||
| Mainnet | 2G | 120G | Linode 8GB |
|
* **X25519 Public Key.** This is a public key to add to Tor's list of authorized clients. If you don't use it, anyone who gets the QR code for your node can access it. You'll get this public key from whichever client you're using to connect to your node. For example, if you use [FullyNoded 2](https://github.com/BlockchainCommons/FullyNoded-2), you can go to its settings and "Export Tor V3 Authentication Public Key" for use here.
|
||||||
| Pruned Mainnet | 2G | ~5G | Linode 2GB |
|
* **SSH Key.** Copy your local computer's SSH key here; this allows you be able to automatically login in via SSH to the standup account. If you haven't setup an SSH key on your local computer yet, there are good instructions for it on [Github](https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/). You may also want to add your SSH key into your Linode LISH (Linode Interactive Shell) by going to your "Linode Home Page / My Preferences / LISH Settings / LISH Keys". Using an SSH key will give you a simpler and safer way to log in to your server.
|
||||||
| Testnet | 2G | ~15G | Linode 2GB |
|
* **SSH-Allowed IPs.** This is a comma-separated list of IPs that will be allowed to SSH into the VPS. For example "192.168.1.15,192.168.1.16". If you do not enter any IPs, _your VPS will not be very secure_. It will constantly be bombarded by attackers trying to find their way in, and they may very well succeed.
|
||||||
| Pruned Testnet | 2G | ~5G | Linode 2GB |
|
4. Select an Image
|
||||||
| Regtest | 2G | ~ | Linode 2GB |
|
* **Target Image.** If you followed the instructions, this will only allow you to select "Debian 10" (though "Debian 9" did also work with previous versions of this Stackscript and might still).
|
||||||
|
5. Choose a region for where the Linode will be located.
|
||||||
Just choose your Linode type, choose a Location that's geographically as close to you as possible, and click "Add your Linode!".
|
|
||||||
|
|
||||||
> :warning: **WARNING:** We've occasionally had machines run out of memory after running `bitcoind` for a few days when they only had 2G. Coming back, we find that `bitcoind` has stopped, leaving the message "Error: Out of memory. Terminating." in the `debug.log` file. This simply requires a restart of `bitcoind` and ten or fifteen minutes to get the blockchain resynced. This problem has seemed more prevelant starting with our upgrade to 0.18 (after largely going away), so it's possible you'll need to upgrade to the 4G machines solely due to memory issues.
|
|
||||||
|
|
||||||
_Be aware that the requirements might change over time as the blockchain continues to grow. Watch for "Out of Memory" or "Disk Space is Low!" errors. Either one indicates that you should migrate to the next larger machine!_
|
|
||||||
|
|
||||||
### Configure Your Linode
|
|
||||||
|
|
||||||
You should now be back on your list of Linodes page, with your VPS listed with a status of "Brand New".
|
|
||||||
|
|
||||||
Click on this new Linode to go to its Dashboard.
|
|
||||||
|
|
||||||
You will see that no disks are installed yet.
|
|
||||||
|
|
||||||
Make sure that Lindeo has completed the initial configuration, which just takes a minute or two. Just look for "Linode Initial Configuration" in the "Host Job Queue" and ensure that it has a little green "Success" buttton.
|
|
||||||
|
|
||||||
### Run the StackScript
|
|
||||||
|
|
||||||
You're now ready to run your StackScript!
|
|
||||||
|
|
||||||
Click on "Deploy an Image" then chooose "Deploying using StackScripts".
|
|
||||||
|
|
||||||
If you already added the StackScript to your account, it should be on the list of "Your StackScripts"; select it.
|
|
||||||
|
|
||||||
### Enter Stackscript Options
|
|
||||||
|
|
||||||
To make sure that you can set up your VPS to your specifications, the StackScript has a few options. Here's what you should enter into them.
|
|
||||||
|
|
||||||
_This setup may not work if you do not enter all of this mandatory information:_
|
|
||||||
|
|
||||||
**Installation Type.** See _Appendix I_ for more on these Bitcoin installation types. If you're planning to get on the main Bitcoin network, you'll probably want to choose "Pruned Mainnet". If you're wanting to play with Bitcoin Core and learn more about how it works, you'll probably want to choose "Unpruned Testnet".
|
|
||||||
|
|
||||||
**Short Hostname.** Pick a name for your VPS. For example, "mybtctest"
|
|
||||||
|
|
||||||
**Fully Qualified Hostname.** If you're going to include this VPS as part of a network with full DNS records, type in the hostname with its domain. For example, "mybtctest.mydomain.com". Otherwise, just repeat the short hostname and add ".local", for example "mybtctest.local".
|
|
||||||
|
|
||||||
**User Password.** Bitcoin will be setup in an account called "user1". This is the password for that account.
|
|
||||||
|
|
||||||
_You can freely choose to enter this optional information or skip it:_
|
|
||||||
|
|
||||||
**SSH Key.** Copy your local computer's SSH key here; this allows you be able to automatically login in via SSH to your user1 account. If you haven't setup an SSH key on your local computer yet, there are good instructions for it on [Github](https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/). You may also want to add your SSH key into your Linode LISH (Linode Interactive Shell) by going to your "Linode Home Page / My Preferences / LISH Settings / LISH Keys". Using an SSH key will give you a simpler and safer way to log in to your server.
|
|
||||||
|
|
||||||
**SSH-Allowed IPs.** This is a comma-separated list of IPs that will be allowed to SSH into the VPS. For example "192.168.1.15,192.168.1.16". If you do not enter any IPs, _your VPS will not be very secure_. It will constantly be bombarded by attackers trying to find their way in, and they may very well succeed.
|
|
||||||
|
|
||||||
*The remaining questions all have to do with the mechanics of the VPS deployment and should be left as they are with one exception: bump the Swap Disk from 256MB to 512MB, to ensure that you have enough memory to download the blockchain._
|
*The remaining questions all have to do with the mechanics of the VPS deployment and should be left as they are with one exception: bump the Swap Disk from 256MB to 512MB, to ensure that you have enough memory to download the blockchain._
|
||||||
|
|
||||||
Finally, you'll need to fill in a root password, which will be the password used for the root account.
|
Finally, you'll need to fill in a root password, which will be the password used for the root account.
|
||||||
|
|
||||||
|
### Choose a Linode Plan
|
||||||
|
|
||||||
|
You'll next to choose a Linode plan.
|
||||||
|
|
||||||
|
A Linode 4GB will suffice for most setups, including: Pruned Mainnet, Pruned Testnet, and even non-Pruned Testnet. They all use less than 50G of storage and 4GB is a comfortable amount of memory. This is the setup we suggest. It runs $20 per month.
|
||||||
|
|
||||||
|
If you want to instead have a non-Pruned Mainnet in a VPS, you'll need to install a Linode with a disk in excess of 280G(!), which is currently the Linode 16GB, which has 320G of storage and 16G of memory and costs approximately $80 per month. We do _not_ suggest this.
|
||||||
|
|
||||||
|
The following chart shows minimum requirements
|
||||||
|
|
||||||
|
| Setup | Memory | Storage | Linnode |
|
||||||
|
|-------|--------|---------|---------|
|
||||||
|
| Mainnet | 2G | 280G | Linode 16GB |
|
||||||
|
| Pruned Mainnet | 2G | ~5G | Linode 4GB |
|
||||||
|
| Testnet | 2G | ~15G | Linode 4GB |
|
||||||
|
| Pruned Testnet | 2G | ~5G | Linode 4GB |
|
||||||
|
| Regtest | 2G | ~ | Linode 4GB |
|
||||||
|
|
||||||
|
Note, there may be ways to reduce both costs.
|
||||||
|
|
||||||
|
* For the machines we suggest as **Linode 4GB**, you may be able to reduce that to a Linode 2GB. Some versions of Bitcoin Core have worked well at that size, some have occasionally run out of memory and then recovered, and some have continuously run out of memory. Remember to up that swap space to maximize the odds of this working. Use at your own risk.
|
||||||
|
* For the Unpruned Mainnet, which we suggest as a **Linode 16GB**, you can probably get by with a Linode 4GB, but add [Block Storage](https://cloud.linode.com/volumes) sufficient to store the blockchain. This is certainly a better long-term solution since the Bitcoin blockchain's storage requirements continuously increase if you don't prune, while the CPU requirements don't (or don't to the same degree). A 320 GibiByte storage would be $32 a month, which combined with a Linode 4GB is $52 a month, instead of $80, and more importantly you can keep growing it. We don't fully document this setup for two reasons (1) we don't suggest the unpruned mainnet setup, and so we suspect it's a much less common setup; and (2) we haven't tested how Linodes volumes compare to their intrinic SSDs for performance and usage. But there's full documentation on the Block Storage page. You'd need to set up the Linode, run its stackscript, but then interrupt it to move the blockchain storage overly to a newly commissioned volume before continuing.
|
||||||
|
|
||||||
|
|
||||||
|
### Do the Final Setup
|
||||||
|
|
||||||
|
The last thing you need to do is enter a root password. (If you missed anything, you'll be told so now!)
|
||||||
|
|
||||||
Click "Deploy" to initialize your disks and to prepare your VPS. The whole queue should run in less than a minute. When it's done you should see in the "Host Job Queue", green "Success" buttons stating "Disk Create from StackScript - Setting password for root… done." and "Create Filesystem - 256MB Swap Image".
|
Click "Deploy" to initialize your disks and to prepare your VPS. The whole queue should run in less than a minute. When it's done you should see in the "Host Job Queue", green "Success" buttons stating "Disk Create from StackScript - Setting password for root… done." and "Create Filesystem - 256MB Swap Image".
|
||||||
|
|
||||||
You may now want to change your Linode VPS's name from the default `linodexxxxxxxx`. Go to the Settings tab, and change the label to be more useful, such as your VPS's short hostname. For instance I have renamed mine to `bitcoin-testnet-pruned` to differentiate it from other VPSs in my account.
|
You may now want to change your Linode VPS's name from the default `linodexxxxxxxx`. Go to the Settings tab, and change the label to be more useful, such as your VPS's short hostname. For instance you might name it `bitcoin-testnet-pruned` to differentiate it from other VPSs in your account.
|
||||||
|
|
||||||
## Boot Your VPS
|
## Login to Your VPS
|
||||||
|
|
||||||
Your Linode VPS is now ready to boot. If you are not at your new VPS's Dashboard, click on it.
|
If you watch your Linode control panel, you should see the new computer spin up. When the job has reached 100%, you'll be able to login.
|
||||||
|
|
||||||
Now select the button "Boot". As soon as you see the green button "Success: System Boot" you can login.
|
|
||||||
|
|
||||||
First, you'll need the IP address. Click on the "Linodes" tab and you should see a listing of your VPS, the fact that it's running, its "plan", its IP address, and some other information.
|
First, you'll need the IP address. Click on the "Linodes" tab and you should see a listing of your VPS, the fact that it's running, its "plan", its IP address, and some other information.
|
||||||
|
|
||||||
Go to your local console and login to the user1 account using that address:
|
Go to your local console and login to the `standup` account using that address:
|
||||||
|
|
||||||
```
|
```
|
||||||
ssh user1@[IP-ADDRESS]
|
ssh standup@[IP-ADDRESS]
|
||||||
```
|
```
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
```
|
```
|
||||||
ssh user1@192.168.33.11
|
ssh standup@192.168.33.11
|
||||||
```
|
```
|
||||||
|
|
||||||
If you configured your VPS to use an SSH key, the login should be automatic (possibly requiring your SSH password to unlock your key). If you didn't configure a SSH key, then you'll need to type in the user1 password.
|
If you configured your VPS to use an SSH key, the login should be automatic (possibly requiring your SSH password to unlock your key). If you didn't configure a SSH key, then you'll need to type in the user1 password.
|
||||||
|
|
||||||
### Get an Espresso
|
### Wait a Few Minutes
|
||||||
|
|
||||||
Here's the big catch: _your StackScript is running right now_. The BASH script gets executed the first time the VPS is booted. That means your VPS isn't ready yet.
|
Here's a little catch: _your StackScript is running right now_. The BASH script gets executed the first time the VPS is booted. That means your VPS isn't ready yet.
|
||||||
|
|
||||||
So, go take a break, get an espresso, or otherwise relax for a few minutes. There are two parts of the script that take a while: the updating of all the Debian packages; and the downloading of the Bitcoin code. They shouldn't take more than 5 minutes each, which means if you come back in 10 minutes, you'll probably be ready to go.
|
The total run time is about 10 minutes. So, go take a break, get an espresso, or otherwise relax for a few minutes. There are two parts of the script that take a while: the updating of all the Debian packages; and the downloading of the Bitcoin code. They shouldn't take more than 5 minutes each, which means if you come back in 10 minutes, you'll probably be ready to go.
|
||||||
|
|
||||||
If you're impatient you can jump ahead and `sudo tail -f ~root/stackscript.log` which will display the current progress of installation, as described in the next section.
|
If you're impatient you can jump ahead and `sudo tail -f ~root/standup.log` which will display the current progress of installation, as described in the next section.
|
||||||
|
|
||||||
## Verify Your Installation
|
## Verify Your Installation
|
||||||
|
|
||||||
You will know the StackScripts are done when a BITCOIN-IS-READY file appears in the user1 home directory. At that point, your home directory should look like this:
|
You'll know that stackscrpit is done when the `tail` of the `standup.log` says something like the following:
|
||||||
|
```
|
||||||
|
/root/StackScript - Bitcoin is setup as a service and will automatically start if your VPS reboots and so is Tor
|
||||||
|
/root/StackScript - You can manually stop Bitcoin with: sudo systemctl stop bitcoind.service
|
||||||
|
/root/StackScript - You can manually start Bitcoin with: sudo systemctl start bitcoind.service
|
||||||
|
```
|
||||||
|
At that point, your home directory should look like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ ls
|
$ ls
|
||||||
bitcoin-0.18.0-x86_64-linux-gnu.tar.gz laanwj-releases.asc
|
bitcoin-0.20.0-x86_64-linux-gnu.tar.gz laanwj-releases.asc SHA256SUMS.asc
|
||||||
BITCOIN-IS-READY SHA256SUMS.asc
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Alongside the BITCOIN-IS-READY file are the various files that were used to install Bitcoin on your VPS. _None_ of them are necessary. We've just left them in case you want to do any additional verification. Otherwise, you can delete them:
|
These are the various files that were used to install Bitcoin on your VPS. _None_ of them are necessary. We've just left them in case you want to do any additional verification. Otherwise, you can delete them:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ rm *
|
$ rm *
|
||||||
@ -163,17 +151,16 @@ $ rm *
|
|||||||
In order to ensure that the downloaded Bitcoin release is valid, the StackScript checks both the signature and the SHA checksum. You should verify that both of those tests came back right:
|
In order to ensure that the downloaded Bitcoin release is valid, the StackScript checks both the signature and the SHA checksum. You should verify that both of those tests came back right:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ sudo grep VERIFICATION ~root/stackscript.log
|
$ sudo grep VERIFICATION ~root/standup.log
|
||||||
```
|
```
|
||||||
|
|
||||||
If you see something like the following, all should be well:
|
If you see something like the following, all should be well:
|
||||||
|
|
||||||
```
|
```
|
||||||
VERIFICATION SUCCESS / SIG: gpg: Good signature from "Wladimir J. van der Laan (Bitcoin Core binary release signing key) <laanwj@gmail.com>"
|
/root/StackScript - VERIFICATION SUCCESS / SIG: gpg: Good signature from "Wladimir J. van der Laan (Bitcoin Core binary release signing key) <laanwj@gmail.com>" [unknown]
|
||||||
VERIFICATION SUCCESS / SHA: 29215a7fe7430224da52fc257686d2d387546eb8acd573a949128696e8761149
|
/root/StackScript - VERIFICATION SUCCESS / SHA: 35ec10f87b6bc1e44fd9cd1157e5dfa4```
|
||||||
```
|
```
|
||||||
|
However, if either of those two checks instead reads "VERIFICATION ERROR", then there's a problem. Since this is all scripted, it's possible that there's just been a minor change that has caused the script's checks not to work right. (This has happened a few times over the existence of the script that became Standup.) But, it's also possible that someone is trying to encourage you to run a fake copy of the Bitcoin daemon. So, _be very sure you know what happened before you make use of Bitcoin!_
|
||||||
However, if either of those two checks instead reads "VERIFICATION ERROR", then there's a problem. Since this is all scripted, it's possible that there's just been a minor change that has caused the script's checks not to work right. But, it's also possible that someone is trying to encourage you to run a fake copy of the Bitcoin daemon. So, _be very sure you know what happened before you make use of Bitcoin!_
|
|
||||||
|
|
||||||
### Read the Logs
|
### Read the Logs
|
||||||
|
|
||||||
@ -181,13 +168,13 @@ You may also want to read through all of the setup log files, to make sure that
|
|||||||
|
|
||||||
It's best to look through the standard StackScript log file, which has all of the output, including errors:
|
It's best to look through the standard StackScript log file, which has all of the output, including errors:
|
||||||
|
|
||||||
`$ sudo more ~root/stackscript.log`
|
`$ sudo more ~root/standup.log`
|
||||||
|
|
||||||
Note that it is totally normal to see _some_ errors, particularly when running the very noisy gpg software and when various things try to access the non-existant /dev/tty device.
|
Note that it is totally normal to see _some_ errors, particularly when running the very noisy gpg software and when various things try to access the non-existant `/dev/tty` device.
|
||||||
|
|
||||||
If you want instead to look at a smaller set of info, all of the errors should be in:
|
If you want instead to look at a smaller set of info, all of the errors should be in:
|
||||||
|
|
||||||
`$ sudo more ~root/stackscript.err`
|
`$ sudo more ~root/standup.err`
|
||||||
|
|
||||||
It still has a fair amount of information that isn't errors, but it's a quicker read.
|
It still has a fair amount of information that isn't errors, but it's a quicker read.
|
||||||
|
|
||||||
@ -195,37 +182,19 @@ If all look good, congratulations, you have a functioning Bitcoin node using Lin
|
|||||||
|
|
||||||
## What We Have Wrought
|
## What We Have Wrought
|
||||||
|
|
||||||
Although the default Debian 8 image that we are using for your VPS has been modified by Linode to be relatively secure, your Bitcoin node as installed through the Linode StackScript is set up with an even high level of security. You may find this limiting, or be unable to do things that you expect. Here are a few notes on that:
|
Although the default Debian 10 image that we are using for your VPS has been modified by Linode to be relatively secure, your Bitcoin node as installed through the Linode StackScript is set up with an even high level of security. You may find this limiting, or be unable to do things that you expect. Here are a few notes on that:
|
||||||
|
|
||||||
### Protected Services
|
### Protected Services
|
||||||
|
|
||||||
Your Bitcoin VPS installation is minimal and allows almost no communication. This is managed through Part 3 of the StackScript, which blocks access to most of the ports on the VPS.
|
Your Bitcoin VPS installation is minimal and allows almost no communication. This is managed through Part 5 of the StackScript, which sets up Tor and ensures that it's the only way to speak with the Bitcoin ports, other than localhost connections. It's further supplemented by the uncomplicated firewall (`ufw`), which blocks everything except SSH connections.
|
||||||
|
|
||||||
Two files control these restrictions, one for IPv4 and one for IPv6:
|
**Adjusting Tor.** You might want to better protect services like SSH. See [Chapter 12: Using Tor](https://github.com/BlockchainCommons/Learning-Bitcoin-from-the-Command-Line/blob/master/12_0_Using_Tor.md) for more on Tor.
|
||||||
|
|
||||||
```
|
**Adjusting UFW.** You should probably leave UFW in its super-protected stage! You don't want to use a Bitcoin machine for other services, because everyone increases your vulnerability! If you decide otherwise, there are several [guides to UFW](https://www.digitalocean.com/community/tutorials/ufw-essentials-common-firewall-rules-and-commands) that will allow you to add services. As advertised, uit's ncomplicated. For example adding mail services would just require opening the mail port: `sudo ufw allow 25`. But don't do that.
|
||||||
/etc/iptables.firewall.rules
|
|
||||||
/etc/ip6tables.firewall.rules
|
|
||||||
```
|
|
||||||
|
|
||||||
If you look at the rules, you'll see they allow all outbound traffic and all local loopback traffic. However, for inbound traffic, the only connections accepted are Bitcoin, Ping, and SSH. If you want to accept other types of traffic, you will need to open up additional ports ("dports") using the same methodology as shown in the Bitcoin port 8333 connection. For example, to allow connections to port 25 (mail), would require adding the following to the iptables and the ip6tables:
|
|
||||||
|
|
||||||
```
|
|
||||||
-A INPUT -p tcp --dport 25 -j ACCEPT
|
|
||||||
-A INPUT -p udp --dport 25 -j ACCEPT
|
|
||||||
```
|
|
||||||
|
|
||||||
This example opens up access to port 25 for TCP and UDP connections. Other popular ports are 80 (HTTP), 443 (HTTPS), 53 (DNS), 110 (POP), and 143 (IMAP). Be sure any such changes are put above the logs and defaults that appear at the end of the iptables.firewall.rules files.
|
|
||||||
|
|
||||||
You can immediately incorporate your changes by running the following script:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ /etc/network/if-pre-up.d/firewall
|
|
||||||
```
|
|
||||||
|
|
||||||
### Protected Shells
|
### Protected Shells
|
||||||
|
|
||||||
If you provided an IP access for SSH control, SSH (and SCP) access to the server is severely restricted. /etc/hosts.deny disallows anyone from logging in. _We do not suggest changing this_. /etc/hosts.allow then allows specific IP addresses. Just add more IP addresses in a comma-separated list if you need to offer more access.
|
If you defined "SSH-allowed IPs", SSH (and SCP) access to the server is severely restricted. `/etc/hosts.deny` disallows anyone from logging in. _We do not suggest changing this_. `/etc/hosts.allow` then allows specific IP addresses. Just add more IP addresses in a comma-separated list if you need to offer more access.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
@ -247,32 +216,33 @@ echo "unattended-upgrades unattended-upgrades/enable_auto_updates boolean false"
|
|||||||
|
|
||||||
So now you probably want to play with Bitcoin!
|
So now you probably want to play with Bitcoin!
|
||||||
|
|
||||||
But wait, your Bitcoin daemon is probably still downloading blocks. This alias, from your .bash configuration will tell you how things are going:
|
But wait, your Bitcoin daemon is probably still downloading blocks. The `bitcoin-cli getblockcount` will tell you how you're currently doing:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ btcblock
|
$ bitcoin-cli getblockcount
|
||||||
|
1771352
|
||||||
```
|
```
|
||||||
|
If it's different every time you type the command, you need to wait before working with Bitcoin. This takes 1-6 hours currently for a pruned setup, depending on your precise machine.
|
||||||
|
|
||||||
If you choose one of the pruned options, it'll probably take a day to download everything.
|
But, once it settles at a number, you're ready to continue!
|
||||||
|
|
||||||
So, it might be time for a few more espressos. But soon enough, your system will be ready to go, and you'll be read to start experimenting.
|
Still, it might be time for a few more espressos. But soon enough, your system will be ready to go, and you'll be read to start experimenting.
|
||||||
|
|
||||||
## Summary: Setting Up a Bitcoin-Core VPS by Hand
|
## Summary: Setting Up a Bitcoin-Core VPS by Hand
|
||||||
|
|
||||||
Creating a Bitcoin-Core VPS with a StackScript made the whole process quick, simple and (hopefully) painless.
|
Creating a Bitcoin-Core VPS with the Standup scripts made the whole process quick, simple and (hopefully) painless.
|
||||||
|
|
||||||
## What's Next?
|
## What's Next?
|
||||||
|
|
||||||
You have a few options for what's next:
|
You have a few options for what's next:
|
||||||
|
|
||||||
* Read the [StackScript](02_2__Script_Linode_Setup.stackscript) to understand your setup.
|
* Read the [StackScript](https://github.com/BlockchainCommons/Bitcoin-Standup-Scripts/blob/master/Scripts/LinodeStandUp.sh) to understand your setup.
|
||||||
* See the other method for setting up a VPS in [§2.1: Setting up a Bitcoin-Core VPS by Hand](02_1_Setting_Up_a_Bitcoin-Core_VPS_by_Hand.md).
|
* See the other method for setting up a VPS in [§2.1: Setting up a Bitcoin-Core VPS by Hand](02_1_Setting_Up_a_Bitcoin-Core_VPS_by_Hand.md).
|
||||||
* Choose an entirely alternate methodology in [§2.3: Setting Up a Bitcoin-Core Machine via Other Means](02_3_Setting_Up_Bitcoin_Core_Other.md).
|
* Choose an entirely alternate methodology in [§2.3: Setting Up a Bitcoin-Core Machine via Other Means](02_3_Setting_Up_Bitcoin_Core_Other.md).
|
||||||
* Move on to "bitcoin-cli" with [Chapter Three: Understanding Your Bitcoin Setup](03_0_Understanding_Your_Bitcoin_Setup.md).
|
* Move on to "bitcoin-cli" with [Chapter Three: Understanding Your Bitcoin Setup](03_0_Understanding_Your_Bitcoin_Setup.md).
|
||||||
|
|
||||||
## Appendix: Bitcoin Installation Types
|
## Appendix: Bitcoin Installation Types
|
||||||
|
|
||||||
**Mainnet.** This will download the entirety of the Bitnet blockchain. That's 120G of data (and getting more every day).
|
**Mainnet.** This will download the entirety of the Bitnet blockchain. That's 280G of data (and getting more every day).
|
||||||
|
|
||||||
**Pruned Mainnet.** This will cut the blockchain you're storing down to just the last 550 blocks. If you're not mining or running some other Bitcoin service, this should be plenty for validation.
|
**Pruned Mainnet.** This will cut the blockchain you're storing down to just the last 550 blocks. If you're not mining or running some other Bitcoin service, this should be plenty for validation.
|
||||||
|
|
||||||
|
@ -1,358 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# This block defines the variables the user of the script needs to input
|
|
||||||
# when deploying using this script.
|
|
||||||
#
|
|
||||||
# <UDF name="btctype" label="Installation Type" oneOf="Mainnet,Pruned Mainnet,Testnet,Pruned Testnet,Private Regtest" default="Testnet" example="Bitcoin node type" />
|
|
||||||
# BTCTYPE=
|
|
||||||
# <UDF name="hostname" label="Short Hostname" example="Example: bitcoincore-testnet-pruned" />
|
|
||||||
# HOSTNAME=
|
|
||||||
# <UDF name="fqdn" label="Fully Qualified Hostname" example="Example: bitcoincore-testnet-pruned.local or bitcoincore-testnet-pruned.domain.com"/>
|
|
||||||
# FQDN=
|
|
||||||
# <UDF name="userpassword" label="User1 Password" example="Password to for the user1 non-privileged account." />
|
|
||||||
# USERPASSWORD=
|
|
||||||
# <UDF name="ssh_key" label="SSH Key" default="" example="Key for automated logins to user1 non-privileged account." optional="true" />
|
|
||||||
# SSH_KEY=
|
|
||||||
# <UDF name="sys_ssh_ip" label="SSH-Allowed IPs" default="" example="Comma separated list of IPs that can use SSH" optional="true" />
|
|
||||||
# SYS_SSH_IP=
|
|
||||||
|
|
||||||
####
|
|
||||||
# 0. Set Initial Variables
|
|
||||||
####
|
|
||||||
|
|
||||||
# CURRENT BITCOIN RELEASE:
|
|
||||||
# Change as necessary
|
|
||||||
|
|
||||||
export BITCOIN=bitcoin-core-0.18.0
|
|
||||||
|
|
||||||
# Set the variable $IPADDR to the IP address the new Linode receives.
|
|
||||||
IPADDR=$(/sbin/ifconfig eth0 | awk '/inet / { print $2 }' | sed 's/addr://')
|
|
||||||
|
|
||||||
# Output stdout and stderr to ~root files
|
|
||||||
|
|
||||||
exec > >(tee -a /root/stackscript.log) 2> >(tee -a /root/stackscript.log /root/stackscript.err >&2)
|
|
||||||
|
|
||||||
echo "$0 - BEGINNING NEW MACHINE SETUP STACKSCRIPT"
|
|
||||||
echo "$0 - BITCOIN SETUP TYPE IS: $BTCTYPE"
|
|
||||||
|
|
||||||
####
|
|
||||||
# 1. Update Hostname
|
|
||||||
####
|
|
||||||
|
|
||||||
echo $HOSTNAME > /etc/hostname
|
|
||||||
# /etc/init.d/hostname.sh start
|
|
||||||
/bin/hostname $HOSTNAME
|
|
||||||
|
|
||||||
echo "$0 - Set hostname as $FQDN ($IPADDR)"
|
|
||||||
echo "$0 - TODO: Put $FQDN with IP $IPADDR in your main DNS file."
|
|
||||||
|
|
||||||
# Add localhost aliases
|
|
||||||
|
|
||||||
echo "127.0.0.1 localhost" > /etc/hosts
|
|
||||||
echo "127.0.1.1 $FQDN $HOSTNAME" >> /etc/hosts
|
|
||||||
|
|
||||||
echo "$0 - Set localhost"
|
|
||||||
|
|
||||||
####
|
|
||||||
# 2. Update Timezone
|
|
||||||
####
|
|
||||||
|
|
||||||
# Set Timezone to America/LA
|
|
||||||
|
|
||||||
TIMEZONE="America/Los_Angeles"
|
|
||||||
echo $TIMEZONE > /etc/timezone
|
|
||||||
cp /usr/share/zoneinfo/${TIMEZONE} /etc/localtime
|
|
||||||
|
|
||||||
echo "$0 - Set Time Zone to Lost Angeles"
|
|
||||||
|
|
||||||
####
|
|
||||||
# 3. Protect the Server
|
|
||||||
####
|
|
||||||
|
|
||||||
# Add firewall rules to block everything that's not Bitcoin, Ping, or SSH
|
|
||||||
|
|
||||||
cat > /etc/iptables.firewall.rules <<EOF
|
|
||||||
*filter
|
|
||||||
|
|
||||||
# Allow all loopback (lo0) traffic and drop all traffic to 127/8 that doesn't use lo0
|
|
||||||
-A INPUT -i lo -j ACCEPT
|
|
||||||
-A INPUT -d 127.0.0.0/8 -j REJECT
|
|
||||||
|
|
||||||
# Accept all established inbound connections
|
|
||||||
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
|
||||||
|
|
||||||
# Allow all outbound traffic - you can modify this to only allow certain traffic
|
|
||||||
-A OUTPUT -j ACCEPT
|
|
||||||
|
|
||||||
# If you want HTTP and HTTPS, uncomment these
|
|
||||||
|
|
||||||
# Allow SSH connections
|
|
||||||
#
|
|
||||||
# The -dport number should be the same port number you set in sshd_config
|
|
||||||
#
|
|
||||||
-A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT
|
|
||||||
|
|
||||||
# Allow ping
|
|
||||||
-A INPUT -p icmp -j ACCEPT
|
|
||||||
|
|
||||||
# Allow Bitcoin connections
|
|
||||||
-A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
|
|
||||||
-A INPUT -p tcp --dport 8333 -j ACCEPT
|
|
||||||
-A INPUT -p tcp --dport 18333 -j ACCEPT
|
|
||||||
-A INPUT -p tcp --dport 9735 -j ACCEPT
|
|
||||||
-A INPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT
|
|
||||||
|
|
||||||
# Log iptables denied calls
|
|
||||||
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
|
|
||||||
|
|
||||||
# Drop all other inbound - default deny unless explicitly allowed policy
|
|
||||||
-A INPUT -j DROP
|
|
||||||
-A FORWARD -j DROP
|
|
||||||
|
|
||||||
COMMIT
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Make a copy of the IPv4 rules for IPv6
|
|
||||||
|
|
||||||
cat /etc/iptables.firewall.rules | sed 's/127.0.0.0\/8/::1\/128/' > /etc/ip6tables.firewall.rules
|
|
||||||
|
|
||||||
# Make a startup file that runs IPv4 and IPv6 rules
|
|
||||||
|
|
||||||
cat > /etc/network/if-pre-up.d/firewall <<EOF
|
|
||||||
#!/bin/sh
|
|
||||||
/sbin/iptables-restore < /etc/iptables.firewall.rules
|
|
||||||
/sbin/ip6tables-restore < /etc/ip6tables.firewall.rules
|
|
||||||
EOF
|
|
||||||
|
|
||||||
chmod a+x /etc/network/if-pre-up.d/firewall
|
|
||||||
|
|
||||||
# Then run it
|
|
||||||
|
|
||||||
/etc/network/if-pre-up.d/firewall
|
|
||||||
|
|
||||||
echo "$0 - Created iptables. NOTE! This will prevent everything but Bitcoin, Ping, and SSH from working!!"
|
|
||||||
|
|
||||||
# Put your Login IPs into the hosts.allow file to allow access
|
|
||||||
|
|
||||||
if [ -n "$SYS_SSH_IP" ]; then
|
|
||||||
|
|
||||||
echo "sshd: $SYS_SSH_IP" >> /etc/hosts.allow
|
|
||||||
echo "sshd: ALL" >> /etc/hosts.deny
|
|
||||||
echo "$0 - Limited SSH access."
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
echo "$0 - WARNING: Your SSH access is not limited; this is a major security hole!"
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Block SSH access from everywhere else
|
|
||||||
|
|
||||||
# Yes, this means that if you don't have an IP address for SSH, you can only login
|
|
||||||
# from Linode's Lish Console
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
####
|
|
||||||
# 4. Set Up User
|
|
||||||
####
|
|
||||||
|
|
||||||
# Create "user1" with optional password and give them sudo capability
|
|
||||||
|
|
||||||
/usr/sbin/useradd -m -p `perl -e 'printf("%s\n",crypt($ARGV[0],"password"))' "$USERPASSWORD"` -g sudo -s /bin/bash user1
|
|
||||||
/usr/sbin/adduser user1 sudo
|
|
||||||
|
|
||||||
echo "$0 - Setup user1 with sudo access."
|
|
||||||
|
|
||||||
# Set up SSH Key
|
|
||||||
|
|
||||||
if [ -n "$SSH_KEY" ]; then
|
|
||||||
|
|
||||||
mkdir ~user1/.ssh
|
|
||||||
echo "$SSH_KEY" >> ~user1/.ssh/authorized_keys
|
|
||||||
chown -R user1 ~user1/.ssh
|
|
||||||
|
|
||||||
echo "$0 - Added .ssh key to user1."
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Give user some helpful bitcoin aliases
|
|
||||||
|
|
||||||
if [ "$BTCTYPE" == "Testnet" ] || [ "$BTCTYPE" == "Pruned Testnet" ]; then
|
|
||||||
|
|
||||||
sudo -u user1 cat >> ~user1/.bash_profile <<EOF
|
|
||||||
alias btcdir="cd ~/.bitcoin/" #linux default bitcoind path
|
|
||||||
# alias btcdir="cd ~/Library/Application\ Support/Bitcoin/" #mac default bitcoind path
|
|
||||||
alias bc="bitcoin-cli"
|
|
||||||
alias bd="bitcoind"
|
|
||||||
alias btcinfo='bitcoin-cli getwalletinfo | egrep "\"balance\""; bitcoin-cli getnetworkinfo | egrep "\"version\"|connections"; bitcoin-cli getmininginfo | egrep "\"blocks\"|errors"'
|
|
||||||
alias btcblock="echo \\\`bitcoin-cli getblockcount 2>&1\\\`/\\\`wget -O - http://blockexplorer.com/testnet/q/getblockcount 2> /dev/null | cut -d : -f2 | rev | cut -c 2- | rev\\\`"
|
|
||||||
EOF
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
sudo -u user1 cat >> ~user1/.bash_profile <<EOF
|
|
||||||
alias btcdir="cd ~/.bitcoin/" #linux default bitcoind path
|
|
||||||
# alias btcdir="cd ~/Library/Application\ Support/Bitcoin/" #mac default bitcoind path
|
|
||||||
alias bc="bitcoin-cli"
|
|
||||||
alias bd="bitcoind"
|
|
||||||
alias btcinfo='bitcoin-cli getwalletinfo | egrep "\"balance\""; bitcoin-cli getnetworkinginfo | egrep "\"version\"|connections"; bitcoin-cli getmininginfo | egrep "\"blocks\"|errors"'
|
|
||||||
alias btcblock="echo \\\`bitcoin-cli getblockcount 2>&1\\\`/\\\`wget -O - http://blockchain.info/q/getblockcount 2>/dev/null\\\`"
|
|
||||||
EOF
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
/bin/chown user1 ~user1/.bash_profile
|
|
||||||
cp ~user1/.bash_profile ~user1/.bashrc
|
|
||||||
/bin/chown user1 ~user1/.bashrc
|
|
||||||
|
|
||||||
echo "$0 - Give user1 bitcoin aliases in their .bash_profile."
|
|
||||||
|
|
||||||
####
|
|
||||||
# 5. Bring Debian Up To Date
|
|
||||||
####
|
|
||||||
|
|
||||||
echo "$0 - Starting Debian updates; this will take a while!"
|
|
||||||
|
|
||||||
# Make sure all packages are up-to-date
|
|
||||||
|
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
|
||||||
apt-get update
|
|
||||||
apt-get upgrade -y
|
|
||||||
apt-get dist-upgrade -y
|
|
||||||
|
|
||||||
# Install emacs (a good text editor), haveged (a random number generator
|
|
||||||
|
|
||||||
apt-get install emacs -y
|
|
||||||
apt-get install haveged -y
|
|
||||||
|
|
||||||
# Set system to automatically update
|
|
||||||
|
|
||||||
echo "unattended-upgrades unattended-upgrades/enable_auto_updates boolean true" | debconf-set-selections
|
|
||||||
apt-get -y install unattended-upgrades
|
|
||||||
|
|
||||||
echo "$0 - Updated Debian Packages"
|
|
||||||
|
|
||||||
|
|
||||||
####
|
|
||||||
# 6. Install Bitcoin
|
|
||||||
####
|
|
||||||
|
|
||||||
# Download Bitcoin
|
|
||||||
|
|
||||||
echo "$0 - Downloading Bitcoin; this will also take a while!"
|
|
||||||
|
|
||||||
export BITCOINPLAIN=`echo $BITCOIN | sed 's/bitcoin-core/bitcoin/'`
|
|
||||||
|
|
||||||
sudo -u user1 wget https://bitcoin.org/bin/$BITCOIN/$BITCOINPLAIN-x86_64-linux-gnu.tar.gz -O ~user1/$BITCOINPLAIN-x86_64-linux-gnu.tar.gz
|
|
||||||
sudo -u user1 wget https://bitcoin.org/bin/$BITCOIN/SHA256SUMS.asc -O ~user1/SHA256SUMS.asc
|
|
||||||
sudo -u user1 wget https://bitcoin.org/laanwj-releases.asc -O ~user1/laanwj-releases.asc
|
|
||||||
|
|
||||||
# Verifying Bitcoin: Signature
|
|
||||||
|
|
||||||
echo "$0 - Verifying Bitcoin."
|
|
||||||
|
|
||||||
sudo -u user1 /usr/bin/gpg --no-tty --import ~user1/laanwj-releases.asc
|
|
||||||
export SHASIG=`sudo -u user1 /usr/bin/gpg --no-tty --verify ~user1/SHA256SUMS.asc 2>&1 | grep "Good signature"`
|
|
||||||
echo "SHASIG is $SHASIG"
|
|
||||||
|
|
||||||
if [[ $SHASIG ]]; then
|
|
||||||
echo "VERIFICATION SUCCESS / SIG: $SHASIG"
|
|
||||||
else
|
|
||||||
(>&2 echo "VERIFICATION ERROR: Signature for Bitcoin did not verify!")
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Verify Bitcoin: SHA
|
|
||||||
|
|
||||||
export TARSHA256=`/usr/bin/sha256sum ~user1/$BITCOINPLAIN-x86_64-linux-gnu.tar.gz | awk '{print $1}'`
|
|
||||||
export EXPECTEDSHA256=`cat ~user1/SHA256SUMS.asc | grep $BITCOINPLAIN-x86_64-linux-gnu.tar.gz | awk '{print $1}'`
|
|
||||||
|
|
||||||
if [ "$TARSHA256" == "$EXPECTEDSHA256" ]; then
|
|
||||||
echo "VERIFICATION SUCCESS / SHA: $TARSHA256"
|
|
||||||
else
|
|
||||||
(>&2 echo "VERIFICATION ERROR: SHA for Bitcoin did not match!")
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install Bitcoin
|
|
||||||
|
|
||||||
echo "$0 - Installinging Bitcoin."
|
|
||||||
|
|
||||||
sudo -u user1 /bin/tar xzf ~user1/$BITCOINPLAIN-x86_64-linux-gnu.tar.gz -C ~user1
|
|
||||||
/usr/bin/install -m 0755 -o root -g root -t /usr/local/bin ~user1/$BITCOINPLAIN/bin/*
|
|
||||||
/bin/rm -rf ~user1/$BITCOINPLAIN/
|
|
||||||
|
|
||||||
# Start Up Bitcoin
|
|
||||||
|
|
||||||
echo "$0 - Starting Bitcoin."
|
|
||||||
|
|
||||||
sudo -u user1 /bin/mkdir ~user1/.bitcoin
|
|
||||||
|
|
||||||
# The only variation between Mainnet and Testnet is that Testnet has the "testnet=1" variable
|
|
||||||
# The only variation between Regular and Pruned is that Pruned has the "prune=550" variable, which is the smallest possible prune
|
|
||||||
|
|
||||||
# TODO: need to test rpcpassword random below using EOF technique
|
|
||||||
# TODO: there are other more modern ways to set up rpc authentication — to investigate and document.
|
|
||||||
|
|
||||||
cat >> ~user1/.bitcoin/bitcoin.conf << EOF
|
|
||||||
server=1
|
|
||||||
dbcache=1536
|
|
||||||
par=1
|
|
||||||
blocksonly=1
|
|
||||||
maxuploadtarget=137
|
|
||||||
maxconnections=16
|
|
||||||
rpcuser=bitcoinrpc
|
|
||||||
rpcpassword=$(xxd -l 16 -p /dev/urandom)
|
|
||||||
rpcallowip=127.0.0.1
|
|
||||||
walletbroadcast=1
|
|
||||||
EOF
|
|
||||||
|
|
||||||
if [ "$BTCTYPE" == "Mainnet" ]; then
|
|
||||||
|
|
||||||
cat >> ~user1/.bitcoin/bitcoin.conf << EOF
|
|
||||||
txindex=1
|
|
||||||
EOF
|
|
||||||
|
|
||||||
elif [ "$BTCTYPE" == "Pruned Mainnet" ]; then
|
|
||||||
|
|
||||||
cat >> ~user1/.bitcoin/bitcoin.conf << EOF
|
|
||||||
prune=550
|
|
||||||
EOF
|
|
||||||
|
|
||||||
elif [ "$BTCTYPE" == "Testnet" ]; then
|
|
||||||
|
|
||||||
cat >> ~user1/.bitcoin/bitcoin.conf << EOF
|
|
||||||
txindex=1
|
|
||||||
testnet=1
|
|
||||||
EOF
|
|
||||||
|
|
||||||
elif [ "$BTCTYPE" == "Pruned Testnet" ]; then
|
|
||||||
|
|
||||||
cat >> ~user1/.bitcoin/bitcoin.conf << EOF
|
|
||||||
prune=550
|
|
||||||
testnet=1
|
|
||||||
EOF
|
|
||||||
|
|
||||||
elif [ "$BTCTYPE" == "Private Regtest" ]; then
|
|
||||||
|
|
||||||
(>&2 echo "$0 - ERROR: Private Regtest is not setup yet.")
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
(>&2 echo "$0 - ERROR: Somehow you managed to select no Bitcoin Installation Type, so Bitcoin hasn't been properly setup. Whoops!")
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
/bin/chown user1 ~user1/.bitcoin/bitcoin.conf
|
|
||||||
/bin/chmod 600 ~user1/.bitcoin/bitcoin.conf
|
|
||||||
|
|
||||||
sudo -u user1 /usr/local/bin/bitcoind -daemon
|
|
||||||
|
|
||||||
# Add Bitcoin Startup to Crontab for User1
|
|
||||||
|
|
||||||
sudo -u user1 sh -c '( /usr/bin/crontab -l -u user1 2>/dev/null; echo "@reboot /usr/local/bin/bitcoind -daemon" ) | /usr/bin/crontab -u user1 -'
|
|
||||||
|
|
||||||
# Alert User!
|
|
||||||
|
|
||||||
sudo -u user1 touch ~user1/BITCOIN-IS-READY
|
|
||||||
|
|
||||||
echo "$0 - ENDING NEW MACHINE SETUP STACKSCRIPT"
|
|
@ -2,14 +2,12 @@
|
|||||||
|
|
||||||
> :information_source: **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
> :information_source: **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
||||||
|
|
||||||
The previous sections presume that you will be [§2.1: Setting up a Bitcoin-Core VPS by Hand](02_1_Setting_Up_a_Bitcoin-Core_VPS_by_Hand.md) or [§2.2: Setting Up a Bitcoin-Core VPS with StackScript](02_2_Setting_Up_a_Bitcoin-Core_VPS_with_StackScript.md) (as a Linode). However, you can actually create a Bitcoin-Core instance via any methodology of your choice and still follow along with the later steps of this tutorial.
|
The previous sections presume that you will be creating a full node on a VPS, either by [§2.1: Setting up a Bitcoin-Core VPS by Hand](02_1_Setting_Up_a_Bitcoin-Core_VPS_by_Hand.md) or [§2.2: Setting Up a Bitcoin-Core VPS with Bitcoin Standup](02_2_Setting_Up_a_Bitcoin-Core_VPS_with_StackScript.md) (as a Linode). However, you can actually create a Bitcoin-Core instance via any methodology of your choice and still follow along with the later steps of this tutorial.
|
||||||
|
|
||||||
Following are other setup methodologies that we are aware of:
|
Following are other setup methodologies that we are aware of:
|
||||||
|
|
||||||
* *[Bitcoin Standup](https://github.com/BlockchainCommons/Bitcoin-Standup).* A completely mechanical setup from Blockchain Commons that will not only install Bitcoin-Core, but will also install other programs such as Tor. It is available as a [Mac App](https://github.com/BlockchainCommons/Bitcoin-StandUp-MacOS) or as [Linux Scripts](https://github.com/BlockchainCommons/Bitcoin-StandUp-MacOS) (based on our own setup in §2.2, but available for any flavor of Debian).
|
* *[Bitcoin Standup](https://github.com/BlockchainCommons/Bitcoin-Standup).* The Blockchain Commons methodology used in §2.2 can alternatively be used as a [Mac App](https://github.com/BlockchainCommons/Bitcoin-StandUp-MacOS) or as [Linux Scripts](https://github.com/BlockchainCommons/Bitcoin-StandUp-MacOS) for any Debian setup.
|
||||||
* *[Setting Up a Bitcoin Node on AWS](https://wolfmcnally.com/115/developer-notes-setting-up-a-bitcoin-node-on-aws/).* A step-by-step tutorial by @wolfmcnally on setting on Bitcoin-Core with Amazon Web Services (aws).
|
* *[Setting Up a Bitcoin Node on AWS](https://wolfmcnally.com/115/developer-notes-setting-up-a-bitcoin-node-on-aws/).* A step-by-step tutorial exists from @wolfmcnally on setting on Bitcoin-Core with Amazon Web Services (AWN).
|
||||||
|
|
||||||
Note that if you use one of these alternative setups, you will need to create the special Bitcoin aliases, as explained in [§3.1: Verifying Your Bitcoin Setup](03_1_Verifying_Your_Bitcoin_Setup.md).
|
|
||||||
|
|
||||||
## What's Next?
|
## What's Next?
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
You're now ready to begin working with the `bitcoin-cli` command-line interface. But that first requires that you understand your Bitcoin setup and its wallet features, which is what will be explained in this chapter.
|
You're now ready to begin working with the `bitcoin-cli` command-line interface. But that first requires that you understand your Bitcoin setup and its wallet features, which is what will be explained in this chapter.
|
||||||
|
|
||||||
For this and future chapters, we presume that you have a VPS with Bitcoin installed, running `bitcoind`. We also presume that you are connected to testnet, allowing for access to bitcoins without using real funds. You can either do this by hand, per [§2.1: Setting up a Bitcoin-Core VPS by Hand](02_1_Setting_Up_a_Bitcoin-Core_VPS_by_Hand.md) or with a Linode StackScript at Linode.com, per [§2.2: Setting up a Bitcoin-Core VPS with StackScript](02_2_Setting_Up_a_Bitcoin-Core_VPS_with_StackScript.md).
|
For this and future chapters, we presume that you have a VPS with Bitcoin installed, running `bitcoind`. We also presume that you are connected to testnet, allowing for access to bitcoins without using real funds. You can either do this by hand, per [§2.1: Setting up a Bitcoin-Core VPS by Hand](02_1_Setting_Up_a_Bitcoin-Core_VPS_by_Hand.md), with Bitcoin Standup at Linode.com, per [§2.2: Setting up a Bitcoin-Core VPS with Bitcoin Standup](02_2_Setting_Up_a_Bitcoin-Core_VPS_with_StackScript.md), or via other means, per [§2.3: Setting up a Bitcoin-Core Machine via Other Means](02_3_Setting_Up_Bitcoin_Core_Other.md).
|
||||||
|
|
||||||
## Objectives for This Chapter
|
## Objectives for This Chapter
|
||||||
|
|
||||||
@ -11,6 +11,7 @@ After working through this chapter, a developer will be able to:
|
|||||||
* Demonstrate that Their Bitcoin Node is Installed and Up-to-date
|
* Demonstrate that Their Bitcoin Node is Installed and Up-to-date
|
||||||
* Create an Address to Receive Bitcoin Funds
|
* Create an Address to Receive Bitcoin Funds
|
||||||
* Use Basic Wallet Commands
|
* Use Basic Wallet Commands
|
||||||
|
* Create an Address from a Descriptor
|
||||||
|
|
||||||
Supporting objectives include the ability to:
|
Supporting objectives include the ability to:
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ Supporting objectives include the ability to:
|
|||||||
* Use Basic Informational Commands
|
* Use Basic Informational Commands
|
||||||
* Understand What a Bitcoin Address Is
|
* Understand What a Bitcoin Address Is
|
||||||
* Understand What a Wallet Is
|
* Understand What a Wallet Is
|
||||||
|
* Understand How to Import Addresses
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
@ -26,4 +28,4 @@ Supporting objectives include the ability to:
|
|||||||
* [Section Three: Setting Up Your Wallet](03_3_Setting_Up_Your_Wallet.md)
|
* [Section Three: Setting Up Your Wallet](03_3_Setting_Up_Your_Wallet.md)
|
||||||
* [Interlude: Using Command-Line Variables](03_3__Interlude_Using_Command-Line_Variables.md)
|
* [Interlude: Using Command-Line Variables](03_3__Interlude_Using_Command-Line_Variables.md)
|
||||||
* [Section Four: Receiving a Transaction](03_4_Receiving_a_Transaction.md)
|
* [Section Four: Receiving a Transaction](03_4_Receiving_a_Transaction.md)
|
||||||
|
* [Section Five: Understanding the Descriptor](03_5_Understanding_the_Descriptor.md)
|
||||||
|
@ -4,58 +4,54 @@
|
|||||||
|
|
||||||
Before you start playing with Bitcoin, you should ensure that everything is setup correctly.
|
Before you start playing with Bitcoin, you should ensure that everything is setup correctly.
|
||||||
|
|
||||||
## Check Your Aliases
|
## Create Your Aliases
|
||||||
|
|
||||||
The Bitcoin setup docs suggest creating a set of aliases. In case you didn't run through those docs, you can create them for your main Bitcoin user with the following command:
|
We suggest creating some aliases to make it easier to use Bitcoin.
|
||||||
|
|
||||||
|
You can do so by putting them in your `.bash_profile`.
|
||||||
```
|
```
|
||||||
cat >> ~/.bash_profile <<EOF
|
cat >> ~/.bash_profile <<EOF
|
||||||
alias btcdir="cd ~/.bitcoin/" #linux default bitcoind path
|
alias btcdir="cd ~/.bitcoin/" #linux default bitcoind path
|
||||||
alias bc="bitcoin-cli"
|
alias bc="bitcoin-cli"
|
||||||
alias bd="bitcoind"
|
alias bd="bitcoind"
|
||||||
alias btcinfo='bitcoin-cli getwalletinfo | egrep "\"balance\""; bitcoin-cli getnetworkinfo | egrep "\"version\"|connections"; bitcoin-cli getmininginfo | egrep "\"blocks\"|errors"'
|
alias btcinfo='bitcoin-cli getwalletinfo | egrep "\"balance\""; bitcoin-cli getnetworkinfo | egrep "\"version\"|connections"; bitcoin-cli getmininginfo | egrep "\"blocks\"|errors"'
|
||||||
alias btcblock="echo \\\`bitcoin-cli getblockcount 2>&1\\\`/\\\`wget -O - http://blockexplorer.com/testnet/q/getblockcount 2> /dev/null | cut -d : -f2 | rev | cut -c 2- | rev\\\`"
|
|
||||||
EOF
|
EOF
|
||||||
```
|
```
|
||||||
|
After you enter these aliases you can either `source .bash_profile` to input them or just log out and back in.
|
||||||
> :warning: **WARNING:** The btcblock alias will not work correctly if you try to place it in your .bash_profile by hand, rather than using the "cat" command as suggested. If you want to enter it by hand, you need to adjust the number of backslashes (usually from three each to one each), so make sure you know what you're doing if you aren't entering the commands exactly as suggested.
|
|
||||||
|
|
||||||
Note that these aliases includes shortcuts for running `bitcoin-cli`, for running `bitcoind`, and for going to the Bitcoin directory. These aliases are mainly meant to make your life easier. We suggest you create other aliases to ease your use of frequent commands (and arguments) and to minimize errors. Aliases of this sort can be even more useful if you have a complex setup where you regularly run commands associated with Mainnet, with Testnet, _and_ with Regtest, as explained further below.
|
Note that these aliases includes shortcuts for running `bitcoin-cli`, for running `bitcoind`, and for going to the Bitcoin directory. These aliases are mainly meant to make your life easier. We suggest you create other aliases to ease your use of frequent commands (and arguments) and to minimize errors. Aliases of this sort can be even more useful if you have a complex setup where you regularly run commands associated with Mainnet, with Testnet, _and_ with Regtest, as explained further below.
|
||||||
|
|
||||||
With that said, use of these aliases in _this_ document might accidentally obscure the core lessons being taught about Bitcoin, so the only aliases directly used here are `btcinfo` and `btcblock`, because they encapsulate much longer and more complex commands. Otherwise, we show the full commands; adjust for your own use as appropriate.
|
With that said, use of these aliases in _this_ document might accidentally obscure the core lessons being taught about Bitcoin, so the only alias directly used here is `btcinfo` because it encapsulatea much longer and more complex command. Otherwise, we show the full commands; adjust for your own use as appropriate.
|
||||||
|
|
||||||
> :link: **TESTNET vs MAINNET:** Remember that this tutorial generally assumes that you are using testnet. The `btcblock` alias needs to be slightly different on mainnet, where you can use the simpler "wget -O - http://blockchain.info/q/getblockcount 2>/dev/null".
|
|
||||||
|
|
||||||
## Run Bitcoind
|
## Run Bitcoind
|
||||||
|
|
||||||
You'll begin your exploration of the Bitcoin network with the `bitcoin-cli` command. However, bitcoind _must_ be running to use bitcoin-cli, as bitcoin-cli sends JSON-RPC commands to the bitcoind. If you used our standard setup, bitcoind should already be up and running. You can double check by looking at the process table.
|
You'll begin your exploration of the Bitcoin network with the `bitcoin-cli` command. However, bitcoind _must_ be running to use bitcoin-cli, as bitcoin-cli sends JSON-RPC commands to the bitcoind. If you used our standard setup, bitcoind should already be up and running. You can double check by looking at the process table.
|
||||||
```
|
```
|
||||||
$ ps auxww | grep bitcoind
|
$ ps auxww | grep bitcoind
|
||||||
user1 29360 11.5 39.6 2676812 1601416 ? SLsl Feb23 163:42 /usr/local/bin/bitcoind -daemon
|
standup 455 1.3 34.4 3387536 1392904 ? SLsl Jun16 59:30 /usr/local/bin/bitcoind -conf=/home/standup/.bitcoin/bitcoin.conf
|
||||||
```
|
```
|
||||||
If it's not running, you'll want to run "/usr/local/bin/bitcoind -daemon" by hand and also place it in your crontab, as explained in [§2.1: Setting up a Bitcoin-Core VPS by Hand](02_1_Setting_Up_a_Bitcoin-Core_VPS_by_Hand.md).
|
If it's not running, you'll want to run `/usr/local/bin/bitcoind -daemon` by hand and also place it in your crontab, as explained in [§2.1: Setting up a Bitcoin-Core VPS by Hand](02_1_Setting_Up_a_Bitcoin-Core_VPS_by_Hand.md).
|
||||||
|
|
||||||
## Verify Your Blocks
|
## Verify Your Blocks
|
||||||
|
|
||||||
You should have the whole blockchain downloaded before you start playing. Just run the `btcblock` alias to see if it's all loaded. You'll see two numbers, which tell you how many blocks have loaded out of how many total.
|
You should have the whole blockchain downloaded before you start playing. Just run the `bitcoin-cli getblockcount` alias to see if it's all loaded.
|
||||||
|
```
|
||||||
|
$ bitcoin-cli getblockcount
|
||||||
|
1772384
|
||||||
|
```
|
||||||
|
That tells you what's loaded; you'll then need to check that against an online service that tells you the current block height.
|
||||||
|
|
||||||
If the two numbers aren't the same, you should wait:
|
> :book: ***What is Block Height?*** Block height is the the distance that a particular block is removed from the genesis block. The current block height is the block height of the newest block added to a blockchain.
|
||||||
```
|
|
||||||
$ btcblock
|
|
||||||
973212/1090099
|
|
||||||
```
|
|
||||||
Total download time can take from an hour to several hours, depending on your setup.
|
|
||||||
|
|
||||||
If the two numbers are the same, you're fully loaded:
|
You can do this by looking at a blocknet explorer, such as [the Blockcypher Testnet explorer](https://live.blockcypher.com/btc-testnet/). Does its most recent number match your `getblockcount`? If so, you're up to date.
|
||||||
```
|
|
||||||
$ btcblock
|
> :link: **TESTNET vs MAINNET:** Remember that this tutorial generally assumes that you are using testnet. If you're using the mainnet instead, you can retrieve the current block height with: `wget -O - http://blockchain.info/q/getblockcount 2>/dev/null`. Unfortunately, functionality like this for testnet has disappeared over the years.
|
||||||
1090099/1090099
|
|
||||||
```
|
If you're not up-to-date, but your `getblockcount` is increasing, no problem. Total download time can take from an hour to several hours, depending on your setup.
|
||||||
And that means you're ready to go!
|
|
||||||
|
|
||||||
## Optional: Know Your Server Types
|
## Optional: Know Your Server Types
|
||||||
|
|
||||||
> **TESTNET vs MAINNET:** When you set up your node, you choose to create it as either a Mainnet, Testnet, or Regtest node. Though this document presumes a testnet setup, it's worth understanding how you might access and use the other setup types — even all on the same machine! But, if you're a first time user, skip on past this, as it's not necessary for a basic setup.
|
> **TESTNET vs MAINNET:** When you set up your node, you choose to create it as either a Mainnet, Testnet, or Regtest node. Though this document presumes a testnet setup, it's worth understanding how you might access and use the other setup types — even all on the same machine! But, if you're a first-time user, skip on past this, as it's not necessary for a basic setup.
|
||||||
|
|
||||||
The type of setup is mainly controlled through the ~/.bitcoin/bitcoin.conf file. If you're running testnet, it probably contains this line:
|
The type of setup is mainly controlled through the ~/.bitcoin/bitcoin.conf file. If you're running testnet, it probably contains this line:
|
||||||
```
|
```
|
||||||
@ -69,13 +65,14 @@ However, if you want to run several different sorts of nodes simultaneously, you
|
|||||||
|
|
||||||
Here's a set of aliases that would make that easier by creating a specific alias for starting and stopping the bitcoind, for going to the bitcoin directory, and for running bitcoin-cli, for each of the mainnet (which has no extra flags), the testnet (which is -testnet), or your regtest (which is -regtest).
|
Here's a set of aliases that would make that easier by creating a specific alias for starting and stopping the bitcoind, for going to the bitcoin directory, and for running bitcoin-cli, for each of the mainnet (which has no extra flags), the testnet (which is -testnet), or your regtest (which is -regtest).
|
||||||
```
|
```
|
||||||
|
cat >> ~/.bash_profile <<EOF
|
||||||
alias bcstart="bitcoind -daemon"
|
alias bcstart="bitcoind -daemon"
|
||||||
alias btstart="bitcoind -testnet -daemon"
|
alias btstart="bitcoind -testnet -daemon"
|
||||||
alias brstart="bitcoind -regtest -daemon"
|
alias brstart="bitcoind -regtest -daemon"
|
||||||
|
|
||||||
alias bcstop="bitcoin-cli stop"
|
alias bcstop="bitcoin-cli stop"
|
||||||
alias btstop="bitcoin-cli -testnet stop"
|
alias btstop="bitcoin-cli -testnet stop"
|
||||||
alias brstop="bitcoin-cli -regtest -stop"
|
alias brstop="bitcoin-cli -regtest stop"
|
||||||
|
|
||||||
alias bcdir="cd ~/.bitcoin/" #linux default bitcoin path
|
alias bcdir="cd ~/.bitcoin/" #linux default bitcoin path
|
||||||
alias btdir="cd ~/.bitcoin/testnet" #linux default bitcoin testnet path
|
alias btdir="cd ~/.bitcoin/testnet" #linux default bitcoin testnet path
|
||||||
@ -84,6 +81,7 @@ alias brdir="cd ~/.bitcoin/regtest" #linux default bitcoin regtest path
|
|||||||
alias bc="bitcoin-cli"
|
alias bc="bitcoin-cli"
|
||||||
alias bt="bitcoin-cli -testnet"
|
alias bt="bitcoin-cli -testnet"
|
||||||
alias br="bitcoin-cli -regtest"
|
alias br="bitcoin-cli -regtest"
|
||||||
|
EOF
|
||||||
```
|
```
|
||||||
For even more complexity, you could have each of your 'start' aliases use the -conf flag to load configuration from a different file. This goes far beyond the scope of this tutorial, but we offer it as a starting point for when your explorations of Bitcoin reaches the next level.
|
For even more complexity, you could have each of your 'start' aliases use the -conf flag to load configuration from a different file. This goes far beyond the scope of this tutorial, but we offer it as a starting point for when your explorations of Bitcoin reaches the next level.
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ Before you start playing with Bitcoin, you may always want to come to a better u
|
|||||||
|
|
||||||
## Know Your Bitcoin Directory
|
## Know Your Bitcoin Directory
|
||||||
|
|
||||||
To start with, you should understand where everything is kept: the ~/.bitcoin directory.
|
To start with, you should understand where everything is kept: the `~/.bitcoin` directory.
|
||||||
|
|
||||||
The main directory just contains your config file and the testnet directory:
|
The main directory just contains your config file and the testnet directory:
|
||||||
```
|
```
|
||||||
@ -18,12 +18,12 @@ The setup guides in [Chapter Two: Creating a Bitcoin-Core VPS](02_0_Setting_Up_a
|
|||||||
Moving back to your ~/.bitcoin directory, you'll find that the testnet3 directory contains all of the guts:
|
Moving back to your ~/.bitcoin directory, you'll find that the testnet3 directory contains all of the guts:
|
||||||
```
|
```
|
||||||
$ ls ~/.bitcoin/testnet3
|
$ ls ~/.bitcoin/testnet3
|
||||||
banlist.dat blocks database debug.log wallet.dat
|
banlist.dat blocks debug.log mempool.dat peers.dat
|
||||||
bitcoind.pid chainstate db.log peers.dat
|
bitcoind.pid chainstate fee_estimates.dat onion_private_key wallets
|
||||||
```
|
```
|
||||||
You shouldn't mess with most of these files and directories — particularly not the blocks and chainstate directories, which contain all of the blockchain data, and the wallet.dat file, which contains your personal wallet. However, do take careful note of the db.log and debug.log file, which you should refer to if you ever have problems with your setup.
|
You shouldn't mess with most of these files and directories — particularly not the `blocks` and `chainstate` directories, which contain all of the blockchain data, and the information in your `wallets` directory, which contains your personal wallet. However, do take careful note of the `debug.log` file, which you should refer to if you ever have problems with your setup.
|
||||||
|
|
||||||
> :link: **TESTNET vs MAINNET:** If you're using mainnet, then _everything_ will instead be placed in the main ~/.bitcoin directory. These various setups _do_ elegantly stack, so if you are using mainnet, testnet, and regtest, you'll find that ~/.bitcoin contains your config file and your mainnet data, ~/.bitcoin/testnet3 contains your testnet data, and ~/.bitcoin/regtest contains your regtest data.
|
> :link: **TESTNET vs MAINNET:** If you're using mainnet, then _everything_ will instead be placed in the main `~/.bitcoin` directory. These various setups _do_ elegantly stack, so if you are using mainnet, testnet, and regtest, you'll find that `~/.bitcoin` contains your config file and your mainnet data, the `~/.bitcoin/testnet3` directory contains your testnet data, and the `~/.bitcoin/regtest` directory contains your regtest data.
|
||||||
|
|
||||||
## Know Your Bitcoin-cli Commands
|
## Know Your Bitcoin-cli Commands
|
||||||
|
|
||||||
@ -35,70 +35,87 @@ getbestblockhash
|
|||||||
getblock "blockhash" ( verbosity )
|
getblock "blockhash" ( verbosity )
|
||||||
getblockchaininfo
|
getblockchaininfo
|
||||||
getblockcount
|
getblockcount
|
||||||
|
getblockfilter "blockhash" ( "filtertype" )
|
||||||
getblockhash height
|
getblockhash height
|
||||||
getblockheader "hash" ( verbose )
|
getblockheader "blockhash" ( verbose )
|
||||||
|
getblockstats hash_or_height ( stats )
|
||||||
getchaintips
|
getchaintips
|
||||||
getchaintxstats ( nblocks blockhash )
|
getchaintxstats ( nblocks "blockhash" )
|
||||||
getdifficulty
|
getdifficulty
|
||||||
getmempoolancestors txid (verbose)
|
getmempoolancestors "txid" ( verbose )
|
||||||
getmempooldescendants txid (verbose)
|
getmempooldescendants "txid" ( verbose )
|
||||||
getmempoolentry txid
|
getmempoolentry "txid"
|
||||||
getmempoolinfo
|
getmempoolinfo
|
||||||
getrawmempool ( verbose )
|
getrawmempool ( verbose )
|
||||||
gettxout "txid" n ( include_mempool )
|
gettxout "txid" n ( include_mempool )
|
||||||
gettxoutproof ["txid",...] ( blockhash )
|
gettxoutproof ["txid",...] ( "blockhash" )
|
||||||
gettxoutsetinfo
|
gettxoutsetinfo
|
||||||
preciousblock "blockhash"
|
preciousblock "blockhash"
|
||||||
pruneblockchain
|
pruneblockchain height
|
||||||
|
savemempool
|
||||||
|
scantxoutset "action" ( [scanobjects,...] )
|
||||||
verifychain ( checklevel nblocks )
|
verifychain ( checklevel nblocks )
|
||||||
verifytxoutproof "proof"
|
verifytxoutproof "proof"
|
||||||
|
|
||||||
== Control ==
|
== Control ==
|
||||||
getmemoryinfo ("mode")
|
getmemoryinfo ( "mode" )
|
||||||
getnetworkinginfo
|
getrpcinfo
|
||||||
help ( "command" )
|
help ( "command" )
|
||||||
|
logging ( ["include_category",...] ["exclude_category",...] )
|
||||||
stop
|
stop
|
||||||
uptime
|
uptime
|
||||||
|
|
||||||
== Generating ==
|
== Generating ==
|
||||||
generate nblocks ( maxtries )
|
generatetoaddress nblocks "address" ( maxtries )
|
||||||
generatetoaddress nblocks address (maxtries)
|
generatetodescriptor num_blocks "descriptor" ( maxtries )
|
||||||
|
|
||||||
== Mining ==
|
== Mining ==
|
||||||
getblocktemplate ( TemplateRequest )
|
getblocktemplate ( "template_request" )
|
||||||
getmininginfo
|
getmininginfo
|
||||||
getnetworkhashps ( nblocks height )
|
getnetworkhashps ( nblocks height )
|
||||||
prioritisetransaction <txid> <dummy value> <fee delta>
|
prioritisetransaction "txid" ( dummy ) fee_delta
|
||||||
submitblock "hexdata" ( "dummy" )
|
submitblock "hexdata" ( "dummy" )
|
||||||
|
submitheader "hexdata"
|
||||||
|
|
||||||
== Network ==
|
== Network ==
|
||||||
addnode "node" "add|remove|onetry"
|
addnode "node" "command"
|
||||||
clearbanned
|
clearbanned
|
||||||
disconnectnode "[address]" [nodeid]
|
disconnectnode ( "address" nodeid )
|
||||||
getaddednodeinfo ( "node" )
|
getaddednodeinfo ( "node" )
|
||||||
getconnectioncount
|
getconnectioncount
|
||||||
getnettotals
|
getnettotals
|
||||||
getnetworkinfo
|
getnetworkinfo
|
||||||
|
getnodeaddresses ( count )
|
||||||
getpeerinfo
|
getpeerinfo
|
||||||
listbanned
|
listbanned
|
||||||
ping
|
ping
|
||||||
setban "subnet" "add|remove" (bantime) (absolute)
|
setban "subnet" "command" ( bantime absolute )
|
||||||
setnetworkactive true|false
|
setnetworkactive state
|
||||||
|
|
||||||
== Rawtransactions ==
|
== Rawtransactions ==
|
||||||
|
analyzepsbt "psbt"
|
||||||
|
combinepsbt ["psbt",...]
|
||||||
combinerawtransaction ["hexstring",...]
|
combinerawtransaction ["hexstring",...]
|
||||||
createrawtransaction [{"txid":"id","vout":n},...] {"address":amount,"data":"hex",...} ( locktime ) ( replaceable )
|
converttopsbt "hexstring" ( permitsigdata iswitness )
|
||||||
decoderawtransaction "hexstring"
|
createpsbt [{"txid":"hex","vout":n,"sequence":n},...] [{"address":amount},{"data":"hex"},...] ( locktime replaceable )
|
||||||
|
createrawtransaction [{"txid":"hex","vout":n,"sequence":n},...] [{"address":amount},{"data":"hex"},...] ( locktime replaceable )
|
||||||
|
decodepsbt "psbt"
|
||||||
|
decoderawtransaction "hexstring" ( iswitness )
|
||||||
decodescript "hexstring"
|
decodescript "hexstring"
|
||||||
fundrawtransaction "hexstring" ( options )
|
finalizepsbt "psbt" ( extract )
|
||||||
getrawtransaction "txid" ( verbose )
|
fundrawtransaction "hexstring" ( options iswitness )
|
||||||
sendrawtransaction "hexstring" ( allowhighfees )
|
getrawtransaction "txid" ( verbose "blockhash" )
|
||||||
signrawtransaction "hexstring" ( [{"txid":"id","vout":n,"scriptPubKey":"hex","redeemScript":"hex"},...] ["privatekey1",...] sighashtype )
|
joinpsbts ["psbt",...]
|
||||||
|
sendrawtransaction "hexstring" ( maxfeerate )
|
||||||
|
signrawtransactionwithkey "hexstring" ["privatekey",...] ( [{"txid":"hex","vout":n,"scriptPubKey":"hex","redeemScript":"hex","witnessScript":"hex","amount":amount},...] "sighashtype" )
|
||||||
|
testmempoolaccept ["rawtx",...] ( maxfeerate )
|
||||||
|
utxoupdatepsbt "psbt" ( ["",{"desc":"str","range":n or [n,n]},...] )
|
||||||
|
|
||||||
== Util ==
|
== Util ==
|
||||||
createmultisig nrequired ["key",...]
|
createmultisig nrequired ["key",...] ( "address_type" )
|
||||||
estimatefee nblocks
|
deriveaddresses "descriptor" ( range )
|
||||||
estimatesmartfee conf_target ("estimate_mode")
|
estimatesmartfee conf_target ( "estimate_mode" )
|
||||||
|
getdescriptorinfo "descriptor"
|
||||||
signmessagewithprivkey "privkey" "message"
|
signmessagewithprivkey "privkey" "message"
|
||||||
validateaddress "address"
|
validateaddress "address"
|
||||||
verifymessage "address" "signature" "message"
|
verifymessage "address" "signature" "message"
|
||||||
@ -106,75 +123,85 @@ verifymessage "address" "signature" "message"
|
|||||||
== Wallet ==
|
== Wallet ==
|
||||||
abandontransaction "txid"
|
abandontransaction "txid"
|
||||||
abortrescan
|
abortrescan
|
||||||
addmultisigaddress nrequired ["key",...] ( "account" )
|
addmultisigaddress nrequired ["key",...] ( "label" "address_type" )
|
||||||
addwitnessaddress "address"
|
|
||||||
backupwallet "destination"
|
backupwallet "destination"
|
||||||
bumpfee "txid" ( options )
|
bumpfee "txid" ( options )
|
||||||
|
createwallet "wallet_name" ( disable_private_keys blank "passphrase" avoid_reuse )
|
||||||
dumpprivkey "address"
|
dumpprivkey "address"
|
||||||
dumpwallet "filename"
|
dumpwallet "filename"
|
||||||
encryptwallet "passphrase"
|
encryptwallet "passphrase"
|
||||||
getaccount "address"
|
getaddressesbylabel "label"
|
||||||
getaccountaddress "account"
|
getaddressinfo "address"
|
||||||
getaddressesbyaccount "account"
|
getbalance ( "dummy" minconf include_watchonly avoid_reuse )
|
||||||
getbalance ( "account" minconf include_watchonly )
|
getbalances
|
||||||
getnewaddress ( "account" )
|
getnewaddress ( "label" "address_type" )
|
||||||
getrawchangeaddress
|
getrawchangeaddress ( "address_type" )
|
||||||
getreceivedbyaccount "account" ( minconf )
|
|
||||||
getreceivedbyaddress "address" ( minconf )
|
getreceivedbyaddress "address" ( minconf )
|
||||||
gettransaction "txid" ( include_watchonly )
|
getreceivedbylabel "label" ( minconf )
|
||||||
|
gettransaction "txid" ( include_watchonly verbose )
|
||||||
getunconfirmedbalance
|
getunconfirmedbalance
|
||||||
getwalletinfo
|
getwalletinfo
|
||||||
importaddress "address" ( "label" rescan p2sh )
|
importaddress "address" ( "label" rescan p2sh )
|
||||||
importmulti "requests" ( "options" )
|
importmulti "requests" ( "options" )
|
||||||
importprivkey "privkey" ( "label" ) ( rescan )
|
importprivkey "privkey" ( "label" rescan )
|
||||||
importprunedfunds
|
importprunedfunds "rawtransaction" "txoutproof"
|
||||||
importpubkey "pubkey" ( "label" rescan )
|
importpubkey "pubkey" ( "label" rescan )
|
||||||
importwallet "filename"
|
importwallet "filename"
|
||||||
keypoolrefill ( newsize )
|
keypoolrefill ( newsize )
|
||||||
listaccounts ( minconf include_watchonly)
|
|
||||||
listaddressgroupings
|
listaddressgroupings
|
||||||
|
listlabels ( "purpose" )
|
||||||
listlockunspent
|
listlockunspent
|
||||||
listreceivedbyaccount ( minconf include_empty include_watchonly)
|
listreceivedbyaddress ( minconf include_empty include_watchonly "address_filter" )
|
||||||
listreceivedbyaddress ( minconf include_empty include_watchonly)
|
listreceivedbylabel ( minconf include_empty include_watchonly )
|
||||||
listsinceblock ( "blockhash" target_confirmations include_watchonly include_removed )
|
listsinceblock ( "blockhash" target_confirmations include_watchonly include_removed )
|
||||||
listtransactions ( "account" count skip include_watchonly)
|
listtransactions ( "label" count skip include_watchonly )
|
||||||
listunspent ( minconf maxconf ["addresses",...] [include_unsafe] [query_options])
|
listunspent ( minconf maxconf ["address",...] include_unsafe query_options )
|
||||||
|
listwalletdir
|
||||||
listwallets
|
listwallets
|
||||||
lockunspent unlock ([{"txid":"txid","vout":n},...])
|
loadwallet "filename"
|
||||||
move "fromaccount" "toaccount" amount ( minconf "comment" )
|
lockunspent unlock ( [{"txid":"hex","vout":n},...] )
|
||||||
removeprunedfunds "txid"
|
removeprunedfunds "txid"
|
||||||
sendfrom "fromaccount" "toaddress" amount ( minconf "comment" "comment_to" )
|
rescanblockchain ( start_height stop_height )
|
||||||
sendmany "fromaccount" {"address":amount,...} ( minconf "comment" ["address",...] replaceable conf_target "estimate_mode")
|
sendmany "" {"address":amount} ( minconf "comment" ["address",...] replaceable conf_target "estimate_mode" )
|
||||||
sendtoaddress "address" amount ( "comment" "comment_to" subtractfeefromamount replaceable conf_target "estimate_mode")
|
sendtoaddress "address" amount ( "comment" "comment_to" subtractfeefromamount replaceable conf_target "estimate_mode" avoid_reuse )
|
||||||
setaccount "address" "account"
|
sethdseed ( newkeypool "seed" )
|
||||||
|
setlabel "address" "label"
|
||||||
settxfee amount
|
settxfee amount
|
||||||
|
setwalletflag "flag" ( value )
|
||||||
signmessage "address" "message"
|
signmessage "address" "message"
|
||||||
|
signrawtransactionwithwallet "hexstring" ( [{"txid":"hex","vout":n,"scriptPubKey":"hex","redeemScript":"hex","witnessScript":"hex","amount":amount},...] "sighashtype" )
|
||||||
|
unloadwallet ( "wallet_name" )
|
||||||
|
walletcreatefundedpsbt [{"txid":"hex","vout":n,"sequence":n},...] [{"address":amount},{"data":"hex"},...] ( locktime options bip32derivs )
|
||||||
|
walletlock
|
||||||
|
walletpassphrase "passphrase" timeout
|
||||||
|
walletpassphrasechange "oldpassphrase" "newpassphrase"
|
||||||
|
walletprocesspsbt "psbt" ( sign "sighashtype" bip32derivs )
|
||||||
|
|
||||||
|
== Zmq ==
|
||||||
|
getzmqnotifications
|
||||||
```
|
```
|
||||||
You can also type `bitcoin-cli help [command]` to get even more extensive info on that command. For example:
|
You can also type `bitcoin-cli help [command]` to get even more extensive info on that command. For example:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli help getmininginfo
|
|
||||||
getmininginfo
|
getmininginfo
|
||||||
|
|
||||||
Returns a json object containing mining-related information.
|
Returns a json object containing mining-related information.
|
||||||
Result:
|
Result:
|
||||||
{
|
{ (json object)
|
||||||
"blocks": nnn, (numeric) The current block
|
"blocks" : n, (numeric) The current block
|
||||||
"currentblocksize": nnn, (numeric) The last block size
|
"currentblockweight" : n, (numeric, optional) The block weight of the last assembled block (only present if a block was ever assembled)
|
||||||
"currentblockweight": nnn, (numeric) The last block weight
|
"currentblocktx" : n, (numeric, optional) The number of block transactions of the last assembled block (only present if a block was ever assembled)
|
||||||
"currentblocktx": nnn, (numeric) The last block transaction
|
"difficulty" : n, (numeric) The current difficulty
|
||||||
"difficulty": xxx.xxxxx (numeric) The current difficulty
|
"networkhashps" : n, (numeric) The network hashes per second
|
||||||
"errors": "..." (string) Current errors
|
"pooledtx" : n, (numeric) The size of the mempool
|
||||||
"networkhashps": nnn, (numeric) The network hashes per second
|
"chain" : "str", (string) current network name (main, test, regtest)
|
||||||
"pooledtx": n (numeric) The size of the mempool
|
"warnings" : "str" (string) any network and blockchain warnings
|
||||||
"testnet": true|false (boolean) If using testnet or not
|
|
||||||
"chain": "xxxx", (string) current network name as defined in BIP70 (main, test, regtest)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
> bitcoin-cli getmininginfo
|
> bitcoin-cli getmininginfo
|
||||||
> curl --user myusername --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getmininginfo", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/
|
> curl --user myusername --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "getmininginfo", "params": []}' -H 'content-type: text/plain;' http://127.0.0.1:8332/
|
||||||
```
|
```
|
||||||
_What is RPC?_ `bitcoin-cli` is just a handy interface that lets you send commands to the `bitcoind`. More specifically, it's an interface that lets you send RPC (or Remote Procedure Protocol) commands to the `bitcoind`. Often, the `bitcoin-cli` command and the RPC command have identical names and interfaces, but some `bitcoin-cli` commands instead provide shortcuts for more complex RPC requests. Generally, the `bitcoin-cli` interface is much cleaner and simpler than trying to send RPC commands by hand, using `curl` or some other method. However, it also has limitations as to what you can ultimately do.
|
> :book: ***What is RPC?*** `bitcoin-cli` is just a handy interface that lets you send commands to the `bitcoind`. More specifically, it's an interface that lets you send RPC (or Remote Procedure Protocol) commands to the `bitcoind`. Often, the `bitcoin-cli` command and the RPC command have identical names and interfaces, but some `bitcoin-cli` commands instead provide shortcuts for more complex RPC requests. Generally, the `bitcoin-cli` interface is much cleaner and simpler than trying to send RPC commands by hand, using `curl` or some other method. However, it also has limitations as to what you can ultimately do.
|
||||||
|
|
||||||
## Optional: Know Your Bitcoin Info
|
## Optional: Know Your Bitcoin Info
|
||||||
|
|
||||||
@ -190,14 +217,18 @@ For example `bitcoin-cli getnetworkinfo` gives you a variety of information on y
|
|||||||
```
|
```
|
||||||
$ bitcoin-cli getnetworkinfo
|
$ bitcoin-cli getnetworkinfo
|
||||||
{
|
{
|
||||||
"version": 150100,
|
"version": 200000,
|
||||||
"subversion": "/Satoshi:0.15.1/",
|
"subversion": "/Satoshi:0.20.0/",
|
||||||
"protocolversion": 70015,
|
"protocolversion": 70015,
|
||||||
"localservices": "000000000000000d",
|
"localservices": "0000000000000408",
|
||||||
"localrelay": false,
|
"localservicesnames": [
|
||||||
|
"WITNESS",
|
||||||
|
"NETWORK_LIMITED"
|
||||||
|
],
|
||||||
|
"localrelay": true,
|
||||||
"timeoffset": 0,
|
"timeoffset": 0,
|
||||||
"networkactive": true,
|
"networkactive": true,
|
||||||
"connections": 9,
|
"connections": 10,
|
||||||
"networks": [
|
"networks": [
|
||||||
{
|
{
|
||||||
"name": "ipv4",
|
"name": "ipv4",
|
||||||
@ -215,28 +246,39 @@ $ bitcoin-cli getnetworkinfo
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "onion",
|
"name": "onion",
|
||||||
"limited": true,
|
"limited": false,
|
||||||
"reachable": false,
|
"reachable": true,
|
||||||
"proxy": "",
|
"proxy": "127.0.0.1:9050",
|
||||||
"proxy_randomize_credentials": false
|
"proxy_randomize_credentials": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"relayfee": 0.00001000,
|
"relayfee": 0.00001000,
|
||||||
"incrementalfee": 0.00001000,
|
"incrementalfee": 0.00001000,
|
||||||
"localaddresses": [
|
"localaddresses": [
|
||||||
{
|
{
|
||||||
"address": "192.168.1.17",
|
"address": "45.79.111.171",
|
||||||
"port": 18333,
|
"port": 18333,
|
||||||
"score": 52
|
"score": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "2600:3c01::f03c:92ff:fecc:fdb7",
|
||||||
|
"port": 18333,
|
||||||
|
"score": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "4wrr3ktm6gl4sojx.onion",
|
||||||
|
"port": 18333,
|
||||||
|
"score": 4
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"warnings": "Warning: unknown
|
"warnings": "Warning: unknown new rules activated (versionbit 28)"
|
||||||
|
}
|
||||||
```
|
```
|
||||||
Feel free to references any of these and to use "bitcoin-cli help" if you want more information on what any of them do.
|
Feel free to reference any of these and to use "bitcoin-cli help" if you want more information on what any of them do.
|
||||||
|
|
||||||
## Summary: Knowing Your Bitcoin Setup
|
## Summary: Knowing Your Bitcoin Setup
|
||||||
|
|
||||||
The ~/.bitcoin directory contains all of your files, while `bitcoin-cli help` and a variety of info commands can be used to get more information on how your setup and Bitcoin work.
|
The `~/.bitcoin` directory contains all of your files, while `bitcoin-cli help` and a variety of info commands can be used to get more information on how your setup and Bitcoin work.
|
||||||
|
|
||||||
## What's Next?
|
## What's Next?
|
||||||
|
|
||||||
|
@ -6,62 +6,93 @@ You're now ready to start working with Bitcoin. To begin with, you'll need to cr
|
|||||||
|
|
||||||
## Create an Address
|
## Create an Address
|
||||||
|
|
||||||
The first thing you need to do is create an address for receiving payments. This is done with the `bitcoin-cli getnewaddress` command. Remember that if you want more information on this command, you should type `bitcoin-cli help getnewaddress`. Bitcoin Core 0.16.0 gives p2sh-segwit as default address, this can be changed by `-addresstype`. Otherwise, you can specify the address type between "legacy", "p2sh-segwit", and "bech32".
|
The first thing you need to do is create an address for receiving payments. This is done with the `bitcoin-cli getnewaddress` command. Remember that if you want more information on this command, you should type `bitcoin-cli help getnewaddress`. Currently, there are three types of addresses: `legacy` and the two types of SegWit address, `p2sh-segwit` and `bech32`. If you do not otherwise specify, you'll get the default, which is currently `bech32`.
|
||||||
|
|
||||||
|
However, for the next few sections we're instead going to be using `legacy` addresses, both because `bitcoin-cli` had some teething problems with its early versions of SegWit addresses, and because other people might not be able to send to `bech32` addresses. This is all unlikely to be a problem for you now, but for the moment we want to get your started with transaction examples that are (mostly) guaranteed to work.
|
||||||
|
|
||||||
|
You can require `legacy` address either with the second argument to `getnewaddress` or with the named `addresstype` argument.
|
||||||
```
|
```
|
||||||
$ bitcoin-cli getnewaddress "" legacy
|
$ bitcoin-cli getnewaddress -addresstype legacy
|
||||||
n4cqjJE6fqcmeWpftygwPoKMMDva6BpyHf
|
moKVV6XEhfrBCE3QCYq6ppT7AaMF8KsZ1B
|
||||||
```
|
```
|
||||||
Note that this address begins with an "n" (or sometimes an "m", "2", or "tb1). This signifies that this is a testnet address.
|
Note that this address begins with an "m" (or sometimes an "n") to signify a testnet Legacy address. It would be a "2" for a P2SH address or a "tb1" for a Bech32 address.
|
||||||
|
|
||||||
The "legacy" flag is necessary to generate a traditional address, rather than a p2sh-segwit or bech32 address. The legacy address is currently required from the command line to make sure that signing works correctly.
|
> :link: **TESTNET vs MAINNET:** The equivalent mainnet address would start with a "1" (for Legacy), "3" (for P2SH), or "bc1" (for Bech320.
|
||||||
|
|
||||||
> :link: **TESTNET vs MAINNET:** The equivalent mainnet address would start with a "1", "3", or "bc1".
|
|
||||||
|
|
||||||
Take careful note of the address. You'll need to give it to whomever will be sending you funds.
|
Take careful note of the address. You'll need to give it to whomever will be sending you funds.
|
||||||
|
|
||||||
_What is a Bitcoin address?_ A Bitcoin address is literally where you receive money. It's like an email address, but for funds. However unlike an email address, a Bitcoin address should be considered single use: use it to receive funds just _once_. When you want to receive funds from someone else or at some other time, generate a new address. This is suggested in large part to improve your privacy. The whole blockchain is immutable, which means that explorers can look at long chains of transactions over time, making it possible to statistically determine who you and your contacts are, no matter how careful you are. However, if you keep reusing the same address, then this becomes even easier.
|
> :book: ***What is a Bitcoin address?*** A Bitcoin address is literally where you receive money. It's like an email address, but for funds. Technically, it's a public key, though different address schemes adjust that in different ways. However unlike an email address, a Bitcoin address should be considered single use: use it to receive funds just _once_. When you want to receive funds from someone else or at some other time, generate a new address. This is suggested in large part to improve your privacy. The whole blockchain is immutable, which means that explorers can look at long chains of transactions over time, making it possible to statistically determine who you and your contacts are, no matter how careful you are. However, if you keep reusing the same address, then this becomes even easier.
|
||||||
|
|
||||||
_What is a P2PKH address?_ A Bitcoin address is also something else: a public key (or more precisely, the 160-bit hash of a public key). For this reason it's called a Pay to PubKey Hash (or P2PKH) address. This public key of your key pair allows you to receive money, while an associated private key lets you spend that money. However, bitcoins may be sent to other sorts of addresses: Pay to Script Hash (P2SH) and Native Segwit (Bech32) addresses feature prominently in the latter part of this tutorial.
|
> :book: ***What is a Bitcoin wallet?*** By creating your first Bitcoin address, you've also begun to fill in your Bitcoin wallet. More precisely, you've begun to fill the `wallet.dat` file in your `~/.bitcoin/testnet3 /wallets`directory. The `wallet.dat` file contains data about preferences and transactions, but more importantly it contains all of the key pairs that you create: both the public key (which is the source of the address where you receive funds) and the private key (which is how you spend those funds). For the most part, you won't have to worry about that private key: `bitcoind` will use it when it's needed. However, this makes the `wallet.dat` file extremely important: if you lose it, you lose your private keys, and if you lose your private keys, you lose your funds!
|
||||||
|
|
||||||
_What is a Bitcoin wallet?_ By creating your first Bitcoin address, you've also begun to fill in your Bitcoin wallet. More precisely, you've begun to fill the `wallet.dat` file in your ~/.bitcoin/testnet3 directory. The `wallet.dat` file contains data about preferences and transactions, but more importantly it contains all of the key pairs that you create: both the public key (which is the source of the address where you receive funds) and the private key (which is how you spend those funds). For the most part, you won't have to worry about that private key: `bitcoind` will use it when it's needed. However, this makes the `wallet.dat` file extremely important: if you lose it, you lose your private keys, and if you lose your private keys, you lose your funds!
|
With a single address in hand, you could jump straight to the next section and begin receiving funds. However, before we get there, we're going to briefly discuss the other sorts of addresses that you'll meet in the future and talk about a few other wallet commands that you might want to use in the future.
|
||||||
|
|
||||||
With a single address in hand, you could jump straight to the next section and begin receiving funds. However, before we get there, we're going to talk about a few other wallet commands that you might want to use in the future.
|
### Knowing Your Bitcoin Addresses
|
||||||
|
|
||||||
|
There are three types of Bitcoin addresses that you can create with the `getnewaddress` RPC command. You'll be using a `legacy` (P2PKH) address here, while you'll move over to a SegWit (P2SH-SegWit) or Bech32 address in [§4.6: Creating a Segwit Transaction](04_6_Creating_a_Segwit_Transaction.md).
|
||||||
|
|
||||||
|
As noted above, the foundation of a Bitcoin address is a public key: someone sends funds to your public key, and then you use your private key to redeem it. Easy? Except putting your public key out there isn't entirely secure. At the moment, if someone has your public key, then they can't retrieve your private key (and thus your funds); that's the basis of cryptography, which uses a trap-door function to ensure that you can only go from private to public key, and not vice-versa. But the problem is that we don't know what the future might bring. Except we do know that cryptography systems eventually get broken by the relentless advance of technology, so it's better not to put raw public keys on the 'net, to future-proof your transactions.
|
||||||
|
|
||||||
|
Classic Bitcoin transactions created P2PKH addresses that added an additional cryptographic step to protect public keys.
|
||||||
|
|
||||||
|
> :book: ***What is a Legacy (P2PKH) address?*** This is a Legacy address of the sort used by the early Bitcoin network. We'll be using it in examples for the next few sections. It's called a Pay to PubKey Hash (or P2PKH) address because the address is a 160-bit hash of a public key. Using a hash of your public key as your address creates a two-step process where to spend funds you need to reveal both the private key and the public key, and it increases future security accordingly. This sort of address remains important for receiving funds from people with out-of-date wallet software.
|
||||||
|
|
||||||
|
As described more fully in [§4.6: Creating a Segwit Transaction](04_6_Creating_a_Segwit_Transaction.md), the Block Size Wars of the late '10s resulted in a new sort of address: SegWit. This is the preferred sort address currently, and should be fully integrated into Bitcoin-Core at this point, but nonetheless we're saving it for §4.6.
|
||||||
|
|
||||||
|
SegWit simply means "segregated witness" and it's a way of separating the transaction signatures out from the rest of the transaction to reduce transaction size. Some SegWit addresses with sneak into some of our examples prior to §4.6 as change addresses, which you'll see as addresses that begin with "tb". This is fine because the `bitcoin-cli` entirely supports their usage. But we won't use them otherwise
|
||||||
|
|
||||||
|
There are two addresses of this sort:
|
||||||
|
|
||||||
|
> :book: ***What is a P2SH-SegWit (aka Nested SegWit, aka P2WSH) address?*** This is the first generation of SegWit. It wraps the SegWit address in a Script hash to ensure backward compatibility. The result creates transactions that are about 25%+ smaller (with corresponding reductions in transaction fees).
|
||||||
|
|
||||||
|
> :book: ***What is a Bech32 (aka Native SegWit, aka P2WPKH) address?*** This is the second generation of SegWit. It's fully described in [BIP 173](https://en.bitcoin.it/wiki/BIP_0173). It creates transactions that are even smaller but more notably also has some advantages in creating addresses that are less prone to human error and have some implicit error-correction beyond that. It is *not* backward compatible like P2SH-SegWit was, and so some people may not be able to send to it.
|
||||||
|
|
||||||
|
There are other sorts of Bitcoin addresses, such as P2PK (which paid to a bare public key, and is deprecated because of its future insecurity) and P2SH (which pays to a Script Hash, and which is used by the first-generation Nested SegWit addresses; we'll meet it more fully in a few chapters).
|
||||||
|
|
||||||
## Optional: Sign a Message
|
## Optional: Sign a Message
|
||||||
|
|
||||||
Sometimes you'll need to prove that you control a Bitcoin address (or rather, that you control its private key). This is important because it lets people know that they're sending funds to the right person. This can be done by creating a signature with the `bitcoin-cli signmessage` command, in the form `bitcoin-cli signmessage [address] [message]`. For example:
|
Sometimes you'll need to prove that you control a Bitcoin address (or rather, that you control its private key). This is important because it lets people know that they're sending funds to the right person. This can be done by creating a signature with the `bitcoin-cli signmessage` command, in the form `bitcoin-cli signmessage [address] [message]`. For example:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli signmessage "n4cqjJE6fqcmeWpftygwPoKMMDva6BpyHf" "Hello, World"
|
$ bitcoin-cli signmessage "moKVV6XEhfrBCE3QCYq6ppT7AaMF8KsZ1B" "Hello, World"
|
||||||
H3yMBZaFeSmG2HgnH38dImzZAwAQADcOiMKTC1fryoV6Y93BelqzDMTCqNcFoik86E8qHa6o3FCmTsxWD7Wa5YY=
|
HyIP0nzdcH12aNbQ2s2rUxLwzG832HxiO1vt8S/jw+W4Ia29lw6hyyaqYOsliYdxne70C6SZ5Utma6QY/trHZBI=
|
||||||
```
|
```
|
||||||
You'll get the signature as a return.
|
You'll get the signature as a return.
|
||||||
|
|
||||||
_What is a signature?_ A digital signature is a combination of a message and a private key that can then be unlocked with a public key. Since there's a one-to-one correspendence between the elements of a keypair, unlocking with a public key proves that the signer controlled the corresponding private key.
|
> :book: ***What is a signature?*** A digital signature is a combination of a message and a private key that can then be unlocked with a public key. Since there's a one-to-one correspendence between the elements of a keypair, unlocking with a public key proves that the signer controlled the corresponding private key.
|
||||||
|
|
||||||
Another person can then use the `bitcoin-cli verifymessage` command to verify the signature. He inputs the address in question, the signature, and the message:
|
Another person can then use the `bitcoin-cli verifymessage` command to verify the signature. He inputs the address in question, the signature, and the message:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli verifymessage "n4cqjJE6fqcmeWpftygwPoKMMDva6BpyHf" "H3yMBZaFeSmG2HgnH38dImzZAwAQADcOiMKTC1fryoV6Y93BelqzDMTCqNcFoik86E8qHa6o3FCmTsxWD7Wa5YY=" "Hello, World"
|
$ bitcoin-cli verifymessage "moKVV6XEhfrBCE3QCYq6ppT7AaMF8KsZ1B" "HyIP0nzdcH12aNbQ2s2rUxLwzG832HxiO1vt8S/jw+W4Ia29lw6hyyaqYOsliYdxne70C6SZ5Utma6QY/trHZBI=" "Hello, World"
|
||||||
true
|
true
|
||||||
```
|
```
|
||||||
If they all match up, then the other person knows that he can safely transfer funds to the person who signed the message by sending to the address.
|
If they all match up, then the other person knows that he can safely transfer funds to the person who signed the message by sending to the address.
|
||||||
|
|
||||||
If some black hat was making up signatures, this would instead produce a negative result:
|
If some black hat was making up signatures, this would instead produce a negative result:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli verifymessage "n4cqjJE6fqcmeWpftygwPoKMMDva6BpyHf" "FAKEBZaFeSmG2HgnH38dImzZAwAQADcOiMKTC1fryoV6Y93BelqzDMTCqNcFoik86E8qHa6o3FCmTsxWD7Wa5YY=" "Hello, World"
|
$ bitcoin-cli verifymessage "FAKEV6XEhfrBCE3QCYq6ppT7AaMF8KsZ1B" "HyIP0nzdcH12aNbQ2s2rUxLwzG832HxiO1vt8S/jw+W4Ia29lw6hyyaqYOsliYdxne70C6SZ5Utma6QY/trHZBI=" "Hello, World"
|
||||||
false
|
error code: -3
|
||||||
|
error message:
|
||||||
|
Invalid address
|
||||||
```
|
```
|
||||||
|
|
||||||
## Optional: Dump Your Wallet
|
## Optional: Dump Your Wallet
|
||||||
|
|
||||||
It might seem dangerous having all of your irreplaceable private keys in a single file. That's what `bitcoin-cli backupwallet` is for. It lets you make a copy of your wallet.dat:
|
It might seem dangerous having all of your irreplaceable private keys in a single file. That's what `bitcoin-cli backupwallet` is for. It lets you make a copy of your wallet.dat:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli backupwallet backup.dat
|
$ bitcoin-cli backupwallet ~/backup.dat
|
||||||
```
|
```
|
||||||
You can then recover it with `bitcoin-cli importwallet`.
|
You can then recover it with `bitcoin-cli importwallet`.
|
||||||
```
|
```
|
||||||
$ bitcoin-cli importwallet backup.dat
|
$ bitcoin-cli importwallet backup.dat
|
||||||
```
|
```
|
||||||
|
But note this requires an unpruned node!
|
||||||
|
```
|
||||||
|
$ bitcoin-cli importwallet ~/backup.dat
|
||||||
|
error code: -4
|
||||||
|
error message:
|
||||||
|
Importing wallets is disabled when blocks are pruned
|
||||||
|
```
|
||||||
|
|
||||||
## Optional: View Your Private Keys
|
## Optional: View Your Private Keys
|
||||||
|
|
||||||
@ -71,15 +102,15 @@ To look at _all_ the keys in your wallet, type `bitcoin-cli dumpwallet ~/mywalle
|
|||||||
```
|
```
|
||||||
$ bitcoin-cli dumpwallet ~/mywallet.txt
|
$ bitcoin-cli dumpwallet ~/mywallet.txt
|
||||||
{
|
{
|
||||||
"filename": "/home/user1/mywallet.txt"
|
"filename": "/home/standup/mywallet.txt"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
This will create a mywallet.txt file in your home directory with a long list of private keys, addresses, and other information. Mind you, you'd never want to put this data out in a plain text file on a Bitcoin setup with real funds!
|
This will create a `mywallet.txt` file in your home directory with a long list of private keys, addresses, and other information. Mind you, you'd never want to put this data out in a plain text file on a Bitcoin setup with real funds!
|
||||||
|
|
||||||
More likely, you just want to look at the private key associated with a specific address. This can be done with the `bitcoin-cli dumpprivkey` command.
|
More likely, you just want to look at the private key associated with a specific address. This can be done with the `bitcoin-cli dumpprivkey` command.
|
||||||
```
|
```
|
||||||
$ bitcoin-cli dumpprivkey "n4cqjJE6fqcmeWpftygwPoKMMDva6BpyHf"
|
$ bitcoin-cli dumpprivkey "moKVV6XEhfrBCE3QCYq6ppT7AaMF8KsZ1B"
|
||||||
cW4s4MdW7BkUmqiKgYzSJdmvnzq8QDrf6gszPMC7eLmfcdoRHtHh
|
cTv75T4B3NsG92tdSxSfzhuaGrzrmc1rJjLKscoQZXqNRs5tpYhH
|
||||||
```
|
```
|
||||||
You can then save that key somewhere safe, preferably somewhere not connected to the internet.
|
You can then save that key somewhere safe, preferably somewhere not connected to the internet.
|
||||||
|
|
||||||
@ -87,7 +118,7 @@ You can import any private key, from a wallet dump or an individual key dump, as
|
|||||||
```
|
```
|
||||||
$ bitcoin-cli importprivkey cW4s4MdW7BkUmqiKgYzSJdmvnzq8QDrf6gszPMC7eLmfcdoRHtHh
|
$ bitcoin-cli importprivkey cW4s4MdW7BkUmqiKgYzSJdmvnzq8QDrf6gszPMC7eLmfcdoRHtHh
|
||||||
```
|
```
|
||||||
Expect this to take a while, as `bitcoind` needs to reread all past transactions, to see if there are any new ones that it should pay attention to.
|
Again, expect this to require an unpruned node. Expect this to take a while, as `bitcoind` needs to reread all past transactions, to see if there are any new ones that it should pay attention to.
|
||||||
|
|
||||||
> :information_source: **NOTE:** Many modern wallets prefer [mnemonic codes](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) to generate the seeds necessary to create the private keys. This methodology is not used `bitcoin-cli`, so you won't be able to generate handy word lists to remember your private keys.
|
> :information_source: **NOTE:** Many modern wallets prefer [mnemonic codes](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) to generate the seeds necessary to create the private keys. This methodology is not used `bitcoin-cli`, so you won't be able to generate handy word lists to remember your private keys.
|
||||||
|
|
||||||
|
@ -20,15 +20,17 @@ These commands clear the NEW_ADDRESS_1 variable, just to be sure, then fill it w
|
|||||||
You can then use your shell's `echo` command to look at your (new) address:
|
You can then use your shell's `echo` command to look at your (new) address:
|
||||||
```
|
```
|
||||||
$ echo $NEW_ADDRESS_1
|
$ echo $NEW_ADDRESS_1
|
||||||
n4cqjJE6fqcmeWpftygwPoKMMDva6BpyHf
|
mi25UrzHnvn3bpEfFCNqJhPWJn5b77a5NE
|
||||||
```
|
```
|
||||||
Because you have your address in a variable, you can now easily sign a message for that address, without worrying about mistyping the address. You'll of course save that signature into a variable too!
|
Because you have your address in a variable, you can now easily sign a message for that address, without worrying about mistyping the address. You'll of course save that signature into a variable too!
|
||||||
```
|
```
|
||||||
$ NEW_SIG_1=$(bitcoin-cli signmessage $NEW_ADDRESS_1 "Hello, World")
|
$ NEW_SIG_1=$(bitcoin-cli signmessage $NEW_ADDRESS_1 "Hello, World")
|
||||||
|
$ echo $NEW_SIG_1
|
||||||
|
IPYIzgj+Rg4bxDwCyoPiFiNNcxWHYxgVcklhmN8aB2XRRJqV731Xu9XkfZ6oxj+QGCRmTe80X81EpXtmGUpXOM4=
|
||||||
```
|
```
|
||||||
The rest of this tutorial will use this style of saving information to variables when it's practical.
|
The rest of this tutorial will use this style of saving information to variables when it's practical.
|
||||||
|
|
||||||
_When is it not practical to use command-line variables?_ Command-line variables aren't practical if you need to use the information somewhere other than on the command line. For example, saving your signature may not actually be useful if you're just going to have to send it to someone else in an email. In addition, some future commands will output JSON objects instead of simple information, and variables can't be used to capture that information ... at least not without a _little_ more work.
|
> :book: ***When is it not practical to use command-line variables?*** Command-line variables aren't practical if you need to use the information somewhere other than on the command line. For example, saving your signature may not actually be useful if you're just going to have to send it to someone else in an email. In addition, some future commands will output JSON objects instead of simple information, and variables can't be used to capture that information ... at least not without a _little_ more work.
|
||||||
|
|
||||||
## Summary: Using Command-Line Variables
|
## Summary: Using Command-Line Variables
|
||||||
|
|
||||||
|
@ -6,11 +6,11 @@ You're now ready to receive some money at the new address you set up.
|
|||||||
|
|
||||||
## Get Some Money
|
## Get Some Money
|
||||||
|
|
||||||
To do anything more, you need to get some money. On testnet this is done through faucets. Since the money is all pretend, you just go to a faucet, request some money, and it will be sent over to you. We suggest using the faucet at https://testnet.coinfaucet.eu/en/. If it's not available for some reason, search for "bitcoin testnet faucet", and you should find others.
|
To do anything more, you need to get some money. On testnet this is done through faucets. Since the money is all pretend, you just go to a faucet, request some money, and it will be sent over to you. We suggest using the faucet at https://testnet-faucet.mempool.co/, https://bitcoinfaucet.uo1.net/, or https://testnet.coinfaucet.eu/en/. If they're not available for some reason, search for "bitcoin testnet faucet", and you should find others.
|
||||||
|
|
||||||
To use a faucet, you'll usually need to go to a URL and copy and paste in your address. Note that this is one of those cases where you won't be able to use command-line variables, alas. Afterward, a transaction will be created that sends money from the faucet to you.
|
To use a faucet, you'll usually need to go to a URL and copy and paste in your address. Note that this is one of those cases where you won't be able to use command-line variables, alas. Afterward, a transaction will be created that sends money from the faucet to you.
|
||||||
|
|
||||||
_What is a transaction?_ A transaction is a bitcoin exchange. The owner of some bitcoins uses his private key to access those coins, then locks the transaction using the recipient's public key.
|
> :book: ***What is a transaction?*** A transaction is a bitcoin exchange. The owner of some bitcoins uses his private key to access those coins, then locks the transaction using the recipient's public key.
|
||||||
|
|
||||||
> :link: **TESTNET vs MAINNET:** Sadly, there are no faucets in real life. If you were playing on the mainnet, you'd need to go and actually buy bitcoins at a bitcoin exchange or ATM, or you'd need to get someone to send them to you. Testnet life is much easier.
|
> :link: **TESTNET vs MAINNET:** Sadly, there are no faucets in real life. If you were playing on the mainnet, you'd need to go and actually buy bitcoins at a bitcoin exchange or ATM, or you'd need to get someone to send them to you. Testnet life is much easier.
|
||||||
|
|
||||||
@ -25,27 +25,27 @@ But wait, there's no balance yet!?
|
|||||||
|
|
||||||
Welcome to the world of Bitcoin latency.The problem is that your transaction hasn't yet been recorded in a block!
|
Welcome to the world of Bitcoin latency.The problem is that your transaction hasn't yet been recorded in a block!
|
||||||
|
|
||||||
_What is a block?_ Transactions are transmitted across the network and gathered into blocks by miners. These blocks are secured with a mathematical proof-of-work, which proves that computing power has been expended as part of the block creation. It's that proof-of-work (multiplied over many blocks, each built atop the last) that ultimately keeps Bitcoin secure.
|
> :book: ***What is a block?*** Transactions are transmitted across the network and gathered into blocks by miners. These blocks are secured with a mathematical proof-of-work, which proves that computing power has been expended as part of the block creation. It's that proof-of-work (multiplied over many blocks, each built atop the last) that ultimately keeps Bitcoin secure.
|
||||||
|
|
||||||
_What is a miner?_ A miner is a participant of the Bitcoin network who works to create blocks. It's a paying job: when a miner successfully creates a block, he is paid a one-time reward plus the fees for the transactions in his block. Mining is big business. Miners tend to run on special hardware, accelerated in ways that make it more likely that they'll be able to create blocks. They also tend to be part of mining pools, where the miners all agree to share out the rewards when one of them successfully creates a block.
|
> :book: ***What is a miner?*** A miner is a participant of the Bitcoin network who works to create blocks. It's a paying job: when a miner successfully creates a block, he is paid a one-time reward plus the fees for the transactions in his block. Mining is big business. Miners tend to run on special hardware, accelerated in ways that make it more likely that they'll be able to create blocks. They also tend to be part of mining pools, where the miners all agree to share out the rewards when one of them successfully creates a block.
|
||||||
|
|
||||||
Fortunately, `bitcoin-cli getunconfirmedbalance` should still show your updated balance as long as the initial transaction has been created:
|
Fortunately, `bitcoin-cli getunconfirmedbalance` should still show your updated balance as long as the initial transaction has been created:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli getunconfirmedbalance
|
$ bitcoin-cli getunconfirmedbalance
|
||||||
0.47000000
|
0.01010000
|
||||||
```
|
```
|
||||||
If that's still showing a zero too, you're probably moving through this tutorial too fast. Wait a second. The coins should show up unconfirmed, then rapidly move to confirmed. Do note that a coin can move from unconfirmedbalance to confirmedbalance almost immediately, so make sure you check both. However, if your `getbalance` and your `getunconfirmedbalance` both still show zero in ten minutes, then there's probably something wrong with the faucet, and you'll need to pick another.
|
If that's still showing a zero too, you're probably moving through this tutorial too fast. Wait a second. The coins should show up unconfirmed, then rapidly move to confirmed. Do note that a coin can move from unconfirmedbalance to confirmedbalance almost immediately, so make sure you check both. However, if your `getbalance` and your `getunconfirmedbalance` both still show zero in ten minutes, then there's probably something wrong with the faucet, and you'll need to pick another.
|
||||||
|
|
||||||
### Gain Confidence in Your Money
|
### Gain Confidence in Your Money
|
||||||
|
|
||||||
You can use `bitcoin-cli getbalance "*" [n]` to see if a confirmed balance is 'n' blocks deep.
|
You can use `bitcoin-cli getbalance "*" [n]`, where you replace `[n]` with an integer, to see if a confirmed balance is 'n' blocks deep.
|
||||||
|
|
||||||
_What is block depth?_ After a block is built and confirmed, another block is built on top of it, and another ... Because this is a stochastic process, there's some chance for reversal when a block is still new. Thus, a block has to be buried several blocks deep in a chain before you can feel totally confident in your funds. Each of those blocks tends to be built in an average of 10 minutes ... so it usually takes about an hour for a confirmed transaction to receive full confidence.
|
> :book: ***What is block depth?*** After a block is built and confirmed, another block is built on top of it, and another ... Because this is a stochastic process, there's some chance for reversal when a block is still new. Thus, a block has to be buried several blocks deep in a chain before you can feel totally confident in your funds. Each of those blocks tends to be built in an average of 10 minutes ... so it usually takes about an hour for a confirmed transaction to receive six blooks deep, which is the measure for full confidence in Bitcoin.
|
||||||
|
|
||||||
The following shows that our transaction has been confirmed one time, but not twice:
|
The following shows that our transactions have been confirmed one time, but not twice:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli getbalance "*" 1
|
$ bitcoin-cli getbalance "*" 1
|
||||||
0.47000000
|
0.01010000
|
||||||
$ bitcoin-cli getbalance "*" 2
|
$ bitcoin-cli getbalance "*" 2
|
||||||
0.00000000
|
0.00000000
|
||||||
```
|
```
|
||||||
@ -59,15 +59,20 @@ The `bitcoin-cli getwalletinfo` command gives you more information on the balanc
|
|||||||
```
|
```
|
||||||
$ bitcoin-cli getwalletinfo
|
$ bitcoin-cli getwalletinfo
|
||||||
{
|
{
|
||||||
"walletversion": 130000,
|
"walletname": "",
|
||||||
"balance": 0.47000000,
|
"walletversion": 169900,
|
||||||
|
"balance": 0.01010000,
|
||||||
"unconfirmed_balance": 0.00000000,
|
"unconfirmed_balance": 0.00000000,
|
||||||
"immature_balance": 0.00000000,
|
"immature_balance": 0.00000000,
|
||||||
"txcount": 1,
|
"txcount": 2,
|
||||||
"keypoololdest": 1488216266,
|
"keypoololdest": 1592335137,
|
||||||
"keypoolsize": 100,
|
"keypoolsize": 999,
|
||||||
|
"hdseedid": "fdea8e2630f00d29a9d6ff2af7bf5b358d061078",
|
||||||
|
"keypoolsize_hd_internal": 1000,
|
||||||
"paytxfee": 0.00000000,
|
"paytxfee": 0.00000000,
|
||||||
"hdmasterkeyid": "b91d5ec57d5ae3e90fff50d12e819429b6496b94"
|
"private_keys_enabled": true,
|
||||||
|
"avoid_reuse": false,
|
||||||
|
"scanning": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -78,114 +83,211 @@ Your money came into your wallet via a transaction. You can discover that transa
|
|||||||
$ bitcoin-cli listtransactions
|
$ bitcoin-cli listtransactions
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"account": "",
|
"address": "mi25UrzHnvn3bpEfFCNqJhPWJn5b77a5NE",
|
||||||
"address": "n4cqjJE6fqcmeWpftygwPoKMMDva6BpyHf",
|
|
||||||
"category": "receive",
|
"category": "receive",
|
||||||
"amount": 0.47000000,
|
"amount": 0.01000000,
|
||||||
"label": "",
|
"label": "",
|
||||||
"vout": 0,
|
"vout": 1,
|
||||||
"confirmations": 2,
|
"confirmations": 1,
|
||||||
"blockhash": "00000000fa4fdd22a2c33c6200b68239939ad65af3f1a48ecea25f8200f5d66b",
|
"blockhash": "00000000000001753b24411d0e4726212f6a53aeda481ceff058ffb49e1cd969",
|
||||||
"blockindex": 45,
|
"blockheight": 1772396,
|
||||||
"blocktime": 1488307692,
|
"blockindex": 73,
|
||||||
"txid": "88e5d5f3077517d76f5a61491fa52e6aaae078c52bc62d849f09507ef0cfada2",
|
"blocktime": 1592600085,
|
||||||
|
"txid": "8e2ab10cabe9ec04ed438086a80b1ac72558cc05bb206e48fc9a18b01b9282e9",
|
||||||
"walletconflicts": [
|
"walletconflicts": [
|
||||||
],
|
],
|
||||||
"time": 1488307692,
|
"time": 1592599884,
|
||||||
"timereceived": 1488307696,
|
"timereceived": 1592599884,
|
||||||
|
"bip125-replaceable": "no"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "mi25UrzHnvn3bpEfFCNqJhPWJn5b77a5NE",
|
||||||
|
"category": "receive",
|
||||||
|
"amount": 0.00010000,
|
||||||
|
"label": "",
|
||||||
|
"vout": 0,
|
||||||
|
"confirmations": 1,
|
||||||
|
"blockhash": "00000000000001753b24411d0e4726212f6a53aeda481ceff058ffb49e1cd969",
|
||||||
|
"blockheight": 1772396,
|
||||||
|
"blockindex": 72,
|
||||||
|
"blocktime": 1592600085,
|
||||||
|
"txid": "ca4898d8f950df03d6bfaa00578bd0305d041d24788b630d0c4a32debcac9f36",
|
||||||
|
"walletconflicts": [
|
||||||
|
],
|
||||||
|
"time": 1592599938,
|
||||||
|
"timereceived": 1592599938,
|
||||||
"bip125-replaceable": "no"
|
"bip125-replaceable": "no"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
```
|
```
|
||||||
This shows one transaction ("88e5d5f3077517d76f5a61491fa52e6aaae078c52bc62d849f09507ef0cfada2") that was received ("receive") by a specific address in my wallet ("n4cqjJE6fqcmeWpftygwPoKMMDva6BpyHf") for a specific amount ("0.47000000").
|
This shows two transactions (`8e2ab10cabe9ec04ed438086a80b1ac72558cc05bb206e48fc9a18b01b9282e9`) and (`ca4898d8f950df03d6bfaa00578bd0305d041d24788b630d0c4a32debcac9f36`) for a specific amount (`0.01000000` and `0.00010000`), which were both received (`receive`) by the same address in our wallet (`mi25UrzHnvn3bpEfFCNqJhPWJn5b77a5NE`). That's bad key hygeine, by the by: you should use a new address for every single Bitcoin you ever receive. In this case, we got impatient because the first faucet didn't seem to be working.
|
||||||
|
|
||||||
You can access similar information with the `bitcoin-cli listunspent` command, but it only shows the transactions for the money that you haven't spent. These are called UTXOs, and will be vitally important when you're sending money back out into the Bitcoin world:
|
You can access similar information with the `bitcoin-cli listunspent` command, but it only shows the transactions for the money that you haven't spent. These are called UTXOs, and will be vitally important when you're sending money back out into the Bitcoin world:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli listunspent
|
$ bitcoin-cli listunspent
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"txid": "88e5d5f3077517d76f5a61491fa52e6aaae078c52bc62d849f09507ef0cfada2",
|
"txid": "ca4898d8f950df03d6bfaa00578bd0305d041d24788b630d0c4a32debcac9f36",
|
||||||
"vout": 0,
|
"vout": 0,
|
||||||
"address": "n4cqjJE6fqcmeWpftygwPoKMMDva6BpyHf",
|
"address": "mi25UrzHnvn3bpEfFCNqJhPWJn5b77a5NE",
|
||||||
"account": "",
|
"label": "",
|
||||||
"scriptPubKey": "76a914fd67e8a7c7813e7a5c376eb71074f373d924d96888ac",
|
"scriptPubKey": "76a9141b72503639a13f190bf79acf6d76255d772360b788ac",
|
||||||
"amount": 0.47000000,
|
"amount": 0.00010000,
|
||||||
"confirmations": 3,
|
"confirmations": 1,
|
||||||
"spendable": true,
|
"spendable": true,
|
||||||
"solvable": true
|
"solvable": true,
|
||||||
|
"desc": "pkh([d6043800/0'/0'/1']02fd5740996d853ea51a6904cf03257fc11204b0179f344c49739ec5b20b39c9ba)#62rud39c",
|
||||||
|
"safe": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"txid": "8e2ab10cabe9ec04ed438086a80b1ac72558cc05bb206e48fc9a18b01b9282e9",
|
||||||
|
"vout": 1,
|
||||||
|
"address": "mi25UrzHnvn3bpEfFCNqJhPWJn5b77a5NE",
|
||||||
|
"label": "",
|
||||||
|
"scriptPubKey": "76a9141b72503639a13f190bf79acf6d76255d772360b788ac",
|
||||||
|
"amount": 0.01000000,
|
||||||
|
"confirmations": 1,
|
||||||
|
"spendable": true,
|
||||||
|
"solvable": true,
|
||||||
|
"desc": "pkh([d6043800/0'/0'/1']02fd5740996d853ea51a6904cf03257fc11204b0179f344c49739ec5b20b39c9ba)#62rud39c",
|
||||||
|
"safe": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
Note that bitcoins are not just a homogeneous mess of cash jammed into your pocket. Each individual transaction that you receive or that you send is placed into the immutable blockchain ledger, in a block. You can see these individual transactions when you look at your unspent money. This means that bitcoin spending isn't quite as anonymous as you'd think. Though the addresses are fairly private, transactions can be examined as they go in and out of addresses. This makes privacy vulnerable to statistical analysis. It also introduces some potential non-fungibility to bitcoins, as you can track back through series of transactions, even if you can't track a specific "bitcoin".
|
Note that bitcoins are not just a homogeneous mess of cash jammed into your pocket. Each individual transaction that you receive or that you send is placed into the immutable blockchain ledger, in a block. You can see these individual transactions when you look at your unspent money. This means that bitcoin spending isn't quite as anonymous as you'd think. Though the addresses are fairly private, transactions can be examined as they go in and out of addresses. This makes privacy vulnerable to statistical analysis. It also introduces some potential non-fungibility to bitcoins, as you can track back through series of transactions, even if you can't track a specific "bitcoin".
|
||||||
|
|
||||||
_Why are all of these bitcoin amounts in fractions?_ Bitcoins are produced slowly, and so there are relatively few in circulation. As a result, each bitcoin over on the mainnet is worth quite a bit (~ $1,000 at the time of this writing). This means that people usually work in fractions. In fact, .47 BTC would be quite a lot in the real-world. You'll often be dealing with even smaller fractions on mainnet. For this reason, names have appeared for smaller amounts of bitcoins, including millibitcoins or mBTCs (one-thousandth of a bitcoin), microbitcoins or bits or μBTCs (one-millionth of a bitcoin), and satoshis (one hundred millionth of a bitcoin).
|
> :book: ***Why are all of these bitcoin amounts in fractions?*** Bitcoins are produced slowly, and so there are relatively few in circulation. As a result, each bitcoin over on the mainnet is worth quite a bit (~ $9,000 at the time of this writing). This means that people usually work in fractions. In fact, the .0101 in Testnet coins would be worth about $100 if they were on the mainnet. For this reason, names have appeared for smaller amounts of bitcoins, including millibitcoins or mBTCs (one-thousandth of a bitcoin), microbitcoins or bits or μBTCs (one-millionth of a bitcoin), and satoshis (one hundred millionth of a bitcoin).
|
||||||
|
|
||||||
## Examine Your Transaction
|
## Examine Your Transaction
|
||||||
|
|
||||||
You can get more information on a transaction with the `bitcoin-cli getrawtransaction` command:
|
You can get more information on a transaction with the `bitcoin-cli gettransaction` command:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli getrawtransaction "88e5d5f3077517d76f5a61491fa52e6aaae078c52bc62d849f09507ef0cfada2"
|
$ bitcoin-cli gettransaction "8e2ab10cabe9ec04ed438086a80b1ac72558cc05bb206e48fc9a18b01b9282e9"
|
||||||
010000000133261a25b44689bab2c6a207381ca21d243de9bbf21f0fa40c3a26ba7282a87b000000006b483045022100a2640761810dfc34dabed599928243afe24e13f520f780ceb382843a530a577c022050b92f5d9843d70ddb60a0aa294938862f2b7372818d6149ffd4f6adec5cf6c80121034dcaa515c2fda0f4a50b90a6d798e01c00a870bef0bd97154066fe202d2b5d75feffffff02c029cd02000000001976a914fd67e8a7c7813e7a5c376eb71074f373d924d96888ac17791703000000001976a914e176ee39c642344df2180863e27e2e936307273c88ac07a41000
|
|
||||||
```
|
|
||||||
Granted, this isn't super useful, because it's the hex-encoded transaction data. Fortunately, you can get a more verbose description just by adding a '1' to your command:
|
|
||||||
```
|
|
||||||
$ bitcoin-cli getrawtransaction "88e5d5f3077517d76f5a61491fa52e6aaae078c52bc62d849f09507ef0cfada2" 1
|
|
||||||
{
|
{
|
||||||
"hex": "010000000133261a25b44689bab2c6a207381ca21d243de9bbf21f0fa40c3a26ba7282a87b000000006b483045022100a2640761810dfc34dabed599928243afe24e13f520f780ceb382843a530a577c022050b92f5d9843d70ddb60a0aa294938862f2b7372818d6149ffd4f6adec5cf6c80121034dcaa515c2fda0f4a50b90a6d798e01c00a870bef0bd97154066fe202d2b5d75feffffff02c029cd02000000001976a914fd67e8a7c7813e7a5c376eb71074f373d924d96888ac17791703000000001976a914e176ee39c642344df2180863e27e2e936307273c88ac07a41000",
|
"amount": 0.01000000,
|
||||||
"txid": "88e5d5f3077517d76f5a61491fa52e6aaae078c52bc62d849f09507ef0cfada2",
|
"confirmations": 1,
|
||||||
"hash": "88e5d5f3077517d76f5a61491fa52e6aaae078c52bc62d849f09507ef0cfada2",
|
"blockhash": "00000000000001753b24411d0e4726212f6a53aeda481ceff058ffb49e1cd969",
|
||||||
"size": 226,
|
"blockheight": 1772396,
|
||||||
"vsize": 226,
|
"blockindex": 73,
|
||||||
"version": 1,
|
"blocktime": 1592600085,
|
||||||
"locktime": 1090567,
|
"txid": "8e2ab10cabe9ec04ed438086a80b1ac72558cc05bb206e48fc9a18b01b9282e9",
|
||||||
|
"walletconflicts": [
|
||||||
|
],
|
||||||
|
"time": 1592599884,
|
||||||
|
"timereceived": 1592599884,
|
||||||
|
"bip125-replaceable": "no",
|
||||||
|
"details": [
|
||||||
|
{
|
||||||
|
"address": "mi25UrzHnvn3bpEfFCNqJhPWJn5b77a5NE",
|
||||||
|
"category": "receive",
|
||||||
|
"amount": 0.01000000,
|
||||||
|
"label": "",
|
||||||
|
"vout": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hex": "0200000000010114d04977d1b0137adbf51dd5d79944b9465a2619f3fa7287eb69a779977bf5800100000017160014e85ba02862dbadabd6d204fcc8bb5d54658c7d4ffeffffff02df690f000000000017a9145c3bfb36b03f279967977ca9d1e35185e39917788740420f00000000001976a9141b72503639a13f190bf79acf6d76255d772360b788ac0247304402201e74bdfc330fc2e093a8eabe95b6c5633c8d6767249fa25baf62541a129359c202204d462bd932ee5c15c7f082ad7a6b5a41c68addc473786a0a9a232093fde8e1330121022897dfbf085ecc6ad7e22fc91593414a845659429a7bbb44e2e536258d2cbc0c270b1b00"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The `gettransaction` command will detail transanctions that are in your wallet, such as this one, that was sent to us.
|
||||||
|
|
||||||
|
Note that `gettransaction` has two optional arguments:
|
||||||
|
```
|
||||||
|
$ bitcoin-cli help gettransaction
|
||||||
|
gettransaction "txid" ( include_watchonly verbose )
|
||||||
|
|
||||||
|
Get detailed information about in-wallet transaction <txid>
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
1. txid (string, required) The transaction id
|
||||||
|
2. include_watchonly (boolean, optional, default=true for watch-only wallets, otherwise false) Whether to include watch-only addresses in balance calculation and details[]
|
||||||
|
3. verbose (boolean, optional, default=false) Whether to include a `decoded` field containing the decoded transaction (equivalent to RPC decoderawtransaction)
|
||||||
|
```
|
||||||
|
By setting these two true or false, we can choose to include watch-only addresses in the output (which we don't care about) or look at more verbose output (which we do).
|
||||||
|
|
||||||
|
Here's what this data instead looks at when we set `include_watchonly` to `false` and `verbose` to `true`.
|
||||||
|
```
|
||||||
|
$ bitcoin-cli gettransaction "8e2ab10cabe9ec04ed438086a80b1ac72558cc05bb206e48fc9a18b01b9282e9" false true
|
||||||
|
{
|
||||||
|
"amount": 0.01000000,
|
||||||
|
"confirmations": 3,
|
||||||
|
"blockhash": "00000000000001753b24411d0e4726212f6a53aeda481ceff058ffb49e1cd969",
|
||||||
|
"blockheight": 1772396,
|
||||||
|
"blockindex": 73,
|
||||||
|
"blocktime": 1592600085,
|
||||||
|
"txid": "8e2ab10cabe9ec04ed438086a80b1ac72558cc05bb206e48fc9a18b01b9282e9",
|
||||||
|
"walletconflicts": [
|
||||||
|
],
|
||||||
|
"time": 1592599884,
|
||||||
|
"timereceived": 1592599884,
|
||||||
|
"bip125-replaceable": "no",
|
||||||
|
"details": [
|
||||||
|
{
|
||||||
|
"address": "mi25UrzHnvn3bpEfFCNqJhPWJn5b77a5NE",
|
||||||
|
"category": "receive",
|
||||||
|
"amount": 0.01000000,
|
||||||
|
"label": "",
|
||||||
|
"vout": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hex": "0200000000010114d04977d1b0137adbf51dd5d79944b9465a2619f3fa7287eb69a779977bf5800100000017160014e85ba02862dbadabd6d204fcc8bb5d54658c7d4ffeffffff02df690f000000000017a9145c3bfb36b03f279967977ca9d1e35185e39917788740420f00000000001976a9141b72503639a13f190bf79acf6d76255d772360b788ac0247304402201e74bdfc330fc2e093a8eabe95b6c5633c8d6767249fa25baf62541a129359c202204d462bd932ee5c15c7f082ad7a6b5a41c68addc473786a0a9a232093fde8e1330121022897dfbf085ecc6ad7e22fc91593414a845659429a7bbb44e2e536258d2cbc0c270b1b00",
|
||||||
|
"decoded": {
|
||||||
|
"txid": "8e2ab10cabe9ec04ed438086a80b1ac72558cc05bb206e48fc9a18b01b9282e9",
|
||||||
|
"hash": "d4ae2b009c43bfe9eba96dcd16e136ceba2842df3d76a67d689fae5975ce49cb",
|
||||||
|
"version": 2,
|
||||||
|
"size": 249,
|
||||||
|
"vsize": 168,
|
||||||
|
"weight": 669,
|
||||||
|
"locktime": 1772327,
|
||||||
"vin": [
|
"vin": [
|
||||||
{
|
{
|
||||||
"txid": "7ba88272ba263a0ca40f1ff2bbe93d241da21c3807a2c6b2ba8946b4251a2633",
|
"txid": "80f57b9779a769eb8772faf319265a46b94499d7d51df5db7a13b0d17749d014",
|
||||||
"vout": 0,
|
"vout": 1,
|
||||||
"scriptSig": {
|
"scriptSig": {
|
||||||
"asm": "3045022100a2640761810dfc34dabed599928243afe24e13f520f780ceb382843a530a577c022050b92f5d9843d70ddb60a0aa294938862f2b7372818d6149ffd4f6adec5cf6c8[ALL] 034dcaa515c2fda0f4a50b90a6d798e01c00a870bef0bd97154066fe202d2b5d75",
|
"asm": "0014e85ba02862dbadabd6d204fcc8bb5d54658c7d4f",
|
||||||
"hex": "483045022100a2640761810dfc34dabed599928243afe24e13f520f780ceb382843a530a577c022050b92f5d9843d70ddb60a0aa294938862f2b7372818d6149ffd4f6adec5cf6c80121034dcaa515c2fda0f4a50b90a6d798e01c00a870bef0bd97154066fe202d2b5d75"
|
"hex": "160014e85ba02862dbadabd6d204fcc8bb5d54658c7d4f"
|
||||||
},
|
},
|
||||||
|
"txinwitness": [
|
||||||
|
"304402201e74bdfc330fc2e093a8eabe95b6c5633c8d6767249fa25baf62541a129359c202204d462bd932ee5c15c7f082ad7a6b5a41c68addc473786a0a9a232093fde8e13301",
|
||||||
|
"022897dfbf085ecc6ad7e22fc91593414a845659429a7bbb44e2e536258d2cbc0c"
|
||||||
|
],
|
||||||
"sequence": 4294967294
|
"sequence": 4294967294
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"vout": [
|
"vout": [
|
||||||
{
|
{
|
||||||
"value": 0.47000000,
|
"value": 0.01010143,
|
||||||
"n": 0,
|
"n": 0,
|
||||||
"scriptPubKey": {
|
"scriptPubKey": {
|
||||||
"asm": "OP_DUP OP_HASH160 fd67e8a7c7813e7a5c376eb71074f373d924d968 OP_EQUALVERIFY OP_CHECKSIG",
|
"asm": "OP_HASH160 5c3bfb36b03f279967977ca9d1e35185e3991778 OP_EQUAL",
|
||||||
"hex": "76a914fd67e8a7c7813e7a5c376eb71074f373d924d96888ac",
|
"hex": "a9145c3bfb36b03f279967977ca9d1e35185e399177887",
|
||||||
"reqSigs": 1,
|
"reqSigs": 1,
|
||||||
"type": "pubkeyhash",
|
"type": "scripthash",
|
||||||
"addresses": [
|
"addresses": [
|
||||||
"n4cqjJE6fqcmeWpftygwPoKMMDva6BpyHf"
|
"2N1ev1WKevSsdmAvRqZf7JjvDg223tPrVCm"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"value": 0.51869975,
|
"value": 0.01000000,
|
||||||
"n": 1,
|
"n": 1,
|
||||||
"scriptPubKey": {
|
"scriptPubKey": {
|
||||||
"asm": "OP_DUP OP_HASH160 e176ee39c642344df2180863e27e2e936307273c OP_EQUALVERIFY OP_CHECKSIG",
|
"asm": "OP_DUP OP_HASH160 1b72503639a13f190bf79acf6d76255d772360b7 OP_EQUALVERIFY OP_CHECKSIG",
|
||||||
"hex": "76a914e176ee39c642344df2180863e27e2e936307273c88ac",
|
"hex": "76a9141b72503639a13f190bf79acf6d76255d772360b788ac",
|
||||||
"reqSigs": 1,
|
"reqSigs": 1,
|
||||||
"type": "pubkeyhash",
|
"type": "pubkeyhash",
|
||||||
"addresses": [
|
"addresses": [
|
||||||
"n256of3JH1A6X8AQUU7LYkcaRcmrfGjGKC"
|
"mi25UrzHnvn3bpEfFCNqJhPWJn5b77a5NE"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"blockhash": "00000000fa4fdd22a2c33c6200b68239939ad65af3f1a48ecea25f8200f5d66b",
|
}
|
||||||
"confirmations": 3,
|
|
||||||
"time": 1488307692,
|
|
||||||
"blocktime": 1488307692
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Now you can see the full information on the transaction, including all of the inputs ("vin") and all the outputs ("vout). One of the interesting things to note is that although we received .47 BTC in the transaction, another .51869975 was sent to another address. That was probably a change address, a concept that is explored in the next section. It is quite typical for a transaction to have multiple inputs and/or multiple outputs.
|
Now you can see the full information on the transaction, including all of the inputs ("vin") and all the outputs ("vout). One of the interesting things to note is that although we received .01 BTC in the transaction, another .01010143 was sent to another address. That was probably a change address, a concept that is explored in the next section. It is quite typical for a transaction to have multiple inputs and/or multiple outputs.
|
||||||
|
|
||||||
> :warning: **WARNING:** These commands will not work in some situations. To be able to view a raw transaction on a standard node, some of the money must be unspent, or the transaction must still be in your mempool — which means that this command will work fine for the money you've just received, but not for old stuff. If you want to be able to view older transactions that have been spent, you can do so by maintaining a set of all transactions with the txindex=1 configuration, which is what our scripts suggest for all non-pruned instances. (You can't maintain a transaction index if your node is pruned.)
|
There is another command, `getrawtransaction`, which allows you to look at transactions that are not in your wallet. However, it requires you to have unpruned node and `txindex=1` in your `bitcoin.conf` file. Unless you have a serious need for information not in your wallet, it's probably just better to use a Bitcoin explorer for this sort of thing ...
|
||||||
|
|
||||||
## Optional: Use a Block Explorer
|
## Optional: Use a Block Explorer
|
||||||
|
|
||||||
@ -195,18 +297,21 @@ Currently, our preferred block explorer is [https://live.blockcypher.com/](https
|
|||||||
|
|
||||||
You can use it to look up transactions for an address:
|
You can use it to look up transactions for an address:
|
||||||
|
|
||||||
[https://live.blockcypher.com/btc-testnet/address/n4cqjJE6fqcmeWpftygwPoKMMDva6BpyHf/](https://live.blockcypher.com/btc-testnet/address/n4cqjJE6fqcmeWpftygwPoKMMDva6BpyHf/)
|
[https://live.blockcypher.com/btc-testnet/address/mi25UrzHnvn3bpEfFCNqJhPWJn5b77a5NE/](https://live.blockcypher.com/btc-testnet/address/mi25UrzHnvn3bpEfFCNqJhPWJn5b77a5NE/)
|
||||||
|
|
||||||
You can also use it to look at individual transactions:
|
You can also use it to look at individual transactions:
|
||||||
|
|
||||||
[https://live.blockcypher.com/btc-testnet/tx/88e5d5f3077517d76f5a61491fa52e6aaae078c52bc62d849f09507ef0cfada2/](https://live.blockcypher.com/btc-testnet/tx/88e5d5f3077517d76f5a61491fa52e6aaae078c52bc62d849f09507ef0cfada2/)
|
[https://live.blockcypher.com/btc-testnet/tx/8e2ab10cabe9ec04ed438086a80b1ac72558cc05bb206e48fc9a18b01b9282e9/](https://live.blockcypher.com/btc-testnet/tx/8e2ab10cabe9ec04ed438086a80b1ac72558cc05bb206e48fc9a18b01b9282e9/)
|
||||||
|
|
||||||
A block explorer doesn't generally provide any more information than a command line look at a raw transaction; it just does a good job of highlighting the important information and putting together the puzzle pieces, including the transaction fees behind a transaction — another concept that we'll be covering in future sections.
|
A block explorer doesn't generally provide any more information than a command line look at a raw transaction; it just does a good job of highlighting the important information and putting together the puzzle pieces, including the transaction fees behind a transaction — another concept that we'll be covering in future sections.
|
||||||
|
|
||||||
## Summary: Receiving a Transaction
|
## Summary: Receiving a Transaction
|
||||||
|
|
||||||
Faucets will give you money on the testnet. They come in as raw transactions, which can be examined with `getrawtransaction` or a block explorer. Once you've receive a transaction, you can see it in your balance and your wallet.
|
Faucets will give you money on the testnet. They come in as raw transactions, which can be examined with `gettransaction` or a block explorer. Once you've receive a transaction, you can see it in your balance and your wallet.
|
||||||
|
|
||||||
## What's Next?
|
## What's Next?
|
||||||
|
|
||||||
Advance through "bitcoin-cli" with [Chapter Four: Sending Bitcoin Transactions](04_0_Sending_Bitcoin_Transactions.md).
|
For a deep dive into how addresses are described, so that they can be transferred or made into parts of a multi-signature, see [§3.5: Understanding the Descriptor](03_5_Understanding_the_Descriptor.md).
|
||||||
|
|
||||||
|
But if that's too in-depth, continue on to [Chapter Four: Sending Bitcoin Transactions](04_0_Sending_Bitcoin_Transactions.md).
|
||||||
|
|
||||||
|
145
03_5_Understanding_the_Descriptor.md
Normal file
145
03_5_Understanding_the_Descriptor.md
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
# 3.5: Understanding the Descriptor
|
||||||
|
|
||||||
|
> :information_source: **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
||||||
|
|
||||||
|
You may have noticed a weird `desc:` field in the `listunspent` command of the previous section. Here's what's all about (and how it can be used to transfer addresses).
|
||||||
|
|
||||||
|
> :warning: **VERSION WARNING:** This is an innovation from Bitcoin Core v 0.17.0 that had continued to be expanded through Bitcoin Core 0.20.0. Most of the commands in this section are from 0.17.0, but the updated `importmulti` that support descriptors is from 0.18.0.
|
||||||
|
|
||||||
|
## Know about Transferring Addresses
|
||||||
|
|
||||||
|
Most of this course presumes that you're working entirely from a single node where you manage your own wallet, sending and receiving payments with the addresses created by that wallet. However, that's not necessarily how the larger Bitcoin ecosystem works. There, you're more likely to be moving addresses between wallets and even setting up wallets to watch over funds controlled by different wallets.
|
||||||
|
|
||||||
|
That's where descriptors come in. They're most useful if you're interacting with software _other_ than Bitcoin Core, and really need to lean on this sort of compatibility function: see [§6.1](https://github.com/BlockchainCommons/Learning-Bitcoin-from-the-Command-Line/blob/master/06_1_Sending_a_Transaction_to_a_Multisig.md) for a real-world example of how having the capability of descriptors is critical.
|
||||||
|
|
||||||
|
Moving addresses between wallets used to focus on `xpub` and `xprv`, and those are still supported.
|
||||||
|
|
||||||
|
> :book: ***What is xprv?*** An extended private key. This is the combination of a private key and a chain code. It's a private key that a whole sequence of children private keys can be derived from.
|
||||||
|
|
||||||
|
> :book: ***What is xpub?*** An extended public key. This is the combination of a public key and a chain code. It's a public key that a whole sequence of children public keys can be derived from.
|
||||||
|
|
||||||
|
The fact that you can have a "whole sequence of children ... keys" reveals the fact that "xpub" and "xprv" aren't standard keys like we've been talking about so far. They're instead hierarchical keys that can be used to create whole families of keys, built on the idea of HD Wallets.
|
||||||
|
|
||||||
|
> :book: ***What is an HD Wallet?*** Most modern wallets are built on [BIP32: Hierarchical Deterministic Wallets](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki). This is a hierarchical design where a single seed can be used to generate a whole sequence of keys. The entire wallet may then be restored from that seed, rather than requiring the restoring of every single private key.
|
||||||
|
|
||||||
|
> :book ***What is a Derivation Path?*** When you have hierarchical keys, you need to be able to define individual keys as descendents of a seed. For example `[0]` is the 0th key, `[0/1]` is the first son of the 0th key, `[1/0/1]` is the first grandson of the zeroth son of the 1st key. Some keys also contain a `'` after the number, to show they're hardened, which protects them from a specific attack that can be used to derive an `xprv` from an `xpub`. You don't need to worry about the specifics, other than the fact that those `'`s will cause you formatting troubles when working from the command line.
|
||||||
|
|
||||||
|
`xpubs` and `xprvs` proved insufficient when the types of public keys multiplied under the [SegWit expansion](https://github.com/BlockchainCommons/Learning-Bitcoin-from-the-Command-Line/blob/master/04_6_Creating_a_Segwit_Transaction.md), thus the need for "output descriptors".
|
||||||
|
|
||||||
|
> :book: ***What is an output descriptor?*** A precise description of how to derive a Bitcoin address from a combination of a function and one or more inputs to that function.
|
||||||
|
|
||||||
|
The introduction of functions into descriptors is what makes them powerful, because they can be used to transfer all sorts of addresses, from the Legacy addresses that we're working with now to the Segwit and multisig addresses that we'll meet down the road. An individual function matches a particular type of address and correlates with specific rules to generate that address.
|
||||||
|
|
||||||
|
## Capture a Descriptor
|
||||||
|
|
||||||
|
Descriptors are visible in several commands such as `listunspent` and `getaddressinfo`:
|
||||||
|
```
|
||||||
|
$ bitcoin-cli getaddressinfo ms7ruzvL4atCu77n47dStMb3of6iScS8kZ
|
||||||
|
{
|
||||||
|
"address": "ms7ruzvL4atCu77n47dStMb3of6iScS8kZ",
|
||||||
|
"scriptPubKey": "76a9147f437379bcc66c40745edc1891ea6b3830e1975d88ac",
|
||||||
|
"ismine": true,
|
||||||
|
"solvable": true,
|
||||||
|
"desc": "pkh([d6043800/0'/0'/18']03efdee34c0009fd175f3b20b5e5a5517fd5d16746f2e635b44617adafeaebc388)#4ahsl9pk",
|
||||||
|
"iswatchonly": false,
|
||||||
|
"isscript": false,
|
||||||
|
"iswitness": false,
|
||||||
|
"pubkey": "03efdee34c0009fd175f3b20b5e5a5517fd5d16746f2e635b44617adafeaebc388",
|
||||||
|
"iscompressed": true,
|
||||||
|
"ischange": false,
|
||||||
|
"timestamp": 1592335136,
|
||||||
|
"hdkeypath": "m/0'/0'/18'",
|
||||||
|
"hdseedid": "fdea8e2630f00d29a9d6ff2af7bf5b358d061078",
|
||||||
|
"hdmasterfingerprint": "d6043800",
|
||||||
|
"labels": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Here the descriptor is `pkh([d6043800/0'/0'/18']03efdee34c0009fd175f3b20b5e5a5517fd5d16746f2e635b44617adafeaebc388)#4ahsl9pk`.
|
||||||
|
|
||||||
|
## Understand a Descriptor
|
||||||
|
|
||||||
|
A descriptor is broken into several parts:
|
||||||
|
```
|
||||||
|
function([derivation-path]key)#checksum
|
||||||
|
```
|
||||||
|
Here's what that all means:
|
||||||
|
* **Function.** The function that is used to create an address from that key. In this cases it's `pkh`, which is the standard P2PKH legacy address that you met in [§3.3: Setting Up Your Wallet](03_3_Setting_Up_Your_Wallet.md). Similarly, a P2WSH SegWit address would use `wsh` and a P2WPKH address would use `wpkh`.
|
||||||
|
* **Derivation Path.** This describes what part of an HD wallet is being exported. In this case it's a seed with the fingerprint `d6043800` and then the 18th child of the 0th child of the 0th child (`0'/0'/18'`) of that seed. There may also be a further derivation after the key: `function([derivation-path]key/more-derivation)#checksum`
|
||||||
|
* It's worth noting here that if you ever get a derivation path without a fingerprint, you can make it up. It's just that if there's an existing one, you should match it, because if you ever go back to the device that created the fingerprint, you'll need to have the same one.
|
||||||
|
* **Key**. The key or keys that are being transferred. This could be something traditional like an `xpub` or `xprv`, it could just be a public key for an address as in this case, it could be a set of addresses for a multi-signature, or it could be something else. This is the core data: the function explains what to do with it.
|
||||||
|
* **Checksum**. Descriptors are meant to be human transferrable. This checksum makes sure you got it right.
|
||||||
|
|
||||||
|
See [Bitcoin Core's Info on Descriptor Support](https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md) for more information.
|
||||||
|
|
||||||
|
## Examine a Descriptor
|
||||||
|
|
||||||
|
You can look at a descriptor with the `getdescriptorinfo` RPC:
|
||||||
|
```
|
||||||
|
$ bitcoin-cli getdescriptorinfo "pkh([d6043800/0'/0'/18']03efdee34c0009fd175f3b20b5e5a5517fd5d16746f2e635b44617adafeaebc388)#4ahsl9pk"
|
||||||
|
{
|
||||||
|
"descriptor": "pkh([d6043800/0'/0'/18']03efdee34c0009fd175f3b20b5e5a5517fd5d16746f2e635b44617adafeaebc388)#4ahsl9pk",
|
||||||
|
"checksum": "4ahsl9pk",
|
||||||
|
"isrange": false,
|
||||||
|
"issolvable": true,
|
||||||
|
"hasprivatekeys": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Note that it returns a checksum. If you're ever given a descriptor without a checksum, you can learn it with this command:
|
||||||
|
```
|
||||||
|
$ bitcoin-cli getdescriptorinfo "pkh([d6043800/0'/0'/18']03efdee34c0009fd175f3b20b5e5a5517fd5d16746f2e635b44617adafeaebc388)"
|
||||||
|
{
|
||||||
|
"descriptor": "pkh([d6043800/0'/0'/18']03efdee34c0009fd175f3b20b5e5a5517fd5d16746f2e635b44617adafeaebc388)#4ahsl9pk",
|
||||||
|
"checksum": "4ahsl9pk",
|
||||||
|
"isrange": false,
|
||||||
|
"issolvable": true,
|
||||||
|
"hasprivatekeys": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Besides giving you the checksum, this command also provides useful information like whether a descriptor contains private keys.
|
||||||
|
|
||||||
|
One of the powers of a descriptor is being able to derive an address in a regular way. This is done with the `deriveaddresses` RPC.
|
||||||
|
```
|
||||||
|
$ bitcoin-cli deriveaddresses "pkh([d6043800/0'/0'/18']03efdee34c0009fd175f3b20b5e5a5517fd5d16746f2e635b44617adafeaebc388)#4ahsl9pk"
|
||||||
|
[
|
||||||
|
"ms7ruzvL4atCu77n47dStMb3of6iScS8kZ"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
You'll note it loops back to the address we started with (as it should).
|
||||||
|
|
||||||
|
## Import a Desciptor
|
||||||
|
|
||||||
|
But, the really important thing about a descriptor is that you can take it to another machine and import it. This is done with the `importmulti` RPC using the `desc` option:
|
||||||
|
```
|
||||||
|
remote$ bitcoin-cli importmulti '[{"desc": "pkh([d6043800/0'"'"'/0'"'"'/18'"'"']03efdee34c0009fd175f3b20b5e5a5517fd5d16746f2e635b44617adafeaebc388)#4ahsl9pk", "timestamp": "now", "watchonly": true}]'
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
First, you'll note our first really ugly use of quotes. Every `'` in the derivation path had to be replaced with `'"'"'`. Just expect to have to do that if you're manipulating a descriptor that contains a derivation path. (The other option is to exchange the `'` with a `h` for hardened, but that will change you checksum, so if you prefer that for its ease of use, you'll need to get a new checksum with `getdescriptorinfo`.)
|
||||||
|
|
||||||
|
Second, you'll note that we flagged this as `watchonly`. That's because we know that it's a public key, so we can't spend with it. If we'd failed to enter this flag, `importmulti` would helpfully have told us something like: `Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag.`.
|
||||||
|
|
||||||
|
> :book: ***What is a watch-only address?*** A watch-only address allows you to watch for transactions related to an address (or to a whole family of addresses if you used an `xpub`), but not to spend funds on those addresses.
|
||||||
|
|
||||||
|
Using `getaddressesbylabel`, we can now see that our address has correctly been imported into our remote machine!
|
||||||
|
```
|
||||||
|
remote$ bitcoin-cli getaddressesbylabel ""
|
||||||
|
{
|
||||||
|
"ms7ruzvL4atCu77n47dStMb3of6iScS8kZ": {
|
||||||
|
"purpose": "receive"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
## Summary: Understanding the Descriptor
|
||||||
|
|
||||||
|
Descriptors let you pass public keys and private keys among wallets, but more than that, they allow you to precisely and correctly to define addresses and to derive addresses of a lot of different sorts from a standardized description format.
|
||||||
|
|
||||||
|
> :fire: ***What is the power of descriptors?*** Descriptors allow you to import and export seeds and keys. That's great if you want to move between different wallets. As a developer, they also allow you to build up the precise sort of addresses that you're interested in creating. For example, we use it in [FullyNoded 2](https://github.com/BlockchainCommons/FullyNoded-2/blob/master/Docs/How-it-works.md) to generate a multi-sig from three seeds.
|
||||||
|
|
||||||
|
## What's Next?
|
||||||
|
|
||||||
|
Advance through "bitcoin-cli" with [Chapter Four: Sending Bitcoin Transactions](04_0_Sending_Bitcoin_Transactions.md).
|
@ -13,6 +13,7 @@ After working through this chapter, a developer will be able to:
|
|||||||
Supporting objectives include the ability to:
|
Supporting objectives include the ability to:
|
||||||
|
|
||||||
* Understand Transactions & Transaction Fees
|
* Understand Transactions & Transaction Fees
|
||||||
|
* Understand Legacy & SegWit Transactions
|
||||||
* Use Basic Methods to Send Money
|
* Use Basic Methods to Send Money
|
||||||
* Use Auto Fee Calculation Methods to Send Money
|
* Use Auto Fee Calculation Methods to Send Money
|
||||||
* Understand the Dangers of Raw Transactions
|
* Understand the Dangers of Raw Transactions
|
||||||
@ -25,3 +26,4 @@ Supporting objectives include the ability to:
|
|||||||
* [Section Three: Creating a Raw Transaction with Named Arguments](04_3_Creating_a_Raw_Transaction_with_Named_Arguments.md)
|
* [Section Three: Creating a Raw Transaction with Named Arguments](04_3_Creating_a_Raw_Transaction_with_Named_Arguments.md)
|
||||||
* [Section Four: Sending Coins with Raw Transactions](04_4_Sending_Coins_with_a_Raw_Transaction.md)
|
* [Section Four: Sending Coins with Raw Transactions](04_4_Sending_Coins_with_a_Raw_Transaction.md)
|
||||||
* [Section Five: Sending Coins with Automated Raw Transactions](04_5_Sending_Coins_with_Automated_Raw_Transactions.md)
|
* [Section Five: Sending Coins with Automated Raw Transactions](04_5_Sending_Coins_with_Automated_Raw_Transactions.md)
|
||||||
|
* [Section Six: Creating a SegWit Transaction](04_6_Creating_a_Segwit_Transaction.md)
|
||||||
|
@ -8,11 +8,11 @@ The `bitcoin-cli` offers three major ways to send coins: as a simple command; as
|
|||||||
|
|
||||||
Before you send any money on the Bitcoin network, you should think about what transaction fees you're going to pay.
|
Before you send any money on the Bitcoin network, you should think about what transaction fees you're going to pay.
|
||||||
|
|
||||||
_What is a transaction fee?_ There's no such thing as a free lunch. Miners incorporate transactions into blocks because they're paid to do so. Not only do they get paid by the network for making the block, but they also get paid by transactors for including their transactions. If you don't pay a fee, your transaction might get stuck ... forever (or, until saved by some of the tricks in section five).
|
> :book: ***What is a transaction fee?*** There's no such thing as a free lunch. Miners incorporate transactions into blocks because they're paid to do so. Not only do they get paid by the network for making the block, but they also get paid by transactors for including their transactions. If you don't pay a fee, your transaction might get stuck ... forever (or, until saved by some of the tricks in [Chapter Five](05_0_Controlling_Bitcoin_Transactions.md)).
|
||||||
|
|
||||||
When you're using the simple and automated methods for creating transactions, as outlined here and in [§4.5: Sending Coins with Automated Raw Transactions](04_5_Sending_Coins_with_Automated_Raw_Transactions.md), Bitcoin will calculate transaction fees for you. This is done using Floating Fees, where the `bitcoind` watches how long transactions are taking to confirm and automatically calculates for you what to spend.
|
When you're using the simple and automated methods for creating transactions, as outlined here and in [§4.5: Sending Coins with Automated Raw Transactions](04_5_Sending_Coins_with_Automated_Raw_Transactions.md), Bitcoin will calculate transaction fees for you. This is done using Floating Fees, where the `bitcoind` watches how long transactions are taking to confirm and automatically calculates for you what to spend.
|
||||||
|
|
||||||
You can help control this by putting rational values into your ~/.bitcoin/bitcoin.conf. The following low-cost values would ensure that there was a minimum transaction fee of 10,000 satoshis per kByte of data in your transaction and request that the floating fees figure out a good amount to get your transaction somewhere into the next six blocks.
|
You can help control this by putting rational values into your `~/.bitcoin/bitcoin.conf`. The following low-cost values would ensure that there was a minimum transaction fee of 10,000 satoshis per kByte of data in your transaction and request that the floating fees figure out a good amount to get your transaction somewhere into the next six blocks.
|
||||||
```
|
```
|
||||||
mintxfee=0.0001
|
mintxfee=0.0001
|
||||||
txconfirmtarget=6
|
txconfirmtarget=6
|
||||||
@ -22,15 +22,24 @@ However, under the theory that you don't want to wait around while working on a
|
|||||||
mintxfee=0.001
|
mintxfee=0.001
|
||||||
txconfirmtarget=1
|
txconfirmtarget=1
|
||||||
```
|
```
|
||||||
In order to get through this tutorial, we're willing to spend 100,00 satoshis per kB on every transaction (about $1!) and we want to get each transaction into the next block! (To put that in perspective, a typical transaction runs between .25 kB and 1 kB, so you'll actually be paying more like a a quarter than a buck.)
|
You should enter these into `~/.bitcoin/bitcoin.conf`, in the main section, toward the top of the file or if you want to be sure you never use it elsewhere, under the `[test]` section.
|
||||||
|
|
||||||
|
In order to get through this tutorial, we're willing to spend 100,00 satoshis per kB on every transaction (about $10!), and we want to get each transaction into the next block! (To put that in perspective, a typical transaction runs between .25 kB and 1 kB, so you'll actually be paying more like $2.50 than $10 ... if this were real money.)
|
||||||
|
|
||||||
After you've edited your bitcoin.conf file, you'll want to kill and restart bitcoind.
|
After you've edited your bitcoin.conf file, you'll want to kill and restart bitcoind.
|
||||||
|
```
|
||||||
|
$ ps auxww | grep -i bitcoind
|
||||||
|
standup 455 1.3 38.4 3387012 1555520 ? SLsl Jun16 60:01 /usr/local/bin/bitcoind -conf=/home/standup/.bitcoin/bitcoin.conf
|
||||||
|
standup 21073 0.0 0.0 6076 876 pts/0 R+ 15:00 0:00 grep -i bitcoind
|
||||||
|
$ kill 455
|
||||||
|
$ /usr/local/bin/bitcoind -conf=/home/standup/.bitcoin/bitcoin.conf &
|
||||||
|
```
|
||||||
|
|
||||||
## Get an Address
|
## Get an Address
|
||||||
|
|
||||||
You need somewhere to send your coins to. Usually, someone would send you an address, and perhaps give you a signature to prove they own that address. Alternatively, they might give you a QR code to scan, so that you can't make mistakes when typing in the address. In our case, we're going to send coins to `n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi`, which is a return address for TP's TestNet faucet.
|
You need somewhere to send your coins to. Usually, someone would send you an address, and perhaps give you a signature to prove they own that address. Alternatively, they might give you a QR code to scan, so that you can't make mistakes when typing in the address. In our case, we're going to send coins to `n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi`, which is a return address for an old Tesetnet faucet.
|
||||||
|
|
||||||
_What is a QR code?_ A QR code is just an encoding of a Bitcoin address. Many wallets will generate QR codes for you, while some sites will convert from an address to a QR code. Obviously, you should only accept a QR code from a site that you absolutely trust. A payer can use a bar-code scanner to read in the QR code, then pay to it.
|
> :book: ***What is a QR code?*** A QR code is just an encoding of a Bitcoin address. Many wallets will generate QR codes for you, while some sites will convert from an address to a QR code. Obviously, you should only accept a QR code from a site that you absolutely trust. A payer can use a bar-code scanner to read in the QR code, then pay to it.
|
||||||
|
|
||||||
## Send the Coins
|
## Send the Coins
|
||||||
|
|
||||||
@ -38,7 +47,7 @@ You're now ready to send some coins. This is actually quite simple via the comma
|
|||||||
```
|
```
|
||||||
$ txid=$(bitcoin-cli sendtoaddress n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi 0.1)
|
$ txid=$(bitcoin-cli sendtoaddress n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi 0.1)
|
||||||
$ echo $txid
|
$ echo $txid
|
||||||
586b3ff591d43948ed4107216be52d831c551747b469626a6b7c84bbf1639f76
|
93250d0cacb0361b8e21030ac65bc4c2159a53de1075425d800b2d7a8ab13ba8
|
||||||
```
|
```
|
||||||
Make sure the address you write in is where you want the money to go. Make _double_ sure. If you make mistakes in Bitcoin, there's no going back.
|
Make sure the address you write in is where you want the money to go. Make _double_ sure. If you make mistakes in Bitcoin, there's no going back.
|
||||||
|
|
||||||
@ -50,45 +59,43 @@ You'll receive a txid back when you issue this command.
|
|||||||
|
|
||||||
You can look at your transaction using your transaction id:
|
You can look at your transaction using your transaction id:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli gettransaction $txid
|
|
||||||
{
|
{
|
||||||
"amount": -0.10000000,
|
"amount": -0.00100000,
|
||||||
"fee": -0.00022600,
|
"fee": -0.00022200,
|
||||||
"confirmations": 0,
|
"confirmations": 0,
|
||||||
"trusted": false,
|
"trusted": true,
|
||||||
"txid": "586b3ff591d43948ed4107216be52d831c551747b469626a6b7c84bbf1639f76",
|
"txid": "93250d0cacb0361b8e21030ac65bc4c2159a53de1075425d800b2d7a8ab13ba8",
|
||||||
"walletconflicts": [
|
"walletconflicts": [
|
||||||
],
|
],
|
||||||
"time": 1490742233,
|
"time": 1592604194,
|
||||||
"timereceived": 1490742233,
|
"timereceived": 1592604194,
|
||||||
"bip125-replaceable": "unknown",
|
"bip125-replaceable": "no",
|
||||||
"details": [
|
"details": [
|
||||||
{
|
{
|
||||||
"account": "",
|
|
||||||
"address": "n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi",
|
"address": "n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi",
|
||||||
"category": "send",
|
"category": "send",
|
||||||
"amount": -0.10000000,
|
"amount": -0.00100000,
|
||||||
"vout": 0,
|
"vout": 1,
|
||||||
"fee": -0.00022600,
|
"fee": -0.00022200,
|
||||||
"abandoned": false
|
"abandoned": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hex": "0200000001a8b61dba544525ad267644cb78f07c1ba58586ff9089aec3ac24d8764dc21dfb000000006a47304402204c38c2530d3283200e4fd3b2d22e609fc6dc941fd3ac4bc8b73ad5a86607e723022050056ae6cfc3233fb38459a6fd5e63d54e4c85e17b91d66fb915e3977a1c77dd0121027a313901f2ac34c87761513cabe69ca9ca61e2db3c7e6f89d7eccd7fc0a5917cfeffffff0280969800000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac4082820b000000001976a914a091d978794d50e5caa3e5454cc8633240640d6688aca6de1000"
|
"hex": "0200000001e982921bb0189afc486e20bb05cc5825c71a0ba8868043ed04ece9ab0cb12a8e010000006a47304402200fc493a01c5c9d9574f7c321cee6880f7f1df847be71039e2d996f7f75c17b3d02203057f5baa48745ba7ab5f1d4eed11585bd8beab838b1ca03a4138516fe52b3b8012102fd5740996d853ea51a6904cf03257fc11204b0179f344c49739ec5b20b39c9bafeffffff02e8640d0000000000160014d37b6ae4a917bcc873f6395741155f565e2dc7c4a0860100000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac780b1b00"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
You can see not only the amount transferred (.1 BTC) but also a transaction fee (.000226 BTC), which is about a quarter of the .001 BTC/kB minimum fee that was set, which suggests that the transaction was about a quarter of a kB in size.
|
You can see not only the amount transferred (.001 BTC) but also a transaction fee (.000222 BTC), which is about a quarter of the .001 BTC/kB minimum fee that was set, which suggests that the transaction was about a quarter of a kB in size.
|
||||||
|
|
||||||
While you are waiting for this transaction to clear, you'll note that `bitcoin-cli getbalance` shows that all of your money is gone (or, at least, all of your money from a single incoming transaction). Similarly, `bitcoin-cli listunspent` would show an appropriately sized blob of incoming money was gone. There's a reason for this: whenever you get money in, you have to send it _all_ out together, and you have to perform some gymnastics if you actually want to keep some of it! Once again, `sendtoaddress` takes care of this all for you, which means you don't have to worry about it in full until you use a raw transaction to send out money.
|
While you are waiting for this transaction to clear, you'll note that `bitcoin-cli getbalance` shows that all of your money is gone (or, at least, all of your money from a single incoming transaction). Similarly, `bitcoin-cli listunspent` will show that an entire transaction is gone, even if it was more than what you wanted to send. There's a reason for this: whenever you get money in, you have to send it _all_ out together, and you have to perform some gymnastics if you actually want to keep some of it! Once again, `sendtoaddress` takes care of this all for you, which means you don't have to worry about making change until you send a raw transaction. In this case, a new transaction will appear with your change when your spend is incorporated into a block.
|
||||||
|
|
||||||
## Summary: Sending Coins the Easy Way
|
## Summary: Sending Coins the Easy Way
|
||||||
|
|
||||||
To send coins the easy way, make sure your transaction defaults are rationale, get an address, and send coins there. That's why they call it easy!
|
To send coins the easy way, make sure your transaction defaults are rationale, get an address, and send coins there. That's why they call it easy!
|
||||||
|
|
||||||
### Why Use The Easy Way?
|
> :fire: ***What is the power of sending coins the easy way?***
|
||||||
|
|
||||||
_The advantages._ It's easy. You don't have to worry about arcane things like UTXOs. You don't have to calculate transaction fees by hand, so you're not likely to make mistakes that cost you large amounts of money. If your sole goal is to sit down at your computer and send some money, this is the way to go.
|
> _The advantages._ It's easy. You don't have to worry about arcane things like UTXOs. You don't have to calculate transaction fees by hand, so you're not likely to make mistakes that cost you large amounts of money. If your sole goal is to sit down at your computer and send some money, this is the way to go.
|
||||||
|
|
||||||
_The disadvantages._ It's high level. You have very little control over what's happening, and you can't do anything fancy. If you're planning to write more complex Bitcoin software or want a deeper understanding of how Bitcoin works, then the easy way is just a dull diversion before you get to the real stuff.
|
> _The disadvantages._ It's high level. You have very little control over what's happening, and you can't do anything fancy. If you're planning to write more complex Bitcoin software or want a deeper understanding of how Bitcoin works, then the easy way is just a dull diversion before you get to the real stuff.
|
||||||
|
|
||||||
## What's Next?
|
## What's Next?
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ You're now ready to create Bitcoin raw transactions. This allows you to send mon
|
|||||||
|
|
||||||
Before you dive into actually creating raw transactions, you should make sure you understand how a Bitcoin transaction works. It's all about the UTXOs.
|
Before you dive into actually creating raw transactions, you should make sure you understand how a Bitcoin transaction works. It's all about the UTXOs.
|
||||||
|
|
||||||
_What is a UTXO?_ When you receive cash in your Bitcoin wallet, it appears as an individual transaction. Each of these transactions is called a Unspent Transaction Output (UTXO). It doesn't matter if various payments were made to the same address or to multiple addresses: each incoming transaction remains distinct in your wallet as a UTXO.
|
> :book: ***What is a UTXO?*** When you receive cash in your Bitcoin wallet, it appears as an individual transaction. Each of these transactions is called a Unspent Transaction Output (UTXO). It doesn't matter if various payments were made to the same address or to multiple addresses: each incoming transaction remains distinct in your wallet as a UTXO.
|
||||||
|
|
||||||
When you create a new outgoing transaction, you gather together one or more UTXOs, each of which represents a blob of money that you received. You use these as inputs for a new transaction. Together their amount must equal what you want to spend _or more_. Then, you generate one or more outputs, which give the money represented by the inputs to one or more people. This creates new UTXOs for the recipients, which may then use _those_ to fund future transactions.
|
When you create a new outgoing transaction, you gather together one or more UTXOs, each of which represents a blob of money that you received. You use these as inputs for a new transaction. Together their amount must equal what you want to spend _or more_. Then, you generate one or more outputs, which give the money represented by the inputs to one or more people. This creates new UTXOs for the recipients, which may then use _those_ to fund future transactions.
|
||||||
|
|
||||||
@ -21,43 +21,50 @@ In order to create a new raw transaction, you must know what UTXOs you have on-h
|
|||||||
$ bitcoin-cli listunspent
|
$ bitcoin-cli listunspent
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"txid": "ee9805676271f6244eba94c3d1a48b303a8f8359bf711c630eb6f2ea339d0e72",
|
"txid": "ca4898d8f950df03d6bfaa00578bd0305d041d24788b630d0c4a32debcac9f36",
|
||||||
"vout": 0,
|
"vout": 0,
|
||||||
"address": "mrS1ypy2pCweh2nBpkMD7r2T3Zj344wxaY",
|
"address": "mi25UrzHnvn3bpEfFCNqJhPWJn5b77a5NE",
|
||||||
"account": "",
|
"label": "",
|
||||||
"scriptPubKey": "76a91477ba616a2778b05a5fd73c7449964050fd1a6fd288ac",
|
"scriptPubKey": "76a9141b72503639a13f190bf79acf6d76255d772360b788ac",
|
||||||
"amount": 0.08000000,
|
"amount": 0.00010000,
|
||||||
"confirmations": 2,
|
"confirmations": 20,
|
||||||
"spendable": true,
|
"spendable": true,
|
||||||
"solvable": true
|
"solvable": true,
|
||||||
|
"desc": "pkh([d6043800/0'/0'/1']02fd5740996d853ea51a6904cf03257fc11204b0179f344c49739ec5b20b39c9ba)#62rud39c",
|
||||||
|
"safe": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"txid": "c1abb6951e6a9aae7e384412b69b69e59c10daac9397d01d0c52b7bc6278d589",
|
"txid": "61f3b7016bf1ecc3987b8805207e79362e4de8026682e149107999b779426e3a",
|
||||||
"vout": 1,
|
"vout": 1,
|
||||||
"address": "mygxipnJUsBgFvscKAaoxDdE8aCmHhRfTZ",
|
"address": "mjehC2KHzXcBDcwTd4LhZ2GzyzrZ3Kd3ff",
|
||||||
"account": "",
|
"label": "",
|
||||||
"scriptPubKey": "76a914c756c7bd67bf83d83c04e3dc6fd1ff0c6fe8ea9888ac",
|
"scriptPubKey": "76a9142d573900aa357a38afd741fbf24b075d263ea6e088ac",
|
||||||
"amount": 0.07800000,
|
"amount": 0.00050000,
|
||||||
"confirmations": 1,
|
"confirmations": 3,
|
||||||
"spendable": true,
|
"spendable": true,
|
||||||
"solvable": true
|
"solvable": true,
|
||||||
|
"desc": "pkh([d6043800/0'/0'/3']0278608b54b8fb0d8379d3823d31f03a7c6ab0adffb07dd3811819fdfc34f8c132)#nhjc3f8y",
|
||||||
|
"safe": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"txid": "ab7ca727055b812df882298f4e6e10ec699fb6250d843c813623171781f896d8",
|
"txid": "91261eafae15ea53dedbea7c1db748c52bbc04a85859ffd0d839bda1421fda4c",
|
||||||
"vout": 0,
|
"vout": 0,
|
||||||
"address": "mygxipnJUsBgFvscKAaoxDdE8aCmHhRfTZ",
|
"address": "mjehC2KHzXcBDcwTd4LhZ2GzyzrZ3Kd3ff",
|
||||||
"account": "",
|
"label": "",
|
||||||
"scriptPubKey": "76a914c756c7bd67bf83d83c04e3dc6fd1ff0c6fe8ea9888ac",
|
"scriptPubKey": "76a9142d573900aa357a38afd741fbf24b075d263ea6e088ac",
|
||||||
"amount": 0.07800000,
|
"amount": 0.00022000,
|
||||||
"confirmations": 1,
|
"confirmations": 3,
|
||||||
"spendable": true,
|
"spendable": true,
|
||||||
"solvable": true
|
"solvable": true,
|
||||||
|
"desc": "pkh([d6043800/0'/0'/3']0278608b54b8fb0d8379d3823d31f03a7c6ab0adffb07dd3811819fdfc34f8c132)#nhjc3f8y",
|
||||||
|
"safe": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
|
||||||
This listing shows three different UTXOs, worth .08, .078, and .078 BTC. Note that each has its own distinct txid and remains distinct in the wallet, even though two of them were sent to the same address. (Bad practice! But used as an example here to show that transaction stay distinct, no matter what!)
|
|
||||||
|
|
||||||
When you want to spend a UTXO, it's not sufficient to just know the transaction id. That's because each transaction can have multiple outputs! Remember that first chunk of money that the faucet sent us? In the transaction, some money went to us and some went to someone else. The `txid` refers to the overall transaction, while a `vout` says which of multiple outputs you've received. In this list, two of our UTXOs are the 0th vout of a transaction, and the other is the 1st. This makes a difference!
|
```
|
||||||
|
This listing shows three different UTXOs, worth .0001, .0005 and .00022 BTC. Note that each has its own distinct txid and remains distinct in the wallet, even the last two, which were sent to the same address.
|
||||||
|
|
||||||
|
When you want to spend a UTXO, it's not sufficient to just know the transaction id. That's because each transaction can have multiple outputs! Remember that first chunk of money that the faucet sent us? In the transaction, some money went to us and some went to someone else. The `txid` refers to the overall transaction, while a `vout` says which of multiple outputs you've received. In this list, each of these transactions is the 0th `vout` of a previous transaction, but _that doesn't have to be the case_.
|
||||||
|
|
||||||
So, txid+vout=UTXO. This will be the foundation of any raw transaction.
|
So, txid+vout=UTXO. This will be the foundation of any raw transaction.
|
||||||
|
|
||||||
@ -65,20 +72,18 @@ So, txid+vout=UTXO. This will be the foundation of any raw transaction.
|
|||||||
|
|
||||||
You're now ready to write a simple, example raw transaction that shows how to send the entirety of a UTXO to another party. As noted, this is not necessarily a very realistic real-world case.
|
You're now ready to write a simple, example raw transaction that shows how to send the entirety of a UTXO to another party. As noted, this is not necessarily a very realistic real-world case.
|
||||||
|
|
||||||
> :warning: **WARNING:** It is very easy to lose money with a raw transaction. Consider all instructions on sending bitcoins via raw transactions to be _very_, _very_ dangerous. Whenever you're actually sending real money to other people, you should instead use one of the other methods explained in this chapter. Creating raw transactions is extremely useful if you're writing bitcoin programs, but _only_ when you're writing bitcoin programs.
|
> :warning: **WARNING:** It is very easy to lose money with a raw transaction. Consider all instructions on sending bitcoins via raw transactions to be _very_, _very_ dangerous. Whenever you're actually sending real money to other people, you should instead use one of the other methods explained in this chapter. Creating raw transactions is extremely useful if you're writing bitcoin programs, but _only_ when you're writing bitcoin programs. (For example: in writing this example for one version of this tutorial, we accidently spend the wrong transaction, even though it had about 10x as much value. Almost all of that was lost to the miners.)
|
||||||
|
|
||||||
### Prepare the Raw Transaction
|
### Prepare the Raw Transaction
|
||||||
|
|
||||||
For best practices, we'll start out each transaction by carefully recording the txids and vouts that we'll be spending.
|
For best practices, we'll start out each transaction by carefully recording the txids and vouts that we'll be spending.
|
||||||
|
|
||||||
In this case, we're going to spend the oldest transaction, worth .08 BTC, because that's the one that's been validated the most:
|
In this case, we're going to spend the one worth .00050000 BTC because it's the only one with a decent value.
|
||||||
```
|
```
|
||||||
$ utxo_txid="ee9805676271f6244eba94c3d1a48b303a8f8359bf711c630eb6f2ea339d0e72"
|
$ utxo_txid="61f3b7016bf1ecc3987b8805207e79362e4de8026682e149107999b779426e3a"
|
||||||
$ utxo_vout="0"
|
$ utxo_vout="1"
|
||||||
```
|
```
|
||||||
|
|
||||||
> :link: **TESTNET vs MAINNET:** Obviously the "validated-the-most" criteria would matter a lot more on mainnet, where real money is being used.
|
|
||||||
|
|
||||||
You should similarly record your recipient address, to make sure you have it right. We're again sending some money back to the TP faucet:
|
You should similarly record your recipient address, to make sure you have it right. We're again sending some money back to the TP faucet:
|
||||||
```
|
```
|
||||||
$ recipient="n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi"
|
$ recipient="n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi"
|
||||||
@ -86,13 +91,27 @@ $ recipient="n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi"
|
|||||||
As always, check your variables carefully, to make sure they're what you expect!
|
As always, check your variables carefully, to make sure they're what you expect!
|
||||||
```
|
```
|
||||||
$ echo $utxo_txid
|
$ echo $utxo_txid
|
||||||
ee9805676271f6244eba94c3d1a48b303a8f8359bf711c630eb6f2ea339d0e72
|
61f3b7016bf1ecc3987b8805207e79362e4de8026682e149107999b779426e3a
|
||||||
$ echo $utxo_vout
|
$ echo $utxo_vout
|
||||||
0
|
1
|
||||||
$ echo $recipient
|
$ echo $recipient
|
||||||
n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi
|
n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi
|
||||||
```
|
```
|
||||||
That recipient is particularly important, because if you mess it up, your money is _gone_! So triple check it.
|
That recipient is particularly important, because if you mess it up, your money is _gone_! (And as we already saw, choosing the wrong transaction can result in lost money!) So triple check it all.
|
||||||
|
|
||||||
|
### Understand the Transaction Fee
|
||||||
|
|
||||||
|
Each transaction has a fee associated with. It's _implicit_ when you send a raw transaction: the amount that you will pay as a fee is always equal to the amount of your input minus the amount of your output. So, you have to decrease your output a little bit from your input to make sure that your transaction goes out.
|
||||||
|
|
||||||
|
> :warning: **WARNING:** This is the very dangerous part of raw transactions!! Because you automatically expend all of the amount in the UTXOs that you use, it's critically important to make sure that you know: (1) precisely what UTXOs you're using; (2) exactly how much money they contain; (3) exactly how much money you're sending out; and (4) what the difference is. If you mess up and you use the wrong UTXO (with more money than you thought) or if you send out too little money, the excess is lost. Forever. Don't make that mistake! Know your inputs and outputs _precisely_. Or better, don't use raw transactions except as part of a carefully considered and triple-checked program.
|
||||||
|
|
||||||
|
> :book: ***How much should you spend on transaction fees?*** [Bitcoin Fees](https://bitcoinfees.21.co/) has a nice live assessment. It says that the "fastest and cheapest transaction fee is currently 42 satoshis/byte" and that "For the median transaction size of 224 bytes, this results in a fee of 9,408 satoshis".
|
||||||
|
|
||||||
|
Currently Bitcoin Fees suggests a transaction fee of about 10,000 satoshis, which is the same as .0001 BC. Yes, that's for the mainnet, not the testnet, but we want to test out things realistically, so that's what we're going to use.
|
||||||
|
|
||||||
|
In this case, that means taking the .0005 BTC in the UTXO we're selected, reducing it by .0001 BTC for the transaction fee, and sending the remaining .0004 BTC. (And this is an example of why micropayments don't work on the Bitcoin network, because a $1 or so transaction fee is pretty expensive when you're sending $4, let alone if you were trying to make a micropayment of $0.50. But that's always why we have Lightning.)
|
||||||
|
|
||||||
|
> :warning: **WARNING:** The lower that you set your transaction fee, the longer before your transaction is built into a block. The Bitcoin Fees sites lists expected times, from an expected 0 blocks, to 22. Since blocks are built on average every 10 minutes, that's the difference between a few minutes and a few hours! So, choose a transaction fee that's appropriate for what you're sending. Note that you should never drop below the minimum relay fee, which is .0001 BTC.
|
||||||
|
|
||||||
### Write the Raw Transaction
|
### Write the Raw Transaction
|
||||||
|
|
||||||
@ -111,43 +130,32 @@ $ bitcoin-cli createrawtransaction
|
|||||||
"'$your_recipient'": bitcoin_amount
|
"'$your_recipient'": bitcoin_amount
|
||||||
}'''
|
}'''
|
||||||
```
|
```
|
||||||
Yeah, there are all kinds of crazy quotes there, but trust that they'll do the right thing. Use `'''` to mark the start and end of the JSON array and the JSON object. Protect normal words like `"this"` and normal numbers like `0`. If they're variables, insert single quotes, like `"'$this_word'"` and `'$this_num'`. (Whew. You'll get used to it.)
|
Yeah, there are all kinds of crazy quotes there, but trust that they'll do the right thing. Use `'''` to mark the start and end of the JSON array and the JSON object. Protect normal words like `"this"`, but you don't need to protect normal numbers: `0`. If they're variables, insert single quotes, like `"'$this_word'"` and `'$this_num'`. (Whew. You'll get used to it.)
|
||||||
|
|
||||||
Here's a command that creates a raw transaction to send your $utxo to your $recipient
|
Here's a command that creates a raw transaction to send your $utxo to your $recipient
|
||||||
```
|
```
|
||||||
$ rawtxhex=$(bitcoin-cli createrawtransaction '''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]''' '''{ "'$recipient'": 0.0795 }''')
|
$ rawtxhex=$(bitcoin-cli createrawtransaction '''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]''' '''{ "'$recipient'": 0.0004 }''')
|
||||||
$ echo $rawtxhex
|
$ echo $rawtxhex
|
||||||
0100000001720e9d33eaf2b60e631c71bf59838f3a308ba4d1c394ba4e24f67162670598ee0000000000ffffffff01b04e7900000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac00000000
|
02000000013a6e4279b799791049e1826602e84d2e36797e2005887b98c3ecf16b01b7f3610100000000ffffffff01409c0000000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac00000000
|
||||||
```
|
```
|
||||||
|
|
||||||
### Understand the Transaction Fee
|
|
||||||
|
|
||||||
You'll note that we didn't send the whole .08 BTC to our recipient. That's because of the transaction fee, which is _implicit_ when you send a raw transaction: the amount that you will pay as a fee is always equal to the amount of your input minus the amount of your output. So, you have to decrease your output a little bit from your input to make sure that your transaction goes out.
|
|
||||||
|
|
||||||
> :warning: **WARNING:** This is the very dangerous part of raw transactions!! Because you automatically expend all of the amount in the UTXOs that you use, it's critically important to make sure that you know: (1) precisely what UTXOs you're using; (2) exactly how much money they contain; (3) exactly how much money you're sending out; and (4) what the difference is. If you mess up and you use the wrong UTXO (with more money than you thought) or if you send out too little money, the excess is lost. Forever. Don't make that mistake! Know your inputs and outputs _precisely_. Or better, don't use raw transactions except as part of a carefully considered and triple-checked program.
|
|
||||||
|
|
||||||
_How much should you spend on transaction fees?_ [Bitcoin Fees](https://bitcoinfees.21.co/) has a nice live assessment. It says that the "fastest and cheapest transaction fee is currently 220 satoshis/byte" and that "For the median transaction size of 226 bytes, this results in a fee of 49,720 satoshis".
|
|
||||||
|
|
||||||
That basic info is what we used to construct our raw transaction. We just subtracted 50,000 satoshis, which is .0005 BTC, from the amount we were sending: .0800 BTC - .0005 BC= .0795, which is what we sent. (Often transactions don't need to be the "fastest" and can get away with much lower transaction fees; we opted not to because we don't want to delay working through this tutorial.)
|
|
||||||
|
|
||||||
> :warning: **WARNING:** The lower that you set your transaction fee, the longer before your transaction is built into a block. The Bitcoin Fees sites lists expected times, from an expected 0 blocks, to 22. Since blocks are built on average every 10 minutes, that's the difference between a few minutes and a few hours! So, choose a transaction fee that's appropriate for what you're sending. Note that you should never drop below the minimum relay fee, which is .0001 BTC.
|
|
||||||
|
|
||||||
### Verify Your Raw Transaction
|
### Verify Your Raw Transaction
|
||||||
|
|
||||||
You should next verify your rawtransaction with `decoderawtransaction` to make sure that it will do the right thing.
|
You should next verify your rawtransaction with `decoderawtransaction` to make sure that it will do the right thing.
|
||||||
```
|
```
|
||||||
$ bitcoin-cli decoderawtransaction $rawtxhex
|
$ bitcoin-cli decoderawtransaction $rawtxhex
|
||||||
{
|
{
|
||||||
"txid": "6d39d612fe382eaab9897d3ea4d5e44233d8acef40e63fe0f06df785fd7a0c45",
|
"txid": "dcd2d8f0ec5581b806a1fbe00325e1680c4da67033761b478a26895380cc1298",
|
||||||
"hash": "6d39d612fe382eaab9897d3ea4d5e44233d8acef40e63fe0f06df785fd7a0c45",
|
"hash": "dcd2d8f0ec5581b806a1fbe00325e1680c4da67033761b478a26895380cc1298",
|
||||||
|
"version": 2,
|
||||||
"size": 85,
|
"size": 85,
|
||||||
"vsize": 85,
|
"vsize": 85,
|
||||||
"version": 1,
|
"weight": 340,
|
||||||
"locktime": 0,
|
"locktime": 0,
|
||||||
"vin": [
|
"vin": [
|
||||||
{
|
{
|
||||||
"txid": "ee9805676271f6244eba94c3d1a48b303a8f8359bf711c630eb6f2ea339d0e72",
|
"txid": "61f3b7016bf1ecc3987b8805207e79362e4de8026682e149107999b779426e3a",
|
||||||
"vout": 0,
|
"vout": 1,
|
||||||
"scriptSig": {
|
"scriptSig": {
|
||||||
"asm": "",
|
"asm": "",
|
||||||
"hex": ""
|
"hex": ""
|
||||||
@ -157,7 +165,7 @@ $ bitcoin-cli decoderawtransaction $rawtxhex
|
|||||||
],
|
],
|
||||||
"vout": [
|
"vout": [
|
||||||
{
|
{
|
||||||
"value": 0.07950000,
|
"value": 0.00040000,
|
||||||
"n": 0,
|
"n": 0,
|
||||||
"scriptPubKey": {
|
"scriptPubKey": {
|
||||||
"asm": "OP_DUP OP_HASH160 e7c1345fc8f87c68170b3aa798a956c2fe6a9eff OP_EQUALVERIFY OP_CHECKSIG",
|
"asm": "OP_DUP OP_HASH160 e7c1345fc8f87c68170b3aa798a956c2fe6a9eff OP_EQUALVERIFY OP_CHECKSIG",
|
||||||
@ -172,7 +180,8 @@ $ bitcoin-cli decoderawtransaction $rawtxhex
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Check the `vin`. Are you spending the right transaction? Does it contain the expected amount of money? Check your vout. Are you sending the right amount? Is it going to the right address? Finally, do the math to make sure the money balances. Does the value of the UTXO minus the amount being spent equal the expected transaction fee?
|
|
||||||
|
Check the `vin`. Are you spending the right transaction? Does it contain the expected amount of money? (Check with `bitcoin-cli gettransaction` and be sure to look at the right `vout`.) Check your `vout`. Are you sending the right amount? Is it going to the right address? Finally, do the math to make sure the money balances. Does the value of the UTXO minus the amount being spent equal the expected transaction fee?
|
||||||
|
|
||||||
> :information_source: **NOTE - SEQUENCE:** You may note that each input has a sequence number, set here to 4294967295, which is 0xFFFFFFFF. This is the last frontier of Bitcoin transactions, because it's a standard field in transactions that was originally intended for a specific purpose, but was never fully implemented. So now there's this integer sitting around in transactions that could be repurposed for other uses. And, in fact, it has been. As of this writing there are three different uses for the variable that's called `nSequence` in the Bitcoin Core code: it enables RBF, `nLockTime`, and relative timelocks. If there's nothing weird going on, `nSequence` will be set to 4294967295. Setting it to a lower value signals that special stuff is going on.
|
> :information_source: **NOTE - SEQUENCE:** You may note that each input has a sequence number, set here to 4294967295, which is 0xFFFFFFFF. This is the last frontier of Bitcoin transactions, because it's a standard field in transactions that was originally intended for a specific purpose, but was never fully implemented. So now there's this integer sitting around in transactions that could be repurposed for other uses. And, in fact, it has been. As of this writing there are three different uses for the variable that's called `nSequence` in the Bitcoin Core code: it enables RBF, `nLockTime`, and relative timelocks. If there's nothing weird going on, `nSequence` will be set to 4294967295. Setting it to a lower value signals that special stuff is going on.
|
||||||
|
|
||||||
@ -185,10 +194,10 @@ First, you need to sign your raw transaction:
|
|||||||
|
|
||||||
$ bitcoin-cli signrawtransactionwithwallet $rawtxhex
|
$ bitcoin-cli signrawtransactionwithwallet $rawtxhex
|
||||||
{
|
{
|
||||||
"hex": "0100000001720e9d33eaf2b60e631c71bf59838f3a308ba4d1c394ba4e24f67162670598ee000000006b483045022100d8f17dadc2501596f75f2c90b8279130e588638d4f7a4f7d5ebb10fea15252f702200ceb164e81335c430893780d06cfe194c36acec26886f180408e3ac4a7d2292f0121035de6239e70523c8f392e32f98e65f6ef704c4b6b0df994e407212b839bf51048ffffffff01b04e7900000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac00000000",
|
"hex": "02000000013a6e4279b799791049e1826602e84d2e36797e2005887b98c3ecf16b01b7f361010000006a4730440220335d15a2a2ca3ce6a302ce041686739d4a38eb0599a5ea08305de71965268d05022015f77a33cf7d613015b2aba5beb03088033625505ad5d4d0624defdbea22262b01210278608b54b8fb0d8379d3823d31f03a7c6ab0adffb07dd3811819fdfc34f8c132ffffffff01409c0000000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac00000000",
|
||||||
"complete": true
|
"complete": true
|
||||||
}
|
}
|
||||||
$ signedtx="0100000001720e9d33eaf2b60e631c71bf59838f3a308ba4d1c394ba4e24f67162670598ee000000006b483045022100d8f17dadc2501596f75f2c90b8279130e588638d4f7a4f7d5ebb10fea15252f702200ceb164e81335c430893780d06cfe194c36acec26886f180408e3ac4a7d2292f0121035de6239e70523c8f392e32f98e65f6ef704c4b6b0df994e407212b839bf51048ffffffff01b04e7900000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac00000000"
|
$ signedtx="02000000013a6e4279b799791049e1826602e84d2e36797e2005887b98c3ecf16b01b7f361010000006a4730440220335d15a2a2ca3ce6a302ce041686739d4a38eb0599a5ea08305de71965268d05022015f77a33cf7d613015b2aba5beb03088033625505ad5d4d0624defdbea22262b01210278608b54b8fb0d8379d3823d31f03a7c6ab0adffb07dd3811819fdfc34f8c132ffffffff01409c0000000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac00000000"
|
||||||
```
|
```
|
||||||
Note that we captured the signed hex by hand, rather than trying to parse it out of the JSON object. A software package called "JQ" could do better, as we'll explain in an upcoming interlude.
|
Note that we captured the signed hex by hand, rather than trying to parse it out of the JSON object. A software package called "JQ" could do better, as we'll explain in an upcoming interlude.
|
||||||
|
|
||||||
@ -197,61 +206,63 @@ Note that we captured the signed hex by hand, rather than trying to parse it out
|
|||||||
You've now got a ready-to-go raw transaction, but it doesn't count until you actually put it on the network, which you do with the `sendrawtransaction` command. You'll get back a txid:
|
You've now got a ready-to-go raw transaction, but it doesn't count until you actually put it on the network, which you do with the `sendrawtransaction` command. You'll get back a txid:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli sendrawtransaction $signedtx
|
$ bitcoin-cli sendrawtransaction $signedtx
|
||||||
bf02068e3d2a99542c6a9ad07d915f1664374e911d809afe156dbacc46a5183e
|
a1fd550d1de727eccde6108c90d4ffec11ed83691e96e119d842b3f390e2f19a
|
||||||
```
|
```
|
||||||
You'll immediately see that the UTXO and its money have been removed from your account:
|
You'll immediately see that the UTXO and its money have been removed from your wallet:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli listunspent
|
$ bitcoin-cli listunspent
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"txid": "c1abb6951e6a9aae7e384412b69b69e59c10daac9397d01d0c52b7bc6278d589",
|
"txid": "ca4898d8f950df03d6bfaa00578bd0305d041d24788b630d0c4a32debcac9f36",
|
||||||
"vout": 1,
|
"vout": 0,
|
||||||
"address": "mygxipnJUsBgFvscKAaoxDdE8aCmHhRfTZ",
|
"address": "mi25UrzHnvn3bpEfFCNqJhPWJn5b77a5NE",
|
||||||
"account": "",
|
"label": "",
|
||||||
"scriptPubKey": "76a914c756c7bd67bf83d83c04e3dc6fd1ff0c6fe8ea9888ac",
|
"scriptPubKey": "76a9141b72503639a13f190bf79acf6d76255d772360b788ac",
|
||||||
"amount": 0.07800000,
|
"amount": 0.00010000,
|
||||||
"confirmations": 12,
|
"confirmations": 23,
|
||||||
"spendable": true,
|
"spendable": true,
|
||||||
"solvable": true
|
"solvable": true,
|
||||||
|
"desc": "pkh([d6043800/0'/0'/1']02fd5740996d853ea51a6904cf03257fc11204b0179f344c49739ec5b20b39c9ba)#62rud39c",
|
||||||
|
"safe": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"txid": "ab7ca727055b812df882298f4e6e10ec699fb6250d843c813623171781f896d8",
|
"txid": "91261eafae15ea53dedbea7c1db748c52bbc04a85859ffd0d839bda1421fda4c",
|
||||||
"vout": 0,
|
"vout": 0,
|
||||||
"address": "mygxipnJUsBgFvscKAaoxDdE8aCmHhRfTZ",
|
"address": "mjehC2KHzXcBDcwTd4LhZ2GzyzrZ3Kd3ff",
|
||||||
"account": "",
|
"label": "",
|
||||||
"scriptPubKey": "76a914c756c7bd67bf83d83c04e3dc6fd1ff0c6fe8ea9888ac",
|
"scriptPubKey": "76a9142d573900aa357a38afd741fbf24b075d263ea6e088ac",
|
||||||
"amount": 0.07800000,
|
"amount": 0.00022000,
|
||||||
"confirmations": 12,
|
"confirmations": 6,
|
||||||
"spendable": true,
|
"spendable": true,
|
||||||
"solvable": true
|
"solvable": true,
|
||||||
|
"desc": "pkh([d6043800/0'/0'/3']0278608b54b8fb0d8379d3823d31f03a7c6ab0adffb07dd3811819fdfc34f8c132)#nhjc3f8y",
|
||||||
|
"safe": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
$ bitcoin-cli getbalance
|
$ bitcoin-cli getbalance
|
||||||
0.15600000
|
0.00032000
|
||||||
```
|
```
|
||||||
Soon `listtransactions` should show a confirmed transaction of category 'send".
|
Soon `listtransactions` should show a confirmed transaction of category 'send".
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
"account": "",
|
|
||||||
"address": "n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi",
|
"address": "n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi",
|
||||||
"category": "send",
|
"category": "send",
|
||||||
"amount": -0.07950000,
|
"amount": -0.00040000,
|
||||||
"vout": 0,
|
"vout": 0,
|
||||||
"fee": -0.00050000,
|
"fee": -0.00010000,
|
||||||
"confirmations": 1,
|
"confirmations": 1,
|
||||||
"blockhash": "0000000000000dd6f6f455be7eecaf8055bb61d5d18d142d75bcdf8aa6d81456",
|
"trusted": true,
|
||||||
"blockindex": 3,
|
"txid": "a1fd550d1de727eccde6108c90d4ffec11ed83691e96e119d842b3f390e2f19a",
|
||||||
"blocktime": 1488410944,
|
|
||||||
"txid": "bf02068e3d2a99542c6a9ad07d915f1664374e911d809afe156dbacc46a5183e",
|
|
||||||
"walletconflicts": [
|
"walletconflicts": [
|
||||||
],
|
],
|
||||||
"time": 1488410872,
|
"time": 1592608574,
|
||||||
"timereceived": 1488410872,
|
"timereceived": 1592608574,
|
||||||
"bip125-replaceable": "no",
|
"bip125-replaceable": "no",
|
||||||
"abandoned": false
|
"abandoned": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
You can see that it matches the txid and the recipient address. Not only does it show the amount sent, but it also shows the transaction fee. And, it's already received a confirmation, because we offered a fee that would get it swept up into a block quickly.
|
You can see that it matches the `txid` and the `recipien` address. Not only does it show the `amount` sent, but it also shows the transaction `fee`. And, it's already received a confirmation, because we offered a fee that would get it swept up into a block quickly.
|
||||||
|
|
||||||
Congratulations! You're now a few satoshis poorer!
|
Congratulations! You're now a few satoshis poorer!
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ Once you've downloaded the binary, you can install it on your system. If you're
|
|||||||
$ mv jq-linux64 jq
|
$ mv jq-linux64 jq
|
||||||
$ sudo /usr/bin/install -m 0755 -o root -g root -t /usr/local/bin jq
|
$ sudo /usr/bin/install -m 0755 -o root -g root -t /usr/local/bin jq
|
||||||
```
|
```
|
||||||
_What is JQ?_ The repository explains it best, saying "jq is like sed for JSON data - you can use it to slice and filter and map and transform structured data with the same ease that sed, awk, grep and friends let you play with text."
|
> :book: ***What is JQ?*** The repository explains it best, saying "jq is like sed for JSON data - you can use it to slice and filter and map and transform structured data with the same ease that sed, awk, grep and friends let you play with text."
|
||||||
|
|
||||||
## Use JQ to Access a JSON Object Value by Key
|
## Use JQ to Access a JSON Object Value by Key
|
||||||
|
|
||||||
@ -21,9 +21,9 @@ _What is JQ?_ The repository explains it best, saying "jq is like sed for JSON d
|
|||||||
|
|
||||||
In the previous section, the use of `signrawtransaction` offered an example of not being able to easily capture data into variables due to the use of JSON output:
|
In the previous section, the use of `signrawtransaction` offered an example of not being able to easily capture data into variables due to the use of JSON output:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli signrawtransaction $rawtxhex
|
$ bitcoin-cli signrawtransactionwithwallet $rawtxhex
|
||||||
{
|
{
|
||||||
"hex": "0200000001735dfa1584b930a78ad2c1d6db72dd2a80ae5e5d552ad97e19f1d50d41fdd6d8000000006a47304402202210ce4b2a037da02622c380278cd79fec4e0e016e66f3eb894a2dcbb9ee998f02202cac167e6abdbbf08af139fb7c6b86e9d2e58e5516cd566ae2d54953ead9923b012102111bb978a3c93a00038ae344a1a017d7fee8a9be9d0558b5793ce6f440704a96ffffffff01b0e78604000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac00000000",
|
"hex": "02000000013a6e4279b799791049e1826602e84d2e36797e2005887b98c3ecf16b01b7f361010000006a4730440220335d15a2a2ca3ce6a302ce041686739d4a38eb0599a5ea08305de71965268d05022015f77a33cf7d613015b2aba5beb03088033625505ad5d4d0624defdbea22262b01210278608b54b8fb0d8379d3823d31f03a7c6ab0adffb07dd3811819fdfc34f8c132ffffffff01409c0000000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac00000000",
|
||||||
"complete": true
|
"complete": true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -33,14 +33,14 @@ To use JQ, run `jq` at the backend of a pipe, and always use the standard invoca
|
|||||||
|
|
||||||
To capture a specific value from a JSON object, you just list the key after the `.`:
|
To capture a specific value from a JSON object, you just list the key after the `.`:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli signrawtransaction $rawtxhex | jq -r '.hex'
|
$ bitcoin-cli signrawtransactionwithwallet $rawtxhex | jq -r '.hex'
|
||||||
0200000001735dfa1584b930a78ad2c1d6db72dd2a80ae5e5d552ad97e19f1d50d41fdd6d8000000006a47304402202210ce4b2a037da02622c380278cd79fec4e0e016e66f3eb894a2dcbb9ee998f02202cac167e6abdbbf08af139fb7c6b86e9d2e58e5516cd566ae2d54953ead9923b012102111bb978a3c93a00038ae344a1a017d7fee8a9be9d0558b5793ce6f440704a96ffffffff01b0e78604000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac00000000
|
02000000013a6e4279b799791049e1826602e84d2e36797e2005887b98c3ecf16b01b7f361010000006a4730440220335d15a2a2ca3ce6a302ce041686739d4a38eb0599a5ea08305de71965268d05022015f77a33cf7d613015b2aba5beb03088033625505ad5d4d0624defdbea22262b01210278608b54b8fb0d8379d3823d31f03a7c6ab0adffb07dd3811819fdfc34f8c132ffffffff01409c0000000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac00000000
|
||||||
```
|
```
|
||||||
With that tool in hand, you can capture information from JSON objects to command-line variables:
|
With that tool in hand, you can capture information from JSON objects to command-line variables:
|
||||||
```
|
```
|
||||||
$ signedtx=$(bitcoin-cli signrawtransaction $rawtxhex | jq -r '.hex')
|
$ signedtx=$(bitcoin-cli signrawtransactionwithwallet $rawtxhex | jq -r '.hex')
|
||||||
$ echo $signedtx
|
$ echo $signedtx
|
||||||
0200000001735dfa1584b930a78ad2c1d6db72dd2a80ae5e5d552ad97e19f1d50d41fdd6d8000000006a47304402202210ce4b2a037da02622c380278cd79fec4e0e016e66f3eb894a2dcbb9ee998f02202cac167e6abdbbf08af139fb7c6b86e9d2e58e5516cd566ae2d54953ead9923b012102111bb978a3c93a00038ae344a1a017d7fee8a9be9d0558b5793ce6f440704a96ffffffff01b0e78604000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac00000000
|
02000000013a6e4279b799791049e1826602e84d2e36797e2005887b98c3ecf16b01b7f361010000006a4730440220335d15a2a2ca3ce6a302ce041686739d4a38eb0599a5ea08305de71965268d05022015f77a33cf7d613015b2aba5beb03088033625505ad5d4d0624defdbea22262b01210278608b54b8fb0d8379d3823d31f03a7c6ab0adffb07dd3811819fdfc34f8c132ffffffff01409c0000000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac00000000
|
||||||
```
|
```
|
||||||
You can then use those variables easily and without error:
|
You can then use those variables easily and without error:
|
||||||
```
|
```
|
||||||
@ -53,38 +53,40 @@ $ bitcoin-cli sendrawtransaction $signedtx
|
|||||||
|
|
||||||
Grabbing data out of a JSON object is easy, but what if that JSON object is in a JSON array? The `listunspent` command offers a great example, because it'll usually contain a number of different transactions. What if you want to capture specific information from _one_ of them?
|
Grabbing data out of a JSON object is easy, but what if that JSON object is in a JSON array? The `listunspent` command offers a great example, because it'll usually contain a number of different transactions. What if you want to capture specific information from _one_ of them?
|
||||||
|
|
||||||
When working with a JSON array, the first thing you need to do is tell JQ which index to access. For example, you might have looked through your transactions in `listunspent` and decided that you wanted to work with the first of them. You use `'.[0]'` to access that first element. The `[]` says that we're referencing a JSON array and the `0` says we want the 0th index.
|
When working with a JSON array, the first thing you need to do is tell JQ which index to access. For example, you might have looked through your transactions in `listunspent` and decided that you wanted to work with the second of them. You use `'.[1]'` to access that first element. The `[]` says that we're referencing a JSON array and the `0` says we want the 0th index.
|
||||||
```
|
```
|
||||||
$ bitcoin-cli listunspent | jq -r '.[0]'
|
$ bitcoin-cli listunspent | jq -r '.[1]'
|
||||||
{
|
{
|
||||||
"txid": "2b5f5798359e0e23e02764588166f222d4ce056419dec83c743b72aad171d708",
|
"txid": "91261eafae15ea53dedbea7c1db748c52bbc04a85859ffd0d839bda1421fda4c",
|
||||||
"vout": 1,
|
"vout": 0,
|
||||||
"address": "mjtEqr4Fffd1XtpAkKoDkMBP54mMXJeQ3j",
|
"address": "mjehC2KHzXcBDcwTd4LhZ2GzyzrZ3Kd3ff",
|
||||||
"account": "",
|
"label": "",
|
||||||
"scriptPubKey": "76a9142fe70d51e886b9ef73b76c1743c5a2bb2894db8588ac",
|
"scriptPubKey": "76a9142d573900aa357a38afd741fbf24b075d263ea6e088ac",
|
||||||
"amount": 0.76,
|
"amount": 0.00022,
|
||||||
"confirmations": 6578,
|
"confirmations": 9,
|
||||||
"spendable": true,
|
"spendable": true,
|
||||||
"solvable": true
|
"solvable": true,
|
||||||
|
"desc": "pkh([d6043800/0'/0'/3']0278608b54b8fb0d8379d3823d31f03a7c6ab0adffb07dd3811819fdfc34f8c132)#nhjc3f8y",
|
||||||
|
"safe": true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
You can then capture an individual value from that selected array by (1) using a pipe _within_ the JQ arguments; and then (2) requesting the specific value afterward, as in the previous example. The following would capture the `txid` from the 0th JSON object in the JSON array produced by `listunspent`:
|
You can then capture an individual value from that selected array by (1) using a pipe _within_ the JQ arguments; and then (2) requesting the specific value afterward, as in the previous example. The following would capture the `txid` from the 0th JSON object in the JSON array produced by `listunspent`:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli listunspent | jq -r '.[0] | .txid'
|
$ bitcoin-cli listunspent | jq -r '.[1] | .txid'
|
||||||
2b5f5798359e0e23e02764588166f222d4ce056419dec83c743b72aad171d708
|
91261eafae15ea53dedbea7c1db748c52bbc04a85859ffd0d839bda1421fda4c
|
||||||
```
|
```
|
||||||
Carefully note how the `' 's` go around the whole JQ expression _including_ the pipe.
|
Carefully note how the `' 's` go around the whole JQ expression _including_ the pipe.
|
||||||
|
|
||||||
This method can be used to fill in variables for a UTXO that you want to use:
|
This method can be used to fill in variables for a UTXO that you want to use:
|
||||||
```
|
```
|
||||||
$ newtxid=$(bitcoin-cli listunspent | jq -r '.[0] | .txid')
|
$ newtxid=$(bitcoin-cli listunspent | jq -r '.[1] | .txid')
|
||||||
$ newvout=$(bitcoin-cli listunspent | jq -r '.[0] | .vout')
|
$ newvout=$(bitcoin-cli listunspent | jq -r '.[1] | .vout')
|
||||||
$ echo $newtxid
|
$ echo $newtxid
|
||||||
2b5f5798359e0e23e02764588166f222d4ce056419dec83c743b72aad171d708
|
91261eafae15ea53dedbea7c1db748c52bbc04a85859ffd0d839bda1421fda4c
|
||||||
$ echo $newvout
|
$ echo $newvout
|
||||||
1
|
0
|
||||||
```
|
```
|
||||||
Voila! We could now create a new raw transaction using our 0th UTXO as an input, without having to type in any of the UTXO info by hand!
|
Voila! We could now create a new raw transaction using our 1st UTXO as an input, without having to type in any of the UTXO info by hand!
|
||||||
|
|
||||||
## Use JQ to Access Matching JSON Object Values in an Array by Key
|
## Use JQ to Access Matching JSON Object Values in an Array by Key
|
||||||
|
|
||||||
@ -93,9 +95,8 @@ Voila! We could now create a new raw transaction using our 0th UTXO as an input,
|
|||||||
Instead of accessing a single, specific value in a specific JSON object, you could instead access all of a specific value across all the JSON objects. This is done with `.[]`, where no index is specified. For example, this would list all unspent funds:
|
Instead of accessing a single, specific value in a specific JSON object, you could instead access all of a specific value across all the JSON objects. This is done with `.[]`, where no index is specified. For example, this would list all unspent funds:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli listunspent | jq -r '.[] | .amount'
|
$ bitcoin-cli listunspent | jq -r '.[] | .amount'
|
||||||
0.76
|
0.0001
|
||||||
3.9
|
0.00022
|
||||||
1.95
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Use JQ for Simple Calculations by Key
|
## Use JQ for Simple Calculations by Key
|
||||||
@ -105,9 +106,9 @@ $ bitcoin-cli listunspent | jq -r '.[] | .amount'
|
|||||||
At this point, you can start using JQ output for simple math. For example, adding up the values of those unspent transactions with a simple `awk` script would give you the equivalent of `getbalance`:
|
At this point, you can start using JQ output for simple math. For example, adding up the values of those unspent transactions with a simple `awk` script would give you the equivalent of `getbalance`:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli listunspent | jq -r '.[] | .amount' | awk '{s+=$1} END {print s}'
|
$ bitcoin-cli listunspent | jq -r '.[] | .amount' | awk '{s+=$1} END {print s}'
|
||||||
6.61
|
0.00032
|
||||||
$ bitcoin-cli getbalance
|
$ bitcoin-cli getbalance
|
||||||
6.61000000
|
0.00032000
|
||||||
```
|
```
|
||||||
|
|
||||||
## Use JQ to Display Multiple JSON Object Values in an Array by Multiple Keys
|
## Use JQ to Display Multiple JSON Object Values in an Array by Multiple Keys
|
||||||
@ -119,15 +120,12 @@ JQ can easily capture individual elements from JSON objects and arrays and place
|
|||||||
For example, you might want to see a listing of all your UTXOs (`.[]`) and get a listing of all of their most important information (`.txid, .vout, .amount`):
|
For example, you might want to see a listing of all your UTXOs (`.[]`) and get a listing of all of their most important information (`.txid, .vout, .amount`):
|
||||||
```
|
```
|
||||||
$ bitcoin-cli listunspent | jq -r '.[] | .txid, .vout, .amount'
|
$ bitcoin-cli listunspent | jq -r '.[] | .txid, .vout, .amount'
|
||||||
2b5f5798359e0e23e02764588166f222d4ce056419dec83c743b72aad171d708
|
ca4898d8f950df03d6bfaa00578bd0305d041d24788b630d0c4a32debcac9f36
|
||||||
1
|
|
||||||
0.76
|
|
||||||
ec0598918f6f5476cb90365651e8a2724ef26f949290bbf196f41ed96092a52f
|
|
||||||
0
|
0
|
||||||
3.9
|
0.0001
|
||||||
3470e5fe08633583d136b9cd49bb1a224c9d9313a0b4584fd3b7438dbdf34dbd
|
91261eafae15ea53dedbea7c1db748c52bbc04a85859ffd0d839bda1421fda4c
|
||||||
0
|
0
|
||||||
1.95
|
0.00022
|
||||||
```
|
```
|
||||||
This makes it easy to decide which UTXOs to spend in a raw transaction, but it's not very pretty.
|
This makes it easy to decide which UTXOs to spend in a raw transaction, but it's not very pretty.
|
||||||
|
|
||||||
@ -137,38 +135,28 @@ The following example shows the exact same parsing of `listunspent`, but with th
|
|||||||
```
|
```
|
||||||
$ bitcoin-cli listunspent | jq -r '.[] | { txid: .txid, vout: .vout, amount: .amount }'
|
$ bitcoin-cli listunspent | jq -r '.[] | { txid: .txid, vout: .vout, amount: .amount }'
|
||||||
{
|
{
|
||||||
"txid": "2b5f5798359e0e23e02764588166f222d4ce056419dec83c743b72aad171d708",
|
"txid": "ca4898d8f950df03d6bfaa00578bd0305d041d24788b630d0c4a32debcac9f36",
|
||||||
"vout": 1,
|
"vout": 0,
|
||||||
"amount": 0.76
|
"amount": 0.0001
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
"txid": "ec0598918f6f5476cb90365651e8a2724ef26f949290bbf196f41ed96092a52f",
|
"txid": "91261eafae15ea53dedbea7c1db748c52bbc04a85859ffd0d839bda1421fda4c",
|
||||||
"vout": 0,
|
"vout": 0,
|
||||||
"amount": 3.9
|
"amount": 0.00022
|
||||||
}
|
|
||||||
{
|
|
||||||
"txid": "3470e5fe08633583d136b9cd49bb1a224c9d9313a0b4584fd3b7438dbdf34dbd",
|
|
||||||
"vout": 0,
|
|
||||||
"amount": 1.95
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
You could of course rename your new keys as you see fit. There's nothing magic in the original names:
|
You could of course rename your new keys as you see fit. There's nothing magic in the original names:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli listunspent | jq -r '.[] | { tx: .txid, output: .vout, bitcoins: .amount }'
|
$ bitcoin-cli listunspent | jq -r '.[] | { tx: .txid, output: .vout, bitcoins: .amount }'
|
||||||
{
|
{
|
||||||
"tx": "2b5f5798359e0e23e02764588166f222d4ce056419dec83c743b72aad171d708",
|
"tx": "ca4898d8f950df03d6bfaa00578bd0305d041d24788b630d0c4a32debcac9f36",
|
||||||
"output": 1,
|
"output": 0,
|
||||||
"bitcoins": 0.76
|
"bitcoins": 0.0001
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
"tx": "ec0598918f6f5476cb90365651e8a2724ef26f949290bbf196f41ed96092a52f",
|
"tx": "91261eafae15ea53dedbea7c1db748c52bbc04a85859ffd0d839bda1421fda4c",
|
||||||
"output": 0,
|
"output": 0,
|
||||||
"bitcoins": 3.9
|
"bitcoins": 0.00022
|
||||||
}
|
|
||||||
{
|
|
||||||
"tx": "3470e5fe08633583d136b9cd49bb1a224c9d9313a0b4584fd3b7438dbdf34dbd",
|
|
||||||
"output": 0,
|
|
||||||
"bitcoins": 1.95
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
## Use JQ to Access JSON Objects by Looked-Up Value
|
## Use JQ to Access JSON Objects by Looked-Up Value
|
||||||
@ -369,6 +357,11 @@ $ bitcoin-cli decoderawtransaction $rawtxhex | jq -r '.vout [] | .value' | awk
|
|||||||
```
|
```
|
||||||
To complete the transaction fee calculation, you subtract the .vout .amount (1.045) from the .vin .amount (1.3).
|
To complete the transaction fee calculation, you subtract the .vout .amount (1.045) from the .vin .amount (1.3).
|
||||||
|
|
||||||
|
To do this, you'll need to install `bc`:
|
||||||
|
```
|
||||||
|
$ sudo apt-get intall bc
|
||||||
|
```
|
||||||
|
|
||||||
Putting it all together creates a complete calculator in just five lines of script:
|
Putting it all together creates a complete calculator in just five lines of script:
|
||||||
```
|
```
|
||||||
$ usedtxid=($(bitcoin-cli decoderawtransaction $rawtxhex | jq -r '.vin | .[] | .txid'))
|
$ usedtxid=($(bitcoin-cli decoderawtransaction $rawtxhex | jq -r '.vin | .[] | .txid'))
|
||||||
|
@ -20,9 +20,10 @@ To learn what the names are for the arguments of a command, consult `bitcoin-cli
|
|||||||
|
|
||||||
For example, `bitcoin-cli help getbalance` lists these arguments:
|
For example, `bitcoin-cli help getbalance` lists these arguments:
|
||||||
|
|
||||||
1. account
|
1. dummy [used to be account]
|
||||||
2. minconf
|
2. minconf
|
||||||
3. include watchonly
|
3. include_watchonly
|
||||||
|
4. avoid_reuse
|
||||||
|
|
||||||
The following shows a traditional, unintuitive usage of `getbalance` using the minimum confirmation argument:
|
The following shows a traditional, unintuitive usage of `getbalance` using the minimum confirmation argument:
|
||||||
```
|
```
|
||||||
@ -30,7 +31,7 @@ $ bitcoin-cli getbalance "*" 1
|
|||||||
```
|
```
|
||||||
With named arguments, you can clarify what you're doing, which also minimizes mistakes:
|
With named arguments, you can clarify what you're doing, which also minimizes mistakes:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli -named getbalance account="*" minconf=1
|
$ bitcoin-cli -named getbalance minconf=1
|
||||||
```
|
```
|
||||||
|
|
||||||
## Test Out a Raw Transaction
|
## Test Out a Raw Transaction
|
||||||
@ -41,19 +42,20 @@ $ utxo_txid=$(bitcoin-cli listunspent | jq -r '.[0] | .txid')
|
|||||||
$ utxo_vout=$(bitcoin-cli listunspent | jq -r '.[0] | .vout')
|
$ utxo_vout=$(bitcoin-cli listunspent | jq -r '.[0] | .vout')
|
||||||
$ recipient="n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi"
|
$ recipient="n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi"
|
||||||
|
|
||||||
$ rawtxhex=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]''' outputs='''{ "'$recipient'": 0.7595 }''')
|
$ rawtxhex=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]''' outputs='''{ "'$recipient'": 0.00001 }''')
|
||||||
$ bitcoin-cli -named decoderawtransaction hexstring=$rawtxhex
|
$ bitcoin-cli -named decoderawtransaction hexstring=$rawtxhex
|
||||||
{
|
{
|
||||||
"txid": "f445f121085d98635f7302e641f815d1ca4ce70f0e1b03f144ad1661dc5e10e7",
|
"txid": "2b59c31bc232c0399acee4c2a381b564b6fec295c21044fbcbb899ffa56c3da5",
|
||||||
"hash": "f445f121085d98635f7302e641f815d1ca4ce70f0e1b03f144ad1661dc5e10e7",
|
"hash": "2b59c31bc232c0399acee4c2a381b564b6fec295c21044fbcbb899ffa56c3da5",
|
||||||
|
"version": 2,
|
||||||
"size": 85,
|
"size": 85,
|
||||||
"vsize": 85,
|
"vsize": 85,
|
||||||
"version": 2,
|
"weight": 340,
|
||||||
"locktime": 0,
|
"locktime": 0,
|
||||||
"vin": [
|
"vin": [
|
||||||
{
|
{
|
||||||
"txid": "2b5f5798359e0e23e02764588166f222d4ce056419dec83c743b72aad171d708",
|
"txid": "ca4898d8f950df03d6bfaa00578bd0305d041d24788b630d0c4a32debcac9f36",
|
||||||
"vout": 1,
|
"vout": 0,
|
||||||
"scriptSig": {
|
"scriptSig": {
|
||||||
"asm": "",
|
"asm": "",
|
||||||
"hex": ""
|
"hex": ""
|
||||||
@ -63,7 +65,7 @@ $ bitcoin-cli -named decoderawtransaction hexstring=$rawtxhex
|
|||||||
],
|
],
|
||||||
"vout": [
|
"vout": [
|
||||||
{
|
{
|
||||||
"value": 0.75950000,
|
"value": 0.00001000,
|
||||||
"n": 0,
|
"n": 0,
|
||||||
"scriptPubKey": {
|
"scriptPubKey": {
|
||||||
"asm": "OP_DUP OP_HASH160 e7c1345fc8f87c68170b3aa798a956c2fe6a9eff OP_EQUALVERIFY OP_CHECKSIG",
|
"asm": "OP_DUP OP_HASH160 e7c1345fc8f87c68170b3aa798a956c2fe6a9eff OP_EQUALVERIFY OP_CHECKSIG",
|
||||||
@ -80,7 +82,7 @@ $ bitcoin-cli -named decoderawtransaction hexstring=$rawtxhex
|
|||||||
|
|
||||||
$ signedtx=$(bitcoin-cli -named signrawtransactionwithwallet hexstring=$rawtxhex | jq -r '.hex')
|
$ signedtx=$(bitcoin-cli -named signrawtransactionwithwallet hexstring=$rawtxhex | jq -r '.hex')
|
||||||
$ bitcoin-cli -named sendrawtransaction hexstring=$signedtx
|
$ bitcoin-cli -named sendrawtransaction hexstring=$signedtx
|
||||||
8000dca7b1e7ab70f4056bc4512af6ffff7727d1588436521da3e5d886dbcddf
|
e70dd2aa13422d12c222481c17ca21a57071f92ff86bdcffd7eaca71772ba172
|
||||||
```
|
```
|
||||||
Voila! You've sent out another raw transaction, but this time using named arguments for clarity and to reduce errors.
|
Voila! You've sent out another raw transaction, but this time using named arguments for clarity and to reduce errors.
|
||||||
|
|
||||||
|
@ -8,15 +8,15 @@ We can now put those together and actually send funds using a raw transaction.
|
|||||||
|
|
||||||
## Create a Change Address
|
## Create a Change Address
|
||||||
|
|
||||||
Our sample raw transaction in section 4.2 was very simplistic: we sent the entirety of a UTXO to a new address. More frequently, you'll want to send someone an amount of money that doesn't match a UTXO. But, you'll recall that the excess money from a UTXO that's not sent to your recipient just becomes a transaction fee. So, how do you send someone just part of a UTXO, while keeping the rest for yourself?
|
Our sample raw transaction in section §4.2 was very simplistic: we sent the entirety of a UTXO to a new address. More frequently, you'll want to send someone an amount of money that doesn't match a UTXO. But, you'll recall that the excess money from a UTXO that's not sent to your recipient just becomes a transaction fee. So, how do you send someone just part of a UTXO, while keeping the rest for yourself?
|
||||||
|
|
||||||
The solution is to _send_ the rest of the funds to a second address, a change address that you've created in your wallet specifically to receive them:
|
The solution is to _send_ the rest of the funds to a second address, a change address that you've created in your wallet specifically to receive them:
|
||||||
```
|
```
|
||||||
$ changeaddress=$(bitcoin-cli getrawchangeaddress)
|
$ changeaddress=$(bitcoin-cli getrawchangeaddress legacy)
|
||||||
$ echo $changeaddress
|
$ echo $changeaddress
|
||||||
myrK8U3SE1nWh9y9XPho5aTrKYW6n8qSQv
|
mk9ry5VVy8mrA8SygxSQQUDNSSXyGFot6h
|
||||||
```
|
```
|
||||||
Note that this uses a new function: `getrawchangeaddress`. It's largely the same as `getnewaddress` but is optimized for use as a change address in a raw transaction, so it doesn't do things like make entries in your address book.
|
Note that this uses a new function: `getrawchangeaddress`. It's largely the same as `getnewaddress` but is optimized for use as a change address in a raw transaction, so it doesn't do things like make entries in your address book. We again selected the `legacy` address, instead of going with the default of `bech32`, simply for consistency. This is a situation where it would have been entirely safe to generate a default Bech32 address, just by using `bitcoin-cli getrawchangeaddress`, because it would being sent and received by you on your Bitcoin Core node which fully supports this. But, hobgoblins; we'll shift this over to Bech32 as well in [§4.6](04_6_Creating_a_Segwit_Transaction.md).
|
||||||
|
|
||||||
You now have an additional address inside your wallet, so that you can receive change from a UTXO! In order to use it, you'll need to create a raw transaction with two outputs.
|
You now have an additional address inside your wallet, so that you can receive change from a UTXO! In order to use it, you'll need to create a raw transaction with two outputs.
|
||||||
|
|
||||||
@ -28,52 +28,70 @@ Our sample raw transaction was simple in another way: it assumed that there was
|
|||||||
|
|
||||||
To summarize: creating a real raw transaction to send coins will sometimes require multiple inputs and will almost always require multiple outputs, one of which is a change address. We'll be creating that sort of more realistic transaction here, in a new example that shows a real-life example of sending funds via Bitcoin's second methodology, raw transactions.
|
To summarize: creating a real raw transaction to send coins will sometimes require multiple inputs and will almost always require multiple outputs, one of which is a change address. We'll be creating that sort of more realistic transaction here, in a new example that shows a real-life example of sending funds via Bitcoin's second methodology, raw transactions.
|
||||||
|
|
||||||
Here's the UTXOs we'll be using:
|
We're going to use our 0th and 2nd UTXOs:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli listunspent
|
$ bitcoin-cli listunspent
|
||||||
|
[
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"txid": "ec0598918f6f5476cb90365651e8a2724ef26f949290bbf196f41ed96092a52f",
|
"txid": "0619fecf6b2668fab1308fbd7b291ac210932602a6ac6b8cc11c7ae22c43701e",
|
||||||
"vout": 0,
|
"vout": 1,
|
||||||
"address": "mjtEqr4Fffd1XtpAkKoDkMBP54mMXJeQ3j",
|
"address": "mwJL7cRiW2bUnY81r1thSu3D4jtMmwyU6d",
|
||||||
"account": "",
|
"label": "",
|
||||||
"scriptPubKey": "76a9142fe70d51e886b9ef73b76c1743c5a2bb2894db8588ac",
|
"scriptPubKey": "76a914ad1ed1c5971b2308f89c1362d4705d020a40e8e788ac",
|
||||||
"amount": 3.90000000,
|
"amount": 0.00899999,
|
||||||
"confirmations": 9551,
|
"confirmations": 1,
|
||||||
"spendable": true,
|
"spendable": true,
|
||||||
"solvable": true
|
"solvable": true,
|
||||||
|
"desc": "pkh([d6043800/0'/0'/4']03eae28c93035f95a620dd96e1822f2a96e0357263fa1f87606a5254d5b9e6698f)#wwnfx2sp",
|
||||||
|
"safe": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"txid": "3470e5fe08633583d136b9cd49bb1a224c9d9313a0b4584fd3b7438dbdf34dbd",
|
"txid": "91261eafae15ea53dedbea7c1db748c52bbc04a85859ffd0d839bda1421fda4c",
|
||||||
"vout": 0,
|
"vout": 0,
|
||||||
"address": "msiyutru5TV33Q2UGK2Bbh2ewdrYALBzTb",
|
"address": "mjehC2KHzXcBDcwTd4LhZ2GzyzrZ3Kd3ff",
|
||||||
"account": "",
|
"label": "",
|
||||||
"scriptPubKey": "76a91485e7d9fe99708d575f3b93be13c0c55c6ffb765088ac",
|
"scriptPubKey": "76a9142d573900aa357a38afd741fbf24b075d263ea6e088ac",
|
||||||
"amount": 1.95000000,
|
"amount": 0.00022000,
|
||||||
"confirmations": 9542,
|
"confirmations": 15,
|
||||||
"spendable": true,
|
"spendable": true,
|
||||||
"solvable": true
|
"solvable": true,
|
||||||
|
"desc": "pkh([d6043800/0'/0'/3']0278608b54b8fb0d8379d3823d31f03a7c6ab0adffb07dd3811819fdfc34f8c132)#nhjc3f8y",
|
||||||
|
"safe": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"txid": "0df23a9dba49e822bbc558f15516f33021a64a5c2e48962cec541e0bcc79854d",
|
||||||
|
"vout": 0,
|
||||||
|
"address": "mwJL7cRiW2bUnY81r1thSu3D4jtMmwyU6d",
|
||||||
|
"label": "",
|
||||||
|
"scriptPubKey": "76a914ad1ed1c5971b2308f89c1362d4705d020a40e8e788ac",
|
||||||
|
"amount": 0.00100000,
|
||||||
|
"confirmations": 1,
|
||||||
|
"spendable": true,
|
||||||
|
"solvable": true,
|
||||||
|
"desc": "pkh([d6043800/0'/0'/4']03eae28c93035f95a620dd96e1822f2a96e0357263fa1f87606a5254d5b9e6698f)#wwnfx2sp",
|
||||||
|
"safe": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
```
|
```
|
||||||
In our example, we're going to send 4.0 BTC, which is larger than either of our UTXOs. This requires combining them, then using our change address to retrieve the unspent funds.
|
In our example, we're going to send .009 BTC, which is (barely) larger than either of our UTXOs. This requires combining them, then using our change address to retrieve the unspent funds.
|
||||||
|
|
||||||
### Set Up Your Variables
|
### Set Up Your Variables
|
||||||
|
|
||||||
We already have `$changeaddress` and `$recipient` variables from previous examples:
|
We already have `$changeaddress` and `$recipient` variables from previous examples:
|
||||||
```
|
```
|
||||||
$ echo $changeaddress
|
$ echo $changeaddress
|
||||||
myrK8U3SE1nWh9y9XPho5aTrKYW6n8qSQv
|
mk9ry5VVy8mrA8SygxSQQUDNSSXyGFot6h
|
||||||
~$ echo $recipient
|
$ echo $recipient
|
||||||
n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi
|
n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi
|
||||||
```
|
```
|
||||||
We also need to record the txid and vout for each of our two UTXOs. Having identified the UTXOs that we want to spend, we can use our JQ techniques to make sure accessing them is error free:
|
We also need to record the txid and vout for each of our two UTXOs. Having identified the UTXOs that we want to spend, we can use our JQ techniques to make sure accessing them is error free:
|
||||||
```
|
```
|
||||||
$ utxo_txid_1=$(bitcoin-cli listunspent | jq -r '.[0] | .txid')
|
$ utxo_txid_1=$(bitcoin-cli listunspent | jq -r '.[0] | .txid')
|
||||||
$ utxo_vout_1=$(bitcoin-cli listunspent | jq -r '.[0] | .vout')
|
$ utxo_vout_1=$(bitcoin-cli listunspent | jq -r '.[0] | .vout')
|
||||||
$ utxo_txid_2=$(bitcoin-cli listunspent | jq -r '.[1] | .txid')
|
$ utxo_txid_2=$(bitcoin-cli listunspent | jq -r '.[2] | .txid')
|
||||||
$ utxo_vout_2=$(bitcoin-cli listunspent | jq -r '.[1] | .vout')
|
$ utxo_vout_2=$(bitcoin-cli listunspent | jq -r '.[2] | .vout')
|
||||||
```
|
```
|
||||||
|
|
||||||
### Write the Transaction
|
### Write the Transaction
|
||||||
@ -82,14 +100,14 @@ Writing the actual raw transaction is surprisingly simple. All you need to do is
|
|||||||
|
|
||||||
Here's the example. Note the multiple inputs after the `inputs` arg and the multiple outputs after the `outputs` arg.
|
Here's the example. Note the multiple inputs after the `inputs` arg and the multiple outputs after the `outputs` arg.
|
||||||
```
|
```
|
||||||
$ rawtxhex2=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid_1'", "vout": '$utxo_vout_1' }, { "txid": "'$utxo_txid_2'", "vout": '$utxo_vout_2' } ]''' outputs='''{ "'$recipient'": 4.0, "'$changeaddress'": 1.8495 }''')
|
$ rawtxhex2=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid_1'", "vout": '$utxo_vout_1' }, { "txid": "'$utxo_txid_2'", "vout": '$utxo_vout_2' } ]''' outputs='''{ "'$recipient'": 0.009, "'$changeaddress'": 0.0009 }''')
|
||||||
```
|
```
|
||||||
We were _very_ careful figuring out our money math. These two UTXOs contain 5.85 BTC. After sending 4.0 BTC, we'll have 1.85 BTC left. We again chose .0005 BTC for the transaction fee. To accommodate that fee, we set our change to 1.8495 BTC. If we'd messed up our math and instead set our change to 1.7495 BTC, that additional .1 BTC would be lost. That's $100 gone to the miners! If we'd forgot to make change at all, then the whole 1.85 BTC ($2000!) would have disappeared. So, again, _be careful_.
|
We were _very_ careful figuring out our money math. These two UTXOs contain 5.85 BTC. After sending 0.009 BTC, we'll have .00099999 BTC left. We chose .00009999 BTC the transaction fee. To accommodate that fee, we set our change to .0009 BTC. If we'd messed up our math and instead set our change to .00009 BTC, that additional BTC would be lost to the miners! If we'd forgot to make change at all, then the whole excess would have disappeared. So, again, _be careful_.
|
||||||
|
|
||||||
Fortunately, we can triple-check with the `btctxfee` alias from the JQ Interlude:
|
Fortunately, we can triple-check with the `btctxfee` alias from the JQ Interlude:
|
||||||
```
|
```
|
||||||
$ btctxfee $rawtxhex2
|
$ ./txfee-calc.sh $rawtxhex2
|
||||||
.0005
|
.00009999
|
||||||
```
|
```
|
||||||
|
|
||||||
### Finish It Up
|
### Finish It Up
|
||||||
@ -98,42 +116,78 @@ You can now sign, seal, and deliver your transaction, and it's yours (and the fa
|
|||||||
```
|
```
|
||||||
$ signedtx2=$(bitcoin-cli -named signrawtransactionwithwallet hexstring=$rawtxhex2 | jq -r '.hex')
|
$ signedtx2=$(bitcoin-cli -named signrawtransactionwithwallet hexstring=$rawtxhex2 | jq -r '.hex')
|
||||||
$ bitcoin-cli -named sendrawtransaction hexstring=$signedtx2
|
$ bitcoin-cli -named sendrawtransaction hexstring=$signedtx2
|
||||||
69c9ef4d1adb48596c470146ee9b60593023b6eb870b79ef666a8c9369768469
|
e7071092dee0b2ae584bf6c1ee3c22164304e3a17feea7a32c22db5603cd6a0d
|
||||||
```
|
```
|
||||||
|
|
||||||
### Wait
|
### Wait
|
||||||
|
|
||||||
As usual, your money will be in flux for a while: the change will be unavailable until the transaction actually gets confirmed and a new UTXO is given to you.
|
As usual, your money will be in flux for a while: the change will be unavailable until the transaction actually gets confirmed and a new UTXO is given to you.
|
||||||
|
|
||||||
But, in 10 minutes or less (probably), you'll have your remaining money back and fully spendable again:
|
But, in 10 minutes or less (probably), you'll have your remaining money back and fully spendable again. For now, we're still waiting:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli listunspent
|
$ bitcoin-cli listunspent
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"txid": "69c9ef4d1adb48596c470146ee9b60593023b6eb870b79ef666a8c9369768469",
|
"txid": "91261eafae15ea53dedbea7c1db748c52bbc04a85859ffd0d839bda1421fda4c",
|
||||||
"vout": 1,
|
"vout": 0,
|
||||||
"address": "myrK8U3SE1nWh9y9XPho5aTrKYW6n8qSQv",
|
"address": "mjehC2KHzXcBDcwTd4LhZ2GzyzrZ3Kd3ff",
|
||||||
"scriptPubKey": "76a914c91b8f2f983aa9f8f0ba552adf6b6491ac01e02888ac",
|
"label": "",
|
||||||
"amount": 1.84950000,
|
"scriptPubKey": "76a9142d573900aa357a38afd741fbf24b075d263ea6e088ac",
|
||||||
"confirmations": 2,
|
"amount": 0.00022000,
|
||||||
|
"confirmations": 15,
|
||||||
"spendable": true,
|
"spendable": true,
|
||||||
"solvable": true
|
"solvable": true,
|
||||||
|
"desc": "pkh([d6043800/0'/0'/3']0278608b54b8fb0d8379d3823d31f03a7c6ab0adffb07dd3811819fdfc34f8c132)#nhjc3f8y",
|
||||||
|
"safe": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
And the change will eventuall arrive:
|
||||||
|
```
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"txid": "e7071092dee0b2ae584bf6c1ee3c22164304e3a17feea7a32c22db5603cd6a0d",
|
||||||
|
"vout": 1,
|
||||||
|
"address": "mk9ry5VVy8mrA8SygxSQQUDNSSXyGFot6h",
|
||||||
|
"scriptPubKey": "76a91432db726320e4ad170c9c1ee83cd4d8a243c3435988ac",
|
||||||
|
"amount": 0.00090000,
|
||||||
|
"confirmations": 1,
|
||||||
|
"spendable": true,
|
||||||
|
"solvable": true,
|
||||||
|
"desc": "pkh([d6043800/0'/1'/2']02881697d252d8bf181d08c58de1f02aec088cd2d468fc5fd888c6e39909f7fabf)#p6k7dptk",
|
||||||
|
"safe": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"txid": "91261eafae15ea53dedbea7c1db748c52bbc04a85859ffd0d839bda1421fda4c",
|
||||||
|
"vout": 0,
|
||||||
|
"address": "mjehC2KHzXcBDcwTd4LhZ2GzyzrZ3Kd3ff",
|
||||||
|
"label": "",
|
||||||
|
"scriptPubKey": "76a9142d573900aa357a38afd741fbf24b075d263ea6e088ac",
|
||||||
|
"amount": 0.00022000,
|
||||||
|
"confirmations": 16,
|
||||||
|
"spendable": true,
|
||||||
|
"solvable": true,
|
||||||
|
"desc": "pkh([d6043800/0'/0'/3']0278608b54b8fb0d8379d3823d31f03a7c6ab0adffb07dd3811819fdfc34f8c132)#nhjc3f8y",
|
||||||
|
"safe": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
This also might be a good time to revisit a blockchain explorer, so that you can see more intuitively how the inputs, outputs, and transaction fee are all laid out: [69c9ef4d1adb48596c470146ee9b60593023b6eb870b79ef666a8c9369768469](https://live.blockcypher.com/btc-testnet/tx/69c9ef4d1adb48596c470146ee9b60593023b6eb870b79ef666a8c9369768469/).
|
This also might be a good time to revisit a blockchain explorer, so that you can see more intuitively how the inputs, outputs, and transaction fee are all laid out: [e7071092dee0b2ae584bf6c1ee3c22164304e3a17feea7a32c22db5603cd6a0d](https://live.blockcypher.com/btc-testnet/tx/e7071092dee0b2ae584bf6c1ee3c22164304e3a17feea7a32c22db5603cd6a0d/).
|
||||||
|
|
||||||
## Summary: Sending Coins with Raw Transactions
|
## Summary: Sending Coins with Raw Transactions
|
||||||
|
|
||||||
To send coins with raw transactions, you need to create a raw transaction with one or more inputs (to have sufficient funds) and one or more outputs (to retrieve change). Then, you can follow your normal procedure of using `createrawtransaction` with named arguments and JQ, as laid out in previous sections.
|
To send coins with raw transactions, you need to create a raw transaction with one or more inputs (to have sufficient funds) and one or more outputs (to retrieve change). Then, you can follow your normal procedure of using `createrawtransaction` with named arguments and JQ, as laid out in previous sections.
|
||||||
|
|
||||||
### Why Use Raw Transactions
|
> :fire: ***What is the power of sending coins with raw transactions?***
|
||||||
|
|
||||||
_The advantages._ It gives you the best control. If your goal is to write a more intricate Bitcoin script or program, you'll probably use raw transactions so that you know exactly what's going on. This is also the _safest_ situation to use raw transactions, because you can programmatically ensure that you don't make mistakes.
|
> _The advantages._ It gives you the best control. If your goal is to write a more intricate Bitcoin script or program, you'll probably use raw transactions so that you know exactly what's going on. This is also the _safest_ situation to use raw transactions, because you can programmatically ensure that you don't make mistakes.
|
||||||
|
|
||||||
_The disadvantages._ It's easy to lose money. There are no warnings, no safeguards, and no programmatic backstops unless you write them. It's also arcane. The formatting is obnoxious, even using the easy-to-use `bitcoin-cli` interface, and you have to do a lot of lookup and calculation by hand.
|
> _The disadvantages._ It's easy to lose money. There are no warnings, no safeguards, and no programmatic backstops unless you write them. It's also arcane. The formatting is obnoxious, even using the easy-to-use `bitcoin-cli` interface, and you have to do a lot of lookup and calculation by hand.
|
||||||
|
|
||||||
## What's Next?
|
## What's Next?
|
||||||
|
|
||||||
Continue "Sending Bitcoin Transactions" with [§4.5: Sending Coins with Automated Raw Transactions](04_5_Sending_Coins_with_Automated_Raw_Transactions.md).
|
See another alternative way to input commands with [Interlude: Using Curl](04_4__Interlude_Using_Curl.md).
|
||||||
|
|
||||||
|
Or, you prefer to skip what's frankly a digression, learn one more way to "Send Bitcoin Transactions" with [§4.5 Sending Coins with Automated Raw Transactions](04_5_Sending_Coins_with_Automated_Raw_Transactions.md).
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
_TODO: THIS NEEDS TO BE REWRITTEN TO REPOSITION THIS INTO THE FLOW OF CHAPTER 4._
|
# Interlude: Using Curl
|
||||||
|
|
||||||
# 12.1: Accessing Bitcoind with Curl
|
|
||||||
|
|
||||||
> **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
> **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
||||||
|
|
||||||
`bitcoin-cli`, the heart of chapters 3-6, is ultimately just a wrapper. It's a way to interface with `bitcoind` from the command line, providing simplified access to its many RPC commands. But RPC can, of course, be accessed directly. That's what this chapter is about: directly connecting to RPC with the `curl` command.
|
`bitcoin-cli` is ultimately just a wrapper. It's a way to interface with `bitcoind` from the command line, providing simplified access to its many RPC commands. But RPC can, of course, be accessed directly. That's what this interlude is about: directly connecting to RPC with the `curl` command.
|
||||||
|
|
||||||
|
It won't be used much in the future chapters, but it's an important building block that you can see as an alternative access to `bitcoind` is you so prefer.
|
||||||
|
|
||||||
## Know Your Curl
|
## Know Your Curl
|
||||||
|
|
||||||
`curl`, short for "see URL", is a command-line tool that allows you to directly access URLs in a programmatic way. It's an easy way to interact with servers like `bitcoind` that listen to ports on the internet and that speak a variety of protocols. Curl is also available as a library for many programming languages, such as C, Java, PHP, and Python. So, once you know how to work with Curl, you'll have a strong foundation for using a lot of different APIs — as we'll touch on in the next few chapters.
|
`curl`, short for "see URL", is a command-line tool that allows you to directly access URLs in a programmatic way. It's an easy way to interact with servers like `bitcoind` that listen to ports on the internet and that speak a variety of protocols. Curl is also available as a library for many programming languages, such as C, Java, PHP, and Python. So, once you know how to work with Curl, you'll have a strong foundation for using a lot of different API.
|
||||||
|
|
||||||
In order to use `curl` with `bitcoind`, you must know three things: the standard format, the user name and password, and the correct port.
|
In order to use `curl` with `bitcoind`, you must know three things: the standard format, the user name and password, and the correct port.
|
||||||
|
|
||||||
@ -21,21 +21,20 @@ getmininginfo
|
|||||||
|
|
||||||
Returns a json object containing mining-related information.
|
Returns a json object containing mining-related information.
|
||||||
Result:
|
Result:
|
||||||
{
|
{ (json object)
|
||||||
"blocks": nnn, (numeric) The current block
|
"blocks" : n, (numeric) The current block
|
||||||
"currentblocksize": nnn, (numeric) The last block size
|
"currentblockweight" : n, (numeric, optional) The block weight of the last assembled block (only present if a block was ever assembled)
|
||||||
"currentblockweight": nnn, (numeric) The last block weight
|
"currentblocktx" : n, (numeric, optional) The number of block transactions of the last assembled block (only present if a block was ever assembled)
|
||||||
"currentblocktx": nnn, (numeric) The last block transaction
|
"difficulty" : n, (numeric) The current difficulty
|
||||||
"difficulty": xxx.xxxxx (numeric) The current difficulty
|
"networkhashps" : n, (numeric) The network hashes per second
|
||||||
"errors": "..." (string) Current errors
|
"pooledtx" : n, (numeric) The size of the mempool
|
||||||
"networkhashps": nnn, (numeric) The network hashes per second
|
"chain" : "str", (string) current network name (main, test, regtest)
|
||||||
"pooledtx": n (numeric) The size of the mempool
|
"warnings" : "str" (string) any network and blockchain warnings
|
||||||
"chain": "xxxx", (string) current network name as defined in BIP70 (main, test, regtest)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
> bitcoin-cli getmininginfo
|
> bitcoin-cli getmininginfo
|
||||||
> curl --user myusername --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getmininginfo", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/
|
> curl --user myusername --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "getmininginfo", "params": []}' -H 'content-type: text/plain;' http://127.0.0.1:8332/
|
||||||
```
|
```
|
||||||
And there's the `curl` command, at the end of the help screen! This somewhat lengthy command has four major parts: (1) a listing of your user name; (2) a `--data-binary` flag; (3) a JSON object that tells `bitcoind` what to do, including a JSON array of parameters; and (4) an HTTP header that includes the `bitcoind` URL.
|
And there's the `curl` command, at the end of the help screen! This somewhat lengthy command has four major parts: (1) a listing of your user name; (2) a `--data-binary` flag; (3) a JSON object that tells `bitcoind` what to do, including a JSON array of parameters; and (4) an HTTP header that includes the `bitcoind` URL.
|
||||||
|
|
||||||
@ -49,42 +48,49 @@ In order to speak with the `bitcoind` port, you need a user name and password. T
|
|||||||
|
|
||||||
For example, here's our current setup:
|
For example, here's our current setup:
|
||||||
```
|
```
|
||||||
$ more ~/.bitcoin/bitcoin.conf
|
$ cat ~/.bitcoin/bitcoin.conf
|
||||||
server=1
|
server=1
|
||||||
dbcache=1536
|
dbcache=1536
|
||||||
par=1
|
par=1
|
||||||
blocksonly=1
|
|
||||||
maxuploadtarget=137
|
maxuploadtarget=137
|
||||||
maxconnections=16
|
maxconnections=16
|
||||||
rpcuser=bitcoinrpc
|
rpcuser=StandUp
|
||||||
rpcpassword=73bd45ba60ab8f9ff9846b6404769487
|
rpcpassword=8eaf562eaf45c33c3328bc66008f2dd1
|
||||||
rpcallowip=127.0.0.1
|
rpcallowip=127.0.0.1
|
||||||
txindex=1
|
debug=tor
|
||||||
|
prune=550
|
||||||
testnet=1
|
testnet=1
|
||||||
|
mintxfee=0.001
|
||||||
|
txconfirmtarget=1
|
||||||
|
[test]
|
||||||
|
rpcbind=127.0.0.1
|
||||||
|
rpcport=18332
|
||||||
|
[main]
|
||||||
|
rpcbind=127.0.0.1
|
||||||
|
rpcport=8332
|
||||||
|
[regtest]
|
||||||
|
rpcbind=127.0.0.1
|
||||||
|
rpcport=18443
|
||||||
```
|
```
|
||||||
Our user name is `bitcoinrpc` and our password is `73bd45ba60ab8f9ff9846b6404769487`.
|
Our user name is `StandUp` and our password is `8eaf562eaf45c33c3328bc66008f2dd1`.
|
||||||
|
|
||||||
> **WARNING:** Clearly, it's not very secure to have this information in a plain text file. As of Bitcoin Core 0.12, you can instead omit the `rpcpassword` from your `bitcoin.conf` file, and have `bitcoind` generate a new cookie whenever it starts up. The downside of this is that it makes use of RPC commands by other applications, such as the ones detailed in this chapter, more difficult. So, we're going to stick with the plain `rpcuser` and `rpcpassword` information for now, but for production software, consider moving to cookies.
|
> **WARNING:** Clearly, it's not very secure to have this information in a plain text file. As of Bitcoin Core 0.12, you can instead omit the `rpcpassword` from your `bitcoin.conf` file, and have `bitcoind` generate a new cookie whenever it starts up. The downside of this is that it makes use of RPC commands by other applications, such as the ones detailed in this chapter, more difficult. So, we're going to stick with the plain `rpcuser` and `rpcpassword` information for now, but for production software, consider moving to cookies.
|
||||||
|
|
||||||
The secure way to RPC with `bitcoind` is as follows:
|
The secure way to RPC with `bitcoind` is as follows:
|
||||||
```
|
```
|
||||||
$ curl --user bitcoinrpc --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getmininginfo", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/
|
$ curl --user StandUp --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getmininginfo", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/
|
||||||
Enter host password for user 'bitcoinrpc':
|
Enter host password for user 'bitcoinrpc':
|
||||||
```
|
```
|
||||||
As noted, you will be prompted for your password.
|
As noted, you will be prompted for your password.
|
||||||
|
|
||||||
|
> :link: **TESTNET vs MAINNET:** Testnet uses a URL with port 18332 and mainnet uses a URL with port 8332. Take a look in your `bitcoin.conf`, it's all laid out there.
|
||||||
|
|
||||||
The insecure way to do so is as follows:
|
The insecure way to do so is as follows:
|
||||||
```
|
```
|
||||||
$ curl --user bitcoinrpc:73bd45ba60ab8f9ff9846b6404769487 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getmininginfo", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/
|
$ curl --user StandUp:8eaf562eaf45c33c3328bc66008f2dd1 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getmininginfo", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/
|
||||||
```
|
```
|
||||||
> **WARNING:** Entering your password on the command line may put your password into the process table and/or save it into a history. This is even less recommended than putting it in a file, except for testing on testnet. If you want to do it anywhere else, make sure you know what you're doing!
|
> **WARNING:** Entering your password on the command line may put your password into the process table and/or save it into a history. This is even less recommended than putting it in a file, except for testing on testnet. If you want to do it anywhere else, make sure you know what you're doing!
|
||||||
|
|
||||||
### Know Your Port
|
|
||||||
|
|
||||||
The port should be easy, it's the `http://127.0.0.1:8332/` shown in the tutorials, right? Not exactly. It depends on whether you're using testnet or mainnet.
|
|
||||||
|
|
||||||
> **MAINNET VS. TESTNET.** To access RPC commands on the mainnet, use port 8332, but to do so on testnet, use port 18332.
|
|
||||||
|
|
||||||
### Know Your Command & Parameters
|
### Know Your Command & Parameters
|
||||||
|
|
||||||
With all of that in hand, you're ready to send off standard RPC commands with `curl` ... but you still need to know how to incorporate the two elements that tend to change in the `curl` command.
|
With all of that in hand, you're ready to send off standard RPC commands with `curl` ... but you still need to know how to incorporate the two elements that tend to change in the `curl` command.
|
||||||
@ -99,38 +105,33 @@ Here's what some parameter arrays will look like:
|
|||||||
* `["000b4430a7a2ba60891b01b718747eaf9665cb93fbc0c619c99419b5b5cf3ad2"]` — An array with data
|
* `["000b4430a7a2ba60891b01b718747eaf9665cb93fbc0c619c99419b5b5cf3ad2"]` — An array with data
|
||||||
* `["'$signedhex'"]` — An array with a variable
|
* `["'$signedhex'"]` — An array with a variable
|
||||||
* `[6, 9999999]` — An array with two parameters
|
* `[6, 9999999]` — An array with two parameters
|
||||||
|
* `{}` - An empty object
|
||||||
* `[''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]'', ''{ "'$recipient'": 0.298, "'$changeaddress'": 1.0}'']` — An array with an array containing an object and a bare object
|
* `[''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]'', ''{ "'$recipient'": 0.298, "'$changeaddress'": 1.0}'']` — An array with an array containing an object and a bare object
|
||||||
|
|
||||||
## Get Information
|
## Get Information
|
||||||
|
|
||||||
You can now send your first `curl` command by accessing the `getmininginfo` RPC:
|
You can now send your first `curl` command by accessing the `getmininginfo` RPC:
|
||||||
```
|
```
|
||||||
$ curl --user bitcoinrpc:73bd45ba60ab8f9ff9846b6404769487 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getmininginfo", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/
|
$ curl --user StandUp:8eaf562eaf45c33c3328bc66008f2dd1 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getmininginfo", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/
|
||||||
{"result":{"blocks":1128599,"currentblocksize":0,"currentblockweight":0,"currentblocktx":0,"difficulty":1,"errors":"Warning: unknown new rules activated (versionbit 28)","networkhashps":8658807967387.751,"pooledtx":0,"chain":"test"},"error":null,"id":"curltest"}
|
{"result":{"blocks":1772428,"difficulty":10178811.40698772,"networkhashps":91963587385939.06,"pooledtx":61,"chain":"test","warnings":"Warning: unknown new rules activated (versionbit 28)"},"error":null,"id":"curltest"}```
|
||||||
```
|
|
||||||
Note that we provided the method, `getmininginfo`, and the parameter, `[]`, but that everything else was the standard `curl` command line.
|
Note that we provided the method, `getmininginfo`, and the parameter, `[]`, but that everything else was the standard `curl` command line.
|
||||||
|
```
|
||||||
> **WARNING:** If you get a result like "Failed to connect to 127.0.0.1 port 8332: Connection refused", be sure that a line like `rpcallowip=127.0.0.1` is in your ~/.bitcoin/bitcoin.conf. If things still don't work, be sure that you're allowing access to port 18332 (or 8332) from localhost. Our standard setup from [Chapter Two: Creating a Bitcoin-Core VPS](02_0_Setting_Up_a_Bitcoin-Core_VPS.md) should do all of this.
|
> **WARNING:** If you get a result like "Failed to connect to 127.0.0.1 port 8332: Connection refused", be sure that a line like `rpcallowip=127.0.0.1` is in your ~/.bitcoin/bitcoin.conf. If things still don't work, be sure that you're allowing access to port 18332 (or 8332) from localhost. Our standard setup from [Chapter Two: Creating a Bitcoin-Core VPS](02_0_Setting_Up_a_Bitcoin-Core_VPS.md) should do all of this.
|
||||||
|
|
||||||
The result is another JSON array, which is unfortunately ugly to read if you're using `curl` by hand. Fortunately, you can clean it up simply by piping it through `jq`:
|
The result is another JSON array, which is unfortunately ugly to read if you're using `curl` by hand. Fortunately, you can clean it up simply by piping it through `jq`:
|
||||||
```
|
```
|
||||||
$ curl --user bitcoinrpc:73bd45ba60ab8f9ff9846b6404769487 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getmininginfo", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.'
|
$ curl --user StandUp:8eaf562eaf45c33c3328bc66008f2dd1 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getmininginfo", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.'
|
||||||
|
% Total % Received % Xferd Average Speed Time Time Time Current
|
||||||
% Total % Received % Xferd Average Speed Time Time Time Current
|
|
||||||
Dload Upload Total Spent Left Speed
|
Dload Upload Total Spent Left Speed
|
||||||
100 354 100 277 100 77 65546 18220 --:--:-- --:--:-- --:--:-- 92333
|
100 295 100 218 100 77 72666 25666 --:--:-- --:--:-- --:--:-- 98333
|
||||||
|
|
||||||
{
|
{
|
||||||
"result": {
|
"result": {
|
||||||
"blocks": 1128609,
|
"blocks": 1772429,
|
||||||
"currentblocksize": 0,
|
"difficulty": 10178811.40698772,
|
||||||
"currentblockweight": 0,
|
"networkhashps": 90580030969896.44,
|
||||||
"currentblocktx": 0,
|
"pooledtx": 4,
|
||||||
"difficulty": 2777757.750498331,
|
"chain": "test",
|
||||||
"errors": "Warning: unknown new rules activated (versionbit 28)",
|
"warnings": "Warning: unknown new rules activated (versionbit 28)"
|
||||||
"networkhashps": 8795436830406.6,
|
|
||||||
"pooledtx": 0,
|
|
||||||
"chain": "test"
|
|
||||||
},
|
},
|
||||||
"error": null,
|
"error": null,
|
||||||
"id": "curltest"
|
"id": "curltest"
|
||||||
@ -144,32 +145,35 @@ Though you're accessing `bitcoind` directly, you'll still get access to wallet f
|
|||||||
|
|
||||||
### Look Up Addresses
|
### Look Up Addresses
|
||||||
|
|
||||||
Use the `getaddressesbyaccount` RPC to list all of your current addresses:
|
Use the `getaddressesbylabel` RPC to list all of your current addresses:
|
||||||
```
|
```
|
||||||
$ curl --user bitcoinrpc:73bd45ba60ab8f9ff9846b6404769487 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getaddressesbylabel", "params": [""] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.'
|
$ curl --user StandUp:8eaf562eaf45c33c3328bc66008f2dd1 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getaddressesbylabel", "params": [""] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.'
|
||||||
|
{
|
||||||
{
|
"result": {
|
||||||
"result": [
|
"mi25UrzHnvn3bpEfFCNqJhPWJn5b77a5NE": {
|
||||||
"mg7YqyvK8HUFvpgZ5iYTfZ5vjfaJWnNTd9",
|
"purpose": "receive"
|
||||||
"mkMkhbUzcSPdEHUoRQkBKHe8otP1SzWWeb",
|
},
|
||||||
"moXjRArk4rWUYZvAiDHnMXQHmwyf3TtAb8",
|
"mjehC2KHzXcBDcwTd4LhZ2GzyzrZ3Kd3ff": {
|
||||||
"mp3qqSTEYqhm3iuo9nM5GnzCGuoEvb9KrK",
|
"purpose": "receive"
|
||||||
"msW5tzCMJRECSATENN11ATmxx3PLwjGEaW",
|
},
|
||||||
"msZ5bMbZC4HQPRuJCoivDWYExAB7ssE5o8",
|
"moKVV6XEhfrBCE3QCYq6ppT7AaMF8KsZ1B": {
|
||||||
"mx3wUEVp526gkqzMteK6NwsPffgtFB3CGX",
|
"purpose": "receive"
|
||||||
"mxwLPpo2X1VXXAiLHNVb55H4JASeLMDm7A",
|
},
|
||||||
"n2AfUVXq9MzsJohCjCKNXKj2SYHfdbrcqy",
|
"mwJL7cRiW2bUnY81r1thSu3D4jtMmwyU6d": {
|
||||||
"n2fGxDXKozkZjpi9DqW93NZ65TnQDqd49V",
|
"purpose": "receive"
|
||||||
"n4cqjJE6fqcmeWpftygwPoKMMDva6BpyHf",
|
},
|
||||||
"2Mzw7WBvh9RAQ4ssKqxyNyP7L9NAojLqSW8",
|
"tb1q5gnwrh7ss5mmqt0qfan85jdagmumnatcscwpk6": {
|
||||||
"2NAGfA4nW6nrZkD5je8tSiAcYB9xL2xYMCz",
|
"purpose": "receive"
|
||||||
"2NDx9HTeLagiEqWrYvBuPkG3gwdhQ32Jz4B"
|
},
|
||||||
],
|
"tb1qmtucvjtga68kgrvkl7q05x4t9lylxhku7kqdpr": {
|
||||||
|
"purpose": "receive"
|
||||||
|
}
|
||||||
|
},
|
||||||
"error": null,
|
"error": null,
|
||||||
"id": "curltest"
|
"id": "curltest"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
This is our first example of a real parameter, `""`. This is the required `account` parameter for `getaddressbyaccount`, but all of our addresses are in the default account, so nothing special was required here.
|
This is our first example of a real parameter, `""`. This is the required `label` parameter for `getaddressesbylabel`, but all of our addresses are under the default label, so nothing special was required here.
|
||||||
|
|
||||||
The result is a list of all the addresses that have been used by this wallet ... some of which presumably contain funds.
|
The result is a list of all the addresses that have been used by this wallet ... some of which presumably contain funds.
|
||||||
|
|
||||||
@ -177,64 +181,33 @@ The result is a list of all the addresses that have been used by this wallet ...
|
|||||||
|
|
||||||
Use the `listunspent` RPC to list the funds that you have available:
|
Use the `listunspent` RPC to list the funds that you have available:
|
||||||
```
|
```
|
||||||
$ curl --user bitcoinrpc:73bd45ba60ab8f9ff9846b6404769487 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "listunspent", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.'
|
$ curl --user StandUp:8eaf562eaf45c33c3328bc66008f2dd1 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "listunspent", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.'
|
||||||
|
|
||||||
{
|
{
|
||||||
"result": [
|
"result": [
|
||||||
{
|
{
|
||||||
"txid": "6a184a2f07fa30189f4831d6f041d52653a103b3883d2bec2f79187331fd7f0e",
|
"txid": "e7071092dee0b2ae584bf6c1ee3c22164304e3a17feea7a32c22db5603cd6a0d",
|
||||||
"vout": 0,
|
"vout": 1,
|
||||||
"address": "mnsdQUxpGa6UsroUJhJGupPrPcuvQbPzmV",
|
"address": "mk9ry5VVy8mrA8SygxSQQUDNSSXyGFot6h",
|
||||||
"scriptPubKey": "76a91450b1d90a130c4f3f1e5fbfa7320fd36b7265db0488ac",
|
"scriptPubKey": "76a91432db726320e4ad170c9c1ee83cd4d8a243c3435988ac",
|
||||||
"amount": 1.2985,
|
"amount": 0.0009,
|
||||||
"confirmations": 1260,
|
"confirmations": 4,
|
||||||
"spendable": true,
|
"spendable": true,
|
||||||
"solvable": true
|
"solvable": true,
|
||||||
|
"desc": "pkh([d6043800/0'/1'/2']02881697d252d8bf181d08c58de1f02aec088cd2d468fc5fd888c6e39909f7fabf)#p6k7dptk",
|
||||||
|
"safe": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"txid": "966099e0fd330007eac0394d97a36985980ff971d64b865c5d474f1931eeec3a",
|
"txid": "91261eafae15ea53dedbea7c1db748c52bbc04a85859ffd0d839bda1421fda4c",
|
||||||
"vout": 0,
|
"vout": 0,
|
||||||
"address": "2NAGfA4nW6nrZkD5je8tSiAcYB9xL2xYMCz",
|
"address": "mjehC2KHzXcBDcwTd4LhZ2GzyzrZ3Kd3ff",
|
||||||
"account": "",
|
"label": "",
|
||||||
"scriptPubKey": "a914babf9063cee8ab6e9334f95f6d4e9148d0e551c287",
|
"scriptPubKey": "76a9142d573900aa357a38afd741fbf24b075d263ea6e088ac",
|
||||||
"amount": 1.2985,
|
"amount": 0.00022,
|
||||||
"confirmations": 3681,
|
"confirmations": 19,
|
||||||
"spendable": false,
|
|
||||||
"solvable": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"txid": "e31f747cb97a364a1a00b56a3d9d43a97c6e02938a47e599ad54d7cf710f056f",
|
|
||||||
"vout": 0,
|
|
||||||
"address": "2NDx9HTeLagiEqWrYvBuPkG3gwdhQ32Jz4B",
|
|
||||||
"account": "",
|
|
||||||
"redeemScript": "52210307fd375ed7cced0f50723e3e1a97bbe7ccff7318c815df4e99a59bc94dbcd81921029bf628adf0698a089137294975c8589cf0cc6be050f92d944faaa8c8a623430352ae",
|
|
||||||
"scriptPubKey": "a914e31fa4be1f718e068ba5cbb2f12a59bba66a2da687",
|
|
||||||
"amount": 1.3,
|
|
||||||
"confirmations": 6515,
|
|
||||||
"spendable": false,
|
|
||||||
"solvable": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"txid": "88e5d5f3077517d76f5a61491fa52e6aaae078c52bc62d849f09507ef0cfada2",
|
|
||||||
"vout": 0,
|
|
||||||
"address": "n4cqjJE6fqcmeWpftygwPoKMMDva6BpyHf",
|
|
||||||
"account": "",
|
|
||||||
"scriptPubKey": "76a914fd67e8a7c7813e7a5c376eb71074f373d924d96888ac",
|
|
||||||
"amount": 0.47,
|
|
||||||
"confirmations": 38043,
|
|
||||||
"spendable": true,
|
"spendable": true,
|
||||||
"solvable": true
|
"solvable": true,
|
||||||
},
|
"desc": "pkh([d6043800/0'/0'/3']0278608b54b8fb0d8379d3823d31f03a7c6ab0adffb07dd3811819fdfc34f8c132)#nhjc3f8y",
|
||||||
{
|
"safe": true
|
||||||
"txid": "7e8abac5040a14f74fc8c44300c89cf93ce7f59f1e1c1d1711fdddd7012941c1",
|
|
||||||
"vout": 0,
|
|
||||||
"address": "moXjRArk4rWUYZvAiDHnMXQHmwyf3TtAb8",
|
|
||||||
"account": "",
|
|
||||||
"scriptPubKey": "76a91457e6ac3cfbc9c34c292ca59f105a2d7a819db7a488ac",
|
|
||||||
"amount": 0.65,
|
|
||||||
"confirmations": 6586,
|
|
||||||
"spendable": true,
|
|
||||||
"solvable": true
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"error": null,
|
"error": null,
|
||||||
@ -247,20 +220,19 @@ This is almost exactly the same output that you receive when you type `bitcoin-c
|
|||||||
|
|
||||||
After you know where your funds are, the next step in crafting a transaction is to get a change address. By now you've probably got the hang of this, and you know that for simple RPC commands, all you need to do is adjust the `method` is the `curl` command:
|
After you know where your funds are, the next step in crafting a transaction is to get a change address. By now you've probably got the hang of this, and you know that for simple RPC commands, all you need to do is adjust the `method` is the `curl` command:
|
||||||
```
|
```
|
||||||
$ curl --user bitcoinrpc:73bd45ba60ab8f9ff9846b6404769487 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getrawchangeaddress", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.'
|
$ curl --user StandUp:8eaf562eaf45c33c3328bc66008f2dd1 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getrawchangeaddress", "params": ["legacy"] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.'
|
||||||
|
{
|
||||||
{
|
"result": "mrSqN37TPs89GcidSZTvXmMzjxoJZ6RKoz",
|
||||||
"result": "mznccEt2ozGFN6oVaYU5BGgTzcdH8Zj8wf",
|
|
||||||
"error": null,
|
"error": null,
|
||||||
"id": "curltest"
|
"id": "curltest"
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
At this point, we can even revert to our standard practice of saving results to variables with additional help from `jq`:
|
At this point, we can even revert to our standard practice of saving results to variables with additional help from `jq`:
|
||||||
```
|
```
|
||||||
$ changeaddress=$(curl --user bitcoinrpc:73bd45ba60ab8f9ff9846b6404769487 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getrawchangeaddress", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.result')
|
$ changeaddress=$(curl --user StandUp:8eaf562eaf45c33c3328bc66008f2dd1 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getrawchangeaddress", "params": ["legacy"] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.result')
|
||||||
|
|
||||||
$ echo $changeaddress
|
$ echo $changeaddress
|
||||||
mmDWRH3CbeXwCqBwdHCj7E9d3oWTuuizxc
|
mqdfnjgWr2r3sCCeuTDfe8fJ1CnycF2e6R
|
||||||
```
|
```
|
||||||
No need to worry about the downloading info. It'll go to `STDERR` and be displayed on your screen, while the results go to `STDOUT` and are saved in your variable.
|
No need to worry about the downloading info. It'll go to `STDERR` and be displayed on your screen, while the results go to `STDOUT` and are saved in your variable.
|
||||||
|
|
||||||
@ -274,29 +246,28 @@ Just as with `bitcoin-cli`, in order to create a transaction by curling RPC comm
|
|||||||
|
|
||||||
This example sets up our variables for using the 1.2985 BTC in funds listed in the first unspent transaction above:
|
This example sets up our variables for using the 1.2985 BTC in funds listed in the first unspent transaction above:
|
||||||
```
|
```
|
||||||
$ utxo_txid=$(curl --user bitcoinrpc:73bd45ba60ab8f9ff9846b6404769487 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "listunspent", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.result | .[0] | .txid')
|
$ utxo_txid=$(curl --user StandUp:8eaf562eaf45c33c3328bc66008f2dd1 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "listunspent", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.result | .[0] | .txid')
|
||||||
$ utxo_vout=$(curl --user bitcoinrpc:73bd45ba60ab8f9ff9846b6404769487 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "listunspent", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.result | .[0] | .vout')
|
$ utxo_vout=$(curl --user StandUp:8eaf562eaf45c33c3328bc66008f2dd1 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "listunspent", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.result | .[0] | .vout')
|
||||||
$ recipient=mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf
|
$ recipient=mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf
|
||||||
$ changeaddress=$(curl --user bitcoinrpc:73bd45ba60ab8f9ff9846b6404769487 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getrawchangeaddress", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.result')
|
$ changeaddress=$(curl --user StandUp:8eaf562eaf45c33c3328bc66008f2dd1 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getrawchangeaddress", "params": ["legacy"] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.result')
|
||||||
|
|
||||||
$ echo $utxo_txid
|
$ echo $utxo_txid
|
||||||
6a184a2f07fa30189f4831d6f041d52653a103b3883d2bec2f79187331fd7f0e
|
e7071092dee0b2ae584bf6c1ee3c22164304e3a17feea7a32c22db5603cd6a0d
|
||||||
$ echo $utxo_vout
|
$ echo $utxo_vout
|
||||||
0
|
1
|
||||||
$ echo $recipient
|
$ echo $recipient
|
||||||
mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf
|
mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf
|
||||||
$ echo $changeaddress
|
$ echo $changeaddress
|
||||||
mmDWRH3CbeXwCqBwdHCj7E9d3oWTuuizxc
|
n2jf3MzeFpFGa7wq8rXKVnVuv5FoNSJZ1N
|
||||||
```
|
```
|
||||||
|
|
||||||
### Create the Transaction
|
### Create the Transaction
|
||||||
|
|
||||||
The transaction created with `curl` is very similar to the transaction created with `bitcoin-cli`, but with a few subtle differences:
|
The transaction created with `curl` is very similar to the transaction created with `bitcoin-cli`, but with a few subtle differences:
|
||||||
```
|
```
|
||||||
$ curl --user bitcoinrpc:73bd45ba60ab8f9ff9846b6404769487 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "createrawtransaction", "params": [''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]'', ''{ "'$recipient'": 0.298, "'$changeaddress'": 1.0}'']}' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.'
|
$ curl --user StandUp:8eaf562eaf45c33c3328bc66008f2dd1 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "createrawtransaction", "params": [''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]'', ''{ "'$recipient'": 0.0003, "'$changeaddress'": 0.0005}'']}' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.'
|
||||||
|
{
|
||||||
{
|
"result": "02000000010d6acd0356db222ca3a7ee7fa1e3044316223ceec1f64b58aeb2e0de921007e70100000000ffffffff0230750000000000001976a914ac19d3fd17710e6b9a331022fe92c693fdf6659588ac50c30000000000001976a9147021efec134057043386decfaa6a6aa4ee5f19eb88ac00000000",
|
||||||
"result": "02000000010e7ffd317318792fec2b3d88b303a15326d541f0d631489f1830fa072f4a186a0000000000ffffffff0240b6c601000000001976a914ac19d3fd17710e6b9a331022fe92c693fdf6659588ac00e1f505000000001976a9143e84156731d67c80c3dff6c1cc3b4f58460e642388ac00000000",
|
|
||||||
"error": null,
|
"error": null,
|
||||||
"id": "curltest"
|
"id": "curltest"
|
||||||
}
|
}
|
||||||
@ -313,7 +284,7 @@ However, there's one last thing of note in this example, and it can be _maddenin
|
|||||||
|
|
||||||
Having verified that things work, you probably want to save the hex code into a variable:
|
Having verified that things work, you probably want to save the hex code into a variable:
|
||||||
```
|
```
|
||||||
$ hexcode=$(curl --user bitcoinrpc:73bd45ba60ab8f9ff9846b6404769487 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "createrawtransaction", "params": [''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]'', ''{ "'$recipient'": 0.298, "'$changeaddress'": 1.0}'']}' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.result')
|
$ hexcode=$(curl --user StandUp:8eaf562eaf45c33c3328bc66008f2dd1 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "createrawtransaction", "params": [''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]'', ''{ "'$recipient'": 0.0003, "'$changeaddress'": 0.0005}'']}' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.result')
|
||||||
```
|
```
|
||||||
|
|
||||||
### Sign and Send
|
### Sign and Send
|
||||||
@ -321,17 +292,19 @@ $ hexcode=$(curl --user bitcoinrpc:73bd45ba60ab8f9ff9846b6404769487 --data-binar
|
|||||||
Signing and sending your transaction using `curl` is an easy use of the `signrawtransactionwithwallet` and `sendrawtransaction` RPC:
|
Signing and sending your transaction using `curl` is an easy use of the `signrawtransactionwithwallet` and `sendrawtransaction` RPC:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ signedhex=$(curl --user bitcoinrpc:73bd45ba60ab8f9ff9846b6404769487 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "signrawtransactionwithwallet", "params": ["'$hexcode'"] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.result | .hex')
|
$ signedhex=$(curl --user StandUp:8eaf562eaf45c33c3328bc66008f2dd1 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "signrawtransactionwithwallet", "params": ["'$hexcode'"] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.result | .hex')
|
||||||
|
|
||||||
$ curl --user bitcoinrpc:73bd45ba60ab8f9ff9846b6404769487 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "sendrawtransaction", "params": ["'$signedhex'"] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.'
|
$ curl --user StandUp:8eaf562eaf45c33c3328bc66008f2dd1 --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "sendrawtransaction", "params": ["'$signedhex'"] }' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq -r '.'
|
||||||
{
|
{
|
||||||
"result": "000b4430a7a2ba60891b01b718747eaf9665cb93fbc0c619c99419b5b5cf3ad2",
|
"result": "eb84c5008038d760805d4d9644ace67849542864220cb2685a1ea2c64176b82d",
|
||||||
"error": null,
|
"error": null,
|
||||||
"id": "curltest"
|
"id": "curltest"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
## Summary: Accessing Bitcoind with Curl
|
## Summary: Accessing Bitcoind with Curl
|
||||||
|
|
||||||
Having finished this section, you may feel that accessing `bitcoind` via `curl` is very much like accessing it through `bitcoin-cli` ... but more cumbersome. And, you'd be right. `bitcoin-cli` has pretty complete RPC functionality, so anything that you do through `curl` you can probably do through `bitcoin-cli`.
|
Having finished this section, you may feel that accessing `bitcoind` via `curl` is very much like accessing it through `bitcoin-cli` ... but more cumbersome. And, you'd be right. `bitcoin-cli` has pretty complete RPC functionality, so anything that you do through `curl` you can probably do through `bitcoin-cli`. Which is why we're going to continue concentrating on `bitcoin-cli` following this digression.
|
||||||
|
|
||||||
_What is the power of curl?_ Most obviously, `curl` takes out one level of indirection. Instead of working with `bitcoin-cli` which sends RPC commands to `bitcoind`, you're sending those RPC commands directly. This allows for more robust programming, because you don't have to worry about what unexpected things that `bitcoin-cli` might do or how it might change over time. However, you're also taking your first steps toward using a more comprehensive programming language than the poor options offered by a shell script. But for that, you'll need to use a `curl` library within a more familiar language like C. In other words, command-line `curl` was just the first step, the basis to better explain what you're doing as you move forward.
|
But there are still reasons you'd use `curl` instead of `bitcoin-cli`:
|
||||||
|
|
||||||
|
_What is the power of curl?_ Most obviously, `curl` takes out one level of indirection. Instead of working with `bitcoin-cli` which sends RPC commands to `bitcoind`, you're sending those RPC commands directly. This allows for more robust programming, because you don't have to worry about what unexpected things that `bitcoin-cli` might do or how it might change over time. However, you're also taking your first steps toward using a more comprehensive programming language than the poor options offered by a shell script. As you'll see in the last few chapters of this, you might actually see curl libraries are other functions to access the RPC commands in a variety of programming languages: but that's still a long ways away.
|
||||||
|
@ -10,7 +10,7 @@ The methodology for automated raw transactions is simple: you create a raw trans
|
|||||||
|
|
||||||
In order to use this command, you'll need to ensure that your ~/.bitcoin/bitcoin.conf file contains rational variables for calculating transaction fees. Please see [§4.1: Sending Coins The Easy Way](04_1_Sending_Coins_The_Easy_Way.md) for more information on this.
|
In order to use this command, you'll need to ensure that your ~/.bitcoin/bitcoin.conf file contains rational variables for calculating transaction fees. Please see [§4.1: Sending Coins The Easy Way](04_1_Sending_Coins_The_Easy_Way.md) for more information on this.
|
||||||
|
|
||||||
For very conservative numbers, we suggested adding the following to the bitcoin.conf:
|
For very conservative numbers, we suggested adding the following to the `bitcoin.conf`:
|
||||||
```
|
```
|
||||||
mintxfee=0.0001
|
mintxfee=0.0001
|
||||||
txconfirmtarget=6
|
txconfirmtarget=6
|
||||||
@ -23,9 +23,10 @@ txconfirmtarget=1
|
|||||||
|
|
||||||
## Create a Bare Bones Raw Transaction
|
## Create a Bare Bones Raw Transaction
|
||||||
|
|
||||||
To use `fundrawtransaction` you first need to create a bare-bones raw transaction that lists _no_ inputs and _no_ change address. You'll just list your recipient and how much you want to send them:
|
To use `fundrawtransaction` you first need to create a bare-bones raw transaction that lists _no_ inputs and _no_ change address. You'll just list your recipient and how much you want to send them, in this case `$recipient` and `0.0002` BTC.
|
||||||
```
|
```
|
||||||
$ unfinishedtx=$(bitcoin-cli -named createrawtransaction inputs='''[]''' outputs='''{ "'$recipient'": 1.0 }''')
|
$ recipient=n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi
|
||||||
|
$ unfinishedtx=$(bitcoin-cli -named createrawtransaction inputs='''[]''' outputs='''{ "'$recipient'": 0.0002 }''')
|
||||||
```
|
```
|
||||||
|
|
||||||
## Fund Your Bare Bones Transaction
|
## Fund Your Bare Bones Transaction
|
||||||
@ -34,9 +35,9 @@ You then tell `bitcoin-cli` to fund that bare-bones transaction:
|
|||||||
```
|
```
|
||||||
$ bitcoin-cli -named fundrawtransaction hexstring=$unfinishedtx
|
$ bitcoin-cli -named fundrawtransaction hexstring=$unfinishedtx
|
||||||
{
|
{
|
||||||
"hex": "020000000169847669938c6a66ef790b87ebb6233059609bee4601476c5948db1a4defc9690100000000feffffff02a8e30f05000000001976a914a6f0ee37c44947f4137d56e4aab12f27ad50369188ac00e1f505000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac00000000",
|
"hex": "02000000012db87641c6a21e5a68b20c226428544978e6ac44964d5d8060d7388000c584eb0100000000feffffff02204e0000000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac781e0000000000001600140cc9cdcf45d4ea17f5227a7ead52367aad10a88400000000",
|
||||||
"changepos": 0,
|
"fee": 0.00022200,
|
||||||
"fee": 0.00022600
|
"changepos": 1
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
That provides a lot of useful information, but once you're confident with how it works, you'll want to use JQ to save your hex to a variable, as usual:
|
That provides a lot of useful information, but once you're confident with how it works, you'll want to use JQ to save your hex to a variable, as usual:
|
||||||
@ -51,15 +52,16 @@ Running `decoderawtransaction` will show that the raw transaction is now laid ou
|
|||||||
```
|
```
|
||||||
$ bitcoin-cli -named decoderawtransaction hexstring=$rawtxhex3
|
$ bitcoin-cli -named decoderawtransaction hexstring=$rawtxhex3
|
||||||
{
|
{
|
||||||
"txid": "2e34603b7449d29412fb7b0d184085d4d839d965f2bba361749c20d9dbae3d0b",
|
"txid": "b3b4c2057dbfbef6690e975ede92fde805ddea13c730f58401939a52c9ac1b99",
|
||||||
"hash": "2e34603b7449d29412fb7b0d184085d4d839d965f2bba361749c20d9dbae3d0b",
|
"hash": "b3b4c2057dbfbef6690e975ede92fde805ddea13c730f58401939a52c9ac1b99",
|
||||||
"size": 119,
|
|
||||||
"vsize": 119,
|
|
||||||
"version": 2,
|
"version": 2,
|
||||||
|
"size": 116,
|
||||||
|
"vsize": 116,
|
||||||
|
"weight": 464,
|
||||||
"locktime": 0,
|
"locktime": 0,
|
||||||
"vin": [
|
"vin": [
|
||||||
{
|
{
|
||||||
"txid": "69c9ef4d1adb48596c470146ee9b60593023b6eb870b79ef666a8c9369768469",
|
"txid": "eb84c5008038d760805d4d9644ace67849542864220cb2685a1ea2c64176b82d",
|
||||||
"vout": 1,
|
"vout": 1,
|
||||||
"scriptSig": {
|
"scriptSig": {
|
||||||
"asm": "",
|
"asm": "",
|
||||||
@ -70,21 +72,8 @@ $ bitcoin-cli -named decoderawtransaction hexstring=$rawtxhex3
|
|||||||
],
|
],
|
||||||
"vout": [
|
"vout": [
|
||||||
{
|
{
|
||||||
"value": 0.84927400,
|
"value": 0.00020000,
|
||||||
"n": 0,
|
"n": 0,
|
||||||
"scriptPubKey": {
|
|
||||||
"asm": "OP_DUP OP_HASH160 123cd8796558d195e52137ce3800e5f8120ee46f OP_EQUALVERIFY OP_CHECKSIG",
|
|
||||||
"hex": "76a914123cd8796558d195e52137ce3800e5f8120ee46f88ac",
|
|
||||||
"reqSigs": 1,
|
|
||||||
"type": "pubkeyhash",
|
|
||||||
"addresses": [
|
|
||||||
"mhBPM8hU2PHjDTUvwa3SC7pqv8ExkK6mH8"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": 1.00000000,
|
|
||||||
"n": 1,
|
|
||||||
"scriptPubKey": {
|
"scriptPubKey": {
|
||||||
"asm": "OP_DUP OP_HASH160 e7c1345fc8f87c68170b3aa798a956c2fe6a9eff OP_EQUALVERIFY OP_CHECKSIG",
|
"asm": "OP_DUP OP_HASH160 e7c1345fc8f87c68170b3aa798a956c2fe6a9eff OP_EQUALVERIFY OP_CHECKSIG",
|
||||||
"hex": "76a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac",
|
"hex": "76a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac",
|
||||||
@ -94,30 +83,52 @@ $ bitcoin-cli -named decoderawtransaction hexstring=$rawtxhex3
|
|||||||
"n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi"
|
"n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": 0.00007800,
|
||||||
|
"n": 1,
|
||||||
|
"scriptPubKey": {
|
||||||
|
"asm": "0 a782f4c6e1e75a5b24f3d675d6f11b5ebf3b2142",
|
||||||
|
"hex": "0014a782f4c6e1e75a5b24f3d675d6f11b5ebf3b2142",
|
||||||
|
"reqSigs": 1,
|
||||||
|
"type": "witness_v0_keyhash",
|
||||||
|
"addresses": [
|
||||||
|
"tb1q57p0f3hpuad9kf8n6e6adugmt6lnkg2zzr592r"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
We saw the fee in the more extensive output, before we saved the hex to a variable with JQ, but you can verify it with the `btctxfee` JQ alias:
|
One thing of interest here is the change address, with is the second `vout`. Note that it's a `tb1` address, which means that it's Bech32; when we gave Bitcoin Core the total ability to manage our change, it did so using its default address type, Bech32, and it worked fine. That's why our change to SegWit addresses in [§4.6](04_6_Creating_a_Segwit_Transaction.md) really isn't that big of a deal, but there are some gotchas for wider usage, which we'll talk about there.
|
||||||
|
|
||||||
|
Though we saw the fee in the `fundrawtransaction` output, it's not visible here. However, you can verify it with the `txfee-calc.sh` JQ script created in the [JQ Interlude](https://github.com/BlockchainCommons/Learning-Bitcoin-from-the-Command-Line/blob/master/04_2__Interlude_Using_JQ.md):
|
||||||
```
|
```
|
||||||
$ btctxfee $rawtxhex3
|
$ ~/txfee-calc.sh $rawtxhex3
|
||||||
.00023
|
.000222
|
||||||
```
|
```
|
||||||
Finally, you can use `validateaddress` to see that the generated change address really belongs to you:
|
Finally, you can use `getaddressinfo` to see that the generated change address really belongs to you:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli -named validateaddress address=mhBPM8hU2PHjDTUvwa3SC7pqv8ExkK6mH8
|
$ bitcoin-cli -named getaddressinfo address=tb1q57p0f3hpuad9kf8n6e6adugmt6lnkg2zzr592r
|
||||||
{
|
{
|
||||||
"isvalid": true,
|
"address": "tb1q57p0f3hpuad9kf8n6e6adugmt6lnkg2zzr592r",
|
||||||
"address": "mhBPM8hU2PHjDTUvwa3SC7pqv8ExkK6mH8",
|
"scriptPubKey": "0014a782f4c6e1e75a5b24f3d675d6f11b5ebf3b2142",
|
||||||
"scriptPubKey": "76a914123cd8796558d195e52137ce3800e5f8120ee46f88ac",
|
|
||||||
"ismine": true,
|
"ismine": true,
|
||||||
|
"solvable": true,
|
||||||
|
"desc": "wpkh([d6043800/0'/1'/10']038a2702938e548eaec28feb92c7e4722042cfd1ea16bec9fc274640dc5be05ec5)#zpv26nar",
|
||||||
"iswatchonly": false,
|
"iswatchonly": false,
|
||||||
"isscript": false,
|
"isscript": false,
|
||||||
"pubkey": "029045eaa55d283526c723e6d5495d9b3f077b545563f86465aafcd9bfdd50359e",Y
|
"iswitness": true,
|
||||||
"iscompressed": true,
|
"witness_version": 0,
|
||||||
"timestamp": 1489170694,
|
"witness_program": "a782f4c6e1e75a5b24f3d675d6f11b5ebf3b2142",
|
||||||
"hdkeypath": "m/0'/0'/11'",
|
"pubkey": "038a2702938e548eaec28feb92c7e4722042cfd1ea16bec9fc274640dc5be05ec5",
|
||||||
"hdmasterkeyid": "144a68bde927a1fed7c2b71ad9010b0201819be5"
|
"ischange": true,
|
||||||
|
"timestamp": 1592335137,
|
||||||
|
"hdkeypath": "m/0'/1'/10'",
|
||||||
|
"hdseedid": "fdea8e2630f00d29a9d6ff2af7bf5b358d061078",
|
||||||
|
"hdmasterfingerprint": "d6043800",
|
||||||
|
"labels": [
|
||||||
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Note the `ismine` results.
|
Note the `ismine` results.
|
||||||
@ -128,20 +139,23 @@ At this point you can sign and send the transaction as usual.
|
|||||||
```
|
```
|
||||||
$ signedtx3=$(bitcoin-cli -named signrawtransactionwithwallet hexstring=$rawtxhex3 | jq -r '.hex')
|
$ signedtx3=$(bitcoin-cli -named signrawtransactionwithwallet hexstring=$rawtxhex3 | jq -r '.hex')
|
||||||
$ bitcoin-cli -named sendrawtransaction hexstring=$signedtx3
|
$ bitcoin-cli -named sendrawtransaction hexstring=$signedtx3
|
||||||
|
8b9dd66c999966462a3d88d6ac9405d09e2aa409c0aa830bdd08dbcbd34a36fa
|
||||||
```
|
```
|
||||||
In several minutes, you'll have your change back:
|
In several minutes, you'll have your change back:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli listunspent
|
$ bitcoin-cli listunspent
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"txid": "37698ad6e7f62df07c2fbc549339aa680a7fa18328d7ad14ecb72b21c505cbc6",
|
"txid": "8b9dd66c999966462a3d88d6ac9405d09e2aa409c0aa830bdd08dbcbd34a36fa",
|
||||||
"vout": 0,
|
"vout": 1,
|
||||||
"address": "mhBPM8hU2PHjDTUvwa3SC7pqv8ExkK6mH8",
|
"address": "tb1q57p0f3hpuad9kf8n6e6adugmt6lnkg2zzr592r",
|
||||||
"scriptPubKey": "76a914123cd8796558d195e52137ce3800e5f8120ee46f88ac",
|
"scriptPubKey": "0014a782f4c6e1e75a5b24f3d675d6f11b5ebf3b2142",
|
||||||
"amount": 0.84927400,
|
"amount": 0.00007800,
|
||||||
"confirmations": 1,
|
"confirmations": 1,
|
||||||
"spendable": true,
|
"spendable": true,
|
||||||
"solvable": true
|
"solvable": true,
|
||||||
|
"desc": "wpkh([d6043800/0'/1'/10']038a2702938e548eaec28feb92c7e4722042cfd1ea16bec9fc274640dc5be05ec5)#zpv26nar",
|
||||||
|
"safe": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
@ -150,12 +164,13 @@ $ bitcoin-cli listunspent
|
|||||||
|
|
||||||
If you must send funds with raw transactions then `fundrawtransaction` gives you a nice alternative where fees, inputs, and outputs are calculated for you, so you don't accidentally lose a bunch of money.
|
If you must send funds with raw transactions then `fundrawtransaction` gives you a nice alternative where fees, inputs, and outputs are calculated for you, so you don't accidentally lose a bunch of money.
|
||||||
|
|
||||||
### Why Use Automated Raw Transactions
|
> :fire: ***What the power of sending coins with automated raw transactions?***
|
||||||
|
|
||||||
_The advantages._ It provides a nice balance. If you're sending funds by hand and `sendtoaddress` doesn't offer enough control for whatever reason, you can get some of the advantages of raw transactions without the dangers. This methodology should be used whenever possible if you're sending raw transactions by hand.
|
> _The advantages._ It provides a nice balance. If you're sending funds by hand and `sendtoaddress` doesn't offer enough control for whatever reason, you can get some of the advantages of raw transactions without the dangers. This methodology should be used whenever possible if you're sending raw transactions by hand.
|
||||||
|
|
||||||
_The disadvantages._ It's a hodge-podge. Though there are a few additional options for the `fundrawtransaction` command that weren't mentioned here, your control is still limited. You'd probably never want to use this method if you were writing a program where the whole goal is to know exactly what's going on.
|
> _The disadvantages._ It's a hodge-podge. Though there are a few additional options for the `fundrawtransaction` command that weren't mentioned here, your control is still limited. You'd probably never want to use this method if you were writing a program where the whole goal is to know exactly what's going on.
|
||||||
|
|
||||||
## What's Next?
|
## What's Next?
|
||||||
|
|
||||||
Advance through "bitcoin-cli" with [Chapter Five: Controlling Bitcoin Transactions](05_0_Controlling_Bitcoin_Transactions.md).
|
Complete your "Sending of Bitcoin Transactions" with [§4.6: Creating a Segwit Transaction](04_6_Creating_a_Segwit_Transaction.md).
|
||||||
|
|
||||||
|
290
04_6_Creating_a_Segwit_Transaction.md
Normal file
290
04_6_Creating_a_Segwit_Transaction.md
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
# 4.6: Creating a SegWit Transaction
|
||||||
|
|
||||||
|
> :information_source: **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
||||||
|
|
||||||
|
Once upon a time, the Bitcoin heavens shook with the blocksize wars. Fees were skyrocketing, and users were worried about scaling. The Bitcoin Core developers were reluctant to simply increase the blocksize, but they came upon a compromise: SegWit, the Segregated Witness. Segregated Witness is a fancy way of saying "Separated Signature". It creates new sorts of transactions that remove signatures to the end of the transaction. By combining this with increased block sizes that only are visible to upgraded nodes, SegWit resolved the scaling problems for Bitcoin at the time (and also resolved a nasty malleability bug that had previously made even better scaling with layer-2 protocols like Lightning impractical).
|
||||||
|
|
||||||
|
The catch? SegWit uses different addresses, some of which are compatible with older nodes, and some of which are not.
|
||||||
|
|
||||||
|
> :warning: **VERSION WARNING:** SegWit was introduced in BitCoin 0.16.0 with what was described at the time as "full support". With that said, there were some flaws in its integration with `bitcoin-cli` at the time which prevented signing from working correctly on new P2SH-SegWit addresses. The non-backward-compatible Bech32 address was also introduced in Bitcoin 0.16.0 and was made the default addresstype in Bitcoin 0.19.0. All of this functionality should now fully work with regard to `bitcoin-cli` functions (and thus this tutorial).
|
||||||
|
|
||||||
|
> The catch comes in interacting with the wider world. Everyone should be able to send to a P2SH-SegWit address because it was purposefully built to support backward compatibility by wrapping the SegWit functionality in a Bitcoin Script. The same isn't true for Bech32 addresses: if someone tells you that they're unable to send to your Bech32 address, this is why, and you need to generate a `legacy` or P2SH-SegWit address for their usage. (Many sites, particularly exchanges, can also not generate or receive on SegWit addresses, particularly Bech32 addresses, but that's a whole different issue and doesn't affect your usage of them.)
|
||||||
|
|
||||||
|
## Understand a SegWit Transaction
|
||||||
|
|
||||||
|
In classic transactions, signature (witness) information was stored toward the middle of the transaction, while in SegWit transactions, it's at the bottom. This goes hand-in-hand with the blocksize increases that were introduced in the SegWit upgrade. The blocksize was increased from 1M to a variable amount based on how many SegWit transactions are in a block, starting as low as 1M (no SegWit transactions) and going as high as 4M (all SegWit transactions). This variable sized was created to accomodate classic nodes, so that everything remains backward compatible. If a classic node sees a SegWit tranaction, it throws out the witness information (resulting in a smaller sized block, under the old 1M limit), while if a new node sees a SegWit transaction, it keeps the witness information (resulting in a larger sized block, up to the new 4M limit).
|
||||||
|
|
||||||
|
So that's the what and how of SegWit transactions. Not that you need to know any of it to use them. Most transactions on the BitCoin network are now SegWit. They're what you're going to natively use for more transactions and receipts of money. The details are no more relevant at this point than the details of how most of Bitcoin works.
|
||||||
|
|
||||||
|
## Create a SegWit Address
|
||||||
|
|
||||||
|
You create a SegWit address the same way as any other address, with the `getnewaddress` and the `getrawchangeaddress` commands.
|
||||||
|
|
||||||
|
If you need to create an address for someone who can't send to the newer Bech32 addresses, then use the `p2sh-segwit` addresstype:
|
||||||
|
```
|
||||||
|
$ bitcoin-cli -named getnewaddress address_type=p2sh-segwit
|
||||||
|
2N5h2r4karVqN7uFtpcn8xnA3t5cbpszgyN
|
||||||
|
```
|
||||||
|
Seeing an address with a "2" prefix means that you did it right.
|
||||||
|
|
||||||
|
> :link: **TESTNET vs MAINNET:** "3" for Mainnet.
|
||||||
|
|
||||||
|
However, if the person you're interacting with has a fully mature client, they'll be able to send to a Bech32 address, which you create using the commands in the default way:
|
||||||
|
```
|
||||||
|
$ bitcoin-cli getnewaddress
|
||||||
|
tb1q5gnwrh7ss5mmqt0qfan85jdagmumnatcscwpk6
|
||||||
|
```
|
||||||
|
As we've already seen, change addresses generated from within `bitcoin-cli` interact fine with Bech32 addresses, so there's no point in using the `legacy` flag there either:
|
||||||
|
```
|
||||||
|
$ bitcoin-cli getrawchangeaddress
|
||||||
|
tb1q05wx5tyadm8qe83exdqdyqvqqzjt3m38vfu8ff
|
||||||
|
```
|
||||||
|
|
||||||
|
Here, note that the unique "tb1" prefix denoted Bech32.
|
||||||
|
|
||||||
|
> :link: **TESTNET vs MAINNET:** "bc1" for mainnet.
|
||||||
|
|
||||||
|
Bitcoin-cli doesn't care which address type you're using. You can run a command like `listaddressgroupings` and it will freely mix addresses of the different types:
|
||||||
|
```
|
||||||
|
$ bitcoin-cli listaddressgroupings
|
||||||
|
[
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"mfsiRhxbQxcD7HLS4PiAim99oeGyb9QY7m",
|
||||||
|
0.01000000,
|
||||||
|
""
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"mi25UrzHnvn3bpEfFCNqJhPWJn5b77a5NE",
|
||||||
|
0.00000000,
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"tb1q6dak4e9fz77vsulk89t5z92l2e0zm37yvre4gt",
|
||||||
|
0.00000000
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"mjehC2KHzXcBDcwTd4LhZ2GzyzrZ3Kd3ff",
|
||||||
|
0.00022000,
|
||||||
|
""
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"mk9ry5VVy8mrA8SygxSQQUDNSSXyGFot6h",
|
||||||
|
0.00000000
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"mqjrdY5raxKzXQf5t2VvVvzhvFAgersu9B",
|
||||||
|
0.00000000
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"mwJL7cRiW2bUnY81r1thSu3D4jtMmwyU6d",
|
||||||
|
0.00000000,
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"tb1q57p0f3hpuad9kf8n6e6adugmt6lnkg2zzr592r",
|
||||||
|
0.00007800
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"mpVLL7iqPr4d7BJkEG54mcdm7WmrAhaW6q",
|
||||||
|
0.01000000,
|
||||||
|
""
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"tb1q5gnwrh7ss5mmqt0qfan85jdagmumnatcscwpk6",
|
||||||
|
0.01000000,
|
||||||
|
""
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Send a SegWit Transaction The Easy Way
|
||||||
|
|
||||||
|
So how do you send a Bitcoin transaction? Exactly like any other transaction. It doesn't matter if the UTXO is SegWit, the address is SegWit, or some combination thereof. You can expect `bitcoin-cli` to do the right thing. Though you can tell the differences via the addresses, they don't matter for interacting with things at the `bitcoin-cli` or RPC level. (And this is one of the advantages of using the command line and the RPC interface, as suggested in this tutorial: experts have already done the hard work for you, including things like how to send to both legacy and Bech32 addresses. You just get to use that functionality to your own advantage.)
|
||||||
|
|
||||||
|
Here's an example of sending to a SegWit address, the easy way:
|
||||||
|
```
|
||||||
|
$ bitcoin-cli sendtoaddress tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx 0.005
|
||||||
|
854a833b667049ac811b4cf1cad40fa7f8dce8b0f4c1018a58b84559b6e05f42
|
||||||
|
```
|
||||||
|
If you look at your transaction, you can see the use of the Bech32 address:
|
||||||
|
```
|
||||||
|
$ bitcoin-cli gettransaction txid="854a833b667049ac811b4cf1cad40fa7f8dce8b0f4c1018a58b84559b6e05f42" verbose=true
|
||||||
|
{
|
||||||
|
"amount": -0.00500000,
|
||||||
|
"fee": -0.00036600,
|
||||||
|
"confirmations": 0,
|
||||||
|
"trusted": true,
|
||||||
|
"txid": "854a833b667049ac811b4cf1cad40fa7f8dce8b0f4c1018a58b84559b6e05f42",
|
||||||
|
"walletconflicts": [
|
||||||
|
],
|
||||||
|
"time": 1592948795,
|
||||||
|
"timereceived": 1592948795,
|
||||||
|
"bip125-replaceable": "no",
|
||||||
|
"details": [
|
||||||
|
{
|
||||||
|
"address": "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx",
|
||||||
|
"category": "send",
|
||||||
|
"amount": -0.00500000,
|
||||||
|
"vout": 1,
|
||||||
|
"fee": -0.00036600,
|
||||||
|
"abandoned": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hex": "0200000002114d5a4c3b847bc796b2dc166ca7120607b874aa6904d4a43dd5f9e0ea79d4ba010000006a47304402200a3cc08b9778e7b616340d4cf7841180321d2fa019e43f25e7f710d9a628b55c02200541fc200a07f2eb073ad8554357777d5f1364c5a96afe5e77c6185d66a40fa7012103ee18c598bafc5fbea72d345329803a40ebfcf34014d0e96aac4f504d54e7042dfeffffffa71321e81ef039af490251379143f7247ad91613c26c8f3e3404184218361733000000006a47304402200dd80206b57beb5fa38a3c3578f4b0e40d56d4079116fd2a6fe28e5b8ece72310220298a8c3a1193ea805b27608ff67a2d8b01e347e33a4222edfba499bb1b64a31601210339c001b00dd607eeafd4c117cfcf86be8efbb0ca0a33700cffc0ae0c6ee69d7efeffffff026854160000000000160014d591091b8074a2375ed9985a9c4b18efecfd416520a1070000000000160014751e76e8199196d454941c45d1b3a323f1433bd6c60e1b00",
|
||||||
|
"decoded": {
|
||||||
|
"txid": "854a833b667049ac811b4cf1cad40fa7f8dce8b0f4c1018a58b84559b6e05f42",
|
||||||
|
"hash": "854a833b667049ac811b4cf1cad40fa7f8dce8b0f4c1018a58b84559b6e05f42",
|
||||||
|
"version": 2,
|
||||||
|
"size": 366,
|
||||||
|
"vsize": 366,
|
||||||
|
"weight": 1464,
|
||||||
|
"locktime": 1773254,
|
||||||
|
"vin": [
|
||||||
|
{
|
||||||
|
"txid": "bad479eae0f9d53da4d40469aa74b8070612a76c16dcb296c77b843b4c5a4d11",
|
||||||
|
"vout": 1,
|
||||||
|
"scriptSig": {
|
||||||
|
"asm": "304402200a3cc08b9778e7b616340d4cf7841180321d2fa019e43f25e7f710d9a628b55c02200541fc200a07f2eb073ad8554357777d5f1364c5a96afe5e77c6185d66a40fa7[ALL] 03ee18c598bafc5fbea72d345329803a40ebfcf34014d0e96aac4f504d54e7042d",
|
||||||
|
"hex": "47304402200a3cc08b9778e7b616340d4cf7841180321d2fa019e43f25e7f710d9a628b55c02200541fc200a07f2eb073ad8554357777d5f1364c5a96afe5e77c6185d66a40fa7012103ee18c598bafc5fbea72d345329803a40ebfcf34014d0e96aac4f504d54e7042d"
|
||||||
|
},
|
||||||
|
"sequence": 4294967294
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"txid": "33173618421804343e8f6cc21316d97a24f7439137510249af39f01ee82113a7",
|
||||||
|
"vout": 0,
|
||||||
|
"scriptSig": {
|
||||||
|
"asm": "304402200dd80206b57beb5fa38a3c3578f4b0e40d56d4079116fd2a6fe28e5b8ece72310220298a8c3a1193ea805b27608ff67a2d8b01e347e33a4222edfba499bb1b64a316[ALL] 0339c001b00dd607eeafd4c117cfcf86be8efbb0ca0a33700cffc0ae0c6ee69d7e",
|
||||||
|
"hex": "47304402200dd80206b57beb5fa38a3c3578f4b0e40d56d4079116fd2a6fe28e5b8ece72310220298a8c3a1193ea805b27608ff67a2d8b01e347e33a4222edfba499bb1b64a31601210339c001b00dd607eeafd4c117cfcf86be8efbb0ca0a33700cffc0ae0c6ee69d7e"
|
||||||
|
},
|
||||||
|
"sequence": 4294967294
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"vout": [
|
||||||
|
{
|
||||||
|
"value": 0.01463400,
|
||||||
|
"n": 0,
|
||||||
|
"scriptPubKey": {
|
||||||
|
"asm": "0 d591091b8074a2375ed9985a9c4b18efecfd4165",
|
||||||
|
"hex": "0014d591091b8074a2375ed9985a9c4b18efecfd4165",
|
||||||
|
"reqSigs": 1,
|
||||||
|
"type": "witness_v0_keyhash",
|
||||||
|
"addresses": [
|
||||||
|
"tb1q6kgsjxuqwj3rwhkenpdfcjccalk06st9z0k0kh"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": 0.00500000,
|
||||||
|
"n": 1,
|
||||||
|
"scriptPubKey": {
|
||||||
|
"asm": "0 751e76e8199196d454941c45d1b3a323f1433bd6",
|
||||||
|
"hex": "0014751e76e8199196d454941c45d1b3a323f1433bd6",
|
||||||
|
"reqSigs": 1,
|
||||||
|
"type": "witness_v0_keyhash",
|
||||||
|
"addresses": [
|
||||||
|
"tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
In fact, both of the `vouts` use Bech32 addresses: your recipient and the automatically generated change address.
|
||||||
|
|
||||||
|
But when we backtrack our `vin`, we discover that came from a legacy address. Because it doesn't matter:
|
||||||
|
```
|
||||||
|
$ bitcoin-cli -named gettransaction txid="33173618421804343e8f6cc21316d97a24f7439137510249af39f01ee82113a7"
|
||||||
|
{
|
||||||
|
"amount": 0.01000000,
|
||||||
|
"confirmations": 43,
|
||||||
|
"blockhash": "00000000000000e2365d2f814d1774b063d9a04356f482010cdfdd537b1a24bb",
|
||||||
|
"blockheight": 1773212,
|
||||||
|
"blockindex": 103,
|
||||||
|
"blocktime": 1592937103,
|
||||||
|
"txid": "33173618421804343e8f6cc21316d97a24f7439137510249af39f01ee82113a7",
|
||||||
|
"walletconflicts": [
|
||||||
|
],
|
||||||
|
"time": 1592936845,
|
||||||
|
"timereceived": 1592936845,
|
||||||
|
"bip125-replaceable": "no",
|
||||||
|
"details": [
|
||||||
|
{
|
||||||
|
"address": "mpVLL7iqPr4d7BJkEG54mcdm7WmrAhaW6q",
|
||||||
|
"category": "receive",
|
||||||
|
"amount": 0.01000000,
|
||||||
|
"label": "",
|
||||||
|
"vout": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hex": "020000000001016a66efa334f06e2c54963e48d049a35d7a1bda44633b7464621cae302f35174a0100000017160014f17b16c6404e85165af6f123173e0705ba31ec25feffffff0240420f00000000001976a914626ab1ca41d98f597d18d1ff8151e31a40d4967288acd2125d000000000017a914d5e76abfe5362704ff6bbb000db9cdfa43cd2881870247304402203b3ba83f51c1895b5f639e9bfc40124715e2495ef2c79d4e49c0f8f70fbf2feb02203d50710abe3cf37df4d2a73680dadf3cecbe4f2b5d0b276dbe7711d0c2fa971a012102e64f83ee1c6548bcf44cb965ffdb803f30224459bd2e57a5df97cb41ba476b119b0e1b00"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Send a SegWit Transaction The Hard Way
|
||||||
|
|
||||||
|
You can similarly fund a transaction with a Bech32 address with no difference to the techniques you've learned so far. Here's an exactly of doing so with a complete raw transaction:
|
||||||
|
```
|
||||||
|
$ changeaddress=$(bitcoin-cli getrawchangeaddress)
|
||||||
|
$ echo $changeaddress
|
||||||
|
tb1q4xje3mx9xn7f8khv7p69ekfn0q72kfs8x3ay4j
|
||||||
|
$ bitcoin-cli listunspent
|
||||||
|
[
|
||||||
|
...
|
||||||
|
{
|
||||||
|
"txid": "003bfdca5578c0045a76768281f05d5e6f57774be399a76f387e2a0e99e4e452",
|
||||||
|
"vout": 0,
|
||||||
|
"address": "tb1q5gnwrh7ss5mmqt0qfan85jdagmumnatcscwpk6",
|
||||||
|
"label": "",
|
||||||
|
"scriptPubKey": "0014a226e1dfd08537b02de04f667a49bd46f9b9f578",
|
||||||
|
"amount": 0.01000000,
|
||||||
|
"confirmations": 5,
|
||||||
|
"spendable": true,
|
||||||
|
"solvable": true,
|
||||||
|
"desc": "wpkh([d6043800/0'/0'/5']0327dbe2d58d9ed2dbeca28cd26e18f48aa94c127fa6fb4b60e4188f6360317640)#hd66hknp",
|
||||||
|
"safe": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
$ recipient=tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx
|
||||||
|
$ utxo_txid=$(bitcoin-cli listunspent | jq -r '.[2] | .txid')
|
||||||
|
$ utxo_vout=$(bitcoin-cli listunspent | jq -r '.[2] | .vout')
|
||||||
|
$ echo $utxo_txid $utxo_vout
|
||||||
|
003bfdca5578c0045a76768281f05d5e6f57774be399a76f387e2a0e99e4e452 0
|
||||||
|
$ rawtxhex=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]''' outputs='''{ "'$recipient'": 0.002, "'$changeaddress'": 0.007 }''')
|
||||||
|
$ signedtx=$(bitcoin-cli -named signrawtransactionwithwallet hexstring=$rawtxhex | jq -r '.hex')
|
||||||
|
$ bitcoin-cli -named sendrawtransaction hexstring=$signedtx
|
||||||
|
e02568b706b21bcb56fcf9c4bb7ba63fdbdec1cf2866168c4f50bc0ad693f26c
|
||||||
|
```
|
||||||
|
It all works exactly the same as other sorts of transactions!
|
||||||
|
|
||||||
|
### Recognize the New Descriptor
|
||||||
|
|
||||||
|
If you look at the `desc` field, you'll note that the SegWit address has a different style descriptor than those encountered in [§3.5: Understanding the Descriptor](03_5_Understanding_the_Descriptor.md). A legacy descriptor described in that section looked like this: `pkh([d6043800/0'/0'/18']03efdee34c0009fd175f3b20b5e5a5517fd5d16746f2e635b44617adafeaebc388)#4ahsl9pk`. Our new SegWit descriptor instead looks like this: `wpkh([d6043800/0'/0'/5']0327dbe2d58d9ed2dbeca28cd26e18f48aa94c127fa6fb4b60e4188f6360317640)#hd66hknp"`.
|
||||||
|
|
||||||
|
The big thing to note is that function has changed. It was previously `pkh`, which is a standard P2PKH hashed public-key address. The SegWit address is instead `wpkh`, which means that it's a P2WPKH native SegWit address. This underlines the :fire: ***power of descriptors***. They describe how to create an address from a key or other information, with the functions unambiguously defining how to make the address based on its type.
|
||||||
|
|
||||||
|
## Summary: Creating a SegWit Transaction
|
||||||
|
|
||||||
|
There's really no complexity to creating SegWit transactions. Internally, they're structured differently from legacy transactions, but from the command line there's no difference: you just use an address with a different prefix. The only thing to watch for is that some people may not be able to send to a Bech32 address if they're using obsolete software.
|
||||||
|
|
||||||
|
> :fire: ***What the power of sending coins with SegWit?***
|
||||||
|
|
||||||
|
> _The Advantages._ SegWit transactions are smaller, and so will be cheaper to send than legacy transactions due to lower fees. Bech32 doubles down on this advantage, and also creates addresses that are harder to foul up when transcribing — and that's pretty important, given that user error is one of the most likely ways to lose your bitcoins.
|
||||||
|
|
||||||
|
> _The Disadvantages._ SegWit addresses may not be supported by obsolete Bitcoin software. In particular, people may not be able to send to your Bech32 address.
|
||||||
|
|
||||||
|
## What's Next?
|
||||||
|
|
||||||
|
Advance through "bitcoin-cli" with [Chapter Five: Controlling Bitcoin Transactions](05_0_Controlling_Bitcoin_Transactions.md).
|
@ -10,35 +10,33 @@ You should _always_ watch to ensure that your transactions go out. `bitcoin-cli
|
|||||||
|
|
||||||
The following shows a transaction that has not been put into a block. You can tell this because it has no confirmations.
|
The following shows a transaction that has not been put into a block. You can tell this because it has no confirmations.
|
||||||
```
|
```
|
||||||
$ bitcoin-cli -named gettransaction txid=0f618e38efe887028a5dd04d0e12241431978b4de32f70308c13a114d7cfcbd2
|
$ bitcoin-cli -named gettransaction txid=fa2ddf84a4a632586d435e10880a2921db6310dfbd6f0f8f583aa0feacb74c8e
|
||||||
{
|
{
|
||||||
"amount": -0.84927000,
|
"amount": -0.00020000,
|
||||||
"fee": -0.00000400,
|
"fee": -0.00001000,
|
||||||
"confirmations": 0,
|
"confirmations": 0,
|
||||||
"trusted": true,
|
"trusted": true,
|
||||||
"txid": "0f618e38efe887028a5dd04d0e12241431978b4de32f70308c13a114d7cfcbd2",
|
"txid": "fa2ddf84a4a632586d435e10880a2921db6310dfbd6f0f8f583aa0feacb74c8e",
|
||||||
"walletconflicts": [
|
"walletconflicts": [
|
||||||
],
|
],
|
||||||
"time": 1491588722,
|
"time": 1592953220,
|
||||||
"timereceived": 1491588722,
|
"timereceived": 1592953220,
|
||||||
"bip125-replaceable": "no",
|
"bip125-replaceable": "no",
|
||||||
"details": [
|
"details": [
|
||||||
{
|
{
|
||||||
"account": "",
|
"address": "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx",
|
||||||
"address": "n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi",
|
|
||||||
"category": "send",
|
"category": "send",
|
||||||
"amount": -0.84927000,
|
"amount": -0.00020000,
|
||||||
"vout": 0,
|
"vout": 0,
|
||||||
"fee": -0.00000400,
|
"fee": -0.00001000,
|
||||||
"abandoned": false
|
"abandoned": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hex": "0200000001c6cb05c5212bb7ec14add72883a17f0a68aa399354bc2f7cf02df6e7d68a6937000000006a47304402203f21d769cbf3cf1626ef09bb35d0d8e88efe9f14d097f4b493628e96b2e1c90b0220162ff60701525e70942f5090dc48ec2b2f3b87cd40185351ab316991567f61c50121029045eaa55d283526c723e6d5495d9b3f077b545563f86465aafcd9bfdd50359effffffff0118e20f05000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac00000000"
|
"hex": "02000000014cda1f42a1bd39d8d0ff5958a804bc2bc548b71d7ceadbde53ea15aeaf1e2691000000006a473044022016a7a9f045a0f6a52129f48adb7da35c2f54a0741d6614e9d55b8a3bc3e1490a0220391e9085a3697bc790e94bb924d5310e16f23489d9c600864a32674e871f523c01210278608b54b8fb0d8379d3823d31f03a7c6ab0adffb07dd3811819fdfc34f8c132ffffffff02204e000000000000160014751e76e8199196d454941c45d1b3a323f1433bd6e8030000000000001600146c45d3afa8762086c4bd76d8a71ac7c976e1919600000000"
|
||||||
}
|
|
||||||
```
|
```
|
||||||
A transaction can be considered stuck if it stays in this state for an extended amount of time. Not too many years ago, you could be sure that every transaction would go out _eventually_. But, that's no longer the case due to the increased usage of Bitcoin. Now, if a transaction is stuck too long, it will drop out of the mempool and then be lost from the Bitcoin network.
|
A transaction can be considered stuck if it stays in this state for an extended amount of time. Not too many years ago, you could be sure that every transaction would go out _eventually_. But, that's no longer the case due to the increased usage of Bitcoin. Now, if a transaction is stuck too long, it will drop out of the mempool and then be lost from the Bitcoin network.
|
||||||
|
|
||||||
_What is mempool?_ Mempool (or Memory Pool) is a pool of all unconfirmed transactions at a bitcoin node. These are the transactions that a node has received from the p2p network which are not yet included in a block. Each bitcoin node can have a slightly different set of transactions in its mempool: different transactions might have propogated to a specific node. This depends on when the node was last started and also its limits on how much it's willing to store. When a miner makes a block, he uses transactions from his mempool. Then, when a block is verified, all the miners remove the transactions it contains from their pools. As of Bitcoin 0.12, unconfirmed transactions can also expire from mempools if they're old enough, typically, 72 hours, and as of version 0.14.0 eviction time was increased to 2 weeks. Mining pools might have their own mempool-management mechanisms.
|
> :book: ***What is mempool?*** Mempool (or Memory Pool) is a pool of all unconfirmed transactions at a bitcoin node. These are the transactions that a node has received from the p2p network which are not yet included in a block. Each bitcoin node can have a slightly different set of transactions in its mempool: different transactions might have propogated to a specific node. This depends on when the node was last started and also its limits on how much it's willing to store. When a miner makes a block, he uses transactions from his mempool. Then, when a block is verified, all the miners remove the transactions it contains from their pools. As of Bitcoin 0.12, unconfirmed transactions can also expire from mempools if they're old enough, typically, 72 hours, and as of version 0.14.0 eviction time was increased to 2 weeks. Mining pools might have their own mempool-management mechanisms.
|
||||||
|
|
||||||
This list of all [unconfirmed transactions](https://blockchain.info/unconfirmed-transactions) might not match any individual machine's mempool, but it should (mostly) be a superset of them.
|
This list of all [unconfirmed transactions](https://blockchain.info/unconfirmed-transactions) might not match any individual machine's mempool, but it should (mostly) be a superset of them.
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
If your Bitcoin transaction is stuck, and you're sender, you can resend it using RBF (replace-by-fee). However, that's not all that RBF can do: it's generally a powerful and multipurpose feature that allows Bitcoin senders to recreate transactions for a variety of reasons.
|
If your Bitcoin transaction is stuck, and you're sender, you can resend it using RBF (replace-by-fee). However, that's not all that RBF can do: it's generally a powerful and multipurpose feature that allows Bitcoin senders to recreate transactions for a variety of reasons.
|
||||||
|
|
||||||
> :warning: **VERSION WARNING:** This is an innovation from Bitcoin Core v 0.12.0,that reached full maturity in the Bitcoin core wallet with Bitcoin Core v 0.14.0. Obviously, most people should be using it by now.
|
> :warning: **VERSION WARNING:** This is an innovation from Bitcoin Core v 0.12.0,that reached full maturity in the Bitcoin Core wallet with Bitcoin Core v 0.14.0. Obviously, most people should be using it by now.
|
||||||
|
|
||||||
## Opt-In for RBF
|
## Opt-In for RBF
|
||||||
|
|
||||||
@ -12,45 +12,37 @@ RBF is an opt-in Bitcoin feature. Transactions are only eligible for using RBF i
|
|||||||
|
|
||||||
This is accomplished simply by adding a `sequence` variable to your UTXO inputs:
|
This is accomplished simply by adding a `sequence` variable to your UTXO inputs:
|
||||||
```
|
```
|
||||||
$ rawtxhex=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout', "sequence": 1 } ]''' outputs='''{ "'$recipient'": 0.1, "'$changeaddress'": 0.9 }''')
|
$ rawtxhex=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout', "sequence": 1 } ]''' outputs='''{ "'$recipient'": 0.00007658, "'$changeaddress'": 0.00000001 }''')
|
||||||
```
|
```
|
||||||
You should of course sign and send your transaction as usual:
|
You should of course sign and send your transaction as usual:
|
||||||
```
|
```
|
||||||
$ signedtx=$(bitcoin-cli -named signrawtransactionwithwallet hexstring=$rawtxhex | jq -r '.hex')
|
$ signedtx=$(bitcoin-cli -named signrawtransactionwithwallet hexstring=$rawtxhex | jq -r '.hex')
|
||||||
$ bitcoin-cli -named sendrawtransaction hexstring=$signedtx
|
standup@btctest20:~$ bitcoin-cli -named sendrawtransaction hexstring=$signedtx
|
||||||
7218b78ad4853eb957b610033b8e1ef48b01d948e0ec5dbf79f12caebc2b17e2
|
5b953a0bdfae0d11d20d195ea43ab7c31a5471d2385c258394f3bb9bb3089375
|
||||||
```
|
```
|
||||||
Now, when you look at your transaction, you should see something new: the `bip125-replaceable` line, which has always been marked `no` before, is now marked `yes`:
|
Now, when you look at your transaction, you should see something new: the `bip125-replaceable` line, which has always been marked `no` before, is now marked `yes`:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli -named gettransaction txid=7218b78ad4853eb957b610033b8e1ef48b01d948e0ec5dbf79f12caebc2b17e2
|
$ bitcoin-cli -named gettransaction txid=5b953a0bdfae0d11d20d195ea43ab7c31a5471d2385c258394f3bb9bb3089375
|
||||||
|
|
||||||
{
|
{
|
||||||
"amount": -0.10000000,
|
"amount": 0.00000000,
|
||||||
"fee": 0.00000000,
|
"fee": -0.00000141,
|
||||||
"confirmations": 0,
|
"confirmations": 0,
|
||||||
"trusted": true,
|
"trusted": true,
|
||||||
"txid": "7218b78ad4853eb957b610033b8e1ef48b01d948e0ec5dbf79f12caebc2b17e2",
|
"txid": "5b953a0bdfae0d11d20d195ea43ab7c31a5471d2385c258394f3bb9bb3089375",
|
||||||
"walletconflicts": [
|
"walletconflicts": [
|
||||||
],
|
],
|
||||||
"time": 1491603320,
|
"time": 1592954399,
|
||||||
"timereceived": 1491603320,
|
"timereceived": 1592954399,
|
||||||
"bip125-replaceable": "yes",
|
"bip125-replaceable": "yes",
|
||||||
"details": [
|
"details": [
|
||||||
{
|
|
||||||
"account": "",
|
|
||||||
"address": "n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi",
|
|
||||||
"category": "send",
|
|
||||||
"amount": -0.10000000,
|
|
||||||
"vout": 0,
|
|
||||||
"fee": 0.00000000,
|
|
||||||
"abandoned": false
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"hex": "02000000014e843e22cb8ee522fbf4d8a0967a733685d2ad92697e63f52ce41bec8f7c8ac0010000006b483045022100834731cd64efcc078d6c3e59cf0963599ffbc44722b7851b0404bb68e4a1fec70220759a0887ea791592c8119bbe61842eb3850a20cdf8433b4ba00d4ead752facfe012103456575f59a127a4c3e79c23f185899fa0a9ccd40162d05617fb112fa31bd14e5010000000280969800000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac804a5d05000000001976a914c101d8c34de7b8d83b3f8d75416ffaea871d664988ac00000000"
|
"hex": "02000000000101fa364ad3cbdb08dd0b83aac009a42a9ed00594acd6883d2a466699996cd69d8b01000000000100000002ea1d000000000000160014d591091b8074a2375ed9985a9c4b18efecfd416501000000000000001600146c45d3afa8762086c4bd76d8a71ac7c976e1919602473044022077007dff4df9ce75430e3065c82321dca9f6bdcfd5812f8dc0daeb957d3dfd1602203a624d4e9720a06def613eeea67fbf13ce1fb6188d3b7e780ce6e40e859f275d0121038a2702938e548eaec28feb92c7e4722042cfd1ea16bec9fc274640dc5be05ec500000000"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
The `bip125-replaceable` flag will stay `yes` until the transaction receives confirmations. At that point, it is no longer replacable.
|
The `bip125-replaceable` flag will stay `yes` until the transaction receives confirmations. At that point, it is no longer replacable.
|
||||||
|
|
||||||
_Should I trust transactions with no confirmations?_ No, never. This was true before RBF and it was true after RBF. Transactions must receive confirmations before they are trustworthy. This is especially true if a transaction is marked as `bip125-replaceable`, because then it can be ... replaced.
|
> :book: ***Should I trust transactions with no confirmations?*** No, never. This was true before RBF and it was true after RBF. Transactions must receive confirmations before they are trustworthy. This is especially true if a transaction is marked as `bip125-replaceable`, because then it can be ... replaced.
|
||||||
|
|
||||||
> :information_source: **NOTE — SEQUENCE:** This is the first use of the `nSequence` value in Bitcoin. You can set it between 1 and 0xffffffff-2 (4294967293) and enable RBF, but if you're not careful you can run up against the parallel use of `nSequence` for relative timelocks. We suggest always setting it to "1", which is what Bitcoin Core does, but the other option is to set it to a value between 0xf0000000 (4026531840) and 0xffffffff-2 (4294967293). Setting it to "1" effectively makes relative timelocks irrelevent and setting it to 0xf0000000 or higher deactivates them. This is all explained further in [§9.3: Using CSV in Scripts](09_3_Using_CSV_in_Scripts.md). For now, just choose one of the non-conflicting values for `nSequence`.
|
> :information_source: **NOTE — SEQUENCE:** This is the first use of the `nSequence` value in Bitcoin. You can set it between 1 and 0xffffffff-2 (4294967293) and enable RBF, but if you're not careful you can run up against the parallel use of `nSequence` for relative timelocks. We suggest always setting it to "1", which is what Bitcoin Core does, but the other option is to set it to a value between 0xf0000000 (4026531840) and 0xffffffff-2 (4294967293). Setting it to "1" effectively makes relative timelocks irrelevent and setting it to 0xf0000000 or higher deactivates them. This is all explained further in [§9.3: Using CSV in Scripts](09_3_Using_CSV_in_Scripts.md). For now, just choose one of the non-conflicting values for `nSequence`.
|
||||||
|
|
||||||
@ -58,56 +50,6 @@ _Should I trust transactions with no confirmations?_ No, never. This was true be
|
|||||||
|
|
||||||
If you prefer, you can _always_ opt in for RBF. Do so by running your `bitcoind` with the `-walletrbf` command. Once you've done this (and restarted your `bitcoind`), then all UTXOs should have a lower sequence number and the transaction should be marked as `bip125-replaceable`.
|
If you prefer, you can _always_ opt in for RBF. Do so by running your `bitcoind` with the `-walletrbf` command. Once you've done this (and restarted your `bitcoind`), then all UTXOs should have a lower sequence number and the transaction should be marked as `bip125-replaceable`.
|
||||||
|
|
||||||
Note the following example generated by `bitcoind`, where the UTXO indeed uses a sequence number of "1":
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"txid": "d261b9494eb29084f668e1abd75d331fc2d6525dd206b2f5236753b5448ca12c",
|
|
||||||
"hash": "d261b9494eb29084f668e1abd75d331fc2d6525dd206b2f5236753b5448ca12c",
|
|
||||||
"size": 226,
|
|
||||||
"vsize": 226,
|
|
||||||
"version": 2,
|
|
||||||
"locktime": 0,
|
|
||||||
"vin": [
|
|
||||||
{
|
|
||||||
"txid": "4075dbf84303c01adcb0b36cd2c164e2b447192c2d9fbf5fde3b99d0ac7e64b6",
|
|
||||||
"vout": 1,
|
|
||||||
"scriptSig": {
|
|
||||||
"asm": "3045022100b3a0d66abe3429f81a6dc397369d6ac9cb025a2243b68649d95665967fe4365b022038cf037aaab9368268e97203494d1b542e83101e6aaaf97957daf70dee6ee0af[ALL] 022615f4b6417b991530df4bc8c8ee10b8925c741773fead7a5edd89337caeba53",
|
|
||||||
"hex": "483045022100b3a0d66abe3429f81a6dc397369d6ac9cb025a2243b68649d95665967fe4365b022038cf037aaab9368268e97203494d1b542e83101e6aaaf97957daf70dee6ee0af0121022615f4b6417b991530df4bc8c8ee10b8925c741773fead7a5edd89337caeba53"
|
|
||||||
},
|
|
||||||
"sequence": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"vout": [
|
|
||||||
{
|
|
||||||
"value": 0.10000000,
|
|
||||||
"n": 0,
|
|
||||||
"scriptPubKey": {
|
|
||||||
"asm": "OP_DUP OP_HASH160 e7c1345fc8f87c68170b3aa798a956c2fe6a9eff OP_EQUALVERIFY OP_CHECKSIG",
|
|
||||||
"hex": "76a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac",
|
|
||||||
"reqSigs": 1,
|
|
||||||
"type": "pubkeyhash",
|
|
||||||
"addresses": [
|
|
||||||
"n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": 0.90000000,
|
|
||||||
"n": 1,
|
|
||||||
"scriptPubKey": {
|
|
||||||
"asm": "OP_DUP OP_HASH160 20219e4f3c6bc0f6524d538009e980091b3613e8 OP_EQUALVERIFY OP_CHECKSIG",
|
|
||||||
"hex": "76a91420219e4f3c6bc0f6524d538009e980091b3613e888ac",
|
|
||||||
"reqSigs": 1,
|
|
||||||
"type": "pubkeyhash",
|
|
||||||
"addresses": [
|
|
||||||
"miSrC3FvkPPZgqqvCiQycq7io7wTSVsAFH"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
> :warning: **VERSION WARNING:** The walletrbf flag require Bitcoin Core v.0.14.0.
|
> :warning: **VERSION WARNING:** The walletrbf flag require Bitcoin Core v.0.14.0.
|
||||||
|
|
||||||
## Understand How RBF Works
|
## Understand How RBF Works
|
||||||
@ -123,13 +65,13 @@ This means that the sequence number must be set to less than 0xffffffff-1. (4294
|
|||||||
> 4. The replacement transaction must pay for its own bandwidth in addition to the amount paid by the original transactions at or above the rate set by the node's minimum relay fee setting. For example, if the minimum relay fee is 1 satoshi/byte and the replacement transaction is 500 bytes total, then the replacement must pay a fee at least 500 satoshis higher than the sum of the originals.
|
> 4. The replacement transaction must pay for its own bandwidth in addition to the amount paid by the original transactions at or above the rate set by the node's minimum relay fee setting. For example, if the minimum relay fee is 1 satoshi/byte and the replacement transaction is 500 bytes total, then the replacement must pay a fee at least 500 satoshis higher than the sum of the originals.
|
||||||
> 5. The number of original transactions to be replaced and their descendant transactions which will be evicted from the mempool must not exceed a total of 100 transactions.
|
> 5. The number of original transactions to be replaced and their descendant transactions which will be evicted from the mempool must not exceed a total of 100 transactions.
|
||||||
|
|
||||||
_What is a BIP?_ A BIP is a Bitcoin Improvement Proposal. It's an in-depth suggestion for a change to the Bitcoin Core code. Often, when a BIP has been sufficiently discussed and updated, it will become an actual part of the Bitcoin Core code. For example, BIP 125 was implemented in Bitcoin Core 0.12.0.
|
> :book: ***What is a BIP?*** A BIP is a Bitcoin Improvement Proposal. It's an in-depth suggestion for a change to the Bitcoin Core code. Often, when a BIP has been sufficiently discussed and updated, it will become an actual part of the Bitcoin Core code. For example, BIP 125 was implemented in Bitcoin Core 0.12.0.
|
||||||
|
|
||||||
The other thing to understand about RBF is that in order to use it, you must double-spend, reusing one or more the same UTXOs. Just sending another transaction with a different UTXO to the same recipient won't do the trick (and will likely result in your losing money). Instead, you must purposefully create a conflict, where the same UTXO is used in two different transactions.
|
The other thing to understand about RBF is that in order to use it, you must double-spend, reusing one or more the same UTXOs. Just sending another transaction with a different UTXO to the same recipient won't do the trick (and will likely result in your losing money). Instead, you must purposefully create a conflict, where the same UTXO is used in two different transactions.
|
||||||
|
|
||||||
Faced with this conflict, the miners will know to use the one with the higher fee, and they'll be incentivized to do so by that higher fee.
|
Faced with this conflict, the miners will know to use the one with the higher fee, and they'll be incentivized to do so by that higher fee.
|
||||||
|
|
||||||
_What is a double-spend?_ A double-spend occurs when someone sends the same electronic funds to two different people (or, to the same person twice, in two different transactions). This is a central problem for any e-cash system. It's solved in Bitcoin by the immutable ledger: once a transaction is sufficiently confirmed, no miners will verify transactions that reuse the same UTXO. However, it's possible to double-spend _before_ a transaction has been confirmed — which is why you always want one or more confirmations before you finalize a transaction. In the case of RBF, you purposefully double-spend because an initial transaction has stalled, and the miners accept your double-spend if you meet the specific criteria laid out by BIP 125.
|
> :book: ***What is a double-spend?*** A double-spend occurs when someone sends the same electronic funds to two different people (or, to the same person twice, in two different transactions). This is a central problem for any e-cash system. It's solved in Bitcoin by the immutable ledger: once a transaction is sufficiently confirmed, no miners will verify transactions that reuse the same UTXO. However, it's possible to double-spend _before_ a transaction has been confirmed — which is why you always want one or more confirmations before you finalize a transaction. In the case of RBF, you purposefully double-spend because an initial transaction has stalled, and the miners accept your double-spend if you meet the specific criteria laid out by BIP 125.
|
||||||
|
|
||||||
> :warning: **WARNING:** Some early discussions of this policy suggested that the `nSequence` number also be increased. This in fact was the intended use of `nSequence` in its original form. This is _not_ a part of the published policy in BIP 125. In fact, increasing your sequence number can accidentally lock your transaction with a relative timelock, unless you use sequence numbers in the range of 0xf0000000 (4026531840) to 0xffffffff-2 (4294967293).
|
> :warning: **WARNING:** Some early discussions of this policy suggested that the `nSequence` number also be increased. This in fact was the intended use of `nSequence` in its original form. This is _not_ a part of the published policy in BIP 125. In fact, increasing your sequence number can accidentally lock your transaction with a relative timelock, unless you use sequence numbers in the range of 0xf0000000 (4026531840) to 0xffffffff-2 (4294967293).
|
||||||
|
|
||||||
@ -139,74 +81,77 @@ In order to create an RBF transaction by hand, all you have to do is create a ra
|
|||||||
|
|
||||||
The following example just reuses our existing variables, but decreases the amount sent to the change address, to increase the fee from the accidental 0 BTC of the original transaction to an overly generous 0.01 BTC in the new transaction:
|
The following example just reuses our existing variables, but decreases the amount sent to the change address, to increase the fee from the accidental 0 BTC of the original transaction to an overly generous 0.01 BTC in the new transaction:
|
||||||
```
|
```
|
||||||
$ rawtxhex=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout', "sequence": 1 } ]''' outputs='''{ "'$recipient'": 0.1, "'$changeaddress'": 0.89 }''')
|
$ rawtxhex=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout', "sequence": 1 } ]''' outputs='''{ "'$recipient'": 0.000075, "'$changeaddress'": 0.00000001 }''')
|
||||||
```
|
```
|
||||||
We of course must re-sign it and resend it:
|
We of course must re-sign it and resend it:
|
||||||
```
|
```
|
||||||
$ signedtx=$(bitcoin-cli -named signrawtransactionwithwallet hexstring=$rawtxhex | jq -r '.hex')
|
$ signedtx=$(bitcoin-cli -named signrawtransactionwithwallet hexstring=$rawtxhex | jq -r '.hex')
|
||||||
$ bitcoin-cli -named sendrawtransaction hexstring=$signedtx
|
$ bitcoin-cli -named sendrawtransaction hexstring=$signedtx
|
||||||
959b0b0f4c8350e9038279dfe0f5ae7b165660cc1281e37bea08d0bd084edb39
|
c6de60427b28d8ec8102e49771e5d0348fc3ef6a5bf02eb864ec745105a6951b
|
||||||
```
|
```
|
||||||
After several blocks have been created, the original transaction continues to hang around:
|
You can now look at your original transaction and see that it has `walletconflicts`:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli -named gettransaction txid=7218b78ad4853eb957b610033b8e1ef48b01d948e0ec5dbf79f12caebc2b17e2
|
$ bitcoin-cli -named gettransaction txid=5b953a0bdfae0d11d20d195ea43ab7c31a5471d2385c258394f3bb9bb3089375
|
||||||
{
|
{
|
||||||
"amount": -0.10000000,
|
"amount": 0.00000000,
|
||||||
"fee": 0.00000000,
|
"fee": -0.00000141,
|
||||||
"confirmations": -5,
|
"confirmations": 0,
|
||||||
"trusted": false,
|
"trusted": false,
|
||||||
"txid": "7218b78ad4853eb957b610033b8e1ef48b01d948e0ec5dbf79f12caebc2b17e2",
|
"txid": "5b953a0bdfae0d11d20d195ea43ab7c31a5471d2385c258394f3bb9bb3089375",
|
||||||
"walletconflicts": [
|
"walletconflicts": [
|
||||||
"959b0b0f4c8350e9038279dfe0f5ae7b165660cc1281e37bea08d0bd084edb39"
|
"c6de60427b28d8ec8102e49771e5d0348fc3ef6a5bf02eb864ec745105a6951b"
|
||||||
],
|
],
|
||||||
"time": 1491603320,
|
"time": 1592954399,
|
||||||
"timereceived": 1491603320,
|
"timereceived": 1592954399,
|
||||||
"bip125-replaceable": "yes",
|
"bip125-replaceable": "yes",
|
||||||
"details": [
|
"details": [
|
||||||
{
|
|
||||||
"account": "",
|
|
||||||
"address": "n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi",
|
|
||||||
"category": "send",
|
|
||||||
"amount": -0.10000000,
|
|
||||||
"vout": 0,
|
|
||||||
"fee": 0.00000000,
|
|
||||||
"abandoned": false
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"hex": "02000000014e843e22cb8ee522fbf4d8a0967a733685d2ad92697e63f52ce41bec8f7c8ac0010000006b483045022100834731cd64efcc078d6c3e59cf0963599ffbc44722b7851b0404bb68e4a1fec70220759a0887ea791592c8119bbe61842eb3850a20cdf8433b4ba00d4ead752facfe012103456575f59a127a4c3e79c23f185899fa0a9ccd40162d05617fb112fa31bd14e5010000000280969800000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac804a5d05000000001976a914c101d8c34de7b8d83b3f8d75416ffaea871d664988ac00000000"
|
"hex": "02000000000101fa364ad3cbdb08dd0b83aac009a42a9ed00594acd6883d2a466699996cd69d8b01000000000100000002ea1d000000000000160014d591091b8074a2375ed9985a9c4b18efecfd416501000000000000001600146c45d3afa8762086c4bd76d8a71ac7c976e1919602473044022077007dff4df9ce75430e3065c82321dca9f6bdcfd5812f8dc0daeb957d3dfd1602203a624d4e9720a06def613eeea67fbf13ce1fb6188d3b7e780ce6e40e859f275d0121038a2702938e548eaec28feb92c7e4722042cfd1ea16bec9fc274640dc5be05ec500000000"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Note that `bitcoin-cli` recognizes that there's a conflict with another transaction in the `walletconflicts` section. Also note that this transaction is now listed with _negative confirmations_, which marks how long it's been since the opposing double-spend was confirmed.
|
This represents the fact that two different transactions are both trying to use the same UTXO.
|
||||||
|
|
||||||
Meanwhile, the new transaction worked fine:
|
Eventually, the transaction with the larger fee should be accepted:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli -named gettransaction txid=959b0b0f4c8350e9038279dfe0f5ae7b165660cc1281e37bea08d0bd084edb39
|
$ bitcoin-cli -named gettransaction txid=c6de60427b28d8ec8102e49771e5d0348fc3ef6a5bf02eb864ec745105a6951b
|
||||||
{
|
{
|
||||||
"amount": -0.10000000,
|
"amount": 0.00000000,
|
||||||
"fee": -0.01000000,
|
"fee": -0.00000299,
|
||||||
"confirmations": 5,
|
"confirmations": 2,
|
||||||
"blockhash": "00000000000006eeb468791e5ee0d86613c03acd871ef7d89c25fd28474754d5",
|
"blockhash": "0000000000000055ac4b6578d7ffb83b0eccef383ca74500b00f59ddfaa1acab",
|
||||||
"blockindex": 20,
|
"blockheight": 1773266,
|
||||||
"blocktime": 1491603862,
|
"blockindex": 9,
|
||||||
"txid": "959b0b0f4c8350e9038279dfe0f5ae7b165660cc1281e37bea08d0bd084edb39",
|
"blocktime": 1592955002,
|
||||||
|
"txid": "c6de60427b28d8ec8102e49771e5d0348fc3ef6a5bf02eb864ec745105a6951b",
|
||||||
"walletconflicts": [
|
"walletconflicts": [
|
||||||
"7218b78ad4853eb957b610033b8e1ef48b01d948e0ec5dbf79f12caebc2b17e2"
|
"5b953a0bdfae0d11d20d195ea43ab7c31a5471d2385c258394f3bb9bb3089375"
|
||||||
],
|
],
|
||||||
"time": 1491603673,
|
"time": 1592954467,
|
||||||
"timereceived": 1491603673,
|
"timereceived": 1592954467,
|
||||||
"bip125-replaceable": "no",
|
"bip125-replaceable": "no",
|
||||||
"details": [
|
"details": [
|
||||||
{
|
|
||||||
"account": "",
|
|
||||||
"address": "n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi",
|
|
||||||
"category": "send",
|
|
||||||
"amount": -0.10000000,
|
|
||||||
"vout": 0,
|
|
||||||
"fee": -0.01000000,
|
|
||||||
"abandoned": false
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"hex": "02000000014e843e22cb8ee522fbf4d8a0967a733685d2ad92697e63f52ce41bec8f7c8ac0010000006a47304402207fea4a11db8576257b9d9e104aa07cb3d3ae6a42e38dd7126111276ca5b45daa0220594a3553cc278c43fd015b35029d5b9596d4ac9f36d3d20fb1a8c9efface5c50012103456575f59a127a4c3e79c23f185899fa0a9ccd40162d05617fb112fa31bd14e5020000000280969800000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac40084e05000000001976a914c101d8c34de7b8d83b3f8d75416ffaea871d664988ac00000000"
|
"hex": "02000000000101fa364ad3cbdb08dd0b83aac009a42a9ed00594acd6883d2a466699996cd69d8b010000000001000000024c1d000000000000160014d591091b8074a2375ed9985a9c4b18efecfd416501000000000000001600146c45d3afa8762086c4bd76d8a71ac7c976e1919602473044022077dcdd98d85f6247450185c2b918a0f434d9b2e647330d741944ecae60d6ff790220424f85628cebe0ffe9fa11029b8240d08bdbfcc0c11f799483e63b437841b1cd0121038a2702938e548eaec28feb92c7e4722042cfd1ea16bec9fc274640dc5be05ec500000000"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Meanwhile, the original transaction with the lower fee starts picking up negative confirmations, to show its divergence from the blockchain:
|
||||||
|
```
|
||||||
|
$ bitcoin-cli -named gettransaction txid=5b953a0bdfae0d11d20d195ea43ab7c31a5471d2385c258394f3bb9bb3089375
|
||||||
|
{
|
||||||
|
"amount": 0.00000000,
|
||||||
|
"fee": -0.00000141,
|
||||||
|
"confirmations": -2,
|
||||||
|
"trusted": false,
|
||||||
|
"txid": "5b953a0bdfae0d11d20d195ea43ab7c31a5471d2385c258394f3bb9bb3089375",
|
||||||
|
"walletconflicts": [
|
||||||
|
"c6de60427b28d8ec8102e49771e5d0348fc3ef6a5bf02eb864ec745105a6951b"
|
||||||
|
],
|
||||||
|
"time": 1592954399,
|
||||||
|
"timereceived": 1592954399,
|
||||||
|
"bip125-replaceable": "yes",
|
||||||
|
"details": [
|
||||||
|
],
|
||||||
|
"hex": "02000000000101fa364ad3cbdb08dd0b83aac009a42a9ed00594acd6883d2a466699996cd69d8b01000000000100000002ea1d000000000000160014d591091b8074a2375ed9985a9c4b18efecfd416501000000000000001600146c45d3afa8762086c4bd76d8a71ac7c976e1919602473044022077007dff4df9ce75430e3065c82321dca9f6bdcfd5812f8dc0daeb957d3dfd1602203a624d4e9720a06def613eeea67fbf13ce1fb6188d3b7e780ce6e40e859f275d0121038a2702938e548eaec28feb92c7e4722042cfd1ea16bec9fc274640dc5be05ec500000000"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Our recipients have their money, and the original, failed transaction will eventually fall out of the mempool.
|
Our recipients have their money, and the original, failed transaction will eventually fall out of the mempool.
|
||||||
@ -263,9 +208,9 @@ $ bitcoin-cli -named gettransaction txid=75208c5c8cbd83081a0085cd050fc7a4064d87c
|
|||||||
|
|
||||||
If a transaction is stuck, and you don't want to wait for it to expire entirely, if you opted-in to RBF, then you can double-spend using RBF to create a replacement transaction (or just use `bumpfee`).
|
If a transaction is stuck, and you don't want to wait for it to expire entirely, if you opted-in to RBF, then you can double-spend using RBF to create a replacement transaction (or just use `bumpfee`).
|
||||||
|
|
||||||
_What is the power of RBF?_ Obviously, RBF is very helpful if you created a transaction with too low of a fee and you need to get those funds through. However, the ability to generally replace unconfirmed transactions with updated ones has more power than just that (and is why you might want to continue using RBF with raw transactions, even following the advent of `bumpfee`).
|
> :fire: ***What is the power of RBF?*** Obviously, RBF is very helpful if you created a transaction with too low of a fee and you need to get those funds through. However, the ability to generally replace unconfirmed transactions with updated ones has more power than just that (and is why you might want to continue using RBF with raw transactions, even following the advent of `bumpfee`).
|
||||||
|
|
||||||
For example, you might send a transaction, and then before it's confirmed, combine it with a second transaction. This allows you to compress multiple transactions down into a single one, decreasing overall fees. It might also offer benefits to privacy. There are other reasons to use RBF too, for smart contracts or transaction cut-throughs, as described in the [Opt-in RBF FAQ](https://bitcoincore.org/en/faq/optin_rbf/).
|
> For example, you might send a transaction, and then before it's confirmed, combine it with a second transaction. This allows you to compress multiple transactions down into a single one, decreasing overall fees. It might also offer benefits to privacy. There are other reasons to use RBF too, for smart contracts or transaction cut-throughs, as described in the [Opt-in RBF FAQ](https://bitcoincore.org/en/faq/optin_rbf/).
|
||||||
|
|
||||||
## What's Next?
|
## What's Next?
|
||||||
|
|
||||||
|
@ -24,49 +24,67 @@ Funding a transaction with CPFP is a very simple process using the methods you'r
|
|||||||
```
|
```
|
||||||
$ bitcoin-cli getrawmempool
|
$ bitcoin-cli getrawmempool
|
||||||
[
|
[
|
||||||
"ed7f68d9e363f710379c83baca1b71360b3a2ceaf62f8414a26b6680f77e132b"
|
"95d51e813daeb9a861b2dcdddf1da8c198d06452bbbecfd827447881ff79e061"
|
||||||
]
|
]
|
||||||
|
|
||||||
$ bitcoin-cli getrawtransaction ed7f68d9e363f710379c83baca1b71360b3a2ceaf62f8414a26b6680f77e132b 1
|
$ bitcoin-cli getrawtransaction 95d51e813daeb9a861b2dcdddf1da8c198d06452bbbecfd827447881ff79e061 true
|
||||||
{
|
{
|
||||||
"hex": "02000000010d5151273464ac8fa74c1b57ff8e4650b8e190c488b1cfadae6561ac3988e83c000000006a473044022059c402379c8e7d5bf60840fc75497ee67694e6dfc9b0391c59b4b202edf4194b0220686c5a27bee1a9214e410cbce3d11e47ca57736d7546ba208adcc068a3f826f5012103fb14f31f5f143463198a3e79c7529cae529a32857ada29bec26f7ea9d8ea67f3ffffffff0130e1be07000000001976a914cbecb861750ee3b00ea845f5c1efa72d1c541cec88ac00000000",
|
"txid": "95d51e813daeb9a861b2dcdddf1da8c198d06452bbbecfd827447881ff79e061",
|
||||||
"txid": "ed7f68d9e363f710379c83baca1b71360b3a2ceaf62f8414a26b6680f77e132b",
|
"hash": "9729e47b8aee776112a82cec46df7638d112ca51856c53e238a9b1f7af3be4ce",
|
||||||
"hash": "ed7f68d9e363f710379c83baca1b71360b3a2ceaf62f8414a26b6680f77e132b",
|
|
||||||
"size": 191,
|
|
||||||
"vsize": 191,
|
|
||||||
"version": 2,
|
"version": 2,
|
||||||
"locktime": 0,
|
"size": 247,
|
||||||
|
"vsize": 166,
|
||||||
|
"weight": 661,
|
||||||
|
"locktime": 1773277,
|
||||||
"vin": [
|
"vin": [
|
||||||
{
|
{
|
||||||
"txid": "3ce88839ac6165aeadcfb188c490e1b850468eff571b4ca78fac64342751510d",
|
"txid": "7a0178472300247d423ac4a04ff9165fa5b944104f6d6f9ebc557c6d207e7524",
|
||||||
"vout": 0,
|
"vout": 0,
|
||||||
"scriptSig": {
|
"scriptSig": {
|
||||||
"asm": "3044022059c402379c8e7d5bf60840fc75497ee67694e6dfc9b0391c59b4b202edf4194b0220686c5a27bee1a9214e410cbce3d11e47ca57736d7546ba208adcc068a3f826f5[ALL] 03fb14f31f5f143463198a3e79c7529cae529a32857ada29bec26f7ea9d8ea67f3",
|
"asm": "0014334f3a112df0f22e743ad97eec8195a00faa59a0",
|
||||||
"hex": "473044022059c402379c8e7d5bf60840fc75497ee67694e6dfc9b0391c59b4b202edf4194b0220686c5a27bee1a9214e410cbce3d11e47ca57736d7546ba208adcc068a3f826f5012103fb14f31f5f143463198a3e79c7529cae529a32857ada29bec26f7ea9d8ea67f3"
|
"hex": "160014334f3a112df0f22e743ad97eec8195a00faa59a0"
|
||||||
},
|
},
|
||||||
"sequence": 4294967295
|
"txinwitness": [
|
||||||
|
"304402207966aa87db340841d76d3c3596d8b4858e02aed1c02d87098dcedbc60721d8940220218aac9d728c9a485820b074804a8c5936fa3145ce68e24dcf477024b19e88ae01",
|
||||||
|
"03574b1328a5dc2d648498fc12523cdf708efd091c28722a422d122f8a0db8daa9"
|
||||||
|
],
|
||||||
|
"sequence": 4294967294
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"vout": [
|
"vout": [
|
||||||
{
|
{
|
||||||
"value": 1.29950000,
|
"value": 0.01000000,
|
||||||
"n": 0,
|
"n": 0,
|
||||||
"scriptPubKey": {
|
"scriptPubKey": {
|
||||||
"asm": "OP_DUP OP_HASH160 cbecb861750ee3b00ea845f5c1efa72d1c541cec OP_EQUALVERIFY OP_CHECKSIG",
|
"asm": "OP_HASH160 f079f77f2ef0ef1187093379d128ec28d0b4bf76 OP_EQUAL",
|
||||||
"hex": "76a914cbecb861750ee3b00ea845f5c1efa72d1c541cec88ac",
|
"hex": "a914f079f77f2ef0ef1187093379d128ec28d0b4bf7687",
|
||||||
"reqSigs": 1,
|
"reqSigs": 1,
|
||||||
"type": "pubkeyhash",
|
"type": "scripthash",
|
||||||
"addresses": [
|
"addresses": [
|
||||||
"mz7D3c6tMtNXV6CTsEwDBJ2vhSVTo9bSMN"
|
"2NFAkGiwnp8wvCodRBx3smJwxncuG3hndn5"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": 0.02598722,
|
||||||
|
"n": 1,
|
||||||
|
"scriptPubKey": {
|
||||||
|
"asm": "OP_HASH160 8799be12fb9eae6644659d95b9602ddfbb4b2aff OP_EQUAL",
|
||||||
|
"hex": "a9148799be12fb9eae6644659d95b9602ddfbb4b2aff87",
|
||||||
|
"reqSigs": 1,
|
||||||
|
"type": "scripthash",
|
||||||
|
"addresses": [
|
||||||
|
"2N5cDPPuCTtYq13oXw8RfpY9dHJW8sL64U2"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"hex": "0200000000010124757e206d7c55bc9e6f6d4f1044b9a55f16f94fa0c43a427d2400234778017a0000000017160014334f3a112df0f22e743ad97eec8195a00faa59a0feffffff0240420f000000000017a914f079f77f2ef0ef1187093379d128ec28d0b4bf768742a727000000000017a9148799be12fb9eae6644659d95b9602ddfbb4b2aff870247304402207966aa87db340841d76d3c3596d8b4858e02aed1c02d87098dcedbc60721d8940220218aac9d728c9a485820b074804a8c5936fa3145ce68e24dcf477024b19e88ae012103574b1328a5dc2d648498fc12523cdf708efd091c28722a422d122f8a0db8daa9dd0e1b00"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Look through the `vout` array. Find the object that matches your address. (Here, it's the only one.) The `n` value is your `vout`. You now have everything you need to create a new CPFP transaction.
|
Look through the `vout` array. Find the object that matches your address. (Here, it's the only one.) The `n` value is your `vout`. You now have everything you need to create a new CPFP transaction.
|
||||||
```
|
```
|
||||||
$ utxo_txid=ed7f68d9e363f710379c83baca1b71360b3a2ceaf62f8414a26b6680f77e132b
|
$ utxo_txid=2NFAkGiwnp8wvCodRBx3smJwxncuG3hndn5
|
||||||
$ utxo_vout=0
|
$ utxo_vout=0
|
||||||
$ recipient2=$(bitcoin-cli getrawchangeaddress)
|
$ recipient2=$(bitcoin-cli getrawchangeaddress)
|
||||||
```
|
```
|
||||||
@ -76,7 +94,7 @@ $ recipient2=$(bitcoin-cli getrawchangeaddress)
|
|||||||
|
|
||||||
When you take these steps, everything should look totally normal, despite the fact that you're working with an unconfirmed transaction. To verify that all was well, we even looked at the results of our signature before we saved off the information to a variable:
|
When you take these steps, everything should look totally normal, despite the fact that you're working with an unconfirmed transaction. To verify that all was well, we even looked at the results of our signature before we saved off the information to a variable:
|
||||||
```
|
```
|
||||||
$ rawtxhex=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]''' outputs='''{ "'$recipient2'": 1.2985 }''')
|
$ rawtxhex=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]''' outputs='''{ "'$recipient2'": 0.03597 }''')
|
||||||
|
|
||||||
$ bitcoin-cli -named signrawtransaction hexstring=$rawtxhex | jq -r '.hex'
|
$ bitcoin-cli -named signrawtransaction hexstring=$rawtxhex | jq -r '.hex'
|
||||||
02000000012b137ef780666ba214842ff6ea2c3a0b36711bcaba839c3710f763e3d9687fed000000006a473044022003ca1f6797d781ef121ba7c2d1d41d763a815e9dad52aa8bc5ea61e4d521f68e022036b992e8e6bf2c44748219ca6e0056a88e8250f6fd0794dc69f79a2e8993671601210317b163ab8c8862e09c71767112b828abd3852e315441893fa0f535de4fa39b8dffffffff01905abd07000000001976a91450b1d90a130c4f3f1e5fbfa7320fd36b7265db0488ac00000000
|
02000000012b137ef780666ba214842ff6ea2c3a0b36711bcaba839c3710f763e3d9687fed000000006a473044022003ca1f6797d781ef121ba7c2d1d41d763a815e9dad52aa8bc5ea61e4d521f68e022036b992e8e6bf2c44748219ca6e0056a88e8250f6fd0794dc69f79a2e8993671601210317b163ab8c8862e09c71767112b828abd3852e315441893fa0f535de4fa39b8dffffffff01905abd07000000001976a91450b1d90a130c4f3f1e5fbfa7320fd36b7265db0488ac00000000
|
||||||
@ -104,8 +122,8 @@ A _recipient_ could use CPFP even if he wasn't planning on immediately spending
|
|||||||
|
|
||||||
You can take advantage of the CPFP incentives to free up funds that have been sent to you but have not been confirmed. Just use the unconfirmed transaction as UTXO and pay a higher-than-average transaction fee.
|
You can take advantage of the CPFP incentives to free up funds that have been sent to you but have not been confirmed. Just use the unconfirmed transaction as UTXO and pay a higher-than-average transaction fee.
|
||||||
|
|
||||||
_What is the power of CPFP?_ Mostly, CPFP is just useful to get funds unstuck when you're the recipient and the sender isn't being helpful for whatever reason. It doesn't have the more powerful possibilities of RBF, but is an alternatve way to exert control over a transaction after it's been placed in the mempool, but before it's confirmed in a block.
|
> :fire: ***What is the power of CPFP?*** Mostly, CPFP is just useful to get funds unstuck when you're the recipient and the sender isn't being helpful for whatever reason. It doesn't have the more powerful possibilities of RBF, but is an alternatve way to exert control over a transaction after it's been placed in the mempool, but before it's confirmed in a block.
|
||||||
|
|
||||||
## What's Next?
|
## What's Next?
|
||||||
|
|
||||||
Advance through "bitcoin-cli" with [Chapter Six: Expanding Bitcoin Transactions](06_0_Expanding_Bitcoin_Transactions.md).
|
Advance through "bitcoin-cli" with [Chapter Six: Expanding Bitcoin Transactions with Multisigs](06_0_Expanding_Bitcoin_Transactions_Multisigs.md).
|
||||||
|
21
06_0_Expanding_Bitcoin_Transactions_Multisigs.md
Normal file
21
06_0_Expanding_Bitcoin_Transactions_Multisigs.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Chapter Six: Expanding Bitcoin Transactions with Multisigs.
|
||||||
|
|
||||||
|
Basic bitcoin transactions: (1) send funds; (2) to a single P2PKH or SegWit recipient; (3) from a single machine; (4) immediately. However, all four parts of this definition can be expanded using more complex Bitcoin transactions. This first chapter on "Expansion" shows how to vary points (2) and (3), by sending money to an address that represents multiple recipients (or at least, multiple signers).
|
||||||
|
|
||||||
|
## Objectives for This Section
|
||||||
|
|
||||||
|
After working through this chapter, a developer will be able to:
|
||||||
|
|
||||||
|
* Create Multisignature Bitcoin Addresses Using Bitcoin Fundamentals
|
||||||
|
* Create Multisignature Bitcoin Addresses Using Easiser Mechanisms
|
||||||
|
|
||||||
|
Supporting objectives include the ability to:
|
||||||
|
|
||||||
|
* Understand How to Spend Funds Sent to a Multisignature
|
||||||
|
* Plan for the Power of Multisignatures
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
* [Section One: Sending a Transaction with a Multsig](06_1_Sending_a_Transaction_to_a_Multisig.md)
|
||||||
|
* [Section Two: Spending a Transaction with a Multsig](06_2_Spending_a_Transaction_to_a_Multisig.md)
|
||||||
|
* [Section Three: Sending & Spending an Automated Multisig](06_3_Sending_an_Automated_Multisig.md)
|
@ -6,17 +6,17 @@ The first way to vary how you send a basic transaction is to use a multisig. Thi
|
|||||||
|
|
||||||
## Understand How Multisigs Work
|
## Understand How Multisigs Work
|
||||||
|
|
||||||
In a typical P2PKH transaction, bitcoins are sent to an address based on your public key, which in turn means that the related private key is required to unlock the transaction, solving the cryptographic puzzle and allowing you to reuse the funds. But what if you could instead lock a transaction with _multiple_ private keys. This would effectively allow funds to be sent to a group of people, where those people all have to agree to reuse the funds.
|
In a typical P2PKH or SegWit transaction, bitcoins are sent to an address based on your public key, which in turn means that the related private key is required to unlock the transaction, solving the cryptographic puzzle and allowing you to reuse the funds. But what if you could instead lock a transaction with _multiple_ private keys. This would effectively allow funds to be sent to a group of people, where those people all have to agree to reuse the funds.
|
||||||
|
|
||||||
_What is a multisignature?_ A multisignature is a methodology that allows more than one person to jointly create a digital signature. It's a general technique for the cryptographic use of keys that goes far beyond Bitcoin.
|
> :book: ***What is a multisignature?*** A multisignature is a methodology that allows more than one person to jointly create a digital signature. It's a general technique for the cryptographic use of keys that goes far beyond Bitcoin.
|
||||||
|
|
||||||
Technically, a multisignature cryptographic puzzle is created by Bitcoin using the OP_CHECKMULTISIG command, and typically that's encapsulated in a P2SH address. [§8.4: Scripting a Multisig](08_4_Scripting_a_Multisig.md) will detail how that works more precisely. For now, all you need to know is that you can use `bitcoin-cli` command to create multisignature addresses; funds can be mailed to these addresses just like any normal P2PKH address, but multiple private keys will be required for the redemption of the funds.
|
Technically, a multisignature cryptographic puzzle is created by Bitcoin using the OP_CHECKMULTISIG command, and typically that's encapsulated in a P2SH address. [§8.4: Scripting a Multisig](08_4_Scripting_a_Multisig.md) will detail how that works more precisely. For now, all you need to know is that you can use `bitcoin-cli` command to create multisignature addresses; funds can be mailed to these addresses just like any normal P2PKH address, but multiple private keys will be required for the redemption of the funds.
|
||||||
|
|
||||||
_What is a multisignature transaction?_ A multisignature transaction is a Bitcoin transaction that has been sent to a multisignature address, thus requiring the signatures of certain people from the multisignature group to reuse the funds.
|
> :book: ***What is a multisignature transaction?*** A multisignature transaction is a Bitcoin transaction that has been sent to a multisignature address, thus requiring the signatures of certain people from the multisignature group to reuse the funds.
|
||||||
|
|
||||||
Simple multisignatures require everyone in the group to sign the UTXO when it's spent. However, there's more complexity possible. Multisignatures are generally described as being "m of n". That means that the transaction is locked with a group of "n" keys, but only "m" of them are required to unlock the transaction.
|
Simple multisignatures require everyone in the group to sign the UTXO when it's spent. However, there's more complexity possible. Multisignatures are generally described as being "m of n". That means that the transaction is locked with a group of "n" keys, but only "m" of them are required to unlock the transaction.
|
||||||
|
|
||||||
_What is a m-of-n multisignature?_ In a multisignature, "m" signatures out of a group of "n" are required to form the signature, where "m ≤ n".
|
> :book: ***What is a m-of-n multisignature?*** In a multisignature, "m" signatures out of a group of "n" are required to form the signature, where "m ≤ n".
|
||||||
|
|
||||||
## Create a Multisig Address
|
## Create a Multisig Address
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ In order to lock a UTXO with multiple private keys, you must first create a mult
|
|||||||
|
|
||||||
### Create the Addresses
|
### Create the Addresses
|
||||||
|
|
||||||
To create a multisignature address, you must first ready the P2PKH addresses that the multisig will combine. Best practice suggests that you always create new addresses. This means that the participants will each run the `getnewaddress` command on their own machine:
|
To create a multisignature address, you must first ready the addresses that the multisig will combine. Best practice suggests that you always create new addresses. This means that the participants will each run the `getnewaddress` command on their own machine:
|
||||||
```
|
```
|
||||||
machine1$ address1=$(bitcoin-cli getnewaddress)
|
machine1$ address1=$(bitcoin-cli getnewaddress)
|
||||||
```
|
```
|
||||||
@ -44,41 +44,28 @@ Over on the remote machine, which we assume here is `machine2`, you can get the
|
|||||||
```
|
```
|
||||||
machine2$ bitcoin-cli -named getaddressinfo address=$address2
|
machine2$ bitcoin-cli -named getaddressinfo address=$address2
|
||||||
{
|
{
|
||||||
"address": "2N9Qnf7kGS5QX8mRDFQv7QWARFRqkKdp9pN",
|
"address": "tb1qr2tkjh8rs9xn5xaktf5phct0wxqufplawrfd9q",
|
||||||
"scriptPubKey": "a914b15107009c65b631226d0626b22150098c91d35587",
|
"scriptPubKey": "00141a97695ce3814d3a1bb65a681be16f7181c487fd",
|
||||||
"ismine": true,
|
"ismine": true,
|
||||||
"solvable": true,
|
"solvable": true,
|
||||||
"desc": "sh(wpkh([801811ed/0'/0'/4']0373de7b25896556c33e7a6f5379151291d380c60b84c3ee9a8c933b08ce0da9f4))#rxfcwarv",
|
"desc": "wpkh([fe6f2292/0'/0'/1']02bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa3)#zc64l8dw",
|
||||||
"iswatchonly": false,
|
"iswatchonly": false,
|
||||||
"isscript": true,
|
|
||||||
"iswitness": false,
|
|
||||||
"script": "witness_v0_keyhash",
|
|
||||||
"hex": "0014c06d895303bd7dff0320d7df9f33f99e8b9b0d93",
|
|
||||||
"pubkey": "0373de7b25896556c33e7a6f5379151291d380c60b84c3ee9a8c933b08ce0da9f4",
|
|
||||||
"embedded": {
|
|
||||||
"isscript": false,
|
"isscript": false,
|
||||||
"iswitness": true,
|
"iswitness": true,
|
||||||
"witness_version": 0,
|
"witness_version": 0,
|
||||||
"witness_program": "c06d895303bd7dff0320d7df9f33f99e8b9b0d93",
|
"witness_program": "1a97695ce3814d3a1bb65a681be16f7181c487fd",
|
||||||
"pubkey": "0373de7b25896556c33e7a6f5379151291d380c60b84c3ee9a8c933b08ce0da9f4",
|
"pubkey": "02bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa3",
|
||||||
"address": "tb1qcpkcj5crh47l7qeq6l0e7vlen69ekrvn509duc",
|
|
||||||
"scriptPubKey": "0014c06d895303bd7dff0320d7df9f33f99e8b9b0d93"
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"ischange": false,
|
"ischange": false,
|
||||||
"timestamp": 1579204237,
|
"timestamp": 1592957904,
|
||||||
"hdkeypath": "m/0'/0'/4'",
|
"hdkeypath": "m/0'/0'/1'",
|
||||||
"hdseedid": "67ffe46aa0cfd46eb342b78579f72fd1597833b4",
|
"hdseedid": "1dc70547f2b80e9bb5fde5f34fb3d85f8d8d1dab",
|
||||||
"hdmasterfingerprint": "801811ed",
|
"hdmasterfingerprint": "fe6f2292",
|
||||||
"labels": [
|
"labels": [
|
||||||
{
|
""
|
||||||
"name": "",
|
|
||||||
"purpose": "receive"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
The `pubkey` address (`0373de7b25896556c33e7a6f5379151291d380c60b84c3ee9a8c933b08ce0da9f4`) is what's required. Copy it over to your local machine by whatever means you find most efficient and _least error prone_.
|
The `pubkey` address (`02bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa3`) is what's required. Copy it over to your local machine by whatever means you find most efficient and _least error prone_.
|
||||||
|
|
||||||
This process needs to be undertaken for _every_ address from a machine other than the one where the multisig is being built. Obviously, if some third-party is creating the address, then you'll to do this for every address.
|
This process needs to be undertaken for _every_ address from a machine other than the one where the multisig is being built. Obviously, if some third-party is creating the address, then you'll to do this for every address.
|
||||||
|
|
||||||
@ -93,27 +80,28 @@ machine1$ pubkey1=$(bitcoin-cli -named getaddressinfo address=$address1 | jq -r
|
|||||||
|
|
||||||
A multisig can now be created with the `createmultisig` command:
|
A multisig can now be created with the `createmultisig` command:
|
||||||
```
|
```
|
||||||
machine1$ bitcoin-cli -named createmultisig nrequired=2 keys='''["'$pubkey1'","0373de7b25896556c33e7a6f5379151291d380c60b84c3ee9a8c933b08ce0da9f4"]'''
|
machine1$ bitcoin-cli -named createmultisig nrequired=2 keys='''["'$pubkey1'","02bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa3"]'''
|
||||||
{
|
{
|
||||||
"address": "2NDU6abQtzh4LcNs4Vd7WQJwZhkXSt1aZGM",
|
"address": "2N8MytPW2ih27LctLjn6LfLFZZb1PFSsqBr",
|
||||||
"redeemScript": "522103f92e9f4c83f4438c86814952ab28836b6e3bfb38089a1f23ff8869eaf217982c210373de7b25896556c33e7a6f5379151291d380c60b84c3ee9a8c933b08ce0da9f452ae"
|
"redeemScript": "522102da2f10746e9778dd57bd0276a4f84101c4e0a711f9cfd9f09cde55acbdd2d1912102bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa352ae",
|
||||||
|
"descriptor": "sh(multi(2,02da2f10746e9778dd57bd0276a4f84101c4e0a711f9cfd9f09cde55acbdd2d191,02bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa3))#0pazcr4y"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
> :warning: **VERSION WARNING:** Older versions of `createmultisig` allowed you to enter an address instead of a public key, if the full information about the address was in your local wallet. This is no longer the case for modern Bitcoin core release, and so the shorthand should not be used.
|
> :warning: **VERSION WARNING:** Older versions of `createmultisig` allowed you to enter an address instead of a public key, if the full information about the address was in your local wallet. This is no longer the case for modern Bitcoin core release, and so the shorthand should not be used.
|
||||||
|
|
||||||
When creating the multisignature address, you list how many signatures are required with the `nrequired` argument (that's "m" in a "m-of-n" multisignature), then you list the total set of possible signatures with the `keys` argument (that's "n"). Note that the the `keys` entries likely came from different places. In this case, we included `$pubkey1` from the local machine and `0373de7b25896556c33e7a6f5379151291d380c60b84c3ee9a8c933b08ce0da9f4` from a remote machine.
|
When creating the multisignature address, you list how many signatures are required with the `nrequired` argument (that's "m" in a "m-of-n" multisignature), then you list the total set of possible signatures with the `keys` argument (that's "n"). Note that the the `keys` entries likely came from different places. In this case, we included `$pubkey1` from the local machine and `02bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa3` from a remote machine.
|
||||||
|
|
||||||
> :information_source: **NOTE — M-OF-N VS N-OF-N:** This example shows the creation of a simple 2-of-2 multisig. If you instead want to create an m-of-n signature where "m < n", you adjust the `nrequired` field and/or the number of signatures in the `keys` JSON object. For a 1-of-2 multisig, you'd set `nrequired=1`, while for a 2-of-3 multisig, you'd leave `nrequired=2`, but add one more public key or address to the `keys` listing.
|
> :information_source: **NOTE — M-OF-N VS N-OF-N:** This example shows the creation of a simple 2-of-2 multisig. If you instead want to create an m-of-n signature where "m < n", you adjust the `nrequired` field and/or the number of signatures in the `keys` JSON object. For a 1-of-2 multisig, you'd set `nrequired=1`, while for a 2-of-3 multisig, you'd leave `nrequired=2`, but add one more public key or address to the `keys` listing.
|
||||||
|
|
||||||
When used correctly, `createmultisig` returns two results, both of which are critically important.
|
When used correctly, `createmultisig` returns two results, both of which are critically important.
|
||||||
|
|
||||||
The _address_ is what you'll give out to people who want to send funds. You'll notice that it has a new prefix of `2`, rather than the prefixes you've seen on Bitcoin addresses to date. That's because `createmultisig` is actually creating a totally new type of address called a P2SH address. It works exactly like a standard P2PKH address for sending funds, but you'll need to do a lot more work to spend them.
|
The _address_ is what you'll give out to people who want to send funds. You'll notice that it has a new prefix of `2`, exactly like those P2SH-SegWit addresses. That's because, like them, `createmultisig` is actually creating a totally new type of address called a P2SH address. It works exactly like a standard P2PKH address for sending funds, but since this one has been built to require multiple addresses, you'll need to do a lot more work to spend them.
|
||||||
|
|
||||||
> :link: **TESTNET vs MAINNET:** On testnet, the prefix for P2SH addresses is `2`, while on mainnet, it's `3`.
|
> :link: **TESTNET vs MAINNET:** On testnet, the prefix for P2SH addresses is `2`, while on mainnet, it's `3`.
|
||||||
|
|
||||||
The _redeemScript_ is what you need to redeem the funds, along with the private keys for "m" of the "n" addresses. This script is another special feature of P2SH addresses and will be fully explained in [§8.1: Building a Bitcoin Script with P2SH](08_1_Building_a_Bitcoin_Script_with_P2SH.md). For now, just be aware that it's a bit of data that's required to get your money.
|
The _redeemScript_ is what you need to redeem the funds (along with the private keys for "m" of the "n" addresses). This script is another special feature of P2SH addresses and will be fully explained in [§8.1: Building a Bitcoin Script with P2SH](08_1_Building_a_Bitcoin_Script_with_P2SH.md). For now, just be aware that it's a bit of data that's required to get your money.
|
||||||
|
|
||||||
_What is a P2SH address?_ P2SH stands for Pay-to-script-hash. It's a different type of receipient than a standard P2PKH address, used for funds whose redemption are based on more complex Bitcoin Scripts. `bitcoin-cli` uses P2SH encapsulation to help standardize and simplify its multisigs as "P2SH multisigs".
|
> :book: ***What is a P2SH address?*** P2SH stands for Pay-to-script-hash. It's a different type of receipient than a standard P2PKH address or even a Bech32, used for funds whose redemption are based on more complex Bitcoin Scripts. `bitcoin-cli` uses P2SH encapsulation to help standardize and simplify its multisigs as "P2SH multisigs", just like P2SH-SegWit was actually using P2SH to standardize its SegWit addresses, and make them fully backward compatible.
|
||||||
|
|
||||||
> :warning: **WARNING:** P2SH multisig addresses, like the ones created by `bitcoin-cli`, have a limit for "m" and "n" in multisigs based on the maximum size of the redeem script, which is currently 520 bytes. Pratically, you won't hit this unless you're doing something excessive.
|
> :warning: **WARNING:** P2SH multisig addresses, like the ones created by `bitcoin-cli`, have a limit for "m" and "n" in multisigs based on the maximum size of the redeem script, which is currently 520 bytes. Pratically, you won't hit this unless you're doing something excessive.
|
||||||
|
|
||||||
@ -124,7 +112,36 @@ Here's an important caveat: nothing about your multisig is saved into your walle
|
|||||||
* A list of the Bitcoin addresses used in the multisig.
|
* A list of the Bitcoin addresses used in the multisig.
|
||||||
* The `redeemScript` output by `createmultsig`.
|
* The `redeemScript` output by `createmultsig`.
|
||||||
|
|
||||||
Technically, the `redeemScript` can be recreated by rerunning `createmultisig` with the complete list of addresses and/or public keys _in the same order_ and with the right m-of-n count. But, it's better to hold onto it and save yourself stress and grief.
|
Technically, the `redeemScript` can be recreated by rerunning `createmultisig` with the complete list of public keys _in the same order_ and with the right m-of-n count. But, it's better to hold onto it and save yourself stress and grief.
|
||||||
|
|
||||||
|
### Watch the Order
|
||||||
|
|
||||||
|
Here's one thing to be very wary of: _order matters_. The order of keys used to create a multi-sig creates a unique hash, which is to say if you put the keys in a different order, they'll produce a different address, as shown:
|
||||||
|
```
|
||||||
|
$ bitcoin-cli -named createmultisig nrequired=2 keys='''["'$pubkey1'","'$pubkey2'"]'''
|
||||||
|
{
|
||||||
|
"address": "2NFBQvz57UzKWDr2Vx5D667epVZifjGixkm",
|
||||||
|
"redeemScript": "52210342b306e410283065ffed38c3139a9bb8805b9f9fa6c16386e7ea96b1ba54da0321039cd6842869c1bfec13cfdbb7d8285bc4c501d413e6633e3ff75d9f13424d99b352ae",
|
||||||
|
"descriptor": "sh(multi(2,0342b306e410283065ffed38c3139a9bb8805b9f9fa6c16386e7ea96b1ba54da03,039cd6842869c1bfec13cfdbb7d8285bc4c501d413e6633e3ff75d9f13424d99b3))#8l6hvjsk"
|
||||||
|
}
|
||||||
|
standup@btctest20:~$ bitcoin-cli -named createmultisig nrequired=2 keys='''["'$pubkey2'","'$pubkey1'"]'''
|
||||||
|
{
|
||||||
|
"address": "2N5bC4Yc5Pqept1y8nPRqvWmFSejkVeRb1k",
|
||||||
|
"redeemScript": "5221039cd6842869c1bfec13cfdbb7d8285bc4c501d413e6633e3ff75d9f13424d99b3210342b306e410283065ffed38c3139a9bb8805b9f9fa6c16386e7ea96b1ba54da0352ae",
|
||||||
|
"descriptor": "sh(multi(2,039cd6842869c1bfec13cfdbb7d8285bc4c501d413e6633e3ff75d9f13424d99b3,0342b306e410283065ffed38c3139a9bb8805b9f9fa6c16386e7ea96b1ba54da03))#audl88kg"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
More notably, each ordering creates a different _redeemScript_. That means that if you used these basic techniques and failed to save the redeemScript as you were instructed, you'll have to walk through an ever-increasing number of variations to find the right one when you try and spend your funds!
|
||||||
|
|
||||||
|
[BIP67](https://github.com/bitcoin/bips/blob/master/bip-0067.mediawiki) suggests a way to lexicographically order keys, so that they always generate the same multisignatures. ColdCard and Electrum are among the wallets that already support this. Of course, this can cause troubles on its own if you don't know if a multisig address was created with sorted or unsorted keys. Once more, [descriptors](03_5_Understanding_the_Descriptor.md) come to the rescue. If a multisig is unsorted, it's built with the function `multi` and if it's sorted it's built with the function `sortedmulti`.
|
||||||
|
|
||||||
|
If you look at the `desc`riptor for the multisig that you created above, you'll see that Bitcoin Core doesn't currently create sort its multisigs:
|
||||||
|
```
|
||||||
|
"descriptor": "sh(multi(2,02da2f10746e9778dd57bd0276a4f84101c4e0a711f9cfd9f09cde55acbdd2d191,02bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa3))#0pazcr4y"
|
||||||
|
```
|
||||||
|
However, if it imports an address with type `sortedmulti`, it'll do the right thing, which is the whole point of descriptors!
|
||||||
|
|
||||||
|
> :warning: **VERSION WARNING:** Bitcoin Core only understands the `sortedmulti` descriptor function beginning with v 0.20.0. Try and access the descriptor on an earlier version of Bitcoin Core and you'll get an error like "A function is needed within P2WSH".
|
||||||
|
|
||||||
## Send to a Multisig Address
|
## Send to a Multisig Address
|
||||||
|
|
||||||
@ -132,20 +149,21 @@ If you've got a multisignature in a convenient P2SH format, like the one generat
|
|||||||
```
|
```
|
||||||
$ utxo_txid=$(bitcoin-cli listunspent | jq -r '.[0] | .txid')
|
$ utxo_txid=$(bitcoin-cli listunspent | jq -r '.[0] | .txid')
|
||||||
$ utxo_vout=$(bitcoin-cli listunspent | jq -r '.[0] | .vout')
|
$ utxo_vout=$(bitcoin-cli listunspent | jq -r '.[0] | .vout')
|
||||||
$ recipient="2NAGfA4nW6nrZkD5je8tSiAcYB9xL2xYMCz"
|
$ recipient="2N8MytPW2ih27LctLjn6LfLFZZb1PFSsqBr"
|
||||||
|
|
||||||
$ rawtxhex=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]''' outputs='''{ "'$recipient'": 1.2995}''')
|
$ rawtxhex=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]''' outputs='''{ "'$recipient'": 0.000065}''')
|
||||||
$ bitcoin-cli -named decoderawtransaction hexstring=$rawtxhex
|
$ bitcoin-cli -named decoderawtransaction hexstring=$rawtxhex
|
||||||
{
|
{
|
||||||
"txid": "ad16ea68a62af2d3930a48c5ca811bf66935f768bb369a85298ee6697167c667",
|
"txid": "b164388854f9701051809eed166d9f6cedba92327e4296bf8a265a5da94f6521",
|
||||||
"hash": "ad16ea68a62af2d3930a48c5ca811bf66935f768bb369a85298ee6697167c667",
|
"hash": "b164388854f9701051809eed166d9f6cedba92327e4296bf8a265a5da94f6521",
|
||||||
|
"version": 2,
|
||||||
"size": 83,
|
"size": 83,
|
||||||
"vsize": 83,
|
"vsize": 83,
|
||||||
"version": 2,
|
"weight": 332,
|
||||||
"locktime": 0,
|
"locktime": 0,
|
||||||
"vin": [
|
"vin": [
|
||||||
{
|
{
|
||||||
"txid": "ad16098f5c8904a4de7c152efc56359c22be37d447cd78019c398791a7bdd928",
|
"txid": "c6de60427b28d8ec8102e49771e5d0348fc3ef6a5bf02eb864ec745105a6951b",
|
||||||
"vout": 0,
|
"vout": 0,
|
||||||
"scriptSig": {
|
"scriptSig": {
|
||||||
"asm": "",
|
"asm": "",
|
||||||
@ -156,31 +174,32 @@ $ bitcoin-cli -named decoderawtransaction hexstring=$rawtxhex
|
|||||||
],
|
],
|
||||||
"vout": [
|
"vout": [
|
||||||
{
|
{
|
||||||
"value": 1.29950000,
|
"value": 0.00006500,
|
||||||
"n": 0,
|
"n": 0,
|
||||||
"scriptPubKey": {
|
"scriptPubKey": {
|
||||||
"asm": "OP_HASH160 babf9063cee8ab6e9334f95f6d4e9148d0e551c2 OP_EQUAL",
|
"asm": "OP_HASH160 a5d106eb8ee51b23cf60d8bd98bc285695f233f3 OP_EQUAL",
|
||||||
"hex": "a914babf9063cee8ab6e9334f95f6d4e9148d0e551c287",
|
"hex": "a914a5d106eb8ee51b23cf60d8bd98bc285695f233f387",
|
||||||
"reqSigs": 1,
|
"reqSigs": 1,
|
||||||
"type": "scripthash",
|
"type": "scripthash",
|
||||||
"addresses": [
|
"addresses": [
|
||||||
"2NAGfA4nW6nrZkD5je8tSiAcYB9xL2xYMCz"
|
"2N8MytPW2ih27LctLjn6LfLFZZb1PFSsqBr"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
$ signedtx=$(bitcoin-cli -named signrawtransaction hexstring=$rawtxhex | jq -r '.hex')
|
|
||||||
|
$ signedtx=$(bitcoin-cli -named signrawtransactionwithwallet hexstring=$rawtxhex | jq -r '.hex')
|
||||||
$ bitcoin-cli -named sendrawtransaction hexstring=$signedtx
|
$ bitcoin-cli -named sendrawtransaction hexstring=$signedtx
|
||||||
621be11aac439d6ec58be398058fc33c3e89cf45138a0e73e05b7001f9b6e328
|
b164388854f9701051809eed166d9f6cedba92327e4296bf8a265a5da94f6521
|
||||||
```
|
```
|
||||||
As you can see, there was nothing unusual in the creation of the transaction, and it looked entirely normal, albeit with an address with a different prefix than normal (`2NAGfA4nW6nrZkD5je8tSiAcYB9xL2xYMCz`).
|
As you can see, there was nothing unusual in the creation of the transaction, and it looked entirely normal, albeit with an address with a different prefix than normal (`2N8MytPW2ih27LctLjn6LfLFZZb1PFSsqBr`). No surprise, as we similarly saw no difference when we sent to Bech32 addresses for the first time in [§4.6](04_6_Creating_a_Segwit_Transaction.md).
|
||||||
|
|
||||||
## Summary: Sending a Transaction with a Multisig
|
## Summary: Sending a Transaction with a Multisig
|
||||||
|
|
||||||
Multisig addresses lock funds to multiple private keys — possibly requiring all of those private keys for redemption, and possibly requiring just some from the set. They're easy enough to create with `bitcoin-cli` and they're entirely normal to send to. This ease is due in large part to the invisible use of P2SH (pay-to-script-hash) addresses, a large topic that will get more coverage in the future.
|
Multisig addresses lock funds to multiple private keys — possibly requiring all of those private keys for redemption, and possibly requiring just some from the set. They're easy enough to create with `bitcoin-cli` and they're entirely normal to send to. This ease is due in large part to the invisible use of P2SH (pay-to-script-hash) addresses, a large topic that we've touched upon twice now, with P2SH-SegWit and multisig addresses, and one that will get more coverage in the future.
|
||||||
|
|
||||||
_What is the power of multisignatures?_ Multisignatures allow the modeling of a variety of financial arrangements such as corporations, partnerships, committees, and other groups. A 1-of-2 multisig might be a married couple's joint bank account, while a 2-of-2 multisig might be used for large expenditures by a Limited Liability Partnership. Multisignatures also form one of the bases of Smart Contracts. For example, a real estate deal could be closed with a 2-of-3 multisig, where the signatures are submitted by the buyer, the seller, and an escrow agent. Once the escrow agent agrees that all of the conditions have been met, he frees up the funds for the seller; or alternatively, the buyer and seller can jointly free the funds.
|
> :fire: ***What is the power of multisignatures?*** Multisignatures allow the modeling of a variety of financial arrangements such as corporations, partnerships, committees, and other groups. A 1-of-2 multisig might be a married couple's joint bank account, while a 2-of-2 multisig might be used for large expenditures by a Limited Liability Partnership. Multisignatures also form one of the bases of Smart Contracts. For example, a real estate deal could be closed with a 2-of-3 multisig, where the signatures are submitted by the buyer, the seller, and an escrow agent. Once the escrow agent agrees that all of the conditions have been met, he frees up the funds for the seller; or alternatively, the buyer and seller can jointly free the funds.
|
||||||
|
|
||||||
## What's Next?
|
## What's Next?
|
||||||
|
|
||||||
|
@ -10,48 +10,103 @@ To start with, you need to find your funds; your computer doesn't know to look f
|
|||||||
```
|
```
|
||||||
$ bitcoin-cli -named importaddress address=2NAGfA4nW6nrZkD5je8tSiAcYB9xL2xYMCz
|
$ bitcoin-cli -named importaddress address=2NAGfA4nW6nrZkD5je8tSiAcYB9xL2xYMCz
|
||||||
```
|
```
|
||||||
This command can take a while to run because it does a `rescan` to find all the related transactions. Afterward the funds should show up when you `listunspent` ... but they still aren't easily spendable.
|
Here's the catch: you can't do this if you have a pruned node! This command can take a while to run because it does a `rescan` to find all the related transactions. Afterward the funds should show up when you `listunspent` ... but they still aren't easily spendable. (In fact, your wallet may claim they're not `spendable` at all!)
|
||||||
|
|
||||||
|
If you're not able to incorporate the address into your wallet due to pruning, using `gettransaction` to get info instead (or look on a block explorer).
|
||||||
```
|
```
|
||||||
$ bitcoin-cli listunspent
|
$ bitcoin-cli -named gettransaction txid=b164388854f9701051809eed166d9f6cedba92327e4296bf8a265a5da94f6521 verbose=true
|
||||||
[
|
{
|
||||||
|
"amount": -0.00006500,
|
||||||
|
"fee": -0.00001000,
|
||||||
|
"confirmations": 3,
|
||||||
|
"blockhash": "0000000000000165b5f602920088a7e36b11214161d6aaebf5229e3ed4f10adc",
|
||||||
|
"blockheight": 1773282,
|
||||||
|
"blockindex": 9,
|
||||||
|
"blocktime": 1592959320,
|
||||||
|
"txid": "b164388854f9701051809eed166d9f6cedba92327e4296bf8a265a5da94f6521",
|
||||||
|
"walletconflicts": [
|
||||||
|
],
|
||||||
|
"time": 1592958753,
|
||||||
|
"timereceived": 1592958753,
|
||||||
|
"bip125-replaceable": "no",
|
||||||
|
"details": [
|
||||||
{
|
{
|
||||||
"txid": "621be11aac439d6ec58be398058fc33c3e89cf45138a0e73e05b7001f9b6e328",
|
"address": "2N8MytPW2ih27LctLjn6LfLFZZb1PFSsqBr",
|
||||||
|
"category": "send",
|
||||||
|
"amount": -0.00006500,
|
||||||
"vout": 0,
|
"vout": 0,
|
||||||
"address": "2NAGfA4nW6nrZkD5je8tSiAcYB9xL2xYMCz",
|
"fee": -0.00001000,
|
||||||
"account": "",
|
"abandoned": false
|
||||||
"scriptPubKey": "a914babf9063cee8ab6e9334f95f6d4e9148d0e551c287",
|
|
||||||
"amount": 1.29950000,
|
|
||||||
"confirmations": 62,
|
|
||||||
"spendable": false,
|
|
||||||
"solvable": false
|
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"hex": "020000000001011b95a6055174ec64b82ef05b6aefc38f34d0e57197e40281ecd8287b4260dec60000000000ffffffff01641900000000000017a914a5d106eb8ee51b23cf60d8bd98bc285695f233f38702473044022070275f81ac4129e1d167ef7e700739f2899ea4c7f1adef3a4da29436f14fb97e02207310d4ec449eba49f0fa404ae45b9c82431d883490c7a0ed882ad0b5d7a623d0012102883bb5463e37d55252d8b3d5c2141b007b37c8a7db6211f75c955acc5ea325eb00000000",
|
||||||
|
"decoded": {
|
||||||
|
"txid": "b164388854f9701051809eed166d9f6cedba92327e4296bf8a265a5da94f6521",
|
||||||
|
"hash": "bdf4e3bc5d354a5dfa5528f172480976321d989d7e5806ac14f1fe9b0b1c093a",
|
||||||
|
"version": 2,
|
||||||
|
"size": 192,
|
||||||
|
"vsize": 111,
|
||||||
|
"weight": 441,
|
||||||
|
"locktime": 0,
|
||||||
|
"vin": [
|
||||||
|
{
|
||||||
|
"txid": "c6de60427b28d8ec8102e49771e5d0348fc3ef6a5bf02eb864ec745105a6951b",
|
||||||
|
"vout": 0,
|
||||||
|
"scriptSig": {
|
||||||
|
"asm": "",
|
||||||
|
"hex": ""
|
||||||
|
},
|
||||||
|
"txinwitness": [
|
||||||
|
"3044022070275f81ac4129e1d167ef7e700739f2899ea4c7f1adef3a4da29436f14fb97e02207310d4ec449eba49f0fa404ae45b9c82431d883490c7a0ed882ad0b5d7a623d001",
|
||||||
|
"02883bb5463e37d55252d8b3d5c2141b007b37c8a7db6211f75c955acc5ea325eb"
|
||||||
|
],
|
||||||
|
"sequence": 4294967295
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"vout": [
|
||||||
|
{
|
||||||
|
"value": 0.00006500,
|
||||||
|
"n": 0,
|
||||||
|
"scriptPubKey": {
|
||||||
|
"asm": "OP_HASH160 a5d106eb8ee51b23cf60d8bd98bc285695f233f3 OP_EQUAL",
|
||||||
|
"hex": "a914a5d106eb8ee51b23cf60d8bd98bc285695f233f387",
|
||||||
|
"reqSigs": 1,
|
||||||
|
"type": "scripthash",
|
||||||
|
"addresses": [
|
||||||
|
"2N8MytPW2ih27LctLjn6LfLFZZb1PFSsqBr"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
(In fact, your wallet claims they're not `spendable` at all!)
|
|
||||||
|
|
||||||
## Set Up Your Variables
|
## Set Up Your Variables
|
||||||
|
|
||||||
When you're ready to spend the funds received by a multisignature address, you're going need to collect a _lot_ of data: much more than you need when you spend a normal P2PKH UTXO. That's in part because the info on the multisig address isn't in your wallet, and in part because you're spending money that was sent to a P2SH (pay-to-script-hash) address, and that's a lot more demanding.
|
When you're ready to spend the funds received by a multisignature address, you're going need to collect a _lot_ of data: much more than you need when you spend a normal P2PKH or SegWit UTXO. That's in part because the info on the multisig address isn't in your wallet, and in part because you're spending money that was sent to a P2SH (pay-to-script-hash) address, and that's a lot more demanding.
|
||||||
|
|
||||||
In total, you're going to need to collect three things: extended information about the UTXO; the redeemScript; and all the private keys involved. You'll of course need a new recipient address too. The private keys need to wait for the signing step, but everything else can be done now.
|
In total, you're going to need to collect three things: extended information about the UTXO; the redeemScript; and all the private keys involved. You'll of course need a new recipient address too. The private keys need to wait for the signing step, but everything else can be done now.
|
||||||
|
|
||||||
### Access the UTXO information
|
### Access the UTXO information
|
||||||
|
|
||||||
To start with, grab the `txid` and the `vout` for the transaction that you want to spend, as usual. For example, if it's your 0th UTXO:
|
To start with, grab the `txid` and the `vout` for the transaction that you want to spend, as usual. In this case, it was retrieved from the `gettransaction` info, above:
|
||||||
```
|
```
|
||||||
$ utxo_txid=$(bitcoin-cli listunspent | jq -r '.[0] | .txid')
|
$ utxo_txid=b164388854f9701051809eed166d9f6cedba92327e4296bf8a265a5da94f6521
|
||||||
$ utxo_vout=$(bitcoin-cli listunspent | jq -r '.[0] | .vout')
|
$ utxo_vout=0
|
||||||
```
|
```
|
||||||
However, you need to also access a third bit of information about the UTXO, its `scriptPubKey`, which is the script that locked the transaction. This is done with a simple `bitcoin-cli`/`jq` invocation that mirrors the previous commands.
|
However, you need to also access a third bit of information about the UTXO, its `scriptPubKey`/`hex`, which is the script that locked the transaction. Again, you're probably doing this by looking at the details of the transaction:
|
||||||
```
|
```
|
||||||
$ utxo_spk=$(bitcoin-cli listunspent | jq -r '.[0] | .scriptPubKey')
|
$ utxo_spk=a914a5d106eb8ee51b23cf60d8bd98bc285695f233f387
|
||||||
```
|
```
|
||||||
|
|
||||||
### Record the Redeem Script
|
### Record the Redeem Script
|
||||||
|
|
||||||
Hopefully, you saved the `redeemScript`. Now you should record it in a variable.
|
Hopefully, you saved the `redeemScript`. Now you should record it in a variable.
|
||||||
|
|
||||||
|
This was drawn from our creation of the address in the previous section.
|
||||||
```
|
```
|
||||||
$ redeem_script="52210307fd375ed7cced0f50723e3e1a97bbe7ccff7318c815df4e99a59bc94dbcd819210367c4f666f18279009c941e57fab3e42653c6553e5ca092c104d1db279e328a2852ae"
|
redeem_script="522102da2f10746e9778dd57bd0276a4f84101c4e0a711f9cfd9f09cde55acbdd2d1912102bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa352ae"
|
||||||
```
|
```
|
||||||
### Decide Your Recipient
|
### Decide Your Recipient
|
||||||
|
|
||||||
@ -63,7 +118,9 @@ $ recipient=$(bitcoin-cli getrawchangeaddress)
|
|||||||
|
|
||||||
You can now create your transaction. This is no different than usual.
|
You can now create your transaction. This is no different than usual.
|
||||||
```
|
```
|
||||||
$ rawtxhex=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]''' outputs='''{ "'$recipient'": 1.299}''')
|
$ rawtxhex=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]''' outputs='''{ "'$recipient'": 0.00005}''')
|
||||||
|
$ echo $rawtxhex
|
||||||
|
020000000121654fa95d5a268abf96427e3292baed6c9f6d16ed9e80511070f954883864b10000000000ffffffff0188130000000000001600142c48d3401f6abed74f52df3f795c644b4398844600000000
|
||||||
```
|
```
|
||||||
|
|
||||||
## Sign Your Transaction
|
## Sign Your Transaction
|
||||||
@ -75,7 +132,7 @@ You're now ready to sign your transaction. This is a multi-step process because
|
|||||||
Because this transaction isn't making full use of your wallet, you're going to need to directly access your private keys. Start on `machine1`, where you should retrieve any of that user's private keys that were involved in the multisig:
|
Because this transaction isn't making full use of your wallet, you're going to need to directly access your private keys. Start on `machine1`, where you should retrieve any of that user's private keys that were involved in the multisig:
|
||||||
```
|
```
|
||||||
machine1$ bitcoin-cli -named dumpprivkey address=$address1
|
machine1$ bitcoin-cli -named dumpprivkey address=$address1
|
||||||
cMgb3KM8hPATCtgMKarKMiFesLft6eEw3DY6BB8d97fkeXeqQagw
|
cNPhhGjatADfhLD5gLfrR2JZKDE99Mn26NCbERsvnr24B3PcSbtR
|
||||||
```
|
```
|
||||||
> :warning: **WARNING:** Directly accessing your private keys from the shell is very dangerous behavior and should be done with extreme care if you're using real money. At the least, don't save the information into a variable that could be accessed from your machine. Removing your shell's history is another great step. At the most, don't do it.
|
> :warning: **WARNING:** Directly accessing your private keys from the shell is very dangerous behavior and should be done with extreme care if you're using real money. At the least, don't save the information into a variable that could be accessed from your machine. Removing your shell's history is another great step. At the most, don't do it.
|
||||||
|
|
||||||
@ -87,51 +144,54 @@ You can now make your first signature with the `signrawtransactionwithkey` comma
|
|||||||
* Include a `privkeys` argument that lists the private keys you dumped on this machine.
|
* Include a `privkeys` argument that lists the private keys you dumped on this machine.
|
||||||
|
|
||||||
```
|
```
|
||||||
machine1$ bitcoin-cli -named signrawtransactionwithkey hexstring=$rawtxhex prevtxs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout', "scriptPubKey": "'$utxo_spk'", "redeemScript": "'$redeem_script'" } ]''' privkeys='["cMgb3KM8hPATCtgMKarKMiFesLft6eEw3DY6BB8d97fkeXeqQagw"]'
|
machine1$ bitcoin-cli -named signrawtransactionwithkey hexstring=$rawtxhex prevtxs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout', "scriptPubKey": "'$utxo_spk'", "redeemScript": "'$redeem_script'" } ]''' privkeys='["cNPhhGjatADfhLD5gLfrR2JZKDE99Mn26NCbERsvnr24B3PcSbtR"]'
|
||||||
{
|
{
|
||||||
"hex": "020000000128e3b6f901705be0730e8a1345cf893e3cc38f0598e38bc56e9d43ac1ae11b62000000009200483045022100a9fe6ed0dbe14c0c4c7c89cee0aef2770f0b2bdcd6b3e8d71fe91e91c4bb765e02200cfba27a59b584a0cc8e70fb4438be94da417ee77eff28deb70449e012b6d6fa014752210307fd375ed7cced0f50723e3e1a97bbe7ccff7318c815df4e99a59bc94dbcd819210367c4f666f18279009c941e57fab3e42653c6553e5ca092c104d1db279e328a2852aeffffffff01e01dbe07000000001976a914cd1b2ba4fa8ae3e62bc4fc6be467a63228ceeedf88ac00000000",
|
"hex": "020000000121654fa95d5a268abf96427e3292baed6c9f6d16ed9e80511070f954883864b100000000920047304402201c97b48215f261055e41b765ab025e77a849b349698ed742b305f2c845c69b3f022013a5142ef61db1ff425fbdcdeb3ea370aaff5265eee0956cff9aa97ad9a357e3010047522102da2f10746e9778dd57bd0276a4f84101c4e0a711f9cfd9f09cde55acbdd2d1912102bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa352aeffffffff0188130000000000001600142c48d3401f6abed74f52df3f795c644b4398844600000000",
|
||||||
"complete": false,
|
"complete": false,
|
||||||
"errors": [
|
"errors": [
|
||||||
{
|
{
|
||||||
"txid": "621be11aac439d6ec58be398058fc33c3e89cf45138a0e73e05b7001f9b6e328",
|
"txid": "b164388854f9701051809eed166d9f6cedba92327e4296bf8a265a5da94f6521",
|
||||||
"vout": 0,
|
"vout": 0,
|
||||||
"scriptSig": "00483045022100a9fe6ed0dbe14c0c4c7c89cee0aef2770f0b2bdcd6b3e8d71fe91e91c4bb765e02200cfba27a59b584a0cc8e70fb4438be94da417ee77eff28deb70449e012b6d6fa014752210307fd375ed7cced0f50723e3e1a97bbe7ccff7318c815df4e99a59bc94dbcd819210367c4f666f18279009c941e57fab3e42653c6553e5ca092c104d1db279e328a2852ae",
|
"witness": [
|
||||||
|
],
|
||||||
|
"scriptSig": "0047304402201c97b48215f261055e41b765ab025e77a849b349698ed742b305f2c845c69b3f022013a5142ef61db1ff425fbdcdeb3ea370aaff5265eee0956cff9aa97ad9a357e3010047522102da2f10746e9778dd57bd0276a4f84101c4e0a711f9cfd9f09cde55acbdd2d1912102bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa352ae",
|
||||||
"sequence": 4294967295,
|
"sequence": 4294967295,
|
||||||
"error": "Operation not valid with the current stack size"
|
"error": "CHECK(MULTI)SIG failing with non-zero signature (possibly need more signatures)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
That produces scary errors and says that it's not `complete`. This is all correct. You can see that the signature has been partially successfully because the `hex` has gotten longer. Though the transaction has been partially signed, it's not done because it needs more signatures.
|
That produces scary errors and says that it's not `failing`. This is all correct. You can see that the signature has been partially successfully because the `hex` has gotten longer. Though the transaction has been partially signed, it's not done because it needs more signatures.
|
||||||
|
|
||||||
### Repeat for Other Signers
|
### Repeat for Other Signers
|
||||||
|
|
||||||
You can now pass the transaction on, to be signed again by anyone else required for the mutisig. They do this by running the same signing command that you did but: (1) with the longer `hex` that you output (`bitcoin-cli -named signrawtransactionwithkey hexstring=$rawtxhex prevtxs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout', "scriptPubKey": "'$utxo_spk'", "redeemScript": "'$redeem_script'" } ]''' privkeys='["cMgb3KM8hPATCtgMKarKMiFesLft6eEw3DY6BB8d97fkeXeqQagw"]' | jq -r '. | .hex'`); and (2) with their own private key.
|
You can now pass the transaction on, to be signed again by anyone else required for the mutisig. They do this by running the same signing command that you did but: (1) with the longer `hex` that you output from (`bitcoin-cli -named signrawtransactionwithkey hexstring=$rawtxhex prevtxs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout', "scriptPubKey": "'$utxo_spk'", "redeemScript": "'$redeem_script'" } ]''' privkeys='["cMgb3KM8hPATCtgMKarKMiFesLft6eEw3DY6BB8d97fkeXeqQagw"]' | jq -r '. | .hex'`); and (2) with their own private key.
|
||||||
|
|
||||||
> :information_source: **NOTE — M-OF-N VS N-OF-N:** Obviously, if you have an n-of-n signature (like the 2-of-2 multisignature in this example), then everyone has to sign, but if you hae a m-of-n multisignature where "m < n", then the signature will be complete when only some ("m") of the signers have signed.
|
> :information_source: **NOTE — M-OF-N VS N-OF-N:** Obviously, if you have an n-of-n signature (like the 2-of-2 multisignature in this example), then everyone has to sign, but if you hae a m-of-n multisignature where "m < n", then the signature will be complete when only some ("m") of the signers have signed.
|
||||||
|
|
||||||
To do so first they access their private keys:
|
To do so first they access their private keys:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli -named dumpprivkey address=$address2
|
machine2$ bitcoin-cli -named dumpprivkey address=$address2
|
||||||
cTi1Muvj24vG159R8orFjtqsPygCxhu8mJt2GLDQv7bNBGYoav4B
|
cVhqpKhx2jgfLUWmyR22JnichoctJCHPtPERm11a2yxnVFKWEKyz
|
||||||
```
|
```
|
||||||
Second, they sign the new `hex` using all the same `prevtxs` values:
|
Second, they sign the new `hex` using all the same `prevtxs` values:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli -named signrawtransactionwithkey hexstring=020000000128e3b6f901705be0730e8a1345cf893e3cc38f0598e38bc56e9d43ac1ae11b62000000009200483045022100a9fe6ed0dbe14c0c4c7c89cee0aef2770f0b2bdcd6b3e8d71fe91e91c4bb765e02200cfba27a59b584a0cc8e70fb4438be94da417ee77eff28deb70449e012b6d6fa014752210307fd375ed7cced0f50723e3e1a97bbe7ccff7318c815df4e99a59bc94dbcd819210367c4f666f18279009c941e57fab3e42653c6553e5ca092c104d1db279e328a2852aeffffffff01e01dbe07000000001976a914cd1b2ba4fa8ae3e62bc4fc6be467a63228ceeedf88ac00000000 prevtxs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout', "scriptPubKey": "'$utxo_spk'", "redeemScript": "'$redeem_script'" } ]''' privkeys='["cTi1Muvj24vG159R8orFjtqsPygCxhu8mJt2GLDQv7bNBGYoav4B"]'
|
machine1$ bitcoin-cli -named signrawtransactionwithkey hexstring=020000000121654fa95d5a268abf96427e3292baed6c9f6d16ed9e80511070f954883864b100000000920047304402201c97b48215f261055e41b765ab025e77a849b349698ed742b305f2c845c69b3f022013a5142ef61db1ff425fbdcdeb3ea370aaff5265eee0956cff9aa97ad9a357e3010047522102da2f10746e9778dd57bd0276a4f84101c4e0a711f9cfd9f09cde55acbdd2d1912102bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa352aeffffffff0188130000000000001600142c48d3401f6abed74f52df3f795c644b4398844600000000 prevtxs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout', "scriptPubKey": "'$utxo_spk'", "redeemScript": "'$redeem_script'" } ]''' privkeys='["cVhqpKhx2jgfLUWmyR22JnichoctJCHPtPERm11a2yxnVFKWEKyz"]'
|
||||||
{
|
{
|
||||||
"hex": "020000000128e3b6f901705be0730e8a1345cf893e3cc38f0598e38bc56e9d43ac1ae11b6200000000db00483045022100a9fe6ed0dbe14c0c4c7c89cee0aef2770f0b2bdcd6b3e8d71fe91e91c4bb765e02200cfba27a59b584a0cc8e70fb4438be94da417ee77eff28deb70449e012b6d6fa01483045022100d5190eb824535423f67b15040efaba66953ea39f312540dd38504ed85ba6436402206171883ff28c235030550c36cadb31e40aaa9a74f71579557b74a5684545675c014752210307fd375ed7cced0f50723e3e1a97bbe7ccff7318c815df4e99a59bc94dbcd819210367c4f666f18279009c941e57fab3e42653c6553e5ca092c104d1db279e328a2852aeffffffff01e01dbe07000000001976a914cd1b2ba4fa8ae3e62bc4fc6be467a63228ceeedf88ac00000000",
|
"hex": "020000000121654fa95d5a268abf96427e3292baed6c9f6d16ed9e80511070f954883864b100000000d90047304402201c97b48215f261055e41b765ab025e77a849b349698ed742b305f2c845c69b3f022013a5142ef61db1ff425fbdcdeb3ea370aaff5265eee0956cff9aa97ad9a357e301473044022000a402ec4549a65799688dd531d7b18b03c6379416cc8c29b92011987084e9f402205470e24781509c70e2410aaa6d827aa133d6df2c578e96a496b885584fb039200147522102da2f10746e9778dd57bd0276a4f84101c4e0a711f9cfd9f09cde55acbdd2d1912102bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa352aeffffffff0188130000000000001600142c48d3401f6abed74f52df3f795c644b4398844600000000",
|
||||||
"complete": true
|
"complete": true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Third, they may need to send on the even longer `hexstring` they produce to additional signers.
|
Third, they may need to send on the even longer `hexstring` they produce to additional signers.
|
||||||
|
|
||||||
In this case, we now see that the signature is `complete`!
|
But in this case, we now see that the signature is `complete`!
|
||||||
|
|
||||||
## Send Your Transaction
|
## Send Your Transaction
|
||||||
|
|
||||||
When done, you should fall back on the standard JQ methodology to save your `hexstring` and then to send it:
|
When done, you should fall back on the standard JQ methodology to save your `hexstring` and then to send it:
|
||||||
```
|
```
|
||||||
$ signedtx = $(bitcoin-cli -named signrawtransactionwithkey hexstring=020000000128e3b6f901705be0730e8a1345cf893e3cc38f0598e38bc56e9d43ac1ae11b62000000009200483045022100a9fe6ed0dbe14c0c4c7c89cee0aef2770f0b2bdcd6b3e8d71fe91e91c4bb765e02200cfba27a59b584a0cc8e70fb4438be94da417ee77eff28deb70449e012b6d6fa014752210307fd375ed7cced0f50723e3e1a97bbe7ccff7318c815df4e99a59bc94dbcd819210367c4f666f18279009c941e57fab3e42653c6553e5ca092c104d1db279e328a2852aeffffffff01e01dbe07000000001976a914cd1b2ba4fa8ae3e62bc4fc6be467a63228ceeedf88ac00000000 prevtxs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout', "scriptPubKey": "'$utxo_spk'", "redeemScript": "'$redeem_script'" } ]''' privkeys='["cTi1Muvj24vG159R8orFjtqsPygCxhu8mJt2GLDQv7bNBGYoav4B"]' | jq -r '.hex')
|
$ signedtx=$(bitcoin-cli -named signrawtransactionwithkey hexstring=020000000121654fa95d5a268abf96427e3292baed6c9f6d16ed9e80511070f954883864b100000000920047304402201c97b48215f261055e41b765ab025e77a849b349698ed742b305f2c845c69b3f022013a5142ef61db1ff425fbdcdeb3ea370aaff5265eee0956cff9aa97ad9a357e3010047522102da2f10746e9778dd57bd0276a4f84101c4e0a711f9cfd9f09cde55acbdd2d1912102bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa352aeffffffff0188130000000000001600142c48d3401f6abed74f52df3f795c644b4398844600000000 prevtxs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout', "scriptPubKey": "'$utxo_spk'", "redeemScript": "'$redeem_script'" } ]''' privkeys='["cVhqpKhx2jgfLUWmyR22JnichoctJCHPtPERm11a2yxnVFKWEKyz"]' | jq -r .hex)
|
||||||
$ bitcoin-cli -named sendrawtransaction hexstring=$signedtx
|
$ bitcoin-cli -named sendrawtransaction hexstring=$signedtx
|
||||||
99d2b5717fed8875a1ed3b2827dd60ae3089f9caa7c7c23d47635f6f5b397c04
|
99d2b5717fed8875a1ed3b2827dd60ae3089f9caa7c7c23d47635f6f5b397c04
|
||||||
```
|
```
|
||||||
|
@ -16,28 +16,36 @@ You start off creating P2PKH addresses and retrieving public keys as usual, for
|
|||||||
```
|
```
|
||||||
machine1$ address3=$(bitcoin-cli getnewaddress)
|
machine1$ address3=$(bitcoin-cli getnewaddress)
|
||||||
machine1$ echo $address3
|
machine1$ echo $address3
|
||||||
mkMkhbUzcSPdEHUoRQkBKHe8otP1SzWWeb
|
tb1q4ep2vmakpkkj6mflu94x5f94q662m0u5ad0t4w
|
||||||
machine1$ bitcoin-cli -named getaddressinfo address=$address3 | jq -r '. | .pubkey'
|
machine1$ bitcoin-cli -named getaddressinfo address=$address3 | jq -r '. | .pubkey'
|
||||||
02e7356952f4bb1daf475c04b95a2f7e0d9a12cf5b5c48a25b2303783d91849ba4
|
0297e681bff16cd4600138449e2527db4b2f83955c691a1b84254ecffddb9bfbfc
|
||||||
|
|
||||||
machine2$ address4=$(bitcoin-cli getnewaddress)
|
machine2$ address4=$(bitcoin-cli getnewaddress)
|
||||||
$ echo $address4
|
$ echo $address4
|
||||||
mkyeUBPDoeyFrfLE4V5oAQfee99pT2W1E3
|
tb1qa9v5h6zkhq8wh0etnv3ae9cdurkh085xufl3de
|
||||||
$ bitcoin-cli -named getaddressinfo address=$address4 | jq -r '. | .pubkey'
|
$ bitcoin-cli -named getaddressinfo address=$address4 | jq -r '. | .pubkey'
|
||||||
030186d2b55de166389aefe209f508ce1fbd79966d9ac417adef74b7c1b5e07776
|
02a0d96e16458ff0c90db4826f86408f2cfa0e960514c0db547ff152d3e567738f
|
||||||
```
|
```
|
||||||
|
|
||||||
### Create the Multisig Address Everywhere
|
### Create the Multisig Address Everywhere
|
||||||
|
|
||||||
Next you create the multisig on _each machine that contributes signatures_ using a new command, `addmultisigaddress`, instead of `createmultisig`. This new command saves some of the information into your wallet, making it a lot easier to spend the money afterward.
|
Next you create the multisig on _each machine that contributes signatures_ using a new command, `addmultisigaddress`, instead of `createmultisig`. This new command saves some of the information into your wallet, making it a lot easier to spend the money afterward.
|
||||||
```
|
```
|
||||||
machine1$ bitcoin-cli -named addmultisigaddress nrequired=2 keys='''["'$address3'","030186d2b55de166389aefe209f508ce1fbd79966d9ac417adef74b7c1b5e07776"]'''
|
machine1$ bitcoin-cli -named addmultisigaddress nrequired=2 keys='''["'$address3'","02a0d96e16458ff0c90db4826f86408f2cfa0e960514c0db547ff152d3e567738f"]'''
|
||||||
2Mzw7WBvh9RAQ4ssKqxyNyP7L9NAojLqSW8
|
{
|
||||||
|
"address": "tb1q9as46kupwcxancdx82gw65365svlzdwmjal4uxs23t3zz3rgg3wqpqlhex",
|
||||||
|
"redeemScript": "52210297e681bff16cd4600138449e2527db4b2f83955c691a1b84254ecffddb9bfbfc2102a0d96e16458ff0c90db4826f86408f2cfa0e960514c0db547ff152d3e567738f52ae",
|
||||||
|
"descriptor": "wsh(multi(2,[d6043800/0'/0'/15']0297e681bff16cd4600138449e2527db4b2f83955c691a1b84254ecffddb9bfbfc,[e9594be8]02a0d96e16458ff0c90db4826f86408f2cfa0e960514c0db547ff152d3e567738f))#wxn4tdju"
|
||||||
|
}
|
||||||
|
|
||||||
machine2$ bitcoin-cli -named addmultisigaddress nrequired=2 keys='''["02e7356952f4bb1daf475c04b95a2f7e0d9a12cf5b5c48a25b2303783d91849ba4","'$address4'"]'''
|
machine2$ bitcoin-cli -named addmultisigaddress nrequired=2 keys='''["0297e681bff16cd4600138449e2527db4b2f83955c691a1b84254ecffddb9bfbfc","'$address4'"]'''
|
||||||
2Mzw7WBvh9RAQ4ssKqxyNyP7L9NAojLqSW8
|
{
|
||||||
|
"address": "tb1q9as46kupwcxancdx82gw65365svlzdwmjal4uxs23t3zz3rgg3wqpqlhex",
|
||||||
|
"redeemScript": "52210297e681bff16cd4600138449e2527db4b2f83955c691a1b84254ecffddb9bfbfc2102a0d96e16458ff0c90db4826f86408f2cfa0e960514c0db547ff152d3e567738f52ae",
|
||||||
|
"descriptor": "wsh(multi(2,[ae42a66f]0297e681bff16cd4600138449e2527db4b2f83955c691a1b84254ecffddb9bfbfc,[fe6f2292/0'/0'/2']02a0d96e16458ff0c90db4826f86408f2cfa0e960514c0db547ff152d3e567738f))#cc96c5n6"
|
||||||
|
}
|
||||||
```
|
```
|
||||||
As noted in the previous section, it doesn't matter whether you use addresses or public keys: you'll get the same multisig address. However, you must use the same order. Thus, it's best for the members of the multisig to check amongst themselves to make sure they all got the same result.
|
As noted in the previous section, it doesn't matter whether you use addresses or public keys: you'll get the same multisig address. However, _you must use the same order_. Thus, it's best for the members of the multisig to check amongst themselves to make sure they all got the same result.
|
||||||
|
|
||||||
### Watch for Funds
|
### Watch for Funds
|
||||||
|
|
||||||
@ -47,6 +55,7 @@ machine1$ bitcoin-cli -named importaddress address=2Mzw7WBvh9RAQ4ssKqxyNyP7L9NAo
|
|||||||
|
|
||||||
machine2$ bitcoin-cli -named importaddress address=2Mzw7WBvh9RAQ4ssKqxyNyP7L9NAojLqSW8
|
machine2$ bitcoin-cli -named importaddress address=2Mzw7WBvh9RAQ4ssKqxyNyP7L9NAojLqSW8
|
||||||
```
|
```
|
||||||
|
(Or use other means if they have a pruned node.)
|
||||||
|
|
||||||
## Respend with an Automated Transaction
|
## Respend with an Automated Transaction
|
||||||
|
|
||||||
@ -54,32 +63,41 @@ Afterward, you will be able to receive funds on the multisiganture address as no
|
|||||||
|
|
||||||
But, it makes life a lot easier. Because everything is in the wallet, the signers will be able to respend the funds sent to the multisignature address exactly the same as any other address ... other than the need to sign on multiple machines.
|
But, it makes life a lot easier. Because everything is in the wallet, the signers will be able to respend the funds sent to the multisignature address exactly the same as any other address ... other than the need to sign on multiple machines.
|
||||||
|
|
||||||
You start by collecting your variables, but you no longer need to worry about `scriptPubKey` or `redeemScript`:
|
You start by collecting your variables, but you no longer need to worry about `scriptPubKey` or `redeemScript`.
|
||||||
|
|
||||||
|
Here's a new transaction sent to our new multisig address:
|
||||||
```
|
```
|
||||||
machine1$ utxo_txid=$(bitcoin-cli listunspent | jq -r '.[1] | .txid')
|
machine1$ utxo_txid=b9f3c4756ef8159d6a66414a4317f865882ee04beb57a0f8349dafcc98f5acbc
|
||||||
machine1$ utxo_vout=$(bitcoin-cli listunspent | jq -r '.[1] | .vout')
|
machine1$ utxo_vout=0
|
||||||
machine1$ recipient=$(bitcoin-cli getrawchangeaddress)
|
machine1$ recipient=$(bitcoin-cli getrawchangeaddress)
|
||||||
```
|
```
|
||||||
You create a raw transaction:
|
You create a raw transaction:
|
||||||
```
|
```
|
||||||
machine1$ rawtxhex=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]''' outputs='''{ "'$recipient'": 1.2995}''')
|
machine1$ rawtxhex=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]''' outputs='''{ "'$recipient'": 0.00005}''')
|
||||||
```
|
```
|
||||||
Then you sign it:
|
Then you sign it:
|
||||||
```
|
```
|
||||||
machine1$ bitcoin-cli -named signrawtransactionwithwallet hexstring=$rawtxhex
|
machine1$ bitcoin-cli -named signrawtransactionwithwallet hexstring=$rawtxhex
|
||||||
{
|
{
|
||||||
"hex": "02000000014ecda61c45f488e35c613a7c4ae26335a8d7bfd0a942f026d0fb1050e744a67d000000009100473044022025decef887fe2e3eb1c4b3edaa155e5755102d1570716f1467bb0b518b777ddf022017e97f8853af8acab4853ccf502213b7ff4cc3bd9502941369905371545de28d0147522102e7356952f4bb1daf475c04b95a2f7e0d9a12cf5b5c48a25b2303783d91849ba421030186d2b55de166389aefe209f508ce1fbd79966d9ac417adef74b7c1b5e0777652aeffffffff0130e1be07000000001976a9148dfbf103e48df7d1993448aa387dc31a2ebd522d88ac00000000",
|
"hex": "02000000000101bcacf598ccaf9d34f8a057eb4be02e8865f817434a41666a9d15f86e75c4f3b90000000000ffffffff0188130000000000001600144f93c831ec739166ea425984170f4dc6bac75829040047304402205f84d40ba16ff49e60a7fc9228ef5917473aae1ab667dad01e113ca0fef3008b02201a50da2c65f38798aea94bcbd5bbf065bc1e38de44bacee69d525dcddcc11bba01004752210297e681bff16cd4600138449e2527db4b2f83955c691a1b84254ecffddb9bfbfc2102a0d96e16458ff0c90db4826f86408f2cfa0e960514c0db547ff152d3e567738f52ae00000000",
|
||||||
"complete": false,
|
"complete": false,
|
||||||
"errors": [
|
"errors": [
|
||||||
{
|
{
|
||||||
"txid": "7da644e75010fbd026f042a9d0bfd7a83563e24a7c3a615ce388f4451ca6cd4e",
|
"txid": "b9f3c4756ef8159d6a66414a4317f865882ee04beb57a0f8349dafcc98f5acbc",
|
||||||
"vout": 0,
|
"vout": 0,
|
||||||
"scriptSig": "00473044022025decef887fe2e3eb1c4b3edaa155e5755102d1570716f1467bb0b518b777ddf022017e97f8853af8acab4853ccf502213b7ff4cc3bd9502941369905371545de28d0147522102e7356952f4bb1daf475c04b95a2f7e0d9a12cf5b5c48a25b2303783d91849ba421030186d2b55de166389aefe209f508ce1fbd79966d9ac417adef74b7c1b5e0777652ae",
|
"witness": [
|
||||||
|
"",
|
||||||
|
"304402205f84d40ba16ff49e60a7fc9228ef5917473aae1ab667dad01e113ca0fef3008b02201a50da2c65f38798aea94bcbd5bbf065bc1e38de44bacee69d525dcddcc11bba01",
|
||||||
|
"",
|
||||||
|
"52210297e681bff16cd4600138449e2527db4b2f83955c691a1b84254ecffddb9bfbfc2102a0d96e16458ff0c90db4826f86408f2cfa0e960514c0db547ff152d3e567738f52ae"
|
||||||
|
],
|
||||||
|
"scriptSig": "",
|
||||||
"sequence": 4294967295,
|
"sequence": 4294967295,
|
||||||
"error": "Operation not valid with the current stack size"
|
"error": "CHECK(MULTI)SIG failing with non-zero signature (possibly need more signatures)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
Note that you no longer had to give `signrawtransactionwithkey` extra help, because all of that extra information was already in your wallet. Most importantly, you didn't make your private keys vulnerable by directly manipulating them. Instead the process was _exactly_ the same as respending a normal UTXO, except that the transaction wasn't fully signed at the end.
|
Note that you no longer had to give `signrawtransactionwithkey` extra help, because all of that extra information was already in your wallet. Most importantly, you didn't make your private keys vulnerable by directly manipulating them. Instead the process was _exactly_ the same as respending a normal UTXO, except that the transaction wasn't fully signed at the end.
|
||||||
|
|
||||||
@ -102,4 +120,4 @@ There's an easier way to resepend funds sent to multisig addresses that simply r
|
|||||||
|
|
||||||
## What's Next?
|
## What's Next?
|
||||||
|
|
||||||
Continue "Expanding Bitcoin Transactions" with [§6.4: Sending a Transaction with a Locktime](06_4_Sending_a_Transaction_with_a_Locktime.md).
|
Advance through "bitcoin-cli" with [Chapter Seven: Expanding Bitcoin Transactions with PSBTs](07_0_Expanding_Bitcoin_Transactions_PSBTs.md).
|
||||||
|
@ -1,120 +0,0 @@
|
|||||||
# 6.5: Sending a Transaction with Data
|
|
||||||
|
|
||||||
> :information_source: **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
|
||||||
|
|
||||||
The third way to vary how you send a basic transaction is to use the transaction to send data instead of funds (or really, in addition to funds). This gives you the ability to embed information in the blockchain.
|
|
||||||
|
|
||||||
## Create Your Data
|
|
||||||
|
|
||||||
The first thing you need to do is create the 80 bytes (or less) of data that you'll be recording in your OP_RETURN. This might be as simple as preparing a message or you might be hashing existing data. For example, `sha256sum` produces 256 bits of data, which is 32 bytes, well under the limits:
|
|
||||||
```
|
|
||||||
$ sha256sum contract.jpg
|
|
||||||
fe7f0a3b69f56ef2d055a78823ed3bd1422e46c3183658ea854253033ae0ccef contract.jpg
|
|
||||||
$ op_return_data="fe7f0a3b69f56ef2d055a78823ed3bd1422e46c3183658ea854253033ae0ccef"
|
|
||||||
```
|
|
||||||
_What is an OP_RETURN?_ All Bitcoin transactions are built upon opcode scripts that we'll meet in the next chapter. The OP_RETURN is a simple opcode that defines a transaction as invalid. Convention has resulted in it being used to embed data on the blockchain.
|
|
||||||
|
|
||||||
## Prepare Some Money
|
|
||||||
|
|
||||||
Your purpose in creating a data transaction isn't to send money to anyone, it's to put data into the blockchain. However, you _must_ send money to do so. You just need to use a change address as your _only_ recipient. Then you can identify a UTXO and send that to your change address, minus a transaction fee, while also using the same transaction to create an OP_RETURN.
|
|
||||||
|
|
||||||
Here's the standard setup:
|
|
||||||
```
|
|
||||||
$ bitcoin-cli listunspent
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"txid": "4460175e8276d5a1935f6136e36868a0a3561532d44ddffb09b7cb878f76f927",
|
|
||||||
"vout": 1,
|
|
||||||
"address": "my7UvPbPWDbqCxYpAcUZELDbK1X7w9Whmc",
|
|
||||||
"scriptPubKey": "76a914c101d8c34de7b8d83b3f8d75416ffaea871d664988ac",
|
|
||||||
"amount": 0.90000000,
|
|
||||||
"confirmations": 682,
|
|
||||||
"spendable": true,
|
|
||||||
"solvable": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
$ utxo_txid=$(bitcoin-cli listunspent | jq -r '.[0] | .txid')
|
|
||||||
$ utxo_vout=$(bitcoin-cli listunspent | jq -r '.[0] | .vout')
|
|
||||||
$ changeaddress=$(bitcoin-cli getrawchangeaddress)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Write A Raw Transaction
|
|
||||||
|
|
||||||
You can now write a new rawtransaction with two outputs: one is your change address to get back (most of) your money, the other is a data address, which is the `bitcoin-cli` term for an OP_RETURN.
|
|
||||||
```
|
|
||||||
$ rawtxhex=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]''' outputs='''{ "data": "'$op_return_data'", "'$changeaddress'": 0.8995 }''')
|
|
||||||
```
|
|
||||||
|
|
||||||
Here's what that transaction actually looks like:
|
|
||||||
```
|
|
||||||
$ bitcoin-cli -named decoderawtransaction hexstring=$rawtxhex
|
|
||||||
{
|
|
||||||
"txid": "531660f9bcb2571b2b790ebe6e7b6ed40618f608f3f6722a511275257942790b",
|
|
||||||
"hash": "531660f9bcb2571b2b790ebe6e7b6ed40618f608f3f6722a511275257942790b",
|
|
||||||
"size": 128,
|
|
||||||
"vsize": 128,
|
|
||||||
"version": 2,
|
|
||||||
"locktime": 0,
|
|
||||||
"vin": [
|
|
||||||
{
|
|
||||||
"txid": "4460175e8276d5a1935f6136e36868a0a3561532d44ddffb09b7cb878f76f927",
|
|
||||||
"vout": 1,
|
|
||||||
"scriptSig": {
|
|
||||||
"asm": "",
|
|
||||||
"hex": ""
|
|
||||||
},
|
|
||||||
"sequence": 4294967295
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"vout": [
|
|
||||||
{
|
|
||||||
"value": 0.00000000,
|
|
||||||
"n": 0,
|
|
||||||
"scriptPubKey": {
|
|
||||||
"asm": "OP_RETURN fe7f0a3b69f56ef2d055a78823ed3bd1422e46c3183658ea854253033ae0ccef",
|
|
||||||
"hex": "6a20fe7f0a3b69f56ef2d055a78823ed3bd1422e46c3183658ea854253033ae0ccef",
|
|
||||||
"type": "nulldata"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": 0.89950000,
|
|
||||||
"n": 1,
|
|
||||||
"scriptPubKey": {
|
|
||||||
"asm": "OP_DUP OP_HASH160 166692bda9f25ced145267bb44286e8ee3963d26 OP_EQUALVERIFY OP_CHECKSIG",
|
|
||||||
"hex": "76a914166692bda9f25ced145267bb44286e8ee3963d2688ac",
|
|
||||||
"reqSigs": 1,
|
|
||||||
"type": "pubkeyhash",
|
|
||||||
"addresses": [
|
|
||||||
"mhZQ3Bih6wi7jP1tpFZrCcyr4NsfCapiZP"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
As you can see, this sends the majority of the money straight back to the change address (mhZQ3Bih6wi7jP1tpFZrCcyr4NsfCapiZP) minus that standard transaction fee we've been using of 0.0005 BTC. More importantly, the first output shows an OP_RETURN with the data (fe7f0a3b69f56ef2d055a78823ed3bd1422e46c3183658ea854253033ae0ccef) right after it.
|
|
||||||
|
|
||||||
## Send A Raw Transaction
|
|
||||||
|
|
||||||
Sign your raw transaction and send it, and soon that OP_RETURN will be embedded in the blockchain!
|
|
||||||
|
|
||||||
## Check Your OP_RETURN
|
|
||||||
|
|
||||||
Again, remember that you can look at this transaction using a blockchain explorer: [https://www.blocktrail.com/tBTC/tx/3a62b396afb8d8a59ebe7b9e52d6aa2485f1082a1d3fc6ece61fb8b55373823d](https://www.blocktrail.com/tBTC/tx/3a62b396afb8d8a59ebe7b9e52d6aa2485f1082a1d3fc6ece61fb8b55373823d)
|
|
||||||
|
|
||||||
You may note a warning about the data being in an "unknown protocol". If you were designing some regular use of OP_RETURN data, you'd probably mark it with a special prefix, to mark that protocol. Then, the actual OP_RETURN data might be something like "CONTRACTS3b110a164aa18d3a5ab064ba93fdce62". This example didn't use a prefix to avoid muddying the data space.
|
|
||||||
|
|
||||||
[Coinsecrets](http://coinsecrets.org/) offers another interesting way to look at OP_RETURN data. It does its best to keep abreast of protocols, so that it can tell you who is doing what in the blockchain. Here's this transaction there: [https://www.blocktrail.com/tBTC/tx/3a62b396afb8d8a59ebe7b9e52d6aa2485f1082a1d3fc6ece61fb8b55373823d](https://www.blocktrail.com/tBTC/tx/3a62b396afb8d8a59ebe7b9e52d6aa2485f1082a1d3fc6ece61fb8b55373823d)
|
|
||||||
|
|
||||||
## Summary: Sending a Transaction with Data
|
|
||||||
|
|
||||||
You can use an OP_RETURN opcode to store up to 80 bytes of data on the blockchain. You do this with the `data` codeword for a `vout`. You still have to send money along too, but you just send it back to a change address, minus a transaction fee.
|
|
||||||
|
|
||||||
_What is the Power of OP_RETURN?_ The OP_RETURN opens up whole new possibilities for the blockchain, because you can embed data that proves that certain things happened at certain times. Various organizations have used OP_RETURNs for proof of existence, for copyright, for colored coins, and [for other purposes](https://en.bitcoin.it/wiki/OP_RETURN). Though 80 bytes might not seem a lot, it can be quite effective if OP_RETURNs are used to store hashes of the actual data. Then, you can prove the existence of your digital data by deminstrating that the hash of it matches the hash on the blockchain.
|
|
||||||
|
|
||||||
Note that there is some controversy over using the Bitcoin blockchain in this way.
|
|
||||||
|
|
||||||
## What's Next?
|
|
||||||
|
|
||||||
Move on to "Bitcoin Scripting" with [Chapter Seven: Introducing Bitcoin Scripts](07_0_Introducing_Bitcoin_Scripts.md).
|
|
@ -1,6 +1,6 @@
|
|||||||
# Chapter Six: Expanding Bitcoin Transactions
|
# Chapter Six: Expanding Bitcoin Transactions
|
||||||
|
|
||||||
Basic bitcoin transactions: (1) send funds; (2) to a single P2PKH recipient; (3) immediately. However, all three parts of this definition can be expanded using more complex Bitcoin transactions, which can alternatively send data, which can send to a group of recipients, or which can send at a later time. These three options represent further empowerment of Bitcoin and also the furthest boundaries of what you can do with the basic `bitcoin-cli` command.
|
Basic bitcoin transactions: (1) send funds; (2) to a single P2PKH or SegWit recipient; (3) immediately. However, all three parts of this definition can be expanded using more complex Bitcoin transactions, which can alternatively send data, which can send to a group of recipients, or which can send at a later time. These three options represent further empowerment of Bitcoin and also the furthest boundaries of what you can do with the basic `bitcoin-cli` command.
|
||||||
|
|
||||||
## Objectives for This Section
|
## Objectives for This Section
|
||||||
|
|
27
07_1_Creating_a_Partially_Signed_Bitcoin_Transaction.md
Normal file
27
07_1_Creating_a_Partially_Signed_Bitcoin_Transaction.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# 6.6: Creating a Partially Signed Bitcoin Transaction
|
||||||
|
|
||||||
|
> :information_source: **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
||||||
|
|
||||||
|
[intro]
|
||||||
|
|
||||||
|
> :warning: **VERSION WARNING:** This is an innovation from Bitcoin Core v 0.17.0. Earlier versions of Bitcoin Core will not be able to work with the PSBT while it is in process (though they will still be able to recognize the final transaction).
|
||||||
|
|
||||||
|
## Understand a PSBT
|
||||||
|
|
||||||
|
## Create a PSBT the Old-Fashioned Way
|
||||||
|
|
||||||
|
## Create a PSBT the Hard Way
|
||||||
|
|
||||||
|
## Create a PSBT the Easy Way
|
||||||
|
|
||||||
|
## Examine a PSBT
|
||||||
|
|
||||||
|
## Update a PSBT
|
||||||
|
|
||||||
|
## Sign a PSBT
|
||||||
|
|
||||||
|
## Summary: Creating a Partially Signed Bitcoin Transaction
|
||||||
|
|
||||||
|
## What's Next?
|
||||||
|
|
||||||
|
Continue "Expanding Bitcoin Transactions" with [§6.7: Using a Partially Signed Bitcoin Transaction](06_7_Using_a_Partially_Signed_Bitcoin_Transaction.md).
|
@ -80,7 +80,7 @@ Let's try this out:
|
|||||||
```Bash
|
```Bash
|
||||||
$ btcc OP_1 OP_2 OP_ADD
|
$ btcc OP_1 OP_2 OP_ADD
|
||||||
515293
|
515293
|
||||||
$ btcdeb '[OP_1 OP_2 OP_ADD]' # or: btcdeb 515293
|
$ btcdeb '[OP_1 OP_2 OP_ADD]' # or: btcdeb 0x515293
|
||||||
btcdeb -- type `btcdeb -h` for start up options
|
btcdeb -- type `btcdeb -h` for start up options
|
||||||
valid script
|
valid script
|
||||||
3 op script loaded. type `help` for usage information
|
3 op script loaded. type `help` for usage information
|
||||||
|
21
07_2_Using_a_Partially_Signed_Bitcoin_Transaction.md
Normal file
21
07_2_Using_a_Partially_Signed_Bitcoin_Transaction.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# 6.7: Using a Partially Signed Bitcoin Transaction
|
||||||
|
|
||||||
|
> :information_source: **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
||||||
|
|
||||||
|
[intro]
|
||||||
|
|
||||||
|
> :warning: **VERSION WARNING:** This is an innovation from Bitcoin Core v 0.17.0. Earlier versions of Bitcoin Core will not be able to work with the PSBT while it is in process (though they will still be able to recognize the final transaction).
|
||||||
|
|
||||||
|
## Use a PSBT to MultiSig
|
||||||
|
|
||||||
|
## Use a PSBT to Pool Money
|
||||||
|
|
||||||
|
## Use a PSBT to CoinJoin
|
||||||
|
|
||||||
|
## Summary: Using a Partially Signed Bitcoin Transaction
|
||||||
|
|
||||||
|
> :fire: ***What's the power of a PSBT?***
|
||||||
|
|
||||||
|
## What's Next?
|
||||||
|
|
||||||
|
Move on to "Bitcoin Scripting" with [Chapter Seven: Introducing Bitcoin Scripts](07_0_Introducing_Bitcoin_Scripts.md).
|
21
07_3_Integrating_with_Hardware_Wallets.md
Normal file
21
07_3_Integrating_with_Hardware_Wallets.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# 7.3: Integrating with Hardware Wallets
|
||||||
|
|
||||||
|
> :information_source: **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
||||||
|
|
||||||
|
[intro]
|
||||||
|
|
||||||
|
> :warning: **VERSION WARNING:** This is an innovation from Bitcoin Core v 0.17.0. Earlier versions of Bitcoin Core will not be able to work with the PSBT while it is in process (though they will still be able to recognize the final transaction).
|
||||||
|
|
||||||
|
## Link to a Ledger
|
||||||
|
|
||||||
|
## Import Addresses
|
||||||
|
|
||||||
|
## Create a Transaction with PSBT
|
||||||
|
|
||||||
|
## Summary: Integrating with Hardware Wallets
|
||||||
|
|
||||||
|
> :fire: ***What's the power of HWI?***
|
||||||
|
|
||||||
|
## What's Next?
|
||||||
|
|
||||||
|
Move on to "Bitcoin Scripting" with [Chapter Seven: Introducing Bitcoin Scripts](07_0_Introducing_Bitcoin_Scripts.md).
|
21
08_0_Expanding_Bitcoin_Transactions_Other.md
Normal file
21
08_0_Expanding_Bitcoin_Transactions_Other.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Chapter Eight: Expanding Bitcoin Transactions in Other Ways
|
||||||
|
|
||||||
|
The definition of basic transactions back in [Chapter Six](06_0_Expanding_Bitcoin_Transactions_Multisigs.md) said that they sent _funds_ _immediately_, but that's another thing that can be changed. This final section on Expanding Bitcoin Transactions talks about how to send things other than cash and how to do it at a time other than now.
|
||||||
|
|
||||||
|
## Objectives for This Section
|
||||||
|
|
||||||
|
After working through this chapter, a developer will be able to:
|
||||||
|
|
||||||
|
* Create Transactions with Locktimes
|
||||||
|
* Create Transactions with Data
|
||||||
|
|
||||||
|
Supporting objectives include the ability to:
|
||||||
|
|
||||||
|
* Understand the Different Sorts of Timelocks
|
||||||
|
* Plan for the Power of Locktime
|
||||||
|
* Plan for the Power of OP_RETURN
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
* [Section One: Sending a Transaction with a Locktime](08_1_Sending_a_Transaction_with_a_Locktime.md)
|
||||||
|
* [Section Two: Sending a Transaction with Data](08_2_Sending_a_Transaction_with_Data.md)
|
@ -1,4 +1,4 @@
|
|||||||
# 6.4: Sending a Transaction with a Locktime
|
# 8.1: Sending a Transaction with a Locktime
|
||||||
|
|
||||||
> :information_source: **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
> :information_source: **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
||||||
|
|
||||||
@ -8,13 +8,15 @@ The second way to vary how you send a basic transaction is to choose a locktime.
|
|||||||
|
|
||||||
When you create a locktime transaction, you lock it with a number that represents either a block height (if it's a small number) or a UNIX timestamp (if it's a big number). This tells the Bitcoin network that the transaction may not be put into a block until either the specified time has arrived or the blockchain has reached the specified height.
|
When you create a locktime transaction, you lock it with a number that represents either a block height (if it's a small number) or a UNIX timestamp (if it's a big number). This tells the Bitcoin network that the transaction may not be put into a block until either the specified time has arrived or the blockchain has reached the specified height.
|
||||||
|
|
||||||
_What is block height?_ It's the total count of blocks in the chain, going back to the genesis block for Bitcoin.
|
> :book: _What is block height?_ It's the total count of blocks in the chain, going back to the genesis block for Bitcoin.
|
||||||
|
|
||||||
When a locktime transaction is waiting to go into a block, it can be cancelled. This means that it is far, far from finalized. In fact, the ability to cancel is the whole purpose of a locktime transaction.
|
When a locktime transaction is waiting to go into a block, it can be cancelled. This means that it is far, far from finalized. In fact, the ability to cancel is the whole purpose of a locktime transaction.
|
||||||
|
|
||||||
_What is nLockTime?_ It's the same thing as locktime. More specifically, it's what locktime is called internal to the Bitcoin Core source code.
|
> :book: _What is nLockTime?_ It's the same thing as locktime. More specifically, it's what locktime is called internal to the Bitcoin Core source code.
|
||||||
|
|
||||||
_What is Timelock?_ Locktime is just one way to lock Bitcoin transactions until some point in the future; collectively these methods are called timelocks. Locktime is the most basic timelock method. It locks an entire transaction with an absolute time, and it's available through `bitcoin-cli` (which is why it's the only timelock covered in this section). A parallel method, which locks a transaction with a relative time is defined in [BIP 68](https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki) and covered in [§9.3: Using CSV in Scripts](09_3_Using_CSV_in_Scripts.md). Bitcoin Script further empowers both sorts of timelocks, allowing for the locking of individual outputs instead of entire transactions. Absolute timelocks are linked to OP_CHECKLOCKTIMEVERIFY, which is defined in [BIP 65](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki) and covered in [§9.2: Using CLTV in Scripts](09_2_Using_CLTV_in_Scripts.md), while relative timelocks are linked to OP_CHECKSEQUENCEVERIFY, which is defined in [BIP 112](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki) and also covered in [§9.3](09_3_Using_CSV_in_Scripts.md).
|
> :book: _What is Timelock?_ Locktime is just one way to lock Bitcoin transactions until some point in the future; collectively these methods are called timelocks. Locktime is the most basic timelock method. It locks an entire transaction with an absolute time, and it's available through `bitcoin-cli` (which is why it's the only timelock covered in this section). A parallel method, which locks a transaction with a relative time, is defined in [BIP 68](https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki) and covered in [§9.3: Using CSV in Scripts](09_3_Using_CSV_in_Scripts.md).
|
||||||
|
|
||||||
|
> Bitcoin Script further empowers both sorts of timelocks, allowing for the locking of individual outputs instead of entire transactions. Absolute timelocks (such as Locktime) are linked to the Script opcode OP_CHECKLOCKTIMEVERIFY, which is defined in [BIP 65](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki) and covered in [§9.2: Using CLTV in Scripts](09_2_Using_CLTV_in_Scripts.md), while relative timelocks (such as Timelock) are linked to the Script opcode OP_CHECKSEQUENCEVERIFY, which is defined in [BIP 112](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki) and also covered in [§9.3](09_3_Using_CSV_in_Scripts.md).
|
||||||
|
|
||||||
## Create a Locktime Transaction
|
## Create a Locktime Transaction
|
||||||
|
|
||||||
@ -24,17 +26,17 @@ In order to create a locktime transaction, you need to first determine what you
|
|||||||
|
|
||||||
Most frequently you will set the locktime to a UNIX timestamp representing a specific date and time. You can calculate a UNIX timestamp at a web site like [UNIX Time Stamp](http://www.unixtimestamp.com/) or [Epoch Convertor](https://www.epochconverter.com/). However, it would be better to [write your own script](https://www.epochconverter.com/#code) on your local machine, so that you know the UNIX timestamp you receive is accurate. If you don't do that, at least double check on two different sites.
|
Most frequently you will set the locktime to a UNIX timestamp representing a specific date and time. You can calculate a UNIX timestamp at a web site like [UNIX Time Stamp](http://www.unixtimestamp.com/) or [Epoch Convertor](https://www.epochconverter.com/). However, it would be better to [write your own script](https://www.epochconverter.com/#code) on your local machine, so that you know the UNIX timestamp you receive is accurate. If you don't do that, at least double check on two different sites.
|
||||||
|
|
||||||
_Why Would I Use a UNIX Timestamp?_ Using a UNIX timestamp makes it easy to definitively link a transaction to a specific time, without worrying about whether the speed of block creation might change at some point. Particularly if you're creating a locktime that's far in the future, it's the safer thing to do. But, beyond that, it's just more intuitive, creating a direct correlation between some calendar date and the time when the transaction can be mined.
|
> :book: _Why Would I Use a UNIX Timestamp?_ Using a UNIX timestamp makes it easy to definitively link a transaction to a specific time, without worrying about whether the speed of block creation might change at some point. Particularly if you're creating a locktime that's far in the future, it's the safer thing to do. But, beyond that, it's just more intuitive, creating a direct correlation between some calendar date and the time when the transaction can be mined.
|
||||||
|
|
||||||
> :warning: **WARNING:** Locktime with UNIX timestamps has a bit of wriggle room: the release of blocks isn't regular and block times can be two hours ahead of real time, so a locktime actually means "within a few hours of this time, plus or minus".
|
> :warning: **WARNING:** Locktime with UNIX timestamps has a bit of wriggle room: the release of blocks isn't regular and block times can be two hours ahead of real time, so a locktime actually means "within a few hours of this time, plus or minus".
|
||||||
|
|
||||||
### Figure Out Your Locktime By Block Height
|
### Figure Out Your Locktime By Block Height
|
||||||
|
|
||||||
Alternatively, you can set the locktime to a smaller number representing a block height. To calculate your future block height, you need to first know what the current block height is. `bitcoin-cli getblockcount` will tell you what your local machine thinks the block height is. You can verify that it's up to date with the `btcblock` alias, which compares the blockheight from your `bitcoind` with a block height taken from the network.
|
Alternatively, you can set the locktime to a smaller number representing a block height. To calculate your future block height, you need to first know what the current block height is. `bitcoin-cli getblockcount` will tell you what your local machine thinks the block height is. You may want to double-check with a Bitcoin explorer.
|
||||||
|
|
||||||
Once you've figured out the current height, you can decide how far in the future to set your locktime to. Remember that on average a new block will be created every 10 minutes. So, for example, if you wanted to set the locktime to a week in the future, you'd choose a block height that is 6 x 24 x 7 = 1,008 blocks in advance of the current one.
|
Once you've figured out the current height, you can decide how far in the future to set your locktime to. Remember that on average a new block will be created every 10 minutes. So, for example, if you wanted to set the locktime to a week in the future, you'd choose a block height that is 6 x 24 x 7 = 1,008 blocks in advance of the current one.
|
||||||
|
|
||||||
_Why Would I Use a Blockheight?_ Unlike with timestamps, there's no fuzziness for blockheights. If you set a blockheight of 120,000 for your locktime, then there's absolutely no way for it to go into block 119,999. This can make it easier to algorithmically control your locktimed transaction. The downside is that you can't be as sure of when precisely the locktime will be.
|
> :book: _Why Would I Use a Blockheight?_ Unlike with timestamps, there's no fuzziness for blockheights. If you set a blockheight of 120,000 for your locktime, then there's absolutely no way for it to go into block 119,999. This can make it easier to algorithmically control your locktimed transaction. The downside is that you can't be as sure of when precisely the locktime will be.
|
||||||
|
|
||||||
> :warning: **WARNING:** If you want to set a block-height locktime, you must set the locktime to less than 500 million. If you set it to 500 million or over, your number will instead be interpreted as a timestamp. Since the UNIX timestamp of 500 million was November 5, 1985, that probably means that your transaction will be put into a block at the miners' first opportunity.
|
> :warning: **WARNING:** If you want to set a block-height locktime, you must set the locktime to less than 500 million. If you set it to 500 million or over, your number will instead be interpreted as a timestamp. Since the UNIX timestamp of 500 million was November 5, 1985, that probably means that your transaction will be put into a block at the miners' first opportunity.
|
||||||
|
|
||||||
@ -42,23 +44,24 @@ _Why Would I Use a Blockheight?_ Unlike with timestamps, there's no fuzziness fo
|
|||||||
|
|
||||||
Once you have figured out your locktime, all you need to do is write up a typical raw transaction, with a third variable for `locktime`:
|
Once you have figured out your locktime, all you need to do is write up a typical raw transaction, with a third variable for `locktime`:
|
||||||
```
|
```
|
||||||
$ rawtxhex=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]''' outputs='''{ "'$recipient'": 0.8, "'$changeaddress'": 0.0895 }''' locktime=1119160)
|
$ rawtxhex=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]''' outputs='''{ "'$recipient'": 0.001, "'$changeaddress'": 0.00095 }''' locktime=1774650)
|
||||||
```
|
```
|
||||||
Note that this usage of `locktime` is under 500 million, which means that it defines a block height. In this case, it's just a few blocks past the current block height, meant to exemplify how locktime works without sitting around for a long time to wait and see what happens.
|
Note that this usage of `locktime` is under 500 million, which means that it defines a block height. In this case, it's just a few blocks past the current block height at the time of this writing, meant to exemplify how locktime works without sitting around for a long time to wait and see what happens.
|
||||||
|
|
||||||
Here's what the created transaction looks like:
|
Here's what the created transaction looks like:
|
||||||
```
|
```
|
||||||
$ bitcoin-cli -named decoderawtransaction hexstring=$rawtxhex
|
$ bitcoin-cli -named decoderawtransaction hexstring=$rawtxhex
|
||||||
{
|
{
|
||||||
"txid": "34650e513b9b84bc47bc10f39ab7f66f59915b65c0c07fdcaf786502d88cec4a",
|
"txid": "ba440b1dd87a7ccb6a200f087d2265992588284eed0ae455d0672aeb918cf71e",
|
||||||
"hash": "34650e513b9b84bc47bc10f39ab7f66f59915b65c0c07fdcaf786502d88cec4a",
|
"hash": "ba440b1dd87a7ccb6a200f087d2265992588284eed0ae455d0672aeb918cf71e",
|
||||||
"size": 119,
|
|
||||||
"vsize": 119,
|
|
||||||
"version": 2,
|
"version": 2,
|
||||||
"locktime": 1119160,
|
"size": 113,
|
||||||
|
"vsize": 113,
|
||||||
|
"weight": 452,
|
||||||
|
"locktime": 1774650,
|
||||||
"vin": [
|
"vin": [
|
||||||
{
|
{
|
||||||
"txid": "4dcef95a7bb24d907cc0954d75754f8bf1b70cc0542ca071a023abde425a734b",
|
"txid": "0ad9fb6992dfe4ea90236b69852b3605c0175633b32996a486dcd0b2e739e385",
|
||||||
"vout": 1,
|
"vout": 1,
|
||||||
"scriptSig": {
|
"scriptSig": {
|
||||||
"asm": "",
|
"asm": "",
|
||||||
@ -69,49 +72,49 @@ $ bitcoin-cli -named decoderawtransaction hexstring=$rawtxhex
|
|||||||
],
|
],
|
||||||
"vout": [
|
"vout": [
|
||||||
{
|
{
|
||||||
"value": 0.80000000,
|
"value": 0.00100000,
|
||||||
"n": 0,
|
"n": 0,
|
||||||
"scriptPubKey": {
|
"scriptPubKey": {
|
||||||
"asm": "OP_DUP OP_HASH160 e7c1345fc8f87c68170b3aa798a956c2fe6a9eff OP_EQUALVERIFY OP_CHECKSIG",
|
"asm": "0 f333554cc0830d03a9c1f26758e2e7e0f155539f",
|
||||||
"hex": "76a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac",
|
"hex": "0014f333554cc0830d03a9c1f26758e2e7e0f155539f",
|
||||||
"reqSigs": 1,
|
"reqSigs": 1,
|
||||||
"type": "pubkeyhash",
|
"type": "witness_v0_keyhash",
|
||||||
"addresses": [
|
"addresses": [
|
||||||
"n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi"
|
"tb1q7ve42nxqsvxs82wp7fn43ch8urc425ul5um4un"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"value": 0.08950000,
|
"value": 0.00095000,
|
||||||
"n": 1,
|
"n": 1,
|
||||||
"scriptPubKey": {
|
"scriptPubKey": {
|
||||||
"asm": "OP_DUP OP_HASH160 695c79109dc8424573ca6963bda9beeb5d8a6c68 OP_EQUALVERIFY OP_CHECKSIG",
|
"asm": "0 a37718a3510958112b6a766e0023ff251b6c2bfb",
|
||||||
"hex": "76a914695c79109dc8424573ca6963bda9beeb5d8a6c6888ac",
|
"hex": "0014a37718a3510958112b6a766e0023ff251b6c2bfb",
|
||||||
"reqSigs": 1,
|
"reqSigs": 1,
|
||||||
"type": "pubkeyhash",
|
"type": "witness_v0_keyhash",
|
||||||
"addresses": [
|
"addresses": [
|
||||||
"mq842Ku2f2ySWpapEwxjuTCjR3Btvz88nx"
|
"tb1q5dm33g63p9vpz2m2wehqqglly5dkc2lmtmr98d"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Note that the sequence number (4294967294) is less than 0xffffffff. This is necessary signalling to show that the transaction includes a locktime. It's also done automatically by `bitcoin-cli`. If the sequence number is instead set to 0xffffffff, your locktime will be ignored.
|
Note that the sequence number (`4294967294`) is less than `0xffffffff`. This is necessary signalling to show that the transaction includes a locktime. It's also done automatically by `bitcoin-cli`. If the sequence number is instead set to `0xffffffff`, your locktime will be ignored.
|
||||||
|
|
||||||
> :information_source: **NOTE — SEQUENCE:** This is the second use of the `nSequence` value in Bitcoin. As with RBF, `nSequence` is again used as an opt-in, this time for the use of locktime. 0xffffffff-1 (4294967294) is the preferred value for signalling locktime because it purposefully disallows the use of both RBF (which requires `nSequence < 0xffffffff-1`) and relative timelock (which requires `nSequence < 0xf0000000`), the other two uses of the `nSequence` value. If you set `nSequence` lower than `0xf0000000`, then you will also relative timelock your transaction, which is probably not what you want.
|
> :information_source: **NOTE — SEQUENCE:** This is the second use of the `nSequence` value in Bitcoin. As with RBF, `nSequence` is again used as an opt-in, this time for the use of locktime. 0xffffffff-1 (4294967294) is the preferred value for signalling locktime because it purposefully disallows the use of both RBF (which requires `nSequence < 0xffffffff-1`) and relative timelock (which requires `nSequence < 0xf0000000`), the other two uses of the `nSequence` value. If you set `nSequence` lower than `0xf0000000`, then you will also relative timelock your transaction, which is probably not what you want.
|
||||||
|
|
||||||
> :warning: **WARNING:** If you are creating a locktime raw transaction by some other means than `bitcoin-cli`, you will have to set the sequence to less than 0xffffffff by hand.
|
> :warning: **WARNING:** If you are creating a locktime raw transaction by some other means than `bitcoin-cli`, you will have to set the sequence to less than `0xffffffff` by hand.
|
||||||
|
|
||||||
## Send Your Transaction
|
## Send Your Transaction
|
||||||
|
|
||||||
By now you're probably well familiar with finishing things up:
|
By now you're probably well familiar with finishing things up:
|
||||||
```
|
```
|
||||||
$ signedtx=$(bitcoin-cli -named signrawtransaction hexstring=$rawtxhex | jq -r '.hex')
|
$ signedtx=$(bitcoin-cli -named signrawtransactionwithwallet hexstring=$rawtxhex | jq -r '.hex')
|
||||||
$ bitcoin-cli -named sendrawtransaction hexstring=$signedtx
|
$ bitcoin-cli -named sendrawtransaction hexstring=$signedtx
|
||||||
error code: -26
|
error code: -26
|
||||||
error message:
|
error message:
|
||||||
64: non-final
|
non-final
|
||||||
```
|
```
|
||||||
Whoop! What's that error!?
|
Whoop! What's that error!?
|
||||||
|
|
||||||
@ -127,8 +130,8 @@ Cancelling a locktime transaction is _very_ simple: you send a new transactions
|
|||||||
|
|
||||||
Locktime offers a way to create a transaction that _should_ not be relayable to the network and that _will_ not be accepted into a block until the appropriate time has arrived. In the meantime, it can be cancelled simply by reusing a UTXO.
|
Locktime offers a way to create a transaction that _should_ not be relayable to the network and that _will_ not be accepted into a block until the appropriate time has arrived. In the meantime, it can be cancelled simply by reusing a UTXO.
|
||||||
|
|
||||||
_What is the Power of Locktime?_ The power of locktime may not be immediately obvious because of the ability to cancel it so easily. However, it's another of the bases of Smart Contracts: it has a lot of utility in a variety of custodial or contractual applications. For example, consider a situation where a third party is holding your bitcoins. In order to guarantee the return of your bitcoins if the custodian ever disappeared, they could produce a timelock transition to return the coins to you, then update that every once in a while with a new one, further in the future. If they ever failed to update, then the coins would return to you when the current timelock expired. Locktime could similarly be applied to a payment network, where the network holds coins while they're being exchanged by network participants. Finally, a will offers an example of a more complex contract, where payments are sent out to a number of people. These payments would be build on locktime transactions, and would be continually updated as long as the owner continues to show signs of life. (The unifying factor of all of these applications is, of course, _trust_. Simple locktime transactions only work if the holder of the coins can be trusted to send them out under the appropriate conditions.)
|
> :fire: _What is the Power of Locktime?_ The power of locktime may not be immediately obvious because of the ability to cancel it so easily. However, it's another of the bases of Smart Contracts: it has a lot of utility in a variety of custodial or contractual applications. For example, consider a situation where a third party is holding your bitcoins. In order to guarantee the return of your bitcoins if the custodian ever disappeared, they could produce a timelock transition to return the coins to you, then update that every once in a while with a new one, further in the future. If they ever failed to update, then the coins would return to you when the current timelock expired. Locktime could similarly be applied to a payment network, where the network holds coins while they're being exchanged by network participants. Finally, a will offers an example of a more complex contract, where payments are sent out to a number of people. These payments would be build on locktime transactions, and would be continually updated as long as the owner continues to show signs of life. (The unifying factor of all of these applications is, of course, _trust_. Simple locktime transactions only work if the holder of the coins can be trusted to send them out under the appropriate conditions.)
|
||||||
|
|
||||||
## What's Next?
|
## What's Next?
|
||||||
|
|
||||||
Continue "Expanding Bitcoin Transactions" with [§6.5: Sending a Transaction with Data](06_5_Sending_a_Transaction_with_Data.md).
|
Continue "Expanding Bitcoin Transactions" with [§8.2: Sending a Transaction with Data](08.2_Sending_a_Transaction_with_Data.md).
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> :information_source: **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
> :information_source: **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
||||||
|
|
||||||
In the previous section we overviewed the theory of how to create P2SH transactions to hold Bitcoin Scripts. The actual practice of doing so is _much more difficult_, but for the sake of completeness, we're going to look at it here. This is probably not something you'd ever do without an API, so if it gets to intimidating, be aware that we'll returning to pristine, high-level Scripts in a moment.
|
In the previous section we overviewed the theory of how to create P2SH transactions to hold Bitcoin Scripts. The actual practice of doing so is _much more difficult_, but for the sake of completeness, we're going to look at it here. This is probably not something you'd ever do without an API, so if it gets too intimidating, be aware that we'll be returning to pristine, high-level Scripts in a moment.
|
||||||
|
|
||||||
## Create a Locking Script
|
## Create a Locking Script
|
||||||
|
|
||||||
|
123
08_2_Sending_a_Transaction_with_Data.md
Normal file
123
08_2_Sending_a_Transaction_with_Data.md
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
# 8.2: Sending a Transaction with Data
|
||||||
|
|
||||||
|
> :information_source: **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
||||||
|
|
||||||
|
The third way to vary how you send a basic transaction is to use the transaction to send data instead of funds (or really, in addition to funds). This gives you the ability to embed information in the blockchain.
|
||||||
|
|
||||||
|
## Create Your Data
|
||||||
|
|
||||||
|
The first thing you need to do is create the 80 bytes (or less) of data that you'll be recording in your OP_RETURN. This might be as simple as preparing a message or you might be hashing existing data. For example, `sha256sum` produces 256 bits of data, which is 32 bytes, well under the limits:
|
||||||
|
```
|
||||||
|
$ sha256sum contract.jpg
|
||||||
|
b9f81a8919e5aba39aeb86145c684010e6e559b580a85003ae25d78237a12e75 contract.jpg
|
||||||
|
$ op_return_data="b9f81a8919e5aba39aeb86145c684010e6e559b580a85003ae25d78237a12e75"
|
||||||
|
```
|
||||||
|
> :book: _What is an OP_RETURN?_ All Bitcoin transactions are built upon opcode scripts that we'll meet in the next chapter. The OP_RETURN is a simple opcode that defines an OUTPUT as invalid. Convention has resulted in it being used to embed data on the blockchain.
|
||||||
|
|
||||||
|
## Prepare Some Money
|
||||||
|
|
||||||
|
Your purpose in creating a data transaction isn't to send money to anyone, it's to put data into the blockchain. However, you _must_ send money to do so. You just need to use a change address as your _only_ recipient. Then you can identify a UTXO and send that to your change address, minus a transaction fee, while also using the same transaction to create an OP_RETURN.
|
||||||
|
|
||||||
|
Here's the standard setup:
|
||||||
|
```
|
||||||
|
$ bitcoin-cli listunspent
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"txid": "854a833b667049ac811b4cf1cad40fa7f8dce8b0f4c1018a58b84559b6e05f42",
|
||||||
|
"vout": 0,
|
||||||
|
"address": "tb1q6kgsjxuqwj3rwhkenpdfcjccalk06st9z0k0kh",
|
||||||
|
"scriptPubKey": "0014d591091b8074a2375ed9985a9c4b18efecfd4165",
|
||||||
|
"amount": 0.01463400,
|
||||||
|
"confirmations": 1392,
|
||||||
|
"spendable": true,
|
||||||
|
"solvable": true,
|
||||||
|
"desc": "wpkh([d6043800/0'/1'/12']02883bb5463e37d55252d8b3d5c2141b007b37c8a7db6211f75c955acc5ea325eb)#cjr03mru",
|
||||||
|
"safe": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
$ utxo_txid=$(bitcoin-cli listunspent | jq -r '.[0] | .txid')
|
||||||
|
$ utxo_vout=$(bitcoin-cli listunspent | jq -r '.[0] | .vout')
|
||||||
|
$ changeaddress=$(bitcoin-cli getrawchangeaddress)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Write A Raw Transaction
|
||||||
|
|
||||||
|
You can now write a new rawtransaction with two outputs: one is your change address to get back (most of) your money, the other is a data address, which is the `bitcoin-cli` term for an OP_RETURN.
|
||||||
|
```
|
||||||
|
rawtxhex=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]''' outputs='''{ "data": "'$op_return_data'", "'$changeaddress'": 0.0146 }''')
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Here's what that transaction actually looks like:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"txid": "a600148ac3b05f0c774b8687a71c545077ea5dfb9677e5c6d708215053d892e8",
|
||||||
|
"hash": "a600148ac3b05f0c774b8687a71c545077ea5dfb9677e5c6d708215053d892e8",
|
||||||
|
"version": 2,
|
||||||
|
"size": 125,
|
||||||
|
"vsize": 125,
|
||||||
|
"weight": 500,
|
||||||
|
"locktime": 0,
|
||||||
|
"vin": [
|
||||||
|
{
|
||||||
|
"txid": "854a833b667049ac811b4cf1cad40fa7f8dce8b0f4c1018a58b84559b6e05f42",
|
||||||
|
"vout": 0,
|
||||||
|
"scriptSig": {
|
||||||
|
"asm": "",
|
||||||
|
"hex": ""
|
||||||
|
},
|
||||||
|
"sequence": 4294967295
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"vout": [
|
||||||
|
{
|
||||||
|
"value": 0.00000000,
|
||||||
|
"n": 0,
|
||||||
|
"scriptPubKey": {
|
||||||
|
"asm": "OP_RETURN b9f81a8919e5aba39aeb86145c684010e6e559b580a85003ae25d78237a12e75",
|
||||||
|
"hex": "6a20b9f81a8919e5aba39aeb86145c684010e6e559b580a85003ae25d78237a12e75",
|
||||||
|
"type": "nulldata"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": 0.01460000,
|
||||||
|
"n": 1,
|
||||||
|
"scriptPubKey": {
|
||||||
|
"asm": "0 998a9b0ed076bbdec1d88da4f475b9dde75e3620",
|
||||||
|
"hex": "0014998a9b0ed076bbdec1d88da4f475b9dde75e3620",
|
||||||
|
"reqSigs": 1,
|
||||||
|
"type": "witness_v0_keyhash",
|
||||||
|
"addresses": [
|
||||||
|
"tb1qnx9fkrksw6aaaswc3kj0gademhn4ud3q7cz4fm"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
As you can see, this sends the majority of the money straight back to the change address (`tb1qnx9fkrksw6aaaswc3kj0gademhn4ud3q7cz4fm`) minus a small transaction fee. More importantly, the first output shows an OP_RETURN with the data (`b9f81a8919e5aba39aeb86145c684010e6e559b580a85003ae25d78237a12e75`) right after it.
|
||||||
|
|
||||||
|
## Send A Raw Transaction
|
||||||
|
|
||||||
|
Sign your raw transaction and send it, and soon that OP_RETURN will be embedded in the blockchain!
|
||||||
|
|
||||||
|
## Check Your OP_RETURN
|
||||||
|
|
||||||
|
Again, remember that you can look at this transaction using a blockchain explorer:
|
||||||
|
[https://live.blockcypher.com/btc-testnet/tx/a600148ac3b05f0c774b8687a71c545077ea5dfb9677e5c6d708215053d892e8/](https://live.blockcypher.com/btc-testnet/tx/a600148ac3b05f0c774b8687a71c545077ea5dfb9677e5c6d708215053d892e8/)
|
||||||
|
|
||||||
|
You may note a warning about the data being in an "unknown protocol". If you were designing some regular use of OP_RETURN data, you'd probably mark it with a special prefix, to mark that protocol. Then, the actual OP_RETURN data might be something like "CONTRACTS3b110a164aa18d3a5ab064ba93fdce62". This example didn't use a prefix to avoid muddying the data space.
|
||||||
|
|
||||||
|
## Summary: Sending a Transaction with Data
|
||||||
|
|
||||||
|
You can use an OP_RETURN opcode to store up to 80 bytes of data on the blockchain. You do this with the `data` codeword for a `vout`. You still have to send money along too, but you just send it back to a change address, minus a transaction fee.
|
||||||
|
|
||||||
|
> :fire: _What is the Power of OP_RETURN?_ The OP_RETURN opens up whole new possibilities for the blockchain, because you can embed data that proves that certain things happened at certain times. Various organizations have used OP_RETURNs for proof of existence, for copyright, for colored coins, and [for other purposes](https://en.bitcoin.it/wiki/OP_RETURN). Though 80 bytes might not seem a lot, it can be quite effective if OP_RETURNs are used to store hashes of the actual data. Then, you can prove the existence of your digital data by deminstrating that the hash of it matches the hash on the blockchain.
|
||||||
|
|
||||||
|
Note that there is some controversy over using the Bitcoin blockchain in this way.
|
||||||
|
|
||||||
|
## What's Next?
|
||||||
|
|
||||||
|
Move on to "Bitcoin Scripting" with [Chapter Seven: Introducing Bitcoin Scripts](07_0_Introducing_Bitcoin_Scripts.md).
|
9
12_0_Using_Tor.md
Normal file
9
12_0_Using_Tor.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Chapter 12: Using Tor
|
||||||
|
|
||||||
|
_This is currently a placeholder; Writing it in Task #15 on the current [TODO list](https://github.com/BlockchainCommons/Learning-Bitcoin-from-the-Command-Line/blob/master/TODO.md).
|
||||||
|
|
||||||
|
This section will talk about using the Tor services that are now available courtesy of Bitcoin Standup._)
|
||||||
|
|
||||||
|
* [12.1: Verifying Your Tor Setup](12_1_Verifying_Your_Tor_Setup.md)
|
||||||
|
* [12.2: Changing Your Bitcoin Hidden Services](12_2_Changing_Your_Bitcoin_Hidden_Services.md)
|
||||||
|
* [12.3: Adding SSH Hidden Services](12_3_Adding_SSH_Hidden_Services.md)
|
98
12_1_Verifying_Your_Tor_Setup.md
Normal file
98
12_1_Verifying_Your_Tor_Setup.md
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
# Chapter 12: Verifying Your Tor Setup
|
||||||
|
|
||||||
|
In this chapter we will verify tor installation and setup.
|
||||||
|
|
||||||
|
```
|
||||||
|
~$ sudo -u debian-tor tor --verify-config
|
||||||
|
```
|
||||||
|
|
||||||
|
If tor is installed correctly you should see an output like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
Jun 26 21:52:09.230 [notice] Tor 0.4.3.5 running on Linux with Libevent 2.0.21-stable, OpenSSL 1.0.2n, Zlib 1.2.11, Liblzma 5.2.2, and Libzstd N/A.
|
||||||
|
Jun 26 21:52:09.230 [notice] Tor can't help you if you use it wrong! Learn how to be safe at https://www.torproject.org/download/download#warning
|
||||||
|
Jun 26 21:52:09.230 [notice] Read configuration file "/etc/tor/torrc".
|
||||||
|
Configuration was valid
|
||||||
|
|
||||||
|
~$
|
||||||
|
```
|
||||||
|
## Verify bitcoind Tor setup
|
||||||
|
|
||||||
|
You should see something like this in your debug log file to verify your ID onion address, which confirms you're using an onion hidden service that will bypass firewalls and NAT's and allows you connect to your node remotely using your ID and port.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ grep -e "tor: " debug.log
|
||||||
|
```
|
||||||
|
Output
|
||||||
|
|
||||||
|
```
|
||||||
|
2020-06-25T18:16:44Z tor: Thread interrupt
|
||||||
|
2020-06-25T19:11:12Z tor: Got service ID [YOUR_ONION_ID], advertising service your_onion_id.onion:8333
|
||||||
|
```
|
||||||
|
Using bitcoin-cli you should use getnetworkinfo to get your onion id like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
"localaddresses": [
|
||||||
|
{
|
||||||
|
"address": "your_onion_id.onion",
|
||||||
|
"port": 8333,
|
||||||
|
"score": 4
|
||||||
|
}
|
||||||
|
],
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
$ bitcoin-cli getnetworkinfo
|
||||||
|
```
|
||||||
|
Output
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"version": 200000,
|
||||||
|
"subversion": "/Satoshi:0.20.0/",
|
||||||
|
"protocolversion": 70015,
|
||||||
|
"localservices": "0000000000000409",
|
||||||
|
"localservicesnames": [
|
||||||
|
"NETWORK",
|
||||||
|
"WITNESS",
|
||||||
|
"NETWORK_LIMITED"
|
||||||
|
],
|
||||||
|
"localrelay": true,
|
||||||
|
"timeoffset": 0,
|
||||||
|
"networkactive": true,
|
||||||
|
"connections": 5,
|
||||||
|
"networks": [
|
||||||
|
{
|
||||||
|
"name": "ipv4",
|
||||||
|
"limited": false,
|
||||||
|
"reachable": true,
|
||||||
|
"proxy": "",
|
||||||
|
"proxy_randomize_credentials": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ipv6",
|
||||||
|
"limited": false,
|
||||||
|
"reachable": true,
|
||||||
|
"proxy": "",
|
||||||
|
"proxy_randomize_credentials": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "onion",
|
||||||
|
"limited": false,
|
||||||
|
"reachable": true,
|
||||||
|
"proxy": "127.0.0.1:9050",
|
||||||
|
"proxy_randomize_credentials": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"relayfee": 0.00001000,
|
||||||
|
"incrementalfee": 0.00001000,
|
||||||
|
"localaddresses": [
|
||||||
|
{
|
||||||
|
"address": "your_onion_id.onion",
|
||||||
|
"port": 8333,
|
||||||
|
"score": 4
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"warnings": ""
|
||||||
|
}
|
||||||
|
```
|
30
12_2_Changing_Your_Bitcoin_Hidden_Services.md
Normal file
30
12_2_Changing_Your_Bitcoin_Hidden_Services.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# Chapter 12.2: Changing Your Bitcoin Hidden Services
|
||||||
|
|
||||||
|
In this chapter we will show you how to create or change your local Bitcoin Hidden Service. To archieve this we need to add current user to tor or debian-tor group to guarantee that Bitcoin daemon can sets up an automatic hidden service on the first startup if it's correclty configured. Bitcoind will create a file called onion_private_key in the data directory. Further check if file control.authcookie exists like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
~$ ll /run/tor/control.authcookie
|
||||||
|
-rw-r----- 1 debian-tor debian-tor 32 jun 26 09:44 /run/tor/control.authcookie
|
||||||
|
```
|
||||||
|
|
||||||
|
To avoid see a message like this in debug log file you need to add your current user to debian-tor group like this:
|
||||||
|
|
||||||
|
debug.log
|
||||||
|
|
||||||
|
```
|
||||||
|
2020-05-15T16:49:20Z tor: Authentication cookie /run/tor/control.authcookie could not be opened (check permissions)
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
~$ sudo usermod -a -G debian-tor [CHANGE_MY_USER]
|
||||||
|
```
|
||||||
|
|
||||||
|
If you're running a older version Tor like 0.2.7 add this lines to /etc/tor/torrc file:
|
||||||
|
|
||||||
|
```
|
||||||
|
HiddenServiceDir /var/lib/tor/bitcoin-service/
|
||||||
|
HiddenServicePort 8333 127.0.0.1:8333
|
||||||
|
HiddenServicePort 18333 127.0.0.1:18333
|
||||||
|
```
|
||||||
|
If you're running Tor version 3 bitcoind will configurate hidden services automatically to listen on.
|
||||||
|
|
50
12_3_Adding_SSH_Hidden_Services.md
Normal file
50
12_3_Adding_SSH_Hidden_Services.md
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# Chapter 12.3: Adding SSH Hidden Services
|
||||||
|
|
||||||
|
In this chapter we will show you how to add a ssh hidden service to login remotelly using Tor.
|
||||||
|
|
||||||
|
## Create SSH Hidden Services
|
||||||
|
|
||||||
|
To create new service you need to add some lines in your torrc file.
|
||||||
|
|
||||||
|
This should be under /etc/tor/torrc
|
||||||
|
|
||||||
|
Add this lines:
|
||||||
|
|
||||||
|
```
|
||||||
|
HiddenServiceDir /var/lib/tor/hidden-service-ssh/
|
||||||
|
HiddenServicePort 22 127.0.0.1:22
|
||||||
|
HiddenServiceAuthorizeClient stealth hidden-service-ssh
|
||||||
|
```
|
||||||
|
|
||||||
|
* HiddenServiceDir: indicates tor that you have a hidden service directory with the necessary configuration on path.
|
||||||
|
* HiddenServicePort: indicates tor port to be used, in SSH case is 22, if you want use other port you can change here.
|
||||||
|
* HiddenServiceAuthorizeClient: As it's name indicates authorize a client to connect to the hidden service.
|
||||||
|
|
||||||
|
After add lines to tor file you need to restart tor service
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo /etc/init.d/tor restart
|
||||||
|
```
|
||||||
|
|
||||||
|
After restart you should have three new files like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
total 24
|
||||||
|
drwx--S--- 3 debian-tor debian-tor 4096 jul 1 18:39 ./
|
||||||
|
drwx--S--- 5 debian-tor debian-tor 4096 jul 1 18:39 ../
|
||||||
|
drwx--S--- 2 debian-tor debian-tor 4096 jul 1 18:39 authorized_clients/
|
||||||
|
-rw------- 1 debian-tor debian-tor 63 jul 1 18:39 hostname
|
||||||
|
-rw------- 1 debian-tor debian-tor 64 jul 1 18:39 hs_ed25519_public_key
|
||||||
|
-rw------- 1 debian-tor debian-tor 96 jul 1 18:39 hs_ed25519_secret_key
|
||||||
|
```
|
||||||
|
The file hostname contains your id onion.
|
||||||
|
|
||||||
|
Use this address to connect to your ssh hidden service like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
torify ssh <your-username>@your_new_onion_id.onion
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2,18 +2,37 @@
|
|||||||
|
|
||||||
> **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
> **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
||||||
|
|
||||||
[needs new intro]
|
Interacting with the bitcoind directly and using command-line curl can get simple if you understand how it works, thus in this section we'll show how to use a good package for doing so in C called libbitcoinrpc that provides the functionality to access JSON-RPC bitcoind API. It uses a curl library for accessing the data and it uses the jansson library for encoding and decoding the JSON.
|
||||||
|
|
||||||
## Set Up libbitcoinrpc
|
## Set Up libbitcoinrpc
|
||||||
|
|
||||||
To use `libbitcoinrpc`, you need to install a basic C setup and the dependent packages `libcurl`, `libjansson`, and `libuuid`. The following will do so on a Ubuntu system:
|
To use `libbitcoinrpc`, you need to install a basic C setup and the dependent packages `libcurl`, `libjansson`, and `libuuid`. The following will do so on a Ubuntu system:
|
||||||
```
|
```
|
||||||
$ sudo apt-get install make gcc libcurl4-openssl-dev libjansson-dev uuid-dev
|
$ sudo apt-get install make gcc libcurl4-openssl-dev libjansson-dev uuid-dev
|
||||||
|
Suggested packages:
|
||||||
|
libcurl4-doc libidn11-dev libkrb5-dev libldap2-dev librtmp-dev libssh2-1-dev
|
||||||
|
The following NEW packages will be installed:
|
||||||
|
libcurl4-openssl-dev libjansson-dev uuid-dev
|
||||||
|
0 upgraded, 3 newly installed, 0 to remove and 4 not upgraded.
|
||||||
|
Need to get 358 kB of archives.
|
||||||
|
After this operation, 1.696 kB of additional disk space will be used.
|
||||||
|
Do you want to continue? [Y/n] y
|
||||||
```
|
```
|
||||||
You can then download [libbitcoinrpc from Github](https://github.com/gitmarek/libbitcoinrpc/blob/master/README.md). Clone it or grab a zip file, as you prefer.
|
You can then download [libbitcoinrpc from Github](https://github.com/gitmarek/libbitcoinrpc/blob/master/README.md). Clone it or grab a zip file, as you prefer.
|
||||||
```
|
```
|
||||||
$ sudo apt-get unzip
|
$ sudo apt-get unzip
|
||||||
$ unzip libbitcoinrpc-master.zip
|
$ unzip libbitcoinrpc-master.zip
|
||||||
|
unzip libbitcoinrpc-master.zip
|
||||||
|
Archive: libbitcoinrpc-master.zip
|
||||||
|
a2285e8f221185cd0afdcfaf1bc8c78988fce09a
|
||||||
|
creating: libbitcoinrpc-master/
|
||||||
|
inflating: libbitcoinrpc-master/.gitignore
|
||||||
|
inflating: libbitcoinrpc-master/.travis.yml
|
||||||
|
inflating: libbitcoinrpc-master/CREDITS
|
||||||
|
inflating: libbitcoinrpc-master/Changelog.md
|
||||||
|
inflating: libbitcoinrpc-master/LICENSE
|
||||||
|
inflating: libbitcoinrpc-master/Makefile
|
||||||
|
inflating: libbitcoinrpc-master/README.md
|
||||||
$ cd libbitcoinrpc-master/
|
$ cd libbitcoinrpc-master/
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -36,11 +55,55 @@ INSTALL_HEADERPATH := $(INSTALL_PREFIX)/usr/include
|
|||||||
|
|
||||||
Then you can compile:
|
Then you can compile:
|
||||||
```
|
```
|
||||||
$ make
|
$ ~/libbitcoinrpc-master$ make
|
||||||
|
|
||||||
|
gcc -fPIC -O3 -g -Wall -Werror -Wextra -std=c99 -D VERSION=\"0.2\" -o src/bitcoinrpc_err.o -c src/bitcoinrpc_err.c
|
||||||
|
gcc -fPIC -O3 -g -Wall -Werror -Wextra -std=c99 -D VERSION=\"0.2\" -o src/bitcoinrpc_global.o -c src/bitcoinrpc_global.c
|
||||||
|
gcc -fPIC -O3 -g -Wall -Werror -Wextra -std=c99 -D VERSION=\"0.2\" -o src/bitcoinrpc.o -c src/bitcoinrpc.c
|
||||||
|
gcc -fPIC -O3 -g -Wall -Werror -Wextra -std=c99 -D VERSION=\"0.2\" -o src/bitcoinrpc_resp.o -c src/bitcoinrpc_resp.c
|
||||||
|
gcc -fPIC -O3 -g -Wall -Werror -Wextra -std=c99 -D VERSION=\"0.2\" -o src/bitcoinrpc_cl.o -c src/bitcoinrpc_cl.c
|
||||||
|
gcc -fPIC -O3 -g -Wall -Werror -Wextra -std=c99 -D VERSION=\"0.2\" -o src/bitcoinrpc_method.o -c src/bitcoinrpc_method.c
|
||||||
|
gcc -fPIC -O3 -g -Wall -Werror -Wextra -std=c99 -D VERSION=\"0.2\" -shared -Wl,-soname,libbitcoinrpc.so.0 \
|
||||||
|
src/bitcoinrpc_err.o src/bitcoinrpc_global.o src/bitcoinrpc.o src/bitcoinrpc_resp.o src/bitcoinrpc_cl.o src/bitcoinrpc_method.o \
|
||||||
|
-o .lib/libbitcoinrpc.so.0.2 \
|
||||||
|
-Wl,--copy-dt-needed-entries -luuid -ljansson -lcurl
|
||||||
|
ldconfig -v -n .lib
|
||||||
|
.lib:
|
||||||
|
libbitcoinrpc.so.0 -> libbitcoinrpc.so.0.2 (changed)
|
||||||
|
ln -fs libbitcoinrpc.so.0 .lib/libbitcoinrpc.so
|
||||||
|
~/libbitcoinrpc-master$ sudo make install
|
||||||
|
Installing to
|
||||||
|
install .lib/libbitcoinrpc.so.0.2 /usr/local/lib
|
||||||
|
ldconfig -n /usr/local/lib
|
||||||
|
ln -fs libbitcoinrpc.so.0 /usr/local/lib/libbitcoinrpc.so
|
||||||
|
install -m 644 src/bitcoinrpc.h /usr/local/include
|
||||||
|
Installing docs to /usr/share/doc/bitcoinrpc
|
||||||
|
mkdir -p /usr/share/doc/bitcoinrpc
|
||||||
|
install -m 644 doc/*.md /usr/share/doc/bitcoinrpc
|
||||||
|
install -m 644 CREDITS /usr/share/doc/bitcoinrpc
|
||||||
|
install -m 644 LICENSE /usr/share/doc/bitcoinrpc
|
||||||
|
install -m 644 Changelog.md /usr/share/doc/bitcoinrpc
|
||||||
|
Installing man pages
|
||||||
|
install -m 644 doc/man3/bitcoinrpc*.gz /usr/local/man/man3
|
||||||
|
~/libbitcoinrpc-master$
|
||||||
|
|
||||||
```
|
```
|
||||||
If that works, you can install the package:
|
If that works, you can install the package:
|
||||||
```
|
```
|
||||||
$ sudo make install
|
$ sudo make install
|
||||||
|
Installing to
|
||||||
|
install .lib/libbitcoinrpc.so.0.2 /usr/local/lib
|
||||||
|
ldconfig -n /usr/local/lib
|
||||||
|
ln -fs libbitcoinrpc.so.0 /usr/local/lib/libbitcoinrpc.so
|
||||||
|
install -m 644 src/bitcoinrpc.h /usr/local/include
|
||||||
|
Installing docs to /usr/share/doc/bitcoinrpc
|
||||||
|
mkdir -p /usr/share/doc/bitcoinrpc
|
||||||
|
install -m 644 doc/*.md /usr/share/doc/bitcoinrpc
|
||||||
|
install -m 644 CREDITS /usr/share/doc/bitcoinrpc
|
||||||
|
install -m 644 LICENSE /usr/share/doc/bitcoinrpc
|
||||||
|
install -m 644 Changelog.md /usr/share/doc/bitcoinrpc
|
||||||
|
Installing man pages
|
||||||
|
install -m 644 doc/man3/bitcoinrpc*.gz /usr/local/man/man3
|
||||||
```
|
```
|
||||||
|
|
||||||
## Write Code in C
|
## Write Code in C
|
||||||
@ -212,7 +275,7 @@ Successfully connected to server!
|
|||||||
```
|
```
|
||||||
## Appendix II: Getting Mining Info
|
## Appendix II: Getting Mining Info
|
||||||
|
|
||||||
Here's the complete code for the `getmininginfo` command, with organized variable initiatialization, error checking, and variable cleanup.
|
Here's the complete code for the `getmininginfo` command, with organized variable initiatialization, error checking, and variable cleanup. For this example we use a testnet network and show it's output.
|
||||||
```
|
```
|
||||||
file: getmininginfo.c
|
file: getmininginfo.c
|
||||||
|
|
||||||
@ -233,7 +296,7 @@ int main(void) {
|
|||||||
|
|
||||||
bitcoinrpc_global_init();
|
bitcoinrpc_global_init();
|
||||||
|
|
||||||
rpc_client = bitcoinrpc_cl_init_params ("bitcoinrpc", "73bd45ba60ab8f9ff9846b6404769487", "127.0.0.1", 18332);
|
rpc_client = bitcoinrpc_cl_init_params ("bitcoinrpc", "73bd45ba60ab8f9ff9846b6404769487", "127.0.0.1", 18443);
|
||||||
|
|
||||||
if (rpc_client) {
|
if (rpc_client) {
|
||||||
getmininginfo = bitcoinrpc_method_init(BITCOINRPC_METHOD_GETMININGINFO);
|
getmininginfo = bitcoinrpc_method_init(BITCOINRPC_METHOD_GETMININGINFO);
|
||||||
@ -295,32 +358,26 @@ As usual, you can compile and run as follows:
|
|||||||
$ cc getmininginfo.c -lbitcoinrpc -ljansson -o getmininginfo
|
$ cc getmininginfo.c -lbitcoinrpc -ljansson -o getmininginfo
|
||||||
$ ./getmininginfo
|
$ ./getmininginfo
|
||||||
Full Response: {
|
Full Response: {
|
||||||
"id": "03406237-cd8f-466d-ac31-86711ea9d1db",
|
|
||||||
"result": {
|
"result": {
|
||||||
"blocks": 1147154,
|
"blocks": 1773353,
|
||||||
"errors": "Warning: unknown new rules activated (versionbit 28)",
|
"difficulty": 10178811.406987719,
|
||||||
"pooledtx": 0,
|
"networkhashps": 129510207940932.2,
|
||||||
"currentblocksize": 0,
|
"pooledtx": 9,
|
||||||
"currentblockweight": 0,
|
"chain": "test",
|
||||||
"currentblocktx": 0,
|
"warnings": "Warning: unknown new rules activated (versionbit 28)"
|
||||||
"difficulty": 313525.08513550513,
|
|
||||||
"networkhashps": 3958339463617.417,
|
|
||||||
"chain": "test"
|
|
||||||
},
|
},
|
||||||
"error": null
|
"error": null,
|
||||||
|
"id": "6e502927-b065-486a-8182-bc1acd843bae"
|
||||||
}
|
}
|
||||||
|
|
||||||
Just the Result: {
|
Just the Result: {
|
||||||
"blocks": 1147154,
|
"blocks": 1773353,
|
||||||
"errors": "Warning: unknown new rules activated (versionbit 28)",
|
"difficulty": 10178811.406987719,
|
||||||
"pooledtx": 0,
|
"networkhashps": 129510207940932.2,
|
||||||
"currentblocksize": 0,
|
"pooledtx": 9,
|
||||||
"currentblockweight": 0,
|
"chain": "test",
|
||||||
"currentblocktx": 0,
|
"warnings": "Warning: unknown new rules activated (versionbit 28)"
|
||||||
"difficulty": 313525.08513550513,
|
|
||||||
"networkhashps": 3958339463617.417,
|
|
||||||
"chain": "test"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Block Count: 1147154
|
Block Count: 1773353
|
||||||
```
|
```
|
||||||
|
@ -11,10 +11,11 @@ We're going to program a simplistic first cut version of `sendtoaddress`, which
|
|||||||
1. Request an address and an amount
|
1. Request an address and an amount
|
||||||
2. Set an arbitrary fee
|
2. Set an arbitrary fee
|
||||||
3. Find a UTXO that's large enough for the amount + the fee
|
3. Find a UTXO that's large enough for the amount + the fee
|
||||||
4. Create a change address
|
4. Create a bench 32 address
|
||||||
5. Create a raw transaction that sends from the UTXO to the address and the change address
|
5. Create a change address
|
||||||
6. Sign the transaction
|
6. Create a raw transaction that sends from the UTXO to the address and the change address
|
||||||
7. Send the transaction
|
7. Sign the transaction
|
||||||
|
8. Send the transaction
|
||||||
|
|
||||||
### Plan for Your Future
|
### Plan for Your Future
|
||||||
|
|
||||||
@ -64,7 +65,7 @@ float tx_total = tx_amount + tx_fee;
|
|||||||
|
|
||||||
### X. Prepare Your RPC
|
### X. Prepare Your RPC
|
||||||
|
|
||||||
Obviously, you're going to need to get all of your variables ready again, as discussed in [§12.2: Accessing Bitcoind with C](12_2_Accessing_Bitcoind_with_C.md). You also need to initialize your library, connect your RPC client, and prepare your response object:
|
Obviously, you're going to need to get all of your variables ready again, as discussed in [§15.1: Accessing Bitcoind with C](15_1_Accessing_Bitcoind_with_C.md). You also need to initialize your library, connect your RPC client, and prepare your response object:
|
||||||
```
|
```
|
||||||
bitcoinrpc_global_init();
|
bitcoinrpc_global_init();
|
||||||
rpc_client = bitcoinrpc_cl_init_params ("bitcoinrpc", "73bd45ba60ab8f9ff9846b6404769487", "127.0.0.1", 18332);
|
rpc_client = bitcoinrpc_cl_init_params ("bitcoinrpc", "73bd45ba60ab8f9ff9846b6404769487", "127.0.0.1", 18332);
|
||||||
@ -152,7 +153,25 @@ if (!tx_id) {
|
|||||||
|
|
||||||
> **WARNING:** A real program would use subroutines for this sort of lookup, so that you could confidentally call various RPCs from a library of C functions. We're just going to blob it all into `main` as part of our KISS philosophy of simple examples.
|
> **WARNING:** A real program would use subroutines for this sort of lookup, so that you could confidentally call various RPCs from a library of C functions. We're just going to blob it all into `main` as part of our KISS philosophy of simple examples.
|
||||||
|
|
||||||
### 4. Create a Change Address
|
### 4. Create a Bech32 Address
|
||||||
|
|
||||||
|
Repeat the standard RPC-lookup methodology to get a new address using get new address method.
|
||||||
|
```
|
||||||
|
rpc_method = bitcoinrpc_method_init(BITCOINRPC_METHOD_GETNEWADDRESS);
|
||||||
|
lu_response = bitcoinrpc_resp_get (btcresponse);
|
||||||
|
lu_result = json_object_get(lu_response,"result");
|
||||||
|
char *address = strdup(json_string_value(lu_result));
|
||||||
|
printf("adress %s \n", address);
|
||||||
|
```
|
||||||
|
|
||||||
|
Output
|
||||||
|
```
|
||||||
|
$ cc getaddress.c -lbitcoinrpc -ljansson -o getaddress
|
||||||
|
$ ./getaddress
|
||||||
|
adress tb1qpxlguwckkqalcg6xrv4ujlmkzqe79nkngcvudf
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Create a Change Address
|
||||||
|
|
||||||
Repeat the standard RPC-lookup methodology to get a change address:
|
Repeat the standard RPC-lookup methodology to get a change address:
|
||||||
```
|
```
|
||||||
@ -183,7 +202,7 @@ The only difference is in what particular information we extract from our JSON o
|
|||||||
|
|
||||||
> **WARNING:** Here's another place that a subroutine would be really nice: to abstract out the whole RPC method initialization and call.
|
> **WARNING:** Here's another place that a subroutine would be really nice: to abstract out the whole RPC method initialization and call.
|
||||||
|
|
||||||
### 5. Create a Raw Transaction
|
### 6. Create a Raw Transaction
|
||||||
|
|
||||||
Creating the actual raw transaction is the other tricky part of programming your `sendtoaddress` replacement. That's because it requires the creation of a complex JSON object as a paramter.
|
Creating the actual raw transaction is the other tricky part of programming your `sendtoaddress` replacement. That's because it requires the creation of a complex JSON object as a paramter.
|
||||||
|
|
||||||
@ -194,7 +213,7 @@ createrawtransaction [{"txid":"id","vout":n},...] {"address":amount,"data":"hex"
|
|||||||
```
|
```
|
||||||
To review, your inputs will be a JSON array containing one JSON object for each UTXO. Then the ouputs will all be in one JSON object. It's easiest to create these JSON elements from the inside out, using `jansson` commands.
|
To review, your inputs will be a JSON array containing one JSON object for each UTXO. Then the ouputs will all be in one JSON object. It's easiest to create these JSON elements from the inside out, using `jansson` commands.
|
||||||
|
|
||||||
#### 5.1. Create the Input Parameters
|
#### 6.1. Create the Input Parameters
|
||||||
|
|
||||||
To create the input object for your UTXO, use `json_object`, then fill it with key-values using either `json_object_set_new` (for newly created references) or `json_object_set` (for existing references):
|
To create the input object for your UTXO, use `json_object`, then fill it with key-values using either `json_object_set_new` (for newly created references) or `json_object_set` (for existing references):
|
||||||
```
|
```
|
||||||
@ -213,7 +232,7 @@ inputparams = json_array();
|
|||||||
json_array_append(inputparams,inputtxid);
|
json_array_append(inputparams,inputtxid);
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 5.2 Create the Output Parameters
|
#### 6.2 Create the Output Parameters
|
||||||
|
|
||||||
To create the output array for your transaction, follow the same format, creating a JSON object with `json_object`, then filling it with `json_object_set`:
|
To create the output array for your transaction, follow the same format, creating a JSON object with `json_object`, then filling it with `json_object_set`:
|
||||||
```
|
```
|
||||||
@ -231,7 +250,7 @@ json_object_set(outputparams, changeaddress, json_string(tx_change_string));
|
|||||||
|
|
||||||
> **WARNING:** You might expect to input your Bitcoin values as numbers, using `json_real`. Unfortunately, this exposes one of the major problems with integrating the `jansson` library and Bitcoin. Bitcoin is only valid to eight significant digits past the decimal point. You might recall that .00000001 BTC is a satoshi, and that's the smallest possible division of a Bitcoin. Doubles in C offer more significant digits than that, though they're often imprecise out past eight decimals. If you try to convert straight from your double value in C (or a float value, for that matter) to a Bitcoin value, the imprecision will often create a Bitcoin value with more than eight significant digits. Before Bitcoin Core 0.12 this appears to work, and you could use `json_real`. But as of Bitcoin Core 0.12, if you try to give `createrawtransaction` a Bitcoin value with too many significant digits, you'll instead get an error and the transaction will not be created. As a result, if the Bitcoin value has _ever_ become a double or float, you must reformat it to eight significant digits past the digit before feeding it in as a string. This is obviously a kludge, so you should make sure it continues to work in future versions of Bitcoin Core.
|
> **WARNING:** You might expect to input your Bitcoin values as numbers, using `json_real`. Unfortunately, this exposes one of the major problems with integrating the `jansson` library and Bitcoin. Bitcoin is only valid to eight significant digits past the decimal point. You might recall that .00000001 BTC is a satoshi, and that's the smallest possible division of a Bitcoin. Doubles in C offer more significant digits than that, though they're often imprecise out past eight decimals. If you try to convert straight from your double value in C (or a float value, for that matter) to a Bitcoin value, the imprecision will often create a Bitcoin value with more than eight significant digits. Before Bitcoin Core 0.12 this appears to work, and you could use `json_real`. But as of Bitcoin Core 0.12, if you try to give `createrawtransaction` a Bitcoin value with too many significant digits, you'll instead get an error and the transaction will not be created. As a result, if the Bitcoin value has _ever_ become a double or float, you must reformat it to eight significant digits past the digit before feeding it in as a string. This is obviously a kludge, so you should make sure it continues to work in future versions of Bitcoin Core.
|
||||||
|
|
||||||
#### 5.3 Create the Parameter Array
|
#### 6.3 Create the Parameter Array
|
||||||
|
|
||||||
To finish creating your parameters, simply to bundle them all up in a JSON array:
|
To finish creating your parameters, simply to bundle them all up in a JSON array:
|
||||||
```
|
```
|
||||||
@ -240,7 +259,7 @@ params = json_array();
|
|||||||
json_array_append(params,inputparams);
|
json_array_append(params,inputparams);
|
||||||
json_array_append(params,outputparams);
|
json_array_append(params,outputparams);
|
||||||
```
|
```
|
||||||
#### 5.4 Make the RPC Call
|
#### 6.4 Make the RPC Call
|
||||||
|
|
||||||
Use the normal method to create your RPC call:
|
Use the normal method to create your RPC call:
|
||||||
```
|
```
|
||||||
@ -263,7 +282,7 @@ lu_result = json_object_get(lu_response,"result");
|
|||||||
|
|
||||||
char *tx_rawhex = strdup(json_string_value(lu_result));
|
char *tx_rawhex = strdup(json_string_value(lu_result));
|
||||||
```
|
```
|
||||||
### 6. Sign the Transaction
|
### 7. Sign the Transaction
|
||||||
|
|
||||||
It's a lot easier to assign a simple parameter to a function. You just create a JSON array, then assign the parameter to the array:
|
It's a lot easier to assign a simple parameter to a function. You just create a JSON array, then assign the parameter to the array:
|
||||||
```
|
```
|
||||||
@ -294,7 +313,7 @@ json_decref(lu_signature);
|
|||||||
|
|
||||||
> ***WARNING:*** A real-world program would obviously carefully test the response of every RPC command to make sure there were no errors. That's especially true for `signrawtransaction`, because you might end up with a partially signed transaction. Worse, if you don't check the errors in the JSON object, you'll just see the `hex` and not realize that it's either unsigned or partially signed.
|
> ***WARNING:*** A real-world program would obviously carefully test the response of every RPC command to make sure there were no errors. That's especially true for `signrawtransaction`, because you might end up with a partially signed transaction. Worse, if you don't check the errors in the JSON object, you'll just see the `hex` and not realize that it's either unsigned or partially signed.
|
||||||
|
|
||||||
### 7. Send the Transaction
|
### 8. Send the Transaction
|
||||||
|
|
||||||
You can now send your transaction, using all of the previous techniques:
|
You can now send your transaction, using all of the previous techniques:
|
||||||
```
|
```
|
||||||
@ -323,7 +342,7 @@ The entire code, with a _little_ more error-checking appears in the Appendix.
|
|||||||
|
|
||||||
## Summary: Programming Bitcoind with C
|
## Summary: Programming Bitcoind with C
|
||||||
|
|
||||||
Using the techniques outlined in [§12.2](12_2_Accessing_Bitcoind_with_C.md) you can write a much more complex program using C calls. This section offers an example, with the first cut of a program that will send money to an address, without your users worrying about where it's coming from, how much they're paying as a fee, or how they get their change back. Obviously, a real-world program would need much better user-input control and error handling, but by outlining how the RPC code works, this section opens up the programming doorways to allow you to take the next step.
|
Using the techniques outlined in [§15.1](15_1_Accessing_Bitcoind_with_C.md) you can write a much more complex program using C calls. This section offers an example, with the first cut of a program that will send money to an address, without your users worrying about where it's coming from, how much they're paying as a fee, or how they get their change back. Obviously, a real-world program would need much better user-input control and error handling, but by outlining how the RPC code works, this section opens up the programming doorways to allow you to take the next step.
|
||||||
|
|
||||||
## Appendix: Sending to an Address
|
## Appendix: Sending to an Address
|
||||||
|
|
||||||
|
224
15_3_Receiving_Bitcoind_Notifications_with_C.md
Normal file
224
15_3_Receiving_Bitcoind_Notifications_with_C.md
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
# 15.3 Receiving Bitcoind Notifications with C
|
||||||
|
|
||||||
|
> **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
||||||
|
|
||||||
|
[§15.3](15_3_Receiving_Bitcoind_Notifications_with_C.md) In this chapter we will show how to create a simple listener for bitcoin blockchain to receive notifications using ZMQ-based (http://zeromq.org/) notification interface, which is a highly scalable networking library written in C that acts like a concurrency framework. Bitcoin allows connection points where clients can subscribe to get notified about blockchain events like raw transactions or raw blocks.
|
||||||
|
|
||||||
|
## Install ZMQ
|
||||||
|
|
||||||
|
To create a blockchain listener in C you need to install ZMQ following this steps.
|
||||||
|
|
||||||
|
1. Install ZMQ
|
||||||
|
2. Create C program.
|
||||||
|
3. Compile C program.
|
||||||
|
4. Configure bitcoind to allow ZMQ notifications
|
||||||
|
5. Execute listener.
|
||||||
|
|
||||||
|
### 1. Install ZMQ
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt-get install libzmq3-dev
|
||||||
|
```
|
||||||
|
Output
|
||||||
|
|
||||||
|
```
|
||||||
|
Reading package lists... Done
|
||||||
|
Building dependency tree
|
||||||
|
Reading state information... Done
|
||||||
|
The following NEW packages will be installed:
|
||||||
|
libzmq3-dev
|
||||||
|
0 upgraded, 1 newly installed, 0 to remove and 18 not upgraded.
|
||||||
|
Need to get 400 kB of archives.
|
||||||
|
After this operation, 2.227 kB of additional disk space will be used.
|
||||||
|
Get:1 http://es.archive.ubuntu.com/ubuntu bionic-updates/universe amd64 libzmq3-dev amd64 4.2.5-1ubuntu0.2 [400 kB]
|
||||||
|
Fetched 400 kB in 0s (1.114 kB/s)
|
||||||
|
Selecting previously unselected package libzmq3-dev:amd64.
|
||||||
|
(Reading database ... 313982 files and directories currently installed.)
|
||||||
|
Preparing to unpack .../libzmq3-dev_4.2.5-1ubuntu0.2_amd64.deb ...
|
||||||
|
Unpacking libzmq3-dev:amd64 (4.2.5-1ubuntu0.2) ...
|
||||||
|
Setting up libzmq3-dev:amd64 (4.2.5-1ubuntu0.2) ...
|
||||||
|
Processing triggers for man-db (2.8.3-2ubuntu0.1) ...
|
||||||
|
```
|
||||||
|
Then
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt-get install libczmq-dev
|
||||||
|
```
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```
|
||||||
|
Reading package lists... Done
|
||||||
|
Building dependency tree
|
||||||
|
Reading state information... Done
|
||||||
|
The following additional packages will be installed:
|
||||||
|
libczmq4
|
||||||
|
The following NEW packages will be installed:
|
||||||
|
libczmq-dev libczmq4
|
||||||
|
0 upgraded, 2 newly installed, 0 to remove and 18 not upgraded.
|
||||||
|
Need to get 496 kB of archives.
|
||||||
|
After this operation, 2.148 kB of additional disk space will be used.
|
||||||
|
Do you want to continue? [Y/n] y
|
||||||
|
Get:1 http://es.archive.ubuntu.com/ubuntu bionic/universe amd64 libczmq4 amd64 4.1.0-2 [145 kB]
|
||||||
|
Get:2 http://es.archive.ubuntu.com/ubuntu bionic/universe amd64 libczmq-dev amd64 4.1.0-2 [351 kB]
|
||||||
|
Fetched 496 kB in 0s (1.287 kB/s)
|
||||||
|
Selecting previously unselected package libczmq4:amd64.
|
||||||
|
(Reading database ... 314055 files and directories currently installed.)
|
||||||
|
Preparing to unpack .../libczmq4_4.1.0-2_amd64.deb ...
|
||||||
|
Unpacking libczmq4:amd64 (4.1.0-2) ...
|
||||||
|
Selecting previously unselected package libczmq-dev:amd64.
|
||||||
|
Preparing to unpack .../libczmq-dev_4.1.0-2_amd64.deb ...
|
||||||
|
Unpacking libczmq-dev:amd64 (4.1.0-2) ...
|
||||||
|
Setting up libczmq4:amd64 (4.1.0-2) ...
|
||||||
|
Setting up libczmq-dev:amd64 (4.1.0-2) ...
|
||||||
|
Processing triggers for man-db (2.8.3-2ubuntu0.1) ...
|
||||||
|
Processing triggers for libc-bin (2.27-3ubuntu1) ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Create C Program
|
||||||
|
|
||||||
|
Now we've installed ZMQ and we can compile our C program using it's notifications. This C program it's a simple client that subscribes to a connection point served by bitcoind and ZMQ interface and reads incoming messages.
|
||||||
|
The program use czmq.h library and receives two parameters as follows, first param is the point exposed by bitcoind that should be tcp connection point depending second param that could be one of these:
|
||||||
|
|
||||||
|
zmqpubrawblock
|
||||||
|
zmqpubrawtx
|
||||||
|
zmqpubhashtx
|
||||||
|
zmqpubhashblock
|
||||||
|
|
||||||
|
|
||||||
|
``` c
|
||||||
|
#include <czmq.h>
|
||||||
|
int main(int argc, char ** argv) {
|
||||||
|
|
||||||
|
char *zmqserver;
|
||||||
|
char *topic;
|
||||||
|
|
||||||
|
if (argc < 3) {
|
||||||
|
printf("\nUSAGE:\nchainlistener <tcp://localhost:port> <topic>\n\n");
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
zmqserver = argv[1];
|
||||||
|
topic = argv[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
zsock_t *socket = zsock_new_sub(zmqserver, topic);
|
||||||
|
assert(socket);
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
zmsg_t *msg;
|
||||||
|
int rc = zsock_recv(socket, "m", &msg);
|
||||||
|
assert(rc == 0);
|
||||||
|
|
||||||
|
char *header = zmsg_popstr(msg);
|
||||||
|
zframe_t *zdata = zmsg_pop(msg);
|
||||||
|
unsigned int *no = (unsigned int*)zmsg_popstr(msg);
|
||||||
|
|
||||||
|
char *data = zframe_strhex(zdata);
|
||||||
|
int len = zframe_size(zdata);
|
||||||
|
printf("Size: %d\n", len);
|
||||||
|
printf("Data: %s", data);
|
||||||
|
printf("\nNo: %d\n", *no);
|
||||||
|
|
||||||
|
free(header);
|
||||||
|
free(data);
|
||||||
|
free(no);
|
||||||
|
free(zdata);
|
||||||
|
zmsg_destroy(&msg);
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
zsock_destroy(&socket);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Compile C program
|
||||||
|
|
||||||
|
To compile this C program you have to do it with clang or gcc compiler and test zmq and czmq libraries.
|
||||||
|
|
||||||
|
```
|
||||||
|
gcc -o chainlistener chainlistener.c -I/usr/local/include -L/usr/local/lib -lzmq -lczmq
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Configure ZMQ on bitcoind
|
||||||
|
|
||||||
|
#### ZMQ
|
||||||
|
|
||||||
|
ZeroMQ is a high-performance asynchronous messaging library that provides a message queue. ZeroMQ supports common messaging patterns (pub/sub, request/reply, client/server and others) over a variety of transports (TCP, in-process, inter-process, multicast, WebSocket and more), making inter-process messaging as simple as inter-thread messaging. As the purpose of this chapter is to show how to receive bitcoind notifications in the C language, ZMQ will be used for it.
|
||||||
|
|
||||||
|
Currently, the ZeroMQ facility only needs to have the ZeroMQ endpoint specified. ZeroMQ publish sockets prepend each data item with an arbitrary topic
|
||||||
|
prefix that allows subscriber clients to request only those items with a matching prefix.
|
||||||
|
|
||||||
|
Topics.
|
||||||
|
|
||||||
|
```
|
||||||
|
zmqpubrawblock=tcp://127.0.0.1:28332
|
||||||
|
zmqpubrawtx=tcp://127.0.0.1:28333
|
||||||
|
zmqpubhashtx=tcp://127.0.0.1:28334
|
||||||
|
zmqpubhashblock=tcp://127.0.0.1:28335
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example we'll use raw tx that is the topic that notifies about new transactions in raw format.
|
||||||
|
|
||||||
|
Add this lines to bitcoin.conf file and restart daemon.
|
||||||
|
|
||||||
|
```
|
||||||
|
zmqpubrawblock=tcp://127.0.0.1:28332
|
||||||
|
zmqpubrawtx=tcp://127.0.0.1:28333
|
||||||
|
```
|
||||||
|
Then test it's working using this command
|
||||||
|
|
||||||
|
```
|
||||||
|
$ bitcoin-cli --testnet getzmqnotifications
|
||||||
|
```
|
||||||
|
Output:
|
||||||
|
```
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "pubrawblock",
|
||||||
|
"address": "tcp://127.0.0.1:28332",
|
||||||
|
"hwm": 1000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "pubrawtx",
|
||||||
|
"address": "tcp://127.0.0.1:28333",
|
||||||
|
"hwm": 1000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
### 5. Execute listener:
|
||||||
|
|
||||||
|
When you execute chainlistener and passes these params you'll get an output like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./chainlistener tcp://127.0.0.1:28333 rawtx
|
||||||
|
Size: 250
|
||||||
|
Data: 02000000000101F5BD2032E5A9E6650D4E411AD272E391F26AFC3C9102B7C0C7444F8F74AE86010000000017160014AE9D51ADEEE8F46ED2017F41CD631D210F2ED9C5FEFFFFFF0203A732000000000017A9147231060F1CDF34B522E9DB650F44EDC6C0714E4C8710270000000000001976A914262437B129CF8592AB2EDC59C07D19C57729F72888AC02483045022100AE316D5F21657E3525271DE39EB285D8A0E89A20AB6413824E88CE47DCD0EFE702202F61E10C2A8F4A7125D5EB63AEF883D8E3584A0ECED0D349283AABB6CA5E066D0121035A77FE575A9005E3D3FF0682E189E753E82FA8BFF0A20F8C45F06DC6EBE3421079111B00
|
||||||
|
No: 67
|
||||||
|
Size: 249
|
||||||
|
Data: 0200000000010165C986992F7DAD22BBCE3FCF0BF546EDBC3C599618B04CFA22D9E64EF0CE4C030000000017160014B58E0A5CD68B249F1C407E9AAE9CD0332AAA3067FEFFFFFF02637932000000000017A914CCC47261489036CB6B9AA610857793FF5752E5378710270000000000001976A914262437B129CF8592AB2EDC59C07D19C57729F72888AC0247304402206CCC3F3B4BE01D4E532A01C2DC6BC3B53E4FFB6B494C8B87DD603EFC648A159902201653841E8B16A814DC375129189BB7CF01CFF7D269E91178645B6A97F5C7F4F10121030E20F3D2F172281B8DC747F007DF24B352248AC09E48CA64016942A8F01D317079111B00
|
||||||
|
No: 68
|
||||||
|
Size: 250
|
||||||
|
Data: 02000000000101E889CFC1FFE127BA49F6C1011388606A194109AE1EDAAB9BEE215E123C14A7920000000017160014577B0B3C2BF91B33B5BD70AE9E8BD8144F4B87E7FEFFFFFF02C34B32000000000017A914A9F1440402B46235822639C4FD2F78A31E8D269E8710270000000000001976A914262437B129CF8592AB2EDC59C07D19C57729F72888AC02483045022100B46318F53E1DCE63E7109DB4FA54AF40AADFC2FEB0E08263756BC3B7A6A744CB02200851982AF87DBABDC3DFC3362016ECE96AECFF50E24D9DCF264AE8966A5646FE0121039C90FCB46AEA1530E5667F8FF15CB36169D2AD81247472F236E3A3022F39917079111B00
|
||||||
|
No: 69
|
||||||
|
Size: 250
|
||||||
|
Data: 0200000000010137527957C9AD6CFF0C9A74597E6EFCD7E1EBD53E942AB2FA34A831046CA11488000000001716001429BFF05B3CD79E9CCEFDB5AE82139F72EB3E9DB0FEFFFFFF0210270000000000001976A914262437B129CF8592AB2EDC59C07D19C57729F72888AC231E32000000000017A9146C8D5FE29BFDDABCED0D6F4D8E82DCBFD9D34A8B8702483045022100F259846BAE29EB2C7A4AD711A3BC6109DE69AE91E35B14CA2742157894DD9760022021464E09C00ABA486AEAA0C49FEE12D2850DC03F57F04A1A9E2CC4D0F4F1459C012102899F24A9D60132F4DD1A5BA6DCD1E4E4B6C728927BA482C2C4E511679F60CA5779111B00
|
||||||
|
No: 70
|
||||||
|
.......
|
||||||
|
```
|
||||||
|
The first param it's bitcoind connection point where ZMQ will notifies all about transactions in raw format. It corresponds to this line in bitcoin.conf file zmqpubrawtx=tcp://127.0.0.1:28333
|
||||||
|
|
||||||
|
The second param is the topic selected rawtx explained above.
|
||||||
|
|
||||||
|
### For More Information
|
||||||
|
|
||||||
|
In this repository you can browse find more details about ZMQ notifications and others kind of messages. (https://github.com/Actinium-project/ChainTools/blob/master/docs/chainlistener.md)
|
||||||
|
|
||||||
|
### Summary Receiving Bitcoind Notifications with C.md
|
||||||
|
|
||||||
|
By using ZMQ framework, you can easily receive notifications by subscribing to a connection point exposed by bitcoind changing configuration file.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
417
18_1_Accessing_Bitcoind_with_Go.md
Normal file
417
18_1_Accessing_Bitcoind_with_Go.md
Normal file
@ -0,0 +1,417 @@
|
|||||||
|
# 18.1: Accessing Bitcoind with Go
|
||||||
|
|
||||||
|
## Set up Go
|
||||||
|
|
||||||
|
First, install curl if you haven't already:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ sudo apt install curl
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, look at the [Go downloads page](https://golang.org/dl/) and get the link for the latest download:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ curl -O https://dl.google.com/go/go1.14.4.linux-amd64.tar.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
Once it finishes downloading, compare the hash of the download to the hash on the [Go downloads page](https://golang.org/dl/):
|
||||||
|
|
||||||
|
```
|
||||||
|
$ sha256sum go1.14.4.linux-amd64.tar.gz
|
||||||
|
aed845e4185a0b2a3c3d5e1d0a35491702c55889192bb9c30e67a3de6849c067 /home/user1/go1.14.4.linux-amd64.tar.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
The hashes should match. Now we can extract the tarball and install Go on our system:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ tar xvf go1.12.5.linux-amd64.tar.gz
|
||||||
|
$ sudo chown -R root:root ./go
|
||||||
|
$ sudo mv go /usr/local
|
||||||
|
```
|
||||||
|
|
||||||
|
Now we need to create a Go path to specify our environment. Open the ```~/.profile``` file with an editor of your choice and add the following to the end of it:
|
||||||
|
|
||||||
|
```
|
||||||
|
export GOPATH=$HOME/work
|
||||||
|
export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, refresh your profile:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ source ~/.profile
|
||||||
|
```
|
||||||
|
|
||||||
|
Lastly, create the directory for your Go workspace:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ mkdir $HOME/work
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setup btcd rpcclient
|
||||||
|
|
||||||
|
We will be using the rpcclient that comes with btcd, a Bitcoin implementation written in Go. Although rpcclient was originally designed to work with the btcd Bitcoin full node, it also works with Bitcoin core. It has some quirks which we will be looking at. We can use ```go get``` to download it:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ go get github.com/btcsuite/btcd/rpcclient
|
||||||
|
```
|
||||||
|
|
||||||
|
To test that it works, navigate to the directory with the Bitcoin core examples:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cd $GOPATH/src/github.com/btcsuite/btcd/rpcclient/examples/bitcoincorehttp
|
||||||
|
```
|
||||||
|
|
||||||
|
Modify the ```main.go``` file and enter the details associated with your Bitcoin core setup:
|
||||||
|
|
||||||
|
```
|
||||||
|
Host: "localhost:18332",
|
||||||
|
User: "bitcoinrpc",
|
||||||
|
Pass: "d8340efbcd34e312044c8431c59c792c",
|
||||||
|
```
|
||||||
|
> **MAINNET VS TESTNET:** The port would be 8332 for a mainnet setup.
|
||||||
|
|
||||||
|
Now run:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ go run main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
You should see the block count printed:
|
||||||
|
|
||||||
|
```
|
||||||
|
2020/06/24 20:41:01 Block count: 1773552
|
||||||
|
```
|
||||||
|
|
||||||
|
### Print the latest block
|
||||||
|
|
||||||
|
Leave the btcd directory and create a new Go project:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ mkdir -p ~/work/src/myproject/bitcoin
|
||||||
|
$ cd ~/work/src/myproject/bitcoin
|
||||||
|
```
|
||||||
|
|
||||||
|
In this directory, create a file called ```main.go``` and enter the following in it:
|
||||||
|
|
||||||
|
```
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"fmt"
|
||||||
|
"github.com/btcsuite/btcd/rpcclient"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
connCfg := &rpcclient.ConnConfig{
|
||||||
|
Host: "localhost:18332",
|
||||||
|
User: "bitcoinrpc",
|
||||||
|
Pass: "431451790e3eee1913115b9dd2fbf0ac",
|
||||||
|
HTTPPostMode: true,
|
||||||
|
DisableTLS: true,
|
||||||
|
}
|
||||||
|
client, err := rpcclient.New(connCfg, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer client.Shutdown()
|
||||||
|
|
||||||
|
blockCount, err := client.GetBlockCount()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
blockHash, err := client.GetBlockHash(blockCount)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%d\n", blockCount)
|
||||||
|
fmt.Printf("%s\n", blockHash.String())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Make sure to change your username and password to those from your ```~/.bitcoin/bitcoin.conf``` file.
|
||||||
|
|
||||||
|
The btcd rpcclient works by calling bitcoin-cli commands through in PascalCase. For example, ```bitcoin-cli getblockcount``` is ```client.GetBlockCount``` in Go. The ```connCfg``` parameters allow you to choose the Bitcoin RPC port, username, password and whether you are on testnet or mainnet. Using ```rpcclient.New(connCfg, nil)``` we configure ```client``` to connect to our Bitcoin node.
|
||||||
|
|
||||||
|
The ```import``` declaration at the top allows you to import relevant libraries. For every example here we will need to import ```"log", "fmt"``` and ```"github.com/btcsuite/btcd/rpcclient"```. We may need to import additional libraries for some examples. ```log``` is used for printing out error messages. After each time our Bitcoin node is called, we have an if statement checking if there are any errors. If there are errors, ```log``` is used to print them out. ```fmt``` is used for printing out our output. Since the examples in this document have different libraries to import, I will be showing the entire Go file, not just the specific function.
|
||||||
|
|
||||||
|
The ```defer client.Shutdown()``` line is for disconnecting from our Bitcoin node, once the ```main()``` function finishes executing. After the ```defer client.Shutdown()``` line is where the exciting stuff is, like ```client.GetBlockCount()``` and ```client.GetBlockHash(blockCount)```.
|
||||||
|
|
||||||
|
The rpcclient functions can take inputs as well, for example ```client.GetBlockHash(blockCount)``` takes the block count as an input. The ```client.GetBlockHash(blockCount)``` from above would look like this as a ```bitcoin-cli``` command:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ bitcoin-cli getblockhash 1773561
|
||||||
|
00000000000000346bab4126f418a5820692c9a7de7ef79717bebfccebacad61
|
||||||
|
```
|
||||||
|
|
||||||
|
A quirk with hashes in rpcclient is that they will typically print in a different encoding if you were to print it normally with ```blockHash```. In order to print them as a string, we need to use ```blockHash.String()```.
|
||||||
|
|
||||||
|
We can run the Go code with ```go run main.go``` and we should get an output that looks like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ go run main.go
|
||||||
|
1773561
|
||||||
|
00000000000000346bab4126f418a5820692c9a7de7ef79717bebfccebacad61
|
||||||
|
```
|
||||||
|
|
||||||
|
The latest block number along with its hash should be printed out.
|
||||||
|
|
||||||
|
### Getting your wallet balance
|
||||||
|
|
||||||
|
Since the btcd rpcclient has some limits, we can't make a use of the ```getwalletinfo``` function, however we can get the balance of our wallet:
|
||||||
|
|
||||||
|
```
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"fmt"
|
||||||
|
"github.com/btcsuite/btcd/rpcclient"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
connCfg := &rpcclient.ConnConfig{
|
||||||
|
Host: "localhost:18332",
|
||||||
|
User: "bitcoinrpc",
|
||||||
|
Pass: "431451790e3eee1913115b9dd2fbf0ac",
|
||||||
|
HTTPPostMode: true,
|
||||||
|
DisableTLS: true,
|
||||||
|
}
|
||||||
|
client, err := rpcclient.New(connCfg, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer client.Shutdown()
|
||||||
|
|
||||||
|
wallet, err := client.GetBalance("*")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(wallet)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You should get an output similar to this:
|
||||||
|
|
||||||
|
```
|
||||||
|
0.000689 BTC
|
||||||
|
```
|
||||||
|
|
||||||
|
```client.GetBalance("*")``` requires the ```"*"``` input, due to a quirk with btcd. The asterisk signifies that we want to get the balance of all our wallets.
|
||||||
|
|
||||||
|
### Generate an address
|
||||||
|
|
||||||
|
We can generate addresses, but we can't specify the address type:
|
||||||
|
|
||||||
|
```
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"fmt"
|
||||||
|
"github.com/btcsuite/btcd/rpcclient"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
connCfg := &rpcclient.ConnConfig{
|
||||||
|
Host: "localhost:18332",
|
||||||
|
User: "bitcoinrpc",
|
||||||
|
Pass: "431451790e3eee1913115b9dd2fbf0ac",
|
||||||
|
HTTPPostMode: true,
|
||||||
|
DisableTLS: true,
|
||||||
|
Params: chaincfg.TestNet3Params.Name,
|
||||||
|
}
|
||||||
|
client, err := rpcclient.New(connCfg, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer client.Shutdown()
|
||||||
|
|
||||||
|
address, err := client.GetNewAddress("")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Println(address)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
> **MAINNET VS TESTNET:** ```Params: chaincfg.TestNet3Params.Name,``` should be ```Params: chaincfg.MainNetParams.Name,``` on mainnet.
|
||||||
|
|
||||||
|
You may notice that the ```connCfg``` clause is different in this example. ```Params: chaincfg.TestNet3Params.Name,``` is added here, so that the rpcclient knows that we are dealing with testnet addresses. You would change ```chaincfg.TestNet3Params.Name``` to ```chaincfg.MainNetParams.Name``` if you were on mainnet. This specification is only required during address generation, which is why I only kept it in this example. You can include this in other examples as well, but it isn't necessary. Make sure to import ```"github.com/btcsuite/btcd/chaincfg"``` if you are using it.
|
||||||
|
|
||||||
|
A quirk with ```client.GetNewAddress("")``` is that an empty string needs to be included for it to work.
|
||||||
|
|
||||||
|
### Get amount received by an address
|
||||||
|
|
||||||
|
We can retrieve the amount of Bitcoin received by a specific address:
|
||||||
|
|
||||||
|
```
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"fmt"
|
||||||
|
"github.com/btcsuite/btcd/rpcclient"
|
||||||
|
"github.com/btcsuite/btcutil"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
connCfg := &rpcclient.ConnConfig{
|
||||||
|
Host: "localhost:18332",
|
||||||
|
User: "bitcoinrpc",
|
||||||
|
Pass: "431451790e3eee1913115b9dd2fbf0ac",
|
||||||
|
HTTPPostMode: true,
|
||||||
|
DisableTLS: true,
|
||||||
|
}
|
||||||
|
client, err := rpcclient.New(connCfg, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer client.Shutdown()
|
||||||
|
|
||||||
|
defaultNet := &chaincfg.TestNet3Params
|
||||||
|
addr, err := btcutil.DecodeAddress("mpGpCMX6SuUimDZKiVViuhd7EGyVxkNnha", defaultNet)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
wallet, err := client.GetReceivedByAddress(addr)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(wallet)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
> **MAINNET VS TESTNET:** ```&chaincfg.TestNet3Params``` should be ```&chaincfg.TestNet3Params``` on mainnet.
|
||||||
|
|
||||||
|
You should get an output similar to this:
|
||||||
|
|
||||||
|
```
|
||||||
|
0.000089 BTC
|
||||||
|
```
|
||||||
|
|
||||||
|
The defaultNet variable is used to specify whether our Bitcoin node is on testnet or on mainnet. This example requires us to import the ```"github.com/btcsuite/btcutil"``` and ```"github.com/btcsuite/btcd/chaincfg"``` libraries. ```btcutil``` allows for a Bitcoin address to be decoded in a way that the rpcclient can understand. This is necessary when working with addresses in rpcclient. ```chaincfg``` is used to configure our chain as the Testnet chain. This is necessary for address decoding since the addresses used on Mainnet and Testnet are different.
|
||||||
|
|
||||||
|
### Sending Bitcoin to an address
|
||||||
|
|
||||||
|
```
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"fmt"
|
||||||
|
"github.com/btcsuite/btcd/rpcclient"
|
||||||
|
"github.com/btcsuite/btcutil"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
connCfg := &rpcclient.ConnConfig{
|
||||||
|
Host: "localhost:18332",
|
||||||
|
User: "bitcoinrpc",
|
||||||
|
Pass: "431451790e3eee1913115b9dd2fbf0ac",
|
||||||
|
HTTPPostMode: true,
|
||||||
|
DisableTLS: true,
|
||||||
|
}
|
||||||
|
client, err := rpcclient.New(connCfg, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer client.Shutdown()
|
||||||
|
|
||||||
|
defaultNet := &chaincfg.TestNet3Params
|
||||||
|
addr, err := btcutil.DecodeAddress("n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi", defaultNet)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
sent, err := client.SendToAddress(addr, btcutil.Amount(1e4))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(sent)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
After the transaction is sent, the txid of the transaction is outputted:
|
||||||
|
|
||||||
|
```
|
||||||
|
9aa4cd6559e0d69059eae142c35bfe78b71a8084e1fcc2c74e2a9675e9e7489d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lookup a transaction
|
||||||
|
|
||||||
|
```
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"fmt"
|
||||||
|
"github.com/btcsuite/btcd/rpcclient"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
connCfg := &rpcclient.ConnConfig{
|
||||||
|
Host: "localhost:18332",
|
||||||
|
User: "bitcoinrpc",
|
||||||
|
Pass: "431451790e3eee1913115b9dd2fbf0ac",
|
||||||
|
HTTPPostMode: true,
|
||||||
|
DisableTLS: true,
|
||||||
|
}
|
||||||
|
client, err := rpcclient.New(connCfg, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer client.Shutdown()
|
||||||
|
|
||||||
|
chash, err := chainhash.NewHashFromStr("1661ce322c128e053b8ea8fcc22d17df680d2052983980e2281d692b9b4ab7df")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
transactions, err := client.GetTransaction(chash)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(transactions)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This prints out the details associated with a transaction, such as its amount and how many times it has been confirmed:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"amount": 0.00100000,
|
||||||
|
"confirmations": 4817,
|
||||||
|
"blockhash": "000000006628870b0a8a66abea9cf0d4e815c491f079e3fa9e658a87b5dc863a",
|
||||||
|
"blockindex": 117,
|
||||||
|
"blocktime": 1591857418,
|
||||||
|
"txid": "1661ce322c128e053b8ea8fcc22d17df680d2052983980e2281d692b9b4ab7df",
|
||||||
|
"walletconflicts": [
|
||||||
|
],
|
||||||
|
"time": 1591857343,
|
||||||
|
"timereceived": 1591857343,
|
||||||
|
"bip125-replaceable": "no",
|
||||||
|
"details": [
|
||||||
|
{
|
||||||
|
"address": "mpGpCMX6SuUimDZKiVViuhd7EGyVxkNnha",
|
||||||
|
"category": "receive",
|
||||||
|
"amount": 0.00100000,
|
||||||
|
"label": "",
|
||||||
|
"vout": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hex": "02000000000101e9e8c3bd057d54e73baadc60c166860163b0e7aa60cab33a03e89fb44321f8d5010000001716001435c2aa3fc09ea53c3e23925c5b2e93b9119b2568feffffff02a0860100000000001976a914600c8c6a4abb0a502ea4de01681fe4fa1ca7800688ac65ec1c000000000017a91425b920efb2fde1a0277d3df11d0fd7249e17cf8587024730440220403a863d312946aae3f3ef0a57206197bc67f71536fb5f4b9ca71a7e226b6dc50220329646cf786cfef79d60de3ef54f702ab1073694022f0618731902d926918c3e012103e6feac9d7a8ad1ac6b36fb4c91c1c9f7fff1e7f63f0340e5253a0e4478b7b13f41fd1a00"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```"github.com/btcsuite/btcd/chaincfg/chainhash"``` is imported in order to allow hashes to be stored in the Go code. ```chainhash.NewHashFromStr("hash")``` converts a hash in a string, to a format that works with rpcclient.
|
||||||
|
|
||||||
|
## Summary: Accessing Bitcoind with Go
|
||||||
|
|
||||||
|
Although the btcd rpcclient has some limits, we can still perform the main rpc commands in Go. The documentation for rpcclient is available on [Godoc](https://godoc.org/github.com/btcsuite/btcd/rpcclient). If the documentation doesn't have what your looking for, have a look at the [btcd repository](https://github.com/btcsuite/btcd). It is generally well documented and easy to read. Based on these examples you should be able to incorporate Bitcoin in a Go project and do things like sending and receiving coins.
|
@ -7,39 +7,110 @@ Interacting with the `bitcoind` directly and using command-line `curl` can get s
|
|||||||
|
|
||||||
## Setup Java
|
## Setup Java
|
||||||
|
|
||||||
To install Java on the VPS Server, you are able to use the `apt-get` command. We will also use [Apache Maven](http://maven.apache.org/) to manage the dependencies, so we will install it together.
|
To install Java on the VPS Server, you are able to use the `apt-get` command. We will also use [Apache Maven](http://maven.apache.org/) to manage the dependencies, so we will install it together. In this project we will create a maven project and indicate minimum configuration about Gradle (https://gradle.org/releases/)
|
||||||
|
|
||||||
```
|
```
|
||||||
$ apt-get install openjdk-9-jre-headless maven
|
$ apt-get install openjdk-11-jre-headless maven
|
||||||
```
|
```
|
||||||
|
|
||||||
You can verify your Java installation:
|
You can verify your Java installation:
|
||||||
```
|
```
|
||||||
$ java -version
|
$ java -version
|
||||||
openjdk version "9-internal"
|
openjdk version "11.0.7" 2020-04-14
|
||||||
OpenJDK Runtime Environment (build 9-internal+0-2016-04-14-195246.buildd.src)
|
OpenJDK Runtime Environment (build 11.0.7+10-post-Ubuntu-2ubuntu218.04)
|
||||||
OpenJDK 64-Bit Server VM (build 9-internal+0-2016-04-14-195246.buildd.src, mixed mode)
|
OpenJDK 64-Bit Server VM (build 11.0.7+10-post-Ubuntu-2ubuntu218.04, mixed mode, sharing)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Setup Dependency
|
## Create maven project
|
||||||
|
|
||||||
|
```
|
||||||
|
mvn archetype:generate -DgroupId=com.blockchaincommons.lbtc -DartifactId=java-project -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
|
||||||
|
```
|
||||||
|
It will download some dependencies
|
||||||
|
|
||||||
|
```
|
||||||
|
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.pom
|
||||||
|
Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.pom (4 KB at 4.2 KB/sec)
|
||||||
|
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/22/maven-plugins-22.pom
|
||||||
|
Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/22/maven-plugins-22.pom (13 KB at 385.9 KB/sec)
|
||||||
|
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/maven-parent/21/maven-parent-21.pom
|
||||||
|
Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/maven-parent/21/maven-parent-21.pom (26 KB at 559.6 KB/sec)
|
||||||
|
Downloading: https://repo.maven.apache.org/maven2/org/apache/apache/10/apache-10.pom
|
||||||
|
..............
|
||||||
|
```
|
||||||
|
It will create a configuration file pom.xml
|
||||||
|
|
||||||
|
```
|
||||||
|
user@machine:~/BitcoinRpcClient/java-project$ ll
|
||||||
|
total 16
|
||||||
|
drwxr-xr-x 3 user user 4096 Jun 17 16:47 ./
|
||||||
|
drwxr-xr-x 3 user user 4096 Jun 17 15:02 ../
|
||||||
|
-rw-r--r-- 1 user user 1175 Jun 17 16:34 pom.xml
|
||||||
|
drwxr-xr-x 4 user user 4096 Jun 17 15:02 src/
|
||||||
|
user@machine:~/BitcoinRpcClient/java-project$
|
||||||
|
```
|
||||||
|
|
||||||
|
This project uses JavaBitcoindRpcClient, so you need include the dependency editing pom.xml file
|
||||||
|
|
||||||
If you use Maven in your Java project, you can include the dependency:
|
|
||||||
```xml
|
```xml
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>wf.bitcoin</groupId>
|
<groupId>wf.bitcoin</groupId>
|
||||||
<artifactId>JavaBitcoindRpcClient</artifactId>
|
<artifactId>JavaBitcoindRpcClient</artifactId>
|
||||||
<version>0.9.13</version>
|
<version>1.1.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
You need to add compiler properties to indicate what JDK version will compile the source code.
|
||||||
|
|
||||||
|
```
|
||||||
|
<properties>
|
||||||
|
<!-- https://maven.apache.org/general.html#encoding-warning -->
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.source>1.11</maven.compiler.source>
|
||||||
|
<maven.compiler.target>1.11</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
```
|
||||||
|
Finally add source code to java classes and execute
|
||||||
|
|
||||||
|
```
|
||||||
|
user@machine:~/BitcoinRpcClient/java-project$ mvn package
|
||||||
|
[INFO] Scanning for projects...
|
||||||
|
[INFO]
|
||||||
|
[INFO] ------------------< com.blockchaincommons.lbtc:java-project >-------------------
|
||||||
|
[INFO] Building java-project 1.0-SNAPSHOT
|
||||||
|
[INFO] --------------------------------[ jar ]---------------------------------
|
||||||
|
[INFO]
|
||||||
|
......
|
||||||
|
-------------------------------------------------------
|
||||||
|
T E S T S
|
||||||
|
-------------------------------------------------------
|
||||||
|
Running com.blockchaincommons.lbtc.AppTest
|
||||||
|
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.067 sec
|
||||||
|
|
||||||
|
Results :
|
||||||
|
|
||||||
|
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
|
||||||
|
[INFO]
|
||||||
|
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ java-project ---
|
||||||
|
[INFO] Building jar: /home/user/BitcoinRpcClient/java-project/target/java-project-1.0-SNAPSHOT.jar
|
||||||
|
[INFO] ------------------------------------------------------------------------
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
[INFO] ------------------------------------------------------------------------
|
||||||
|
[INFO] Total time: 1.956 s
|
||||||
|
[INFO] Finished at: 2020-06-1716T12:21:18+31:00
|
||||||
|
[INFO] ------------------------------------------------------------------------```
|
||||||
|
|
||||||
|
```
|
||||||
Or if you use Gradle:
|
Or if you use Gradle:
|
||||||
```groovy
|
```groovy
|
||||||
compile 'wf.bitcoin:JavaBitcoindRpcClient:0.9.13'
|
compile 'wf.bitcoin:JavaBitcoindRpcClient:1.1.0'
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want a sample project and some instructions on how to run it on the server that we just created, you can refer to the [Bitcoind Java Sample Project](https://github.com/brunocvcunha/bitcoind-java-client-sample/).
|
If you want a sample project and some instructions on how to run it on the server that we just created, you can refer to the [Bitcoind Java Sample Project](https://github.com/brunocvcunha/bitcoind-java-client-sample/).
|
||||||
|
|
||||||
### Build Your Connection
|
## Build Your Connection
|
||||||
|
|
||||||
|
### Make an RPC Call
|
||||||
|
|
||||||
To use `JavaBitcoindRpcClient`, you need to create a `BitcoindRpcClient` instance. The arguments in the URL are username, password, IP address and port. You should know this information from your work with `curl` . As you'll recall, the IP address 127.0.0.1 and port 18332 should be correct for the standard testnet setup described in this documents, while you can extract the user and password from `~/.bitcoin/bitcoin.conf`.
|
To use `JavaBitcoindRpcClient`, you need to create a `BitcoindRpcClient` instance. The arguments in the URL are username, password, IP address and port. You should know this information from your work with `curl` . As you'll recall, the IP address 127.0.0.1 and port 18332 should be correct for the standard testnet setup described in this documents, while you can extract the user and password from `~/.bitcoin/bitcoin.conf`.
|
||||||
|
|
||||||
@ -79,6 +150,54 @@ Blocks.....: 1254920
|
|||||||
Difficulty.: 1.0
|
Difficulty.: 1.0
|
||||||
Hash Power.: 6585163152453.466796875
|
Hash Power.: 6585163152453.466796875
|
||||||
```
|
```
|
||||||
|
### Making an RPC Call with Arguments
|
||||||
|
|
||||||
|
When you make a RPC call with arguments you need to setup parameters depending method or type of object you use.
|
||||||
|
|
||||||
|
### Look up Address
|
||||||
|
|
||||||
|
You can look up addresses on your wallet passing it as an argument. In this case we use getAddressInfo method to obtain some information about an address.
|
||||||
|
|
||||||
|
```java
|
||||||
|
public AddressInfo getAddressInfo(String address) throws GenericRpcException {
|
||||||
|
return new AddressInfoMapWrapper((Map<String, ?>) query("getaddressinfo", address));
|
||||||
|
}
|
||||||
|
|
||||||
|
String addr1 = "bcrt1qs4ylwj2v5v0gq7eqzp9k9vxazdrkexhkghxpyp"; //regtest
|
||||||
|
AddressInfo addr1Info = rpcClient.getAddressInfo(addr1);
|
||||||
|
System.out.println("Address: " + addr1Info.address());
|
||||||
|
System.out.println("HdKeyPath: " + addr1Info.hdKeyPath());
|
||||||
|
System.out.println("PubKey: " + addr1Info.pubKey());
|
||||||
|
System.out.println("MasterFingerPrint: " + addr1Info.hdMasterFingerprint());
|
||||||
|
```
|
||||||
|
Output:
|
||||||
|
```
|
||||||
|
Jun 19, 2020 8:01:56 PM wf.bitcoin.javabitcoindrpcclient.BitcoinJSONRPCClient <clinit>
|
||||||
|
WARNING: Currently relying on rpcuser / rpcpassword for authentication. This will soon be deprecated in bitcoind. To use newer auth mechanism based on a temporary password, remove properties rpcuser / rpcpassword from bitcoin.conf
|
||||||
|
Address: bcrt1qs4ylwj2v5v0gq7eqzp9k9vxazdrkexhkghxpyp
|
||||||
|
HdKeyPath: m/0'/0'/16'
|
||||||
|
PubKey: 03cf852403abbcf0431e8c82b414b0c805f5e1b863989cbc9adb3a316510e0d1f5
|
||||||
|
MasterFingerPrint: 91cfb0fc
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Look up Funds
|
||||||
|
|
||||||
|
You can look up your balance.
|
||||||
|
|
||||||
|
```java
|
||||||
|
public BigDecimal getBalance(String account) throws GenericRpcException {
|
||||||
|
return (BigDecimal) query("getbalance");
|
||||||
|
}
|
||||||
|
String balance = rpcClient.getBalance(account);
|
||||||
|
System.out.println("Balance: " + balance);
|
||||||
|
|
||||||
|
```
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```
|
||||||
|
Balance: 14701.56249600
|
||||||
|
```
|
||||||
|
|
||||||
### Creating an Address
|
### Creating an Address
|
||||||
|
|
||||||
@ -98,30 +217,115 @@ New Address: mpsFtZ8qTJPRGZy1gaaUw37fHeUSPLkzzs
|
|||||||
Priv Key: cTy2AnmAALsHokYzJzTdsUBSqBtypmWfmSNYgG6qQH43euUZgqic
|
Priv Key: cTy2AnmAALsHokYzJzTdsUBSqBtypmWfmSNYgG6qQH43euUZgqic
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Create a Transaction
|
||||||
|
|
||||||
|
You can create a raw transaction using createRawTransaction method passing as arguments two ArrayList objects containing inputs and outputs to be used. In this example we will create two addresses and we will use generateToAddress method in regtest to mine some bitcoin.
|
||||||
|
|
||||||
|
```java
|
||||||
|
public String create() throws GenericRpcException {
|
||||||
|
return bitcoin.createRawTransaction(new ArrayList<>(inputs), outputs);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### generatetoaddress
|
||||||
|
|
||||||
|
The generatetoaddress RPC mines blocks immediately to a specified address. It receives numBlocks param as the number of blocks to generate. The address to send the newly generated bitcoin to and param maxTries as the maximum number of iterations that are tried to create the requested number of blocks.
|
||||||
|
|
||||||
|
We use generateToAddress method to mine some bitcoin and we use listUnspent (https://github.com/Polve/bitcoin-rpc-client/blob/master/src/main/java/wf/bitcoin/javabitcoindrpcclient/BitcoinJSONRPCClient.java#L756) method to load object utxos with coins associated with address object addr1.
|
||||||
|
|
||||||
|
```java
|
||||||
|
System.out.println("Created address addr1: " + addr1);
|
||||||
|
String addr2 = rpcClient.getNewAddress();
|
||||||
|
System.out.println("Created address addr2: " + addr2);
|
||||||
|
List<String> generatedBlocksHashes = rpcClient.generateToAddress(110, addr1);
|
||||||
|
System.out.println("Generated " + generatedBlocksHashes.size() + " blocks for addr1");
|
||||||
|
List<Unspent> utxos = rpcClient.listUnspent(0, Integer.MAX_VALUE, addr1);
|
||||||
|
System.out.println("Found " + utxos.size() + " UTXOs (unspent transaction outputs) belonging to addr1");
|
||||||
|
```
|
||||||
|
|
||||||
|
debug.log
|
||||||
|
```
|
||||||
|
2020-06-19T18:22:30Z [default wallet] AddToWallet 0bed0fc1b6190b85b93bda6fe752c7596234bea8399827d27e347f35ca68d59f new
|
||||||
|
2020-06-19T18:22:30Z CreateNewBlock(): block weight: 892 txs: 0 fees: 0 sigops 400
|
||||||
|
2020-06-19T18:22:30Z UpdateTip: new best=6125a1648f84e11d9d8ee1b003056c20142e9f1e54376f5d117554785957aadf height=1100 version=0x20000000 log2_work=11.104599 tx=1103 date='2020-06-19T18:22:48Z' progress=1.000000 cache=0.0MiB(114txo)
|
||||||
|
2020-06-19T18:22:30Z [default wallet] AddToWallet a2690eb4c50b1140dfc77f95db9c8065e8d7e88b2cfbd9a75e9b2dd157857afd new
|
||||||
|
```
|
||||||
|
Output
|
||||||
|
```
|
||||||
|
Created address addr1: bcrt1qs4ylwj2v5v0gq7eqzp9k9vxazdrkexhkghxpyp
|
||||||
|
Created address addr2: bcrt1qdp6fut9pmchwacpr28vfszdp5qayza8jkq5t3v
|
||||||
|
Generated 110 blocks for addr1
|
||||||
|
Found 118 UTXOs (unspent transaction outputs) belonging to addr1
|
||||||
|
```
|
||||||
|
Now we have created UXTO's we can create a transaction, to perform this we will use three objects, TxInput, TxOutput and Transactions Builder. With this code we got inputs and outputs for our transaction. Object uxto is a list with all UXTO's belonging to addr1. We will choose uxto in position zero on the list and add it to txb object as input. Then we add addr2 object as the output and set fee subtracting estimatedFee value.
|
||||||
|
|
||||||
|
```java
|
||||||
|
BitcoinRawTxBuilder txb = new BitcoinRawTxBuilder(rpcClient);
|
||||||
|
BigDecimal estimatedFee = BigDecimal.valueOf(0.00000200);
|
||||||
|
TxInput in = utxos.get(0);
|
||||||
|
txb.in(in);
|
||||||
|
|
||||||
|
txToAddr2Amount = utxos.get(0).amount().subtract(estimatedFee);
|
||||||
|
txb.out(addr2, txToAddr2Amount);
|
||||||
|
|
||||||
|
System.out.println("unsignedRawTx in amount: " + utxos.get(0).amount());
|
||||||
|
System.out.println("unsignedRawTx out amount: " + txToAddr2Amount);
|
||||||
|
```
|
||||||
|
|
||||||
|
Output
|
||||||
|
```
|
||||||
|
unsignedRawTx in amount: 0.78125000
|
||||||
|
unsignedRawTx out amount: 0.78124800
|
||||||
|
```
|
||||||
|
|
||||||
### Sending Transactions
|
### Sending Transactions
|
||||||
|
|
||||||
You can easily send a transaction using the method `sendToAddress()`.
|
Before send a transaction we need to create and sign it. To create you can use create method of Builder Transaction object. This method returns a unsigned string transaction in hexadecimal format.
|
||||||
|
|
||||||
|
```java
|
||||||
|
String unsignedRawTxHex = txb.create();
|
||||||
|
System.out.println("Created unsignedRawTx from addr1 to addr2: " + unsignedRawTxHex);
|
||||||
|
```
|
||||||
|
Output:
|
||||||
|
```
|
||||||
|
Created unsignedRawTx from addr1 to addr2: 020000000101f08cabf817b8fb076501f04b69df0aae59d61d94edfb40c2c41b1b2bd16a3f0000000000ffffffff010017a8040000000016001468749e2ca1de2eeee02351d89809a1a03a4174f200000000
|
||||||
|
```
|
||||||
|
Later you should sign transaction with method signRawTransactionWithKey. This method receives as parameters a unsigned raw string transaction, the private key of address and TxInput object.
|
||||||
|
|
||||||
|
```java
|
||||||
|
SignedRawTransaction srTx = rpcClient.signRawTransactionWithKey(
|
||||||
|
unsignedRawTxHex,
|
||||||
|
Arrays.asList(rpcClient.dumpPrivKey(addr1)), //
|
||||||
|
Arrays.asList(in),
|
||||||
|
null);
|
||||||
|
System.out.println("signedRawTx hex: " + srTx.hex());
|
||||||
|
System.out.println("signedRawTx complete: " + srTx.complete());
|
||||||
|
```
|
||||||
|
Output
|
||||||
|
```
|
||||||
|
signedRawTx hex: 0200000000010101f08cabf817b8fb076501f04b69df0aae59d61d94edfb40c2c41b1b2bd16a3f0000000000ffffffff010017a8040000000016001468749e2ca1de2eeee02351d89809a1a03a4174f20247304402204ed1ce8ea7e36cd53ba78beaccaf3ef62b094c29413a451e3abae99548520f7f02206b606c21cd38cc4e61d84c229d42ce69a01cb3a0ed360fced2a6f5b5d8dbe951012103cf852403abbcf0431e8c82b414b0c805f5e1b863989cbc9adb3a316510e0d1f500000000
|
||||||
|
signedRawTx complete: true
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally you can send signed transaction using
|
||||||
|
|
||||||
|
```java
|
||||||
|
String sentRawTransactionID = rpcClient.sendRawTransaction(srTx.hex());
|
||||||
|
System.out.println("Sent signedRawTx (txID): " + sentRawTransactionID);```
|
||||||
|
```
|
||||||
|
This program will output a transaction id:
|
||||||
|
|
||||||
|
```
|
||||||
|
Sent signedRawTx (txID): 03b2327117264837f449a718e5aeedb07f90d435892a33c3c2772d4c3b40111f
|
||||||
|
```
|
||||||
|
debug.log.
|
||||||
|
|
||||||
|
```
|
||||||
|
2020-06-19T18:22:31Z [default wallet] AddToWallet 03b2327117264837f449a718e5aeedb07f90d435892a33c3c2772d4c3b40111f new
|
||||||
|
```
|
||||||
|
|
||||||
For more information about sending transactions, you can check [4: Sending Bitcoin Transactions](04_0_Sending_Bitcoin_Transactions.md).
|
For more information about sending transactions, you can check [4: Sending Bitcoin Transactions](04_0_Sending_Bitcoin_Transactions.md).
|
||||||
|
|
||||||
|
|
||||||
```java
|
|
||||||
String sendToAddress = rpcClient.sendToAddress("mgnNsZj6tPzpd7JwTTidUKnGoDTkcucLT5", 1);
|
|
||||||
System.out.println("Send: " + sendToAddress);
|
|
||||||
```
|
|
||||||
This program will output a transaction id, for example:
|
|
||||||
```
|
|
||||||
a2d2f629d6666ca6e440169a322850cd9d133f637f7a02a02a0a7477bc5687d4
|
|
||||||
```
|
|
||||||
|
|
||||||
In case you want to adjust the transaction fee, you can use the `setTxFee` method before sending the output:
|
|
||||||
|
|
||||||
```java
|
|
||||||
rpcClient.setTxFee(new BigDecimal(0.001).setScale(3, BigDecimal.ROUND_DOWN));
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Listening to Transactions or Blocks
|
### Listening to Transactions or Blocks
|
||||||
|
|
||||||
You may want to write applications that keep listening the Blockchain, and execute a specific code when something happens, such as a transaction that involves an address in your wallet, or even the generation of a new block in the network.
|
You may want to write applications that keep listening the Blockchain, and execute a specific code when something happens, such as a transaction that involves an address in your wallet, or even the generation of a new block in the network.
|
||||||
@ -152,3 +356,11 @@ Transaction: {account=Tests, address=mhopuJzgmTwhGfpNLCJ9CRknugY691oXp1, categor
|
|||||||
|
|
||||||
Block: 000000004564adfee3738314549f7ca35d96c4da0afc6b232183917086b6d971
|
Block: 000000004564adfee3738314549f7ca35d96c4da0afc6b232183917086b6d971
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### For More Information
|
||||||
|
|
||||||
|
In this repository you can browse all souce code for bitcoin-rpc-client (https://github.com/Polve/bitcoin-rpc-client)
|
||||||
|
|
||||||
|
### Summary Accessing Bitcoind with Java
|
||||||
|
|
||||||
|
By using javabitcoinrpc library, you can easily access bitcoind via RPC calls from a Java. To do so, you should create a client, an RPC connection, then create objects and classes to interact with individual RPC calls, some of them with parameters. You can easily create address, sign transactions, query balances and others RPC methods.
|
||||||
|
@ -1,41 +1,45 @@
|
|||||||
# 18.3: Accessing Bitcoind with NodeJS
|
# 18.3: Accessing Bitcoind with NodeJS
|
||||||
|
|
||||||
> **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
|
||||||
|
|
||||||
## Set Up Node.js
|
## Set Up Node.js
|
||||||
|
|
||||||
BCRPC is built on node.js. Thus, you'll first need to install the `node.js` and `npm` (node package manager) packages for your system.
|
BCRPC is built on node.js. Thus, you'll first need to install the `node.js` and `npm` (node package manager) packages for your system.
|
||||||
|
|
||||||
If you're using a Ubuntu machine, you can run the following commands to get a new version of `node.js` (as opposed to the horribly out-of-date version in the Ubuntu package system).
|
If you're using a Ubuntu machine, you can run the following commands to get a new version of `node.js` (as opposed to the horribly out-of-date version in the Ubuntu package system).
|
||||||
```
|
|
||||||
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
|
|
||||||
sudo apt-get install -y nodejs
|
|
||||||
sudo apt-get install mocha -g
|
|
||||||
```
|
|
||||||
### Set Up BCRPC
|
|
||||||
|
|
||||||
Create a new node.js project and install BCRPC via NPM.
|
|
||||||
```
|
```
|
||||||
$ mkdir myproject
|
curl -sL https://deb.nodesource.com/setup_14.x | sudo bash -
|
||||||
$ cd myproject
|
sudo apt-get install -y nodejs
|
||||||
$ npm init
|
sudo npm install mocha -g
|
||||||
[...]
|
|
||||||
$ npm install --save bcrpc
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Test BCRPC
|
### Test BCRPC
|
||||||
|
|
||||||
To test the BCRPC package, you must first set environmental variables for your rpcuser and rpcpassword. As noted in [§12.1: Accessing Bitcoind with Curl](12_1_Accessing_Bitcoind_with_Curl.md), these come from `~/.bitcoin/bitcoin.conf`.
|
Clone the BCRPC package from GitHub and install its dependencies.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git clone https://github.com/dgarage/bcrpc.git
|
||||||
|
$ cd bcrpc
|
||||||
|
$ npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
To test the BCRPC package, you must first set environmental variables for your rpcuser and rpcpassword. As noted in [§12.1: Accessing Bitcoind with Curl](12_1_Accessing_Bitcoind_with_Curl.md), these come from `~/.bitcoin/bitcoin.conf`. You must also set the RPC port to 18332 which should be correct for the standard testnet setup described in these documents.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ export BITCOIND_USER=bitcoinrpc
|
$ export BITCOIND_USER=bitcoinrpc
|
||||||
$ export BITCOIND_PASS=d8340efbcd34e312044c8431c59c792c
|
$ export BITCOIND_PASS=d8340efbcd34e312044c8431c59c792c
|
||||||
|
$ export BITCOIND_PORT=18332
|
||||||
```
|
```
|
||||||
|
|
||||||
> **WARNING:** Obviously, you'd never put set your password in an environmental variable in a production environment.
|
> **WARNING:** Obviously, you'd never put set your password in an environmental variable in a production environment.
|
||||||
|
|
||||||
|
> **MAINNET VS TESTNET:** The port would be 8332 for a mainnet setup.
|
||||||
|
|
||||||
You can now verify everything is working correctly:
|
You can now verify everything is working correctly:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ npm test
|
$ npm test
|
||||||
|
|
||||||
> bcrpc@0.0.5 test /home/user1/bcrpc-master
|
> bcrpc@0.2.2 test /home/user1/bcrpc
|
||||||
> mocha tests.js
|
> mocha tests.js
|
||||||
|
|
||||||
BitcoinD
|
BitcoinD
|
||||||
@ -46,16 +50,226 @@ $ npm test
|
|||||||
|
|
||||||
2 passing (36ms)
|
2 passing (36ms)
|
||||||
```
|
```
|
||||||
Congratulations, you now have a Bitcoin-ready RPC wrapper for node.js.
|
|
||||||
|
|
||||||
## Manipulate Your Wallet
|
Congratulations, you now have a Bitcoin-ready RPC wrapper for Node.js working with your Bitcoin setup.
|
||||||
|
|
||||||
### Look Up Addresses
|
### Set Up BCRPC
|
||||||
|
|
||||||
### Look Up Funds
|
Leave the BCRPC directory and create a new Node.js project with BCRPC installed via npm.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cd ..
|
||||||
|
$ mkdir myproject
|
||||||
|
$ cd myproject
|
||||||
|
$ npm init
|
||||||
|
[continue with default options]
|
||||||
|
$ npm install bcrpc
|
||||||
|
```
|
||||||
|
|
||||||
|
In this ```myproject``` directory, create a file called ```server.js``` in which the JavaScript code can be executed. Enter the following at the top of the file to connect to your Bitcoin node:
|
||||||
|
|
||||||
|
```
|
||||||
|
const RpcAgent = require('bcrpc');
|
||||||
|
agent = new RpcAgent({port: 18332, user: 'bitcoinrpc', pass: 'd8340efbcd34e312044c8431c59c792c'});
|
||||||
|
```
|
||||||
|
|
||||||
|
Now we can start writing programs in JavaScript that access Bitcoind. You can use the same RPC commands you would usually use via ```bitcoin-cli```, except they need to be in camelCase. For example, ```getblockhash``` would be ```getBlockHash``` instead.
|
||||||
|
|
||||||
|
### Print the Newest Block
|
||||||
|
|
||||||
|
To print the newest block number along with its hash, we can first call ```getBlockCount```, print it, then pass the block number to ```getBlockHash``` and then print the latest hash. Add this code to ```server.js```:
|
||||||
|
|
||||||
|
```
|
||||||
|
agent.getBlockCount(function (err, blockCount) {
|
||||||
|
if (err)
|
||||||
|
throw Error(JSON.stringify(err));
|
||||||
|
console.log(blockCount.result);
|
||||||
|
agent.getBlockHash(blockCount.result, function (err, hash) {
|
||||||
|
if (err)
|
||||||
|
throw Error(JSON.stringify(err));
|
||||||
|
console.log(hash.result);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
You can run it with ```$ node server.js```. You should get an output similar to this:
|
||||||
|
|
||||||
|
```
|
||||||
|
1773373
|
||||||
|
0000000000000083d29c524d4cfc257adfab8fa9b6f0d207d1d0f1b63e1de11e
|
||||||
|
```
|
||||||
|
|
||||||
|
The BCRPC functions can accept inputs. For example, ```getBlockHash``` takes ```blockCount.result``` as an input. The result of the BCRPC functions is a JSON object containing information about any errors and the id of the request. When accessing our result, we add ```.result``` to the end of it to specify that we are interested in the actual result, not information about errors. This is what output of the above example would look like if we replaced ```console.log(blockCount.result);``` and ```console.log(hash.result);``` with ```console.log(blockCount);``` and ```console.log(hash);```, respectively:
|
||||||
|
|
||||||
|
```
|
||||||
|
{ result: 1774686, error: null, id: null }
|
||||||
|
{
|
||||||
|
result: '00000000000000d980c495a2b7addf09bb0a9c78b5b199c8e965ee54753fa5da',
|
||||||
|
error: null,
|
||||||
|
id: null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Look Up Your Wallet
|
||||||
|
|
||||||
|
You can also lookup your wallet and view your balance, transaction count et cetera:
|
||||||
|
|
||||||
|
```
|
||||||
|
agent.getWalletInfo(function (err, walletInfo) {
|
||||||
|
if (err)
|
||||||
|
throw Error(JSON.stringify(err));
|
||||||
|
console.log(walletInfo.result);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
When you run it, you should get an output similar to this:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
walletname: '',
|
||||||
|
walletversion: 169900,
|
||||||
|
balance: 0.0011,
|
||||||
|
unconfirmed_balance: 0,
|
||||||
|
immature_balance: 0,
|
||||||
|
txcount: 2,
|
||||||
|
keypoololdest: 1591844503,
|
||||||
|
keypoolsize: 999,
|
||||||
|
keypoolsize_hd_internal: 1000,
|
||||||
|
paytxfee: 0,
|
||||||
|
hdseedid: '6b9ccb7d96c45a3ca407a3f3b0e9b42501f05c49',
|
||||||
|
private_keys_enabled: true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Instead of printing all the details associated with your wallet, you can print specific information such as your balance. Since a JSON object is being accessed, this can be done by changing the line ```console.log(walletInfo.result);``` to ```console.log(walletInfo.result.balance);```:
|
||||||
|
|
||||||
|
```
|
||||||
|
0.0011
|
||||||
|
```
|
||||||
|
|
||||||
### Create an Address
|
### Create an Address
|
||||||
|
|
||||||
## Create a Transaction
|
We can pass additional arguments to RPC commands as well. Here is an example for generating a new legacy address, with the ```-addresstype``` flag.
|
||||||
|
|
||||||
|
```
|
||||||
|
agent.getNewAddress('-addresstype', 'legacy', function (err, newAddress) {
|
||||||
|
if (err)
|
||||||
|
throw Error(JSON.stringify(err));
|
||||||
|
console.log(newAddress.result);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
```
|
||||||
|
mtGPcBvRPZFEHo2YX8un9qqPBydhG82uuZ
|
||||||
|
```
|
||||||
|
|
||||||
|
This example shows how we can use the same flags as ```bitcoin-cli``` in BCRPC. The example above would look like this from the command line:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ bitcoin-cli getnewaddress -addresstype legacy
|
||||||
|
mtGPcBvRPZFEHo2YX8un9qqPBydhG82uuZ
|
||||||
|
```
|
||||||
|
|
||||||
|
In BCRPC ```getnewaddress``` is in camelCase (```getNewAddress```) and the that flags are normally separated by spaces are instead put in strings and separated by commas.
|
||||||
|
|
||||||
|
### List transactions
|
||||||
|
|
||||||
|
We can list our previous transactions and view information about transactions such as amount and number of confirmations:
|
||||||
|
|
||||||
|
```
|
||||||
|
agent.listTransactions(function (err, transactions) {
|
||||||
|
if (err)
|
||||||
|
throw Error(JSON.stringify(err));
|
||||||
|
console.log(transactions.result);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
We can also examine a transaction based on its txid:
|
||||||
|
|
||||||
|
```
|
||||||
|
agent.getTransaction('1661ce322c128e053b8ea8fcc22d17df680d2052983980e2281d692b9b4ab7df', function (err, transaction) {
|
||||||
|
if (err)
|
||||||
|
throw Error(JSON.stringify(err));
|
||||||
|
console.log(transaction.result);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
You should get an output similar to this:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
amount: 0.001,
|
||||||
|
confirmations: 4776,
|
||||||
|
blockhash: '000000006628870b0a8a66abea9cf0d4e815c491f079e3fa9e658a87b5dc863a',
|
||||||
|
blockindex: 117,
|
||||||
|
blocktime: 1591857418,
|
||||||
|
txid: '1661ce322c128e053b8ea8fcc22d17df680d2052983980e2281d692b9b4ab7df',
|
||||||
|
walletconflicts: [],
|
||||||
|
time: 1591857343,
|
||||||
|
timereceived: 1591857343,
|
||||||
|
'bip125-replaceable': 'no',
|
||||||
|
details: [
|
||||||
|
{
|
||||||
|
address: 'mpGpCMX6SuUimDZKiVViuhd7EGyVxkNnha',
|
||||||
|
category: 'receive',
|
||||||
|
amount: 0.001,
|
||||||
|
label: '',
|
||||||
|
vout: 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
hex: '02000000000101e9e8c3bd057d54e73baadc60c166860163b0e7aa60cab33a03e89fb44321f8d5010000001716001435c2aa3fc09ea53c3e23925c5b2e93b9119b2568feffffff02a0860100000000001976a914600c8c6a4abb0a502ea4de01681fe4fa1ca7800688ac65ec1c000000000017a91425b920efb2fde1a0277d3df11d0fd7249e17cf8587024730440220403a863d312946aae3f3ef0a57206197bc67f71536fb5f4b9ca71a7e226b6dc50220329646cf786cfef79d60de3ef54f702ab1073694022f0618731902d926918c3e012103e6feac9d7a8ad1ac6b36fb4c91c1c9f7fff1e7f63f0340e5253a0e4478b7b13f41fd1a00'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Get Amount Received by an Address
|
||||||
|
|
||||||
|
A useful function when accepting Bitcoin is checking the received Bitcoin of a specific address in your wallet. For example, if you were running an online store accepting Bitcoin, for each payment from a customer, you would generate a new address (as we did with ```getNewAddress``` function), show that address to the customer, then check the balance of the address after some time, to make sure the correct amount has been received:
|
||||||
|
|
||||||
|
```
|
||||||
|
agent.getReceivedByAddress('mpGpCMX6SuUimDZKiVViuhd7EGyVxkNnha', function (err, addressInfo) {
|
||||||
|
if (err)
|
||||||
|
throw Error(JSON.stringify(err));
|
||||||
|
console.log(addressInfo.result);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
By default this functions checks the transactions that have been confirmed once, however we can increase this to a higher number such as 6:
|
||||||
|
|
||||||
|
```
|
||||||
|
agent.getReceivedByAddress('mpGpCMX6SuUimDZKiVViuhd7EGyVxkNnha', 6, function (err, addressInfo) {
|
||||||
|
if (err)
|
||||||
|
throw Error(JSON.stringify(err));
|
||||||
|
console.log(addressInfo.result);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```
|
||||||
|
0.0011
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sending coins
|
||||||
|
|
||||||
|
We can send coins to an address using the ```sendToAddress``` function:
|
||||||
|
|
||||||
|
```
|
||||||
|
agent.sendToAddress('n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi', 0.00001, function(err, txid) {
|
||||||
|
if (err)
|
||||||
|
throw Error(JSON.stringify(err));
|
||||||
|
console.log(txid.result);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
This should print the txid of the transaction:
|
||||||
|
|
||||||
|
```
|
||||||
|
6172d60d154cd4bbb5b6adeaffa191866f3904dd3f525c7a079154aea906b723
|
||||||
|
```
|
||||||
|
|
||||||
|
We can also use the ```getTransaction``` function to view how many confirmations it has, what fee we paid, et cetera.
|
||||||
|
|
||||||
## Summary: Accessing Bitcoind with Node
|
## Summary: Accessing Bitcoind with Node
|
||||||
|
|
||||||
|
With BCRPC we can do all the commands available through ```bitcoin-cli```, in JavaScript. The [BCRPC README](https://github.com/dgarage/bcrpc) has some examples which use promises (the examples in this document use callbacks). The [JavaScript behind it](https://github.com/dgarage/bcrpc/blob/master/index.js) is short and readable. Based on these examples you should be able to incorporate Bitcoin in a Node.js project and do things like sending and receiving coins.
|
||||||
|
@ -2,15 +2,15 @@
|
|||||||
|
|
||||||
> NOTE: This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
> NOTE: This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
||||||
|
|
||||||
Interacting with the bitcoind directly and using command-line curl can be simple if you understand how it works, but there's a project [Python-BitcoinRPC](https://github.com/jgarzik/python-bitcoinrpc) that provides the functionality in a Python-API level, making it even easier to interact with your Bitcoin Server.
|
Interacting with the bitcoind directly and using command-line curl can get simple if you understand how it works, but there's a project [Python-BitcoinRPC](https://github.com/jgarzik/python-bitcoinrpc) that provides the functionality in a Python-API level, making it even easier to interact with your Bitcoin Server.
|
||||||
|
|
||||||
## Setup Python
|
## Setup Python
|
||||||
|
|
||||||
We are going to be using python 3 for this chapter. Debian 10 installs python3 and if you have bitcoin-core installed, you should have it installed already. Let's check, run:
|
We are going to be using python 3 for this chapter. Debian 10 installs python3 and if you have bitcoin-core installed, you should have it installed. Let's check, run:
|
||||||
|
|
||||||
`$ python3 --version`
|
`$ python3 --version`
|
||||||
|
|
||||||
If it returns the version number, then you have python3 installed. If it returns nothing or an error, then we will build python from source with the following steps:
|
If it returns the version number then you have python3 installed. If it returns nothing or an error then we will build python from source, do:
|
||||||
|
|
||||||
#### 1. Install dependencies:
|
#### 1. Install dependencies:
|
||||||
```sh
|
```sh
|
||||||
|
395
18_5_Accessing_Bitcoind_with_Rust.md
Normal file
395
18_5_Accessing_Bitcoind_with_Rust.md
Normal file
@ -0,0 +1,395 @@
|
|||||||
|
# 18.5: Accessing Bitcoind with Rust
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
We'll need `Rust` and `Cargo`. Installing them is easy:
|
||||||
|
|
||||||
|
```vim
|
||||||
|
$ curl https://sh.rustup.rs -sSf | sh
|
||||||
|
```
|
||||||
|
|
||||||
|
If everything goes well, we should see:
|
||||||
|
|
||||||
|
```vim
|
||||||
|
Rust is installed now. Great!
|
||||||
|
```
|
||||||
|
|
||||||
|
To set `Bitcoin Regtest` network up and allow communication with our Rust program we
|
||||||
|
will be using the following `bitcoind` configuration in `bitcoin.conf`
|
||||||
|
|
||||||
|
```vim
|
||||||
|
regtest=1
|
||||||
|
server=1
|
||||||
|
rpcuser=bitcoin
|
||||||
|
rpcpassword=password
|
||||||
|
[test]
|
||||||
|
rpcport=18443
|
||||||
|
```
|
||||||
|
|
||||||
|
> Note: Never use a simple password like that when on Bitcoin Mainnet!
|
||||||
|
|
||||||
|
### Create a New Project
|
||||||
|
|
||||||
|
We create a new project with `cargo new btc_test`:
|
||||||
|
|
||||||
|
```vim
|
||||||
|
gorazd@gorazd-MS-7C37:~/Projects/BlockchainCommons$ cargo new btc_test
|
||||||
|
Created binary (application) `btc_test` package
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's move into the newly created project `btc_test`. We notice a "hello world" example
|
||||||
|
with the source code in `src/main.rs` and a `Cargo.toml` file. Let's run it with `cargo run`:
|
||||||
|
|
||||||
|
```vim
|
||||||
|
gorazd@gorazd-MS-7C37:~/Projects/BlockchainCommons/btc_test$ cargo run
|
||||||
|
Compiling btc_test v0.1.0 (/home/gorazd/Projects/BlockchainCommons/btc_test)
|
||||||
|
Finished dev [unoptimized + debuginfo] target(s) in 0.14s
|
||||||
|
Running `target/debug/btc_test`
|
||||||
|
Hello, world!
|
||||||
|
```
|
||||||
|
|
||||||
|
> Note: if you run into error “linker ‘cc’ not found”, you'll have to install a
|
||||||
|
C compiler. If on Linux, go ahead and install the [development tools](https://www.ostechnix.com/install-development-tools-linux/).
|
||||||
|
|
||||||
|
|
||||||
|
We will use `bitcoincore-rpc` crate (library), therefore we add it to our `Cargo.toml`
|
||||||
|
under section `dependencies` like so:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
[dependencies]
|
||||||
|
bitcoincore-rpc = "0.11.0"
|
||||||
|
```
|
||||||
|
|
||||||
|
Running our example again will install our crate and
|
||||||
|
its dependencies.
|
||||||
|
|
||||||
|
```vim
|
||||||
|
gorazd@gorazd-MS-7C37:~/Projects/BlockchainCommons/btc_test$ cargo run
|
||||||
|
Updating crates.io index
|
||||||
|
...
|
||||||
|
Compiling bitcoin v0.23.0
|
||||||
|
Compiling bitcoincore-rpc-json v0.11.0
|
||||||
|
Compiling bitcoincore-rpc v0.11.0
|
||||||
|
Compiling btc_test v0.1.0 (/home/gorazd/Projects/BlockchainCommons/btc_test)
|
||||||
|
Finished dev [unoptimized + debuginfo] target(s) in 23.56s
|
||||||
|
Running `target/debug/btc_test`
|
||||||
|
Hello, world!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build Your Connection
|
||||||
|
|
||||||
|
Let us create a Bitcoin `RPC client` and modify the `main.rs`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use bitcoincore_rpc::{Auth, Client};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let rpc = Client::new(
|
||||||
|
"http://localhost:18443".to_string(),
|
||||||
|
Auth::UserPass("bitcoin".to_string(), "password".to_string()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`Cargo run` should successfully compile and run the example with one warning
|
||||||
|
`warning: unused variable: rpc`
|
||||||
|
|
||||||
|
|
||||||
|
### Make an RPC Call
|
||||||
|
|
||||||
|
This is a simple RPC call without arguments:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let mining_info = rpc.get_mining_info().unwrap();
|
||||||
|
println!("{:#?}", mining_info);
|
||||||
|
```
|
||||||
|
|
||||||
|
The compiler will tell us to include traits into scope. So lets add them:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use bitcoincore_rpc::{Auth, Client, RpcApi};
|
||||||
|
```
|
||||||
|
|
||||||
|
If our properly configured `bitcoind` is running, executing our example should
|
||||||
|
result in:
|
||||||
|
|
||||||
|
```vim
|
||||||
|
gorazd@gorazd-MS-7C37:~/Projects/BlockchainCommons/btc_test$ cargo run
|
||||||
|
Compiling btc_test v0.1.0 (/home/gorazd/Projects/BlockchainCommons/btc_test)
|
||||||
|
Finished dev [unoptimized + debuginfo] target(s) in 0.80s
|
||||||
|
Running `target/debug/btc_test`
|
||||||
|
GetMiningInfoResult {
|
||||||
|
blocks: 5167,
|
||||||
|
current_block_weight: Some(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
current_block_tx: Some(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
difficulty: 0.00000000046565423739069247,
|
||||||
|
network_hash_ps: 1.764705882352941,
|
||||||
|
pooled_tx: 2,
|
||||||
|
chain: "regtest",
|
||||||
|
warnings: "",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If we wanted we could close the connection:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let _ = rpc.stop().unwrap();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Manipulate Your Wallet
|
||||||
|
|
||||||
|
### Look Up Addresses
|
||||||
|
|
||||||
|
Here we will make our first call with an argument. To see the type of an argument,
|
||||||
|
we want to look at the function definition:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn get_address_info(&self, address: &Address) -> Result<json::GetAddressInfoResult> {
|
||||||
|
self.call("getaddressinfo", &[address.to_string().into()])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We see that our argument is of type `Address` and that it will be borrowed. Further,
|
||||||
|
looking at the structure `Address`, we notice a convenient `trait` implemented which
|
||||||
|
allows us to create an `Address` out of a string:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
impl FromStr for Address {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Address, Error> {
|
||||||
|
```
|
||||||
|
|
||||||
|
Now that we now what structure and trait we are dealing with, we bring them into
|
||||||
|
scope
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use bitcoincore_rpc::bitcoin::Address;
|
||||||
|
use std::str::FromStr;
|
||||||
|
```
|
||||||
|
|
||||||
|
so we can use them:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let addr = Address::from_str("bcrt1qanga5jxx845q82h9qgjfuedps92lktqv073qct").unwrap();
|
||||||
|
let addr_info = rpc.get_address_info(&addr).unwrap();
|
||||||
|
println!("{:?}", addr_info);
|
||||||
|
```
|
||||||
|
|
||||||
|
Running our program results in:
|
||||||
|
|
||||||
|
```vim
|
||||||
|
GetAddressInfoResult { address: bcrt1qanga5jxx845q82h9qgjfuedps92lktqv073qct, script_pub_key: Script(OP_0 OP_PUSHBYTES_20 ecd1da48c63d6803aae502249e65a18155fb2c0c), is_mine: Some(true), is_watchonly: Some(false), is_script: Some(false), is_witness: Some(true), witness_version: Some(0), witness_program: Some([236, 209, 218, 72, 198, 61, 104, 3, 170, 229, 2, 36, 158, 101, 161, 129, 85, 251, 44, 12]), script: None, hex: None, pubkeys: None, n_signatures_required: None, pubkey: Some(PublicKey { compressed: true, key: PublicKey(f895d610ab1ceddfd87814b1f7a911fee1135a9347d4fd1754a06ddf84757c5c527a90804949b025d7272bef4d58a1324c18d7a8f6b7ffa949447bcb6a225e6e) }), embedded: None, is_compressed: None, label: "lbl", timestamp: Some(1582063890), hd_key_path: Some(m/0'/0'/99'), hd_seed_id: Some(00b332a133c03c4e613f0106dc814bcc79af60ff), labels: [GetAddressInfoResultLabel { name: "lbl", purpose: Receive }] }
|
||||||
|
```
|
||||||
|
|
||||||
|
> Note: this call doesn't work with recent versions of Bitcoin Core due to the
|
||||||
|
crate not addressing the latest API changes in Bitcoin Core.
|
||||||
|
We expect it to be solved in the next crate's release.
|
||||||
|
|
||||||
|
### Look Up Funds
|
||||||
|
|
||||||
|
We can look up our funds without optional arguments like so:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let balance = rpc.get_balance(None, None).unwrap();
|
||||||
|
println!("Balance: {:?} BTC", balance.as_btc());
|
||||||
|
```
|
||||||
|
|
||||||
|
```vim
|
||||||
|
Balance: 3433.71692741 BTC
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create an Address
|
||||||
|
|
||||||
|
Here is an example of calling an RPC method with the optional arguments specified, i.e.
|
||||||
|
a label and an address type:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Generate a new address
|
||||||
|
let myaddress = rpc
|
||||||
|
.get_new_address(Option::Some("BlockchainCommons"), Option::Some(json::AddressType::Bech32))
|
||||||
|
.unwrap();
|
||||||
|
println!("address: {:?}", myaddress);
|
||||||
|
```
|
||||||
|
|
||||||
|
If we have inspected our function's definition we bring the missing things into
|
||||||
|
scope. Otherwise the compiler will hint us to do so:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use bitcoincore_rpc::{json, Auth, Client, RpcApi};
|
||||||
|
```
|
||||||
|
|
||||||
|
Program execution results in:
|
||||||
|
|
||||||
|
```vim
|
||||||
|
address: bcrt1q0y0dk70lut5l3y4f0fe52am23egfmr63dejy9r
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, we would like to have some bitcoins to our newly generated address. Since
|
||||||
|
we are on the `Regtest` network we can generate them ourselves:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Generate 101 blocks to our address
|
||||||
|
let _ = rpc.generate_to_address(101, &myaddress);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create a Transaction
|
||||||
|
|
||||||
|
First, we list unspent transactions. Let's look at those with at least 3 BTC and take the first one:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let unspent = rpc
|
||||||
|
.list_unspent(
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Option::Some(json::ListUnspentQueryOptions {
|
||||||
|
minimum_amount: Option::Some(Amount::from_btc(3.0).unwrap()),
|
||||||
|
maximum_amount: None,
|
||||||
|
maximum_count: None,
|
||||||
|
minimum_sum_amount: None,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let selected_tx = &unspent[0];
|
||||||
|
|
||||||
|
println!("selected unspent transaction: {:#?}", selected_tx);
|
||||||
|
```
|
||||||
|
Here it is:
|
||||||
|
|
||||||
|
```vim
|
||||||
|
selected unspent transaction: ListUnspentResultEntry {
|
||||||
|
txid: 34e283eb5b52c66aba9766bdda46eb038bc1138e992b593c22f7cbf1d2e9ba10,
|
||||||
|
vout: 0,
|
||||||
|
address: Some(
|
||||||
|
bcrt1q7lju6c0ynwerch0te4saxwxgm70ltd3lr9vj6l,
|
||||||
|
),
|
||||||
|
label: Some(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
redeem_script: None,
|
||||||
|
witness_script: None,
|
||||||
|
script_pub_key: Script(OP_0 OP_PUSHBYTES_20 f7e5cd61e49bb23c5debcd61d338c8df9ff5b63f),
|
||||||
|
amount: Amount(625000000 satoshi),
|
||||||
|
confirmations: 4691,
|
||||||
|
spendable: true,
|
||||||
|
solvable: true,
|
||||||
|
descriptor: None,
|
||||||
|
safe: true,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This will require to bring another structure into scope:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use bitcoincore_rpc::bitcoin::{Address, Amount};
|
||||||
|
```
|
||||||
|
|
||||||
|
We can now populate some variables: the available amount and
|
||||||
|
the utxo, the recipient's address and the amount we want to send.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let unspent_amount = selected_tx.amount;
|
||||||
|
|
||||||
|
let selected_utxos = json::CreateRawTransactionInput {
|
||||||
|
txid: selected_tx.txid,
|
||||||
|
vout: selected_tx.vout,
|
||||||
|
sequence: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let recipient = Address::from_str("bcrt1q6rhpng9evdsfnn833a4f4vej0asu6dk5srld6x").unwrap();
|
||||||
|
println!("recipient: {:?}", recipient);
|
||||||
|
|
||||||
|
// send all bitcoin in the UTXO except a minor value which will be paid to miners
|
||||||
|
let amount = unspent_amount - Amount::from_btc(0.00001).unwrap();
|
||||||
|
|
||||||
|
let mut output = HashMap::new();
|
||||||
|
output.insert(
|
||||||
|
"bcrt1q6rhpng9evdsfnn833a4f4vej0asu6dk5srld6x".to_string(),
|
||||||
|
amount,
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Another trait is necessary for the output variable: HashMap. It allows us to store
|
||||||
|
values by key which we need to represent `{address : amount}` information.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use std::collections::HashMap;
|
||||||
|
```
|
||||||
|
|
||||||
|
We are ready to create a raw transaction:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let unsigned_tx = rpc
|
||||||
|
.create_raw_transaction(&[selected_utxos], &output, None, None)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!("unsigned tx {:#?}", unsigned_tx);
|
||||||
|
```
|
||||||
|
|
||||||
|
Here it is:
|
||||||
|
|
||||||
|
```vim
|
||||||
|
unsigned tx Transaction {
|
||||||
|
version: 2,
|
||||||
|
lock_time: 0,
|
||||||
|
input: [
|
||||||
|
TxIn {
|
||||||
|
previous_output: OutPoint {
|
||||||
|
txid: 34e283eb5b52c66aba9766bdda46eb038bc1138e992b593c22f7cbf1d2e9ba10,
|
||||||
|
vout: 0,
|
||||||
|
},
|
||||||
|
script_sig: Script(),
|
||||||
|
sequence: 4294967295,
|
||||||
|
witness: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
output: [
|
||||||
|
TxOut {
|
||||||
|
value: 624999000,
|
||||||
|
script_pubkey: Script(OP_0 OP_PUSHBYTES_20 d0ee19a0b9636099ccf18f6a9ab3327f61cd36d4),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, we can sign and broadcast our transaction:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// sign transaction
|
||||||
|
let signed_tx = rpc
|
||||||
|
.sign_raw_transaction_with_wallet(&unsigned_tx, None, None)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!("singed tx {:?}", signed_tx.transaction().unwrap());
|
||||||
|
|
||||||
|
// broadcast transaction
|
||||||
|
let txid_sent = rpc
|
||||||
|
.send_raw_transaction(&signed_tx.transaction().unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!("{:?}", txid_sent);
|
||||||
|
```
|
||||||
|
|
||||||
|
```vim
|
||||||
|
singed tx Transaction { version: 2, lock_time: 0, input: [TxIn { previous_output: OutPoint { txid: 34e283eb5b52c66aba9766bdda46eb038bc1138e992b593c22f7cbf1d2e9ba10, vout: 0 }, script_sig: Script(), sequence: 4294967295, witness: [[48, 68, 2, 32, 85, 113, 140, 197, 142, 140, 122, 26, 174, 71, 94, 152, 76, 104, 5, 111, 113, 192, 179, 1, 58, 6, 27, 141, 18, 50, 217, 53, 154, 26, 5, 98, 2, 32, 53, 148, 139, 57, 234, 151, 71, 149, 134, 202, 160, 136, 15, 144, 103, 232, 134, 37, 136, 184, 117, 159, 235, 92, 59, 102, 197, 213, 67, 64, 89, 207, 1], [3, 4, 197, 157, 36, 136, 177, 169, 182, 219, 121, 187, 251, 153, 207, 165, 173, 117, 142, 93, 181, 107, 185, 97, 10, 168, 210, 148, 67, 127, 246, 229, 12]] }], output: [TxOut { value: 624999000, script_pubkey: Script(OP_0 OP_PUSHBYTES_20 d0ee19a0b9636099ccf18f6a9ab3327f61cd36d4) }] }
|
||||||
|
5d2f1b7c6fc29967d820532c46200b35f62b6e6f8da614ae86922c20167f6d0e
|
||||||
|
```
|
||||||
|
|
||||||
|
## For More Information
|
||||||
|
|
||||||
|
You can now mine a block and try to see for yourself if the last transaction is really in the block.
|
||||||
|
If you need help look at the crate's [documentation](https://crates.io/crates/bitcoincore-rpc) or run some tests in its [repository](https://github.com/rust-bitcoin/rust-bitcoincore-rpc).
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
We have shown how to access `bitcoind` in `Rust` and send a transaction
|
||||||
|
on the `Bitcoin Regtest Network` explaining all the steps required.
|
74
CLA-signed/CLA.ChristopherA.F8D36C91357405ED.asc
Normal file
74
CLA-signed/CLA.ChristopherA.F8D36C91357405ED.asc
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
-----BEGIN PGP SIGNED MESSAGE-----
|
||||||
|
Hash: SHA512
|
||||||
|
|
||||||
|
# Contributor License Agreement
|
||||||
|
|
||||||
|
Version 1.0
|
||||||
|
|
||||||
|
Name: Christopher Allen
|
||||||
|
|
||||||
|
E-Mail: ChristopherA@LifeWithAlacrity.com
|
||||||
|
|
||||||
|
Legal Jurisdiction: Wyoming, United States of America
|
||||||
|
|
||||||
|
Project: https://github.com/BlockchainCommons/Learning-Bitcoin-from-the-Command-Line
|
||||||
|
|
||||||
|
Date: 05/19/2020
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
This agreement gives Blockchain Commons, LLC the permission it needs in order to accept my contributions into its open software project and to manage the intellectual property in that project over time.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
I hereby license Blockchain Commons, LLC to:
|
||||||
|
|
||||||
|
1. do anything with my contributions that would otherwise infringe my copyright in them
|
||||||
|
|
||||||
|
2. do anything with my contributions that would otherwise infringe patents that I can or become able to license
|
||||||
|
|
||||||
|
3. sublicense these rights to others on any terms they like
|
||||||
|
|
||||||
|
## Reliability
|
||||||
|
|
||||||
|
I understand that Blockchain Commons will rely on this license. I may not revoke this license.
|
||||||
|
|
||||||
|
## Awareness
|
||||||
|
|
||||||
|
I promise that I am familiar with legal rules, like ["work made for hire" rules](http://worksmadeforhire.com), that can give employers and clients ownership of intellectual property in work that I do. I am also aware that legal agreements I might sign, like confidential information and invention assignment agreements, will usually give ownership of intellectual property in my work to employers, clients, and companies that I found. If someone else owns intellectual property in my work, I need their permission to license it.
|
||||||
|
|
||||||
|
## Copyright Guarantee
|
||||||
|
|
||||||
|
I promise not to offer contributions to the project that contain copyrighted work that I do not have legally binding permission to contribute under these terms. When I offer a contribution with permission, I promise to document in the contribution who owns copyright in what work, and how they gave permission to contribute it. If I later become aware that one of my contributions may have copyrighted work of others that I did not have permission to contribute, I will notify Blockchain Commons, in confidence, immediately.
|
||||||
|
|
||||||
|
## Patent Guarantee
|
||||||
|
|
||||||
|
I promise not to offer contributions to the project that I know infringe patents of others that I do not have permission to contribute under these terms.
|
||||||
|
|
||||||
|
## Open Source Guarantee
|
||||||
|
|
||||||
|
I promise not to offer contributions that contain or depend on the work of others, unless that work is available under a license that [Blue Oak Council rates bronze or better](https://blueoakconcil.org/list), such as the MIT License, two- or three-clause BSD License, the Apache License Version 2.0, or the Blue Oak Model License 1.0.0. When I offer a contribution containing or depending on others' work, I promise to document in the contribution who licenses that work, along with copies of their license terms.
|
||||||
|
|
||||||
|
## Disclaimers
|
||||||
|
|
||||||
|
***As far as the law allows, my contributions come as is, without any warranty or condition. Other than under [Copyright Guarantee](#copyright-guarantee), [Patent Guarantee](#patent-guarantee), or [Open Source Guarantee](#open-source-guarantee), I will not be liable to anyone for any damages related to my contributions or this contributor license agreement, under any kind of legal claim.***
|
||||||
|
|
||||||
|
- - ---
|
||||||
|
|
||||||
|
To sign this Contributor License Agreement, fill in `$name`, `$email`, and `$date` above. Then sign using GPG using the following command `gpg --armor --clearsign --output ./signed-cla/CLA.YOURGITHUBNAME.YOURGPGFINGERPRINT.asc CLA.md`, then either submit your signed Contributor License Agreement to this repo as a GPG signed Pull Request or email it to [ChristopherA@BlockchainCommons.com](mailto:ChristopherA@BlockchainCommons.com).
|
||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
|
||||||
|
iQIzBAEBCgAdFiEE/f4UpU7LMPxdInTv+NNskTV0Be0FAl7ue+AACgkQ+NNskTV0
|
||||||
|
Be1G+Q//WEORFEFVG0VTFFd5yDgEs+pMNtb0ig2VXD8VB5lN9QntTXjgoVbfsdL7
|
||||||
|
LzIVtS/rNnoxZ+e8wvKq67uG82RMm6NoCJPI6SBhF/cWdmFWIjnPL/vU/t1CJx9/
|
||||||
|
XI52qy8xE45iNgtNncETQFoAila+0oDufmUwKTsLb8ioZ1B1NSWcUAoWaYvQkUxz
|
||||||
|
VPuSZzCzYsN+m+qwjzxVXQjqGJ4Ya5o4GpTNq32R7ajHL9kVJNf6s48mFEN6KsG5
|
||||||
|
30pUeS9GLJH98B8jUDTJAd/Z06o+ID5Eh2dgXdBa0AcN0GeSXFp/0UEqyQcmAUFk
|
||||||
|
jn/mXS5ojBCFGguZ7mgBRo6QeHoQiAJp+bCep6Ar7X5+yMHbSi71Kk0FYSeF79xB
|
||||||
|
B7ZnIqztEal8X8UoqZmbti3m++IMcxQgVkQdcTYhES0bCtqYjewXWY5ipd16QSqh
|
||||||
|
22l49SrG7o4Sipsd+1GmBtnJCBU9CyCDDYyO52x9+V2hLN6KByyInv1YSltOR59O
|
||||||
|
0umgijDxEZ4IgiM7h6RzNIDeC1rqvHRg8oN/MprLSeU+wl49b6ijBCgfpV+n62Ht
|
||||||
|
JW//XZDfEcqIYfw5e5jTMCQAxtNijQ7W+8nBB3UOwbQRy7wC/g2mfID1GPPHou2F
|
||||||
|
OI9oM7zm4Uw0dG/VfEqvnMzMN5022gZYxsZQI00rPFIPvkYRLWY=
|
||||||
|
=zrSz
|
||||||
|
-----END PGP SIGNATURE-----
|
74
README.md
74
README.md
@ -4,22 +4,25 @@ Learning Bitcoin from the Command Line is a tutorial for working with Bitcoin (a
|
|||||||
|
|
||||||
> NOTE: This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for use.
|
> NOTE: This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for use.
|
||||||
|
|
||||||
## Table of Contents
|
|
||||||
|
|
||||||
_This tutorial assumes that you have some minimal background of how to use the command line interface. If not, there are many tutorials available, and I have one for Mac users at https://github.com/ChristopherA/intro-mac-command-line._
|
_This tutorial assumes that you have some minimal background of how to use the command line interface. If not, there are many tutorials available, and I have one for Mac users at https://github.com/ChristopherA/intro-mac-command-line._
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
* [0.0: Introduction to Programming with Bitcoin Core and Lightning](00_0_Introduction.md)
|
* [0.0: Introduction to Programming with Bitcoin Core and Lightning](00_0_Introduction.md)
|
||||||
|
|
||||||
### PART ONE: PREPARING FOR BITCOIN
|
### PART ONE: PREPARING FOR BITCOIN
|
||||||
|
|
||||||
|
**Status:** May need new concepts from 0.20.
|
||||||
|
|
||||||
* [1.0: Introducing Bitcoin](01_0_Introducing_Bitcoin.md)
|
* [1.0: Introducing Bitcoin](01_0_Introducing_Bitcoin.md)
|
||||||
* [2.0: Setting Up a Bitcoin-Core VPS](02_0_Setting_Up_a_Bitcoin-Core_VPS.md)
|
* [2.0: Setting Up a Bitcoin-Core VPS](02_0_Setting_Up_a_Bitcoin-Core_VPS.md)
|
||||||
* [2.1: Setting Up a Bitcoin-Core VPS by Hand](02_1_Setting_Up_a_Bitcoin-Core_VPS_by_Hand.md)
|
* [2.1: Setting Up a Bitcoin-Core VPS by Hand](02_1_Setting_Up_a_Bitcoin-Core_VPS_by_Hand.md)
|
||||||
* [2.2: Setting Up a Bitcoin-Core VPS with StackScript.md](02_2_Setting_Up_a_Bitcoin-Core_VPS_with_StackScript.md)
|
* [2.2: Setting Up a Bitcoin-Core VPS with Bitcoin Standup](02_2_Setting_Up_a_Bitcoin-Core_VPS_with_StackScript.md)
|
||||||
* [Linode_Bitcoin-Core_VPS_Setup.stackscript](02_2__Script_Linode_Setup.stackscript)
|
|
||||||
* [2.3: Setting Up a Bitcoin-Core Machine via Other Means](02_3_Setting_Up_Bitcoin_Core_Other.md)
|
* [2.3: Setting Up a Bitcoin-Core Machine via Other Means](02_3_Setting_Up_Bitcoin_Core_Other.md)
|
||||||
|
|
||||||
### PART TWO: BITCOIN-CLI
|
### PART TWO: USING BITCOIN-CLI
|
||||||
|
|
||||||
|
**Status:** The PSBT and HWI content (chapter 6) is new, and so still being written.
|
||||||
|
|
||||||
* [3.0: Understanding Your Bitcoin Setup](03_0_Understanding_Your_Bitcoin_Setup.md)
|
* [3.0: Understanding Your Bitcoin Setup](03_0_Understanding_Your_Bitcoin_Setup.md)
|
||||||
* [3.1: Verifying Your Bitcoin Setup](03_1_Verifying_Your_Bitcoin_Setup.md)
|
* [3.1: Verifying Your Bitcoin Setup](03_1_Verifying_Your_Bitcoin_Setup.md)
|
||||||
@ -27,28 +30,37 @@ _This tutorial assumes that you have some minimal background of how to use the c
|
|||||||
* [3.3: Setting Up Your Wallet](03_3_Setting_Up_Your_Wallet.md)
|
* [3.3: Setting Up Your Wallet](03_3_Setting_Up_Your_Wallet.md)
|
||||||
* [Interlude: Using Command-Line Variables](03_3__Interlude_Using_Command-Line_Variables.md)
|
* [Interlude: Using Command-Line Variables](03_3__Interlude_Using_Command-Line_Variables.md)
|
||||||
* [3.4: Receiving a Transaction](03_4_Receiving_a_Transaction.md)
|
* [3.4: Receiving a Transaction](03_4_Receiving_a_Transaction.md)
|
||||||
|
* [3.5: Understanding the Descriptor](03_5_Understanding_the_Descriptor.md)
|
||||||
|
|
||||||
* [4.0: Sending Bitcoin Transactions](04_0_Sending_Bitcoin_Transactions.md)
|
* [4.0: Sending Bitcoin Transactions](04_0_Sending_Bitcoin_Transactions.md)
|
||||||
* [4.1: Sending Coins the Easy Way](04_1_Sending_Coins_The_Easy_Way.md)
|
* [4.1: Sending Coins the Easy Way](04_1_Sending_Coins_The_Easy_Way.md)
|
||||||
* [4.2: Creating a Raw Transaction](04_2_Creating_a_Raw_Transaction.md)
|
* [4.2: Creating a Raw Transaction](04_2_Creating_a_Raw_Transaction.md)
|
||||||
* [Interlude: Using JQ](04_2__Interlude_Using_JQ.md)
|
* [Interlude: Using JQ](04_2__Interlude_Using_JQ.md)
|
||||||
* [4.3: Creating a Raw Transaction with Named Arguments](04_3_Creating_a_Raw_Transaction_with_Named_Arguments.md)
|
* [4.3: Creating a Raw Transaction with Named Arguments](04_3_Creating_a_Raw_Transaction_with_Named_Arguments.md)
|
||||||
* [4.4: Sending Coins with Raw Transactions](04_4_Sending_Coins_with_a_Raw_Transaction.md)
|
* [4.4: Sending Coins with Raw Transactions](04_4_Sending_Coins_with_a_Raw_Transaction.md)
|
||||||
* [Interlude: Using Curl](04_4__Interlude_Using_Curl.md) — Needs Rewrite for New Section
|
* [Interlude: Using Curl](04_4__Interlude_Using_Curl.md)
|
||||||
* [4.5: Sending Coins with Automated Raw Transactions](04_5_Sending_Coins_with_Automated_Raw_Transactions.md)
|
* [4.5: Sending Coins with Automated Raw Transactions](04_5_Sending_Coins_with_Automated_Raw_Transactions.md)
|
||||||
* [4.6: Creating a Segwit Transaction](04_6_Creating_a_Segwit_Transaction) — Awaiting Better Integration of Segwit into CLI
|
* [4.6: Creating a Segwit Transaction](04_6_Creating_a_Segwit_Transaction.md)
|
||||||
* [5.0: Controlling Bitcoin Transactions](05_0_Controlling_Bitcoin_Transactions.md)
|
* [5.0: Controlling Bitcoin Transactions](05_0_Controlling_Bitcoin_Transactions.md)
|
||||||
* [5.1 Watching for Stuck Transactions](05_1_Watching_for_Stuck_Transactions.md)
|
* [5.1 Watching for Stuck Transactions](05_1_Watching_for_Stuck_Transactions.md)
|
||||||
* [5.2: Resending a Transaction with RBF](05_2_Resending_a_Transaction_with_RBF.md)
|
* [5.2: Resending a Transaction with RBF](05_2_Resending_a_Transaction_with_RBF.md)
|
||||||
* [5.3: Funding a Transaction with CPFP](05_3_Funding_a_Transaction_with_CPFP.md)
|
* [5.3: Funding a Transaction with CPFP](05_3_Funding_a_Transaction_with_CPFP.md)
|
||||||
* [6.0: Expanding Bitcoin Transactions](06_0_Expanding_Bitcoin_Transactions.md)
|
* [6.0: Expanding Bitcoin Transactions with Multisigs](06_0_Expanding_Bitcoin_Transactions_Multisigs.md)
|
||||||
* [6.1: Sending a Transaction with a Multsig](06_1_Sending_a_Transaction_to_a_Multisig.md)
|
* [6.1: Sending a Transaction with a Multsig](06_1_Sending_a_Transaction_to_a_Multisig.md)
|
||||||
* [6.2: Spending a Transaction with a Multsig](06_2_Spending_a_Transaction_to_a_Multisig.md)
|
* [6.2: Spending a Transaction with a Multsig](06_2_Spending_a_Transaction_to_a_Multisig.md)
|
||||||
* [6.3: Sending & Spending an Automated Multisig](06_3_Sending_an_Automated_Multisig.md)
|
* [6.3: Sending & Spending an Automated Multisig](06_3_Sending_an_Automated_Multisig.md)
|
||||||
* [6.4: Sending a Transaction with a Locktime](06_4_Sending_a_Transaction_with_a_Locktime.md)
|
* [7.0: Expanding Bitcoin Transactions with PSBTs](07_0_Expanding_Bitcoin_Transactions_PSBTs.md)
|
||||||
* [6.5: Sending a Transaction with Data](06_5_Sending_a_Transaction_with_Data.md)
|
* [7.1: Creating a Partially Signed Bitcoin Transaction](07_1_Creating_a_Partially_Signed_Bitcoin_Transaction.md)
|
||||||
|
* [7.2: Using a Partially Signed Bitcoin Transaction](07_2_Using_a_Partially_Signed_Bitcoin_Transaction.md)
|
||||||
|
* [7.3: Integrating with Hardware Wallets](07_3_Integrating_with_Hardware_Wallets.md)
|
||||||
|
* [8.0: Expanding Bitcoin Transactions in Other Ways](08_0_Expanding_Bitcoin_Transactions_Other.md)
|
||||||
|
* [8.1: Sending a Transaction with a Locktime](08_1_Sending_a_Transaction_with_a_Locktime.md)
|
||||||
|
* [8.2: Sending a Transaction with Data](08_2_Sending_a_Transaction_with_Data.md)
|
||||||
|
|
||||||
## PART THREE: BITCOIN SCRIPTING
|
## PART THREE: BITCOIN SCRIPTING
|
||||||
|
|
||||||
|
**Status:** Requires renumbering; requires editing; requires check that it works in 0.20; requires concepts brought up to 0.20.
|
||||||
|
|
||||||
* [7.0: Introducing Bitcoin Scripts](07_0_Introducing_Bitcoin_Scripts.md)
|
* [7.0: Introducing Bitcoin Scripts](07_0_Introducing_Bitcoin_Scripts.md)
|
||||||
* [7.1: Understanding the Foundation of Transactions](07_1_Understanding_the_Foundation_of_Transactions.md)
|
* [7.1: Understanding the Foundation of Transactions](07_1_Understanding_the_Foundation_of_Transactions.md)
|
||||||
* [7.2: Running a Bitcoin Script](07_2_Running_a_Bitcoin_Script.md)
|
* [7.2: Running a Bitcoin Script](07_2_Running_a_Bitcoin_Script.md)
|
||||||
@ -72,27 +84,34 @@ _This tutorial assumes that you have some minimal background of how to use the c
|
|||||||
* [11.2: Writing Complex Multisig Scripts](11_2_Writing_Complex_Multisig_Scripts.md)
|
* [11.2: Writing Complex Multisig Scripts](11_2_Writing_Complex_Multisig_Scripts.md)
|
||||||
* [11.3: Empowering Bitcoin with Scripts](11_3_Empowering_Bitcoin_with_Scripts.md)
|
* [11.3: Empowering Bitcoin with Scripts](11_3_Empowering_Bitcoin_with_Scripts.md)
|
||||||
|
|
||||||
### PART FOUR: TOR
|
### PART FOUR: USING TOR
|
||||||
|
|
||||||
_The section is currently unwritten._
|
**Status:** Requires renumbering; Unwritten.
|
||||||
|
|
||||||
* 12.0: Setting Up Tor
|
* [12.0: Using Tor](12_0_Using_Tor.md)
|
||||||
* 12.1: Verifying Your Tor Setup
|
* 12.1: Verifying Your Tor Setup
|
||||||
* 12.2: Changing Your Bitcoin Hidden Services
|
* 12.2: Changing Your Bitcoin Hidden Services
|
||||||
* 12.3: Adding SSH Hiddne Services
|
* 12.3: Adding SSH Hiddne Services
|
||||||
|
|
||||||
### PART FIVE: LIGHTNING-CLI
|
### PART FIVE: USING LIGHTNING-CLI
|
||||||
|
|
||||||
_This section is unwritten currently, and will probably involve chapter 14.0 expanding into at least two chapters._
|
**Status:** Requires renumbering; Unwritten. Chapter 14 may expand into multiple chapters.
|
||||||
|
|
||||||
* 13.0: Setting Up Lightning
|
(At this point, I'm assuming that Lightning will be integrated into Standup, at which point we just need to tech how to use it at a pretty basic level.)
|
||||||
|
|
||||||
|
* 13.0: Understanding Lightning
|
||||||
|
* 13.1: Verifying Your Lightning Setup
|
||||||
|
* 13.2: Setting up a Channel
|
||||||
|
* 13.3: Receiving a Transaction
|
||||||
* 14.0: Using Lightning
|
* 14.0: Using Lightning
|
||||||
|
* 14.1: Sending a Transaction
|
||||||
|
* 14.X: Closing a Channel
|
||||||
|
|
||||||
> _Some good docs from one of the developers are here: https://diyhpl.us/wiki/transcripts/blockstream-webinars/2019-07-31-rusty-russell-getting-started-with-c-lightning/._
|
> _Some good docs from one of the developers are here: https://diyhpl.us/wiki/transcripts/blockstream-webinars/2019-07-31-rusty-russell-getting-started-with-c-lightning/._
|
||||||
|
|
||||||
### PART SIX: BITCOIN & LIGHTNING PROGRAMMING
|
### PART SIX: PROGRAMMING WITH RPC
|
||||||
|
|
||||||
_This section is currently a messy set of older writings which are being reorganized as listed below. We're not going to work through this section until we've got chapters 1-11 updated per our current notes, then 12-14 written. But, if you'd like to see what we have to date on programming in correlation with Bitcoin Core, please feel free._
|
**Status:** Requires renumbering; This section is currently a messy set of older writings which are being reorganized as listed below. We're not going to work through this section until we've got chapters 1-11 updated per our current notes, then 12-14 written. But, if you'd like to see what we have to date on programming in correlation with Bitcoin Core, please feel free.
|
||||||
|
|
||||||
* [15.0: Talking to Bitcoind with C](15_0_Talking_to_Bitcoind.md) — Needs Rewrite + Editing
|
* [15.0: Talking to Bitcoind with C](15_0_Talking_to_Bitcoind.md) — Needs Rewrite + Editing
|
||||||
* [15.1: Accessing Bitcoind with C](15_1_Accessing_Bitcoind_with_C.md) — Needs Rewrite
|
* [15.1: Accessing Bitcoind with C](15_1_Accessing_Bitcoind_with_C.md) — Needs Rewrite
|
||||||
@ -105,16 +124,18 @@ _This section is currently a messy set of older writings which are being reorgan
|
|||||||
* 17.4: Integrating Libwally and Scripts
|
* 17.4: Integrating Libwally and Scripts
|
||||||
* 17.0: Talking to Lightningd with C
|
* 17.0: Talking to Lightningd with C
|
||||||
* 18.0: Talking to Bitcoind with Other Languages
|
* 18.0: Talking to Bitcoind with Other Languages
|
||||||
* [18.1: Accessing Bitcoind with Go]
|
* [18.1: Accessing Bitcoind with Go](18_1_Accessing_Bitcoind_with_Go.md)
|
||||||
* [18.2: Accessing Bitcoind with Java](18_2_Accessing_Bitcoind_with_Java.md) — Needs Rewrite + Editing
|
* [18.2: Accessing Bitcoind with Java](18_2_Accessing_Bitcoind_with_Java.md)
|
||||||
* [18.3: Accessing Bitcoind with_Node_JS](18_3_Accessing_Bitcoind_with_NodeJS.md) — Needs Rewrite + Editing
|
* [18.3: Accessing Bitcoind with Node JS](18_3_Accessing_Bitcoind_with_NodeJS.md)
|
||||||
* [18.4: Accessing Bitcoind with Python]
|
* [18.4: Accessing Bitcoind with Python](18_4_Accessing_Bitcoind_with_Python.md)
|
||||||
* [18.5: Accessing Bitcoind with Rust]
|
* [18.5: Accessing Bitcoind with Rust](18_5_Accessing_Bitcoind_with_Rust.md)
|
||||||
* [18.6: Accessing Bitcoind with Swift]
|
* [18.6: Accessing Bitcoind with Swift]
|
||||||
|
|
||||||
### APPENDICE
|
### APPENDICES
|
||||||
|
|
||||||
* Appendix I: Compiling Bitcoin
|
**Status:** Varied. Appendix I is recent that just needs editing for style; Appendix II needs a rewrite.
|
||||||
|
|
||||||
|
* [Appendix I: Compiling Bitcoin from Source](A1_0_Compiling_Bitcoin_from_Source.md)
|
||||||
* [Appendix II: Using Bitcoin Regtest](A2_0_Setting_Up_a_Bitcoin_Regtest.md) — Needs Rewrite + Editing
|
* [Appendix II: Using Bitcoin Regtest](A2_0_Setting_Up_a_Bitcoin_Regtest.md) — Needs Rewrite + Editing
|
||||||
* [A2.1: Starting the Regtest](A2_1_Starting_the_Regtest.md) — Needs Rewrite + Editing
|
* [A2.1: Starting the Regtest](A2_1_Starting_the_Regtest.md) — Needs Rewrite + Editing
|
||||||
* [A2.2: Mining with Regtest](A2_2_Mining_with_Regtest.md) — Needs Rewrite + Editing
|
* [A2.2: Mining with Regtest](A2_2_Mining_with_Regtest.md) — Needs Rewrite + Editing
|
||||||
@ -164,7 +185,7 @@ Additional contributions are listed below:
|
|||||||
|
|
||||||
| Role | Names |
|
| Role | Names |
|
||||||
| ------------------- | ---------------------------------------- |
|
| ------------------- | ---------------------------------------- |
|
||||||
| ***Contributors:*** | |
|
| ***Contributors:*** | [gg2001](https://github.com/gg2001) (Go, Node.js sections), [gorazdko](https://github.com/gorazdko) (Rust section), [Javier Vargas](https://github.com/javiervargas) (C, Java, Tor sections), [jodobear](https://github.com/jodobear) (Appendix I, Python section) |
|
||||||
| ***Reviewers:*** | Glen Willem [@gwillem](https://github.com/gwillem) |
|
| ***Reviewers:*** | Glen Willem [@gwillem](https://github.com/gwillem) |
|
||||||
| ***Sponsors:*** | Blockstream Corporation |
|
| ***Sponsors:*** | Blockstream Corporation |
|
||||||
|
|
||||||
@ -186,4 +207,3 @@ The following keys may be used to communicate sensitive information to developer
|
|||||||
| Christopher Allen | FDFE 14A5 4ECB 30FC 5D22 74EF F8D3 6C91 3574 05ED |
|
| Christopher Allen | FDFE 14A5 4ECB 30FC 5D22 74EF F8D3 6C91 3574 05ED |
|
||||||
|
|
||||||
You can import a key by running the following command with that individual’s fingerprint: `gpg --recv-keys "<fingerprint>"` Ensure that you put quotes around fingerprints that contain spaces.
|
You can import a key by running the following command with that individual’s fingerprint: `gpg --recv-keys "<fingerprint>"` Ensure that you put quotes around fingerprints that contain spaces.
|
||||||
|
|
||||||
|
67
TODO.md
67
TODO.md
@ -8,21 +8,35 @@ Replace our current scripts with Bitcoin Standup (which was based on those scrip
|
|||||||
|
|
||||||
1. <strike>Fix Verification errors in Bitcoin Standup Scripts</strike> **6/10**
|
1. <strike>Fix Verification errors in Bitcoin Standup Scripts</strike> **6/10**
|
||||||
* <strike>Fix IPADDR error in Bitcoin Standup Scripts</strike> **6/10**
|
* <strike>Fix IPADDR error in Bitcoin Standup Scripts</strike> **6/10**
|
||||||
2. Upgrade Bitcoin Standup Scripts to Debian 10
|
2. <strike>Upgrade Bitcoin Standup Scripts to Debian 10</strike> **6/16**
|
||||||
3. Upgrade Bitcoin Standup Scripts to Bitcoin 0.20
|
3. <strike>Upgrade Bitcoin Standup Scripts to Bitcoin 0.20</strike> **6/16**
|
||||||
4. Ensure Bitcoin Standup covers everything else in previous scripts
|
4. <strike>Ensure Bitcoin Standup covers everything else in previous scripts</strike> **6/16**
|
||||||
5. Rewrite the StackScript chapter
|
5. <strike>Rewrite the StackScript chapter</strike> **6/16**
|
||||||
6. Rewrite the "by-hand" chapter to match Bitcoin Standup
|
6. Rewrite the "by-hand" chapter to match Bitcoin Standup (Pending summer work)
|
||||||
7. Reintroduce aliases after setup
|
7. <strike>Reintroduce aliases after setup</strike> **6/19**
|
||||||
8. Figure out what to do about the old `btcblock` on testnet, where there seems to be no CLI way to determine blockheight.
|
8. <strike>Figure out what to do about the old `btcblock` on testnet, where there seems to be no CLI way to determine blockheight.</strike> **No Known Solution on Testnet**
|
||||||
|
|
||||||
## 2. Upgrade to 0.20
|
## 2. Upgrade to 0.20
|
||||||
|
|
||||||
9. Walk through chapters 1-11, making sure all commands work with Bitcoin Core 0.20 (and changing them if not). Redo the output of all examples.
|
9. <strike>Walk through chapters 1-6, making sure all commands work with Bitcoin Core 0.20 (and changing them if not). Redo the output of all examples.</strike> **6/19**, **6/23**, **6/30**
|
||||||
|
* <strike>Edit Chapter 0</strike> **6/19**
|
||||||
|
* <strike>Edit Chapter 1</strike> **6/19**
|
||||||
|
* <strike>Re-edit Chapter 2</strike> **6/19**
|
||||||
|
* <strike>Edit & Check Chapter 3</strike> **6/19**
|
||||||
|
* <strike>Edit & Check Chapter 4</strike> **6/23**
|
||||||
|
* <strike>Double-check fee calculator in 4.2I with a more complex example</strike> **6/19**
|
||||||
|
* <strike>Integrate older Curl Interlude</strike> **6/19**
|
||||||
|
* <strike>Edit & Check Chapter 5</strike> **6/23**
|
||||||
|
* <strike>Edit & Check Chapter 6</strike> **6/30**
|
||||||
|
|
||||||
## 3. Add BTCDEB Support
|
## 3. Add BTCDEB Support
|
||||||
|
|
||||||
10. Make all examples in [7.4](https://github.com/BlockchainCommons/Learning-Bitcoin-from-the-Command-Line/blob/master/07_4_Testing_a_Bitcoin_Script.md) and possibly elsewhere use BTCDeb.
|
10. Make all examples in [7.4](https://github.com/BlockchainCommons/Learning-Bitcoin-from-the-Command-Line/blob/master/07_4_Testing_a_Bitcoin_Script.md) and possibly elsewhere use BTCDeb.
|
||||||
|
* Edit & Integrate Chapter 7
|
||||||
|
* Edit & Integrate Chapter 8
|
||||||
|
* Edit & Integrate Chapter 9
|
||||||
|
* Edit & Integrate Chapter 10
|
||||||
|
* Edit & Integrate Chapter 11
|
||||||
|
|
||||||
Per @ChristopherA:
|
Per @ChristopherA:
|
||||||
|
|
||||||
@ -34,20 +48,39 @@ Per @ChristopherA:
|
|||||||
|
|
||||||
Add and document the following new concepts:
|
Add and document the following new concepts:
|
||||||
|
|
||||||
11. Add SegWit Transactions. The majority of Bitcoin transactions now use this signing methodology, so it needs to be fully explained and incorporated, alongside its newer bech32 addresses.
|
11. <strike>Add SegWit Transactions. The majority of Bitcoin transactions now use this signing methodology, so it needs to be fully explained and incorporated, alongside its newer bech32 addresses.</strike> **6/23**
|
||||||
|
* <strike>Add definitions of Segwit and bech32 addresses to 3.3</strike> **6/23**
|
||||||
|
* <strike>Do we still have to use "bitcoin-cli getnewaddress "" legacy" on CLI? If not, run back through chapters that use legacy in their examples, starting in 3.3</strike> **Gonna leave it for now. A future version might shift to P2SH-SegWit as default.**
|
||||||
|
* <strike>Integrate discussions of SegWit into early parts of chapter 4.</strike> **6/23**
|
||||||
|
* <strike>Write chapter 4.6</strike> **6/23**
|
||||||
12. Add PSBT. Partially Signed Bitcoin Transactions are a powerful new interchange format that should be fully included in our discussions of multi-sigs and escrows (which instead fell back on older methodologies in v1.0).
|
12. Add PSBT. Partially Signed Bitcoin Transactions are a powerful new interchange format that should be fully included in our discussions of multi-sigs and escrows (which instead fell back on older methodologies in v1.0).
|
||||||
13. Add Wallet Updates. Some improvements have been made to wallet functionality, including Bitcoin Descriptors, and they should be added to the course.
|
* <strike>Research PSBTs</strike> **7/2**
|
||||||
|
* <strike>Update Outlines</strike> **7/2**
|
||||||
|
* Write 6.6: Creating a PSBT
|
||||||
|
* Write 6.7: Using a PSBT
|
||||||
|
13. Add Wallet Updates.
|
||||||
|
* <strike>Bitcoin Descriptors</strike> **6/30**
|
||||||
|
* <strike>Show what a SegWit descriptor looks like</strike> **7/2**
|
||||||
|
* <strike>Show what a multi-sig descriptor looks like</strike> **7/2**
|
||||||
|
* Revise based on comments
|
||||||
|
* <strike>Key Ordering (sortedmulti)</strike> **7/2** **Partially Supported in 0.20**
|
||||||
14. Consider HWI. We need to investigate the Hardware Wallet Interface, which uses PSBTs with hardware wallets, and see if it's something we should include (and if our readers want us to).
|
14. Consider HWI. We need to investigate the Hardware Wallet Interface, which uses PSBTs with hardware wallets, and see if it's something we should include (and if our readers want us to).
|
||||||
|
15. Consider splitting up Chapter 6 (definitely if we get an HWI chapter)
|
||||||
|
* <strike>Break apart Chapter 6</strike> **7/2**
|
||||||
|
* Rewrite all section links in early chapters (1-5, 6, 8)
|
||||||
|
* Update Script Chapters
|
||||||
|
* Update Tor Chapter
|
||||||
|
* Update Programming Chapters
|
||||||
|
|
||||||
## 5. Finish Later Chapters
|
## 5. Finish Later Chapters
|
||||||
|
|
||||||
15. Write Tor Chapter. We want to fully integrate Tor into the installation of Bitcoin and then later talk about its usage.
|
16. Edit & Intregtrate Tor Chapter.
|
||||||
16. Write Lightning Chapters. We'd like to parallel the CLI introduction to Bitcoin with a similar CLI introduction to Lightning.
|
17. Write Lightning Chapters. We'd like to parallel the CLI introduction to Bitcoin with a similar CLI introduction to Lightning.
|
||||||
17. Edit & Integrate all "C" work
|
18. Edit & Integrate all "C" work
|
||||||
18. Edit & Integrate all "Other Languages" work
|
19. Edit & Integrate all "Other Languages" work
|
||||||
19. Edit & Integrate Appendices work
|
20. Edit & Integrate Appendices work
|
||||||
20. Write or request Lightning/C chapter
|
21. Write or request Lightning/C chapter
|
||||||
|
|
||||||
## 6. Finalize Book
|
## 6. Finalize Book
|
||||||
|
|
||||||
21. Re-edit everything
|
22. Re-edit everything
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
-----BEGIN PGP SIGNED MESSAGE-----
|
||||||
|
Hash: SHA512
|
||||||
|
|
||||||
|
# Contributor License Agreement
|
||||||
|
|
||||||
|
Version 1.0
|
||||||
|
|
||||||
|
Name: Gorazd Kovacic
|
||||||
|
|
||||||
|
E-Mail: gorazdko@gmail.com
|
||||||
|
|
||||||
|
Legal Jurisdiction: Wyoming, United States of America
|
||||||
|
|
||||||
|
Project: https://github.com/BlockchainCommons/Learning-Bitcoin-from-the-Command-Line
|
||||||
|
|
||||||
|
Date: 06-27-2020
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
This agreement gives Blockchain Commons, LLC the permission it needs in order to accept my contributions into its open software project and to manage the intellectual property in that project over time.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
I hereby license Blockchain Commons, LLC to:
|
||||||
|
|
||||||
|
1. do anything with my contributions that would otherwise infringe my copyright in them
|
||||||
|
|
||||||
|
2. do anything with my contributions that would otherwise infringe patents that I can or become able to license
|
||||||
|
|
||||||
|
3. sublicense these rights to others on any terms they like
|
||||||
|
|
||||||
|
## Reliability
|
||||||
|
|
||||||
|
I understand that Blockchain Commons will rely on this license. I may not revoke this license.
|
||||||
|
|
||||||
|
## Awareness
|
||||||
|
|
||||||
|
I promise that I am familiar with legal rules, like ["work made for hire" rules](http://worksmadeforhire.com), that can give employers and clients ownership of intellectual property in work that I do. I am also aware that legal agreements I might sign, like confidential information and invention assignment agreements, will usually give ownership of intellectual property in my work to employers, clients, and companies that I found. If someone else owns intellectual property in my work, I need their permission to license it.
|
||||||
|
|
||||||
|
## Copyright Guarantee
|
||||||
|
|
||||||
|
I promise not to offer contributions to the project that contain copyrighted work that I do not have legally binding permission to contribute under these terms. When I offer a contribution with permission, I promise to document in the contribution who owns copyright in what work, and how they gave permission to contribute it. If I later become aware that one of my contributions may have copyrighted work of others that I did not have permission to contribute, I will notify Blockchain Commons, in confidence, immediately.
|
||||||
|
|
||||||
|
## Patent Guarantee
|
||||||
|
|
||||||
|
I promise not to offer contributions to the project that I know infringe patents of others that I do not have permission to contribute under these terms.
|
||||||
|
|
||||||
|
## Open Source Guarantee
|
||||||
|
|
||||||
|
I promise not to offer contributions that contain or depend on the work of others, unless that work is available under a license that [Blue Oak Council rates bronze or better](https://blueoakconcil.org/list), such as the MIT License, two- or three-clause BSD License, the Apache License Version 2.0, or the Blue Oak Model License 1.0.0. When I offer a contribution containing or depending on others' work, I promise to document in the contribution who licenses that work, along with copies of their license terms.
|
||||||
|
|
||||||
|
## Disclaimers
|
||||||
|
|
||||||
|
***As far as the law allows, my contributions come as is, without any warranty or condition. Other than under [Copyright Guarantee](#copyright-guarantee), [Patent Guarantee](#patent-guarantee), or [Open Source Guarantee](#open-source-guarantee), I will not be liable to anyone for any damages related to my contributions or this contributor license agreement, under any kind of legal claim.***
|
||||||
|
|
||||||
|
- ---
|
||||||
|
|
||||||
|
To sign this Contributor License Agreement, fill in `$name`, `$email`, and `$date` above. Then sign using GPG using the following command `gpg --armor --clearsign --output ./signed-cla/CLA.YOURGITHUBNAME.YOURGPGFINGERPRINT.asc CLA.md`, then either submit your signed Contributor License Agreement to this repo as a GPG signed Pull Request or email it to [ChristopherA@BlockchainCommons.com](mailto:ChristopherA@BlockchainCommons.com).
|
||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
|
||||||
|
iQIzBAEBCgAdFiEEQfDqFpmnTB4vpBtTjPlrw/+du84FAl73XO4ACgkQjPlrw/+d
|
||||||
|
u84JUg//TT6ySwiHIjl+gZiIZQjiNIpPqrpeb8pmJvoL1+CkJgidPR03pMbuz5XT
|
||||||
|
TGczVIreLa9jf3K96Sj/0uRcTi5mZWE6pfAZT6hTVPiajHPri14oDrXB0Yar1btx
|
||||||
|
Oq+3b+ryb6BOfXKub4Kl1JP8PyUanBD/1/j4jRAZcxOjHOEUdw5gJYH3VpKm3+ds
|
||||||
|
RaMABUmpPd6dOCrfGojbAeajcWSP1KpX2zxDYxJvnYZ8f3spl2mVb8ajqHt7T3FK
|
||||||
|
Kff2YQBT3HE3Ln81bu2SbhE7E5X5BWasNe1oH73jS9T2CsXkretg68lVUR2Cw7C2
|
||||||
|
uHtbn8zA2ySntb6xyInHHA7uhBNRtnxwm9QSRgq/PisuIqA9qMnhDSSSVXopi6FE
|
||||||
|
iF4Zf5NOq0l2ZIJQpNEOXG7WnMpqaXr7GYqq47PBaY80H+UdnLyVmJxattPDSV5e
|
||||||
|
tiBUF3862PWeDxsIe3jbce4N8SPstcqgnTGiSTcNft7WzhW6pxDhYDaNa/oqJ3bk
|
||||||
|
zAkX6LAG1MhNsGnnOZ75e9hEjFZg7grVmjwRbyo+2PgNy1e0+UNTCndCM7q3fhjO
|
||||||
|
AE3hNB+YGQGiXJmD5oh4zFVeJldIkRBjOdagOmvrQizJsWpgh9xD0jEfL5dxcq9c
|
||||||
|
/1InfBONgGnEwXSengCHVVEOIJeB/VPxd1y6O3240szyJxYe6p0=
|
||||||
|
=UKxv
|
||||||
|
-----END PGP SIGNATURE-----
|
@ -0,0 +1,73 @@
|
|||||||
|
-----BEGIN PGP SIGNED MESSAGE-----
|
||||||
|
Hash: SHA512
|
||||||
|
|
||||||
|
# Contributor License Agreement
|
||||||
|
|
||||||
|
Version 1.0
|
||||||
|
|
||||||
|
Name: Javier Vargas
|
||||||
|
|
||||||
|
E-Mail: jevargas@uniandes.edu.co
|
||||||
|
|
||||||
|
Legal Jurisdiction: Madrid, Spain
|
||||||
|
|
||||||
|
Project: https://github.com/BlockchainCommons/Learning-Bitcoin-from-the-Command-Line
|
||||||
|
|
||||||
|
Date: 06/19/2020
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
This agreement gives Blockchain Commons, LLC the permission it needs in order to accept my contributions into its open software project and to manage the intellectual property in that project over time.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
I hereby license Blockchain Commons, LLC to:
|
||||||
|
|
||||||
|
1. do anything with my contributions that would otherwise infringe my copyright in them
|
||||||
|
|
||||||
|
2. do anything with my contributions that would otherwise infringe patents that I can or become able to license
|
||||||
|
|
||||||
|
3. sublicense these rights to others on any terms they like
|
||||||
|
|
||||||
|
## Reliability
|
||||||
|
|
||||||
|
I understand that Blockchain Commons will rely on this license. I may not revoke this license.
|
||||||
|
|
||||||
|
## Awareness
|
||||||
|
|
||||||
|
I promise that I am familiar with legal rules, like ["work made for hire" rules](http://worksmadeforhire.com), that can give employers and clients ownership of intellectual property in work that I do. I am also aware that legal agreements I might sign, like confidential information and invention assignment agreements, will usually give ownership of intellectual property in my work to employers, clients, and companies that I found. If someone else owns intellectual property in my work, I need their permission to license it.
|
||||||
|
|
||||||
|
## Copyright Guarantee
|
||||||
|
|
||||||
|
I promise not to offer contributions to the project that contain copyrighted work that I do not have legally binding permission to contribute under these terms. When I offer a contribution with permission, I promise to document in the contribution who owns copyright in what work, and how they gave permission to contribute it. If I later become aware that one of my contributions may have copyrighted work of others that I did not have permission to contribute, I will notify Blockchain Commons, in confidence, immediately.
|
||||||
|
|
||||||
|
## Patent Guarantee
|
||||||
|
|
||||||
|
I promise not to offer contributions to the project that I know infringe patents of others that I do not have permission to contribute under these terms.
|
||||||
|
|
||||||
|
## Open Source Guarantee
|
||||||
|
|
||||||
|
I promise not to offer contributions that contain or depend on the work of others, unless that work is available under a license that [Blue Oak Council rates bronze or better](https://blueoakconcil.org/list), such as the MIT License, two- or three-clause BSD License, the Apache License Version 2.0, or the Blue Oak Model License 1.0.0. When I offer a contribution containing or depending on others' work, I promise to document in the contribution who licenses that work, along with copies of their license terms.
|
||||||
|
|
||||||
|
## Disclaimers
|
||||||
|
|
||||||
|
***As far as the law allows, my contributions come as is, without any warranty or condition. Other than under [Copyright Guarantee](#copyright-guarantee), [Patent Guarantee](#patent-guarantee), or [Open Source Guarantee](#open-source-guarantee), I will not be liable to anyone for any damages related to my contributions or this contributor license agreement, under any kind of legal claim.***
|
||||||
|
|
||||||
|
- - - ---
|
||||||
|
|
||||||
|
To sign this Contributor License Agreement, fill in `$name`, `$email`, and `$date` above. Then sign using GPG using the following command `gpg --armor --clearsign --output ./signed-cla/CLA.YOURGITHUBNAME.YOURGPGFINGERPRINT.asc CLA.md`, then either submit your signed Contributor License Agreement to this repo as a GPG signed Pull Request or email it to [ChristopherA@BlockchainCommons.com](mailto:ChristopherA@BlockchainCommons.com).
|
||||||
|
|
||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
|
||||||
|
iQGzBAEBCgAdFiEEcD2N9So1EMOo5qpdy91nx0pUcAQFAl7yQ8cACgkQy91nx0pU
|
||||||
|
cAQnIQwAsIn+KseET6IN9SqwQoj6zpZX8pXCHohxqFt9c87LbPraKgv/TzuyUphI
|
||||||
|
AMLRt/a4hOng8cUKdD9bsq70eiGHpW/uOd6VUQKMMdvgyr2+FCY0ljqxB2ZkVccN
|
||||||
|
/sApr0WAlNnfpxaQAYm6ft+FG3rGKEtdwUBzQ0kGShUdt8I0EiHwvevS5x1SNwaT
|
||||||
|
kruRWy8xoLD7hWQ3OPOqserwtkqK8fIu4PLVtoiBA+qnFpTCoaREjLoEHpX/KKkk
|
||||||
|
W2r0VxMm2tr+n0+BgLVOAMMSJkhOA6/hlzFXmUIl5WOOeh5AlLlcowGeaQRsCRJL
|
||||||
|
Sgd3KwOBA7XRFLai72AjSybLnIVn3yhwoHaCPQiVe4Z5qYf0QDYg1W7janYITgMP
|
||||||
|
s4a2p/4adH/OP5Xkm/8KFzOLCc0Ixjlm8/jHIfm8odIRCcyEiqJYon2ZSBbYiM3N
|
||||||
|
UPcxE+XrGnSmanK7PEOjrL9D+pLyozcsZc+HZ56CFwoWUn6PNzBUqeLcalAp5f2X
|
||||||
|
BRGP20HH
|
||||||
|
=0qBm
|
||||||
|
-----END PGP SIGNATURE-----
|
118
src/Client.java
Normal file
118
src/Client.java
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
import java.math.*;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import javax.xml.bind.DatatypeConverter;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import wf.bitcoin.javabitcoindrpcclient.BitcoinJSONRPCClient;
|
||||||
|
import wf.bitcoin.javabitcoindrpcclient.BitcoindRpcClient.SignedRawTransaction;
|
||||||
|
import wf.bitcoin.javabitcoindrpcclient.BitcoindRpcClient.AddressInfo;
|
||||||
|
import wf.bitcoin.javabitcoindrpcclient.BitcoindRpcClient.RawTransactionSigningOrVerificationError;
|
||||||
|
import wf.bitcoin.javabitcoindrpcclient.*;
|
||||||
|
import wf.bitcoin.krotjson.HexCoder;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import wf.bitcoin.javabitcoindrpcclient.BitcoindRpcClient.Unspent;
|
||||||
|
import wf.bitcoin.javabitcoindrpcclient.BitcoinRawTxBuilder;
|
||||||
|
import wf.bitcoin.javabitcoindrpcclient.BitcoindRpcClient.Transaction;
|
||||||
|
import wf.bitcoin.javabitcoindrpcclient.BitcoindRpcClient.ExtendedTxInput;
|
||||||
|
import wf.bitcoin.javabitcoindrpcclient.BitcoindRpcClient.TxInput;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class Client{
|
||||||
|
|
||||||
|
static BitcoinJSONRPCClient bitcoinClient = null;
|
||||||
|
static BitcoinRawTxBuilder txb = null;
|
||||||
|
static String address = "";
|
||||||
|
|
||||||
|
public boolean connect(){
|
||||||
|
String user = "user";
|
||||||
|
String password = "password";
|
||||||
|
String host = "127.0.0.1";
|
||||||
|
String port = "18443"; //regtest
|
||||||
|
// String host = "192.168.0.22"; //testnet
|
||||||
|
// String port = "18332"; //testnet
|
||||||
|
try {
|
||||||
|
URL url = new URL("http://" + user + ':' + password + "@" + host + ":" + port + "/");
|
||||||
|
bitcoinClient = new BitcoinJSONRPCClient(url);
|
||||||
|
return true;
|
||||||
|
}catch (MalformedURLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String args[]){
|
||||||
|
BigDecimal txToAddr2Amount = new BigDecimal("0");
|
||||||
|
BigDecimal estimatedFee = BigDecimal.valueOf(0.00000200);
|
||||||
|
Client rpcClient = new Client();
|
||||||
|
bitcoin.connect();
|
||||||
|
LinkedList<BitcoindRpcClient.ExtendedTxInput> inputList = new LinkedList<BitcoindRpcClient.ExtendedTxInput>();
|
||||||
|
txb = new BitcoinRawTxBuilder(bitcoinClient);
|
||||||
|
// String addr1 = bitcoinClient.getNewAddress();
|
||||||
|
String addr1 = "bcrt1qs4ylwj2v5v0gq7eqzp9k9vxazdrkexhkghxpyp"; //regtest
|
||||||
|
AddressInfo addr1Info = bitcoinClient.getAddressInfo(addr1);
|
||||||
|
System.out.println("Address: " + addr1Info.address());
|
||||||
|
System.out.println("HdKeyPath: " + addr1Info.hdKeyPath());
|
||||||
|
System.out.println("PubKey: " + addr1Info.pubKey());
|
||||||
|
System.out.println("MasterFingerPrint: " + addr1Info.hdMasterFingerprint());
|
||||||
|
System.out.println("Balance: " + bitcoinClient.getBalance());
|
||||||
|
|
||||||
|
// String addr1 = "tb1qrj3dgs6kh2mtr7ulgmlx3pvavqwtw6vxdk468y"; //testnet
|
||||||
|
System.out.println("Created address addr1: " + addr1);
|
||||||
|
String addr2 = bitcoinClient.getNewAddress();
|
||||||
|
System.out.println("Created address addr2: " + addr2);
|
||||||
|
List<String> generatedBlocksHashes = bitcoinClient.generateToAddress(110, addr1);
|
||||||
|
System.out.println("Generated " + generatedBlocksHashes.size() + " blocks for addr1");
|
||||||
|
List<Unspent> utxos = bitcoinClient.listUnspent(0, Integer.MAX_VALUE, addr1);
|
||||||
|
System.out.println("Found " + utxos.size() + " UTXOs (unspent transaction outputs) belonging to addr1");
|
||||||
|
/*for(int i =0;i<utxos.size();i++) {
|
||||||
|
Unspent selectedUtxo = utxos.get(i);
|
||||||
|
System.out.println("Selected UTXO which will be sent from addr1 to addr2: " + selectedUtxo);
|
||||||
|
System.out.println("txid:" + selectedUtxo.txid());
|
||||||
|
System.out.println("vout:" + selectedUtxo.vout());
|
||||||
|
System.out.println("scriptPubKey:" + selectedUtxo.scriptPubKey());
|
||||||
|
System.out.println("amount:" + selectedUtxo.amount());
|
||||||
|
BigDecimal estimatedFee = BigDecimal.valueOf(0.00000200);
|
||||||
|
txToAddr2Amount = selectedUtxo.amount().subtract(estimatedFee);
|
||||||
|
}*/
|
||||||
|
TxInput in = utxos.get(0);
|
||||||
|
txb.in(in);
|
||||||
|
|
||||||
|
txToAddr2Amount = utxos.get(0).amount().subtract(estimatedFee);
|
||||||
|
txb.out(addr2, txToAddr2Amount);
|
||||||
|
|
||||||
|
System.out.println("unsignedRawTx in amount: " + utxos.get(0).amount());
|
||||||
|
System.out.println("unsignedRawTx out amount: " + txToAddr2Amount);
|
||||||
|
|
||||||
|
String unsignedRawTxHex = txb.create();
|
||||||
|
System.out.println("Created unsignedRawTx from addr1 to addr2: " + unsignedRawTxHex);
|
||||||
|
|
||||||
|
SignedRawTransaction srTx = bitcoinClient.signRawTransactionWithKey(
|
||||||
|
unsignedRawTxHex,
|
||||||
|
Arrays.asList(bitcoinClient.dumpPrivKey(addr1)),
|
||||||
|
Arrays.asList(in),
|
||||||
|
null);
|
||||||
|
System.out.println("signedRawTx hex: " + srTx.hex());
|
||||||
|
System.out.println("signedRawTx complete: " + srTx.complete());
|
||||||
|
|
||||||
|
List<RawTransactionSigningOrVerificationError> errors = srTx.errors();
|
||||||
|
if (errors != null)
|
||||||
|
{
|
||||||
|
System.out.println("Found errors when signing");
|
||||||
|
|
||||||
|
for (RawTransactionSigningOrVerificationError error : errors)
|
||||||
|
{
|
||||||
|
System.out.println("Error: " + error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String sentRawTransactionID = bitcoinClient.sendRawTransaction(srTx.hex());
|
||||||
|
System.out.println("Sent signedRawTx (txID): " + sentRawTransactionID);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user