2017-04-06 09:41:49 +03:00
// Copyright 2016 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Main for building the binary of a Grin peer-to-peer node.
2017-04-28 08:07:25 +03:00
extern crate clap ;
extern crate daemonize ;
2017-04-25 04:55:01 +03:00
#[ macro_use ]
extern crate log ;
extern crate env_logger ;
extern crate serde ;
extern crate serde_json ;
2017-05-25 02:08:39 +03:00
extern crate tiny_keccak ;
2017-04-25 04:55:01 +03:00
2017-05-25 02:08:39 +03:00
extern crate grin_api as api ;
2017-04-25 04:55:01 +03:00
extern crate grin_grin as grin ;
2017-05-25 02:08:39 +03:00
extern crate grin_wallet as wallet ;
extern crate secp256k1zkp as secp ;
2017-04-25 04:55:01 +03:00
const GRIN_HOME : & 'static str = " .grin " ;
use std ::env ;
use std ::thread ;
use std ::io ::Read ;
use std ::fs ::File ;
use std ::time ::Duration ;
2017-05-25 02:08:39 +03:00
use tiny_keccak ::Keccak ;
2017-04-25 04:55:01 +03:00
2017-04-28 08:07:25 +03:00
use clap ::{ Arg , App , SubCommand , ArgMatches } ;
use daemonize ::Daemonize ;
2017-05-25 02:08:39 +03:00
use secp ::Secp256k1 ;
2017-04-06 09:41:49 +03:00
fn main ( ) {
2017-04-25 04:55:01 +03:00
env_logger ::init ( ) . unwrap ( ) ;
2017-04-28 08:07:25 +03:00
let args = App ::new ( " Grin " )
. version ( " 0.1 " )
. author ( " The Grin Team " )
. about ( " Lightweight implementation of the MimbleWimble protocol. " )
2017-04-25 04:55:01 +03:00
2017-04-28 08:07:25 +03:00
// specification of all the server commands and options
. subcommand ( SubCommand ::with_name ( " server " )
. about ( " Control the Grin server " )
. arg ( Arg ::with_name ( " port " )
. short ( " p " )
. long ( " port " )
. help ( " Port to start the server on " )
. takes_value ( true ) )
. arg ( Arg ::with_name ( " seed " )
. short ( " s " )
. long ( " seed " )
. help ( " Override seed node(s) to connect to " )
. takes_value ( true )
. multiple ( true ) )
. arg ( Arg ::with_name ( " mine " )
. short ( " m " )
. long ( " mine " )
. help ( " Starts the debugging mining loop " ) )
. arg ( Arg ::with_name ( " config " )
. short ( " c " )
. long ( " config " )
. value_name ( " FILE.json " )
. help ( " Sets a custom json configuration file " )
. takes_value ( true ) )
. subcommand ( SubCommand ::with_name ( " start " )
. about ( " Start the Grin server as a daemon " ) )
. subcommand ( SubCommand ::with_name ( " stop " )
. about ( " Stop the Grin server daemon " ) )
. subcommand ( SubCommand ::with_name ( " run " )
. about ( " Run the Grin server in this console " ) ) )
// specification of all the client commands and options
. subcommand ( SubCommand ::with_name ( " client " )
. about ( " Communicates with the Grin server " )
. subcommand ( SubCommand ::with_name ( " status " )
. about ( " current status of the Grin chain " ) ) )
2017-05-25 02:08:39 +03:00
// specification of the wallet commands and options
. subcommand ( SubCommand ::with_name ( " wallet " )
. about ( " Wallet software for Grin " )
. arg ( Arg ::with_name ( " pass " )
. short ( " p " )
. long ( " pass " )
. help ( " Wallet passphrase used to generate the private key seed " )
. takes_value ( true ) )
. subcommand ( SubCommand ::with_name ( " receive " )
2017-05-29 06:21:29 +03:00
. about ( " Run the wallet in receiving mode " ) )
. subcommand ( SubCommand ::with_name ( " send " )
. about ( " Builds a transaction to send someone some coins. By default, the transaction will just be printed to stdout. If a destination is provided, the command will attempt to contact the receiver at that address and send the transaction directly. " )
. arg ( Arg ::with_name ( " amount " )
. help ( " Amount to send in the smallest denomination " )
. index ( 1 ) )
. arg ( Arg ::with_name ( " dest " )
. help ( " Send the transaction to the provided server " )
. short ( " d " )
. long ( " dest " )
. takes_value ( true ) ) ) )
2017-04-28 08:07:25 +03:00
. get_matches ( ) ;
match args . subcommand ( ) {
// server commands and options
( " server " , Some ( server_args ) ) = > {
server_command ( server_args ) ;
2017-05-29 06:21:29 +03:00
}
2017-04-28 08:07:25 +03:00
// client commands and options
( " client " , Some ( client_args ) ) = > {
match client_args . subcommand ( ) {
( " status " , _ ) = > {
println! ( " status info... " ) ;
} ,
_ = > panic! ( " Unknown client command, use 'grin help client' for details " ) ,
}
}
2017-05-25 02:08:39 +03:00
// client commands and options
( " wallet " , Some ( wallet_args ) ) = > {
2017-05-29 06:21:29 +03:00
wallet_command ( wallet_args ) ;
2017-05-25 02:08:39 +03:00
}
_ = > println! ( " Unknown command, use 'grin help' for a list of all commands " ) ,
2017-04-28 08:07:25 +03:00
}
}
/// Handles the server part of the command line, mostly running, starting and
/// stopping the Grin blockchain server. Processes all the command line arguments
/// to build a proper configuration and runs Grin with that configuration.
fn server_command ( server_args : & ArgMatches ) {
info! ( " Starting the Grin server... " ) ;
// configuration wrangling
let mut server_config = read_config ( ) ;
if let Some ( port ) = server_args . value_of ( " port " ) {
server_config . p2p_config . port = port . parse ( ) . unwrap ( ) ;
}
if server_args . is_present ( " mine " ) {
2017-05-26 03:22:21 +03:00
server_config . mining_config . enable_mining = true ;
2017-04-28 08:07:25 +03:00
}
if let Some ( seeds ) = server_args . values_of ( " seed " ) {
server_config . seeding_type = grin ::Seeding ::List ( seeds . map ( | s | s . to_string ( ) ) . collect ( ) ) ;
}
// start the server in the different run modes (interactive or daemon)
match server_args . subcommand ( ) {
( " run " , _ ) = > {
grin ::Server ::start ( server_config ) . unwrap ( ) ;
loop {
thread ::sleep ( Duration ::from_secs ( 60 ) ) ;
}
} ,
( " start " , _ ) = > {
let daemonize = Daemonize ::new ( )
. pid_file ( " /tmp/grin.pid " )
. chown_pid_file ( true )
. privileged_action ( move | | {
grin ::Server ::start ( server_config . clone ( ) ) . unwrap ( ) ;
loop {
thread ::sleep ( Duration ::from_secs ( 60 ) ) ;
}
} ) ;
match daemonize . start ( ) {
Ok ( _ ) = > info! ( " Grin server succesfully started. " ) ,
Err ( e ) = > error! ( " Error starting: {} " , e ) ,
}
}
( " stop " , _ ) = > {
println! ( " TODO, just 'kill $pid' for now. " )
}
_ = > panic! ( " Unknown server command, use 'grin help server' for details " ) ,
}
2017-04-25 04:55:01 +03:00
}
2017-05-25 02:08:39 +03:00
fn wallet_command ( wallet_args : & ArgMatches ) {
let hd_seed = wallet_args . value_of ( " pass " ) . expect ( " Wallet passphrase required. " ) ;
// TODO do something closer to BIP39, eazy solution right now
let mut sha3 = Keccak ::new_sha3_256 ( ) ;
sha3 . update ( hd_seed . as_bytes ( ) ) ;
let mut seed = [ 0 ; 32 ] ;
sha3 . finalize ( & mut seed ) ;
let s = Secp256k1 ::new ( ) ;
let key = wallet ::ExtendedKey ::from_seed ( & s , & seed [ .. ] ) . expect ( " Error deriving extended key from seed. " ) ;
match wallet_args . subcommand ( ) {
( " receive " , _ ) = > {
info! ( " Starting the Grin wallet receiving daemon... " ) ;
let mut apis = api ::ApiServer ::new ( " /v1 " . to_string ( ) ) ;
apis . register_endpoint ( " /receive_coinbase " . to_string ( ) , wallet ::WalletReceiver { key : key } ) ;
apis . start ( " 127.0.0.1:13416 " ) . unwrap_or_else ( | e | {
error! ( " Failed to start Grin wallet receiver: {}. " , e ) ;
} ) ;
}
2017-05-29 06:21:29 +03:00
( " send " , Some ( send_args ) ) = > {
let amount = send_args . value_of ( " amount " ) . expect ( " Amount to send required " ) . parse ( ) . expect ( " Could not parse amount as a whole number. " ) ;
let mut dest = " stdout " ;
if let Some ( d ) = send_args . value_of ( " dest " ) {
dest = d ;
}
wallet ::issue_send_tx ( & key , amount , dest . to_string ( ) ) . unwrap ( ) ;
}
2017-05-25 02:08:39 +03:00
_ = > panic! ( " Unknown wallet command, use 'grin help wallet' for details " ) ,
}
}
2017-04-25 04:55:01 +03:00
fn read_config ( ) -> grin ::ServerConfig {
let mut config_path = env ::home_dir ( ) . ok_or ( " Failed to detect home directory! " ) . unwrap ( ) ;
config_path . push ( GRIN_HOME ) ;
if ! config_path . exists ( ) {
return default_config ( ) ;
}
let mut config_file = File ::open ( config_path ) . unwrap ( ) ;
let mut config_content = String ::new ( ) ;
config_file . read_to_string ( & mut config_content ) . unwrap ( ) ;
serde_json ::from_str ( config_content . as_str ( ) ) . unwrap ( )
}
fn default_config ( ) -> grin ::ServerConfig {
grin ::ServerConfig {
cuckoo_size : 12 ,
seeding_type : grin ::Seeding ::WebStatic ,
.. Default ::default ( )
}
2017-04-06 09:41:49 +03:00
}