mirror of
https://github.com/SeedSigner/seedsigner.git
synced 2026-10-05 23:18:25 +00:00
@@ -0,0 +1,3 @@
|
||||
[submodule "urtypes"]
|
||||
path = src/vendor/urtypes
|
||||
url = https://github.com/jreesun/urtypes.git
|
||||
@@ -223,7 +223,14 @@ mkvirtualenv --python=python3 seedsigner-env
|
||||
|
||||
Now download the SeedSigner code:
|
||||
```
|
||||
git clone https://github.com/SeedSigner/seedsigner
|
||||
git clone --recursive https://github.com/SeedSigner/seedsigner
|
||||
```
|
||||
|
||||
If you have already cloned in the past and just need to init/pull submodules:
|
||||
```
|
||||
cd seedsigner
|
||||
git submodule update --init
|
||||
cd ..
|
||||
```
|
||||
|
||||
Install the necessary Python dependencies:
|
||||
|
||||
@@ -7,3 +7,4 @@ qrcode==6.1
|
||||
RPi.GPIO==0.7.0
|
||||
six==1.16.0
|
||||
spidev==3.5
|
||||
-e src/vendor/urtypes
|
||||
@@ -5,7 +5,7 @@ persistent_settings = False
|
||||
|
||||
[display]
|
||||
text_color = ORANGE
|
||||
qr_background_color = FFFFFF
|
||||
qr_background_color = 555555
|
||||
camera_rotation = 0
|
||||
|
||||
[wallet]
|
||||
|
||||
@@ -10,6 +10,8 @@ from seedsigner.helpers.ur2.ur_decoder import URDecoder
|
||||
from seedsigner.helpers.bcur import (cbor_decode, bc32decode)
|
||||
from seedsigner.models import QRType, Seed
|
||||
|
||||
from urtypes.crypto import PSBT as UR_PSBT
|
||||
|
||||
###
|
||||
### DecodeQR Class
|
||||
### Purpose: used to process images or string data from animated qr codes with psbt data to create binary/base64 psbt
|
||||
@@ -141,7 +143,8 @@ class DecodeQR:
|
||||
if self.complete:
|
||||
if self.qr_type == QRType.PSBTUR2:
|
||||
cbor = self.ur_decoder.result_message().cbor
|
||||
return cbor_decode(cbor)
|
||||
return UR_PSBT.from_cbor(cbor).data
|
||||
# return cbor_decode(cbor)
|
||||
elif self.qr_type == QRType.PSBTSPECTER:
|
||||
return self.specter_qr.getData()
|
||||
elif self.qr_type == QRType.PSBTURLEGACY:
|
||||
|
||||
@@ -9,6 +9,9 @@ from seedsigner.helpers.bcur import (bc32encode, cbor_encode, bcur_encode)
|
||||
from seedsigner.helpers.qr import QR
|
||||
from seedsigner.models import Seed, QRType, EncodeQRDensity
|
||||
|
||||
from urtypes.crypto import PSBT as UR_PSBT
|
||||
from urtypes.crypto import Account, HDKey, Output, Keypath, PathComponent, SCRIPT_EXPRESSION_TAG_MAP
|
||||
|
||||
###
|
||||
### EncodeQR Class
|
||||
### Purpose: used to encode psbt for displaying as qr image
|
||||
@@ -63,6 +66,8 @@ class EncodeQR:
|
||||
self.encoder = UREncodePSBTQR(self.psbt, self.qr_density)
|
||||
elif self.qr_type == QRType.SEEDSSQR:
|
||||
self.encoder = SeedSSQR(self.seed_phrase, self.wordlist)
|
||||
elif self.qr_type == QRType.URXPUBQR:
|
||||
self.encoder = URXPubQR(self.seed_phrase, self.passphrase, self.derivation, self.network, self.qr_density, self.wordlist)
|
||||
elif self.qr_type == QRType.XPUBQR:
|
||||
self.encoder = XPubQR(self.seed_phrase, self.passphrase, self.derivation, self.network, self.wordlist)
|
||||
elif self.qr_type == QRType.SPECTERXPUBQR:
|
||||
@@ -100,10 +105,8 @@ class UREncodePSBTQR:
|
||||
def __init__(self, p, qr_density):
|
||||
self.psbt = p
|
||||
self.qr_max_fragement_size = 20
|
||||
|
||||
cbor_encoder = CBOREncoder()
|
||||
cbor_encoder.encodeBytes(self.psbt.serialize())
|
||||
qr_ur_bytes = UR("crypto-psbt", cbor_encoder.get_bytes())
|
||||
|
||||
qr_ur_bytes = UR("crypto-psbt", UR_PSBT(self.psbt.serialize()).to_cbor())
|
||||
|
||||
if qr_density == EncodeQRDensity.LOW:
|
||||
self.qr_max_fragement_size = 10
|
||||
@@ -228,7 +231,6 @@ class XPubQR:
|
||||
raise Exception('Wordlist Required')
|
||||
|
||||
version = bip32.detect_version(self.derivation, default="xpub", network=NETWORKS[network])
|
||||
print(self.derivation)
|
||||
self.seed = Seed(mnemonic=self.seed_phrase, passphrase=self.passphrase, wordlist=self.wordlist)
|
||||
self.root = bip32.HDKey.from_seed(self.seed.seed, version=NETWORKS[network]["xprv"])
|
||||
self.fingerprint = self.root.child(0).fingerprint
|
||||
@@ -313,4 +315,79 @@ class SpecterXPubQR(XPubQR):
|
||||
return len(self.parts)
|
||||
|
||||
def isComplete(self):
|
||||
return self.sent_complete
|
||||
return self.sent_complete
|
||||
|
||||
class URXPubQR(XPubQR):
|
||||
|
||||
def __init__(self, seed_phrase, passphrase, derivation, network, qr_density, wordlist):
|
||||
|
||||
XPubQR.__init__(self, seed_phrase, passphrase, derivation, network, wordlist)
|
||||
|
||||
if qr_density == EncodeQRDensity.LOW:
|
||||
self.qr_max_fragement_size = 10
|
||||
elif qr_density == EncodeQRDensity.MEDIUM:
|
||||
self.qr_max_fragement_size = 30
|
||||
elif qr_density == EncodeQRDensity.HIGH:
|
||||
self.qr_max_fragement_size = 120
|
||||
|
||||
def derivation_to_keypath(path: str) -> list:
|
||||
arr = path.split("/")
|
||||
if arr[0] == "m":
|
||||
arr = arr[1:]
|
||||
if len(arr) == 0:
|
||||
return Keypath([],self.root.my_fingerprint, None)
|
||||
if arr[-1] == "":
|
||||
# trailing slash
|
||||
arr = arr[:-1]
|
||||
|
||||
for i, e in enumerate(arr):
|
||||
if e[-1] == "h" or e[-1] == "'":
|
||||
arr[i] = PathComponent(int(e[:-1]), True)
|
||||
else:
|
||||
arr[i] = PathComponent(int(e), False)
|
||||
|
||||
return Keypath(arr, self.root.my_fingerprint, len(arr))
|
||||
|
||||
origin = derivation_to_keypath(derivation)
|
||||
|
||||
self.ur_hdkey = HDKey({ 'key': self.xpub.key.serialize(),
|
||||
'chain_code': self.xpub.chain_code,
|
||||
'origin': origin,
|
||||
'parent_fingerprint': self.xpub.fingerprint})
|
||||
|
||||
ur_outputs = []
|
||||
|
||||
if len(origin.components) > 0:
|
||||
if origin.components[0].index == 84: # Native Single Sig
|
||||
ur_outputs.append(Output([SCRIPT_EXPRESSION_TAG_MAP[404]],self.ur_hdkey))
|
||||
elif origin.components[0].index == 49: # Nested Single Sig
|
||||
ur_outputs.append(Output([SCRIPT_EXPRESSION_TAG_MAP[400], SCRIPT_EXPRESSION_TAG_MAP[404]],self.ur_hdkey))
|
||||
elif origin.components[0].index == 48: # Multisig
|
||||
if len(origin.components) >= 4:
|
||||
if origin.components[3].index == 2: # Native Multisig
|
||||
ur_outputs.append(Output([SCRIPT_EXPRESSION_TAG_MAP[401]],self.ur_hdkey))
|
||||
elif origin.components[3].index == 1: # Nested Multisig
|
||||
ur_outputs.append(Output([SCRIPT_EXPRESSION_TAG_MAP[400], SCRIPT_EXPRESSION_TAG_MAP[401]],self.ur_hdkey))
|
||||
|
||||
# If empty, add all script types
|
||||
if len(ur_outputs) == 0:
|
||||
ur_outputs.append(Output([SCRIPT_EXPRESSION_TAG_MAP[404]],self.ur_hdkey))
|
||||
ur_outputs.append(Output([SCRIPT_EXPRESSION_TAG_MAP[400], SCRIPT_EXPRESSION_TAG_MAP[404]],self.ur_hdkey))
|
||||
ur_outputs.append(Output([SCRIPT_EXPRESSION_TAG_MAP[401]],self.ur_hdkey))
|
||||
ur_outputs.append(Output([SCRIPT_EXPRESSION_TAG_MAP[400], SCRIPT_EXPRESSION_TAG_MAP[401]],self.ur_hdkey))
|
||||
ur_outputs.append(Output([SCRIPT_EXPRESSION_TAG_MAP[403]],self.ur_hdkey))
|
||||
|
||||
ur_account = Account(self.root.my_fingerprint, ur_outputs)
|
||||
|
||||
qr_ur_bytes = UR("crypto-account", ur_account.to_cbor())
|
||||
|
||||
self.ur2_encode = UREncoder(qr_ur_bytes, self.qr_max_fragement_size, 0)
|
||||
|
||||
def seqLen(self):
|
||||
return self.ur2_encode.fountain_encoder.seq_len()
|
||||
|
||||
def nextPart(self) -> str:
|
||||
return self.ur2_encode.next_part().upper()
|
||||
|
||||
def isComplete(self):
|
||||
return self.ur2_encode.is_complete()
|
||||
@@ -17,6 +17,7 @@ class QRType(IntEnum):
|
||||
SEED4LETTERMNEMONIC = 9
|
||||
XPUBQR = 20
|
||||
SPECTERXPUBQR = 21
|
||||
URXPUBQR = 22
|
||||
BITCOINADDRESSQR = 30
|
||||
SPECTERWALLETQR = 40
|
||||
URWALLETQR = 41
|
||||
|
||||
@@ -203,8 +203,10 @@ class Settings(Singleton):
|
||||
def getXPubType(software):
|
||||
if software == "Specter Desktop":
|
||||
return QRType.SPECTERXPUBQR
|
||||
else:
|
||||
elif software == "Blue Wallet":
|
||||
return QRType.XPUBQR
|
||||
else:
|
||||
return QRType.URXPUBQR
|
||||
|
||||
@property
|
||||
def qr_density_name(self):
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ persistent_settings = False
|
||||
|
||||
[display]
|
||||
text_color = ORANGE
|
||||
qr_background_color = FFFFFF
|
||||
qr_background_color = 555555
|
||||
camera_rotation = 0
|
||||
|
||||
[wallet]
|
||||
|
||||
+1
Submodule src/vendor/urtypes added at 8d643aa7f5
@@ -85,4 +85,14 @@ def test_specter_xpub_qr():
|
||||
assert e.nextPart() == "p3of4 MxfrVggXj5NJmaNEToWpax8GMMucozvAdqf1bW1J"
|
||||
assert e.nextPart() == "p4of4 sZsfdBzsK3VUC5"
|
||||
|
||||
def test_ur_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.URXPUBQR, network="test", derivation="m/48h/1h/0h/2h", qr_density=EncodeQRDensity.MEDIUM, wordlist=bip39.WORDLIST)
|
||||
|
||||
assert e.nextPart() == "UR:CRYPTO-ACCOUNT/1-4/LPADAACSJKCYYNRDRTYAHDCAOEADCYSSMECPONAOLYTAADMETAADDLOXAXHDCLAOKSRLNLKPUEGYATHPMNRTAAOLAH"
|
||||
assert e.nextPart() == "UR:CRYPTO-ACCOUNT/2-4/LPAOAACSJKCYYNRDRTYAHDCASNKKGHZMLUZORPVDGUOTECSTTKTOLPCWPTNTLKZTTIZTBEAAHDCXVDTPMYQDLAWNGL"
|
||||
assert e.nextPart() == "UR:CRYPTO-ACCOUNT/3-4/LPAXAACSJKCYYNRDRTYAHDCARSTDSPZSBZSPGERLGDATUYNLPYBTGYIYYKBTWTAOSWKSVTSGCHBYDKYAVDSEPDJLAT"
|
||||
assert e.nextPart() == "UR:CRYPTO-ACCOUNT/4-4/LPAAAACSJKCYYNRDRTYAHDCAAMTAADDYOEADLOCSDYYKADYKAEYKAOYKAOCYSSMECPONAYCYIOREKKJKAECADRRKIE"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user