mirror of
https://github.com/basicswap/basicswap.git
synced 2025-11-05 18:38:09 +01:00
backports
This commit is contained in:
@@ -640,7 +640,7 @@ class CTransaction:
|
|||||||
self.hash = tx.hash
|
self.hash = tx.hash
|
||||||
self.wit = copy.deepcopy(tx.wit)
|
self.wit = copy.deepcopy(tx.wit)
|
||||||
|
|
||||||
def deserialize(self, f):
|
def deserialize(self, f, allow_witness: bool = True):
|
||||||
self.nVersion = int.from_bytes(f.read(1), "little")
|
self.nVersion = int.from_bytes(f.read(1), "little")
|
||||||
if self.nVersion == PARTICL_TX_VERSION:
|
if self.nVersion == PARTICL_TX_VERSION:
|
||||||
self.nVersion |= int.from_bytes(f.read(1), "little") << 8
|
self.nVersion |= int.from_bytes(f.read(1), "little") << 8
|
||||||
@@ -668,7 +668,7 @@ class CTransaction:
|
|||||||
# self.nVersion = int.from_bytes(f.read(4), "little")
|
# self.nVersion = int.from_bytes(f.read(4), "little")
|
||||||
self.vin = deser_vector(f, CTxIn)
|
self.vin = deser_vector(f, CTxIn)
|
||||||
flags = 0
|
flags = 0
|
||||||
if len(self.vin) == 0:
|
if len(self.vin) == 0 and allow_witness:
|
||||||
flags = int.from_bytes(f.read(1), "little")
|
flags = int.from_bytes(f.read(1), "little")
|
||||||
# Not sure why flags can't be zero, but this
|
# Not sure why flags can't be zero, but this
|
||||||
# matches the implementation in bitcoind
|
# matches the implementation in bitcoind
|
||||||
|
|||||||
@@ -166,6 +166,9 @@ class WebsocketServer(ThreadingMixIn, TCPServer, API):
|
|||||||
def _message_received_(self, handler, msg):
|
def _message_received_(self, handler, msg):
|
||||||
self.message_received(self.handler_to_client(handler), self, msg)
|
self.message_received(self.handler_to_client(handler), self, msg)
|
||||||
|
|
||||||
|
def _binary_message_received_(self, handler, msg):
|
||||||
|
self.binary_message_received(self.handler_to_client(handler), self, msg)
|
||||||
|
|
||||||
def _ping_received_(self, handler, msg):
|
def _ping_received_(self, handler, msg):
|
||||||
handler.send_pong(msg)
|
handler.send_pong(msg)
|
||||||
|
|
||||||
@@ -309,6 +312,7 @@ class WebSocketHandler(StreamRequestHandler):
|
|||||||
opcode = b1 & OPCODE
|
opcode = b1 & OPCODE
|
||||||
masked = b2 & MASKED
|
masked = b2 & MASKED
|
||||||
payload_length = b2 & PAYLOAD_LEN
|
payload_length = b2 & PAYLOAD_LEN
|
||||||
|
is_binary: bool = False
|
||||||
|
|
||||||
if opcode == OPCODE_CLOSE_CONN:
|
if opcode == OPCODE_CLOSE_CONN:
|
||||||
logger.info("Client asked to close connection.")
|
logger.info("Client asked to close connection.")
|
||||||
@@ -322,8 +326,8 @@ class WebSocketHandler(StreamRequestHandler):
|
|||||||
logger.warning("Continuation frames are not supported.")
|
logger.warning("Continuation frames are not supported.")
|
||||||
return
|
return
|
||||||
elif opcode == OPCODE_BINARY:
|
elif opcode == OPCODE_BINARY:
|
||||||
logger.warning("Binary frames are not supported.")
|
is_binary = True
|
||||||
return
|
opcode_handler = self.server._binary_message_received_
|
||||||
elif opcode == OPCODE_TEXT:
|
elif opcode == OPCODE_TEXT:
|
||||||
opcode_handler = self.server._message_received_
|
opcode_handler = self.server._message_received_
|
||||||
elif opcode == OPCODE_PING:
|
elif opcode == OPCODE_PING:
|
||||||
@@ -345,7 +349,8 @@ class WebSocketHandler(StreamRequestHandler):
|
|||||||
for message_byte in self.read_bytes(payload_length):
|
for message_byte in self.read_bytes(payload_length):
|
||||||
message_byte ^= masks[len(message_bytes) % 4]
|
message_byte ^= masks[len(message_bytes) % 4]
|
||||||
message_bytes.append(message_byte)
|
message_bytes.append(message_byte)
|
||||||
opcode_handler(self, message_bytes.decode('utf8'))
|
|
||||||
|
opcode_handler(self, message_bytes if is_binary else message_bytes.decode('utf8'))
|
||||||
|
|
||||||
def send_message(self, message):
|
def send_message(self, message):
|
||||||
self.send_text(message)
|
self.send_text(message)
|
||||||
@@ -375,6 +380,35 @@ class WebSocketHandler(StreamRequestHandler):
|
|||||||
with self._send_lock:
|
with self._send_lock:
|
||||||
self.request.send(header + payload)
|
self.request.send(header + payload)
|
||||||
|
|
||||||
|
def send_bytes(self, message, opcode=OPCODE_BINARY):
|
||||||
|
header = bytearray()
|
||||||
|
payload = message
|
||||||
|
payload_length = len(payload)
|
||||||
|
|
||||||
|
# Normal payload
|
||||||
|
if payload_length <= 125:
|
||||||
|
header.append(FIN | opcode)
|
||||||
|
header.append(payload_length)
|
||||||
|
|
||||||
|
# Extended payload
|
||||||
|
elif payload_length >= 126 and payload_length <= 65535:
|
||||||
|
header.append(FIN | opcode)
|
||||||
|
header.append(PAYLOAD_LEN_EXT16)
|
||||||
|
header.extend(struct.pack(">H", payload_length))
|
||||||
|
|
||||||
|
# Huge extended payload
|
||||||
|
elif payload_length < 18446744073709551616:
|
||||||
|
header.append(FIN | opcode)
|
||||||
|
header.append(PAYLOAD_LEN_EXT64)
|
||||||
|
header.extend(struct.pack(">Q", payload_length))
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise Exception("Message is too big. Consider breaking it into chunks.")
|
||||||
|
return
|
||||||
|
|
||||||
|
with self._send_lock:
|
||||||
|
self.request.send(header + payload)
|
||||||
|
|
||||||
def send_text(self, message, opcode=OPCODE_TEXT):
|
def send_text(self, message, opcode=OPCODE_TEXT):
|
||||||
"""
|
"""
|
||||||
Important: Fragmented(=continuation) messages are not supported since
|
Important: Fragmented(=continuation) messages are not supported since
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import sqlite3
|
|||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from typing import Dict, Optional
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
from basicswap.basicswap_util import (
|
from basicswap.basicswap_util import (
|
||||||
getVoutByAddress,
|
getVoutByAddress,
|
||||||
@@ -77,17 +77,19 @@ from basicswap.contrib.test_framework.messages import (
|
|||||||
from basicswap.contrib.test_framework.script import (
|
from basicswap.contrib.test_framework.script import (
|
||||||
CScript,
|
CScript,
|
||||||
CScriptOp,
|
CScriptOp,
|
||||||
OP_IF,
|
|
||||||
OP_ELSE,
|
|
||||||
OP_ENDIF,
|
|
||||||
OP_0,
|
OP_0,
|
||||||
OP_2,
|
OP_2,
|
||||||
OP_CHECKSIG,
|
|
||||||
OP_CHECKMULTISIG,
|
OP_CHECKMULTISIG,
|
||||||
OP_CHECKSEQUENCEVERIFY,
|
OP_CHECKSEQUENCEVERIFY,
|
||||||
|
OP_CHECKSIG,
|
||||||
OP_DROP,
|
OP_DROP,
|
||||||
OP_HASH160,
|
OP_DUP,
|
||||||
|
OP_ELSE,
|
||||||
|
OP_ENDIF,
|
||||||
OP_EQUAL,
|
OP_EQUAL,
|
||||||
|
OP_EQUALVERIFY,
|
||||||
|
OP_HASH160,
|
||||||
|
OP_IF,
|
||||||
OP_RETURN,
|
OP_RETURN,
|
||||||
SIGHASH_ALL,
|
SIGHASH_ALL,
|
||||||
SegwitV0SignatureHash,
|
SegwitV0SignatureHash,
|
||||||
@@ -761,10 +763,11 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
# p2wpkh
|
# p2wpkh
|
||||||
return CScript([OP_0, pkh])
|
return CScript([OP_0, pkh])
|
||||||
|
|
||||||
def loadTx(self, tx_bytes: bytes) -> CTransaction:
|
def loadTx(self, tx_bytes: bytes, allow_witness: bool = True) -> CTransaction:
|
||||||
# Load tx from bytes to internal representation
|
# Load tx from bytes to internal representation
|
||||||
|
# Transactions with no inputs require allow_witness set to false to decode correctly
|
||||||
tx = CTransaction()
|
tx = CTransaction()
|
||||||
tx.deserialize(BytesIO(tx_bytes))
|
tx.deserialize(BytesIO(tx_bytes), allow_witness)
|
||||||
return tx
|
return tx
|
||||||
|
|
||||||
def createSCLockTx(
|
def createSCLockTx(
|
||||||
@@ -801,6 +804,35 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
CScriptOp(OP_ENDIF)])
|
CScriptOp(OP_ENDIF)])
|
||||||
# fmt: on
|
# fmt: on
|
||||||
|
|
||||||
|
def isScriptP2PKH(self, script: bytes) -> bool:
|
||||||
|
if len(script) != 25:
|
||||||
|
return False
|
||||||
|
if script[0] != OP_DUP:
|
||||||
|
return False
|
||||||
|
if script[1] != OP_HASH160:
|
||||||
|
return False
|
||||||
|
if script[2] != 20:
|
||||||
|
return False
|
||||||
|
if script[23] != OP_EQUALVERIFY:
|
||||||
|
return False
|
||||||
|
if script[24] != OP_CHECKSIG:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def isScriptP2WPKH(self, script: bytes) -> bool:
|
||||||
|
if len(script) != 22:
|
||||||
|
return False
|
||||||
|
if script[0] != OP_0:
|
||||||
|
return False
|
||||||
|
if script[1] != 20:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def getScriptDummyWitness(self, script: bytes) -> List[bytes]:
|
||||||
|
if self.isScriptP2WPKH(script):
|
||||||
|
return [bytes(72), bytes(33)]
|
||||||
|
raise ValueError("Unknown script type")
|
||||||
|
|
||||||
def createSCLockRefundTx(
|
def createSCLockRefundTx(
|
||||||
self,
|
self,
|
||||||
tx_lock_bytes,
|
tx_lock_bytes,
|
||||||
@@ -1959,6 +1991,9 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
def getBlockWithTxns(self, block_hash: str):
|
def getBlockWithTxns(self, block_hash: str):
|
||||||
return self.rpc("getblock", [block_hash, 2])
|
return self.rpc("getblock", [block_hash, 2])
|
||||||
|
|
||||||
|
def listUtxos(self):
|
||||||
|
return self.rpc_wallet("listunspent")
|
||||||
|
|
||||||
def getUnspentsByAddr(self):
|
def getUnspentsByAddr(self):
|
||||||
unspent_addr = dict()
|
unspent_addr = dict()
|
||||||
unspent = self.rpc_wallet("listunspent")
|
unspent = self.rpc_wallet("listunspent")
|
||||||
|
|||||||
@@ -521,7 +521,7 @@ class CTransaction(object):
|
|||||||
self.hash = tx.hash
|
self.hash = tx.hash
|
||||||
self.wit = copy.deepcopy(tx.wit)
|
self.wit = copy.deepcopy(tx.wit)
|
||||||
|
|
||||||
def deserialize(self, f):
|
def deserialize(self, f, allow_witness: bool = True):
|
||||||
ver32bit = struct.unpack("<i", f.read(4))[0]
|
ver32bit = struct.unpack("<i", f.read(4))[0]
|
||||||
self.nVersion = ver32bit & 0xffff
|
self.nVersion = ver32bit & 0xffff
|
||||||
self.nType = (ver32bit >> 16) & 0xffff
|
self.nType = (ver32bit >> 16) & 0xffff
|
||||||
|
|||||||
@@ -455,12 +455,12 @@ class CTransaction(object):
|
|||||||
self.wit = copy.deepcopy(tx.wit)
|
self.wit = copy.deepcopy(tx.wit)
|
||||||
self.strDZeel = copy.deepcopy(tx.strDZeel)
|
self.strDZeel = copy.deepcopy(tx.strDZeel)
|
||||||
|
|
||||||
def deserialize(self, f):
|
def deserialize(self, f, allow_witness: bool = True):
|
||||||
self.nVersion = struct.unpack("<i", f.read(4))[0]
|
self.nVersion = struct.unpack("<i", f.read(4))[0]
|
||||||
self.nTime = struct.unpack("<i", f.read(4))[0]
|
self.nTime = struct.unpack("<i", f.read(4))[0]
|
||||||
self.vin = deser_vector(f, CTxIn)
|
self.vin = deser_vector(f, CTxIn)
|
||||||
flags = 0
|
flags = 0
|
||||||
if len(self.vin) == 0:
|
if len(self.vin) == 0 and allow_witness:
|
||||||
flags = struct.unpack("<B", f.read(1))[0]
|
flags = struct.unpack("<B", f.read(1))[0]
|
||||||
# Not sure why flags can't be zero, but this
|
# Not sure why flags can't be zero, but this
|
||||||
# matches the implementation in bitcoind
|
# matches the implementation in bitcoind
|
||||||
|
|||||||
@@ -505,7 +505,7 @@ class CTransaction:
|
|||||||
self.sha256 = tx.sha256
|
self.sha256 = tx.sha256
|
||||||
self.hash = tx.hash
|
self.hash = tx.hash
|
||||||
|
|
||||||
def deserialize(self, f):
|
def deserialize(self, f, allow_witness: bool = True):
|
||||||
self.nVersion = struct.unpack("<h", f.read(2))[0]
|
self.nVersion = struct.unpack("<h", f.read(2))[0]
|
||||||
self.nType = struct.unpack("<h", f.read(2))[0]
|
self.nType = struct.unpack("<h", f.read(2))[0]
|
||||||
self.vin = deser_vector(f, CTxIn)
|
self.vin = deser_vector(f, CTxIn)
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import logging
|
|||||||
import random
|
import random
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
|
||||||
from basicswap.basicswap_util import getVoutByScriptPubKey, TxLockTypes
|
from basicswap.basicswap_util import getVoutByScriptPubKey, TxLockTypes
|
||||||
from basicswap.chainparams import Coins
|
from basicswap.chainparams import Coins
|
||||||
from basicswap.contrib.test_framework.script import (
|
from basicswap.contrib.test_framework.script import (
|
||||||
@@ -1609,11 +1611,11 @@ class DCRInterface(Secp256k1Interface):
|
|||||||
script_pk = self.getScriptDest(script)
|
script_pk = self.getScriptDest(script)
|
||||||
return findOutput(tx, script_pk)
|
return findOutput(tx, script_pk)
|
||||||
|
|
||||||
def getScriptLockTxDummyWitness(self, script: bytes):
|
def getScriptLockTxDummyWitness(self, script: bytes) -> List[bytes]:
|
||||||
return [bytes(72), bytes(72), bytes(len(script))]
|
return [bytes(72), bytes(72), bytes(len(script))]
|
||||||
|
|
||||||
def getScriptLockRefundSpendTxDummyWitness(self, script: bytes):
|
def getScriptLockRefundSpendTxDummyWitness(self, script: bytes) -> List[bytes]:
|
||||||
return [bytes(72), bytes(72), bytes((1,)), bytes(len(script))]
|
return [bytes(72), bytes(72), bytes(len(script))]
|
||||||
|
|
||||||
def extractLeaderSig(self, tx_bytes: bytes) -> bytes:
|
def extractLeaderSig(self, tx_bytes: bytes) -> bytes:
|
||||||
tx = self.loadTx(tx_bytes)
|
tx = self.loadTx(tx_bytes)
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ class CTransaction:
|
|||||||
self.locktime = tx.locktime
|
self.locktime = tx.locktime
|
||||||
self.expiry = tx.expiry
|
self.expiry = tx.expiry
|
||||||
|
|
||||||
def deserialize(self, data: bytes) -> None:
|
def deserialize(self, data: bytes, allow_witness: bool = True) -> None:
|
||||||
|
|
||||||
version = int.from_bytes(data[:4], "little")
|
version = int.from_bytes(data[:4], "little")
|
||||||
self.version = version & 0xFFFF
|
self.version = version & 0xFFFF
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
|
from typing import List
|
||||||
|
|
||||||
from basicswap.contrib.test_framework.messages import (
|
from basicswap.contrib.test_framework.messages import (
|
||||||
CTxOutPart,
|
CTxOutPart,
|
||||||
@@ -134,6 +135,11 @@ class PARTInterface(BTCInterface):
|
|||||||
def getScriptForPubkeyHash(self, pkh: bytes) -> CScript:
|
def getScriptForPubkeyHash(self, pkh: bytes) -> CScript:
|
||||||
return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG])
|
return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG])
|
||||||
|
|
||||||
|
def getScriptDummyWitness(self, script: bytes) -> List[bytes]:
|
||||||
|
if self.isScriptP2WPKH(script) or self.isScriptP2PKH(script):
|
||||||
|
return [bytes(72), bytes(33)]
|
||||||
|
raise ValueError("Unknown script type")
|
||||||
|
|
||||||
def formatStealthAddress(self, scan_pubkey, spend_pubkey) -> str:
|
def formatStealthAddress(self, scan_pubkey, spend_pubkey) -> str:
|
||||||
prefix_byte = chainparams[self.coin_type()][self._network]["stealth_key_prefix"]
|
prefix_byte = chainparams[self.coin_type()][self._network]["stealth_key_prefix"]
|
||||||
|
|
||||||
|
|||||||
@@ -249,6 +249,7 @@ def smsgDecrypt(
|
|||||||
pubkey_signer = PublicKey.from_signature_and_message(
|
pubkey_signer = PublicKey.from_signature_and_message(
|
||||||
signature, payload_hash, hasher=None
|
signature, payload_hash, hasher=None
|
||||||
).format()
|
).format()
|
||||||
|
|
||||||
pkh_from_recovered: bytes = hash160(pubkey_signer)
|
pkh_from_recovered: bytes = hash160(pubkey_signer)
|
||||||
assert pkh_from == pkh_from_recovered
|
assert pkh_from == pkh_from_recovered
|
||||||
|
|
||||||
|
|||||||
@@ -1011,7 +1011,7 @@ class BasicSwapTest(TestFunctions):
|
|||||||
def test_002_native_segwit(self):
|
def test_002_native_segwit(self):
|
||||||
# p2wpkh
|
# p2wpkh
|
||||||
logging.info(
|
logging.info(
|
||||||
"---------- Test {} p2sh native segwit".format(self.test_coin_from.name)
|
"---------- Test {} native segwit".format(self.test_coin_from.name)
|
||||||
)
|
)
|
||||||
ci = self.swap_clients[0].ci(self.test_coin_from)
|
ci = self.swap_clients[0].ci(self.test_coin_from)
|
||||||
|
|
||||||
@@ -1073,6 +1073,14 @@ class BasicSwapTest(TestFunctions):
|
|||||||
tx_signed,
|
tx_signed,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
prev_txo = tx["vout"][tx_signed_decoded["vin"][0]["vout"]]
|
||||||
|
prevscript: bytes = bytes.fromhex(prev_txo["scriptPubKey"]["hex"])
|
||||||
|
assert ci.isScriptP2WPKH(prevscript) is True
|
||||||
|
txin_witness = tx_signed_decoded["vin"][0]["txinwitness"]
|
||||||
|
assert len(txin_witness) == 2
|
||||||
|
txin_witness_0 = bytes.fromhex(txin_witness[0])
|
||||||
|
assert len(txin_witness_0) > 68 and len(txin_witness_0) <= 72
|
||||||
|
assert len(bytes.fromhex(txin_witness[1])) == 33
|
||||||
assert tx_funded_decoded["txid"] == tx_signed_decoded["txid"]
|
assert tx_funded_decoded["txid"] == tx_signed_decoded["txid"]
|
||||||
|
|
||||||
def test_003_cltv(self):
|
def test_003_cltv(self):
|
||||||
@@ -2295,6 +2303,11 @@ class BasicSwapTest(TestFunctions):
|
|||||||
def test_09_expire_accepted_rev(self):
|
def test_09_expire_accepted_rev(self):
|
||||||
self.do_test_09_expire_accepted(Coins.XMR, self.test_coin_from)
|
self.do_test_09_expire_accepted(Coins.XMR, self.test_coin_from)
|
||||||
|
|
||||||
|
def test_10_presigned_txns(self):
|
||||||
|
raise RuntimeError(
|
||||||
|
"TODO"
|
||||||
|
) # Build without xmr first for quicker test iterations
|
||||||
|
|
||||||
|
|
||||||
class TestBTC(BasicSwapTest):
|
class TestBTC(BasicSwapTest):
|
||||||
__test__ = True
|
__test__ = True
|
||||||
|
|||||||
@@ -51,7 +51,18 @@ from basicswap.util import (
|
|||||||
from basicswap.messages_npb import (
|
from basicswap.messages_npb import (
|
||||||
BidMessage,
|
BidMessage,
|
||||||
)
|
)
|
||||||
from basicswap.contrib.test_framework.script import hash160 as hash160_btc
|
from basicswap.contrib.test_framework.script import (
|
||||||
|
hash160 as hash160_btc,
|
||||||
|
SegwitV0SignatureHash,
|
||||||
|
SIGHASH_ALL,
|
||||||
|
)
|
||||||
|
from basicswap.contrib.test_framework.messages import (
|
||||||
|
COutPoint,
|
||||||
|
CTransaction,
|
||||||
|
CTxIn,
|
||||||
|
CTxOut,
|
||||||
|
uint256_from_str,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
@@ -663,6 +674,37 @@ class Test(unittest.TestCase):
|
|||||||
finally:
|
finally:
|
||||||
db_test.closeDB(cursor)
|
db_test.closeDB(cursor)
|
||||||
|
|
||||||
|
def test_tx_hashes(self):
|
||||||
|
tx = CTransaction()
|
||||||
|
tx.nVersion = 2
|
||||||
|
tx.nLockTime = 0
|
||||||
|
tx.vout.append(CTxOut(1, bytes.fromhex("a15143aa086e05e3b5a73046")))
|
||||||
|
tx.vout.append(CTxOut(2, bytes.fromhex("eed7d63fd86225f7159ed7e5")))
|
||||||
|
tx.vin.append(
|
||||||
|
CTxIn(
|
||||||
|
COutPoint(
|
||||||
|
uint256_from_str(
|
||||||
|
bytes.fromhex(
|
||||||
|
"0101010101010101010101010101010101010010101010101010101010101010"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
bytes.fromhex("c2dca8ecbcf058b79f188692"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
tx.rehash()
|
||||||
|
== "28d0e9afad2740504eb9d0428352bc77a7b94eaafa364ef4cc07aeeff0c631a2"
|
||||||
|
)
|
||||||
|
sighash = SegwitV0SignatureHash(
|
||||||
|
bytes.fromhex("a15143aa086e05e3b5a73046"), tx, 0, SIGHASH_ALL, 3
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
sighash.hex()
|
||||||
|
== "252cd6e85b99e0fd554c44d5fe638923f7ef563048362406a665cf3400feb1bd"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
@@ -315,6 +315,146 @@ class Test(BaseTest):
|
|||||||
break
|
break
|
||||||
assert len(tx_wallet["blockhash"]) == 64
|
assert len(tx_wallet["blockhash"]) == 64
|
||||||
|
|
||||||
|
def test_004_native_segwit(self):
|
||||||
|
test_coin_from = Coins.PART
|
||||||
|
# p2wpkh
|
||||||
|
logging.info("---------- Test {} segwit".format(test_coin_from.name))
|
||||||
|
ci = self.swap_clients[0].ci(test_coin_from)
|
||||||
|
|
||||||
|
addr_native = ci.rpc_wallet("getnewaddress", ["p2pkh segwit test"])
|
||||||
|
addr_info = ci.rpc_wallet(
|
||||||
|
"getaddressinfo",
|
||||||
|
[
|
||||||
|
addr_native,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert addr_info["iswitness"] is False # address is p2pkh, not p2wpkh
|
||||||
|
addr_segwit = ci.rpc_wallet(
|
||||||
|
"getnewaddress", ["p2wpkh segwit test", True, False, False, "bech32"]
|
||||||
|
)
|
||||||
|
addr_info = ci.rpc_wallet(
|
||||||
|
"getaddressinfo",
|
||||||
|
[
|
||||||
|
addr_segwit,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert addr_info["iswitness"] is True
|
||||||
|
|
||||||
|
txid = ci.rpc_wallet(
|
||||||
|
"sendtypeto",
|
||||||
|
[
|
||||||
|
"part",
|
||||||
|
"part",
|
||||||
|
[
|
||||||
|
{"address": addr_native, "amount": 1.0},
|
||||||
|
{"address": addr_segwit, "amount": 1.0},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert len(txid) == 64
|
||||||
|
tx_wallet = ci.rpc_wallet(
|
||||||
|
"gettransaction",
|
||||||
|
[
|
||||||
|
txid,
|
||||||
|
],
|
||||||
|
)["hex"]
|
||||||
|
tx = ci.rpc(
|
||||||
|
"decoderawtransaction",
|
||||||
|
[
|
||||||
|
tx_wallet,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Wait for stake
|
||||||
|
for i in range(20):
|
||||||
|
test_delay_event.wait(1)
|
||||||
|
ro = ci.rpc("scantxoutset", ["start", ["addr({})".format(addr_native)]])
|
||||||
|
if len(ro["unspents"]) > 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
ro = ci.rpc("scantxoutset", ["start", ["addr({})".format(addr_native)]])
|
||||||
|
assert len(ro["unspents"]) == 1
|
||||||
|
assert ro["unspents"][0]["txid"] == txid
|
||||||
|
ro = ci.rpc("scantxoutset", ["start", ["addr({})".format(addr_segwit)]])
|
||||||
|
assert len(ro["unspents"]) == 1
|
||||||
|
assert ro["unspents"][0]["txid"] == txid
|
||||||
|
|
||||||
|
prevout_p2pkh_n: int = -1
|
||||||
|
prevout_p2wpkh_n: int = -1
|
||||||
|
for txo in tx["vout"]:
|
||||||
|
if addr_native == txo["scriptPubKey"]["address"]:
|
||||||
|
prevout_p2pkh_n = txo["n"]
|
||||||
|
if addr_segwit == txo["scriptPubKey"]["address"]:
|
||||||
|
prevout_p2wpkh_n = txo["n"]
|
||||||
|
assert prevout_p2pkh_n > -1
|
||||||
|
assert prevout_p2wpkh_n > -1
|
||||||
|
|
||||||
|
tx_funded = ci.rpc(
|
||||||
|
"createrawtransaction",
|
||||||
|
[[{"txid": txid, "vout": prevout_p2pkh_n}], {addr_segwit: 0.99}],
|
||||||
|
)
|
||||||
|
tx_signed = ci.rpc_wallet(
|
||||||
|
"signrawtransactionwithwallet",
|
||||||
|
[
|
||||||
|
tx_funded,
|
||||||
|
],
|
||||||
|
)["hex"]
|
||||||
|
tx_funded_decoded = ci.rpc(
|
||||||
|
"decoderawtransaction",
|
||||||
|
[
|
||||||
|
tx_funded,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
tx_signed_decoded = ci.rpc(
|
||||||
|
"decoderawtransaction",
|
||||||
|
[
|
||||||
|
tx_signed,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
prev_txo = tx["vout"][tx_signed_decoded["vin"][0]["vout"]]
|
||||||
|
prevscript: bytes = bytes.fromhex(prev_txo["scriptPubKey"]["hex"])
|
||||||
|
assert ci.isScriptP2PKH(prevscript) is True
|
||||||
|
assert ci.isScriptP2WPKH(prevscript) is False
|
||||||
|
txin_witness = tx_signed_decoded["vin"][0]["txinwitness"]
|
||||||
|
assert len(txin_witness) == 2
|
||||||
|
txin_witness_0 = bytes.fromhex(txin_witness[0])
|
||||||
|
assert len(txin_witness_0) > 68 and len(txin_witness_0) <= 72
|
||||||
|
assert len(bytes.fromhex(txin_witness[1])) == 33
|
||||||
|
assert tx_funded_decoded["txid"] == tx_signed_decoded["txid"]
|
||||||
|
|
||||||
|
tx_funded = ci.rpc(
|
||||||
|
"createrawtransaction",
|
||||||
|
[[{"txid": txid, "vout": prevout_p2wpkh_n}], {addr_segwit: 0.99}],
|
||||||
|
)
|
||||||
|
tx_signed = ci.rpc_wallet(
|
||||||
|
"signrawtransactionwithwallet",
|
||||||
|
[
|
||||||
|
tx_funded,
|
||||||
|
],
|
||||||
|
)["hex"]
|
||||||
|
tx_funded_decoded = ci.rpc(
|
||||||
|
"decoderawtransaction",
|
||||||
|
[
|
||||||
|
tx_funded,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
tx_signed_decoded = ci.rpc(
|
||||||
|
"decoderawtransaction",
|
||||||
|
[
|
||||||
|
tx_signed,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
prev_txo = tx["vout"][tx_signed_decoded["vin"][0]["vout"]]
|
||||||
|
prevscript: bytes = bytes.fromhex(prev_txo["scriptPubKey"]["hex"])
|
||||||
|
assert ci.isScriptP2PKH(prevscript) is False
|
||||||
|
assert ci.isScriptP2WPKH(prevscript) is True
|
||||||
|
txin_witness = tx_signed_decoded["vin"][0]["txinwitness"]
|
||||||
|
assert len(txin_witness) == 2
|
||||||
|
txin_witness_0 = bytes.fromhex(txin_witness[0])
|
||||||
|
assert len(txin_witness_0) > 68 and len(txin_witness_0) <= 72
|
||||||
|
assert len(bytes.fromhex(txin_witness[1])) == 33
|
||||||
|
assert tx_funded_decoded["txid"] == tx_signed_decoded["txid"]
|
||||||
|
|
||||||
def test_01_verifyrawtransaction(self):
|
def test_01_verifyrawtransaction(self):
|
||||||
txn = "0200000001eb6e5c4ebba4efa32f40c7314cad456a64008e91ee30b2dd0235ab9bb67fbdbb01000000ee47304402200956933242dde94f6cf8f195a470f8d02aef21ec5c9b66c5d3871594bdb74c9d02201d7e1b440de8f4da672d689f9e37e98815fb63dbc1706353290887eb6e8f7235012103dc1b24feb32841bc2f4375da91fa97834e5983668c2a39a6b7eadb60e7033f9d205a803b28fe2f86c17db91fa99d7ed2598f79b5677ffe869de2e478c0d1c02cc7514c606382012088a8201fe90717abb84b481c2a59112414ae56ec8acc72273642ca26cc7a5812fdc8f68876a914225fbfa4cb725b75e511810ac4d6f74069bdded26703520140b27576a914207eb66b2fd6ed9924d6217efc7fa7b38dfabe666888acffffffff01e0167118020000001976a9140044e188928710cecba8311f1cf412135b98145c88ac00000000"
|
txn = "0200000001eb6e5c4ebba4efa32f40c7314cad456a64008e91ee30b2dd0235ab9bb67fbdbb01000000ee47304402200956933242dde94f6cf8f195a470f8d02aef21ec5c9b66c5d3871594bdb74c9d02201d7e1b440de8f4da672d689f9e37e98815fb63dbc1706353290887eb6e8f7235012103dc1b24feb32841bc2f4375da91fa97834e5983668c2a39a6b7eadb60e7033f9d205a803b28fe2f86c17db91fa99d7ed2598f79b5677ffe869de2e478c0d1c02cc7514c606382012088a8201fe90717abb84b481c2a59112414ae56ec8acc72273642ca26cc7a5812fdc8f68876a914225fbfa4cb725b75e511810ac4d6f74069bdded26703520140b27576a914207eb66b2fd6ed9924d6217efc7fa7b38dfabe666888acffffffff01e0167118020000001976a9140044e188928710cecba8311f1cf412135b98145c88ac00000000"
|
||||||
prevout = {
|
prevout = {
|
||||||
|
|||||||
Reference in New Issue
Block a user