From 08055ea1638d4513c3196225aa2bc6aca8b58fb8 Mon Sep 17 00:00:00 2001 From: "Andreas M. Antonopoulos" Date: Fri, 29 Aug 2014 12:35:42 -0400 Subject: [PATCH] get_utxo and select_utxo --- ch05.asciidoc | 57 +++++++++++++++++++++++++++++++++++-- code/get-utxo.py | 28 +++++++++++++++++++ code/select-utxo.py | 68 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 code/get-utxo.py create mode 100644 code/select-utxo.py diff --git a/ch05.asciidoc b/ch05.asciidoc index e8d12bae..03fdb853 100644 --- a/ch05.asciidoc +++ b/ch05.asciidoc @@ -78,7 +78,6 @@ The UTXO consumed by a transaction are called transaction inputs, while the UTXO The exception to the output and input chain is a special type of transaction called the _coinbase_ transaction, which is the first transaction in each block. This transaction is placed there by the "winning" miner and creates brand-new bitcoin payable to that miner as a reward for mining. This is how bitcoin's money supply is created during the mining process as we will see in <>. - [TIP] ==== What comes first? Inputs or outputs, the chicken or the egg? Strictly speaking, outputs come first because coinbase transactions, which generate new bitcoin, have no inputs and create outputs from nothing. @@ -108,6 +107,32 @@ The transaction scripting language, used in the locking script mentioned above, | Variable | Locking-Script | A script defining the conditions needed to spend the output |======= +In the example below, we use the blockchain.info API to find the unspent outputs (UTXO) of a specific address: +[[get_utxo]] +.A script that calls the blockchain.info API to find the UTXO related to an address +==== +[source, python] +---- +include::code/get-utxo.py[] +---- +==== + +Running the script, we see a list of transaction IDs, a colon, the index number of the specific unspent transaction output (UTXO), and the value of that UTXO in Satoshis. The locking script is not shown in this output: + +[[get_utxo_run]] +.Running the get-utxo.py script +==== +[source,bash] +---- +$ python get-utxo.py +ebadfaa92f1fd29e2fe296eda702c48bd11ffd52313e986e99ddad9084062167:1 - 8000000 Satoshis +6596fd070679de96e405d52b51b8e1d644029108ec4cbfe451454486796a1ecf:0 - 16050000 Satoshis +74d788804e2aae10891d72753d1520da1206e6f4f20481cc1555b7f2cb44aca0:0 - 5000000 Satoshis +b2affea89ff82557c60d635a2a3137b8f88f12ecec85082f7d0a1f82ee203ac4:0 - 10000000 Satoshis +... +---- +==== + ===== Spending Conditions (Encumbrances) Transaction outputs associate a specific amount (in satoshis) to a specific _encumbrance_ or locking-script that defines the condition that must be met to spend that amount. In most cases the locking script will lock the output to a specific bitcoin address, thereby transferring ownership of that amount to the new owner. When Alice paid Bob's Cafe for a cup of coffee, her transaction created a 0.015 bitcoin output _encumbered_ or locked to the Cafe's bitcoin address. That 0.015 bitcoin output was recorded on the blockchain and became part of the Unspent Transaction Output set, meaning it showed in Bob's wallet as part of the available balance. When Bob chooses to spend that amount, his transaction will release the encumbrance, unlocking the output by providing an unlocking script containing a signature from Bob's private key. @@ -117,7 +142,33 @@ Transaction outputs associate a specific amount (in satoshis) to a specific _enc In simple terms, transaction inputs are pointers to UTXO. They point to a specific UTXO by reference to the transaction hash and sequence number where the UTXO is recorded in the blockchain. To spend UTXO, a transaction input also includes unlocking-scripts that satisfy the spending conditions set by the UTXO. The unlocking script is usually a signature proving ownership of the bitcoin address that is in the locking script. -When a user makes a payment, their wallet constructs a transaction by selecting from the available UTXO. For example, to make a 0.015 bitcoin payment, the wallet app may select a 0.01 UTXO and a 0.005 UTXO, using them both to add up to the desired payment amount. The wallet then produces unlocking scripts containing signatures for each of the UTXO, thereby making them spendable by satisfying their locking script conditions. The wallet adds these UTXO references and unlocking scripts as inputs to the transaction. +When a user makes a payment, their wallet constructs a transaction by selecting from the available UTXO. For example, to make a 0.015 bitcoin payment, the wallet app may select a 0.01 UTXO and a 0.005 UTXO, using them both to add up to the desired payment amount. + +In the example below, we show the use of a "greedy" algorithm to select from available UTXO in order to make a specific payment amount. In the example, the available UTXO are provided as a constant array, but in reality, the available UTXO would be retrieved with an RPC call to Bitcoin Core, or to a third-party API as shown in <>. + +[[select_utxo]] +.A script for calculating how much total bitcoin will be issued +==== +[source, python] +---- +include::code/select-utxo.py[] +---- +==== + +If we run the select-utxo.py script without a parameter it will attempt to construct a set of UTXO (and change) for a payment of 55000000 Satoshis (0.55 bitcoin). If you provide a target payment amount as a parameter, the script will select UTXO to make that target payment amount. Below, we run the script trying to make a payment of 0.5 bitcoin or 50000000 Satoshis: + +[[select_utxo_run]] +.Running the select-utxo.py script +==== +[source,bash] +---- +$ python select-utxo.py 50000000 +For transaction amount 50000000 Satoshis (0.500000 bitcoin) use: +([<7dbc497969c7475e45d952c4a872e213fb15d45e5cd3473c386a71a1b0c136a1:0 with 25000000 Satoshis>, <7f42eda67921ee92eae5f79bd37c68c9cb859b899ce70dba68c48338857b7818:0 with 16100000 Satoshis>, <6596fd070679de96e405d52b51b8e1d644029108ec4cbfe451454486796a1ecf:0 with 16050000 Satoshis>], 'Change: 7150000 Satoshis') +---- +==== + +The wallet then produces unlocking scripts containing signatures for each of the UTXO, thereby making them spendable by satisfying their locking script conditions. The wallet adds these UTXO references and unlocking scripts as inputs to the transaction. [[tx_in_structure]] .The structure of a transaction input @@ -131,7 +182,7 @@ When a user makes a payment, their wallet constructs a transaction by selecting | 4 bytes | Sequence Number | Currently-disabled Tx-replacement feature, set to 0xFFFFFFFF |======= -Note: The sequence number is used to override a transaction prior to the expiration of the transaction locktime, which is a feature that is currently disabled in bitcoin. Most transactions set this value to the maximum integer value (0xFFFFFFFF) and it is ignored by the bitcoin network. If the transaction has a non-zero locktime, at least one of its inputs must have a sequence number below 0xFFFFFFFF in order to enable locktime. +Note: The sequence number is used to override a transaction prior to the expiration of the transaction locktime, which is a feature that is currently disabled in bitcoin. Most transactions set this value to the maximum integer value (0xFFFFFFFF) and it is ignored by the bitcoin network. If the transaction has a non-zero locktime, at least one of its inputs must have a sequence number below 0xFFFFFFFF in order to enable locktime. [[tx_fees]] ==== Transaction Fees diff --git a/code/get-utxo.py b/code/get-utxo.py new file mode 100644 index 00000000..285aba4c --- /dev/null +++ b/code/get-utxo.py @@ -0,0 +1,28 @@ +# get unspent outputs from blockchain API + +import json +import requests + +# example address +address = '1Dorian4RoXcnBv9hnQ4Y2C1an6NJ4UrjX' + +# The API URL is https://blockchain.info/unspent?active=
+# It returns a JSON object with a list "unspent_outputs", containing UTXO, like this: +#{ "unspent_outputs":[ +# { +# "tx_hash":"ebadfaa92f1fd29e2fe296eda702c48bd11ffd52313e986e99ddad9084062167", +# "tx_index":51919767, +# "tx_output_n": 1, +# "script":"76a9148c7e252f8d64b0b6e313985915110fcfefcf4a2d88ac", +# "value": 8000000, +# "value_hex": "7a1200", +# "confirmations":28691 +# }, +# ... +#]} + +resp = requests.get('https://blockchain.info/unspent?active=%s' % address) +utxo_set = json.loads(resp.text)["unspent_outputs"] + +for utxo in utxo_set: + print "%s:%d - %ld Satoshis" % (utxo['tx_hash'], utxo['tx_output_n'], utxo['value']) diff --git a/code/select-utxo.py b/code/select-utxo.py new file mode 100644 index 00000000..a8557f69 --- /dev/null +++ b/code/select-utxo.py @@ -0,0 +1,68 @@ +# Selects outputs from a UTXO list using a greedy algorithm. + +from sys import argv + +class OutputInfo: + + def __init__(self, tx_hash, tx_index, value): + self.tx_hash = tx_hash + self.tx_index = tx_index + self.value = value + + def __repr__(self): + return "<%s:%s with %s Satoshis>" % (self.tx_hash, self.tx_index, + self.value) + +# Select optimal outputs for a send from unspent outputs list. +# Returns output list and remaining change to be sent to +# a change address. +def select_outputs_greedy(unspent, min_value): + # Fail if empty. + if not unspent: + return None + # Partition into 2 lists. + lessers = [utxo for utxo in unspent if utxo.value < min_value] + greaters = [utxo for utxo in unspent if utxo.value >= min_value] + key_func = lambda utxo: utxo.value + if greaters: + # Not-empty. Find the smallest greater. + min_greater = min(greaters) + change = min_greater.value - min_value + return [min_greater], change + # Not found in greaters. Try several lessers instead. + # Rearrange them from biggest to smallest. We want to use the least + # amount of inputs as possible. + lessers.sort(key=key_func, reverse=True) + result = [] + accum = 0 + for utxo in lessers: + result.append(utxo) + accum += utxo.value + if accum >= min_value: + change = accum - min_value + return result, "Change: %d Satoshis" % change + # No results found. + return None, 0 + +def main(): + unspent = [ + OutputInfo("ebadfaa92f1fd29e2fe296eda702c48bd11ffd52313e986e99ddad9084062167", 1, 8000000), + OutputInfo("6596fd070679de96e405d52b51b8e1d644029108ec4cbfe451454486796a1ecf", 0, 16050000), + OutputInfo("b2affea89ff82557c60d635a2a3137b8f88f12ecec85082f7d0a1f82ee203ac4", 0, 10000000), + OutputInfo("7dbc497969c7475e45d952c4a872e213fb15d45e5cd3473c386a71a1b0c136a1", 0, 25000000), + OutputInfo("55ea01bd7e9afd3d3ab9790199e777d62a0709cf0725e80a7350fdb22d7b8ec6", 17, 5470541), + OutputInfo("12b6a7934c1df821945ee9ee3b3326d07ca7a65fd6416ea44ce8c3db0c078c64", 0, 10000000), + OutputInfo("7f42eda67921ee92eae5f79bd37c68c9cb859b899ce70dba68c48338857b7818", 0, 16100000), + ] + + if len(argv) > 1: + target = long(argv[1]) + else: + target = 55000000 + + print "For transaction amount %d Satoshis (%f bitcoin) use: " % (target, target/10.0**8) + print select_outputs_greedy(unspent, target) + +if __name__ == "__main__": + main() +