Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add lock_height to Transaction and TxKernel #167

Merged
merged 10 commits into from
Oct 11, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions .hooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,23 @@ problem_files=()
printf "[pre_commit] rustfmt "
for file in $(git diff --name-only --cached); do
if [ ${file: -3} == ".rs" ]; then
cargo +nightly fmt -- --skip-children --write-mode=diff $file &>/dev/null
if [ $? != 0 ]; then
problem_files+=($file)
result=1
fi
cargo +nightly fmt -- --skip-children --write-mode=diff $file &>/dev/null
if [ $? != 0 ]; then
problem_files+=($file)
result=1
fi
fi
done

if [ $result != 0 ]; then
printf "\033[0;31mfail\033[0m \n"
printf "[pre_commit] the following files need formatting: \n"
printf "[pre_commit] the following files need formatting: \n"

for file in $problem_files; do
printf " cargo +nightly fmt -- $file\n"
done
else
printf "\033[0;32mok\033[0m \n"
printf "\033[0;32mok\033[0m \n"
fi

exit 0
Expand Down
1 change: 1 addition & 0 deletions api/src/endpoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ impl ApiEndpoint for OutputApi {
let commit = Commitment::from_vec(c);

let out = self.chain.get_unspent(&commit).map_err(|_| Error::NotFound)?;

let header = self.chain
.get_block_header_by_output_commit(&commit)
.map_err(|_| Error::NotFound)?;
Expand Down
3 changes: 2 additions & 1 deletion api/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub struct Output {
pub proof: pedersen::RangeProof,
/// The height of the block creating this output
pub height: u64,
/// The lock height (spendable after block)
/// The lock height (earliest block this output can be spent)
pub lock_height: u64,
}

Expand All @@ -65,6 +65,7 @@ impl Output {
}
_ => (OutputType::Transaction, 0),
};

Output {
output_type: output_type,
commit: output.commit,
Expand Down
9 changes: 5 additions & 4 deletions chain/src/pipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,10 @@ fn validate_header(header: &BlockHeader, ctx: &mut BlockContext) -> Result<(), E

// check version, enforces scheduled hard fork
if !consensus::valid_header_version(header.height, header.version) {
error!("Invalid block header version received ({}), maybe update Grin?",
header.version);
error!(
"Invalid block header version received ({}), maybe update Grin?",
header.version
);
return Err(Error::InvalidBlockVersion(header.version));
}

Expand Down Expand Up @@ -269,8 +271,7 @@ fn validate_block(
return Err(Error::InvalidRoot);
}

// check that any coinbase outputs are spendable (that they have matured
// sufficiently)
// check for any outputs with lock_heights greater than current block height
for input in &b.inputs {
if let Ok(output) = ctx.store.get_output_by_commit(&input.commitment()) {
if output.features.contains(transaction::COINBASE_OUTPUT) {
Expand Down
1 change: 1 addition & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ workspace = ".."
bitflags = "~0.7.0"
blake2-rfc = "~0.2.17"
byteorder = "^0.5"
log = "~0.3"
num-bigint = "^0.1.35"
rand = "^0.3"
serde = "~1.0.8"
Expand Down
1 change: 1 addition & 0 deletions core/rustfmt.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
hard_tabs = true
wrap_comments = true
comment_width = 120 # we have some long urls in comments
write_mode = "Overwrite"
37 changes: 28 additions & 9 deletions core/src/core/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ pub enum Error {
OddKernelFee,
/// Too many inputs, outputs or kernels in the block
WeightExceeded,
/// Underlying Secp256k1 error (signature validation or invalid public
/// key typically)
/// Kernel not valid due to lock_height exceeding block header height
KernelLockHeight,
/// Underlying Secp256k1 error (signature validation or invalid public key typically)
Secp(secp::Error),
}

Expand Down Expand Up @@ -252,15 +253,21 @@ impl Block {
/// Builds a new block from the header of the previous block, a vector of
/// transactions and the private key that will receive the reward. Checks
/// that all transactions are valid and calculates the Merkle tree.
///
/// Only used in tests (to be confirmed, may be wrong here).
///
pub fn new(
prev: &BlockHeader,
txs: Vec<&Transaction>,
keychain: &keychain::Keychain,
pubkey: &keychain::Identifier,
) -> Result<Block, keychain::Error> {

let fees = txs.iter().map(|tx| tx.fee).sum();
let (reward_out, reward_proof) = Block::reward_output(keychain, pubkey, fees)?;
let (reward_out, reward_proof) = Block::reward_output(
keychain,
pubkey,
fees,
)?;
let block = Block::with_reward(prev, txs, reward_out, reward_proof)?;
Ok(block)
}
Expand Down Expand Up @@ -415,6 +422,9 @@ impl Block {
/// Validates all the elements in a block that can be checked without
/// additional data. Includes commitment sums and kernels, Merkle
/// trees, reward, etc.
///
/// TODO - performs various verification steps - discuss renaming this to "verify"
///
pub fn validate(&self, secp: &Secp256k1) -> Result<(), Error> {
if exceeds_weight(self.inputs.len(), self.outputs.len(), self.kernels.len()) {
return Err(Error::WeightExceeded);
Expand All @@ -424,14 +434,21 @@ impl Block {
Ok(())
}

/// Validate the sum of input/output commitments match the sum in kernels
/// Verifies the sum of input/output commitments match the sum in kernels
/// and that all kernel signatures are valid.
/// TODO - when would we skip_sig? Is this needed or used anywhere?
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);
}

if k.lock_height > self.header.height {
println!("verify_kernels!!!!!! checking heights {}, {}", k.lock_height, self.header.height);
return Err(Error::KernelLockHeight);
}
}

// sum all inputs and outs commitments
let io_sum = self.sum_commitments(secp)?;

Expand Down Expand Up @@ -483,8 +500,7 @@ impl Block {
Ok(())
}

/// Builds the blinded output and related signature proof for the block
/// reward.
/// Builds the blinded output and related signature proof for the block reward.
pub fn reward_output(
keychain: &keychain::Keychain,
pubkey: &keychain::Identifier,
Expand Down Expand Up @@ -515,6 +531,7 @@ impl Block {
excess: excess,
excess_sig: sig.serialize_der(&secp),
fee: 0,
lock_height: 0,
};
Ok((output, proof))
}
Expand Down Expand Up @@ -553,7 +570,7 @@ mod test {
let max_out = MAX_BLOCK_WEIGHT / BLOCK_OUTPUT_WEIGHT;

let mut pks = vec![];
for n in 0..(max_out+1) {
for n in 0..(max_out + 1) {
pks.push(keychain.derive_pubkey(n as u32).unwrap());
}

Expand All @@ -564,7 +581,9 @@ mod test {

let now = Instant::now();
parts.append(&mut vec![input(500000, pks.pop().unwrap()), with_fee(2)]);
let mut tx = build::transaction(parts, &keychain).map(|(tx, _)| tx).unwrap();
let mut tx = build::transaction(parts, &keychain)
.map(|(tx, _)| tx)
.unwrap();
println!("Build tx: {}", now.elapsed().as_secs());

let b = new_block(vec![&mut tx], &keychain);
Expand Down
50 changes: 33 additions & 17 deletions core/src/core/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,19 @@ pub fn output(value: u64, pubkey: Identifier) -> Box<Append> {
Box::new(move |build, (tx, sum)| -> (Transaction, BlindSum) {
let commit = build.keychain.commit(value, &pubkey).unwrap();
let msg = secp::pedersen::ProofMessage::empty();
let rproof = build.keychain.range_proof(value, &pubkey, commit, msg).unwrap();

(tx.with_output(Output {
features: DEFAULT_OUTPUT,
commit: commit,
proof: rproof,
}), sum.add_pubkey(pubkey.clone()))
let rproof = build
.keychain
.range_proof(value, &pubkey, commit, msg)
.unwrap();

(
tx.with_output(Output {
features: DEFAULT_OUTPUT,
commit: commit,
proof: rproof,
}),
sum.add_pubkey(pubkey.clone()),
)
})
}

Expand All @@ -73,6 +79,13 @@ pub fn with_fee(fee: u64) -> Box<Append> {
})
}

/// Sets the lock_height on the transaction being built.
pub fn with_lock_height(lock_height: u64) -> Box<Append> {
Box::new(move |_build, (tx, sum)| -> (Transaction, BlindSum) {
(tx.with_lock_height(lock_height), sum)
})
}

/// Sets a known excess value on the transaction being built. Usually used in
/// combination with the initial_tx function when a new transaction is built
/// by adding to a pre-existing one.
Expand All @@ -95,28 +108,33 @@ pub fn initial_tx(tx: Transaction) -> Box<Append> {
///
/// Example:
/// let (tx1, sum) = build::transaction(vec![input_rand(4), output_rand(1),
/// with_fee(1)]).unwrap();
/// with_fee(1)], keychain).unwrap();
/// let (tx2, _) = build::transaction(vec![initial_tx(tx1), with_excess(sum),
/// output_rand(2)]).unwrap();
/// output_rand(2)], keychain).unwrap();
///
pub fn transaction(
elems: Vec<Box<Append>>,
keychain: &keychain::Keychain,
) -> Result<(Transaction, BlindingFactor), keychain::Error> {
let mut ctx = Context { keychain };
let (mut tx, sum) = elems.iter().fold(
(Transaction::empty(), BlindSum::new()), |acc, elem| elem(&mut ctx, acc)
(Transaction::empty(), BlindSum::new()),
|acc, elem| elem(&mut ctx, acc),
);
let blind_sum = ctx.keychain.blind_sum(&sum)?;
let msg = secp::Message::from_slice(&u64_to_32bytes(tx.fee))?;
let msg = secp::Message::from_slice(&transaction_fee_and_lock_height_to_bytes(
tx.fee,
tx.lock_height,
))?;
let sig = ctx.keychain.sign_with_blinding(&msg, &blind_sum)?;
tx.excess_sig = sig.serialize_der(&ctx.keychain.secp());
Ok((tx, blind_sum))
}

fn u64_to_32bytes(n: u64) -> [u8; 32] {
fn transaction_fee_and_lock_height_to_bytes(fee: u64, lock_height: u64) -> [u8; 32] {
let mut bytes = [0; 32];
BigEndian::write_u64(&mut bytes[24..32], n);
BigEndian::write_u64(&mut bytes[16..24], fee);
BigEndian::write_u64(&mut bytes[24..], lock_height);
bytes
}

Expand Down Expand Up @@ -146,10 +164,8 @@ mod test {
let pk1 = keychain.derive_pubkey(1).unwrap();
let pk2 = keychain.derive_pubkey(2).unwrap();

let (tx, _) = transaction(
vec![input(6, pk1), output(2, pk2), with_fee(4)],
&keychain,
).unwrap();
let (tx, _) = transaction(vec![input(6, pk1), output(2, pk2), with_fee(4)], &keychain)
.unwrap();

tx.verify_sig(&keychain.secp()).unwrap();
}
Expand Down
50 changes: 28 additions & 22 deletions core/src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,8 @@ mod test {
let tx = tx2i1o();
let mut vec = Vec::new();
ser::serialize(&mut vec, &tx).expect("serialized failed");
assert!(vec.len() > 5320);
assert!(vec.len() < 5340);
assert!(vec.len() > 5340);
assert!(vec.len() < 5360);
}

#[test]
Expand Down Expand Up @@ -249,11 +249,15 @@ mod test {
let pk2 = keychain.derive_pubkey(2).unwrap();
let pk3 = keychain.derive_pubkey(3).unwrap();

let (tx, _) =
build::transaction(
vec![input(75, pk1), output(42, pk2), output(32, pk3), with_fee(1)],
&keychain,
).unwrap();
let (tx, _) = build::transaction(
vec![
input(75, pk1),
output(42, pk2),
output(32, pk3),
with_fee(1),
],
&keychain,
).unwrap();
let h = tx.outputs[0].hash();
assert!(h != ZERO_HASH);
let h2 = tx.outputs[1].hash();
Expand Down Expand Up @@ -304,22 +308,19 @@ mod test {

// Alice builds her transaction, with change, which also produces the sum
// of blinding factors before they're obscured.
let (tx, sum) = build::transaction(
vec![in1, in2, output(1, pk3), with_fee(2)],
&keychain,
).unwrap();
let (tx, sum) =
build::transaction(vec![in1, in2, output(1, pk3), with_fee(2)], &keychain).unwrap();
tx_alice = tx;
blind_sum = sum;
}

// From now on, Bob only has the obscured transaction and the sum of
// blinding factors. He adds his output, finalizes the transaction so it's
// ready for broadcast.
let (tx_final, _) =
build::transaction(
vec![initial_tx(tx_alice), with_excess(blind_sum), output(4, pk4)],
&keychain,
).unwrap();
let (tx_final, _) = build::transaction(
vec![initial_tx(tx_alice), with_excess(blind_sum), output(4, pk4)],
&keychain,
).unwrap();

tx_final.validate(&keychain.secp()).unwrap();
}
Expand Down Expand Up @@ -360,7 +361,12 @@ mod test {
let mut tx2 = tx1i1o();
tx2.verify_sig(keychain.secp()).unwrap();

let b = Block::new(&BlockHeader::default(), vec![&mut tx1, &mut tx2], &keychain, &pubkey).unwrap();
let b = Block::new(
&BlockHeader::default(),
vec![&mut tx1, &mut tx2],
&keychain,
&pubkey,
).unwrap();
b.validate(keychain.secp()).unwrap();
}

Expand Down Expand Up @@ -388,7 +394,8 @@ mod test {
build::transaction(
vec![input(10, pk1), input(11, pk2), output(19, pk3), with_fee(2)],
&keychain,
).map(|(tx, _)| tx).unwrap()
).map(|(tx, _)| tx)
.unwrap()
}

// utility producing a transaction with a single input and output
Expand All @@ -397,9 +404,8 @@ mod test {
let pk1 = keychain.derive_pubkey(1).unwrap();
let pk2 = keychain.derive_pubkey(2).unwrap();

build::transaction(
vec![input(5, pk1), output(3, pk2), with_fee(2)],
&keychain,
).map(|(tx, _)| tx).unwrap()
build::transaction(vec![input(5, pk1), output(3, pk2), with_fee(2)], &keychain)
.map(|(tx, _)| tx)
.unwrap()
}
}
Loading