2020-05-01 15:28:51 +02:00
|
|
|
from binascii import unhexlify
|
2020-05-18 14:22:23 +02:00
|
|
|
from string import hexdigits
|
2020-09-17 01:50:51 +02:00
|
|
|
from typing import TYPE_CHECKING, Type
|
2018-06-12 17:53:29 +02:00
|
|
|
|
2020-05-01 15:28:51 +02:00
|
|
|
from lbry.crypto.hash import hash160, double_sha256
|
2020-01-03 04:18:49 +01:00
|
|
|
from lbry.crypto.base58 import Base58
|
2020-05-01 15:28:51 +02:00
|
|
|
from lbry.schema.url import URL
|
2020-01-03 04:18:49 +01:00
|
|
|
from .header import Headers, UnvalidatedHeaders
|
2020-04-02 20:31:03 +02:00
|
|
|
from .checkpoints import HASHES
|
2020-05-06 16:50:00 +02:00
|
|
|
from .dewies import lbc_to_dewies
|
2020-01-03 04:18:49 +01:00
|
|
|
|
|
|
|
|
2020-09-17 01:50:51 +02:00
|
|
|
if TYPE_CHECKING:
|
2020-07-12 00:18:33 +02:00
|
|
|
from lbry.conf import Config
|
|
|
|
|
|
|
|
|
2020-05-01 15:28:51 +02:00
|
|
|
class Ledger:
|
2018-06-12 17:53:29 +02:00
|
|
|
name = 'LBRY Credits'
|
|
|
|
symbol = 'LBC'
|
2018-06-14 06:53:38 +02:00
|
|
|
network_name = 'mainnet'
|
2018-06-12 17:53:29 +02:00
|
|
|
|
|
|
|
headers_class = Headers
|
2019-03-24 21:55:04 +01:00
|
|
|
|
2018-10-15 23:16:43 +02:00
|
|
|
secret_prefix = bytes((0x1c,))
|
|
|
|
pubkey_address_prefix = bytes((0x55,))
|
|
|
|
script_address_prefix = bytes((0x7a,))
|
2018-07-01 23:21:18 +02:00
|
|
|
extended_public_key_prefix = unhexlify('0488b21e')
|
|
|
|
extended_private_key_prefix = unhexlify('0488ade4')
|
2018-06-14 06:53:38 +02:00
|
|
|
|
|
|
|
max_target = 0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
|
|
|
genesis_hash = '9c89283ba0f3227f6c03b70216b9f665f0118d5e0fa729cedf4fb34d6a34f463'
|
|
|
|
genesis_bits = 0x1f00ffff
|
|
|
|
target_timespan = 150
|
|
|
|
|
2020-05-01 15:28:51 +02:00
|
|
|
fee_per_byte = 50
|
|
|
|
fee_per_name_char = 200000
|
2018-06-12 17:53:29 +02:00
|
|
|
|
2020-04-02 20:31:03 +02:00
|
|
|
checkpoints = HASHES
|
|
|
|
|
2020-07-12 00:18:33 +02:00
|
|
|
def __init__(self, conf: 'Config'):
|
|
|
|
self.conf = conf
|
2020-01-03 04:18:49 +01:00
|
|
|
self.coin_selection_strategy = None
|
2018-08-28 02:03:08 +02:00
|
|
|
|
2020-01-03 04:18:49 +01:00
|
|
|
@classmethod
|
|
|
|
def get_id(cls):
|
|
|
|
return '{}_{}'.format(cls.symbol.lower(), cls.network_name.lower())
|
|
|
|
|
2020-09-22 03:46:41 +02:00
|
|
|
@staticmethod
|
|
|
|
def address_to_hash160(address) -> bytes:
|
|
|
|
return Base58.decode(address)[1:21]
|
|
|
|
|
2020-01-03 04:18:49 +01:00
|
|
|
@classmethod
|
2020-09-22 03:46:41 +02:00
|
|
|
def pubkey_hash_to_address(cls, h160):
|
2020-01-03 04:18:49 +01:00
|
|
|
raw_address = cls.pubkey_address_prefix + h160
|
|
|
|
return Base58.encode(bytearray(raw_address + double_sha256(raw_address)[0:4]))
|
|
|
|
|
2020-09-22 03:46:41 +02:00
|
|
|
@classmethod
|
|
|
|
def public_key_to_address(cls, public_key):
|
|
|
|
return cls.pubkey_hash_to_address(hash160(public_key))
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def script_hash_to_address(cls, h160):
|
|
|
|
raw_address = cls.script_address_prefix + h160
|
|
|
|
return Base58.encode(bytearray(raw_address + double_sha256(raw_address)[0:4]))
|
|
|
|
|
2020-01-03 04:18:49 +01:00
|
|
|
@staticmethod
|
2020-09-22 03:46:41 +02:00
|
|
|
def private_key_to_wif(private_key):
|
|
|
|
return b'\x1c' + private_key + b'\x01'
|
2020-01-03 04:18:49 +01:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def is_valid_address(cls, address):
|
|
|
|
decoded = Base58.decode_check(address)
|
|
|
|
return decoded[0] == cls.pubkey_address_prefix[0]
|
|
|
|
|
|
|
|
@classmethod
|
2020-05-01 15:28:51 +02:00
|
|
|
def valid_address_or_error(cls, address):
|
2020-03-25 18:17:08 +01:00
|
|
|
try:
|
2020-05-01 15:28:51 +02:00
|
|
|
assert cls.is_valid_address(address)
|
|
|
|
except:
|
|
|
|
raise Exception(f"'{address}' is not a valid address")
|
2020-03-25 18:17:08 +01:00
|
|
|
|
2020-05-18 14:22:23 +02:00
|
|
|
@staticmethod
|
|
|
|
def valid_claim_id(claim_id: str):
|
|
|
|
if not len(claim_id) == 40:
|
|
|
|
raise Exception(f"Incorrect claimid length: {len(claim_id)}")
|
|
|
|
if set(claim_id).difference(hexdigits):
|
|
|
|
raise Exception("Claim id is not hex encoded")
|
|
|
|
|
2020-05-06 16:50:00 +02:00
|
|
|
@staticmethod
|
|
|
|
def valid_channel_name_or_error(name: str):
|
2019-05-22 19:08:22 +02:00
|
|
|
try:
|
2020-05-01 15:28:51 +02:00
|
|
|
if not name:
|
2020-05-06 16:50:00 +02:00
|
|
|
raise Exception("Channel name cannot be blank.")
|
2020-05-01 15:28:51 +02:00
|
|
|
parsed = URL.parse(name)
|
|
|
|
if not parsed.has_channel:
|
|
|
|
raise Exception("Channel names must start with '@' symbol.")
|
|
|
|
if parsed.channel.name != name:
|
|
|
|
raise Exception("Channel name has invalid character")
|
|
|
|
except (TypeError, ValueError):
|
|
|
|
raise Exception("Invalid channel name.")
|
2018-07-12 05:18:59 +02:00
|
|
|
|
2020-05-06 16:50:00 +02:00
|
|
|
@staticmethod
|
|
|
|
def valid_stream_name_or_error(name: str):
|
|
|
|
try:
|
|
|
|
if not name:
|
|
|
|
raise Exception('Stream name cannot be blank.')
|
|
|
|
parsed = URL.parse(name)
|
|
|
|
if parsed.has_channel:
|
|
|
|
raise Exception(
|
|
|
|
"Stream names cannot start with '@' symbol. This is reserved for channels claims."
|
|
|
|
)
|
|
|
|
if not parsed.has_stream or parsed.stream.name != name:
|
|
|
|
raise Exception('Stream name has invalid characters.')
|
|
|
|
except (TypeError, ValueError):
|
|
|
|
raise Exception("Invalid stream name.")
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def valid_collection_name_or_error(name: str):
|
|
|
|
try:
|
|
|
|
if not name:
|
|
|
|
raise Exception('Collection name cannot be blank.')
|
|
|
|
parsed = URL.parse(name)
|
|
|
|
if parsed.has_channel:
|
|
|
|
raise Exception(
|
|
|
|
"Collection names cannot start with '@' symbol. This is reserved for channels claims."
|
|
|
|
)
|
|
|
|
if not parsed.has_stream or parsed.stream.name != name:
|
|
|
|
raise Exception('Collection name has invalid characters.')
|
|
|
|
except (TypeError, ValueError):
|
|
|
|
raise Exception("Invalid collection name.")
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def get_dewies_or_error(argument: str, lbc: str, positive_value=False):
|
|
|
|
try:
|
|
|
|
dewies = lbc_to_dewies(lbc)
|
|
|
|
if positive_value and dewies <= 0:
|
|
|
|
raise ValueError(f"'{argument}' value must be greater than 0.0")
|
|
|
|
return dewies
|
|
|
|
except ValueError as e:
|
|
|
|
raise ValueError(f"Invalid value for '{argument}': {e.args[0]}")
|
|
|
|
|
|
|
|
def get_fee_address(self, kwargs: dict, claim_address: str) -> str:
|
|
|
|
if 'fee_address' in kwargs:
|
|
|
|
self.valid_address_or_error(kwargs['fee_address'])
|
|
|
|
return kwargs['fee_address']
|
|
|
|
if 'fee_currency' in kwargs or 'fee_amount' in kwargs:
|
|
|
|
return claim_address
|
|
|
|
|
2018-06-12 17:53:29 +02:00
|
|
|
|
2020-01-03 04:18:49 +01:00
|
|
|
class TestNetLedger(Ledger):
|
2018-06-12 17:53:29 +02:00
|
|
|
network_name = 'testnet'
|
2018-10-15 23:16:43 +02:00
|
|
|
pubkey_address_prefix = bytes((111,))
|
|
|
|
script_address_prefix = bytes((196,))
|
2018-06-12 17:53:29 +02:00
|
|
|
extended_public_key_prefix = unhexlify('043587cf')
|
|
|
|
extended_private_key_prefix = unhexlify('04358394')
|
2020-04-02 20:31:03 +02:00
|
|
|
checkpoints = {}
|
2018-04-30 09:04:52 +02:00
|
|
|
|
|
|
|
|
2020-01-03 04:18:49 +01:00
|
|
|
class RegTestLedger(Ledger):
|
2018-06-12 17:53:29 +02:00
|
|
|
network_name = 'regtest'
|
2018-08-16 07:38:28 +02:00
|
|
|
headers_class = UnvalidatedHeaders
|
2018-10-15 23:16:43 +02:00
|
|
|
pubkey_address_prefix = bytes((111,))
|
|
|
|
script_address_prefix = bytes((196,))
|
2018-06-12 17:53:29 +02:00
|
|
|
extended_public_key_prefix = unhexlify('043587cf')
|
|
|
|
extended_private_key_prefix = unhexlify('04358394')
|
2018-04-30 09:04:52 +02:00
|
|
|
|
|
|
|
max_target = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
|
|
|
genesis_hash = '6e3fcf1299d4ec5d79c3a4c91d624a4acf9e2e173d95a1a0504f677669687556'
|
|
|
|
genesis_bits = 0x207fffff
|
|
|
|
target_timespan = 1
|
2020-04-02 20:31:03 +02:00
|
|
|
checkpoints = {}
|
2020-09-17 01:50:51 +02:00
|
|
|
|
|
|
|
|
|
|
|
def ledger_class_from_name(name) -> Type[Ledger]:
|
|
|
|
return {
|
|
|
|
Ledger.network_name: Ledger,
|
|
|
|
TestNetLedger.network_name: TestNetLedger,
|
|
|
|
RegTestLedger.network_name: RegTestLedger
|
|
|
|
}[name]
|