Skip to content

Commit

Permalink
Fix zpub QR exports and single-part UR parsing (selfcustody#178)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeff authored Sep 13, 2022
1 parent bda26bb commit 1c6ec0c
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 30 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
# Version 22.08.2 - September 13, 2022

This patch release reverts the zpub QR code format, once again including key origin derivation info which is necessary for BlueWallet to use when preparing PSBTs for signing with single-key wallets.

It is recommended to update to this version if you are using a single-key "Imported Watch-only" wallet with BlueWallet and are seeing a "cannot sign" error message when trying to send an outgoing transaction. If so, please do the following:

1. Upgrade Krux to this new release
2. Delete the affected wallet in BlueWallet (funds are safu as long as you have your mnemonic)
3. Create a new wallet in BlueWallet by importing from the new zpub QR code that Krux now displays.
4. Open the wallet in BlueWallet and pull down to fetch the old wallet's transaction history.
5. Create a new outgoing transaction and scan the QR code with Krux.
6. Krux should display the tx information and allow you to sign.
7. Display the signed QR back to BlueWallet.
8. Broadcast!

# Version 22.08.1 - August 11, 2022

This release is to fix a bug that would have prevented Amigos from performing airgapped upgrades to the next release.
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

[tool.poetry]
name = "krux"
version = "22.08.1"
version = "22.08.2"
description = "Open-source signing device firmware for Bitcoin"
authors = ["Jeff S <jeffreesun@protonmail.com>"]

Expand Down
2 changes: 1 addition & 1 deletion src/krux/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
VERSION = "22.08.1"
VERSION = "22.08.2"
SIGNER_PUBKEY = "03339e883157e45891e61ca9df4cd3bb895ef32d475b8e793559ea10a36766689b"
30 changes: 10 additions & 20 deletions src/krux/pages/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,26 +63,16 @@ def mnemonic(self):

def public_key(self):
"""Handler for the 'xpub' menu item"""
# Display xpub with key origin + derivation info
self.ctx.display.clear()
self.ctx.display.draw_centered_text(
self.ctx.wallet.key.key_expression(None, pretty=True)
)
self.ctx.input.wait_for_button()
xpub = self.ctx.wallet.key.key_expression(None)
self.display_qr_codes(xpub, FORMAT_NONE, None)
self.print_qr_prompt(xpub, FORMAT_NONE)
# Display zpub without key origin
zpub = self.ctx.wallet.key.xpub(
self.ctx.wallet.key.network[
"Zpub" if self.ctx.wallet.key.multisig else "zpub"
]
)
self.ctx.display.clear()
self.ctx.display.draw_centered_text(zpub)
self.ctx.input.wait_for_button()
self.display_qr_codes(zpub, FORMAT_NONE, None)
self.print_qr_prompt(zpub, FORMAT_NONE)
zpub = "Zpub" if self.ctx.wallet.key.multisig else "zpub"
for version in [None, self.ctx.wallet.key.network[zpub]]:
self.ctx.display.clear()
self.ctx.display.draw_centered_text(
self.ctx.wallet.key.key_expression(version, pretty=True)
)
self.ctx.input.wait_for_button()
xpub = self.ctx.wallet.key.key_expression(version)
self.display_qr_codes(xpub, FORMAT_NONE, None)
self.print_qr_prompt(xpub, FORMAT_NONE)
return MENU_CONTINUE

def wallet(self):
Expand Down
10 changes: 8 additions & 2 deletions src/krux/qr.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,20 @@ def __init__(self):
def parsed_count(self):
"""Returns the number of parsed parts so far"""
if self.format == FORMAT_UR:
completion_pct = self.decoder.fountain_decoder.estimated_percent_complete()
# Single-part URs have no expected part indexes
if self.decoder.fountain_decoder.expected_part_indexes is None:
return 1 if self.decoder.result is not None else 0
completion_pct = self.decoder.estimated_percent_complete()
return math.ceil(completion_pct * self.total_count())
return len(self.parts)

def total_count(self):
"""Returns the total number of parts there should be"""
if self.format == FORMAT_UR:
return self.decoder.fountain_decoder.expected_part_count()
# Single-part URs have no expected part indexes
if self.decoder.fountain_decoder.expected_part_indexes is None:
return 1
return self.decoder.expected_part_count()
return self.total

def parse(self, data):
Expand Down
4 changes: 2 additions & 2 deletions tests/pages/test_home.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,15 +287,15 @@ def test_public_key(mocker, m5stickv, tdata):
None,
),
mocker.call(
ctx.wallet.key.xpub(ctx.wallet.key.network[version]),
ctx.wallet.key.key_expression(ctx.wallet.key.network[version]),
FORMAT_NONE,
None,
),
]
print_qr_calls = [
mocker.call(ctx.wallet.key.key_expression(None), FORMAT_NONE),
mocker.call(
ctx.wallet.key.xpub(ctx.wallet.key.network[version]),
ctx.wallet.key.key_expression(ctx.wallet.key.network[version]),
FORMAT_NONE,
),
]
Expand Down
14 changes: 10 additions & 4 deletions tests/test_qr.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ def tdata(mocker):
"p1of3 UUucvki6KWyS35DhetbWPw1DiaccbHKywScF96E8VUwEnN1gss947UasRfkNxtrkzCeHziHyMCuoiQ2mSYsbYXuV3YwYBZwFh1c6xtBAEK1aDgPwMgqf74xTzf3m4KH",
"p3of3 gNNg4A4LNC2ZUGLsALZffNvg3yh3qg6rFxhkiyzWc44kx9Khp6Evm1j4Njh8kjifkngLTPFtX3uWNLAB1XrvpPMx6kkkhr7RnFVrA4JsDp5BwVGAXBoSBLTqweFevZ5",
]
TEST_PARTS_FORMAT_UR = [
TEST_PARTS_FORMAT_SINGLEPART_UR = [
"ur:crypto-psbt/hkadchjojkidjyzmadaejsaoaeaeaeadtkfnhdsrdtlfplcxgdlotarygawmndaopsurgtfsplkooncmswqdampahlvloyglaeaeaeaeaezczmzmzmaokefhhlahaeaeaeaecmaebbdleepkcewtbkgupfgooemenbftkifewtolmklugmlamtmkaeaeaeaeaecmaebbvaimzezmsrlsmnjswtoekgatvlpfbaueimvsvyhnaeaeaeaeaeadadctaevyykahaeaeaeaecmaebbtissotwsaswlmsrpwlnneskbgymyvlvecybylkoycpamaovdpydaemretynnmsaxaspkvtjtnngawfjzvysozerktyglspvtttsfnbqzytsrcfcsjksktnbkghaeaelaadaeaelaaeaeaelaaeaeaeaeaeaeaeaeaecpaoaxhlgawpsnghtiasnnfxioidktstoltyidhlhscapdlehlwkndytgyknktmeosktoncsjksktnbkghaeaelaadaeaelaaeaeaelaadaeaeaeaeaeaeaeaeaeamgrmswl"
]
TEST_PARTS_FORMAT_MULTIPART_UR = [
"ur:crypto-psbt/2-8/lpaoaycfadcycyamgrmswlhddkplkooncmswqdampahlvloyglaeaeaeaeaezczmzmzmaokefhhlahaeaeaeaecmaebbdleepktpcpnyde",
"ur:crypto-psbt/1-8/lpadaycfadcycyamgrmswlhddkhkadchjojkidjyzmadaejsaoaeaeaeadtkfnhdsrdtlfplcxgdlotarygawmndaopsurgtfswmwkinva",
"ur:crypto-psbt/3-8/lpaxaycfadcycyamgrmswlhddkcewtbkgupfgooemenbftkifewtolmklugmlamtmkaeaeaeaeaecmaebbvaimzezmsrlsmnjseylanegy",
Expand All @@ -38,15 +41,17 @@ def tdata(mocker):
"TEST_DATA_UR",
"TEST_PARTS_FORMAT_NONE",
"TEST_PARTS_FORMAT_PMOFN",
"TEST_PARTS_FORMAT_UR",
"TEST_PARTS_FORMAT_SINGLEPART_UR",
"TEST_PARTS_FORMAT_MULTIPART_UR",
],
)(
TEST_DATA_BYTES,
TEST_DATA_B58,
TEST_DATA_UR,
TEST_PARTS_FORMAT_NONE,
TEST_PARTS_FORMAT_PMOFN,
TEST_PARTS_FORMAT_UR,
TEST_PARTS_FORMAT_SINGLEPART_UR,
TEST_PARTS_FORMAT_MULTIPART_UR,
)


Expand All @@ -71,7 +76,8 @@ def test_parser(mocker, m5stickv, tdata):
cases = [
(FORMAT_NONE, tdata.TEST_PARTS_FORMAT_NONE),
(FORMAT_PMOFN, tdata.TEST_PARTS_FORMAT_PMOFN),
(FORMAT_UR, tdata.TEST_PARTS_FORMAT_UR),
(FORMAT_UR, tdata.TEST_PARTS_FORMAT_SINGLEPART_UR),
(FORMAT_UR, tdata.TEST_PARTS_FORMAT_MULTIPART_UR),
]
for case in cases:
fmt = case[0]
Expand Down

0 comments on commit 1c6ec0c

Please sign in to comment.