Decred CSV test.

master^2
tecnovert 8 months ago
parent ab472c04be
commit 74c7072926
  1. 28
      basicswap/interface/dcr/dcr.py
  2. 19
      basicswap/interface/dcr/messages.py
  3. 9
      basicswap/interface/dcr/script.py
  4. 2
      basicswap/interface/nav.py
  5. 78
      tests/basicswap/extended/test_dcr.py
  6. 2
      tests/basicswap/extended/test_nav.py

@ -21,8 +21,8 @@ from basicswap.util.crypto import (
from basicswap.util.extkey import ExtKeyPair from basicswap.util.extkey import ExtKeyPair
from basicswap.util.integer import encode_varint from basicswap.util.integer import encode_varint
from basicswap.interface.dcr.rpc import make_rpc_func from basicswap.interface.dcr.rpc import make_rpc_func
from .messages import CTransaction, SigHashType, TxSerializeType from .messages import CTransaction, CTxOut, SigHashType, TxSerializeType
from .script import push_script_data from .script import push_script_data, OP_HASH160, OP_EQUAL, OP_DUP, OP_EQUALVERIFY, OP_CHECKSIG
from coincurve.keys import ( from coincurve.keys import (
PrivateKey PrivateKey
@ -128,6 +128,14 @@ class DCRInterface(Secp256k1Interface):
def nbK() -> int: # No. of bytes requires to encode a public key def nbK() -> int: # No. of bytes requires to encode a public key
return 33 return 33
@staticmethod
def txVersion() -> int:
return 2
@staticmethod
def txoType():
return CTxOut
def __init__(self, coin_settings, network, swap_client=None): def __init__(self, coin_settings, network, swap_client=None):
super().__init__(network) super().__init__(network)
self._rpc_host = coin_settings.get('rpchost', '127.0.0.1') self._rpc_host = coin_settings.get('rpchost', '127.0.0.1')
@ -169,6 +177,9 @@ class DCRInterface(Secp256k1Interface):
else: else:
self.rpc('getblockchaininfo') self.rpc('getblockchaininfo')
def getChainHeight(self) -> int:
return self.rpc('getblockcount')
def checkWallets(self) -> int: def checkWallets(self) -> int:
# Only one wallet possible? # Only one wallet possible?
return 1 return 1
@ -239,3 +250,16 @@ class DCRInterface(Secp256k1Interface):
def stripTxSignature(self, tx_bytes) -> bytes: def stripTxSignature(self, tx_bytes) -> bytes:
tx = self.loadTx(tx_bytes) tx = self.loadTx(tx_bytes)
return tx.serialize(TxSerializeType.NoWitness) return tx.serialize(TxSerializeType.NoWitness)
def getScriptDest(self, script: bytes) -> bytes:
# P2SH
script_hash = self.pkh(script)
assert len(script_hash) == 20
return OP_HASH160.to_bytes(1) + len(script_hash).to_bytes(1) + script_hash + OP_EQUAL.to_bytes(1)
def getPubkeyHashDest(self, pkh: bytes) -> bytes:
# P2PKH
assert len(pkh) == 20
return OP_DUP.to_bytes(1) + OP_HASH160.to_bytes(1) + len(pkh).to_bytes(1) + pkh + OP_EQUALVERIFY.to_bytes(1) + OP_CHECKSIG.to_bytes(1)

@ -32,9 +32,14 @@ class SignatureType(IntEnum):
STSchnorrSecp256k1 = 2 STSchnorrSecp256k1 = 2
class COutpoint: class COutPoint:
__slots__ = ('hash', 'n', 'tree') __slots__ = ('hash', 'n', 'tree')
def __init__(self, hash=0, n=0, tree=0):
self.hash = hash
self.n = n
self.tree = tree
def get_hash(self) -> bytes: def get_hash(self) -> bytes:
return self.hash.to_bytes(32, 'big') return self.hash.to_bytes(32, 'big')
@ -43,15 +48,23 @@ class CTxIn:
__slots__ = ('prevout', 'sequence', __slots__ = ('prevout', 'sequence',
'value_in', 'block_height', 'block_index', 'signature_script') # Witness 'value_in', 'block_height', 'block_index', 'signature_script') # Witness
def __init__(self, tx=None): def __init__(self, prevout=COutPoint(), sequence=0):
self.prevout = prevout
self.sequence = sequence
self.value_in = -1 self.value_in = -1
self.block_height = 0 self.block_height = 0
self.block_index = 0xffffffff self.block_index = 0xffffffff
self.signature_script = bytes()
class CTxOut: class CTxOut:
__slots__ = ('value', 'version', 'script_pubkey') __slots__ = ('value', 'version', 'script_pubkey')
def __init__(self, value=0, script_pubkey=bytes()):
self.value = value
self.version = 0
self.script_pubkey = script_pubkey
class CTransaction: class CTransaction:
__slots__ = ('hash', 'version', 'vin', 'vout', 'locktime', 'expiry') __slots__ = ('hash', 'version', 'vin', 'vout', 'locktime', 'expiry')
@ -83,7 +96,7 @@ class CTransaction:
for i in range(num_txin): for i in range(num_txin):
txi = CTxIn() txi = CTxIn()
txi.prevout = COutpoint() txi.prevout = COutPoint()
txi.prevout.hash = int.from_bytes(data[o:o + 32], 'little') txi.prevout.hash = int.from_bytes(data[o:o + 32], 'little')
o += 32 o += 32
txi.prevout.n = int.from_bytes(data[o:o + 4], 'little') txi.prevout.n = int.from_bytes(data[o:o + 4], 'little')

@ -9,9 +9,15 @@ OP_0 = 0x00
OP_DATA_1 = 0x01 OP_DATA_1 = 0x01
OP_1NEGATE = 0x4f OP_1NEGATE = 0x4f
OP_1 = 0x51 OP_1 = 0x51
OP_EQUAL = 0x87
OP_PUSHDATA1 = 0x4c OP_PUSHDATA1 = 0x4c
OP_PUSHDATA2 = 0x4d OP_PUSHDATA2 = 0x4d
OP_PUSHDATA4 = 0x4e OP_PUSHDATA4 = 0x4e
OP_DUP = 0x76
OP_EQUALVERIFY = 0x88
OP_HASH160 = 0xa9
OP_CHECKSIG = 0xac
OP_CHECKSEQUENCEVERIFY = 0xb2
def push_script_data(data_array: bytearray, data: bytes) -> None: def push_script_data(data_array: bytearray, data: bytes) -> None:
@ -28,7 +34,7 @@ def push_script_data(data_array: bytearray, data: bytes) -> None:
return return
if len_data < OP_PUSHDATA1: if len_data < OP_PUSHDATA1:
data_array += bytes(((OP_DATA_1 - 1) + len_data,)) data_array += len_data.to_bytes(1)
elif len_data <= 0xff: elif len_data <= 0xff:
data_array += bytes((OP_PUSHDATA1, len_data)) data_array += bytes((OP_PUSHDATA1, len_data))
elif len_data <= 0xffff: elif len_data <= 0xffff:
@ -36,4 +42,5 @@ def push_script_data(data_array: bytearray, data: bytes) -> None:
else: else:
data_array += bytes((OP_PUSHDATA4,)) + len_data.to_bytes(4, 'little') data_array += bytes((OP_PUSHDATA4,)) + len_data.to_bytes(4, 'little')
print('[rm] data_array', (data_array + data).hex())
data_array += data data_array += data

@ -488,7 +488,7 @@ class NAVInterface(BTCInterface):
return block_rv return block_rv
def getScriptScriptSig(self, script: bytes) -> bytearray: def getScriptScriptSig(self, script: bytes) -> bytes:
return self.getP2SHP2WSHScriptSig(script) return self.getP2SHP2WSHScriptSig(script)
def getScriptDest(self, script): def getScriptDest(self, script):

@ -36,6 +36,8 @@ from tests.basicswap.util import (
from tests.basicswap.test_xmr import BaseTest, test_delay_event from tests.basicswap.test_xmr import BaseTest, test_delay_event
from basicswap.interface.dcr import DCRInterface from basicswap.interface.dcr import DCRInterface
from basicswap.interface.dcr.messages import CTransaction, CTxIn, COutPoint
from basicswap.interface.dcr.script import OP_CHECKSEQUENCEVERIFY, push_script_data
from bin.basicswap_run import startDaemon from bin.basicswap_run import startDaemon
logger = logging.getLogger() logger = logging.getLogger()
@ -276,6 +278,7 @@ class Test(BaseTest):
frtx = ci0.rpc_wallet('fundrawtransaction', [rtx, account_from]) frtx = ci0.rpc_wallet('fundrawtransaction', [rtx, account_from])
f_decoded = ci0.rpc_wallet('decoderawtransaction', [frtx['hex'], ]) f_decoded = ci0.rpc_wallet('decoderawtransaction', [frtx['hex'], ])
assert (f_decoded['version'] == 1)
sfrtx = ci0.rpc_wallet('signrawtransaction', [frtx['hex']]) sfrtx = ci0.rpc_wallet('signrawtransaction', [frtx['hex']])
s_decoded = ci0.rpc_wallet('decoderawtransaction', [sfrtx['hex'], ]) s_decoded = ci0.rpc_wallet('decoderawtransaction', [sfrtx['hex'], ])
@ -330,6 +333,7 @@ class Test(BaseTest):
# Set prevout value # Set prevout value
ctx = ci0.loadTx(tx_bytes_signed) ctx = ci0.loadTx(tx_bytes_signed)
assert (ctx.vout[0].version == 0)
ctx.vin[0].value_in = ci0.make_int(prevout['amount']) ctx.vin[0].value_in = ci0.make_int(prevout['amount'])
tx_bytes_signed = ctx.serialize() tx_bytes_signed = ctx.serialize()
assert (tx_bytes_signed.hex() == sfrtx['hex']) assert (tx_bytes_signed.hex() == sfrtx['hex'])
@ -337,6 +341,80 @@ class Test(BaseTest):
sent_txid = ci0.rpc_wallet('sendrawtransaction', [tx_bytes_signed.hex(), ]) sent_txid = ci0.rpc_wallet('sendrawtransaction', [tx_bytes_signed.hex(), ])
assert (len(sent_txid) == 64) assert (len(sent_txid) == 64)
def test_004_csv(self):
logging.info('---------- Test {} csv'.format(self.test_coin_from.name))
swap_clients = self.swap_clients
ci0 = swap_clients[0].ci(self.test_coin_from)
script = bytearray()
push_script_data(script, bytes((3,)))
script += OP_CHECKSEQUENCEVERIFY.to_bytes(1)
script_dest = ci0.getScriptDest(script)
prevout_amount: int = ci0.make_int(1.1)
tx = CTransaction()
tx.version = ci0.txVersion()
tx.vout.append(ci0.txoType()(prevout_amount, script_dest))
tx_hex = tx.serialize().hex()
tx_decoded = ci0.rpc_wallet('decoderawtransaction', [tx_hex, ])
utxo_pos = None
script_address = None
for i, txo in enumerate(tx_decoded['vout']):
script_address = tx_decoded['vout'][0]['scriptPubKey']['addresses'][0]
addr_info = ci0.rpc_wallet('validateaddress', [script_address,])
if addr_info['isscript'] is True:
utxo_pos = i
break
assert (utxo_pos is not None)
accounts = ci0.rpc_wallet('listaccounts')
for account_from in accounts:
try:
frtx = ci0.rpc_wallet('fundrawtransaction', [tx_hex, account_from])
break
except Exception as e:
logging.warning('fundrawtransaction failed {}'.format(e))
sfrtx = ci0.rpc_wallet('signrawtransaction', [frtx['hex']])
sent_txid = ci0.rpc_wallet('sendrawtransaction', [sfrtx['hex'], ])
tx_spend = CTransaction()
tx_spend.version = ci0.txVersion()
tx_spend.vin.append(CTxIn(COutPoint(int(sent_txid, 16), utxo_pos), sequence=3))
tx_spend.vin[0].value_in = prevout_amount
signature_script = bytearray()
push_script_data(signature_script, script)
tx_spend.vin[0].signature_script = signature_script
addr_out = ci0.rpc_wallet('getnewaddress')
pkh = ci0.decode_address(addr_out)[2:]
tx_spend.vout.append(ci0.txoType()())
tx_spend.vout[0].value = ci0.make_int(1.09)
tx_spend.vout[0].script_pubkey = ci0.getPubkeyHashDest(pkh)
tx_spend_hex = tx_spend.serialize().hex()
try:
sent_spend_txid = ci0.rpc_wallet('sendrawtransaction', [tx_spend_hex, ])
except Exception as e:
assert ('transaction sequence locks on inputs not met' in str(e))
else:
assert False, 'Should fail'
sent_spend_txid = None
for i in range(20):
try:
sent_spend_txid = ci0.rpc_wallet('sendrawtransaction', [tx_spend_hex, ])
break
except Exception as e:
logging.info('sendrawtransaction failed {}, height {}'.format(e, ci0.getChainHeight()))
test_delay_event.wait(1)
assert (sent_spend_txid is not None)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

@ -425,7 +425,7 @@ class Test(TestFunctions):
tx.vout.append(ci.txoType()(ci.make_int(1.1), script_dest)) tx.vout.append(ci.txoType()(ci.make_int(1.1), script_dest))
tx_hex = ToHex(tx) tx_hex = ToHex(tx)
tx_funded = self.callnoderpc('fundrawtransaction', [tx_hex]) tx_funded = self.callnoderpc('fundrawtransaction', [tx_hex])
utxo_pos = 0 if tx_funded['changepos'] == 1 else 1 utxo_pos: int = 0 if tx_funded['changepos'] == 1 else 1
tx_signed = self.callnoderpc('signrawtransaction', [tx_funded['hex'], ])['hex'] tx_signed = self.callnoderpc('signrawtransaction', [tx_funded['hex'], ])['hex']
self.sync_blocks() self.sync_blocks()
txid = self.callnoderpc('sendrawtransaction', [tx_signed, ]) txid = self.callnoderpc('sendrawtransaction', [tx_signed, ])

Loading…
Cancel
Save