mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-20 19:11:08 +03:00
Miner querying wallet receiver for coinbase output
With the coinbase receiver daemon in place, when starting a Grin server in mining mode, the miner will now ask the receiver for a coinbase output. The output is then used to insert in a block when successfully mined.
This commit is contained in:
parent
f45cfe97f2
commit
791d2355ee
8 changed files with 114 additions and 29 deletions
|
@ -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<Block, secp::Error> {
|
||||
// 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 {
|
||||
|
|
|
@ -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<Mutex<chain::Tip>>,
|
||||
chain_store: Arc<chain::ChainStore>,
|
||||
/// 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<Mutex<chain::Tip>>,
|
||||
pub fn new(config: MinerConfig,
|
||||
chain_head: Arc<Mutex<chain::Tip>>,
|
||||
chain_store: Arc<chain::ChainStore>,
|
||||
chain_adapter: Arc<ChainToNetAdapter>)
|
||||
-> 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,
|
||||
}
|
||||
|
|
|
@ -57,10 +57,10 @@ impl Server {
|
|||
/// Instantiates and starts a new server.
|
||||
pub fn start(config: ServerConfig) -> Result<Server, Error> {
|
||||
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 || {
|
||||
|
|
|
@ -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<chain::Error> for Error {
|
||||
|
@ -47,6 +50,12 @@ impl From<store::Error> for Error {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<api::Error> 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ use std::num;
|
|||
pub fn to_hex(bytes: Vec<u8>) -> 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<Vec<u8>, 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::<Result<Vec<u8>, _>>()
|
||||
}
|
||||
|
|
|
@ -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<CbData> {
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue