Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
dist/
build/
*.egg-info/
testing.py
57 changes: 57 additions & 0 deletions btcpy/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from .setup import net_name

wif_prefixes = {
'mainnet': 0x80,
'testnet': 0xEF,
'litecoin': 0xB0,
'dashcoin': 0xCC
}

raw_prefixes_c = {('mainnet', 'p2pkh'): bytearray(b'\x00'),
('testnet', 'p2pkh'): bytearray(b'\x6f'),
('mainnet', 'p2sh'): bytearray(b'\x05'),
('testnet', 'p2sh'): bytearray(b'\xc4'),
('litecoin', 'p2pkh'): bytearray(b'\x30'),
('litecoin', 'p2sh'): bytearray(b'\x32'),
('dashcoin', 'p2pkh'): bytearray(b'\x4c'),
('dashcoin', 'p2sh'): bytearray(b'\x13')
}

prefixes_c = {'1': ('p2pkh', 'mainnet'),
'm': ('p2pkh', 'testnet'),
'n': ('p2pkh', 'testnet'),
'3': ('p2sh', 'mainnet'),
'2': ('p2sh', 'testnet'),
'L': ('p2pkh', 'litecoin'),
'M': ('p2sh', 'litecoin'),
'X': ('p2pkh', 'dashcoin')
}

raw_prefixes_to_init = {
'litecoin':
{
'L': ('p2pkh', 'litecoin'),
'M': ('p2sh', 'litecoin')
},
'dashcoin':
{
'X': ('p2pkh', 'dashcoin'),
'8': ('p2sh', 'dashcoin'),
'9': ('p2sh', 'dashcoin')
},
'mainnet':
{
'1': ('p2pkh', 'mainnet'),
'3': ('p2sh', 'mainnet')
},
'testnet':
{
'm': ('p2pkh', 'testnet'),
'n': ('p2pkh', 'testnet'),
'2': ('p2sh', 'testnet')
}
}


def prefixes_c():
return raw_prefixes_to_init[net_name()]
16 changes: 5 additions & 11 deletions btcpy/lib/codecs.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from .bech32 import decode, encode
from ..setup import is_mainnet, net_name
from ..structs.address import Address, SegWitAddress
from ..constants import prefixes_c, raw_prefixes_c


class CouldNotDecode(ValueError):
Expand All @@ -39,22 +40,15 @@ def decode(string: str, check_network=True) -> Address:

@classmethod
def check_network(cls, network):
if (network == 'mainnet') != is_mainnet():
if network != net_name():
raise CouldNotDecode('Trying to parse {} address in {} environment'.format(network, net_name()))


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')}
raw_prefixes = raw_prefixes_c

prefixes = {'1': ('p2pkh', 'mainnet'),
'm': ('p2pkh', 'testnet'),
'n': ('p2pkh', 'testnet'),
'3': ('p2sh', 'mainnet'),
'2': ('p2sh', 'testnet')}
prefixes = prefixes_c()

hash_len = 20

Expand All @@ -81,7 +75,7 @@ def decode(string, check_network=True):
if check_network:
Base58Codec.check_network(network)

return Address(addr_type, hashed_data, network == 'mainnet')
return Address(addr_type, hashed_data)


class Bech32Codec(Codec):
Expand Down
2 changes: 1 addition & 1 deletion btcpy/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# propagated, or distributed except according to the terms contained in the
# LICENSE.md file.

networks = {'mainnet', 'testnet', 'regtest'}
networks = {'mainnet', 'testnet', 'regtest', 'litecoin', 'dashcoin'}

MAINNET = None
NETNAME = None
Expand Down
8 changes: 3 additions & 5 deletions btcpy/structs/address.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from abc import ABCMeta, abstractmethod

from ..setup import is_mainnet
from ..setup import is_mainnet, net_name


class BaseAddress(metaclass=ABCMeta):
Expand Down Expand Up @@ -50,9 +50,7 @@ def get_codec():
return Base58Codec

def __init__(self, addr_type, hashed_data, mainnet=None):
if mainnet is None:
mainnet = is_mainnet()
network = 'mainnet' if mainnet else 'testnet'
network = net_name()
self.network = network
self.type = addr_type
self.hash = hashed_data
Expand All @@ -79,7 +77,7 @@ def to_address(self):
addr_type = 'p2sh'
else:
raise ValueError('SegWitAddress type does not match p2wpkh nor p2wsh, {} instead'.format(self.type))
return Address(addr_type, self.hash, self.network == 'mainnet')
return Address(addr_type, self.hash)

def __eq__(self, other):
return super().__eq__(other) and self.version == other.version
18 changes: 7 additions & 11 deletions btcpy/structs/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@

from ..lib.types import HexSerializable
from .address import Address, SegWitAddress
from ..setup import is_mainnet
from ..setup import is_mainnet, net_name
from ..constants import wif_prefixes


class Key(HexSerializable, metaclass=ABCMeta):
Expand All @@ -39,14 +40,11 @@ def from_wif(wif, check_network=True):
decoded = b58decode_check(wif)
prefix, *rest = decoded

if prefix not in {0x80, 0xef}:
if prefix not in 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 envirnment')
if check_network and not prefix == wif_prefixes[net_name()]:
raise ValueError('Bad enviroment')

public_compressed = len(rest) == 33
privk = rest[0:32]
Expand All @@ -61,10 +59,8 @@ def __init__(self, priv, public_compressed=True):
self.key = priv
self.public_compressed = public_compressed

def to_wif(self, mainnet=None):
if mainnet is None:
mainnet = is_mainnet()
prefix = bytearray([0x80]) if mainnet else bytearray([0xef])
def to_wif(self):
prefix = bytearray([wif_prefixes[net_name()]])
decoded = prefix + self.key
if self.public_compressed:
decoded.append(0x01)
Expand Down
20 changes: 10 additions & 10 deletions btcpy/structs/hd.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,33 +20,33 @@

from ..lib.types import HexSerializable
from ..lib.parsing import Stream, Parser
from ..setup import is_mainnet
from ..setup import is_mainnet, net_name
from .crypto import PrivateKey, PublicKey


class ExtendedKey(HexSerializable, metaclass=ABCMeta):

master_parent_fingerprint = bytearray([0]*4)
master_parent_fingerprint = bytearray([0] * 4)
first_hardened_index = 1 << 31
curve_order = SECP256k1.order
networks = {
'x': ['mainnet', 'litecoin', 'dashcoin'],
't': ['testnet'],
}

@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':
mainnet = True
elif string[0] == 't':
mainnet = False
if string[0] in cls.networks:
network = cls.networks[string[0]]
else:
raise ValueError('Encoded key not recognised: {}'.format(string))

if check_network and mainnet != is_mainnet():
if check_network and net_name() in network:
raise ValueError('Trying to decode {}mainnet key '
'in {}mainnet environment'.format('' if mainnet else 'non-',
'non-' if mainnet else ''))
'in {}' + network + ' environment')

decoded = b58decode_check(string)
parser = Parser(bytearray(decoded))
Expand Down