From 002c85449768054efc64ebc7bfbcd797f168d906 Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Mon, 15 Jun 2026 12:03:46 +0200 Subject: [PATCH] feat(wallet): add P2PKH, P2SH-P2WPKH, and P2TR (Taproot/BIP86) address types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ScriptKind.Taproot (BIP86, purpose 86', P2WPKH → witness v1 bech32m plm1p) derivation wired in DerivationPaths and ChainProfiles (xprv/xpub headers, no SLIP-132 standard for P2TR); NBitcoin ScriptPubKeyType.TaprootBIP86 - Wizard step StepScriptType inserted between passphrase and password: 4 radio buttons (Legacy / Wrapped SegWit / Native SegWit★ / Taproot), localized in 6 languages; CreateOrRestore now uses SelectedScriptKind instead of hardcoded NativeSegwit; back navigation updated accordingly - BIP86 golden-vector tests (Bip86TaprootTests): output key verified against official Bitcoin BIP86 addresses, plm1p prefix asserted --- src/App/Localization/Loc.cs | 12 +++ .../ViewModels/MainWindowViewModel.Wizard.cs | 47 ++++++++-- src/App/Views/MainView.axaml | 48 ++++++++++ src/Core/Chain/ChainProfile.cs | 3 + src/Core/Chain/ChainProfiles.cs | 3 + src/Core/Crypto/DerivationPaths.cs | 5 ++ .../Crypto/Bip86TaprootTests.cs | 89 +++++++++++++++++++ 7 files changed, 198 insertions(+), 9 deletions(-) create mode 100644 tests/PalladiumWallet.Tests/Crypto/Bip86TaprootTests.cs diff --git a/src/App/Localization/Loc.cs b/src/App/Localization/Loc.cs index 51af199..6bfa0db 100644 --- a/src/App/Localization/Loc.cs +++ b/src/App/Localization/Loc.cs @@ -110,6 +110,18 @@ public sealed class Loc "Achtung: ohne Verschlüsselung bleibt der Seed im Klartext auf der Festplatte."], ["wiz.back"] = ["Indietro", "Back", "Atrás", "Retour", "Voltar", "Zurück"], ["wiz.next"] = ["Avanti", "Next", "Siguiente", "Suivant", "Próximo", "Weiter"], + ["wiz.scripttype.title"] = ["Tipo di script e indirizzi", "Script type and addresses", "Tipo de script y direcciones", "Type de script et adresses", "Tipo de script e endereços", "Skripttyp und Adressen"], + ["wiz.scripttype.hint"] = [ + "Determina il formato degli indirizzi. Se non sai cosa scegliere, usa Native SegWit.", + "Determines the address format. If unsure, use Native SegWit.", + "Determina el formato de los direcciones. Si no sabes, usa Native SegWit.", + "Détermine le format des adresses. En cas de doute, utilisez Native SegWit.", + "Determina o formato dos endereços. Em caso de dúvida, use Native SegWit.", + "Bestimmt das Adressformat. Wenn Sie unsicher sind, verwenden Sie Native SegWit."], + ["wiz.scripttype.legacy.desc"] = ["BIP44 · m/44'/… · indirizzi P", "BIP44 · m/44'/… · P addresses", "BIP44 · m/44'/… · direcciones P", "BIP44 · m/44'/… · adresses P", "BIP44 · m/44'/… · endereços P", "BIP44 · m/44'/… · P-Adressen"], + ["wiz.scripttype.wrapped.desc"] = ["BIP49 · m/49'/… · indirizzi 3", "BIP49 · m/49'/… · 3 addresses", "BIP49 · m/49'/… · direcciones 3", "BIP49 · m/49'/… · adresses 3", "BIP49 · m/49'/… · endereços 3", "BIP49 · m/49'/… · 3-Adressen"], + ["wiz.scripttype.native.desc"] = ["BIP84 · m/84'/… · indirizzi plm1q — consigliato", "BIP84 · m/84'/… · plm1q addresses — recommended", "BIP84 · m/84'/… · direcciones plm1q — recomendado", "BIP84 · m/84'/… · adresses plm1q — recommandé", "BIP84 · m/84'/… · endereços plm1q — recomendado", "BIP84 · m/84'/… · plm1q-Adressen — empfohlen"], + ["wiz.scripttype.taproot.desc"] = ["BIP86 · m/86'/… · indirizzi plm1p", "BIP86 · m/86'/… · plm1p addresses", "BIP86 · m/86'/… · direcciones plm1p", "BIP86 · m/86'/… · adresses plm1p", "BIP86 · m/86'/… · endereços plm1p", "BIP86 · m/86'/… · plm1p-Adressen"], // Pannello wallet ["wallet.close"] = ["Chiudi wallet", "Close wallet", "Cerrar wallet", "Fermer le wallet", "Fechar carteira", "Wallet schließen"], diff --git a/src/App/ViewModels/MainWindowViewModel.Wizard.cs b/src/App/ViewModels/MainWindowViewModel.Wizard.cs index 1af35ff..6af00bf 100644 --- a/src/App/ViewModels/MainWindowViewModel.Wizard.cs +++ b/src/App/ViewModels/MainWindowViewModel.Wizard.cs @@ -23,6 +23,7 @@ public partial class MainWindowViewModel public const string StepConfirmSeed = "confirm-seed"; public const string StepWords = "words"; public const string StepPassphrase = "passphrase"; + public const string StepScriptType = "script-type"; public const string StepPassword = "password"; [ObservableProperty] @@ -34,18 +35,37 @@ public partial class MainWindowViewModel [NotifyPropertyChangedFor(nameof(IsStepConfirmSeed))] [NotifyPropertyChangedFor(nameof(IsStepWords))] [NotifyPropertyChangedFor(nameof(IsStepPassphrase))] + [NotifyPropertyChangedFor(nameof(IsStepScriptType))] [NotifyPropertyChangedFor(nameof(IsStepPassword))] private string setupStep = StepStart; public bool IsStepDataLocation => SetupStep == StepDataLocation; - public bool IsStepStart => SetupStep == StepStart; + public bool IsStepStart => SetupStep == StepStart; public bool IsStepChooseWallet => SetupStep == StepChooseWallet; - public bool IsStepOpen => SetupStep == StepOpen; - public bool IsStepShowSeed => SetupStep == StepShowSeed; - public bool IsStepConfirmSeed => SetupStep == StepConfirmSeed; - public bool IsStepWords => SetupStep == StepWords; - public bool IsStepPassphrase => SetupStep == StepPassphrase; - public bool IsStepPassword => SetupStep == StepPassword; + public bool IsStepOpen => SetupStep == StepOpen; + public bool IsStepShowSeed => SetupStep == StepShowSeed; + public bool IsStepConfirmSeed => SetupStep == StepConfirmSeed; + public bool IsStepWords => SetupStep == StepWords; + public bool IsStepPassphrase => SetupStep == StepPassphrase; + public bool IsStepScriptType => SetupStep == StepScriptType; + public bool IsStepPassword => SetupStep == StepPassword; + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(IsLegacySelected))] + [NotifyPropertyChangedFor(nameof(IsWrappedSegwitSelected))] + [NotifyPropertyChangedFor(nameof(IsNativeSegwitSelected))] + [NotifyPropertyChangedFor(nameof(IsTaprootSelected))] + private ScriptKind selectedScriptKind = ScriptKind.NativeSegwit; + + public bool IsLegacySelected => SelectedScriptKind == ScriptKind.Legacy; + public bool IsWrappedSegwitSelected => SelectedScriptKind == ScriptKind.WrappedSegwit; + public bool IsNativeSegwitSelected => SelectedScriptKind == ScriptKind.NativeSegwit; + public bool IsTaprootSelected => SelectedScriptKind == ScriptKind.Taproot; + + [RelayCommand] private void SelectLegacy() => SelectedScriptKind = ScriptKind.Legacy; + [RelayCommand] private void SelectWrappedSegwit() => SelectedScriptKind = ScriptKind.WrappedSegwit; + [RelayCommand] private void SelectNativeSegwit() => SelectedScriptKind = ScriptKind.NativeSegwit; + [RelayCommand] private void SelectTaproot() => SelectedScriptKind = ScriptKind.Taproot; public string DefaultDataPath => AppPaths.DefaultDataRoot(); @@ -96,6 +116,7 @@ public partial class MainWindowViewModel private void RefreshSetupState() { SetupStep = StepStart; + SelectedScriptKind = ScriptKind.NativeSegwit; MnemonicInput = ConfirmMnemonicInput = PassphraseInput = PasswordInput = ConfirmPasswordInput = ""; WalletFileExists = AppPaths.WalletFiles(Net).Count > 0; StatusMessage = ""; @@ -192,6 +213,13 @@ public partial class MainWindowViewModel [RelayCommand] private void WizardNextFromPassphrase() + { + SetupStep = StepScriptType; + StatusMessage = ""; + } + + [RelayCommand] + private void WizardNextFromScriptType() { PasswordInput = ConfirmPasswordInput = ""; EncryptWallet = true; @@ -208,7 +236,8 @@ public partial class MainWindowViewModel StepChooseWallet or StepShowSeed or StepWords => StepStart, StepConfirmSeed => StepShowSeed, StepPassphrase => _isRestoreFlow ? StepWords : StepConfirmSeed, - StepPassword => StepPassphrase, + StepScriptType => StepPassphrase, + StepPassword => StepScriptType, _ => StepStart, }; if (SetupStep == StepStart) @@ -243,7 +272,7 @@ public partial class MainWindowViewModel var (doc, account) = WalletLoader.NewFromMnemonic( MnemonicInput, string.IsNullOrEmpty(PassphraseInput) ? null : PassphraseInput, - ScriptKind.NativeSegwit, + SelectedScriptKind, Profile); var path = AppPaths.DefaultWalletPath(Net); for (var n = 2; WalletStore.Exists(path); n++) diff --git a/src/App/Views/MainView.axaml b/src/App/Views/MainView.axaml index bf8334f..ad40194 100644 --- a/src/App/Views/MainView.axaml +++ b/src/App/Views/MainView.axaml @@ -171,6 +171,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +