create appropriate script for scripthash address

This commit is contained in:
Lex Berezhny 2021-06-02 09:51:08 -04:00
parent 550ef9a1c4
commit fe469ae57f
11 changed files with 75 additions and 25 deletions

View file

@ -1477,12 +1477,21 @@ class Daemon(metaclass=JSONRPCServerType):
outputs = []
for address in addresses:
self.valid_address_or_error(address)
self.valid_address_or_error(address, allow_script_address=True)
if self.ledger.is_pubkey_address(address):
outputs.append(
Output.pay_pubkey_hash(
amount, self.ledger.address_to_hash160(address)
)
)
elif self.ledger.is_script_address(address):
outputs.append(
Output.pay_script_hash(
amount, self.ledger.address_to_hash160(address)
)
)
else:
raise ValueError(f"Unsupported address: '{address}'")
tx = await Transaction.create(
[], outputs, accounts, account
@ -5525,9 +5534,11 @@ class Daemon(metaclass=JSONRPCServerType):
async def broadcast_or_release(self, tx, blocking=False):
await self.wallet_manager.broadcast_or_release(tx, blocking)
def valid_address_or_error(self, address):
def valid_address_or_error(self, address, allow_script_address=False):
try:
assert self.ledger.is_valid_address(address)
assert self.ledger.is_pubkey_address(address) or (
allow_script_address and self.ledger.is_script_address(address)
)
except:
raise Exception(f"'{address}' is not a valid address")

View file

@ -577,6 +577,11 @@ class CommandTestCase(IntegrationTestCase):
self.daemon.jsonrpc_support_abandon(*args, **kwargs), confirm
)
async def account_send(self, *args, confirm=True, **kwargs):
return await self.confirm_and_render(
self.daemon.jsonrpc_account_send(*args, **kwargs), confirm
)
async def wallet_send(self, *args, confirm=True, **kwargs):
return await self.confirm_and_render(
self.daemon.jsonrpc_wallet_send(*args, **kwargs), confirm

View file

@ -769,9 +769,10 @@ class Database(SQLiteMixin):
conn.execute(*self._insert_sql(
"txo", self.txo_to_row(tx, txo), ignore_duplicate=True
)).fetchall()
elif txo.script.is_pay_script_hash:
# TODO: implement script hash payments
log.warning('Database.save_transaction_io: pay script hash is not implemented!')
elif txo.script.is_pay_script_hash and is_my_input:
conn.execute(*self._insert_sql(
"txo", self.txo_to_row(tx, txo), ignore_duplicate=True
)).fetchall()
def save_transaction_io(self, tx: Transaction, address, txhash, history):
return self.save_transaction_io_batch([tx], address, txhash, history)

View file

@ -178,17 +178,24 @@ class Ledger(metaclass=LedgerRegistry):
raw_address = cls.pubkey_address_prefix + h160
return Base58.encode(bytearray(raw_address + double_sha256(raw_address)[0:4]))
@classmethod
def hash160_to_script_address(cls, h160):
raw_address = cls.script_address_prefix + h160
return Base58.encode(bytearray(raw_address + double_sha256(raw_address)[0:4]))
@staticmethod
def address_to_hash160(address):
return Base58.decode(address)[1:21]
@classmethod
def is_valid_address(cls, address):
def is_pubkey_address(cls, address):
decoded = Base58.decode_check(address)
return (
decoded[0] == cls.pubkey_address_prefix[0] or
decoded[0] == cls.script_address_prefix[0]
)
return decoded[0] == cls.pubkey_address_prefix[0]
@classmethod
def is_script_address(cls, address):
decoded = Base58.decode_check(address)
return decoded[0] == cls.script_address_prefix[0]
@classmethod
def public_key_to_address(cls, public_key):
@ -1192,6 +1199,7 @@ class TestNetLedger(Ledger):
extended_private_key_prefix = unhexlify('04358394')
checkpoints = {}
class RegTestLedger(Ledger):
network_name = 'regtest'
headers_class = UnvalidatedHeaders

View file

@ -410,7 +410,7 @@ class BlockchainNode:
return self._cli_cmnd('getnewaddress', "", address_type)
async def get_balance(self):
return float(await self._cli_cmnd('getbalance'))
return await self._cli_cmnd('getbalance')
def send_to_address(self, address, amount):
return self._cli_cmnd('sendtoaddress', address, str(amount))

View file

@ -276,12 +276,19 @@ class Output(InputOutput):
def pubkey_hash(self):
return self.script.values['pubkey_hash']
@property
def script_hash(self):
return self.script.values['script_hash']
@property
def has_address(self):
return 'pubkey_hash' in self.script.values
def get_address(self, ledger):
if self.script.is_pay_pubkey_hash:
return ledger.hash160_to_address(self.pubkey_hash)
elif self.script.is_pay_script_hash:
return ledger.hash160_to_script_address(self.script_hash)
def get_estimator(self, ledger):
return OutputEffectiveAmountEstimator(ledger, self)
@ -290,6 +297,10 @@ class Output(InputOutput):
def pay_pubkey_hash(cls, amount, pubkey_hash):
return cls(amount, OutputScript.pay_pubkey_hash(pubkey_hash))
@classmethod
def pay_script_hash(cls, amount, pubkey_hash):
return cls(amount, OutputScript.pay_script_hash(pubkey_hash))
@classmethod
def deserialize_from(cls, stream):
return cls(

View file

@ -35,7 +35,7 @@ class WalletServerPayer:
if not address or not amount:
continue
if not self.ledger.is_valid_address(address):
if not self.ledger.is_pubkey_address(address):
self._on_payment_controller.add_error(ServerPaymentInvalidAddressError(address))
continue

View file

@ -44,7 +44,7 @@ class PurchaseCommandTests(CommandTestCase):
await self.account.release_all_outputs()
buyer_balance = await self.account.get_balance()
merchant_balance = lbc_to_dewies(str(await self.blockchain.get_balance()))
merchant_balance = lbc_to_dewies(await self.blockchain.get_balance())
pre_purchase_count = (await self.daemon.jsonrpc_purchase_list())['total_items']
purchase = await operation()
stream_txo, purchase_txo = stream.outputs[0], purchase.outputs[0]
@ -60,7 +60,9 @@ class PurchaseCommandTests(CommandTestCase):
self.assertEqual(
await self.account.get_balance(), buyer_balance - (purchase.input_sum-purchase.outputs[2].amount))
self.assertEqual(
str(await self.blockchain.get_balance()), dewies_to_lbc(merchant_balance + purchase_txo.amount))
await self.blockchain.get_balance(),
dewies_to_lbc(merchant_balance + purchase_txo.amount)
)
purchases = await self.daemon.jsonrpc_purchase_list()
self.assertEqual(purchases['total_items'], pre_purchase_count+1)

View file

@ -57,6 +57,17 @@ class WalletCommands(CommandTestCase):
self.assertEqual(len(status['wallet']['servers']), 1)
self.assertEqual(status['wallet']['servers'][0]['port'], 54320)
async def test_sending_to_scripthash_address(self):
self.assertEqual(await self.blockchain.get_balance(), '95.99973580')
await self.assertBalance(self.account, '10.0')
p2sh_address1 = await self.blockchain.get_new_address(self.blockchain.P2SH_SEGWIT_ADDRESS)
await self.account_send('2.0', p2sh_address1)
self.assertEqual(await self.blockchain.get_balance(), '98.99973580') # +1 lbc for confirm block
await self.assertBalance(self.account, '7.999877')
await self.wallet_send('3.0', p2sh_address1)
self.assertEqual(await self.blockchain.get_balance(), '102.99973580') # +1 lbc for confirm block
await self.assertBalance(self.account, '4.999754')
async def test_balance_caching(self):
account2 = await self.daemon.jsonrpc_account_create("Tip-er")
address2 = await self.daemon.jsonrpc_address_unused(account2.id)

View file

@ -410,11 +410,12 @@ class FileCommands(CommandTestCase):
await asyncio.wait_for(self.wait_files_to_complete(), timeout=1)
# check that the fee was received
starting_balance = await self.blockchain.get_balance()
starting_balance = float(await self.blockchain.get_balance())
await self.generate(1)
block_reward_and_claim_fee = 2.0
self.assertEqual(
await self.blockchain.get_balance(), starting_balance + block_reward_and_claim_fee
float(await self.blockchain.get_balance()),
starting_balance + block_reward_and_claim_fee
)
# restart the daemon and make sure the fee is still there
@ -463,11 +464,11 @@ class FileCommands(CommandTestCase):
self.assertItemCount(await self.daemon.jsonrpc_file_list(), 1)
# Assert the transaction is recorded to the blockchain
starting_balance = await self.blockchain.get_balance()
starting_balance = float(await self.blockchain.get_balance())
await self.generate(1)
block_reward_and_claim_fee = 2.0
self.assertEqual(
await self.blockchain.get_balance(), starting_balance + block_reward_and_claim_fee
float(await self.blockchain.get_balance()), starting_balance + block_reward_and_claim_fee
)
async def test_null_fee(self):

View file

@ -89,7 +89,7 @@ class LedgerTestCase(AsyncioTestCase):
class TestUtils(TestCase):
def test_valid_address(self):
self.assertTrue(Ledger.is_valid_address("rCz6yb1p33oYHToGZDzTjX7nFKaU3kNgBd"))
self.assertTrue(Ledger.is_script_address("rCz6yb1p33oYHToGZDzTjX7nFKaU3kNgBd"))
class TestSynchronization(LedgerTestCase):