protocol: Validate CLTV block values

This commit is contained in:
tecnovert 2022-09-26 19:20:52 +02:00
parent 46d1856f71
commit ef0f5ea1ea
No known key found for this signature in database
GPG key ID: 8ED6D8750C4E3F93
3 changed files with 21 additions and 16 deletions

View file

@ -2415,21 +2415,9 @@ class BasicSwap(BaseApp):
initiate_tx_block_time = int(self.callcoinrpc(coin_from, 'getblock', [initiate_tx_block_hash, ])['time']) initiate_tx_block_time = int(self.callcoinrpc(coin_from, 'getblock', [initiate_tx_block_hash, ])['time'])
if offer.lock_type == TxLockTypes.ABS_LOCK_BLOCKS: if offer.lock_type == TxLockTypes.ABS_LOCK_BLOCKS:
# Walk the coin_to chain back until block time matches # Walk the coin_to chain back until block time matches
blockchaininfo = self.callcoinrpc(coin_to, 'getblockchaininfo') block_header_at = ci_to.getBlockHeaderAt(initiate_tx_block_time, block_after=True)
cblock_hash = blockchaininfo['bestblockhash'] cblock_hash = block_header_at['hash']
cblock_height = blockchaininfo['blocks'] cblock_height = block_header_at['height']
max_tries = 1000
for i in range(max_tries):
prev_block = self.callcoinrpc(coin_to, 'getblock', [cblock_hash, ])
self.log.debug('prev_block %s', str(prev_block))
if prev_block['time'] <= initiate_tx_block_time:
break
# cblock_hash and height are out of step unless loop breaks
cblock_hash = prev_block['previousblockhash']
cblock_height = prev_block['height']
ensure(prev_block['time'] <= initiate_tx_block_time, 'Block not found for lock height')
self.log.debug('Setting lock value from height of block %s %s', coin_to, cblock_hash) self.log.debug('Setting lock value from height of block %s %s', coin_to, cblock_hash)
contract_lock_value = cblock_height + lock_value contract_lock_value = cblock_height + lock_value
@ -4084,7 +4072,10 @@ class BasicSwap(BaseApp):
ensure(script_lock_value == expect_sequence, 'sequence mismatch') ensure(script_lock_value == expect_sequence, 'sequence mismatch')
else: else:
if offer.lock_type == TxLockTypes.ABS_LOCK_BLOCKS: if offer.lock_type == TxLockTypes.ABS_LOCK_BLOCKS:
self.log.warning('TODO: validate absolute lock values') block_header_from = ci_from.getBlockHeaderAt(bid.created_at)
chain_height_at_bid_creation = block_header_from['height']
ensure(script_lock_value <= chain_height_at_bid_creation + offer.lock_value + atomic_swap_1.ABS_LOCK_BLOCKS_LEEWAY, 'script lock height too high')
ensure(script_lock_value >= chain_height_at_bid_creation + offer.lock_value - atomic_swap_1.ABS_LOCK_BLOCKS_LEEWAY, 'script lock height too low')
else: else:
ensure(script_lock_value <= bid.created_at + offer.lock_value + atomic_swap_1.INITIATE_TX_TIMEOUT, 'script lock time too high') ensure(script_lock_value <= bid.created_at + offer.lock_value + atomic_swap_1.INITIATE_TX_TIMEOUT, 'script lock time too high')
ensure(script_lock_value >= bid.created_at + offer.lock_value, 'script lock time too low') ensure(script_lock_value >= bid.created_at + offer.lock_value, 'script lock time too low')

View file

@ -243,6 +243,19 @@ class BTCInterface(CoinInterface):
def getBlockHeader(self, block_hash): def getBlockHeader(self, block_hash):
return self.rpc_callback('getblockheader', [block_hash]) return self.rpc_callback('getblockheader', [block_hash])
def getBlockHeaderAt(self, time, block_after=False):
blockchaininfo = self.rpc_callback('getblockchaininfo')
last_block_header = self.rpc_callback('getblockheader', [blockchaininfo['bestblockhash']])
max_tries = 5000
for i in range(max_tries):
prev_block_header = self.rpc_callback('getblock', [last_block_header['previousblockhash']])
if prev_block_header['time'] <= time:
return last_block_header if block_after else prev_block_header
last_block_header = prev_block_header
raise ValueError(f'Block header not found at time: {time}')
def initialiseWallet(self, key_bytes): def initialiseWallet(self, key_bytes):
key_wif = self.encodeKey(key_bytes) key_wif = self.encodeKey(key_bytes)

View file

@ -12,6 +12,7 @@ from basicswap.script import (
) )
INITIATE_TX_TIMEOUT = 40 * 60 # TODO: make variable per coin INITIATE_TX_TIMEOUT = 40 * 60 # TODO: make variable per coin
ABS_LOCK_BLOCKS_LEEWAY = 5
def buildContractScript(lock_val, secret_hash, pkh_redeem, pkh_refund, op_lock=OpCodes.OP_CHECKSEQUENCEVERIFY): def buildContractScript(lock_val, secret_hash, pkh_redeem, pkh_refund, op_lock=OpCodes.OP_CHECKSEQUENCEVERIFY):