Skip to content

Commit

Permalink
Merge pull request #16 from belovachap/chapman_cleanup_merge
Browse files Browse the repository at this point in the history
Clean up merge with chainside/master
  • Loading branch information
peerchemist authored Aug 27, 2018
2 parents 84eccd0 + 3a81d54 commit ea9daec
Show file tree
Hide file tree
Showing 13 changed files with 305 additions and 648 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
language: python
python:
- "3.3"
- "3.4"
- "3.5"
- "3.6"
# command to install dependencies
Expand Down
13 changes: 6 additions & 7 deletions btcpy/lib/codecs.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from .base58 import b58encode_check, b58decode_check

from .bech32 import decode, encode
from ..constants import BitcoinMainnet
from ..structs.address import Address, P2pkhAddress, P2shAddress, P2wpkhAddress, P2wshAddress


Expand All @@ -33,7 +34,7 @@ def encode(address: Address) -> str:

@staticmethod
@abstractmethod
def decode(string: str, strict=None) -> Address:
def decode(string: str, network=BitcoinMainnet) -> Address:
raise NotImplemented


Expand All @@ -51,7 +52,7 @@ def encode(address):
return b58encode_check(bytes(prefix + address.hash))

@staticmethod
def decode(network, string):
def decode(string, network=BitcoinMainnet):

try:
addr_type = network.base58_prefixes[string[0]]
Expand All @@ -69,7 +70,7 @@ def decode(network, string):
else:
raise ValueError('Unknown address type: {}'.format(addr_type))

return cls(network, hashed_data)
return cls(hashed_data, network=network)


class Bech32Codec(Codec):
Expand All @@ -82,7 +83,7 @@ def encode(address):
return encode(address.network.hrp, address.version, address.hash)

@staticmethod
def decode(network, string):
def decode(string, network=BitcoinMainnet):

if not string:
raise CouldNotDecode('Impossible to decode empty string')
Expand All @@ -93,10 +94,8 @@ def decode(network, string):
raise CouldNotDecode('String {} mixes upper- and lower-case characters'.format(string))

string = string.lower()

if string[:2] != network.bech32_hrp:
raise CouldNotDecode('Impossible to decode address {}'.format(string))

try:
addr_type = Bech32Codec.lengths[len(string)]
except KeyError:
Expand All @@ -113,4 +112,4 @@ def decode(network, string):
else:
raise ValueError('Unknown address type: {}'.format(addr_type))

return cls(network, bytearray(hashed_data), version)
return cls(bytearray(hashed_data), version, network=network)
95 changes: 23 additions & 72 deletions btcpy/lib/parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

from .types import Serializable, HexSerializable
from .opcodes import OpCodeConverter
from btcpy.constants import BitcoinMainnet


class IncompleteParsingException(ValueError):
Expand Down Expand Up @@ -133,34 +134,8 @@ def get_block_header(self):
return BlockHeader(version, prev_block_hash, merkle_root, block_timestamp, bits, block_nonce)


class BlockParser(BlockHeaderParser):

def __init__(self, bytes_, tx_parser, network):
super().__init__(bytes_)
self.tx_parser = tx_parser
self.network = network

def get_txn_count(self):

return self.parse_varint()

def get_txns(self):

txn_count = self.get_txn_count()
counter = 0
txns_parser = self.tx_parser(self >> len(self), self.network)
txns = []
for i in range(txn_count):
txns.append(txns_parser.get_next_tx())
counter += 1
if len(txns_parser) != 0:
raise IncompleteParsingException("Incomplete Block parsing, leftover data...")
return txns


class TransactionParser(Parser):

def __init__(self, bytes_, network):
def __init__(self, bytes_, network=BitcoinMainnet):
super().__init__(bytes_)
self.network = network
self.segwit = False
Expand Down Expand Up @@ -202,10 +177,9 @@ def _txins_data(self):
def _txout(self, n):
from ..structs.script import ScriptBuilder
from ..structs.transaction import TxOut

value = int.from_bytes(self >> 8, 'little')
script = ScriptBuilder.identify(self >> self.parse_varint())
return TxOut(value, n, script, self.network)
return TxOut(value, n, script, network=self.network)

def _txouts(self):
return [self._txout(i) for i in range(self.parse_varint())]
Expand Down Expand Up @@ -249,58 +223,35 @@ def get_next_tx(self, mutable=False):
raise ValueError('Transaction looks like coinbase but has more than one txin')

if segwit:
result = SegWitTransaction(version, txins, txouts, locktime, self.network)
result = SegWitTransaction(version, txins, txouts, locktime, network=self.network)
else:
result = Transaction(version, txins, txouts, locktime, self.network)

result = Transaction(version, txins, txouts, locktime, network=self.network)
return result.to_mutable() if mutable else result


class PeercoinTxParser(TransactionParser):

def _timestamp(self):
'''get transaction timestamp (peercoin specific)'''
return int.from_bytes(self >> 4, 'little')

def _txout(self, n):
from ..structs.script import ScriptBuilder
from ..structs.transaction import PeercoinTxOut

value = int.from_bytes(self >> 8, 'little')
script = ScriptBuilder.identify(self >> self.parse_varint())
return PeercoinTxOut(value, n, script, self.network)

def get_next_tx(self, mutable=False):
from ..structs.script import CoinBaseScriptSig
from ..structs.transaction import (CoinBaseTxIn, Witness, TxIn, PeercoinTx)
class BlockParser(BlockHeaderParser):

version = self._version()
tstamp = self._timestamp()
segwit, txins_data = self._txins_data()
txouts = self._txouts()
if segwit:
witness = self._witness()
txins = [CoinBaseTxIn(*txin_data[2:], witness=Witness(wit))
if isinstance(txin_data[2], CoinBaseScriptSig)
else TxIn(*txin_data, witness=Witness(wit))
for txin_data, wit in zip(txins_data, witness)]
else:
txins = [CoinBaseTxIn(*txin_data[2:])
if isinstance(txin_data[2], CoinBaseScriptSig)
else TxIn(*txin_data)
for txin_data in txins_data]
def __init__(self, bytes_, tx_parser=TransactionParser, network=BitcoinMainnet):
super().__init__(bytes_)
self.tx_parser = tx_parser
self.network = network

locktime = self._locktime()
def get_txn_count(self):

if len(txins) > 1 and isinstance(txins[0], CoinBaseTxIn):
raise ValueError('Transaction looks like coinbase but has more than one txin')
return self.parse_varint()

if segwit:
raise Exception('Peercoin does not currently support SegWit.')
else:
result = PeercoinTx(version, tstamp, txins, txouts, locktime, self.network)
def get_txns(self):

return result.to_mutable() if mutable else result
txn_count = self.get_txn_count()
counter = 0
txns_parser = self.tx_parser(self >> len(self), network=self.network)
txns = []
for i in range(txn_count):
txns.append(txns_parser.get_next_tx())
counter += 1
if len(txns_parser) != 0:
raise IncompleteParsingException("Incomplete Block parsing, leftover data...")
return txns


class UnexpectedOperationFound(Exception):
Expand Down
74 changes: 32 additions & 42 deletions btcpy/structs/address.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

from abc import ABCMeta, abstractmethod

from btcpy.constants import BitcoinMainnet


class WrongScriptType(Exception):
pass
Expand All @@ -19,73 +21,61 @@ class WrongScriptType(Exception):
class InvalidAddress(Exception):
pass

@staticmethod
def is_valid(network, string):
from ..lib.codecs import CouldNotDecode


class Address(metaclass=ABCMeta):

@classmethod
def is_valid(cls, string):
def is_valid(cls, string, network=BitcoinMainnet):
try:
Address.from_string(network)
return True
except CouldNotDecode:
try:
SegWitAddress.from_string(network)
return True
except CouldNotDecode:
return False
cls.from_string(string, network=network)
except InvalidAddress:
return False
return True

@classmethod
def get_codec(cls):
raise NotImplemented

@classmethod
@abstractmethod
def from_script(cls, network, script):
def from_script(cls, script, network=BitcoinMainnet):
raise NotImplemented

@classmethod
@abstractmethod
def get_type(cls):
raise NotImplemented

@classmethod
def from_string(cls, network, string):
return cls.get_codec().decode(network, string)

@classmethod
def hash_length(cls):
raise NotImplemented

def __init__(self, network, 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)))

self.network = network
self.hash = hashed_data

def __str__(self):
return self.__class__.get_codec().encode(self)

@staticmethod
def from_string(string):
def from_string(string, network=BitcoinMainnet):
from ..lib.codecs import CouldNotDecode

try:
return ClassicAddress.decode(string)
return ClassicAddress.decode(string, network=network)
except CouldNotDecode:
try:
return SegWitAddress.decode(string)
return SegWitAddress.decode(string, network=network)
except CouldNotDecode:
raise InvalidAddress

@classmethod
def decode(cls, string):
return cls.get_codec().decode(string)
def decode(cls, string, network=BitcoinMainnet):
return cls.get_codec().decode(string, network=network)

def __init__(self, hashed_data, network=BitcoinMainnet):
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)))

self.network = network
self.hash = hashed_data

def __eq__(self, other):
return (self.network, self.hash) == (other.network, other.hash)
Expand All @@ -112,8 +102,8 @@ def get_codec(cls):
from ..lib.codecs import Bech32Codec
return Bech32Codec

def __init__(self, network, hashed_data, version):
super().__init__(network, hashed_data)
def __init__(self, hashed_data, version, network=BitcoinMainnet):
super().__init__(hashed_data, network=network)
self.version = version

def __eq__(self, other):
Expand All @@ -127,13 +117,13 @@ def get_type(cls):
return 'p2pkh'

@classmethod
def from_script(cls, network, script):
def from_script(cls, script, network=BitcoinMainnet):
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(network, script.pubkeyhash)
return cls(script.pubkeyhash, network=network)

@classmethod
def hash_length(cls):
Expand All @@ -151,12 +141,12 @@ def get_type(cls):
return 'p2sh'

@classmethod
def from_script(cls, network, script):
def from_script(cls, script, network=BitcoinMainnet):
from .script import P2shScript
# can't use isinstance here: P2wshScript is child of P2shScript
if script.__class__ is P2shScript:
return cls(network, script.scripthash)
return cls(network, script.p2sh_hash())
return cls(script.scripthash, network=network)
return cls(script.p2sh_hash(), network=network)

@classmethod
def hash_length(cls):
Expand All @@ -174,12 +164,12 @@ def get_type(cls):
return 'p2wpkh'

@classmethod
def from_script(cls, network, script):
def from_script(cls, script, network=BitcoinMainnet):
from .script import P2wpkhScript
if not isinstance(script, P2wpkhScript):
raise WrongScriptType('Trying to produce P2pkhAddress from {} script'.format(script.__class__.__name__))

return cls(network, script.pubkeyhash, script.__class__.get_version())
return cls(script.pubkeyhash, script.__class__.get_version(), network=network)

@classmethod
def hash_length(cls):
Expand All @@ -197,14 +187,14 @@ def get_type(cls):
return 'p2wsh'

@classmethod
def from_script(cls, network, script):
def from_script(cls, script, network=BitcoinMainnet):
from .script import P2wshScript
version = script.__class__.get_version()
if isinstance(script, P2wshScript):
hashed_data = script.scripthash
else:
hashed_data = script.p2wsh_hash()
return cls(network, hashed_data, version)
return cls(hashed_data, version, network=network)

@classmethod
def hash_length(cls):
Expand Down
Loading

0 comments on commit ea9daec

Please sign in to comment.