Skip to content
Permalink
Browse files

split transaction and add features

  • Loading branch information...
vincenthz committed Mar 14, 2019
1 parent f735a4f commit c64502626290a74ff933d5d90b221c5058c81b0e
@@ -1,64 +1,26 @@
mod transaction;
mod transfer;
mod utxo;
mod witness;

use crate::value::*;
use chain_addr::Address;
use chain_core::property;

// to remove..
pub use transaction::*;
pub use transfer::*;
pub use utxo::*;

/// Transaction, transaction maps old unspent tokens into the
/// set of the new addresses.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Transaction<OutAddress> {
pub inputs: Vec<UtxoPointer>,
pub outputs: Vec<Output<OutAddress>>,
}
pub use witness::*;

/// Each transaction must be signed in order to be executed
/// by the ledger. `SignedTransaction` represents such a transaction.
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone)]
pub struct SignedTransaction<OutAddress> {
pub transaction: Transaction<OutAddress>,
pub witnesses: Vec<Witness>,
}

impl property::Serialize for Value {
type Error = std::io::Error;
fn serialize<W: std::io::Write>(&self, writer: W) -> Result<(), Self::Error> {
use chain_core::packer::*;
let mut codec = Codec::from(writer);
codec.put_u64(self.0)
}
}

impl property::Serialize for Transaction<Address> {
type Error = std::io::Error;

fn serialize<W: std::io::Write>(&self, writer: W) -> Result<(), Self::Error> {
use chain_core::packer::*;

let mut codec = Codec::from(writer);

// store the number of inputs and outputs
codec.put_u8(self.inputs.len() as u8)?;
codec.put_u8(self.outputs.len() as u8)?;

for input in self.inputs.iter() {
codec.put_u8(input.output_index)?;
input.transaction_id.serialize(&mut codec)?;
input.value.serialize(&mut codec)?;
}
for output in self.outputs.iter() {
output.0.serialize(&mut codec)?;
output.1.serialize(&mut codec)?;
}
Ok(())
}
}

impl property::Serialize for SignedTransaction<Address> {
type Error = std::io::Error;

@@ -97,20 +59,14 @@ impl property::Deserialize for Transaction<Address> {
};

for _ in 0..num_inputs {
let output_index = codec.get_u8()?;
let transaction_id = TransactionId::deserialize(&mut codec)?;
let value = Value::deserialize(&mut codec)?;
transaction.inputs.push(UtxoPointer {
transaction_id,
output_index,
value,
});
let input = Input::deserialize(&mut codec)?;
transaction.inputs.push(input);
}

for _ in 0..num_outputs {
let address = Address::deserialize(&mut codec)?;
let value = Value::deserialize(&mut codec)?;
transaction.outputs.push(Output(address, value));
transaction.outputs.push(Output { address, value });
}

Ok(transaction)
@@ -142,6 +98,7 @@ impl property::Deserialize for SignedTransaction<Address> {
}
}

/*
impl<OutAddress> property::Transaction for Transaction<OutAddress>
where
Transaction<OutAddress>: property::Serialize + property::Deserialize,
@@ -176,6 +133,7 @@ where
self.transaction.outputs()
}
}
*/

#[cfg(test)]
mod test {
@@ -186,9 +144,11 @@ mod test {
fn transaction_encode_decode(transaction: Transaction<Address>) -> TestResult {
chain_core::property::testing::serialization_bijection(transaction)
}
/*
fn signed_transaction_encode_decode(transaction: SignedTransaction<Address>) -> TestResult {
chain_core::property::testing::serialization_bijection(transaction)
}
*/
}

impl Arbitrary for Value {
@@ -207,9 +167,18 @@ mod test {
}
}

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

impl Arbitrary for Output<Address> {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
Output(Arbitrary::arbitrary(g), Arbitrary::arbitrary(g))
Output {
address: Arbitrary::arbitrary(g),
value: Arbitrary::arbitrary(g),
}
}
}

@@ -0,0 +1,71 @@
use super::transfer::*;
use crate::{key::Hash, value::Value};
use chain_addr::Address;
use chain_core::property;

// FIXME: should this be a wrapper type?
pub type TransactionId = Hash;

/// Transaction, transaction maps old unspent tokens into the
/// set of the new addresses.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Transaction<OutAddress> {
pub inputs: Vec<Input>,
pub outputs: Vec<Output<OutAddress>>,
}

impl property::Serialize for Value {
type Error = std::io::Error;
fn serialize<W: std::io::Write>(&self, writer: W) -> Result<(), Self::Error> {
use chain_core::packer::*;
let mut codec = Codec::from(writer);
codec.put_u64(self.0)
}
}

impl property::Serialize for Transaction<Address> {
type Error = std::io::Error;

fn serialize<W: std::io::Write>(&self, writer: W) -> Result<(), Self::Error> {
use chain_core::packer::*;

let mut codec = Codec::from(writer);

assert!(self.inputs.len() < 255);
assert!(self.outputs.len() < 255);

// store the number of inputs and outputs
codec.put_u8(self.inputs.len() as u8)?;
codec.put_u8(self.outputs.len() as u8)?;

for input in self.inputs.iter() {
input.serialize(&mut codec)?;
}
for output in self.outputs.iter() {
output.address.serialize(&mut codec)?;
output.value.serialize(&mut codec)?;
}
Ok(())
}
}

impl Transaction<Address> {
pub fn hash(&self) -> TransactionId {
use chain_core::packer::*;
use chain_core::property::Serialize;

let writer = Vec::new();
let mut codec = Codec::from(writer);
let bytes = {
for input in self.inputs.iter() {
input.serialize(&mut codec).unwrap();
}
for output in self.outputs.iter() {
output.address.serialize(&mut codec).unwrap();
output.value.serialize(&mut codec).unwrap();
}
codec.into_inner()
};
TransactionId::hash_bytes(&bytes)
}
}
@@ -1,3 +1,5 @@
use super::transaction::TransactionId;
use crate::account;
use crate::key::{
deserialize_signature, serialize_signature, Hash, SpendingPublicKey, SpendingSecretKey,
SpendingSignature,
@@ -6,9 +8,6 @@ use crate::value::*;
use chain_core::property;
use chain_crypto::Verification;

// FIXME: should this be a wrapper type?
pub type TransactionId = Hash;

pub type TransactionIndex = u8;

/// Unspent transaction pointer.
@@ -38,99 +37,3 @@ impl UtxoPointer {
}
}
}

/// Structure that proofs that certain user agrees with
/// some data. This structure is used to sign `Transaction`
/// and get `SignedTransaction` out.
///
/// It's important that witness works with opaque structures
/// and may not know the contents of the internal transaction.
#[derive(Debug, Clone)]
pub struct Witness(SpendingSignature<TransactionId>);

impl Witness {
/// Creates new `Witness` value.
pub fn new(transaction_id: &TransactionId, secret_key: &SpendingSecretKey) -> Self {
Witness(SpendingSignature::generate(secret_key, transaction_id))
}

/// Verify the given `TransactionId` using the witness.
pub fn verifies(
&self,
public_key: &SpendingPublicKey,
transaction_id: &TransactionId,
) -> Verification {
self.0.verify(public_key, transaction_id)
}
}

impl PartialEq<Self> for Witness {
fn eq(&self, other: &Self) -> bool {
self.0.as_ref() == other.0.as_ref()
}
}
impl Eq for Witness {}

impl property::Serialize for Witness {
type Error = std::io::Error;

fn serialize<W: std::io::Write>(&self, writer: W) -> Result<(), Self::Error> {
serialize_signature(&self.0, writer)
}
}

impl property::Deserialize for Witness {
type Error = std::io::Error;

fn deserialize<R: std::io::BufRead>(reader: R) -> Result<Self, Self::Error> {
deserialize_signature(reader).map(Witness)
}
}

#[cfg(test)]
pub mod test {
use super::*;
use quickcheck::{Arbitrary, Gen};

#[derive(Clone)]
pub struct TransactionSigningKey(pub SpendingSecretKey);

impl std::fmt::Debug for TransactionSigningKey {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "TransactionSigningKey(<secret-key>)")
}
}

impl Arbitrary for TransactionSigningKey {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
use rand_chacha::ChaChaRng;
use rand_core::SeedableRng;
let mut seed = [0; 32];
for byte in seed.iter_mut() {
*byte = Arbitrary::arbitrary(g);
}
let mut rng = ChaChaRng::from_seed(seed);
TransactionSigningKey(SpendingSecretKey::generate(&mut rng))
}
}

impl Arbitrary for Witness {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
let sk = TransactionSigningKey::arbitrary(g);
let txid = TransactionId::arbitrary(g);
Witness(SpendingSignature::generate(&sk.0, &txid))
}
}

quickcheck! {

/// ```
/// \forall w=Witness(tx) => w.verifies(tx)
/// ```
fn prop_witness_verifies_own_tx(sk: TransactionSigningKey, tx:TransactionId) -> bool {
let pk = sk.0.to_public();
let witness = Witness::new(&tx, &sk.0);
witness.verifies(&pk, &tx) == Verification::Success
}
}
}
Oops, something went wrong.

0 comments on commit c645026

Please sign in to comment.
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.