Host-customized fork of https://github.com/tecnovert/basicswap/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
143 lines
4.6 KiB
143 lines
4.6 KiB
# -*- coding: utf-8 -*- |
|
|
|
# Copyright (c) 2020-2024 tecnovert |
|
# Distributed under the MIT software license, see the accompanying |
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php. |
|
|
|
from basicswap.db import ( |
|
Concepts, |
|
) |
|
from basicswap.util import ( |
|
SerialiseNum, |
|
) |
|
from basicswap.util.script import ( |
|
decodeScriptNum, |
|
) |
|
from basicswap.script import ( |
|
OpCodes, |
|
) |
|
from basicswap.basicswap_util import ( |
|
EventLogTypes, |
|
SwapTypes, |
|
) |
|
from . import ProtocolInterface |
|
|
|
INITIATE_TX_TIMEOUT = 40 * 60 # TODO: make variable per coin |
|
ABS_LOCK_TIME_LEEWAY = 10 * 60 |
|
|
|
|
|
def buildContractScript(lock_val: int, secret_hash: bytes, pkh_redeem: bytes, pkh_refund: bytes, op_lock=OpCodes.OP_CHECKSEQUENCEVERIFY, op_hash=OpCodes.OP_SHA256) -> bytearray: |
|
script = bytearray([ |
|
OpCodes.OP_IF, |
|
OpCodes.OP_SIZE, |
|
0x01, 0x20, # 32 |
|
OpCodes.OP_EQUALVERIFY, |
|
op_hash, |
|
0x20]) \ |
|
+ secret_hash \ |
|
+ bytearray([ |
|
OpCodes.OP_EQUALVERIFY, |
|
OpCodes.OP_DUP, |
|
OpCodes.OP_HASH160, |
|
0x14]) \ |
|
+ pkh_redeem \ |
|
+ bytearray([OpCodes.OP_ELSE, ]) \ |
|
+ SerialiseNum(lock_val) \ |
|
+ bytearray([ |
|
op_lock, |
|
OpCodes.OP_DROP, |
|
OpCodes.OP_DUP, |
|
OpCodes.OP_HASH160, |
|
0x14]) \ |
|
+ pkh_refund \ |
|
+ bytearray([ |
|
OpCodes.OP_ENDIF, |
|
OpCodes.OP_EQUALVERIFY, |
|
OpCodes.OP_CHECKSIG]) |
|
return script |
|
|
|
|
|
def verifyContractScript(script, op_lock=OpCodes.OP_CHECKSEQUENCEVERIFY, op_hash=OpCodes.OP_SHA256): |
|
if script[0] != OpCodes.OP_IF or \ |
|
script[1] != OpCodes.OP_SIZE or \ |
|
script[2] != 0x01 or script[3] != 0x20 or \ |
|
script[4] != OpCodes.OP_EQUALVERIFY or \ |
|
script[5] != op_hash or \ |
|
script[6] != 0x20: |
|
return False, None, None, None, None |
|
o = 7 |
|
script_hash = script[o: o + 32] |
|
o += 32 |
|
if script[o] != OpCodes.OP_EQUALVERIFY or \ |
|
script[o + 1] != OpCodes.OP_DUP or \ |
|
script[o + 2] != OpCodes.OP_HASH160 or \ |
|
script[o + 3] != 0x14: |
|
return False, script_hash, None, None, None |
|
o += 4 |
|
pkh_redeem = script[o: o + 20] |
|
o += 20 |
|
if script[o] != OpCodes.OP_ELSE: |
|
return False, script_hash, pkh_redeem, None, None |
|
o += 1 |
|
lock_val, nb = decodeScriptNum(script, o) |
|
o += nb |
|
if script[o] != op_lock or \ |
|
script[o + 1] != OpCodes.OP_DROP or \ |
|
script[o + 2] != OpCodes.OP_DUP or \ |
|
script[o + 3] != OpCodes.OP_HASH160 or \ |
|
script[o + 4] != 0x14: |
|
return False, script_hash, pkh_redeem, lock_val, None |
|
o += 5 |
|
pkh_refund = script[o: o + 20] |
|
o += 20 |
|
if script[o] != OpCodes.OP_ENDIF or \ |
|
script[o + 1] != OpCodes.OP_EQUALVERIFY or \ |
|
script[o + 2] != OpCodes.OP_CHECKSIG: |
|
return False, script_hash, pkh_redeem, lock_val, pkh_refund |
|
return True, script_hash, pkh_redeem, lock_val, pkh_refund |
|
|
|
|
|
def extractScriptSecretHash(script): |
|
return script[7:39] |
|
|
|
|
|
def redeemITx(self, bid_id: bytes, session): |
|
bid, offer = self.getBidAndOffer(bid_id, session) |
|
ci_from = self.ci(offer.coin_from) |
|
|
|
txn = self.createRedeemTxn(ci_from.coin_type(), bid, for_txn_type='initiate') |
|
txid = ci_from.publishTx(bytes.fromhex(txn)) |
|
|
|
bid.initiate_tx.spend_txid = bytes.fromhex(txid) |
|
self.log.debug('Submitted initiate redeem txn %s to %s chain for bid %s', txid, ci_from.coin_name(), bid_id.hex()) |
|
self.logEvent(Concepts.BID, bid_id, EventLogTypes.ITX_REDEEM_PUBLISHED, '', session) |
|
|
|
|
|
class AtomicSwapInterface(ProtocolInterface): |
|
swap_type = SwapTypes.SELLER_FIRST |
|
|
|
def getFundedInitiateTxTemplate(self, ci, amount: int, sub_fee: bool) -> bytes: |
|
addr_to = self.getMockAddrTo(ci) |
|
funded_tx = ci.createRawFundedTransaction(addr_to, amount, sub_fee, lock_unspents=False) |
|
|
|
return bytes.fromhex(funded_tx) |
|
|
|
def promoteMockTx(self, ci, mock_tx: bytes, script: bytearray) -> bytearray: |
|
mock_txo_script = self.getMockScriptScriptPubkey(ci) |
|
real_txo_script = ci.getScriptDest(script) if ci._use_segwit else ci.get_p2sh_script_pubkey(script) |
|
|
|
found: int = 0 |
|
ctx = ci.loadTx(mock_tx) |
|
for txo in ctx.vout: |
|
if txo.scriptPubKey == mock_txo_script: |
|
txo.scriptPubKey = real_txo_script |
|
found += 1 |
|
|
|
if found < 1: |
|
raise ValueError('Mocked output not found') |
|
if found > 1: |
|
raise ValueError('Too many mocked outputs found') |
|
ctx.nLockTime = 0 |
|
|
|
funded_tx = ctx.serialize() |
|
return ci.signTxWithWallet(funded_tx)
|
|
|