Merge pull request #2196 from mimblewimble/floonet

* Get last bitcon block hash, setup genesis header without PoW (for now)
* More a few properties to mainnet genesis. Don't get too excited, several are placeholders.
* Mine a valid Cuckaroo solution for genesis block
* Use miner as library to get a solution for genesis. Replace final values in genesis.rs before committing it.
* Complete genesis replacement
* Fixed various replacements to obtain a compilable, well-formed genesis
* Check plugin errors, uncomment PoW validation
* Fixes to nonce handling in genesis mining
* Also produce full block hashes
* Fix genesis hash test
* Switch commitments (#2157)
* [Floonet] Use switch commits for all blinding factors (#2178)
* move wallet mods back into dirs
* use switched keys for blinding factor in all cases
* re-implement flag to turn off switch commit derivation
* rename tx log entry field tx_hex -> stored_tx (#2181)
* [Floonet] add feature for height locked kernels (#2168)
* add feature for height locked kernels
* add function to compute kernel features appropriate for lock height, and use it
* only sign kernel-features relevant fields; refactor Features
* simplify invalid kernel logic
* remove unused height arg to reward::output and run some rustfmt
* replace nested if/else by match
* Floonet chain type and genesis, testnets cleanup (#2182)
* [Floonet] Encrypt private slate data upon storage in DB (#2189)
* xor encrypt stored nonce and blind sum in transaction data
* stop doc tests splatting wallet files throughout
* Remove bzip2 dependency
* Changed magic number and seeds for Floonet (#2188)
* Genesis generator now loads a local wallet seed to build coinbase.
* Floonet genesis block
* Add floonet to generated grin-server.toml comments
* Test with final Floonet genesis hashes
* Fix get_header_for_output for genesis (#2192)
* start search with min height 0 (#2195)
This commit is contained in:
Ignotus Peverell 2018-12-20 18:12:08 -08:00 committed by GitHub
commit ecf736d78a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 978 additions and 453 deletions

28
Cargo.lock generated
View file

@ -192,24 +192,6 @@ dependencies = [
"iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bzip2"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bzip2-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bzip2-sys"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cc"
version = "1.0.25"
@ -865,7 +847,7 @@ dependencies = [
[[package]]
name = "grin_secp256k1zkp"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
source = "git+https://github.com/mimblewimble/rust-secp256k1-zkp?tag=grin_integration_29#a245051ce72524696a787e60ff7a7e2a9551c699"
dependencies = [
"arrayvec 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)",
"gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)",
@ -935,7 +917,7 @@ dependencies = [
"backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"grin_secp256k1zkp 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"grin_secp256k1zkp 0.7.2 (git+https://github.com/mimblewimble/rust-secp256k1-zkp?tag=grin_integration_29)",
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log4rs 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2894,8 +2876,6 @@ name = "zip"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"flate2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"msdos_time 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2927,8 +2907,6 @@ dependencies = [
"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40"
"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d"
"checksum bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "40ade3d27603c2cb345eb0912aec461a6dec7e06a4ae48589904e808335c7afa"
"checksum bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b"
"checksum bzip2-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6584aa36f5ad4c9247f5323b0a42f37802b37a836f0ad87084d7a33961abe25f"
"checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16"
"checksum cexpr 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42aac45e9567d97474a834efdee3081b3c942b2205be932092f53354ce503d6c"
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
@ -2984,7 +2962,7 @@ dependencies = [
"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d"
"checksum git2 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "591f8be1674b421644b6c030969520bc3fa12114d2eb467471982ed3e9584e71"
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
"checksum grin_secp256k1zkp 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aea95f8b846440f6a9caf0fd4c22c91c124f2a896d69d781f7dc0fa88e33b0ff"
"checksum grin_secp256k1zkp 0.7.2 (git+https://github.com/mimblewimble/rust-secp256k1-zkp?tag=grin_integration_29)" = "<none>"
"checksum h2 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "1ac030ae20dee464c5d0f36544d8b914a6bc606da44a57e052d2b0f5dae129e0"
"checksum hmac 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "733e1b3ac906631ca01ebb577e9bb0f5e37a454032b9036b5eaea4013ed6f99a"
"checksum http 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "02096a6d2c55e63f7fcb800690e4f889a25f6ec342e3adb4594e293b625215ab"

View file

@ -13,6 +13,7 @@ edition = "2018"
[workspace]
members = ["api", "chain", "config", "core", "keychain", "p2p", "servers", "store", "util", "pool", "wallet"]
exclude = ["etc/gen_gen"]
[[bin]]
name = "grin"

View file

@ -44,8 +44,8 @@ pub fn get_output(
// For now we can just try both (but this probably needs to be part of the api
// params)
let outputs = [
OutputIdentifier::new(OutputFeatures::DEFAULT_OUTPUT, &commit),
OutputIdentifier::new(OutputFeatures::COINBASE_OUTPUT, &commit),
OutputIdentifier::new(OutputFeatures::PLAIN, &commit),
OutputIdentifier::new(OutputFeatures::COINBASE, &commit),
];
for x in outputs.iter() {

View file

@ -251,10 +251,7 @@ impl OutputPrintable {
block_header: Option<&core::BlockHeader>,
include_proof: bool,
) -> OutputPrintable {
let output_type = if output
.features
.contains(core::transaction::OutputFeatures::COINBASE_OUTPUT)
{
let output_type = if output.is_coinbase() {
OutputType::Coinbase
} else {
OutputType::Transaction
@ -278,11 +275,7 @@ impl OutputPrintable {
// We require the rewind() to be stable even after the PMMR is pruned and
// compacted so we can still recreate the necessary proof.
let mut merkle_proof = None;
if output
.features
.contains(core::transaction::OutputFeatures::COINBASE_OUTPUT)
&& !spent && block_header.is_some()
{
if output.is_coinbase() && !spent && block_header.is_some() {
merkle_proof = chain.get_merkle_proof(&out_id, &block_header.unwrap()).ok()
};

View file

@ -1130,7 +1130,7 @@ impl Chain {
let (_, pos) = txhashset.is_unspent(output_ref)?;
let mut min = 1;
let mut min = 0;
let mut max = {
let head = self.head()?;
head.height
@ -1139,6 +1139,9 @@ impl Chain {
loop {
let search_height = max - (max - min) / 2;
let h = txhashset.get_header_by_height(search_height)?;
if search_height == 0 {
return Ok(h);
}
let h_prev = txhashset.get_header_by_height(search_height - 1)?;
if pos > h.output_mmr_size {
min = search_height;

View file

@ -16,7 +16,7 @@
use crate::core::core::hash::Hash;
use crate::core::core::pmmr::{self, ReadonlyPMMR};
use crate::core::core::{Block, BlockHeader, Input, Output, OutputFeatures, Transaction};
use crate::core::core::{Block, BlockHeader, Input, Output, Transaction};
use crate::core::global;
use crate::core::ser::PMMRIndexHashable;
use crate::error::{Error, ErrorKind};
@ -105,7 +105,7 @@ impl<'a> UTXOView<'a> {
// outputs we are attempting to spend.
let pos = inputs
.iter()
.filter(|x| x.features.contains(OutputFeatures::COINBASE_OUTPUT))
.filter(|x| x.is_coinbase())
.filter_map(|x| self.batch.get_output_pos(&x.commitment()).ok())
.max()
.unwrap_or(0);

View file

@ -83,7 +83,7 @@ fn data_files() {
let prev = chain.head_header().unwrap();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter());
let pk = ExtKeychainPath::new(1, n as u32, 0, 0, 0).to_identifier();
let reward = libtx::reward::output(&keychain, &pk, 0, prev.height).unwrap();
let reward = libtx::reward::output(&keychain, &pk, 0).unwrap();
let mut b =
core::core::Block::new(&prev, vec![], next_header_info.clone().difficulty, reward)
.unwrap();
@ -159,7 +159,7 @@ fn _prepare_block_nosum(
let key_id = ExtKeychainPath::new(1, diff as u32, 0, 0, 0).to_identifier();
let fees = txs.iter().map(|tx| tx.fee()).sum();
let reward = libtx::reward::output(kc, &key_id, fees, prev.height).unwrap();
let reward = libtx::reward::output(kc, &key_id, fees).unwrap();
let mut b = match core::core::Block::new(
prev,
txs.into_iter().cloned().collect(),

View file

@ -70,7 +70,7 @@ fn mine_genesis_reward_chain() {
let mut genesis = genesis::genesis_dev();
let keychain = keychain::ExtKeychain::from_random_seed().unwrap();
let key_id = keychain::ExtKeychain::derive_key_id(0, 1, 0, 0, 0);
let reward = reward::output(&keychain, &key_id, 0, 0).unwrap();
let reward = reward::output(&keychain, &key_id, 0).unwrap();
genesis = genesis.with_reward(reward.0, reward.1);
{
@ -103,7 +103,7 @@ where
let prev = chain.head_header().unwrap();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter());
let pk = ExtKeychainPath::new(1, n as u32, 0, 0, 0).to_identifier();
let reward = libtx::reward::output(keychain, &pk, 0, prev.height).unwrap();
let reward = libtx::reward::output(keychain, &pk, 0).unwrap();
let mut b =
core::core::Block::new(&prev, vec![], next_header_info.clone().difficulty, reward)
.unwrap();
@ -283,7 +283,7 @@ fn spend_in_fork_and_compact() {
// so we can spend the coinbase later
let b = prepare_block(&kc, &fork_head, &chain, 2);
let out_id = OutputIdentifier::from_output(&b.outputs()[0]);
assert!(out_id.features.contains(OutputFeatures::COINBASE_OUTPUT));
assert!(out_id.features.is_coinbase());
fork_head = b.header.clone();
chain
.process_block(b.clone(), chain::Options::SKIP_POW)
@ -411,7 +411,7 @@ fn output_header_mappings() {
let prev = chain.head_header().unwrap();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter());
let pk = ExtKeychainPath::new(1, n as u32, 0, 0, 0).to_identifier();
let reward = libtx::reward::output(&keychain, &pk, 0, prev.height).unwrap();
let reward = libtx::reward::output(&keychain, &pk, 0).unwrap();
reward_outputs.push(reward.0.clone());
let mut b =
core::core::Block::new(&prev, vec![], next_header_info.clone().difficulty, reward)
@ -511,7 +511,7 @@ where
let key_id = ExtKeychainPath::new(1, diff as u32, 0, 0, 0).to_identifier();
let fees = txs.iter().map(|tx| tx.fee()).sum();
let reward = libtx::reward::output(kc, &key_id, fees, prev.height).unwrap();
let reward = libtx::reward::output(kc, &key_id, fees).unwrap();
let mut b = match core::core::Block::new(
prev,
txs.into_iter().cloned().collect(),

View file

@ -62,7 +62,7 @@ fn test_various_store_indices() {
setup_chain(&genesis, chain_store.clone()).unwrap();
let reward = libtx::reward::output(&keychain, &key_id, 0, 1).unwrap();
let reward = libtx::reward::output(&keychain, &key_id, 0).unwrap();
let block = Block::new(&genesis.header, vec![], Difficulty::min(), reward).unwrap();
let block_hash = block.hash();

View file

@ -14,7 +14,6 @@
use self::chain::types::NoopAdapter;
use self::chain::ErrorKind;
use self::core::core::transaction;
use self::core::core::verifier_cache::LruVerifierCache;
use self::core::global::{self, ChainTypes};
use self::core::libtx::{self, build};
@ -68,7 +67,7 @@ fn test_coinbase_maturity() {
let key_id4 = ExtKeychainPath::new(1, 4, 0, 0, 0).to_identifier();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter());
let reward = libtx::reward::output(&keychain, &key_id1, 0, prev.height).unwrap();
let reward = libtx::reward::output(&keychain, &key_id1, 0).unwrap();
let mut block = core::core::Block::new(&prev, vec![], Difficulty::min(), reward).unwrap();
block.header.timestamp = prev.timestamp + Duration::seconds(60);
block.header.pow.secondary_scaling = next_header_info.secondary_scaling;
@ -85,9 +84,7 @@ fn test_coinbase_maturity() {
assert_eq!(block.outputs().len(), 1);
let coinbase_output = block.outputs()[0];
assert!(coinbase_output
.features
.contains(transaction::OutputFeatures::COINBASE_OUTPUT));
assert!(coinbase_output.is_coinbase());
chain
.process_block(block.clone(), chain::Options::MINE)
@ -114,7 +111,7 @@ fn test_coinbase_maturity() {
let txs = vec![coinbase_txn.clone()];
let fees = txs.iter().map(|tx| tx.fee()).sum();
let reward = libtx::reward::output(&keychain, &key_id3, fees, prev.height).unwrap();
let reward = libtx::reward::output(&keychain, &key_id3, fees).unwrap();
let mut block = core::core::Block::new(&prev, txs, Difficulty::min(), reward).unwrap();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter());
block.header.timestamp = prev.timestamp + Duration::seconds(60);
@ -148,7 +145,7 @@ fn test_coinbase_maturity() {
let keychain = ExtKeychain::from_random_seed().unwrap();
let pk = ExtKeychainPath::new(1, 1, 0, 0, 0).to_identifier();
let reward = libtx::reward::output(&keychain, &pk, 0, prev.height).unwrap();
let reward = libtx::reward::output(&keychain, &pk, 0).unwrap();
let mut block = core::core::Block::new(&prev, vec![], Difficulty::min(), reward).unwrap();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter());
block.header.timestamp = prev.timestamp + Duration::seconds(60);
@ -176,7 +173,7 @@ fn test_coinbase_maturity() {
let txs = vec![coinbase_txn];
let fees = txs.iter().map(|tx| tx.fee()).sum();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter());
let reward = libtx::reward::output(&keychain, &key_id4, fees, prev.height).unwrap();
let reward = libtx::reward::output(&keychain, &key_id4, fees).unwrap();
let mut block = core::core::Block::new(&prev, txs, Difficulty::min(), reward).unwrap();
block.header.timestamp = prev.timestamp + Duration::seconds(60);

View file

@ -73,10 +73,7 @@ fn comments() -> HashMap<String, String> {
#parameters used for mining as well as wallet output coinbase maturity. Can be:
#AutomatedTesting - For CI builds and instant blockchain creation
#UserTesting - For regular user testing (cuckoo 16)
#Testnet1 - Testnet1 genesis block (cuckoo 16)
#Testnet2 - Testnet2 genesis block (cuckoo 30)
#Testnet3 - Testnet3 genesis block (cuckoo 30)
#Testnet4 - Testnet4 genesis block (cuckatoo 29+)
#Floonet - For the long term Floonet test network
"
.to_string(),
);
@ -403,6 +400,13 @@ fn comments() -> HashMap<String, String> {
"dark_background_color_scheme".to_string(),
"
#Whether to use the black background color scheme for command line
"
.to_string(),
);
retval.insert(
"use_switch_commitments".to_string(),
"
#Whether to use switch commitments for this wallet
"
.to_string(),
);

View file

@ -62,32 +62,13 @@ pub const COINBASE_MATURITY: u64 = DAY_HEIGHT;
/// function of block height (time). Starts at 90% losing a percent
/// approximately every week. Represented as an integer between 0 and 100.
pub fn secondary_pow_ratio(height: u64) -> u64 {
if global::is_testnet() {
if height < T4_CUCKAROO_HARDFORK {
// Maintaining pre hardfork testnet4 behavior
90u64.saturating_sub(height / WEEK_HEIGHT)
} else {
90u64.saturating_sub(height / (2 * YEAR_HEIGHT / 90))
}
} else {
// Mainnet (or testing mainnet code).
90u64.saturating_sub(height / (2 * YEAR_HEIGHT / 90))
}
90u64.saturating_sub(height / (2 * YEAR_HEIGHT / 90))
}
/// The AR scale damping factor to use. Dependent on block height
/// to account for pre HF behavior on testnet4.
fn ar_scale_damp_factor(height: u64) -> u64 {
if global::is_testnet() {
if height < T4_CUCKAROO_HARDFORK {
DIFFICULTY_DAMP_FACTOR
} else {
AR_SCALE_DAMP_FACTOR
}
} else {
// Mainnet (or testing mainnet code).
AR_SCALE_DAMP_FACTOR
}
fn ar_scale_damp_factor(_height: u64) -> u64 {
AR_SCALE_DAMP_FACTOR
}
/// Cuckoo-cycle proof size (cycle length)
@ -99,10 +80,6 @@ pub const DEFAULT_MIN_EDGE_BITS: u8 = 31;
/// Cuckaroo proof-of-work edge_bits, meant to be ASIC resistant.
pub const SECOND_POW_EDGE_BITS: u8 = 29;
/// Block height at which testnet 4 hard forks to use Cuckaroo instead of
/// Cuckatoo for ASIC-resistant PoW
pub const T4_CUCKAROO_HARDFORK: u64 = 64_000;
/// Original reference edge_bits to compute difficulty factors for higher
/// Cuckoo graph sizes, changing this would hard fork
pub const BASE_EDGE_BITS: u8 = 24;
@ -332,15 +309,10 @@ where
/// Count the number of "secondary" (AR) blocks in the provided window of blocks.
/// Note: we skip the first one, but testnet4 was incorrectly including it before
/// the hardfork.
fn ar_count(height: u64, diff_data: &[HeaderInfo]) -> u64 {
let mut to_skip = 1;
if global::is_testnet() && height < T4_CUCKAROO_HARDFORK {
// Maintain behavior of testnet4 pre-HF.
to_skip = 0;
}
fn ar_count(_height: u64, diff_data: &[HeaderInfo]) -> u64 {
100 * diff_data
.iter()
.skip(to_skip)
.skip(1)
.filter(|n| n.is_secondary)
.count() as u64
}

View file

@ -28,7 +28,7 @@ use crate::core::compact_block::{CompactBlock, CompactBlockBody};
use crate::core::hash::{Hash, Hashed, ZERO_HASH};
use crate::core::verifier_cache::VerifierCache;
use crate::core::{
transaction, Commitment, Input, KernelFeatures, Output, OutputFeatures, Transaction,
transaction, Commitment, Input, Output, Transaction,
TransactionBody, TxKernel,
};
use crate::global;
@ -535,8 +535,8 @@ impl Block {
/// Consumes this block and returns a new block with the coinbase output
/// and kernels added
pub fn with_reward(mut self, reward_out: Output, reward_kern: TxKernel) -> Block {
self.body.outputs.push(reward_out);
self.body.kernels.push(reward_kern);
self.body.outputs = vec![reward_out];
self.body.kernels = vec![reward_kern];
self
}
@ -663,14 +663,14 @@ impl Block {
.body
.outputs
.iter()
.filter(|out| out.features.contains(OutputFeatures::COINBASE_OUTPUT))
.filter(|out| out.is_coinbase())
.collect::<Vec<&Output>>();
let cb_kerns = self
.body
.kernels
.iter()
.filter(|kernel| kernel.features.contains(KernelFeatures::COINBASE_KERNEL))
.filter(|kernel| kernel.is_coinbase())
.collect::<Vec<&TxKernel>>();
{

View file

@ -19,7 +19,7 @@ use rand::{thread_rng, Rng};
use crate::core::block::{Block, BlockHeader, Error};
use crate::core::hash::Hashed;
use crate::core::id::ShortIdentifiable;
use crate::core::{KernelFeatures, Output, OutputFeatures, ShortId, TxKernel};
use crate::core::{Output, ShortId, TxKernel};
use crate::ser::{self, read_multi, Readable, Reader, VerifySortedAndUnique, Writeable, Writer};
/// Container for full (full) outputs and kernels and kern_ids for a compact block.
@ -168,7 +168,7 @@ impl From<Block> for CompactBlock {
let out_full = block
.outputs()
.iter()
.filter(|x| x.features.contains(OutputFeatures::COINBASE_OUTPUT))
.filter(|x| x.is_coinbase())
.cloned()
.collect::<Vec<_>>();
@ -176,7 +176,7 @@ impl From<Block> for CompactBlock {
let mut kern_ids = vec![];
for k in block.kernels() {
if k.features.contains(KernelFeatures::COINBASE_KERNEL) {
if k.is_coinbase() {
kern_full.push(k.clone());
} else {
kern_ids.push(k.short_id(&header.hash(), nonce));

View file

@ -40,10 +40,12 @@ bitflags! {
/// Options for a kernel's structure or use
#[derive(Serialize, Deserialize)]
pub struct KernelFeatures: u8 {
/// No flags
const DEFAULT_KERNEL = 0b00000000;
/// Kernel matching a coinbase output
const COINBASE_KERNEL = 0b00000001;
/// plain kernel has fee, but no lock_height
const PLAIN = 0;
/// coinbase kernel has neither fee nor lock_height (both zero)
const COINBASE = 1;
/// absolute height locked kernel; has fee and lock_height
const HEIGHT_LOCKED = 2;
}
}
@ -202,7 +204,39 @@ impl PMMRable for TxKernel {
}
}
impl KernelFeatures {
/// Is this a coinbase kernel?
pub fn is_coinbase(&self) -> bool {
self.contains(KernelFeatures::COINBASE)
}
/// Is this a plain kernel?
pub fn is_plain(&self) -> bool {
!self.is_coinbase() && !self.is_height_locked()
}
/// Is this a height locked kernel?
pub fn is_height_locked(&self) -> bool {
self.contains(KernelFeatures::HEIGHT_LOCKED)
}
}
impl TxKernel {
/// Is this a coinbase kernel?
pub fn is_coinbase(&self) -> bool {
self.features.is_coinbase()
}
/// Is this a plain kernel?
pub fn is_plain(&self) -> bool {
self.features.is_plain()
}
/// Is this a height locked kernel?
pub fn is_height_locked(&self) -> bool {
self.features.is_height_locked()
}
/// Return the excess commitment for this tx_kernel.
pub fn excess(&self) -> Commitment {
self.excess
@ -219,6 +253,10 @@ impl TxKernel {
/// as a public key and checking the signature verifies with the fee as
/// message.
pub fn verify(&self) -> Result<(), Error> {
if self.is_coinbase() && self.fee != 0 || !self.is_height_locked() && self.lock_height != 0
{
return Err(Error::InvalidKernelFeatures);
}
let secp = static_secp_instance();
let secp = secp.lock();
let sig = &self.excess_sig;
@ -242,7 +280,7 @@ impl TxKernel {
/// Build an empty tx kernel with zero values.
pub fn empty() -> TxKernel {
TxKernel {
features: KernelFeatures::DEFAULT_KERNEL,
features: KernelFeatures::PLAIN,
fee: 0,
lock_height: 0,
excess: Commitment::from_vec(vec![0; 33]),
@ -258,6 +296,7 @@ impl TxKernel {
/// Builds a new tx kernel with the provided lock_height.
pub fn with_lock_height(self, lock_height: u64) -> TxKernel {
TxKernel {
features: kernel_features(lock_height),
lock_height,
..self
}
@ -586,25 +625,17 @@ impl TransactionBody {
Ok(())
}
// Verify we have no outputs tagged as COINBASE_OUTPUT.
// Verify we have no outputs tagged as COINBASE.
fn verify_output_features(&self) -> Result<(), Error> {
if self
.outputs
.iter()
.any(|x| x.features.contains(OutputFeatures::COINBASE_OUTPUT))
{
if self.outputs.iter().any(|x| x.is_coinbase()) {
return Err(Error::InvalidOutputFeatures);
}
Ok(())
}
// Verify we have no kernels tagged as COINBASE_KERNEL.
// Verify we have no kernels tagged as COINBASE.
fn verify_kernel_features(&self) -> Result<(), Error> {
if self
.kernels
.iter()
.any(|x| x.features.contains(KernelFeatures::COINBASE_KERNEL))
{
if self.kernels.iter().any(|x| x.is_coinbase()) {
return Err(Error::InvalidKernelFeatures);
}
Ok(())
@ -1081,6 +1112,16 @@ impl Input {
pub fn commitment(&self) -> Commitment {
self.commit
}
/// Is this a coinbase input?
pub fn is_coinbase(&self) -> bool {
self.features.is_coinbase()
}
/// Is this a plain input?
pub fn is_plain(&self) -> bool {
self.features.is_plain()
}
}
bitflags! {
@ -1088,9 +1129,9 @@ bitflags! {
#[derive(Serialize, Deserialize)]
pub struct OutputFeatures: u8 {
/// No flags
const DEFAULT_OUTPUT = 0b00000000;
const PLAIN = 0;
/// Output is a coinbase output, must not be spent until maturity
const COINBASE_OUTPUT = 0b00000001;
const COINBASE = 1;
}
}
@ -1160,12 +1201,34 @@ impl PMMRable for Output {
}
}
impl OutputFeatures {
/// Is this a coinbase output?
pub fn is_coinbase(&self) -> bool {
self.contains(OutputFeatures::COINBASE)
}
/// Is this a plain output?
pub fn is_plain(&self) -> bool {
!self.contains(OutputFeatures::COINBASE)
}
}
impl Output {
/// Commitment for the output
pub fn commitment(&self) -> Commitment {
self.commit
}
/// Is this a coinbase kernel?
pub fn is_coinbase(&self) -> bool {
self.features.is_coinbase()
}
/// Is this a plain kernel?
pub fn is_plain(&self) -> bool {
self.features.is_plain()
}
/// Range proof for the output
pub fn proof(&self) -> RangeProof {
self.proof
@ -1291,25 +1354,43 @@ impl From<Output> for OutputIdentifier {
/// to produce a 32 byte message to sign.
///
/// testnet4: msg = (fee || lock_height)
/// mainnet: msg = hash(fee || lock_height || features)
/// mainnet: msg = hash(features) for coinbase kernels
/// hash(features || fee) for plain kernels
/// hash(features || fee || lock_height) for height locked kernels
///
pub fn kernel_sig_msg(
fee: u64,
lock_height: u64,
features: KernelFeatures,
) -> Result<secp::Message, Error> {
if features.is_coinbase() && fee != 0 || !features.is_height_locked() && lock_height != 0 {
return Err(Error::InvalidKernelFeatures);
}
let msg = if global::is_testnet() {
let mut bytes = [0; 32];
BigEndian::write_u64(&mut bytes[16..24], fee);
BigEndian::write_u64(&mut bytes[24..], lock_height);
secp::Message::from_slice(&bytes)?
} else {
let hash = (fee, lock_height, features).hash();
let hash = match features {
KernelFeatures::COINBASE => (features).hash(),
KernelFeatures::PLAIN => (features, fee).hash(),
_ => (features, fee, lock_height).hash(),
};
secp::Message::from_slice(&hash.as_bytes())?
};
Ok(msg)
}
/// kernel features as determined by lock height
pub fn kernel_features(lock_height: u64) -> KernelFeatures {
if lock_height > 0 {
KernelFeatures::HEIGHT_LOCKED
} else {
KernelFeatures::PLAIN
}
}
#[cfg(test)]
mod test {
use super::*;
@ -1328,7 +1409,7 @@ mod test {
let sig = secp::Signature::from_raw_data(&[0; 64]).unwrap();
let kernel = TxKernel {
features: KernelFeatures::DEFAULT_KERNEL,
features: KernelFeatures::PLAIN,
lock_height: 0,
excess: commit,
excess_sig: sig.clone(),
@ -1338,7 +1419,7 @@ mod test {
let mut vec = vec![];
ser::serialize(&mut vec, &kernel).expect("serialized failed");
let kernel2: TxKernel = ser::deserialize(&mut &vec[..]).unwrap();
assert_eq!(kernel2.features, KernelFeatures::DEFAULT_KERNEL);
assert_eq!(kernel2.features, KernelFeatures::PLAIN);
assert_eq!(kernel2.lock_height, 0);
assert_eq!(kernel2.excess, commit);
assert_eq!(kernel2.excess_sig, sig.clone());
@ -1346,7 +1427,7 @@ mod test {
// now check a kernel with lock_height serialize/deserialize correctly
let kernel = TxKernel {
features: KernelFeatures::DEFAULT_KERNEL,
features: KernelFeatures::HEIGHT_LOCKED,
lock_height: 100,
excess: commit,
excess_sig: sig.clone(),
@ -1356,7 +1437,7 @@ mod test {
let mut vec = vec![];
ser::serialize(&mut vec, &kernel).expect("serialized failed");
let kernel2: TxKernel = ser::deserialize(&mut &vec[..]).unwrap();
assert_eq!(kernel2.features, KernelFeatures::DEFAULT_KERNEL);
assert_eq!(kernel2.features, KernelFeatures::HEIGHT_LOCKED);
assert_eq!(kernel2.lock_height, 100);
assert_eq!(kernel2.excess, commit);
assert_eq!(kernel2.excess_sig, sig.clone());
@ -1383,7 +1464,7 @@ mod test {
let commit = keychain.commit(5, &key_id).unwrap();
let input = Input {
features: OutputFeatures::DEFAULT_OUTPUT,
features: OutputFeatures::PLAIN,
commit: commit,
};
@ -1394,16 +1475,16 @@ mod test {
let nonce = 0;
let short_id = input.short_id(&block_hash, nonce);
assert_eq!(short_id, ShortId::from_hex("df31d96e3cdb").unwrap());
assert_eq!(short_id, ShortId::from_hex("c4b05f2ba649").unwrap());
// now generate the short_id for a *very* similar output (single feature flag
// different) and check it generates a different short_id
let input = Input {
features: OutputFeatures::COINBASE_OUTPUT,
features: OutputFeatures::COINBASE,
commit: commit,
};
let short_id = input.short_id(&block_hash, nonce);
assert_eq!(short_id, ShortId::from_hex("784fc5afd5d9").unwrap());
assert_eq!(short_id, ShortId::from_hex("3f0377c624e9").unwrap());
}
}

View file

@ -14,12 +14,21 @@
//! Definition of the genesis block. Placeholder for now.
// required for genesis replacement
//! #![allow(unused_imports)]
use chrono::prelude::{TimeZone, Utc};
use crate::consensus;
use crate::core;
use crate::global;
use crate::pow::{Difficulty, Proof, ProofOfWork};
use crate::util;
use crate::util::secp::constants::SINGLE_BULLET_PROOF_SIZE;
use crate::util::secp::pedersen::{Commitment, RangeProof};
use crate::util::secp::Signature;
use crate::core::hash::Hash;
use crate::keychain::BlindingFactor;
/// Genesis block definition for development networks. The proof of work size
/// is small enough to mine it on the fly, so it does not contain its own
@ -37,114 +46,196 @@ pub fn genesis_dev() -> core::Block {
})
}
/// First testnet genesis block, still subject to change (especially the date,
/// will hopefully come before Christmas).
pub fn genesis_testnet1() -> core::Block {
core::Block::with_header(core::BlockHeader {
/// Placeholder for floonet genesis block, will definitely change before
/// release
pub fn genesis_floo() -> core::Block {
let gen = core::Block::with_header(core::BlockHeader {
height: 0,
timestamp: Utc.ymd(2017, 11, 16).and_hms(20, 0, 0),
timestamp: Utc.ymd(2018, 12, 20).and_hms(20, 58, 32),
prev_root: Hash::from_hex(
"ae144568a9ec32faf57e9ca5b5f0997d33f30bd3352fd84c953e6526d847c26b",
)
.unwrap(),
output_root: Hash::from_hex(
"47d2570266451203c62cd003c706e3ec37e9cb4292316744abfa68a1b133bc1c",
)
.unwrap(),
range_proof_root: Hash::from_hex(
"53ea93e80fe37e9a0cbb9c1a1ddf467213922481a4435921aacf55ffb3f388fc",
)
.unwrap(),
kernel_root: Hash::from_hex(
"3ff7655b2846a1313dd72e1c516a2fa262638fabc8e0d4c1dddf80773bbd472d",
)
.unwrap(),
total_kernel_offset: BlindingFactor::from_hex(
"0000000000000000000000000000000000000000000000000000000000000000",
)
.unwrap(),
output_mmr_size: 1,
kernel_mmr_size: 1,
pow: ProofOfWork {
total_difficulty: Difficulty::min(),
secondary_scaling: 1,
nonce: 28205,
proof: Proof::new(vec![
0x21e, 0x7a2, 0xeae, 0x144e, 0x1b1c, 0x1fbd, 0x203a, 0x214b, 0x293b, 0x2b74,
0x2bfa, 0x2c26, 0x32bb, 0x346a, 0x34c7, 0x37c5, 0x4164, 0x42cc, 0x4cc3, 0x55af,
0x5a70, 0x5b14, 0x5e1c, 0x5f76, 0x6061, 0x60f9, 0x61d7, 0x6318, 0x63a1, 0x63fb,
0x649b, 0x64e5, 0x65a1, 0x6b69, 0x70f8, 0x71c7, 0x71cd, 0x7492, 0x7b11, 0x7db8,
0x7f29, 0x7ff8,
]),
},
..Default::default()
})
}
/// Second testnet genesis block (cuckoo30).
pub fn genesis_testnet2() -> core::Block {
core::Block::with_header(core::BlockHeader {
height: 0,
// previous: core::hash::Hash([0xff; 32]),
timestamp: Utc.ymd(2018, 3, 26).and_hms(16, 0, 0),
pow: ProofOfWork {
total_difficulty: Difficulty::from_num(global::initial_block_difficulty()),
secondary_scaling: 1,
nonce: 1060,
proof: Proof::new(vec![
0x1940730, 0x333b9d0, 0x4739d6f, 0x4c6cfb1, 0x6e3d6c3, 0x74408a3, 0x7ba2bd2,
0x83e2024, 0x8ca22b5, 0x9d39ab8, 0xb6646dd, 0xc6698b6, 0xc6f78fe, 0xc99b662,
0xcf2ae8c, 0xcf41eed, 0xdd073e6, 0xded6af8, 0xf08d1a5, 0x1156a144, 0x11d1160a,
0x131bb0a5, 0x137ad703, 0x13b0831f, 0x1421683f, 0x147e3c1f, 0x1496fda0, 0x150ba22b,
0x15cc5bc6, 0x16edf697, 0x17ced40c, 0x17d84f9e, 0x18a515c1, 0x19320d9c, 0x19da4f6d,
0x1b50bcb1, 0x1b8bc72f, 0x1c7b6964, 0x1d07b3a9, 0x1d189d4d, 0x1d1f9a15, 0x1dafcd41,
]),
},
..Default::default()
})
}
/// Second testnet genesis block (cuckoo30). Temporary values for now.
pub fn genesis_testnet3() -> core::Block {
core::Block::with_header(core::BlockHeader {
height: 0,
// previous: core::hash::Hash([0xff; 32]),
timestamp: Utc.ymd(2018, 7, 8).and_hms(18, 0, 0),
pow: ProofOfWork {
total_difficulty: Difficulty::from_num(global::initial_block_difficulty()),
secondary_scaling: 1,
nonce: 4956988373127691,
proof: Proof::new(vec![
0xa420dc, 0xc8ffee, 0x10e433e, 0x1de9428, 0x2ed4cea, 0x52d907b, 0x5af0e3f,
0x6b8fcae, 0x8319b53, 0x845ca8c, 0x8d2a13e, 0x8d6e4cc, 0x9349e8d, 0xa7a33c5,
0xaeac3cb, 0xb193e23, 0xb502e19, 0xb5d9804, 0xc9ac184, 0xd4f4de3, 0xd7a23b8,
0xf1d8660, 0xf443756, 0x10b833d2, 0x11418fc5, 0x11b8aeaf, 0x131836ec, 0x132ab818,
0x13a46a55, 0x13df89fe, 0x145d65b5, 0x166f9c3a, 0x166fe0ef, 0x178cb36f, 0x185baf68,
0x1bbfe563, 0x1bd637b4, 0x1cfc8382, 0x1d1ed012, 0x1e391ca5, 0x1e999b4c, 0x1f7c6d21,
]),
},
..Default::default()
})
}
/// 4th testnet genesis block (cuckatoo29 AR, 30+ AF). Temporary values for now (Pow won't verify)
/// NB: Currently set to intenal pre-testnet values
pub fn genesis_testnet4() -> core::Block {
core::Block::with_header(core::BlockHeader {
height: 0,
// previous: core::hash::Hash([0xff; 32]),
timestamp: Utc.ymd(2018, 10, 17).and_hms(20, 0, 0),
pow: ProofOfWork {
total_difficulty: Difficulty::from_num(global::initial_block_difficulty()),
secondary_scaling: global::initial_graph_weight(),
nonce: 8612241555342799290,
total_difficulty: Difficulty::from_num(10_u64.pow(6)),
secondary_scaling: 1856,
nonce: 22,
proof: Proof {
nonces: vec![
0x46f3b4, 0x1135f8c, 0x1a1596f, 0x1e10f71, 0x41c03ea, 0x63fe8e7, 0x65af34f,
0x73c16d3, 0x8216dc3, 0x9bc75d0, 0xae7d9ad, 0xc1cb12b, 0xc65e957, 0xf67a152,
0xfac6559, 0x100c3d71, 0x11eea08b, 0x1225dfbb, 0x124d61a1, 0x132a14b4,
0x13f4ec38, 0x1542d236, 0x155f2df0, 0x1577394e, 0x163c3513, 0x19349845,
0x19d46953, 0x19f65ed4, 0x1a0411b9, 0x1a2fa039, 0x1a72a06c, 0x1b02ddd2,
0x1b594d59, 0x1b7bffd3, 0x1befe12e, 0x1c82e4cd, 0x1d492478, 0x1de132a5,
0x1e578b3c, 0x1ed96855, 0x1f222896, 0x1fea0da6,
48398361, 50434294, 73758991, 93493375, 94716564, 101961133, 153506566,
159476458, 164019912, 208165915, 216747111, 218441011, 221663358, 262514197,
264746362, 278423427, 282069592, 284508695, 297003554, 327321117, 327780367,
329474453, 333639856, 356316379, 366419120, 381872178, 386038638, 389726932,
390055244, 392425788, 399530286, 426997994, 436531599, 456084550, 456375883,
459156409, 474067792, 480904139, 487380747, 489307817, 496780560, 530227836,
],
edge_bits: 29,
},
},
..Default::default()
})
});
let kernel = core::TxKernel {
features: core::KernelFeatures::COINBASE,
fee: 0,
lock_height: 0,
excess: Commitment::from_vec(
util::from_hex(
"0817a9e97a070ba5f9fa185c093b4b13b262ed4b4712b6f7c92881b27168f9a2cb".to_string(),
)
.unwrap(),
),
excess_sig: Signature::from_raw_data(&[
172, 131, 105, 224, 31, 11, 0, 70, 109, 54, 230, 184, 177, 138, 46, 137, 202, 215, 152,
37, 192, 132, 88, 254, 110, 76, 57, 32, 42, 13, 19, 89, 82, 89, 116, 66, 30, 132, 120,
148, 122, 100, 97, 38, 141, 219, 57, 184, 171, 130, 213, 235, 83, 202, 69, 13, 213, 60,
150, 172, 33, 37, 209, 57,
])
.unwrap(),
};
let output = core::Output {
features: core::OutputFeatures::COINBASE,
commit: Commitment::from_vec(
util::from_hex(
"08f5523cbd8b2e1dae3eefdd9dd1069e0c8a7f055a0611da79f42530c5de0d044b".to_string(),
)
.unwrap(),
),
proof: RangeProof {
plen: SINGLE_BULLET_PROOF_SIZE,
proof: [
47, 196, 194, 238, 233, 164, 218, 64, 54, 92, 83, 248, 225, 116, 189, 225, 202, 66,
213, 63, 195, 209, 238, 189, 153, 198, 231, 219, 3, 146, 102, 67, 26, 7, 199, 150,
160, 244, 48, 166, 113, 6, 241, 49, 133, 248, 201, 80, 34, 19, 118, 249, 44, 213,
215, 235, 228, 187, 215, 116, 212, 203, 232, 183, 12, 66, 29, 11, 28, 17, 212, 104,
126, 203, 103, 60, 176, 149, 182, 206, 70, 138, 180, 213, 76, 99, 25, 184, 40, 177,
197, 179, 71, 63, 19, 72, 253, 129, 115, 107, 90, 249, 39, 108, 134, 10, 231, 172,
172, 59, 207, 118, 175, 124, 197, 132, 73, 154, 148, 8, 73, 26, 231, 75, 24, 134,
199, 93, 15, 43, 45, 49, 69, 167, 194, 23, 114, 16, 117, 209, 127, 123, 18, 209,
12, 34, 219, 196, 37, 7, 226, 132, 70, 111, 113, 164, 203, 175, 105, 175, 196, 62,
225, 138, 162, 176, 190, 109, 96, 210, 15, 38, 245, 200, 83, 155, 185, 111, 85,
234, 6, 3, 246, 98, 175, 127, 94, 65, 29, 78, 27, 53, 32, 230, 85, 91, 195, 112,
84, 135, 56, 207, 213, 165, 40, 248, 238, 202, 225, 142, 79, 89, 81, 197, 138, 65,
14, 232, 145, 44, 73, 6, 43, 8, 43, 42, 127, 151, 68, 18, 19, 83, 14, 142, 180, 75,
25, 4, 97, 166, 237, 212, 187, 106, 154, 36, 223, 231, 177, 58, 70, 1, 195, 113,
144, 151, 45, 185, 0, 174, 116, 212, 122, 239, 96, 1, 122, 211, 41, 96, 230, 110,
242, 145, 176, 230, 55, 143, 142, 234, 151, 49, 151, 109, 252, 120, 147, 244, 178,
73, 196, 221, 150, 85, 69, 113, 50, 166, 92, 91, 98, 188, 77, 76, 48, 192, 112,
184, 108, 143, 134, 56, 46, 119, 21, 71, 247, 119, 133, 225, 72, 15, 158, 60, 64,
71, 57, 134, 243, 228, 58, 13, 58, 209, 71, 4, 72, 87, 129, 51, 46, 64, 188, 60,
157, 56, 120, 23, 2, 47, 143, 79, 176, 54, 3, 47, 227, 124, 70, 242, 8, 59, 113,
203, 51, 65, 138, 131, 121, 45, 131, 132, 171, 161, 49, 235, 129, 39, 164, 234, 69,
172, 95, 28, 180, 118, 163, 151, 148, 66, 65, 104, 222, 232, 154, 22, 30, 149, 196,
214, 163, 93, 76, 128, 142, 233, 106, 171, 213, 148, 59, 101, 56, 22, 127, 232, 4,
63, 111, 9, 188, 163, 40, 158, 24, 65, 81, 203, 231, 93, 197, 102, 170, 70, 239,
229, 13, 172, 110, 157, 226, 112, 182, 28, 150, 222, 62, 224, 94, 182, 220, 243,
236, 62, 156, 129, 220, 127, 155, 141, 0, 243, 159, 113, 28, 158, 95, 205, 35, 72,
132, 46, 235, 176, 146, 233, 93, 111, 4, 105, 236, 176, 165, 102, 168, 188, 121,
105, 175, 197, 114, 97, 40, 2, 165, 153, 85, 135, 114, 147, 95, 216, 50, 108, 52,
225, 186, 215, 110, 122, 230, 14, 246, 141, 180, 41, 22, 132, 58, 8, 31, 187, 221,
231, 14, 33, 52, 88, 219, 200, 77, 246, 134, 18, 0, 113, 144, 6, 146, 54, 24, 113,
14, 64, 182, 116, 229, 250, 201, 126, 84, 192, 80, 13, 57, 232, 55, 113, 139, 249,
166, 231, 123, 101, 236, 147, 144, 2, 9, 51, 2, 189, 188, 200, 66, 29, 16, 22, 150,
45, 220, 15, 161, 180, 214, 244, 104, 41, 77, 171, 246, 243, 56, 47, 63, 103, 216,
151, 199, 249, 169, 165, 119, 200, 243, 161, 83, 46, 225, 195, 92, 96, 150, 0, 165,
170, 14, 211, 226, 244, 70, 218, 137, 254, 197, 175, 208, 119, 199, 121, 4, 7, 190,
118, 55, 197, 208, 41, 109, 161, 34, 33, 210, 58, 99, 81, 97, 57, 156, 57, 144, 83,
97, 49, 248, 89, 201, 88, 169, 9, 211, 34, 136, 174, 195, 224, 51, 103, 12, 237,
172, 46, 216, 5, 168,
],
},
};
gen.with_reward(output, kernel)
}
/// Placeholder for mainnet genesis block, will definitely change before
/// release so no use trying to pre-mine it.
pub fn genesis_main() -> core::Block {
core::Block::with_header(core::BlockHeader {
let gen = core::Block::with_header(core::BlockHeader {
height: 0,
// previous: core::hash::Hash([0xff; 32]),
timestamp: Utc.ymd(2018, 8, 14).and_hms(0, 0, 0),
timestamp: Utc.ymd(2019, 1, 15).and_hms(12, 0, 0), // REPLACE
prev_root: Hash::default(), // REPLACE
output_root: Hash::default(), // REPLACE
range_proof_root: Hash::default(), // REPLACE
kernel_root: Hash::default(), // REPLACE
total_kernel_offset: BlindingFactor::zero(), // REPLACE
output_mmr_size: 1,
kernel_mmr_size: 1,
pow: ProofOfWork {
total_difficulty: Difficulty::from_num(global::initial_block_difficulty()),
secondary_scaling: 1,
nonce: global::get_genesis_nonce(),
proof: Proof::zero(consensus::PROOFSIZE),
total_difficulty: Difficulty::from_num(10_u64.pow(8)),
secondary_scaling: 1856,
nonce: 1, // REPLACE
proof: Proof {
nonces: vec![0; 42], // REPLACE
edge_bits: 29,
},
},
..Default::default()
})
});
let kernel = core::TxKernel {
features: core::KernelFeatures::COINBASE,
fee: 0,
lock_height: 0,
excess: Commitment::from_vec(vec![]), // REPLACE
excess_sig: Signature::from_raw_data(&[0; 64]).unwrap(), //REPLACE
};
let output = core::Output {
features: core::OutputFeatures::COINBASE,
commit: Commitment::from_vec(vec![]), // REPLACE
proof: RangeProof {
plen: SINGLE_BULLET_PROOF_SIZE,
proof: [0; SINGLE_BULLET_PROOF_SIZE], // REPLACE
},
};
gen.with_reward(output, kernel)
}
#[cfg(test)]
mod test {
use super::*;
use crate::core::hash::Hashed;
use crate::ser;
#[test]
fn floonet_genesis_hash() {
let gen_hash = genesis_floo().hash();
println!("floonet genesis hash: {}", gen_hash.to_hex());
let gen_bin = ser::ser_vec(&genesis_floo()).unwrap();
println!("floonet genesis full hash: {}\n", gen_bin.hash().to_hex());
assert_eq!(
gen_hash.to_hex(),
"cb272478ee4abbf41a3d8cc8f2f828785cf38bd7f0dcacfdd6db5f8f2d8f6e24"
);
assert_eq!(
gen_bin.hash().to_hex(),
"5fcc7afebc2dcfb98f982dd4d9ff7878fca45038d22677ef6360745c90505035"
);
}
// TODO hardcode the hashes once genesis is set
#[test]
fn mainnet_genesis_hash() {
let gen_hash = genesis_main().hash();
println!("mainnet genesis hash: {}", gen_hash.to_hex());
let gen_bin = ser::ser_vec(&genesis_main()).unwrap();
println!("mainnet genesis full hash: {}\n", gen_bin.hash().to_hex());
//assert_eq!(gene_hash.to_hex, "");
}
}

View file

@ -20,7 +20,7 @@ use crate::consensus::HeaderInfo;
use crate::consensus::{
graph_weight, BASE_EDGE_BITS, BLOCK_TIME_SEC, COINBASE_MATURITY, CUT_THROUGH_HORIZON,
DAY_HEIGHT, DEFAULT_MIN_EDGE_BITS, DIFFICULTY_ADJUST_WINDOW, INITIAL_DIFFICULTY, PROOFSIZE,
SECOND_POW_EDGE_BITS, STATE_SYNC_THRESHOLD, T4_CUCKAROO_HARDFORK, UNIT_DIFFICULTY,
SECOND_POW_EDGE_BITS, STATE_SYNC_THRESHOLD,
};
use crate::pow::{self, new_cuckaroo_ctx, new_cuckatoo_ctx, EdgeType, PoWContext};
/// An enum collecting sets of parameters used throughout the
@ -62,13 +62,6 @@ pub const TESTING_INITIAL_GRAPH_WEIGHT: u32 = 1;
/// Testing initial block difficulty
pub const TESTING_INITIAL_DIFFICULTY: u64 = 1;
/// Testnet 2 initial block difficulty, high to see how it goes
pub const TESTNET2_INITIAL_DIFFICULTY: u64 = 1000;
/// Testnet 3 initial block difficulty, moderately high, taking into account
/// a 30x Cuckoo adjustment factor
pub const TESTNET3_INITIAL_DIFFICULTY: u64 = 30000;
/// If a peer's last updated difficulty is 2 hours ago and its difficulty's lower than ours,
/// we're sure this peer is a stuck node, and we will kick out such kind of stuck peers.
pub const STUCK_PEER_KICK_TIME: i64 = 2 * 3600 * 1000;
@ -79,13 +72,6 @@ const PEER_EXPIRATION_DAYS: i64 = 7 * 2;
/// Constant that expresses defunct peer timeout in seconds to be used in checks.
pub const PEER_EXPIRATION_REMOVE_TIME: i64 = PEER_EXPIRATION_DAYS * 24 * 3600;
/// Testnet 4 initial block difficulty
/// 1_000 times natural scale factor for cuckatoo29
pub const TESTNET4_INITIAL_DIFFICULTY: u64 = 1_000 * UNIT_DIFFICULTY;
/// Cuckatoo edge_bits on T4
pub const TESTNET4_MIN_EDGE_BITS: u8 = 30;
/// Trigger compaction check on average every day for all nodes.
/// Randomized per node - roll the dice on every block to decide.
/// Will compact the txhashset to remove pruned data.
@ -101,21 +87,15 @@ pub enum ChainTypes {
AutomatedTesting,
/// For User testing
UserTesting,
/// First test network
Testnet1,
/// Second test network
Testnet2,
/// Third test network
Testnet3,
/// Fourth test network
Testnet4,
/// Protocol testing network
Floonet,
/// Main production network
Mainnet,
}
impl Default for ChainTypes {
fn default() -> ChainTypes {
ChainTypes::Testnet4
ChainTypes::Floonet
}
}
@ -149,7 +129,7 @@ pub fn set_mining_mode(mode: ChainTypes) {
/// Return either a cuckoo context or a cuckatoo context
/// Single change point
pub fn create_pow_context<T>(
height: u64,
_height: u64,
edge_bits: u8,
proof_size: usize,
max_sols: u32,
@ -163,12 +143,9 @@ where
ChainTypes::Mainnet if edge_bits == 29 => new_cuckaroo_ctx(edge_bits, proof_size),
ChainTypes::Mainnet => new_cuckatoo_ctx(edge_bits, proof_size, max_sols),
// T4 has Cuckatoo for everything up to hard fork, then Cuckaroo29 for AR
// and Cuckatoo30+ for AF PoW
ChainTypes::Testnet4 if edge_bits == 29 && height >= T4_CUCKAROO_HARDFORK => {
new_cuckaroo_ctx(edge_bits, proof_size)
}
ChainTypes::Testnet4 => new_cuckatoo_ctx(edge_bits, proof_size, max_sols),
// Same for Floonet
ChainTypes::Floonet if edge_bits == 29 => new_cuckaroo_ctx(edge_bits, proof_size),
ChainTypes::Floonet => new_cuckatoo_ctx(edge_bits, proof_size, max_sols),
// Everything else is Cuckatoo only
_ => new_cuckatoo_ctx(edge_bits, proof_size, max_sols),
@ -186,8 +163,6 @@ pub fn min_edge_bits() -> u8 {
match *param_ref {
ChainTypes::AutomatedTesting => AUTOMATED_TESTING_MIN_EDGE_BITS,
ChainTypes::UserTesting => USER_TESTING_MIN_EDGE_BITS,
ChainTypes::Testnet1 => USER_TESTING_MIN_EDGE_BITS,
ChainTypes::Testnet4 => TESTNET4_MIN_EDGE_BITS,
_ => DEFAULT_MIN_EDGE_BITS,
}
}
@ -200,7 +175,6 @@ pub fn base_edge_bits() -> u8 {
match *param_ref {
ChainTypes::AutomatedTesting => AUTOMATED_TESTING_MIN_EDGE_BITS,
ChainTypes::UserTesting => USER_TESTING_MIN_EDGE_BITS,
ChainTypes::Testnet1 => USER_TESTING_MIN_EDGE_BITS,
_ => BASE_EDGE_BITS,
}
}
@ -231,10 +205,7 @@ pub fn initial_block_difficulty() -> u64 {
match *param_ref {
ChainTypes::AutomatedTesting => TESTING_INITIAL_DIFFICULTY,
ChainTypes::UserTesting => TESTING_INITIAL_DIFFICULTY,
ChainTypes::Testnet1 => TESTING_INITIAL_DIFFICULTY,
ChainTypes::Testnet2 => TESTNET2_INITIAL_DIFFICULTY,
ChainTypes::Testnet3 => TESTNET3_INITIAL_DIFFICULTY,
ChainTypes::Testnet4 => TESTNET4_INITIAL_DIFFICULTY,
ChainTypes::Floonet => INITIAL_DIFFICULTY,
ChainTypes::Mainnet => INITIAL_DIFFICULTY,
}
}
@ -244,10 +215,7 @@ pub fn initial_graph_weight() -> u32 {
match *param_ref {
ChainTypes::AutomatedTesting => TESTING_INITIAL_GRAPH_WEIGHT,
ChainTypes::UserTesting => TESTING_INITIAL_GRAPH_WEIGHT,
ChainTypes::Testnet1 => TESTING_INITIAL_GRAPH_WEIGHT,
ChainTypes::Testnet2 => TESTING_INITIAL_GRAPH_WEIGHT,
ChainTypes::Testnet3 => TESTING_INITIAL_GRAPH_WEIGHT,
ChainTypes::Testnet4 => graph_weight(0, SECOND_POW_EDGE_BITS) as u32,
ChainTypes::Floonet => graph_weight(0, SECOND_POW_EDGE_BITS) as u32,
ChainTypes::Mainnet => graph_weight(0, SECOND_POW_EDGE_BITS) as u32,
}
}
@ -288,20 +256,14 @@ pub fn is_user_testing_mode() -> bool {
/// Production defined as a live public network, testnet[n] or mainnet.
pub fn is_production_mode() -> bool {
let param_ref = CHAIN_TYPE.read();
ChainTypes::Testnet1 == *param_ref
|| ChainTypes::Testnet2 == *param_ref
|| ChainTypes::Testnet3 == *param_ref
|| ChainTypes::Testnet4 == *param_ref
ChainTypes::Floonet == *param_ref
|| ChainTypes::Mainnet == *param_ref
}
/// Are we in one of our (many) testnets?
pub fn is_testnet() -> bool {
let param_ref = CHAIN_TYPE.read();
ChainTypes::Testnet1 == *param_ref
|| ChainTypes::Testnet2 == *param_ref
|| ChainTypes::Testnet3 == *param_ref
|| ChainTypes::Testnet4 == *param_ref
ChainTypes::Floonet == *param_ref
}
/// Helper function to get a nonce known to create a valid POW on
@ -315,8 +277,10 @@ pub fn get_genesis_nonce() -> u64 {
ChainTypes::AutomatedTesting => 0,
// Magic nonce for current genesis block at cuckatoo15
ChainTypes::UserTesting => 27944,
// Magic nonce for genesis block for testnet2 (cuckatoo29)
_ => panic!("Pre-set"),
// Placeholder, obviously not the right value
ChainTypes::Floonet => 0,
// Placeholder, obviously not the right value
ChainTypes::Mainnet => 0,
}
}

View file

@ -241,34 +241,35 @@ pub fn verify_partial_sig(
/// let commit = keychain.commit(value, &key_id).unwrap();
/// let rproof = proof::create(&keychain, value, &key_id, commit, None).unwrap();
/// let output = Output {
/// features: OutputFeatures::COINBASE_OUTPUT,
/// features: OutputFeatures::COINBASE,
/// commit: commit,
/// proof: rproof,
/// };
/// let height = 20;
/// let over_commit = secp.commit_value(reward(fees)).unwrap();
/// let out_commit = output.commitment();
/// let msg = kernel_sig_msg(0, height, KernelFeatures::DEFAULT_KERNEL).unwrap();
/// let msg = kernel_sig_msg(0, height, KernelFeatures::HEIGHT_LOCKED).unwrap();
/// let excess = secp.commit_sum(vec![out_commit], vec![over_commit]).unwrap();
/// let pubkey = excess.to_pubkey(&secp).unwrap();
/// let sig = aggsig::sign_from_key_id(&secp, &keychain, &msg, &key_id, Some(&pubkey)).unwrap();
/// let sig = aggsig::sign_from_key_id(&secp, &keychain, &msg, value, &key_id, Some(&pubkey)).unwrap();
/// ```
pub fn sign_from_key_id<K>(
secp: &Secp256k1,
k: &K,
msg: &Message,
value: u64,
key_id: &Identifier,
blind_sum: Option<&PublicKey>,
) -> Result<Signature, Error>
where
K: Keychain,
{
let skey = k.derive_key(key_id)?;
let skey = k.derive_key(value, key_id)?;
let sig = aggsig::sign_single(
secp,
&msg,
&skey.secret_key,
&skey,
None,
None,
None,
@ -314,17 +315,17 @@ where
/// let commit = keychain.commit(value, &key_id).unwrap();
/// let rproof = proof::create(&keychain, value, &key_id, commit, None).unwrap();
/// let output = Output {
/// features: OutputFeatures::COINBASE_OUTPUT,
/// features: OutputFeatures::COINBASE,
/// commit: commit,
/// proof: rproof,
/// };
/// let height = 20;
/// let over_commit = secp.commit_value(reward(fees)).unwrap();
/// let out_commit = output.commitment();
/// let msg = kernel_sig_msg(0, height, KernelFeatures::DEFAULT_KERNEL).unwrap();
/// let msg = kernel_sig_msg(0, height, KernelFeatures::HEIGHT_LOCKED).unwrap();
/// let excess = secp.commit_sum(vec![out_commit], vec![over_commit]).unwrap();
/// let pubkey = excess.to_pubkey(&secp).unwrap();
/// let sig = aggsig::sign_from_key_id(&secp, &keychain, &msg, &key_id, Some(&pubkey)).unwrap();
/// let sig = aggsig::sign_from_key_id(&secp, &keychain, &msg, value, &key_id, Some(&pubkey)).unwrap();
///
/// // Verify the signature from the excess commit
/// let sig_verifies =

View file

@ -54,7 +54,7 @@ where
move |build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
let commit = build.keychain.commit(value, &key_id).unwrap();
let input = Input::new(features, commit);
(tx.with_input(input), kern, sum.sub_key_id(key_id.to_path()))
(tx.with_input(input), kern, sum.sub_key_id(key_id.to_value_path(value)))
},
)
}
@ -69,7 +69,7 @@ where
"Building input (spending regular output): {}, {}",
value, key_id
);
build_input(value, OutputFeatures::DEFAULT_OUTPUT, key_id)
build_input(value, OutputFeatures::PLAIN, key_id)
}
/// Adds a coinbase input spending a coinbase output.
@ -78,7 +78,7 @@ where
K: Keychain,
{
debug!("Building input (spending coinbase): {}, {}", value, key_id);
build_input(value, OutputFeatures::COINBASE_OUTPUT, key_id)
build_input(value, OutputFeatures::COINBASE, key_id)
}
/// Adds an output with the provided value and key identifier from the
@ -97,12 +97,12 @@ where
(
tx.with_output(Output {
features: OutputFeatures::DEFAULT_OUTPUT,
features: OutputFeatures::PLAIN,
commit: commit,
proof: rproof,
}),
kern,
sum.add_key_id(key_id.to_path()),
sum.add_key_id(key_id.to_value_path(value)),
)
},
)

View file

@ -26,7 +26,7 @@ where
K: Keychain,
{
// hash(commit|wallet root secret key (m)) as nonce
let root_key = k.derive_key(&K::root_key_id())?.secret_key;
let root_key = k.derive_key(0, &K::root_key_id())?;
let res = blake2::blake2b::blake2b(32, &commit.0, &root_key.0[..]);
let res = res.as_bytes();
let mut ret_val = [0; 32];
@ -53,11 +53,11 @@ where
K: Keychain,
{
let commit = k.commit(amount, key_id)?;
let skey = k.derive_key(key_id)?;
let skey = k.derive_key(amount, key_id)?;
let nonce = create_nonce(k, &commit)?;
let message = ProofMessage::from_bytes(&key_id.serialize_path());
Ok(k.secp()
.bullet_proof(amount, skey.secret_key, nonce, extra_data, Some(message)))
.bullet_proof(amount, skey, nonce, extra_data, Some(message)))
}
/// Verify a proof

View file

@ -27,7 +27,6 @@ pub fn output<K>(
keychain: &K,
key_id: &Identifier,
fees: u64,
height: u64,
) -> Result<(Output, TxKernel), Error>
where
K: Keychain,
@ -40,7 +39,7 @@ where
let rproof = proof::create(keychain, value, key_id, commit, None)?;
let output = Output {
features: OutputFeatures::COINBASE_OUTPUT,
features: OutputFeatures::COINBASE,
commit: commit,
proof: rproof,
};
@ -53,22 +52,18 @@ where
let pubkey = excess.to_pubkey(&secp)?;
// NOTE: Remember we sign the fee *and* the lock_height.
// For a coinbase output the fee is 0 and the lock_height is
// the lock_height of the coinbase output itself,
// not the lock_height of the tx (there is no tx for a coinbase output).
// This output will not be spendable earlier than lock_height (and we sign this
// here).
let msg = kernel_sig_msg(0, height, KernelFeatures::COINBASE_KERNEL)?;
let sig = aggsig::sign_from_key_id(&secp, keychain, &msg, &key_id, Some(&pubkey))?;
// For a coinbase output the fee is 0 and the lock_height is 0
let msg = kernel_sig_msg(0, 0, KernelFeatures::COINBASE)?;
let sig = aggsig::sign_from_key_id(&secp, keychain, &msg, value, &key_id, Some(&pubkey))?;
let proof = TxKernel {
features: KernelFeatures::COINBASE_KERNEL,
features: KernelFeatures::COINBASE,
excess: excess,
excess_sig: sig,
fee: 0,
// lock_height here is the height of the block (tx should be valid immediately)
// *not* the lock_height of the coinbase output (only spendable 1,000 blocks later)
lock_height: height,
// lock_height here is 0
// *not* the maturity of the coinbase output (only spendable 1,440 blocks later)
lock_height: 0,
};
Ok((output, proof))
}

View file

@ -16,10 +16,10 @@
//! around during an interactive wallet exchange
use crate::blake2::blake2b::blake2b;
use crate::core::committed::Committed;
use crate::core::transaction::{kernel_sig_msg, KernelFeatures, Transaction};
use crate::core::verifier_cache::LruVerifierCache;
use crate::core::amount_to_hr_string;
use crate::core::committed::Committed;
use crate::core::transaction::{kernel_features, kernel_sig_msg, Transaction};
use crate::core::verifier_cache::LruVerifierCache;
use crate::keychain::{BlindSum, BlindingFactor, Keychain};
use crate::libtx::error::{Error, ErrorKind};
use crate::libtx::{aggsig, build, tx_fee};
@ -160,8 +160,7 @@ impl Slate {
// Currently includes the fee and the lock_height.
fn msg_to_sign(&self) -> Result<secp::Message, Error> {
// Currently we only support interactively creating a tx with a "default" kernel.
let features = KernelFeatures::DEFAULT_KERNEL;
let features = kernel_features(self.lock_height);
let msg = kernel_sig_msg(self.fee, self.lock_height, features)?;
Ok(msg)
}

View file

@ -69,7 +69,7 @@ pub fn verify_size(bh: &BlockHeader) -> Result<(), Error> {
/// Mines a genesis block using the internal miner
pub fn mine_genesis_block() -> Result<Block, Error> {
let mut gen = genesis::genesis_testnet2();
let mut gen = genesis::genesis_dev();
if global::is_user_testing_mode() || global::is_automated_testing_mode() {
gen = genesis::genesis_dev();
gen.header.timestamp = Utc::now();

View file

@ -131,7 +131,7 @@ fn empty_block_with_coinbase_is_valid() {
let coinbase_outputs = b
.outputs()
.iter()
.filter(|out| out.features.contains(OutputFeatures::COINBASE_OUTPUT))
.filter(|out| out.is_coinbase())
.map(|o| o.clone())
.collect::<Vec<_>>();
assert_eq!(coinbase_outputs.len(), 1);
@ -139,7 +139,7 @@ fn empty_block_with_coinbase_is_valid() {
let coinbase_kernels = b
.kernels()
.iter()
.filter(|out| out.features.contains(KernelFeatures::COINBASE_KERNEL))
.filter(|out| out.is_coinbase())
.map(|o| o.clone())
.collect::<Vec<_>>();
assert_eq!(coinbase_kernels.len(), 1);
@ -152,7 +152,7 @@ fn empty_block_with_coinbase_is_valid() {
}
#[test]
// test that flipping the COINBASE_OUTPUT flag on the output features
// test that flipping the COINBASE flag on the output features
// invalidates the block and specifically it causes verify_coinbase to fail
// additionally verifying the merkle_inputs_outputs also fails
fn remove_coinbase_output_flag() {
@ -161,12 +161,8 @@ fn remove_coinbase_output_flag() {
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let mut b = new_block(vec![], &keychain, &prev, &key_id);
assert!(b.outputs()[0]
.features
.contains(OutputFeatures::COINBASE_OUTPUT));
b.outputs_mut()[0]
.features
.remove(OutputFeatures::COINBASE_OUTPUT);
assert!(b.outputs()[0].is_coinbase());
b.outputs_mut()[0].features = OutputFeatures::PLAIN;
assert_eq!(b.verify_coinbase(), Err(Error::CoinbaseSumMismatch));
assert!(b
@ -179,7 +175,7 @@ fn remove_coinbase_output_flag() {
}
#[test]
// test that flipping the COINBASE_KERNEL flag on the kernel features
// test that flipping the COINBASE flag on the kernel features
// invalidates the block and specifically it causes verify_coinbase to fail
fn remove_coinbase_kernel_flag() {
let keychain = ExtKeychain::from_random_seed().unwrap();
@ -187,12 +183,8 @@ fn remove_coinbase_kernel_flag() {
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let mut b = new_block(vec![], &keychain, &prev, &key_id);
assert!(b.kernels()[0]
.features
.contains(KernelFeatures::COINBASE_KERNEL));
b.kernels_mut()[0]
.features
.remove(KernelFeatures::COINBASE_KERNEL);
assert!(b.kernels()[0].is_coinbase());
b.kernels_mut()[0].features = KernelFeatures::PLAIN;
// Flipping the coinbase flag results in kernels not summing correctly.
assert_eq!(
@ -380,7 +372,7 @@ fn convert_block_to_compact_block() {
cb.kern_ids()[0],
b.kernels()
.iter()
.find(|x| !x.features.contains(KernelFeatures::COINBASE_KERNEL))
.find(|x| !x.is_coinbase())
.unwrap()
.short_id(&cb.hash(), cb.nonce)
);

View file

@ -92,7 +92,7 @@ where
K: Keychain,
{
let fees = txs.iter().map(|tx| tx.fee()).sum();
let reward_output = reward::output(keychain, &key_id, fees, previous_header.height).unwrap();
let reward_output = reward::output(keychain, &key_id, fees).unwrap();
Block::new(
&previous_header,
txs.into_iter().cloned().collect(),

View file

@ -206,18 +206,6 @@ fn add_block(
ret_chain_sim
}
// Adds many defined blocks
fn add_blocks(
intervals: Vec<u64>,
chain_sim: Vec<(HeaderInfo, DiffStats)>,
) -> Vec<(HeaderInfo, DiffStats)> {
let mut return_chain = chain_sim.clone();
for i in intervals {
return_chain = add_block(i, return_chain.clone());
}
return_chain
}
// Adds another n 'blocks' to the iterator, with difficulty calculated
fn add_block_repeated(
interval: u64,
@ -355,25 +343,6 @@ fn adjustment_scenarios() {
print_chain_sim(chain_sim);
println!("*********************************************************");
// Actual testnet 2 timings
let testnet2_intervals = [
2880, 16701, 1882, 3466, 614, 605, 1551, 538, 931, 23, 690, 1397, 2112, 2058, 605, 721,
2148, 1605, 134, 1234, 1569, 482, 1775, 2732, 540, 958, 883, 3475, 518, 1346, 1926, 780,
865, 269, 1079, 141, 105, 781, 289, 256, 709, 68, 165, 1813, 3899, 1458, 955, 2336, 239,
674, 1059, 157, 214, 15, 157, 558, 1945, 1677, 1825, 1307, 1973, 660, 77, 3134, 410, 347,
537, 649, 325, 370, 2271, 106, 19, 329,
];
global::set_mining_mode(global::ChainTypes::Testnet2);
let chain_sim = create_chain_sim(global::initial_block_difficulty());
let chain_sim = add_blocks(testnet2_intervals.to_vec(), chain_sim);
println!("");
println!("*********************************************************");
println!("Scenario 6) Testnet2");
println!("*********************************************************");
print_chain_sim(chain_sim);
println!("*********************************************************");
}
/// Checks different next_target adjustments and difficulty boundaries
@ -526,7 +495,7 @@ fn test_secondary_pow_ratio() {
// Tests for testnet4 chain type (covers pre and post hardfork).
{
global::set_mining_mode(global::ChainTypes::Testnet4);
global::set_mining_mode(global::ChainTypes::Floonet);
assert_eq!(global::is_testnet(), true);
assert_eq!(secondary_pow_ratio(1), 90);
@ -539,16 +508,16 @@ fn test_secondary_pow_ratio() {
let one_week = 60 * 24 * 7;
assert_eq!(secondary_pow_ratio(one_week - 1), 90);
assert_eq!(secondary_pow_ratio(one_week), 89);
assert_eq!(secondary_pow_ratio(one_week + 1), 89);
assert_eq!(secondary_pow_ratio(one_week), 90);
assert_eq!(secondary_pow_ratio(one_week + 1), 90);
let two_weeks = one_week * 2;
assert_eq!(secondary_pow_ratio(two_weeks - 1), 89);
assert_eq!(secondary_pow_ratio(two_weeks), 88);
assert_eq!(secondary_pow_ratio(two_weeks + 1), 88);
assert_eq!(secondary_pow_ratio(two_weeks), 89);
assert_eq!(secondary_pow_ratio(two_weeks + 1), 89);
let t4_fork_height = 64_000;
assert_eq!(secondary_pow_ratio(t4_fork_height - 1), 84);
assert_eq!(secondary_pow_ratio(t4_fork_height - 1), 85);
assert_eq!(secondary_pow_ratio(t4_fork_height), 85);
assert_eq!(secondary_pow_ratio(t4_fork_height + 1), 85);
@ -572,9 +541,9 @@ fn test_secondary_pow_scale() {
let window = DIFFICULTY_ADJUST_WINDOW;
let mut hi = HeaderInfo::from_diff_scaling(Difficulty::from_num(10), 100);
// testnet4 testing
// floonet testing
{
global::set_mining_mode(global::ChainTypes::Testnet4);
global::set_mining_mode(global::ChainTypes::Floonet);
assert_eq!(global::is_testnet(), true);
// all primary, factor should increase so it becomes easier to find a high
@ -582,13 +551,13 @@ fn test_secondary_pow_scale() {
hi.is_secondary = false;
assert_eq!(
secondary_pow_scaling(1, &(0..window).map(|_| hi.clone()).collect::<Vec<_>>()),
147
106
);
// all secondary on 90%, factor should go down a bit
hi.is_secondary = true;
assert_eq!(
secondary_pow_scaling(1, &(0..window).map(|_| hi.clone()).collect::<Vec<_>>()),
94
97
);
// all secondary on 1%, factor should go down to bound (divide by 2)
assert_eq!(
@ -631,7 +600,7 @@ fn test_secondary_pow_scale() {
.chain((0..(window * 95 / 100)).map(|_| hi.clone()))
.collect::<Vec<_>>()
),
94
96
);
// 40% secondary, should come up based on 70 average
assert_eq!(
@ -642,7 +611,7 @@ fn test_secondary_pow_scale() {
.chain((0..(window * 4 / 10)).map(|_| hi.clone()))
.collect::<Vec<_>>()
),
84
72
);
}

View file

@ -75,7 +75,8 @@ fn tx_double_ser_deser() {
#[test]
#[should_panic(expected = "Keychain Error")]
fn test_zero_commit_fails() {
let keychain = ExtKeychain::from_random_seed().unwrap();
let mut keychain = ExtKeychain::from_random_seed().unwrap();
keychain.set_use_switch_commits(false);
let key_id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
// blinding should fail as signing with a zero r*G shouldn't work
@ -121,7 +122,7 @@ fn build_tx_kernel() {
let kern = &tx.kernels()[0];
kern.verify().unwrap();
assert_eq!(kern.features, KernelFeatures::DEFAULT_KERNEL);
assert_eq!(kern.features, KernelFeatures::PLAIN);
assert_eq!(kern.fee, tx.fee());
}

View file

@ -31,7 +31,7 @@ fn test_output_ser_deser() {
let proof = proof::create(&keychain, 5, &key_id, commit, None).unwrap();
let out = Output {
features: OutputFeatures::DEFAULT_OUTPUT,
features: OutputFeatures::PLAIN,
commit: commit,
proof: proof,
};
@ -40,7 +40,7 @@ fn test_output_ser_deser() {
ser::serialize(&mut vec, &out).expect("serialized failed");
let dout: Output = ser::deserialize(&mut &vec[..]).unwrap();
assert_eq!(dout.features, OutputFeatures::DEFAULT_OUTPUT);
assert_eq!(dout.features, OutputFeatures::PLAIN);
assert_eq!(dout.commit, out.commit);
assert_eq!(dout.proof, out.proof);
}

View file

@ -38,7 +38,7 @@ fn test_verifier_cache_rangeproofs() {
let proof = proof::create(&keychain, 5, &key_id, commit, None).unwrap();
let out = Output {
features: OutputFeatures::DEFAULT_OUTPUT,
features: OutputFeatures::PLAIN,
commit: commit,
proof: proof,
};

36
etc/gen_gen/Cargo.toml Normal file
View file

@ -0,0 +1,36 @@
[package]
name = "grin_gen_gen"
version = "0.0.1"
edition = "2018"
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
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"
cuckoo_miner = "0.4.2"
curl = "0.4.19"
grin_core = { path = "../../core" }
grin_chain = { path = "../../chain" }
grin_keychain = { path = "../../keychain" }
grin_miner_plugin = "0.4.2"
grin_store = { path = "../../store" }
grin_util = { path = "../../util" }
grin_wallet = { path = "../../wallet" }
rpassword = "2.0.0"
serde_json = "1"
[patch.crates-io]
grin_api = { path = "../../api" }
grin_core = { path = "../../core" }
grin_chain = { path = "../../chain" }
grin_keychain = { path = "../../keychain" }
grin_util = { path = "../../util" }
grin_wallet = { path = "../../wallet" }

18
etc/gen_gen/README.md Normal file
View file

@ -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 <file> --difficulty <u64> --tag <version>`

View file

@ -0,0 +1,339 @@
// 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::io::{BufRead, Write};
use std::sync::Arc;
use std::{fs, io, path, process};
use chrono::prelude::Utc;
use chrono::{Datelike, Duration, Timelike};
use curl;
use rpassword;
use serde_json;
use cuckoo_miner as cuckoo;
use grin_chain as chain;
use grin_core as core;
use grin_miner_plugin as plugin;
use grin_store as store;
use grin_util as util;
use grin_wallet as wallet;
use grin_core::core::hash::Hashed;
use grin_core::core::verifier_cache::LruVerifierCache;
use grin_keychain::{BlindingFactor, 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";
static GENESIS_RS_PATH: &str = "../../core/src/genesis.rs";
static PLUGIN_PATH: &str = "./cuckaroo_mean_cuda_29.cuckooplugin";
static WALLET_SEED_PATH: &str = "./wallet.seed";
fn main() {
if !path::Path::new(GENESIS_RS_PATH).exists() {
panic!(
"File {} not found, make sure you're running this from the gen_gen directory",
GENESIS_RS_PATH
);
}
if !path::Path::new(PLUGIN_PATH).exists() {
panic!(
"File {} not found, make sure you're running this from the gen_gen directory",
PLUGIN_PATH
);
}
if !path::Path::new(WALLET_SEED_PATH).exists() {
panic!(
"File {} not found, make sure you're running this from the gen_gen directory",
WALLET_SEED_PATH
);
}
// 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 ({}, {}, {}).",
h1, h2, h3
);
}
println!("Using bitcoin block hash {}", h1);
// build the basic parts of the genesis block header
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();
// build the wallet seed and derive a coinbase from local wallet.seed
let seed = wallet::WalletSeed::from_file(
&wallet::WalletConfig::default(),
&rpassword::prompt_password_stdout("Password: ").unwrap()
).unwrap();
let keychain: ExtKeychain = seed.derive_keychain().unwrap();
let key_id = ExtKeychain::derive_key_id(2, 1, 0, 0, 0);
let reward = core::libtx::reward::output(&keychain, &key_id, 0).unwrap();
gen = gen.with_reward(reward.0, reward.1);
{
// setup a tmp chain to set block header roots
core::global::set_mining_mode(core::global::ChainTypes::AutomatedTesting);
let tmp_chain = setup_chain(".grin.tmp", core::pow::mine_genesis_block().unwrap());
tmp_chain.set_txhashset_roots(&mut gen).unwrap();
}
// mine a Cuckaroo29 block
core::global::set_mining_mode(core::global::ChainTypes::Mainnet);
let plugin_lib = cuckoo::PluginLibrary::new(PLUGIN_PATH).unwrap();
let mut params = plugin_lib.get_default_params();
params.mutate_nonce = false;
let solver_ctx = plugin_lib.create_solver_ctx(&mut params);
let mut solver_sols = plugin::SolverSolutions::default();
let mut solver_stats = plugin::SolverStats::default();
let mut nonce = 0;
while solver_sols.num_sols == 0 {
solver_sols = plugin::SolverSolutions::default();
gen.header.pow.nonce = nonce;
let _ = plugin_lib.run_solver(
solver_ctx,
gen.header.pre_pow(),
nonce,
1,
&mut solver_sols,
&mut solver_stats,
);
if solver_stats.has_errored {
println!(
"Plugin {} has errored, device: {}. Reason: {}",
solver_stats.get_plugin_name(),
solver_stats.get_device_name(),
solver_stats.get_error_reason(),
);
return;
}
nonce += 1;
}
// Set the PoW solution and make sure the block is mostly valid
gen.header.pow.proof.nonces = solver_sols.sols[0].to_u64s();
assert!(gen.header.pow.is_secondary(), "Not a secondary header");
println!("Built genesis:\n{:?}", gen);
core::pow::verify_size(&gen.header).unwrap();
gen.validate(
&BlindingFactor::zero(),
Arc::new(util::RwLock::new(LruVerifierCache::new())),
)
.unwrap();
println!("\nFinal genesis cyclehash: {}", gen.hash().to_hex());
let gen_bin = core::ser::ser_vec(&gen).unwrap();
println!("Final genesis full hash: {}\n", gen_bin.hash().to_hex());
update_genesis_rs(&gen);
println!("genesis.rs has been updated, check it and run mainnet_genesis_hash test");
println!("also check bitcoin block {} hasn't been orphaned.", h1);
println!("press c+enter to proceed.");
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
if input != "c\n" {
return;
}
// Commit genesis block info in git and tag
process::Command::new("git")
.args(&["commit", "-am", "Minor: finalized genesis block"])
.status()
.expect("git commit failed");
process::Command::new("git")
.args(&["tag", "-a", "v1.0", "-m", "Mainnet release"])
.status()
.expect("git tag failed");
process::Command::new("git")
.args(&["push", "origin", "v1.0"])
.status()
.expect("git tag push failed");
println!("All done!");
}
fn update_genesis_rs(gen: &core::core::Block) {
// set the replacement patterns
let mut replacements = vec![];
replacements.push((
"timestamp".to_string(),
format!(
"Utc.ymd({}, {}, {}).and_hms({}, {}, {})",
gen.header.timestamp.date().year(),
gen.header.timestamp.date().month(),
gen.header.timestamp.date().day(),
gen.header.timestamp.time().hour(),
gen.header.timestamp.time().minute(),
gen.header.timestamp.time().second(),
),
));
replacements.push((
"prev_root".to_string(),
format!(
"Hash::from_hex(\"{}\").unwrap()",
gen.header.prev_root.to_hex()
),
));
replacements.push((
"output_root".to_string(),
format!(
"Hash::from_hex(\"{}\").unwrap()",
gen.header.output_root.to_hex()
),
));
replacements.push((
"range_proof_root".to_string(),
format!(
"Hash::from_hex(\"{}\").unwrap()",
gen.header.range_proof_root.to_hex()
),
));
replacements.push((
"kernel_root".to_string(),
format!(
"Hash::from_hex(\"{}\").unwrap()",
gen.header.kernel_root.to_hex()
),
));
replacements.push((
"total_kernel_offset".to_string(),
format!(
"BlindingFactor::from_hex(\"{}\").unwrap()",
gen.header.total_kernel_offset.to_hex()
),
));
replacements.push(("nonce".to_string(), format!("{}", gen.header.pow.nonce)));
replacements.push((
"nonces".to_string(),
format!("vec!{:?}", gen.header.pow.proof.nonces),
));
replacements.push((
"excess".to_string(),
format!(
"Commitment::from_vec(util::from_hex({:x?}.to_string()).unwrap())",
util::to_hex(gen.kernels()[0].excess.0.to_vec())
),
));
replacements.push((
"excess_sig".to_string(),
format!(
"Signature::from_raw_data(&{:?}).unwrap()",
gen.kernels()[0].excess_sig.to_raw_data().to_vec(),
),
));
replacements.push((
"commit".to_string(),
format!(
"Commitment::from_vec(util::from_hex({:x?}.to_string()).unwrap())",
util::to_hex(gen.outputs()[0].commitment().0.to_vec())
),
));
replacements.push((
"proof".to_string(),
format!("{:?}", gen.outputs()[0].proof.bytes().to_vec()),
));
// check each possible replacement in the file, remove the replacement from
// the list when found to avoid double replacements
let mut replaced = String::new();
{
let genesis_rs = fs::File::open(GENESIS_RS_PATH).unwrap();
let reader = io::BufReader::new(&genesis_rs);
for rline in reader.lines() {
let line = rline.unwrap();
let mut has_replaced = false;
if line.contains("REPLACE") {
for (pos, replacement) in replacements.iter().enumerate() {
if line.contains(&replacement.0) {
replaced.push_str(&format!("{}: {},\n", replacement.0, replacement.1));
replacements.remove(pos);
has_replaced = true;
break;
}
}
}
if !has_replaced {
replaced.push_str(&format!("{}\n", line));
}
}
}
let mut genesis_rs = fs::File::create(GENESIS_RS_PATH).unwrap();
genesis_rs.write_all(replaced.as_bytes()).unwrap();
}
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,
Arc::new(util::Mutex::new(util::StopState::new())),
)
.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()
}

View file

@ -29,6 +29,7 @@ use crate::util::secp::{self, Message, Secp256k1, Signature};
pub struct ExtKeychain {
secp: Secp256k1,
master: ExtendedPrivKey,
use_switch_commits: bool,
}
impl Keychain for ExtKeychain {
@ -39,6 +40,7 @@ impl Keychain for ExtKeychain {
let keychain = ExtKeychain {
secp: secp,
master: master,
use_switch_commits: true,
};
Ok(keychain)
}
@ -49,6 +51,7 @@ impl Keychain for ExtKeychain {
let keychain = ExtKeychain {
secp: secp,
master: master,
use_switch_commits: true,
};
Ok(keychain)
}
@ -68,19 +71,23 @@ impl Keychain for ExtKeychain {
ExtKeychainPath::new(depth, d1, d2, d3, d4).to_identifier()
}
fn derive_key(&self, id: &Identifier) -> Result<ExtendedPrivKey, Error> {
fn derive_key(&self, amount: u64, id: &Identifier) -> Result<SecretKey, Error> {
let mut h = BIP32GrinHasher::new();
let p = id.to_path();
let mut sk = self.master;
let mut ext_key = self.master;
for i in 0..p.depth {
sk = sk.ckd_priv(&self.secp, &mut h, p.path[i as usize])?;
ext_key = ext_key.ckd_priv(&self.secp, &mut h, p.path[i as usize])?;
}
match self.use_switch_commits {
true => Ok(self.secp.blind_switch(amount, ext_key.secret_key)?),
false => Ok(ext_key.secret_key),
}
Ok(sk)
}
fn commit(&self, amount: u64, id: &Identifier) -> Result<Commitment, Error> {
let key = self.derive_key(id)?;
let commit = self.secp.commit(amount, key.secret_key)?;
let key = self.derive_key(amount, id)?;
let commit = self.secp.commit(amount, key)?;
Ok(commit)
}
@ -89,9 +96,9 @@ impl Keychain for ExtKeychain {
.positive_key_ids
.iter()
.filter_map(|k| {
let res = self.derive_key(&Identifier::from_path(&k));
let res = self.derive_key(k.value, &Identifier::from_path(&k.ext_keychain_path));
if let Ok(s) = res {
Some(s.secret_key)
Some(s)
} else {
None
}
@ -102,9 +109,9 @@ impl Keychain for ExtKeychain {
.negative_key_ids
.iter()
.filter_map(|k| {
let res = self.derive_key(&Identifier::from_path(&k));
let res = self.derive_key(k.value, &Identifier::from_path(&k.ext_keychain_path));
if let Ok(s) = res {
Some(s.secret_key)
Some(s)
} else {
None
}
@ -131,9 +138,9 @@ impl Keychain for ExtKeychain {
Ok(BlindingFactor::from_secret_key(sum))
}
fn sign(&self, msg: &Message, id: &Identifier) -> Result<Signature, Error> {
let skey = self.derive_key(id)?;
let sig = self.secp.sign(msg, &skey.secret_key)?;
fn sign(&self, msg: &Message, amount: u64, id: &Identifier) -> Result<Signature, Error> {
let skey = self.derive_key(amount, id)?;
let sig = self.secp.sign(msg, &skey)?;
Ok(sig)
}
@ -147,6 +154,10 @@ impl Keychain for ExtKeychain {
Ok(sig)
}
fn set_use_switch_commits(&mut self, value: bool) {
self.use_switch_commits = value;
}
fn secp(&self) -> &Secp256k1 {
&self.secp
}
@ -175,7 +186,7 @@ mod test {
let commit = keychain.commit(0, &key_id).unwrap();
// now check we can use our key to verify a signature from this zero commitment
let sig = keychain.sign(&msg, &key_id).unwrap();
let sig = keychain.sign(&msg, 0, &key_id).unwrap();
secp.verify_from_commit(&msg, &sig, &commit).unwrap();
}

View file

@ -23,7 +23,7 @@ use std::ops::Add;
use std::{error, fmt};
use crate::blake2::blake2b::blake2b;
use crate::extkey_bip32::{self, ChildNumber, ExtendedPrivKey};
use crate::extkey_bip32::{self, ChildNumber};
use serde::{de, ser}; //TODO: Convert errors to use ErrorKind
use crate::util;
@ -44,6 +44,7 @@ pub enum Error {
KeyDerivation(extkey_bip32::Error),
Transaction(String),
RangeProof(String),
SwitchCommitment,
}
impl From<secp::Error> for Error {
@ -126,6 +127,13 @@ impl Identifier {
ExtKeychainPath::from_identifier(&self)
}
pub fn to_value_path(&self, value: u64) -> ValueExtKeychainPath {
ValueExtKeychainPath {
value,
ext_keychain_path: self.to_path(),
}
}
/// output the path itself, for insertion into bulletproof
/// recovery processes can grind through possiblities to find the
/// correct length if required
@ -327,8 +335,8 @@ pub struct SplitBlindingFactor {
/// factor as well as the "sign" with which they should be combined.
#[derive(Clone, Debug, PartialEq)]
pub struct BlindSum {
pub positive_key_ids: Vec<ExtKeychainPath>,
pub negative_key_ids: Vec<ExtKeychainPath>,
pub positive_key_ids: Vec<ValueExtKeychainPath>,
pub negative_key_ids: Vec<ValueExtKeychainPath>,
pub positive_blinding_factors: Vec<BlindingFactor>,
pub negative_blinding_factors: Vec<BlindingFactor>,
}
@ -344,12 +352,12 @@ impl BlindSum {
}
}
pub fn add_key_id(mut self, path: ExtKeychainPath) -> BlindSum {
pub fn add_key_id(mut self, path: ValueExtKeychainPath) -> BlindSum {
self.positive_key_ids.push(path);
self
}
pub fn sub_key_id(mut self, path: ExtKeychainPath) -> BlindSum {
pub fn sub_key_id(mut self, path: ValueExtKeychainPath) -> BlindSum {
self.negative_key_ids.push(path);
self
}
@ -430,17 +438,25 @@ impl ExtKeychainPath {
}
}
/// Wrapper for amount + path
#[derive(Copy, Clone, PartialEq, Eq, Debug, Deserialize)]
pub struct ValueExtKeychainPath {
pub value: u64,
pub ext_keychain_path: ExtKeychainPath,
}
pub trait Keychain: Sync + Send + Clone {
fn from_seed(seed: &[u8]) -> Result<Self, Error>;
fn from_mnemonic(word_list: &str, extension_word: &str) -> Result<Self, Error>;
fn from_random_seed() -> Result<Self, Error>;
fn root_key_id() -> Identifier;
fn derive_key_id(depth: u8, d1: u32, d2: u32, d3: u32, d4: u32) -> Identifier;
fn derive_key(&self, id: &Identifier) -> Result<ExtendedPrivKey, Error>;
fn derive_key(&self, amount: u64, id: &Identifier) -> Result<SecretKey, Error>;
fn commit(&self, amount: u64, id: &Identifier) -> Result<Commitment, Error>;
fn blind_sum(&self, blind_sum: &BlindSum) -> Result<BlindingFactor, Error>;
fn sign(&self, msg: &Message, id: &Identifier) -> Result<Signature, Error>;
fn sign(&self, msg: &Message, amount: u64, id: &Identifier) -> Result<Signature, Error>;
fn sign_with_blinding(&self, _: &Message, _: &BlindingFactor) -> Result<Signature, Error>;
fn set_use_switch_commits(&mut self, value: bool);
fn secp(&self) -> &Secp256k1;
}

View file

@ -36,7 +36,7 @@ pub const PROTOCOL_VERSION: u32 = 1;
pub const USER_AGENT: &'static str = concat!("MW/Grin ", env!("CARGO_PKG_VERSION"));
/// Magic number expected in the header of every message
const MAGIC: [u8; 2] = [0x54, 0x34];
const MAGIC: [u8; 2] = [0x53, 0x35];
/// Max theoretical size of a block filled with outputs.
const MAX_BLOCK_SIZE: u64 =

View file

@ -44,7 +44,7 @@ fn test_transaction_pool_block_building() {
let height = prev_header.height + 1;
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
let fee = txs.iter().map(|x| x.fee()).sum();
let reward = libtx::reward::output(&keychain, &key_id, fee, height).unwrap();
let reward = libtx::reward::output(&keychain, &key_id, fee).unwrap();
let mut block = Block::new(&prev_header, txs, Difficulty::min(), reward).unwrap();
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).

View file

@ -44,7 +44,7 @@ fn test_transaction_pool_block_reconciliation() {
let header = {
let height = 1;
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
let reward = libtx::reward::output(&keychain, &key_id, 0, height).unwrap();
let reward = libtx::reward::output(&keychain, &key_id, 0).unwrap();
let genesis = BlockHeader::default();
let mut block = Block::new(&genesis, vec![], Difficulty::min(), reward).unwrap();
@ -63,7 +63,7 @@ fn test_transaction_pool_block_reconciliation() {
let block = {
let key_id = ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
let fees = initial_tx.fee();
let reward = libtx::reward::output(&keychain, &key_id, fees, 0).unwrap();
let reward = libtx::reward::output(&keychain, &key_id, fees).unwrap();
let mut block = Block::new(&header, vec![initial_tx], Difficulty::min(), reward).unwrap();
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).
@ -156,7 +156,7 @@ fn test_transaction_pool_block_reconciliation() {
let block = {
let key_id = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
let fees = block_txs.iter().map(|tx| tx.fee()).sum();
let reward = libtx::reward::output(&keychain, &key_id, fees, 0).unwrap();
let reward = libtx::reward::output(&keychain, &key_id, fees).unwrap();
let mut block = Block::new(&header, block_txs, Difficulty::min(), reward).unwrap();
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).

View file

@ -43,7 +43,7 @@ fn test_the_transaction_pool() {
let header = {
let height = 1;
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
let reward = libtx::reward::output(&keychain, &key_id, 0, height).unwrap();
let reward = libtx::reward::output(&keychain, &key_id, 0).unwrap();
let block = Block::new(&BlockHeader::default(), vec![], Difficulty::min(), reward).unwrap();
chain.update_db_for_block(&block);

View file

@ -24,14 +24,18 @@ use std::net::{SocketAddr, ToSocketAddrs};
use std::sync::{mpsc, Arc};
use std::{cmp, io, str, thread, time};
use crate::core::global;
use crate::p2p;
use crate::p2p::ChainAdapter;
use crate::pool::DandelionConfig;
use crate::util::{Mutex, StopState};
// DNS Seeds with contact email associated
const DNS_SEEDS: &'static [&'static str] = &[
"t4.seed.grin-tech.org", // igno.peverell@protonmail.com
const MAINNET_DNS_SEEDS: &'static [&'static str] = &[
"mainnet.seed.grin-tech.org", // igno.peverell@protonmail.com
];
const FLOONET_DNS_SEEDS: &'static [&'static str] = &[
"floonet.seed.grin-tech.org", // igno.peverell@protonmail.com
];
pub fn connect_and_monitor(
@ -331,7 +335,12 @@ fn listen_for_addrs(
pub fn dns_seeds() -> Box<dyn Fn() -> Vec<SocketAddr> + Send> {
Box::new(|| {
let mut addresses: Vec<SocketAddr> = vec![];
for dns_seed in DNS_SEEDS {
let net_seeds = if global::is_testnet() {
FLOONET_DNS_SEEDS
} else {
MAINNET_DNS_SEEDS
};
for dns_seed in net_seeds {
let temp_addresses = addresses.clone();
debug!("Retrieving seed nodes from dns {}", dns_seed);
match (dns_seed.to_owned(), 0).to_socket_addrs() {

View file

@ -133,13 +133,10 @@ impl Server {
));
let genesis = match config.chain_type {
global::ChainTypes::Testnet1 => genesis::genesis_testnet1(),
global::ChainTypes::Testnet2 => genesis::genesis_testnet2(),
global::ChainTypes::Testnet3 => genesis::genesis_testnet3(),
global::ChainTypes::Testnet4 => genesis::genesis_testnet4(),
global::ChainTypes::AutomatedTesting => genesis::genesis_dev(),
global::ChainTypes::UserTesting => genesis::genesis_dev(),
global::ChainTypes::Mainnet => genesis::genesis_testnet2(), //TODO: Fix, obviously
global::ChainTypes::Floonet => genesis::genesis_floo(),
global::ChainTypes::Mainnet => genesis::genesis_main(),
};
info!("Starting server, genesis block: {}", genesis.hash());

View file

@ -173,7 +173,7 @@ fn burn_reward(block_fees: BlockFees) -> Result<(core::Output, core::TxKernel, B
let keychain = ExtKeychain::from_random_seed().unwrap();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let (out, kernel) =
crate::core::libtx::reward::output(&keychain, &key_id, block_fees.fees, block_fees.height)
crate::core::libtx::reward::output(&keychain, &key_id, block_fees.fees)
.unwrap();
Ok((out, kernel, block_fees))
}

View file

@ -20,12 +20,12 @@ serde_derive = "1"
log4rs = { version = "0.8.1", features = ["rolling_file_appender", "compound_policy", "size_trigger", "fixed_window_roller"] }
log = "0.4"
walkdir = "2"
zip = "0.4"
zip = { version = "0.4", default-features = false }
parking_lot = {version = "0.6"}
[dependencies.grin_secp256k1zkp]
#git = "https://github.com/mimblewimble/rust-secp256k1-zkp"
#tag = "grin_integration_28"
git = "https://github.com/mimblewimble/rust-secp256k1-zkp"
tag = "grin_integration_29"
#path = "../../rust-secp256k1-zkp"
version = "0.7.1"
#version = "0.7.1"
features = ["bullet-proof-sizing"]

View file

@ -177,7 +177,7 @@ pub fn txs(
core::amount_to_hr_string(t.amount_debited - t.amount_credited, true)
)
};
let tx_data = match t.tx_hex {
let tx_data = match t.stored_tx {
Some(t) => format!("{}", t),
None => "None".to_owned(),
};

View file

@ -99,6 +99,7 @@ where
/// use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
///
/// let mut wallet_config = WalletConfig::default();
/// # wallet_config.data_file_dir = "test_output/doc/wallet1".to_owned();
///
/// // A NodeClient must first be created to handle communication between
/// // the wallet and the node.
@ -148,6 +149,7 @@ where
/// # use wallet::libwallet::api::APIOwner;
/// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
/// # let mut wallet_config = WalletConfig::default();
/// # wallet_config.data_file_dir = "test_output/doc/wallet1".to_owned();
/// # let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// # Arc::new(Mutex::new(
@ -203,6 +205,7 @@ where
/// # use wallet::libwallet::api::APIOwner;
/// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
/// # let mut wallet_config = WalletConfig::default();
/// # wallet_config.data_file_dir = "test_output/doc/wallet1".to_owned();
/// # let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// # Arc::new(Mutex::new(
@ -256,6 +259,7 @@ where
/// # use wallet::libwallet::api::APIOwner;
/// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
/// # let mut wallet_config = WalletConfig::default();
/// # wallet_config.data_file_dir = "test_output/doc/wallet1".to_owned();
/// # let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// # Arc::new(Mutex::new(
@ -313,6 +317,7 @@ where
/// # use wallet::libwallet::api::APIOwner;
/// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
/// # let mut wallet_config = WalletConfig::default();
/// # wallet_config.data_file_dir = "test_output/doc/wallet1".to_owned();
/// # let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// # Arc::new(Mutex::new(
@ -389,6 +394,7 @@ where
/// # use wallet::libwallet::api::APIOwner;
/// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
/// # let mut wallet_config = WalletConfig::default();
/// # wallet_config.data_file_dir = "test_output/doc/wallet1".to_owned();
/// # let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// # Arc::new(Mutex::new(
@ -462,6 +468,7 @@ where
/// # use wallet::libwallet::api::APIOwner;
/// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
/// # let mut wallet_config = WalletConfig::default();
/// # wallet_config.data_file_dir = "test_output/doc/wallet1".to_owned();
/// # let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// # Arc::new(Mutex::new(
@ -575,6 +582,7 @@ where
/// # use wallet::libwallet::api::APIOwner;
/// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
/// # let mut wallet_config = WalletConfig::default();
/// # wallet_config.data_file_dir = "test_output/doc/wallet1".to_owned();
/// # let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// # Arc::new(Mutex::new(

View file

@ -102,7 +102,7 @@ where
let mut t = TxLogEntry::new(parent_key_id.clone(), TxLogEntryType::TxSent, log_id);
t.tx_slate_id = Some(slate_id);
let filename = format!("{}.grintx", slate_id);
t.tx_hex = Some(filename);
t.stored_tx = Some(filename);
t.fee = Some(fee);
let mut amount_debited = 0;
t.num_inputs = lock_inputs.len();

View file

@ -196,7 +196,7 @@ where
return Err(ErrorKind::TransactionDoesntExist(tx_id.to_string()))?;
}
let tx = tx_vec[0].clone();
Ok((tx.confirmed, tx.tx_hex))
Ok((tx.confirmed, tx.stored_tx))
}
/// Update the stored transaction (this update needs to happen when the TX is finalised)

View file

@ -458,13 +458,7 @@ where
debug!("receive_coinbase: {:?}", block_fees);
let (out, kern) = reward::output(
wallet.keychain(),
&key_id,
block_fees.fees,
block_fees.height,
)
.unwrap();
let (out, kern) = reward::output(wallet.keychain(), &key_id, block_fees.fees).unwrap();
/* .context(ErrorKind::Keychain)?; */
Ok((out, kern, block_fees))
}

View file

@ -600,9 +600,8 @@ pub struct TxLogEntry {
pub amount_debited: u64,
/// Fee
pub fee: Option<u64>,
// TODO: rename this to 'stored_tx_file' or something for mainnet
/// The transaction json itself, stored for reference or resending
pub tx_hex: Option<String>,
/// Location of the store transaction, (reference or resending)
pub stored_tx: Option<String>,
}
impl ser::Writeable for TxLogEntry {
@ -634,7 +633,7 @@ impl TxLogEntry {
num_inputs: 0,
num_outputs: 0,
fee: None,
tx_hex: None,
stored_tx: None,
}
}

View file

@ -26,6 +26,8 @@ use serde_json;
use failure::ResultExt;
use uuid::Uuid;
use crate::blake2::blake2b::Blake2b;
use crate::keychain::{ChildNumber, ExtKeychain, Identifier, Keychain};
use crate::store::{self, option_to_not_found, to_key, to_key_u64};
@ -35,6 +37,7 @@ use crate::libwallet::types::*;
use crate::libwallet::{internal, Error, ErrorKind};
use crate::types::{WalletConfig, WalletSeed};
use crate::util;
use crate::util::secp::constants::SECRET_KEY_SIZE;
use crate::util::secp::pedersen;
pub const DB_DIR: &'static str = "db";
@ -62,6 +65,39 @@ pub fn wallet_db_exists(config: WalletConfig) -> bool {
db_path.exists()
}
/// Helper to derive XOR keys for storing private transaction keys in the DB
/// (blind_xor_key, nonce_xor_key)
fn private_ctx_xor_keys<K>(
keychain: &K,
slate_id: &[u8],
) -> Result<([u8; SECRET_KEY_SIZE], [u8; SECRET_KEY_SIZE]), Error>
where
K: Keychain,
{
let root_key = keychain.derive_key(0, &K::root_key_id())?;
// derive XOR values for storing secret values in DB
// h(root_key|slate_id|"blind")
let mut hasher = Blake2b::new(SECRET_KEY_SIZE);
hasher.update(&root_key.0[..]);
hasher.update(&slate_id[..]);
hasher.update(&"blind".as_bytes()[..]);
let blind_xor_key = hasher.finalize();
let mut ret_blind = [0; SECRET_KEY_SIZE];
ret_blind.copy_from_slice(&blind_xor_key.as_bytes()[0..SECRET_KEY_SIZE]);
// h(root_key|slate_id|"nonce")
let mut hasher = Blake2b::new(SECRET_KEY_SIZE);
hasher.update(&root_key.0[..]);
hasher.update(&slate_id[..]);
hasher.update(&"nonce".as_bytes()[..]);
let nonce_xor_key = hasher.finalize();
let mut ret_nonce = [0; SECRET_KEY_SIZE];
ret_nonce.copy_from_slice(&nonce_xor_key.as_bytes()[0..SECRET_KEY_SIZE]);
Ok((ret_blind, ret_nonce))
}
pub struct LMDBBackend<C, K> {
db: store::Store,
config: WalletConfig,
@ -140,8 +176,11 @@ where
fn open_with_credentials(&mut self) -> Result<(), Error> {
let wallet_seed = WalletSeed::from_file(&self.config, &self.passphrase)
.context(ErrorKind::CallbackImpl("Error opening wallet"))?;
let keychain = wallet_seed.derive_keychain();
self.keychain = Some(keychain.context(ErrorKind::CallbackImpl("Error deriving keychain"))?);
self.keychain = Some(
wallet_seed
.derive_keychain()
.context(ErrorKind::CallbackImpl("Error deriving keychain"))?,
);
Ok(())
}
@ -229,11 +268,19 @@ where
fn get_private_context(&mut self, slate_id: &[u8]) -> Result<Context, Error> {
let ctx_key = to_key(PRIVATE_TX_CONTEXT_PREFIX, &mut slate_id.to_vec());
option_to_not_found(
let (blind_xor_key, nonce_xor_key) = private_ctx_xor_keys(self.keychain(), slate_id)?;
let mut ctx: Context = option_to_not_found(
self.db.get_ser(&ctx_key),
&format!("Slate id: {:x?}", slate_id.to_vec()),
)
.map_err(|e| e.into())
)?;
for i in 0..SECRET_KEY_SIZE {
ctx.sec_key.0[i] = ctx.sec_key.0[i] ^ blind_xor_key[i];
ctx.sec_nonce.0[i] = ctx.sec_nonce.0[i] ^ nonce_xor_key[i];
}
Ok(ctx)
}
fn acct_path_iter<'a>(&'a self) -> Box<dyn Iterator<Item = AcctPathMapping> + 'a> {
@ -259,7 +306,7 @@ where
}
fn get_stored_tx(&self, entry: &TxLogEntry) -> Result<Option<Transaction>, Error> {
let filename = match entry.tx_hex.clone() {
let filename = match entry.stored_tx.clone() {
Some(f) => f,
None => return Ok(None),
};
@ -498,11 +545,21 @@ where
self.save(out.clone())
}
//TODO: Keys stored unencrypted in DB.. not good
// should store keys as derivation paths instead
fn save_private_context(&mut self, slate_id: &[u8], ctx: &Context) -> Result<(), Error> {
let ctx_key = to_key(PRIVATE_TX_CONTEXT_PREFIX, &mut slate_id.to_vec());
self.db.borrow().as_ref().unwrap().put_ser(&ctx_key, &ctx)?;
let (blind_xor_key, nonce_xor_key) = private_ctx_xor_keys(self.keychain(), slate_id)?;
let mut s_ctx = ctx.clone();
for i in 0..SECRET_KEY_SIZE {
s_ctx.sec_key.0[i] = s_ctx.sec_key.0[i] ^ blind_xor_key[i];
s_ctx.sec_nonce.0[i] = s_ctx.sec_nonce.0[i] ^ nonce_xor_key[i];
}
self.db
.borrow()
.as_ref()
.unwrap()
.put_ser(&ctx_key, &s_ctx)?;
Ok(())
}

View file

@ -45,8 +45,8 @@ pub use self::{testclient::LocalWalletClient, testclient::WalletProxy};
/// Get an output from the chain locally and present it back as an API output
fn get_output_local(chain: &chain::Chain, commit: &pedersen::Commitment) -> Option<api::Output> {
let outputs = [
OutputIdentifier::new(OutputFeatures::DEFAULT_OUTPUT, commit),
OutputIdentifier::new(OutputFeatures::COINBASE_OUTPUT, commit),
OutputIdentifier::new(OutputFeatures::PLAIN, commit),
OutputIdentifier::new(OutputFeatures::COINBASE, commit),
];
for x in outputs.iter() {

View file

@ -50,9 +50,9 @@ pub struct WalletConfig {
pub check_node_api_http_addr: String,
// The directory in which wallet files are stored
pub data_file_dir: String,
/// TLS ceritificate file
/// TLS certificate file
pub tls_certificate_file: Option<String>,
/// TLS ceritificate private key file
/// TLS certificate private key file
pub tls_certificate_key: Option<String>,
/// Whether to use the black background color scheme for command line
/// if enabled, wallet command output color will be suitable for black background terminal
@ -62,7 +62,7 @@ pub struct WalletConfig {
impl Default for WalletConfig {
fn default() -> WalletConfig {
WalletConfig {
chain_type: Some(ChainTypes::Testnet4),
chain_type: Some(ChainTypes::Floonet),
api_listen_interface: "127.0.0.1".to_string(),
api_listen_port: 13415,
api_secret_path: Some(".api_secret".to_string()),

View file

@ -26,7 +26,7 @@ use grin_wallet as wallet;
use rand::thread_rng;
fn kernel_sig_msg() -> secp::Message {
transaction::kernel_sig_msg(0, 0, transaction::KernelFeatures::DEFAULT_KERNEL).unwrap()
transaction::kernel_sig_msg(0, 0, transaction::KernelFeatures::PLAIN).unwrap()
}
#[test]
@ -38,8 +38,8 @@ fn aggsig_sender_receiver_interaction() {
// Normally this would happen during transaction building.
let kernel_excess = {
let id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let skey1 = sender_keychain.derive_key(&id1).unwrap().secret_key;
let skey2 = receiver_keychain.derive_key(&id1).unwrap().secret_key;
let skey1 = sender_keychain.derive_key(0, &id1).unwrap();
let skey2 = receiver_keychain.derive_key(0, &id1).unwrap();
let keychain = ExtKeychain::from_random_seed().unwrap();
let blinding_factor = keychain
@ -62,7 +62,7 @@ fn aggsig_sender_receiver_interaction() {
let (sender_pub_excess, _sender_pub_nonce) = {
let keychain = sender_keychain.clone();
let id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let skey = keychain.derive_key(&id1).unwrap().secret_key;
let skey = keychain.derive_key(0, &id1).unwrap();
// dealing with an input here so we need to negate the blinding_factor
// rather than use it as is
@ -85,7 +85,7 @@ fn aggsig_sender_receiver_interaction() {
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
// let blind = blind_sum.secret_key(&keychain.secp())?;
let blind = keychain.derive_key(&key_id).unwrap().secret_key;
let blind = keychain.derive_key(0, &key_id).unwrap();
rx_cx = Context::new(&keychain.secp(), blind);
let (pub_excess, pub_nonce) = rx_cx.get_public_keys(&keychain.secp());
@ -247,8 +247,8 @@ fn aggsig_sender_receiver_interaction_offset() {
// Normally this would happen during transaction building.
let kernel_excess = {
let id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let skey1 = sender_keychain.derive_key(&id1).unwrap().secret_key;
let skey2 = receiver_keychain.derive_key(&id1).unwrap().secret_key;
let skey1 = sender_keychain.derive_key(0, &id1).unwrap();
let skey2 = receiver_keychain.derive_key(0, &id1).unwrap();
let keychain = ExtKeychain::from_random_seed().unwrap();
let blinding_factor = keychain
@ -274,7 +274,7 @@ fn aggsig_sender_receiver_interaction_offset() {
let (sender_pub_excess, _sender_pub_nonce) = {
let keychain = sender_keychain.clone();
let id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let skey = keychain.derive_key(&id1).unwrap().secret_key;
let skey = keychain.derive_key(0, &id1).unwrap();
// dealing with an input here so we need to negate the blinding_factor
// rather than use it as is
@ -301,7 +301,7 @@ fn aggsig_sender_receiver_interaction_offset() {
let keychain = receiver_keychain.clone();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let blind = keychain.derive_key(&key_id).unwrap().secret_key;
let blind = keychain.derive_key(0, &key_id).unwrap();
rx_cx = Context::new(&keychain.secp(), blind);
let (pub_excess, pub_nonce) = rx_cx.get_public_keys(&keychain.secp());