blockchain-parser-2 0.1.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- blockchain_parser/__init__.py +12 -0
- blockchain_parser/address.py +83 -0
- blockchain_parser/block.py +104 -0
- blockchain_parser/block_header.py +90 -0
- blockchain_parser/blockchain.py +302 -0
- blockchain_parser/index.py +81 -0
- blockchain_parser/input.py +78 -0
- blockchain_parser/output.py +145 -0
- blockchain_parser/script.py +164 -0
- blockchain_parser/tests/__init__.py +10 -0
- blockchain_parser/tests/data/bech32_p2wpkh.txt +1 -0
- blockchain_parser/tests/data/bech32_p2wsh.txt +1 -0
- blockchain_parser/tests/data/bip69_false.txt +1 -0
- blockchain_parser/tests/data/bip69_true.txt +1 -0
- blockchain_parser/tests/data/genesis_block.txt +1 -0
- blockchain_parser/tests/data/invalid_tx.txt +1 -0
- blockchain_parser/tests/data/large_tx.txt +1 -0
- blockchain_parser/tests/data/scripts_invalid.txt +1 -0
- blockchain_parser/tests/data/segwit.txt +1 -0
- blockchain_parser/tests/data/size_non_segwit.txt +1 -0
- blockchain_parser/tests/data/size_segwit.txt +1 -0
- blockchain_parser/tests/test_address.py +45 -0
- blockchain_parser/tests/test_block.py +55 -0
- blockchain_parser/tests/test_blockchain.py +79 -0
- blockchain_parser/tests/test_index.py +48 -0
- blockchain_parser/tests/test_output.py +66 -0
- blockchain_parser/tests/test_script.py +171 -0
- blockchain_parser/tests/test_transaction.py +138 -0
- blockchain_parser/tests/test_undo.py +70 -0
- blockchain_parser/tests/test_utils.py +55 -0
- blockchain_parser/tests/utils.py +9 -0
- blockchain_parser/transaction.py +196 -0
- blockchain_parser/undo.py +198 -0
- blockchain_parser/utils.py +152 -0
- blockchain_parser/utils_taproot.py +143 -0
- blockchain_parser_2-0.1.6.dist-info/METADATA +143 -0
- blockchain_parser_2-0.1.6.dist-info/RECORD +40 -0
- blockchain_parser_2-0.1.6.dist-info/WHEEL +5 -0
- blockchain_parser_2-0.1.6.dist-info/licenses/LICENSE +177 -0
- blockchain_parser_2-0.1.6.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Copyright (C) 2015-2016 The bitcoin-blockchain-parser developers
|
|
2
|
+
#
|
|
3
|
+
# This file is part of bitcoin-blockchain-parser.
|
|
4
|
+
#
|
|
5
|
+
# It is subject to the license terms in the LICENSE file found in the top-level
|
|
6
|
+
# directory of this distribution.
|
|
7
|
+
#
|
|
8
|
+
# No part of bitcoin-blockchain-parser, including this file, may be copied,
|
|
9
|
+
# modified, propagated, or distributed except according to the terms contained
|
|
10
|
+
# in the LICENSE file.
|
|
11
|
+
|
|
12
|
+
from .utils import decode_compactsize, decode_uint32, format_hash
|
|
13
|
+
from .script import Script
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Input(object):
|
|
17
|
+
"""Represents a transaction input"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, raw_hex):
|
|
20
|
+
self._transaction_hash = None
|
|
21
|
+
self._transaction_index = None
|
|
22
|
+
self._script = None
|
|
23
|
+
self._sequence_number = None
|
|
24
|
+
self._witnesses = []
|
|
25
|
+
|
|
26
|
+
self._script_length, varint_length = decode_compactsize(raw_hex[36:])
|
|
27
|
+
self._script_start = 36 + varint_length
|
|
28
|
+
|
|
29
|
+
self.size = self._script_start + self._script_length + 4
|
|
30
|
+
self.hex = raw_hex[:self.size]
|
|
31
|
+
|
|
32
|
+
def add_witness(self, witness):
|
|
33
|
+
self._witnesses.append(witness)
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def from_hex(cls, hex_):
|
|
37
|
+
return cls(hex_)
|
|
38
|
+
|
|
39
|
+
def __repr__(self):
|
|
40
|
+
return "Input(%s,%d)" % (self.transaction_hash, self.transaction_index)
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def transaction_hash(self):
|
|
44
|
+
"""Returns the hash of the transaction containing the output
|
|
45
|
+
redeemed by this input"""
|
|
46
|
+
if self._transaction_hash is None:
|
|
47
|
+
self._transaction_hash = format_hash(self.hex[:32])
|
|
48
|
+
return self._transaction_hash
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def transaction_index(self):
|
|
52
|
+
"""Returns the index of the output inside the transaction that is
|
|
53
|
+
redeemed by this input"""
|
|
54
|
+
if self._transaction_index is None:
|
|
55
|
+
self._transaction_index = decode_uint32(self.hex[32:36])
|
|
56
|
+
return self._transaction_index
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def sequence_number(self):
|
|
60
|
+
"""Returns the input's sequence number"""
|
|
61
|
+
if self._sequence_number is None:
|
|
62
|
+
self._sequence_number = decode_uint32(
|
|
63
|
+
self.hex[self.size-4:self.size]
|
|
64
|
+
)
|
|
65
|
+
return self._sequence_number
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def script(self):
|
|
69
|
+
"""Returns a Script object representing the redeem script"""
|
|
70
|
+
if self._script is None:
|
|
71
|
+
end = self._script_start + self._script_length
|
|
72
|
+
self._script = Script.from_hex(self.hex[self._script_start:end])
|
|
73
|
+
return self._script
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def witnesses(self):
|
|
77
|
+
"""Return a list of witness data attached to this input, empty if non segwit"""
|
|
78
|
+
return self._witnesses
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Copyright (C) 2015-2016 The bitcoin-blockchain-parser developers
|
|
2
|
+
#
|
|
3
|
+
# This file is part of bitcoin-blockchain-parser.
|
|
4
|
+
#
|
|
5
|
+
# It is subject to the license terms in the LICENSE file found in the top-level
|
|
6
|
+
# directory of this distribution.
|
|
7
|
+
#
|
|
8
|
+
# No part of bitcoin-blockchain-parser, including this file, may be copied,
|
|
9
|
+
# modified, propagated, or distributed except according to the terms contained
|
|
10
|
+
# in the LICENSE file.
|
|
11
|
+
|
|
12
|
+
from .utils import decode_compactsize, decode_uint64
|
|
13
|
+
from .script import Script
|
|
14
|
+
from .address import Address
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Output(object):
|
|
18
|
+
"""Represents a Transaction output"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, raw_hex):
|
|
21
|
+
self._value = None
|
|
22
|
+
self._script = None
|
|
23
|
+
self._addresses = None
|
|
24
|
+
|
|
25
|
+
script_length, varint_size = decode_compactsize(raw_hex[8:])
|
|
26
|
+
script_start = 8 + varint_size
|
|
27
|
+
|
|
28
|
+
self._script_hex = raw_hex[script_start:script_start+script_length]
|
|
29
|
+
self.size = script_start + script_length
|
|
30
|
+
self._value_hex = raw_hex[:8]
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def from_hex(cls, hex_):
|
|
34
|
+
return cls(hex_)
|
|
35
|
+
|
|
36
|
+
def __repr__(self):
|
|
37
|
+
return "Output(satoshis=%d)" % self.value
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def value(self):
|
|
41
|
+
"""Returns the value of the output expressed in satoshis"""
|
|
42
|
+
if self._value is None:
|
|
43
|
+
self._value = decode_uint64(self._value_hex)
|
|
44
|
+
return self._value
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def script(self):
|
|
48
|
+
"""Returns the output's script as a Script object"""
|
|
49
|
+
if self._script is None:
|
|
50
|
+
self._script = Script.from_hex(self._script_hex)
|
|
51
|
+
return self._script
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def addresses(self):
|
|
55
|
+
"""Returns a list containing all the addresses mentioned
|
|
56
|
+
in the output's script
|
|
57
|
+
"""
|
|
58
|
+
if self._addresses is None:
|
|
59
|
+
self._addresses = []
|
|
60
|
+
if self.type == "pubkey":
|
|
61
|
+
address = Address.from_public_key(self.script.operations[0])
|
|
62
|
+
self._addresses.append(address)
|
|
63
|
+
elif self.type == "pubkeyhash":
|
|
64
|
+
address = Address.from_ripemd160(self.script.operations[2])
|
|
65
|
+
self._addresses.append(address)
|
|
66
|
+
elif self.type == "p2sh":
|
|
67
|
+
address = Address.from_ripemd160(self.script.operations[1],
|
|
68
|
+
type="p2sh")
|
|
69
|
+
self._addresses.append(address)
|
|
70
|
+
elif self.type == "multisig":
|
|
71
|
+
for operation in self.script.operations[1:-2]:
|
|
72
|
+
if isinstance(operation, (bytes, bytearray)):
|
|
73
|
+
self._addresses.append(
|
|
74
|
+
Address.from_public_key(operation)
|
|
75
|
+
)
|
|
76
|
+
elif self.type == "p2wpkh":
|
|
77
|
+
address = Address.from_bech32(self.script.operations[1], 0)
|
|
78
|
+
self._addresses.append(address)
|
|
79
|
+
elif self.type == "p2wsh":
|
|
80
|
+
address = Address.from_bech32(self.script.operations[1], 0)
|
|
81
|
+
self._addresses.append(address)
|
|
82
|
+
elif self.type == "p2tr":
|
|
83
|
+
address = Address.from_bech32m(self.script.operations[1], 1)
|
|
84
|
+
self._addresses.append(address)
|
|
85
|
+
return self._addresses
|
|
86
|
+
|
|
87
|
+
def is_return(self):
|
|
88
|
+
return self.script.is_return()
|
|
89
|
+
|
|
90
|
+
def is_p2sh(self):
|
|
91
|
+
return self.script.is_p2sh()
|
|
92
|
+
|
|
93
|
+
def is_pubkey(self):
|
|
94
|
+
return self.script.is_pubkey()
|
|
95
|
+
|
|
96
|
+
def is_pubkeyhash(self):
|
|
97
|
+
return self.script.is_pubkeyhash()
|
|
98
|
+
|
|
99
|
+
def is_multisig(self):
|
|
100
|
+
return self.script.is_multisig()
|
|
101
|
+
|
|
102
|
+
def is_unknown(self):
|
|
103
|
+
return self.script.is_unknown()
|
|
104
|
+
|
|
105
|
+
def is_p2wpkh(self):
|
|
106
|
+
return self.script.is_p2wpkh()
|
|
107
|
+
|
|
108
|
+
def is_p2wsh(self):
|
|
109
|
+
return self.script.is_p2wsh()
|
|
110
|
+
|
|
111
|
+
def is_p2tr(self):
|
|
112
|
+
return self.script.is_p2tr()
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def type(self):
|
|
116
|
+
"""Returns the output's script type as a string"""
|
|
117
|
+
# Fix for issue 11
|
|
118
|
+
if not self.script.script.is_valid():
|
|
119
|
+
return "invalid"
|
|
120
|
+
|
|
121
|
+
if self.is_pubkeyhash():
|
|
122
|
+
return "pubkeyhash"
|
|
123
|
+
|
|
124
|
+
if self.is_pubkey():
|
|
125
|
+
return "pubkey"
|
|
126
|
+
|
|
127
|
+
if self.is_p2sh():
|
|
128
|
+
return "p2sh"
|
|
129
|
+
|
|
130
|
+
if self.is_multisig():
|
|
131
|
+
return "multisig"
|
|
132
|
+
|
|
133
|
+
if self.is_return():
|
|
134
|
+
return "OP_RETURN"
|
|
135
|
+
|
|
136
|
+
if self.is_p2wpkh():
|
|
137
|
+
return "p2wpkh"
|
|
138
|
+
|
|
139
|
+
if self.is_p2wsh():
|
|
140
|
+
return "p2wsh"
|
|
141
|
+
|
|
142
|
+
if self.is_p2tr():
|
|
143
|
+
return "p2tr"
|
|
144
|
+
|
|
145
|
+
return "unknown"
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Copyright (C) 2015-2016 The bitcoin-blockchain-parser developers
|
|
2
|
+
#
|
|
3
|
+
# This file is part of bitcoin-blockchain-parser.
|
|
4
|
+
#
|
|
5
|
+
# It is subject to the license terms in the LICENSE file found in the top-level
|
|
6
|
+
# directory of this distribution.
|
|
7
|
+
#
|
|
8
|
+
# No part of bitcoin-blockchain-parser, including this file, may be copied,
|
|
9
|
+
# modified, propagated, or distributed except according to the terms contained
|
|
10
|
+
# in the LICENSE file.
|
|
11
|
+
|
|
12
|
+
from bitcoin.core.script import *
|
|
13
|
+
from binascii import b2a_hex
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def is_public_key(hex_data):
|
|
17
|
+
"""Given a bytes string, returns whether its is probably a bitcoin
|
|
18
|
+
public key or not. It doesn't actually ensures that the data is a valid
|
|
19
|
+
public key, justs that it looks like one
|
|
20
|
+
"""
|
|
21
|
+
if type(hex_data) != bytes:
|
|
22
|
+
return False
|
|
23
|
+
|
|
24
|
+
# Uncompressed public key
|
|
25
|
+
if len(hex_data) == 65 and int(hex_data[0]) == 4:
|
|
26
|
+
return True
|
|
27
|
+
|
|
28
|
+
# Compressed public key
|
|
29
|
+
if len(hex_data) == 33 and int(hex_data[0]) in [2, 3]:
|
|
30
|
+
return True
|
|
31
|
+
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class Script(object):
|
|
36
|
+
"""Represents a bitcoin script contained in an input or output"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, raw_hex):
|
|
39
|
+
self.hex = raw_hex
|
|
40
|
+
self._script = None
|
|
41
|
+
self._type = None
|
|
42
|
+
self._value = None
|
|
43
|
+
self._operations = None
|
|
44
|
+
self._addresses = None
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def from_hex(cls, hex_):
|
|
48
|
+
return cls(hex_)
|
|
49
|
+
|
|
50
|
+
def __repr__(self):
|
|
51
|
+
return "Script(%s)" % self.value
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def script(self):
|
|
55
|
+
"""Returns the underlying CScript object"""
|
|
56
|
+
if self._script is None:
|
|
57
|
+
self._script = CScript(self.hex)
|
|
58
|
+
|
|
59
|
+
return self._script
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def operations(self):
|
|
63
|
+
"""Returns the list of operations done by this script,
|
|
64
|
+
an operation is one of :
|
|
65
|
+
- a CScriptOP
|
|
66
|
+
- bytes data pushed to the stack
|
|
67
|
+
- an int pushed to the stack
|
|
68
|
+
If the script is invalid (some coinbase scripts are), an exception is
|
|
69
|
+
thrown
|
|
70
|
+
"""
|
|
71
|
+
if self._operations is None:
|
|
72
|
+
# Some coinbase scripts are garbage, they could not be valid
|
|
73
|
+
try:
|
|
74
|
+
self._operations = list(self.script)
|
|
75
|
+
except CScriptInvalidError:
|
|
76
|
+
self._operations = []
|
|
77
|
+
|
|
78
|
+
return self._operations
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def value(self):
|
|
82
|
+
"""Returns a string representation of the script"""
|
|
83
|
+
if self._value is None:
|
|
84
|
+
parts = []
|
|
85
|
+
try:
|
|
86
|
+
for operation in list(self.script):
|
|
87
|
+
if isinstance(operation, bytes):
|
|
88
|
+
parts.append(b2a_hex(operation).decode("ascii"))
|
|
89
|
+
else:
|
|
90
|
+
parts.append(str(operation))
|
|
91
|
+
|
|
92
|
+
self._value = " ".join(parts)
|
|
93
|
+
except CScriptInvalidError:
|
|
94
|
+
self._value = "INVALID_SCRIPT"
|
|
95
|
+
|
|
96
|
+
return self._value
|
|
97
|
+
|
|
98
|
+
def is_return(self):
|
|
99
|
+
return self.script.is_unspendable()
|
|
100
|
+
|
|
101
|
+
def is_p2sh(self):
|
|
102
|
+
return self.script.is_p2sh()
|
|
103
|
+
|
|
104
|
+
def is_p2wsh(self):
|
|
105
|
+
return self.script.is_witness_v0_scripthash()
|
|
106
|
+
|
|
107
|
+
def is_p2wpkh(self):
|
|
108
|
+
return self.script.is_witness_v0_keyhash()
|
|
109
|
+
|
|
110
|
+
def is_p2tr(self):
|
|
111
|
+
"""Return whether this is a BIP341 taproot output (OP_1 <32 bytes>)."""
|
|
112
|
+
operations = self.operations
|
|
113
|
+
return (
|
|
114
|
+
len(self.hex) == 34
|
|
115
|
+
and len(operations) == 2
|
|
116
|
+
and operations[0] == 1
|
|
117
|
+
and isinstance(operations[1], (bytes, bytearray))
|
|
118
|
+
and len(operations[1]) == 32
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
def is_pubkey(self):
|
|
122
|
+
return len(self.operations) == 2 \
|
|
123
|
+
and self.operations[-1] == OP_CHECKSIG \
|
|
124
|
+
and is_public_key(self.operations[0])
|
|
125
|
+
|
|
126
|
+
def is_pubkeyhash(self):
|
|
127
|
+
operations = self.operations
|
|
128
|
+
if len(self.hex) != 25 or len(operations) != 5:
|
|
129
|
+
return False
|
|
130
|
+
pushed = operations[2]
|
|
131
|
+
return (
|
|
132
|
+
operations[0] == OP_DUP
|
|
133
|
+
and operations[1] == OP_HASH160
|
|
134
|
+
and isinstance(pushed, (bytes, bytearray))
|
|
135
|
+
and len(pushed) == 20
|
|
136
|
+
and operations[-2] == OP_EQUALVERIFY
|
|
137
|
+
and operations[-1] == OP_CHECKSIG
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
def is_multisig(self):
|
|
141
|
+
operations = self.operations
|
|
142
|
+
if len(operations) < 4:
|
|
143
|
+
return False
|
|
144
|
+
m = operations[0]
|
|
145
|
+
n = operations[-2]
|
|
146
|
+
if not isinstance(m, int) or not isinstance(n, int):
|
|
147
|
+
return False
|
|
148
|
+
if m < 0 or n < 1 or m > n or n > 16:
|
|
149
|
+
return False
|
|
150
|
+
if operations[-1] != OP_CHECKMULTISIG:
|
|
151
|
+
return False
|
|
152
|
+
# OP_m, exactly n pubkeys, OP_n, OP_CHECKMULTISIG
|
|
153
|
+
if len(operations) != n + 3:
|
|
154
|
+
return False
|
|
155
|
+
for operation in operations[1:-2]:
|
|
156
|
+
if not is_public_key(operation):
|
|
157
|
+
return False
|
|
158
|
+
return True
|
|
159
|
+
|
|
160
|
+
def is_unknown(self):
|
|
161
|
+
return not self.is_pubkeyhash() and not self.is_pubkey() \
|
|
162
|
+
and not self.is_p2sh() and not self.is_multisig() \
|
|
163
|
+
and not self.is_return() and not self.is_p2wpkh() \
|
|
164
|
+
and not self.is_p2wsh() and not self.is_p2tr()
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Copyright (C) 2015-2016 The bitcoin-blockchain-parser developers
|
|
2
|
+
#
|
|
3
|
+
# This file is part of bitcoin-blockchain-parser.
|
|
4
|
+
#
|
|
5
|
+
# It is subject to the license terms in the LICENSE file found in the top-level
|
|
6
|
+
# directory of this distribution.
|
|
7
|
+
#
|
|
8
|
+
# No part of bitcoin-blockchain-parser, including this file, may be copied,
|
|
9
|
+
# modified, propagated, or distributed except according to the terms contained
|
|
10
|
+
# in the LICENSE file.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
020000000001016dee1ae581bf13134aa6aea8051b974c064bc2227094dc971561aac44a02ec500000000000ffffffff023cad17000000000017a914682ca28dc5e71f0139ad95a71c639c9e61c9c82e8773a33a0400000000160014a89e7f54db49b31364169f3e8982fcb5d3275e8b024830450221009424d2dcee3e8a00261cd3df8c7a103b14a8f0853075118329a21856f9cef36802207b9a2d51347bd2f73359375260e29fb67c67fa87825a625362a16b4a3c38f2350121030731907d43214f178bd4c9e279ac22043858001e220a935fff94f5fd3e8606a300000000
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
020000000001019425b8169d86af46b96afb6d8e1f40af44fedbb27fab196a88d1ae95b23230fd0200000000000000000260c084000000000017a914a0d015cec889e4cce8b359c6d607346e3985d66587fca67410000000002200206f49e7412b232a3d02c199caff40e9246d935a64799c369db577ecfd0929113804004730440220706ee05f17d6dee8ad42f3bf714a7e4911fed31325bad04284bc4ed7f3261e6b02203d72b089ee893164b203db722517a5bde37c791a3a256976c0379c73ad9f30110147304402207e36fcea59efb1b371dcabbe6766fb6334a498fe4769b263c00c7774df74b7f502200aea2ec5bab47ac16ed67cfc709665cfdcbd4f48d187a6a043267fe96789e1ce0169522102820ade490833f7c3e1abf944caed54b43f88f255f781a393ac4a6eedf04ff4862103c31b3287a9d6820a67c00af26c4955957ddf5fd21312c0ac8f11021b3f6192af2103ae579b44326ac0ba725038a982e3873f338c107b9d169172ca49e5bc7178469153ae00000000
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0100000011aad553bb1650007e9982a8ac79d227cd8c831e1573b11f25573a37664e5f3e64000000006a47304402205438cedd30ee828b0938a863e08d810526123746c1f4abee5b7bc2312373450c02207f26914f4275f8f0040ab3375bacc8c5d610c095db8ed0785de5dc57456591a601210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffffc26f3eb7932f7acddc5ddd26602b77e7516079b03090a16e2c2f5485d1fde028000000006b483045022100f81d98c1de9bb61063a5e6671d191b400fda3a07d886e663799760393405439d0220234303c9af4bad3d665f00277fe70cdd26cd56679f114a40d9107249d29c979401210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff456a9e597129f5df2e11b842833fc19a94c563f57449281d3cd01249a830a1f0000000006a47304402202310b00924794ef68a8f09564fd0bb128838c66bc45d1a3f95c5cab52680f166022039fc99138c29f6c434012b14aca651b1c02d97324d6bd9dd0ffced0782c7e3bd01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff571fb3e02278217852dd5d299947e2b7354a639adc32ec1fa7b82cfb5dec530e000000006b483045022100d276251f1f4479d8521269ec8b1b45c6f0e779fcf1658ec627689fa8a55a9ca50220212a1e307e6182479818c543e1b47d62e4fc3ce6cc7fc78183c7071d245839df01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff5d8de50362ff33d3526ac3602e9ee25c1a349def086a7fc1d9941aaeb9e91d38010000006b4830450221008768eeb1240451c127b88d89047dd387d13357ce5496726fc7813edc6acd55ac022015187451c3fb66629af38fdb061dfb39899244b15c45e4a7ccc31064a059730d01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff60ad3408b89ea19caf3abd5e74e7a084344987c64b1563af52242e9d2a8320f3000000006b4830450221009be4261ec050ebf33fa3d47248c7086e4c247cafbb100ea7cee4aa81cd1383f5022008a70d6402b153560096c849d7da6fe61c771a60e41ff457aac30673ceceafee01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffffe9b483a8ac4129780c88d1babe41e89dc10a26dedbf14f80a28474e9a11104de010000006b4830450221009bc40eee321b39b5dc26883f79cd1f5a226fc6eed9e79e21d828f4c23190c57e022078182fd6086e265589105023d9efa4cba83f38c674a499481bd54eee196b033f01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffffe28db9462d3004e21e765e03a45ecb147f136a20ba8bca78ba60ebfc8e2f8b3b000000006a47304402200fb572b7c6916515452e370c2b6f97fcae54abe0793d804a5a53e419983fae1602205191984b6928bf4a1e25b00e5b5569a0ce1ecb82db2dea75fe4378673b53b9e801210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff7a1ef65ff1b7b7740c662ab6c9735ace4a16279c23a1db5709ed652918ffff54010000006a47304402206bc218a925f7280d615c8ea4f0131a9f26e7fc64cff6eeeb44edb88aba14f1910220779d5d67231bc2d2d93c3c5ab74dcd193dd3d04023e58709ad7ffbf95161be6201210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff850cecf958468ca7ffa6a490afe13b8c271b1326b0ddc1fdfdf9f3c7e365fdba000000006a473044022047df98cc26bd2bfdc5b2b97c27aead78a214810ff023e721339292d5ce50823d02205fe99dc5f667908974dae40cc7a9475af7fa6671ba44f64a00fcd01fa12ab523012102ca46fa75454650afba1784bc7b079d687e808634411e4beff1f70e44596308a1ffffffff8640e312040e476cf6727c60ca3f4a3ad51623500aacdda96e7728dbdd99e8a5000000006a47304402205566aa84d3d84226d5ab93e6f253b57b3ef37eb09bb73441dae35de86271352a02206ee0b7f800f73695a2073a2967c9ad99e19f6ddf18ce877adf822e408ba9291e01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff91c1889c5c24b93b56e643121f7a05a34c10c5495c450504c7b5afcb37e11d7a000000006b483045022100df61d45bbaa4571cdd6c5c822cba458cdc55285cdf7ba9cd5bb9fc18096deb9102201caf8c771204df7fd7c920c4489da7bc3a60e1d23c1a97e237c63afe53250b4a01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff2470947216eb81ea0eeeb4fe19362ec05767db01c3aa3006bb499e8b6d6eaa26010000006a473044022031501a0b2846b8822a32b9947b058d89d32fc758e009fc2130c2e5effc925af70220574ef3c9e350cef726c75114f0701fd8b188c6ec5f84adce0ed5c393828a5ae001210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff0abcd77d65cc14363f8262898335f184d6da5ad060ff9e40bf201741022c2b40010000006b483045022100a6ac110802b699f9a2bff0eea252d32e3d572b19214d49d8bb7405efa2af28f1022033b7563eb595f6d7ed7ec01734e17b505214fe0851352ed9c3c8120d53268e9a01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffffa43bebbebf07452a893a95bfea1d5db338d23579be172fe803dce02eeb7c037d010000006b483045022100ebc77ed0f11d15fe630fe533dc350c2ddc1c81cfeb81d5a27d0587163f58a28c02200983b2a32a1014bab633bfc9258083ac282b79566b6b3fa45c1e6758610444f401210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffffb102113fa46ce949616d9cda00f6b10231336b3928eaaac6bfe42d1bf3561d6c010000006a473044022010f8731929a55c1c49610722e965635529ed895b2292d781b183d465799906b20220098359adcbc669cd4b294cc129b110fe035d2f76517248f4b7129f3bf793d07f01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffffb861fab2cde188499758346be46b5fbec635addfc4e7b0c8a07c0a908f2b11b4000000006a47304402207328142bb02ef5d6496a210300f4aea71f67683b842fa3df32cae6c88b49a9bb022020f56ddff5042260cfda2c9f39b7dec858cc2f4a76a987cd2dc25945b04e15fe01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff027064d817000000001976a9144a5fba237213a062f6f57978f796390bdcf8d01588ac00902f50090000001976a9145be32612930b8323add2212a4ec03c1562084f8488ac00000000
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
010000000255605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d28350000000049483045022100aa46504baa86df8a33b1192b1b9367b4d729dc41e389f2c04f3e5c7f0559aae702205e82253a54bf5c4f65b7428551554b2045167d6d206dfe6a2e198127d3f7df1501ffffffff55605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d2835010000004847304402202329484c35fa9d6bb32a55a70c0982f606ce0e3634b69006138683bcd12cbb6602200c28feb1e2555c3210f1dddb299738b4ff8bbe9667b68cb8764b5ac17b7adf0001ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac00000000
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
010000000b04a4abee8360f7a660bcf1c298496d927b6ad1f25dd12485e6a572e275cbd43f000000008b483045022100c6d59290df934bac08bc4124b06154ee21394d37ec958d145129a885dbd63eab0220036251120e4ceacc8e9ff13585049945c7dbdb85921a6e077a80c7e410931f4a014104f3ce8948c592690cb36a7e6e31534927569f41e45c77fbdb95fdb0e235717a295b2a281c04619139c453dd9139e951086325b4021bd28da6fc3da86943cd04cfffffffff21fd91e6dd64838eadce9bc0d687af571779c3af4b53bada845485466decf005000000008b48304502205d309b632a816f1f2479b8ca284301fce3fc8f979306722bba4cf28c8fa7ed76022100a3313ee59bbf0e61e29302b2ea597a993a73e93be841f2f7dd3f6e9623572793014104ca3b25750307a483c0cff8efb1413c98b4e18765485d94eac147f21f507169997a72aeb50f0b01ed6b468c9554143125f63c0b2b229a597e695718b86d711607ffffffff4dd6c46e3e68fc05243b6f894e2f3e4d2782d1c8a3d17b9a2c5e78912bbaf433010000008b483045022100f3f1d6c0e76a00e89a5278fa14eae8de93454174199f72c98aef54dabb2074d10220172dc0d6958efa8dc18c621f2d56fd0484ecb04e3d65e2284268916c1c324ade014104daeab6af397baf06fcc91b6c04a28ffb2cd80312e954a2a55f7c864094608032ef657e8296dfc2f86ecf28fe32925c0f648d3ef16b96934a34b29dd871ff057affffffff52aabc7fdee0588d63fff3fc00e20b00a87cffa6a37aad7168741b1f0e77bdff000000008b4830450220274a5a9577f0786fb17d373b6d1995a6cff752320bf4e3d5ee41b15fdd86ef2a022100fd1d0c0f43d05e479430c3c9bd5481f5be929a7f2b82d14292ef192653c3b94c014104988828ea7144ff00def9988e88aad98b1f22d01000fc4b52d1003c9b32de0534cff71c0a045098bb28f699bcc9db13533c0998eb2133085d37cac65b5bd74c1fffffffff5aab5581c575a56a803ef5d8698dc34014261d196ada1da3300731380d7e672d000000008b48304502201615a116a2e057f3f8f2a63340d0387b820a6f9d1067758f4f6cdec76c04951a022100a1ada9e03a0d9603743d41347ffd992642544d6dfdc51121b7bf622e12cb1547014104830b3b139b0a8707f348840b0fa358164869fbc0c423a2785cb0a18ff73517f8878cefd643e86e682eb93d07216f76993c9e980592de07c507c3a2659efff6aaffffffff74d7cd370a334aabee3b8328b67a63f1768583405e73ebd291dd86189983a61c000000008a4730440220432ff0f90a3f5372a8dc4b3d2e7165e110efa16b40784ea4d5ab866b736712ed02201a759b630d562f4dbc4a7d2fca406ad54e235564346bc360dec26d6e72bb60fa014104ca0bb72303672885ad953279309694c71574d50d2c6e1f5f1e7ab1b0bf3df67e5439b3f6e2501dda539812e414991ebaa547631374ce2bda9dd7211099886827ffffffff8941eb7fe688ff48357981c4756906e331c07f332429be5b2cfc7a945644f80f000000008a47304402204e074678b525927a8f459d1dc93ad738ce02c0ea77c50a749647032eda662eee02203c1a98e9b116dc2fe9e7325a5696ad8e3ad26d2d10503e6e3209f4f5f40f6e34014104f9baa86b46b04f2a2dc4270903bcca9444026b7f184d879f089462e332f260f6de8e05f08285a4a3c716220d9ec0f7e9fde354c779f32f00dd4d4cd8d4a1ac28ffffffff948be2e03f9537a35319e8cee2b7b9ed2769ca59734e5d4b42951ef7ba3bb4a8000000008b483045022100fcf1deaaa047e8f1b8b8ac783760ea5a0af15adbb9eed1c1748942a57510e27402204c278b12c4991480dc714af66395f5d8f42e411423bf27b024f62267c45a0d37014104cfd128c3f322b59c21d452d5c4c7b4d1a3237a2d31c6f4033b32db78c19cb342ffc151acc272a02a13ba985a523b655689d8118f502b8a7fb8df5fc7bd8458ceffffffffb2cae04cf238b4d47c614da5ce9948f089a02f1ff3e22cda6b9f9196bda87215010000008b483045022017a74373ef19756e275f2d987925a8bd22cfaf1c93eede7db63102dc73b7cfd0022100fd2f040d7d3ccd1a2188923f8c7560253aa2a910d8e9cd06a8a05fc69c12f6ce014104daeab6af397baf06fcc91b6c04a28ffb2cd80312e954a2a55f7c864094608032ef657e8296dfc2f86ecf28fe32925c0f648d3ef16b96934a34b29dd871ff057affffffffbe75c9f1faf79fb885018e3353ada4a7fb5c989bc26f031c5aee69d1eabad957000000008a4730440220360d35b0a98dd086d242f8976aa704b808541ad78ef5cba45e4eb73c1efbaaa7022025a3d981b9e0836a2493698b802bf7f0eae72de0252c7eac059ad14b1b27001b014104d969f0d456196f4c16945bc71fc460615002d1554fc8d774288aa8fc5c9eca1622a3dcb1e6a8314c94844e516c0543316b2e75d465c8fc2f0d0d20334e53f7dbffffffffbcbad95cac8bbe08538f7b12c240e96866358dfa819de6e940bb60ce7fae5c3d000000008b483045022100d916990888d0d26246b8d6cb944b1e8ebd6a4cdb2965ff7e4430883eef02c4e102205693b96aa1d6e9335f3cb2986b7f3a03357959e989570d03a56ccdb18a8d7e6d0141040840a410ed7afdc3e3fe3923ae388413bbd8a9089062403e0adce62212bccfe1f80400530121cfdba5b52f3e16397f1959b14a09089778afd5ff41f8a856980fffffffff02005039278c0400001976a914f1c87a5e8ff7d14e74b858089bf771c94b1b6db488ac00203d88792d00001976a914dcdf2f9892bfa1cb086530354eab3ba078a2f0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
010000000b04a4abee8360f7a660bcf1c298496d927b6ad1f25dd12485e6a572e275cbd43f000000008b483045022100c6d59290df934bac08bc4124b06154ee21394d37ec958d145129a885dbd63eab0220036251120e4ceacc8e9ff13585049945c7dbdb85921a6e077a80c7e410931f4a014104f3ce8948c592690cb36a7e6e31534927569f41e45c77fbdb95fdb0e235717a295b2a281c04619139c453dd9139e951086325b4021bd28da6fc3da86943cd04cfffffffff21fd91e6dd64838eadce9bc0d687af571779c3af4b53bada845485466decf005000000008b48304502205d309b632a816f1f2479b8ca284301fce3fc8f979306722bba4cf28c8fa7ed76022100a3313ee59bbf0e61e29302b2ea597a993a73e93be841f2f7dd3f6e9623572793014104ca3b25750307a483c0cff8efb1413c98b4e18765485d94eac147f21f507169997a72aeb50f0b01ed6b468c9554143125f63c0b2b229a597e695718b86d711607ffffffff4dd6c46e3e68fc05243b6f894e2f3e4d2782d1c8a3d17b9a2c5e78912bbaf433010000008b483045022100f3f1d6c0e76a00e89a5278fa14eae8de93454174199f72c98aef54dabb2074d10220172dc0d6958efa8dc18c621f2d56fd0484ecb04e3d65e2284268916c1c324ade014104daeab6af397baf06fcc91b6c04a28ffb2cd80312e954a2a55f7c864094608032ef657e8296dfc2f86ecf28fe32925c0f648d3ef16b96934a34b29dd871ff057affffffff52aabc7fdee0588d63fff3fc00e20b00a87cffa6a37aad7168741b1f0e77bdff000000008b4830450220274a5a9577f0786fb17d373b6d1995a6cff752320bf4e3d5ee41b15fdd86ef2a022100fd1d0c0f43d05e479430c3c9bd5481f5be929a7f2b82d14292ef192653c3b94c014104988828ea7144ff00def9988e88aad98b1f22d01000fc4b52d1003c9b32de0534cff71c0a045098bb28f699bcc9db13533c0998eb2133085d37cac65b5bd74c1fffffffff5aab5581c575a56a803ef5d8698dc34014261d196ada1da3300731380d7e672d000000008b48304502201615a116a2e057f3f8f2a63340d0387b820a6f9d1067758f4f6cdec76c04951a022100a1ada9e03a0d9603743d41347ffd992642544d6dfdc51121b7bf622e12cb1547014104830b3b139b0a8707f348840b0fa358164869fbc0c423a2785cb0a18ff73517f8878cefd643e86e682eb93d07216f76993c9e980592de07c507c3a2659efff6aaffffffff74d7cd370a334aabee3b8328b67a63f1768583405e73ebd291dd86189983a61c000000008a4730440220432ff0f90a3f5372a8dc4b3d2e7165e110efa16b40784ea4d5ab866b736712ed02201a759b630d562f4dbc4a7d2fca406ad54e235564346bc360dec26d6e72bb60fa014104ca0bb72303672885ad953279309694c71574d50d2c6e1f5f1e7ab1b0bf3df67e5439b3f6e2501dda539812e414991ebaa547631374ce2bda9dd7211099886827ffffffff8941eb7fe688ff48357981c4756906e331c07f332429be5b2cfc7a945644f80f000000008a47304402204e074678b525927a8f459d1dc93ad738ce02c0ea77c50a749647032eda662eee02203c1a98e9b116dc2fe9e7325a5696ad8e3ad26d2d10503e6e3209f4f5f40f6e34014104f9baa86b46b04f2a2dc4270903bcca9444026b7f184d879f089462e332f260f6de8e05f08285a4a3c716220d9ec0f7e9fde354c779f32f00dd4d4cd8d4a1ac28ffffffff948be2e03f9537a35319e8cee2b7b9ed2769ca59734e5d4b42951ef7ba3bb4a8000000008b483045022100fcf1deaaa047e8f1b8b8ac783760ea5a0af15adbb9eed1c1748942a57510e27402204c278b12c4991480dc714af66395f5d8f42e411423bf27b024f62267c45a0d37014104cfd128c3f322b59c21d452d5c4c7b4d1a3237a2d31c6f4033b32db78c19cb342ffc151acc272a02a13ba985a523b655689d8118f502b8a7fb8df5fc7bd8458ceffffffffb2cae04cf238b4d47c614da5ce9948f089a02f1ff3e22cda6b9f9196bda87215010000008b483045022017a74373ef19756e275f2d987925a8bd22cfaf1c93eede7db63102dc73b7cfd0022100fd2f040d7d3ccd1a2188923f8c7560253aa2a910d8e9cd06a8a05fc69c12f6ce014104daeab6af397baf06fcc91b6c04a28ffb2cd80312e954a2a55f7c864094608032ef657e8296dfc2f86ecf28fe32925c0f648d3ef16b96934a34b29dd871ff057affffffffbe75c9f1faf79fb885018e3353ada4a7fb5c989bc26f031c5aee69d1eabad957000000008a4730440220360d35b0a98dd086d242f8976aa704b808541ad78ef5cba45e4eb73c1efbaaa7022025a3d981b9e0836a2493698b802bf7f0eae72de0252c7eac059ad14b1b27001b014104d969f0d456196f4c16945bc71fc460615002d1554fc8d774288aa8fc5c9eca1622a3dcb1e6a8314c94844e516c0543316b2e75d465c8fc2f0d0d20334e53f7dbffffffffbcbad95cac8bbe08538f7b12c240e96866358dfa819de6e940bb60ce7fae5c3d000000008b483045022100d916990888d0d26246b8d6cb944b1e8ebd6a4cdb2965ff7e4430883eef02c4e102205693b96aa1d6e9335f3cb2986b7f3a03357959e989570d03a56ccdb18a8d7e6d0141040840a410ed7afdc3e3fe3923ae388413bbd8a9089062403e0adce62212bccfe1f80400530121cfdba5b52f3e16397f1959b14a09089778afd5ff41f8a856980fffffffff02005039278c0400001976a914f1c87a5e8ff7d14e74b858089bf771c94b1b6db488ac00203d88792d00001976a914dcdf2f9892bfa1cb086530354eab3ba078a2f09088ac00000000
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
01000000019ac03d5ae6a875d970128ef9086cef276a1919684a6988023cc7254691d97e6d010000006b4830450221009d41dc793ba24e65f571473d40b299b6459087cea1509f0d381740b1ac863cb6022039c425906fcaf51b2b84d8092569fb3213de43abaff2180e2a799d4fcb4dd0aa012102d5ede09a8ae667d0f855ef90325e27f6ce35bbe60a1e6e87af7f5b3c652140fdffffffff080100000000000000010101000000000000000202010100000000000000014c0100000000000000034c02010100000000000000014d0100000000000000044dffff010100000000000000014e0100000000000000064effffffff0100000000
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
010000000001019750f68b12fc617dc777d1420e1a34700d763f105303d7199348ff4b315da6b11300000000ffffffff0440164000000000001976a914ad62f43b9eba175ae6df7b3482a4fe6a3dad755588ac00c2eb0b000000001976a91415efba4572d98d9d19be7378e85fe6b2f5a111fb88ac80584f00000000001976a9147cce8012dd98ffa09129016d2fdedf8725b8c0ef88ac60a3a90c00000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d0400483045022100bc2ba8808127f8a74beed6dfa1b9fe54675c55aab85a61d7a74c15b993e67e5f02204dada4e15f0b4e659dae7bf0d0f648010d1f2b665f587a35eb6f22e44194952301483045022100f4c7ec7c2064fe2cc4389733ac0a57d8080a62180a004b02a19b89267113a17f022004ee9fdb081359c549ee42ffb58279363563eaf0191cd8b2b0ceebf62146b50b01695221022b003d276bce58bef509bdcd9cf7e156f0eae18e1175815282e65e7da788bb5b21035c58f2f60ecf38c9c8b9d1316b662627ec672f5fd912b1a2cc28d0b9b00575fd2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
010000000153dc411869f3b96f3dc48b6ac52438e895d6641142e0d4f6383fabc20e49e761010000006a4730440220177a4f6729b41ee6a06c2e4826b12313d9703e6fd3ee6cca347b4418cfe3a17b0220177ed399130c11f1b37a97f6e7982fc654f70139de0361eddc5d1cbf88d0c6e001210258764172106289665db078db8c6c3510e17e93caa0522d3ffca30a75562699b1ffffffff01ca06fc000000000017a914169e3bb06b5f0355e5085a8a1a5e430a3f6b39258700000000
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
01000000000101621adcd8ea06cc206490ad64f938e30a5fffa01d0d529a29140a858414cb9f7f0100000023220020be38f99e2cb9f29f7a6c34c4e83f35dcc8dae66becd64dcef6d5e5f147301fb7ffffffff02dc2a0400000000001976a9140d599e7368f1f44f5ede79adafa58079e789443f88ac0f9b18000000000017a914cc0a76a97b96703b66b3f0c8adf619e9c3e22fac87040047304402203fbb18de9b0770882f62e2f2d2cb0b6e0256bbee0f95df5083a09f74dbab9f57022053919922e9752c11320d9b76134c659f1588095d42aecda340da5e103399ecaa01483045022100885fc1e985fac55aa3c018d0fb328fc90a2d23bc673bc22155aa62d8fa8963ad02202875de558913de4fc72386a653f1c60ab2ef42be572955675b46467f5c7906b901475221020c13434b1a50bf18863dde6ef0303c7dd4942459512679595b97ecd9b199e0f72102f78ef4eba789c98b5c92e55d139232dbacfa0d5b2dbbd3e727d4b3774dcd79fe52ae00000000
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Copyright (C) 2015-2016 The bitcoin-blockchain-parser developers
|
|
2
|
+
#
|
|
3
|
+
# This file is part of bitcoin-blockchain-parser.
|
|
4
|
+
#
|
|
5
|
+
# It is subject to the license terms in the LICENSE file found in the top-level
|
|
6
|
+
# directory of this distribution.
|
|
7
|
+
#
|
|
8
|
+
# No part of bitcoin-blockchain-parser, including this file, may be copied,
|
|
9
|
+
# modified, propagated, or distributed except according to the terms contained
|
|
10
|
+
# in the LICENSE file.
|
|
11
|
+
|
|
12
|
+
import unittest
|
|
13
|
+
from binascii import a2b_hex
|
|
14
|
+
|
|
15
|
+
from blockchain_parser.address import Address
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TestUtils(unittest.TestCase):
|
|
19
|
+
def test_from_public_key(self):
|
|
20
|
+
public_key = "0450863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A" \
|
|
21
|
+
"04887E5B23522CD470243453A299FA9E77237716103ABC11A1DF38" \
|
|
22
|
+
"855ED6F2EE187E9C582BA6"
|
|
23
|
+
hex_pk = a2b_hex(public_key)
|
|
24
|
+
address = Address.from_public_key(hex_pk)
|
|
25
|
+
self.assertEqual(address.address, "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM")
|
|
26
|
+
address_hash = "010966776006953D5567439E5E39F86A0D273BEE"
|
|
27
|
+
self.assertEqual(address.hash, a2b_hex(address_hash))
|
|
28
|
+
|
|
29
|
+
def test_from_ripemd160(self):
|
|
30
|
+
ripemd160 = "010966776006953D5567439E5E39F86A0D273BEE"
|
|
31
|
+
address = Address.from_ripemd160(a2b_hex(ripemd160))
|
|
32
|
+
self.assertEqual(address.address, "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM")
|
|
33
|
+
|
|
34
|
+
def test_from_bech32(self):
|
|
35
|
+
# Example sourced from https://en.bitcoin.it/wiki/Bech32
|
|
36
|
+
bech32 = "751e76e8199196d454941c45d1b3a323f1433bd6"
|
|
37
|
+
address = Address.from_bech32(a2b_hex(bech32), segwit_version=0)
|
|
38
|
+
self.assertEqual(address.address, "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4")
|
|
39
|
+
|
|
40
|
+
def test_from_bech32m(self):
|
|
41
|
+
# https://blockstream.info/tx/33e794d097969002ee05d336686fc03c9e15a597c1b9827669460fac98799036?expand
|
|
42
|
+
# Second output
|
|
43
|
+
bech32m = "a37c3903c8d0db6512e2b40b0dffa05e5a3ab73603ce8c9c4b7771e5412328f9"
|
|
44
|
+
address = Address.from_bech32m(a2b_hex(bech32m), segwit_version=1)
|
|
45
|
+
self.assertEqual(address.address, "bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg3297")
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Copyright (C) 2015-2016 The bitcoin-blockchain-parser developers
|
|
2
|
+
#
|
|
3
|
+
# This file is part of bitcoin-blockchain-parser.
|
|
4
|
+
#
|
|
5
|
+
# It is subject to the license terms in the LICENSE file found in the top-level
|
|
6
|
+
# directory of this distribution.
|
|
7
|
+
#
|
|
8
|
+
# No part of bitcoin-blockchain-parser, including this file, may be copied,
|
|
9
|
+
# modified, propagated, or distributed except according to the terms contained
|
|
10
|
+
# in the LICENSE file.
|
|
11
|
+
|
|
12
|
+
import unittest
|
|
13
|
+
from datetime import datetime, timezone
|
|
14
|
+
|
|
15
|
+
from .utils import read_test_data
|
|
16
|
+
from blockchain_parser.block import Block, get_block_transactions
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TestBlock(unittest.TestCase):
|
|
20
|
+
def test_from_hex(self):
|
|
21
|
+
block_hex = read_test_data("genesis_block.txt")
|
|
22
|
+
block = Block.from_hex(block_hex)
|
|
23
|
+
self.assertEqual(1, block.n_transactions)
|
|
24
|
+
block_hash = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1" \
|
|
25
|
+
"b60a8ce26f"
|
|
26
|
+
self.assertEqual(block_hash, block.hash)
|
|
27
|
+
self.assertEqual(486604799, block.header.bits)
|
|
28
|
+
merkle_root = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127" \
|
|
29
|
+
"b7afdeda33b"
|
|
30
|
+
self.assertEqual(merkle_root, block.header.merkle_root)
|
|
31
|
+
self.assertEqual(2083236893, block.header.nonce)
|
|
32
|
+
self.assertEqual(1, block.header.version)
|
|
33
|
+
self.assertEqual(1, block.header.difficulty)
|
|
34
|
+
self.assertEqual(285, block.size)
|
|
35
|
+
self.assertEqual(datetime.fromtimestamp(1231006505, timezone.utc),
|
|
36
|
+
block.header.timestamp)
|
|
37
|
+
self.assertEqual("0" * 64, block.header.previous_block_hash)
|
|
38
|
+
|
|
39
|
+
for tx in block.transactions:
|
|
40
|
+
self.assertEqual(1, tx.version)
|
|
41
|
+
tx_hash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127" \
|
|
42
|
+
"b7afdeda33b"
|
|
43
|
+
self.assertEqual(tx_hash, tx.hash)
|
|
44
|
+
self.assertEqual(204, tx.size)
|
|
45
|
+
self.assertEqual(0, tx.locktime)
|
|
46
|
+
self.assertEqual(0xffffffff, tx.inputs[0].transaction_index)
|
|
47
|
+
self.assertEqual(0xffffffff, tx.inputs[0].sequence_number)
|
|
48
|
+
self.assertTrue("ffff001d" in tx.inputs[0].script.value)
|
|
49
|
+
self.assertEqual("0" * 64, tx.inputs[0].transaction_hash)
|
|
50
|
+
self.assertEqual(50 * 100000000, tx.outputs[0].value)
|
|
51
|
+
|
|
52
|
+
def test_truncated_transaction_raises(self):
|
|
53
|
+
raw_block = b"\x00" * 80 + b"\x01" + b"\xff" * 8
|
|
54
|
+
with self.assertRaises(ValueError):
|
|
55
|
+
list(get_block_transactions(raw_block))
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from blockchain_parser.blockchain import (
|
|
4
|
+
Blockchain,
|
|
5
|
+
iter_chain_indexes,
|
|
6
|
+
select_main_chain,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
EASY = 0x1d00ffff
|
|
11
|
+
HARD = 0x1c00ffff
|
|
12
|
+
ZERO = "0" * 64
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Index(object):
|
|
16
|
+
def __init__(self, hash, height, prev, bits, file=0, data_pos=1):
|
|
17
|
+
self.hash = hash
|
|
18
|
+
self.height = height
|
|
19
|
+
self.prev_hash = prev
|
|
20
|
+
self.bits = bits
|
|
21
|
+
self.file = file
|
|
22
|
+
self.data_pos = data_pos
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class TestMainChain(unittest.TestCase):
|
|
26
|
+
def test_longer_chain_beats_orphan(self):
|
|
27
|
+
genesis = Index("aa", 0, ZERO, EASY)
|
|
28
|
+
main = Index("bb", 1, "aa", EASY)
|
|
29
|
+
tip = Index("cc", 2, "bb", EASY)
|
|
30
|
+
orphan = Index("dd", 1, "aa", EASY)
|
|
31
|
+
# Pass the entries in hash order rather than height order.
|
|
32
|
+
ordered = select_main_chain([orphan, tip, genesis, main])
|
|
33
|
+
self.assertEqual([block.hash for block in ordered], ["aa", "bb", "cc"])
|
|
34
|
+
|
|
35
|
+
def test_total_work_beats_a_longer_weaker_chain(self):
|
|
36
|
+
genesis = Index("aa", 0, ZERO, EASY)
|
|
37
|
+
hard = Index("00", 1, "aa", HARD)
|
|
38
|
+
first = Index("b1", 1, "aa", EASY)
|
|
39
|
+
second = Index("b2", 2, "b1", EASY)
|
|
40
|
+
third = Index("b3", 3, "b2", EASY)
|
|
41
|
+
ordered = select_main_chain([third, second, first, hard, genesis])
|
|
42
|
+
self.assertEqual([block.hash for block in ordered], ["aa", "00"])
|
|
43
|
+
|
|
44
|
+
def test_more_work_wins_at_the_same_height(self):
|
|
45
|
+
genesis = Index("aa", 0, ZERO, EASY)
|
|
46
|
+
# "ff" would win a hash tie-break, so this only passes when work differs.
|
|
47
|
+
easy = Index("ff", 1, "aa", EASY)
|
|
48
|
+
hard = Index("00", 1, "aa", HARD)
|
|
49
|
+
ordered = select_main_chain([easy, hard, genesis])
|
|
50
|
+
self.assertEqual([block.hash for block in ordered], ["aa", "00"])
|
|
51
|
+
|
|
52
|
+
def test_height_window_skips_blocks_without_data(self):
|
|
53
|
+
blocks = []
|
|
54
|
+
for height in range(6):
|
|
55
|
+
prev = "h%d" % (height - 1) if height else ZERO
|
|
56
|
+
blocks.append(Index("h%d" % height, height, prev, EASY))
|
|
57
|
+
blocks[2].file = -1
|
|
58
|
+
ordered = select_main_chain(blocks)
|
|
59
|
+
|
|
60
|
+
forward = [block.height for block in iter_chain_indexes(ordered, 0, 5)]
|
|
61
|
+
self.assertEqual(forward, [0, 1, 3, 4])
|
|
62
|
+
|
|
63
|
+
backward = [block.height for block in iter_chain_indexes(ordered, 5, 0)]
|
|
64
|
+
self.assertEqual(backward, [5, 4, 3, 1, 0])
|
|
65
|
+
|
|
66
|
+
def test_cycle_does_not_hang(self):
|
|
67
|
+
first = Index("aa", 1, "bb", EASY)
|
|
68
|
+
second = Index("bb", 2, "aa", EASY)
|
|
69
|
+
ordered = select_main_chain([first, second])
|
|
70
|
+
self.assertGreater(len(ordered), 0)
|
|
71
|
+
self.assertLessEqual(len(ordered), 2)
|
|
72
|
+
|
|
73
|
+
def test_missing_transaction_returns_none(self):
|
|
74
|
+
class EmptyDB(object):
|
|
75
|
+
def get(self, key):
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
blockchain = Blockchain("blocks")
|
|
79
|
+
self.assertIsNone(blockchain.get_transaction("ab" * 32, EmptyDB()))
|