create appropriate script for scripthash address
This commit is contained in:
parent
550ef9a1c4
commit
fe469ae57f
11 changed files with 75 additions and 25 deletions
|
@ -1477,12 +1477,21 @@ class Daemon(metaclass=JSONRPCServerType):
|
|||
|
||||
outputs = []
|
||||
for address in addresses:
|
||||
self.valid_address_or_error(address)
|
||||
outputs.append(
|
||||
Output.pay_pubkey_hash(
|
||||
amount, self.ledger.address_to_hash160(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")
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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):
|
||||
return ledger.hash160_to_address(self.pubkey_hash)
|
||||
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(
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in a new issue