Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

Handle event signatures consistently #251

Merged
merged 5 commits into from
May 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
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
86 changes: 49 additions & 37 deletions src/bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use std::collections::{HashMap, HashSet, VecDeque};
use std::result;
use std::sync::RwLock;
use std::sync::atomic::{AtomicIsize, AtomicUsize, Ordering};
use transaction::Transaction;
use transaction::{Instruction, Transaction};

pub const MAX_ENTRY_IDS: usize = 1024 * 4;

Expand Down Expand Up @@ -160,7 +160,9 @@ impl Bank {
/// Deduct tokens from the 'from' address the account has sufficient
/// funds and isn't a duplicate.
pub fn process_verified_transaction_debits(&self, tr: &Transaction) -> Result<()> {
info!("Transaction {}", tr.contract.tokens);
if let Instruction::NewContract(contract) = &tr.instruction {
info!("Transaction {}", contract.tokens);
}
let bals = self.balances
.read()
.expect("'balances' read lock in process_verified_transaction_debits");
Expand All @@ -175,20 +177,24 @@ impl Bank {
}

loop {
let bal = option.expect("assignment of option to bal");
let current = bal.load(Ordering::Relaxed) as i64;
let result = if let Instruction::NewContract(contract) = &tr.instruction {
let bal = option.expect("assignment of option to bal");
let current = bal.load(Ordering::Relaxed) as i64;

if current < tr.contract.tokens {
self.forget_signature_with_last_id(&tr.sig, &tr.last_id);
return Err(BankError::InsufficientFunds(tr.from));
}
if current < contract.tokens {
self.forget_signature_with_last_id(&tr.sig, &tr.last_id);
return Err(BankError::InsufficientFunds(tr.from));
}

let result = bal.compare_exchange(
current as isize,
(current - tr.contract.tokens) as isize,
Ordering::Relaxed,
Ordering::Relaxed,
);
bal.compare_exchange(
current as isize,
(current - contract.tokens) as isize,
Ordering::Relaxed,
Ordering::Relaxed,
)
} else {
Ok(0)
};

match result {
Ok(_) => {
Expand All @@ -201,18 +207,28 @@ impl Bank {
}

pub fn process_verified_transaction_credits(&self, tr: &Transaction) {
let mut plan = tr.contract.plan.clone();
plan.apply_witness(&Witness::Timestamp(*self.last_time
.read()
.expect("timestamp creation in process_verified_transaction_credits")));

if let Some(ref payment) = plan.final_payment() {
apply_payment(&self.balances, payment);
} else {
let mut pending = self.pending
.write()
.expect("'pending' write lock in process_verified_transaction_credits");
pending.insert(tr.sig, plan);
match &tr.instruction {
Instruction::NewContract(contract) => {
let mut plan = contract.plan.clone();
plan.apply_witness(&Witness::Timestamp(*self.last_time
.read()
.expect("timestamp creation in process_verified_transaction_credits")));

if let Some(ref payment) = plan.final_payment() {
apply_payment(&self.balances, payment);
} else {
let mut pending = self.pending
.write()
.expect("'pending' write lock in process_verified_transaction_credits");
pending.insert(tr.sig, plan);
}
}
Instruction::ApplyTimestamp(dt) => {
let _ = self.process_verified_timestamp(tr.from, *dt);
}
Instruction::ApplySignature(tx_sig) => {
let _ = self.process_verified_sig(tr.from, *tx_sig);
}
}
}

Expand Down Expand Up @@ -243,15 +259,13 @@ impl Bank {
}

fn partition_events(events: Vec<Event>) -> (Vec<Transaction>, Vec<Event>) {
let mut trs = vec![];
let mut rest = vec![];
for event in events {
match event {
Event::Transaction(tr) => trs.push(tr),
_ => rest.push(event),
}
}
(trs, rest)
(
events
.into_iter()
.map(|Event::Transaction(tr)| tr)
.collect(),
vec![],
)
}

pub fn process_verified_events(&self, events: Vec<Event>) -> Vec<Result<Event>> {
Expand Down Expand Up @@ -351,8 +365,6 @@ impl Bank {
pub fn process_verified_event(&self, event: Event) -> Result<Event> {
match event {
Event::Transaction(ref tr) => self.process_verified_transaction(tr),
Event::Signature { from, tx_sig, .. } => self.process_verified_sig(from, tx_sig),
Event::Timestamp { from, dt, .. } => self.process_verified_timestamp(from, dt),
}?;
Ok(event)
}
Expand Down
6 changes: 4 additions & 2 deletions src/bin/testnode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use solana::entry::Entry;
use solana::event::Event;
use solana::server::Server;
use solana::signature::{KeyPair, KeyPairUtil};
use solana::transaction::Instruction;
use std::env;
use std::fs::File;
use std::io::{stdin, stdout, Read};
Expand Down Expand Up @@ -96,8 +97,9 @@ fn main() {
// fields are the same. That entry should be treated as a deposit, not a
// transfer to oneself.
let entry1: Entry = entries.next().unwrap();
let deposit = if let Event::Transaction(ref tr) = entry1.events[0] {
tr.contract.plan.final_payment()
let Event::Transaction(ref tr) = entry1.events[0];
let deposit = if let Instruction::NewContract(contract) = &tr.instruction {
contract.plan.final_payment()
} else {
None
};
Expand Down
17 changes: 7 additions & 10 deletions src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,6 @@ fn add_event_data(hash_data: &mut Vec<u8>, event: &Event) {
hash_data.push(0u8);
hash_data.extend_from_slice(&tr.sig);
}
Event::Signature { ref sig, .. } => {
hash_data.push(1u8);
hash_data.extend_from_slice(sig);
}
Event::Timestamp { ref sig, .. } => {
hash_data.push(2u8);
hash_data.extend_from_slice(sig);
}
}
}

Expand Down Expand Up @@ -120,6 +112,7 @@ mod tests {
use event::Event;
use hash::hash;
use signature::{KeyPair, KeyPairUtil};
use transaction::Transaction;

#[test]
fn test_entry_verify() {
Expand Down Expand Up @@ -154,8 +147,12 @@ mod tests {

// First, verify entries
let keypair = KeyPair::new();
let tr0 = Event::new_timestamp(&keypair, Utc::now());
let tr1 = Event::new_signature(&keypair, Default::default());
let tr0 = Event::Transaction(Transaction::new_timestamp(&keypair, Utc::now(), zero));
let tr1 = Event::Transaction(Transaction::new_signature(
&keypair,
Default::default(),
zero,
));
let mut e0 = Entry::new(&zero, 0, vec![tr0.clone(), tr1.clone()]);
assert!(e0.verify(&zero));

Expand Down
52 changes: 1 addition & 51 deletions src/event.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,13 @@
//! The `event` module handles events, which may be a `Transaction`, or a `Witness` used to process a pending
//! Transaction.

use bincode::serialize;
use chrono::prelude::*;
use hash::Hash;
use signature::{KeyPair, KeyPairUtil, PublicKey, Signature, SignatureUtil};
use signature::{KeyPair, PublicKey};
use transaction::Transaction;

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum Event {
Transaction(Transaction),
Signature {
from: PublicKey,
tx_sig: Signature,
sig: Signature,
},
Timestamp {
from: PublicKey,
dt: DateTime<Utc>,
sig: Signature,
},
}

impl Event {
Expand All @@ -33,49 +21,11 @@ impl Event {
Event::Transaction(tr)
}

/// Create and sign a new Witness Timestamp. Used for unit-testing.
pub fn new_timestamp(from: &KeyPair, dt: DateTime<Utc>) -> Self {
let sign_data = serialize(&dt).expect("serialize 'dt' in pub fn new_timestamp");
let sig = Signature::clone_from_slice(from.sign(&sign_data).as_ref());
Event::Timestamp {
from: from.pubkey(),
dt,
sig,
}
}

/// Create and sign a new Witness Signature. Used for unit-testing.
pub fn new_signature(from: &KeyPair, tx_sig: Signature) -> Self {
let sig = Signature::clone_from_slice(from.sign(&tx_sig).as_ref());
Event::Signature {
from: from.pubkey(),
tx_sig,
sig,
}
}

/// Verify the Event's signature's are valid and if a transaction, that its
/// spending plan is valid.
pub fn verify(&self) -> bool {
match *self {
Event::Transaction(ref tr) => tr.verify_sig(),
Event::Signature { from, tx_sig, sig } => sig.verify(&from, &tx_sig),
Event::Timestamp { from, dt, sig } => sig.verify(
&from,
&serialize(&dt).expect("serialize 'dt' in pub fn verify"),
),
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use signature::{KeyPair, KeyPairUtil};

#[test]
fn test_event_verify() {
assert!(Event::new_timestamp(&KeyPair::new(), Utc::now()).verify());
assert!(Event::new_signature(&KeyPair::new(), Signature::default()).verify());
}
}
6 changes: 4 additions & 2 deletions src/mint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,14 @@ mod tests {
use super::*;
use ledger::Block;
use plan::Plan;
use transaction::Instruction;

#[test]
fn test_create_events() {
let mut events = Mint::new(100).create_events().into_iter();
if let Event::Transaction(tr) = events.next().unwrap() {
if let Plan::Pay(payment) = tr.contract.plan {
let Event::Transaction(tr) = events.next().unwrap();
if let Instruction::NewContract(contract) = tr.instruction {
if let Plan::Pay(payment) = contract.plan {
assert_eq!(tr.from, payment.to);
}
}
Expand Down
1 change: 1 addition & 0 deletions src/plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use chrono::prelude::*;
use signature::PublicKey;
use std::mem;

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum Witness {
Timestamp(DateTime<Utc>),
Signature(PublicKey),
Expand Down
7 changes: 5 additions & 2 deletions src/thin_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ mod tests {
use std::thread::sleep;
use std::time::Duration;
use streamer::default_window;
use transaction::Instruction;
use tvu::tests::TestNode;

#[test]
Expand Down Expand Up @@ -284,8 +285,10 @@ mod tests {
let last_id = client.get_last_id().wait().unwrap();

let mut tr2 = Transaction::new(&alice.keypair(), bob_pubkey, 501, last_id);
tr2.contract.tokens = 502;
tr2.contract.plan = Plan::new_payment(502, bob_pubkey);
if let Instruction::NewContract(contract) = &mut tr2.instruction {
contract.tokens = 502;
contract.plan = Plan::new_payment(502, bob_pubkey);
}
let _sig = client.transfer_signed(tr2).unwrap();

let balance = poll_get_balance(&mut client, &bob_pubkey);
Expand Down
Loading