diff --git a/README.md b/README.md index 6219ea4..6d53293 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Table of Contents ================= * [btcpy](#btcpy) + * [Table of Contents](#table-of-contents) * [Requirements](#requirements) * [Installation](#installation) * [What it does](#what-it-does) @@ -27,10 +28,11 @@ Table of Contents * [Usage examples](#usage-examples) * [Network setup](#network-setup) * [Parsing and serialization](#parsing-and-serialization) - * [Keys and addresses](#keys-and-addresses) + * [Keys](#keys) * [HD keys](#hd-keys) * [Scripts](#scripts) * [Low-level scripting functionalities](#low-level-scripting-functionalities) + * [Addresses](#addresses) * [Transactions](#transactions) * [Creating transactions](#creating-transactions) * [Spending a transaction](#spending-a-transaction) @@ -42,9 +44,11 @@ Table of Contents * [Multisig](#multisig) * [Timelocks, Hashlocks, IfElse](#timelocks-hashlocks-ifelse) * [Low-level signing](#low-level-signing) - * [Contributing](#contributing-and-running-tests) + * [Contributing and running tests](#contributing-and-running-tests) + * [Roadmp to v1](#roadmp-to-v1) * [TODO](#todo) + # Requirements The strict requirements of this library are: @@ -152,7 +156,7 @@ In the same way, these structures can be serialized and deserialized by using th `serialize()` and `deserialize()` methods. These methods respectively return and expect a `bytearray` type. -## Keys and addresses +## Keys The `PublicKey` class can handle both compressed and uncompressed public keys. In any case both the compressed and uncompressed version can be extracted. However, the structure will remember how it was initialised, so the `hexlify()`, @@ -209,19 +213,7 @@ using its `compress()` method: ``` Addresses can be either created from a `PublicKey` or from a script. -In particular this second use case will be documented in the **Script** section. - -Another low-level way to build an `Address` or `SegWitAddress` is by using their - constructor and providing the following data: - -```python -address = Address(addr_type, hashed_data) -sw_address = SegWitAddress(addr_type, hashed_data, version) -``` - -where `addr_type` can be either `'p2pkh'` or `'p2sh'` in the case of `Address` -and `'p2wpkh'` or `'p2wsh'` in the case of SegWitAddress. `hashed_data` must be a -160 or 256 bits long `bytearray`. +In particular this second use case will be documented in the **Addresses** section. ### HD keys The `structs.hd` module provides functionalities to handle BIP32 HD keys. @@ -256,8 +248,10 @@ the following hierarchy * `ScriptPubKey` * `P2pkhscript` * `P2wpkhScript` + * `P2wpkhV0Script` * `P2shScript` * `P2wshScript` + * `P2wshV0Script` * `P2pkScript` * `NulldataScript` * `MultisigScript` @@ -355,6 +349,81 @@ will contain `[error]` where the push takes place. For non-existing opcodes the the special opcode `OP_INVALIDOPCODE`. These two beahviours match Bitcoin Core's behaviour when producing script asm. +## Addresses + +Supported addresses are: `P2pkhAddress`, `P2shAddress`, `P2wpkhAddress` and `P2wshAddress`. +These constructors can be used to build an address from a hash (plus a SegWit version in the +case of `P2wpkhAddress` or `P2wshAddress`), for example: + +```python +from btcpy.structs.crypto import PublicKey +from btcpy.structs.address import P2pkhAddress, P2wpkhAddress +pubk = PublicKey.unhexlify('02ea4e183e8c751a4cc72abb7088cea79351dbfb7981ceb48f286ccfdade4d42c8') +address = P2pkhAddress(pubk.hash()) +sw_address = P2wpkhAddress(pubk.hash(), version=0) +print(str(address)) # prints "mkGY1QBotzNCrpJaEsje3BpYJsktksi3gJ" +print(str(sw_address)) # prints "tb1qxs0gs9dzukv863jud3wpldtrjh9edeqqqzahcz" +``` + +Please note that by default all the address constructors will return an address in the +format of the network type specified in setup (testnet in the case of this example) but +a flag can be passed to them to return an address for another network: + +```python +address = P2pkhAddress(pubk.hash(), mainnet=True) +sw_address = P2wpkhAddress(pubk.hash(), version=0, mainnet=True) +print(str(address)) # prints "15kaiM6q5xvx5hpxXJmGDGcDStABoGTzSX" +print(str(sw_address)) # prints "bc1qxs0gs9dzukv863jud3wpldtrjh9edeqq2yxyr3" +``` + +However, a more common usecase is generating an address for a script, for this the `from_script` +static method of all address classes can be used, in particular: + +* `P2pkhAddress.from_script(script, mainnet=None)` will instantiate a `P2pkhAddress` from a +`P2pkhScript`, raising `WrongScriptType` exception in case another type of script is provided. +* `P2shAddress.from_script(script, mainnet=None)` will instantiate a `P2shAddress` representing +the script address if a `P2shscript` is provided, while returning the address of the script +embedded in P2SH format if other script types are provided. +* `P2wpkhAddress.from_script(script, version, mainnet=None)` will instantiate a `P2wpkhAddress` + from a `P2wpkhScript`, raising `WrongScriptType` exception in case another type of script + is provided. +* `P2wshAddress.from_script(script, version, mainnet=None)` will instantiate a `P2wshAddress` +representing the script address if a `P2wshscript` is provided, while returning the address +of the script embedded in P2WSH format if other script types are provided. + +The only scripts that directly support an address (i.e. `P2pkhScript`, `P2wpkhScript`, +`P2shscript`, `P2wshScript`) also provide a helper method `address()` to return the script +address, for all other script types will return `None` if the `address()` method is called +and will need to be explicitly converted to P2SH or P2WSH format to obtain an address. Some +examples follow: + +```python +>>> str(P2pkhAddress.from_script(P2pkhScript(pubk))) +'mkGY1QBotzNCrpJaEsje3BpYJsktksi3gJ' +>>> str(P2pkhScript(pubk).address()) +'mkGY1QBotzNCrpJaEsje3BpYJsktksi3gJ' +>>> str(P2pkhAddress.from_script(P2shScript(P2pkhScript(pubk)))) +Traceback (most recent call last): + File "", line 1, in + File ".../btcpy/btcpy/structs/address.py", line 120, in from_script + raise WrongScriptType('Trying to produce P2pkhAddress from {} script'.format(script.__class__.__name__)) +btcpy.structs.address.WrongScriptType: Trying to produce P2pkhAddress from P2shScript script +>>> str(P2shAddress.from_script(P2shScript(P2pkhScript(pubk)))) +'2NAJWD6EnXMVt16HUp5vmfwPjz4FemvPhYt' +>>> str(P2shScript(P2pkhScript(pubk)).address()) +'2NAJWD6EnXMVt16HUp5vmfwPjz4FemvPhYt' +>>> str(P2wpkhAddress.from_script(P2wpkhV0Script(pubk))) +'tb1qxs0gs9dzukv863jud3wpldtrjh9edeqqqzahcz' +>>> str(P2wpkhV0Script(pubk).address()) +'tb1qxs0gs9dzukv863jud3wpldtrjh9edeqqqzahcz' +>>> str(P2wpkhAddress.from_script(P2shScript(P2wpkhV0Script(pubk)))) +Traceback (most recent call last): + File "", line 1, in + File ".../btcpy/btcpy/structs/address.py", line 158, in from_script + raise WrongScriptType('Trying to produce P2pkhAddress from {} script'.format(script.__class__.__name__)) +btcpy.structs.address.WrongScriptType: Trying to produce P2pkhAddress from P2shScript script +``` + ## Transactions ### Creating transactions @@ -754,14 +823,20 @@ python3 -m unittest tests/integration.py Contributors are invited to run these tests before submitting PRs. Also, contributions to improve and expand these tests are highly welcome. +# Roadmp to v1 +This library's stable version 1 will be released once the following changes are made: +* More efficient script matching (i.e. scripts should be able to specify fast matching conditions +instead of trying to parse the raw bytes to decide whether the template is matched) +* Caching on SegWit digest computation to avoid quadratic hashing +* Generation of private keys through secure entropy sources +* An extensive documentation of all modules, classes and their parameters is produced + # TODO -Since this library is still a work in progress, the following roadmap lists the improvements to be done: +Since this library is still a work in progress, the following roadmap lists the improvements to be +done eventually: * Expanding the test suites -* Improving and expanding this documentation * Adding docstrings where missing (many places) * Handling `OP_CODESEPARATOR`s in the signing process -* Adding caching to segwit digest computation to avoid quadratic hashing * Add further transaction creation helpers * Add RPC calls to Bitcoin Core nodes * Add networking with Bitcoin Core nodes -* Add methods to generate private keys from entropy diff --git a/btcpy/constants.py b/btcpy/constants.py new file mode 100644 index 0000000..257176f --- /dev/null +++ b/btcpy/constants.py @@ -0,0 +1,26 @@ +class Constants(object): + + _lookup = {'base58.prefixes': {'1': ('p2pkh', 'mainnet'), + 'm': ('p2pkh', 'testnet'), + 'n': ('p2pkh', 'testnet'), + '3': ('p2sh', 'mainnet'), + '2': ('p2sh', 'testnet')}, + 'base58.raw_prefixes': {('mainnet', 'p2pkh'): bytearray(b'\x00'), + ('testnet', 'p2pkh'): bytearray(b'\x6f'), + ('mainnet', 'p2sh'): bytearray(b'\x05'), + ('testnet', 'p2sh'): bytearray(b'\xc4')}, + 'bech32.net_to_hrp': {'mainnet': 'bc', + 'testnet': 'tb'}, + 'bech32.hrp_to_net': {'bc': 'mainnet', + 'tb': 'testnet'}, + 'xkeys.prefixes': {'mainnet': 'x', 'testnet': 't'}, + 'xpub.version': {'mainnet': b'\x04\x88\xb2\x1e', 'testnet': b'\x04\x35\x87\xcf'}, + 'xprv.version': {'mainnet': b'\x04\x88\xad\xe4', 'testnet': b'\x04\x35\x83\x94'}, + 'wif.prefixes': {'mainnet': 0x80, 'testnet': 0xef}} + + @staticmethod + def get(key): + try: + return Constants._lookup[key] + except KeyError: + raise ValueError('Unknown constant: {}'.format(key)) diff --git a/btcpy/lib/codecs.py b/btcpy/lib/codecs.py index 1b30a2c..eb06bdb 100644 --- a/btcpy/lib/codecs.py +++ b/btcpy/lib/codecs.py @@ -14,7 +14,8 @@ from .bech32 import decode, encode from ..setup import is_mainnet, net_name -from ..structs.address import Address, SegWitAddress +from ..constants import Constants +from ..structs.address import Address, P2pkhAddress, P2shAddress, P2wpkhAddress, P2wshAddress class CouldNotDecode(ValueError): @@ -45,32 +46,21 @@ def check_network(cls, network): class Base58Codec(Codec): - raw_prefixes = {('mainnet', 'p2pkh'): bytearray(b'\x00'), - ('testnet', 'p2pkh'): bytearray(b'\x6f'), - ('mainnet', 'p2sh'): bytearray(b'\x05'), - ('testnet', 'p2sh'): bytearray(b'\xc4')} - - prefixes = {'1': ('p2pkh', 'mainnet'), - 'm': ('p2pkh', 'testnet'), - 'n': ('p2pkh', 'testnet'), - '3': ('p2sh', 'mainnet'), - '2': ('p2sh', 'testnet')} - hash_len = 20 @staticmethod def encode(address): try: - prefix = Base58Codec.raw_prefixes[(address.network, address.type)] + prefix = Constants.get('base58.raw_prefixes')[(address.network, address.get_type())] except KeyError: - raise CouldNotEncode('Impossible to encode address type: {}, network: {}'.format(address.type, + raise CouldNotEncode('Impossible to encode address type: {}, network: {}'.format(address.get_type(), address.network)) return b58encode_check(bytes(prefix + address.hash)) @staticmethod def decode(string, check_network=True): try: - addr_type, network = Base58Codec.prefixes[string[0]] + addr_type, network = Constants.get('base58.prefixes')[string[0]] except KeyError: raise CouldNotDecode('Impossible to decode address {}'.format(string)) hashed_data = bytearray(b58decode_check(string))[1:] @@ -81,23 +71,24 @@ def decode(string, check_network=True): if check_network: Base58Codec.check_network(network) - return Address(addr_type, hashed_data, network == 'mainnet') - + if addr_type == 'p2pkh': + cls = P2pkhAddress + elif addr_type == 'p2sh': + cls = P2shAddress + else: + raise ValueError('Unknown address type: {}'.format(addr_type)) -class Bech32Codec(Codec): + return cls(hashed_data, network == 'mainnet') - net_to_hrp = {'mainnet': 'bc', - 'testnet': 'tb'} - hrp_to_net = {'bc': 'mainnet', - 'tb': 'testnet'} +class Bech32Codec(Codec): lengths = {42: 'p2wpkh', 62: 'p2wsh'} @staticmethod def encode(address): - prefix = Bech32Codec.net_to_hrp[address.network] + prefix = Constants.get('bech32.net_to_hrp')[address.network] return encode(prefix, address.version, address.hash) @staticmethod @@ -112,7 +103,7 @@ def decode(string, check_network=True): string = string.lower() try: - network = Bech32Codec.hrp_to_net[string[:2]] + network = Constants.get('bech32.hrp_to_net')[string[:2]] addr_type = Bech32Codec.lengths[len(string)] except KeyError: raise CouldNotDecode('Impossible to decode address {}'.format(string)) @@ -124,4 +115,11 @@ def decode(string, check_network=True): if check_network: Bech32Codec.check_network(network) - return SegWitAddress(addr_type, bytearray(hashed_data), version, network == 'mainnet') + if addr_type == 'p2wpkh': + cls = P2wpkhAddress + elif addr_type == 'p2wsh': + cls = P2wshAddress + else: + raise ValueError('Unknown address type: {}'.format(addr_type)) + + return cls(bytearray(hashed_data), version, network == 'mainnet') diff --git a/btcpy/structs/address.py b/btcpy/structs/address.py index b4e960a..64dafcb 100644 --- a/btcpy/structs/address.py +++ b/btcpy/structs/address.py @@ -14,6 +14,10 @@ from ..setup import is_mainnet +class WrongScriptType(Exception): + pass + + class BaseAddress(metaclass=ABCMeta): @staticmethod @@ -34,52 +38,148 @@ def is_valid(string, check_network=True): def get_codec(): raise NotImplemented + @classmethod + @abstractmethod + def from_script(cls, script, mainnet=None): + raise NotImplemented + + @classmethod + @abstractmethod + def get_type(cls): + raise NotImplemented + @classmethod def from_string(cls, string, check_network=True): return cls.get_codec().decode(string, check_network) + @classmethod + def hash_length(cls): + raise NotImplemented + + def __init__(self, hashed_data): + if len(hashed_data) != self.__class__.hash_length(): + raise ValueError('Hashed data must be {}-bytes long, length: {}'.format(self.__class__.hash_length(), + len(hashed_data))) + def __str__(self): return self.__class__.get_codec().encode(self) -class Address(BaseAddress): +class Address(BaseAddress, metaclass=ABCMeta): @staticmethod def get_codec(): from ..lib.codecs import Base58Codec return Base58Codec - def __init__(self, addr_type, hashed_data, mainnet=None): + def __init__(self, hashed_data, mainnet=None): + + super().__init__(hashed_data) + if mainnet is None: mainnet = is_mainnet() - network = 'mainnet' if mainnet else 'testnet' - self.network = network - self.type = addr_type + self.network = 'mainnet' if mainnet else 'testnet' self.hash = hashed_data def __eq__(self, other): - return (self.type, self.network, self.hash) == (other.type, other.network, other.hash) + return (self.network, self.hash) == (other.network, other.hash) -class SegWitAddress(Address): +class SegWitAddress(BaseAddress, metaclass=ABCMeta): @staticmethod def get_codec(): from ..lib.codecs import Bech32Codec return Bech32Codec - def __init__(self, addr_type, hashed_data, version, mainnet=None): - super().__init__(addr_type, hashed_data, mainnet) + def __init__(self, hashed_data, version, mainnet=None): + + super().__init__(hashed_data) + + if mainnet is None: + mainnet = is_mainnet() + self.network = 'mainnet' if mainnet else 'testnet' + self.hash = hashed_data self.version = version - def to_address(self): - if self.type == 'p2wpkh': - addr_type = 'p2pkh' - elif self.type == 'p2wsh': - addr_type = 'p2sh' + def __eq__(self, other): + return (self.network, self.hash, self.version) == (other.network, other.hash, other.version) + + +class P2pkhAddress(Address): + + @classmethod + def get_type(cls): + return 'p2pkh' + + @classmethod + def from_script(cls, script, mainnet=None): + from .script import P2pkhScript + # can't use isinstance here: P2wpkhScript is child of P2pkhScript + if script.__class__ is not P2pkhScript: + raise WrongScriptType('Trying to produce P2pkhAddress from {} script'.format(script.__class__.__name__)) + + return cls(script.pubkeyhash, mainnet) + + @classmethod + def hash_length(cls): + return 20 + + +class P2shAddress(Address): + + @classmethod + def get_type(cls): + return 'p2sh' + + @classmethod + def from_script(cls, script, mainnet=None): + from .script import P2shScript + # can't use isinstance here: P2wshScript is child of P2shScript + if script.__class__ is P2shScript: + return cls(script.scripthash, mainnet) + return cls(script.p2sh_hash(), mainnet) + + @classmethod + def hash_length(cls): + return 20 + + +class P2wpkhAddress(SegWitAddress): + + @classmethod + def get_type(cls): + return 'p2wpkh' + + @classmethod + def from_script(cls, script, mainnet=None): + from .script import P2wpkhScript + if not isinstance(script, P2wpkhScript): + raise WrongScriptType('Trying to produce P2pkhAddress from {} script'.format(script.__class__.__name__)) + + return cls(script.pubkeyhash, script.__class__.get_version(), mainnet) + + @classmethod + def hash_length(cls): + return 20 + + +class P2wshAddress(SegWitAddress): + + @classmethod + def get_type(cls): + return 'p2wsh' + + @classmethod + def from_script(cls, script, mainnet=None): + from .script import P2wshScript + version = script.__class__.get_version() + if isinstance(script, P2wshScript): + hashed_data = script.scripthash else: - raise ValueError('SegWitAddress type does not match p2wpkh nor p2wsh, {} instead'.format(self.type)) - return Address(addr_type, self.hash, self.network == 'mainnet') + hashed_data = script.p2wsh_hash() + return cls(hashed_data, version, mainnet) - def __eq__(self, other): - return super().__eq__(other) and self.version == other.version + @classmethod + def hash_length(cls): + return 32 diff --git a/btcpy/structs/crypto.py b/btcpy/structs/crypto.py index 5b22ff8..ee0f469 100644 --- a/btcpy/structs/crypto.py +++ b/btcpy/structs/crypto.py @@ -9,7 +9,7 @@ # propagated, or distributed except according to the terms contained in the # LICENSE.md file. -from binascii import hexlify, unhexlify +from binascii import unhexlify from ..lib.base58 import b58decode_check, b58encode_check from hashlib import sha256 from ecdsa import SigningKey, SECP256k1 @@ -18,8 +18,9 @@ from abc import ABCMeta from ..lib.types import HexSerializable -from .address import Address, SegWitAddress -from ..setup import is_mainnet +from .address import P2pkhAddress, P2wpkhAddress +from ..setup import is_mainnet, net_name +from ..constants import Constants class Key(HexSerializable, metaclass=ABCMeta): @@ -39,14 +40,12 @@ def from_wif(wif, check_network=True): decoded = b58decode_check(wif) prefix, *rest = decoded - if prefix not in {0x80, 0xef}: + if prefix not in Constants.get('wif.prefixes').values(): raise ValueError('Unknown private key prefix: {:02x}'.format(prefix)) if check_network: - if prefix == 0x80 and not is_mainnet(): - raise ValueError('Mainnet prefix in testnet environment') - elif prefix == 0xef and is_mainnet(): - raise ValueError('Testnet prefix in mainnet environment') + if prefix != Constants.get('wif.prefixes')['mainnet' if is_mainnet() else 'testnet']: + raise ValueError('{0} prefix in non-{0} environment'.format(net_name())) public_compressed = len(rest) == 33 privk = rest[0:32] @@ -64,8 +63,8 @@ def __init__(self, priv, public_compressed=True): def to_wif(self, mainnet=None): if mainnet is None: mainnet = is_mainnet() - prefix = bytearray([0x80]) if mainnet else bytearray([0xef]) - decoded = prefix + self.key + prefix = Constants.get('wif.prefixes')['mainnet' if mainnet else 'testnet'] + decoded = bytearray([prefix]) + self.key if self.public_compressed: decoded.append(0x01) return b58encode_check(bytes(decoded)) @@ -202,16 +201,16 @@ def serialize(self): def to_address(self, mainnet=None): if mainnet is None: mainnet = is_mainnet() - return Address('p2pkh', self.hash(), mainnet) + return P2pkhAddress(self.hash(), mainnet) - def to_segwit_address(self, mainnet=None): + def to_segwit_address(self, version, mainnet=None): if mainnet is None: mainnet = is_mainnet() if self.type == 'uncompressed': pubk = PublicKey(self.compressed) else: pubk = self - return SegWitAddress('p2wpkh', pubk.hash(), mainnet) + return P2wpkhAddress(pubk.hash(), version, mainnet) def compress(self): if self.type != 'uncompressed': diff --git a/btcpy/structs/hd.py b/btcpy/structs/hd.py index 5488117..5f84af4 100644 --- a/btcpy/structs/hd.py +++ b/btcpy/structs/hd.py @@ -22,69 +22,70 @@ from ..lib.parsing import Stream, Parser from ..setup import is_mainnet from .crypto import PrivateKey, PublicKey +from ..constants import Constants class ExtendedKey(HexSerializable, metaclass=ABCMeta): - + master_parent_fingerprint = bytearray([0]*4) first_hardened_index = 1 << 31 curve_order = SECP256k1.order - + @classmethod def master(cls, key, chaincode): return cls(key, chaincode, 0, cls.master_parent_fingerprint, 0, hardened=True) - + @classmethod def decode(cls, string, check_network=True): - if string[0] == 'x': + if string[0] == Constants.get('xkeys.prefixes')['mainnet']: mainnet = True - elif string[0] == 't': + elif string[0] == Constants.get('xkeys.prefixes')['testnet']: mainnet = False else: raise ValueError('Encoded key not recognised: {}'.format(string)) - + if check_network and mainnet != is_mainnet(): raise ValueError('Trying to decode {}mainnet key ' 'in {}mainnet environment'.format('' if mainnet else 'non-', 'non-' if mainnet else '')) - + decoded = b58decode_check(string) parser = Parser(bytearray(decoded)) parser >> 4 depth = int.from_bytes(parser >> 1, 'big') fingerprint = parser >> 4 index = int.from_bytes(parser >> 4, 'big') - + if index >= cls.first_hardened_index: index -= cls.first_hardened_index hardened = True else: hardened = False - + chaincode = parser >> 32 keydata = parser >> 33 - + if string[1:4] == 'prv': subclass = ExtendedPrivateKey elif string[1:4] == 'pub': subclass = ExtendedPublicKey else: raise ValueError('Encoded key not recognised: {}'.format(string)) - + key = subclass.decode_key(keydata) - + return subclass(key, chaincode, depth, fingerprint, index, hardened) - + @staticmethod @abstractmethod def decode_key(keydata): raise NotImplemented - + @staticmethod @abstractmethod def get_version(mainnet=None): raise NotImplemented - + def __init__(self, key, chaincode, depth, pfing, index, hardened=False): if not 0 <= depth <= 255: raise ValueError('Depth must be between 0 and 255') @@ -94,7 +95,7 @@ def __init__(self, key, chaincode, depth, pfing, index, hardened=False): self.parent_fingerprint = pfing self.index = index self.hardened = hardened - + def derive(self, path): """ :param path: a path like "m/44'/0'/1'/0/10" if deriving from a master key, @@ -103,13 +104,13 @@ def derive(self, path): the derived ExtendedPrivateKey if deriving from an ExtendedPrivateKey """ steps = path.split('/') - + if steps[0] not in {'m', '.'}: raise ValueError('Invalid derivation path: {}'.format(path)) - + if steps[0] == 'm' and not self.is_master(): raise ValueError('Trying to derive absolute path from non-master key') - + current = self for step in steps[1:]: hardened = False @@ -118,14 +119,13 @@ def derive(self, path): step = step[:-1] index = int(step) current = current.get_child(index, hardened) - # print(current) - + return current @abstractmethod def get_child(self, index, hardened=False): raise NotImplemented - + @abstractmethod def _serialized_public(self): raise NotImplemented @@ -133,7 +133,7 @@ def _serialized_public(self): @abstractmethod def _serialize_key(self): raise NotImplemented - + def get_hash(self, index, hardened=False): cls = self.__class__ if hardened: @@ -145,15 +145,15 @@ def get_hash(self, index, hardened=False): if left > cls.curve_order: raise ValueError('Left side of hmac generated number bigger than SECP256k1 curve order') return left, right - + def is_master(self): return all([self.depth == 0, self.parent_fingerprint == ExtendedKey.master_parent_fingerprint, self.index == 0]) - + def encode(self, mainnet=None): return b58encode_check(bytes(self.serialize(mainnet))) - + def serialize(self, mainnet=None): cls = self.__class__ result = Stream() @@ -167,7 +167,7 @@ def serialize(self, mainnet=None): result << self.chaincode result << self._serialize_key() return result.serialize() - + def __str__(self): return 'version: {}\ndepth: {}\nparent fp: {}\n' \ 'index: {}\nchaincode: {}\nkey: {}\nhardened: {}'.format(self.__class__.get_version(), @@ -177,7 +177,7 @@ def __str__(self): self.chaincode, self.key, self.hardened) - + def __eq__(self, other): return all([self.key == other.key, self.chaincode == other.chaincode, @@ -185,20 +185,21 @@ def __eq__(self, other): self.parent_fingerprint == other.parent_fingerprint, self.index == other.index, self.hardened == other.hardened]) - - + + class ExtendedPrivateKey(ExtendedKey): - + @staticmethod def get_version(mainnet=None): if mainnet is None: mainnet = is_mainnet() - return bytearray(b'\x04\x88\xad\xe4') if mainnet else bytearray(b'\x04\x35\x83\x94') - + # using net_name here would ignore the mainnet=None flag + return Constants.get('xprv.version')['mainnet' if mainnet else 'testnet'] + @staticmethod def decode_key(keydata): return PrivateKey(keydata[1:]) - + def __init__(self, key, chaincode, depth, pfing, index, hardened=False): if not isinstance(key, PrivateKey): raise TypeError('ExtendedPrivateKey expects a PrivateKey') @@ -206,7 +207,7 @@ def __init__(self, key, chaincode, depth, pfing, index, hardened=False): def __int__(self): return int.from_bytes(self.key.key, 'big') - + def get_child(self, index, hardened=False): left, right = self.get_hash(index, hardened) k = (int(self) + left) % self.__class__.curve_order @@ -218,16 +219,16 @@ def get_child(self, index, hardened=False): self.get_fingerprint(), index, hardened) - + def get_fingerprint(self): return self.pub().get_fingerprint() - + def _serialize_key(self): return bytearray([0]) + self.key.serialize() - + def _serialized_public(self): return self.pub()._serialize_key() - + def pub(self): return ExtendedPublicKey(self.key.pub(), self.chaincode, @@ -235,20 +236,21 @@ def pub(self): self.parent_fingerprint, self.index, self.hardened) - + class ExtendedPublicKey(ExtendedKey): - + @staticmethod def get_version(mainnet=None): if mainnet is None: mainnet = is_mainnet() - return bytearray(b'\x04\x88\xb2\x1e') if mainnet else bytearray(b'\x04\x35\x87\xcf') - + # using net_name here would ignore the mainnet=None flag + return Constants.get('xpub.version')['mainnet' if mainnet else 'testnet'] + @staticmethod def decode_key(keydata): return PublicKey(keydata) - + def __init__(self, key, chaincode, depth, pfing, index, hardened=False): if not isinstance(key, PublicKey): raise TypeError('ExtendedPublicKey expects a PublicKey') @@ -256,7 +258,7 @@ def __init__(self, key, chaincode, depth, pfing, index, hardened=False): def __int__(self): return int.from_bytes(self.key.key, 'big') - + def get_fingerprint(self): return self.key.hash()[:4] @@ -267,17 +269,17 @@ def get_child(self, index, hardened=False): if point == INFINITY: raise ValueError('Computed point equals INFINITY') return ExtendedPublicKey(PublicKey.from_point(point), right, self.depth+1, self.get_fingerprint(), index, False) - + def get_hash(self, index, hardened=False): if hardened: raise ValueError('Trying to generate hardened child from public key') return super().get_hash(index, hardened) - + def _serialized_public(self): return self._serialize_key() - + def _serialize_key(self): return self.key.compressed - + def __lt__(self, other): return self.key < other.key diff --git a/btcpy/structs/script.py b/btcpy/structs/script.py index 3fc0d20..c0949f5 100644 --- a/btcpy/structs/script.py +++ b/btcpy/structs/script.py @@ -19,8 +19,7 @@ from ..lib.parsing import ScriptParser, Parser, Stream, UnexpectedOperationFound from ..lib.opcodes import OpCodeConverter from .crypto import WrongPubKeyFormat -from .address import Address, SegWitAddress -from ..setup import is_mainnet +from .address import P2pkhAddress, P2shAddress, P2wpkhAddress, P2wshAddress class WrongScriptTypeException(Exception): @@ -449,12 +448,6 @@ def p2wsh_hash(self): def to_stack_data(self): return StackData.from_bytes(self.serialize()) - def to_address(self, segwit_version=None): - if segwit_version is not None: - return SegWitAddress('p2wsh', self.p2wsh_hash(), segwit_version, is_mainnet()) - else: - return Address('p2sh', self.p2sh_hash(), is_mainnet()) - def is_standard(self): """Subclasses which have standard types should reimplement this method""" return False @@ -491,9 +484,7 @@ def __init__(self, param): object.__setattr__(self, 'pubkeyhash', param.hash()) elif isinstance(param, bytearray): object.__setattr__(self, 'pubkeyhash', param) - elif isinstance(param, Address): - if param.type != self.type: - raise ValueError('Non-p2pkh address provided. Address type: {}'.format(param.type)) + elif isinstance(param, P2pkhAddress): object.__setattr__(self, 'pubkeyhash', param.hash) else: raise TypeError('Wrong type for P2pkhScript __init__: {}'.format(type(param))) @@ -508,24 +499,46 @@ def type(self): return 'p2pkh' def address(self, mainnet=None): - return Address('p2pkh', self.pubkeyhash, mainnet) + return P2pkhAddress.from_script(self, mainnet) def is_standard(self): return True -class P2wpkhV0Script(P2pkhScript): +class SegWitScript(ScriptPubKey, metaclass=ABCMeta): + + @staticmethod + @abstractmethod + def get_version(): + raise NotImplemented + + +class P2wpkhScript(P2pkhScript, SegWitScript, metaclass=ABCMeta): + + @staticmethod + def get(segwit_version): + for cls in P2wpkhScript.__subclasses__(): + if cls.get_version() == segwit_version: + return cls + raise ValueError('Undefined version: {}'.format(segwit_version)) + + def address(self, mainnet=None): + return P2wpkhAddress.from_script(self, mainnet) + + +class P2wpkhV0Script(P2wpkhScript): template = 'OP_0 <20>' compile_fmt = 'OP_0 {}' + @staticmethod + def get_version(): + return 0 + def __init__(self, param): - if isinstance(param, SegWitAddress): - if param.type != 'p2wpkh': - raise ValueError('Non-p2wpkh address provided. Address type: {}'.format(param.type)) - else: - param = param.hash + if isinstance(param, P2wpkhAddress): + param = param.hash super().__init__(param) @@ -536,22 +549,10 @@ def __repr__(self): def type(self): return 'p2wpkhv0' - def address(self, mainnet=None): - return SegWitAddress('p2wpkh', self.pubkeyhash, 0) - def get_scriptcode(self): return P2pkhScript(self.pubkeyhash).to_stack_data() -class P2wpkhScript(ScriptPubKey, metaclass=ABCMeta): - - versions = {0: P2wpkhV0Script} - - @classmethod - def get(cls, segwit_version): - return cls.versions[segwit_version] - - # noinspection PyUnresolvedReferences class P2shScript(ScriptPubKey): @@ -575,9 +576,7 @@ def __init__(self, param): if isinstance(param, ScriptPubKey): object.__setattr__(self, 'scripthash', param.p2sh_hash()) - elif isinstance(param, Address): - if param.type != self.type: - raise ValueError('Non-p2sh address provided. Address type: {}'.format(param.type)) + elif isinstance(param, P2shAddress): object.__setattr__(self, 'scripthash', param.hash) elif isinstance(param, bytearray): object.__setattr__(self, 'scripthash', param) @@ -596,29 +595,42 @@ def type(self): def is_standard(self): return True - def address(self): - return Address('p2sh', self.scripthash) + def address(self, mainnet=None): + return P2shAddress.from_script(self, mainnet) - def to_address(self, segwit_version=None): - return self.address() + +class P2wshScript(P2shScript, SegWitScript, metaclass=ABCMeta): + + @staticmethod + def get(segwit_version): + for cls in P2wshScript.__subclasses__(): + if cls.get_version() == segwit_version: + return cls + raise ValueError('Undefined version: {}'.format(segwit_version)) + + def address(self, mainnet=None): + return P2wshAddress.from_script(self, mainnet) # noinspection PyUnresolvedReferences -class P2wshV0Script(P2shScript): +class P2wshV0Script(P2wshScript): template = 'OP_0 <32>' compile_fmt = 'OP_0 {}' + @staticmethod + def get_version(): + return 0 + def __init__(self, param): + if isinstance(param, P2wpkhScript): + raise ValueError("Can't embed P2WPKH script in P2WSH format") # segwit p2wsh have different hashing method than regular p2sh scripts! if isinstance(param, ScriptPubKey): param = param.p2wsh_hash() - if isinstance(param, SegWitAddress): - if param.type != 'p2wsh': - raise ValueError('Non-p2wsh address provided. Address type: {}'.format(param.type)) - else: - param = param.hash + if isinstance(param, P2wshAddress): + param = param.hash super().__init__(param) @@ -629,23 +641,6 @@ def __repr__(self): def type(self): return 'p2wshv0' - def address(self): - return SegWitAddress('p2wsh', self.scripthash, 0) - - def to_address(self, segwit_version=None): - if segwit_version is not None: - raise ValueError("Can't request p2wsh address of p2wsh script") - return self.address() - - -class P2wshScript(ScriptPubKey, metaclass=ABCMeta): - - versions = {0: P2wshV0Script} - - @classmethod - def get(cls, segwit_version): - return cls.versions[segwit_version] - # noinspection PyUnresolvedReferences class P2pkScript(ScriptPubKey): diff --git a/btcpy/structs/transaction.py b/btcpy/structs/transaction.py index 5318c73..4fde5b2 100644 --- a/btcpy/structs/transaction.py +++ b/btcpy/structs/transaction.py @@ -200,6 +200,9 @@ def type(self): def req_sigs(self): return self.script_pubkey.req_sigs + def address(self): + return self.script_pubkey.address() + def is_standard(self): return self.script_pubkey.is_standard() diff --git a/tests/data/p2wpkh_over_p2sh.json b/tests/data/p2wpkh_over_p2sh.json new file mode 100644 index 0000000..40bc15c --- /dev/null +++ b/tests/data/p2wpkh_over_p2sh.json @@ -0,0 +1,15 @@ +[{"address": "3GNEDq5ePGb7bWgnw5XZwqu1obQrKs54Pn", + "witness_version": 0, + "script_pubkey": "OP_HASH160 a0fc3fd6676fb885b622f55d85fa7af15d6ba009 OP_EQUAL", + "redeem_script": "0014aa7689051186911d8f2a81a6fee617caeaf9604d", + "pubkey": "03b84b20c51ce27af451fdf98dd2be8b981ad05bb2b76a1a1fe3f91092ff2700dd"}, + {"address": "3JzvEy64J63pDv7KjyKsVzupMgKcwdgQU4", + "witness_version": 0, + "script_pubkey": "OP_HASH160 bddce1db77593a7ac8d67f0d488c4311d5103ffa OP_EQUAL", + "redeem_script": "0014394c1d55120fea8442873d75d93263e22518f914", + "pubkey": "03e196163b6d30d0fef27eff5bae2754a7ccba75d466b78c87116b6535bacce31b"}, + {"address": "3AgdMpM9Z8CfE7mT16Aph4ycVvrp1UtumH", + "witness_version": 0, + "script_pubkey": "OP_HASH160 62a64bde96830dd23fe48720195705dbe929f6b7 OP_EQUAL", + "redeem_script": "0014a2242bc7074146d6a7929028860aa177672757ea", + "pubkey": "02c9e7cdf2d3d87ce3bc5893533691b48c08b886fd398bbe1746a501ab711710d0"}] diff --git a/tests/data/p2wsh_over_p2sh.json b/tests/data/p2wsh_over_p2sh.json new file mode 100644 index 0000000..dc5552a --- /dev/null +++ b/tests/data/p2wsh_over_p2sh.json @@ -0,0 +1,463 @@ +[{ + "address": "37MCavD53VpVhNdN89rdcFjryvTWZCMyug", + "witness_version": 0, + "script_pubkey": "OP_HASH160 3e1155d93cb6807f09f1c8a7a52a56184d1be049 OP_EQUAL", + "redeem_script": "0020927eb1e44aa5e31d6945976e4a216e068554350e24b5b7283a80209b81690b96", + "witness_script": "522102a4dc511beeee6cd98b42614908ab7d98c1424495f254d42bd3352670b7f1de1921038dd13e2e67f0477f5d191863d3728bad0df7948438bf5ef8be1c0261270e3f4621031aa7eecccb2730c58286f264f466ca1e5eac83482a334d8db1fd29c2994ce19e53ae" + }, + { + "address": "3AqCz2ziK5AeEzgLguq3gYJXRxgbyxuyAZ", + "witness_version": 0, + "script_pubkey": "OP_HASH160 6445b13cf3cda289b74e68e719509e8c548a5bc0 OP_EQUAL", + "redeem_script": "00202df169b680f83895eef968bdfb78d83f15ff247e045848949ae42ec5a4be1ab3", + "witness_script": "522102996b6f36115165888487af75af89bb84de0024406c6bb44201f4b4b1edf16fb32103c7a61d4622e65f48e03d43ed3edd476994ca1ac64dbca359b05a63ae550ce9fe2103e03387744e055820fde25eec10c01bd334ebf3f00d3bf1e854a797308896026953ae" + }, + { + "address": "3MmDgQqpSqj57QrAPkJutGSYW7y8awZmSJ", + "witness_version": 0, + "script_pubkey": "OP_HASH160 dc2dfe441f897cd063e345f2122c05025aab8120 OP_EQUAL", + "redeem_script": "002066c4fb20950e79f881e991b123a9ee0ba5ceac684733b305ec48f4353850ede0", + "witness_script": "5221039de5987f36c9977ef0b99aa1d0ccb5b2f2d0e837e41470f18e5dabe018c1d90f210312389566003f771bdcd547f0b76b5911a2321596365530b16fcd27f2a0489bc9210296b52df63fae1857a13d76980a9c8a224313bc059df2b48dab39d6544bb264a553ae" + }, + { + "address": "3Nbe9yDkaRH1JqHh5zxW5UiB1YhdkR8v2D", + "witness_version": 0, + "script_pubkey": "OP_HASH160 e55663e4f99ded9a9abad045114e57c1b1492d2e OP_EQUAL", + "redeem_script": "002071906db7ea26606fadee6a29801c0e59bfb0ad7b51100e06785bb2353b7406c8", + "witness_script": "522103e00a4efb7c7204acb601424876d41a8aad5f3c224d9e6842dd5f05c160d08b272102182e67775f970f112de01f15bf7e4bafa205da9ee00b17df762d34b3915e5a74210319a4e94cf3d1cda91ca6fc9d6d31d18ab723d6756146fab8241a45736a3609ca53ae" + }, + { + "address": "3MnKJ96TFjmPjAtRKjJt2ZTe7Kej5dwQMj", + "witness_version": 0, + "script_pubkey": "OP_HASH160 dc6318e1ded0c09b1997edd61b6933889a616e43 OP_EQUAL", + "redeem_script": "002053caa76ede92a9fd05af1c014e4dbfc6fec21c532242d91f75bed89da5fde835", + "witness_script": "522102f56b23d21e30e415ae99514dd809c8fff3716150507cb050b95b211f34c8daec2103415a7ad594d840817f79cb5fb807852628a62a8f0dbb59602ba5bf210d08ded72102bc2a24d0b24697f911cf7e69b3a104db4547e0472f10775417c200120d13080053ae" + }, + { + "address": "35qM2RgfB2YzECGFnVmm5xK5fiVmPMfp4k", + "witness_version": 0, + "script_pubkey": "OP_HASH160 2d73c6a11e0413d9fe44647102772e04d33adabc OP_EQUAL", + "redeem_script": "002081d117049ffe270a0eb22cb61c8ac50f8bc953d14670f64ce96c97d901417b01", + "witness_script": "522103f1c64eae18ecaa251e11c8536e82ac0a4a60d31ee5c1b95400d314a37a24d1532103aae1469e575f7c7ec0185ad9a9e32b8b1ceeddb463836444fdaa9536ef9f851d2102fdc932228cc23064560cfaca2dc320d94b40107b60f191303a84973e7e2eb7b053ae" + }, + { + "address": "3L5uvDatA1MemJdXzjD8v2UB9dwc1ZUgpz", + "witness_version": 0, + "script_pubkey": "OP_HASH160 c9c6d167c97ca3582d3e6ae2d0995b9dad2782c8 OP_EQUAL", + "redeem_script": "00205b655a8052dfd4aa3bcdaa41ddf7a8f2cf51703adb1685d08da1e16e638933b9", + "witness_script": "5221030fa7b3ab47da43337519cef0858a4864efa302b00b6b37033c748cf6fe0f126921039ade9c06f601a2a8f8ebbfdd812f43d17c7970b3e705de009403f5bd411a8f4a2102096c6dc22f51304bdce270905c70463b9d541ac545211194646ae6da4ca2233553ae" + }, + { + "address": "3853gHmiHvBvfAFjMesDeem8s7ecsP3k45", + "witness_version": 0, + "script_pubkey": "OP_HASH160 45fb5ec787ea3e974cde273c7c2192176881bf47 OP_EQUAL", + "redeem_script": "002080e30817e628eaabe12f92d30ead42ad7ac66b1f350389896b7e414e18522630", + "witness_script": "522103176835873f048e3a703aa9388482c62737a95c166f49e3c7bef93dab4d1814af2102473e2cf64a137985cdd72b058cb3ba0d4dfa29d75da6265a066753b80dc537c02102fb81d61df17a40f90e5e7b8d77ae0efc7538ea1bb63b39bfbb5a8aa810aa1f7053ae" + }, + { + "address": "3DMhXMtvQnJSKkuQkCiRyWSoqGx9rAjp6T", + "witness_version": 0, + "script_pubkey": "OP_HASH160 7ffa3f30865cdc7888867056235a5eff24cf1d46 OP_EQUAL", + "redeem_script": "00206d40eed476c3499f45ea15e7afb9c14a19d199b02ab56f2827909c69bd7af292", + "witness_script": "5221029d3edf8e864745375e508b433366003c62f445a20cde820e9c982fa39949f572210388ba559f3334e626b26abec00acd26144f168bf0d40c2520316987fdf37042282103e756984487e784a70148664443e572b554860ca68066d34fb85e6e9240edadc953ae" + }, + { + "address": "3BbWsLUjveyqCWKQ6KYQMy5nAA311TQHg6", + "witness_version": 0, + "script_pubkey": "OP_HASH160 6ca6ec840f3fa7c60244a35006d2773fbedfe3d9 OP_EQUAL", + "redeem_script": "0020549e1122c823d16d483d996c7057cad5878d60118c7e39368a03ad6dcbdac1d8", + "witness_script": "5221024eca19149b4647b9cd31deb0533c77d32f78029cc47f5a6a43291dea009574d72103d0f95e21c52e523c13802172e2542f9cf110e59de295fa6b876499befad0cca62103d27faefb802456751c2dbeccaf470814f9bddd0f4877854042fd0b67d48daa9f53ae" + }, + { + "address": "32e77BbHYjPp3m7gtZVWvL97BHTERM5GAu", + "witness_version": 0, + "script_pubkey": "OP_HASH160 0a6b346ef9cfffcabd0a5dd6e425dd715d147e70 OP_EQUAL", + "redeem_script": "0020f330e1afe3a20e707f243fa62be79939010aef64055e604270ab1e08e07dcebc", + "witness_script": "522102c0b57679848f763a7b3957ba98bd31c0dceec49ebc6e089e017b733114185a0121037ca9136a9c5f2d6deb4cd83a492ef93fcb503613c23236212c9b5bef34a4b2402102ebae4c0a57056ed496da8c987a0f34c876ff611471508b18996e87d50d59fd9253ae" + }, + { + "address": "3GMRzhghv7q6uTxmcpZPqWUQv1fvZF5n5o", + "witness_version": 0, + "script_pubkey": "OP_HASH160 a0d5a95c30ea69280b43cd623949fab081651d27 OP_EQUAL", + "redeem_script": "002071858f111a67398b24f05c429cb93a80c672c6de4c9d6a678ecbd981c88e51a4", + "witness_script": "5221029ab105eb813c7b52dc3c34034b86054cf021e97a0288863813edda550245987521031c113a4d86803a622d048ee01aa78ba61daaa87ab5b1dbceb2b11ff1928361c821038e9b0173bddff17f920a8c9bef65838b70be7280086be9ddd615c214e245129053ae" + }, + { + "address": "3P361kM4xNfATn7iaxCCxjBhxfcDdnS8pL", + "witness_version": 0, + "script_pubkey": "OP_HASH160 ea265fe7cd58d17e90a1399a0f001c92b11ad9ec OP_EQUAL", + "redeem_script": "002098dc4a02fff468cd1da423df36a8556d172326b8e197cfb8c8651a101379726d", + "witness_script": "5221023a5b8249ff033be6779a42996776a1fde7147caf913fd730516f946746868fe62102503ac1222d10f0c8bdd24c76b183db4f7b1087211525b652cfb350dfe7a085052103c7e464128c46cf682d88b6f2f7f213fb66f2ffb1ed4252fb96be9abbc4b2075853ae" + }, + { + "address": "35ZyBcWQALbJKmT9cBF2LUv9329NkTUkyc", + "witness_version": 0, + "script_pubkey": "OP_HASH160 2a8b4dd3ab3709526b10e7885bb0932d69904e4b OP_EQUAL", + "redeem_script": "0020a2ac5bca53bf38309d629adfb0793696ec874818c64cdf12d2062abdb63de470", + "witness_script": "522103225f5c1d685e5841e69aa0c5a75f570a869d9cf6f9a6cc4fe9df3808c45847e921039e12c752f565adb2255037574f30220ea55c17acdef5325fd7b2eed3abf69cb42103ad43eb43d3cdba4bc31f257a991203faa25f758d0c76d4ba2b9072328b233e2853ae" + }, + { + "address": "3JYrNMoPey2iyzFizRCno63xkEWNLoX1Go", + "witness_version": 0, + "script_pubkey": "OP_HASH160 b8eed5a84d633a291a25193f5534a6d6788948a7 OP_EQUAL", + "redeem_script": "0020e7561e734ffb7ebd958d89234a5c929af18d775de252405eac96a9bbe1e663ca", + "witness_script": "522103fd088ac1d1db47f5e6d3be2b7317340b81974fa0ef0ca6f1be1081034599090621021da581b4b3d4d04cfe7dcc88f6923ca2656b3fd5c5232c6b7585f8185443340821034e737a9a1105e697b117e14bad47f63b9bb006ad704bddf9bd519573d8a0f89853ae" + }, + { + "address": "3JfUTim7L8BEPdYTcuhAsQkxen5rXDmzLQ", + "witness_version": 0, + "script_pubkey": "OP_HASH160 ba2f7575380ab4a3c1e3222e7f64428c1a2f38c9 OP_EQUAL", + "redeem_script": "00200fda32f719b746bb7d505fb5fa422b7a6c0934b811751574e5cf1665c8d84575", + "witness_script": "52210225d3c55ba0aa861b590d59797e51825a0555c2bb528348809d8c7fe988eb400f2102ff22f1a71bd39a6d775648b2b6d0a48349c95f9d5a8c0d98a3d5ddd7f93ae22b21026d9e4c44e09f2d9b40bf7dc6138cafc4d5a5208eace2bc23e9d75b80f9ade34353ae" + }, + { + "address": "3EUD6LS9HgNWHu8nyv1kmCgzdCow9wpJJg", + "witness_version": 0, + "script_pubkey": "OP_HASH160 8c2d8d0b69505070a177666a56946ce9b3c80e84 OP_EQUAL", + "redeem_script": "0020c8ba1e281fc978918073a117a4c054abaf5d7fe53a7c8f8357ececd3a4b152be", + "witness_script": "522103fd6d6ef05158eccda278a24bc57a0a4d25b3df4bc87db7e3b5b279aeda13de472103f07fff9760ea0a5675f7fc9890c8e3df1ebac8bbf11224f331fb91f0bc36bfd421038f1b9bb16bce944e0cd20cb80ceac7c7989606c81572e3c0a4d298fd73df713753ae" + }, + { + "address": "35AMWTahqXfweSJih6RUEGJ2jfm9qNLaP9", + "witness_version": 0, + "script_pubkey": "OP_HASH160 2613f4f3b3975a76a0af7e2b20b2c8ae8ca98d34 OP_EQUAL", + "redeem_script": "00207e45c166288c67f679e51daaadc2e3b3e6d1f79e59f8f34267b142313cba2595", + "witness_script": "5221038f08f9350d1def2b1b0a7b70a1fde0e35ac3300f8d587402651054d4e8a88c37210376497a5fc5a9e19fcbc55eae8ff6ed780ec517a8d243ecc656125ec9b02bea2b210257d0f34ccbf4a1f2642a535bc4e8a2afd3a83a6fe8d4616f1f8505cfb48c05c253ae" + }, + { + "address": "3A87Q27zyGA8RwUtPsjMraa6NzVqwsqNiu", + "witness_version": 0, + "script_pubkey": "OP_HASH160 5c7ff97c74be53bec0343b97d9d94ef1c2aec48d OP_EQUAL", + "redeem_script": "0020338935022441855bd01848ed78c61996fc28b22efd5764dee9bf870fe6c23758", + "witness_script": "522103479b16baa5b851d71e34ae7e9e2793b0ac59c79fc77e81a668f03f24845a0fee210308fb8ce67580b1c03c01354cb2a7878464950da53e3d6e0e3ced64f59307fccb21030f9c9c9fe5b0b43a87b0f8d24a602109511fb30d432563c98b156efc0a680f8b53ae" + }, + { + "address": "3FSpJL8ge7jMpsLxCwCDzD96oQ59UzYNtB", + "witness_version": 0, + "script_pubkey": "OP_HASH160 96e23be5fcdf5dad77bf9ca7264b09aebca60c2b OP_EQUAL", + "redeem_script": "0020b7fb7e71dcfc243ee16dc9b111016eca9ea51c9937f851119fccddf16a5cf532", + "witness_script": "52210369dc6a3bafde195ec93fd0cdfb372014939eb037fb3c8a1d17af3f5c60afe14a2103cfd78af3990c2f79b8f3406582378644b18e5f40dd0217551f2c2bceb2000354210260d49683df6945d8f7ba0b2b11c90c131cd5da6d06afdc0c7d793f2d631db0ec53ae" + }, + { + "address": "35PcQNhWabYcsuM29UHTCwsdYzAG3aADQw", + "witness_version": 0, + "script_pubkey": "OP_HASH160 2895cc5395b568f8169e0361894b66a018b90937 OP_EQUAL", + "redeem_script": "00202038ea59caa617ac20e081882d915edc1ac26a0d164e0589e692950291dc0cc4", + "witness_script": "5221027f5ee87d5648dd227f5c5b5e03da2e54c9d45a987c1efb64bea168f9ef55aefa21035257f0a227f00851d1e83bbd8ccb37faf7dd4d146f2ff8edc111d5041abac5272102e8d0603375dcad40e54c9c6c25105fa43ff34c0ded7d94e50242491733e06a8b53ae" + }, + { + "address": "3GPmc543B3yuWcoUuDiqJGwf6aem23tQw7", + "witness_version": 0, + "script_pubkey": "OP_HASH160 a146dce88bf437c09f03d3c3c54b1dd90512ebd0 OP_EQUAL", + "redeem_script": "002089ad5db387f0c8517aab633db4c2397beb3f11cafcd2733d7c357d23d896b2ac", + "witness_script": "522102638b94c611a44dea3dcb0224043351813d8543feee55abfd55300cfac64a55222103c6aaccd66fd02bf8f04de2e3e3473df6839b1764c33b7a41a091a13649ba60cd2103bcbe986175bede39e361a4e543486da03dde98189fd3ef013ce880773f2065ea53ae" + }, + { + "address": "3MAcXcAyBdxJFxxgCipWLekp4N8zXzXDqL", + "witness_version": 0, + "script_pubkey": "OP_HASH160 d5a281dcf1b63c2026690d4e4047f8d734bbd175 OP_EQUAL", + "redeem_script": "0020869775de5ab556b36507133818464006f211f48a0c5f30c6ca789e47b0e96c9f", + "witness_script": "5221032bea507d47d38ac9fd547210acd48dca0c217ec1499a35c9fb7727fd29d7126a21036bc98eada5b83e17f7a8b5cf1eb0a116d67d722e27c907f87c7ed8581dc3c0742102f2364a63fe67dd711527b75e34b03f636dfed98be9a42f00d616556c0c4f1b8c53ae" + }, + { + "address": "3QwtxLf931y54YRXxX7qL9M5bSnQo3KDsy", + "witness_version": 0, + "script_pubkey": "OP_HASH160 ff1b50a39c6d55dbbb253e2e46bb55430815c226 OP_EQUAL", + "redeem_script": "00203c29aa12e41276511e664a07d28a9f1579a2bd3cf62c3aea58de5c4b70c90641", + "witness_script": "522102cf0d688964877bc67ef65587f26a31c1a3d3ae47fe63c7fd5e3448479f998131210304c5d09501e6d092f699bcea4497a7f8ea75e98c95a8a88725b92f5856c5c9a121033a6eeae9e9895d4d2db867f32e9128c8a0e4484db9abadf065ba781fc94c6a1653ae" + }, + { + "address": "3Ac6wSUizhX2kaXp3qoCheQMnydpdiru61", + "witness_version": 0, + "script_pubkey": "OP_HASH160 61cb3d2add46b59006082e3cc1e2109e608ac4a5 OP_EQUAL", + "redeem_script": "0020c981480780f9f7c9386d44fb690286710805fe82bee6a25231c9dded451cebda", + "witness_script": "52210355cb5d9a19d9831b7eecd87449bbf2d1062cbd93398818bdbcd9b31511a8306c210305f9471cc0cd4a9450df03357730539de24a0060c214ab4a181abd30bbcd708421032e47732903fde84bc0cb79b20322808e2b98f7c405fa97f5e9c692814fddc8e653ae" + }, + { + "address": "32hYNLaP7BfMw2YeaEwhAR59koi6mjkmp8", + "witness_version": 0, + "script_pubkey": "OP_HASH160 0b118a2e3a5e7dadaff48f10cae0889f0479075e OP_EQUAL", + "redeem_script": "0020bbe1bbd58ddec66007115168bfe1310675afe3472f6964768a9d20881572d907", + "witness_script": "52210369d9d455dd16e907da6fdbc2e55991b66397d2d7cad584607488af51b90a76cb21023eaa5750779b2a008d63a42b6bda4dc1f610ba1f63dfdba712f77021dff4ea172103d414ab0f0a504760196e1f34ffbda829984ab59cf69f137912897e8574e3765b53ae" + }, + { + "address": "3DJcJEmxmo96jpuwxr2Qvyuk9VBidRFTvh", + "witness_version": 0, + "script_pubkey": "OP_HASH160 7f64a2e3fc80570aefda64c15241bb2a5d5aca77 OP_EQUAL", + "redeem_script": "0020275800a807a60e23e7068390300b98914210bcde48d2222d8f24be10c18e7572", + "witness_script": "522102fc774a69a8ba0cec7b8d4c4a4ff1c391f669735f99f7f0df8caee6dd89cf63bd21039136c85728c345a411ab3e68c6e31a488920cff0babbfde3c17384cc4ddd095621035148e06178aba0f0d3ff9938995d50fc276773e53d6e69f4899b4958d22e181353ae" + }, + { + "address": "32dYRMNDCfuC6BGkFLm82PehfHwbYoe1ts", + "witness_version": 0, + "script_pubkey": "OP_HASH160 0a4feb56d8edb378746cd33526d3c204f7482acb OP_EQUAL", + "redeem_script": "0020d838f7c0caa805f8d3cd584810df40a6f0dd89300dafbb830049d5290b83c24f", + "witness_script": "522102ad48cc89001bb5cbfed89cee7cfde6ebd49f6b4153a36bb9068f7dc05a1aa456210310ff35226fcb093512acd92dda218ca99d0dcfce7c9ccf00cf6e8aac67b21c3721029ce66d7d82caad8b909776f8902b6f21c4b96dd3e65cd38d84fb6f256b03303553ae" + }, + { + "address": "3MQeAxTUtAewLVeGiaCYp2cE3qmJBeaNeN", + "witness_version": 0, + "script_pubkey": "OP_HASH160 d849b3fb39b6f8d5c2ac17789c5a6b6cc0b06c1f OP_EQUAL", + "redeem_script": "00203c1f5403ec168998aee7fafcad25cf7f4f548a4b2021c5cb5dcd485bd71d3568", + "witness_script": "522103165bc9cd4cf0b77b2b4e00945564e6ade8750f4a13fd3b825949218817b67602210278d5c5f3975e9dcb4160132d0c3bd1a4d77a52e95851010a58cfa185d7780e4221024ab85181f09ca97d8bb13a2ec5b259abe7bf2b7361171511b19f2667f9638c1553ae" + }, + { + "address": "3Kwp6Yb2D5t4QcqtkvqsoU2enNJRijDxWR", + "witness_version": 0, + "script_pubkey": "OP_HASH160 c83ea1610a1a1b0dd82af0d489c385b8bb40a278 OP_EQUAL", + "redeem_script": "0020224cb19bf24d8d4dc12f915bd5582c05cd743baf0671816ac462ba9779ff7e51", + "witness_script": "522102859db451b37394e120580de34cd8e28de7848641c796395d5e9320470a15d395210347f9eddcf1fee9f5ea0fc439a837db2a3fa4bd729cba5cbc8ea15b1c7840823a21030c444677911ecf0b42e680478f91e7a04df88a0cf6a387f38f265cee47aa675b53ae" + }, + { + "address": "3NC23HVF5foiQvCf6crAJcpHhaj8uWzvqB", + "witness_version": 0, + "script_pubkey": "OP_HASH160 e0deacf38975010305cd3349ad4e919ddd79fb70 OP_EQUAL", + "redeem_script": "002055755bc7c5db79b742300e4df2c77ca9ec8c3ec8100616ec56ac257218e85995", + "witness_script": "52210258979175f5e853b60342d96c0c9d5519b6ec15db67938a2ebd2699495f2070e62103e8c0c134f0c76c596ee868167d56bd567281bd0d75d3d8ae68e11e0b8f4a0bf321028bb58290b6c85bb09847ec39611d0203f5bed65aac29246c43597c924250404753ae" + }, + { + "address": "31kvEd63Bkbm1RnRjzWr1hfd1yWZstLzCL", + "witness_version": 0, + "script_pubkey": "OP_HASH160 00bcea5e6431c8db93038c6dce2dc6879d3da1f9 OP_EQUAL", + "redeem_script": "002028d195d8735d605150f5ee93ac6c06fe02bc2c0a29b91fbc6941c1226d74ee66", + "witness_script": "522102b67063700aff5c137dd6b80688d0a6fa93edf8756f364de920d7ef3cf5a058e5210220a6e4fe1a7707a925a2b0f0a8b672e0baafe75e86ab7f969201beb001f35b502103c0117e25a9dc1c739adca762ddd99ebfa12970cfb2c5bf3a65beff00e7dcbc9853ae" + }, + { + "address": "34H6QoBcPq2zK7AzZpbRe5mSGSTmAtksje", + "witness_version": 0, + "script_pubkey": "OP_HASH160 1c6223d2a1845ca6c3a2776bda39a0fe1c23a6a1 OP_EQUAL", + "redeem_script": "002063f0c1e3b7e0984642962818147168b0398f39d55d8b251030a42f8f5d098fe5", + "witness_script": "52210377448d967f270533b7e2d0b500f5986f3bc3f106cfdc5dcf9b2b0391935d341521034e793551b19f6920e0cdd331beb9acb7c663c34f287417785036c34edd83dec52102552d45effc88dd47fb98af42b6c0eb50d19a084e3d9a93bde9a010bd34ac5d0f53ae" + }, + { + "address": "39RQCY6mM8BCG469EcA2NJc297sp74agE5", + "witness_version": 0, + "script_pubkey": "OP_HASH160 54ccf21bdf4c9b66c01c3fd7dc93d99a9ee3848e OP_EQUAL", + "redeem_script": "0020ebd3475a96d3e44ab8c91dce6c3ef7b7e54ef79caecb9d986dea4cfeb2556fd3", + "witness_script": "5221034a7cae543ce4410340b66b3a9e7928524ced90833a2863cae00f7c65de63264d2102633769db6ac4b593483e37b189c8600f48e8a711319e6b0f428beecf49b1631721024c49c69df7b6c2194ac00ec5f745ac49a7e391f68bc0dd868cb9e0c811e87c1653ae" + }, + { + "address": "3Q3AVAS1FyBHvcybpv1v2c37ej8TBRdCKd", + "witness_version": 0, + "script_pubkey": "OP_HASH160 f5223be88d315909817c529d771a5c75234a8ada OP_EQUAL", + "redeem_script": "00202e0c92d40f9ddae6d9e71003d55dcc37fdd25722fc488250523759c7901c2854", + "witness_script": "522102adeddbb6d5538cce5c3e928645b5ef151cd0428b1bdaccf714bbd4ba1cca87432102d0613570a9f8bfce0485361c503addc163f922bb9bba2ed66685e3bbe33f15fa21035a9f10c868a2e8d4438136c0b6884778907327e4506d8f4340fd99a2798d801753ae" + }, + { + "address": "33JMtT9o19qNtH2iyVUhqDUFbDQnR1bxc3", + "witness_version": 0, + "script_pubkey": "OP_HASH160 11a759789397115c29bfece9906356cb577397c6 OP_EQUAL", + "redeem_script": "002059217b76388c413350792c6dbc27fa0ffaa4302fc9e004a28915fe4ee07d32b7", + "witness_script": "5221031e9418fcba24ae5fbcd728ee47a05a11e4805bb2517553bf60a69818e446172f21022dee1d0b6d14411a78695cb3c8179914174b73d426c995100028165490929bf22103428d46a9a71cd381efd00b5dcc29a043de3023549be9b9bd7601f9448af0163a53ae" + }, + { + "address": "3EMi6NZEcJLWsNuY8DUjtiZUTU5Tkn1qfr", + "witness_version": 0, + "script_pubkey": "OP_HASH160 8af2d904db960cad59bb9eb15b5e3124d871f642 OP_EQUAL", + "redeem_script": "0020877e7595cac945283951059dca9c202236dc46dfcf0610a06ee2a77a86c60837", + "witness_script": "5221021fae6c440e5121e7d5a93eb97254ffa3e2daae7c347c91e26ba8dfa351ef7a76210313c675e42e29057c05d4d96e29dfebaeebeac19ddb8e67c98285c3f8fadac3c421027083ec2e998327d609fa2a1d49b26c106739e3612d5990f7622e5e537482d06b53ae" + }, + { + "address": "3Kwp6Yb2D5t4QcqtkvqsoU2enNJRijDxWR", + "witness_version": 0, + "script_pubkey": "OP_HASH160 c83ea1610a1a1b0dd82af0d489c385b8bb40a278 OP_EQUAL", + "redeem_script": "0020224cb19bf24d8d4dc12f915bd5582c05cd743baf0671816ac462ba9779ff7e51", + "witness_script": "522102859db451b37394e120580de34cd8e28de7848641c796395d5e9320470a15d395210347f9eddcf1fee9f5ea0fc439a837db2a3fa4bd729cba5cbc8ea15b1c7840823a21030c444677911ecf0b42e680478f91e7a04df88a0cf6a387f38f265cee47aa675b53ae" + }, + { + "address": "3NC23HVF5foiQvCf6crAJcpHhaj8uWzvqB", + "witness_version": 0, + "script_pubkey": "OP_HASH160 e0deacf38975010305cd3349ad4e919ddd79fb70 OP_EQUAL", + "redeem_script": "002055755bc7c5db79b742300e4df2c77ca9ec8c3ec8100616ec56ac257218e85995", + "witness_script": "52210258979175f5e853b60342d96c0c9d5519b6ec15db67938a2ebd2699495f2070e62103e8c0c134f0c76c596ee868167d56bd567281bd0d75d3d8ae68e11e0b8f4a0bf321028bb58290b6c85bb09847ec39611d0203f5bed65aac29246c43597c924250404753ae" + }, + { + "address": "31kvEd63Bkbm1RnRjzWr1hfd1yWZstLzCL", + "witness_version": 0, + "script_pubkey": "OP_HASH160 00bcea5e6431c8db93038c6dce2dc6879d3da1f9 OP_EQUAL", + "redeem_script": "002028d195d8735d605150f5ee93ac6c06fe02bc2c0a29b91fbc6941c1226d74ee66", + "witness_script": "522102b67063700aff5c137dd6b80688d0a6fa93edf8756f364de920d7ef3cf5a058e5210220a6e4fe1a7707a925a2b0f0a8b672e0baafe75e86ab7f969201beb001f35b502103c0117e25a9dc1c739adca762ddd99ebfa12970cfb2c5bf3a65beff00e7dcbc9853ae" + }, + { + "address": "34H6QoBcPq2zK7AzZpbRe5mSGSTmAtksje", + "witness_version": 0, + "script_pubkey": "OP_HASH160 1c6223d2a1845ca6c3a2776bda39a0fe1c23a6a1 OP_EQUAL", + "redeem_script": "002063f0c1e3b7e0984642962818147168b0398f39d55d8b251030a42f8f5d098fe5", + "witness_script": "52210377448d967f270533b7e2d0b500f5986f3bc3f106cfdc5dcf9b2b0391935d341521034e793551b19f6920e0cdd331beb9acb7c663c34f287417785036c34edd83dec52102552d45effc88dd47fb98af42b6c0eb50d19a084e3d9a93bde9a010bd34ac5d0f53ae" + }, + { + "address": "39RQCY6mM8BCG469EcA2NJc297sp74agE5", + "witness_version": 0, + "script_pubkey": "OP_HASH160 54ccf21bdf4c9b66c01c3fd7dc93d99a9ee3848e OP_EQUAL", + "redeem_script": "0020ebd3475a96d3e44ab8c91dce6c3ef7b7e54ef79caecb9d986dea4cfeb2556fd3", + "witness_script": "5221034a7cae543ce4410340b66b3a9e7928524ced90833a2863cae00f7c65de63264d2102633769db6ac4b593483e37b189c8600f48e8a711319e6b0f428beecf49b1631721024c49c69df7b6c2194ac00ec5f745ac49a7e391f68bc0dd868cb9e0c811e87c1653ae" + }, + { + "address": "3Q3AVAS1FyBHvcybpv1v2c37ej8TBRdCKd", + "witness_version": 0, + "script_pubkey": "OP_HASH160 f5223be88d315909817c529d771a5c75234a8ada OP_EQUAL", + "redeem_script": "00202e0c92d40f9ddae6d9e71003d55dcc37fdd25722fc488250523759c7901c2854", + "witness_script": "522102adeddbb6d5538cce5c3e928645b5ef151cd0428b1bdaccf714bbd4ba1cca87432102d0613570a9f8bfce0485361c503addc163f922bb9bba2ed66685e3bbe33f15fa21035a9f10c868a2e8d4438136c0b6884778907327e4506d8f4340fd99a2798d801753ae" + }, + { + "address": "33JMtT9o19qNtH2iyVUhqDUFbDQnR1bxc3", + "witness_version": 0, + "script_pubkey": "OP_HASH160 11a759789397115c29bfece9906356cb577397c6 OP_EQUAL", + "redeem_script": "002059217b76388c413350792c6dbc27fa0ffaa4302fc9e004a28915fe4ee07d32b7", + "witness_script": "5221031e9418fcba24ae5fbcd728ee47a05a11e4805bb2517553bf60a69818e446172f21022dee1d0b6d14411a78695cb3c8179914174b73d426c995100028165490929bf22103428d46a9a71cd381efd00b5dcc29a043de3023549be9b9bd7601f9448af0163a53ae" + }, + { + "address": "3EMi6NZEcJLWsNuY8DUjtiZUTU5Tkn1qfr", + "witness_version": 0, + "script_pubkey": "OP_HASH160 8af2d904db960cad59bb9eb15b5e3124d871f642 OP_EQUAL", + "redeem_script": "0020877e7595cac945283951059dca9c202236dc46dfcf0610a06ee2a77a86c60837", + "witness_script": "5221021fae6c440e5121e7d5a93eb97254ffa3e2daae7c347c91e26ba8dfa351ef7a76210313c675e42e29057c05d4d96e29dfebaeebeac19ddb8e67c98285c3f8fadac3c421027083ec2e998327d609fa2a1d49b26c106739e3612d5990f7622e5e537482d06b53ae" + }, + { + "address": "35PcQNhWabYcsuM29UHTCwsdYzAG3aADQw", + "witness_version": 0, + "script_pubkey": "OP_HASH160 2895cc5395b568f8169e0361894b66a018b90937 OP_EQUAL", + "redeem_script": "00202038ea59caa617ac20e081882d915edc1ac26a0d164e0589e692950291dc0cc4", + "witness_script": "5221027f5ee87d5648dd227f5c5b5e03da2e54c9d45a987c1efb64bea168f9ef55aefa21035257f0a227f00851d1e83bbd8ccb37faf7dd4d146f2ff8edc111d5041abac5272102e8d0603375dcad40e54c9c6c25105fa43ff34c0ded7d94e50242491733e06a8b53ae" + }, + { + "address": "3GPmc543B3yuWcoUuDiqJGwf6aem23tQw7", + "witness_version": 0, + "script_pubkey": "OP_HASH160 a146dce88bf437c09f03d3c3c54b1dd90512ebd0 OP_EQUAL", + "redeem_script": "002089ad5db387f0c8517aab633db4c2397beb3f11cafcd2733d7c357d23d896b2ac", + "witness_script": "522102638b94c611a44dea3dcb0224043351813d8543feee55abfd55300cfac64a55222103c6aaccd66fd02bf8f04de2e3e3473df6839b1764c33b7a41a091a13649ba60cd2103bcbe986175bede39e361a4e543486da03dde98189fd3ef013ce880773f2065ea53ae" + }, + { + "address": "3MAcXcAyBdxJFxxgCipWLekp4N8zXzXDqL", + "witness_version": 0, + "script_pubkey": "OP_HASH160 d5a281dcf1b63c2026690d4e4047f8d734bbd175 OP_EQUAL", + "redeem_script": "0020869775de5ab556b36507133818464006f211f48a0c5f30c6ca789e47b0e96c9f", + "witness_script": "5221032bea507d47d38ac9fd547210acd48dca0c217ec1499a35c9fb7727fd29d7126a21036bc98eada5b83e17f7a8b5cf1eb0a116d67d722e27c907f87c7ed8581dc3c0742102f2364a63fe67dd711527b75e34b03f636dfed98be9a42f00d616556c0c4f1b8c53ae" + }, + { + "address": "3QwtxLf931y54YRXxX7qL9M5bSnQo3KDsy", + "witness_version": 0, + "script_pubkey": "OP_HASH160 ff1b50a39c6d55dbbb253e2e46bb55430815c226 OP_EQUAL", + "redeem_script": "00203c29aa12e41276511e664a07d28a9f1579a2bd3cf62c3aea58de5c4b70c90641", + "witness_script": "522102cf0d688964877bc67ef65587f26a31c1a3d3ae47fe63c7fd5e3448479f998131210304c5d09501e6d092f699bcea4497a7f8ea75e98c95a8a88725b92f5856c5c9a121033a6eeae9e9895d4d2db867f32e9128c8a0e4484db9abadf065ba781fc94c6a1653ae" + }, + { + "address": "3Ac6wSUizhX2kaXp3qoCheQMnydpdiru61", + "witness_version": 0, + "script_pubkey": "OP_HASH160 61cb3d2add46b59006082e3cc1e2109e608ac4a5 OP_EQUAL", + "redeem_script": "0020c981480780f9f7c9386d44fb690286710805fe82bee6a25231c9dded451cebda", + "witness_script": "52210355cb5d9a19d9831b7eecd87449bbf2d1062cbd93398818bdbcd9b31511a8306c210305f9471cc0cd4a9450df03357730539de24a0060c214ab4a181abd30bbcd708421032e47732903fde84bc0cb79b20322808e2b98f7c405fa97f5e9c692814fddc8e653ae" + }, + { + "address": "32hYNLaP7BfMw2YeaEwhAR59koi6mjkmp8", + "witness_version": 0, + "script_pubkey": "OP_HASH160 0b118a2e3a5e7dadaff48f10cae0889f0479075e OP_EQUAL", + "redeem_script": "0020bbe1bbd58ddec66007115168bfe1310675afe3472f6964768a9d20881572d907", + "witness_script": "52210369d9d455dd16e907da6fdbc2e55991b66397d2d7cad584607488af51b90a76cb21023eaa5750779b2a008d63a42b6bda4dc1f610ba1f63dfdba712f77021dff4ea172103d414ab0f0a504760196e1f34ffbda829984ab59cf69f137912897e8574e3765b53ae" + }, + { + "address": "3DJcJEmxmo96jpuwxr2Qvyuk9VBidRFTvh", + "witness_version": 0, + "script_pubkey": "OP_HASH160 7f64a2e3fc80570aefda64c15241bb2a5d5aca77 OP_EQUAL", + "redeem_script": "0020275800a807a60e23e7068390300b98914210bcde48d2222d8f24be10c18e7572", + "witness_script": "522102fc774a69a8ba0cec7b8d4c4a4ff1c391f669735f99f7f0df8caee6dd89cf63bd21039136c85728c345a411ab3e68c6e31a488920cff0babbfde3c17384cc4ddd095621035148e06178aba0f0d3ff9938995d50fc276773e53d6e69f4899b4958d22e181353ae" + }, + { + "address": "32dYRMNDCfuC6BGkFLm82PehfHwbYoe1ts", + "witness_version": 0, + "script_pubkey": "OP_HASH160 0a4feb56d8edb378746cd33526d3c204f7482acb OP_EQUAL", + "redeem_script": "0020d838f7c0caa805f8d3cd584810df40a6f0dd89300dafbb830049d5290b83c24f", + "witness_script": "522102ad48cc89001bb5cbfed89cee7cfde6ebd49f6b4153a36bb9068f7dc05a1aa456210310ff35226fcb093512acd92dda218ca99d0dcfce7c9ccf00cf6e8aac67b21c3721029ce66d7d82caad8b909776f8902b6f21c4b96dd3e65cd38d84fb6f256b03303553ae" + }, + { + "address": "3MQeAxTUtAewLVeGiaCYp2cE3qmJBeaNeN", + "witness_version": 0, + "script_pubkey": "OP_HASH160 d849b3fb39b6f8d5c2ac17789c5a6b6cc0b06c1f OP_EQUAL", + "redeem_script": "00203c1f5403ec168998aee7fafcad25cf7f4f548a4b2021c5cb5dcd485bd71d3568", + "witness_script": "522103165bc9cd4cf0b77b2b4e00945564e6ade8750f4a13fd3b825949218817b67602210278d5c5f3975e9dcb4160132d0c3bd1a4d77a52e95851010a58cfa185d7780e4221024ab85181f09ca97d8bb13a2ec5b259abe7bf2b7361171511b19f2667f9638c1553ae" + }, + { + "address": "3Kwp6Yb2D5t4QcqtkvqsoU2enNJRijDxWR", + "witness_version": 0, + "script_pubkey": "OP_HASH160 c83ea1610a1a1b0dd82af0d489c385b8bb40a278 OP_EQUAL", + "redeem_script": "0020224cb19bf24d8d4dc12f915bd5582c05cd743baf0671816ac462ba9779ff7e51", + "witness_script": "522102859db451b37394e120580de34cd8e28de7848641c796395d5e9320470a15d395210347f9eddcf1fee9f5ea0fc439a837db2a3fa4bd729cba5cbc8ea15b1c7840823a21030c444677911ecf0b42e680478f91e7a04df88a0cf6a387f38f265cee47aa675b53ae" + }, + { + "address": "3NC23HVF5foiQvCf6crAJcpHhaj8uWzvqB", + "witness_version": 0, + "script_pubkey": "OP_HASH160 e0deacf38975010305cd3349ad4e919ddd79fb70 OP_EQUAL", + "redeem_script": "002055755bc7c5db79b742300e4df2c77ca9ec8c3ec8100616ec56ac257218e85995", + "witness_script": "52210258979175f5e853b60342d96c0c9d5519b6ec15db67938a2ebd2699495f2070e62103e8c0c134f0c76c596ee868167d56bd567281bd0d75d3d8ae68e11e0b8f4a0bf321028bb58290b6c85bb09847ec39611d0203f5bed65aac29246c43597c924250404753ae" + }, + { + "address": "31kvEd63Bkbm1RnRjzWr1hfd1yWZstLzCL", + "witness_version": 0, + "script_pubkey": "OP_HASH160 00bcea5e6431c8db93038c6dce2dc6879d3da1f9 OP_EQUAL", + "redeem_script": "002028d195d8735d605150f5ee93ac6c06fe02bc2c0a29b91fbc6941c1226d74ee66", + "witness_script": "522102b67063700aff5c137dd6b80688d0a6fa93edf8756f364de920d7ef3cf5a058e5210220a6e4fe1a7707a925a2b0f0a8b672e0baafe75e86ab7f969201beb001f35b502103c0117e25a9dc1c739adca762ddd99ebfa12970cfb2c5bf3a65beff00e7dcbc9853ae" + }, + { + "address": "34H6QoBcPq2zK7AzZpbRe5mSGSTmAtksje", + "witness_version": 0, + "script_pubkey": "OP_HASH160 1c6223d2a1845ca6c3a2776bda39a0fe1c23a6a1 OP_EQUAL", + "redeem_script": "002063f0c1e3b7e0984642962818147168b0398f39d55d8b251030a42f8f5d098fe5", + "witness_script": "52210377448d967f270533b7e2d0b500f5986f3bc3f106cfdc5dcf9b2b0391935d341521034e793551b19f6920e0cdd331beb9acb7c663c34f287417785036c34edd83dec52102552d45effc88dd47fb98af42b6c0eb50d19a084e3d9a93bde9a010bd34ac5d0f53ae" + }, + { + "address": "39RQCY6mM8BCG469EcA2NJc297sp74agE5", + "witness_version": 0, + "script_pubkey": "OP_HASH160 54ccf21bdf4c9b66c01c3fd7dc93d99a9ee3848e OP_EQUAL", + "redeem_script": "0020ebd3475a96d3e44ab8c91dce6c3ef7b7e54ef79caecb9d986dea4cfeb2556fd3", + "witness_script": "5221034a7cae543ce4410340b66b3a9e7928524ced90833a2863cae00f7c65de63264d2102633769db6ac4b593483e37b189c8600f48e8a711319e6b0f428beecf49b1631721024c49c69df7b6c2194ac00ec5f745ac49a7e391f68bc0dd868cb9e0c811e87c1653ae" + }, + { + "address": "3Q3AVAS1FyBHvcybpv1v2c37ej8TBRdCKd", + "witness_version": 0, + "script_pubkey": "OP_HASH160 f5223be88d315909817c529d771a5c75234a8ada OP_EQUAL", + "redeem_script": "00202e0c92d40f9ddae6d9e71003d55dcc37fdd25722fc488250523759c7901c2854", + "witness_script": "522102adeddbb6d5538cce5c3e928645b5ef151cd0428b1bdaccf714bbd4ba1cca87432102d0613570a9f8bfce0485361c503addc163f922bb9bba2ed66685e3bbe33f15fa21035a9f10c868a2e8d4438136c0b6884778907327e4506d8f4340fd99a2798d801753ae" + }, + { + "address": "33JMtT9o19qNtH2iyVUhqDUFbDQnR1bxc3", + "witness_version": 0, + "script_pubkey": "OP_HASH160 11a759789397115c29bfece9906356cb577397c6 OP_EQUAL", + "redeem_script": "002059217b76388c413350792c6dbc27fa0ffaa4302fc9e004a28915fe4ee07d32b7", + "witness_script": "5221031e9418fcba24ae5fbcd728ee47a05a11e4805bb2517553bf60a69818e446172f21022dee1d0b6d14411a78695cb3c8179914174b73d426c995100028165490929bf22103428d46a9a71cd381efd00b5dcc29a043de3023549be9b9bd7601f9448af0163a53ae" + }, + { + "address": "3EMi6NZEcJLWsNuY8DUjtiZUTU5Tkn1qfr", + "witness_version": 0, + "script_pubkey": "OP_HASH160 8af2d904db960cad59bb9eb15b5e3124d871f642 OP_EQUAL", + "redeem_script": "0020877e7595cac945283951059dca9c202236dc46dfcf0610a06ee2a77a86c60837", + "witness_script": "5221021fae6c440e5121e7d5a93eb97254ffa3e2daae7c347c91e26ba8dfa351ef7a76210313c675e42e29057c05d4d96e29dfebaeebeac19ddb8e67c98285c3f8fadac3c421027083ec2e998327d609fa2a1d49b26c106739e3612d5990f7622e5e537482d06b53ae" + }, + { + "address": "3EMCrEkACMYFtEhbS7jCkXwFAAawghxtDe", + "witness_version": 0, + "script_pubkey": "OP_HASH160 8ada6fb2854716c8affc6b1be1836d79ade89669 OP_EQUAL", + "redeem_script": "0020851b3b8df0e5ad8856298346775ea70c13c8b7b89ddc7328e8efb5ee314ec325", + "witness_script": "522102e16480952138d1036079652a9fd004916355f3e7eb7d9ff130ec799385d204aa21033e8cab033ca692adbca8d4f02b13eebbe5322a6c5de91a5588222cc39e5803722102afc5b63ba53acce03bdfbedb322dbece2b75ed15b5e17ce5879b58b33140afcb53ae" + }, + { + "address": "37N9yEcRQn1jLmxtqVub6NR2625Q1pRkBm", + "witness_version": 0, + "script_pubkey": "OP_HASH160 3e3f9177b3d50414bf5b816187ca32a60cde0af1 OP_EQUAL", + "redeem_script": "0020f95db34a02b49cba7e26e29fa24cf8dc6cdfa149e9c6410876cb3765ab3a3fa6", + "witness_script": "5221025da092047f474e1b04c8166618aa5f68eca58cc981f1c05fa856bc7c71ac02592103fbe589f79e6ebb62414f350e735d885657cefb955a264eabca0734173f3a21a021037de8d4b5340af451468b32fd685dc9c4e28561db136947035c776d02ee63b9b553ae" + }, + { + "address": "3K2AHpJN6HbDhMfTzAU3WxCtBRreQiA8PZ", + "witness_version": 0, + "script_pubkey": "OP_HASH160 be19069c73f65e3436cc1f7c8f2d1f632b864afc OP_EQUAL", + "redeem_script": "0020d8e3b0bf29a4c9896cbf67e3bf99b818eafec53d9a0f1bb5ab9e845786a0a5fc", + "witness_script": "52210224f4430bfa314b2effb42a28691dfd71d0d8ec62dd75536443a0a6859f0b2419210323ea6a85e5b3f97a66a7dce0489b7fdaa2ef1fbb34a0c5f602da2bd91b18e9b12103d45b6e98520460457b8dabad86982491c0a3ba56b94d5c87249af0096fccc19f53ae" + }, + { + "address": "393utvxhwEzhUq9gbN1SnDnJuVpLtap36N", + "witness_version": 0, + "script_pubkey": "OP_HASH160 50bc957c42bd97ece094687559b4d77948fd105b OP_EQUAL", + "redeem_script": "00202f6f7a2b364b21a2cce5d0233cbce0c749792fc647dd6b61b766ee9ce62f6ca4", + "witness_script": "522102e3c23eec81ab0be597e308c7c3a39fbf3271210a013033f1ff84d0c02fe00cec21039580d0a0e3bb466157c52ecd47d2752a5730bac19e744436e61c9eab90701305210322845046d9751c258f31a53dbaf5296524bf39362c8d1c2493a3b88e0a7b1d8753ae" + } +] diff --git a/tests/data/wif.json b/tests/data/wif.json index 6cd7e8e..4090aa9 100644 --- a/tests/data/wif.json +++ b/tests/data/wif.json @@ -3,52 +3,60 @@ "hex": "0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d", "compressed": false, "mainnet": true}, - {"hex": "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35", - "wif": "L52XzL2cMkHxqxBXRyEpnPQZGUs3uKiL3R11XbAdHigRzDozKZeW", - "compressed": true, - "mainnet": true}, - {"hex": "edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea", - "wif": "L5BmPijJjrKbiUfG4zbiFKNqkvuJ8usooJmzuD7Z8dkRoTThYnAT", - "compressed": true, - "mainnet": true}, - {"hex": "3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368", - "wif": "KyFAjQ5rgrKvhXvNMtFB5PCSKUYD1yyPEe3xr3T34TZSUHycXtMM", - "compressed": true, - "mainnet": true}, - {"hex": "cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca", - "wif": "L43t3od1Gh7Lj55Bzjj1xDAgJDcL7YFo2nEcNaMGiyRZS1CidBVU", - "compressed": true, - "mainnet": true}, - {"hex": "0f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4", - "wif": "KwjQsVuMjbCP2Zmr3VaFaStav7NvevwjvvkqrWd5Qmh1XVnCteBR", - "compressed": true, - "mainnet": true}, - {"hex": "471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8", - "wif": "Kybw8izYevo5xMh1TK7aUr7jHFCxXS1zv8p3oqFz3o2zFbhRXHYs", - "compressed": true, - "mainnet": true}, - {"hex": "4b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e", - "wif": "KyjXhyHF9wTphBkfpxjL8hkDXDUSbE3tKANT94kXSyh6vn6nKaoy", - "compressed": true, - "mainnet": true}, - {"hex": "abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e", - "wif": "L2ysLrR6KMSAtx7uPqmYpoTeiRzydXBattRXjXz5GDFPrdfPzKbj", - "compressed": true, - "mainnet": true}, - {"hex": "877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93", - "wif": "L1m5VpbXmMp57P3knskwhoMTLdhAAaXiHvnGLMribbfwzVRpz2Sr", - "compressed": true, - "mainnet": true}, - {"hex": "704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7", - "wif": "KzyzXnznxSv249b4KuNkBwowaN3akiNeEHy5FWoPCJpStZbEKXN2", - "compressed": true, - "mainnet": true}, - {"hex": "f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d", - "wif": "L5KhaMvPYRW1ZoFmRjUtxxPypQ94m6BcDrPhqArhggdaTbbAFJEF", - "compressed": true, - "mainnet": true}, - {"hex": "bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23", - "wif": "L3WAYNAZPxx1fr7KCz7GN9nD5qMBnNiqEJNJMU1z9MMaannAt4aK", - "compressed": true, - "mainnet": true} -] \ No newline at end of file + {"hex": "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35", + "wif": "L52XzL2cMkHxqxBXRyEpnPQZGUs3uKiL3R11XbAdHigRzDozKZeW", + "compressed": true, + "mainnet": true}, + {"hex": "edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea", + "wif": "L5BmPijJjrKbiUfG4zbiFKNqkvuJ8usooJmzuD7Z8dkRoTThYnAT", + "compressed": true, + "mainnet": true}, + {"hex": "3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368", + "wif": "KyFAjQ5rgrKvhXvNMtFB5PCSKUYD1yyPEe3xr3T34TZSUHycXtMM", + "compressed": true, + "mainnet": true}, + {"hex": "cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca", + "wif": "L43t3od1Gh7Lj55Bzjj1xDAgJDcL7YFo2nEcNaMGiyRZS1CidBVU", + "compressed": true, + "mainnet": true}, + {"hex": "0f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4", + "wif": "KwjQsVuMjbCP2Zmr3VaFaStav7NvevwjvvkqrWd5Qmh1XVnCteBR", + "compressed": true, + "mainnet": true}, + {"hex": "471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8", + "wif": "Kybw8izYevo5xMh1TK7aUr7jHFCxXS1zv8p3oqFz3o2zFbhRXHYs", + "compressed": true, + "mainnet": true}, + {"hex": "4b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e", + "wif": "KyjXhyHF9wTphBkfpxjL8hkDXDUSbE3tKANT94kXSyh6vn6nKaoy", + "compressed": true, + "mainnet": true}, + {"hex": "abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e", + "wif": "L2ysLrR6KMSAtx7uPqmYpoTeiRzydXBattRXjXz5GDFPrdfPzKbj", + "compressed": true, + "mainnet": true}, + {"hex": "877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93", + "wif": "L1m5VpbXmMp57P3knskwhoMTLdhAAaXiHvnGLMribbfwzVRpz2Sr", + "compressed": true, + "mainnet": true}, + {"hex": "704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7", + "wif": "KzyzXnznxSv249b4KuNkBwowaN3akiNeEHy5FWoPCJpStZbEKXN2", + "compressed": true, + "mainnet": true}, + {"hex": "f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d", + "wif": "L5KhaMvPYRW1ZoFmRjUtxxPypQ94m6BcDrPhqArhggdaTbbAFJEF", + "compressed": true, + "mainnet": true}, + {"hex": "bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23", + "wif": "L3WAYNAZPxx1fr7KCz7GN9nD5qMBnNiqEJNJMU1z9MMaannAt4aK", + "compressed": true, + "mainnet": true}, + {"hex": "6c4313b03f2e7324d75e642f0ab81b734b724e13fec930f309e222470236d66b", + "wif": "cRD9b1m3vQxmwmjSoJy7Mj56f4uNFXjcWMCzpQCEmHASS4edEwXv", + "compressed": true, + "mainnet": false}, + {"hex": "6c4313b03f2e7324d75e642f0ab81b734b724e13fec930f309e222470236d66b", + "wif": "92Qba5hnyWSn5Ffcka56yMQauaWY6ZLd91Vzxbi4a9CCetaHtYj", + "compressed": false, + "mainnet": false} +] diff --git a/tests/unit.py b/tests/unit.py index 26a77a3..f48d608 100644 --- a/tests/unit.py +++ b/tests/unit.py @@ -17,7 +17,7 @@ from btcpy.structs.script import * from btcpy.structs.block import * from btcpy.structs.crypto import PublicKey, PrivateKey -from btcpy.structs.address import Address +from btcpy.structs.address import Address, SegWitAddress, P2shAddress, P2wshAddress from btcpy.lib.codecs import CouldNotDecode from btcpy.setup import setup from btcpy.structs.hd import * @@ -55,6 +55,9 @@ def get_data(filename): b58 = get_data('base58') b58chk = get_data('base58_check') segwit_hashes = get_data('segwit_hashes') +wif = get_data('wif') +p2wpkh_over_p2sh = get_data("p2wpkh_over_p2sh") +p2wsh_over_p2sh = get_data("p2wsh_over_p2sh") class TestB58(unittest.TestCase): @@ -265,6 +268,27 @@ def test_invalid(self): print(SegWitAddress.from_string(address, check_network=False)) +class TestSegwitOverP2sh(unittest.TestCase): + + def test_p2wpkh_over_p2sh(self): + for spend in p2wpkh_over_p2sh: + pubkey = PublicKey.unhexlify(spend['pubkey']) + self.assertEqual(str(P2shAddress.from_script(P2wpkhScript.get(spend['witness_version'])(pubkey), + mainnet=True)), + spend['address']) + self.assertEqual(P2wpkhScript.get(spend['witness_version'])(pubkey).hexlify(), spend['redeem_script']) + self.assertEqual(str(P2shScript(P2wpkhScript.get(spend['witness_version'])(pubkey))), spend['script_pubkey']) + + def test_p2wsh_over_p2sh(self): + for spend in p2wsh_over_p2sh: + wit_script = ScriptBuilder.identify(spend['witness_script']) + self.assertEqual(str(P2shScript(P2wshScript.get(spend['witness_version'])(wit_script))), spend['script_pubkey']) + self.assertEqual(P2wshScript.get(spend['witness_version'])(wit_script).hexlify(), spend['redeem_script']) + self.assertEqual(str(P2shAddress.from_script(P2wshScript.get(spend['witness_version'])(wit_script), + mainnet=True)), + spend['address']) + + class TestReplace(unittest.TestCase): def test_success(self): @@ -346,13 +370,12 @@ def test_derivation(self): self.assertEqual(m.derive(path).key, PrivateKey.from_wif(priv)) def test_to_wif(self): - vectors = get_data('wif') - for v in vectors: - self.assertEqual(PrivateKey.from_wif(v['wif'], check_network=False).hexlify(), v['hex']) - priv = PrivateKey.unhexlify(v['hex']) - if not v['compressed']: + for w in wif: + self.assertEqual(PrivateKey.from_wif(w['wif'], check_network=False).hexlify(), w['hex']) + priv = PrivateKey.unhexlify(w['hex']) + if not w['compressed']: priv.public_compressed = False - self.assertEqual(priv.to_wif(v['mainnet']), v['wif']) + self.assertEqual(priv.to_wif(w['mainnet']), w['wif']) class TestPubkey(unittest.TestCase): @@ -529,7 +552,7 @@ def __init__(self, *args, **kwargs): self.redeem_script = P2pkhScript(PublicKey.unhexlify('0384478d41e71dc6c3f9edde0f928a47d1b724c' '05984ebfb4e7d0422e80abe95ff')) self.as_data = StackData.from_bytes(self.redeem_script.p2sh_hash()) - self.address = self.redeem_script.to_address() + self.address = P2shAddress.from_script(self.redeem_script) def test_success_hash(self): script = P2shScript(self.redeem_script.p2sh_hash()) @@ -550,7 +573,7 @@ def test_success_addresses(self): from_addr = P2shScript(Address.from_string(address)) from_script = P2shScript(script) self.assertTrue(str(from_addr.address()) == address) - self.assertTrue(str(script.to_address()) == address) + self.assertTrue(str(P2shAddress.from_script(script)) == address) self.assertTrue(str(from_script.address()) == address) script = P2shScript(self.address) @@ -785,15 +808,15 @@ class TestAddress(unittest.TestCase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.good_addresses = {('mainnet', 'p2pkh', '1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa', + self.good_addresses = {('mainnet', P2pkhAddress, '1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa', b'\xc8\xe9\t\x96\xc7\xc6\x08\x0e\xe0b\x84`\x0chN\xd9\x04\xd1L\\'), - ('testnet', 'p2pkh', 'mtH6FLMQNu2fFQ4mrb7UjEUjTAhUNCMFoi', + ('testnet', P2pkhAddress, 'mtH6FLMQNu2fFQ4mrb7UjEUjTAhUNCMFoi', b'\x8b\xfas]\x98\xabb\x1e\xdbOw\xd7\xb7\xfe\nK\x1f\xfc\xc0$'), - ('testnet', 'p2pkh', 'n3wEvhujG7SDcgeKCXZMrty5QqhQZ7f6jW', + ('testnet', P2pkhAddress, 'n3wEvhujG7SDcgeKCXZMrty5QqhQZ7f6jW', b'\xf5\xea\xa2K\x82\xc8\x1f4L\x9a\x16\xa8\xfb\x84t\xe1\x10\xfd\xb1\xc3'), - ('mainnet', 'p2sh', '3P14159f73E4gFr7JterCCQh9QjiTjiZrG', + ('mainnet', P2shAddress, '3P14159f73E4gFr7JterCCQh9QjiTjiZrG', b'\xe9\xc3\xdd\x0c\x07\xaa\xc7ay\xeb\xc7jlx\xd4\xd6|l\x16\n'), - ('testnet', 'p2sh', '2N6JFaB5rMtPwutovP6cirwBVxHuAVaHvMG', + ('testnet', P2shAddress, '2N6JFaB5rMtPwutovP6cirwBVxHuAVaHvMG', b'\x8f,4\xa2