diff --git a/Cargo.toml b/Cargo.toml index ad5625825..8a9fc7881 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ build = "src/build/build.rs" [workspace] members = ["api", "chain", "config", "core", "keychain", "p2p", "servers", "store", "util", "pool", "wallet"] +exclude = ["etc/gen_gen"] [[bin]] name = "grin" diff --git a/etc/gen_gen/Cargo.toml b/etc/gen_gen/Cargo.toml new file mode 100644 index 000000000..b90811c3f --- /dev/null +++ b/etc/gen_gen/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "grin_gen_gen" +version = "0.0.1" +edition = "2018" +authors = ["Grin Developers "] +description = "Utility to automate the generation of Grin's genesis block" +license = "Apache-2.0" +repository = "https://github.com/mimblewimble/grin" +keywords = [ "crypto", "grin", "mimblewimble" ] +readme = "README.md" + +[[bin]] +name = "gen_gen" +path = "src/bin/gen_gen.rs" + +[dependencies] +chrono = "0.4.4" +curl = "0.4.19" +serde_json = "1" +grin_core = "0.4.2" +grin_chain = "0.4.2" +grin_keychain = "0.4.2" +grin_store = "0.4.2" +grin_util = "0.4.2" diff --git a/etc/gen_gen/README.md b/etc/gen_gen/README.md new file mode 100644 index 000000000..d97b67579 --- /dev/null +++ b/etc/gen_gen/README.md @@ -0,0 +1,18 @@ +# Genesis Genesis + +This crate isn't strictly part of grin but allows the generation and release of a new Grin Genesis in an automated fashion. The process is the following: + +* Prepare a multisig output and kernel to use as coinbase. In the case of Grin mainnet, this is done and owned by the council treasurers. This can be down a few days prior. +* Grab the latest bitcoin block hash from publicly available APIs. +* Build a genesis block with the prepared coinbase and the bitcoin block hash as `prev_root`. The timestamp of the block is set to 30 min ahead to leave enough time to run a build and start a node. +* Mine the block so we have at least a valid Cuckatoo Cycle. We don't require a given difficulty for that solution. +* Finalize the block with the proof-of-work, setting a high-enough difficulty (to be agreed upon separately). +* Commit the block information to github. +* Tag version 1.0.0 (scary). + +N.B. This was written while listening to Genesis. Unfortunately, I'm not rich enough to do it while driving a Genesis. And that'd be dangerous. + +# Usage + +1. Build this crate. +2. From its root run `./target/release/gen-gen --coinbase --difficulty --tag ` diff --git a/etc/gen_gen/src/bin/gen_gen.rs b/etc/gen_gen/src/bin/gen_gen.rs new file mode 100644 index 000000000..a2ee67ab3 --- /dev/null +++ b/etc/gen_gen/src/bin/gen_gen.rs @@ -0,0 +1,114 @@ +// Copyright 2018 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 genesis generation utility. + +use std::fs; +use std::sync::Arc; + +use chrono::Duration; +use chrono::prelude::Utc; +use curl; +use serde_json; + +use grin_chain as chain; +use grin_core as core; +use grin_store as store; +use grin_util as util; + +use grin_keychain::{ExtKeychain, Keychain}; + +static BCHAIN_INFO_URL: &str = "https://blockchain.info/latestblock"; +static BCYPHER_URL: &str = "https://api.blockcypher.com/v1/btc/main"; +static BCHAIR_URL: &str = "https://api.blockchair.com/bitcoin/blocks?limit=2"; + +fn main() { + // get the latest bitcoin hash + let h1 = get_bchain_head(); + let h2 = get_bcypher_head(); + let h3 = get_bchair_head(); + if h1 != h2 || h1 != h3 { + panic!("Bitcoin chain head is inconsistent, please retry."); + } + println!("Using bitcoin block hash {}", h1); + + // build the basic parts of the genesis block header, perhaps some of this + // can be moved to core + let mut gen = core::genesis::genesis_main(); + gen.header.timestamp = Utc::now() + Duration::minutes(30); + gen.header.prev_root = core::core::hash::Hash::from_hex(&h1).unwrap(); + gen.header.pow.proof.edge_bits = 29; + println!("Built genesis:\n{:?}", gen); + + // TODO get the proper keychain and/or raw coinbase + let keychain = ExtKeychain::from_random_seed().unwrap(); + let key_id = ExtKeychain::derive_key_id(0, 1, 0, 0, 0); + let reward = core::libtx::reward::output(&keychain, &key_id, 0, 0).unwrap(); + gen = gen.with_reward(reward.0, reward.1); + + { + // setup a tmp chain to set block header roots + let tmp_chain = setup_chain(".grin.tmp", core::pow::mine_genesis_block().unwrap()); + tmp_chain.set_txhashset_roots(&mut gen).unwrap(); + gen.header.output_mmr_size = 1; + gen.header.kernel_mmr_size = 1; + } + + // TODO mine a valid Cuckatoo29 solution + // TODO check again the bitcoin block to make sure it's not been orphaned + // TODO Commit genesis block info in git and tag +} + +fn setup_chain(dir_name: &str, genesis: core::core::Block) -> chain::Chain { + util::init_test_logger(); + let _ = fs::remove_dir_all(dir_name); + let verifier_cache = Arc::new(util::RwLock::new(core::core::verifier_cache::LruVerifierCache::new())); + let db_env = Arc::new(store::new_env(dir_name.to_string())); + chain::Chain::init( + dir_name.to_string(), + db_env, + Arc::new(chain::types::NoopAdapter {}), + genesis, + core::pow::verify_size, + verifier_cache, + false, + ).unwrap() +} + +fn get_bchain_head() -> String { + get_json(BCHAIN_INFO_URL)["hash"].as_str().unwrap().to_string() +} + +fn get_bcypher_head() -> String { + get_json(BCYPHER_URL)["hash"].as_str().unwrap().to_string() +} + +fn get_bchair_head() -> String { + get_json(BCHAIR_URL)["data"][0]["hash"].as_str().unwrap().to_string() +} + +fn get_json(url: &str) -> serde_json::Value { + let mut body = Vec::new(); + let mut easy = curl::easy::Easy::new(); + easy.url(url).unwrap(); + { + let mut transfer = easy.transfer(); + transfer.write_function(|data| { + body.extend_from_slice(data); + Ok(data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + serde_json::from_slice(&body).unwrap() +}