mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-01 17:01:09 +03:00
Half of fees get rewarded, half burnt. Minor cleanups
Update coinbase building and block summation to account for half of the fees going to the coinbase. Forcing fees to be even as a consequence. Now that we can't build the coinbase independently from the block (because fees), had to update the miner to keep the key derivation so a new derivation isn't made any time a new block gets worked on. Minor doc and warning cleanups.
This commit is contained in:
parent
e060b1953e
commit
7012d37f5f
10 changed files with 149 additions and 91 deletions
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use secp;
|
|
||||||
use secp::pedersen::Commitment;
|
use secp::pedersen::Commitment;
|
||||||
|
|
||||||
use grin_store as store;
|
use grin_store as store;
|
||||||
|
|
|
@ -26,6 +26,11 @@ use core::target::Difficulty;
|
||||||
/// The block subsidy amount
|
/// The block subsidy amount
|
||||||
pub const REWARD: u64 = 1_000_000_000;
|
pub const REWARD: u64 = 1_000_000_000;
|
||||||
|
|
||||||
|
/// Actual block reward for a given total fee amount
|
||||||
|
pub fn reward(fee: u64) -> u64 {
|
||||||
|
REWARD + fee / 2
|
||||||
|
}
|
||||||
|
|
||||||
/// Number of blocks before a coinbase matures and can be spent
|
/// Number of blocks before a coinbase matures and can be spent
|
||||||
pub const COINBASE_MATURITY: u64 = 1_000;
|
pub const COINBASE_MATURITY: u64 = 1_000;
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ use std::collections::HashSet;
|
||||||
|
|
||||||
use core::Committed;
|
use core::Committed;
|
||||||
use core::{Input, Output, Proof, TxKernel, Transaction, COINBASE_KERNEL, COINBASE_OUTPUT};
|
use core::{Input, Output, Proof, TxKernel, Transaction, COINBASE_KERNEL, COINBASE_OUTPUT};
|
||||||
use consensus::REWARD;
|
use consensus::{REWARD, reward};
|
||||||
use consensus::MINIMUM_DIFFICULTY;
|
use consensus::MINIMUM_DIFFICULTY;
|
||||||
use core::hash::{Hash, Hashed, ZERO_HASH};
|
use core::hash::{Hash, Hashed, ZERO_HASH};
|
||||||
use core::target::Difficulty;
|
use core::target::Difficulty;
|
||||||
|
@ -42,6 +42,10 @@ pub enum Error {
|
||||||
/// The sum of output minus input commitments does not match the sum of
|
/// The sum of output minus input commitments does not match the sum of
|
||||||
/// kernel commitments
|
/// kernel commitments
|
||||||
KernelSumMismatch,
|
KernelSumMismatch,
|
||||||
|
/// Same as above but for the coinbase part of a block, including reward
|
||||||
|
CoinbaseSumMismatch,
|
||||||
|
/// Kernel fee can't be odd, due to half fee burning
|
||||||
|
OddKernelFee,
|
||||||
/// Underlying Secp256k1 error (signature validation or invalid public
|
/// Underlying Secp256k1 error (signature validation or invalid public
|
||||||
/// key typically)
|
/// key typically)
|
||||||
Secp(secp::Error),
|
Secp(secp::Error),
|
||||||
|
@ -237,7 +241,7 @@ impl Committed for Block {
|
||||||
&self.outputs
|
&self.outputs
|
||||||
}
|
}
|
||||||
fn overage(&self) -> i64 {
|
fn overage(&self) -> i64 {
|
||||||
(self.total_fees() as i64) - (REWARD as i64)
|
((self.total_fees() / 2) as i64) - (REWARD as i64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,7 +268,8 @@ impl Block {
|
||||||
pubkey: keychain::Identifier,
|
pubkey: keychain::Identifier,
|
||||||
) -> Result<Block, keychain::Error> {
|
) -> Result<Block, keychain::Error> {
|
||||||
|
|
||||||
let (reward_out, reward_proof) = Block::reward_output(keychain, pubkey)?;
|
let fees = txs.iter().map(|tx| tx.fee).sum();
|
||||||
|
let (reward_out, reward_proof) = Block::reward_output(keychain, pubkey, fees)?;
|
||||||
let block = Block::with_reward(prev, txs, reward_out, reward_proof)?;
|
let block = Block::with_reward(prev, txs, reward_out, reward_proof)?;
|
||||||
Ok(block)
|
Ok(block)
|
||||||
}
|
}
|
||||||
|
@ -278,6 +283,7 @@ impl Block {
|
||||||
reward_out: Output,
|
reward_out: Output,
|
||||||
reward_kern: TxKernel,
|
reward_kern: TxKernel,
|
||||||
) -> Result<Block, secp::Error> {
|
) -> Result<Block, secp::Error> {
|
||||||
|
|
||||||
// note: the following reads easily but may not be the most efficient due to
|
// note: the following reads easily but may not be the most efficient due to
|
||||||
// repeated iterations, revisit if a problem
|
// repeated iterations, revisit if a problem
|
||||||
let secp = Secp256k1::with_caps(secp::ContextFlag::Commit);
|
let secp = Secp256k1::with_caps(secp::ContextFlag::Commit);
|
||||||
|
@ -420,13 +426,18 @@ impl Block {
|
||||||
/// trees, reward, etc.
|
/// trees, reward, etc.
|
||||||
pub fn validate(&self, secp: &Secp256k1) -> Result<(), Error> {
|
pub fn validate(&self, secp: &Secp256k1) -> Result<(), Error> {
|
||||||
self.verify_coinbase(secp)?;
|
self.verify_coinbase(secp)?;
|
||||||
self.verify_kernels(secp)?;
|
self.verify_kernels(secp, false)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validate the sum of input/output commitments match the sum in kernels
|
/// Validate the sum of input/output commitments match the sum in kernels
|
||||||
/// and that all kernel signatures are valid.
|
/// and that all kernel signatures are valid.
|
||||||
pub fn verify_kernels(&self, secp: &Secp256k1) -> Result<(), Error> {
|
fn verify_kernels(&self, secp: &Secp256k1, skip_sig: bool) -> Result<(), Error> {
|
||||||
|
for k in &self.kernels {
|
||||||
|
if k.fee & 1 != 0 {
|
||||||
|
return Err(Error::OddKernelFee);
|
||||||
|
}
|
||||||
|
}
|
||||||
// sum all inputs and outs commitments
|
// sum all inputs and outs commitments
|
||||||
let io_sum = self.sum_commitments(secp)?;
|
let io_sum = self.sum_commitments(secp)?;
|
||||||
|
|
||||||
|
@ -440,8 +451,10 @@ impl Block {
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify all signatures with the commitment as pk
|
// verify all signatures with the commitment as pk
|
||||||
for proof in &self.kernels {
|
if !skip_sig {
|
||||||
proof.verify(secp)?;
|
for proof in &self.kernels {
|
||||||
|
proof.verify(secp)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -452,25 +465,21 @@ impl Block {
|
||||||
// * That the sum of blinding factors for all coinbase-marked outputs match
|
// * That the sum of blinding factors for all coinbase-marked outputs match
|
||||||
// the coinbase-marked kernels.
|
// the coinbase-marked kernels.
|
||||||
fn verify_coinbase(&self, secp: &Secp256k1) -> Result<(), Error> {
|
fn verify_coinbase(&self, secp: &Secp256k1) -> Result<(), Error> {
|
||||||
let cb_outs = self.outputs
|
let cb_outs = filter_map_vec!(self.outputs, |out| {
|
||||||
.iter()
|
if out.features.contains(COINBASE_OUTPUT) { Some(out.commitment()) } else { None }
|
||||||
.filter(|out| out.features.contains(COINBASE_OUTPUT))
|
});
|
||||||
.map(|o| o.clone())
|
let cb_kerns = filter_map_vec!(self.kernels, |k| {
|
||||||
.collect::<Vec<_>>();
|
if k.features.contains(COINBASE_KERNEL) { Some(k.excess) } else { None }
|
||||||
let cb_kerns = self.kernels
|
});
|
||||||
.iter()
|
|
||||||
.filter(|k| k.features.contains(COINBASE_KERNEL))
|
|
||||||
.map(|k| k.clone())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
// verifying the kernels on a block composed of just the coinbase outputs
|
let over_commit = secp.commit_value(reward(self.total_fees()))?;
|
||||||
// and kernels checks all we need
|
let out_adjust_sum = secp.commit_sum(cb_outs, vec![over_commit])?;
|
||||||
Block {
|
|
||||||
header: BlockHeader::default(),
|
let kerns_sum = secp.commit_sum(cb_kerns, vec![])?;
|
||||||
inputs: vec![],
|
if kerns_sum != out_adjust_sum {
|
||||||
outputs: cb_outs,
|
return Err(Error::CoinbaseSumMismatch);
|
||||||
kernels: cb_kerns,
|
}
|
||||||
}.verify_kernels(secp)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the blinded output and related signature proof for the block
|
/// Builds the blinded output and related signature proof for the block
|
||||||
|
@ -478,16 +487,15 @@ impl Block {
|
||||||
pub fn reward_output(
|
pub fn reward_output(
|
||||||
keychain: &keychain::Keychain,
|
keychain: &keychain::Keychain,
|
||||||
pubkey: keychain::Identifier,
|
pubkey: keychain::Identifier,
|
||||||
|
fees: u64,
|
||||||
) -> Result<(Output, TxKernel), keychain::Error> {
|
) -> Result<(Output, TxKernel), keychain::Error> {
|
||||||
|
|
||||||
let secp = keychain.secp();
|
let secp = keychain.secp();
|
||||||
|
|
||||||
let msg = secp::Message::from_slice(&[0; secp::constants::MESSAGE_SIZE])?;
|
let commit = keychain.commit(reward(fees), &pubkey)?;
|
||||||
let sig = keychain.sign(&msg, &pubkey)?;
|
|
||||||
let commit = keychain.commit(REWARD, &pubkey)?;
|
|
||||||
let msg = secp::pedersen::ProofMessage::empty();
|
|
||||||
// let switch_commit = keychain.switch_commit(pubkey)?;
|
// let switch_commit = keychain.switch_commit(pubkey)?;
|
||||||
|
let msg = secp::pedersen::ProofMessage::empty();
|
||||||
let rproof = keychain.range_proof(REWARD, &pubkey, commit, msg)?;
|
let rproof = keychain.range_proof(reward(fees), &pubkey, commit, msg)?;
|
||||||
|
|
||||||
let output = Output {
|
let output = Output {
|
||||||
features: COINBASE_OUTPUT,
|
features: COINBASE_OUTPUT,
|
||||||
|
@ -495,9 +503,12 @@ impl Block {
|
||||||
proof: rproof,
|
proof: rproof,
|
||||||
};
|
};
|
||||||
|
|
||||||
let over_commit = try!(secp.commit_value(REWARD as u64));
|
let over_commit = secp.commit_value(reward(fees))?;
|
||||||
let out_commit = output.commitment();
|
let out_commit = output.commitment();
|
||||||
let excess = try!(secp.commit_sum(vec![out_commit], vec![over_commit]));
|
let excess = secp.commit_sum(vec![out_commit], vec![over_commit])?;
|
||||||
|
|
||||||
|
let msg = secp::Message::from_slice(&[0; secp::constants::MESSAGE_SIZE])?;
|
||||||
|
let sig = keychain.sign(&msg, &pubkey)?;
|
||||||
|
|
||||||
let proof = TxKernel {
|
let proof = TxKernel {
|
||||||
features: COINBASE_KERNEL,
|
features: COINBASE_KERNEL,
|
||||||
|
@ -529,7 +540,7 @@ mod test {
|
||||||
// utility producing a transaction that spends an output with the provided
|
// utility producing a transaction that spends an output with the provided
|
||||||
// value and blinding key
|
// value and blinding key
|
||||||
fn txspend1i1o(v: u64, keychain: &Keychain, pk1: Identifier, pk2: Identifier) -> Transaction {
|
fn txspend1i1o(v: u64, keychain: &Keychain, pk1: Identifier, pk2: Identifier) -> Transaction {
|
||||||
build::transaction(vec![input(v, pk1), output(3, pk2), with_fee(1)], &keychain)
|
build::transaction(vec![input(v, pk1), output(3, pk2), with_fee(2)], &keychain)
|
||||||
.map(|(tx, _)| tx)
|
.map(|(tx, _)| tx)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
@ -544,13 +555,13 @@ mod test {
|
||||||
|
|
||||||
let mut btx1 = tx2i1o();
|
let mut btx1 = tx2i1o();
|
||||||
let (mut btx2, _) = build::transaction(
|
let (mut btx2, _) = build::transaction(
|
||||||
vec![input(5, pk1), output(4, pk2.clone()), with_fee(1)],
|
vec![input(7, pk1), output(5, pk2.clone()), with_fee(2)],
|
||||||
&keychain,
|
&keychain,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
// spending tx2 - reuse pk2
|
// spending tx2 - reuse pk2
|
||||||
|
|
||||||
let mut btx3 = txspend1i1o(4, &keychain, pk2.clone(), pk3);
|
let mut btx3 = txspend1i1o(5, &keychain, pk2.clone(), pk3);
|
||||||
let b = new_block(vec![&mut btx1, &mut btx2, &mut btx3], &keychain);
|
let b = new_block(vec![&mut btx1, &mut btx2, &mut btx3], &keychain);
|
||||||
|
|
||||||
// block should have been automatically compacted (including reward
|
// block should have been automatically compacted (including reward
|
||||||
|
@ -572,12 +583,12 @@ mod test {
|
||||||
let mut btx1 = tx2i1o();
|
let mut btx1 = tx2i1o();
|
||||||
|
|
||||||
let (mut btx2, _) = build::transaction(
|
let (mut btx2, _) = build::transaction(
|
||||||
vec![input(5, pk1), output(4, pk2.clone()), with_fee(1)],
|
vec![input(7, pk1), output(5, pk2.clone()), with_fee(2)],
|
||||||
&keychain,
|
&keychain,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
// spending tx2 - reuse pk2
|
// spending tx2 - reuse pk2
|
||||||
let mut btx3 = txspend1i1o(4, &keychain, pk2.clone(), pk3);
|
let mut btx3 = txspend1i1o(5, &keychain, pk2.clone(), pk3);
|
||||||
|
|
||||||
let b1 = new_block(vec![&mut btx1, &mut btx2], &keychain);
|
let b1 = new_block(vec![&mut btx1, &mut btx2], &keychain);
|
||||||
b1.validate(&keychain.secp()).unwrap();
|
b1.validate(&keychain.secp()).unwrap();
|
||||||
|
@ -632,13 +643,13 @@ mod test {
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
b.verify_coinbase(&keychain.secp()),
|
b.verify_coinbase(&keychain.secp()),
|
||||||
Err(Error::KernelSumMismatch)
|
Err(Error::CoinbaseSumMismatch)
|
||||||
);
|
);
|
||||||
assert_eq!(b.verify_kernels(&keychain.secp()), Ok(()));
|
assert_eq!(b.verify_kernels(&keychain.secp(), false), Ok(()));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
b.validate(&keychain.secp()),
|
b.validate(&keychain.secp()),
|
||||||
Err(Error::KernelSumMismatch)
|
Err(Error::CoinbaseSumMismatch)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -656,7 +667,7 @@ mod test {
|
||||||
b.verify_coinbase(&keychain.secp()),
|
b.verify_coinbase(&keychain.secp()),
|
||||||
Err(Error::Secp(secp::Error::IncorrectCommitSum))
|
Err(Error::Secp(secp::Error::IncorrectCommitSum))
|
||||||
);
|
);
|
||||||
assert_eq!(b.verify_kernels(&keychain.secp()), Ok(()));
|
assert_eq!(b.verify_kernels(&keychain.secp(), true), Ok(()));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
b.validate(&keychain.secp()),
|
b.validate(&keychain.secp()),
|
||||||
|
|
|
@ -219,7 +219,7 @@ mod test {
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
ser::serialize(&mut vec, &tx).expect("serialization failed");
|
ser::serialize(&mut vec, &tx).expect("serialization failed");
|
||||||
let dtx: Transaction = ser::deserialize(&mut &vec[..]).unwrap();
|
let dtx: Transaction = ser::deserialize(&mut &vec[..]).unwrap();
|
||||||
assert_eq!(dtx.fee, 1);
|
assert_eq!(dtx.fee, 2);
|
||||||
assert_eq!(dtx.inputs.len(), 2);
|
assert_eq!(dtx.inputs.len(), 2);
|
||||||
assert_eq!(dtx.outputs.len(), 1);
|
assert_eq!(dtx.outputs.len(), 1);
|
||||||
assert_eq!(tx.hash(), dtx.hash());
|
assert_eq!(tx.hash(), dtx.hash());
|
||||||
|
@ -305,7 +305,7 @@ mod test {
|
||||||
// Alice builds her transaction, with change, which also produces the sum
|
// Alice builds her transaction, with change, which also produces the sum
|
||||||
// of blinding factors before they're obscured.
|
// of blinding factors before they're obscured.
|
||||||
let (tx, sum) = build::transaction(
|
let (tx, sum) = build::transaction(
|
||||||
vec![in1, in2, output(1, pk3), with_fee(1)],
|
vec![in1, in2, output(1, pk3), with_fee(2)],
|
||||||
&keychain,
|
&keychain,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
tx_alice = tx;
|
tx_alice = tx;
|
||||||
|
@ -317,7 +317,7 @@ mod test {
|
||||||
// ready for broadcast.
|
// ready for broadcast.
|
||||||
let (tx_final, _) =
|
let (tx_final, _) =
|
||||||
build::transaction(
|
build::transaction(
|
||||||
vec![initial_tx(tx_alice), with_excess(blind_sum), output(5, pk4)],
|
vec![initial_tx(tx_alice), with_excess(blind_sum), output(4, pk4)],
|
||||||
&keychain,
|
&keychain,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
|
@ -386,7 +386,7 @@ mod test {
|
||||||
let pk3 = keychain.derive_pubkey(3).unwrap();
|
let pk3 = keychain.derive_pubkey(3).unwrap();
|
||||||
|
|
||||||
build::transaction(
|
build::transaction(
|
||||||
vec![input(10, pk1), input(11, pk2), output(20, pk3), with_fee(1)],
|
vec![input(10, pk1), input(11, pk2), output(19, pk3), with_fee(2)],
|
||||||
&keychain,
|
&keychain,
|
||||||
).map(|(tx, _)| tx).unwrap()
|
).map(|(tx, _)| tx).unwrap()
|
||||||
}
|
}
|
||||||
|
@ -398,7 +398,7 @@ mod test {
|
||||||
let pk2 = keychain.derive_pubkey(2).unwrap();
|
let pk2 = keychain.derive_pubkey(2).unwrap();
|
||||||
|
|
||||||
build::transaction(
|
build::transaction(
|
||||||
vec![input(5, pk1), output(4, pk2), with_fee(1)],
|
vec![input(5, pk1), output(3, pk2), with_fee(2)],
|
||||||
&keychain,
|
&keychain,
|
||||||
).map(|(tx, _)| tx).unwrap()
|
).map(|(tx, _)| tx).unwrap()
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,6 +181,10 @@ where
|
||||||
/// implementation.
|
/// implementation.
|
||||||
fn append(&mut self, position: u64, data: Vec<HashSum<T>>) -> Result<(), String>;
|
fn append(&mut self, position: u64, data: Vec<HashSum<T>>) -> Result<(), String>;
|
||||||
|
|
||||||
|
/// Rewind the backend state to a previous position, as if all append
|
||||||
|
/// operations after that had been canceled. Expects a position in the PMMR
|
||||||
|
/// to rewind to as well as the consumer-provided index of when the change
|
||||||
|
/// occurred (see remove).
|
||||||
fn rewind(&mut self, position: u64, index: u32) -> Result<(), String>;
|
fn rewind(&mut self, position: u64, index: u32) -> Result<(), String>;
|
||||||
|
|
||||||
/// Get a HashSum by insertion position
|
/// Get a HashSum by insertion position
|
||||||
|
@ -382,6 +386,7 @@ pub struct VecBackend<T>
|
||||||
where
|
where
|
||||||
T: Summable + Clone,
|
T: Summable + Clone,
|
||||||
{
|
{
|
||||||
|
/// Backend elements
|
||||||
pub elems: Vec<Option<HashSum<T>>>,
|
pub elems: Vec<Option<HashSum<T>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,6 +402,7 @@ where
|
||||||
fn get(&self, position: u64) -> Option<HashSum<T>> {
|
fn get(&self, position: u64) -> Option<HashSum<T>> {
|
||||||
self.elems[(position - 1) as usize].clone()
|
self.elems[(position - 1) as usize].clone()
|
||||||
}
|
}
|
||||||
|
#[allow(unused_variables)]
|
||||||
fn remove(&mut self, positions: Vec<u64>, index: u32) -> Result<(), String> {
|
fn remove(&mut self, positions: Vec<u64>, index: u32) -> Result<(), String> {
|
||||||
for n in positions {
|
for n in positions {
|
||||||
self.elems[(n - 1) as usize] = None
|
self.elems[(n - 1) as usize] = None
|
||||||
|
@ -971,7 +977,7 @@ mod test {
|
||||||
{
|
{
|
||||||
let mut pmmr = PMMR::at(&mut ba, sz);
|
let mut pmmr = PMMR::at(&mut ba, sz);
|
||||||
for n in 1..16 {
|
for n in 1..16 {
|
||||||
pmmr.prune(n, 0);
|
let _ = pmmr.prune(n, 0);
|
||||||
}
|
}
|
||||||
assert_eq!(orig_root, pmmr.root());
|
assert_eq!(orig_root, pmmr.root());
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,22 @@ bitflags! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Errors thrown by Block validation
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Transaction fee can't be odd, due to half fee burning
|
||||||
|
OddFee,
|
||||||
|
/// Underlying Secp256k1 error (signature validation or invalid public
|
||||||
|
/// key typically)
|
||||||
|
Secp(secp::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<secp::Error> for Error {
|
||||||
|
fn from(e: secp::Error) -> Error {
|
||||||
|
Error::Secp(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A proof that a transaction sums to zero. Includes both the transaction's
|
/// A proof that a transaction sums to zero. Includes both the transaction's
|
||||||
/// Pedersen commitment and the signature, that guarantees that the commitments
|
/// Pedersen commitment and the signature, that guarantees that the commitments
|
||||||
/// amount to zero. The signature signs the fee, which is retained for
|
/// amount to zero. The signature signs the fee, which is retained for
|
||||||
|
@ -245,11 +261,14 @@ impl Transaction {
|
||||||
/// Validates all relevant parts of a fully built transaction. Checks the
|
/// Validates all relevant parts of a fully built transaction. Checks the
|
||||||
/// excess value against the signature as well as range proofs for each
|
/// excess value against the signature as well as range proofs for each
|
||||||
/// output.
|
/// output.
|
||||||
pub fn validate(&self, secp: &Secp256k1) -> Result<TxKernel, secp::Error> {
|
pub fn validate(&self, secp: &Secp256k1) -> Result<TxKernel, Error> {
|
||||||
|
if self.fee & 1 != 0 {
|
||||||
|
return Err(Error::OddFee);
|
||||||
|
}
|
||||||
for out in &self.outputs {
|
for out in &self.outputs {
|
||||||
out.verify_proof(secp)?;
|
out.verify_proof(secp)?;
|
||||||
}
|
}
|
||||||
self.verify_sig(secp)
|
self.verify_sig(secp).map_err(&From::from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ use core::core;
|
||||||
use core::core::Proof;
|
use core::core::Proof;
|
||||||
use pow::cuckoo;
|
use pow::cuckoo;
|
||||||
use core::core::target::Difficulty;
|
use core::core::target::Difficulty;
|
||||||
use core::core::{Block, BlockHeader};
|
use core::core::{Block, BlockHeader, Transaction};
|
||||||
use core::core::hash::{Hash, Hashed};
|
use core::core::hash::{Hash, Hashed};
|
||||||
use pow::MiningWorker;
|
use pow::MiningWorker;
|
||||||
use pow::types::MinerConfig;
|
use pow::types::MinerConfig;
|
||||||
|
@ -43,7 +43,7 @@ use secp;
|
||||||
use pool;
|
use pool;
|
||||||
use util;
|
use util;
|
||||||
use keychain::Keychain;
|
use keychain::Keychain;
|
||||||
use wallet::{CbAmount, WalletReceiveRequest, CbData};
|
use wallet::{BlockFees, WalletReceiveRequest, CbData};
|
||||||
|
|
||||||
use pow::plugin::PluginMiner;
|
use pow::plugin::PluginMiner;
|
||||||
|
|
||||||
|
@ -428,13 +428,16 @@ impl Miner {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut coinbase = self.get_coinbase();
|
// to prevent the wallet from generating a new HD key derivation for each
|
||||||
|
// iteration, we keep the returned derivation to provide it back when
|
||||||
|
// nothing has changed
|
||||||
|
let mut derivation = 0;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// get the latest chain state and build a block on top of it
|
// get the latest chain state and build a block on top of it
|
||||||
let head = self.chain.head_header().unwrap();
|
let head = self.chain.head_header().unwrap();
|
||||||
let mut latest_hash = self.chain.head().unwrap().last_block_h;
|
let mut latest_hash = self.chain.head().unwrap().last_block_h;
|
||||||
let mut b = self.build_block(&head, coinbase.clone());
|
let (mut b, deriv) = self.build_block(&head, derivation);
|
||||||
|
|
||||||
let mut sol = None;
|
let mut sol = None;
|
||||||
let mut use_async = false;
|
let mut use_async = false;
|
||||||
|
@ -496,34 +499,37 @@ impl Miner {
|
||||||
self.debug_output_id,
|
self.debug_output_id,
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
coinbase = self.get_coinbase();
|
|
||||||
}
|
}
|
||||||
|
derivation = 0;
|
||||||
|
} else {
|
||||||
|
derivation = deriv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a new block with the chain head as previous and eligible
|
/// Builds a new block with the chain head as previous and eligible
|
||||||
/// transactions from the pool.
|
/// transactions from the pool.
|
||||||
fn build_block(
|
fn build_block(&self, head: &core::BlockHeader, deriv: u32) -> (core::Block, u32) {
|
||||||
&self,
|
// prepare the block header timestamp
|
||||||
head: &core::BlockHeader,
|
|
||||||
coinbase: (core::Output, core::TxKernel),
|
|
||||||
) -> core::Block {
|
|
||||||
let mut now_sec = time::get_time().sec;
|
let mut now_sec = time::get_time().sec;
|
||||||
let head_sec = head.timestamp.to_timespec().sec;
|
let head_sec = head.timestamp.to_timespec().sec;
|
||||||
if now_sec == head_sec {
|
if now_sec == head_sec {
|
||||||
now_sec += 1;
|
now_sec += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the difficulty our block should be at
|
||||||
let diff_iter = self.chain.difficulty_iter();
|
let diff_iter = self.chain.difficulty_iter();
|
||||||
let difficulty = consensus::next_difficulty(diff_iter).unwrap();
|
let difficulty = consensus::next_difficulty(diff_iter).unwrap();
|
||||||
|
|
||||||
|
// extract current transaction from the pool
|
||||||
let txs_box = self.tx_pool.read().unwrap().prepare_mineable_transactions(
|
let txs_box = self.tx_pool.read().unwrap().prepare_mineable_transactions(
|
||||||
MAX_TX,
|
MAX_TX,
|
||||||
);
|
);
|
||||||
let txs = txs_box.iter().map(|tx| tx.as_ref()).collect();
|
let txs: Vec<&Transaction> = txs_box.iter().map(|tx| tx.as_ref()).collect();
|
||||||
let (output, kernel) = coinbase;
|
|
||||||
|
// build the coinbase and the block itself
|
||||||
|
let fees = txs.iter().map(|tx| tx.fee).sum();
|
||||||
|
let (output, kernel, deriv) = self.get_coinbase(fees, deriv);
|
||||||
let mut b = core::Block::with_reward(head, txs, output, kernel).unwrap();
|
let mut b = core::Block::with_reward(head, txs, output, kernel).unwrap();
|
||||||
debug!(
|
debug!(
|
||||||
"(Server ID: {}) Built new block with {} inputs and {} outputs, difficulty: {}",
|
"(Server ID: {}) Built new block with {} inputs and {} outputs, difficulty: {}",
|
||||||
|
@ -544,20 +550,21 @@ impl Miner {
|
||||||
self.chain.set_sumtree_roots(&mut b).expect(
|
self.chain.set_sumtree_roots(&mut b).expect(
|
||||||
"Error setting sum tree roots",
|
"Error setting sum tree roots",
|
||||||
);
|
);
|
||||||
b
|
(b, deriv)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_coinbase(&self) -> (core::Output, core::TxKernel) {
|
fn get_coinbase(&self, fees: u64, derivation: u32) -> (core::Output, core::TxKernel, u32) {
|
||||||
if self.config.burn_reward {
|
if self.config.burn_reward {
|
||||||
let keychain = Keychain::from_random_seed().unwrap();
|
let keychain = Keychain::from_random_seed().unwrap();
|
||||||
let pubkey = keychain.derive_pubkey(1).unwrap();
|
let pubkey = keychain.derive_pubkey(1).unwrap();
|
||||||
core::Block::reward_output(&keychain, pubkey).unwrap()
|
let (out, kern) = core::Block::reward_output(&keychain, pubkey, fees).unwrap();
|
||||||
|
(out, kern, 0)
|
||||||
} else {
|
} else {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/v1/receive/coinbase",
|
"{}/v1/receive/coinbase",
|
||||||
self.config.wallet_receiver_url.as_str()
|
self.config.wallet_receiver_url.as_str()
|
||||||
);
|
);
|
||||||
let request = WalletReceiveRequest::Coinbase(CbAmount { amount: consensus::REWARD });
|
let request = WalletReceiveRequest::Coinbase(BlockFees { fees: fees, derivation: derivation });
|
||||||
let res: CbData = api::client::post(url.as_str(), &request).expect(
|
let res: CbData = api::client::post(url.as_str(), &request).expect(
|
||||||
format!(
|
format!(
|
||||||
"(Server ID: {}) Wallet receiver unreachable, could not claim reward. Is it running?",
|
"(Server ID: {}) Wallet receiver unreachable, could not claim reward. Is it running?",
|
||||||
|
@ -570,7 +577,7 @@ impl Miner {
|
||||||
let output = ser::deserialize(&mut &out_bin[..]).unwrap();
|
let output = ser::deserialize(&mut &out_bin[..]).unwrap();
|
||||||
let kernel = ser::deserialize(&mut &kern_bin[..]).unwrap();
|
let kernel = ser::deserialize(&mut &kern_bin[..]).unwrap();
|
||||||
|
|
||||||
(output, kernel)
|
(output, kernel, res.derivation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,4 +39,4 @@ mod types;
|
||||||
pub use info::show_info;
|
pub use info::show_info;
|
||||||
pub use receiver::{WalletReceiver, receive_json_tx};
|
pub use receiver::{WalletReceiver, receive_json_tx};
|
||||||
pub use sender::issue_send_tx;
|
pub use sender::issue_send_tx;
|
||||||
pub use types::{WalletConfig, WalletReceiveRequest, CbAmount, CbData};
|
pub use types::{WalletConfig, WalletReceiveRequest, BlockFees, CbData};
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
//! double-exchange will be required as soon as we support Schnorr signatures.
|
//! double-exchange will be required as soon as we support Schnorr signatures.
|
||||||
//! So we may as well have it in place already.
|
//! So we may as well have it in place already.
|
||||||
|
|
||||||
|
use core::consensus::reward;
|
||||||
use core::core::{Block, Transaction, TxKernel, Output, build};
|
use core::core::{Block, Transaction, TxKernel, Output, build};
|
||||||
use core::ser;
|
use core::ser;
|
||||||
use api::{self, ApiEndpoint, Operation, ApiResult};
|
use api::{self, ApiEndpoint, Operation, ApiResult};
|
||||||
|
@ -106,16 +106,14 @@ impl ApiEndpoint for WalletReceiver {
|
||||||
match op.as_str() {
|
match op.as_str() {
|
||||||
"coinbase" => {
|
"coinbase" => {
|
||||||
match input {
|
match input {
|
||||||
WalletReceiveRequest::Coinbase(cb_amount) => {
|
WalletReceiveRequest::Coinbase(cb_fees) => {
|
||||||
debug!("Operation {} with amount {}", op, cb_amount.amount);
|
debug!("Operation {} with amount {}", op, cb_fees.fees);
|
||||||
if cb_amount.amount == 0 {
|
let (out, kern, derivation) =
|
||||||
return Err(api::Error::Argument(format!("Zero amount not allowed.")));
|
|
||||||
}
|
|
||||||
let (out, kern) =
|
|
||||||
receive_coinbase(
|
receive_coinbase(
|
||||||
&self.config,
|
&self.config,
|
||||||
&self.keychain,
|
&self.keychain,
|
||||||
cb_amount.amount,
|
cb_fees.fees,
|
||||||
|
cb_fees.derivation,
|
||||||
).map_err(|e| {
|
).map_err(|e| {
|
||||||
api::Error::Internal(format!("Error building coinbase: {:?}", e))
|
api::Error::Internal(format!("Error building coinbase: {:?}", e))
|
||||||
})?;
|
})?;
|
||||||
|
@ -130,6 +128,7 @@ impl ApiEndpoint for WalletReceiver {
|
||||||
Ok(CbData {
|
Ok(CbData {
|
||||||
output: util::to_hex(out_bin),
|
output: util::to_hex(out_bin),
|
||||||
kernel: util::to_hex(kern_bin),
|
kernel: util::to_hex(kern_bin),
|
||||||
|
derivation: derivation,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ => Err(api::Error::Argument(
|
_ => Err(api::Error::Argument(
|
||||||
|
@ -149,6 +148,7 @@ impl ApiEndpoint for WalletReceiver {
|
||||||
Ok(CbData {
|
Ok(CbData {
|
||||||
output: String::from(""),
|
output: String::from(""),
|
||||||
kernel: String::from(""),
|
kernel: String::from(""),
|
||||||
|
derivation: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ => Err(api::Error::Argument(
|
_ => Err(api::Error::Argument(
|
||||||
|
@ -165,20 +165,24 @@ impl ApiEndpoint for WalletReceiver {
|
||||||
fn receive_coinbase(
|
fn receive_coinbase(
|
||||||
config: &WalletConfig,
|
config: &WalletConfig,
|
||||||
keychain: &Keychain,
|
keychain: &Keychain,
|
||||||
amount: u64,
|
fees: u64,
|
||||||
) -> Result<(Output, TxKernel), Error> {
|
mut derivation: u32,
|
||||||
|
) -> Result<(Output, TxKernel, u32), Error> {
|
||||||
|
|
||||||
let fingerprint = keychain.clone().fingerprint();
|
let fingerprint = keychain.clone().fingerprint();
|
||||||
|
|
||||||
// operate within a lock on wallet data
|
// operate within a lock on wallet data
|
||||||
WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
|
WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
|
||||||
let derivation = wallet_data.next_child(fingerprint.clone());
|
if derivation == 0 {
|
||||||
|
derivation = wallet_data.next_child(fingerprint.clone());
|
||||||
|
}
|
||||||
let pubkey = keychain.derive_pubkey(derivation)?;
|
let pubkey = keychain.derive_pubkey(derivation)?;
|
||||||
|
|
||||||
// track the new output and return the stuff needed for reward
|
// track the new output and return the stuff needed for reward
|
||||||
wallet_data.append_output(OutputData {
|
wallet_data.append_output(OutputData {
|
||||||
fingerprint: fingerprint.clone(),
|
fingerprint: fingerprint.clone(),
|
||||||
n_child: derivation,
|
n_child: derivation,
|
||||||
value: amount,
|
value: reward(fees),
|
||||||
status: OutputStatus::Unconfirmed,
|
status: OutputStatus::Unconfirmed,
|
||||||
height: 0,
|
height: 0,
|
||||||
lock_height: 0,
|
lock_height: 0,
|
||||||
|
@ -186,8 +190,8 @@ fn receive_coinbase(
|
||||||
debug!("Received coinbase and built output - {}, {}, {}",
|
debug!("Received coinbase and built output - {}, {}, {}",
|
||||||
fingerprint.clone(), pubkey.fingerprint(), derivation);
|
fingerprint.clone(), pubkey.fingerprint(), derivation);
|
||||||
|
|
||||||
let result = Block::reward_output(&keychain, pubkey)?;
|
let (out, kern) = Block::reward_output(&keychain, pubkey, fees)?;
|
||||||
Ok(result)
|
Ok((out, kern, derivation))
|
||||||
})?
|
})?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,6 +203,7 @@ fn receive_transaction(
|
||||||
blinding: BlindingFactor,
|
blinding: BlindingFactor,
|
||||||
partial: Transaction,
|
partial: Transaction,
|
||||||
) -> Result<Transaction, Error> {
|
) -> Result<Transaction, Error> {
|
||||||
|
|
||||||
let fingerprint = keychain.clone().fingerprint();
|
let fingerprint = keychain.clone().fingerprint();
|
||||||
|
|
||||||
// operate within a lock on wallet data
|
// operate within a lock on wallet data
|
||||||
|
|
|
@ -23,7 +23,7 @@ use serde_json;
|
||||||
use secp;
|
use secp;
|
||||||
|
|
||||||
use api;
|
use api;
|
||||||
use core::core::Transaction;
|
use core::core::{Transaction, transaction};
|
||||||
use core::ser;
|
use core::ser;
|
||||||
use keychain;
|
use keychain;
|
||||||
use util;
|
use util;
|
||||||
|
@ -36,6 +36,7 @@ const LOCK_FILE: &'static str = "wallet.lock";
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
NotEnoughFunds(u64),
|
NotEnoughFunds(u64),
|
||||||
Keychain(keychain::Error),
|
Keychain(keychain::Error),
|
||||||
|
Transaction(transaction::Error),
|
||||||
Secp(secp::Error),
|
Secp(secp::Error),
|
||||||
WalletData(String),
|
WalletData(String),
|
||||||
/// An error in the format of the JSON structures exchanged by the wallet
|
/// An error in the format of the JSON structures exchanged by the wallet
|
||||||
|
@ -52,6 +53,10 @@ impl From<secp::Error> for Error {
|
||||||
fn from(e: secp::Error) -> Error { Error::Secp(e) }
|
fn from(e: secp::Error) -> Error { Error::Secp(e) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<transaction::Error> for Error {
|
||||||
|
fn from(e: transaction::Error) -> Error { Error::Transaction(e) }
|
||||||
|
}
|
||||||
|
|
||||||
impl From<serde_json::Error> for Error {
|
impl From<serde_json::Error> for Error {
|
||||||
fn from(e: serde_json::Error) -> Error { Error::Format(e.to_string()) }
|
fn from(e: serde_json::Error) -> Error { Error::Format(e.to_string()) }
|
||||||
}
|
}
|
||||||
|
@ -268,8 +273,7 @@ impl WalletData {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Select a subset of unspent outputs to spend in a transaction
|
/// Select a subset of unspent outputs to spend in a transaction
|
||||||
/// transferring
|
/// transferring the provided amount.
|
||||||
/// the provided amount.
|
|
||||||
pub fn select(
|
pub fn select(
|
||||||
&self,
|
&self,
|
||||||
fingerprint: keychain::Fingerprint,
|
fingerprint: keychain::Fingerprint,
|
||||||
|
@ -356,15 +360,16 @@ pub fn partial_tx_from_json(
|
||||||
/// Amount in request to build a coinbase output.
|
/// Amount in request to build a coinbase output.
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub enum WalletReceiveRequest {
|
pub enum WalletReceiveRequest {
|
||||||
Coinbase(CbAmount),
|
Coinbase(BlockFees),
|
||||||
PartialTransaction(String),
|
PartialTransaction(String),
|
||||||
Finalize(String),
|
Finalize(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Amount in request to build a coinbase output.
|
/// Fees in block to use for coinbase amount calculation
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct CbAmount {
|
pub struct BlockFees {
|
||||||
pub amount: u64,
|
pub fees: u64,
|
||||||
|
pub derivation: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Response to build a coinbase output.
|
/// Response to build a coinbase output.
|
||||||
|
@ -372,4 +377,5 @@ pub struct CbAmount {
|
||||||
pub struct CbData {
|
pub struct CbData {
|
||||||
pub output: String,
|
pub output: String,
|
||||||
pub kernel: String,
|
pub kernel: String,
|
||||||
|
pub derivation: u32,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue