mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-21 14:04:12 +01:00
Support BIP129 Multisig wallet import (#5389)
This commit is contained in:
parent
a97172cea6
commit
28265b30d2
3 changed files with 108 additions and 0 deletions
|
@ -958,6 +958,40 @@ namespace BTCPayServer.Tests
|
|||
Assert.Equal("49'/0'/0'", specter.AccountKeySettings[0].AccountKeyPath.ToString());
|
||||
Assert.Equal("Specter", specter.Label);
|
||||
Assert.Null(error);
|
||||
|
||||
//BSMS BIP129, Nunchuk
|
||||
|
||||
var bsms = @"BSMS 1.0
|
||||
wsh(sortedmulti(1,[5c9e228d/48'/0'/0'/2']xpub6EgGHjcvovyN3nK921zAGPfuB41cJXkYRdt3tLGmiMyvbgHpss4X1eRZwShbEBb1znz2e2bCkCED87QZpin3sSYKbmCzQ9Sc7LaV98ngdeX/**,[2b0e251e/48'/0'/0'/2']xpub6DrimHB8KUSkPvmJ8Pk8RE769EdDm2VEoZ8MBz76w9QupP8Py4wexs4Pa3aRB1LUEhc9GyY6ypDWEFFRCgqeDQePcyWQfjtmintrehq3JCL/**))
|
||||
/0/*,/1/*
|
||||
bc1qfzu57kgu5jthl934f9xrdzzx8mmemx7gn07tf0grnvz504j6kzusu2v0ku
|
||||
";
|
||||
|
||||
Assert.True(DerivationSchemeSettings.TryParseFromWalletFile(bsms,
|
||||
mainnet, out var nunchuk, out error));
|
||||
|
||||
Assert.Equal(2, nunchuk.AccountKeySettings.Length);
|
||||
//check that the account key settings match those in bsms string
|
||||
Assert.Equal("5c9e228d", nunchuk.AccountKeySettings[0].RootFingerprint.ToString());
|
||||
Assert.Equal("48'/0'/0'/2'", nunchuk.AccountKeySettings[0].AccountKeyPath.ToString());
|
||||
Assert.Equal("2b0e251e", nunchuk.AccountKeySettings[1].RootFingerprint.ToString());
|
||||
Assert.Equal("48'/0'/0'/2'", nunchuk.AccountKeySettings[1].AccountKeyPath.ToString());
|
||||
|
||||
var multsig = Assert.IsType < MultisigDerivationStrategy >
|
||||
(Assert.IsType<P2WSHDerivationStrategy>(nunchuk.AccountDerivation).Inner);
|
||||
|
||||
Assert.True(multsig.LexicographicOrder);
|
||||
Assert.Equal(1, multsig.RequiredSignatures);
|
||||
|
||||
var deposit = new NBXplorer.KeyPathTemplates(null).GetKeyPathTemplate(DerivationFeature.Deposit);
|
||||
var line =nunchuk.AccountDerivation.GetLineFor(deposit).Derive(0);
|
||||
|
||||
Assert.Equal(BitcoinAddress.Create("bc1qfzu57kgu5jthl934f9xrdzzx8mmemx7gn07tf0grnvz504j6kzusu2v0ku", Network.Main).ScriptPubKey,
|
||||
line.ScriptPubKey);
|
||||
|
||||
Assert.Equal("BSMS", nunchuk.Source);
|
||||
Assert.Null(error);
|
||||
|
||||
|
||||
// Failure case
|
||||
Assert.False(DerivationSchemeSettings.TryParseFromWalletFile(
|
||||
|
|
|
@ -121,6 +121,65 @@ namespace BTCPayServer
|
|||
}
|
||||
}
|
||||
|
||||
public static bool TryParseBSMSFile(string filecontent, DerivationSchemeParser derivationSchemeParser, ref DerivationSchemeSettings derivationSchemeSettings,
|
||||
out string error)
|
||||
{
|
||||
error = null;
|
||||
try
|
||||
{
|
||||
string[] lines = filecontent.Split(
|
||||
new[] {"\r\n", "\r", "\n"},
|
||||
StringSplitOptions.None
|
||||
);
|
||||
|
||||
if (!lines[0].Trim().Equals("BSMS 1.0"))
|
||||
{;
|
||||
return false;
|
||||
}
|
||||
|
||||
var descriptor = lines[1];
|
||||
var derivationPath = lines[2].Split(',', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault()?? "/0/*";
|
||||
if (derivationPath == "No path restrictions")
|
||||
{
|
||||
derivationPath = "/0/*";
|
||||
}
|
||||
if(derivationPath != "/0/*")
|
||||
{
|
||||
error = "BTCPay Server can only derive address to the deposit and change paths";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
descriptor = descriptor.Replace("/**", derivationPath);
|
||||
var testAddress = BitcoinAddress.Create( lines[3], derivationSchemeParser.Network);
|
||||
var result = derivationSchemeParser.ParseOutputDescriptor(descriptor);
|
||||
|
||||
var deposit = new NBXplorer.KeyPathTemplates(null).GetKeyPathTemplate(DerivationFeature.Deposit);
|
||||
var line = result.Item1.GetLineFor(deposit).Derive(0);
|
||||
|
||||
if (testAddress.ScriptPubKey != line.ScriptPubKey)
|
||||
{
|
||||
error = "BSMS test address did not match our generated address";
|
||||
return false;
|
||||
}
|
||||
|
||||
derivationSchemeSettings.Source = "BSMS";
|
||||
derivationSchemeSettings.AccountDerivation = result.Item1;
|
||||
derivationSchemeSettings.AccountOriginal = descriptor.Trim();
|
||||
derivationSchemeSettings.AccountKeySettings = result.Item2.Select((path, i) => new AccountKeySettings()
|
||||
{
|
||||
RootFingerprint = path?.MasterFingerprint,
|
||||
AccountKeyPath = path?.KeyPath,
|
||||
AccountKey = result.Item1.GetExtPubKeys().ElementAt(i).GetWif(derivationSchemeParser.Network)
|
||||
}).ToArray();
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
error = $"BSMS parse error: {e.Message}";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public static bool TryParseFromWalletFile(string fileContents, BTCPayNetwork network, out DerivationSchemeSettings settings, out string error)
|
||||
{
|
||||
settings = null;
|
||||
|
@ -140,6 +199,17 @@ namespace BTCPayServer
|
|||
}
|
||||
catch
|
||||
{
|
||||
if (TryParseBSMSFile(fileContents, derivationSchemeParser,ref result, out var bsmsError))
|
||||
{
|
||||
settings = result;
|
||||
settings.Network = network;
|
||||
return true;
|
||||
}
|
||||
if (bsmsError is not null)
|
||||
{
|
||||
error = bsmsError;
|
||||
return false;
|
||||
}
|
||||
result.Source = "GenericFile";
|
||||
if (TryParseXpub(fileContents, derivationSchemeParser, ref result, ref error) ||
|
||||
TryParseXpub(fileContents, derivationSchemeParser, ref result, ref error, false))
|
||||
|
|
|
@ -56,6 +56,10 @@
|
|||
<tr>
|
||||
<td>Passport</td>
|
||||
<td>Wallet ❯ Connect Wallet ❯ BTCPay ❯ microSD ❯ Save wallet file</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Nunchuk</td>
|
||||
<td>... ❯ Export wallet configuration</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
Loading…
Add table
Reference in a new issue