From f43df5f601ed7a7b6f7fef4dd7a3a4e16c6fab26 Mon Sep 17 00:00:00 2001 From: John Tromp Date: Tue, 18 Dec 2018 19:26:34 +0100 Subject: [PATCH] [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 --- api/src/handlers/utils.rs | 4 +- api/src/types.rs | 11 +- chain/src/txhashset/utxo_view.rs | 4 +- chain/tests/data_file_integrity.rs | 4 +- chain/tests/mine_simple_chain.rs | 10 +- chain/tests/store_indices.rs | 2 +- chain/tests/test_coinbase_maturity.rs | 13 +-- core/src/core/block.rs | 6 +- core/src/core/compact_block.rs | 6 +- core/src/core/transaction.rs | 135 ++++++++++++++++++----- core/src/libtx/aggsig.rs | 8 +- core/src/libtx/build.rs | 6 +- core/src/libtx/reward.rs | 19 ++-- core/src/libtx/slate.rs | 9 +- core/tests/block.rs | 26 ++--- core/tests/common.rs | 2 +- core/tests/core.rs | 2 +- core/tests/transaction.rs | 4 +- core/tests/verifier_cache.rs | 2 +- pool/tests/block_building.rs | 2 +- pool/tests/block_reconciliation.rs | 6 +- pool/tests/transaction_pool.rs | 2 +- servers/src/mining/mine_block.rs | 2 +- wallet/src/libwallet/internal/updater.rs | 8 +- wallet/src/test_framework/mod.rs | 4 +- wallet/tests/libwallet.rs | 2 +- 26 files changed, 175 insertions(+), 124 deletions(-) diff --git a/api/src/handlers/utils.rs b/api/src/handlers/utils.rs index 2e2bc01e3..3b5c9d0df 100644 --- a/api/src/handlers/utils.rs +++ b/api/src/handlers/utils.rs @@ -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() { diff --git a/api/src/types.rs b/api/src/types.rs index 2024d07fb..bb2469f35 100644 --- a/api/src/types.rs +++ b/api/src/types.rs @@ -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() }; diff --git a/chain/src/txhashset/utxo_view.rs b/chain/src/txhashset/utxo_view.rs index 586ea108e..1b648ba72 100644 --- a/chain/src/txhashset/utxo_view.rs +++ b/chain/src/txhashset/utxo_view.rs @@ -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); diff --git a/chain/tests/data_file_integrity.rs b/chain/tests/data_file_integrity.rs index fdd10273c..d3cc0b8ad 100644 --- a/chain/tests/data_file_integrity.rs +++ b/chain/tests/data_file_integrity.rs @@ -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(), diff --git a/chain/tests/mine_simple_chain.rs b/chain/tests/mine_simple_chain.rs index 9ab310bf5..adcd930c3 100644 --- a/chain/tests/mine_simple_chain.rs +++ b/chain/tests/mine_simple_chain.rs @@ -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(), diff --git a/chain/tests/store_indices.rs b/chain/tests/store_indices.rs index 9ef7ae7a1..c5b4f04bb 100644 --- a/chain/tests/store_indices.rs +++ b/chain/tests/store_indices.rs @@ -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(); diff --git a/chain/tests/test_coinbase_maturity.rs b/chain/tests/test_coinbase_maturity.rs index 14d948a49..939797baf 100644 --- a/chain/tests/test_coinbase_maturity.rs +++ b/chain/tests/test_coinbase_maturity.rs @@ -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); diff --git a/core/src/core/block.rs b/core/src/core/block.rs index abe6c1d7d..bf474d3d8 100644 --- a/core/src/core/block.rs +++ b/core/src/core/block.rs @@ -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; @@ -663,14 +663,14 @@ impl Block { .body .outputs .iter() - .filter(|out| out.features.contains(OutputFeatures::COINBASE_OUTPUT)) + .filter(|out| out.is_coinbase()) .collect::>(); let cb_kerns = self .body .kernels .iter() - .filter(|kernel| kernel.features.contains(KernelFeatures::COINBASE_KERNEL)) + .filter(|kernel| kernel.is_coinbase()) .collect::>(); { diff --git a/core/src/core/compact_block.rs b/core/src/core/compact_block.rs index 3b7aec424..031728069 100644 --- a/core/src/core/compact_block.rs +++ b/core/src/core/compact_block.rs @@ -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 for CompactBlock { let out_full = block .outputs() .iter() - .filter(|x| x.features.contains(OutputFeatures::COINBASE_OUTPUT)) + .filter(|x| x.is_coinbase()) .cloned() .collect::>(); @@ -176,7 +176,7 @@ impl From 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)); diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index 325faec1d..d30ef4b55 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -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 } @@ -583,25 +622,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(()) @@ -1078,6 +1109,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! { @@ -1085,9 +1126,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; } } @@ -1157,12 +1198,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 @@ -1288,25 +1351,43 @@ impl From 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 { + 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::*; @@ -1325,7 +1406,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(), @@ -1335,7 +1416,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()); @@ -1343,7 +1424,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(), @@ -1353,7 +1434,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()); @@ -1380,7 +1461,7 @@ mod test { let commit = keychain.commit(5, &key_id).unwrap(); let input = Input { - features: OutputFeatures::DEFAULT_OUTPUT, + features: OutputFeatures::PLAIN, commit: commit, }; @@ -1396,7 +1477,7 @@ mod test { // 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, }; diff --git a/core/src/libtx/aggsig.rs b/core/src/libtx/aggsig.rs index 50e213710..43e37ea53 100644 --- a/core/src/libtx/aggsig.rs +++ b/core/src/libtx/aggsig.rs @@ -241,14 +241,14 @@ 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, value, &key_id, Some(&pubkey)).unwrap(); @@ -315,14 +315,14 @@ 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, value, &key_id, Some(&pubkey)).unwrap(); diff --git a/core/src/libtx/build.rs b/core/src/libtx/build.rs index d4e75d710..f502f4d86 100644 --- a/core/src/libtx/build.rs +++ b/core/src/libtx/build.rs @@ -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,7 +97,7 @@ where ( tx.with_output(Output { - features: OutputFeatures::DEFAULT_OUTPUT, + features: OutputFeatures::PLAIN, commit: commit, proof: rproof, }), diff --git a/core/src/libtx/reward.rs b/core/src/libtx/reward.rs index e114e4272..a85c12507 100644 --- a/core/src/libtx/reward.rs +++ b/core/src/libtx/reward.rs @@ -27,7 +27,6 @@ pub fn output( 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)?; + // 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)) } diff --git a/core/src/libtx/slate.rs b/core/src/libtx/slate.rs index fd9551da5..8816d028f 100644 --- a/core/src/libtx/slate.rs +++ b/core/src/libtx/slate.rs @@ -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 { // 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) } diff --git a/core/tests/block.rs b/core/tests/block.rs index 9766a3419..908b94962 100644 --- a/core/tests/block.rs +++ b/core/tests/block.rs @@ -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::>(); 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::>(); 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) ); diff --git a/core/tests/common.rs b/core/tests/common.rs index 5c2227629..0ef7494cd 100644 --- a/core/tests/common.rs +++ b/core/tests/common.rs @@ -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(), diff --git a/core/tests/core.rs b/core/tests/core.rs index acc391d00..56940a7dc 100644 --- a/core/tests/core.rs +++ b/core/tests/core.rs @@ -122,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()); } diff --git a/core/tests/transaction.rs b/core/tests/transaction.rs index ced3c94ee..fc89f9774 100644 --- a/core/tests/transaction.rs +++ b/core/tests/transaction.rs @@ -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); } diff --git a/core/tests/verifier_cache.rs b/core/tests/verifier_cache.rs index ff7d62dc4..6a0b00857 100644 --- a/core/tests/verifier_cache.rs +++ b/core/tests/verifier_cache.rs @@ -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, }; diff --git a/pool/tests/block_building.rs b/pool/tests/block_building.rs index f31824a41..29dd0ba39 100644 --- a/pool/tests/block_building.rs +++ b/pool/tests/block_building.rs @@ -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). diff --git a/pool/tests/block_reconciliation.rs b/pool/tests/block_reconciliation.rs index b9a324f80..6404cddd0 100644 --- a/pool/tests/block_reconciliation.rs +++ b/pool/tests/block_reconciliation.rs @@ -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). diff --git a/pool/tests/transaction_pool.rs b/pool/tests/transaction_pool.rs index 20799dfdf..1cf726108 100644 --- a/pool/tests/transaction_pool.rs +++ b/pool/tests/transaction_pool.rs @@ -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); diff --git a/servers/src/mining/mine_block.rs b/servers/src/mining/mine_block.rs index 3c4ccf115..5aa3216b1 100644 --- a/servers/src/mining/mine_block.rs +++ b/servers/src/mining/mine_block.rs @@ -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)) } diff --git a/wallet/src/libwallet/internal/updater.rs b/wallet/src/libwallet/internal/updater.rs index 26469ab6b..10228f697 100644 --- a/wallet/src/libwallet/internal/updater.rs +++ b/wallet/src/libwallet/internal/updater.rs @@ -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)) } diff --git a/wallet/src/test_framework/mod.rs b/wallet/src/test_framework/mod.rs index 23a1a7c3f..69f62772f 100644 --- a/wallet/src/test_framework/mod.rs +++ b/wallet/src/test_framework/mod.rs @@ -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 { 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() { diff --git a/wallet/tests/libwallet.rs b/wallet/tests/libwallet.rs index 04ef3ad46..3ca5ac974 100644 --- a/wallet/tests/libwallet.rs +++ b/wallet/tests/libwallet.rs @@ -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]