diff --git a/core/src/core/block.rs b/core/src/core/block.rs index 01bd5f2b..b301898a 100644 --- a/core/src/core/block.rs +++ b/core/src/core/block.rs @@ -228,6 +228,7 @@ impl Default for Block { } impl Block { + /// Builds a new block from the header of the previous block, a vector of /// transactions and the private key that will receive the reward. Checks /// that all transactions are valid and calculates the Merkle tree. @@ -239,12 +240,24 @@ impl Block { let secp = Secp256k1::with_caps(secp::ContextFlag::Commit); let (reward_out, reward_proof) = try!(Block::reward_output(reward_key, &secp)); + Block::with_reward(prev, txs, reward_out, reward_proof) + } + + /// Builds a new block ready to mine from the header of the previous block, + /// a vector of transactions and the reward information. Checks + /// that all transactions are valid and calculates the Merkle tree. + pub fn with_reward(prev: &BlockHeader, + txs: Vec<&mut Transaction>, + reward_out: Output, + reward_kern: TxKernel) + -> Result { // note: the following reads easily but may not be the most efficient due to // repeated iterations, revisit if a problem + let secp = Secp256k1::with_caps(secp::ContextFlag::Commit); // validate each transaction and gather their kernels let mut kernels = try_map_vec!(txs, |tx| tx.verify_sig(&secp)); - kernels.push(reward_proof); + kernels.push(reward_kern); // build vectors with all inputs and all outputs, ordering them by hash // needs to be a fold so we don't end up with a vector of vectors and we @@ -282,7 +295,8 @@ impl Block { kernels: kernels, } .compact()) - } + } + /// Blockhash, computed using only the header pub fn hash(&self) -> Hash { diff --git a/grin/src/miner.rs b/grin/src/miner.rs index ddda34a6..a23e30f4 100644 --- a/grin/src/miner.rs +++ b/grin/src/miner.rs @@ -20,14 +20,19 @@ use std::sync::{Arc, Mutex}; use time; use adapters::ChainToNetAdapter; +use api; use core::consensus; use core::core; use core::core::hash::{Hash, Hashed}; use core::pow::cuckoo; +use core::ser; use chain; use secp; +use types::{MinerConfig, Error}; +use util; pub struct Miner { + config: MinerConfig, chain_head: Arc>, chain_store: Arc, /// chain adapter to net @@ -37,11 +42,13 @@ pub struct Miner { impl Miner { /// Creates a new Miner. Needs references to the chain state and its /// storage. - pub fn new(chain_head: Arc>, + pub fn new(config: MinerConfig, + chain_head: Arc>, chain_store: Arc, chain_adapter: Arc) -> Miner { Miner { + config: config, chain_head: chain_head, chain_store: chain_store, chain_adapter: chain_adapter, @@ -52,6 +59,7 @@ impl Miner { /// chain anytime required and looking for PoW solution. pub fn run_loop(&self) { info!("Starting miner loop."); + let mut coinbase = self.get_coinbase(); loop { // get the latest chain state and build a block on top of it let head: core::BlockHeader; @@ -60,7 +68,7 @@ impl Miner { head = self.chain_store.head_header().unwrap(); latest_hash = self.chain_head.lock().unwrap().last_block_h; } - let mut b = self.build_block(&head); + let mut b = self.build_block(&head, coinbase.clone()); // look for a pow for at most 2 sec on the same block (to give a chance to new // transactions) and as long as the head hasn't changed @@ -101,6 +109,7 @@ impl Miner { } else if let Ok(Some(tip)) = res { let chain_head = self.chain_head.clone(); let mut head = chain_head.lock().unwrap(); + coinbase = self.get_coinbase(); *head = tip; } } else { @@ -112,7 +121,7 @@ impl Miner { /// Builds a new block with the chain head as previous and eligible /// transactions from the pool. - fn build_block(&self, head: &core::BlockHeader) -> core::Block { + fn build_block(&self, head: &core::BlockHeader, coinbase: (core::Output, core::TxKernel)) -> core::Block { let mut now_sec = time::get_time().sec; let head_sec = head.timestamp.to_timespec().sec; if now_sec == head_sec { @@ -121,17 +130,45 @@ impl Miner { let (difficulty, cuckoo_len) = consensus::next_target(now_sec, head_sec, head.difficulty.clone(), head.cuckoo_len); - let mut rng = rand::OsRng::new().unwrap(); - let secp_inst = secp::Secp256k1::with_caps(secp::ContextFlag::Commit); - // TODO get a new key from the user's wallet or something - let skey = secp::key::SecretKey::new(&secp_inst, &mut rng); - // TODO populate inputs and outputs from pool transactions - let mut b = core::Block::new(head, vec![], skey).unwrap(); + let (output, kernel) = coinbase; + let mut b = core::Block::with_reward(head, vec![], output, kernel).unwrap(); + + let mut rng = rand::OsRng::new().unwrap(); b.header.nonce = rng.gen(); b.header.cuckoo_len = cuckoo_len; b.header.difficulty = difficulty; b.header.timestamp = time::at(time::Timespec::new(now_sec, 0)); b } + + fn get_coinbase(&self) -> (core::Output, core::TxKernel) { + if self.config.burn_reward { + let mut rng = rand::OsRng::new().unwrap(); + let secp_inst = secp::Secp256k1::with_caps(secp::ContextFlag::Commit); + let skey = secp::key::SecretKey::new(&secp_inst, &mut rng); + core::Block::reward_output(skey, &secp_inst).unwrap() + } else { + let url = format!("{}/v1/receive_coinbase", self.config.wallet_receiver_url.as_str()); + let res: CbData = api::client::post(url.as_str(), &CbAmount { amount: consensus::REWARD }) + .expect("Wallet receiver unreachable, could not claim reward. Is it running?"); + let out_bin = util::from_hex(res.output).unwrap(); + let kern_bin = util::from_hex(res.kernel).unwrap(); + let output = ser::deserialize(&mut &out_bin[..]).unwrap(); + let kernel = ser::deserialize(&mut &kern_bin[..]).unwrap(); + + (output, kernel) + } + } +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct CbAmount { + amount: u64, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct CbData { + output: String, + kernel: String, } diff --git a/grin/src/server.rs b/grin/src/server.rs index a854b150..59d28afb 100644 --- a/grin/src/server.rs +++ b/grin/src/server.rs @@ -57,10 +57,10 @@ impl Server { /// Instantiates and starts a new server. pub fn start(config: ServerConfig) -> Result { let mut evtlp = reactor::Core::new().unwrap(); - let enable_mining = config.enable_mining; + let mining_config = config.mining_config.clone(); let serv = Server::future(config, &evtlp.handle())?; - if enable_mining { - serv.start_miner(); + if mining_config.enable_mining { + serv.start_miner(mining_config); } let forever = Timer::default() @@ -133,8 +133,9 @@ impl Server { /// Start mining for blocks on a separate thread. Relies on a toy miner, /// mostly for testing. - pub fn start_miner(&self) { - let miner = miner::Miner::new(self.chain_head.clone(), + pub fn start_miner(&self, config: MinerConfig) { + let miner = miner::Miner::new(config, + self.chain_head.clone(), self.chain_store.clone(), self.chain_adapter.clone()); thread::spawn(move || { diff --git a/grin/src/types.rs b/grin/src/types.rs index f5309a53..5fd65652 100644 --- a/grin/src/types.rs +++ b/grin/src/types.rs @@ -14,6 +14,7 @@ use std::convert::From; +use api; use chain; use p2p; use store; @@ -27,6 +28,8 @@ pub enum Error { Chain(chain::Error), /// Error originating from the peer-to-peer network. P2P(p2p::Error), + /// Error originating from HTTP API calls + API(api::Error), } impl From for Error { @@ -47,6 +50,12 @@ impl From for Error { } } +impl From for Error { + fn from(e: api::Error) -> Error { + Error::API(e) + } +} + /// Type of seeding the server will use to find other peers on the network. #[derive(Debug, Clone, Serialize, Deserialize)] pub enum Seeding { @@ -81,8 +90,22 @@ pub struct ServerConfig { /// Configuration for the peer-to-peer server pub p2p_config: p2p::P2PConfig, - /// Whethere to start the miner with the server + /// Configuration for the mining daemon + pub mining_config: MinerConfig, +} + +/// Mining configuration +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MinerConfig { + /// Whether to start the miner with the server pub enable_mining: bool, + + /// Base address to the HTTP wallet receiver + pub wallet_receiver_url: String, + + /// Attributes the reward to a random private key instead of contacting the + /// wallet receiver. Mostly used for tests. + pub burn_reward: bool, } impl Default for ServerConfig { @@ -94,7 +117,17 @@ impl Default for ServerConfig { capabilities: p2p::FULL_NODE, seeding_type: Seeding::None, p2p_config: p2p::P2PConfig::default(), - enable_mining: false, + mining_config: MinerConfig::default(), + } + } +} + +impl Default for MinerConfig { + fn default() -> MinerConfig { + MinerConfig { + enable_mining: false, + wallet_receiver_url: "http://localhost:13416".to_string(), + burn_reward: false, } } } diff --git a/src/bin/grin.rs b/src/bin/grin.rs index 5d308d19..f84002a6 100644 --- a/src/bin/grin.rs +++ b/src/bin/grin.rs @@ -141,7 +141,7 @@ fn server_command(server_args: &ArgMatches) { server_config.p2p_config.port = port.parse().unwrap(); } if server_args.is_present("mine") { - server_config.enable_mining = true; + server_config.mining_config.enable_mining = true; } if let Some(seeds) = server_args.values_of("seed") { server_config.seeding_type = grin::Seeding::List(seeds.map(|s| s.to_string()).collect()); @@ -218,7 +218,6 @@ fn default_config() -> grin::ServerConfig { grin::ServerConfig { cuckoo_size: 12, seeding_type: grin::Seeding::WebStatic, - enable_mining: false, ..Default::default() } } diff --git a/util/src/hex.rs b/util/src/hex.rs index 1cf92842..56d21547 100644 --- a/util/src/hex.rs +++ b/util/src/hex.rs @@ -24,7 +24,7 @@ use std::num; pub fn to_hex(bytes: Vec) -> String { let mut s = String::new(); for byte in bytes { - write!(&mut s, "{:X}", byte).expect("Unable to write"); + write!(&mut s, "{:02X}", byte).expect("Unable to write"); } s } @@ -36,7 +36,7 @@ pub fn from_hex(hex_str: String) -> Result, num::ParseIntError> { } else { hex_str.clone() }; - split_n(&hex_str.trim()[..], 2).iter() + split_n(&hex_trim.trim()[..], 2).iter() .map(|b| u8::from_str_radix(b, 16)) .collect::, _>>() } diff --git a/wallet/src/receiver.rs b/wallet/src/receiver.rs index 08881c67..eb745650 100644 --- a/wallet/src/receiver.rs +++ b/wallet/src/receiver.rs @@ -55,7 +55,7 @@ use secp::key::SecretKey; use core::core::{Block, Transaction, TxKernel, Output, build}; use core::ser; -use api::*; +use api::{self, ApiEndpoint, Operation, ApiResult}; use extkey::{self, ExtendedKey}; use types::*; use util; @@ -93,22 +93,22 @@ impl ApiEndpoint for WalletReceiver { fn operation(&self, op: String, input: CbAmount) -> ApiResult { debug!("Operation {} with amount {}", op, input.amount); if input.amount == 0 { - return Err(ApiError::Argument(format!("Zero amount not allowed."))) + return Err(api::Error::Argument(format!("Zero amount not allowed."))) } match op.as_str() { "receive_coinbase" => { let (out, kern) = receive_coinbase(&self.key, input.amount). - map_err(|e| ApiError::Internal(format!("Error building coinbase: {:?}", e)))?; + map_err(|e| api::Error::Internal(format!("Error building coinbase: {:?}", e)))?; let out_bin = ser::ser_vec(&out). - map_err(|e| ApiError::Internal(format!("Error serializing output: {:?}", e)))?; + map_err(|e| api::Error::Internal(format!("Error serializing output: {:?}", e)))?; let kern_bin = ser::ser_vec(&kern). - map_err(|e| ApiError::Internal(format!("Error serializing kernel: {:?}", e)))?; + map_err(|e| api::Error::Internal(format!("Error serializing kernel: {:?}", e)))?; Ok(CbData { output: util::to_hex(out_bin), kernel: util::to_hex(kern_bin), }) }, - _ => Err(ApiError::Argument(format!("Unknown operation: {}", op))), + _ => Err(api::Error::Argument(format!("Unknown operation: {}", op))), } } } @@ -131,5 +131,7 @@ fn receive_coinbase(ext_key: &ExtendedKey, amount: u64) -> Result<(Output, TxKer }); wallet_data.write()?; + info!("Using child {} for a new coinbase output.", coinbase_key.n_child); + Block::reward_output(ext_key.key, &secp).map_err(&From::from) } diff --git a/wallet/src/types.rs b/wallet/src/types.rs index 8d5f0157..4981a7b8 100644 --- a/wallet/src/types.rs +++ b/wallet/src/types.rs @@ -152,7 +152,6 @@ impl WalletData { pub fn next_child(&self, fingerprint: [u8; 4]) -> u32 { let mut max_n = 0; for out in &self.outputs { - println!("{:?} {:?}", out.fingerprint, fingerprint); if max_n < out.n_child && out.fingerprint == fingerprint { max_n = out.n_child; }