Update 4_2__Interlude_Using_JQ.md

This commit is contained in:
Shannon Appelcline 2017-03-29 15:45:03 -07:00 committed by GitHub
parent e376c801e7
commit 911081a092

View File

@ -2,7 +2,7 @@
> **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
Creating a raw transaction revealed how more complex bitcoin-cli results can't be easily saved into command-line variables. The answer is JQ, which allows you to filter out individual elements from more complex JSON data.
Creating a raw transaction revealed how more complex bitcoin-cli results can't easily be saved into command-line variables. The answer is JQ, which allows you to filter out individual elements from more complex JSON data.
## Install JQ
@ -13,6 +13,8 @@ Once you've downloaded the binary, you can install it on your system:
$ mv jq-linux64 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."
## Use JQ to Access a JSON Object Value by Key
**Usage Example:** _Capture the hex from a signed raw transaction._
@ -25,30 +27,30 @@ $ bitcoin-cli signrawtransaction $rawtxhex
"complete": true
}
```
JQ can be easily used to capture information from a JSON Object of this sort.
Fortunately, JQ can easily capture data of that sort!
To start with, always run `jq` at the backend of a pipe, and always use the standard invocation of `jq -r .`. The `-r` tells JQ to produce raw output, which will work for command-line variables and future reuse, while the `.` tells jq to ouput.
To use JQ, run `jq` at the backend of a pipe, and always use the standard invocation of `jq -r '.'`. The `-r` tells JQ to produce raw output, which will work for command-line variables, while the `.` tells jq to ouput. We protected it in `' '` because we'll need that protection later.
To capture a specific key-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 signrawtransaction $rawtxhex | jq -r '.hex'
0200000001735dfa1584b930a78ad2c1d6db72dd2a80ae5e5d552ad97e19f1d50d41fdd6d8000000006a47304402202210ce4b2a037da02622c380278cd79fec4e0e016e66f3eb894a2dcbb9ee998f02202cac167e6abdbbf08af139fb7c6b86e9d2e58e5516cd566ae2d54953ead9923b012102111bb978a3c93a00038ae344a1a017d7fee8a9be9d0558b5793ce6f440704a96ffffffff01b0e78604000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac00000000
```
With that tool in hand, you can capture information from JSON objects to command-line variables, as usual:
```
$ signedtx=$(bitcoin-cli signrawtransaction $rawtxhex | jq -r .hex)
$ signedtx=$(bitcoin-cli signrawtransaction $rawtxhex | jq -r '.hex')
$ echo $signedtx
0200000001735dfa1584b930a78ad2c1d6db72dd2a80ae5e5d552ad97e19f1d50d41fdd6d8000000006a47304402202210ce4b2a037da02622c380278cd79fec4e0e016e66f3eb894a2dcbb9ee998f02202cac167e6abdbbf08af139fb7c6b86e9d2e58e5516cd566ae2d54953ead9923b012102111bb978a3c93a00038ae344a1a017d7fee8a9be9d0558b5793ce6f440704a96ffffffff01b0e78604000000001976a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac00000000
$ bitcoin-cli sendrawtransaction $signedtx
3f9ccb6e16663e66dc119de1866610cc4f7a83079bfec2abf0598ed3adf10a78
```
## Use JQ to Access a JSON Array Value by Key
## Use JQ to Access a JSON Object Value in an Array by Key
**Usage Example:** _Capture the txid and vout for a selected UTXO._
So what happens when you instead use `jq` to parse a JSON Array? The `listunspent` command offers a great example, because it'll usually contain a number of different transactions.
Grabbing data out of a JSON object is easy, but 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.
When working with an array, the first thing you need to tell JQ is which index to access. For example, `jq '.[0]'` says to access the first element of an array. (Note that we're now protecting our jq arguments with single quotes, because of their increased complexity.)
When working with an 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 would then use `'.[0]'` to access that first element.
```
$ bitcoin-cli listunspent | jq -r '.[0]'
{
@ -63,7 +65,7 @@ $ bitcoin-cli listunspent | jq -r '.[0]'
"solvable": true
}
```
You can then capture an individual key-value from that array by (1) using a pipe within the JQ arguments; and then (2) listing the key-value you want to capture after the pipe.
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, just like 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'
2b5f5798359e0e23e02764588166f222d4ce056419dec83c743b72aad171d708
@ -79,11 +81,11 @@ $ echo $newvout
```
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!
## Use JQ to Access Multiple JSON Array Values by Key
## Use JQ to Access Multiple JSON Object Values in an Array by Key
**Usage Example:** _List the value of all unspent UTXOs._
You can alternatively use `jq '.[]'` to access all elements of a JSON Array. You might wonder why that's useful. It's because you can then capture _all_ of a specific value, from _all_ of the array indices, using the same methodology you just used to capture a simple element. For example, this would list all of our unspent funds:
Instead of accessing a specific value in a specific JSON object, you can 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'
0.76
@ -95,7 +97,7 @@ $ bitcoin-cli listunspent | jq -r '.[] | .amount'
**Usage Example:** _Sum the value of all unspent UTXOs._
At this point, you can start using JQ output for simple math. For example, adding up the amount 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}'
6.61
@ -103,11 +105,13 @@ $ bitcoin-cli getbalance
6.61000000
```
## Use JQ to Display Multiple JSON Object Values by Multiple Keys
## Use JQ to Display Multiple JSON Object Values in an Array by Multiple Keys
**Usage Example:** _List usage information for all UTXOs._
JQ is great for capturing individual elements from JSON objects and arrays, and placing those elements into variables. That will be its prime use in future sections. However, it can also be used to cut down huge amounts of information into reasonable amounts of information.
JQ is great for capturing individual elements from JSON objects and arrays and placing those elements into variables. That will be its prime use in future sections. However, it can also be used to cut down huge amounts of information output by `bitcoin-cli` into reasonable amounts of information.
The following shows you the most important information on your UTXOs:
```
$ bitcoin-cli listunspent | jq -r '.[] | .txid, .vout, .amount'
2b5f5798359e0e23e02764588166f222d4ce056419dec83c743b72aad171d708
@ -120,7 +124,11 @@ ec0598918f6f5476cb90365651e8a2724ef26f949290bbf196f41ed96092a52f
0
1.95
```
That gets you the info you need to decide what UTXOs to spend, but it's not very pretty. If you want to be really fancy, you can put those elements in `{}`s to build a new JSON object and then name each of the individual elements in the object. The result should be much more intuitive and less prone to error (though obviously, less useful for dumping info straight into variables). The following example shows this same look at `listunspent`, but with each object rebuilt as a new JSON Object, with all of the new key-values named with their old keys:
This can make it easy to decide what UTXOs to spend in a raw transaction, but it's not very pretty.
However, JQ lets you be fancy. You can use `{}`s to create new JSON objects (either for additional parsing or for pretty output). You also get to define a new key for each of your values. The resulting output should be much more intuitive and less prone to error (though obviously, less useful for dumping info straight into variables).
The following example shows the exact same parsing of `listunspent`, but with the abridgement of each old JSON object rebuilt as a new JSON object, with all of the new values named with their old keys:
```
$ bitcoin-cli listunspent | jq -r '.[] | { txid: .txid, vout: .vout, amount: .amount }'
{
@ -139,7 +147,7 @@ $ bitcoin-cli listunspent | jq -r '.[] | { txid: .txid, vout: .vout, amount: .am
"amount": 1.95
}
```
You can of course rename your new keys as you see fit:
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 }'
{
@ -158,13 +166,13 @@ You can of course rename your new keys as you see fit:
"bitcoins": 1.95
}
```
## Use JQ to Access a JSON Object by Accessed Value
## Use JQ to Access JSON Objects by Looked-Up Value
**Usage Example:** _Automatically look up a UTXO being used in a transaction._
The JQ lookups so far have been fairly simple: you use a key to lookup one or more key-values in a JSON Object or Array. But what if you instead want to lookup a key-value in an object ... by another key-value? This sort of indirect lookup has real applicability when you're working with transactions built on existing UTXOs. For example, what if you wanted to figure out the value of a UTXO we're using, something that we know is vitally important in raw transactions?
The JQ lookups so far have been fairly simple: you use a key to lookup one or more values in a JSON object or array. But what if you instead want to lookup a value in a JSON object ... by another value? This sort of indirect lookup has real applicability when you're working with transactions built on existing UTXOs. For example, it can allow you to calculate the sum value of the UTXOs being used in a transaction, something that is vitally important.
To start with, we have a raw transaction. Note that this is a more complex raw transaction with two inputs and two outputs. We'll learn about making those in a few sections, but for now it'll serve as an example of how to pluck all of the information out of a transaction:
This example uses the following raw transaction. Note that this is a more complex raw transaction with two inputs and two outputs. We'll learn about making those in a few sections; for now, it's necessary to be able to offer robust examples. Note that unlike our previous examples, this one has two objects in its `vin` array and two in its `vout` array.
```
$ bitcoin-cli decoderawtransaction $rawtxhex
{
@ -228,7 +236,7 @@ $ bitcoin-cli decoderawtransaction $rawtxhex
### Retrieve the Value(s)
If we wanted to just retrieve the txid for the two UTXOs, we need to `jq` to the .vin key-value, then to its 0th array, then to that array's .txid key-value. Then do the same with the 1st. Easy:
Assume that we know exactly how this transaction is constructed: we know that it uses two UTXOs as input. To retrieve the txid for the two UTXOs, we could `jq` to the .vin key-value, then to its 0th array, then to that array's .txid key-value. Then we could do the same with the 1st array. Easy:
```
$ usedtxid1=$(bitcoin-cli decoderawtransaction $rawtxhex | jq -r '.vin | .[0] | .txid')
$ echo $usedtxid1
@ -237,7 +245,9 @@ $ usedtxid2=$(bitcoin-cli decoderawtransaction $rawtxhex | jq -r '.vin | .[1] |
$ echo $usedtxid2
ec0598918f6f5476cb90365651e8a2724ef26f949290bbf196f41ed96092a52f
```
However, it'd be better to have a general case that _automatically_ saved all the txids of our UTXOs. Fortunately, bash lets you nest a set of `()`s to put your output into an array. Here's an example
However, it would be better to have a general case that _automatically_ saved all the txids of our UTXOs.
We already know that we can access all of the `.txid`s by using an `.[]` array value:
```
usedtxid=($(bitcoin-cli decoderawtransaction $rawtxhex | jq -r '.vin | .[] | .txid'))
$ echo ${usedtxid[0]}
@ -245,10 +255,11 @@ $ echo ${usedtxid[0]}
$ echo ${usedtxid[1]}
ec0598918f6f5476cb90365651e8a2724ef26f949290bbf196f41ed96092a52f
```
The only real trick here is how we saved the information to the bash shell. Rather than saving to a variable with `$(commands)`, we instead saved to an array with `($(commands))`. We were then able to access the individual bash array elements with a `${variable[n]}` construction. We can instead access the whole array with `${variable[@]}`, which we'll use in the next section. (Yeah, no one ever said bash was pretty.)
### Retrieve the Related Object(s)
We can now step back to `listunspent`. To find our transactions, we need to look through the entire JSON Array (`[]`). We can then choose (`select`) individual JSON objects that include (`contain`) our txids. (The `select` and `contain` arguments show off some of the complexity of JSON, but for now just know that this particular invocation will work.)
We can now move back to `listunspent`. To find our transactions, we need to look through the entire JSON array (`[]`). We can then choose (`select`) individual JSON objects that include (`contains`) our txids. (The `select` and `contains` arguments show off some of the complexity of JSON, but for now just know that this particular invocation will work.)
Here we are picking out our two UTXOs, one at a time:
```
@ -308,14 +319,17 @@ for txid in ${usedtxid[@]}; do bitcoin-cli listunspent | jq -r '.[] | select (.t
**Usage Example:** _Automatically calculate the value of the UTXOs used in a transaction._
You can now take one further step, and request the .amount (or any other JSON key-value) from the Objects you're retrieving:
You can now go one step further, and request the .amount (or any other JSON key-value) from the objects you're retrieving. For this example, we're still using that `$usedtxid` variable we set:
```
$ usedtxid=($(bitcoin-cli decoderawtransaction $rawtxhex | jq -r '.vin | .[] | .txid'))
```
You can look at them individual with a `for` script:
```
$ for txid in ${usedtxid[@]}; do bitcoin-cli listunspent | jq -r '.[] | select (.txid | contains("'$txid'")) | .amount'; done
0.76
3.9
```
Using our earlier `awk` script, you can even add them up, to see how much money is in the UTXOs your transaction is spending:
You can sum them up with an `awk` script, to really see how much money is in the UTXOs your transaction is spending:
```
$ usedtxid=($(bitcoin-cli decoderawtransaction $rawtxhex | jq -r '.vin | .[] | .txid'))
$ for txid in ${usedtxid[@]}; do bitcoin-cli listunspent | jq -r '.[] | select (.txid | contains("'$txid'")) | .amount'; done | awk '{s+=$1} END {print s}'
@ -326,7 +340,9 @@ $ for txid in ${usedtxid[@]}; do bitcoin-cli listunspent | jq -r '.[] | select (
**Usage Example:** _Calculate the fee for a real transaction._
A few more lines of code which add out the values of your vout, then subtract your vout from your vin can be used to calculate your transaction fee:
A few more lines of code that calculate the value of the vout, then subtract the vout from the vin can be used to calculate the transaction fee.
Putting it all together gives us:
```
$ usedtxid=($(bitcoin-cli decoderawtransaction $rawtxhex | jq -r '.vin | .[] | .txid'))
$ btcin=$(for txid in ${usedtxid[@]}; do bitcoin-cli listunspent | jq -r '.[] | select (.txid | contains("'$txid'")) | .amount'; done | awk '{s+=$1} END {print s}')
@ -336,7 +352,7 @@ $ echo "$btcin-$btcout"| /usr/bin/bc
```
And that's also a good example of why you double-check your fees: we'd intended to send a transaction fee of 5,000 satoshis, but sent 95,000 satoshis instead. Whoops!
> **WARNING:** This was not a clever little lesson. We messed up our calculation of the the transaction fee when we wrote our raw transaction. It's *that* easy, then your money is gone.
> **WARNING:** This was not an intentional lesson. We really messed up our calculation of the transaction fee when we wrote our raw transaction. It's *that* easy, then your money is gone.
For more JSON magic (and if any of this isn't clear), please read the [JSON Manual](https://stedolan.github.io/jq/manual/) and the [JSON Cookbook](https://github.com/stedolan/jq/wiki/Cookbook). We'll be regularly using JQ in future examples.
@ -371,7 +387,9 @@ $ ./txfee-calc.sh $rawtxhex
## Make Some New Aliases
JQ code can be a little unwieldly, so you should consider adding some longer and more interesting invocations to your ~/.bash_profile. Any time you're looking throug a large mass of information in a JSON Object output from a bitcoin-cli command, consider writing an alias to strip it down to just what you want to see.
JQ code can be a little unwieldly, so you should consider adding some longer and more interesting invocations to your ~/.bash_profile.
Any time you're looking through a large mass of information in a JSON object output by a bitcoin-cli command, consider writing an alias to strip it down to just what you want to see.
```
alias btcunspent="bitcoin-cli listunspent | jq -r '.[] | { txid: .txid, vout: .vout, amount: .amount }'"
alias btctxfee="~/txfee-calc.sh"