Merge pull request #237 from garious/hoist-lastid

Hoist last_id
This commit is contained in:
Greg Fitzgerald
2018-05-22 17:48:25 -06:00
committed by GitHub
5 changed files with 32 additions and 38 deletions

View File

@ -160,7 +160,7 @@ impl Bank {
/// Deduct tokens from the 'from' address the account has sufficient /// Deduct tokens from the 'from' address the account has sufficient
/// funds and isn't a duplicate. /// funds and isn't a duplicate.
pub fn process_verified_transaction_debits(&self, tr: &Transaction) -> Result<()> { pub fn process_verified_transaction_debits(&self, tr: &Transaction) -> Result<()> {
info!("Transaction {}", tr.data.tokens); info!("Transaction {}", tr.contract.tokens);
let bals = self.balances let bals = self.balances
.read() .read()
.expect("'balances' read lock in process_verified_transaction_debits"); .expect("'balances' read lock in process_verified_transaction_debits");
@ -170,7 +170,7 @@ impl Bank {
return Err(BankError::AccountNotFound); return Err(BankError::AccountNotFound);
} }
if !self.reserve_signature_with_last_id(&tr.sig, &tr.data.last_id) { if !self.reserve_signature_with_last_id(&tr.sig, &tr.last_id) {
return Err(BankError::InvalidTransferSignature); return Err(BankError::InvalidTransferSignature);
} }
@ -178,14 +178,14 @@ impl Bank {
let bal = option.expect("assignment of option to bal"); let bal = option.expect("assignment of option to bal");
let current = bal.load(Ordering::Relaxed) as i64; let current = bal.load(Ordering::Relaxed) as i64;
if current < tr.data.tokens { if current < tr.contract.tokens {
self.forget_signature_with_last_id(&tr.sig, &tr.data.last_id); self.forget_signature_with_last_id(&tr.sig, &tr.last_id);
return Err(BankError::InsufficientFunds); return Err(BankError::InsufficientFunds);
} }
let result = bal.compare_exchange( let result = bal.compare_exchange(
current as isize, current as isize,
(current - tr.data.tokens) as isize, (current - tr.contract.tokens) as isize,
Ordering::Relaxed, Ordering::Relaxed,
Ordering::Relaxed, Ordering::Relaxed,
); );
@ -201,7 +201,7 @@ impl Bank {
} }
pub fn process_verified_transaction_credits(&self, tr: &Transaction) { pub fn process_verified_transaction_credits(&self, tr: &Transaction) {
let mut plan = tr.data.plan.clone(); let mut plan = tr.contract.plan.clone();
plan.apply_witness(&Witness::Timestamp(*self.last_time plan.apply_witness(&Witness::Timestamp(*self.last_time
.read() .read()
.expect("timestamp creation in process_verified_transaction_credits"))); .expect("timestamp creation in process_verified_transaction_credits")));

View File

@ -88,7 +88,7 @@ fn main() {
// transfer to oneself. // transfer to oneself.
let entry1: Entry = entries.next().unwrap(); let entry1: Entry = entries.next().unwrap();
let deposit = if let Event::Transaction(ref tr) = entry1.events[0] { let deposit = if let Event::Transaction(ref tr) = entry1.events[0] {
tr.data.plan.final_payment() tr.contract.plan.final_payment()
} else { } else {
None None
}; };

View File

@ -76,7 +76,7 @@ mod tests {
fn test_create_events() { fn test_create_events() {
let mut events = Mint::new(100).create_events().into_iter(); let mut events = Mint::new(100).create_events().into_iter();
if let Event::Transaction(tr) = events.next().unwrap() { if let Event::Transaction(tr) = events.next().unwrap() {
if let Plan::Pay(payment) = tr.data.plan { if let Plan::Pay(payment) = tr.contract.plan {
assert_eq!(tr.from, payment.to); assert_eq!(tr.from, payment.to);
} }
} }

View File

@ -301,8 +301,8 @@ mod tests {
let last_id = client.get_last_id().wait().unwrap(); let last_id = client.get_last_id().wait().unwrap();
let mut tr2 = Transaction::new(&alice.keypair(), bob_pubkey, 501, last_id); let mut tr2 = Transaction::new(&alice.keypair(), bob_pubkey, 501, last_id);
tr2.data.tokens = 502; tr2.contract.tokens = 502;
tr2.data.plan = Plan::new_payment(502, bob_pubkey); tr2.contract.plan = Plan::new_payment(502, bob_pubkey);
let _sig = client.transfer_signed(tr2).unwrap(); let _sig = client.transfer_signed(tr2).unwrap();
let balance = poll_get_balance(&mut client, &bob_pubkey); let balance = poll_get_balance(&mut client, &bob_pubkey);

View File

@ -12,9 +12,8 @@ pub const SIG_OFFSET: usize = 8;
pub const PUB_KEY_OFFSET: usize = 80; pub const PUB_KEY_OFFSET: usize = 80;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct TransactionData { pub struct Contract {
pub tokens: i64, pub tokens: i64,
pub last_id: Hash,
pub plan: Plan, pub plan: Plan,
} }
@ -22,7 +21,8 @@ pub struct TransactionData {
pub struct Transaction { pub struct Transaction {
pub sig: Signature, pub sig: Signature,
pub from: PublicKey, pub from: PublicKey,
pub data: TransactionData, pub contract: Contract,
pub last_id: Hash,
} }
impl Transaction { impl Transaction {
@ -32,12 +32,9 @@ impl Transaction {
let plan = Plan::Pay(Payment { tokens, to }); let plan = Plan::Pay(Payment { tokens, to });
let mut tr = Transaction { let mut tr = Transaction {
sig: Signature::default(), sig: Signature::default(),
data: TransactionData { contract: Contract { plan, tokens },
plan, last_id,
tokens, from,
last_id,
},
from: from,
}; };
tr.sign(from_keypair); tr.sign(from_keypair);
tr tr
@ -57,12 +54,9 @@ impl Transaction {
(Condition::Signature(from), Payment { tokens, to: from }), (Condition::Signature(from), Payment { tokens, to: from }),
); );
let mut tr = Transaction { let mut tr = Transaction {
data: TransactionData { contract: Contract { plan, tokens },
plan, from,
tokens, last_id,
last_id,
},
from: from,
sig: Signature::default(), sig: Signature::default(),
}; };
tr.sign(from_keypair); tr.sign(from_keypair);
@ -70,7 +64,10 @@ impl Transaction {
} }
fn get_sign_data(&self) -> Vec<u8> { fn get_sign_data(&self) -> Vec<u8> {
serialize(&(&self.data)).expect("serialize TransactionData in fn get_sign_data") let mut data = serialize(&(&self.contract)).expect("serialize Contract");
let last_id_data = serialize(&(&self.last_id)).expect("serialize last_id");
data.extend_from_slice(&last_id_data);
data
} }
/// Sign this transaction. /// Sign this transaction.
@ -84,7 +81,7 @@ impl Transaction {
} }
pub fn verify_plan(&self) -> bool { pub fn verify_plan(&self) -> bool {
self.data.plan.verify(self.data.tokens) self.contract.plan.verify(self.contract.tokens)
} }
} }
@ -153,12 +150,9 @@ mod tests {
to: Default::default(), to: Default::default(),
}); });
let claim0 = Transaction { let claim0 = Transaction {
data: TransactionData { contract: Contract { plan, tokens: 0 },
plan,
tokens: 0,
last_id: Default::default(),
},
from: Default::default(), from: Default::default(),
last_id: Default::default(),
sig: Default::default(), sig: Default::default(),
}; };
let buf = serialize(&claim0).unwrap(); let buf = serialize(&claim0).unwrap();
@ -172,9 +166,9 @@ mod tests {
let keypair = KeyPair::new(); let keypair = KeyPair::new();
let pubkey = keypair.pubkey(); let pubkey = keypair.pubkey();
let mut tr = Transaction::new(&keypair, pubkey, 42, zero); let mut tr = Transaction::new(&keypair, pubkey, 42, zero);
tr.data.tokens = 1_000_000; // <-- attack, part 1! tr.contract.tokens = 1_000_000; // <-- attack, part 1!
if let Plan::Pay(ref mut payment) = tr.data.plan { if let Plan::Pay(ref mut payment) = tr.contract.plan {
payment.tokens = tr.data.tokens; // <-- attack, part 2! payment.tokens = tr.contract.tokens; // <-- attack, part 2!
}; };
assert!(tr.verify_plan()); assert!(tr.verify_plan());
assert!(!tr.verify_sig()); assert!(!tr.verify_sig());
@ -188,7 +182,7 @@ mod tests {
let pubkey1 = keypair1.pubkey(); let pubkey1 = keypair1.pubkey();
let zero = Hash::default(); let zero = Hash::default();
let mut tr = Transaction::new(&keypair0, pubkey1, 42, zero); let mut tr = Transaction::new(&keypair0, pubkey1, 42, zero);
if let Plan::Pay(ref mut payment) = tr.data.plan { if let Plan::Pay(ref mut payment) = tr.contract.plan {
payment.to = thief_keypair.pubkey(); // <-- attack! payment.to = thief_keypair.pubkey(); // <-- attack!
}; };
assert!(tr.verify_plan()); assert!(tr.verify_plan());
@ -210,13 +204,13 @@ mod tests {
let keypair1 = KeyPair::new(); let keypair1 = KeyPair::new();
let zero = Hash::default(); let zero = Hash::default();
let mut tr = Transaction::new(&keypair0, keypair1.pubkey(), 1, zero); let mut tr = Transaction::new(&keypair0, keypair1.pubkey(), 1, zero);
if let Plan::Pay(ref mut payment) = tr.data.plan { if let Plan::Pay(ref mut payment) = tr.contract.plan {
payment.tokens = 2; // <-- attack! payment.tokens = 2; // <-- attack!
} }
assert!(!tr.verify_plan()); assert!(!tr.verify_plan());
// Also, ensure all branchs of the plan spend all tokens // Also, ensure all branchs of the plan spend all tokens
if let Plan::Pay(ref mut payment) = tr.data.plan { if let Plan::Pay(ref mut payment) = tr.contract.plan {
payment.tokens = 0; // <-- whoops! payment.tokens = 0; // <-- whoops!
} }
assert!(!tr.verify_plan()); assert!(!tr.verify_plan());