Skip to content
This repository has been archived by the owner on Jun 4, 2018. It is now read-only.

Commit

Permalink
Add method to import seed when creating a wallet.
Browse files Browse the repository at this point in the history
This only adds the dialog and will call the CreateNewWallet RPC with
the decoded seed rather than a randomly-generated one.

As btcwallet does not support recovering addresses and transactions
for an imported seed, this feature won't work correctly and
transaction history will have to be recovered after generating the
same number of addresses used from the previous wallet.  However, once
that is implemented, this GUI feature should work as intended.

The seed can be inputed using either hex or the PGP word list
encoding.  At the moment, no checksumming is used or removed from the
input, and all bytes are considered part of the seed.  If any
additional data is added to a seed encoding later, the
WalletSeed.DecodeAndValidateUserInput method will need to be changed
to interpret this separately.

Closes #24.
  • Loading branch information
jrick committed Feb 17, 2016
1 parent ba8ca3c commit 481c689
Show file tree
Hide file tree
Showing 12 changed files with 256 additions and 81 deletions.
1 change: 1 addition & 0 deletions Paymetheus.Bitcoin/Paymetheus.Bitcoin.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
<Compile Include="Wallet\UnspentOutput.cs" />
<Compile Include="Wallet\Wallet.cs" />
<Compile Include="Wallet\WalletChanges.cs" />
<Compile Include="Wallet\WalletSeed.cs" />
<Compile Include="Wallet\WalletTransaction.cs" />
</ItemGroup>
<ItemGroup>
Expand Down
7 changes: 6 additions & 1 deletion Paymetheus.Bitcoin/Util/Hexadecimal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public static byte[] Decode(string value)
{
byte[] result;
if (!TryDecode(value, out result))
throw new Exception("Value is not a valid hexadecimal string");
throw new HexadecimalEncodingException();
return result;
}

Expand Down Expand Up @@ -72,4 +72,9 @@ public static string Encode(byte[] bytes)
return s.ToString();
}
}

public class HexadecimalEncodingException : Exception
{
public HexadecimalEncodingException() : base("Value is not a valid hexadecimal string") { }
}
}
16 changes: 0 additions & 16 deletions Paymetheus.Bitcoin/Wallet/Wallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;

namespace Paymetheus.Bitcoin.Wallet
{
Expand All @@ -28,11 +27,6 @@ public sealed class Wallet
/// </summary>
public const int MinRecentTransactions = 10;

/// <summary>
/// The length in bytes of the wallet's BIP0032 seed.
/// </summary>
public const int SeedLength = 32;

public Wallet(BlockChainIdentity activeChain, TransactionSet txSet, Dictionary<Account, AccountProperties> accounts, BlockIdentity chainTip)
{
if (activeChain == null)
Expand Down Expand Up @@ -328,15 +322,5 @@ public string OutputDestination(WalletTransaction.Output output)
public string AccountName(Account account) => _accounts[account].AccountName;

public IEnumerable<KeyValuePair<Account, AccountProperties>> EnumrateAccounts() => _accounts;

public static byte[] RandomSeed()
{
var seed = new byte[SeedLength];
using (var prng = new RNGCryptoServiceProvider())
{
prng.GetBytes(seed);
}
return seed;
}
}
}
62 changes: 62 additions & 0 deletions Paymetheus.Bitcoin/Wallet/WalletSeed.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright (c) 2016 The btcsuite developers
// Licensed under the ISC license. See LICENSE file in the project root for full license information.

using Paymetheus.Bitcoin.Util;
using System;
using System.Security.Cryptography;

namespace Paymetheus.Bitcoin.Wallet
{
public static class WalletSeed
{
/// <summary>
/// The length in bytes of the wallet's BIP0032 seed.
/// </summary>
public const int SeedLength = 32;

public static byte[] GenerateRandomSeed()
{
var seed = new byte[SeedLength];
using (var prng = new RNGCryptoServiceProvider())
{
prng.GetBytes(seed);
}
return seed;
}

/// <summary>
/// Decodes user input as either the hexadecimal or PGP word list encoding
/// of a seed and validates the seed length.
/// </summary>
public static byte[] DecodeAndValidateUserInput(string userInput, PgpWordList pgpWordList)
{
if (userInput == null)
throw new ArgumentNullException(nameof(userInput));
if (pgpWordList == null)
throw new ArgumentNullException(nameof(pgpWordList));

var decodedInput = DecodeUserInput(userInput, pgpWordList);
if (decodedInput.Length != SeedLength)
{
throw new Exception($"Decoded seed must have byte length {SeedLength}");
}
return decodedInput;
}

private static byte[] DecodeUserInput(string userInput, PgpWordList pgpWordList)
{
byte[] seed;
if (Hexadecimal.TryDecode(userInput, out seed))
return seed;

var splitInput = userInput.Split(new char[0], StringSplitOptions.RemoveEmptyEntries);
if (splitInput.Length == 1)
{
// Hex decoding failed, but it's not a multi-word mneumonic either.
// Assume the user intended hex.
throw new HexadecimalEncodingException();
}
return pgpWordList.Decode(splitInput);
}
}
}
54 changes: 54 additions & 0 deletions Paymetheus/BindingProperties.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) 2016 The btcsuite developers
// Licensed under the ISC license. See LICENSE file in the project root for full license information.

using System.Windows;
using System.Windows.Data;
using System.Windows.Input;

namespace Paymetheus
{
static class BindingProperties
{
public static readonly DependencyProperty UpdateSourceOnEnterProperty =
DependencyProperty.RegisterAttached(nameof(UpdateSourceOnEnter), typeof(DependencyProperty),
typeof(BindingProperties), new PropertyMetadata(null, UpdateSourceOnEnter));

public static DependencyProperty GetUpdateSourceOnEnterProperty(DependencyObject instance)
{
return (DependencyProperty)instance.GetValue(UpdateSourceOnEnterProperty);
}

public static void SetUpdateSourceOnEnterProperty(DependencyObject instance, DependencyProperty value)
{
instance.SetValue(UpdateSourceOnEnterProperty, value);
}

private static void UpdateSourceOnEnter(DependencyObject dp, DependencyPropertyChangedEventArgs e)
{
var element = dp as UIElement;
if (element == null)
return;

if (e.OldValue != null)
element.PreviewKeyDown -= UpdateSourceOnEnter_PreviewKeyDown;
if (e.NewValue != null)
element.PreviewKeyDown += UpdateSourceOnEnter_PreviewKeyDown;
}

private static void UpdateSourceOnEnter_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
var element = sender as UIElement;
if (element == null)
return;

var property = GetUpdateSourceOnEnterProperty(element);
if (property == null)
return;

BindingOperations.GetBindingExpression(element, property)?.UpdateSource();
}
}
}
}
4 changes: 2 additions & 2 deletions Paymetheus/ConnectionWizardView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
<DataTemplate DataType="{x:Type local:ConsensusServerRpcConnectionDialog}">
<local:ConsensusServerConnectionOptionsView/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:DisplaySeedDialog}">
<local:DisplaySeedView/>
<DataTemplate DataType="{x:Type local:CreateOrImportSeedDialog}">
<local:CreateOrImportSeedView/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:PromptPublicPassphraseDialog}">
<local:PublicPassphrasePromptView/>
Expand Down
43 changes: 43 additions & 0 deletions Paymetheus/CreateOrImportSeedView.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<UserControl x:Class="Paymetheus.CreateOrImportSeedView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Paymetheus"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="600">
<DockPanel Focusable="False">
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="Continue" IsDefault="True" Command="{Binding ContinueCommand}" Width="100" Margin="6,12,3,6" Padding="10 2"/>
</StackPanel>
<Label DockPanel.Dock="Top" FontSize="18" Content="Create or recover wallet"/>
<RadioButton GroupName="seedSelection" DockPanel.Dock="Top" Content="Create new wallet" IsChecked="{Binding CreateChecked}"/>
<RadioButton GroupName="seedSelection" DockPanel.Dock="Top" Content="Recover wallet from seed" IsChecked="{Binding ImportChecked}"/>
<Grid>
<StackPanel Visibility="{Binding CreateChecked, Converter={StaticResource hiddenConverter}}">
<Label>
<TextBlock TextWrapping="Wrap">
The wallet seed must be backed up before the wallet may be used.
This seed is necessary to recover wallet keys in case of data loss.
Write down this seed and save it in a secure location.
The next prompt will require entering this seed to confirm it has been saved.
</TextBlock>
</Label>
<Label Content="{Binding Bip0032SeedHex}" FontSize="16" HorizontalContentAlignment="Center"/>
</StackPanel>
<StackPanel Visibility="{Binding ImportChecked, Converter={StaticResource hiddenConverter}}">
<Label>
<TextBlock TextWrapping="Wrap">
The seed from a previously-created wallet may be used to recreate the wallet
with all of its addresses and keys. The seed should be formatted as a
hexadecimal number (64 characters between 0-9 and A-F) or a list of
mneumonic words.
</TextBlock>
</Label>
<Label Content="Seed:"/>
<TextBox Text="{Binding ImportedSeed, Mode=OneWayToSource}"
local:BindingProperties.UpdateSourceOnEnterProperty="TextBox.Text"/>
</StackPanel>
</Grid>
</DockPanel>
</UserControl>
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
// Copyright (c) 2016 The btcsuite developers
// Licensed under the ISC license. See LICENSE file in the project root for full license information.

using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
Expand All @@ -19,11 +16,11 @@
namespace Paymetheus
{
/// <summary>
/// Interaction logic for DisplaySeedView.xaml
/// Interaction logic for CreateOrImportSeedView.xaml
/// </summary>
public partial class DisplaySeedView : UserControl
public partial class CreateOrImportSeedView : UserControl
{
public DisplaySeedView()
public CreateOrImportSeedView()
{
InitializeComponent();
}
Expand Down
24 changes: 0 additions & 24 deletions Paymetheus/DisplaySeedView.xaml

This file was deleted.

2 changes: 1 addition & 1 deletion Paymetheus/PassphraseDialogViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ private async void ExecuteAction()
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
MessageBox.Show(ex.ToString());
}
}
}
Expand Down
11 changes: 6 additions & 5 deletions Paymetheus/Paymetheus.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,13 @@
<DependentUpon>AccountView.xaml</DependentUpon>
</Compile>
<Compile Include="AccountViewModel.cs" />
<Compile Include="BindingProperties.cs" />
<Compile Include="ConfirmSeedView.xaml.cs">
<DependentUpon>ConfirmSeedView.xaml</DependentUpon>
</Compile>
<Compile Include="CreateOrImportSeedView.xaml.cs">
<DependentUpon>CreateOrImportSeedView.xaml</DependentUpon>
</Compile>
<Compile Include="StartupWizard.cs" />
<Compile Include="ConnectionWizardView.xaml.cs">
<DependentUpon>ConnectionWizardView.xaml</DependentUpon>
Expand All @@ -160,9 +164,6 @@
<DependentUpon>CreateTransactionView.xaml</DependentUpon>
</Compile>
<Compile Include="DialogViewModelBase.cs" />
<Compile Include="DisplaySeedView.xaml.cs">
<DependentUpon>DisplaySeedView.xaml</DependentUpon>
</Compile>
<Compile Include="ImportDialogView.xaml.cs">
<DependentUpon>ImportDialogView.xaml</DependentUpon>
</Compile>
Expand Down Expand Up @@ -232,11 +233,11 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="CreateTransactionView.xaml">
<Page Include="CreateOrImportSeedView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="DisplaySeedView.xaml">
<Page Include="CreateTransactionView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
Expand Down
Loading

0 comments on commit 481c689

Please sign in to comment.