From d10a88c79bf55d76c4770c33f04d5e0b27f8cf8b Mon Sep 17 00:00:00 2001 From: Lex Berezhny Date: Mon, 21 Sep 2020 21:46:41 -0400 Subject: [PATCH] script hash addresses --- lbry/blockchain/ledger.py | 31 +++++++++++++---------- lbry/blockchain/transaction.py | 5 +++- lbry/wallet/wallet.py | 2 +- tests/unit/blockchain/test_transaction.py | 25 ++++++++++++++++++ tests/unit/wallet/test_database.py | 4 +-- 5 files changed, 50 insertions(+), 17 deletions(-) diff --git a/lbry/blockchain/ledger.py b/lbry/blockchain/ledger.py index d0ec5d582..4cd85d71b 100644 --- a/lbry/blockchain/ledger.py +++ b/lbry/blockchain/ledger.py @@ -45,15 +45,28 @@ class Ledger: def get_id(cls): return '{}_{}'.format(cls.symbol.lower(), cls.network_name.lower()) - @classmethod - def hash160_to_address(cls, h160): - raw_address = cls.pubkey_address_prefix + h160 - return Base58.encode(bytearray(raw_address + double_sha256(raw_address)[0:4])) - @staticmethod def address_to_hash160(address) -> bytes: return Base58.decode(address)[1:21] + @classmethod + def pubkey_hash_to_address(cls, h160): + raw_address = cls.pubkey_address_prefix + h160 + return Base58.encode(bytearray(raw_address + double_sha256(raw_address)[0:4])) + + @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])) + + @staticmethod + def private_key_to_wif(private_key): + return b'\x1c' + private_key + b'\x01' + @classmethod def is_valid_address(cls, address): decoded = Base58.decode_check(address) @@ -133,14 +146,6 @@ class Ledger: if 'fee_currency' in kwargs or 'fee_amount' in kwargs: return claim_address - @classmethod - def public_key_to_address(cls, public_key): - return cls.hash160_to_address(hash160(public_key)) - - @staticmethod - def private_key_to_wif(private_key): - return b'\x1c' + private_key + b'\x01' - class TestNetLedger(Ledger): network_name = 'testnet' diff --git a/lbry/blockchain/transaction.py b/lbry/blockchain/transaction.py index 4abe38541..e99bfcd4d 100644 --- a/lbry/blockchain/transaction.py +++ b/lbry/blockchain/transaction.py @@ -282,11 +282,14 @@ class Output(InputOutput): def has_address(self): return ( 'pubkey_hash' in self.script.values or + 'script_hash' in self.script.values or 'pubkey' in self.script.values ) def get_address(self, ledger): - return ledger.hash160_to_address(self.pubkey_hash) + if 'script_hash' in self.script.values: + return ledger.script_hash_to_address(self.script.values['script_hash']) + return ledger.pubkey_hash_to_address(self.pubkey_hash) @classmethod def pay_pubkey_hash(cls, amount, pubkey_hash): diff --git a/lbry/wallet/wallet.py b/lbry/wallet/wallet.py index 85296bf26..9b1cb29fb 100644 --- a/lbry/wallet/wallet.py +++ b/lbry/wallet/wallet.py @@ -334,7 +334,7 @@ class Wallet: assert txi.txo_ref.txo is not None txo_script = txi.txo_ref.txo.script if txo_script.is_pay_pubkey_hash: - address = self.ledger.hash160_to_address(txo_script.values['pubkey_hash']) + address = self.ledger.pubkey_hash_to_address(txo_script.values['pubkey_hash']) private_key = await self.get_private_key_for_address(address) assert private_key is not None, 'Cannot find private key for signing output.' serialized = tx._serialize_for_signature(i) diff --git a/tests/unit/blockchain/test_transaction.py b/tests/unit/blockchain/test_transaction.py index d522c07c5..649cc6022 100644 --- a/tests/unit/blockchain/test_transaction.py +++ b/tests/unit/blockchain/test_transaction.py @@ -96,6 +96,10 @@ class TestAccountBalanceImpactFromTransaction(TestCase): class TestTransactionSerialization(TestCase): + def setUp(self): + super().setUp() + self.ledger = Ledger(Config.with_null_dir()) + def test_genesis_transaction(self): raw = unhexlify( "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1f0" @@ -228,3 +232,24 @@ class TestTransactionSerialization(TestCase): tx._reset() self.assertEqual(tx.raw, raw) + + def test_pubkey_address(self): + t = Transaction(unhexlify( + "010000000100000000000000000000000000000000000000000000000000000000" + "00000000ffffffff03510101ffffffff0100e1f505000000002321024ca653fc09" + "4c95aa409430caf2eee08fa6e5fbbe78431e0ec9e7cd80193d98f9ac00000000" + )) + txo = t.outputs[0] + self.assertEqual(txo.script.template.name, 'pay_pubkey_full') + self.assertEqual(txo.get_address(self.ledger), 'bZi1WEjGtsdAwuZTnNNTCAZLxhHkiHec4m') + + def test_script_hash_address(self): + t = Transaction(unhexlify( + "020000000100000000000000000000000000000000000000000000000000000000" + "00000000ffffffff2403b5a50b04b9e6ba5e08810007e6675b03002f504c41594d" + "4f4e415f69735f676f6f642f000000000100c37ec60600000017a914fbbf4e9bfe" + "70f27fb8aacd5386acc57f7a5ff7f58700000000" + )) + txo = t.outputs[0] + self.assertEqual(txo.script.template.name, 'pay_script_hash') + self.assertEqual(txo.get_address(self.ledger), 'rVBhueRT9E8RPdVcpCdXV5gRiiXVjE6VD9') diff --git a/tests/unit/wallet/test_database.py b/tests/unit/wallet/test_database.py index a46522153..3bd6b0660 100644 --- a/tests/unit/wallet/test_database.py +++ b/tests/unit/wallet/test_database.py @@ -237,7 +237,7 @@ class TestQueries(AsyncioTestCase): async def create_tx_from_txo(self, txo, to_account, height): from_hash = txo.script.values['pubkey_hash'] - from_address = self.ledger.hash160_to_address(from_hash) + from_address = self.ledger.pubkey_hash_to_address(from_hash) to_address = await to_account.receiving.get_or_create_usable_address() to_hash = Ledger.address_to_hash160(to_address) tx = Transaction(height=height, is_verified=True) \ @@ -250,7 +250,7 @@ class TestQueries(AsyncioTestCase): async def create_tx_to_nowhere(self, txo, height): from_hash = txo.script.values['pubkey_hash'] - from_address = self.ledger.hash160_to_address(from_hash) + from_address = self.ledger.pubkey_hash_to_address(from_hash) to_hash = NULL_HASH tx = Transaction(height=height, is_verified=True) \ .add_inputs([self.txi(txo)]) \