diff --git a/src/seedsigner/models/decode_qr.py b/src/seedsigner/models/decode_qr.py index 4ac166423..ec99f292e 100644 --- a/src/seedsigner/models/decode_qr.py +++ b/src/seedsigner/models/decode_qr.py @@ -2,6 +2,7 @@ import json import logging import re +import zlib from binascii import a2b_base64, b2a_base64 from enum import IntEnum @@ -11,6 +12,7 @@ from urtypes.crypto import PSBT as UR_PSBT from urtypes.crypto import Account, Output from urtypes.bytes import Bytes +from base64 import b32encode, b32decode from seedsigner.helpers.ur2.ur_decoder import URDecoder from seedsigner.models.qr_type import QRType @@ -74,6 +76,9 @@ def add_data(self, data): elif self.qr_type == QRType.PSBT__BASE43: self.decoder = Base43PsbtQrDecoder() # Single Segment Base43 + elif self.qr_type == QRType.PSBT__BBQR: + self.decoder = BBQRPsbtQrDecoder() # BBQr Decoder + elif self.qr_type in [QRType.SEED__SEEDQR, QRType.SEED__COMPACTSEEDQR, QRType.SEED__MNEMONIC, QRType.SEED__FOUR_LETTER_MNEMONIC, QRType.SEED__UR2]: self.decoder = SeedQrDecoder(wordlist_language_code=self.wordlist_language_code) @@ -229,7 +234,7 @@ def get_percent_complete(self, weight_mixed_frames: bool = False) -> int: if self.qr_type in [QRType.PSBT__UR2, QRType.OUTPUT__UR, QRType.ACCOUNT__UR, QRType.BYTES__UR]: return int(self.decoder.estimated_percent_complete(weight_mixed_frames=weight_mixed_frames) * 100) - elif self.qr_type in [QRType.PSBT__SPECTER]: + elif self.qr_type in [QRType.PSBT__SPECTER, QRType.PSBT__BBQR]: if self.decoder.total_segments == None: return 0 return int((self.decoder.collected_segments / self.decoder.total_segments) * 100) @@ -262,6 +267,7 @@ def is_psbt(self) -> bool: QRType.PSBT__SPECTER, QRType.PSBT__BASE64, QRType.PSBT__BASE43, + QRType.PSBT__BBQR, ] @@ -326,9 +332,6 @@ def extract_qr_data(image, is_binary:bool = False) -> str | None: @staticmethod def detect_segment_type(s, wordlist_language_code=None): - # print("-------------- DecodeQR.detect_segment_type --------------") - # print(type(s)) - # print(len(s)) try: # Convert to str data @@ -338,6 +341,9 @@ def detect_segment_type(s, wordlist_language_code=None): # TODO: Convert the test suite rather than handle here? s = s.decode('utf-8') + logger.debug(f"segment string: {s}") + logger.debug(f"segment string length: {len(s)}") + # PSBT if re.search("^UR:CRYPTO-PSBT/", s, re.IGNORECASE): return QRType.PSBT__UR2 @@ -357,6 +363,9 @@ def detect_segment_type(s, wordlist_language_code=None): elif DecodeQR.is_base64_psbt(s): return QRType.PSBT__BASE64 + elif re.search(r"^B\$[2HZ]P[0-9A-Z]{4}", s): # https://github.com/coinkite/BBQr/blob/master/BBQr.md#spliting-the-data + return QRType.PSBT__BBQR + # Wallet Descriptor desc_str = s.replace("\n","").replace(" ","") if re.search(r'^p(\d+)of(\d+) ', s, re.IGNORECASE): @@ -654,8 +663,9 @@ def add(self, segment, qr_type=None): elif self.total_segments != self.total_segment_nums(segment): raise Exception('Segment total changed unexpectedly') - if self.segments[self.current_segment_num(segment) - 1] == None: - self.segments[self.current_segment_num(segment) - 1] = self.parse_segment(segment) + current_segment_num = self.current_segment_num(segment) + if self.segments[current_segment_num - 1] == None: + self.segments[current_segment_num - 1] = self.parse_segment(segment) self.collected_segments += 1 if self.total_segments == self.collected_segments: if self.is_valid: @@ -704,6 +714,59 @@ def parse_segment(self, segment) -> str: +class BBQRPsbtQrDecoder(BaseAnimatedQrDecoder): + """ + Used to decode BBQR Animated PSBT encoding. + """ + def __init__(self): + super().__init__() + self.encoding = None + + + def get_data(self) -> str: + logger.debug("BBQRPsbtQrDecoder get_data") + data = "".join(self.segments) + if self.complete and self.encoding: + if self.encoding == 'H': + return b''.join(bytes.fromhex(s) for s in self.segments) + + # base32 decode, but insert padding for API + rv = b'' + for p in self.segments: + padding = (8 - (len(p) % 8)) % 8 + rv += b32decode(p + (padding*'=')) + + if self.encoding == 'Z': + # decompress + z = zlib.decompressobj(wbits=-10) + rv = z.decompress(rv) + rv += z.flush() + + return rv + + return None + + def current_segment_num(self, segment) -> int: + current_segment = int(segment[6:8], 36) + 1 + logger.debug(f"BBQRPsbtQrDecoder current_segment_num {current_segment}") + return current_segment + + + def total_segment_nums(self, segment) -> int: + total_segments = int(segment[4:6], 36) + logger.debug(f"BBQRPsbtQrDecoder total_segment_nums {total_segments}") + return total_segments + + + def parse_segment(self, segment) -> str: + self.encoding = segment[2] + file_type = segment[3] + data = segment[8:] + + return data.strip() + + + class Base64PsbtQrDecoder(BaseSingleFrameQrDecoder): """ Decodes single frame base64 encoded qr image. diff --git a/src/seedsigner/models/qr_type.py b/src/seedsigner/models/qr_type.py index 6ec1f5ecc..bd678f7e8 100644 --- a/src/seedsigner/models/qr_type.py +++ b/src/seedsigner/models/qr_type.py @@ -6,6 +6,7 @@ class QRType: PSBT__SPECTER = "psbt__specter" PSBT__BASE43 = "psbt__base43" PSBT__UR2 = "psbt__ur2" + PSBT__BBQR = "psbt__bbqr" SEED__SEEDQR = "seed__seedqr" SEED__COMPACTSEEDQR = "seed__compactseedqr" diff --git a/tests/test_decodepsbtqr.py b/tests/test_decodepsbtqr.py index 9b7d6981d..230d8e685 100644 --- a/tests/test_decodepsbtqr.py +++ b/tests/test_decodepsbtqr.py @@ -357,6 +357,7 @@ def test_seed_qr(): assert d.get_seed_phrase() == "obscure bone gas open exotic abuse virus bunker shuffle nasty ship dash".split() + def test_specter_wallet_json(): parts = [ 'p1of3 {"label": "SeedSigner Dev Funds", "blockheight": 692143, "descriptor": "wsh(sortedmulti(4,[e0811b6b/48h/0h/0h/2h]xpub6E8v7uy63pCeJvHe5W8ea8zTnCtKMFgMRb5bueWWcUFMw6sWmUwTqxM8cFiKQRWkA2Fxth9HJZufJwjWTTvU1UGZNpTrh9khrswYMgeHiCt/0/*,[852b308f/48h/0h/0h/2h]xpub6ErhgAWfnEqW7xDBm1iLq5JjNyUS65YUFnjHLrRv9zmdDEtuE75bpWQ8o6bSBnpT6AkrrsA8eA5SmEFArZn11KEPaZJzx9mHTXPWZCsxLyh/0/*,[7edf9c59/48h/0h/0h/2h]xpub6DaFfKoe7Wpofr' @@ -374,6 +375,7 @@ def test_specter_wallet_json(): assert d.get_wallet_descriptor() == "wsh(sortedmulti(4,[e0811b6b/48h/0h/0h/2h]xpub6E8v7uy63pCeJvHe5W8ea8zTnCtKMFgMRb5bueWWcUFMw6sWmUwTqxM8cFiKQRWkA2Fxth9HJZufJwjWTTvU1UGZNpTrh9khrswYMgeHiCt/0/*,[852b308f/48h/0h/0h/2h]xpub6ErhgAWfnEqW7xDBm1iLq5JjNyUS65YUFnjHLrRv9zmdDEtuE75bpWQ8o6bSBnpT6AkrrsA8eA5SmEFArZn11KEPaZJzx9mHTXPWZCsxLyh/0/*,[7edf9c59/48h/0h/0h/2h]xpub6DaFfKoe7WpofrbYeNo3Wv2AiLUMeyrPwotXfukFxUHbK4JxaLHTd5394QtH5wnjFzBgr2YnJpHhXv25Zsqv2APmMFvH1DsKHj5LCr3pmXs/0/*,[b433e095/48h/0h/0h/2h]xpub6EF51itHko2YhGTjVeuYbBgJjVbTzzpYzn2a3JwZHpDrMePRVgXGBHMx2Yv1KwgLsUn9i7ExcAo8uqMx4pDjVRY9J7qnceFAwRRj16dd5AS/0/*,[184d07eb/48h/0h/0h/2h]xpub6EEoTpcQu7N4R8D84pJjZ69j3minevnYLDDoo2HBzYBXTQ4rGVf4XGTyCYFwJuZdsF9MyFYJNzYEjg5LGMA1ubTGWuDnjHAZz6ficVRDTSy/0/*,[3e451efe/48h/0h/0h/2h]xpub6ExQPvQxGBMaPxr8Fv7Vq91ztJFFX3VWvtpvex6UPZ1AptTeuAiJGCtKkgwJkrwpMZMagh9ex6rL4sM8axfFcdQbERoFCRUKTJxrBkJh56g/0/*))#c44hel9e" + def test_specter_wallet_json2(): part = '{"label": "Testnet Single Zone", "blockheight": 2090512, "descriptor": "wpkh([990a73ad/84h/1h/0h]tpubDDHQMDnFdan2GyHBsG32VW9qiygbhVizGRTjiS3H79M49FSvpsvLXqLgp1yC7r43dXVHozWavi2Fc4WHUpZmQYmzoQbit28qJhLjScbAQWU/0/*)#ujr0xunp","devices": [{"type": "seedsigner", "label": "Single Seed Zone Testnet"}]}' @@ -382,3 +384,96 @@ def test_specter_wallet_json2(): assert d.qr_type == QRType.WALLET__SPECTER assert d.is_complete + + + +def test_basic_1_in_2_out_bbqr_psbt_qr(): + parts = [ + 'B$ZP0100FMUE4KXZZ7EHBEUJQGAYDGMXLP2O34WEVGERCKNOQWTY3URDYXHGTXTTHOVHKW6YXZQYEVSN6UGQMEHYB4CP4RI3BJK43MVLSZ7ZRH5R5SB4527A5C6N3LL65GZRPNZZGU2NK332BFLVR7VVKSYTCILLOLCJZ5PGFQGCC2U37YW5J6CTXMW6NHMT533MMPJ3YXQQVGL2ZZ76AADRETFVNCV4MGOMY6GEPPPG5XSZFIOQ6PW2JUFQVDWX5FMIHLHCI3Y23V5WGPPK5OF5GSFOKZI576FTVA5FGM5NNACNMNSAQBLZQZY62PJQS7Q72PQGVTABGZRVEPB4HL5MGCB3SVONS5P7W7BGVVT557AFA33L63OW43GW5BAYVTCMNLH5XCUEDNRF4HXFFLMHCWV25FZM6VQNQFPW2PZWI5MXP3QKHGIKOPMXR6CZGOO6CFSQ42IKN3USTQ44OHX53YYXTSSX74XXZ7ZPFXMRMCDTBDJU3JIJ2Z6ZXJXOK3LD6YHA3NYV4PDUDFR2K2H6RIPZ4BI7V2YOXRXPQY5YJELSQ4EHNXBM6HQX74PC26NXEXI6B7UR6IQMH446CDUR36RHYRKV5DE5ZUUVS5FEIWNKJLGJXHGPK66WGP5YNX3PEW24ZRLTJYAOMEBNXIGEYT2OSSUTLGKFZX7M7VTZUGI5LH5GM6X6PC76XZOO3PM7WV67BCM3D5M3XMAEQBYPJAWRAJZQAMEQA' + ] + + d = DecodeQR() + for i in parts: + if d.add_data(i) == DecodeQRStatus.COMPLETE: + break + assert d.qr_type == QRType.PSBT__BBQR + + #complete should be true + assert d.is_complete + + + +def test_basic_1_in_2_out_bbqr_non_compressed_psbt_qr(): + parts = [ + 'B$2P0200OBZWE5H7AEAMGAQAAAAAGLZDYX3EGIZDZMNR5DQRFVCQAIG3FL5BAHWRJ3PGCJ3NTFFLVFRKBIAAAAAA7X77776SNCAHN5R4BCQGWFWOT7MQUMN7IYDICAIMVZOQWEMKWXPVVJWTZ4AAAAAAAD6777775FCZSAY26LQG5V3DG7J4Y6HFZSAL65OCPEMYB6SLACVNXVAAIOPAAAAAAAAP377774BBAJYAAAAAAAAACYABITRMR72K5TBKF5JQHFEP3N7MTN6BSTVZO6AAAAAAAAAAAALAAFH572746E5HEAFOBJOKAWW3AZSTJ5YGQ4IAAAAAATYBAQ2YPTYD3PPOOG4AAAAABMZKKMFX7N7X3N7UFH3DJ6TPIJJNKDDEZ2V5WXVEFF7ED2KPRLQWAJQJYJPKMVHIF3B567MHOVVTZEREDZ3JU5K7433BO4PCIWIAXMMKCEDF7NB74VAAACAACAAAQAAAAAEAAAAQB7MIAEBAAAAAAFYISWF3KWA2BQJIWFBLOAQUC75FZ7U663FLHY2UPMSMBIORDQQ4ICAAAAAAB7P7777QWEBHAAAAAAAAAALAAFDLBORR4YUXZXUT2AMGPV5STUNSAWXZC3AQE4AAAAAAAAABMAAUGQY57O3ZSGKZQPJIVDDRHCZR26EBKEXBCATQAAAAAAAAAFQACRQDOY52JHJTNRMSBEU3ZNF7MU3CQ67OE7UAGAAAAAAAAAAWAAKHTOIMMYL4F27RNYMKHR27HEACX2GC5OBRAJYAAAAAAAAACYABIUXQGIMLE6T4JV4HB7DBKMD2ZSOAJ3BNWEBHAAAAAAAAAALAAFG6U2YBVVC5EOTPROOXWHTT3NQNDN24INYQE4AAAAAAAAABMAAUCUT7GZTM564L2AHZT2WWKSNMW2XN6SRTTQZSWAAAAAAAAFQACSIVWLYCFXTXUOPJHVTG424NY2MHVFAUJ4ICOAAAAAAAAAAWAAKO5BX4TBBZOLBTGINJPGZXHCHXLSFXOGFRAJYAAAAAAAAACYABJ6E4FYLPW652VRHZ2TXZ', + 'B$2P0201MH36EIIPZFAIMEBHAAAAAAAAAALAAFGZMA575IOQ64JRLZ7FDFP5HXDSI7T5TCB3K4TQAAIBD4ICOAAAAAAAAAAWAAKNSYB372Q5B5YTCXT6KGK72POHER7H3GECEBQDUQRHUJOBI3ZLGB5D45DTXHXDO2KR3SYOVVK7B3IWBLBTMZOLAWMBQZP3IP7FIAAAQAAQAAEAAAAABAAAAAAAALAAAAAAAAIAOEBAAAAAAGYGM76R4MFE4PVHWKO7UMETKM7CMCYSGXHLZBKFSTV77SGMMGAIWAIAAAAAB7P7777QF2ADAAAAAAAAAALAAFDVHQFTAZIUYBERQK72U7UJXL4TP4IATCF7VQXQAAAAAAABMAAUJKWTGIZ24GA6VEFIYS3FJBDRMPCZEAN75DGSKAABAEP6QAYAAAAAAAAACYABI5J4BMYGKFGAJEMCX6VH5CN27E37CAEYQIQGALL3CUCJCC5XCJYUJJZQTXXO3YZORCQGK4GZNW3IGGPLOVQF2VCBEGDF7NB74VAAACAACAAAQAAAAAEAAAAAAAAEAAAAAAABABJAEAAAAAA7YEPDL6LPKO5S4PGHT7RSZD7WRGO3JUDPI2L2LPGRPBZ3ORRB5NYAAAAAAAH577776ALCA4AAAAAAAAABMAAUXRNOMGZL4W3EIBN7B33H6UPCYV3NJF32PVLCOAABAEPWEBYAAAAAAAAACYABJPC24YNSXZNWIQC36DXWP5I6FRLW2SLXUIQGALPV276UOSFQ4GYKIORFRCTTGOSCPBCJ7QWFFL2K4UCZDA27YOVQGGDF7NB74VAAACAACAAAQAAAAAEAAAAAAAA6AAAAAABCAIB2VZDSEABFIYX7GUZUJM27QMBDRYRRCHKG6SGYIID7ZDZIBMWDLLAYMX5UH7SUAAAIAAIAACAAAAAAQAAAAAAAGUAAAAAAEIBAENBW7O5JKFANLCOJJSIFIZLZ2JPIDZRECITJNP2QDDZACAOE57MRDBS7WQ76KQAABAABAAAIAAAAACAACAAAAAIQAAAAAA', + ] + + d = DecodeQR() + for i in parts: + if d.add_data(i) == DecodeQRStatus.COMPLETE: + break + assert d.qr_type == QRType.PSBT__BBQR + + #complete should be true + assert d.is_complete + + + +def test_large_multipart_bbqr_psbt_qr(): + parts = [ + 'B$ZP04003WJHSUCTQ4PIK3YSIJKDNJLBCXBGKDICWLEI4D4CBWEBAEIEEJFSCLFSDACSVGGKEJBAQWCBWAUAEATBRHAFHCXMKEQBDGVAMKSFIZBJFCSWAUMUPUVSRMR77N6H7NBTZROGMLDKL5TH5E3E4YTHHPXPTSJ2O7BINMYCAEIOAAAAZH3LXF52M3I63NSBGPULBVT34BA7XQIKWNHBOV7D37BSCX6WIKYV63XCCYDFNVWQ37VDXBIMUH27CQHIGNTLS44FRYBV7QW32CULRZGLY5WO5ZXO6NJRFHY2VCG5IHZ3BJ6RYABDZ7L7R47JFBP5UBXVR46NCVK3OLSXIGCCGO7FUIPWFYLRUH4PDZPEOQAVTQJ7DYBWAMDVMDDXMJOY7OPYAJLROUZR2KBVLUU6H6DPT55OQDW55CXBZW5WARPNAPUQPH4J4BGVI6GGXH2KU42DFLE5NKJQQXX2EOISJVAFG4I5L4MMFYV44ZDWFPKYJS5QHRZGK63T53YJHEIJORCV5F5VFDU233LWQZIPDRORLJDLD7JC3IY2M4EBURPMKHSTNPXRMKXGTKVWSNMXOMCYKQUCR6QBZHYXWMWMUQBZHVQYYCZB3GT52KDFO6ZZHJXA3Y6ZMFOZ7UIQPHGYDKYW4J7VLVFECBJ4PJIHB7C3HRJPJXYXMLHIIZKSOG6HWENEGPLFKCICHNU7RCCOGZEHW7HGGXENTRV4OYJ32HKZEUZ6SJRBLO4TLF3YQVW3LNS4OHEPIBA3ZSXW6JZQM5UTQIM732LBENYKXYDDPMVI6AVN6I22JJTXYEDP23BANCYIKE3MPQ42AIBV5F3EDWGH53GCP5XGYSBC557I2FQJTF7EUQQEZYTOM4DJ2BBIUKCMEDTMUPQFZ4QIEWD4MJZBHIR36DXB336K57AWDDHKQSG3MDHIOCBRBO3YZXPTLZV4LJPL4ZVQ5HFCCHLJ6OBIJE6AJSXJFS7S6N7Y5MCHH6K32CDA6BBIF6OM7SVGUBJ46OT2YSCHGONHGJ4U2GBBYPUEZQDIKMMHRS5OV2WS6DWFTW4MQ5Z55JTR256REQT3S5FMMOSCVKDBJUMWMQINRUVZCCFAE2DBUTIX4Z4VTL4FM52OONLPKAFPPTIKFVSB42FTPMV3', + 'B$ZP0401R2NIJC6YGI5PXFPPSXIZIJ6F5MPABQ7JTUK3MABHDBG7WWGCED33GILBH3IMEZENRW77JOMKNVVTZNDY4XGRE5PH7EQFYQ4WTBWIJBVLV7CVL453J47I4EHIPX6D2QRWWCQDPWWCFPSRT5R27HHKS22TMGPIZZMDYLEWXUTPMXHAM7LRETD4ANY5OS4EDCC5XTTO6GTTLYWS6XZT3B2OLEEOWR4UDQSJMATFPSLF7G4WGTHBAJE33CR5U3LN4LKUZQ2VPU3KF3TQUCUFAQPUCOJ6F5TFS3CEDD2D4PRDMEXXYE3FUPKTQME6VJAXRXJRS7JR2YIK7VXXD4K2O6RWAAKXOMNRLSBAWCSXGIIUAXIMESFD7XHCUNPRVXXJZZVM5IAV56NAIWWYGDHUYKQPN4ZZBGJRPB5KS6CYJGQQSSYSECQTRC43ET6BGOECAFU7TDOIJ2AOXR54PO7SPNYBRIZ2MGRS2A6VBDKMJCJ5JXFBUACAQW6TXYH5AHXT4A7S4JIE27BYDNZ4RKFLVYXA3T46WXGXQOHHNUNHPXJHEG2HPKLEVQR2I2SICFW7TILMU2ERRSPV3GQHNDQEUL3GGPHEYWQ3NVQA5EAKVP6NOYQIEJHDWX76IAHRC4H7T5PNATTCYDJH4TJ5WUXCI3A5PYCGCYKV5ZL3IRF6TDAP4LGVUNT7X5IPYUD4LC5G5SH2DULRDITMR2DTKBIGXQXMSOQY7XMIL7643CIEJXX5DIWREMX5SSCAR7GTWNAXLW2XGNPLQPW3UOP4UXQEGB4CDQL4MZ7FLNICT34VGVVEUMM43OITZJUECDQ7IKDIL7GU3GQXUIYTKZM6XMOBERAPWVB2UWIWD3KUTNCPBUQ7RESKCLVYRGUI4Z3ULFYSNM23VETFZGBELD7VFP7N5SRZ3GIS2SKM4376WUWWRZ4T7IO35UVCXNBTKVZHWTKZSYQABQE72N74FPJHY4DOFGUGIL2XCAVZU3NHE57HTSA7OGUWNF7DBE63DSAY4N6OGHMGL4SBFGSLYEBW7HERFLABVNQYLELH2X7VNLYM24WN52CWDZPFSA3YXDAQYOCCQNXIEWSTKNMPBVLXYJNX4SDW5KYPNKOSYJRHYRYB23LRXC7F7SVC7PMCBPMR', + 'B$ZP0401R2NIJC6YGI5PXFPPSXIZIJ6F5MPABQ7JTUK3MABHDBG7WWGCED33GILBH3IMEZENRW77JOMKNVVTZNDY4XGRE5PH7EQFYQ4WTBWIJBVLV7CVL453J47I4EHIPX6D2QRWWCQDPWWCFPSRT5R27HHKS22TMGPIZZMDYLEWXUTPMXHAM7LRETD4ANY5OS4EDCC5XTTO6GTTLYWS6XZT3B2OLEEOWR4UDQSJMATFPSLF7G4WGTHBAJE33CR5U3LN4LKUZQ2VPU3KF3TQUCUFAQPUCOJ6F5TFS3CEDD2D4PRDMEXXYE3FUPKTQME6VJAXRXJRS7JR2YIK7VXXD4K2O6RWAAKXOMNRLSBAWCSXGIIUAXIMESFD7XHCUNPRVXXJZZVM5IAV56NAIWWYGDHUYKQPN4ZZBGJRPB5KS6CYJGQQSSYSECQTRC43ET6BGOECAFU7TDOIJ2AOXR54PO7SPNYBRIZ2MGRS2A6VBDKMJCJ5JXFBUACAQW6TXYH5AHXT4A7S4JIE27BYDNZ4RKFLVYXA3T46WXGXQOHHNUNHPXJHEG2HPKLEVQR2I2SICFW7TILMU2ERRSPV3GQHNDQEUL3GGPHEYWQ3NVQA5EAKVP6NOYQIEJHDWX76IAHRC4H7T5PNATTCYDJH4TJ5WUXCI3A5PYCGCYKV5ZL3IRF6TDAP4LGVUNT7X5IPYUD4LC5G5SH2DULRDITMR2DTKBIGXQXMSOQY7XMIL7643CIEJXX5DIWREMX5SSCAR7GTWNAXLW2XGNPLQPW3UOP4UXQEGB4CDQL4MZ7FLNICT34VGVVEUMM43OITZJUECDQ7IKDIL7GU3GQXUIYTKZM6XMOBERAPWVB2UWIWD3KUTNCPBUQ7RESKCLVYRGUI4Z3ULFYSNM23VETFZGBELD7VFP7N5SRZ3GIS2SKM4376WUWWRZ4T7IO35UVCXNBTKVZHWTKZSYQABQE72N74FPJHY4DOFGUGIL2XCAVZU3NHE57HTSA7OGUWNF7DBE63DSAY4N6OGHMGL4SBFGSLYEBW7HERFLABVNQYLELH2X7VNLYM24WN52CWDZPFSA3YXDAQYOCCQNXIEWSTKNMPBVLXYJNX4SDW5KYPNKOSYJRHYRYB23LRXC7F7SVC7PMCBPMR', + 'B$ZP0402CZV73DGFLBG3EGYTGIKV35X6GU4QSR7BE43JT5KS7C4DSBBW3CTBEH674AVXGNWAF65UGU65RJALJ5PUVX2KNTYJ6MXTTVFFIZT3FCJZRAG4LXJ3A52IYP7UFTRI2K5FGFJNVOYLK4NZC6ZZAZWMVQEJT7JAMXEN3ENQB6VM4L7N7WMFA35IPRMF6RH4ZALTUHBZI7U4GAN3CK4PQJZMWQTO5F5PZ7EV5R6QKCR27AMNN4H2GTXGJR2YM5CXOLMBSDZKHABOYMD46G2TWREHUOSNGQY3BJ6EBENF5DOF5VOXJF5Z7LN4LJRZR3MLNG4OPOMGNNIPANZB7JJJQ3OUVIU3R6FI2SD4HUINHY2ZOQ4F7ZDWIG6S7PHO6IZZNM3C7KAMAN2TW6CTLY44TAZ2DOQTXHTKOE6UEVHE7JVMJJ5PVTWBLWIRD6SCDXYPIXOXZGTVNGKGPY24F7L4OIFGLJIYF4ZGDFNFAEAFJWF3APPL777XEWHYEKVCAGERBXPKG7ZUYBTRLJE5BXOPHSYDGZ5QMBUJYJVWPB2G4V3I25AJUFAU7NTLHCJAZF4O2P5REH7MEBZVEMLCJX5H52HNRU5EXR7HP5VJORFCHQ62VE3ITYFFH4JESQS5OAJVCHOP5CZOETDGW5JE3OCIEBVD7RSDD3SSLJJVGWHQ2V34EW36JB3OVMHWVHJMEYT4I4A5NVY3RPS7ZKRPXIA5PTVH2Y7JXIQ6WO5AG7JGFLM7WEMKWSLWMNBKNIWL327SMJ7BFD6MIJVTLYFB6N4HWD4WYH62N5P426HB7JWG2KXR6EG34MTHAP6LEOZU2WWQQRC3J7PUU372TQYP7ESDLVVHJZVH54GYVTJUCER3SHQ3ABBYEUHLQXY26SHTYG42LIBQWHC4S473LH6JNLYUEUWH3OC6YY5AVGMJ47OHOXJ4H2ZVFMBHNVPSSKHG34DDHIM2G42OX4K5RYENJ4QPTWFJ2PWKK6ZIOUBVNTRE5F6ZZEDA3HUKK4CARMT3R73TBY73N6ESQBTU6BUZJOUGR367NVNNRZEPHNF5NQYGFFWKPPRMP4RQDCW3M4LBZKPTOJHM56KSX6B5GWYFJD7ETR3SFEANFZLMI6KVAPJZMSRUXHLQUQ2G5UWSSGRI', + 'B$ZP0402CZV73DGFLBG3EGYTGIKV35X6GU4QSR7BE43JT5KS7C4DSBBW3CTBEH674AVXGNWAF65UGU65RJALJ5PUVX2KNTYJ6MXTTVFFIZT3FCJZRAG4LXJ3A52IYP7UFTRI2K5FGFJNVOYLK4NZC6ZZAZWMVQEJT7JAMXEN3ENQB6VM4L7N7WMFA35IPRMF6RH4ZALTUHBZI7U4GAN3CK4PQJZMWQTO5F5PZ7EV5R6QKCR27AMNN4H2GTXGJR2YM5CXOLMBSDZKHABOYMD46G2TWREHUOSNGQY3BJ6EBENF5DOF5VOXJF5Z7LN4LJRZR3MLNG4OPOMGNNIPANZB7JJJQ3OUVIU3R6FI2SD4HUINHY2ZOQ4F7ZDWIG6S7PHO6IZZNM3C7KAMAN2TW6CTLY44TAZ2DOQTXHTKOE6UEVHE7JVMJJ5PVTWBLWIRD6SCDXYPIXOXZGTVNGKGPY24F7L4OIFGLJIYF4ZGDFNFAEAFJWF3APPL777XEWHYEKVCAGERBXPKG7ZUYBTRLJE5BXOPHSYDGZ5QMBUJYJVWPB2G4V3I25AJUFAU7NTLHCJAZF4O2P5REH7MEBZVEMLCJX5H52HNRU5EXR7HP5VJORFCHQ62VE3ITYFFH4JESQS5OAJVCHOP5CZOETDGW5JE3OCIEBVD7RSDD3SSLJJVGWHQ2V34EW36JB3OVMHWVHJMEYT4I4A5NVY3RPS7ZKRPXIA5PTVH2Y7JXIQ6WO5AG7JGFLM7WEMKWSLWMNBKNIWL327SMJ7BFD6MIJVTLYFB6N4HWD4WYH62N5P426HB7JWG2KXR6EG34MTHAP6LEOZU2WWQQRC3J7PUU372TQYP7ESDLVVHJZVH54GYVTJUCER3SHQ3ABBYEUHLQXY26SHTYG42LIBQWHC4S473LH6JNLYUEUWH3OC6YY5AVGMJ47OHOXJ4H2ZVFMBHNVPSSKHG34DDHIM2G42OX4K5RYENJ4QPTWFJ2PWKK6ZIOUBVNTRE5F6ZZEDA3HUKK4CARMT3R73TBY73N6ESQBTU6BUZJOUGR367NVNNRZEPHNF5NQYGFFWKPPRMP4RQDCW3M4LBZKPTOJHM56KSX6B5GWYFJD7ETR3SFEANFZLMI6KVAPJZMSRUXHLQUQ2G5UWSSGRI', + 'B$ZP0403GGGKSI5N46HND3DI3RFTMMIH6HI6EHTRJWJDZCBB3NGI63AFRYCSF5WNM5YYF3D2TV7W5USN7RXMS7KEOABUA7273S7SDTB7ZXXANY2CQUU72OCVN4SC47CDOY7ZXOHSFCULALBE2FNHTHDDLX3PYAJ25W53KKHOEWFMUMYDHNZODN3H44P5ZPAXZC7H3SL77GCXENXYTB4WB4HWDFBLXZE65ZPRAAQJVBUQPRQPWG2TZSVR3T426MYPK7OSYTFYY2HKLHA46CX2ZMG2EHU3HBHTWZAK4PVIS636I5OBTJUTVP3POGUK73T5DAKW2GH2KV4NLZIE6X4LT4SUJVCT2W4DGFGV5UTPR6EPOM5MF43L5VJT62QNPIDKN4P3QFND6JTPB6ZPLPGWDJUJDBEQ2AFAYLPB34H6AD4LXE2HIHWWN46Z3LABXII53DM3JBGBDNC6366NSSKW62DAZWPSN32LDOAESPZYOMPQ3RKGE2WLFVTHQONBAC5Z3G6XYKA3WGTUS6L5G35VIOVFSFQ62VE3ITYNEH4JESQS5OEJVCHGO5CZOETLGW5JEZOCIEFVD7RSDDZ24KNCI6IQIO3JSHWZBIOAWRHMTPH6EBGZ6U5P7XFETP4N3EX3RBQMGB7XXPMQ4REQMS6PNHY4ED7OEILCS3FHXYWH6IYBRLNWOFQ4VHZXETWO7FJL7A6TLMCUR7SJY5ZCODZ7P65B7R6TLY4F5N33YWAR45KQ2TC3FLI6IXRLBQM3LN5ENCQMIMFGR22J4O2GWORXCL5B3E7PK7VROTOZB5OYQD7QA' + ] + + d = DecodeQR() + for i in parts: + if d.add_data(i) == DecodeQRStatus.COMPLETE: + break + assert d.qr_type == QRType.PSBT__BBQR + + #complete should be true + assert d.is_complete + + + +def test_bbqr_psbt_p2wsh_qr(): + base64_psbt = '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' + + parts = [ + 'B$ZP05044YHEDDPOKBPHMMY3URG6GYXLQ5ZR5A4CLN3UXSKVLAYAQQIQ4BTBKU2LUWOKPXE663ZRWTRXGZTDOF3RY4CQINUEMTJJVM3G5OKCCIF5GRB7O2TPT7OF5W7VQTHDDQ4X5M4ZU67C5E255ZYWWD5NMI5IGIELQIRRP5F6R3PZI42R36XT6GO6HRJEFGNU4VIS62QTHHTLJL6JRVAVBBODWWT7EDAOK3RLLFUQNG27K5WFNNUJ3W42W7ODTTR6O3352YNGN5ZBODKI4S46OA56X445SLBKQCTHB4J57YRYJUKZFZXCSNJVVYOZIILKO7ZZIS5AVLGD6PAK6PJCLUB5NYLSD3Y2DMAOLF42I5OAHJSOLEMWAXVZB5L6USJGCHJS6NFQ5GNEDQU3Z7KTETRYWRRU57LSHHIP7R3R26RY2AZLTDYDZAYQLNYEN7IIDABMYPFNZ3FUGMBNN7MXWBWLFK5WLC2MVJO4B4NJ6XNU2O7C7SSN3IBQCFL2QTOTFJ526I7U4ZXDMQFAXUNCWINWPDDXSRPXPXP74ODKLQTKGICLJX5F6M2D2SZEL5GPWPSN4XAUMTQ4LNTRPX2H6TUOMGPMJXG3XAUDYAEN3NMDANWB5ZZUYTIYK7R3SWSAWSWENNZ3XSXRG6H3YV35SO5GSDIEE32PKZBGRYMGYTV6SVVUBOXSJIL4HU5BCW6GA7C2UPGKB3R3YXQPEERJADYRALFJIDGFFJIA4I2WASJN44I4XARFYXFCGVIJJOFJKR22E4WCSVQ6NFI3BJEYPHKH6AA', + 'B$ZP0503KUEKQJY5P37EIJDAYR75LV37UXPX2IJ7V3WD2GVR6NO44MLTGHNCRPZJBCARXNQ4PNBSVQSN7DOVL6CHAOBU3TRRNJPMRC4F5Y453TAW23M2G6AUAKHXBPND5FLHK7PA6LEEMNLWCKWSITNPTN6VK2EJN6WIYOKRHEOPOAQC2OPNVCRXHCGWUJJ4VBLYRBECPE2T27DY5WAGJJIGGOOL64XBBVU2MQ6EIGYA5O4W66AHIDKYQ7FXTRB2MEO3F4SIKXBDUZHFSGLAL24Q6V7KJETBDV3LCHCGVVNHCODUOIRFFFBKKFOYQPV25PW2SEBSXWAFUGU6RQVV6J36GNOV6Z677QPQ5QKYWOF75MLOB6PXQVITDTCQFMRN3IFAG6OTMKA4C4XXPFA7TFSRBDIBV7UGXU6VGNOMKB42TXZYVHZWUUKWW6UVRI3PRW5UP7FCOZJGMDAEHRHVT44G2T5KGNWCE5OOZPEJUOIXB7BM6Z5LKJO2OSBWFZHMS7UXYCAY4EYNKIZVO7R6QUCC33FTNJ6GB5CKPCYLNRVVLN2XSWVFSVQEKZB2AC2YUK6PWUORONEWPHXITVISATY3YFTGXL7466V6XJZPAWF6VVG3INQDB5N5JQ2JWEPXA5RUM77LUMAPYVVRGGIVF3F2Q6QQML5G6464W7GPOSEHQFKYQ5RWZP7WRB2FCWEHVQ6FEWNQB2L7HCEPQR24RSRD2UEOXEDVP4SA5MAOTF42NQ3Z5YKYPHD3RBVYFJK5ZQGBPLG37HFJ56MOT4PDFMIMPTOVX4DJICHOELCL7XBSDSVB', + 'B$ZP0503KUEKQJY5P37EIJDAYR75LV37UXPX2IJ7V3WD2GVR6NO44MLTGHNCRPZJBCARXNQ4PNBSVQSN7DOVL6CHAOBU3TRRNJPMRC4F5Y453TAW23M2G6AUAKHXBPND5FLHK7PA6LEEMNLWCKWSITNPTN6VK2EJN6WIYOKRHEOPOAQC2OPNVCRXHCGWUJJ4VBLYRBECPE2T27DY5WAGJJIGGOOL64XBBVU2MQ6EIGYA5O4W66AHIDKYQ7FXTRB2MEO3F4SIKXBDUZHFSGLAL24Q6V7KJETBDV3LCHCGVVNHCODUOIRFFFBKKFOYQPV25PW2SEBSXWAFUGU6RQVV6J36GNOV6Z677QPQ5QKYWOF75MLOB6PXQVITDTCQFMRN3IFAG6OTMKA4C4XXPFA7TFSRBDIBV7UGXU6VGNOMKB42TXZYVHZWUUKWW6UVRI3PRW5UP7FCOZJGMDAEHRHVT44G2T5KGNWCE5OOZPEJUOIXB7BM6Z5LKJO2OSBWFZHMS7UXYCAY4EYNKIZVO7R6QUCC33FTNJ6GB5CKPCYLNRVVLN2XSWVFSVQEKZB2AC2YUK6PWUORONEWPHXITVISATY3YFTGXL7466V6XJZPAWF6VVG3INQDB5N5JQ2JWEPXA5RUM77LUMAPYVVRGGIVF3F2Q6QQML5G6464W7GPOSEHQFKYQ5RWZP7WRB2FCWEHVQ6FEWNQB2L7HCEPQR24RSRD2UEOXEDVP4SA5MAOTF42NQ3Z5YKYPHD3RBVYFJK5ZQGBPLG37HFJ56MOT4PDFMIMPTOVX4DJICHOELCL7XBSDSVB', + 'B$ZP05044YHEDDPOKBPHMMY3URG6GYXLQ5ZR5A4CLN3UXSKVLAYAQQIQ4BTBKU2LUWOKPXE663ZRWTRXGZTDOF3RY4CQINUEMTJJVM3G5OKCCIF5GRB7O2TPT7OF5W7VQTHDDQ4X5M4ZU67C5E255ZYWWD5NMI5IGIELQIRRP5F6R3PZI42R36XT6GO6HRJEFGNU4VIS62QTHHTLJL6JRVAVBBODWWT7EDAOK3RLLFUQNG27K5WFNNUJ3W42W7ODTTR6O3352YNGN5ZBODKI4S46OA56X445SLBKQCTHB4J57YRYJUKZFZXCSNJVVYOZIILKO7ZZIS5AVLGD6PAK6PJCLUB5NYLSD3Y2DMAOLF42I5OAHJSOLEMWAXVZB5L6USJGCHJS6NFQ5GNEDQU3Z7KTETRYWRRU57LSHHIP7R3R26RY2AZLTDYDZAYQLNYEN7IIDABMYPFNZ3FUGMBNN7MXWBWLFK5WLC2MVJO4B4NJ6XNU2O7C7SSN3IBQCFL2QTOTFJ526I7U4ZXDMQFAXUNCWINWPDDXSRPXPXP74ODKLQTKGICLJX5F6M2D2SZEL5GPWPSN4XAUMTQ4LNTRPX2H6TUOMGPMJXG3XAUDYAEN3NMDANWB5ZZUYTIYK7R3SWSAWSWENNZ3XSXRG6H3YV35SO5GSDIEE32PKZBGRYMGYTV6SVVUBOXSJIL4HU5BCW6GA7C2UPGKB3R3YXQPEERJADYRALFJIDGFFJIA4I2WASJN44I4XARFYXFCGVIJJOFJKR22E4WCSVQ6NFI3BJEYPHKH6AA', + 'B$ZP05044YHEDDPOKBPHMMY3URG6GYXLQ5ZR5A4CLN3UXSKVLAYAQQIQ4BTBKU2LUWOKPXE663ZRWTRXGZTDOF3RY4CQINUEMTJJVM3G5OKCCIF5GRB7O2TPT7OF5W7VQTHDDQ4X5M4ZU67C5E255ZYWWD5NMI5IGIELQIRRP5F6R3PZI42R36XT6GO6HRJEFGNU4VIS62QTHHTLJL6JRVAVBBODWWT7EDAOK3RLLFUQNG27K5WFNNUJ3W42W7ODTTR6O3352YNGN5ZBODKI4S46OA56X445SLBKQCTHB4J57YRYJUKZFZXCSNJVVYOZIILKO7ZZIS5AVLGD6PAK6PJCLUB5NYLSD3Y2DMAOLF42I5OAHJSOLEMWAXVZB5L6USJGCHJS6NFQ5GNEDQU3Z7KTETRYWRRU57LSHHIP7R3R26RY2AZLTDYDZAYQLNYEN7IIDABMYPFNZ3FUGMBNN7MXWBWLFK5WLC2MVJO4B4NJ6XNU2O7C7SSN3IBQCFL2QTOTFJ526I7U4ZXDMQFAXUNCWINWPDDXSRPXPXP74ODKLQTKGICLJX5F6M2D2SZEL5GPWPSN4XAUMTQ4LNTRPX2H6TUOMGPMJXG3XAUDYAEN3NMDANWB5ZZUYTIYK7R3SWSAWSWENNZ3XSXRG6H3YV35SO5GSDIEE32PKZBGRYMGYTV6SVVUBOXSJIL4HU5BCW6GA7C2UPGKB3R3YXQPEERJADYRALFJIDGFFJIA4I2WASJN44I4XARFYXFCGVIJJOFJKR22E4WCSVQ6NFI3BJEYPHKH6AA', + 'B$ZP0500QXJHSNCUP4PQP4EZPOGW3CDEZNGMZNKEFBJXNP6UYRCGJCLMMNBFTMUVKD3MVPRHEJNWQIMTFDETEUUWJJ7FJMRGEVMUHJKVVCEHSTHGHD2TZXPHZY4XHTX46F647Z73PV7V5XSHDWHTDUFIAUAA3ICQFDGOTIKJS5LW73YPVGTW3FQ7PFUWAMMVM3R5DWZMVPSRW5BCEZI4GKQQYX6CYMAYBRKLDLVB7TKYZUGV4RFA54VJJ5PX6DUMSKTWUEUCL2VRV5E7GQ4CXOLSSC4DDPPJNTT4NAE23YH6WA4F3WCDJPFW62AIOJ5YXE4X3V2RZZRMOVQOT2YFNYDTXZF6K45JBWR2RD45XHWUPJ6QG7L45MH56SJZEKHIYST2YHHM2H74ZVYJ7GU7LURR22FL3F7CZOVALHI4NRCHILTJWQ4PVDJL22WRYRBLNX7PKGZFRERDIHRE6LNOMBUQQ47SZBWLYRQFLLDFH5S3JEOL43GDFUN3GIYAWN27RDZ2ETGRNRY6LYTM42CE6E3BFAKFAU6LOM6I36XY7DQIKPTCST2VBZWMYBH3OWC7FGPGN7QTBXCXOMFOYDW2UE6XIIJ2MJUE6YPNP3DTMWRXPDAFNR5R3GZJ2DGX7WFW5VZ3NGXAKA4M5E4IIIW73WPSO6U6EMGWSBFBSWJO42IKDSGEEDKEFPR2T6L7QHVRYYPPHQNBD4O7SORVSST77PCQHBWJXRAA2RRZF6AK6XCIIJMFXO2FL6VPPB7FKGKIDRLY53EKBTVHZL56OD5JCYFUAIYRSZQIE2RLVDZZUTQOO5NX', + 'B$ZP0501MYQ75A2GHMJQMOZQJEBRSBKUMKHZCS2YMKT2NWKTBX7RCUDLMBXKBKXVLWBJBHMPPVDXXDJ6X7PLVVTPQ6DDWM2KU3FB432NJUITYOMYJOARTMR7AVR4SHGSQKKXP2ELCFUK4U72Y367ZZDUQB626E4H6RD5H3IPZDZM7HVFCYSACX6WCTCRW2J3Q2DFFQ7LBAY4ERZKLLKWI42JQ5X3CSY5SRLGRW36KCYFE6JIDVTIRQZH6ZG4YIM7BQU73ONOSCC6ZI4PF67OEZ3MXQC7P6I2OOW4KXV3PTAN3W3J6EAIBMULRT2GY3ARBE2ND4QT34P6RUQQ3HYUGT6HNFBQPZZ35ZYHHMYYIFIWHDSJTAQ7PISJ53GCCN54NKBLIJ6LYJEBMRIP4QYVC7OGCLEWMUZFTLD5W5O3PKHEQDRZQAMICDHWF6H36Z5IRHGE3ZHAOYMWYDABH7P5Q7NUROXEOC6P3J532RLQ3NHPDRP7D2BEFIYVUZLK3AB44L7LBTN75HZHUOSXJLVCVU54DVFCGEHH3EF6W6PGN3O5YMRJWWTBQDNSUGLU5IRR5ULBBMFO4QLE3GTTW3BU4LW6VHPOPVMZ7D6TNJDNMH3MIBQL6O2PDIRKLYEFIE5Y2D6XUXCEFWODB7HO2LRNXTMFEUTIJYGYEISYE77M6X2OQKIAAI27SKVMPU4HCI5NDLL6JTFDFFXC3OCZLMX2WI62HPR6JTWTIJG6EIOQJMRMPRWV5SYXZBKK7NPJ7K7CVKGMKIHFS4LUF3ZVBHMDFPDHZ43EBIHXM73T257LLY5ZWKR3', + 'B$ZP0502N6QQLR6S2WU3KVVIEEGC3427H6WUVXVEFQEEZ5X7M5CKWKE3I5I77YADUXLG3RFZNNSQK7VWUK43HONJQVRQNLSWK2SZF3EIZ7AQ7BPDHT72PP5FZ5D3NJPO3AIYPYFDCWEMT7ZHR72AZ7G7XD7ZMRZKDP7TPSN74WIYVRX7BXUW66NEKXRP72UPSVTTVNGCVQ4LI4IPKY533OR6W2P2MOTW4OIHAVXUUJCQQPOU73F3YUOKO4B2KZ5JP44BJLGGM6EXA24EPCO2CCZ5P64HTHA2DELNPHGRJXK6F2ON4MUY54EGACZNLATHDRHLIIX7K27AWJKKHOWTLRVMYPDTB6ENWVXVSZWUSTCH2CGI2NO4XGGUEZKNWRQFHCGMPSBLNZYB25VNTONJJGNA7XODH45B7MVQJSVLZI2JW3DBUQ36S6ZWGVVQV4BFE4UOCL63G3NFIHNYNV7NQR7EZOJPN4O4C3HDAKYZ2X5BFORVJXYRUVNEPN7NNWQQZSYTU5BIEV2Q4C4B2TI5FUHSAYXDSR2XHXO5S374ZLESDD6YWM6HKLQB34FHQLLS2XCH4OKEPIHKZQ2BQC4QPRGHYI5GJZMRVKAHLOE4I6WADVRBFOHVINFMINLB5KLELCD4YPGT4GTNKKYO4P3K4UR7S2CNFSXSOBAE7SWI34MTRHVXTAMCDNZIHIOGMX3QUZT64D4HAT7STN2GCVZARI6M2JIMVY24AR35IVXKUTQ2JU4AAX7735YCPE5BBMDDWBQJYS3NM22UBKMR5IQTS42KNKKAIN5F5OFIDBV5CFDP3JHF', + ] + + d = DecodeQR() + for i in parts: + if d.add_data(i) == DecodeQRStatus.COMPLETE: + break + assert d.qr_type == QRType.PSBT__BBQR + + #complete should be true + assert d.is_complete + + tx = d.get_psbt() + + assert str(tx) == base64_psbt + + assert tx.inputs[0].witness_utxo.value == 3826 # input 1 amount in psbt + assert tx.inputs[1].witness_utxo.value == 1366 # input 2 amount in psbt + assert tx.inputs[2].witness_utxo.value == 4398 # input 3 amount in psbt + assert tx.inputs[3].witness_utxo.value == 3716 # input 4 amount in psbt + assert tx.inputs[4].witness_utxo.value == 2811 # input 5 amount in psbt + + assert len(tx.outputs) == 1 + +