mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-31 17:31:36 +00:00
lnpeer: cooperative close: verify scriptpubkey matches templates
This commit is contained in:
parent
c121230706
commit
aaf174ef3e
2 changed files with 36 additions and 24 deletions
|
@ -25,7 +25,8 @@ from . import ecc
|
||||||
from .ecc import sig_string_from_r_and_s, get_r_and_s_from_sig_string, der_sig_from_sig_string
|
from .ecc import sig_string_from_r_and_s, get_r_and_s_from_sig_string, der_sig_from_sig_string
|
||||||
from . import constants
|
from . import constants
|
||||||
from .util import bh2u, bfh, log_exceptions, ignore_exceptions, chunks, SilentTaskGroup
|
from .util import bh2u, bfh, log_exceptions, ignore_exceptions, chunks, SilentTaskGroup
|
||||||
from .transaction import Transaction, TxOutput, PartialTxOutput
|
from . import transaction
|
||||||
|
from .transaction import Transaction, TxOutput, PartialTxOutput, match_script_against_template
|
||||||
from .logging import Logger
|
from .logging import Logger
|
||||||
from .lnonion import (new_onion_packet, decode_onion_error, OnionFailureCode, calc_hops_data_for_payment,
|
from .lnonion import (new_onion_packet, decode_onion_error, OnionFailureCode, calc_hops_data_for_payment,
|
||||||
process_onion_packet, OnionPacket, construct_onion_error, OnionRoutingFailureMessage,
|
process_onion_packet, OnionPacket, construct_onion_error, OnionRoutingFailureMessage,
|
||||||
|
@ -1357,9 +1358,12 @@ class Peer(Logger):
|
||||||
|
|
||||||
@log_exceptions
|
@log_exceptions
|
||||||
async def on_shutdown(self, payload):
|
async def on_shutdown(self, payload):
|
||||||
# length of scripts allowed in BOLT-02
|
their_scriptpubkey = payload['scriptpubkey']
|
||||||
if int.from_bytes(payload['len'], 'big') not in (3+20+2, 2+20+1, 2+20, 2+32):
|
# BOLT-02 restrict the scriptpubkey to some templates:
|
||||||
raise Exception('scriptpubkey length in received shutdown message invalid: ' + str(payload['len']))
|
if not (match_script_against_template(their_scriptpubkey, transaction.SCRIPTPUBKEY_TEMPLATE_WITNESS_V0)
|
||||||
|
or match_script_against_template(their_scriptpubkey, transaction.SCRIPTPUBKEY_TEMPLATE_P2SH)
|
||||||
|
or match_script_against_template(their_scriptpubkey, transaction.SCRIPTPUBKEY_TEMPLATE_P2PKH)):
|
||||||
|
raise Exception(f'scriptpubkey in received shutdown message does not conform to any template: {their_scriptpubkey.hex()}')
|
||||||
chan_id = payload['channel_id']
|
chan_id = payload['channel_id']
|
||||||
if chan_id in self.shutdown_received:
|
if chan_id in self.shutdown_received:
|
||||||
self.shutdown_received[chan_id].set_result(payload)
|
self.shutdown_received[chan_id].set_result(payload)
|
||||||
|
|
|
@ -390,20 +390,32 @@ class OPPushDataGeneric:
|
||||||
|
|
||||||
|
|
||||||
OPPushDataPubkey = OPPushDataGeneric(lambda x: x in (33, 65))
|
OPPushDataPubkey = OPPushDataGeneric(lambda x: x in (33, 65))
|
||||||
# note that this does not include x_pubkeys !
|
|
||||||
|
SCRIPTPUBKEY_TEMPLATE_P2PKH = [opcodes.OP_DUP, opcodes.OP_HASH160,
|
||||||
|
OPPushDataGeneric(lambda x: x == 20),
|
||||||
|
opcodes.OP_EQUALVERIFY, opcodes.OP_CHECKSIG]
|
||||||
|
SCRIPTPUBKEY_TEMPLATE_P2SH = [opcodes.OP_HASH160, OPPushDataGeneric(lambda x: x == 20), opcodes.OP_EQUAL]
|
||||||
|
SCRIPTPUBKEY_TEMPLATE_WITNESS_V0 = [opcodes.OP_0, OPPushDataGeneric(lambda x: x in (20, 32))]
|
||||||
|
|
||||||
|
|
||||||
def match_decoded(decoded, to_match):
|
def match_script_against_template(script, template) -> bool:
|
||||||
if decoded is None:
|
"""Returns whether 'script' matches 'template'."""
|
||||||
|
if script is None:
|
||||||
return False
|
return False
|
||||||
if len(decoded) != len(to_match):
|
# optionally decode script now:
|
||||||
|
if isinstance(script, (bytes, bytearray)):
|
||||||
|
try:
|
||||||
|
script = [x for x in script_GetOp(script)]
|
||||||
|
except MalformedBitcoinScript:
|
||||||
|
return False
|
||||||
|
if len(script) != len(template):
|
||||||
return False
|
return False
|
||||||
for i in range(len(decoded)):
|
for i in range(len(script)):
|
||||||
to_match_item = to_match[i]
|
template_item = template[i]
|
||||||
decoded_item = decoded[i]
|
script_item = script[i]
|
||||||
if OPPushDataGeneric.is_instance(to_match_item) and to_match_item.check_data_len(decoded_item[0]):
|
if OPPushDataGeneric.is_instance(template_item) and template_item.check_data_len(script_item[0]):
|
||||||
continue
|
continue
|
||||||
if to_match_item != decoded_item[0]:
|
if template_item != script_item[0]:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -412,28 +424,25 @@ def get_address_from_output_script(_bytes: bytes, *, net=None) -> Optional[str]:
|
||||||
try:
|
try:
|
||||||
decoded = [x for x in script_GetOp(_bytes)]
|
decoded = [x for x in script_GetOp(_bytes)]
|
||||||
except MalformedBitcoinScript:
|
except MalformedBitcoinScript:
|
||||||
decoded = None
|
return None
|
||||||
|
|
||||||
# p2pkh
|
# p2pkh
|
||||||
match = [opcodes.OP_DUP, opcodes.OP_HASH160, OPPushDataGeneric(lambda x: x == 20), opcodes.OP_EQUALVERIFY, opcodes.OP_CHECKSIG]
|
if match_script_against_template(decoded, SCRIPTPUBKEY_TEMPLATE_P2PKH):
|
||||||
if match_decoded(decoded, match):
|
|
||||||
return hash160_to_p2pkh(decoded[2][1], net=net)
|
return hash160_to_p2pkh(decoded[2][1], net=net)
|
||||||
|
|
||||||
# p2sh
|
# p2sh
|
||||||
match = [opcodes.OP_HASH160, OPPushDataGeneric(lambda x: x == 20), opcodes.OP_EQUAL]
|
if match_script_against_template(decoded, SCRIPTPUBKEY_TEMPLATE_P2SH):
|
||||||
if match_decoded(decoded, match):
|
|
||||||
return hash160_to_p2sh(decoded[1][1], net=net)
|
return hash160_to_p2sh(decoded[1][1], net=net)
|
||||||
|
|
||||||
# segwit address (version 0)
|
# segwit address (version 0)
|
||||||
match = [opcodes.OP_0, OPPushDataGeneric(lambda x: x in (20, 32))]
|
if match_script_against_template(decoded, SCRIPTPUBKEY_TEMPLATE_WITNESS_V0):
|
||||||
if match_decoded(decoded, match):
|
|
||||||
return hash_to_segwit_addr(decoded[1][1], witver=0, net=net)
|
return hash_to_segwit_addr(decoded[1][1], witver=0, net=net)
|
||||||
|
|
||||||
# segwit address (version 1-16)
|
# segwit address (version 1-16)
|
||||||
future_witness_versions = list(range(opcodes.OP_1, opcodes.OP_16 + 1))
|
future_witness_versions = list(range(opcodes.OP_1, opcodes.OP_16 + 1))
|
||||||
for witver, opcode in enumerate(future_witness_versions, start=1):
|
for witver, opcode in enumerate(future_witness_versions, start=1):
|
||||||
match = [opcode, OPPushDataGeneric(lambda x: 2 <= x <= 40)]
|
match = [opcode, OPPushDataGeneric(lambda x: 2 <= x <= 40)]
|
||||||
if match_decoded(decoded, match):
|
if match_script_against_template(decoded, match):
|
||||||
return hash_to_segwit_addr(decoded[1][1], witver=witver, net=net)
|
return hash_to_segwit_addr(decoded[1][1], witver=witver, net=net)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
@ -1348,14 +1357,13 @@ class PartialTxInput(TxInput, PSBTSection):
|
||||||
except MalformedBitcoinScript:
|
except MalformedBitcoinScript:
|
||||||
decoded = None
|
decoded = None
|
||||||
# witness version 0
|
# witness version 0
|
||||||
match = [opcodes.OP_0, OPPushDataGeneric(lambda x: x in (20, 32))]
|
if match_script_against_template(decoded, SCRIPTPUBKEY_TEMPLATE_WITNESS_V0):
|
||||||
if match_decoded(decoded, match):
|
|
||||||
return True
|
return True
|
||||||
# witness version 1-16
|
# witness version 1-16
|
||||||
future_witness_versions = list(range(opcodes.OP_1, opcodes.OP_16 + 1))
|
future_witness_versions = list(range(opcodes.OP_1, opcodes.OP_16 + 1))
|
||||||
for witver, opcode in enumerate(future_witness_versions, start=1):
|
for witver, opcode in enumerate(future_witness_versions, start=1):
|
||||||
match = [opcode, OPPushDataGeneric(lambda x: 2 <= x <= 40)]
|
match = [opcode, OPPushDataGeneric(lambda x: 2 <= x <= 40)]
|
||||||
if match_decoded(decoded, match):
|
if match_script_against_template(decoded, match):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue