Permalink
Browse files

Merge remote-tracking branch 'origin/master' into chain-storage

  • Loading branch information...
edolstra committed Jan 11, 2019
2 parents 9465500 + c3d0a2b commit 66c23b85c621cb6fe0bfb356ba97483df841b26a
@@ -259,7 +259,7 @@ impl<T: Serialize> Serialize for &T {
#[cfg(feature = "property-test-api")]
pub mod testing {
use super::*;
use quickcheck::{Arbitrary, Gen};
use quickcheck::{Arbitrary, Gen, TestResult};

/// test that any arbitrary given object can serialize and deserialize
/// back into itself (i.e. it is a bijection, or a one to one match
@@ -281,12 +281,15 @@ pub mod testing {
/// and signatures will compose into a valid transaction, but if such
/// event would happen it can be treated as error due to lack of the
/// randomness.
pub fn prop_bad_transaction_fails<L, T>(ledger: L, transaction: T) -> bool
pub fn prop_bad_transaction_fails<L, T>(ledger: L, transaction: T) -> TestResult
where
L: Ledger<T> + Arbitrary,
T: Transaction + Arbitrary,
{
ledger.diff_transaction(&transaction).is_err()
if transaction.inputs().len() == 0 && transaction.outputs().len() == 0 {
return TestResult::discard();
}
TestResult::from_bool(ledger.diff_transaction(&transaction).is_err())
}

/// Pair with a ledger and transaction that is valid in such state.
@@ -333,4 +336,20 @@ pub mod testing {
}
}

/// Checks that transaction id uniquely identifies the transaction,
/// i.e.
///
/// ```text
/// forall tx1, tx2:Transaction: tx1.id() == tx2.id() <=> tx1 == tx2
/// ```
pub fn transaction_id_is_unique<T>(tx1: T, tx2: T) -> bool
where
T: Transaction + Arbitrary + PartialEq,
T::Id: PartialEq,
{
let id1 = tx1.id();
let id2 = tx2.id();
(id1 == id2 && tx1 == tx2) || (id1 != id2 && tx1 != tx2)
}

}
@@ -53,7 +53,11 @@ impl std::fmt::Display for Error {
}
Error::InvalidSignature(_, _, _) => write!(f, "Input is not signed properly"),
Error::InvalidTxSignature(_) => write!(f, "Transaction was not signed"),
Error::TransactionSumIsNonZero(_, _) => write!(f, "Transaction sum is non zero"),
Error::TransactionSumIsNonZero(inp, out) => write!(
f,
"Transaction values do not match input is {}, output is {}",
inp, out
),
Error::NotEnoughSignatures(required, actual) => write!(
f,
"Transaction has not enough signatures: {} out of {}",
@@ -161,11 +161,16 @@ mod test {
}
}

fn make_key(u: u8) -> (PrivateKey, Address) {
let pk1 = PrivateKey::normalize_bytes([u; crypto::XPRV_SIZE]);
let user_address = Address::new(&pk1.public());
(pk1, user_address)
}

#[test]
pub fn tx_no_witness() -> () {
use chain_core::property::Ledger;
let pk1 = PrivateKey::normalize_bytes([0; crypto::XPRV_SIZE]);
let user1_address = Address::new(&pk1.public());
let (_pk1, user1_address) = make_key(0);
let tx0_id = TransactionId(Hash::hash_bytes(&[0]));
let utxo0 = UtxoPointer {
transaction_id: tx0_id,
@@ -195,26 +200,25 @@ mod test {
pub fn tx_wrong_witness() -> () {
use chain_core::property::Ledger;
use chain_core::property::Transaction;
let pk1 = PrivateKey::normalize_bytes([0; crypto::XPRV_SIZE]);
let user1_address = Address::new(&pk1.public());
let (_, user0_address) = make_key(0);
let tx0_id = TransactionId(Hash::hash_bytes(&[0]));
let utxo0 = UtxoPointer {
transaction_id: tx0_id,
output_index: 0,
};
let ledger = crate::ledger::Ledger::new(
vec![(utxo0, Output(user1_address, Value(1)))]
vec![(utxo0, Output(user0_address, Value(1)))]
.iter()
.cloned()
.collect(),
);
let output0 = Output(user1_address, Value(1));
let output0 = Output(user0_address, Value(1));
let tx = crate::transaction::Transaction {
inputs: vec![utxo0],
outputs: vec![output0],
};
let pk2 = PrivateKey::normalize_bytes([1; crypto::XPRV_SIZE]);
let witness = Witness::new(tx.id(), &pk2);
let (pk1, _) = make_key(1);
let witness = Witness::new(tx.id(), &pk1);
let signed_tx = SignedTransaction {
tx: tx,
witnesses: vec![witness.clone()],
@@ -225,4 +229,36 @@ mod test {
)
}

#[test]
fn cant_loose_money() {
use chain_core::property::Ledger;
use chain_core::property::Transaction;
let (pk1, user1_address) = make_key(0);
let tx0_id = TransactionId(Hash::hash_bytes(&[0]));
let utxo0 = UtxoPointer {
transaction_id: tx0_id,
output_index: 0,
};
let ledger = crate::ledger::Ledger::new(
vec![(utxo0, Output(user1_address, Value(10)))]
.iter()
.cloned()
.collect(),
);
let output0 = Output(user1_address, Value(9));
let tx = crate::transaction::Transaction {
inputs: vec![utxo0],
outputs: vec![output0],
};
let witness = Witness::new(tx.id(), &pk1);
let signed_tx = SignedTransaction {
tx: tx,
witnesses: vec![witness],
};
assert_eq!(
Err(Error::TransactionSumIsNonZero(10, 9)),
ledger.diff_transaction(&signed_tx)
)
}

}
@@ -19,10 +19,11 @@ mod tests {
use crate::ledger::Ledger;
use crate::transaction::SignedTransaction;
use chain_core::property::testing;
use quickcheck::TestResult;

quickcheck! {
/// Randomly generated transaction should fail.
fn prop_bad_tx_fails(l: Ledger, tx: SignedTransaction) -> bool {
fn prop_bad_tx_fails(l: Ledger, tx: SignedTransaction) -> TestResult {
testing::prop_bad_transaction_fails(l, tx)
}
}
@@ -161,6 +161,32 @@ mod test {

use quickcheck::{Arbitrary, Gen};

quickcheck! {

/// ```
/// \forall w=Witness(tx) => w.verifies(tx)
/// ```
fn prop_witness_verifies_own_tx(pk: PrivateKey, tx:TransactionId) -> bool {
let witness = Witness::new(tx, &pk);
witness.verifies(tx)
}

/// ```
/// \forall w1,w2:Witness, w1.verifies(t1), w2.verifies(t2):
/// w1.verifies(v2) <=> w1 == w2
/// ```
fn witness_verifies_only_own_tx(pk: PrivateKey, tx1: TransactionId, tx2: TransactionId) -> bool {
let witness1 = Witness::new(tx1, &pk);
let witness2 = Witness::new(tx2, &pk);
(witness1.verifies(tx2) && witness1 == witness2)
|| (!witness1.verifies(tx2))
}

fn transaction_id_is_unique(tx1: Transaction, tx2: Transaction) -> bool {
chain_core::property::testing::transaction_id_is_unique(tx1, tx2)
}
}

impl Arbitrary for Value {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
Value(Arbitrary::arbitrary(g))

0 comments on commit 66c23b8

Please sign in to comment.