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 = []
|
outputs = []
|
||||||
for address in addresses:
|
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(
|
outputs.append(
|
||||||
Output.pay_pubkey_hash(
|
Output.pay_pubkey_hash(
|
||||||
amount, self.ledger.address_to_hash160(address)
|
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(
|
tx = await Transaction.create(
|
||||||
[], outputs, accounts, account
|
[], outputs, accounts, account
|
||||||
|
@ -5525,9 +5534,11 @@ class Daemon(metaclass=JSONRPCServerType):
|
||||||
async def broadcast_or_release(self, tx, blocking=False):
|
async def broadcast_or_release(self, tx, blocking=False):
|
||||||
await self.wallet_manager.broadcast_or_release(tx, blocking)
|
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:
|
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:
|
except:
|
||||||
raise Exception(f"'{address}' is not a valid address")
|
raise Exception(f"'{address}' is not a valid address")
|
||||||
|
|
||||||
|
|
|
@ -577,6 +577,11 @@ class CommandTestCase(IntegrationTestCase):
|
||||||
self.daemon.jsonrpc_support_abandon(*args, **kwargs), confirm
|
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):
|
async def wallet_send(self, *args, confirm=True, **kwargs):
|
||||||
return await self.confirm_and_render(
|
return await self.confirm_and_render(
|
||||||
self.daemon.jsonrpc_wallet_send(*args, **kwargs), confirm
|
self.daemon.jsonrpc_wallet_send(*args, **kwargs), confirm
|
||||||
|
|
|
@ -769,9 +769,10 @@ class Database(SQLiteMixin):
|
||||||
conn.execute(*self._insert_sql(
|
conn.execute(*self._insert_sql(
|
||||||
"txo", self.txo_to_row(tx, txo), ignore_duplicate=True
|
"txo", self.txo_to_row(tx, txo), ignore_duplicate=True
|
||||||
)).fetchall()
|
)).fetchall()
|
||||||
elif txo.script.is_pay_script_hash:
|
elif txo.script.is_pay_script_hash and is_my_input:
|
||||||
# TODO: implement script hash payments
|
conn.execute(*self._insert_sql(
|
||||||
log.warning('Database.save_transaction_io: pay script hash is not implemented!')
|
"txo", self.txo_to_row(tx, txo), ignore_duplicate=True
|
||||||
|
)).fetchall()
|
||||||
|
|
||||||
def save_transaction_io(self, tx: Transaction, address, txhash, history):
|
def save_transaction_io(self, tx: Transaction, address, txhash, history):
|
||||||
return self.save_transaction_io_batch([tx], 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
|
raw_address = cls.pubkey_address_prefix + h160
|
||||||
return Base58.encode(bytearray(raw_address + double_sha256(raw_address)[0:4]))
|
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
|
@staticmethod
|
||||||
def address_to_hash160(address):
|
def address_to_hash160(address):
|
||||||
return Base58.decode(address)[1:21]
|
return Base58.decode(address)[1:21]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_valid_address(cls, address):
|
def is_pubkey_address(cls, address):
|
||||||
decoded = Base58.decode_check(address)
|
decoded = Base58.decode_check(address)
|
||||||
return (
|
return decoded[0] == cls.pubkey_address_prefix[0]
|
||||||
decoded[0] == cls.pubkey_address_prefix[0] or
|
|
||||||
decoded[0] == cls.script_address_prefix[0]
|
@classmethod
|
||||||
)
|
def is_script_address(cls, address):
|
||||||
|
decoded = Base58.decode_check(address)
|
||||||
|
return decoded[0] == cls.script_address_prefix[0]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def public_key_to_address(cls, public_key):
|
def public_key_to_address(cls, public_key):
|
||||||
|
@ -1192,6 +1199,7 @@ class TestNetLedger(Ledger):
|
||||||
extended_private_key_prefix = unhexlify('04358394')
|
extended_private_key_prefix = unhexlify('04358394')
|
||||||
checkpoints = {}
|
checkpoints = {}
|
||||||
|
|
||||||
|
|
||||||
class RegTestLedger(Ledger):
|
class RegTestLedger(Ledger):
|
||||||
network_name = 'regtest'
|
network_name = 'regtest'
|
||||||
headers_class = UnvalidatedHeaders
|
headers_class = UnvalidatedHeaders
|
||||||
|
|
|
@ -410,7 +410,7 @@ class BlockchainNode:
|
||||||
return self._cli_cmnd('getnewaddress', "", address_type)
|
return self._cli_cmnd('getnewaddress', "", address_type)
|
||||||
|
|
||||||
async def get_balance(self):
|
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):
|
def send_to_address(self, address, amount):
|
||||||
return self._cli_cmnd('sendtoaddress', address, str(amount))
|
return self._cli_cmnd('sendtoaddress', address, str(amount))
|
||||||
|
|
|
@ -276,12 +276,19 @@ class Output(InputOutput):
|
||||||
def pubkey_hash(self):
|
def pubkey_hash(self):
|
||||||
return self.script.values['pubkey_hash']
|
return self.script.values['pubkey_hash']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def script_hash(self):
|
||||||
|
return self.script.values['script_hash']
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def has_address(self):
|
def has_address(self):
|
||||||
return 'pubkey_hash' in self.script.values
|
return 'pubkey_hash' in self.script.values
|
||||||
|
|
||||||
def get_address(self, ledger):
|
def get_address(self, ledger):
|
||||||
|
if self.script.is_pay_pubkey_hash:
|
||||||
return ledger.hash160_to_address(self.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):
|
def get_estimator(self, ledger):
|
||||||
return OutputEffectiveAmountEstimator(ledger, self)
|
return OutputEffectiveAmountEstimator(ledger, self)
|
||||||
|
@ -290,6 +297,10 @@ class Output(InputOutput):
|
||||||
def pay_pubkey_hash(cls, amount, pubkey_hash):
|
def pay_pubkey_hash(cls, amount, pubkey_hash):
|
||||||
return cls(amount, OutputScript.pay_pubkey_hash(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
|
@classmethod
|
||||||
def deserialize_from(cls, stream):
|
def deserialize_from(cls, stream):
|
||||||
return cls(
|
return cls(
|
||||||
|
|
|
@ -35,7 +35,7 @@ class WalletServerPayer:
|
||||||
if not address or not amount:
|
if not address or not amount:
|
||||||
continue
|
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))
|
self._on_payment_controller.add_error(ServerPaymentInvalidAddressError(address))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ class PurchaseCommandTests(CommandTestCase):
|
||||||
|
|
||||||
await self.account.release_all_outputs()
|
await self.account.release_all_outputs()
|
||||||
buyer_balance = await self.account.get_balance()
|
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']
|
pre_purchase_count = (await self.daemon.jsonrpc_purchase_list())['total_items']
|
||||||
purchase = await operation()
|
purchase = await operation()
|
||||||
stream_txo, purchase_txo = stream.outputs[0], purchase.outputs[0]
|
stream_txo, purchase_txo = stream.outputs[0], purchase.outputs[0]
|
||||||
|
@ -60,7 +60,9 @@ class PurchaseCommandTests(CommandTestCase):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
await self.account.get_balance(), buyer_balance - (purchase.input_sum-purchase.outputs[2].amount))
|
await self.account.get_balance(), buyer_balance - (purchase.input_sum-purchase.outputs[2].amount))
|
||||||
self.assertEqual(
|
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()
|
purchases = await self.daemon.jsonrpc_purchase_list()
|
||||||
self.assertEqual(purchases['total_items'], pre_purchase_count+1)
|
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(len(status['wallet']['servers']), 1)
|
||||||
self.assertEqual(status['wallet']['servers'][0]['port'], 54320)
|
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):
|
async def test_balance_caching(self):
|
||||||
account2 = await self.daemon.jsonrpc_account_create("Tip-er")
|
account2 = await self.daemon.jsonrpc_account_create("Tip-er")
|
||||||
address2 = await self.daemon.jsonrpc_address_unused(account2.id)
|
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)
|
await asyncio.wait_for(self.wait_files_to_complete(), timeout=1)
|
||||||
|
|
||||||
# check that the fee was received
|
# 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)
|
await self.generate(1)
|
||||||
block_reward_and_claim_fee = 2.0
|
block_reward_and_claim_fee = 2.0
|
||||||
self.assertEqual(
|
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
|
# 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)
|
self.assertItemCount(await self.daemon.jsonrpc_file_list(), 1)
|
||||||
|
|
||||||
# Assert the transaction is recorded to the blockchain
|
# 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)
|
await self.generate(1)
|
||||||
block_reward_and_claim_fee = 2.0
|
block_reward_and_claim_fee = 2.0
|
||||||
self.assertEqual(
|
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):
|
async def test_null_fee(self):
|
||||||
|
|
|
@ -89,7 +89,7 @@ class LedgerTestCase(AsyncioTestCase):
|
||||||
class TestUtils(TestCase):
|
class TestUtils(TestCase):
|
||||||
|
|
||||||
def test_valid_address(self):
|
def test_valid_address(self):
|
||||||
self.assertTrue(Ledger.is_valid_address("rCz6yb1p33oYHToGZDzTjX7nFKaU3kNgBd"))
|
self.assertTrue(Ledger.is_script_address("rCz6yb1p33oYHToGZDzTjX7nFKaU3kNgBd"))
|
||||||
|
|
||||||
|
|
||||||
class TestSynchronization(LedgerTestCase):
|
class TestSynchronization(LedgerTestCase):
|
||||||
|
|
Loading…
Reference in a new issue