Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions bip-0352/reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from secp256k1lab.util import tagged_hash, hash_sha256


from bech32m import convertbits, bech32_encode, decode, Encoding
from bech32m import bech32_decode, bech32_encode, convertbits, Encoding
from bitcoin_utils import (
deser_txid,
from_hex,
Expand Down Expand Up @@ -116,11 +116,20 @@ def create_labeled_silent_payment_address(b_scan: Scalar, B_spend: GE, m: int, h


def decode_silent_payment_address(address: str, hrp: str = "tsp") -> Tuple[GE, GE]:
_, data = decode(hrp, address)
if data is None:
hrpgot, data, spec = bech32_decode(address)
if hrpgot != hrp or spec != Encoding.BECH32M or data is None or not data:
return GE(), GE()
B_scan = GE.from_bytes_compressed(data[:33])
B_spend = GE.from_bytes_compressed(data[33:])

version = data[0]
if version == 31:
return GE(), GE()

decoded = convertbits(data[1:], 5, 8, False)
if decoded is None or len(decoded) < 66 or (version == 0 and len(decoded) != 66):
return GE(), GE()

B_scan = GE.from_bytes_compressed(decoded[:33])
B_spend = GE.from_bytes_compressed(decoded[33:66])

return B_scan, B_spend

Expand Down
48 changes: 48 additions & 0 deletions bip-0352/test_address_decoding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env python3

import unittest

from bech32m import Encoding, bech32_encode, convertbits
from reference import decode_silent_payment_address
from secp256k1lab.secp256k1 import GE


SCAN_KEY = bytes.fromhex("0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4")
SPEND_KEY = bytes.fromhex("025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36")
PAYLOAD = SCAN_KEY + SPEND_KEY


def make_address(version, payload=PAYLOAD, spec=Encoding.BECH32M):
data = convertbits(payload, 8, 5)
return bech32_encode("tsp", [version] + data, spec)


class SilentPaymentAddressDecodingTest(unittest.TestCase):
def test_accepts_higher_forward_compatible_version(self):
scan, spend = decode_silent_payment_address(make_address(17))
self.assertEqual(scan, GE.from_bytes_compressed(SCAN_KEY))
self.assertEqual(spend, GE.from_bytes_compressed(SPEND_KEY))

def test_discards_forward_compatible_extension_data(self):
scan, spend = decode_silent_payment_address(make_address(1, PAYLOAD + b"extension"))
self.assertEqual(scan, GE.from_bytes_compressed(SCAN_KEY))
self.assertEqual(spend, GE.from_bytes_compressed(SPEND_KEY))

def test_rejects_bech32_for_version_zero(self):
scan, spend = decode_silent_payment_address(make_address(0, spec=Encoding.BECH32))
self.assertTrue(scan.infinity)
self.assertTrue(spend.infinity)

def test_rejects_extra_data_for_version_zero(self):
scan, spend = decode_silent_payment_address(make_address(0, PAYLOAD + b"extension"))
self.assertTrue(scan.infinity)
self.assertTrue(spend.infinity)

def test_rejects_version_31(self):
scan, spend = decode_silent_payment_address(make_address(31))
self.assertTrue(scan.infinity)
self.assertTrue(spend.infinity)


if __name__ == "__main__":
unittest.main()