80 lines
2.8 KiB
Python
80 lines
2.8 KiB
Python
|
import six
|
||
|
from typing import Dict, Type
|
||
|
from torba.hash import hash160, double_sha256, Base58
|
||
|
|
||
|
|
||
|
class CoinRegistry(type):
|
||
|
coins = {} # type: Dict[str, Type[BaseCoin]]
|
||
|
|
||
|
def __new__(mcs, name, bases, attrs):
|
||
|
cls = super(CoinRegistry, mcs).__new__(mcs, name, bases, attrs) # type: Type[BaseCoin]
|
||
|
if not (name == 'BaseCoin' and not bases):
|
||
|
coin_id = cls.get_id()
|
||
|
assert coin_id not in mcs.coins, 'Coin with id "{}" already registered.'.format(coin_id)
|
||
|
mcs.coins[coin_id] = cls
|
||
|
assert cls.ledger_class.coin_class is None, (
|
||
|
"Ledger ({}) which this coin ({}) references is already referenced by another "
|
||
|
"coin ({}). One to one relationship between a coin and a ledger is strictly and "
|
||
|
"automatically enforced. Make sure that coin_class=None in the ledger and that "
|
||
|
"another Coin isn't already referencing this Ledger."
|
||
|
).format(cls.ledger_class.__name__, name, cls.ledger_class.coin_class.__name__)
|
||
|
# create back reference from ledger to the coin
|
||
|
cls.ledger_class.coin_class = cls
|
||
|
return cls
|
||
|
|
||
|
@classmethod
|
||
|
def get_coin_class(mcs, coin_id): # type: (str) -> Type[BaseCoin]
|
||
|
return mcs.coins[coin_id]
|
||
|
|
||
|
|
||
|
class BaseCoin(six.with_metaclass(CoinRegistry)):
|
||
|
|
||
|
name = None
|
||
|
symbol = None
|
||
|
network = None
|
||
|
|
||
|
ledger_class = None # type: Type[BaseLedger]
|
||
|
transaction_class = None # type: Type[BaseTransaction]
|
||
|
|
||
|
secret_prefix = None
|
||
|
pubkey_address_prefix = None
|
||
|
script_address_prefix = None
|
||
|
extended_public_key_prefix = None
|
||
|
extended_private_key_prefix = None
|
||
|
|
||
|
def __init__(self, ledger, fee_per_byte):
|
||
|
self.ledger = ledger
|
||
|
self.fee_per_byte = fee_per_byte
|
||
|
|
||
|
@classmethod
|
||
|
def get_id(cls):
|
||
|
return '{}_{}'.format(cls.symbol.lower(), cls.network.lower())
|
||
|
|
||
|
def to_dict(self):
|
||
|
return {'fee_per_byte': self.fee_per_byte}
|
||
|
|
||
|
def get_input_output_fee(self, io):
|
||
|
""" Fee based on size of the input / output. """
|
||
|
return self.fee_per_byte * io.size
|
||
|
|
||
|
def get_transaction_base_fee(self, tx):
|
||
|
""" Fee for the transaction header and all outputs; without inputs. """
|
||
|
return self.fee_per_byte * tx.base_size
|
||
|
|
||
|
def hash160_to_address(self, h160):
|
||
|
raw_address = self.pubkey_address_prefix + h160
|
||
|
return Base58.encode(bytearray(raw_address + double_sha256(raw_address)[0:4]))
|
||
|
|
||
|
@staticmethod
|
||
|
def address_to_hash160(address):
|
||
|
bytes = Base58.decode(address)
|
||
|
prefix, pubkey_bytes, addr_checksum = bytes[0], bytes[1:21], bytes[21:]
|
||
|
return pubkey_bytes
|
||
|
|
||
|
def public_key_to_address(self, public_key):
|
||
|
return self.hash160_to_address(hash160(public_key))
|
||
|
|
||
|
@staticmethod
|
||
|
def private_key_to_wif(private_key):
|
||
|
return b'\x1c' + private_key + b'\x01'
|