mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-01 17:01:09 +03:00
Small tweaks and addition to a Transaction to make 2-parts building easier. Unit test covering the standard exchange between 2 parties when building a transaction.
This commit is contained in:
parent
f64d20749f
commit
cb84f588fb
3 changed files with 114 additions and 22 deletions
|
@ -448,14 +448,14 @@ mod test {
|
||||||
let ref secp = new_secp();
|
let ref secp = new_secp();
|
||||||
|
|
||||||
let tx1 = tx2i1o(secp, &mut rng);
|
let tx1 = tx2i1o(secp, &mut rng);
|
||||||
let mut btx1 = tx1.blind(&secp).unwrap();
|
let mut btx1 = tx1.blind(&secp, None).unwrap();
|
||||||
|
|
||||||
let tx2 = tx1i1o(secp, &mut rng);
|
let tx2 = tx1i1o(secp, &mut rng);
|
||||||
let mut btx2 = tx2.blind(&secp).unwrap();
|
let mut btx2 = tx2.blind(&secp, None).unwrap();
|
||||||
|
|
||||||
// spending tx2
|
// spending tx2
|
||||||
let spending = txspend1i1o(secp, &mut rng, tx2.outputs[0], btx2.outputs[0].hash());
|
let spending = txspend1i1o(secp, &mut rng, tx2.outputs[0], btx2.outputs[0].hash());
|
||||||
let mut btx3 = spending.blind(&secp).unwrap();
|
let mut btx3 = spending.blind(&secp, None).unwrap();
|
||||||
let b = new_block(vec![&mut btx1, &mut btx2, &mut btx3], secp);
|
let b = new_block(vec![&mut btx1, &mut btx2, &mut btx3], secp);
|
||||||
|
|
||||||
// block should have been automatically compacted (including reward output) and
|
// block should have been automatically compacted (including reward output) and
|
||||||
|
@ -473,14 +473,14 @@ mod test {
|
||||||
let ref secp = new_secp();
|
let ref secp = new_secp();
|
||||||
|
|
||||||
let tx1 = tx2i1o(secp, &mut rng);
|
let tx1 = tx2i1o(secp, &mut rng);
|
||||||
let mut btx1 = tx1.blind(&secp).unwrap();
|
let mut btx1 = tx1.blind(&secp, None).unwrap();
|
||||||
|
|
||||||
let tx2 = tx1i1o(secp, &mut rng);
|
let tx2 = tx1i1o(secp, &mut rng);
|
||||||
let mut btx2 = tx2.blind(&secp).unwrap();
|
let mut btx2 = tx2.blind(&secp, None).unwrap();
|
||||||
|
|
||||||
// spending tx2
|
// spending tx2
|
||||||
let spending = txspend1i1o(secp, &mut rng, tx2.outputs[0], btx2.outputs[0].hash());
|
let spending = txspend1i1o(secp, &mut rng, tx2.outputs[0], btx2.outputs[0].hash());
|
||||||
let mut btx3 = spending.blind(&secp).unwrap();
|
let mut btx3 = spending.blind(&secp, None).unwrap();
|
||||||
|
|
||||||
let b1 = new_block(vec![&mut btx1, &mut btx2], secp);
|
let b1 = new_block(vec![&mut btx1, &mut btx2], secp);
|
||||||
b1.verify(&secp).unwrap();
|
b1.verify(&secp).unwrap();
|
||||||
|
|
|
@ -235,7 +235,7 @@ mod test {
|
||||||
}],
|
}],
|
||||||
9);
|
9);
|
||||||
// blinding should fail as signing with a zero r*G shouldn't work
|
// blinding should fail as signing with a zero r*G shouldn't work
|
||||||
tx.blind(&secp).unwrap();
|
tx.blind(&secp, None).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -255,7 +255,7 @@ mod test {
|
||||||
let skey = SecretKey::new(secp, &mut rng);
|
let skey = SecretKey::new(secp, &mut rng);
|
||||||
|
|
||||||
let tx1 = tx2i1o(secp, &mut rng);
|
let tx1 = tx2i1o(secp, &mut rng);
|
||||||
let mut btx1 = tx1.blind(&secp).unwrap();
|
let mut btx1 = tx1.blind(&secp, None).unwrap();
|
||||||
btx1.verify_sig(&secp).unwrap();
|
btx1.verify_sig(&secp).unwrap();
|
||||||
|
|
||||||
let b = Block::new(&BlockHeader::default(), vec![&mut btx1], skey).unwrap();
|
let b = Block::new(&BlockHeader::default(), vec![&mut btx1], skey).unwrap();
|
||||||
|
@ -269,11 +269,11 @@ mod test {
|
||||||
let skey = SecretKey::new(secp, &mut rng);
|
let skey = SecretKey::new(secp, &mut rng);
|
||||||
|
|
||||||
let tx1 = tx2i1o(secp, &mut rng);
|
let tx1 = tx2i1o(secp, &mut rng);
|
||||||
let mut btx1 = tx1.blind(&secp).unwrap();
|
let mut btx1 = tx1.blind(&secp, None).unwrap();
|
||||||
btx1.verify_sig(&secp).unwrap();
|
btx1.verify_sig(&secp).unwrap();
|
||||||
|
|
||||||
let tx2 = tx1i1o(secp, &mut rng);
|
let tx2 = tx1i1o(secp, &mut rng);
|
||||||
let mut btx2 = tx2.blind(&secp).unwrap();
|
let mut btx2 = tx2.blind(&secp, None).unwrap();
|
||||||
btx2.verify_sig(&secp).unwrap();
|
btx2.verify_sig(&secp).unwrap();
|
||||||
|
|
||||||
let b = Block::new(&BlockHeader::default(), vec![&mut btx1, &mut btx2], skey).unwrap();
|
let b = Block::new(&BlockHeader::default(), vec![&mut btx1, &mut btx2], skey).unwrap();
|
||||||
|
|
|
@ -165,6 +165,20 @@ impl Transaction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builds a new transaction with the provided outputs added. Existing
|
||||||
|
/// outputs, if any, are kept intact.
|
||||||
|
pub fn with_outputs(&self, outputs: &mut Vec<Output>) -> Transaction {
|
||||||
|
let mut new_outs = self.outputs.clone();
|
||||||
|
new_outs.append(outputs);
|
||||||
|
Transaction {
|
||||||
|
hash_mem: None,
|
||||||
|
fee: self.fee,
|
||||||
|
zerosig: vec![],
|
||||||
|
inputs: self.inputs.clone(),
|
||||||
|
outputs: new_outs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The hash of a transaction is the Merkle tree of its inputs and outputs
|
/// The hash of a transaction is the Merkle tree of its inputs and outputs
|
||||||
/// hashes. None of the rest is required.
|
/// hashes. None of the rest is required.
|
||||||
fn hash(&mut self) -> Hash {
|
fn hash(&mut self) -> Hash {
|
||||||
|
@ -178,9 +192,15 @@ impl Transaction {
|
||||||
/// algorithm: calculates the commitments for each inputs and outputs
|
/// algorithm: calculates the commitments for each inputs and outputs
|
||||||
/// using the values and blinding factors, takes the blinding factors
|
/// using the values and blinding factors, takes the blinding factors
|
||||||
/// remainder and uses it for an empty signature.
|
/// remainder and uses it for an empty signature.
|
||||||
pub fn blind(&self, secp: &Secp256k1) -> Result<Transaction, secp::Error> {
|
/// An excess value can optionally be provided to account for cases when the
|
||||||
|
/// transaction has already been partially blinded (when a recipient
|
||||||
|
/// receives a partially built transaction).
|
||||||
|
pub fn blind(&self,
|
||||||
|
secp: &Secp256k1,
|
||||||
|
excess: Option<SecretKey>)
|
||||||
|
-> Result<Transaction, secp::Error> {
|
||||||
// we compute the sum of blinding factors to get the k remainder
|
// we compute the sum of blinding factors to get the k remainder
|
||||||
let remainder = try!(self.blind_sum(secp));
|
let remainder = try!(self.blind_sum(secp, excess));
|
||||||
|
|
||||||
// next, blind the inputs and outputs if they haven't been yet
|
// next, blind the inputs and outputs if they haven't been yet
|
||||||
let blind_inputs = map_vec!(self.inputs, |inp| inp.blind(secp));
|
let blind_inputs = map_vec!(self.inputs, |inp| inp.blind(secp));
|
||||||
|
@ -200,11 +220,20 @@ impl Transaction {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the sum of blinding factors on all overt inputs and outputs
|
/// Compute the sum of blinding factors on all overt inputs and outputs of
|
||||||
/// of the transaction to get the k remainder.
|
/// the transaction to get the k remainder.
|
||||||
pub fn blind_sum(&self, secp: &Secp256k1) -> Result<SecretKey, secp::Error> {
|
/// An excess value can optionally be provided to account for cases when the
|
||||||
let inputs_blinding_fact = filter_map_vec!(self.inputs, |inp| inp.blinding_factor());
|
/// transaction has already been partially blinded (when a recipient
|
||||||
|
/// receives a partially built transaction).
|
||||||
|
pub fn blind_sum(&self,
|
||||||
|
secp: &Secp256k1,
|
||||||
|
excess: Option<SecretKey>)
|
||||||
|
-> Result<SecretKey, secp::Error> {
|
||||||
|
let mut inputs_blinding_fact = filter_map_vec!(self.inputs, |inp| inp.blinding_factor());
|
||||||
let outputs_blinding_fact = filter_map_vec!(self.outputs, |out| out.blinding_factor());
|
let outputs_blinding_fact = filter_map_vec!(self.outputs, |out| out.blinding_factor());
|
||||||
|
if let Some(exc) = excess {
|
||||||
|
inputs_blinding_fact.push(exc);
|
||||||
|
}
|
||||||
|
|
||||||
secp.blind_sum(inputs_blinding_fact, outputs_blinding_fact)
|
secp.blind_sum(inputs_blinding_fact, outputs_blinding_fact)
|
||||||
}
|
}
|
||||||
|
@ -231,6 +260,16 @@ impl Transaction {
|
||||||
fee: self.fee,
|
fee: self.fee,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validates all relevant parts of a fully built transaction. Checks the
|
||||||
|
/// excess value against the signature as well as range proofs for each
|
||||||
|
/// output.
|
||||||
|
pub fn validate(&self, secp: &Secp256k1) -> Result<TxProof, secp::Error> {
|
||||||
|
for out in &self.outputs {
|
||||||
|
out.verify_proof(secp)?;
|
||||||
|
}
|
||||||
|
self.verify_sig(secp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A transaction input, mostly a reference to an output being spent by the
|
/// A transaction input, mostly a reference to an output being spent by the
|
||||||
|
@ -410,7 +449,7 @@ mod test {
|
||||||
let ref secp = new_secp();
|
let ref secp = new_secp();
|
||||||
|
|
||||||
let tx = tx2i1o(secp, &mut rng);
|
let tx = tx2i1o(secp, &mut rng);
|
||||||
let btx = tx.blind(&secp).unwrap();
|
let btx = tx.blind(&secp, None).unwrap();
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
serialize(&mut vec, &btx).expect("serialized failed");
|
serialize(&mut vec, &btx).expect("serialized failed");
|
||||||
assert!(vec.len() > 5320);
|
assert!(vec.len() > 5320);
|
||||||
|
@ -423,7 +462,7 @@ mod test {
|
||||||
let ref secp = new_secp();
|
let ref secp = new_secp();
|
||||||
|
|
||||||
let tx = tx2i1o(secp, &mut rng);
|
let tx = tx2i1o(secp, &mut rng);
|
||||||
let btx = tx.blind(&secp).unwrap();
|
let btx = tx.blind(&secp, None).unwrap();
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
serialize(&mut vec, &btx).expect("serialization failed");
|
serialize(&mut vec, &btx).expect("serialization failed");
|
||||||
// let mut dtx = Transaction::read(&mut BinReader { source: &mut &vec[..]
|
// let mut dtx = Transaction::read(&mut BinReader { source: &mut &vec[..]
|
||||||
|
@ -442,7 +481,7 @@ mod test {
|
||||||
let ref secp = new_secp();
|
let ref secp = new_secp();
|
||||||
|
|
||||||
let tx = tx2i1o(secp, &mut rng);
|
let tx = tx2i1o(secp, &mut rng);
|
||||||
let btx = tx.blind(&secp).unwrap();
|
let btx = tx.blind(&secp, None).unwrap();
|
||||||
|
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
assert!(serialize(&mut vec, &btx).is_ok());
|
assert!(serialize(&mut vec, &btx).is_ok());
|
||||||
|
@ -520,7 +559,7 @@ mod test {
|
||||||
let mut rng = OsRng::new().unwrap();
|
let mut rng = OsRng::new().unwrap();
|
||||||
|
|
||||||
let tx = tx2i1o(secp, &mut rng);
|
let tx = tx2i1o(secp, &mut rng);
|
||||||
let btx = tx.blind(&secp).unwrap();
|
let btx = tx.blind(&secp, None).unwrap();
|
||||||
btx.verify_sig(&secp).unwrap(); // unwrap will panic if invalid
|
btx.verify_sig(&secp).unwrap(); // unwrap will panic if invalid
|
||||||
|
|
||||||
// checks that the range proof on our blind output is sufficiently hiding
|
// checks that the range proof on our blind output is sufficiently hiding
|
||||||
|
@ -537,13 +576,66 @@ mod test {
|
||||||
let mut rng = OsRng::new().unwrap();
|
let mut rng = OsRng::new().unwrap();
|
||||||
|
|
||||||
let tx1 = tx2i1o(secp, &mut rng);
|
let tx1 = tx2i1o(secp, &mut rng);
|
||||||
let btx1 = tx1.blind(&secp).unwrap();
|
let btx1 = tx1.blind(&secp, None).unwrap();
|
||||||
|
|
||||||
let tx2 = tx1i1o(secp, &mut rng);
|
let tx2 = tx1i1o(secp, &mut rng);
|
||||||
let btx2 = tx2.blind(&secp).unwrap();
|
let btx2 = tx2.blind(&secp, None).unwrap();
|
||||||
|
|
||||||
if btx1.hash() == btx2.hash() {
|
if btx1.hash() == btx2.hash() {
|
||||||
panic!("diff txs have same hash")
|
panic!("diff txs have same hash")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Simulate the standard exchange between 2 parties when creating a basic
|
||||||
|
/// 2 inputs, 2 outputs transaction.
|
||||||
|
#[test]
|
||||||
|
fn tx_build_exchange() {
|
||||||
|
let ref secp = new_secp();
|
||||||
|
let mut rng = OsRng::new().unwrap();
|
||||||
|
let outh = ZERO_HASH;
|
||||||
|
|
||||||
|
let tx_alice: Transaction;
|
||||||
|
let blind_sum: SecretKey;
|
||||||
|
|
||||||
|
{
|
||||||
|
// Alice gets 2 of her outputs to send 5 coins to Bob, they become
|
||||||
|
// inputs in the new transaction
|
||||||
|
let inputs = vec![Input::OvertInput {
|
||||||
|
// should match hash, value and blinding factor of output 1
|
||||||
|
output: outh,
|
||||||
|
value: 4,
|
||||||
|
blindkey: SecretKey::new(secp, &mut rng),
|
||||||
|
},
|
||||||
|
Input::OvertInput {
|
||||||
|
// should match hash, value and blinding factor of output 2
|
||||||
|
output: outh,
|
||||||
|
value: 3,
|
||||||
|
blindkey: SecretKey::new(secp, &mut rng),
|
||||||
|
}];
|
||||||
|
|
||||||
|
// Alice also builds her change (we assume fees of 1 coin, so 1 coin change
|
||||||
|
// left)
|
||||||
|
let kc = SecretKey::new(secp, &mut rng);
|
||||||
|
let change = Output::OvertOutput {
|
||||||
|
value: 1,
|
||||||
|
blindkey: kc,
|
||||||
|
};
|
||||||
|
|
||||||
|
// All of this gets into a resulting transaction, which we also use to get
|
||||||
|
// the sum of blinding factors, before we obscure the transaction itself.
|
||||||
|
let tx_open = Transaction::new(inputs, vec![change], 1);
|
||||||
|
blind_sum = tx_open.blind_sum(&secp, None).unwrap();
|
||||||
|
tx_alice = tx_open.blind(&secp, None).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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_full = tx_alice.with_outputs(&mut vec![Output::OvertOutput {
|
||||||
|
value: 5,
|
||||||
|
blindkey: SecretKey::new(secp, &mut rng),
|
||||||
|
}]);
|
||||||
|
let tx_final = tx_full.blind(&secp, Some(blind_sum)).unwrap();
|
||||||
|
tx_final.validate(&secp).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue