@@ -32,7 +32,8 @@ use service::{
3232 FactoryGenesis , PruningMode , ChainSpec ,
3333} ;
3434use network:: {
35- Protocol , config:: { NetworkConfiguration , NonReservedPeerMode , Secret } ,
35+ self , multiaddr:: Protocol ,
36+ config:: { NetworkConfiguration , NonReservedPeerMode , NodeKeyConfig } ,
3637 build_multiaddr,
3738} ;
3839use primitives:: H256 ;
@@ -50,6 +51,7 @@ pub use structopt::clap::App;
5051use params:: {
5152 RunCmd , PurgeChainCmd , RevertCmd , ImportBlocksCmd , ExportBlocksCmd , BuildSpecCmd ,
5253 NetworkConfigurationParams , SharedParams , MergeParameters , TransactionPoolParams ,
54+ NodeKeyParams , NodeKeyType
5355} ;
5456pub use params:: { NoCustom , CoreParams } ;
5557pub use traits:: { GetLogFilter , AugmentClap } ;
@@ -61,7 +63,18 @@ use lazy_static::lazy_static;
6163use futures:: Future ;
6264use substrate_telemetry:: TelemetryEndpoints ;
6365
64- const MAX_NODE_NAME_LENGTH : usize = 32 ;
66+ /// The maximum number of characters for a node name.
67+ const NODE_NAME_MAX_LENGTH : usize = 32 ;
68+
69+ /// The file name of the node's Secp256k1 secret key inside the chain-specific
70+ /// network config directory, if neither `--node-key` nor `--node-key-file`
71+ /// is specified in combination with `--node-key-type=secp256k1`.
72+ const NODE_KEY_SECP256K1_FILE : & str = "secret" ;
73+
74+ /// The file name of the node's Ed25519 secret key inside the chain-specific
75+ /// network config directory, if neither `--node-key` nor `--node-key-file`
76+ /// is specified in combination with `--node-key-type=ed25519`.
77+ const NODE_KEY_ED25519_FILE : & str = "secret_ed25519" ;
6578
6679/// Executable version. Used to pass version information from the root crate.
6780pub struct VersionInfo {
@@ -101,7 +114,7 @@ fn generate_node_name() -> String {
101114 let node_name = Generator :: with_naming ( Name :: Numbered ) . next ( ) . unwrap ( ) ;
102115 let count = node_name. chars ( ) . count ( ) ;
103116
104- if count < MAX_NODE_NAME_LENGTH {
117+ if count < NODE_NAME_MAX_LENGTH {
105118 break node_name
106119 }
107120 } ;
@@ -133,14 +146,14 @@ fn base_path(cli: &SharedParams, version: &VersionInfo) -> PathBuf {
133146 )
134147}
135148
136- fn create_input_err < T : Into < String > > ( msg : T ) -> error:: Error {
149+ fn input_err < T : Into < String > > ( msg : T ) -> error:: Error {
137150 error:: ErrorKind :: Input ( msg. into ( ) ) . into ( )
138151}
139152
140153/// Check whether a node name is considered as valid
141154fn is_node_name_valid ( _name : & str ) -> Result < ( ) , & str > {
142155 let name = _name. to_string ( ) ;
143- if name. chars ( ) . count ( ) >= MAX_NODE_NAME_LENGTH {
156+ if name. chars ( ) . count ( ) >= NODE_NAME_MAX_LENGTH {
144157 return Err ( "Node name too long" ) ;
145158 }
146159
@@ -231,14 +244,60 @@ where
231244 }
232245}
233246
234- fn parse_node_key ( key : Option < String > ) -> error:: Result < Option < Secret > > {
235- match key. map ( |k| H256 :: from_str ( & k) ) {
236- Some ( Ok ( secret) ) => Ok ( Some ( secret. into ( ) ) ) ,
237- Some ( Err ( err) ) => Err ( create_input_err ( format ! ( "Error parsing node key: {}" , err) ) ) ,
238- None => Ok ( None ) ,
247+ /// Create a `NodeKeyConfig` from the given `NodeKeyParams` in the context
248+ /// of an optional network config storage directory.
249+ fn node_key_config < P > ( params : NodeKeyParams , net_config_dir : & Option < P > )
250+ -> error:: Result < NodeKeyConfig >
251+ where
252+ P : AsRef < Path >
253+ {
254+ match params. node_key_type {
255+ NodeKeyType :: Secp256k1 =>
256+ params. node_key . as_ref ( ) . map ( parse_secp256k1_secret) . unwrap_or_else ( ||
257+ Ok ( params. node_key_file
258+ . or_else ( || net_config_file ( net_config_dir, NODE_KEY_SECP256K1_FILE ) )
259+ . map ( network:: Secret :: File )
260+ . unwrap_or ( network:: Secret :: New ) ) )
261+ . map ( NodeKeyConfig :: Secp256k1 ) ,
262+
263+ NodeKeyType :: Ed25519 =>
264+ params. node_key . as_ref ( ) . map ( parse_ed25519_secret) . unwrap_or_else ( ||
265+ Ok ( params. node_key_file
266+ . or_else ( || net_config_file ( net_config_dir, NODE_KEY_ED25519_FILE ) )
267+ . map ( network:: Secret :: File )
268+ . unwrap_or ( network:: Secret :: New ) ) )
269+ . map ( NodeKeyConfig :: Ed25519 )
239270 }
240271}
241272
273+ fn net_config_file < P > ( net_config_dir : & Option < P > , name : & str ) -> Option < PathBuf >
274+ where
275+ P : AsRef < Path >
276+ {
277+ net_config_dir. as_ref ( ) . map ( |d| d. as_ref ( ) . join ( name) )
278+ }
279+
280+ /// Create an error caused by an invalid node key argument.
281+ fn invalid_node_key ( e : impl std:: fmt:: Display ) -> error:: Error {
282+ input_err ( format ! ( "Invalid node key: {}" , e) )
283+ }
284+
285+ /// Parse a Secp256k1 secret key from a hex string into a `network::Secret`.
286+ fn parse_secp256k1_secret ( hex : & String ) -> error:: Result < network:: Secp256k1Secret > {
287+ H256 :: from_str ( hex) . map_err ( invalid_node_key) . and_then ( |bytes|
288+ network:: identity:: secp256k1:: SecretKey :: from_bytes ( bytes)
289+ . map ( network:: Secret :: Input )
290+ . map_err ( invalid_node_key) )
291+ }
292+
293+ /// Parse a Ed25519 secret key from a hex string into a `network::Secret`.
294+ fn parse_ed25519_secret ( hex : & String ) -> error:: Result < network:: Ed25519Secret > {
295+ H256 :: from_str ( & hex) . map_err ( invalid_node_key) . and_then ( |bytes|
296+ network:: identity:: ed25519:: SecretKey :: from_bytes ( bytes)
297+ . map ( network:: Secret :: Input )
298+ . map_err ( invalid_node_key) )
299+ }
300+
242301/// Fill the given `PoolConfiguration` by looking at the cli parameters.
243302fn fill_transaction_pool_configuration < F : ServiceFactory > (
244303 options : & mut FactoryFullConfiguration < F > ,
@@ -295,7 +354,7 @@ fn fill_network_configuration(
295354 config. public_addresses = Vec :: new ( ) ;
296355
297356 config. client_version = client_id;
298- config. use_secret = parse_node_key ( cli. node_key ) ?;
357+ config. node_key = node_key_config ( cli. node_key_params , & config . net_config_path ) ?;
299358
300359 config. in_peers = cli. in_peers ;
301360 config. out_peers = cli. out_peers ;
@@ -324,7 +383,7 @@ where
324383 match is_node_name_valid ( & config. name ) {
325384 Ok ( _) => ( ) ,
326385 Err ( msg) => bail ! (
327- create_input_err (
386+ input_err (
328387 format!( "Invalid node name '{}'. Reason: {}. If unsure, use none." ,
329388 config. name,
330389 msg
@@ -347,7 +406,7 @@ where
347406 Some ( ref s) if s == "archive" => PruningMode :: ArchiveAll ,
348407 None => PruningMode :: default ( ) ,
349408 Some ( s) => PruningMode :: keep_blocks (
350- s. parse ( ) . map_err ( |_| create_input_err ( "Invalid pruning mode specified" ) ) ?
409+ s. parse ( ) . map_err ( |_| input_err ( "Invalid pruning mode specified" ) ) ?
351410 ) ,
352411 } ;
353412
@@ -443,31 +502,27 @@ where
443502// 9926-9949 Unassigned
444503
445504fn with_default_boot_node < F > (
446- mut spec : ChainSpec < FactoryGenesis < F > > ,
447- cli : & BuildSpecCmd ,
505+ spec : & mut ChainSpec < FactoryGenesis < F > > ,
506+ cli : BuildSpecCmd ,
448507 version : & VersionInfo ,
449- ) -> error:: Result < ChainSpec < FactoryGenesis < F > > >
508+ ) -> error:: Result < ( ) >
450509where
451510 F : ServiceFactory
452511{
453512 if spec. boot_nodes ( ) . is_empty ( ) {
454- let network_path =
455- Some ( network_path ( & base_path ( & cli. shared_params , version) , spec. id ( ) ) . to_string_lossy ( ) . into ( ) ) ;
456- let network_key = parse_node_key ( cli. node_key . clone ( ) ) ?;
457-
458- let network_keys =
459- network:: obtain_private_key ( & network_key, & network_path)
460- . map_err ( |err| format ! ( "Error obtaining network key: {}" , err) ) ?;
461-
462- let peer_id = network_keys. to_peer_id ( ) ;
513+ let base_path = base_path ( & cli. shared_params , version) ;
514+ let storage_path = network_path ( & base_path, spec. id ( ) ) ;
515+ let node_key = node_key_config ( cli. node_key_params , & Some ( storage_path) ) ?;
516+ let keys = node_key. into_keypair ( ) ?;
517+ let peer_id = keys. public ( ) . into_peer_id ( ) ;
463518 let addr = build_multiaddr ! [
464519 Ip4 ( [ 127 , 0 , 0 , 1 ] ) ,
465520 Tcp ( 30333u16 ) ,
466521 P2p ( peer_id)
467522 ] ;
468523 spec. add_boot_node ( addr)
469524 }
470- Ok ( spec )
525+ Ok ( ( ) )
471526}
472527
473528fn build_spec < F , S > (
@@ -480,9 +535,10 @@ where
480535 S : FnOnce ( & str ) -> Result < Option < ChainSpec < FactoryGenesis < F > > > , String > ,
481536{
482537 info ! ( "Building chain spec" ) ;
483- let spec = load_spec ( & cli. shared_params , spec_factory) ?;
484- let spec = with_default_boot_node :: < F > ( spec, & cli, version) ?;
485- let json = service:: chain_ops:: build_spec :: < FactoryGenesis < F > > ( spec, cli. raw ) ?;
538+ let raw_output = cli. raw ;
539+ let mut spec = load_spec ( & cli. shared_params , spec_factory) ?;
540+ with_default_boot_node :: < F > ( & mut spec, cli, version) ?;
541+ let json = service:: chain_ops:: build_spec :: < FactoryGenesis < F > > ( spec, raw_output) ?;
486542
487543 print ! ( "{}" , json) ;
488544
@@ -707,6 +763,8 @@ fn kill_color(s: &str) -> String {
707763#[ cfg( test) ]
708764mod tests {
709765 use super :: * ;
766+ use tempdir:: TempDir ;
767+ use network:: identity:: { secp256k1, ed25519} ;
710768
711769 #[ test]
712770 fn tests_node_name_good ( ) {
@@ -722,4 +780,111 @@ mod tests {
722780 assert ! ( is_node_name_valid( "www.visit.me" ) . is_err( ) ) ;
723781 assert ! ( is_node_name_valid( "email@domain" ) . is_err( ) ) ;
724782 }
783+
784+ #[ test]
785+ fn test_node_key_config_input ( ) {
786+ fn secret_input ( net_config_dir : Option < String > ) -> error:: Result < ( ) > {
787+ NodeKeyType :: variants ( ) . into_iter ( ) . try_for_each ( |t| {
788+ let node_key_type = NodeKeyType :: from_str ( t) . unwrap ( ) ;
789+ let sk = match node_key_type {
790+ NodeKeyType :: Secp256k1 => secp256k1:: SecretKey :: generate ( ) . as_ref ( ) . to_vec ( ) ,
791+ NodeKeyType :: Ed25519 => ed25519:: SecretKey :: generate ( ) . as_ref ( ) . to_vec ( )
792+ } ;
793+ let params = NodeKeyParams {
794+ node_key_type,
795+ node_key : Some ( format ! ( "{:x}" , H256 :: from_slice( sk. as_ref( ) ) ) ) ,
796+ node_key_file : None
797+ } ;
798+ node_key_config ( params, & net_config_dir) . and_then ( |c| match c {
799+ NodeKeyConfig :: Secp256k1 ( network:: Secret :: Input ( ref ski) )
800+ if node_key_type == NodeKeyType :: Secp256k1 &&
801+ & sk[ ..] == ski. as_ref ( ) => Ok ( ( ) ) ,
802+ NodeKeyConfig :: Ed25519 ( network:: Secret :: Input ( ref ski) )
803+ if node_key_type == NodeKeyType :: Ed25519 &&
804+ & sk[ ..] == ski. as_ref ( ) => Ok ( ( ) ) ,
805+ _ => Err ( input_err ( "Unexpected node key config" ) )
806+ } )
807+ } )
808+ }
809+
810+ assert ! ( secret_input( None ) . is_ok( ) ) ;
811+ assert ! ( secret_input( Some ( "x" . to_string( ) ) ) . is_ok( ) ) ;
812+ }
813+
814+ #[ test]
815+ fn test_node_key_config_file ( ) {
816+ fn secret_file ( net_config_dir : Option < String > ) -> error:: Result < ( ) > {
817+ NodeKeyType :: variants ( ) . into_iter ( ) . try_for_each ( |t| {
818+ let node_key_type = NodeKeyType :: from_str ( t) . unwrap ( ) ;
819+ let tmp = TempDir :: new ( "alice" ) ?;
820+ let file = tmp. path ( ) . join ( format ! ( "{}_mysecret" , t) ) . to_path_buf ( ) ;
821+ let params = NodeKeyParams {
822+ node_key_type,
823+ node_key : None ,
824+ node_key_file : Some ( file. clone ( ) )
825+ } ;
826+ node_key_config ( params, & net_config_dir) . and_then ( |c| match c {
827+ NodeKeyConfig :: Secp256k1 ( network:: Secret :: File ( ref f) )
828+ if node_key_type == NodeKeyType :: Secp256k1 && f == & file => Ok ( ( ) ) ,
829+ NodeKeyConfig :: Ed25519 ( network:: Secret :: File ( ref f) )
830+ if node_key_type == NodeKeyType :: Ed25519 && f == & file => Ok ( ( ) ) ,
831+ _ => Err ( input_err ( "Unexpected node key config" ) )
832+ } )
833+ } )
834+ }
835+
836+ assert ! ( secret_file( None ) . is_ok( ) ) ;
837+ assert ! ( secret_file( Some ( "x" . to_string( ) ) ) . is_ok( ) ) ;
838+ }
839+
840+ #[ test]
841+ fn test_node_key_config_default ( ) {
842+ fn with_def_params < F > ( f : F ) -> error:: Result < ( ) >
843+ where
844+ F : Fn ( NodeKeyParams ) -> error:: Result < ( ) >
845+ {
846+ NodeKeyType :: variants ( ) . into_iter ( ) . try_for_each ( |t| {
847+ let node_key_type = NodeKeyType :: from_str ( t) . unwrap ( ) ;
848+ f ( NodeKeyParams {
849+ node_key_type,
850+ node_key : None ,
851+ node_key_file : None
852+ } )
853+ } )
854+ }
855+
856+ fn no_config_dir ( ) -> error:: Result < ( ) > {
857+ with_def_params ( |params| {
858+ let typ = params. node_key_type ;
859+ node_key_config :: < String > ( params, & None )
860+ . and_then ( |c| match c {
861+ NodeKeyConfig :: Secp256k1 ( network:: Secret :: New )
862+ if typ == NodeKeyType :: Secp256k1 => Ok ( ( ) ) ,
863+ NodeKeyConfig :: Ed25519 ( network:: Secret :: New )
864+ if typ == NodeKeyType :: Ed25519 => Ok ( ( ) ) ,
865+ _ => Err ( input_err ( "Unexpected node key config" ) )
866+ } )
867+ } )
868+ }
869+
870+ fn some_config_dir ( net_config_dir : String ) -> error:: Result < ( ) > {
871+ with_def_params ( |params| {
872+ let dir = PathBuf :: from ( net_config_dir. clone ( ) ) ;
873+ let typ = params. node_key_type ;
874+ node_key_config ( params, & Some ( net_config_dir. clone ( ) ) )
875+ . and_then ( move |c| match c {
876+ NodeKeyConfig :: Secp256k1 ( network:: Secret :: File ( ref f) )
877+ if typ == NodeKeyType :: Secp256k1 &&
878+ f == & dir. join ( NODE_KEY_SECP256K1_FILE ) => Ok ( ( ) ) ,
879+ NodeKeyConfig :: Ed25519 ( network:: Secret :: File ( ref f) )
880+ if typ == NodeKeyType :: Ed25519 &&
881+ f == & dir. join ( NODE_KEY_ED25519_FILE ) => Ok ( ( ) ) ,
882+ _ => Err ( input_err ( "Unexpected node key config" ) )
883+ } )
884+ } )
885+ }
886+
887+ assert ! ( no_config_dir( ) . is_ok( ) ) ;
888+ assert ! ( some_config_dir( "x" . to_string( ) ) . is_ok( ) ) ;
889+ }
725890}
0 commit comments