@@ -78,6 +78,7 @@ def env(name, default=None):
7878SLOW_MACHINE = env ("SLOW_MACHINE" , "0" ) == "1"
7979DEPRECATED_APIS = env ("DEPRECATED_APIS" , "0" ) == "1"
8080TIMEOUT = int (env ("TIMEOUT" , 180 if SLOW_MACHINE else 60 ))
81+ SUBDAEMON = env ("SUBDAEMON" , "" )
8182EXPERIMENTAL_DUAL_FUND = env ("EXPERIMENTAL_DUAL_FUND" , "0" ) == "1"
8283EXPERIMENTAL_SPLICING = env ("EXPERIMENTAL_SPLICING" , "0" ) == "1"
8384
@@ -578,6 +579,43 @@ def getnewaddress(self):
578579 return info ['unconfidential' ]
579580
580581
582+ class ValidatingLightningSignerD (TailableProc ):
583+ def __init__ (self , vlsd_dir , vlsd_port , node_id , network ):
584+ TailableProc .__init__ (self , vlsd_dir , verbose = True )
585+ self .executable = env ("REMOTE_SIGNER_CMD" , 'vlsd2' )
586+ os .environ ['ALLOWLIST' ] = env (
587+ 'REMOTE_SIGNER_ALLOWLIST' ,
588+ 'contrib/remote_hsmd/TESTING_ALLOWLIST' )
589+ self .opts = [
590+ '--network={}' .format (network ),
591+ '--datadir={}' .format (vlsd_dir ),
592+ '--connect=http://localhost:{}' .format (vlsd_port ),
593+ '--integration-test' ,
594+ ]
595+ self .prefix = 'vlsd2-%d' % (node_id )
596+ self .vlsd_port = vlsd_port
597+
598+ @property
599+ def cmd_line (self ):
600+ return [self .executable ] + self .opts
601+
602+ def start (self , stdin = None , stdout_redir = True , stderr_redir = True ,
603+ wait_for_initialized = True ):
604+ TailableProc .start (self , stdin , stdout_redir , stderr_redir )
605+ # We need to always wait for initialization
606+ self .wait_for_log ("vlsd2 git_desc" )
607+ logging .info ("vlsd2 started" )
608+
609+ def stop (self , timeout = 10 ):
610+ logging .info ("stopping vlsd2" )
611+ rc = TailableProc .stop (self , timeout )
612+ logging .info ("vlsd2 stopped" )
613+ self .logs_catchup ()
614+ return rc
615+
616+ def __del__ (self ):
617+ self .logs_catchup ()
618+
581619class LightningD (TailableProc ):
582620 def __init__ (
583621 self ,
@@ -594,6 +632,12 @@ def __init__(
594632 self .lightning_dir = lightning_dir
595633 self .port = port
596634 self .cmd_prefix = []
635+ self .lightning_dir = lightning_dir
636+ self .use_vlsd = False
637+ self .vlsd_dir = os .path .join (lightning_dir , "vlsd" )
638+ self .vlsd_port = None
639+ self .vlsd = None
640+ self .node_id = node_id
597641
598642 self .rpcproxy = bitcoindproxy
599643 self .env ['CLN_PLUGIN_LOG' ] = "cln_plugin=trace,cln_rpc=trace,cln_grpc=trace,debug"
@@ -616,12 +660,38 @@ def __init__(
616660 if grpc_port is not None :
617661 opts ['grpc-port' ] = grpc_port
618662
663+ if SUBDAEMON :
664+ assert node_id > 0
665+ subdaemons = SUBDAEMON .split (',' )
666+ # VLS_SERIAL_SELECT "swaps" the selected item with the first item
667+ select = env ("VLS_SERIAL_SELECT" , '1' )
668+ if node_id == int (select ):
669+ ndx = 1
670+ elif node_id == 1 :
671+ ndx = int (select )
672+ else :
673+ ndx = node_id
674+ if ndx > len (subdaemons ):
675+ # use the last element if not as many specifiers as nodes
676+ opts ['subdaemon' ] = subdaemons [- 1 ]
677+ else :
678+ # use the matching specifier
679+ opts ['subdaemon' ] = subdaemons [ndx - 1 ]
680+
681+ print (f"starting node { node_id } with subdaemon { opts ['subdaemon' ]} " )
682+ if SUBDAEMON == 'hsmd:remote_hsmd_socket' :
683+ self .use_vlsd = True
684+
619685 for k , v in opts .items ():
620686 self .opts [k ] = v
621687
622688 if not os .path .exists (os .path .join (lightning_dir , TEST_NETWORK )):
623689 os .makedirs (os .path .join (lightning_dir , TEST_NETWORK ))
624690
691+ if self .use_vlsd :
692+ if not os .path .exists (self .vlsd_dir ):
693+ os .makedirs (self .vlsd_dir )
694+
625695 # Last 32-bytes of final part of dir -> seed.
626696 seed = (bytes (re .search ('([^/]+)/*$' , lightning_dir ).group (1 ), encoding = 'utf-8' ) + bytes (32 ))[:32 ]
627697 if not random_hsm :
@@ -637,6 +707,10 @@ def __init__(
637707 self .early_opts = ['--developer' ]
638708
639709 def cleanup (self ):
710+ if self .use_vlsd :
711+ # Make sure the remotesigner is shutdown
712+ self .vlsd .stop ()
713+
640714 # To force blackhole to exit, disconnect file must be truncated!
641715 if 'dev-disconnect' in self .opts :
642716 with open (self .opts ['dev-disconnect' ], "w" ) as f :
@@ -657,12 +731,65 @@ def cmd_line(self):
657731
658732 return self .cmd_prefix + [self .executable ] + self .early_opts + opts
659733
734+ def __del__ (self ):
735+ if self .vlsd_port is not None :
736+ drop_unused_port (self .vlsd_port )
737+
660738 def start (self , stdin = None , wait_for_initialized = True , stderr_redir = False ):
661- self .opts ['bitcoin-rpcport' ] = self .rpcproxy .rpcport
662- TailableProc .start (self , stdin , stdout_redir = False , stderr_redir = stderr_redir )
663- if wait_for_initialized :
664- self .wait_for_log ("Server started with public key" )
665- logging .info ("LightningD started" )
739+ try :
740+ self .env ['VLS_LSS' ] = f"http://localhost:{ self .lssd_port } "
741+ self .env ['RUST_LOG' ] = 'debug'
742+ # Some of the remote hsmd proxies need a bitcoind RPC connection
743+ self .env ['BITCOIND_RPC_URL' ] = 'http://{}:{}@localhost:{}' .format (
744+ BITCOIND_CONFIG ['rpcuser' ],
745+ BITCOIND_CONFIG ['rpcpassword' ],
746+ BITCOIND_CONFIG ['rpcport' ])
747+
748+ # The remote hsmd proxies need to know which network we are using
749+ if 'network' in self .opts :
750+ self .env ['VLS_NETWORK' ] = self .opts ['network' ]
751+
752+ self .opts ['bitcoin-rpcport' ] = self .rpcproxy .rpcport
753+
754+ if self .use_vlsd :
755+ self .vlsd_port = reserve_unused_port ()
756+ # We can't do this in the constructor because we need a new port on each restart.
757+ self .env ['VLS_PORT' ] = str (self .vlsd_port )
758+ # Kill any previous vlsd (we may have been restarted)
759+ if self .vlsd is not None :
760+ logging .info ("killing prior vlsd" )
761+ self .vlsd .kill ()
762+
763+ TailableProc .start (self , stdin , stdout_redir = False , stderr_redir = stderr_redir )
764+
765+ if self .use_vlsd :
766+ # Start the remote signer first
767+ self .vlsd = ValidatingLightningSignerD (
768+ self .vlsd_dir , self .vlsd_port , self .node_id , self .opts ['network' ])
769+ self .vlsd .start (
770+ stdin , stdout_redir = True , stderr_redir = True ,
771+ wait_for_initialized = wait_for_initialized )
772+
773+ if wait_for_initialized :
774+ self .wait_for_log ("Server started with public key" )
775+ logging .info ("LightningD started" )
776+ except Exception :
777+ if self .use_vlsd :
778+ # LightningD didn't start, stop the remotesigner
779+ self .vlsd .stop ()
780+ raise
781+
782+ def stop (self , timeout = 10 ):
783+ if self .use_vlsd :
784+ # Stop the remote signer first
785+ self .vlsd .stop (timeout )
786+ return TailableProc .stop (self , timeout )
787+
788+ def kill (self ):
789+ if self .use_vlsd :
790+ # Kill the remote signer first
791+ self .vlsd .kill ()
792+ TailableProc .kill (self )
666793
667794 def wait (self , timeout = TIMEOUT ):
668795 """Wait for the daemon to stop for up to timeout seconds
0 commit comments