@@ -130,14 +130,15 @@ public NodeOSDownloadData GetCurrentOSDownloadData()
130130 }
131131 public class NodeBuilder : IDisposable
132132 {
133- public static NodeBuilder Create ( NodeDownloadData downloadData , Network network = null , [ CallerMemberNameAttribute ] string caller = null , bool showNodeConsole = false )
133+ public static NodeBuilder Create ( NodeDownloadData downloadData , Network network = null , Func < Network , bool > useNetwork = null , [ CallerMemberNameAttribute ] string caller = null , Func < NodeRunner > customNodeRunner = null , bool showNodeConsole = false )
134134 {
135135 network = network ?? Network . RegTest ;
136+ if ( useNetwork != null && useNetwork ( network ) == false ) return null ;
136137 var isFilePath = downloadData . Version . Length >= 2 && downloadData . Version [ 1 ] == ':' ;
137138 var path = isFilePath ? downloadData . Version : EnsureDownloaded ( downloadData ) ;
138139 if ( ! Directory . Exists ( caller ) )
139140 Directory . CreateDirectory ( caller ) ;
140- return new NodeBuilder ( caller , path ) { Network = network , NodeImplementation = downloadData , ShowNodeConsole = showNodeConsole } ;
141+ return new NodeBuilder ( caller , path ) { Network = network , NodeImplementation = downloadData , CustomNodeRunner = customNodeRunner , ShowNodeConsole = showNodeConsole } ;
141142 }
142143
143144 public static string EnsureDownloaded ( NodeDownloadData downloadData )
@@ -218,6 +219,7 @@ public Network Network
218219 set ;
219220 } = Network . RegTest ;
220221 public NodeDownloadData NodeImplementation { get ; private set ; }
222+ public Func < NodeRunner > CustomNodeRunner { get ; private set ; }
221223 public RPCWalletType ? RPCWalletType { get ; set ; }
222224 public bool CreateWallet { get ; set ; } = true ;
223225
@@ -295,6 +297,8 @@ public NodeConfigParameters ConfigParameters
295297 }
296298 }
297299
300+ private readonly NodeRunner _NodeRunner ;
301+
298302 public CoreNode ( string folder , NodeBuilder builder )
299303 {
300304 this . _Builder = builder ;
@@ -308,6 +312,19 @@ public CoreNode(string folder, NodeBuilder builder)
308312 ConfigParameters . Import ( builder . ConfigParameters , true ) ;
309313 ports = new int [ 2 ] ;
310314
315+ // Some coins (such as decred) have a (slightly) different way of
316+ // running their nodes. They may require more than 2 ports, for
317+ // example. And they'd usually require a (slightly) different
318+ // cleanup proceedure than the one doen below.
319+ if ( builder . CustomNodeRunner != null )
320+ {
321+ this . _NodeRunner = builder . CustomNodeRunner ( ) ;
322+ if ( _NodeRunner . PortsNeeded > 2 )
323+ ports = new int [ _NodeRunner . PortsNeeded ] ;
324+ if ( builder . CleanBeforeStartingNode )
325+ _NodeRunner . StopPreviouslyRunningProcesses ( dataDir ) ;
326+ }
327+
311328 if ( builder . CleanBeforeStartingNode && File . Exists ( _Config ) )
312329 {
313330 var oldCreds = ExtractCreds ( File . ReadAllText ( _Config ) ) ;
@@ -330,6 +347,15 @@ public CoreNode(string folder, NodeBuilder builder)
330347 throw new InvalidOperationException ( "A running instance of bitcoind of a previous run prevent this test from starting. Please close bitcoind process manually and restart the test." ) ;
331348 }
332349 }
350+ }
351+
352+ // If cleaning previous process(es) is required, it would have been
353+ // done above by `builder.NodeRunner.StopPreviouslyRunningProcesses`
354+ // if applicable or by the if block just above. Now repeatedly try
355+ // to delete the data directory for about 10 seconds, since it may
356+ // take a while for all process(es) to fully stop.
357+ if ( builder . CleanBeforeStartingNode )
358+ {
333359 CancellationTokenSource cts = new CancellationTokenSource ( ) ;
334360 cts . CancelAfter ( 10000 ) ;
335361 while ( ! cts . IsCancellationRequested && Directory . Exists ( _Folder ) )
@@ -441,16 +467,23 @@ public NetworkCredential RPCCredentials
441467 creds = value ;
442468 }
443469 }
470+
471+ public string TLSCertFilePath => _NodeRunner == null ? null : _NodeRunner . TLSCertFilePath ( dataDir ) ;
472+
444473 public RPCClient CreateRPCClient ( )
445474 {
446- return new RPCClient ( GetRPCAuth ( ) , RPCUri , Network ) ;
475+ // Some coins (like decred) require a tls cert for rpc connections.
476+ return new RPCClient ( GetRPCAuth ( ) , RPCUri , TLSCertFilePath , Network ) ;
447477 }
448478
449479 public Uri RPCUri
450480 {
451481 get
452482 {
453- return new Uri ( "http://127.0.0.1:" + ports [ 1 ] . ToString ( ) + "/" ) ;
483+ // Some coins (like decred) require a tls cert for rpc
484+ // connections. In such cases, use https.
485+ var uriScheme = TLSCertFilePath == null ? "http" : "https" ;
486+ return new Uri ( uriScheme + "://127.0.0.1:" + ports [ 1 ] . ToString ( ) + "/" ) ;
454487 }
455488 }
456489
@@ -466,6 +499,7 @@ public RestClient CreateRESTClient()
466499 {
467500 return new RestClient ( new Uri ( "http://127.0.0.1:" + ports [ 1 ] . ToString ( ) + "/" ) ) ;
468501 }
502+
469503#if ! NOSOCKET
470504 public Node CreateNodeClient ( )
471505 {
@@ -500,6 +534,16 @@ public NodeDownloadData NodeImplementation
500534
501535 public async Task StartAsync ( )
502536 {
537+ // Some coins (such as decred) use different configuration options
538+ // and values. For such coins, do not use the generic config builder
539+ // below.
540+ if ( _NodeRunner != null )
541+ {
542+ _NodeRunner . WriteConfigFile ( dataDir , creds , ports , ConfigParameters ) ;
543+ await Run ( ) ;
544+ return ;
545+ }
546+
503547 NodeConfigParameters config = new NodeConfigParameters ( ) ;
504548 StringBuilder configStr = new StringBuilder ( ) ;
505549 if ( String . IsNullOrEmpty ( NodeImplementation . Chain ) )
@@ -562,7 +606,14 @@ private async Task Run()
562606 string appPath = new FileInfo ( this . _Builder . BitcoinD ) . FullName ;
563607 string args = "-conf=bitcoin.conf" + " -datadir=" + dataDir + " -debug=net" ;
564608
565- if ( _Builder . ShowNodeConsole )
609+ // Some coins (such as decred) have a (slightly) different way
610+ // of running their nodes. Use the custom node runner for such
611+ // coins.
612+ if ( _NodeRunner != null )
613+ {
614+ _Process = _NodeRunner . Run ( appPath , dataDir , _Builder . ShowNodeConsole ) . GetAwaiter ( ) . GetResult ( ) ;
615+ }
616+ else if ( _Builder . ShowNodeConsole )
566617 {
567618 ProcessStartInfo info = new ProcessStartInfo ( appPath , args ) ;
568619 info . UseShellExecute = true ;
@@ -602,7 +653,7 @@ private void CreateDefaultWallet()
602653 _ => string . Empty
603654 } ;
604655
605- retry :
656+ retry :
606657 string walletToolArgs = $ "{ string . Format ( _Builder . NodeImplementation . GetWalletChainSpecifier , _Builder . NodeImplementation . Chain ) } -wallet=\" wallet.dat\" { walletType } -datadir=\" { dataDir } \" create";
607658
608659 var info = new ProcessStartInfo ( walletToolPath , walletToolArgs )
@@ -615,7 +666,7 @@ private void CreateDefaultWallet()
615666 info . RedirectStandardOutput = true ;
616667 }
617668 using ( var walletToolProcess = Process . Start ( info ) )
618- {
669+ {
619670 walletToolProcess . WaitForExit ( ) ;
620671 // Some doesn't support this
621672 if ( walletToolProcess . ExitCode != 0 && walletType != string . Empty )
@@ -685,6 +736,10 @@ public void Kill(bool cleanFolder = false)
685736 _Process . Kill ( ) ;
686737 _Process . WaitForExit ( ) ;
687738 }
739+ if ( _NodeRunner != null )
740+ {
741+ _NodeRunner . Kill ( ) ;
742+ }
688743 _State = CoreNodeState . Killed ;
689744 if ( cleanFolder )
690745 CleanFolder ( ) ;
0 commit comments