mirror of
https://github.com/SeedSigner/seedsigner.git
synced 2026-10-05 15:08:25 +00:00
added xpub to qr encoder
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
from .qr_type import *
|
||||
from .decode_qr import *
|
||||
from .encode_qr import *
|
||||
from .psbt_parser import *
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user