2019-05-08 16:39:11 +02:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Threading.Tasks ;
using BTCPayServer.Payments ;
using NBitcoin ;
using NBXplorer.DerivationStrategy ;
using Newtonsoft.Json ;
using Newtonsoft.Json.Linq ;
namespace BTCPayServer
{
public class DerivationSchemeSettings : ISupportedPaymentMethod
{
public static DerivationSchemeSettings Parse ( string derivationStrategy , BTCPayNetwork network )
{
if ( network = = null )
throw new ArgumentNullException ( nameof ( network ) ) ;
if ( derivationStrategy = = null )
throw new ArgumentNullException ( nameof ( derivationStrategy ) ) ;
var result = new NBXplorer . DerivationStrategy . DerivationStrategyFactory ( network . NBitcoinNetwork ) . Parse ( derivationStrategy ) ;
return new DerivationSchemeSettings ( result , network ) { AccountOriginal = derivationStrategy . Trim ( ) } ;
}
2019-05-08 17:40:30 +02:00
2019-05-09 09:11:09 +02:00
public static bool TryParseFromJson ( string config , BTCPayNetwork network , out DerivationSchemeSettings strategy )
{
if ( network = = null )
throw new ArgumentNullException ( nameof ( network ) ) ;
if ( config = = null )
throw new ArgumentNullException ( nameof ( config ) ) ;
strategy = null ;
try
{
strategy = network . NBXplorerNetwork . Serializer . ToObject < DerivationSchemeSettings > ( config ) ;
strategy . Network = network ;
}
catch { }
return strategy ! = null ;
}
2019-05-08 17:40:30 +02:00
public static bool TryParseFromColdcard ( string coldcardExport , BTCPayNetwork network , out DerivationSchemeSettings settings )
{
settings = null ;
if ( coldcardExport = = null )
throw new ArgumentNullException ( nameof ( coldcardExport ) ) ;
if ( network = = null )
throw new ArgumentNullException ( nameof ( network ) ) ;
var result = new DerivationSchemeSettings ( ) ;
2019-05-08 17:54:53 +02:00
result . Source = "Coldcard" ;
2019-05-09 09:05:18 +02:00
var derivationSchemeParser = new DerivationSchemeParser ( network ) ;
2019-05-08 17:40:30 +02:00
JObject jobj = null ;
try
{
jobj = JObject . Parse ( coldcardExport ) ;
jobj = ( JObject ) jobj [ "keystore" ] ;
}
catch
{
return false ;
}
if ( jobj . ContainsKey ( "xpub" ) )
{
try
{
result . AccountOriginal = jobj [ "xpub" ] . Value < string > ( ) . Trim ( ) ;
result . AccountDerivation = derivationSchemeParser . ParseElectrum ( result . AccountOriginal ) ;
2019-05-08 17:54:53 +02:00
if ( result . AccountDerivation is DirectDerivationStrategy direct & & ! direct . Segwit )
result . AccountOriginal = null ; // Saving this would be confusing for user, as xpub of electrum is legacy derivation, but for btcpay, it is segwit derivation
2019-05-08 17:40:30 +02:00
}
catch
{
return false ;
}
}
else
{
return false ;
}
if ( jobj . ContainsKey ( "label" ) )
{
try
{
result . Label = jobj [ "label" ] . Value < string > ( ) ;
}
catch { return false ; }
}
if ( jobj . ContainsKey ( "ckcc_xfp" ) )
{
try
{
result . RootFingerprint = new HDFingerprint ( jobj [ "ckcc_xfp" ] . Value < uint > ( ) ) ;
}
catch { return false ; }
}
if ( jobj . ContainsKey ( "derivation" ) )
{
try
{
result . AccountKeyPath = new KeyPath ( jobj [ "derivation" ] . Value < string > ( ) ) ;
}
catch { return false ; }
}
settings = result ;
settings . Network = network ;
return true ;
}
2019-05-08 16:39:11 +02:00
public DerivationSchemeSettings ( )
{
}
public DerivationSchemeSettings ( DerivationStrategyBase derivationStrategy , BTCPayNetwork network )
{
if ( network = = null )
throw new ArgumentNullException ( nameof ( network ) ) ;
if ( derivationStrategy = = null )
throw new ArgumentNullException ( nameof ( derivationStrategy ) ) ;
AccountDerivation = derivationStrategy ;
Network = network ;
}
[JsonIgnore]
public BTCPayNetwork Network { get ; set ; }
2019-05-08 17:54:53 +02:00
public string Source { get ; set ; }
2019-05-08 16:39:11 +02:00
public KeyPath AccountKeyPath { get ; set ; }
2019-05-10 12:30:10 +02:00
2019-05-08 16:39:11 +02:00
public DerivationStrategyBase AccountDerivation { get ; set ; }
public string AccountOriginal { get ; set ; }
public HDFingerprint ? RootFingerprint { get ; set ; }
2019-05-10 12:30:10 +02:00
public BitcoinExtPubKey ExplicitAccountKey { get ; set ; }
[JsonIgnore]
public BitcoinExtPubKey AccountKey
{
get
{
return ExplicitAccountKey ? ? new BitcoinExtPubKey ( AccountDerivation . GetExtPubKeys ( ) . First ( ) , Network . NBitcoinNetwork ) ;
}
}
2019-05-12 04:07:41 +02:00
public IEnumerable < NBXplorer . Models . PSBTRebaseKeyRules > GetPSBTRebaseKeyRules ( )
{
if ( AccountKey ! = null & & AccountKeyPath ! = null & & RootFingerprint is HDFingerprint fp )
{
yield return new NBXplorer . Models . PSBTRebaseKeyRules ( )
{
AccountKey = AccountKey ,
AccountKeyPath = AccountKeyPath ,
MasterFingerprint = fp
} ;
}
}
2019-05-08 16:39:11 +02:00
public string Label { get ; set ; }
2019-05-08 18:06:03 +02:00
[JsonIgnore]
2019-05-08 16:39:11 +02:00
public PaymentMethodId PaymentId = > new PaymentMethodId ( Network . CryptoCode , PaymentTypes . BTCLike ) ;
public override string ToString ( )
{
return AccountDerivation . ToString ( ) ;
}
public string ToPrettyString ( )
{
2019-05-08 18:07:05 +02:00
return ! string . IsNullOrEmpty ( Label ) ? Label :
! String . IsNullOrEmpty ( AccountOriginal ) ? AccountOriginal :
2019-05-08 16:39:11 +02:00
ToString ( ) ;
}
2019-05-09 09:11:09 +02:00
public string ToJson ( )
{
return Network . NBXplorerNetwork . Serializer . ToString ( this ) ;
}
2019-05-12 04:07:41 +02:00
public void RebaseKeyPaths ( PSBT psbt )
{
foreach ( var rebase in GetPSBTRebaseKeyRules ( ) )
{
psbt . RebaseKeyPaths ( rebase . AccountKey , rebase . AccountKeyPath , rebase . MasterFingerprint ) ;
}
}
2019-05-08 16:39:11 +02:00
}
}