added xpub to qr encoder

This commit is contained in:
newtonick
2021-08-02 14:37:16 -04:00
parent 0f7cc42e4c
commit 6aa91aeb37
5 changed files with 195 additions and 45 deletions
+1
View File
@@ -1,3 +1,4 @@
from .qr_type import *
from .decode_qr import *
from .encode_qr import *
from .psbt_parser import *
+8 -18
View File
@@ -1,12 +1,13 @@
from pyzbar import pyzbar
from pyzbar.pyzbar import ZBarSymbol
import re
from enum import IntEnum
import re
import base64
from embit import bip39, psbt
from binascii import a2b_base64, b2a_base64
from seedsigner.helpers.ur2.ur_decoder import URDecoder
from seedsigner.helpers.bcur import (cbor_decode, bc32decode)
from seedsigner.models.qr_type import QRType
###
### DecodeQR Class
@@ -15,15 +16,19 @@ from seedsigner.helpers.bcur import (cbor_decode, bc32decode)
class DecodeQR:
def __init__(self, qr_type=None):
def __init__(self, **kwargs):
self.complete = False
self.qr_type = qr_type
self.qr_type = None
self.ur_decoder = URDecoder() # UR2 decoder
self.specter_qr = SpecterDecodePSBTQR() # Specter Desktop PSBT QR base64 decoder
self.legacy_ur = LegacyURDecodeQR() # UR Legacy decoder
self.base64_qr = Base64DecodeQR() # Single Segments Base64
self.seedqr = SeedQR()
for key, value in kwargs.items():
if key == "qr_type":
self.qr_type = value
def addImage(self, image):
qr_str = DecodeQR.QR2Str(image)
if not qr_str:
@@ -402,21 +407,6 @@ class SeedQR:
return self.seed_phrase
return None
###
### QRType Class IntEum
### Purpose: used in DecodeQR to communicate qr encoding type
###
class QRType(IntEnum):
PSBTBASE64 = 1
PSBTSPECTER = 2
PSBTURLEGACY = 3
PSBTUR2 = 5
SEEDSSQR = 6
SEEDUR2 = 7
INVALID = 100
###
### DecodeQRStatus Class IntEum
### Purpose: used in DecodeQR to communicate status of adding qr frame/segment
+148 -24
View File
@@ -1,12 +1,13 @@
from enum import IntEnum
from embit import psbt, bip39
from binascii import b2a_base64
from embit import psbt, bip39, bip32
from embit.networks import NETWORKS
from binascii import b2a_base64, hexlify
from seedsigner.helpers.ur2.ur_encoder import UREncoder
from seedsigner.helpers.ur2.cbor_lite import CBOREncoder
from seedsigner.helpers.ur2.ur import UR
from seedsigner.helpers.bcur import (bc32encode, cbor_encode, bcur_encode)
from seedsigner.helpers.qr import QR
from seedsigner.models.decode_qr import QRType
from seedsigner.models.qr_type import QRType
###
### EncodeQR Class
@@ -15,32 +16,52 @@ from seedsigner.models.decode_qr import QRType
class EncodeQR:
def __init__(self, p, qr_type=None, qr_density=None):
if qr_type == None and self.psbt != None:
self.qr_type = QRType.PSBTSPECTER
else:
self.qr_type = qr_type
if self.qr_type in (QRType.PSBTSPECTER, QRType.PSBTUR2):
self.psbt = p
else:
self.psbt = None
if qr_density == None:
self.qr_density = EncodeQRDensity.MEDIUM
else:
self.qr_density = qr_density
def __init__(self, **kwargs):
self.psbt = None
self.seed_phrase = None
self.passphrase = None
self.derivation = None
self.network = None
self.policy = None
self.qr_type = None
self.qr_density = None
self.qr = QR()
for key, value in kwargs.items():
if key == "psbt":
self.psbt = value
elif key == "seed_phrase":
self.seed_phrase = value
elif key == "passphrase":
self.passphrase = value
elif key == "derivation":
self.derivation = value
elif key == "network":
self.network = value
elif key == "policy":
self.policy = value
elif key == "qr_type":
self.qr_type = value
elif key == "qr_density":
self.qr_density = value
if self.qr_type == None:
raise Exception('Encoder Type Required')
if self.qr_density == None:
self.qr_density = EncodeQRDensity.MEDIUM
self.encoder = None
if qr_type == QRType.PSBTSPECTER:
if self.qr_type == QRType.PSBTSPECTER:
self.encoder = SpecterEncodePSBTQR(self.psbt, self.qr_density)
elif qr_type == QRType.PSBTUR2:
elif self.qr_type == QRType.PSBTUR2:
self.encoder = UREncodePSBTQR(self.psbt, self.qr_density)
elif qr_type == QRType.SEEDSSQR:
seed_phrase = p
self.encoder = SeedSSQR(seed_phrase)
elif self.qr_type == QRType.SEEDSSQR:
self.encoder = SeedSSQR(self.seed_phrase)
elif self.qr_type == QRType.XPUBQR:
self.encoder = XPubQR(self.seed_phrase, self.passphrase, self.derivation, self.network, self.policy)
elif self.qr_type == QRType.SPECTERXPUBQR:
self.encoder = SpecterXPubQR(self.seed_phrase, self.passphrase, self.derivation, self.network, self.policy, self.qr_density)
else:
raise Exception('Encoder Type not Supported')
@@ -180,6 +201,109 @@ class SeedSSQR:
def isComplete(self):
return True
class XPubQR:
def __init__(self, seed_phrase, passphrase, derivation, network, policy):
self.seed_phrase = seed_phrase
self.passphrase = passphrase
self.derivation = derivation
self.parts = []
self.part_num_sent = 0
self.sent_complete = False
self.network = network
self.seed = bip39.mnemonic_to_seed((" ".join(self.seed_phrase)).strip(), self.passphrase)
self.root = bip32.HDKey.from_seed(self.seed, version=NETWORKS[self.network]["xprv"])
self.fingerprint = self.root.child(0).fingerprint
self.bip48_xprv = self.root.derive(self.derivation)
self.bip48_xpub = self.bip48_xprv.to_public()
xpub_base58 = ""
if policy == "PKWPKH":
self.xpub_base58 = self.bip48_xpub.to_base58(NETWORKS[self.network]["zpub"])
elif policy == "PKWSH":
self.xpub_base58 = self.bip48_xpub.to_base58(NETWORKS[self.network]["Zpub"])
else:
raise Exception('Policy Type not Supported')
self.xpubstring = "[%s%s]%s" % (hexlify(self.fingerprint).decode('utf-8'),self.derivation[1:],self.xpub_base58)
if isinstance(self, XPubQR):
self.__createParts()
def __createParts(self):
self.parts = []
self.parts.append(self.xpubstring)
def nextPart(self) -> str:
if len(self.parts) > 0:
self.sent_complete = True
return self.parts[0]
def seqLen(self):
return len(self.parts)
def isComplete(self):
return self.sent_complete
class SpecterXPubQR(XPubQR):
def __init__(self, seed_phrase, passphrase, derivation, network, policy, qr_density):
self.qr_max_fragement_size = 65
if qr_density == EncodeQRDensity.LOW:
self.qr_max_fragement_size = 40
elif qr_density == EncodeQRDensity.MEDIUM:
self.qr_max_fragement_size = 65
elif qr_density == EncodeQRDensity.HIGH:
self.qr_max_fragement_size = 90
XPubQR.__init__(self, seed_phrase, passphrase, derivation, network, policy)
self.__createParts()
def __createParts(self):
self.parts = []
start = 0
stop = self.qr_max_fragement_size
qr_cnt = ((len(self.xpubstring)-1) // self.qr_max_fragement_size) + 1
if qr_cnt == 1:
self.parts.append(self.xpubstring[start:stop])
cnt = 0
while cnt < qr_cnt and qr_cnt != 1:
part = "p" + str(cnt+1) + "of" + str(qr_cnt) + " " + self.xpubstring[start:stop]
self.parts.append(part)
start = start + self.qr_max_fragement_size
stop = stop + self.qr_max_fragement_size
if stop > len(self.xpubstring):
stop = len(self.xpubstring)
cnt += 1
def nextPart(self) -> str:
# if part num sent is gt number of parts, start at 0
if self.part_num_sent > (len(self.parts) - 1):
self.part_num_sent = 0
part = self.parts[self.part_num_sent]
# when parts sent eq num of parts in list
if self.part_num_sent == (len(self.parts) - 1):
self.sent_complete = True
# increment to next part
self.part_num_sent += 1
return part
def seqLen(self):
return len(self.parts)
def isComplete(self):
return self.sent_complete
class EncodeQRDensity(IntEnum):
LOW = 1
+17
View File
@@ -0,0 +1,17 @@
from enum import IntEnum
###
### QRType Class IntEum
### Purpose: used with DecodeQR and EncodeQR to communicate qr encoding type
###
class QRType(IntEnum):
PSBTBASE64 = 1
PSBTSPECTER = 2
PSBTURLEGACY = 3
PSBTUR2 = 5
SEEDSSQR = 6
SEEDUR2 = 7
XPUBQR = 8
SPECTERXPUBQR = 9
INVALID = 100
+21 -3
View File
@@ -11,7 +11,7 @@ def test_ur_qr_encode():
tx = psbt.PSBT.parse(a2b_base64(base64_psbt))
e = EncodeQR(tx, QRType.PSBTUR2)
e = EncodeQR(psbt=tx, qr_type=QRType.PSBTUR2)
cnt = 0
while cnt <= 10:
@@ -26,7 +26,7 @@ def test_specter_qr_encode():
tx = psbt.PSBT.parse(a2b_base64(base64_psbt))
e = EncodeQR(tx, QRType.PSBTSPECTER)
e = EncodeQR(psbt=tx, qr_type=QRType.PSBTSPECTER)
cnt = 0
while cnt <= 10:
@@ -61,11 +61,29 @@ def test_seedsigner_qr():
mnemonic = "obscure bone gas open exotic abuse virus bunker shuffle nasty ship dash"
e = EncodeQR(mnemonic.split(" "), QRType.SEEDSSQR)
e = EncodeQR(seed_phrase=mnemonic.split(" "), qr_type=QRType.SEEDSSQR)
print(e.nextPart())
assert e.nextPart() == "121802020768124106400009195602431595117715840445"
def test_xpub_qr():
mnemonic = "obscure bone gas open exotic abuse virus bunker shuffle nasty ship dash"
e = EncodeQR(seed_phrase=mnemonic.split(" "), passphrase="pass", qr_type=QRType.XPUBQR, network="test", policy="PKWPKH", derivation="m/48h/1h/0h/2h")
assert e.nextPart() == "[c49122a5/48h/1h/0h/2h]vpub5adb6xr5X1yqx2v7qoo5uR32BBCQsj9tRe9zbMRsH8X6tYGE6CzuiF6k3njCyHrU5JWPMsoPwNZd9Bk9Kaqy29izB8PbuU6smtv8HLcTEAv"
def test_specter_xpub_qr():
mnemonic = "obscure bone gas open exotic abuse virus bunker shuffle nasty ship dash"
e = EncodeQR(seed_phrase=mnemonic.split(" "), passphrase="pass", qr_type=QRType.SPECTERXPUBQR, network="test", policy="PKWPKH", derivation="m/48h/1h/0h/2h", qr_density=EncodeQRDensity.LOW)
assert e.nextPart() == "p1of4 [c49122a5/48h/1h/0h/2h]vpub5adb6xr5X1yqx"
assert e.nextPart() == "p2of4 2v7qoo5uR32BBCQsj9tRe9zbMRsH8X6tYGE6Czui"
assert e.nextPart() == "p3of4 F6k3njCyHrU5JWPMsoPwNZd9Bk9Kaqy29izB8Pbu"
assert e.nextPart() == "p4of4 U6smtv8HLcTEAv"