Skip to content

Commit

Permalink
Merge pull request #546 from Foundation-Devices/SFT-4094-multisig-pol…
Browse files Browse the repository at this point in the history
…icy-clarification

SFT-4094: multisig policy clarification
  • Loading branch information
mjg-foundation authored Aug 23, 2024
2 parents 6db84f7 + a477a0c commit 6e8f4a7
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
get_deriv_path_from_addr_type_and_acct,
get_addr_type_from_deriv,
derive_address)
from public_constants import TRUST_PSBT
from public_constants import MUSIG_SKIP
from wallets.constants import EXPORT_MODE_MICROSD, EXPORT_MODE_QR
from wallets.sw_wallets import supported_software_wallets
from utils import random_hex, spinner_task
Expand Down Expand Up @@ -306,7 +306,7 @@ async def export_by_qr(self):
self.set_result(False)
return
else:
common.settings.set('multisig_policy', TRUST_PSBT)
common.settings.set('multisig_policy', MUSIG_SKIP)
self.goto(self.complete)
else:
self.goto(self.complete)
Expand Down Expand Up @@ -375,7 +375,7 @@ async def export_by_microsd(self):
self.set_result(False)
return
else:
common.settings.set('multisig_policy', TRUST_PSBT)
common.settings.set('multisig_policy', MUSIG_SKIP)
self.goto(self.complete)
else:
self.goto(self.complete)
Expand Down Expand Up @@ -475,7 +475,7 @@ async def do_multisig_config_import(self):
self.set_result(False)
return
else:
common.settings.set('multisig_policy', TRUST_PSBT)
common.settings.set('multisig_policy', MUSIG_SKIP)
self.goto(self.complete)
else:
self.goto(self.complete)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,50 @@ async def show_overview(self):
msg, is_dup = self.ms.format_overview()

if is_dup:
await ErrorPage(text="Duplicate wallet will not be imported.").show()
await ErrorPage(text="Duplicate multisig wallet will not be imported.").show()
self.set_result(False)
return
else:
result = await LongTextPage(card_header={'title': 'Confirm Import'}, text=msg, centered=True).show()
if not result:
self.set_result(False)
return

await LongTextPage(card_header={'title': 'Confirm Import'}, text=msg, centered=True, left_micron=None).show()

self.goto(self.show_details)

async def show_details(self):
from errors import Error
from utils import spinner_task, escape_text
from tasks import save_multisig_wallet_task
from utils import escape_text

msg = self.ms.format_details()

result = await LongTextPage(card_header={'title': escape_text(self.ms.name)}, text=msg, centered=True).show()

if not result:
self.back()
return

self.goto(self.confirm_import)

async def confirm_import(self):
from pages import QuestionPage

result = await QuestionPage('Import new multisig wallet?').show()

if not result:
self.set_result(False)
return

self.goto(self.import_wallet)

async def import_wallet(self):
from utils import spinner_task
from errors import Error
from tasks import save_multisig_wallet_task

# User confirmed the import, so save
(error,) = await spinner_task('Saving multisig', save_multisig_wallet_task, args=[self.ms])
if not error:
self.set_result(True)
elif error is Error.USER_SETTINGS_FULL:
await ErrorPage(text='Out of space in user settings. Too many multisig configs?').show()
self.set_result(False)
else:
# User confirmed the import, so save
(error,) = await spinner_task('Saving multisig', save_multisig_wallet_task, args=[self.ms])
if not error:
self.set_result(True)
elif error is Error.USER_SETTINGS_FULL:
await ErrorPage(text='Out of space in user settings. Too many multisig configs?').show()
self.set_result(False)
else:
await ErrorPage(text='Unable to save multisig config.').show()
self.set_result(False)
await ErrorPage(text='Unable to save multisig config.').show()
self.set_result(False)
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ async def check_multisig_import(self):
if self.psbt.multisig_import_needs_approval:
result = await ImportMultisigWalletFlow(self.psbt.active_multisig).run()
if not result:
await ErrorPage(
'The transaction can still be signed, but this multisig config will not be saved.').show()
text = 'The transaction can still be signed, but this multisig config will not be saved.'
result2 = await ErrorPage(text=text, left_micron=microns.Back).show()
if not result2:
return

self.goto(self.show_transaction_details)

Expand Down
33 changes: 8 additions & 25 deletions ports/stm32/boards/Passport/modules/multisig_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from ubinascii import hexlify as b2a_hex
from utils import xfp2str, str2xfp, cleanup_deriv_path, keypath_to_str, str_to_keypath
from public_constants import (AF_P2SH, AF_P2WSH_P2SH, AF_P2WSH, AFC_SCRIPT,
TRUST_OFFER, TRUST_PSBT, TRUST_VERIFY, MAX_SIGNERS)
MUSIG_DEFAULT, MUSIG_SKIP, MUSIG_REQUIRE, MAX_SIGNERS)
from constants import MAX_MULTISIG_NAME_LEN
from exceptions import FatalPSBTIssue
from opcodes import OP_CHECKMULTISIG
Expand Down Expand Up @@ -158,14 +158,8 @@ def chain(self):

@classmethod
def get_trust_policy(cls):
from common import settings

which = settings.get('multisig_policy', None)

if which is None:
which = TRUST_VERIFY if cls.exists() else TRUST_OFFER

return which
from utils import get_multisig_policy
return get_multisig_policy()

def serialize(self):
# return a JSON-able object
Expand Down Expand Up @@ -329,12 +323,6 @@ def get_all(cls):
# return them all, as a generator
return cls.iter_wallets()

@classmethod
def exists(cls):
# are there any wallets defined?
from common import settings
return bool(settings.get('multisig', False))

@classmethod
def get_count(cls):
from common import settings
Expand Down Expand Up @@ -876,9 +864,9 @@ def import_from_psbt(cls, M, N, xpubs_list):
trust_mode = cls.get_trust_policy()
# print('import_from_psbt(): trust_mode = {}'.format(trust_mode))

if trust_mode == TRUST_VERIFY:
if trust_mode == MUSIG_REQUIRE:
# already checked for existing import and wasn't found, so fail
raise FatalPSBTIssue("XPUBs in PSBT do not match any existing wallet")
raise FatalPSBTIssue("XPUBs in PSBT do not match any existing wallet as required by multisig policy")

# build up an in-memory version of the wallet.
# - capture address format based on path used for my leg (if standards compliant)
Expand Down Expand Up @@ -907,7 +895,7 @@ def import_from_psbt(cls, M, N, xpubs_list):

# may just keep just in-memory version, no approval required, if we are
# trusting PSBT's today, otherwise caller will need to handle UX w.r.t new wallet
return ms, (trust_mode != TRUST_PSBT)
return ms, (trust_mode != MUSIG_SKIP)

def validate_psbt_xpubs(self, xpubs_list):
# The xpubs provided in PSBT must be exactly right, compared to our record.
Expand Down Expand Up @@ -988,18 +976,13 @@ def format_overview(self, importing=True):
wallet first. Differences: '''.format(recolor(COPPER_HEX, 'WARNING:')) + ', '.join(diff_items)
is_dup = True
elif importing and num_dups:
msg = 'Duplicate wallet. All details are the same as an existing wallet, so it will not be added.'
msg = 'Duplicate wallet. All details are the same as an existing wallet, so it will not be added.\n\n'
is_dup = True
elif not importing:
msg = ''
else:
msg = 'Create new multisig wallet?'
msg = ''

derivs, dsum = self.get_deriv_paths()

if importing:
msg += '\n\n'

msg += '''{name_title}
{name}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@
# multisig_policy_setting_page.py - Set the multisig policy

from pages import SettingPage
from public_constants import TRUST_OFFER, TRUST_VERIFY, TRUST_PSBT
from public_constants import MUSIG_ASK, MUSIG_REQUIRE, MUSIG_SKIP, MUSIG_DEFAULT
from utils import get_multisig_policy

# Chooser for trust policy
ch = ['Ask to Import', 'Require Existing', 'Skip Verification']
values = [TRUST_OFFER, TRUST_VERIFY, TRUST_PSBT]
values = [MUSIG_ASK, MUSIG_REQUIRE, MUSIG_SKIP]


class MultisigPolicySettingPage(SettingPage):
OPTIONS = [
{'label': 'Ask to Import', 'value': TRUST_OFFER},
{'label': 'Require Existing', 'value': TRUST_VERIFY},
{'label': 'Skip Verification', 'value': TRUST_PSBT},
{'label': 'Ask to Import', 'value': MUSIG_ASK},
{'label': 'Require Existing', 'value': MUSIG_REQUIRE},
{'label': 'Skip Verification', 'value': MUSIG_SKIP},
]

def __init__(self, card_header=None, statusbar=None):
Expand All @@ -24,4 +25,4 @@ def __init__(self, card_header=None, statusbar=None):
statusbar=statusbar,
setting_name='multisig_policy',
options=self.OPTIONS,
default_value=self.OPTIONS[1].get('value'))
default_value=get_multisig_policy())
4 changes: 2 additions & 2 deletions ports/stm32/boards/Passport/modules/pages/page.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ def detach(self):

def left_action(self, is_pressed):
# print('Page.right_action()')
if not is_pressed:
if not is_pressed and self.left_micron:
self.set_result(False)

def right_action(self, is_pressed):
# print('Page.right_action()')
if not is_pressed:
if not is_pressed and self.right_micron:
self.set_result(True)

# By default, returns None so caller can differentiate between user pressing a button to back out
Expand Down
8 changes: 5 additions & 3 deletions ports/stm32/boards/Passport/modules/public_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,11 @@
MAX_SIGNERS = const(15)

# PSBT Xpub trust policies
TRUST_VERIFY = const(0)
TRUST_OFFER = const(1)
TRUST_PSBT = const(2)
MUSIG_REQUIRE = const(0)
MUSIG_ASK = const(1)
MUSIG_SKIP = const(2)
MUSIG_DEFAULT = MUSIG_ASK
MUSIG_TEMP_DEFAULT = MUSIG_SKIP

# Default Directories
DIR_BACKUPS = 'backups'
Expand Down
7 changes: 6 additions & 1 deletion ports/stm32/boards/Passport/modules/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import lvgl as lv
from constants import NUM_BACKUP_CODE_SECTIONS, NUM_DIGITS_PER_BACKUP_CODE_SECTION
from public_constants import DIR_BACKUPS
from public_constants import DIR_BACKUPS, MUSIG_DEFAULT, MUSIG_TEMP_DEFAULT
from files import CardSlot
from styles.colors import DEFAULT_LARGE_ICON_COLOR
import ustruct
Expand Down Expand Up @@ -1569,4 +1569,9 @@ def insufficient_randomness(seed_words):
return False


def get_multisig_policy():
default = MUSIG_TEMP_DEFAULT if has_temporary_seed() else MUSIG_DEFAULT
return common.settings.get('multisig_policy', default)


# EOF
3 changes: 0 additions & 3 deletions ports/stm32/boards/Passport/modules/wallets/theya.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,4 @@
'filename_pattern_multisig': '{xfp}-theya-multisig.json'}
],
'export_fw_version': True,
# 'skip_address_validation': True,
# 'skip_multisig_import': True,
# 'force_multisig_policy': True
}

0 comments on commit 6e8f4a7

Please sign in to comment.