1from pycoin.block import Block
2from pycoin.coins.bitcoin.ScriptTools import BitcoinScriptTools
3from pycoin.coins.bitcoin.Tx import Tx
4from pycoin.coins.exceptions import ValidationFailureError
5from pycoin.coins.tx_utils import create_tx, split_with_remainder, distribute_from_split_pool, sign_tx, create_signed_tx
6from pycoin.coins.SolutionChecker import ScriptError
7from pycoin.contrib.msg_signing import MessageSigner
8from pycoin.contrib.who_signed import WhoSigned
9from pycoin.ecdsa.secp256k1 import secp256k1_generator
10from pycoin.encoding.b58 import b2a_hashed_base58
11from pycoin.key.HDSeed import HDSeed
12from pycoin.key.Keychain import Keychain
13from pycoin.key.Key import Key, InvalidSecretExponentError, InvalidPublicPairError
14from pycoin.key.BIP32Node import BIP32Node
15from pycoin.key.BIP49Node import BIP49Node
16from pycoin.key.BIP84Node import BIP84Node
17from pycoin.key.electrum import ElectrumWallet
18from pycoin.message.make_parser_and_packer import (
19    make_parser_and_packer, standard_messages,
20    standard_message_post_unpacks, standard_streamer, standard_parsing_functions
21)
22from pycoin.encoding.hexbytes import b2h, h2b
23from pycoin.satoshi import errno, flags
24from pycoin.solve.utils import build_hash160_lookup, build_p2sh_lookup, build_sec_lookup
25from pycoin.vm.annotate import Annotate
26
27from .AddressAPI import make_address_api
28from .ParseAPI import ParseAPI
29from .ContractAPI import ContractAPI
30from .parseable_str import parseable_str
31
32
33class API(object):
34    pass
35
36
37class Network(object):
38    def __init__(self, symbol, network_name, subnet_name):
39        self.symbol = symbol
40        self.network_name = network_name
41        self.subnet_name = subnet_name
42
43    def full_name(self):
44        return "%s %s" % (self.network_name, self.subnet_name)
45
46    def __repr__(self):
47        return "<Network %s>" % self.full_name()
48
49
50def make_output_for_secret_exponent(Key):
51    def f(secret_exponent):
52        yield ("secret_exponent", '%d' % secret_exponent, None)
53        yield ("secret_exponent_hex", '%x' % secret_exponent, " hex")
54        key = Key(secret_exponent)
55        yield ("wif", key.wif(is_compressed=True), None)
56        yield ("wif_uncompressed", key.wif(is_compressed=False), " uncompressed")
57    return f
58
59
60def make_output_for_public_pair(Key, network):
61    def f(public_pair):
62        yield ("public_pair_x", '%d' % public_pair[0], None)
63        yield ("public_pair_y", '%d' % public_pair[1], None)
64        yield ("public_pair_x_hex", '%x' % public_pair[0], " x as hex")
65        yield ("public_pair_y_hex", '%x' % public_pair[1], " y as hex")
66        yield ("y_parity", "odd" if (public_pair[1] & 1) else "even", None)
67
68        key = Key(public_pair=public_pair)
69        yield ("key_pair_as_sec", b2h(key.sec(is_compressed=True)), None)
70        yield ("key_pair_as_sec_uncompressed", b2h(key.sec(is_compressed=False)), " uncompressed")
71
72        network_name = network.network_name
73        hash160_u = key.hash160(is_compressed=False)
74        hash160_c = key.hash160(is_compressed=True)
75
76        yield ("hash160", b2h(hash160_c), None)
77
78        if hash160_c and hash160_u:
79            yield ("hash160_uncompressed", b2h(hash160_u), " uncompressed")
80
81        address = network.address.for_p2pkh(hash160_c)
82        yield ("address", address, "%s address" % network_name)
83        yield ("%s_address" % network.symbol, address, "legacy")
84
85        address = key.address(is_compressed=False)
86        yield ("address_uncompressed", address, "%s address uncompressed" % network_name)
87        yield ("%s_address_uncompressed" % network.symbol, address, "legacy")
88
89        # don't print segwit addresses unless we're sure we have a compressed key
90        if hash160_c and hasattr(network.address, "for_p2pkh_wit"):
91            address_segwit = network.address.for_p2pkh_wit(hash160_c)
92            if address_segwit:
93                # this network seems to support segwit
94                yield ("address_segwit", address_segwit, "%s segwit address" % network_name)
95                yield ("%s_address_segwit" % network.symbol, address_segwit, "legacy")
96
97                p2sh_script = network.contract.for_p2pkh_wit(hash160_c)
98                p2s_address = network.address.for_p2s(p2sh_script)
99                if p2s_address:
100                    yield ("p2sh_segwit", p2s_address, None)
101
102                p2sh_script_hex = b2h(p2sh_script)
103                yield ("p2sh_segwit_script", p2sh_script_hex, " corresponding p2sh script")
104
105    return f
106
107
108def create_bitcoinish_network(symbol, network_name, subnet_name, **kwargs):
109    # potential kwargs:
110    #   tx, block, magic_header_hex, default_port, dns_bootstrap,
111    #   wif_prefix_hex, address_prefix_hex, pay_to_script_prefix_hex
112    #   bip32_prv_prefix_hex, bip32_pub_prefix_hex, sec_prefix, script_tools
113    #   bip49_prv_prefix, bip49_pub_prefix, bip84_prv_prefix, bip84_pub_prefix
114
115    network = Network(symbol, network_name, subnet_name)
116
117    generator = kwargs.get("generator", secp256k1_generator)
118    kwargs.setdefault("sec_prefix", "%sSEC" % symbol.upper())
119    KEYS_TO_H2B = ("bip32_prv_prefix bip32_pub_prefix bip49_prv_prefix bip49_pub_prefix "
120                   "bip84_prv_prefix bip84_pub_prefix wif_prefix address_prefix "
121                   "pay_to_script_prefix sec_prefix magic_header").split()
122    for k in KEYS_TO_H2B:
123        k_hex = "%s_hex" % k
124        if k_hex in kwargs:
125            kwargs[k] = h2b(kwargs[k_hex])
126
127    script_tools = kwargs.get("script_tools", BitcoinScriptTools)
128
129    UI_KEYS = ("bip32_prv_prefix bip32_pub_prefix bip49_prv_prefix bip49_pub_prefix "
130               "bip84_prv_prefix bip84_pub_prefix wif_prefix sec_prefix "
131               "address_prefix pay_to_script_prefix bech32_hrp").split()
132    ui_kwargs = {k: kwargs[k] for k in UI_KEYS if k in kwargs}
133
134    _bip32_prv_prefix = ui_kwargs.get("bip32_prv_prefix")
135    _bip32_pub_prefix = ui_kwargs.get("bip32_pub_prefix")
136    _wif_prefix = ui_kwargs.get("wif_prefix")
137    _sec_prefix = ui_kwargs.get("sec_prefix")
138
139    def bip32_as_string(blob, as_private):
140        prefix = ui_kwargs.get("bip32_%s_prefix" % ("prv" if as_private else "pub"))
141        return b2a_hashed_base58(prefix + blob)
142
143    def bip49_as_string(blob, as_private):
144        prefix = ui_kwargs.get("bip49_%s_prefix" % ("prv" if as_private else "pub"))
145        return b2a_hashed_base58(prefix + blob)
146
147    def bip84_as_string(blob, as_private):
148        prefix = ui_kwargs.get("bip84_%s_prefix" % ("prv" if as_private else "pub"))
149        return b2a_hashed_base58(prefix + blob)
150
151    def wif_for_blob(blob):
152        return b2a_hashed_base58(_wif_prefix + blob)
153
154    def sec_text_for_blob(blob):
155        return _sec_prefix + b2h(blob)
156
157    NetworkKey = Key.make_subclass(symbol, network=network, generator=generator)
158    NetworkElectrumKey = ElectrumWallet.make_subclass(symbol, network=network, generator=generator)
159    NetworkBIP32Node = BIP32Node.make_subclass(symbol, network=network, generator=generator)
160    NetworkBIP49Node = BIP49Node.make_subclass(symbol, network=network, generator=generator)
161    NetworkBIP84Node = BIP84Node.make_subclass(symbol, network=network, generator=generator)
162
163    NETWORK_KEYS = "network_name subnet_name dns_bootstrap default_port magic_header".split()
164    for k in NETWORK_KEYS:
165        if k in kwargs:
166            setattr(network, k, kwargs[k])
167
168    network.Tx = network.tx = kwargs.get("tx") or Tx
169    network.Block = network.block = kwargs.get("block") or Block.make_subclass(symbol, network.tx)
170
171    streamer = standard_streamer(standard_parsing_functions(network.block, network.tx))
172
173    network.message = API()
174    network.message.parse, network.message.pack = make_parser_and_packer(
175        streamer, standard_messages(), standard_message_post_unpacks(streamer))
176
177    network.output_for_secret_exponent = make_output_for_secret_exponent(NetworkKey)
178    network.output_for_public_pair = make_output_for_public_pair(NetworkKey, network)
179
180    network.keychain = Keychain
181
182    parse_api_class = kwargs.get("parse_api_class", ParseAPI)
183    network.parse = parse_api_class(network, **ui_kwargs)
184
185    network.contract = ContractAPI(network, script_tools)
186
187    network.address = make_address_api(network.contract, **ui_kwargs)
188
189    def keys_private(secret_exponent, is_compressed=True):
190        return NetworkKey(secret_exponent=secret_exponent, is_compressed=is_compressed)
191
192    def keys_public(item, is_compressed=None):
193        if isinstance(item, tuple):
194            if is_compressed is None:
195                is_compressed = True
196            # it's a public pair
197            return NetworkKey(public_pair=item, is_compressed=is_compressed)
198        if is_compressed is not None:
199            raise ValueError("can't set is_compressed from sec")
200        return NetworkKey.from_sec(item)
201
202    network.keys = API()
203    network.keys.private = keys_private
204    network.keys.public = keys_public
205
206    def electrum_seed(seed):
207        return NetworkElectrumKey(initial_key=seed)
208
209    def electrum_private(master_private_key):
210        return NetworkElectrumKey(master_private_key=master_private_key)
211
212    def electrum_public(master_public_key):
213        return NetworkElectrumKey(master_public_key=master_public_key)
214
215    network.keys.bip32_seed = NetworkBIP32Node.from_master_secret
216    network.keys.bip32_deserialize = NetworkBIP32Node.deserialize
217    network.keys.bip49_deserialize = NetworkBIP49Node.deserialize
218    network.keys.bip84_deserialize = NetworkBIP84Node.deserialize
219
220    network.keys.electrum_seed = electrum_seed
221    network.keys.electrum_private = electrum_private
222    network.keys.electrum_public = electrum_public
223    network.keys.InvalidSecretExponentError = InvalidSecretExponentError
224    network.keys.InvalidPublicPairError = InvalidPublicPairError
225
226    network.msg = API()
227    message_signer = MessageSigner(network, generator)
228    network.msg.sign = message_signer.sign_message
229    network.msg.verify = message_signer.verify_message
230    network.msg.parse_signed = message_signer.parse_signed_message
231    network.msg.hash_for_signing = message_signer.hash_for_signing
232    network.msg.signature_for_message_hash = message_signer.signature_for_message_hash
233    network.msg.pair_for_message_hash = message_signer.pair_for_message_hash
234    network.script = script_tools
235
236    network.bip32_as_string = bip32_as_string
237    network.bip49_as_string = bip49_as_string
238    network.sec_text_for_blob = sec_text_for_blob
239    network.wif_for_blob = wif_for_blob
240
241    def network_build_hash160_lookup(iter):
242        return build_hash160_lookup(iter, [generator])
243
244    network.tx.solve = API()
245    network.tx.solve.build_hash160_lookup = network_build_hash160_lookup
246    network.tx.solve.build_p2sh_lookup = build_p2sh_lookup
247    network.tx.solve.build_sec_lookup = build_sec_lookup
248
249    network.validator = API()
250    network.validator.ScriptError = ScriptError
251    network.validator.ValidationFailureError = ValidationFailureError
252    network.validator.errno = errno
253    network.validator.flags = flags
254
255    def my_create_tx(*args, **kwargs):
256        return create_tx(network, *args, **kwargs)
257
258    def my_sign_tx(*args, **kwargs):
259        return sign_tx(network, *args, **kwargs)
260
261    def my_create_signed_tx(*args, **kwargs):
262        return create_signed_tx(network, *args, **kwargs)
263
264    def my_split_with_remainder(*args, **kwargs):
265        return split_with_remainder(network, *args, **kwargs)
266
267    network.tx_utils = API()
268    network.tx_utils.create_tx = my_create_tx
269    network.tx_utils.sign_tx = my_sign_tx
270    network.tx_utils.create_signed_tx = my_create_signed_tx
271    network.tx_utils.split_with_remainder = my_split_with_remainder
272    network.tx_utils.distribute_from_split_pool = distribute_from_split_pool
273
274    network.annotate = Annotate(script_tools, network.address)
275
276    network.who_signed = WhoSigned(script_tools, network.address, generator)
277
278    network.str = parseable_str
279
280    network.generator = generator
281
282    return network
283