├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── block_io ├── __init__.py ├── bitcoinutils_patches.py └── pbkdf2.py ├── examples ├── basic.py ├── dtrust.py └── sweep.py ├── setup.py └── tests ├── context.py ├── data ├── .gitignore ├── LICENSE ├── README.md └── json │ ├── create_and_sign_transaction_response.json │ ├── create_and_sign_transaction_response_P2WSH-over-P2SH_1of2_251inputs.json │ ├── create_and_sign_transaction_response_P2WSH-over-P2SH_1of2_252inputs.json │ ├── create_and_sign_transaction_response_P2WSH-over-P2SH_1of2_253inputs.json │ ├── create_and_sign_transaction_response_P2WSH-over-P2SH_1of2_762inputs.json │ ├── create_and_sign_transaction_response_dtrust_P2SH_3of5_195inputs.json │ ├── create_and_sign_transaction_response_dtrust_P2SH_4of5_195inputs.json │ ├── create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_3of5_251inputs.json │ ├── create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_3of5_252inputs.json │ ├── create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_3of5_253inputs.json │ ├── create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_4of5_251inputs.json │ ├── create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_4of5_252inputs.json │ ├── create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_4of5_253inputs.json │ ├── create_and_sign_transaction_response_dtrust_WITNESS_V0_3of5_251inputs.json │ ├── create_and_sign_transaction_response_dtrust_WITNESS_V0_3of5_252inputs.json │ ├── create_and_sign_transaction_response_dtrust_WITNESS_V0_3of5_253inputs.json │ ├── create_and_sign_transaction_response_dtrust_WITNESS_V0_4of5_251inputs.json │ ├── create_and_sign_transaction_response_dtrust_WITNESS_V0_4of5_252inputs.json │ ├── create_and_sign_transaction_response_dtrust_WITNESS_V0_4of5_253inputs.json │ ├── create_and_sign_transaction_response_dtrust_p2sh_3_of_5_keys.json │ ├── create_and_sign_transaction_response_dtrust_p2sh_4_of_5_keys.json │ ├── create_and_sign_transaction_response_dtrust_p2wsh_over_p2sh_3_of_5_keys.json │ ├── create_and_sign_transaction_response_dtrust_p2wsh_over_p2sh_4_of_5_keys.json │ ├── create_and_sign_transaction_response_dtrust_witness_v0_3_of_5_keys.json │ ├── create_and_sign_transaction_response_dtrust_witness_v0_3of5_251outputs.json │ ├── create_and_sign_transaction_response_dtrust_witness_v0_3of5_252outputs.json │ ├── create_and_sign_transaction_response_dtrust_witness_v0_3of5_253outputs.json │ ├── create_and_sign_transaction_response_dtrust_witness_v0_4_of_5_keys.json │ ├── create_and_sign_transaction_response_dtrust_witness_v0_4of5_251outputs.json │ ├── create_and_sign_transaction_response_dtrust_witness_v0_4of5_252outputs.json │ ├── create_and_sign_transaction_response_dtrust_witness_v0_4of5_253outputs.json │ ├── create_and_sign_transaction_response_sweep_p2pkh.json │ ├── create_and_sign_transaction_response_sweep_p2wpkh.json │ ├── create_and_sign_transaction_response_sweep_p2wpkh_over_p2sh.json │ ├── create_and_sign_transaction_response_with_blockio_fee_and_expected_unsigned_txid.json │ ├── create_and_sign_transaction_response_witness_v1_output.json │ ├── get_balance_response.json │ ├── prepare_dtrust_transaction_response_P2SH_3of5_195inputs.json │ ├── prepare_dtrust_transaction_response_P2SH_4of5_195inputs.json │ ├── prepare_dtrust_transaction_response_P2WSH-over-P2SH_3of5_251inputs.json │ ├── prepare_dtrust_transaction_response_P2WSH-over-P2SH_3of5_252inputs.json │ ├── prepare_dtrust_transaction_response_P2WSH-over-P2SH_3of5_253inputs.json │ ├── prepare_dtrust_transaction_response_P2WSH-over-P2SH_4of5_251inputs.json │ ├── prepare_dtrust_transaction_response_P2WSH-over-P2SH_4of5_252inputs.json │ ├── prepare_dtrust_transaction_response_P2WSH-over-P2SH_4of5_253inputs.json │ ├── prepare_dtrust_transaction_response_WITNESS_V0_3of5_251inputs.json │ ├── prepare_dtrust_transaction_response_WITNESS_V0_3of5_252inputs.json │ ├── prepare_dtrust_transaction_response_WITNESS_V0_3of5_253inputs.json │ ├── prepare_dtrust_transaction_response_WITNESS_V0_4of5_251inputs.json │ ├── prepare_dtrust_transaction_response_WITNESS_V0_4of5_252inputs.json │ ├── prepare_dtrust_transaction_response_WITNESS_V0_4of5_253inputs.json │ ├── prepare_dtrust_transaction_response_p2sh.json │ ├── prepare_dtrust_transaction_response_p2wsh_over_p2sh.json │ ├── prepare_dtrust_transaction_response_witness_v0.json │ ├── prepare_dtrust_transaction_response_witness_v0_3of5_251outputs.json │ ├── prepare_dtrust_transaction_response_witness_v0_3of5_252outputs.json │ ├── prepare_dtrust_transaction_response_witness_v0_3of5_253outputs.json │ ├── prepare_dtrust_transaction_response_witness_v0_4of5_251outputs.json │ ├── prepare_dtrust_transaction_response_witness_v0_4of5_252outputs.json │ ├── prepare_dtrust_transaction_response_witness_v0_4of5_253outputs.json │ ├── prepare_sweep_transaction_response_p2pkh.json │ ├── prepare_sweep_transaction_response_p2wpkh.json │ ├── prepare_sweep_transaction_response_p2wpkh_over_p2sh.json │ ├── prepare_transaction_response.json │ ├── prepare_transaction_response_P2WSH-over-P2SH_1of2_251inputs.json │ ├── prepare_transaction_response_P2WSH-over-P2SH_1of2_252inputs.json │ ├── prepare_transaction_response_P2WSH-over-P2SH_1of2_253inputs.json │ ├── prepare_transaction_response_P2WSH-over-P2SH_1of2_762inputs.json │ ├── prepare_transaction_response_with_blockio_fee_and_expected_unsigned_txid.json │ ├── prepare_transaction_response_witness_v1_output.json │ └── summarize_prepared_transaction_response_with_blockio_fee_and_expected_unsigned_txid.json ├── test_bitcoinutils.py ├── test_bitcoinutils_utils.py ├── test_blockio.py ├── test_blockio_larger_transactions.py └── test_blockio_prepare_transactions.py /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.pyc 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | dist: focal 3 | python: 4 | - 3.6 5 | - 3.7 6 | - 3.8 7 | - 3.9 8 | - 3.10 9 | - 3.11-dev 10 | 11 | install: 12 | - pip3 install . 13 | 14 | script: 15 | - pytest -vv 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 BlockIo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BlockIo 2 | 3 | This Python package is the official reference client for the Block.io payments API. To use this, you will need the Dogecoin, Bitcoin, or Litecoin API key(s) from Block.io. Go ahead, sign up :) 4 | 5 | #### COMPATIBILITY: Please use Python 3.6+. Untested on Windows: ensure all tests pass before using this package regardless. 6 | 7 | ## Installation 8 | 9 | [Using virtualenv is recommended when installing Python packages](https://packaging.python.org/en/latest/installing.html#virtual-environments). 10 | 11 | Install the package using `pip3`: 12 | 13 | pip3 install block-io 14 | 15 | ## Usage 16 | 17 | It's super easy to get started. In your Python shell, do: 18 | 19 | from block_io import BlockIo 20 | 21 | block_io = BlockIo('API KEY', 'SECRET PIN', API_VERSION) 22 | 23 | # print the account balance 24 | print block_io.get_balance() 25 | 26 | # print all addresses on this account 27 | print block_io.get_my_addresses() 28 | 29 | For more information, see [Python API Docs](https://block.io/api/simple/python). This Python client provides a mapping for all methods listed on the Block.io API site. 30 | 31 | ## Contributing 32 | 33 | 1. Fork it ( https://github.com/BlockIo/block_io-python/fork ) 34 | 2. Create your feature branch (`git checkout -b my-new-feature`) 35 | 3. Commit your changes (`git commit -am 'Add some feature'`) 36 | 4. Push to the branch (`git push origin my-new-feature`) 37 | 5. Create a new Pull Request 38 | -------------------------------------------------------------------------------- /block_io/__init__.py: -------------------------------------------------------------------------------- 1 | from Crypto.Cipher import AES 2 | import base64 3 | import base58 4 | from binascii import hexlify, unhexlify 5 | import json 6 | import requests 7 | import pkg_resources 8 | 9 | from hashlib import sha256 10 | from . import pbkdf2 11 | 12 | from .bitcoinutils_patches import * 13 | 14 | VERSION=pkg_resources.get_distribution("block-io").version 15 | 16 | class BlockIoInvalidResponseError(Exception): 17 | """Thrown when we receive an unexpected/unparseable response from Block.io""" 18 | 19 | class BlockIoUnknownError(Exception): 20 | """Thrown when response status codes are outside of 200-299, 419-420, 500-599.""" 21 | 22 | class BlockIoAPIThrottleError(Exception): 23 | """Thrown when API call gets throttled at Block.io.""" 24 | 25 | class BlockIoAPIInternalError(Exception): 26 | """Thrown on 500-599 errors.""" 27 | 28 | class BlockIoAPIError(Exception): 29 | """Thrown when block.io API call fails.""" 30 | 31 | def set_raw_data(self, data): 32 | self.raw_data = data 33 | 34 | def get_raw_data(self): 35 | return self.raw_data 36 | 37 | 38 | class BlockIo(object): 39 | 40 | class Key: 41 | # wrapper around bitcoinutils.keys.PrivateKey 42 | def __init__(self, privkey, pubkey = None): 43 | # we will always use compressed public keys 44 | self.private_key = bitcoinutils.keys.PrivateKey(secret_exponent=int(hexlify(privkey),16)) 45 | self.public_key = self.private_key.get_public_key() 46 | 47 | @staticmethod 48 | def generate(): 49 | return BlockIo.Key(bitcoinutils.keys.PrivateKey().to_bytes(), None) 50 | 51 | @staticmethod 52 | def from_privkey_hex(privkey): 53 | return BlockIo.Key(unhexlify(privkey.rjust(64, "0")), None) 54 | 55 | def sign(self, data_to_sign): 56 | # use the sign_input method from bitcoinutils.keys.PrivateKey 57 | der_sig = self.private_key._sign_input(data_to_sign, bitcoinutils.constants.SIGHASH_ALL) 58 | return unhexlify(der_sig) 59 | 60 | def sign_hex(self, hex_data): 61 | return hexlify(self.sign(unhexlify(hex_data))) 62 | 63 | def privkey_hex(self): 64 | return hexlify(self.private_key.to_bytes()) 65 | 66 | def pubkey_hex(self): 67 | return self.public_key.to_hex(compressed=True).encode('utf-8') 68 | 69 | @staticmethod 70 | def from_passphrase(passphrase): 71 | # use the sha256 of the given passphrase as the private key 72 | private_key = sha256(passphrase).digest() 73 | return BlockIo.Key(private_key) 74 | 75 | @staticmethod 76 | def from_wif(privkey): 77 | # extract the secret exponent from the given coin-formatted private key 78 | private_key = "" 79 | 80 | try: 81 | extended_key_bytes = base58.b58decode_check(privkey) 82 | except ValueError as e: 83 | # Invalid checksum! 84 | raise Exception("Invalid Private Key provided. Must be in Wallet Import Format.") 85 | 86 | # is this a compressed WIF or not? 87 | is_compressed = len(hexlify(extended_key_bytes)) == 68 and hexlify(extended_key_bytes)[-2:].decode("utf-8") == "01" 88 | 89 | if is_compressed == False: 90 | raise Exception("WIF must always be compressed.") 91 | 92 | # Drop the network bytes 93 | extended_key_bytes = extended_key_bytes[1:] 94 | 95 | private_key = extended_key_bytes 96 | 97 | if (len(private_key) == 33): 98 | private_key = extended_key_bytes[:-1] 99 | 100 | return BlockIo.Key(private_key, None) 101 | 102 | class Helper: 103 | 104 | @staticmethod 105 | def pinToAesKey(pin, salt = "", iterations = 2048, hashfn = sha256, phase1_key_length = 16, phase2_key_length = 32): 106 | # use pbkdf2 magic 107 | ret = pbkdf2.pbkdf2(pin, phase1_key_length, salt, int(iterations/2), hashfn) 108 | ret = pbkdf2.pbkdf2(hexlify(ret), phase2_key_length, salt, int(iterations/2), hashfn) 109 | return hexlify(ret) # the encryption key 110 | 111 | @staticmethod 112 | def dynamicExtractKey(user_key, pin): 113 | # uses the algorithm specified in the user_key object 114 | # if no algorithm specified, uses the following default (legacy) 115 | 116 | algorithm = { 117 | "pbkdf2_salt": "", 118 | "pbkdf2_iterations": 2048, 119 | "pbkdf2_hash_function": "SHA256", 120 | "pbkdf2_phase1_key_length": 16, 121 | "pbkdf2_phase2_key_length": 32, 122 | "aes_iv": None, 123 | "aes_cipher": "AES-256-ECB", 124 | "aes_auth_tag": None, 125 | "aes_auth_data": None 126 | } 127 | 128 | if 'algorithm' in user_key: 129 | algorithm = user_key['algorithm'] 130 | 131 | if algorithm['pbkdf2_hash_function'] != "SHA256": 132 | raise Exception("Unknown hash function specified. Are you using current version of this library?") 133 | 134 | aes_key = BlockIo.Helper.pinToAesKey(pin, algorithm['pbkdf2_salt'], algorithm['pbkdf2_iterations'], 135 | sha256, algorithm['pbkdf2_phase1_key_length'], algorithm['pbkdf2_phase2_key_length']) 136 | 137 | decrypted = BlockIo.Helper.decrypt(user_key['encrypted_passphrase'], aes_key, 138 | algorithm['aes_iv'], algorithm['aes_cipher'], algorithm['aes_auth_tag']) 139 | 140 | return BlockIo.Key.from_passphrase(unhexlify(decrypted)) 141 | 142 | @staticmethod 143 | def extractKey(encrypted_data, enc_key_hex): 144 | # encryption key is in hex 145 | decrypted = BlockIo.Helper.decrypt(encrypted_data, enc_key_hex) 146 | return BlockIo.Key.from_passphrase(unhexlify(decrypted)) 147 | 148 | @staticmethod 149 | def encrypt(data, key, iv = None, cipher_type = "AES-256-ECB", auth_data = None): 150 | # key in hex, data as string 151 | # returns ciphertext in base64 152 | 153 | key = unhexlify(key) # get bytes 154 | 155 | BS = 16 156 | pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) 157 | unpad = lambda s : s[0:-s[-1]] 158 | 159 | obj = None 160 | 161 | if cipher_type == "AES-256-ECB": 162 | obj = AES.new(key, AES.MODE_ECB) 163 | elif cipher_type == "AES-256-CBC": 164 | obj = AES.new(key, AES.MODE_CBC, unhexlify(iv)) 165 | elif cipher_type == "AES-256-GCM": 166 | obj = AES.new(key, AES.MODE_GCM, unhexlify(iv)) 167 | else: 168 | raise Exception("Unsupported cipher", cipher_type) 169 | 170 | response = {"aes_iv": iv, "aes_cipher_text": None, "aes_auth_tag": None, "aes_auth_data": None, "aes_cipher": cipher_type} 171 | 172 | if cipher_type != "AES-256-GCM": 173 | response["aes_cipher_text"] = base64.b64encode(obj.encrypt(pad(data).encode('latin-1'))) 174 | else: 175 | # AES-256-GCM 176 | # no padding for data 177 | ciphertext = obj.encrypt(data.encode('latin-1')) 178 | response["aes_cipher_text"] = base64.b64encode(ciphertext) 179 | response["aes_auth_tag"] = hexlify(obj.digest()) 180 | 181 | return response 182 | 183 | @staticmethod 184 | def decrypt(b64data, key, iv = None, cipher_type = "AES-256-ECB", auth_tag = None): 185 | # key in hex, b64data as base64 string 186 | # returns utf-8 string 187 | 188 | message = None 189 | 190 | try: 191 | 192 | key = unhexlify(key) # get bytes 193 | 194 | BS = 16 195 | pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) 196 | unpad = lambda s : s[0:-s[-1]] 197 | 198 | data = base64.b64decode(b64data) # decode from base64 199 | 200 | obj = None 201 | 202 | if cipher_type == "AES-256-ECB": 203 | obj = AES.new(key, AES.MODE_ECB) 204 | elif cipher_type == "AES-256-CBC": 205 | obj = AES.new(key, AES.MODE_CBC, unhexlify(iv)) 206 | elif cipher_type == "AES-256-GCM": 207 | obj = AES.new(key, AES.MODE_GCM, unhexlify(iv)) 208 | else: 209 | raise Exception("Unsupported cipher", cipher_type) 210 | 211 | message = None 212 | 213 | if cipher_type != "AES-256-GCM": 214 | message = unpad(obj.decrypt(data)) 215 | else: 216 | # AES-256-GCM 217 | # Auth tag must be exactly 16 bytes 218 | if (len(auth_tag) != 32): 219 | raise Exception("Auth tag must be 16 bytes exactly.") 220 | message = obj.decrypt_and_verify(data, unhexlify(auth_tag)) 221 | 222 | except: 223 | # error decrypting? must be an invalid secret pin 224 | raise Exception('Invalid Secret PIN provided.') 225 | 226 | return message 227 | 228 | @staticmethod 229 | def compress_pubkey(pubkey): 230 | x = pubkey[:32] 231 | y = pubkey[33:64] 232 | y_int = 0; 233 | for c in bytes(y): 234 | y_int = 256 * y_int + c 235 | return bytes((2+(y_int % 2),)) + x 236 | 237 | def __init__(self, api_key, pin, version = 2): 238 | # initiate the object 239 | self.api_key = api_key 240 | self.pin = pin 241 | self.version = version 242 | self.clientVersion = VERSION 243 | self.encryption_key = None 244 | self.base_url = 'https://block.io/api/v'+str(version)+'/API_CALL/?api_key='+api_key 245 | self.sweep_calls = ['prepare_sweep_transaction'] 246 | self.request_headers = {'Accept': 'application/json', 'User-Agent': 'python:block_io:'+self.clientVersion} 247 | self.private_keys = dict() 248 | 249 | def __getattr__(self, attr, *args, **kwargs): 250 | 251 | def hook(*args, **kwargs): 252 | return self.api_call(attr, **kwargs) 253 | 254 | def sweep_hook(*args, **kwargs): 255 | return self.internal_prepare_sweep_transaction(attr, **kwargs) 256 | 257 | if any(attr in s for s in self.sweep_calls): 258 | return sweep_hook 259 | else: 260 | return hook 261 | 262 | 263 | def internal_prepare_sweep_transaction(self, method, **kwargs): 264 | # sweep call meta 265 | 266 | key = self.Key.from_wif(kwargs['private_key']) 267 | 268 | del kwargs['private_key'] # remove the key, we're not going to pass it on 269 | kwargs['public_key'] = key.pubkey_hex().decode("utf-8") 270 | 271 | # save the key for later use 272 | self.private_keys[key.pubkey_hex().decode('utf-8')] = PrivateKey(secret_exponent=int(key.privkey_hex().decode('utf-8'),16)) 273 | 274 | response = self.api_call(method, **kwargs) 275 | 276 | return response 277 | 278 | def create_redeem_script(self, required_signatures, public_keys): 279 | # returns the redeem script given the ordered public_keys and required signatures 280 | 281 | script_elements = [] 282 | script_elements.append('OP_' + str(required_signatures)) 283 | 284 | for public_key in public_keys: 285 | script_elements.append(public_key) 286 | 287 | script_elements.append('OP_' + str(len(public_keys))) 288 | script_elements.append('OP_CHECKMULTISIG') 289 | 290 | return Script(script_elements) 291 | 292 | def summarize_prepared_transaction(self, data): 293 | # returns summary of the prepared data 294 | # includes network fee, blockio fee, total amount to send 295 | 296 | inputs = data['data']['inputs'] 297 | outputs = data['data']['outputs'] 298 | 299 | network_fee = Decimal(0) 300 | blockio_fee = Decimal(0) 301 | change_amount = Decimal(0) 302 | input_sum = Decimal(0) 303 | output_sum = Decimal(0) 304 | 305 | # get the sum of coins being spent 306 | for cur_input in inputs: 307 | input_sum += Decimal(cur_input['input_value']) 308 | 309 | # populate various categories of outputs 310 | for cur_output in outputs: 311 | if cur_output['output_category'] == 'blockio-fee': 312 | blockio_fee += Decimal(cur_output['output_value']) 313 | elif cur_output['output_category'] == 'change': 314 | change_amount += Decimal(cur_output['output_value']) 315 | else: 316 | # user-specified 317 | output_sum += Decimal(cur_output['output_value']) 318 | 319 | # summarize the data 320 | return {"network": data['data']['network'], 321 | "network_fee": format(input_sum - output_sum - change_amount - blockio_fee, '.8f'), 322 | "blockio_fee": format(blockio_fee, '.8f'), 323 | "total_amount_to_send": format(output_sum, '.8f')} 324 | 325 | def create_and_sign_transaction(self, prepare_data, keys = []): 326 | # creates the specified transaction with the inputs and outputs 327 | # signs what we can and returns payload and signatures left to append, if any 328 | 329 | # set the appropriate network first 330 | bitcoinutils_patches.bitcoinutils_setup(prepare_data['data']['network']) 331 | 332 | # save the provided keys so we can use them below 333 | for cur_key_hex in keys: 334 | cur_key = PrivateKey(secret_exponent=int(cur_key_hex,16)) 335 | self.private_keys[cur_key.get_public_key().to_hex(compressed=True)] = cur_key 336 | 337 | if self.pin is None and 'user_key' in prepare_data['data'] and prepare_data['data']['user_key']['public_key'] not in self.private_keys: 338 | raise BlockIoUnknownError("No PIN provided to decrypt signer private key.") 339 | 340 | # decrypt the signer private key if we can 341 | if self.pin is not None and 'user_key' in prepare_data['data'] and prepare_data['data']['user_key']['public_key'] not in self.private_keys: 342 | 343 | key = self.Helper.dynamicExtractKey(prepare_data['data']['user_key'], self.pin) 344 | 345 | if (key.pubkey_hex().decode('utf-8') != prepare_data['data']['user_key']['public_key']): 346 | raise Exception("Expected pubkey=",prepare_data['data']['user_key']['public_key'],"but got pubkey=",key.pubkey_hex(),". Invalid PIN provided.") 347 | 348 | self.private_keys[key.pubkey_hex().decode('utf-8')] = PrivateKey(secret_exponent=int(key.privkey_hex().decode('utf-8'),16)) 349 | 350 | # we can create the transaction now 351 | inputs = prepare_data['data']['inputs'] 352 | outputs = prepare_data['data']['outputs'] 353 | 354 | # create a dictionary for these input addresses 355 | input_address_data = prepare_data['data']['input_address_data'] 356 | address_data = dict() 357 | 358 | for input_address in input_address_data: 359 | address_data[input_address['address']] = input_address 360 | 361 | has_segwit_inputs = False 362 | 363 | # create the transaction 364 | 365 | # inputs 366 | tx_inputs = [] 367 | 368 | for cur_input in inputs: 369 | tx_input = TxInput(cur_input['previous_txid'], cur_input['previous_output_index']) 370 | cur_address_data = address_data[cur_input['spending_address']] 371 | cur_address_type = cur_address_data['address_type'] 372 | 373 | # if this transaction has any segwit inputs, set has_segwit_inputs 374 | if (cur_address_type == 'P2WSH-over-P2SH' or 375 | cur_address_type == 'P2WPKH-over-P2SH' or 376 | cur_address_type == 'P2WPKH' or 377 | cur_address_type == 'WITNESS_V0'): 378 | has_segwit_inputs = True 379 | 380 | tx_inputs.append(tx_input) 381 | 382 | # outputs 383 | tx_outputs = [] 384 | 385 | for cur_output in outputs: 386 | tx_output = TxOutput(bitcoinutils.utils.to_satoshis(cur_output['output_value']), get_output_script(cur_output['receiving_address'])) 387 | tx_outputs.append(tx_output) 388 | 389 | tx = Transaction(tx_inputs, tx_outputs, has_segwit=has_segwit_inputs) 390 | 391 | # if we have expected unsigned txid, make sure this library's serialized the unsigned transaction properly 392 | if 'expected_unsigned_txid' in prepare_data['data'] and prepare_data['data']['expected_unsigned_txid'] is not None: 393 | if prepare_data['data']['expected_unsigned_txid'] != tx.get_txid(): 394 | raise Exception("Expected unsigned transaction ID mismatch. Please report this error to support@block.io.") 395 | 396 | # start signing inputs 397 | signatures = [] 398 | signatures_dict = dict() # makes our job easier for when we need to serialize the transaction with signatures 399 | tx_fully_signed = True # assume tx will be fully signed 400 | 401 | for cur_input in inputs: 402 | cur_address_data = address_data[cur_input['spending_address']] 403 | cur_public_keys = cur_address_data['public_keys'] 404 | cur_address_type = cur_address_data['address_type'] 405 | cur_required_signatures = cur_address_data['required_signatures'] 406 | cur_signatures = dict() 407 | 408 | if cur_address_type == 'P2SH' or cur_address_type == 'P2WSH-over-P2SH' or cur_address_type == 'WITNESS_V0': 409 | # P2SH, or P2WSH-over-P2SH, or P2WSH (WITNESS_V0) input 410 | 411 | redeem_script = self.create_redeem_script(cur_required_signatures, cur_public_keys) 412 | 413 | # sign for each public key, if we can 414 | for public_key in cur_public_keys: 415 | if (public_key in self.private_keys): 416 | if (cur_address_type == 'P2SH'): 417 | # P2SH address 418 | cur_signatures[public_key] = self.private_keys[public_key].sign_input(tx, cur_input['input_index'], redeem_script) 419 | else: 420 | # witness input 421 | cur_signatures[public_key] = self.private_keys[public_key].sign_segwit_input(tx, 422 | cur_input['input_index'], 423 | redeem_script, 424 | bitcoinutils.utils.to_satoshis(cur_input['input_value'])) 425 | 426 | if (len(cur_signatures) < cur_required_signatures): 427 | # transaction is going to be missing signatures 428 | # we'll need to use Block.io's signatures as well 429 | tx_fully_signed = False 430 | 431 | elif cur_address_type == 'P2PKH' or cur_address_type == 'P2WPKH-over-P2SH' or cur_address_type == 'P2WPKH': 432 | pkh_script = get_output_script(PublicKey(cur_public_keys[0]).get_address().to_string()) 433 | 434 | if (cur_public_keys[0] in self.private_keys): 435 | if cur_address_type == 'P2PKH': 436 | # P2PKH address 437 | cur_signatures[cur_public_keys[0]] = self.private_keys[cur_public_keys[0]].sign_input(tx, cur_input['input_index'], pkh_script) 438 | else: 439 | # witness input 440 | cur_signatures[cur_public_keys[0]] = self.private_keys[cur_public_keys[0]].sign_segwit_input(tx, 441 | cur_input['input_index'], 442 | pkh_script, 443 | bitcoinutils.utils.to_satoshis(cur_input['input_value'])) 444 | 445 | else: 446 | raise Exception("Unrecognized address type:", cur_address_type) 447 | 448 | # signatures done here for this input 449 | # append signatures to our signatures array 450 | for public_key in cur_signatures: 451 | signatures.append({'public_key': public_key, 'signature': cur_signatures[public_key].decode('utf-8'), 'input_index': cur_input['input_index']}) 452 | 453 | if (str(cur_input['input_index']) not in signatures_dict): 454 | signatures_dict[str(cur_input['input_index'])] = dict() 455 | 456 | signatures_dict[str(cur_input['input_index'])][public_key] = cur_signatures[public_key] 457 | 458 | # this will be our response object 459 | response = {"tx_type": prepare_data['data']['tx_type'], "tx_hex": None, "signatures": None} 460 | 461 | if tx_fully_signed == True: 462 | # if the transaction is fully signed, we will just serialize it with all the signatures 463 | 464 | for cur_input in inputs: 465 | # for each input, prepare the script_sig and/or witnesses 466 | cur_address_data = address_data[cur_input['spending_address']] 467 | cur_public_keys = cur_address_data['public_keys'] 468 | cur_address_type = cur_address_data['address_type'] 469 | cur_input_index = cur_input['input_index'] 470 | 471 | if cur_address_data['required_signatures'] > 1: 472 | # P2SH, P2WSH-over-P2SH, or P2WSH (WITNESS_V0) input 473 | 474 | # we will need the redeem script now 475 | redeem_script = self.create_redeem_script(cur_address_data['required_signatures'], cur_public_keys) 476 | script_elements = ["OP_0"] # the blank push 477 | 478 | signatures_left = cur_address_data['required_signatures'] + 0 479 | 480 | for public_key in cur_public_keys: 481 | if (signatures_left > 0): 482 | # append signatures only if we haven't reached the required number of signatures yet 483 | if public_key in signatures_dict[str(cur_input_index)]: 484 | script_elements.append(signature_with_sighash(signatures_dict[str(cur_input_index)][public_key])) 485 | signatures_left = signatures_left - 1 486 | 487 | if (signatures_left > 0): 488 | raise BlockIoUnknownError("Signatures left should be zero, but signatures_left=", signatures_left) 489 | 490 | script_elements.append(redeem_script.to_hex()) 491 | 492 | script_sig = Script(script_elements) 493 | 494 | if cur_address_type == "P2SH": 495 | tx_inputs[cur_input_index].script_sig = script_sig 496 | else: 497 | # P2WSH-over-P2SH and P2WSH (WITNESS_V0) 498 | tx.witnesses.append(script_sig) 499 | 500 | if cur_address_type == "P2WSH-over-P2SH": 501 | # needs script_sig set as well 502 | tx_inputs[cur_input_index].script_sig = Script([get_output_script(P2wshAddress.from_script(redeem_script).to_string()).to_hex()]) 503 | 504 | else: 505 | # P2PKH, P2WPKH-over-P2SH, or P2WPKH 506 | cur_signature = signatures_dict[str(cur_input_index)][cur_public_keys[0]] 507 | script_sig = Script([signature_with_sighash(cur_signature), cur_public_keys[0]]) 508 | 509 | if cur_address_type == "P2PKH": 510 | tx_inputs[cur_input_index].script_sig = script_sig 511 | else: 512 | # P2WPKH-over-P2SH and P2WPKH 513 | tx.witnesses.append(script_sig) 514 | 515 | if cur_address_type == "P2WPKH-over-P2SH": 516 | # needs script_sig set as well 517 | tx_inputs[cur_input_index].script_sig = Script([get_output_script(PublicKey(cur_public_keys[0]).get_segwit_address().to_string()).to_hex()]) 518 | 519 | 520 | if (tx_fully_signed == False): 521 | # we have signatures to append 522 | response["signatures"] = signatures 523 | 524 | response["tx_hex"] = tx.serialize() # the payload 525 | 526 | # remove all private keys from self 527 | self.private_keys = dict() 528 | 529 | return response 530 | 531 | def api_call(self, method, **kwargs): 532 | # the actual API call 533 | 534 | # http parameters 535 | payload = {} 536 | 537 | if self.api_key is not None: 538 | payload["api_key"] = self.api_key 539 | 540 | payload.update(kwargs) 541 | 542 | # update the parameters with the API key 543 | session = requests.session() 544 | response = session.post(self.base_url.replace('API_CALL',method), json = payload, headers = self.request_headers) 545 | status_code = response.status_code 546 | 547 | try: 548 | response = response.json() # convert response to JSON 549 | except: 550 | response = {} 551 | 552 | session.close() # we're done with it, let's close it 553 | 554 | if not ('status' in response.keys()): 555 | # unexpected response 556 | raise BlockIoInvalidResponseError("Failed, invalid response received from Block.io, method %s" % method) 557 | elif ('status' in response.keys()) and (response['status'] == 'fail'): 558 | 559 | # give the user the raw response as well in the exception object 560 | api_error = BlockIoAPIError(response['data']['error_message']) 561 | api_error.set_raw_data(response) 562 | 563 | raise api_error 564 | 565 | elif 500 <= status_code <= 599: 566 | # using the status_code since a JSON response was not provided 567 | raise BlockIoAPIInternalError("API call to Block.io failed externally, method %s" % method) 568 | elif 419 <= status_code <= 420: 569 | # using the status_code since a JSON response was not provided 570 | raise BlockIoAPIThrottleError("API call got throttled by rate limits at Block.io, method %s" % method) 571 | elif not (200 <= status_code <= 299): 572 | # using the status_code since a JSON response was not provided 573 | raise BlockIoUnknownError("Unknown error occurred when querying Block.io, method %s" % method) 574 | 575 | return response 576 | -------------------------------------------------------------------------------- /block_io/bitcoinutils_patches.py: -------------------------------------------------------------------------------- 1 | from decimal import Decimal 2 | from binascii import hexlify, unhexlify 3 | import hashlib 4 | import ecdsa 5 | import bitcoinutils.constants 6 | import bitcoinutils.bech32 7 | import base58 8 | 9 | bitcoinutils.constants.NETWORK_WIF_PREFIXES = { 'BTC': b'\x80', 10 | 'LTC': b'\xb0', 11 | 'DOGE': b'\x9e', 12 | 'BTCTEST': b'\xef', 13 | 'DOGETEST': b'\xf1', 14 | 'LTCTEST': b'\xef' 15 | } 16 | 17 | bitcoinutils.constants.NETWORK_P2PKH_PREFIXES = { 'BTC': b'\x00', 18 | 'BTCTEST': b'\x6f', 19 | 'LTC': b'\x30', 20 | 'DOGE': b'\x1e', 21 | 'LTCTEST': b'\x6f', 22 | 'DOGETEST': b'\x71' 23 | } 24 | 25 | bitcoinutils.constants.NETWORK_P2SH_PREFIXES = { 'BTC': b'\x05', 26 | 'BTCTEST': b'\xc4', 27 | 'LTC': b'\x32', 28 | 'DOGE': b'\x16', 29 | 'LTCTEST': b'\x3a', 30 | 'DOGETEST': b'\xc4' 31 | } 32 | 33 | bitcoinutils.constants.NETWORK_SEGWIT_PREFIXES = { 'BTC': 'bc', 34 | 'BTCTEST': 'tb', 35 | 'LTC': 'ltc', 36 | 'LTCTEST': 'tltc', 37 | 'DOGE': 'doge', 38 | 'DOGETEST': 'tdge' 39 | } 40 | 41 | bitcoinutils.constants.DEFAULT_TX_VERSION = (1).to_bytes(4, byteorder="little") # b'\x01\x00\x00\x00' # little-ended version 1 42 | 43 | from bitcoinutils.setup import setup as bitcoinutils_setup 44 | from bitcoinutils.setup import get_network as bitcoinutils_get_network 45 | from bitcoinutils.keys import P2pkhAddress, PrivateKey, PublicKey, P2shAddress, P2wshAddress, Address 46 | from bitcoinutils.script import Script 47 | from bitcoinutils.transactions import Transaction, TxInput, TxOutput 48 | 49 | # add p2sh_address.to_script_pub_key() 50 | def added_p2sh_to_script_pub_key(self): 51 | return Script(['OP_HASH160', self.to_hash160(), 'OP_EQUAL']) 52 | 53 | bitcoinutils.keys.P2shAddress.to_script_pub_key = added_p2sh_to_script_pub_key 54 | #### 55 | 56 | # override to use low R signatures 57 | def low_r_sign_input(self, tx_digest, sighash=bitcoinutils.constants.SIGHASH_ALL): 58 | # unlike the overriden method, this does not append SIGHASH_ALL, 59 | # so we'll do it ourselves if we add signatures to serialize the transaction 60 | counter = 0 61 | der_sig = None 62 | 63 | while True: 64 | extra_entropy = b"" 65 | if (counter > 0): 66 | extra_entropy = (counter).to_bytes(32, byteorder="little") # bytearray.fromhex(hex(counter).split("x")[1].rjust(64,"0"))[::-1] 67 | der_sig = hexlify(self.key.sign_digest_deterministic(tx_digest, hashlib.sha256, ecdsa.util.sigencode_der_canonize, extra_entropy)) 68 | if (int(der_sig[6:8],16) == 32 and int(der_sig[8:10],16) < 128): 69 | break 70 | counter = counter + 1 71 | 72 | return der_sig 73 | 74 | bitcoinutils.keys.PrivateKey._sign_input = low_r_sign_input 75 | #### 76 | 77 | # return output script given address 78 | def get_output_script(address): 79 | 80 | # try to see if this a valid bech32 address 81 | decoded_bech32 = bitcoinutils.bech32.decode(bitcoinutils.constants.NETWORK_SEGWIT_PREFIXES[bitcoinutils_get_network()], address) 82 | output_script = None 83 | 84 | if (decoded_bech32[0] is None or decoded_bech32[1] is None): 85 | # failed to decode information for bech32 address, so let's try decoding it as legacy addresses 86 | addr_encoded = address.encode('ascii') 87 | decoded_hex = hexlify(base58.b58decode( addr_encoded )) 88 | network_prefix = decoded_hex[:2] 89 | address_hash160 = decoded_hex[2:len(decoded_hex)-8] 90 | decoded_checksum = decoded_hex[-8:] 91 | network_prefix_and_hash160 = decoded_hex[:len(decoded_hex)-8] 92 | correct_checksum = hexlify(hashlib.sha256(hashlib.sha256(unhexlify(network_prefix_and_hash160)).digest()).digest()) 93 | 94 | if (correct_checksum[:8] != decoded_checksum): 95 | raise Exception("Invalid P2SH/P2PKH address checksum") 96 | 97 | # the address is fine, let's figure out if it's P2SH or P2PKH format 98 | if (bitcoinutils.constants.NETWORK_P2SH_PREFIXES[bitcoinutils_get_network()] == unhexlify(network_prefix)): 99 | output_script = Script(['OP_HASH160', address_hash160, 'OP_EQUAL']) 100 | elif (bitcoinutils.constants.NETWORK_P2PKH_PREFIXES[bitcoinutils_get_network()] == unhexlify(network_prefix)): 101 | output_script = Script(['OP_DUP', 'OP_HASH160', address_hash160, 'OP_EQUALVERIFY', 'OP_CHECKSIG']) 102 | else: 103 | raise Exception("Invalid address provided") 104 | elif (decoded_bech32[0] == 0 or decoded_bech32[0] == 1): 105 | # only support witness v0 and witness v1 addresses for now 106 | # it's a bech32/bech32m address 107 | output_script = Script(["OP_" + str(decoded_bech32[0]), hexlify(bytearray(decoded_bech32[1]))]) 108 | else: 109 | raise Exception("Unsupported address provided") 110 | 111 | return output_script 112 | 113 | #### 114 | 115 | # returns signature with sighash 116 | def signature_with_sighash(signature, sighash = bitcoinutils.constants.SIGHASH_ALL): 117 | # takes signature as a hex 118 | return hexlify(unhexlify(signature) + (sighash).to_bytes(1, byteorder="big")) # byte order doesn't matter here since it's just one byte struct.pack('B', sighash)) 119 | #### 120 | -------------------------------------------------------------------------------- /block_io/pbkdf2.py: -------------------------------------------------------------------------------- 1 | 2 | #from Crypto.Protocol.KDF import PBKDF2 3 | #>>> PBKDF2("alpha","",64,1,hmac_sha256) (returns hex) 4 | # hmac_256 pseudo-random function 5 | 6 | import base64 7 | import sys 8 | import hmac 9 | from binascii import hexlify, unhexlify 10 | from struct import pack 11 | from hashlib import sha256 12 | 13 | def pbkdf2( password, keylen, salt = "", itercount = 1024, hashfn = sha256 ): 14 | # native pbkdf2, no external libs 15 | 16 | try: 17 | # depending whether the hashfn is from hashlib or sha/md5 18 | digest_size = hashfn().digest_size 19 | except TypeError: 20 | digest_size = hashfn.digest_size 21 | # l - number of output blocks to produce 22 | l = keylen // digest_size 23 | if keylen % digest_size != 0: 24 | l += 1 25 | 26 | if type(password) is not bytes: 27 | password = password.encode('utf-8') 28 | 29 | h = hmac.new( password, None, hashfn ) 30 | 31 | T = b'' 32 | for i in range(1, l+1): 33 | T += pbkdf2_F( h, salt.encode('utf-8'), itercount, i ) 34 | 35 | return T[0: keylen] 36 | 37 | def xorbytes( a, b ): 38 | if len(a) != len(b): 39 | raise "xorstr(): lengths differ" 40 | 41 | ret = b'' 42 | for i in range(len(a)): 43 | ret += bytes([a[i] ^ b[i]]) 44 | 45 | return ret 46 | 47 | def xorstr( a, b ): 48 | if len(a) != len(b): 49 | raise "xorstr(): lengths differ" 50 | ret = '' 51 | for i in range(len(a)): 52 | ret += chr(ord(a[i]) ^ ord(b[i])) 53 | return ret 54 | 55 | def prf( h, data ): 56 | hm = h.copy() 57 | hm.update( data ) 58 | return hm.digest() 59 | 60 | # Helper as per the spec. h is a hmac which has been created seeded with the 61 | # password, it will be copy()ed and not modified. 62 | def pbkdf2_F( h, salt, itercount, blocknum ): 63 | U = prf( h, salt + pack('>i',blocknum ) ) 64 | T = U 65 | 66 | xor_func = xorbytes 67 | 68 | for i in range(2, itercount+1): 69 | U = prf( h, U ) 70 | T = xor_func( T, U ) 71 | 72 | return T 73 | 74 | -------------------------------------------------------------------------------- /examples/basic.py: -------------------------------------------------------------------------------- 1 | # Basic example for using Block.io for generating wallet addresses and withdrawing coins 2 | 3 | from block_io import BlockIo 4 | from decimal import * 5 | import os 6 | import random 7 | import sys 8 | import json 9 | 10 | version = 2 # API version 11 | 12 | # use a testnet api key here, say, dogecoin 13 | block_io = BlockIo(os.environ.get('BLOCK_IO_API_KEY'), os.environ.get('BLOCK_IO_PIN'), version) 14 | 15 | # create a new address with a random label 16 | address_label = 'tlabel'+str(int(random.random()*10000)) 17 | 18 | new_address = None 19 | 20 | try: 21 | new_address = block_io.get_new_address(label=address_label)['data']['address'] 22 | except Exception: 23 | exc = sys.exc_info()[1] 24 | print(exc) 25 | 26 | if (new_address is None): 27 | # the address label already existed, let's get the associated address 28 | new_address = block_io.get_address_by_label(label=address_label)['data']['address'] 29 | 30 | print("Address Generated for Label=",address_label,":",new_address) 31 | 32 | # get address balance 33 | available_balance = Decimal('0.0') 34 | try: 35 | response = block_io.get_address_balance(label=address_label) 36 | available_balance = Decimal(response['data']['available_balance']) 37 | network = response['data']['network'] 38 | print("Available Balance in Label=",address_label,":",format(available_balance,'.8f'),network) 39 | except Exception: 40 | exc = sys.exc_info()[1] 41 | print(exc) 42 | 43 | # get total balance on the account 44 | try: 45 | response = block_io.get_balance() 46 | available_balance = Decimal(response['data']['available_balance']) 47 | network = response['data']['network'] 48 | print("Total Balance in Account=",format(available_balance,'.8f'),network) 49 | except Exception: 50 | exc = sys.exc_info()[1] 51 | print(exc) 52 | 53 | # send 1% of the coins in our account to our new label's address 54 | try: 55 | amount_to_send = Decimal(0.01) * available_balance 56 | print("Sending Coins=",format(amount_to_send,'.8f'),"to Label=",address_label) 57 | 58 | # prepare the transaction 59 | prepared_transaction = block_io.prepare_transaction(to_label=address_label, amount=format(amount_to_send, '.8f')) 60 | 61 | # review its response 62 | # for in-depth information about the transaction you will create, look at the prepared_transaction object directly 63 | print(json.dumps(block_io.summarize_prepared_transaction(prepared_transaction))) 64 | 65 | # once satisfied, create the transaction and sign it 66 | created_transaction_and_signatures = block_io.create_and_sign_transaction(prepared_transaction) 67 | 68 | # inspect the transaction_data (particularly the tx_hex) to ensure it is what you wanted 69 | # once satisfied, submit the transaction to Block.io for its signature + broadcast to the peer-to-peer network 70 | response = block_io.submit_transaction(transaction_data=created_transaction_and_signatures) 71 | 72 | print("Coins sent. Transaction ID=", response['data']['txid']) 73 | except Exception: 74 | exc = sys.exc_info()[1] 75 | print(exc) 76 | 77 | # get the new balance on our new address 78 | try: 79 | response = block_io.get_address_balance(label=address_label) 80 | available_balance = Decimal(response['data']['available_balance']) 81 | network = response['data']['network'] 82 | 83 | print("New Balance in Label=",address_label+":",format(available_balance,'.8f'),network) 84 | except Exception: 85 | exc = sys.exc_info()[1] 86 | print(exc) 87 | 88 | # end :) 89 | -------------------------------------------------------------------------------- /examples/dtrust.py: -------------------------------------------------------------------------------- 1 | # This script demonstrates use of 4 of 5 MultiSig addresses with the Distributed Trust API 2 | # at Block.io. Each key can be used separately -- perfect for escrow, a variety of security 3 | # architectures, and ofcourse, for personal use + cold storage of savings. 4 | # 5 | # Any questions? Contact support@block.io. 6 | 7 | from block_io import BlockIo 8 | from decimal import Decimal 9 | import json 10 | import random 11 | import sys 12 | import os 13 | 14 | version = 2 # API version 15 | 16 | # use a testnet api key here, say, dogecoin 17 | block_io = BlockIo(os.environ.get('BLOCK_IO_API_KEY'), os.environ.get('BLOCK_IO_PIN'), version) 18 | 19 | # create a new address with a random label 20 | address_label = 'dtrust'+str(int(random.random()*10000)) 21 | 22 | # create the key objects for each private key 23 | # WARNING: generate your own keys, these are just for demonstrating 24 | # key = BlockIo.Key.generate() 25 | # key.pubkey_hex().decode('utf-8') # store this yourself 26 | # key.privkey_hex().decode('utf-8') # store this yourself 27 | keys = [ 28 | "b515fd806a662e061b488e78e5d0c2ff46df80083a79818e166300666385c0a2", 29 | "1584b821c62ecdc554e185222591720d6fe651ed1b820d83f92cdc45c5e21f", 30 | "2f9090b8aa4ddb32c3b0b8371db1b50e19084c720c30db1d6bb9fcd3a0f78e61", 31 | "6c1cefdfd9187b36b36c3698c1362642083dcc1941dc76d751481d3aa29ca65" 32 | ] 33 | 34 | pubkeys = [] 35 | 36 | # get the compressed public keys for our private keys 37 | for key in keys: 38 | pubkeys.insert(len(pubkeys), BlockIo.Key.from_privkey_hex(key).pubkey_hex().decode("utf-8")) 39 | print(pubkeys[-1]) # key.pubkey_hex()) 40 | 41 | # create a dTrust address that requires 4 out of 5 keys (4 of ours, 1 at Block.io). 42 | # Block.io automatically adds +1 to specified required signatures because of its own key 43 | 44 | print("* Creating a new 4 of 5 MultiSig address for DOGETEST") 45 | print(','.join(str(x) for x in pubkeys)) 46 | response = block_io.get_new_dtrust_address(label=address_label,public_keys=','.join(str(x) for x in pubkeys),required_signatures=3) 47 | 48 | # what's our new address? 49 | new_dtrust_address = response['data']['address'] 50 | print(">> New dTrust Address on Network=", response['data']['network'], "is", new_dtrust_address) 51 | 52 | # save this redeem script so you can use this address without depending on Block.io 53 | print(">> Redeem Script:", response['data']['redeem_script']) 54 | 55 | # let's deposit some coins into this dTrust address of ours 56 | print("* Sending 50.12345678 DOGETEST to", new_dtrust_address) 57 | 58 | # prepare the transaction 59 | prepared_transaction = block_io.prepare_transaction(from_labels='default', to_addresses=new_dtrust_address, amounts='50.12345678') 60 | 61 | # inspect it in-depth yourself, below is just a summary of the amounts being transacted 62 | print("prepared transaction summary=", json.dumps(block_io.summarize_prepared_transaction(prepared_transaction))) 63 | 64 | # create the transaction and its signatures 65 | created_transaction_and_signatures = block_io.create_and_sign_transaction(prepared_transaction) 66 | 67 | # once satisfied with the data in created_transaction_and_signatures, submit it to Block.io for its signature and to broadcast to the peer-to-peer network 68 | response = block_io.submit_transaction(transaction_data=created_transaction_and_signatures) 69 | 70 | print(">> Transaction ID:", response['data']['txid']) # you can check this on SoChain or any other blockchain explorer immediately 71 | 72 | # spend coins from our dTrust address 73 | print("* Getting address balance for", new_dtrust_address) 74 | response = block_io.get_dtrust_address_balance(address=new_dtrust_address) 75 | available_balance = response['data']['available_balance'] 76 | print(response) 77 | 78 | print(">> Available Balance in", new_dtrust_address, "is", available_balance, response['data']['network']) 79 | 80 | # let's send coins back to the default address we withdraw from just now 81 | # use high precision decimals when dealing with money (8 decimal places) 82 | amount_to_send = Decimal(available_balance) - Decimal('1.0') # the amount minus the network fee needed to transact it 83 | 84 | print("* Sending", "%0.8f" % amount_to_send, "back to 'default' address") 85 | 86 | # detour: what was our default address for the Dogecoin Testnet? 87 | default_address = block_io.get_address_by_label(label='default')['data']['address'] 88 | print(">> 'default' address:", default_address) 89 | 90 | # create the withdrawal request 91 | print("* Creating withdrawal request") 92 | 93 | # see above for what these steps mean and what you should be doing 94 | # this is just a quick demo 95 | prepared_transaction = block_io.prepare_dtrust_transaction(from_addresses=new_dtrust_address, to_addresses=default_address, amounts=("%0.8f" % amount_to_send)) 96 | 97 | print(">> Network Fee To Incur:", block_io.summarize_prepared_transaction(prepared_transaction)['network_fee']) 98 | 99 | # this signs the complete transaction here if you provide keys to get enough signatures to finalize the transaction 100 | # otherwise the returned object contains the payload of the unsigned transaction you created locally, and the signatures you want to append to it 101 | created_and_signed_transaction = block_io.create_and_sign_transaction(prepared_transaction, keys) 102 | 103 | # submit it to Block.io to append its signatures if necessary, and broadcast the transaction to the peer-to-peer network 104 | response = block_io.submit_transaction(transaction_data=created_and_signed_transaction) 105 | 106 | print(">> Transaction ID:", response['data']['txid']) 107 | 108 | # Relevant dTrust API calls 109 | # For a list of parameters and how to use these calls, please refer to their equivalent counterparts at https://block.io/api 110 | # For any help whatsoever, please reach support@block.io 111 | # 1. get_dtrust_address_balance 112 | # 2. get_dtrust_address_by_label 113 | # 3. get_my_dtrust_addresses 114 | # 4. get_new_dtrust_address -- as demonstrated above 115 | # 5. get_dtrust_transactions 116 | # 6. prepare_dtrust_transaction 117 | # API Calls marked with * are specific to the Distributed Trust framework. 118 | 119 | # end :) 120 | -------------------------------------------------------------------------------- /examples/sweep.py: -------------------------------------------------------------------------------- 1 | # Sweeps the entire balance (including unconfirmed transactions) from a given address 2 | # You must provide the private key in Wallet Import Format for this to work 3 | # 4 | # IMPORTANT! 5 | # Must use Block.io API V2 6 | # Must use API Key for the network your sweep addresses belong to 7 | # 8 | # Any issues? Contact support@block.io 9 | 10 | from block_io import BlockIo 11 | import json 12 | import os 13 | 14 | # must use private key of the address we're trying to sweep from 15 | private_key = os.environ.get('PRIVATE_KEY') # this key never goes to Block.io, it stays here with you 16 | 17 | to_address = os.environ.get('TO_ADDRESS') # the address to send coins to 18 | 19 | api_version = 2 # must use API V2 20 | block_io = BlockIo(os.environ.get('BLOCK_IO_API_KEY'), "", api_version) 21 | 22 | try: 23 | 24 | prepared_transaction = block_io.prepare_sweep_transaction(private_key=private_key, to_address=to_address) 25 | 26 | # print the summary, but inspect the prepared transaction in-depth yourself 27 | print("Summary=", json.dumps(block_io.summarize_prepared_transaction(prepared_transaction))) 28 | 29 | created_transaction = block_io.create_and_sign_transaction(prepared_transaction) 30 | 31 | response = block_io.submit_transaction(transaction_data=created_transaction) 32 | 33 | print("Sweep Complete. Transaction ID:", response['data']['txid']) 34 | 35 | except (Exception) as err: 36 | print(err) 37 | 38 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | setup(name='block-io', 7 | version='2.0.6', 8 | description='The easiest way to integrate Bitcoin, Dogecoin and Litecoin in your applications. Sign up at Block.io for your API key.', 9 | url='https://github.com/BlockIo/block_io-python', 10 | author='Atif Nazir', 11 | author_email='a@block.io', 12 | license='MIT', 13 | packages=['block_io'], 14 | python_requires='>=3.6, <4', 15 | long_description=long_description, 16 | long_description_content_type="text/markdown", 17 | install_requires=[ 18 | 'requests>=2.25.0,<3.0', 19 | 'pycryptodome>=3.9.9,<4.0', 20 | 'base58>=2.1,<2.2', 21 | 'bitcoin-utils-fork-minimal==0.4.11.6' 22 | ], 23 | zip_safe=False) 24 | -------------------------------------------------------------------------------- /tests/context.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'block_io'))) 4 | 5 | from block_io import BlockIo 6 | from bitcoinutils_patches import * 7 | import bitcoinutils 8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/data/.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /tests/data/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Block.io, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/data/README.md: -------------------------------------------------------------------------------- 1 | # test-cases 2 | Test cases for libraries 3 | -------------------------------------------------------------------------------- /tests/data/json/create_and_sign_transaction_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "tx_type": "basic", 3 | "tx_hex": "010000000b735465c299f05c5c7103e2b88e86ac7d91041055c2110d8b330bcb8da636b2170000000000ffffffff34965dc0e3e4aef1c244d426837c83291d9da1bfac830d41cc5a4e75504a570a0000000000ffffffff8f11671a7dbd5ad8f12c265266c6bbe259b5b960dfc8a394bab77b33d17409020000000000fffffffff3959c0d0281f0c265ea27e28c4f7369b0c4599338c3edde8f999d85b9a685cd0100000000ffffffff465503fc60f66fa094fcd506d27e099cd462af2209350d26e41cd2ee4b6fbe4c0000000000ffffffffe85f49ce97b5561c1fe96253c72daf37220855e710db0da9792c47c4035e7a020100000000ffffffff144cb1d56e06f514fba86afdfe39b79346a21490753a4af4125caedf79cdffe10000000000ffffffff343a521829667d856f38dbb8dd7f54f630305781ce1e8dd664863158c2f87ee30000000000ffffffff343a521829667d856f38dbb8dd7f54f630305781ce1e8dd664863158c2f87ee30100000000ffffffffaf3901d009e01e2e5946b6cab911b1b0e3e852cf0344488a50d5d0b0ec8a0df30000000000fffffffff84a933e79a29d870bec3059849cf44eb8eb9b1a000beb1ef486b2edd38de12a0000000000ffffffff044e61bc00000000002200203397d06887d687628c5581c79fad2e6591279a0635630dd876ad336c86bc86d487d612000000000017a91449e0ff8beaca290b0461e3160b0df0921d4468988740e201000000000017a914dac6654620e9a5126ba9fc129486d7177bf9f12187cf6f4c000000000022002028572b37c3e4907068225a8dc6157960a499efebcc0a44760634a399919bbc2c00000000", 4 | "signatures": [ 5 | { 6 | "input_index": 0, 7 | "public_key": "03f2e51b2f298bab1e4485d7d31a559de73fc29dced12f568e30264dbae4ec326e", 8 | "signature": "3044022017fb6ea34dfbe60437cb84ec67ec73454b9d9f58b75811a147b59bd5ca954bfc02203ee7b640466404c0676e8602ffbef48248a95aa1652ae6b228ec9b368b35138f" 9 | }, 10 | { 11 | "input_index": 1, 12 | "public_key": "03f2e51b2f298bab1e4485d7d31a559de73fc29dced12f568e30264dbae4ec326e", 13 | "signature": "3044022026e3ceba44ce578a7098c3f365603fc21d2d282d026c369927721de1590dc980022059d24fa4abe1ccc9b6986bd2cddbec2a3f5f97ad8c5f815146803113912b7312" 14 | }, 15 | { 16 | "input_index": 2, 17 | "public_key": "03f2e51b2f298bab1e4485d7d31a559de73fc29dced12f568e30264dbae4ec326e", 18 | "signature": "30440220517de4d3e1f51c9e5f7fb1376b22b404d8bc03c8df5ac734fb3353bade44fbab0220381d612eaafe6c2b0f3c44e265d3f47ded8ca706780a0f5448dcefc70d529f68" 19 | }, 20 | { 21 | "input_index": 3, 22 | "public_key": "03f2e51b2f298bab1e4485d7d31a559de73fc29dced12f568e30264dbae4ec326e", 23 | "signature": "30440220757c86210a336cc07367b2d12da8487244d1e84208547a61bd86f77e840aca3b02204fabd15eb44c582c66b3dd784512bf5c3ab80618f6761265168ec91d73094462" 24 | }, 25 | { 26 | "input_index": 4, 27 | "public_key": "03f2e51b2f298bab1e4485d7d31a559de73fc29dced12f568e30264dbae4ec326e", 28 | "signature": "304402201f14e391bfe16c22a32c061b9193be1465476f87509c93e1ea118fb188ac287702206ffa69ad153249597ab306dd35ba3c89a61358df3228f81cc406821915ee7a4f" 29 | }, 30 | { 31 | "input_index": 5, 32 | "public_key": "03f2e51b2f298bab1e4485d7d31a559de73fc29dced12f568e30264dbae4ec326e", 33 | "signature": "3044022030d55965464233ba251f0dbf9322717594d1718b045500abde2992cb8555b17502202f79d2952fd871102048a453f5cd52e1b09eac36012f6facddfe04a963d5c2d6" 34 | }, 35 | { 36 | "input_index": 6, 37 | "public_key": "03f2e51b2f298bab1e4485d7d31a559de73fc29dced12f568e30264dbae4ec326e", 38 | "signature": "3044022048cb445613a187c71ffa6ef3cdc6e9af0d86bdeaa3373a11bd9f04c6ad10a9b802204a4f84a78245c60532f93f0bdacecc53942f722ffc8764616613ebb98fa0f60d" 39 | }, 40 | { 41 | "input_index": 7, 42 | "public_key": "03f2e51b2f298bab1e4485d7d31a559de73fc29dced12f568e30264dbae4ec326e", 43 | "signature": "304402203147ac2addd51426c7e5fbf83daa82f5b7b59280111b236a3b42c98f98568d2702202869bbfb1aa9a0ce29c730d6eb27951e3d295f7315e617f5ad856c9e398ea2bb" 44 | }, 45 | { 46 | "input_index": 8, 47 | "public_key": "03f2e51b2f298bab1e4485d7d31a559de73fc29dced12f568e30264dbae4ec326e", 48 | "signature": "304402206385e6e518f6c40ae8ed5c33109e93b335d131eb49229c529b9ba722f737529e0220232374f110f84014e6274bcc4dce606fa25a3d59d4f028ebc42e45d070275688" 49 | }, 50 | { 51 | "input_index": 9, 52 | "public_key": "03f2e51b2f298bab1e4485d7d31a559de73fc29dced12f568e30264dbae4ec326e", 53 | "signature": "304402205019b1479d453820555969e0c101df35b8c505f558dbd0dc0d5ed5289976d94502201b392ca1fb42f0b616f5fe1558aba46736750998cedf619d760eb7787c47ac39" 54 | }, 55 | { 56 | "input_index": 10, 57 | "public_key": "03f2e51b2f298bab1e4485d7d31a559de73fc29dced12f568e30264dbae4ec326e", 58 | "signature": "30440220337043e970615bf1a12c6bb952aaca2bb26d159c4d1a1e8a389b003150c88f4702201bfac594e6ebf526b507512c38afcdffe90301b705f3a6344dfb94ddb8a62f40" 59 | } 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /tests/data/json/create_and_sign_transaction_response_dtrust_p2sh_3_of_5_keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "tx_type": "dtrust", 3 | "tx_hex": "010000000134965dc0e3e4aef1c244d426837c83291d9da1bfac830d41cc5a4e75504a570a0100000000ffffffff02204e00000000000017a914b181639abb73131894e5e96e00b0f5db8a42d7a087c4ce0d000000000017a9148d5d2999eb915d22a3584775f542cd48176fc5e28700000000", 4 | "signatures": [ 5 | { 6 | "input_index": 0, 7 | "public_key": "02510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11", 8 | "signature": "304402207bb0bf476f44b895e8daf52b8e810712e3ee1993ba25714eb9a26ee9d7c43571022070853c14e30aa98723d6277c7d44b618cebdf8ac0bc7b3cde7bc8951c2fbf775" 9 | }, 10 | { 11 | "input_index": 0, 12 | "public_key": "0201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf79139", 13 | "signature": "304402207649104a18c341be32d604b64c60b3afc8c8aa6f1691e39190cfafa605882b0b02204632fcac63ea0a110063736b8c284ddb1c07aa897999d0cd239a5f5baef25dbc" 14 | }, 15 | { 16 | "input_index": 0, 17 | "public_key": "039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d62", 18 | "signature": "304402203d91e8faa1bef7a5a0f0d8b3785a8ff00bbaff2b791cdc1a9b5fa9d3807d2e1002201e4c4019a3749d5181158a55915cf561c6500bce7f8dc48cf6691fb239423e57" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /tests/data/json/create_and_sign_transaction_response_dtrust_p2sh_4_of_5_keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "tx_type": "dtrust", 3 | "tx_hex": "010000000134965dc0e3e4aef1c244d426837c83291d9da1bfac830d41cc5a4e75504a570a01000000fdd0010047304402207bb0bf476f44b895e8daf52b8e810712e3ee1993ba25714eb9a26ee9d7c43571022070853c14e30aa98723d6277c7d44b618cebdf8ac0bc7b3cde7bc8951c2fbf7750147304402207649104a18c341be32d604b64c60b3afc8c8aa6f1691e39190cfafa605882b0b02204632fcac63ea0a110063736b8c284ddb1c07aa897999d0cd239a5f5baef25dbc0147304402203d91e8faa1bef7a5a0f0d8b3785a8ff00bbaff2b791cdc1a9b5fa9d3807d2e1002201e4c4019a3749d5181158a55915cf561c6500bce7f8dc48cf6691fb239423e570147304402204ad00b8fd0918e5a0e9ec353a32139265ab3e633748dc85494561f1cee748551022073b229aad08f7bf62020300a34df587336a30784b29439abb405435413c961f4014cad542102c59aa2350d024c456bb5047df28a5cc73fe2799999ed11035adff1dd24eb035a2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55aeffffffff02204e00000000000017a914b181639abb73131894e5e96e00b0f5db8a42d7a087c4ce0d000000000017a9148d5d2999eb915d22a3584775f542cd48176fc5e28700000000", 4 | "signatures": null 5 | } 6 | -------------------------------------------------------------------------------- /tests/data/json/create_and_sign_transaction_response_dtrust_p2wsh_over_p2sh_3_of_5_keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "tx_type": "dtrust", 3 | "tx_hex": "0100000001f3959c0d0281f0c265ea27e28c4f7369b0c4599338c3edde8f999d85b9a685cd0000000000ffffffff02204e00000000000017a914b181639abb73131894e5e96e00b0f5db8a42d7a087777312000000000017a914d4d8bb14b4edc0c080f5f8cd009da3949a92a3cd8700000000", 4 | "signatures": [ 5 | { 6 | "input_index": 0, 7 | "public_key": "02510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11", 8 | "signature": "304402202993a66e25fafe1d1b3135dc5318c7eaada3b261f11ee487bdae663927aad944022071853079a911468c8f39daedf245196b0117bcff490e611836bbbf5a56cdf28f" 9 | }, 10 | { 11 | "input_index": 0, 12 | "public_key": "0201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf79139", 13 | "signature": "3044022019c6f407d87a5d0a00d0627da79d64c9e947e8f0268108283807bb9a4b2ae86202203b0b3fe94998038e7d6f950bff05d94c9398343181b44653bd99fcb188357df2" 14 | }, 15 | { 16 | "input_index": 0, 17 | "public_key": "039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d62", 18 | "signature": "304402201fabc50f84f8577ec60157276b1d00c3375bbeeb9005c07fa6a76a87d786238c022055e67b6ad7f63b6eb1c70eadeb9208768dc2acad04472dd837c99f3afa0e0c23" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /tests/data/json/create_and_sign_transaction_response_dtrust_p2wsh_over_p2sh_4_of_5_keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "tx_type": "dtrust", 3 | "tx_hex": "01000000000101f3959c0d0281f0c265ea27e28c4f7369b0c4599338c3edde8f999d85b9a685cd0000000023220020d6c7f0329b85272f1a42e92dda2f69bc4aebd4714df489717684bc22fae56eb0ffffffff02204e00000000000017a914b181639abb73131894e5e96e00b0f5db8a42d7a087777312000000000017a914d4d8bb14b4edc0c080f5f8cd009da3949a92a3cd87060047304402202993a66e25fafe1d1b3135dc5318c7eaada3b261f11ee487bdae663927aad944022071853079a911468c8f39daedf245196b0117bcff490e611836bbbf5a56cdf28f01473044022019c6f407d87a5d0a00d0627da79d64c9e947e8f0268108283807bb9a4b2ae86202203b0b3fe94998038e7d6f950bff05d94c9398343181b44653bd99fcb188357df20147304402201fabc50f84f8577ec60157276b1d00c3375bbeeb9005c07fa6a76a87d786238c022055e67b6ad7f63b6eb1c70eadeb9208768dc2acad04472dd837c99f3afa0e0c230147304402202a6645d538554dfd0e6d88737e1711ef575e6acd1041f2e10f982e58b49a997102205ab473fab9426408cb95604d61bb0c00c48bdf599a29543aceb6b84f5d547f8701ad542102bdecaf3813bfb1d3d9a3a20844fb979a2cdd9664fdf5b8a8eaddaff95df67a032102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae00000000", 4 | "signatures": null 5 | } 6 | -------------------------------------------------------------------------------- /tests/data/json/create_and_sign_transaction_response_dtrust_witness_v0_3_of_5_keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "tx_type": "dtrust", 3 | "tx_hex": "0100000002e85f49ce97b5561c1fe96253c72daf37220855e710db0da9792c47c4035e7a020000000000ffffffff7105c9ea492ebac1fd204d066eeced78b5391b26ab7c87b6963e5e4e0a3a2ef40000000000ffffffff02204e00000000000017a914b181639abb73131894e5e96e00b0f5db8a42d7a087b82e0000000000002200205b218e070e9fe04b724129c8f95d27854d53ec431c58761d4a22d1a66c2360ba00000000", 4 | "signatures": [ 5 | { 6 | "input_index": 0, 7 | "public_key": "02510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11", 8 | "signature": "304402206d07505e3db3bf8c740e2d5c4ec9fce64e0bfa8758c073ea9757b4b1fa943b74022052c49f2601842688e234f3aeca59f6e47a72928226b3c0745370fc05f2a725ba" 9 | }, 10 | { 11 | "input_index": 0, 12 | "public_key": "0201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf79139", 13 | "signature": "304402201201d5df81209c101780ab520b13574513ef04a281045ba7dbf3bb8cf7d09315022025b62392b9b755d742b6a5f5d73e1b5f99b6fef4307a8cd7713564c6acd3fe38" 14 | }, 15 | { 16 | "input_index": 0, 17 | "public_key": "039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d62", 18 | "signature": "30440220387157472111bed5e8e2c3b598144bf2794408d7ca9c576e41a6d199de5ed4520220735a6b5b836ee17dd7974cb60d8b11fae451b1640a3d1e2e71491cee38c20f9b" 19 | }, 20 | { 21 | "input_index": 1, 22 | "public_key": "02510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11", 23 | "signature": "304402205824896bc40e3b213b3d21d4c4f00a01312f9fb4aa0d09311227a50a21a2d3880220033c688dd9302d56107cf4e4210b4dc03993e34c168022f5ec87498ab06e4c74" 24 | }, 25 | { 26 | "input_index": 1, 27 | "public_key": "0201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf79139", 28 | "signature": "3044022045448fff60a911ec7a061e25fc09172bfe0eb66ea308eacf3014a419e939e8e3022008c3f6df9f949437fbd6514f6eb6cf71ea507bbbd9eec1ee907cab701ac3c665" 29 | }, 30 | { 31 | "input_index": 1, 32 | "public_key": "039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d62", 33 | "signature": "304402207f702b80aa73343fea0bf8d0ff5bbcf6bd532f1f1dd0a12d8e44ea3ef4df8adf02202904cf3fa8c1c08c87be6d2465d8a0a0a5fad7cee0ec96d91e65bc58de46b6ef" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /tests/data/json/create_and_sign_transaction_response_dtrust_witness_v0_4_of_5_keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "tx_type": "dtrust", 3 | "tx_hex": "01000000000102e85f49ce97b5561c1fe96253c72daf37220855e710db0da9792c47c4035e7a020000000000ffffffff7105c9ea492ebac1fd204d066eeced78b5391b26ab7c87b6963e5e4e0a3a2ef40000000000ffffffff02204e00000000000017a914b181639abb73131894e5e96e00b0f5db8a42d7a087b82e0000000000002200205b218e070e9fe04b724129c8f95d27854d53ec431c58761d4a22d1a66c2360ba060047304402206d07505e3db3bf8c740e2d5c4ec9fce64e0bfa8758c073ea9757b4b1fa943b74022052c49f2601842688e234f3aeca59f6e47a72928226b3c0745370fc05f2a725ba0147304402201201d5df81209c101780ab520b13574513ef04a281045ba7dbf3bb8cf7d09315022025b62392b9b755d742b6a5f5d73e1b5f99b6fef4307a8cd7713564c6acd3fe38014730440220387157472111bed5e8e2c3b598144bf2794408d7ca9c576e41a6d199de5ed4520220735a6b5b836ee17dd7974cb60d8b11fae451b1640a3d1e2e71491cee38c20f9b0147304402200298d300b22bc1f02d63142ff0ffff2236cf2d47e3c79af28cae3da6488698a0022075647bf8137c9272eb3f5b8906842ade89efe57df2f08d299bbddb7d05c8ceed01ad542103a37fd7b94d49db3a3ab57d0b07b1408057e63d76d80a1286f841f04fa58e35402102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae060047304402205824896bc40e3b213b3d21d4c4f00a01312f9fb4aa0d09311227a50a21a2d3880220033c688dd9302d56107cf4e4210b4dc03993e34c168022f5ec87498ab06e4c7401473044022045448fff60a911ec7a061e25fc09172bfe0eb66ea308eacf3014a419e939e8e3022008c3f6df9f949437fbd6514f6eb6cf71ea507bbbd9eec1ee907cab701ac3c6650147304402207f702b80aa73343fea0bf8d0ff5bbcf6bd532f1f1dd0a12d8e44ea3ef4df8adf02202904cf3fa8c1c08c87be6d2465d8a0a0a5fad7cee0ec96d91e65bc58de46b6ef01473044022002cbf5c78011eea75f89f38fa3733dcbdfb2853cc11aa76ceb7c9a3839bf056f02200250151d80d85810fcb5bfd0db0515027c92d272514699987faa0e27a88bfcc101ad542103a37fd7b94d49db3a3ab57d0b07b1408057e63d76d80a1286f841f04fa58e35402102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae00000000", 4 | "signatures": null 5 | } 6 | -------------------------------------------------------------------------------- /tests/data/json/create_and_sign_transaction_response_dtrust_witness_v0_4of5_253outputs.json: -------------------------------------------------------------------------------- 1 | { 2 | "tx_type": "dtrust", 3 | "tx_hex": "010000000001230ecb7ff26cb82cebab87c44b4d1031239456b338308ee753e6ecfc1ab2ac79010200000000ffffffff3659d0c80935a690ae7c1c840525727ef5850784c3e3cfe74989451f13bdc0e00200000000ffffffff51918a88a68b449bea1823bab4d008a31b6505b2111da8a3baec4be8eb7abb870200000000ffffffff93cf389b25a9260ef8dc38c85b2997fcd499ba20ac4455982969070fec8832120200000000ffffffff778008f3a1b2b444f9585ec12111c74fe38ea7f9e61775f774abbe989a7df61d0200000000ffffffffd754c449213fc9a221d2521ec5c0662d338a8ede824d674889303cdd2e39861d0200000000ffffffff0a734cc8387abf17c9613e057d9e34423e0d07e80df3c9cddd7458944ded117f0200000000ffffffff733ce47a6506673b674bbe02e9a5081da1e948bb01712de25c71534743117bbb0200000000ffffffff257103178d2faaa3c97b142c8bc524415c5e10d5c45a35f4643da7ead22defe60200000000ffffffff6221ddc287332d3e495f04e644eae4b69f2773f7f7d9628a2cafbc1ce8eea2770200000000ffffffffd7312488afc29bc6374ed2624ab11565596ab3a1202e791b20aaf63ceb754dc70200000000ffffffff2c449bda0cddb0cb86f87d08f81fda0dda99e6ca6d338803ee585566890ef7f80200000000ffffffff1bb26c75d87f21cc577823488f322476a1a9056d3eacc7c2461ce95f89f94bfd0200000000ffffffffb030c4407fc529bcb3b5a3717fb4653186ec08f79b3ca26b4eca0ef763cf565b0200000000fffffffffceb5395bf8571d5d13b30b0852e7d97f152a02936c24d84c5ca7e69f84c334e0200000000ffffffffbe9099bea7b97eb2c9e29741c3f8ab3d5b2a7868f4d8863a6c71d62b65780c530200000000ffffffff3b9ed91b0d084784289b35ec1369d068f4b53f5aad8ae8ee4d0ee06f049c652c0200000000ffffffffe42e08cdca8167f4ebe30e3d7bfdb5774d54c7978682637f5536923e44ce18660200000000ffffffffb5398061d44ae37c61e8e01376437c9e437fd2977d4eb8197a6db746417e98990200000000ffffffff03d866a8bb550b859fd62b093cba74250800cf8795e8cece91ca54af74c64fb50200000000ffffffffdbbd804213a9a9fbe70cf68645ec128e4b8b0b0860ee7fd2c4ddc04628dca6c40200000000ffffffffe88df8e2623971ad70e1be880097d91f85401d1eb530ec4d8f5eaa2e185adf7e0200000000ffffffff7209394752ab5f0d2feb8012cfe78e896c96b0f50b83de295e41af00fbbed9ef0200000000ffffffff436c77f9ef61d8e520c578e07b978bbe86762aa567f72427ce9d8aab3014b4eb0200000000fffffffffb9443f80ad675c0599d74d215c22fe0de0be9dd186c1dbf898816ba9033e5a10200000000ffffffffeaa335d2dfa86fb90429fe9095153838e392ee557d97869cea43da6bc44e99570200000000ffffffff2a48566fa4df226efe9e7d659cd1f938486e376c2bec5e497899e5ead8a058480200000000ffffffffd5cb1966c64a8414a879fd45876180e043109bd33225bb1b0362349241e25e610200000000ffffffffc3412113a3c60e6f43d55d134117a16d1e90df439f58138e94857059ecbb24b40200000000ffffffffd714d13f5acec80070a2ea7dce701fd6be2c0b4f86306d90a36d90874b34fdee0200000000ffffffff24777b9fa9fdf16bef805fa0e30f7863adb1834ee8569dab3b70ad21bec378100200000000ffffffff79dee647f8bc65e418f79cd1ecfda4c742e0815c8e768018b38bd03d5a98dd7a0200000000ffffffff0806590239a55f6af0e6465e47cd93eb92ac168f933b3ab4feaadfb5057cc55a0200000000ffffffff0149916c16e288757fc96b05a70d3a52878b2c743251b0b31e723448b1582a000200000000ffffffff5837cad3f5a8b419ba6bcca212aad16e95295cf42d89fe4b2854e52786c616bf0200000000fffffffffdfd00204e00000000000017a914c70ca37da55009f249a3fd80403b92ff14eae32a87204e00000000000017a914d68ca31649c642e0a2270b13f61bfa34b0ae632387204e00000000000017a91402a6d0c2e11682b8bf3ebcc883b6971210d4eda487204e00000000000017a914d48910dbdd8f23890f747f79dbc61e82144176a787204e00000000000017a9149779b023948be8441ae94c965d79ee74fe6e5cc387204e00000000000017a91458762cca9caeec958ea6fe8be2ddee67ba50c74687204e00000000000017a914177f2ee61cd8508a4752254beedcf66207b07caf87204e00000000000017a91424379b17a26e4c012fd5a2fc1026bbd1f85a15a287204e00000000000017a91420919c0b1ffd57e98610f44732f94b2d68d0224287204e00000000000017a91463a822339e45214fe8b92bd51d9ef73d349b0cda87204e00000000000017a914eb289b268893a9ef4a9e0e39068b13c9b10d4fd187204e00000000000017a914016f454ea8043f115cce2f4200cb2f56381b7fa387204e00000000000017a9149a274c5b1f1df994bb89a7f3723c63c402d1fa8887204e00000000000017a914dc2a484c3a24f8ad5953fc8febb444bf1e940cf087204e00000000000017a91421f29dc04b03e1bb2450f6052707496d7b1b474d87204e00000000000017a91424e2df3368ce99bd4d41319b04abd8469d6694e187204e00000000000017a914cf902171ca56ad7b01e267e344496e5e1cf9e61087204e00000000000017a914bb1e9ebcc99cbd469eb5d67ee9df5f60fceaf93d87204e00000000000017a91498f1798c691fd542e74a2b03ece8250e86a3dd3187204e00000000000017a914c8ef62b7d8e072a4a9df23e829dec0fb289dae5987204e00000000000017a91415c078637957df24b949af0828741755d0e926e687204e00000000000017a9142baccc41414c0a0e9235f5cb055c955851492c0787204e00000000000017a914dac2255699bd060cc9312fb477e8ff448a8c797787204e00000000000017a914e8f9ae0b8e7969202bf14c85fcf2ba761a6073bb87204e00000000000017a91417d545c2201dfc96bf9184ad8e86253142f1b19787204e00000000000017a914a09fe56dd20658e7e9adf71dc2d8afa5ddff79b987204e00000000000017a914a776fa0514799a9a7c9e1d332967711b9f63783887204e00000000000017a9143f119c6199b9e8e198384fce365f10f07acb5fea87204e00000000000017a9146c195cd1d98117f70755bc1d46db7b7f1b00c19587204e00000000000017a9140c432b3ed2d70b38b468ee14c2a743f6eab3283687204e00000000000017a914b20028d3beccf3e5954cf052a4357d6bab1341b587204e00000000000017a91430893081aa4767eecc25fbd1dd08675cfc528d6787204e00000000000017a914f33e15d2cfe86f99b667716a9d2de7e481b0dbe087204e00000000000017a914f3d08bca97dceac1189a52c77c0040e7b4913a6c87204e00000000000017a9144f04565d837ee8392fdc62e857520c45c631c03287204e00000000000017a9149f4039ed6a4922a56a57af7fdb1baaeacdb5c6ff87204e00000000000017a914cb738b1034e6222cb0d9b8ed8936407ac51b341e87204e00000000000017a9141a0a85ae00716c932c15986d141162f772057c7c87204e00000000000017a914c061e2e5a9172ccfade0a4842fbc92392005d27987204e00000000000017a914c3d5accb2865297e4a8ffe11db6234a83906490087204e00000000000017a91451d348b9866f0094b05a47e491547edb217eff3987204e00000000000017a9145e20461572c961727609a08e7f6ee15b895dbbf287204e00000000000017a91494f6a68ecda68f050c01d38a8661c18d9570e75b87204e00000000000017a9146f5d541a57cbcaee2a3517e5c19b3531647325bf87204e00000000000017a9140fd6453e3b81a539d40df2a247b842b67a3fb45b87204e00000000000017a914f1c95abd6913b9c03eefe88a5db9244f1bc8958e87204e00000000000017a9149d65640c7eed06eb19af23451a52677a37da917387204e00000000000017a914a52f37cfe2e91082fb14e5369a5efcf06dfe455a87204e00000000000017a9145489e5be6671eb367e5312beedc1a646e791e1fb87204e00000000000017a91497cc3e013180e75995f78ba39d764cd8d947545687204e00000000000017a91467ec996b691b5ec2fc11b24b9f3df4f73bf2c71f87204e00000000000017a914843b91dececa4a52275c411712a46293afa8f06187204e00000000000017a9142f614fd46c84e844abca6a720e85a73d79cbd70b87204e00000000000017a914597ce1f024490182c88e8c4f27d444a1265aed6087204e00000000000017a914525a7ef0b2305e94c3065b40a7bba476a89b89b387204e00000000000017a91455e11b35615ef0dc7d9b1747133e1bc6beef1d0b87204e00000000000017a914c72a861aff69ecc85da0502d925f3a9051693bb187204e00000000000017a9143492fb75132e38c3877d1edca0ec9f0bc08591ba87204e00000000000017a914ce09e6b9e12290d4650ce8cdf0c8c22d4fa4fe5387204e00000000000017a9140e5f1256df04df0fae6fbc33fd7e5683e87d41ad87204e00000000000017a914f1611925fa1fc3edcc957a81a4d081095255357f87204e00000000000017a91414d3f994edc35fb63de481520f0f5f25c7c82a5687204e00000000000017a9148f60e53ed4a93e48f87167a2360940cad243ae5e87204e00000000000017a914a45d33d7f26f2312f9e5ebce208d897a3f19d76487204e00000000000017a91482f113fad254efc0ddebd7a72df2ffaf4ae7234d87204e00000000000017a9148cf557233b27ddfaacc42f1600d7c8c2cc52e4ae87204e00000000000017a91463519e110b48129266416f862b76480d9989122587204e00000000000017a91406c7f078624495e7cc9d5b961737791146f06bdc87204e00000000000017a9148ac7698446f2a9ae075c3963e2f904ab0feaef7887204e00000000000017a9142d6a4f776cf8be0ab280e658c008a9e17fd2acc687204e00000000000017a9146f1b2d5ec71e5f58c1bbc52fa3f79b7f430e3e8e87204e00000000000017a9147ba5adc1aafbd7f984756d361a259be75c2fda2d87204e00000000000017a914b8e4b815a7867d253e7944a11549838593df5ff987204e00000000000017a914e912992da037597e2124d8336c252c40fd77d4d087204e00000000000017a9146f61e0febe02316cc7ab2985064ee5e0ade447bd87204e00000000000017a9142ff040b8ded1e2c0dd7bdc2a9eb1394ef08db83e87204e00000000000017a914c6eea09c4df875106da4c37297bc5682cebbfea487204e00000000000017a91429d17ba52abee77015aef19c51538052ca795b7987204e00000000000017a9148e859aea188324b9998fce7a157444d4f2c886b987204e00000000000017a914ab83839b96f40d70496b01412b36ca97e5107ce587204e00000000000017a9144ab2acb452ecb754a6f5dccae97b3987dbc872cc87204e00000000000017a9145f3514dbb154baef2bc2aac14af04c3c21b0ed5487204e00000000000017a914a9082ee9496eebd65d8a62cee3fa690884c7105287204e00000000000017a914e6643d9e166537e201f6694f9dc1b51a703baa9387204e00000000000017a914a2f0925c5537e915d25b0461dcefc6e745bb8f8e87204e00000000000017a91489da55406ffbf13ce537360fd8210db8647d236c87204e00000000000017a914aa7ed0cba255995e9e687e1379b9f4ff15cc7f6087204e00000000000017a91462986ee5ae20ae7808ff88028692dbb1ae8f395387204e00000000000017a91416a7bf1bf0b4516ff3d5785ee78fc0e34e93191e87204e00000000000017a914f656fe02405be5c68a895b27b303f1c7b55d525b87204e00000000000017a914f5cf2e87478869515f0c4d1a9d4b400cbcc599e987204e00000000000017a914339c0b5574808db02ba45e21d8d5fb21fff033a487204e00000000000017a9147a2964d470109a0f3a013f4de53b30db9efaec3687204e00000000000017a9141adf7f9e196236c7a0a32149334b6f1b56ed4a2287204e00000000000017a914e0d5a795294bd75c38ec4211be6252c7024a138c87204e00000000000017a91468b134d148dba5466ac0d5d19c88145e41e6785a87204e00000000000017a9147665c49682957bc2b0fa654424e2eaedce0ecf7887204e00000000000017a9147e3598eaaff836836e4d5547d96c42b8d57649e687204e00000000000017a914a0c5607b8362addf43825d9db771514b3b67e4ad87204e00000000000017a914d6acc66dda8ca1bf3b333b682fe642b75395517a87204e00000000000017a9147b286c930df4b06fcaca9705b2a435781897cab787204e00000000000017a914b23aa167f04162139bf72615c9717c7d54e3328887204e00000000000017a914223cd01fbf593bb9dc4719c75caae068339f5b1d87204e00000000000017a914939910bdbe6eb72e7ace6852635df3dc5a73ca1d87204e00000000000017a91471ed3ff151db1d7bb7c2fe3889f2522db960e14487204e00000000000017a9148e92b798ed4d754a2d98d43e366719c15e61365a87204e00000000000017a914fa22b883f76df1c0efc91c80855410d748d29c0087204e00000000000017a914e2ce057de83516f064ac5283e3e4508d8694871887204e00000000000017a914437c8976357dc50bd57da966ca392fbe46e73ebb87204e00000000000017a9144399b0ab710ea0860819aa131b77ee420684f68d87204e00000000000017a91423a9d032bb06b54a8c9e6bc400d2e86e636a648687204e00000000000017a91490520584d77580e139e419d89bb33b832996128787204e00000000000017a91427419341c9995ff899d0e40f676f77e3cdf5cbae87204e00000000000017a914eb43ab55cfdfe4e6ec9d4e21b6102e9cdf38115d87204e00000000000017a91459ce0419660258cf9d0370a1a6591b29a151ee9687204e00000000000017a9143921e2e09cc601bf21b50c7bfc09e351e3a5d55f87204e00000000000017a914d3cd1ddb45f17ddda58e5e4f77597a89a8c043c787204e00000000000017a914e67cd562e6b889d8b05ca035cd39281c1d09d38787204e00000000000017a91428f5315db4b45876e567710bfca17848652803fa87204e00000000000017a914f75db64bffe7efa47c68bbebd69c420eb44e2fae87204e00000000000017a91490388d5bec04599aa169f199834ebe5b3d1a246187204e00000000000017a9145648290d53523ea618363ffb1ef8d26223989cec87204e00000000000017a914f11e9488d38659428b8d871a47203bd2fb15530487204e00000000000017a914e4c1094a2dec208713d0c7c57c83dc3967f5f78087204e00000000000017a9141c66bc481e0cde94d739c345931c43a4a12ed57687204e00000000000017a914d1c0f4cdc61ca1044fec849ddc16f7979a0c436e87204e00000000000017a914b26aec1e468b42386c54834ecd5bb23148405ca187204e00000000000017a9144a3808be26cf7a18d276483f1746df14ef77ad0487204e00000000000017a91490527e48cee3317debc8f019f8faef27a0eaf6f787204e00000000000017a914b60091832547ea6a76a16e3b20206c79d054167b87204e00000000000017a9148985d09ddb596767996654a7c1a1124412c6b95787204e00000000000017a914c379fb79e2da83bd03086403a9da122254e2adf687204e00000000000017a914349e1dbac217d98f1a0d0ff77c1101c7bc1e7be187204e00000000000017a914f96e2e22cef965e1750aca76bc11335b5f6d9d4487204e00000000000017a9147f56f33e4b2b8ec4a74c8d9800dc90543a02b4f287204e00000000000017a91462828a6d8a9f2dac5e7c1d2028862d73b2a29a6687204e00000000000017a914577cd5ced4c20b8e8a1eae86b08a280ea690bc9787204e00000000000017a9147ee9b2417dba8a982026a69d16399a898fff07b587204e00000000000017a9146f6c7a2e8e0d57e06b22d037eece88e0f5f9efa887204e00000000000017a91479695ab284b031b3d7c41b757d90d89f37da863f87204e00000000000017a9146cac27f59f45db03597d62af931d5a022032e7c087204e00000000000017a914d681a71ec418fa6a6fe0c6c160959502dab88d1787204e00000000000017a9147e185697d46ab2a704ee7ef364ff7d075489eea487204e00000000000017a91484222114ce30e97b61538543bae9dc63e464393b87204e00000000000017a9140f5a97be94462d30491369a012beb5db65062bcb87204e00000000000017a91406843e78ee746914c1e158cbb7da3a9c9e46b3f387204e00000000000017a9144d8a82e0f76eea534464d06184c75f1ae3e931b287204e00000000000017a9148e1ad7465067b64753454ddf89e1156eee2fd62687204e00000000000017a9145be71e09c9c98bb47dd66410478b9ac51f9fe78087204e00000000000017a914e49f3a6301a57370849318d8409942b6adea015187204e00000000000017a914255145b8c05395327cccc90170379ea462c8d61f87204e00000000000017a914424d96ea4b3e2abe256f50b2197e6e1d79faac2487204e00000000000017a91402bea32eef3147444b147ea7a68591c7ab1dafcf87204e00000000000017a914456898fe1970b00b906a8c7faaf1298ec7e9654387204e00000000000017a914194dfddfe779bba09ba08bd4c94b1d05f82a980d87204e00000000000017a914f4accceb0e217883f3abb841d7bc8d7e1fa944d487204e00000000000017a914b5dd0b4705f6e0c40f7d289a4660600324a936bf87204e00000000000017a91412746676d0ed638048e2b81d95a2af01c9a508e087204e00000000000017a914c564b4d005da76c8d5e4262ca1f8d67ea6bd06e887204e00000000000017a914a7764b658cbda8693839ed36bfa65ef0de3632d687204e00000000000017a914d6d3b2dbf66fcddd84225ef39c703faa3a1d937887204e00000000000017a914d97ca7f99258cf54ff171f5ec934cf6bcfe0a13887204e00000000000017a9145294c70f2fd193894badacc41a33fb98e2680c2587204e00000000000017a9143bd5a16fb18512195c03573dcccd43cba46a09ea87204e00000000000017a914e50eaa2388ce643aab45435cf7b2412a0687956a87204e00000000000017a9148e86d92c3b8478a515e6b376a2d541732186f07187204e00000000000017a9146ec9314580777f5a7e1a2367a5b193953681b33987204e00000000000017a914a9d92d790fc981de95ce126816ba708037ce5d8c87204e00000000000017a914ad1dae39d8311425b79344a30d391b2b94b9816487204e00000000000017a91407cdf0ba650b58669d1cf51292fed74b0b57911587204e00000000000017a91493537c3660e1776583bff1183057e2cbe5240d8287204e00000000000017a91482a960ffc504d4aedc26d59c1e1731e58f7d48ff87204e00000000000017a9146100c594e7020b3881a2c062155f1d46dea15dbe87204e00000000000017a914b6d953ef8b88d81337a2882c7aff857094d8013c87204e00000000000017a914c1f3bd17b8ce806b6027444824d45ce84bbfac0687204e00000000000017a9145b5fba5e28732acebb243b9ea702499411b1a95b87204e00000000000017a91439444d3d3e25aafb350d0bce15adfefc7641fa9687204e00000000000017a91448bb343960332dffad5b5cb7ddee3ae5f327d82f87204e00000000000017a914bf060028a12de9a514a1a12aaa0652a06b63c3cd87204e00000000000017a9140b0ada0a85364d792b8c70141ff1675273bdd07187204e00000000000017a9144511438c5b92b9b6e1820c2a3697b8367dc939d687204e00000000000017a91499088387b92766b80aa22334e442944395fe5a6787204e00000000000017a914f927ef7161bb94a379330467ac9e86edadd4e4f387204e00000000000017a914c310616f9d738cb67e009205c5bffb4a99bb4e0187204e00000000000017a9148556648c99999a790c740b44c153a3256cfd2b3f87204e00000000000017a91416d1831ff2f5ecdc9806ca854cc6843ac677c59e87204e00000000000017a914a7f0fca07a49f42a8583f2e5023e9b4bde14417687204e00000000000017a9149150943a90c9666d871a5179302b23189f12de4b87204e00000000000017a914b097d64d8988f6b2f8442f6151f915c4a322841787204e00000000000017a9142918a1c8076dd8790451658a45447dd69d7324ad87204e00000000000017a9142b8a885b73c0c39f8fada9438991045cd8c5224787204e00000000000017a914d78a5d792fe7d5a1d87da234e4d2dba2c80ef6fc87204e00000000000017a91465d68e2dd8bcddebea29427267fe519311e9e99687204e00000000000017a9147acd71ad1533337f5493152cee84dadb2a39b84f87204e00000000000017a91418e58092fb7deea5b4eaef54f741a3df14a9a7bb87204e00000000000017a914481a9d6208477c745b7e17b87ab9d70bf5a2b20587204e00000000000017a91432c7ad5734d7904ff052a28daea3db9dc1dd4f6e87204e00000000000017a914db7cd471731e1dfecea8a94daa5ea13f1c95053387204e00000000000017a9140fe89d25449e56e63fefe8cfdab9f55dc7569e2487204e00000000000017a91461e74ff03c4e45cfe8ad40e44906def1a60d54a187204e00000000000017a914dc2729762f3f1613ac3939ffeb1a5ed2e0870c1387204e00000000000017a9143648bb2cc883b4e773e5e7d9d572f22918d1513487204e00000000000017a914857cde84f456e7ad5dbe56318b63d514894f537887204e00000000000017a91431fa56b5f0ab182ca9f1cbf47439b21b3e22758687204e00000000000017a914e43398ee6950f497dbf4ad16432137016f0dbbfc87204e00000000000017a9144c65ac70cd9e7fcfa866d19a0df6602668bc736987204e00000000000017a914651a2390bae098dc7e0f1241d996f7f71608d67287204e00000000000017a91451d8ab518f385fd14ab54392a166f61670d629dd87204e00000000000017a914f9b2011a14eb8716e1e0582aea2e000d5b97b6d487204e00000000000017a9148559699aa17028be681352667457a79ce24587df87204e00000000000017a9142687fd65b92d58f847318b7c464e9ec593a4f1ac87204e00000000000017a914cdff3777ab46b6b89dd0e0f7a585d1369ad4644787204e00000000000017a91416afdee43f0fceff42d0557e9d7f4fa6243856c787204e00000000000017a9147e682c04f7d1a091b98b4df7344923a7b349e09087204e00000000000017a914dbc3942c1dbd1d1cec822af127d0272a7db31a2187204e00000000000017a91456887317ba69cf3bc890fdc061d54e4ee40d4a3c87204e00000000000017a914b08bf64d0a8b83e5ad9f6262bf6cf732a647168387204e00000000000017a91412e31056fad122fc5b617d66732ca5e554fdc24f87204e00000000000017a9147c98891d20a34c92bb4b2d9dd7aecf76d1f3747387204e00000000000017a914c828a75a60435ea6a38d5bb028cb4bafd7941a7e87204e00000000000017a9140918ec0f15f82286d6e970fac5cf3a2156cc026f87204e00000000000017a91429e05da33b9fde9c28ffc911eb8ecb7798dfa5a087204e00000000000017a914f0f30daddd9e300ed5cf5dd3093e7eba2659598c87204e00000000000017a914370bf38b1d7bc158db49d7502280e9100235e4f187204e00000000000017a914e8d0a07b25f925136f2de976312e6c1f7e1976de87204e00000000000017a91418e46a538473c16fde66e802f762aa8c5616ff0387204e00000000000017a914ae6aec39d55d9202c498e617f620f76d1a53dc4087204e00000000000017a914a18c026cf4806bee7eb9a52164e0b9ce24d34c8887204e00000000000017a9140ebe036c016275059a9494e1347462139bb2faeb87204e00000000000017a914f5862dabd1f9a8a7074ced69f194d46ff73ac25987204e00000000000017a9146f9614fceb2babaa49ac4404f8f5999fb967031387204e00000000000017a9144be1efbfa39e87918f4ab1fd39e8d8f6c377749387204e00000000000017a914d561f8e47117f9a8127527334ffeef4d1755b05187204e00000000000017a9146bf1357ef0f580f3134cd767e1e53cb690a9b4ea87204e00000000000017a914b396652d1462a5fca8fc3e7b36dc34da0c96bd3087204e00000000000017a914fa6622fd7a6f68cd9c997620859dd885a556c0c987204e00000000000017a91418e7c88f8ea3493a4f5478ed2c73919995337d0887204e00000000000017a914c558c17c58902a6edc08cdfd376c4ec6434b33c687204e00000000000017a91496b6c3e5826ebfd657c08192a02f980251dc26b687204e00000000000017a91464add069e9b93588b62678966a8b7b5928a7461887204e00000000000017a9143409d5b034c11527cb16101cdeb5272974f7786087204e00000000000017a914c47d6251ef57b0491626054e8837ab72307b4c9287204e00000000000017a9148e673f8d2a0596416e8d5b2da9a41d6150885c8587204e00000000000017a91463bc94a8c1c39c39cb00e4ec1e138c8a8af355f787204e00000000000017a91442d34007d750fe5f52e93f3b364ac3ca5f76ddc287204e00000000000017a91469e833a168fff65cdd5e2c83fb6294facc46c0ac87204e00000000000017a914ab394039bdbeac8930c5953a1ae4bf1c306c08f587204e00000000000017a91485ba862815c75fa68229041415e6c5094819b9bd87204e00000000000017a914ffeb0666dedc4b132c6744041b857068ae232a6887204e00000000000017a914260dfdf15ac6fd37f69543b7ad8c192120dce91887204e00000000000017a9144de0551ff83005d268afc95649679c7260e9cc4887204e00000000000017a914daddf63ee568a056dbd0143c113f8be24bc8d59887204e00000000000017a9147622e657691bddeb4a620a0012685dd1eef1f546870600473044022065bd10ddabace3327b2363f148cf87a9f31492c2568f849ac75d3b1c75bba99302203a4706843be12397fa0bc74610aa3b72424884f03800c4effad21442fa7b0c380147304402202748fe19a59e74a0af23b57b7e912e1dc50462b4341be19e4db014965e3f5e5202200b787963c0b4116922dfdaece89aec8e63a03098ab94db28ab95a297b4a1699801473044022008b609d0fae84fa3232da0c18d76764003f9d47d016cbb7f498f41d40e692bc102207d2aa4a7fb95597466a2dcc459ff7cc3629d90227727abf45c326663a04a6785014730440220447f26ad0faa23e4a114665414b4ddf016c4b4befcdef308e21a1be747f2bcae02205b6079e544c36f4ceac13bcaed556ec4ecea44f6b4a4e11f3142529a2dcf77f101ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae060047304402202cebf5c4f0661f79f1c0d391be460b3baf5044fd6157bbdab560ad633b9fda1202207911e5788a06d24085915ab0c61339c610a3d3b50eb2d4059793dafa5581456a01473044022073874ec2cd037cf39a9fff3b8aa6025d586d8f0e7c7085845e083800d6f5b4d4022002c36ceb9aac798b998d6cee355108c29174b43f9a866bcf1f5a4f153d1f3f7b01473044022044e9dc0537fa50522cc084e4b82a9be351c1089beb601982234216c60ba0834402206402699e82dc6119c52fd3d66a36c97db238555356fa8389c957bcff1256c8dc0147304402203edd67dc2533763dbe80ce8af76e2b0dc3ad1e79cd6430815b870d11d842d813022076aa5e4860eb09c3b967521f722b0a9027da8bfea95b58f9d6ddbc8651df769801ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae060047304402203e8adbb03fe009d199be1fc11c7065a219bb73e7d8f773cfe4dfd2ae45a5ecc8022003f330ee98b5b3c6ab22271b6d50ffe609eea0c56b5706fd4433f5767f193fc60147304402201f00ee93b92e3949b419b1b9a5b863c2f5895559710bb36280d677c0fbfd528c02200acaa55f9b7104ab0659c809b860206ce71326c9634368db223570bc22c8a9ee0147304402200fa7a2531961ebb4b2055624c0e8f244b556931e61f2e63cfc981599444e2f6d022002d1c2030861bcbb764c4846c51efcacff7ba290ddd056ba07efd702f3a508540147304402207ec9a6cfc2d08360a8d6168f0d8ce9f3a58b529ebe968a21a499e20b2b4521640220377a277fe03d991d9e3e73f029b29768d519dadc0a5e59c1ec13a9c2b3aff72001ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae0600473044022013e1ebdc2da7ac61ce583c50a646f0b39363a79c08a085ec2d881189544ecc5802204e9be87a8978c23bd40faefe770c6c68ad4af43dc3ccd25e7d1685c36bf62047014730440220189baa9b7156326764eee9cf0855400522fbf3898a8fea2326298c46dae5f26f0220372b701003941f047c3902f5e7552bac022a6712da7739e6e4976d5205d729a001473044022076b0a84773d9604b05f12c36d05b580583bf62aac8b0d64cb99a873c588aab0e02206b3a6d4e6d83abb7a8bcfe36030bb0ca3219ed2efe3af6e61dcea9aabfe70e5c0147304402201caf9833d03c99522a8a7dc31d2dc16ead5bc902e22f6e7f4dc32db4c8fe4a5902204f9a04673537c87451d595fb9210a4d3deafff15aa93beea68f525c494d0762b01ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae0600473044022032ef270d05aaf28c5a1bc0bb64179ffbd952ddacdf86c1d6f99191266c9cc36f02206ec1cda5ff29ea4379ffcf603952def31f5f3123c54aac6155139f8644eaa88801473044022062500e88357a520a2b60e61ecd935c2b2dfa4312b8d333d4dbfe38bbdd2fe83f02204316a8cabc05549bb64cffaf78e5e3c1a04a97ff0337b94c54d99932908aa80801473044022036d2a14f234e0b9ee5944b3a80c1d54dac16a190517caf8185b76e798b0324c002207c33d2ea8c1ece8e624e6d47e2cd85c0086b07d502eb29615bc863886b9d58040147304402201db4efab99e60e288b861687c762bd6d82ebb411c77f8d4c02622d6951b7e60a02200d52c5667d1168114dc7ed1478037327b1d0bd6011ab60c40426d3407816949b01ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae06004730440220341e1ca48244eb97f1aecded7b04e1bbdad5a12ee1c954ad07e3a6b0fd11e7700220123e49f3f26be7cab166260fea51c596b0098c3cfa2b5d97f2d6dff50d2c0c550147304402207c39f31848a84853dd5475f98f961fd0b16fa6b0e55090eb78756f1df77174d202204304fdb4de86a7ceffe904a02b36c03f206760d4a53b4f6ffbe7c9338a8ad3e20147304402200349d7265dd40737b59a8472a7168e72c39246ae082c5ed836fb7faaccf57d6e022078cbf66014dfbe9124a1c1622297cf0ab35ff35cab6432d321359b92a05cc35c014730440220162f2da5c12acda5ec3a41820ed1f3209c2b75934c153336907bad7785a6c2e402203101da60392741cb915021555c835c2d341a80578e1737db351dd514750774f301ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae06004730440220522513cca0c3ff5225f64934a232ee3809696899c2a23f8d7c09318b0f7169340220418186f2826600d328ff5bfb79eadad7c454f641b425b63919e3aa4f9511e3300147304402200ddd83191734b03cf3edb79db4b59e76d123189a4b800fc02e8fba77ebecd9fc022014fb8bad3b87a6c52bf121744788cd7da746e3792b4b2ecee9e3b73b735a0b8f0147304402202bac17b3c72cb892e411cc6090cfe1e26c9dfa5fe4f20ea4dd6002ed3f6c42b002206c9eeed07deb7ca797403637ea57167cf82bad7db17e5647c81e1689baf0b8d00147304402200dbfd121c1b8545318202c1dc3b3675b76efd8a152fb02825e3b656e68955a2202203e060ca439ad0f2be8be44e4294622b1abd363629dc1e1c9df4bcbbceb28ca5c01ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae060047304402207c7f563c238933cd1a7436cb947bc739ce2c40643df7da094b788d48ce579e7202207082759ded78c9c38ccd686a50b7fca2b12c33c25c9d38e6aa7752e5c7bd89b60147304402206c4767cf664de3cbcdcf9803c9b447288bac1d407b3455c3cfc627e31a732e1102206f13564f5a6aede0671602ab8e0af6307968701444dbaccf3904a4b9eafe43160147304402205904b78bb8d831761a9b072e6739f2c700b0fa1c3ccba34c31d21fdd2631b3d10220693fd3708d1b7c83023023c74ad2e2ade1ffc82b7d6b6f5dd8bb2665136584d50147304402204064aa8e81bbd2443c77ee5e7cd4feb04ba0f401cb0dcbb199d5b676cb077bdc02202fc52eef470a769379a4c54eb2f1e642c5d88730cbdee5da3d34adca1964d43501ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae06004730440220729d7c3a2a2c92ed6603a0cd34d75e13b5352dd12f1c3adb066d7ff470f937bd02200de11e8f0c491e4a0fed859946acec6af92ce312b840597d6fc025da3de9428301473044022001dd191eceefa1d43f0f32d29a57bef63f4d90cb58c625989920df7a48203d930220447690c8fd15ceae9ef7c86ec5834b3507c0e03ed13a759fa80f992a914e2a3f0147304402201b25c1cd6608a488bf46f19beba7ee9e9bc1736cc4a5212a2c88832524005f5702206ffe9b5e72908044dbe94c2d791ebf1a66322a4689ec23bbf28c9443ffe6dafe014730440220210e60b36e439b85b2a1a65ad109d4c3c442ef479e43d82f0f340618e73e69df02207b5251b81cc7a9565c34d7525379ad6256d920d63bd0ee81d8f23d4a2adc57ac01ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae060047304402200bf685bf2fd84fc44d3caec18e295de941f43391f03971c59987c920c052fb1902205142955c9ae36ac1d799f0d799ee1fa0c17890420e473276070641c93b7c62cf01473044022058f6c0c1552145b9342bac021b7cd258aad9a029a5f23b3e7365af3d9d7c805002200c4bb3ba5621ff7e5baf0bacc145a6e07e7562d8337a5b59eea2b9431318592c014730440220301206a59297e15db90418fcb8a0f6b073f634f0fbc493e7f25156448e82d11002206fce236e0f32a613f1f0891e4ba04f527708046d7bee91eae9e2a6104b9cbeae01473044022070ed6ed8676567dabbbe8f5f417a97649368cd6032149f8ce9ae1fd7afbad24a02204433207051a6d12c33636e094c03c10e30d7a11e54cc34a4ccbc48c83447e48901ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae060047304402202dcb5d1c9554a3f800c621b3c171617f47fc64b93560c453f8d4c2000d85e3480220209c23936d8ff6d7612611088adb70be99abe744e5e8646309645378471cbf2401473044022005b5b38a0e400d46f6b8595dea6d706f9aea7840189130bf5f44ac08a147b7cd0220612a752eb8d3c7dd79234b384c12aeec211915f3b1322a866a40fa1c243ccc7a0147304402200b497b0e030e084c3a8ed4db0d5e98ef6be59b63e12b34086a0969b1073028ff0220220a891059ead777659c38b16f710f2d2a6b15ec430836bf76b1d1305e535d4b0147304402203c2c87f02ed021901f746b05cd0111279be481e4c2c3ef97ba709e7460e78fd20220540385625f4b7f5869048aeb33708ed04ed6f21af84a09cce7bf2dc76a54a1b701ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae060047304402200ee03c52fc1a67a44724be3575f37f4addf1d183cf4c321b7a87c72d930ac5d3022032cee1024e38e4c7936b3d0d67eab31cd0dac660ecee32be96d6491e091cdab6014730440220081749ff93afdaf0deaed41a7d9e11f340ace71b557acc5f486eb30be4e08f3702200bb163ea81ac35ac2fd03a5ff333d412b655b8dc261a4099b55217864b7490ab0147304402207677095b8744c59c4269d78457151e49a6fa6dccde3a87224b7fcd5c80d1a8660220795447b84f6d57d3471ebc22d6ba69efa5c8a41191bcd6f9515402a0e66dc5420147304402204ba5407c257252abe7ec31ec77a49d8de3db8d58d443fd5e34e3f590ba448ab60220513f0bfc4803220aa39be8824365391725492bc99c46355474ba8f5c9c77e0b201ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae0600473044022062f01265ff043bbe25a749dc757c99aaefe9c47cf8a71dad80811f8dc8b4766a02201e54448243824090b793fb2176b613f2c14e92fdb464d82aec19e28f02a7d0ac0147304402204d9f72d2b464ca90d56c5a08f50d16bcddde8b9670775f68f4653bdb6ce804200220508c50f627625dc01044581d75f49920d6da60d16485494a8d3800d47718b1a30147304402206e5b8b791d3e37b1b32fb084729cb51696e11777ef17110b5567935d020ac745022023bde8093875e1f37638ef78d861b4bcb60a0876547502a9df2894bae310d19a01473044022019138101433c6805771f0401ab14c357d70c5d0347bf34283aba56fe2042c72702206dc21b4019af58f017f4d91ce2a5948ef9412de8688b673ec5b0160cc4e3df2d01ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae06004730440220310804ffe3e3c6f646949f959734deed4874eccefc6436306f6d626b1abfad6402201e7d132cd0c056950126ec0e9cd441a43258b03eff3386e75b244963fc9385f201473044022016c893fc293e23ce48ed5e2cbbe5e20df92d026b08c04ceded4a073a2ff9d6e602203eb7a9e7aef14a9e3517546a03e169743978065259fac90cd361ad40ca891099014730440220790ea2e86cbf757a0b521a4ade42f63c261a1d60326317a3efdb8c9d21171fea0220769baf1647b74dd2cbbb35c8ef460153cbb0a913337d246987a710b55efe08e6014730440220795a1580bf6660410dac078b5de7bf842aeb7096c70a2e29f96168b069d64d9b0220634f770e012f301be600629de0229acc228b1d11efcc900d053a7e143ba7947501ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae06004730440220320efd1ea327311cc83ed498d2407f598872f8da127f0447c1531ed864405775022001884e7ffc829f7d81254bf468636590c75ae65c04539e2cd63f0c650d8ed028014730440220782d157941635b8c63fcf83a76d371d02657ae73b54d40363b5f8e77a74a456d02200a259156aa7cd82930ca2759e2aa1d0913e9df8caca45d466447737863c79e9b0147304402202ea2ac6ddf0ae0f6d4b07b7d00fa2b19fceab912985309560e77ddd3475abd2102207b76096670b001ab8f7036c2962f88589ea6121ec4e14d08359c69d0664f3b5a014730440220658505b2b5e978a8f55c9ef0df8596c0d0cffc523f249d6715cc12baf58c9cd2022066e5b46ec446e05aecb8db7682d7b29b7918bfbd87ab0d0c5bc38f42cff59d4201ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae06004730440220401035e995908c72811354a55464a076dc3062b7ffc5548b5507afbdf12c523802207bfde36a01bc96c5f90487909b6b63b9426531528c9a90dff68f4a55de8bcf6c0147304402206b70ab68ba9b1634f041a981bbcaef6d3c57884ce08d65303adeaab2c2274e6302203d5ad58d6bf365a39dad04dab1059a02919b70bddf1b6b3f7c4902f7daa89ca4014730440220640d72b2868ee9a2883bd84cadc4243c2f8a5a30b5ee303943abb84e7e572dda02206d285a1c6e34d4b87b4aefbaefc05ed0612b4523f70354652bd232fbc0ad0ef40147304402205dc84b14baff101bd9fdb9a4e7e58c6e202b7668f8fd40274adf528cec92eec402205dd7e12b2a867d619f0174e0cb6c6fe752e69f881106d86cce4c2dc744b7a8eb01ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae060047304402205c8b7d148f329e38408c723b4c3b649ce076dae16fb4d168c9c31e47626af38002201014be05b5483e7984edec4cff0d8c873aa264185c55ce0a04cbb618ba32883901473044022051bb66657b70e86628c309a3aaaab2ba83349d8a40dcb440eb2fde0c9d5ec29f022047cc6675f9b6f50633ee998543b303d3c6e6d25970846340d8e5b6ae171c489f0147304402203a6bfb188347d1558e2efc5ba7671a2768ad1ade8563fe4deb329114567169a802207dbab5210551c2187d34976881f6cb2aae013c5e82411bdcb330e4440d463c140147304402207c44ce1b3e10919de0f61f2cc6cc45fdbcbdf00c5d2e747ecdc852a99ed3c74a02204aa374b609c81b214de573f20212fc571606044357425f0a09cc651abcdeaace01ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae060047304402203577ab6c0f7fef55790a21fe7cb85ac89fba8452a2c59dfb8ff8cb6e9afaf3e702202eef3772f0743d50d45bb78ce0f52ccaf3233b6faf976ffd953ce16259739ef101473044022074c8f1cd22d78499695608b503c8f5f7edac48a7326432065931e9235116667b0220730e6f76c2d7cc94a8968349451db208b243050cc11d66d3fddfea0c302de0510147304402206a7ccedd071f321a6baca582f8f20a4fb92e66935387b8484507ac1e3918d5af02204bcf5113a8cc0697ace3147db35f78d239f5d3e98c7d1bcdc18be72576d5b0eb01473044022027143e76892feb5d1b5905e31ce04b97b75d80f121a3c25ebff0a29227bc68d3022028865f36d43c032595b1000632303d3baf1b54f26c07cf768c2320bfe1ff710501ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae060047304402203f7b869031b07fff33ec782d616c1cf22be70f2006bb1b406dba625610f7601602202e21c8347d465c7895d2394f7de8fd63a54f0f35f1d5c2f757be9a792db99c590147304402207dcf8d088d7d07367089b0b5e8269d553f65ed224b549eec644ad07b5b267f150220534f0de2ff6aaac17bae62e44e45ccf2d561c6d57d1817525913a173938bf72b0147304402200a50ac0836d019461b6a2cf4e532a73856fd0577888e707f5afb5fefb646fcef022008a98ce580a60cee412a8126d113eb468c9042e4ba4945988cd20d1d1f44fedf014730440220195725b7c269772dd19e06846e5d6ba5026bcf8e8fb59027e8fa8c222d0717c102204bf52047181024551b5993a9425c84f7b1b99bf2483d061e373f7bf7fda1049101ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae06004730440220445d3d89d1fc3b7236e467bf938838bca84a263a7d62a6a78552337c5cc62038022071822284ec45d064de439c268406396fe395614f0d3f02851254fcdbad679f100147304402202ce48fa9bc6b4b54d7b8dc0d2aa46c46a51665ba52ffd27936801f4b2276cda302205a4711c8c01fc4bc01790bdc90f9901c7fd03819af9276151d7c0c2c981c8f41014730440220423588c14b95a3f19aab2d9230b3527e5954b87b29ec15ee61b1a2ab4d63419e02204c9bd7823afb2890a80c01c4ed12e9e8642af7b943d4355eb9a173c531b16c1f0147304402201628ef284db0ae5ed10749b94a4c19015cdc9d44e535167a115d890d6fc55e8d02204e5bc9684b2fd06925dc84e99ea75da9a0976b8f05fa160a076be25a2768de3501ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae060047304402204d6be5b792667f4ef92bcf2f3dc7a7c753533044919be30c862696d7c0572b6902203e10507b9ef5c599b27a86dfef1434a7ce9889b3f8f773e0ac3594b292d6ad56014730440220427d9bbe40cc9be631858c79dbcd3dd2ab7df4583014baccbfe0faa628994ef60220099397e5579c5e93b71c47946a96d9227589f31c9495b8a5eb304e9d3ce3dccd01473044022056b2812a6c839fa41bf6b10151c59e49cb665e1087c01d943d8b1d13c81f5d1802204137cc6e2df045d83b861681b337bf762759cb5162ec1e888463bbc67325ea470147304402206a179d08718f21a250395d675487f47e8bc5f081d17ed8ce2b4fd791cb7371f1022048c51c1bb6442a2d0984e66ec7f5638a7b745d840b80c96563df3f3868fed03301ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae060047304402200afa09e07d0efa87fb7041864a6c2d546229ffed9e164648dc23cce1d0ffae4a02206acd69e23627e50ddb59607c44515f0fd32f574304924e2f7e339de2d5b3e253014730440220668da976b3b4b1c7fc07d3e660296f0b139c1898c0dd6fefa252c849b86eb8ad02203b05f03af044b057eb32f11b43960b5e58a66a6ecf625dab74c6db72ec081f7f0147304402206861104c5cc7f162be6566c59bfedd0bf2ff1b5ca01deef8eab7d48aeb0c0cbf022071be913695f3fb07144658298b52cf6da484c169e873ad6706d24cbfc5dbbedc0147304402203ddb7490ed7c3ab798c1f45dde69977578f65494740b3c302a684918048d90ea022056aea75c4f539338ac5d3fbec3eacaf640f8e522b04f2df96492d17797c5685d01ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae0600473044022014935323818897afbefb265a6aebe76b8d249b0221147c6d3bbf9cf9ca7f927302201459eaf3c028b3d1facc7856262be3f9db27a38bf4c9cbe848e6665bf9d31df801473044022066942aaab171ac8b063c4a5e5ff804217f11576f4c361ba5c92ea2dace5f0aff02206c0766302c34b77e6e836fa6689755a08310b7aa38adb530e1cadcd9f2079cdf01473044022004423b4fd0d446522e0e2f57c8cbca615b29c51acd0f14f2b16a5e026c14271f02203d84b011927819960a43642de92b1c8f0638d011285b6241f77a186d3bc554c60147304402206fc8e07f5cf2c2e6c2b50a68884db69da43b6ab809c2885981275e07e8cc93aa022042cca1dbe4331a65cbcf837005d0fe29bf6051fdedb9ae8583b94b3d0ed0f7ee01ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae06004730440220496557115f8e567d0a31054564ada9f73d542744a8e749c66520ddeda2c514390220269a8b78db0847e7ef35056fdb10dde5eb76a1fe41b6f4eb87825dce78b39bf30147304402201c8af06fdc96a01df80977c0865db7049f82ac8057cdeb212ae41e8f477a353a022074804bc6095dc729c5ff3fac3ed23039bab24077853c4b55476ecfdbe6721d5201473044022003e06784e0133dc6a9fd896d43ceff63e0f5ed250698e26ccbf05ff3eddc562b0220647807d1e510fd07f8e8e3135baa8e83b2ee1efbdad9bf2f646230b44fea6af3014730440220110b44430b094128ab37af272e276e04653c949554a40b5577482ad2cd7edd23022007687cc5b6cb832c83867a525f9638be546d8a451a0a86d1d46cb3d528ad366d01ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae060047304402205e94a7e115864e06a54ab3acc7f5622f6908dad86967bdb2ee71e0a08e0fa3b3022058a0cbd9eeea52256ee38859650cb95d560c8506e0e4f2b1f5a0e314530a1ac10147304402203a79b2411ebf0c02e1625ff5f3610ad6f036557703a3274f9d1aea6d593edfa10220120df88db2806777862879fd6679df0e2d6ad4a28615138c68633456fe1441f60147304402202b55a8404f292c3f43d83fb9c45115e0a1fd165d077d53ea48ddf35b7d2eeab602204d269470671749688986cbca65d065bbd6e1f525e0feeb420298c80029c8d4f401473044022028996b7da50179ab0f497a5e970c82be1a6d4e36aa0b25a2d538c06f5e6c137e022040ec1b06aa14e328d6167d55c8da7c1aa2b5311661a4ea8df121e5b673fa9a4101ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae060047304402202c1dd2ca3bc57f76f9e75228f72cf161885808d20d6c4666b54db3df28f33cdd02203262799779f585837b51aaafd086c8f147180895c9c448d6cb1b99311483e7500147304402203f56ee6036de8582783200f708a039001ab6040b07d9807b32ec10a7f4800ffe02202d70bbae318b000f27a2011e5f1118dd721d99848403b2b4e2ccb798b91b0aa00147304402201897c2e154d6d903a2c44b42ab971e840a49bfaf6d933ac1b4661631ac50bd0702207b4b1ffb29fdc73f2b24a029e468bea9e37855fe765031d195e584852fb92f3d01473044022041075bd90068f809044c07d65e3e00d766bf2936de351c85d2d46d9f3860adad02200e89757ebf48eff9780ed5b959265bcee0e2f2d01e5c1e33cf5c71310c7dd85301ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae0600473044022031c5a55fe1f360fb50c8c7d4c300b73d5456768d8e2e1a9e5f0b7463f9bf57fb02202e3ee59e1ca6c676838a1577b6df1662a8ceed44db3b9812b21e96e5cf2135740147304402203519363e72c5a0d5ed4f6e7674fa9eb7df4f8e798838629af0b24ab593f51d2002202100dc6ca817d6c25bc52bf32e81acf4195c4d92123b8f205c73f4f749f42384014730440220469afc4d2659cf31c147699f4f894efd364f902324d64e2e81132249cfe89e6902200e9ee3694e407798032f20c0e3f23e732a0cf452d1987f691bb405878c7cd032014730440220027188911c32909d86e1c2afff9a94900d2806c03355497b132b25fa8a210880022038c4c36c500dd9e1da5cc3dc9a7cb2ad9905a66fb2ecab3b8d71047e28aa457c01ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae060047304402207a4700117eec4715abb553de0850109021ff9f9719dd863faa4b8417957f8f5f02205f86a7f098dfdfe4696b9ec3b0e3e16e5eb5c6057db02b4693456cddcfbd39e701473044022061013500ea068e73df6c57c368b1b8ae1a2e7fef576d2da3911f342a68bcc82f02202aedc060f6d2f4ee1f50a38ec7fbbb691f38be9990296d789e3cdd87f1ab022601473044022026a1aae8a21c0d8264ff5a3e72ed884b8e435c77cd8417550015c014c7293b7202202450716abd8b7770d8b9b1eb22ef2158e00435e9fdc1715aa8fcf46b030214ee014730440220409d7688b6a701107618796fce9d1831c1a835152ed5705288d24b5b342c4f8602203fb904f3dbf04e3319abf3b1ed79f58ea2f474dcf6b8097edcf8e673c57bd29d01ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae060047304402206ccbabe19e7038e444d7104b117c755118dd6d98a1732d7f4a3973d238102a1c02201b05cfcb7bfff576372807511fe844eb60f598578455c865a320efad03df34e6014730440220179486411bfc134c9b90d6f7f028b3a6a30d5965d32730161fe38f56c1f97cdc02201fee01a1fbba7bd5760db8b354f218061594f26527ad208c987877a1a270c20201473044022024b4c6ec9f434dde29a488e6d391e8b2f30bbccad8362dc6ad4d860cfe67b829022068b09b4386d8d6c88d5645a9f077df7473f3627ecc1d257adba834c268ecc0df014730440220776d99aab713b601a5592f8066a027ac24aed42e83b73ec833606561cbebb739022020ab6aaf0f98153d173394a4a3f15c02115df9eb1a736e6e405a129c0cd9c52701ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae0600473044022044cbc6e8f6d50f6b67c63c87b44a8dffbe02fc11ce093ee136709dee85a0bacc02201d088a8af13a605285c9db5637d9b84b777f96e89c5664927a731ce740c04082014730440220391b5b84d6061dd6803f4e281f21c49648fdc55e10e4067ba09f3dde673184b50220220240802bec46d2823ed96e51cb2f67142aa1b0a6d46adcc0d19184a1ef269901473044022045b6fc7378794b4a711a64e1134def87db3db88cb85a529ebe420f53ddd0f89b02205d1f96ac712b2eb06ef7b1061b9d9372f4a8bb39da805891c95c9568e776b6460147304402206947118fe72f33408fdad56c57786e430b24bd42ae008a8e9ce48812bbec3442022064ae34e8bbc4e20ea73d2ab9cf520094a43fe16de8964c5788f9d94ad7eb73d001ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae060047304402206451fb9c96de7eaddd5726219abad66fb510cd8826f67b9f04ab5162afa3130802201d0d851a3daca0c707a4b0cd2f7da27cbf8501fb3dfb67b28414ae29ad44b491014730440220712cbace84d7f0318bfe0de12beaf1d5f04b0802584500aa61b7d973c8722f2402205bc8f4f82dfa1154f8c021338ff1d326b37ace8eea32373bd23e8ef9d347ab9401473044022007f9eb10256e75c20ebe62008be2abb8a3afa2efbc7ac30e50780b06071163cb0220391a39d8bb1d259fa183fb6b827eb59551f5ae4d644c981e94b8e15a74be09840147304402202f28dcb0e27b3c61f99f721540e760bb02dcdcae61b0fb97d912160516f2b24102205b6ec8d02ece399e365c90eae70c6d9b79c8c41e272598f35cfa50d2a841d82c01ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae060047304402203c91e79b790f4af859483ae7b719e194cbf01d4b6bf7913cbe16237a6724af4e022061723a7cf3a2f40d864eeece957b04f319a245a354cfcb8064496fa3304274ed01473044022073d5b711d7e145ef9dbe3f6b9123225a2725244da0df9641109f1567f2d3ac6302207a51125f18814a62bbcc50ee8d8b83db8eebe5cdff13f2e591300e09ba0ae9b90147304402202a05245db339bd351bf2f848e43177059fc9994811073c4115394c8b20338c4e0220012c5289689e1a77dc83028e7473287dcfd85ce53276d41d053d9daed314cb630147304402200ea768c6acf94b4d2b3edb4b49bac69713c60b73acc1e152a1d18af9f595adbc02207b7ef4be5915b415109f339b69d815ecd9a9b401f1fb5f4be2e308441dbc789301ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae0600473044022026a206092e1f8f84fb1989c09909b529a89f35db1715b66833c367aae30c841702201651647d6fe47519dfe37e54bbb68e7b4bb0a76bb95c935e845f522becf48aac0147304402205140f2e94b3ed9c37963a5259c605d21d98012915b555bd30f33e8b98a77f72c02204259d05c18efa8d98242bf5515c8429a73fe3442ccaae0cfadbe32b6946252850147304402202736238b04e99eef7433074ac73c5b77d6b543cfff0ddaed274c7cfaa8d8c06c0220739dbb8e80bb992ee42756b85bf6afe2150b84ca8a97be4bc472efc6db6690e2014730440220757272b033cbf9e52f027f5cd67e959194640d261652ab845da7d04c54952cfb02203078ac884c1cdd7a13f27317a47e00f434fad5e64a85312e90f84c5953f8aed801ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae060047304402204b3f562b6cdd686ff0765760df91966647e5ae05c1fa6d7067516a1967308f1002202e3440c35b3c1a58f796f07e3ad188f0dfe428fe2fa3bd4c2faff78acf82495a0147304402206c0f4aa6d05f8846fe4957f327b46ab6cd8e535c992d1ac28a87071ea059c32d02204aad72ed46b9385cb6befe779a1b119eed7b7ae917253f45796c47498541328c014730440220124224d84aa9d610f85551846ed758e9a8a92f3a23e551d2892105c99ee4b7130220143e6a03b4689e0b2108d49d788d2c207a1c40ff9e240a132a2169636865755e014730440220357fa4c9b04f3d3f0865ec5dedc72544e79e32eb68336fc4a02bc349819210f502207e0b6c805fc60a0c4bf66e4186e161d9059dadc0da9174451a251e6a892c960b01ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae060047304402201fda6b9c0d16547fde557b12f5b5865b77525a01e5e1d3d441e25f963df1d3930220481e3dc56935e545d2edcfa3c0892bf260fdfa87a8764fd88e9aedcd89367565014730440220649c7c0d9d4f151454c16b3396149ef0331c389362311e9eb264497726ba40fe0220748f54aae582136ac14ac85bcf1d1cbd479279190b456bce4fe4b809a744fae50147304402203dd25a0144c88f0b9cbb0d8885a4fd215576a9a4edd986ec0eedb237b15b56b9022066c040ceb7f9cfaafe19e53a78651037891dc2859f4866289b25a5d76527bff70147304402204464b08502924ab396af57074796a5f468b7ac969b4a3d1a6d177ae5f5001e9e02203f31886b9e78b8a58cdb9578d377c74f6e0cfd5b62a2f7e42873ab1ee5393cbf01ad542103974f49bffe0d5276fb04c1972372f9af676d71cdfe5c3b4192edb13d7a22d77b2102510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11210201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf7913921039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d622103ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc55ae00000000", 4 | "signatures": null 5 | } 6 | -------------------------------------------------------------------------------- /tests/data/json/create_and_sign_transaction_response_sweep_p2pkh.json: -------------------------------------------------------------------------------- 1 | { 2 | "tx_type": "sweep", 3 | "tx_hex": "0100000001388ee4b67ff518b8b6df2a968e56f955ff2a641c2da83c57af71dc6927f069fe000000006a473044022072051d3b7c82d064e6254cb05e555e1aa3b9dd01c485e5f8dd14a9554cba594f02203584a1df0551b022dfdf0c1c8fd43ce8e613561ff0699bbb400854b676f328d40121021499295873879c280c31fff94845a6153142e76b84a29afc92128d482857ed93ffffffff019c7601000000000017a91449e0ff8beaca290b0461e3160b0df0921d4468988700000000", 4 | "signatures": null 5 | } 6 | -------------------------------------------------------------------------------- /tests/data/json/create_and_sign_transaction_response_sweep_p2wpkh.json: -------------------------------------------------------------------------------- 1 | { 2 | "tx_type": "sweep", 3 | "tx_hex": "01000000000101833a790831abeef4bfed5c058cd0829e63ed7bc2376dcd6fdb17e8e487c3032e0000000000ffffffff01c88804000000000017a91449e0ff8beaca290b0461e3160b0df0921d446898870247304402200834738ce6c085a477e12262ce118bef94deaf1dc853d8ee2bb2fd4627c65b8802207453dcf8e92cbd5c4226f6ced9430e4d55f011e58814cc1e9e149af16d5d4e100121021499295873879c280c31fff94845a6153142e76b84a29afc92128d482857ed9300000000", 4 | "signatures": null 5 | } 6 | -------------------------------------------------------------------------------- /tests/data/json/create_and_sign_transaction_response_sweep_p2wpkh_over_p2sh.json: -------------------------------------------------------------------------------- 1 | { 2 | "tx_type": "sweep", 3 | "tx_hex": "01000000000101ca4e083e5a5d1c5324579b5b4ec631152003d4613d327d3fdabc810ca1b282470000000017160014426c9f352ce2c8f06bd8a90a265ded4b3759c243ffffffff01280203000000000017a91449e0ff8beaca290b0461e3160b0df0921d446898870247304402201f96089d0ffa9bd6652c601ea4b8296bd5cb18f44b341ee07df772b4cffdccbc02203afa9197ea471f6f45e4e9d19392581ae22826e639b1b9844d3dad3ead70d82b0121021499295873879c280c31fff94845a6153142e76b84a29afc92128d482857ed9300000000", 4 | "signatures": null 5 | } 6 | -------------------------------------------------------------------------------- /tests/data/json/create_and_sign_transaction_response_with_blockio_fee_and_expected_unsigned_txid.json: -------------------------------------------------------------------------------- 1 | { 2 | "tx_type": "basic", 3 | "tx_hex": "010000000330063782af1e81eddbca00856ff646a6df8164585ae66c784924572f909a5d5d0000000000ffffffffd4ea031218137a06524a7c13c921a3d271fa97905d378e719927d6fedc48c3ad0000000000fffffffff05c2af854968d1d38b1a2e8b3982c13cb0b5dccebb09f09fb098bacce4dfa9f0100000000ffffffff0344d612000000000017a91410eb388a17a299ed87b8962e25efdb15f3cd86fe87393000000000000022002025e75758225bf2706fdd2dac763f0134fdea3be04a3f15414f9bde188280f72d147bfc000000000017a914108dc42df9d23e581d09a512cd38f58b48bd444d8700000000", 4 | "signatures": [ 5 | { 6 | "input_index": 0, 7 | "public_key": "034309721935937713a2dd1c33c5c44a10a30674bebe0542aeb145ce4c9790e742", 8 | "signature": "3044022024d0b5749b7c198f5bc57a61e63dddf9b90b5c98ae53b063b647fb98c8be61090220794354afeaae1447fe9d60fb101990d55855ca90c5ac5290f4cb18aadf2ca65a" 9 | }, 10 | { 11 | "input_index": 1, 12 | "public_key": "034309721935937713a2dd1c33c5c44a10a30674bebe0542aeb145ce4c9790e742", 13 | "signature": "3044022074abc51fc7f25405053de3e4f861ff75400a64053fe7ccb93cd15b642a428ea5022043ec5b19cabedfe164e1b05fbedb675c060ab5f6f390132442bf712289d29036" 14 | }, 15 | { 16 | "input_index": 2, 17 | "public_key": "034309721935937713a2dd1c33c5c44a10a30674bebe0542aeb145ce4c9790e742", 18 | "signature": "304402201591e9a6fe6dee39da9883ed550d3fbe26f347fd2e1ad492f6a59643059d0b8102207c550357b459852d05577c5069391740267878c420e55e9aca47e1c672680348" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /tests/data/json/create_and_sign_transaction_response_witness_v1_output.json: -------------------------------------------------------------------------------- 1 | { 2 | "tx_type": "basic", 3 | "tx_hex": "0100000001784ecc9b864ff9b8a97eaacd138fdf193a5d889735702a4f726d2f87631de45f0100000000ffffffff03204e000000000000225120000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433204e000000000000220020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e864339f4c00000000000017a914026b9608a26f40f74644cc60622d1067c3696ac98700000000", 4 | "signatures": [ 5 | { 6 | "input_index": 0, 7 | "public_key": "02d2cbf77287c1443759abdd35f239e7da2f52c992258653bc8dd577ae63c78628", 8 | "signature": "304402206d093e2a14e80e01f3c8eefc76d651d01c3dedbd1f0dabf78ace5b352ca660b602202aac312b5b7f0b8c79ad22e75d2523ee39d9c62a35be4db102b5c8f3f296f662" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /tests/data/json/get_balance_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "status" : "success", 3 | "data" : { 4 | "network" : "LTCTEST", 5 | "available_balance" : "0.2432256", 6 | "pending_received_balance" : "0.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/data/json/prepare_dtrust_transaction_response_p2sh.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "success", 3 | "data": { 4 | "network": "LTCTEST", 5 | "tx_type": "dtrust", 6 | "inputs": [ 7 | { 8 | "input_index": 0, 9 | "previous_txid": "0a574a50754e5acc410d83acbfa19d1d29837c8326d444c2f1aee4e3c05d9634", 10 | "previous_output_index": 1, 11 | "input_value": "0.00936600", 12 | "spending_address": "QZVSzPeaEJxB9bYuDEL7iWrHSdGbAP3pXV" 13 | } 14 | ], 15 | "outputs": [ 16 | { 17 | "output_index": 0, 18 | "output_category": "user-specified", 19 | "output_value": "0.00020000", 20 | "receiving_address": "QcnYiN3t3foHxHv7CnqXrmRoiMkADhapZw" 21 | }, 22 | { 23 | "output_index": 1, 24 | "output_category": "change", 25 | "output_value": "0.00904900", 26 | "receiving_address": "QZVSzPeaEJxB9bYuDEL7iWrHSdGbAP3pXV" 27 | } 28 | ], 29 | "input_address_data": [ 30 | { 31 | "required_signatures": 4, 32 | "public_keys": [ 33 | "02c59aa2350d024c456bb5047df28a5cc73fe2799999ed11035adff1dd24eb035a", 34 | "02510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11", 35 | "0201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf79139", 36 | "039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d62", 37 | "03ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc" 38 | ], 39 | "address": "QZVSzPeaEJxB9bYuDEL7iWrHSdGbAP3pXV", 40 | "address_type": "P2SH" 41 | } 42 | ], 43 | "estimated_tx_size": 585 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/data/json/prepare_dtrust_transaction_response_p2wsh_over_p2sh.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "success", 3 | "data": { 4 | "network": "LTCTEST", 5 | "tx_type": "dtrust", 6 | "inputs": [ 7 | { 8 | "input_index": 0, 9 | "previous_txid": "cd85a6b9859d998fdeedc3389359c4b069734f8ce227ea65c2f081020d9c95f3", 10 | "previous_output_index": 0, 11 | "input_value": "0.01234567", 12 | "spending_address": "Qg1QzgjUDkwaHeT7Yznuyu2V1keJieDVAF" 13 | } 14 | ], 15 | "outputs": [ 16 | { 17 | "output_index": 0, 18 | "output_category": "user-specified", 19 | "output_value": "0.00020000", 20 | "receiving_address": "QcnYiN3t3foHxHv7CnqXrmRoiMkADhapZw" 21 | }, 22 | { 23 | "output_index": 1, 24 | "output_category": "change", 25 | "output_value": "0.01209207", 26 | "receiving_address": "Qg1QzgjUDkwaHeT7Yznuyu2V1keJieDVAF" 27 | } 28 | ], 29 | "input_address_data": [ 30 | { 31 | "required_signatures": 4, 32 | "public_keys": [ 33 | "02bdecaf3813bfb1d3d9a3a20844fb979a2cdd9664fdf5b8a8eaddaff95df67a03", 34 | "02510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11", 35 | "0201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf79139", 36 | "039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d62", 37 | "03ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc" 38 | ], 39 | "address": "Qg1QzgjUDkwaHeT7Yznuyu2V1keJieDVAF", 40 | "address_type": "P2WSH-over-P2SH" 41 | } 42 | ], 43 | "estimated_tx_size": 268 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/data/json/prepare_dtrust_transaction_response_witness_v0.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "success", 3 | "data": { 4 | "network": "LTCTEST", 5 | "tx_type": "dtrust", 6 | "inputs": [ 7 | { 8 | "input_index": 0, 9 | "previous_txid": "027a5e03c4472c79a90ddb10e755082237af2dc75362e91f1c56b597ce495fe8", 10 | "previous_output_index": 0, 11 | "input_value": "0.00020000", 12 | "spending_address": "tltc1qtvscupcwnlsykujp98y0jhf8s4x48mzrr3v8v822ytg6vmprvzaqj8jd0h" 13 | }, 14 | { 15 | "input_index": 1, 16 | "previous_txid": "f42e3a0a4e5e3e96b6877cab261b39b578edec6e064d20fdc1ba2e49eac90571", 17 | "previous_output_index": 0, 18 | "input_value": "0.00020000", 19 | "spending_address": "tltc1qtvscupcwnlsykujp98y0jhf8s4x48mzrr3v8v822ytg6vmprvzaqj8jd0h" 20 | } 21 | ], 22 | "outputs": [ 23 | { 24 | "output_index": 0, 25 | "output_category": "user-specified", 26 | "output_value": "0.00020000", 27 | "receiving_address": "QcnYiN3t3foHxHv7CnqXrmRoiMkADhapZw" 28 | }, 29 | { 30 | "output_index": 1, 31 | "output_category": "change", 32 | "output_value": "0.00011960", 33 | "receiving_address": "tltc1qtvscupcwnlsykujp98y0jhf8s4x48mzrr3v8v822ytg6vmprvzaqj8jd0h" 34 | } 35 | ], 36 | "input_address_data": [ 37 | { 38 | "required_signatures": 4, 39 | "public_keys": [ 40 | "03a37fd7b94d49db3a3ab57d0b07b1408057e63d76d80a1286f841f04fa58e3540", 41 | "02510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11", 42 | "0201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf79139", 43 | "039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d62", 44 | "03ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc" 45 | ], 46 | "address": "tltc1qtvscupcwnlsykujp98y0jhf8s4x48mzrr3v8v822ytg6vmprvzaqj8jd0h", 47 | "address_type": "WITNESS_V0" 48 | } 49 | ], 50 | "estimated_tx_size": 402 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/data/json/prepare_sweep_transaction_response_p2pkh.json: -------------------------------------------------------------------------------- 1 | { 2 | "status" : "success", 3 | "data" : { 4 | "network" : "LTCTEST", 5 | "tx_type" : "sweep", 6 | "inputs" : [ 7 | { 8 | "input_index" : 0, 9 | "previous_txid" : "fe69f02769dc71af573ca82d1c642aff55f9568e962adfb6b818f57fb6e48e38", 10 | "previous_output_index" : 0, 11 | "input_value" : "0.00100000", 12 | "spending_address" : "mmaB1tz8Nth1WdLx2bQJDavD15gJEyeCAM" 13 | } 14 | ], 15 | "outputs" : [ 16 | { 17 | "output_index" : 0, 18 | "output_category" : "sweep-amount", 19 | "output_value" : "0.00095900", 20 | "receiving_address" : "QTLcyTFrH7T6kqUsi1VV2mJVXmX3AmwUNH" 21 | } 22 | ], 23 | "input_address_data" : [ 24 | { 25 | "required_signatures" : 1, 26 | "public_keys" : [ 27 | "021499295873879c280c31fff94845a6153142e76b84a29afc92128d482857ed93" 28 | ], 29 | "address" : "mmaB1tz8Nth1WdLx2bQJDavD15gJEyeCAM", 30 | "address_type" : "P2PKH" 31 | } 32 | ], 33 | "estimated_tx_size" : 205 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/data/json/prepare_sweep_transaction_response_p2wpkh.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "success", 3 | "data": { 4 | "network": "LTCTEST", 5 | "tx_type": "sweep", 6 | "inputs": [ 7 | { 8 | "input_index": 0, 9 | "previous_txid": "2e03c387e4e817db6fcd6d37c27bed639e82d08c055cedbff4eeab3108793a83", 10 | "previous_output_index": 0, 11 | "input_value": "0.00300000", 12 | "spending_address": "tltc1qgfkf7dfvuty0q67c4y9zvh0dfvm4nsjr86ltvf" 13 | } 14 | ], 15 | "outputs": [ 16 | { 17 | "output_index": 0, 18 | "output_category": "sweep-amount", 19 | "output_value": "0.00297160", 20 | "receiving_address": "QTLcyTFrH7T6kqUsi1VV2mJVXmX3AmwUNH" 21 | } 22 | ], 23 | "input_address_data": [ 24 | { 25 | "required_signatures": 1, 26 | "public_keys": [ 27 | "021499295873879c280c31fff94845a6153142e76b84a29afc92128d482857ed93" 28 | ], 29 | "address": "tltc1qgfkf7dfvuty0q67c4y9zvh0dfvm4nsjr86ltvf", 30 | "address_type": "P2WPKH" 31 | } 32 | ], 33 | "estimated_tx_size": 142 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/data/json/prepare_sweep_transaction_response_p2wpkh_over_p2sh.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "success", 3 | "data": { 4 | "network": "LTCTEST", 5 | "tx_type": "sweep", 6 | "inputs": [ 7 | { 8 | "input_index": 0, 9 | "previous_txid": "4782b2a10c81bcda3f7d323d61d403201531c64e5b9b5724531c5d5a3e084eca", 10 | "previous_output_index": 0, 11 | "input_value": "0.00200000", 12 | "spending_address": "QQxAAYSmsYGhq3W5NMFTvjV3QABTKErjAV" 13 | } 14 | ], 15 | "outputs": [ 16 | { 17 | "output_index": 0, 18 | "output_category": "sweep-amount", 19 | "output_value": "0.00197160", 20 | "receiving_address": "QTLcyTFrH7T6kqUsi1VV2mJVXmX3AmwUNH" 21 | } 22 | ], 23 | "input_address_data": [ 24 | { 25 | "required_signatures": 1, 26 | "public_keys": [ 27 | "021499295873879c280c31fff94845a6153142e76b84a29afc92128d482857ed93" 28 | ], 29 | "address": "QQxAAYSmsYGhq3W5NMFTvjV3QABTKErjAV", 30 | "address_type": "P2WPKH-over-P2SH" 31 | } 32 | ], 33 | "estimated_tx_size": 142 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/data/json/prepare_transaction_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "success", 3 | "data": { 4 | "network": "LTCTEST", 5 | "tx_type": "basic", 6 | "inputs": [ 7 | { 8 | "input_index": 0, 9 | "previous_txid": "17b236a68dcb0b338b0d11c2551004917dac868eb8e203715c5cf099c2655473", 10 | "previous_output_index": 0, 11 | "input_value": "0.00020000", 12 | "spending_address": "QcnYiN3t3foHxHv7CnqXrmRoiMkADhapZw" 13 | }, 14 | { 15 | "input_index": 1, 16 | "previous_txid": "0a574a50754e5acc410d83acbfa19d1d29837c8326d444c2f1aee4e3c05d9634", 17 | "previous_output_index": 0, 18 | "input_value": "0.00020000", 19 | "spending_address": "QcnYiN3t3foHxHv7CnqXrmRoiMkADhapZw" 20 | }, 21 | { 22 | "input_index": 2, 23 | "previous_txid": "020974d1337bb7ba94a3c8df60b9b559e2bbc66652262cf1d85abd7d1a67118f", 24 | "previous_output_index": 0, 25 | "input_value": "0.00020000", 26 | "spending_address": "QcnYiN3t3foHxHv7CnqXrmRoiMkADhapZw" 27 | }, 28 | { 29 | "input_index": 3, 30 | "previous_txid": "cd85a6b9859d998fdeedc3389359c4b069734f8ce227ea65c2f081020d9c95f3", 31 | "previous_output_index": 1, 32 | "input_value": "0.00618893", 33 | "spending_address": "QcnYiN3t3foHxHv7CnqXrmRoiMkADhapZw" 34 | }, 35 | { 36 | "input_index": 4, 37 | "previous_txid": "4cbe6f4beed21ce4260d350922af62d49c097ed206d5fc94a06ff660fc035546", 38 | "previous_output_index": 0, 39 | "input_value": "0.00020000", 40 | "spending_address": "QcnYiN3t3foHxHv7CnqXrmRoiMkADhapZw" 41 | }, 42 | { 43 | "input_index": 5, 44 | "previous_txid": "027a5e03c4472c79a90ddb10e755082237af2dc75362e91f1c56b597ce495fe8", 45 | "previous_output_index": 1, 46 | "input_value": "0.01215660", 47 | "spending_address": "QcnYiN3t3foHxHv7CnqXrmRoiMkADhapZw" 48 | }, 49 | { 50 | "input_index": 6, 51 | "previous_txid": "e1ffcd79dfae5c12f44a3a759014a24693b739fefd6aa8fb14f5066ed5b14c14", 52 | "previous_output_index": 0, 53 | "input_value": "0.00020000", 54 | "spending_address": "QcnYiN3t3foHxHv7CnqXrmRoiMkADhapZw" 55 | }, 56 | { 57 | "input_index": 7, 58 | "previous_txid": "e37ef8c258318664d68d1ece81573030f6547fddb8db386f857d662918523a34", 59 | "previous_output_index": 0, 60 | "input_value": "0.00500000", 61 | "spending_address": "QUMD2qFssQKXGiTPJqPpGXhBeNuXxjrt1b" 62 | }, 63 | { 64 | "input_index": 8, 65 | "previous_txid": "e37ef8c258318664d68d1ece81573030f6547fddb8db386f857d662918523a34", 66 | "previous_output_index": 1, 67 | "input_value": "0.10577860", 68 | "spending_address": "QgAhHtRN25P334nZqg7GaqQ6oDZEBmdGpT" 69 | }, 70 | { 71 | "input_index": 9, 72 | "previous_txid": "f30d8aecb0d0d5508a484403cf52e8e3b0b111b9cab646592e1ee009d00139af", 73 | "previous_output_index": 0, 74 | "input_value": "0.00500000", 75 | "spending_address": "QRSVjcGho5ToUPhJ1xMPMArWrzxXLBWVcD" 76 | }, 77 | { 78 | "input_index": 10, 79 | "previous_txid": "2ae18dd3edb286f41eeb0b001a9bebb84ef49c845930ec0b879da2793e934af8", 80 | "previous_output_index": 0, 81 | "input_value": "0.05234567", 82 | "spending_address": "tltc1q9ptjkd7rujg8q6pzt2xuv9tevzjfnmltes9ygasxxj3enyvmhskqxprm44" 83 | } 84 | ], 85 | "outputs": [ 86 | { 87 | "output_index": 0, 88 | "output_category": "user-specified", 89 | "output_value": "0.12345678", 90 | "receiving_address": "tltc1qxwtaq6y866rk9rz4s8reltfwvkgj0xsxx43smkrk45ekep4usm2q3kqsjy" 91 | }, 92 | { 93 | "output_index": 1, 94 | "output_category": "user-specified", 95 | "output_value": "0.01234567", 96 | "receiving_address": "QTLcyTFrH7T6kqUsi1VV2mJVXmX3AmwUNH" 97 | }, 98 | { 99 | "output_index": 2, 100 | "output_category": "user-specified", 101 | "output_value": "0.00123456", 102 | "receiving_address": "QgYm5tLdYBaVYiGdhq2XdpwEazSVQQZ5Ya" 103 | }, 104 | { 105 | "output_index": 3, 106 | "output_category": "change", 107 | "output_value": "0.05009359", 108 | "receiving_address": "tltc1q9ptjkd7rujg8q6pzt2xuv9tevzjfnmltes9ygasxxj3enyvmhskqxprm44" 109 | } 110 | ], 111 | "input_address_data": [ 112 | { 113 | "required_signatures": 2, 114 | "public_keys": [ 115 | "0293e2e2c8ffa27cdb686a169ac02edd4cd72845da4080a2c3a25fc1d81f3a1541", 116 | "03f2e51b2f298bab1e4485d7d31a559de73fc29dced12f568e30264dbae4ec326e" 117 | ], 118 | "address": "QcnYiN3t3foHxHv7CnqXrmRoiMkADhapZw", 119 | "address_type": "P2WSH-over-P2SH" 120 | }, 121 | { 122 | "required_signatures": 2, 123 | "public_keys": [ 124 | "0366b0a62cf55af8fab2c20ac61c8ce0e21577b9502e83af0955ac706fa395e1a9", 125 | "03f2e51b2f298bab1e4485d7d31a559de73fc29dced12f568e30264dbae4ec326e" 126 | ], 127 | "address": "QUMD2qFssQKXGiTPJqPpGXhBeNuXxjrt1b", 128 | "address_type": "P2SH" 129 | }, 130 | { 131 | "required_signatures": 2, 132 | "public_keys": [ 133 | "02d0ac8192c1bb328c7b1a46cc6974b903995ed4b9746b77ca06c20a9e16b95b8f", 134 | "03f2e51b2f298bab1e4485d7d31a559de73fc29dced12f568e30264dbae4ec326e" 135 | ], 136 | "address": "QgAhHtRN25P334nZqg7GaqQ6oDZEBmdGpT", 137 | "address_type": "P2WSH-over-P2SH" 138 | }, 139 | { 140 | "required_signatures": 2, 141 | "public_keys": [ 142 | "0327bba51878eb35200f2a15e4a3724d675074eb64610aab109c9ded35b0625128", 143 | "03f2e51b2f298bab1e4485d7d31a559de73fc29dced12f568e30264dbae4ec326e" 144 | ], 145 | "address": "QRSVjcGho5ToUPhJ1xMPMArWrzxXLBWVcD", 146 | "address_type": "P2WSH-over-P2SH" 147 | }, 148 | { 149 | "required_signatures": 2, 150 | "public_keys": [ 151 | "035bb6a1cb961a9b54e666a366b896dac3ec9ad352a57714ffb22b45dabaed702b", 152 | "03f2e51b2f298bab1e4485d7d31a559de73fc29dced12f568e30264dbae4ec326e" 153 | ], 154 | "address": "tltc1q9ptjkd7rujg8q6pzt2xuv9tevzjfnmltes9ygasxxj3enyvmhskqxprm44", 155 | "address_type": "WITNESS_V0" 156 | } 157 | ], 158 | "user_key": { 159 | "public_key": "03f2e51b2f298bab1e4485d7d31a559de73fc29dced12f568e30264dbae4ec326e", 160 | "encrypted_passphrase": "3FmrhIEX7tV46alsRuMonmLHPIX7hBSMCQmZKcVt2a3pcgDG4cmwLjaiDPJzIAPtBI9NZ5ybnO824EOzreak9bxzPhg/D2CFhGZAl+Iq3RzURwhY6kQ6kdtj5G///5UlJgY17SQa8/i+TXwLj5/AOfNIfMU0QUxVzSsfAJO3VfH9QioLnUJW5ZH1OkSdM849" 161 | }, 162 | "estimated_tx_size": 1696 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /tests/data/json/prepare_transaction_response_with_blockio_fee_and_expected_unsigned_txid.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "success", 3 | "data": { 4 | "network": "LTCTEST", 5 | "tx_type": "basic", 6 | "inputs": [ 7 | { 8 | "input_index": 0, 9 | "previous_txid": "5d5d9a902f572449786ce65a586481dfa646f66f8500cadbed811eaf82370630", 10 | "previous_output_index": 0, 11 | "input_value": "0.00249485", 12 | "spending_address": "Qjgm6PfqcCmQr31DKnnch7zwG7sFpVc7xb" 13 | }, 14 | { 15 | "input_index": 1, 16 | "previous_txid": "adc348dcfed62799718e375d9097fa71d2a321c9137c4a52067a13181203ead4", 17 | "previous_output_index": 0, 18 | "input_value": "0.00090000", 19 | "spending_address": "QN7WkTwGmTxEnK4EpzsspPofsKPXvjLKxC" 20 | }, 21 | { 22 | "input_index": 2, 23 | "previous_txid": "9ffa4dceac8b09fb099fb0ebcc5d0bcb132c98b3e8a2b1381d8d9654f82a5cf0", 24 | "previous_output_index": 1, 25 | "input_value": "0.17464160", 26 | "spending_address": "QN7WkTwGmTxEnK4EpzsspPofsKPXvjLKxC" 27 | } 28 | ], 29 | "outputs": [ 30 | { 31 | "output_index": 0, 32 | "output_category": "user-specified", 33 | "output_value": "0.01234500", 34 | "receiving_address": "QN9ShoLwKTnW7ms7bfSrH7YgMPh2cjUkdi" 35 | }, 36 | { 37 | "output_index": 1, 38 | "output_category": "blockio-fee", 39 | "output_value": "0.00012345", 40 | "receiving_address": "tltc1qyhn4wkpzt0e8qm7a9kk8v0cpxn775wlqfgl32s20n00p3q5q7uksc4xqfe" 41 | }, 42 | { 43 | "output_index": 2, 44 | "output_category": "change", 45 | "output_value": "0.16546580", 46 | "receiving_address": "QN7WkTwGmTxEnK4EpzsspPofsKPXvjLKxC" 47 | } 48 | ], 49 | "input_address_data": [ 50 | { 51 | "required_signatures": 2, 52 | "public_keys": [ 53 | "0325e036e22b3ee1593baba8052e92505a11446a1ba3e8bc3f9f74b17f2e6b6a3e", 54 | "034309721935937713a2dd1c33c5c44a10a30674bebe0542aeb145ce4c9790e742" 55 | ], 56 | "address": "Qjgm6PfqcCmQr31DKnnch7zwG7sFpVc7xb", 57 | "address_type": "P2WSH-over-P2SH" 58 | }, 59 | { 60 | "required_signatures": 2, 61 | "public_keys": [ 62 | "03b934de0933b8b80051530d353f5fb22ec7e5d6233d8b6bf2ddcaf7119e2f4fe7", 63 | "034309721935937713a2dd1c33c5c44a10a30674bebe0542aeb145ce4c9790e742" 64 | ], 65 | "address": "QN7WkTwGmTxEnK4EpzsspPofsKPXvjLKxC", 66 | "address_type": "P2WSH-over-P2SH" 67 | } 68 | ], 69 | "user_key": { 70 | "public_key": "034309721935937713a2dd1c33c5c44a10a30674bebe0542aeb145ce4c9790e742", 71 | "encrypted_passphrase": "UMT6oOUQf/hRNCdiSc3HAHRdn95G9iSxvU5pqTCveqaQxpVShoyCBfgDOfzbgrk3Cw4oNY0sBStoWqCc4ha4kFXBblt/6DzxV7YBD2lDkjGYdrYISrGG+kAHEbLxcVwHL/6Feep9ve3NgvCSEV0MTrm2HBNfX1ke/+XBoYhn7ZX9QioLnUJW5ZH1OkSdM849" 72 | }, 73 | "estimated_tx_size": 511, 74 | "expected_unsigned_txid": "4ee43869f50d226686a0ad0949e4adeb02f4d93bb7ea2efda62dfc36f2a1b0b7" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /tests/data/json/prepare_transaction_response_witness_v1_output.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "success", 3 | "data": { 4 | "network": "BTCTEST", 5 | "tx_type": "basic", 6 | "inputs": [ 7 | { 8 | "input_index": 0, 9 | "previous_txid": "5fe41d63872f6d724f2a703597885d3a19df8f13cdaa7ea9b8f94f869bcc4e78", 10 | "previous_output_index": 1, 11 | "input_value": "0.00060915", 12 | "spending_address": "2MsU2DsvP7okZ2sZGZUvH3rZZuQt4pf75Am" 13 | } 14 | ], 15 | "outputs": [ 16 | { 17 | "output_index": 0, 18 | "output_category": "user-specified", 19 | "output_value": "0.00020000", 20 | "receiving_address": "tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c" 21 | }, 22 | { 23 | "output_index": 1, 24 | "output_category": "user-specified", 25 | "output_value": "0.00020000", 26 | "receiving_address": "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy" 27 | }, 28 | { 29 | "output_index": 2, 30 | "output_category": "change", 31 | "output_value": "0.00019615", 32 | "receiving_address": "2MsU2DsvP7okZ2sZGZUvH3rZZuQt4pf75Am" 33 | } 34 | ], 35 | "input_address_data": [ 36 | { 37 | "required_signatures": 2, 38 | "public_keys": [ 39 | "021916c1ea9215990263e2862bcecc85397d199a4411c863983176ee3d44e27f7f", 40 | "02d2cbf77287c1443759abdd35f239e7da2f52c992258653bc8dd577ae63c78628" 41 | ], 42 | "address": "2MsU2DsvP7okZ2sZGZUvH3rZZuQt4pf75Am", 43 | "address_type": "P2WSH-over-P2SH" 44 | } 45 | ], 46 | "user_key": { 47 | "public_key": "02d2cbf77287c1443759abdd35f239e7da2f52c992258653bc8dd577ae63c78628", 48 | "encrypted_passphrase": "jlPuw8CJGTWTb+O4I/IKGWGDdF9G8/MX5meX+IfuLfbb7rRABoSUGYSU2BXxxRqR95K64u8gH46h3zr/NKsj8OFv5gj4JwClM7RN03fvb+3CyXgwy4eYSSpFE6vVsdyoxJ8rshUbpf8tvCerUKC0LhE9d61q7mWYoVAik61WRwc=", 49 | "algorithm": { 50 | "pbkdf2_salt": "7ccf40ce398f0fb475fe91043f7dce57", 51 | "pbkdf2_iterations": 102400, 52 | "pbkdf2_hash_function": "SHA256", 53 | "pbkdf2_phase1_key_length": 16, 54 | "pbkdf2_phase2_key_length": 32, 55 | "aes_iv": "7c69a5e81e53ba05213b35bb", 56 | "aes_cipher": "AES-256-GCM", 57 | "aes_auth_tag": "d19b5e48e068f3b1179a4724b3862650", 58 | "aes_auth_data": "" 59 | } 60 | }, 61 | "estimated_tx_size": 260, 62 | "expected_unsigned_txid": "e1925681c6986df2617d41c90b2bbc32cd01f7df0323ca52417b4a8677b2a160" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /tests/data/json/summarize_prepared_transaction_response_with_blockio_fee_and_expected_unsigned_txid.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": "LTCTEST", 3 | "network_fee": "0.00010220", 4 | "blockio_fee": "0.00012345", 5 | "total_amount_to_send": "0.01234500" 6 | } 7 | -------------------------------------------------------------------------------- /tests/test_bitcoinutils.py: -------------------------------------------------------------------------------- 1 | 2 | from block_io.bitcoinutils_patches import * 3 | 4 | import unittest 5 | 6 | class TestBitcoinUtilsTransactions(unittest.TestCase): 7 | 8 | def setUp(self): 9 | self.private_keys = ["ef4fc6cfd682494093bbadf041ba4341afbe22b224432e21a4bc4470c5b939d4", 10 | "123f37eb9a7f24a120969a1b2d6ac4859fb8080cfc2e8d703abae0f44305fc12"] 11 | 12 | self.public_keys = [PrivateKey(secret_exponent=int(self.private_keys[0],16)).get_public_key().to_hex(compressed=True), 13 | PrivateKey(secret_exponent=int(self.private_keys[1],16)).get_public_key().to_hex(compressed=True)] 14 | 15 | self.redeem_script = Script(['OP_2', self.public_keys[0], self.public_keys[1], 'OP_2', 'OP_CHECKMULTISIG']) 16 | 17 | self.priv0 = PrivateKey(secret_exponent=int(self.private_keys[0], 16)) 18 | self.pub0 = self.priv0.get_public_key() 19 | 20 | bitcoinutils_setup("LTCTEST") 21 | 22 | self.fee = 10000 23 | self.output_value = 1000000000 24 | 25 | def test_transaction_p2sh_to_p2wsh_over_p2sh(self): 26 | 27 | txin = TxInput("4ad80b9776f574a125f89e96bda75bb6fe046f7560847d16446bbdcdc160be62", 1) 28 | 29 | prev_output_value = self.output_value + 0 30 | output_value = prev_output_value - self.fee 31 | txout = TxOutput(output_value, get_output_script("QeyxkrKbgKvxbBY1HLiBYjMnZx1HDRMYmd")) 32 | 33 | tx = Transaction([txin], [txout]) 34 | 35 | self.assertEqual(tx.serialize(), 36 | "010000000162be60c1cdbd6b44167d8460756f04feb65ba7bd969ef825a174f576970bd84a0100000000ffffffff01f0a29a3b0000000017a914c99a494597ade09b5194f9ec8e02d96607ae64798700000000") 37 | 38 | sighash0 = hexlify(tx.get_transaction_digest(0, self.redeem_script, bitcoinutils.constants.SIGHASH_ALL)) 39 | 40 | self.assertEqual(sighash0, b"93a075651d1b6b79cd9bf128bf5e15001fe65865defea6cedab0a1da438f565e") 41 | 42 | input0_sig0 = PrivateKey(secret_exponent=int(self.private_keys[0],16)).sign_input(tx,0,self.redeem_script) 43 | input0_sig1 = PrivateKey(secret_exponent=int(self.private_keys[1],16)).sign_input(tx,0,self.redeem_script) 44 | 45 | txin.script_sig = Script(['', 46 | signature_with_sighash(input0_sig0), 47 | signature_with_sighash(input0_sig1), 48 | self.redeem_script.to_hex()]) 49 | 50 | self.assertEqual(tx.serialize(), 51 | "010000000162be60c1cdbd6b44167d8460756f04feb65ba7bd969ef825a174f576970bd84a01000000d900473044022009143b07279ef6d5317865672e9fc28ada31314abf242ae786917b92cf027ac002207544d055f2b8bb249dc0294d565c6d538f4e04f9b142331fa103d82e0498a181014730440220561f9c23560c6d994c666b9b327f3ef1d9c0b29d0404396d1d6c7a86fc45fc7d02201909041cbe02fc9367f8ce019278629e3f8eae9b7a33fc8223e6fa89e368bd810147522103820317ad251bca573c8fda2b8f26ffc9aae9d5ecb15b50ee08d8f9e009def38e210238de8c9eb2842ecaf0cc61ee6ba23fe4e46f1cfd82eac0910e1d8e865bd76df952aeffffffff01f0a29a3b0000000017a914c99a494597ade09b5194f9ec8e02d96607ae64798700000000") 52 | 53 | self.assertEqual(tx.get_txid(), "2464c6122378ee5ed9a42d5192e15713b107924d05d15b58254eb7b2030118c7") 54 | 55 | def test_transaction_p2wsh_over_p2sh_to_p2wpkh(self): 56 | 57 | prev_txid = "2464c6122378ee5ed9a42d5192e15713b107924d05d15b58254eb7b2030118c7" 58 | prev_output_value = self.output_value - self.fee 59 | output_value = prev_output_value - self.fee 60 | txin = TxInput(prev_txid, 0) 61 | txout = TxOutput(output_value, get_output_script("tltc1qk2erszs7fp407kh94e6v3yhfq2njczjvg4hnz6")) 62 | 63 | tx = Transaction([txin], [txout], has_segwit=True) 64 | 65 | self.assertEqual(tx.serialize(), 66 | "0100000001c7180103b2b74e25585bd1054d9207b11357e192512da4d95eee782312c664240000000000ffffffff01e07b9a3b00000000160014b2b2380a1e486aff5ae5ae74c892e902a72c0a4c00000000") 67 | 68 | sighash0 = hexlify(tx.get_transaction_segwit_digest(0, self.redeem_script, prev_output_value, bitcoinutils.constants.SIGHASH_ALL)) 69 | 70 | self.assertEqual(sighash0, b"e1c684f769c0e186be215ece3b7c1f3f23985ecbafafe0c8d43936fcd79eafdc") 71 | 72 | input0_sig0 = PrivateKey(secret_exponent=int(self.private_keys[0],16)).sign_segwit_input(tx,0,self.redeem_script,prev_output_value) 73 | input0_sig1 = PrivateKey(secret_exponent=int(self.private_keys[1],16)).sign_segwit_input(tx,0,self.redeem_script,prev_output_value) 74 | 75 | txin.script_sig = Script([get_output_script(P2wshAddress.from_script(self.redeem_script).to_string()).to_hex()]) 76 | 77 | tx.witnesses.append( 78 | Script(["OP_0", signature_with_sighash(input0_sig0), signature_with_sighash(input0_sig1), self.redeem_script.to_hex()]) 79 | ) 80 | 81 | self.assertEqual(tx.serialize(), 82 | "01000000000101c7180103b2b74e25585bd1054d9207b11357e192512da4d95eee782312c664240000000023220020d42b8341140559b7da105e8669e8f7d5a03773642ad82403ba91b80ffcc415deffffffff01e07b9a3b00000000160014b2b2380a1e486aff5ae5ae74c892e902a72c0a4c0400473044022067c9f8ed5c8f0770be1b7d44ade72c4d976a2b0e6c4df39ea70923daff26ea5e02205894350de5304d446343fbf95245cd656876a11c94025554bf878b3ecf90db720147304402204ee76a1814b3eb289e492409bd29ebb77088c9c20645c8a63c75bfe44eac41f70220232bcd35a0cc78e88dfa59dc15331023c3d3bb3a8b63e6b753c8ab4599b7bd290147522103820317ad251bca573c8fda2b8f26ffc9aae9d5ecb15b50ee08d8f9e009def38e210238de8c9eb2842ecaf0cc61ee6ba23fe4e46f1cfd82eac0910e1d8e865bd76df952ae00000000") 83 | 84 | self.assertEqual(tx.get_txid(), "66a78d3cda988e4c90611b192ae5bd02e0fa70c08c3219110c02594802a42c01") 85 | 86 | def test_transaction_p2wpkh_to_witness_v0(self): 87 | 88 | prev_txid = "66a78d3cda988e4c90611b192ae5bd02e0fa70c08c3219110c02594802a42c01" 89 | prev_output_value = self.output_value - self.fee - self.fee 90 | output_value = prev_output_value - self.fee 91 | txin = TxInput(prev_txid, 0) 92 | txout = TxOutput(output_value, get_output_script("tltc1q6s4cxsg5q4vm0ksst6rxn68h6ksrwumy9tvzgqa6jxuqllxyzh0qxt7q8g")) 93 | 94 | tx = Transaction([txin], [txout], has_segwit=True) 95 | 96 | self.assertEqual(tx.serialize(), 97 | "0100000001012ca4024859020c1119328cc070fae002bde52a191b61904c8e98da3c8da7660000000000ffffffff01d0549a3b00000000220020d42b8341140559b7da105e8669e8f7d5a03773642ad82403ba91b80ffcc415de00000000") 98 | 99 | p2wpkh_script = get_output_script(PublicKey(self.public_keys[0]).get_address().to_string()) 100 | sighash0 = hexlify(tx.get_transaction_segwit_digest(0, p2wpkh_script, prev_output_value, bitcoinutils.constants.SIGHASH_ALL)) 101 | 102 | self.assertEqual(sighash0, b"ff94560e1ca289de4d661695029f495dde37b16bddd6645fb65c8f61decec22c") 103 | 104 | input0_sig0 = PrivateKey(secret_exponent=int(self.private_keys[0],16)).sign_segwit_input(tx,0,p2wpkh_script,prev_output_value) 105 | 106 | tx.witnesses.append(Script([signature_with_sighash(input0_sig0), self.public_keys[0]])) 107 | 108 | self.assertEqual(tx.serialize(), 109 | "01000000000101012ca4024859020c1119328cc070fae002bde52a191b61904c8e98da3c8da7660000000000ffffffff01d0549a3b00000000220020d42b8341140559b7da105e8669e8f7d5a03773642ad82403ba91b80ffcc415de024730440220436f9c0d1bb66cb507da29ba3a583b152b5265d9eba1f4067612124ea7f536470220414213d205f4becf61481a1c816684b0e9912f4abcc174211b20c88b6158b005012103820317ad251bca573c8fda2b8f26ffc9aae9d5ecb15b50ee08d8f9e009def38e00000000") 110 | 111 | self.assertEqual(tx.get_txid(), "d14891128bc4c72dfa45269f302edf690289214874c5ee40b118c1d5465319e6") 112 | 113 | def test_transaction_witness_v0_to_p2wpkh_over_p2sh(self): 114 | 115 | prev_txid = "d14891128bc4c72dfa45269f302edf690289214874c5ee40b118c1d5465319e6" 116 | prev_output_value = self.output_value - self.fee - self.fee - self.fee 117 | output_value = prev_output_value - self.fee 118 | 119 | txin = TxInput(prev_txid, 0) 120 | txout = TxOutput(output_value, get_output_script("Qgn9vENxxnNCPun8CN6KR1PPB7WCo9oxqc")) 121 | tx = Transaction([txin], [txout], has_segwit=True) 122 | 123 | self.assertEqual(tx.serialize(), 124 | "0100000001e6195346d5c118b140eec5744821890269df2e309f2645fa2dc7c48b129148d10000000000ffffffff01c02d9a3b0000000017a914dd4edd1406541e476450fda7924720fe19f337b98700000000") 125 | 126 | sighash0 = hexlify(tx.get_transaction_segwit_digest(0, self.redeem_script, prev_output_value, bitcoinutils.constants.SIGHASH_ALL)) 127 | 128 | self.assertEqual(sighash0, b"bd77fd23a1e80c3670d7a547ce45031f5f611e4dc49a2eb65def2e6db841e011") 129 | 130 | input0_sig0 = PrivateKey(secret_exponent=int(self.private_keys[0],16)).sign_segwit_input(tx,0,self.redeem_script,prev_output_value) 131 | input0_sig1 = PrivateKey(secret_exponent=int(self.private_keys[1],16)).sign_segwit_input(tx,0,self.redeem_script,prev_output_value) 132 | 133 | tx.witnesses.append(Script(["OP_0", signature_with_sighash(input0_sig0), signature_with_sighash(input0_sig1), self.redeem_script.to_hex()])) 134 | 135 | self.assertEqual(tx.serialize(), 136 | "01000000000101e6195346d5c118b140eec5744821890269df2e309f2645fa2dc7c48b129148d10000000000ffffffff01c02d9a3b0000000017a914dd4edd1406541e476450fda7924720fe19f337b987040047304402205f2cabd4fa34e0947f07454a9d905073a21bb0818a009356481c1acd6f915f6f02203ba6bb8148a1790f4e1939ea74a21dcc8a837595e54323bbba0615a15b779c4901473044022033d8136791bc5658700b385ca5728b9e188a3ba1aa3bc691d6adfd1b8431cee6022073d565e5d1e96c0257f7cefdab946e48fb3857248f49048e00f6b701e97457c30147522103820317ad251bca573c8fda2b8f26ffc9aae9d5ecb15b50ee08d8f9e009def38e210238de8c9eb2842ecaf0cc61ee6ba23fe4e46f1cfd82eac0910e1d8e865bd76df952ae00000000") 137 | 138 | self.assertEqual(tx.get_txid(), "d76dd93d5afbc8cb3bfd487445fac9f81d7ae409723990f7744f398feae9c0e4") 139 | 140 | def test_transaction_p2wpkh_over_p2sh_to_p2pkh(self): 141 | 142 | prev_txid = "d76dd93d5afbc8cb3bfd487445fac9f81d7ae409723990f7744f398feae9c0e4" 143 | prev_output_value = self.output_value - self.fee - self.fee - self.fee - self.fee 144 | output_value = prev_output_value - self.fee 145 | txin = TxInput(prev_txid, 0) 146 | txout = TxOutput(output_value, get_output_script("mwop54ocwGjeErSTLCKgKxrdYp1k9o6Cgk")) 147 | 148 | tx = Transaction([txin], [txout], has_segwit=True) 149 | 150 | self.assertEqual(tx.serialize(), 151 | "0100000001e4c0e9ea8f394f74f790397209e47a1df8c9fa457448fd3bcbc8fb5a3dd96dd70000000000ffffffff01b0069a3b000000001976a914b2b2380a1e486aff5ae5ae74c892e902a72c0a4c88ac00000000") 152 | 153 | p2wpkh_script = get_output_script(PublicKey(self.public_keys[0]).get_address().to_string()) 154 | sighash0 = hexlify(tx.get_transaction_segwit_digest(0, p2wpkh_script, prev_output_value, bitcoinutils.constants.SIGHASH_ALL)) 155 | 156 | self.assertEqual(sighash0, b"59e2322a152dbad2c283232bd098a55c61bc0cd324dfd85311a0a9e73053d46b") 157 | 158 | input0_sig0 = PrivateKey(secret_exponent=int(self.private_keys[0],16)).sign_segwit_input(tx,0,p2wpkh_script,prev_output_value) 159 | 160 | txin.script_sig = Script([get_output_script(PublicKey(self.public_keys[0]).get_segwit_address().to_string()).to_hex()]) 161 | 162 | tx.witnesses.append(Script([signature_with_sighash(input0_sig0), self.public_keys[0]])) 163 | 164 | self.assertEqual(tx.serialize(), 165 | "01000000000101e4c0e9ea8f394f74f790397209e47a1df8c9fa457448fd3bcbc8fb5a3dd96dd70000000017160014b2b2380a1e486aff5ae5ae74c892e902a72c0a4cffffffff01b0069a3b000000001976a914b2b2380a1e486aff5ae5ae74c892e902a72c0a4c88ac02473044022067efbe904404b388bf11cf8af610f2efa95ac943a67071c3c5fe0332286d672e02205f3917d8967d7f32fb65c0808c6c0de7dda8a080bf92f80c1ee13d33757fd1df012103820317ad251bca573c8fda2b8f26ffc9aae9d5ecb15b50ee08d8f9e009def38e00000000") 166 | 167 | self.assertEqual(tx.get_txid(), "74b178c39268acd0663c88d3a56665b2f5335b60711445a5f8cd8aa59c2c7d38") 168 | 169 | def test_transaction_p2pkh_to_p2sh(self): 170 | 171 | prev_txid = "74b178c39268acd0663c88d3a56665b2f5335b60711445a5f8cd8aa59c2c7d38" 172 | prev_output_value = self.output_value - self.fee - self.fee - self.fee - self.fee - self.fee 173 | output_value = prev_output_value - self.fee 174 | txin = TxInput(prev_txid, 0) 175 | txout = TxOutput(output_value, get_output_script("QPZMy7ivpYdkJRLhtTx7tj5Fa4doQ2auWk")) 176 | 177 | tx = Transaction([txin], [txout], has_segwit=False) 178 | 179 | self.assertEqual(tx.serialize(), 180 | "0100000001387d2c9ca58acdf8a5451471605b33f5b26566a5d3883c66d0ac6892c378b1740000000000ffffffff01a0df993b0000000017a9142069605a7742286aef950b68ae7818f7294e876c8700000000") 181 | 182 | p2pkh_script = get_output_script(PublicKey(self.public_keys[0]).get_address().to_string()) 183 | sighash0 = hexlify(tx.get_transaction_digest(0, p2pkh_script, bitcoinutils.constants.SIGHASH_ALL)) 184 | 185 | self.assertEqual(sighash0, b"ae52a447200543a0e5a5ca8de0bad10eebb411748d137f7b2fba380b98ea6651") 186 | 187 | input0_sig0 = PrivateKey(secret_exponent=int(self.private_keys[0],16)).sign_input(tx,0,p2pkh_script) 188 | 189 | txin.script_sig = Script([signature_with_sighash(input0_sig0), self.public_keys[0]]) 190 | 191 | self.assertEqual(tx.serialize(), 192 | "0100000001387d2c9ca58acdf8a5451471605b33f5b26566a5d3883c66d0ac6892c378b174000000006a47304402200baec9555d3852ff2627971e5b4f26c186792bb4960bacf1270372c3b540b85b0220461100cd59a45fe922df4c4d8a3c14f358bfdc68f500811ac3cde0fb8d8c1f2a012103820317ad251bca573c8fda2b8f26ffc9aae9d5ecb15b50ee08d8f9e009def38effffffff01a0df993b0000000017a9142069605a7742286aef950b68ae7818f7294e876c8700000000") 193 | 194 | self.assertEqual(tx.get_txid(), "e4dd8c000f65fcf42598ff332ef81852b44bac9dcdecac72d69a4c56b8c59b73") 195 | 196 | class TestBitcoinUtilsAddresses(unittest.TestCase): 197 | # TODO test for all networks 198 | 199 | def setUp(self): 200 | self.private_keys = ["ef4fc6cfd682494093bbadf041ba4341afbe22b224432e21a4bc4470c5b939d4", 201 | "123f37eb9a7f24a120969a1b2d6ac4859fb8080cfc2e8d703abae0f44305fc12"] 202 | 203 | self.public_keys = [PrivateKey(secret_exponent=int(self.private_keys[0],16)).get_public_key().to_hex(compressed=True), 204 | PrivateKey(secret_exponent=int(self.private_keys[1],16)).get_public_key().to_hex(compressed=True)] 205 | 206 | self.redeem_script = Script(['OP_2', self.public_keys[0], self.public_keys[1], 'OP_2', 'OP_CHECKMULTISIG']) 207 | 208 | self.priv0 = PrivateKey(secret_exponent=int(self.private_keys[0], 16)) 209 | self.pub0 = self.priv0.get_public_key() 210 | 211 | bitcoinutils_setup("LTCTEST") 212 | 213 | def test_wif(self): 214 | # hex to wif 215 | self.assertEqual(self.priv0.to_wif(compressed=True), "cVbthX9iskbZZcLx3PyVJ81hiiGarJNVF8diJryC27A6fV1b5jwR") 216 | 217 | def test_pubkey(self): 218 | # priv to pub compressed 219 | self.assertEqual(self.pub0.to_hex(compressed=True), "03820317ad251bca573c8fda2b8f26ffc9aae9d5ecb15b50ee08d8f9e009def38e") 220 | 221 | def test_address_p2pkh(self): 222 | # P2PKH 223 | self.assertEqual(self.pub0.get_address().to_string(), "mwop54ocwGjeErSTLCKgKxrdYp1k9o6Cgk") 224 | self.assertEqual(get_output_script("mwop54ocwGjeErSTLCKgKxrdYp1k9o6Cgk").to_hex(), "76a914b2b2380a1e486aff5ae5ae74c892e902a72c0a4c88ac") 225 | 226 | def test_address_p2wpkh(self): 227 | # P2WPKH 228 | self.assertEqual(self.pub0.get_segwit_address().to_string(), "tltc1qk2erszs7fp407kh94e6v3yhfq2njczjvg4hnz6") 229 | self.assertEqual(get_output_script("tltc1qk2erszs7fp407kh94e6v3yhfq2njczjvg4hnz6").to_hex(), "0014b2b2380a1e486aff5ae5ae74c892e902a72c0a4c") 230 | 231 | def test_address_p2wpkh_over_p2sh(self): 232 | # P2WPKH-over-P2SH 233 | self.assertEqual(P2shAddress.from_script(self.pub0.get_segwit_address().to_script_pub_key()).to_string(), 234 | "Qgn9vENxxnNCPun8CN6KR1PPB7WCo9oxqc") 235 | self.assertEqual(get_output_script("Qgn9vENxxnNCPun8CN6KR1PPB7WCo9oxqc").to_hex(), "a914dd4edd1406541e476450fda7924720fe19f337b987") 236 | 237 | def test_address_p2sh(self): 238 | # P2SH 239 | self.assertEqual(P2shAddress.from_script(self.redeem_script).to_string(), "QPZMy7ivpYdkJRLhtTx7tj5Fa4doQ2auWk") 240 | self.assertEqual(get_output_script("QPZMy7ivpYdkJRLhtTx7tj5Fa4doQ2auWk").to_hex(), "a9142069605a7742286aef950b68ae7818f7294e876c87") 241 | 242 | def test_address_witness_v0(self): 243 | # P2WSH (WITNESS_V0) 244 | self.assertEqual(P2wshAddress.from_script(self.redeem_script).to_string(), "tltc1q6s4cxsg5q4vm0ksst6rxn68h6ksrwumy9tvzgqa6jxuqllxyzh0qxt7q8g") 245 | self.assertEqual(get_output_script("tltc1q6s4cxsg5q4vm0ksst6rxn68h6ksrwumy9tvzgqa6jxuqllxyzh0qxt7q8g").to_hex(), 246 | "0020d42b8341140559b7da105e8669e8f7d5a03773642ad82403ba91b80ffcc415de") 247 | 248 | def test_address_p2wsh_over_p2sh(self): 249 | # P2WSH-over-P2SH 250 | self.assertEqual(P2shAddress.from_script(P2wshAddress.from_script(self.redeem_script).to_script_pub_key()).to_string(), 251 | "QeyxkrKbgKvxbBY1HLiBYjMnZx1HDRMYmd") 252 | self.assertEqual(get_output_script("QeyxkrKbgKvxbBY1HLiBYjMnZx1HDRMYmd").to_hex(), "a914c99a494597ade09b5194f9ec8e02d96607ae647987") 253 | 254 | 255 | -------------------------------------------------------------------------------- /tests/test_bitcoinutils_utils.py: -------------------------------------------------------------------------------- 1 | 2 | from block_io.bitcoinutils_patches import * 3 | 4 | import unittest 5 | 6 | class TestBitcoinUtilsAmounts(unittest.TestCase): 7 | 8 | def setUp(self): 9 | None 10 | 11 | def test_amount_precision(self): 12 | 13 | self.assertEqual(bitcoinutils.utils.to_satoshis("0.12345678"), 12345678) 14 | self.assertEqual(bitcoinutils.utils.to_satoshis("1.12345678"), 112345678) 15 | self.assertEqual(bitcoinutils.utils.to_satoshis("10000000"), 1000000000000000) 16 | self.assertEqual(bitcoinutils.utils.to_satoshis("10000000.12345678"), 1000000012345678) 17 | 18 | -------------------------------------------------------------------------------- /tests/test_blockio.py: -------------------------------------------------------------------------------- 1 | from block_io import BlockIo, BlockIoAPIError 2 | 3 | import os 4 | import unittest 5 | import time 6 | import json 7 | 8 | from struct import pack 9 | from types import * 10 | from binascii import hexlify, unhexlify 11 | from ecdsa import rfc6979, SECP256k1, util 12 | from hashlib import sha256 13 | from decimal import Decimal 14 | 15 | class TestDeterministicSignatures(unittest.TestCase): 16 | 17 | def setUp(self): 18 | self.key = BlockIo.Key(unhexlify("6b0e34587dece0ef042c4c7205ce6b3d4a64d0bc484735b9325f7971a0ead963")) 19 | self.hex_data = "feedfacedeadbeeffeedfacedeadbeeffeedfacedeadbeeffeedfacedeadbeef" 20 | 21 | def test_deterministic_k_no_extra_entropy(self): 22 | k = rfc6979.generate_k(SECP256k1.generator.order(), self.key.private_key.key.privkey.secret_multiplier, sha256, unhexlify(self.hex_data)) 23 | self.assertEqual(hexlify(util.number_to_string(k, self.key.private_key.key.privkey.order)), b'ab56733dc6b9cf8fbecd9af7ba64ee5b658b8a1def2f4c4c510a2996d2761d6f') 24 | 25 | def test_deterministic_k_extra_entropy_1(self): 26 | extra_entropy = (1).to_bytes(32, byteorder="little") 27 | k = rfc6979.generate_k(SECP256k1.generator.order(), self.key.private_key.key.privkey.secret_multiplier, sha256, unhexlify(self.hex_data), 0, extra_entropy) 28 | self.assertEqual(hexlify(util.number_to_string(k, self.key.private_key.key.privkey.order)), b'f24f24e2e6510071c86da612ef04ccc21664a3801e0e06a227023b9c513a8290') 29 | 30 | def test_deterministic_k_extra_entropy_16(self): 31 | extra_entropy = (16).to_bytes(32, byteorder="little") 32 | k = rfc6979.generate_k(SECP256k1.generator.order(), self.key.private_key.key.privkey.secret_multiplier, sha256, unhexlify(self.hex_data), 0, extra_entropy) 33 | self.assertEqual(hexlify(util.number_to_string(k, self.key.private_key.key.privkey.order)), b'f42eeee9d30ec008d58ce23b2ff08fac85127e87390bccccbecc68a537da3d47') 34 | 35 | def test_deterministic_k_extra_entropy_255(self): 36 | extra_entropy = (255).to_bytes(32, byteorder="little") 37 | k = rfc6979.generate_k(SECP256k1.generator.order(), self.key.private_key.key.privkey.secret_multiplier, sha256, unhexlify(self.hex_data), 0, extra_entropy) 38 | self.assertEqual(hexlify(util.number_to_string(k, self.key.private_key.key.privkey.order)), b'a2c913d48ca5d18c62126a90059a552f4cafeab85b55e9acdc6848473910f150') 39 | 40 | def test_deterministic_k_extra_entropy_256(self): 41 | extra_entropy = (256).to_bytes(32, byteorder="little") 42 | k = rfc6979.generate_k(SECP256k1.generator.order(), self.key.private_key.key.privkey.secret_multiplier, sha256, unhexlify(self.hex_data), 0, extra_entropy) 43 | self.assertEqual(hexlify(util.number_to_string(k, self.key.private_key.key.privkey.order)), b'39c9dc355f042b24fe44184119c31b62ff9d8a3d0c5a26bada674d9595e6988d') 44 | 45 | def test_signature_with_low_r(self): 46 | sig = self.key.sign_hex(self.hex_data) 47 | self.assertEqual(sig, b'3044022042b9b4d673c85798f226c85f55ea6e114a0805bd5a0efba35f14c05235bb67b2022016333edae230c0ab607e948b48ceaefb5cab07300fb869d9da0a1b0f6bb53f65') 48 | 49 | class TestHelpers(unittest.TestCase): 50 | def setUp(self): 51 | self.pin = "123456"; 52 | 53 | def test_pin_derivation(self): 54 | key = BlockIo.Helper.pinToAesKey(self.pin) 55 | self.assertEqual(key, b'd0478c395b66e588a1518cdd08d8257aa2145a4c20bcd05c466afb334b7d18e7') 56 | 57 | def test_pin_derivation_with_salt(self): 58 | key = BlockIo.Helper.pinToAesKey("deadbeef", "922445847c173e90667a19d90729e1fb", 500000) 59 | self.assertEqual(key, b'f206403c6bad20e1c8cb1f3318e17cec5b2da0560ed6c7b26826867452534172') 60 | 61 | def test_aes256ecb_encryption(self): 62 | clear = "I\'m a little tea pot short and stout" 63 | key = BlockIo.Helper.pinToAesKey(self.pin) 64 | encrypted_data = BlockIo.Helper.encrypt(clear, key) 65 | self.assertEqual(encrypted_data['aes_cipher_text'], b'7HTfNBYJjq09+vi8hTQhy6lCp3IHv5rztNnKCJ5RB7cSL+NjHrFVv1jl7qkxJsOg') 66 | cleartext = BlockIo.Helper.decrypt(encrypted_data['aes_cipher_text'], key) 67 | self.assertEqual(cleartext, clear.encode('utf-8')) 68 | 69 | def test_aes256cbc_encryption(self): 70 | clear = "beadbeef" 71 | key = BlockIo.Helper.pinToAesKey("deadbeef", "922445847c173e90667a19d90729e1fb", 500000) 72 | result = BlockIo.Helper.encrypt(clear, key, "11bc22166c8cf8560e5fa7e5c622bb0f", "AES-256-CBC") 73 | self.assertEqual(result['aes_cipher_text'], b'LExu1rUAtIBOekslc328Lw==') 74 | cleartext = BlockIo.Helper.decrypt(result['aes_cipher_text'], key, result['aes_iv'], result['aes_cipher']) 75 | self.assertEqual(cleartext, clear.encode('utf-8')) 76 | 77 | def test_aes256gcm_encryption(self): 78 | clear = "beadbeef" 79 | key = BlockIo.Helper.pinToAesKey("deadbeef", "922445847c173e90667a19d90729e1fb", 500000) 80 | result = BlockIo.Helper.encrypt(clear, key, "a57414b88b67f977829cbdca", "AES-256-GCM") 81 | self.assertEqual(result['aes_cipher_text'], b'ELV56Z57KoA=') 82 | self.assertEqual(result['aes_auth_tag'], b'adeb7dfe53027bdda5824dc524d5e55a') 83 | cleartext = BlockIo.Helper.decrypt(result['aes_cipher_text'], key, result['aes_iv'], result['aes_cipher'], result['aes_auth_tag']) 84 | self.assertEqual(cleartext, clear.encode('utf-8')) 85 | 86 | def test_dynamic_extract_key_aes256ecb(self): 87 | 88 | user_key = json.loads('{"encrypted_passphrase":"3wIJtPoC8KO6S7x6LtrN0g==","public_key":"02f87f787bffb30396984cb6b3a9d6830f32d5b656b3e39b0abe4f3b3c35d99323","algorithm":{"pbkdf2_salt":"","pbkdf2_iterations":2048,"pbkdf2_hash_function":"SHA256","pbkdf2_phase1_key_length":16,"pbkdf2_phase2_key_length":32,"aes_iv":null,"aes_cipher":"AES-256-ECB","aes_auth_tag":null,"aes_auth_data":null}}') 89 | 90 | key = BlockIo.Helper.dynamicExtractKey(user_key, "deadbeef") 91 | self.assertEqual(key.pubkey_hex(), BlockIo.Key.from_passphrase(unhexlify("beadbeef")).pubkey_hex()) 92 | 93 | def test_dynamic_extract_key_aes256cbc(self): 94 | 95 | user_key = json.loads('{"encrypted_passphrase":"LExu1rUAtIBOekslc328Lw==","public_key":"02f87f787bffb30396984cb6b3a9d6830f32d5b656b3e39b0abe4f3b3c35d99323","algorithm":{"pbkdf2_salt":"922445847c173e90667a19d90729e1fb","pbkdf2_iterations":500000,"pbkdf2_hash_function":"SHA256","pbkdf2_phase1_key_length":16,"pbkdf2_phase2_key_length":32,"aes_iv":"11bc22166c8cf8560e5fa7e5c622bb0f","aes_cipher":"AES-256-CBC","aes_auth_tag":null,"aes_auth_data":null}}') 96 | 97 | key = BlockIo.Helper.dynamicExtractKey(user_key, "deadbeef") 98 | self.assertEqual(key.pubkey_hex(), BlockIo.Key.from_passphrase(unhexlify("beadbeef")).pubkey_hex()) 99 | 100 | def test_dynamic_extract_key_aes256gcm(self): 101 | 102 | user_key = json.loads('{"encrypted_passphrase":"ELV56Z57KoA=","public_key":"02f87f787bffb30396984cb6b3a9d6830f32d5b656b3e39b0abe4f3b3c35d99323","algorithm":{"pbkdf2_salt":"922445847c173e90667a19d90729e1fb","pbkdf2_iterations":500000,"pbkdf2_hash_function":"SHA256","pbkdf2_phase1_key_length":16,"pbkdf2_phase2_key_length":32,"aes_iv":"a57414b88b67f977829cbdca","aes_cipher":"AES-256-GCM","aes_auth_tag":"adeb7dfe53027bdda5824dc524d5e55a","aes_auth_data":""}}') 103 | 104 | key = BlockIo.Helper.dynamicExtractKey(user_key, "deadbeef") 105 | self.assertEqual(key.pubkey_hex(), BlockIo.Key.from_passphrase(unhexlify("beadbeef")).pubkey_hex()) 106 | 107 | 108 | class TestKeys(unittest.TestCase): 109 | def setUp(self): 110 | self.priv = "6b0e34587dece0ef042c4c7205ce6b3d4a64d0bc484735b9325f7971a0ead963" 111 | self.passphrase = "deadbeeffeedface"; 112 | self.dtrust_keys = ["b515fd806a662e061b488e78e5d0c2ff46df80083a79818e166300666385c0a2", 113 | "1584b821c62ecdc554e185222591720d6fe651ed1b820d83f92cdc45c5e21f", 114 | "2f9090b8aa4ddb32c3b0b8371db1b50e19084c720c30db1d6bb9fcd3a0f78e61", 115 | "6c1cefdfd9187b36b36c3698c1362642083dcc1941dc76d751481d3aa29ca65"] 116 | 117 | self.dtrust_pubkeys = ["02510e29d51e9a4268e6a5253c1fbd8144857945b82acb0accfc235cc7ca36da11", 118 | "0201a819bf549c253c4397bdd1535374de39e9bc278f637afdc27642d52cf79139", 119 | "039e3aa9ea182ccdaff2d8d150010b27cc4765c1d55ce674e52631af7376354d62", 120 | "03ee980e6334142342fcd9e6facfecfa139981e2276584c91d6a9739d533ac99fc"] 121 | 122 | def test_key_init(self): 123 | key = BlockIo.Key.from_privkey_hex(self.priv) 124 | self.assertEqual(key.pubkey_hex(), b'029c06f988dc6b44696e002e8abf496a13c73c2f1db3bde2dfb69be129f3711b01') 125 | 126 | def test_from_passphrase(self): 127 | key = BlockIo.Key.from_passphrase(unhexlify(self.passphrase)) 128 | self.assertEqual(key.pubkey_hex(), b'029023d9738c623cdd7e5fdd0f41666accb82f21df5d27dc5ef07040f7bdc5d9f5') 129 | 130 | def test_dtrust_keys(self): 131 | for i in range(len(self.dtrust_keys)): 132 | self.assertEqual(BlockIo.Key.from_privkey_hex(self.dtrust_keys[i]).pubkey_hex().decode("utf-8"), self.dtrust_pubkeys[i]) 133 | 134 | class TestBlockIoAPIError(unittest.TestCase): 135 | def setUp(self): 136 | self.blockio = BlockIo("","",2) 137 | 138 | def test_throws_blockio_api_error(self): 139 | try: 140 | self.blockio.get_balance() 141 | self.assertEquals(True,False) # will fail if we get here 142 | except BlockIoAPIError as e: 143 | self.assertEqual(e.get_raw_data()['data']['error_message'], "API Key invalid or you have not enabled API access for this machine's IP address(es). Check that your API Keys are correct, and that you have enabled API access for this machine's IP Address(es) on your account's Settings page.") 144 | 145 | -------------------------------------------------------------------------------- /tests/test_blockio_larger_transactions.py: -------------------------------------------------------------------------------- 1 | from block_io import BlockIo 2 | 3 | import os 4 | import unittest 5 | import time 6 | 7 | import json 8 | 9 | class TestBlockIoLargerTransactions(unittest.TestCase): 10 | 11 | def setUp(self): 12 | 13 | self.blockio = BlockIo("", "d1650160bd8d2bb32bebd139d0063eb6063ffa2f9e4501ad", 2) 14 | self.dtrust_keys = [ 15 | "b515fd806a662e061b488e78e5d0c2ff46df80083a79818e166300666385c0a2", 16 | "1584b821c62ecdc554e185222591720d6fe651ed1b820d83f92cdc45c5e21f", 17 | "2f9090b8aa4ddb32c3b0b8371db1b50e19084c720c30db1d6bb9fcd3a0f78e61", 18 | "6c1cefdfd9187b36b36c3698c1362642083dcc1941dc76d751481d3aa29ca65" 19 | ] 20 | 21 | self.sweep_key = BlockIo.Key.from_wif("cTj8Ydq9LhZgttMpxb7YjYSqsZ2ZfmyzVprQgjEzAzQ28frQi4ML").privkey_hex().decode("utf-8"); 22 | self.maxDiff = None 23 | 24 | def load_json_file(self, path): 25 | json_file = open(os.path.join(os.path.dirname(__file__), path)) 26 | data = json.load(json_file) 27 | json_file.close() 28 | 29 | return data 30 | 31 | def test_prepare_dtrust_transaction_p2sh_3of5_195_inputs(self): 32 | # dTrust P2WSH-over-P2SH with 195 inputs partial 33 | 34 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_P2SH_3of5_195inputs.json") 35 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_P2SH_3of5_195inputs.json") 36 | 37 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys[0:3]) 38 | 39 | self.assertDictEqual(create_and_sign_transaction_response, response) 40 | 41 | def test_prepare_dtrust_transaction_p2sh_4of5_195_inputs(self): 42 | # dTrust P2WSH-over-P2SH with 195 inputs full 43 | 44 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_P2SH_4of5_195inputs.json") 45 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_P2SH_4of5_195inputs.json") 46 | 47 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys[0:4]) 48 | 49 | self.assertDictEqual(create_and_sign_transaction_response, response) 50 | 51 | def test_prepare_dtrust_transaction_p2wsh_over_p2sh_3of5_251_inputs(self): 52 | # dTrust P2WSH-over-P2SH with 251 inputs partial 53 | 54 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_P2WSH-over-P2SH_3of5_251inputs.json") 55 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_3of5_251inputs.json") 56 | 57 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys[0:3]) 58 | 59 | self.assertDictEqual(create_and_sign_transaction_response, response) 60 | 61 | def test_prepare_dtrust_transaction_p2wsh_over_p2sh_3of5_252_inputs(self): 62 | # dTrust P2WSH-over-P2SH with 252 inputs partial 63 | 64 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_P2WSH-over-P2SH_3of5_252inputs.json") 65 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_3of5_252inputs.json") 66 | 67 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys[0:3]) 68 | 69 | self.assertDictEqual(create_and_sign_transaction_response, response) 70 | 71 | def test_prepare_dtrust_transaction_p2wsh_over_p2sh_3of5_253_inputs(self): 72 | # dTrust P2WSH-over-P2SH with 253 inputs partial 73 | 74 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_P2WSH-over-P2SH_3of5_253inputs.json") 75 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_3of5_253inputs.json") 76 | 77 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys[0:3]) 78 | 79 | self.assertDictEqual(create_and_sign_transaction_response, response) 80 | 81 | def test_prepare_dtrust_transaction_p2wsh_over_p2sh_4of5_251_inputs(self): 82 | # dTrust P2WSH-over-P2SH with 251 inputs full 83 | 84 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_P2WSH-over-P2SH_4of5_251inputs.json") 85 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_4of5_251inputs.json") 86 | 87 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys[0:4]) 88 | 89 | self.assertDictEqual(create_and_sign_transaction_response, response) 90 | 91 | def test_prepare_dtrust_transaction_p2wsh_over_p2sh_4of5_252_inputs(self): 92 | # dTrust P2WSH-over-P2SH with 252 inputs full 93 | 94 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_P2WSH-over-P2SH_4of5_252inputs.json") 95 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_4of5_252inputs.json") 96 | 97 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys[0:4]) 98 | 99 | self.assertDictEqual(create_and_sign_transaction_response, response) 100 | 101 | def test_prepare_dtrust_transaction_p2wsh_over_p2sh_4of5_253_inputs(self): 102 | # dTrust P2WSH-over-P2SH with 253 inputs full 103 | 104 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_P2WSH-over-P2SH_4of5_253inputs.json") 105 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_4of5_253inputs.json") 106 | 107 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys[0:4]) 108 | 109 | self.assertDictEqual(create_and_sign_transaction_response, response) 110 | 111 | def test_prepare_dtrust_transaction_witness_v0_3of5_251_inputs(self): 112 | # dTrust WITNESS_V0 with 251 inputs partial 113 | 114 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_WITNESS_V0_3of5_251inputs.json") 115 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_WITNESS_V0_3of5_251inputs.json") 116 | 117 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys[0:3]) 118 | 119 | self.assertDictEqual(create_and_sign_transaction_response, response) 120 | 121 | def test_prepare_dtrust_transaction_witness_v0_3of5_252_inputs(self): 122 | # dTrust WITNESS_V0 with 252 inputs partial 123 | 124 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_WITNESS_V0_3of5_252inputs.json") 125 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_WITNESS_V0_3of5_252inputs.json") 126 | 127 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys[0:3]) 128 | 129 | self.assertDictEqual(create_and_sign_transaction_response, response) 130 | 131 | def test_prepare_dtrust_transaction_witness_v0_3of5_253_inputs(self): 132 | # dTrust WITNESS_V0 with 253 inputs partial 133 | 134 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_WITNESS_V0_3of5_253inputs.json") 135 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_WITNESS_V0_3of5_253inputs.json") 136 | 137 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys[0:3]) 138 | 139 | self.assertDictEqual(create_and_sign_transaction_response, response) 140 | 141 | def test_prepare_dtrust_transaction_witness_v0_3of5_251_outputs(self): 142 | # dTrust WITNESS_V0 with 251 outputs partial 143 | 144 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_witness_v0_3of5_251outputs.json") 145 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_witness_v0_3of5_251outputs.json") 146 | 147 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys[0:3]) 148 | 149 | self.assertDictEqual(create_and_sign_transaction_response, response) 150 | 151 | def test_prepare_dtrust_transaction_witness_v0_3of5_252_outputs(self): 152 | # dTrust WITNESS_V0 with 252 outputs partial 153 | 154 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_witness_v0_3of5_252outputs.json") 155 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_witness_v0_3of5_252outputs.json") 156 | 157 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys[0:3]) 158 | 159 | self.assertDictEqual(create_and_sign_transaction_response, response) 160 | 161 | def test_prepare_dtrust_transaction_witness_v0_3of5_253_outputs(self): 162 | # dTrust WITNESS_V0 with 253 outputs partial 163 | 164 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_witness_v0_3of5_253outputs.json") 165 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_witness_v0_3of5_253outputs.json") 166 | 167 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys[0:3]) 168 | 169 | self.assertDictEqual(create_and_sign_transaction_response, response) 170 | 171 | def test_prepare_dtrust_transaction_witness_v0_4of5_251_inputs(self): 172 | # dTrust WITNESS_V0 with 251 inputs full 173 | 174 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_WITNESS_V0_4of5_251inputs.json") 175 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_WITNESS_V0_4of5_251inputs.json") 176 | 177 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys) 178 | 179 | self.assertDictEqual(create_and_sign_transaction_response, response) 180 | 181 | def test_prepare_dtrust_transaction_witness_v0_4of5_252_inputs(self): 182 | # dTrust WITNESS_V0 with 252 inputs full 183 | 184 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_WITNESS_V0_4of5_252inputs.json") 185 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_WITNESS_V0_4of5_252inputs.json") 186 | 187 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys) 188 | 189 | self.assertDictEqual(create_and_sign_transaction_response, response) 190 | 191 | def test_prepare_dtrust_transaction_witness_v0_4of5_253_inputs(self): 192 | # dTrust WITNESS_V0 with 253 inputs full 193 | 194 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_WITNESS_V0_4of5_253inputs.json") 195 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_WITNESS_V0_4of5_253inputs.json") 196 | 197 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys) 198 | 199 | self.assertDictEqual(create_and_sign_transaction_response, response) 200 | 201 | def test_prepare_dtrust_transaction_witness_v0_4of5_251_outputs(self): 202 | # dTrust WITNESS_V0 with 251 outputs full 203 | 204 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_witness_v0_4of5_251outputs.json") 205 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_witness_v0_4of5_251outputs.json") 206 | 207 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys) 208 | 209 | self.assertDictEqual(create_and_sign_transaction_response, response) 210 | 211 | def test_prepare_dtrust_transaction_witness_v0_4of5_252_outputs(self): 212 | # dTrust WITNESS_V0 with 252 outputs full 213 | 214 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_witness_v0_4of5_252outputs.json") 215 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_witness_v0_4of5_252outputs.json") 216 | 217 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys) 218 | 219 | self.assertDictEqual(create_and_sign_transaction_response, response) 220 | 221 | def test_prepare_dtrust_transaction_witness_v0_4of5_253_outputs(self): 222 | # dTrust WITNESS_V0 with 253 outputs full 223 | 224 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_witness_v0_4of5_253outputs.json") 225 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_witness_v0_4of5_253outputs.json") 226 | 227 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys) 228 | 229 | self.assertDictEqual(create_and_sign_transaction_response, response) 230 | 231 | def test_prepare_transaction_p2sh_1of2_251_inputs(self): 232 | # P2SH transaction with 251 inputs 233 | 234 | prepare_transaction_response = self.load_json_file("data/json/prepare_transaction_response_P2WSH-over-P2SH_1of2_251inputs.json") 235 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_P2WSH-over-P2SH_1of2_251inputs.json") 236 | 237 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response) 238 | 239 | self.assertDictEqual(create_and_sign_transaction_response, response) 240 | 241 | def test_prepare_transaction_p2sh_1of2_252_inputs(self): 242 | # P2SH transaction with 252 inputs 243 | 244 | prepare_transaction_response = self.load_json_file("data/json/prepare_transaction_response_P2WSH-over-P2SH_1of2_252inputs.json") 245 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_P2WSH-over-P2SH_1of2_252inputs.json") 246 | 247 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response) 248 | 249 | self.assertDictEqual(create_and_sign_transaction_response, response) 250 | 251 | def test_prepare_transaction_p2sh_1of2_253_inputs(self): 252 | # P2SH transaction with 253 inputs 253 | 254 | prepare_transaction_response = self.load_json_file("data/json/prepare_transaction_response_P2WSH-over-P2SH_1of2_253inputs.json") 255 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_P2WSH-over-P2SH_1of2_253inputs.json") 256 | 257 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response) 258 | 259 | self.assertDictEqual(create_and_sign_transaction_response, response) 260 | 261 | def test_prepare_transaction_p2sh_1of2_762_inputs(self): 262 | # P2SH transaction with 762 inputs 263 | 264 | prepare_transaction_response = self.load_json_file("data/json/prepare_transaction_response_P2WSH-over-P2SH_1of2_762inputs.json") 265 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_P2WSH-over-P2SH_1of2_762inputs.json") 266 | 267 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response) 268 | 269 | self.assertDictEqual(create_and_sign_transaction_response, response) 270 | -------------------------------------------------------------------------------- /tests/test_blockio_prepare_transactions.py: -------------------------------------------------------------------------------- 1 | from block_io import BlockIo 2 | 3 | import os 4 | import unittest 5 | import time 6 | 7 | import json 8 | 9 | class TestBlockIoPrepareTransactions(unittest.TestCase): 10 | 11 | def setUp(self): 12 | 13 | self.blockio = BlockIo("", "d1650160bd8d2bb32bebd139d0063eb6063ffa2f9e4501ad", 2) 14 | self.dtrust_keys = [ 15 | "b515fd806a662e061b488e78e5d0c2ff46df80083a79818e166300666385c0a2", 16 | "1584b821c62ecdc554e185222591720d6fe651ed1b820d83f92cdc45c5e21f", 17 | "2f9090b8aa4ddb32c3b0b8371db1b50e19084c720c30db1d6bb9fcd3a0f78e61", 18 | "6c1cefdfd9187b36b36c3698c1362642083dcc1941dc76d751481d3aa29ca65" 19 | ] 20 | 21 | self.sweep_key = BlockIo.Key.from_wif("cTj8Ydq9LhZgttMpxb7YjYSqsZ2ZfmyzVprQgjEzAzQ28frQi4ML").privkey_hex().decode("utf-8"); 22 | self.maxDiff = None 23 | 24 | def load_json_file(self, path): 25 | json_file = open(os.path.join(os.path.dirname(__file__), path)) 26 | data = json.load(json_file) 27 | json_file.close() 28 | 29 | return data 30 | 31 | def test_summarize_prepared_transaction(self): 32 | # tests response of summarize_prepared_transaction 33 | 34 | prepare_transaction_response = self.load_json_file("data/json/prepare_transaction_response_with_blockio_fee_and_expected_unsigned_txid.json") 35 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_with_blockio_fee_and_expected_unsigned_txid.json") 36 | summarize_prepared_transaction_response = self.load_json_file("data/json/summarize_prepared_transaction_response_with_blockio_fee_and_expected_unsigned_txid.json") 37 | 38 | summary = self.blockio.summarize_prepared_transaction(prepare_transaction_response) 39 | 40 | self.assertDictEqual(summarize_prepared_transaction_response, summary) 41 | 42 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response) 43 | 44 | self.assertDictEqual(create_and_sign_transaction_response, response) 45 | 46 | def test_expected_unsigned_txid(self): 47 | # tests success and failure for expected_unsigned_txid 48 | 49 | prepare_transaction_response = self.load_json_file("data/json/prepare_transaction_response_with_blockio_fee_and_expected_unsigned_txid.json") 50 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_with_blockio_fee_and_expected_unsigned_txid.json") 51 | 52 | # success 53 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response) 54 | self.assertDictEqual(create_and_sign_transaction_response, response) 55 | 56 | # failure 57 | prepare_transaction_response['data']['expected_unsigned_txid'] += 'x' 58 | try: 59 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response) 60 | self.assertEqual(True, False) # shouldn't happen 61 | except Exception as e: 62 | self.assertEqual(str(e), "Expected unsigned transaction ID mismatch. Please report this error to support@block.io.") 63 | 64 | def test_prepare_sweep_transaction_p2pkh(self): 65 | # P2PKH sweep 66 | 67 | prepare_transaction_response = self.load_json_file("data/json/prepare_sweep_transaction_response_p2pkh.json") 68 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_sweep_p2pkh.json") 69 | 70 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=[self.sweep_key]) 71 | 72 | self.assertDictEqual(create_and_sign_transaction_response, response) 73 | 74 | def test_prepare_sweep_transaction_p2wpkh_over_p2sh(self): 75 | # P2WPKH-over-P2SH sweep 76 | 77 | prepare_transaction_response = self.load_json_file("data/json/prepare_sweep_transaction_response_p2wpkh_over_p2sh.json") 78 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_sweep_p2wpkh_over_p2sh.json") 79 | 80 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=[self.sweep_key]) 81 | 82 | self.assertDictEqual(create_and_sign_transaction_response, response) 83 | 84 | def test_prepare_sweep_transaction_p2wpkh(self): 85 | # P2WPKH sweep 86 | 87 | prepare_transaction_response = self.load_json_file("data/json/prepare_sweep_transaction_response_p2wpkh.json") 88 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_sweep_p2wpkh.json") 89 | 90 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=[self.sweep_key]) 91 | 92 | self.assertDictEqual(create_and_sign_transaction_response, response) 93 | 94 | def test_prepare_transaction_dtrust_p2sh_3_of_5_keys(self): 95 | # P2SH dTrust partial 96 | 97 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_p2sh.json") 98 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_p2sh_3_of_5_keys.json") 99 | 100 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys[:3]) 101 | 102 | self.assertDictEqual(create_and_sign_transaction_response, response) 103 | 104 | def test_prepare_transaction_dtrust_p2sh_4_of_5_keys(self): 105 | # P2SH dTrust full 106 | 107 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_p2sh.json") 108 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_p2sh_4_of_5_keys.json") 109 | 110 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys[:4]) 111 | 112 | self.assertDictEqual(create_and_sign_transaction_response, response) 113 | 114 | def test_prepare_transaction_dtrust_p2wsh_over_p2sh_3_of_5_keys(self): 115 | # P2WSH-over-P2SH dTrust partial 116 | 117 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_p2wsh_over_p2sh.json") 118 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_p2wsh_over_p2sh_3_of_5_keys.json") 119 | 120 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys[:3]) 121 | 122 | self.assertDictEqual(create_and_sign_transaction_response, response) 123 | 124 | def test_prepare_transaction_dtrust_p2wsh_over_p2sh_4_of_5_keys(self): 125 | # P2WSH-over-P2SH dTrust full 126 | 127 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_p2wsh_over_p2sh.json") 128 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_p2wsh_over_p2sh_4_of_5_keys.json") 129 | 130 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys[:4]) 131 | 132 | self.assertDictEqual(create_and_sign_transaction_response, response) 133 | 134 | def test_prepare_transaction_dtrust_witness_v0_3_of_5_keys(self): 135 | # P2WSH (WITNESS_V0) dTrust partial 136 | 137 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_witness_v0.json") 138 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_witness_v0_3_of_5_keys.json") 139 | 140 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys[:3]) 141 | 142 | self.assertDictEqual(create_and_sign_transaction_response, response) 143 | 144 | def test_prepare_transaction_dtrust_witness_v0_4_of_5_keys(self): 145 | # P2WSH (WITNESS_V0) dTrust full 146 | 147 | prepare_transaction_response = self.load_json_file("data/json/prepare_dtrust_transaction_response_witness_v0.json") 148 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_dtrust_witness_v0_4_of_5_keys.json") 149 | 150 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response, keys=self.dtrust_keys[:4]) 151 | 152 | self.assertDictEqual(create_and_sign_transaction_response, response) 153 | 154 | def test_prepare_transaction(self): 155 | # mixed inputs 156 | 157 | prepare_transaction_response = self.load_json_file("data/json/prepare_transaction_response.json") 158 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response.json") 159 | 160 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response) 161 | 162 | self.assertDictEqual(create_and_sign_transaction_response, response) 163 | 164 | def test_prepare_transaction_witness_v1_output(self): 165 | # WITNESS_V1 output 166 | 167 | prepare_transaction_response = self.load_json_file("data/json/prepare_transaction_response_witness_v1_output.json") 168 | create_and_sign_transaction_response = self.load_json_file("data/json/create_and_sign_transaction_response_witness_v1_output.json") 169 | 170 | response = self.blockio.create_and_sign_transaction(prepare_transaction_response) 171 | 172 | self.assertDictEqual(create_and_sign_transaction_response, response) 173 | 174 | --------------------------------------------------------------------------------