Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added BIP 69: Lexicographical Indexing of Inputs and Outputs #157

Merged
merged 21 commits into from
Sep 19, 2015
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
61a9711
Added BIP 79
kristovatlas Jun 12, 2015
469e7e7
BIP #69 assigned by gmaxwell
kristovatlas Jun 24, 2015
f5b652d
BIP 69 not 79
kristovatlas Jun 24, 2015
9a0d3bb
Links to discussion, changed Acknowledgements
kristovatlas Jun 24, 2015
0ec82c9
Merge remote-tracking branch 'bitcoin/master'
kristovatlas Aug 18, 2015
5f07cd0
trying to fix git mixup with bip68
kristovatlas Aug 18, 2015
831a003
added list of open source implementations
kristovatlas Aug 18, 2015
d9acdcb
split sentences over lines for smaller diffs
dcousens Aug 20, 2015
66cb834
human-readable string representation is big-endian
dcousens Aug 20, 2015
8d2a159
sort by prevTxHash in little-endian, descending order, lexicographically
dcousens Aug 20, 2015
3420586
remove probability, specify behaviour for matching prevOut indices
dcousens Aug 20, 2015
c351ae0
remove mention of information typically found in wallets
dcousens Aug 20, 2015
c6ed18d
remove malleability foot note
dcousens Aug 20, 2015
c60771d
reword transaction outputs in-line with transaction inputs format
dcousens Aug 20, 2015
64a4418
remove random 104
dcousens Aug 20, 2015
5f75c82
add editor note
dcousens Aug 20, 2015
64f321e
dont include pseudo-code for lexicographical sorting
dcousens Aug 20, 2015
1d74463
Revert "remove malleability foot note"
dcousens Aug 28, 2015
8368e75
endianness not relevant, use byte order
dcousens Sep 9, 2015
fe9249a
Merge pull request #1 from dcousens/bip69fixes
Sep 11, 2015
42c0650
add EOF newlines per @luke-jr
kristovatlas Sep 19, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.mediawiki
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,12 @@ Those proposing changes should consider that ultimately consent may rest with th
| Standard
| Draft
|-
| [[bip-0069.mediawiki|69]]
| Lexicographical Indexing of Transaction Inputs and Outputs
| Kristov Atlas
| Standard
| Draft
|-
| [[bip-0070.mediawiki|70]]
| Payment protocol
| Gavin Andresen
Expand Down
134 changes: 134 additions & 0 deletions bip-0069.mediawiki
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<pre>
BIP: BIP: 69
Title: Lexicographical Indexing of Transaction Inputs and Outputs
Authors: Kristov Atlas <[email protected]>
Status: Draft
Type: Informational
Created: 2015-06-12
</pre>

==Abstract==

Currently there is no standard for bitcoin wallet clients when ordering transaction inputs and outputs. As a result, wallet clients often have a discernible blockchain fingerprint, and can leak private information about their users. By contrast, a standard for non-deterministic sorting could be difficult to audit. This document proposes deterministic lexicographical sorting, using hashes of previous transactions and output indices to sort transaction inputs, as well as values and scriptPubKeys to sort transaction outputs.

==Copyright==

This BIP is in the public domain.

==Motivation==

Currently, there is no clear standard for how wallet clients ought to order transaction inputs and outputs. Since wallet clients are left to their own devices to determine this ordering, they often leak information about their users’ finances. For example, a wallet client might naively order inputs based on when addresses were added to a wallet by the user through importing or random generation. Many wallets will place spending outputs first and change outputs second, leaking information about both the sender and receiver’s finances to passive blockchain observers. Such information should remain private not only for the benefit of consumers, but in higher order financial systems must be kept secret to prevent fraud. A researcher recently demonstrated this principle when he detected that Bitstamp leaked information when creating exchange transactions, enabling potential espionage among traders. [1]

One way to address these privacy weaknesses is by randomly ordering inputs and outputs. [2] After all, the order of inputs and outputs does not impact the function of the transaction they belong to, making random sorting viable. Unfortunately, it can be difficult to prove that this sorting process is genuinely randomly sorted based on code or run-time analysis, especially if the software is closed source. A malicious software developer can abuse the ordering of inputs and outputs as a side channel of leaking information. For example, if an attacker can patch a victim’s HD wallet client to order inputs and outputs based on the bits of a master private key, then the attacker can eventually steal all of the victim’s funds by monitoring the blockchain. Non-deterministic methods of sorting are difficult to audit because they are not repeatable.

The lack of standardization between wallet clients when ordering inputs and outputs can yield predictable quirks that characterize particular wallet clients or services. Such quirks create unique fingerprints that a privacy attacker can employ through simple passive blockchain observation.

The solution is to create an algorithm for sorting transaction inputs and outputs that is deterministic. Since it is deterministic, it should also be unambiguous — that is, given a particular transaction, the proper order of inputs and outputs should be obvious. To make this standard as widely applicable as possible, it should rely on information that is downloaded by both full nodes (with or without typical efficiency techniques such as pruning) and SPV nodes. In order to ensure that it does not leak confidential data, it must rely on information that is publicly accessible through the blockchain. The use of public blockchain information also allows a transaction to be sorted even when it is a multi-party transaction, such as in the example of a CoinJoin.

==Specification==

===Applicability===

This BIP applies to any transaction for which the order of its inputs and outputs does not impact the transaction’s function. Currently, this refers to any transaction that employs the SIGHASH_ALL signature hash type, in which signatures commit to the exact order of inputs and outputs. Transactions that use SIGHASH_ANYONECANPAY and/or SIGHASH_NONE may include inputs and/or outputs that are not signed; however, compliant software should still emit transactions with lexicographically sorted inputs and outputs, even though they may later be modified by others.

In the event that future protocol upgrades introduce new signature hash types, compliant software should apply the lexicographical ordering principle analogously.

While out of scope of this BIP, protocols that do require a specified order of inputs/outputs (e.g. due to use of SIGHASH_SINGLE) should consider the goals of this BIP and how best to adapt them to the specific needs of those protocols.

===Lexicographical Sorting===
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if an algorithmic description of lexicographical sorting is required, this entire section could be summarised (including the pseudocode) with just a link to the Lexicographical ordering Wikipedia entry.


Ordering of inputs and outputs will rely on the output of sorting functions. These functions can be defined as taking two inputs or two outputs as parameters and returning their appropriate ordering with respect to each other.

Byte arrays must be sorted with an algorithm that produces the same output as the following comparison algorithm:

<nowiki>#returns -1 if barr1 is lesser, 1 if barr1 is greater, and 0 if equal
def bytearr_cmp(barr1, barr2):
pos = 0
while (pos < len(barr1) and pos < len(barr2)):
if (barr1[pos] < barr2[pos]):
return -1;
elif (barr1[pos] > barr2[pos]):
return 1;
pos = pos + 1
#the shorter array will be ordered first
if (len(barr1) < len(barr2)):
return -1
elif (len(barr1) > len(barr2)):
return 1
else:
return 0</nowiki>

N.B. These comparisons do not need to operate in constant time since they are not processing secret information.

===Transaction Inputs===

104 Transaction inputs are defined by the hash of a previous transaction, the output index of of a UTXO from that previous transaction, the size of an unlocking script, the unlocking script, and a sequence number. [3] For sorting inputs, the hash of the previous transaction and the output index within that transaction are sufficient for sorting purposes; each transaction hash has an extremely high probability of being unique in the blockchain — this is enforced for coinbase transactions by BIP30 — and output indices within a transaction are unique. For the sake of efficiency, transaction hashes should be compared first before output indices, since output indices from different transactions are often equivalent, while all bytes of the transaction hash are effectively random variables.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This entire paragraph is quite verbose and seems more like a primer on things we already know in the bitcoin space. I'm not sure if its relevant to describing this process.


Hashes of previous transactions are considered for the purposes of this BIP in their little-endian, byte array format in order to match the traditional, human-readable string representation of the hashes. They must be sorted in accordance with the output of the bytearr_cmp() function above: the hash with the earliest lesser byte is ordered first, and shorter hashes are ordered before longer ones as a tie-breaker. In the event of two matching transaction hashes, output indices will be compared based on their integer value, with the smaller value ordered first. A further tie is extremely improbable for the aforementioned reasons.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A further tie is extremely improbable for the aforementioned reasons.

In this case, why not just define the behaviour and say that the expected behaviour is that they are equal?
Why do we need to have a gap in the spec here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shorter hashes are ordered before longer ones as a tie-breaker

Transaction hashes are always going to be 256-bit (32 bytes), as they are the direct output of SHA256. Why are we accounting for 'shorter hashes'?


Because the hash of previous transactions and output indices must be included in a signed transaction, wallet clients capable of signing transactions will necessarily have access to this data.

Transaction malleability will not negatively impact the correctness of this process. Even if a wallet client follows this process using unconfirmed UTXOs as inputs and an attacker changes modifies the blockchain’s record of the hash of the previous transaction, the wallet client will include the invalidated previous transaction hash in its input data, and will still correctly sort with respect to that invalidated hash.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is malleability even relevant? I don't foresee this being a common question to ask, but, I guess it doesn't hurt to leave it in.


===Transaction Outputs===

A transaction output is defined by its scriptPubKey and amount. [3] For sorting purposes, we will consider a scriptPubKey in its byte array representation, and a bitcoin amount in terms of their integer number of satoshis (smallest amount ordered first).

For the sake of efficiency, amounts will be considered first for sorting, since they contain fewer bytes of information (8 bytes) compared to a standard P2PKH scriptPubKey (25 bytes). [4] When the values are tied, the scriptPubKey is then considered. In the event of a tie between scriptPubKeys, sorting is irrelevant since the outputs are exactly equivalent.

===Examples===

Transaction 0a6a357e2f7796444e02638749d9611c008b253fb55f5dc88b739b230ed0c4c3:

Inputs:

<nowiki>0: 0e53ec5dfb2cb8a71fec32dc9a634a35b7e24799295ddd5278217822e0b31f57[0]
1: 26aa6e6d8b9e49bb0630aac301db6757c02e3619feb4ee0eea81eb1672947024[1]
2: 28e0fdd185542f2c6ea19030b0796051e7772b6026dd5ddccd7a2f93b73e6fc2[0]
3: 381de9b9ae1a94d9c17f6a08ef9d341a5ce29e2e60c36a52d333ff6203e58d5d[1]
4: 3b8b2f8efceb60ba78ca8bba206a137f14cb5ea4035e761ee204302d46b98de2[0]
5: 402b2c02411720bf409eff60d05adad684f135838962823f3614cc657dd7bc0a[1]
6: 54ffff182965ed0957dba1239c27164ace5a73c9b62a660c74b7b7f15ff61e7a[1]
7: 643e5f4e66373a57251fb173151e838ccd27d279aca882997e005016bb53d5aa[0]
8: 6c1d56f31b2de4bfc6aaea28396b333102b1f600da9c6d6149e96ca43f1102b1[1]
9: 7a1de137cbafb5c70405455c49c5104ca3057a1f1243e6563bb9245c9c88c191[0]
10: 7d037ceb2ee0dc03e82f17be7935d238b35d1deabf953a892a4507bfbeeb3ba4[1]
11: a5e899dddb28776ea9ddac0a502316d53a4a3fca607c72f66c470e0412e34086[0]
12: b4112b8f900a7ca0c8b0e7c4dfad35c6be5f6be46b3458974988e1cdb2fa61b8[0]
13: bafd65e3c7f3f9fdfdc1ddb026131b278c3be1af90a4a6ffa78c4658f9ec0c85[0]
14: de0411a1e97484a2804ff1dbde260ac19de841bebad1880c782941aca883b4e9[1]
15: f0a130a84912d03c1d284974f563c5949ac13f8342b8112edff52971599e6a45[0]
16: f320832a9d2e2452af63154bc687493484a0e7745ebd3aaf9ca19eb80834ad60[0]</nowiki>

Outputs:

<nowiki>0: 400057456 76a9144a5fba237213a062f6f57978f796390bdcf8d01588ac
1: 40000000000 76a9145be32612930b8323add2212a4ec03c1562084f8488ac</nowiki>

Transaction 28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f

Inputs:

<nowiki>0: 35288d269cee1941eaebb2ea85e32b42cdb2b04284a56d8b14dcc3f5c65d6055[0]
1: 35288d269cee1941eaebb2ea85e32b42cdb2b04284a56d8b14dcc3f5c65d6055[1]</nowiki>

Outputs:

<nowiki>0: 100000000 41046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac
1: 2400000000 41044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac</nowiki>

==Discussion==

* [[https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2015-June/008484.html|<nowiki>[Bitcoin-development]</nowiki> Lexicographical Indexing of Transaction Inputs and Outputs]]
* [[https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2015-June/008487.html|<nowiki>[Bitcoin-development] [RFC]</nowiki> Canonical input and output ordering in transactions]]

==References==

* [[https://bitcoinmagazine.com/20273/bitstamp-exchange-activity-trackable-due-multisig-wallet-implementation/|1: Bitstamp Info Leak]]
* [[https://github.com/OpenBitcoinPrivacyProject/wallet-ratings/blob/master/2015-1/criteria.md|2: OBPP Random Indexing as Countermeasure]]
* [[https://github.com/aantonop/bitcoinbook/blob/develop/ch05.asciidoc|3: Mastering Bitcoin]]
* [[https://en.bitcoin.it/wiki/Script|4: Bitcoin Wiki on Script]]

==Acknowledgements==

Danno Ferrin &lt;[email protected]&gt;, Sergio Demian Lerner &lt;[email protected]&gt;, Justus Ranvier &lt;[email protected]&gt;, and Peter Todd &lt;[email protected]&gt; contributed to the design and motivations for this BIP. A similar proposal was submitted to the Bitcoin-dev mailing list independently by Rusty Russell &lt;[email protected]&gt;
112 changes: 112 additions & 0 deletions bip-0069/bip-0069_examples.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import binascii

#returns -1 if barr1 is less, 1 if barr1 is greater, and 0 if equal
def bytearr_cmp(barr1, barr2):
pos = 0
while (pos < len(barr1) and pos < len(barr2)):
if (barr1[pos] < barr2[pos]):
return -1;
elif (barr1[pos] > barr2[pos]):
return 1;
pos = pos + 1
#the shorter array will be ordered first
if (len(barr1) < len(barr2)):
return -1
elif (len(barr1) > len(barr2)):
return 1
else:
return 0

#tuples: (prev_tx_hash_byte_arr_little_endian, prev_tx_output_index)
def input_cmp(input_tuple1, input_tuple2):
#test prev_tx_hash_byte_arr_little_endian first
prev_tx_hash_cmp = bytearr_cmp(input_tuple1[0], input_tuple2[0])
if (prev_tx_hash_cmp != 0):
return prev_tx_hash_cmp
#tie-breaker: prev_tx_output_index
if (input_tuple1[1] < input_tuple2[1]):
return -1
elif (input_tuple1[1] > input_tuple2[1]):
return 1
else:
raise ValueError('Matching previous transaction hash and previous transaction output index for two disinct inputs. Invalid!')

def sort_inputs(input_tuples):
return sorted(input_tuples, cmp=input_cmp)

def print_inputs(ordered_input_tuples):
index = 0
for prev_tx_hash_byte_arr_little_endian, prev_tx_output_index in ordered_input_tuples:
prev_tx_hash_hex = binascii.hexlify(bytearray(prev_tx_hash_byte_arr_little_endian))
print("%d: %s[%d]" % (index, prev_tx_hash_hex, prev_tx_output_index))
index = index + 1

#tuples: (amount, scriptPubKey_byte_arr)
def output_cmp(output_tuple1, output_tuple2):
#test amount first
if (output_tuple1[0] < output_tuple2[0]):
return -1
elif (output_tuple1[0] > output_tuple2[0]):
return 1
#tie-breaker: scriptPubKey_byte_arr
return bytearray_cmp(output_tuple1[1], output_tuple2[1])

def sort_outputs(output_tuples):
return sorted(output_tuples, cmp=output_cmp)

def print_outputs(ordered_output_tuples):
index = 0
for amount, scriptPubKey_byte_arr in ordered_output_tuples:
scriptPubKey_hex = binascii.hexlify(bytearray(scriptPubKey_byte_arr))
print("%d:\t%d\t%s" % (index, amount, scriptPubKey_hex))
index = index + 1

def main():
#reference data: https://blockchain.info/rawtx/0a6a357e2f7796444e02638749d9611c008b253fb55f5dc88b739b230ed0c4c3
tx_0a6a_input_tuples = [
# (prev_tx_hash_byte_arr_little_endian, prev_tx_output_index)
([0x64, 0x3e, 0x5f, 0x4e, 0x66, 0x37, 0x3a, 0x57, 0x25, 0x1f, 0xb1, 0x73, 0x15, 0x1e, 0x83, 0x8c, 0xcd, 0x27, 0xd2, 0x79, 0xac, 0xa8, 0x82, 0x99, 0x7e, 0x00, 0x50, 0x16, 0xbb, 0x53, 0xd5, 0xaa], 0),
([0x28, 0xe0, 0xfd, 0xd1, 0x85, 0x54, 0x2f, 0x2c, 0x6e, 0xa1, 0x90, 0x30, 0xb0, 0x79, 0x60, 0x51, 0xe7, 0x77, 0x2b, 0x60, 0x26, 0xdd, 0x5d, 0xdc, 0xcd, 0x7a, 0x2f, 0x93, 0xb7, 0x3e, 0x6f, 0xc2], 0),
([0xf0, 0xa1, 0x30, 0xa8, 0x49, 0x12, 0xd0, 0x3c, 0x1d, 0x28, 0x49, 0x74, 0xf5, 0x63, 0xc5, 0x94, 0x9a, 0xc1, 0x3f, 0x83, 0x42, 0xb8, 0x11, 0x2e, 0xdf, 0xf5, 0x29, 0x71, 0x59, 0x9e, 0x6a, 0x45], 0),
([0x0e, 0x53, 0xec, 0x5d, 0xfb, 0x2c, 0xb8, 0xa7, 0x1f, 0xec, 0x32, 0xdc, 0x9a, 0x63, 0x4a, 0x35, 0xb7, 0xe2, 0x47, 0x99, 0x29, 0x5d, 0xdd, 0x52, 0x78, 0x21, 0x78, 0x22, 0xe0, 0xb3, 0x1f, 0x57], 0),
([0x38, 0x1d, 0xe9, 0xb9, 0xae, 0x1a, 0x94, 0xd9, 0xc1, 0x7f, 0x6a, 0x08, 0xef, 0x9d, 0x34, 0x1a, 0x5c, 0xe2, 0x9e, 0x2e, 0x60, 0xc3, 0x6a, 0x52, 0xd3, 0x33, 0xff, 0x62, 0x03, 0xe5, 0x8d, 0x5d], 1),
([0xf3, 0x20, 0x83, 0x2a, 0x9d, 0x2e, 0x24, 0x52, 0xaf, 0x63, 0x15, 0x4b, 0xc6, 0x87, 0x49, 0x34, 0x84, 0xa0, 0xe7, 0x74, 0x5e, 0xbd, 0x3a, 0xaf, 0x9c, 0xa1, 0x9e, 0xb8, 0x08, 0x34, 0xad, 0x60], 0),
([0xde, 0x04, 0x11, 0xa1, 0xe9, 0x74, 0x84, 0xa2, 0x80, 0x4f, 0xf1, 0xdb, 0xde, 0x26, 0x0a, 0xc1, 0x9d, 0xe8, 0x41, 0xbe, 0xba, 0xd1, 0x88, 0x0c, 0x78, 0x29, 0x41, 0xac, 0xa8, 0x83, 0xb4, 0xe9], 1),
([0x3b, 0x8b, 0x2f, 0x8e, 0xfc, 0xeb, 0x60, 0xba, 0x78, 0xca, 0x8b, 0xba, 0x20, 0x6a, 0x13, 0x7f, 0x14, 0xcb, 0x5e, 0xa4, 0x03, 0x5e, 0x76, 0x1e, 0xe2, 0x04, 0x30, 0x2d, 0x46, 0xb9, 0x8d, 0xe2], 0),
([0x54, 0xff, 0xff, 0x18, 0x29, 0x65, 0xed, 0x09, 0x57, 0xdb, 0xa1, 0x23, 0x9c, 0x27, 0x16, 0x4a, 0xce, 0x5a, 0x73, 0xc9, 0xb6, 0x2a, 0x66, 0x0c, 0x74, 0xb7, 0xb7, 0xf1, 0x5f, 0xf6, 0x1e, 0x7a], 1),
([0xba, 0xfd, 0x65, 0xe3, 0xc7, 0xf3, 0xf9, 0xfd, 0xfd, 0xc1, 0xdd, 0xb0, 0x26, 0x13, 0x1b, 0x27, 0x8c, 0x3b, 0xe1, 0xaf, 0x90, 0xa4, 0xa6, 0xff, 0xa7, 0x8c, 0x46, 0x58, 0xf9, 0xec, 0x0c, 0x85], 0),
([0xa5, 0xe8, 0x99, 0xdd, 0xdb, 0x28, 0x77, 0x6e, 0xa9, 0xdd, 0xac, 0x0a, 0x50, 0x23, 0x16, 0xd5, 0x3a, 0x4a, 0x3f, 0xca, 0x60, 0x7c, 0x72, 0xf6, 0x6c, 0x47, 0x0e, 0x04, 0x12, 0xe3, 0x40, 0x86], 0),
([0x7a, 0x1d, 0xe1, 0x37, 0xcb, 0xaf, 0xb5, 0xc7, 0x04, 0x05, 0x45, 0x5c, 0x49, 0xc5, 0x10, 0x4c, 0xa3, 0x05, 0x7a, 0x1f, 0x12, 0x43, 0xe6, 0x56, 0x3b, 0xb9, 0x24, 0x5c, 0x9c, 0x88, 0xc1, 0x91], 0),
([0x26, 0xaa, 0x6e, 0x6d, 0x8b, 0x9e, 0x49, 0xbb, 0x06, 0x30, 0xaa, 0xc3, 0x01, 0xdb, 0x67, 0x57, 0xc0, 0x2e, 0x36, 0x19, 0xfe, 0xb4, 0xee, 0x0e, 0xea, 0x81, 0xeb, 0x16, 0x72, 0x94, 0x70, 0x24], 1),
([0x40, 0x2b, 0x2c, 0x02, 0x41, 0x17, 0x20, 0xbf, 0x40, 0x9e, 0xff, 0x60, 0xd0, 0x5a, 0xda, 0xd6, 0x84, 0xf1, 0x35, 0x83, 0x89, 0x62, 0x82, 0x3f, 0x36, 0x14, 0xcc, 0x65, 0x7d, 0xd7, 0xbc, 0x0a], 1),
([0x7d, 0x03, 0x7c, 0xeb, 0x2e, 0xe0, 0xdc, 0x03, 0xe8, 0x2f, 0x17, 0xbe, 0x79, 0x35, 0xd2, 0x38, 0xb3, 0x5d, 0x1d, 0xea, 0xbf, 0x95, 0x3a, 0x89, 0x2a, 0x45, 0x07, 0xbf, 0xbe, 0xeb, 0x3b, 0xa4], 1),
([0x6c, 0x1d, 0x56, 0xf3, 0x1b, 0x2d, 0xe4, 0xbf, 0xc6, 0xaa, 0xea, 0x28, 0x39, 0x6b, 0x33, 0x31, 0x02, 0xb1, 0xf6, 0x00, 0xda, 0x9c, 0x6d, 0x61, 0x49, 0xe9, 0x6c, 0xa4, 0x3f, 0x11, 0x02, 0xb1], 1),
([0xb4, 0x11, 0x2b, 0x8f, 0x90, 0x0a, 0x7c, 0xa0, 0xc8, 0xb0, 0xe7, 0xc4, 0xdf, 0xad, 0x35, 0xc6, 0xbe, 0x5f, 0x6b, 0xe4, 0x6b, 0x34, 0x58, 0x97, 0x49, 0x88, 0xe1, 0xcd, 0xb2, 0xfa, 0x61, 0xb8], 0)]
tx_0a6a_sorted_input_tuples = sort_inputs(tx_0a6a_input_tuples)
print_inputs(tx_0a6a_sorted_input_tuples)

tx_0a6a_output_tuples = [
# (amount, scriptPubKey_byte_arr)
(400057456, [0x76, 0xa9, 0x14, 0x4a, 0x5f, 0xba, 0x23, 0x72, 0x13, 0xa0, 0x62, 0xf6, 0xf5, 0x79, 0x78, 0xf7, 0x96, 0x39, 0x0b, 0xdc, 0xf8, 0xd0, 0x15, 0x88, 0xac]),
(40000000000, [0x76, 0xa9, 0x14, 0x5b, 0xe3, 0x26, 0x12, 0x93, 0x0b, 0x83, 0x23, 0xad, 0xd2, 0x21, 0x2a, 0x4e, 0xc0, 0x3c, 0x15, 0x62, 0x08, 0x4f, 0x84, 0x88, 0xac])]
tx_0a6a_sorted_output_tuples = sort_outputs(tx_0a6a_output_tuples)
print_outputs(tx_0a6a_sorted_output_tuples)

#reference data: https://blockchain.info/rawtx/28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f thanks @quantabytes!
tx_2820_input_tuples = [
# (prev_tx_hash, prev_tx_output_index)
("35288d269cee1941eaebb2ea85e32b42cdb2b04284a56d8b14dcc3f5c65d6055", 0),
("35288d269cee1941eaebb2ea85e32b42cdb2b04284a56d8b14dcc3f5c65d6055", 1)] #duplicate prev_tx_hash

tx_2820_sorted_input_tuples = sort_inputs(tx_2820_input_tuples)
print_inputs(tx_2820_sorted_input_tuples)

tx_2820_output_tuples = [
# (amount, scriptPubKey_byte_arr)
(100000000, [0x41, 0x04, 0x6a, 0x07, 0x65, 0xb5, 0x86, 0x56, 0x41, 0xce, 0x08, 0xdd, 0x39, 0x69, 0x0a, 0xad, 0xe2, 0x6d, 0xfb, 0xf5, 0x51, 0x14, 0x30, 0xca, 0x42, 0x8a, 0x30, 0x89, 0x26, 0x13, 0x61, 0xce, 0xf1, 0x70, 0xe3, 0x92, 0x9a, 0x68, 0xae, 0xe3, 0xd8, 0xd4, 0x84, 0x8b, 0x0c, 0x51, 0x11, 0xb0, 0xa3, 0x7b, 0x82, 0xb8, 0x6a, 0xd5, 0x59, 0xfd, 0x2a, 0x74, 0x5b, 0x44, 0xd8, 0xe8, 0xd9, 0xdf, 0xdc, 0x0c, 0xac]),
(2400000000, [0x41, 0x04, 0x4a, 0x65, 0x6f, 0x06, 0x58, 0x71, 0xa3, 0x53, 0xf2, 0x16, 0xca, 0x26, 0xce, 0xf8, 0xdd, 0xe2, 0xf0, 0x3e, 0x8c, 0x16, 0x20, 0x2d, 0x2e, 0x8a, 0xd7, 0x69, 0xf0, 0x20, 0x32, 0xcb, 0x86, 0xa5, 0xeb, 0x5e, 0x56, 0x84, 0x2e, 0x92, 0xe1, 0x91, 0x41, 0xd6, 0x0a, 0x01, 0x92, 0x8f, 0x8d, 0xd2, 0xc8, 0x75, 0xa3, 0x90, 0xf6, 0x7c, 0x1f, 0x6c, 0x94, 0xcf, 0xc6, 0x17, 0xc0, 0xea, 0x45, 0xaf, 0xac])]
tx_2820_sorted_output_tuples = sort_outputs(tx_2820_output_tuples)
print_outputs(tx_2820_output_tuples)

if __name__ == "__main__":
main()