Allow for addtional smart contract languages

Fixes #159
This commit is contained in:
Greg Fitzgerald 2018-05-29 13:51:48 -06:00
parent f82cbf3a27
commit d2622b7798
4 changed files with 44 additions and 16 deletions

View File

@ -5,7 +5,6 @@
extern crate libc;
use budget::Budget;
use chrono::prelude::*;
use entry::Entry;
use hash::Hash;
@ -18,7 +17,7 @@ use std::collections::{HashMap, HashSet, VecDeque};
use std::result;
use std::sync::RwLock;
use std::sync::atomic::{AtomicIsize, AtomicUsize, Ordering};
use transaction::{Instruction, Transaction};
use transaction::{Instruction, Plan, Transaction};
pub const MAX_ENTRY_IDS: usize = 1024 * 4;
@ -33,7 +32,7 @@ pub type Result<T> = result::Result<T, BankError>;
pub struct Bank {
balances: RwLock<HashMap<PublicKey, AtomicIsize>>,
pending: RwLock<HashMap<Signature, Budget>>,
pending: RwLock<HashMap<Signature, Plan>>,
last_ids: RwLock<VecDeque<(Hash, RwLock<HashSet<Signature>>)>>,
time_sources: RwLock<HashSet<PublicKey>>,
last_time: RwLock<DateTime<Utc>>,

View File

@ -70,14 +70,14 @@ mod tests {
use super::*;
use budget::Budget;
use ledger::Block;
use transaction::Instruction;
use transaction::{Instruction, Plan};
#[test]
fn test_create_transactions() {
let mut transactions = Mint::new(100).create_transactions().into_iter();
let tx = transactions.next().unwrap();
if let Instruction::NewContract(contract) = tx.instruction {
if let Budget::Pay(payment) = contract.plan {
if let Plan::Budget(Budget::Pay(payment)) = contract.plan {
assert_eq!(tx.from, payment.to);
}
}

View File

@ -187,7 +187,7 @@ mod tests {
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::sleep;
use std::time::Duration;
use transaction::Instruction;
use transaction::{Instruction, Plan};
use tvu::TestNode;
#[test]
@ -282,7 +282,7 @@ mod tests {
let mut tr2 = Transaction::new(&alice.keypair(), bob_pubkey, 501, last_id);
if let Instruction::NewContract(contract) = &mut tr2.instruction {
contract.tokens = 502;
contract.plan = Budget::new_payment(502, bob_pubkey);
contract.plan = Plan::Budget(Budget::new_payment(502, bob_pubkey));
}
let _sig = client.transfer_signed(tr2).unwrap();

View File

@ -4,17 +4,43 @@ use bincode::serialize;
use budget::{Budget, Condition};
use chrono::prelude::*;
use hash::Hash;
use plan::{Payment, PaymentPlan};
use plan::{Payment, PaymentPlan, Witness};
use signature::{KeyPair, KeyPairUtil, PublicKey, Signature, SignatureUtil};
pub const SIGNED_DATA_OFFSET: usize = 112;
pub const SIG_OFFSET: usize = 8;
pub const PUB_KEY_OFFSET: usize = 80;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum Plan {
Budget(Budget),
}
// A proxy for the underlying DSL.
impl PaymentPlan for Plan {
fn final_payment(&self) -> Option<Payment> {
match self {
Plan::Budget(budget) => budget.final_payment(),
}
}
fn verify(&self, spendable_tokens: i64) -> bool {
match self {
Plan::Budget(budget) => budget.verify(spendable_tokens),
}
}
fn apply_witness(&mut self, witness: &Witness) {
match self {
Plan::Budget(budget) => budget.apply_witness(witness),
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Contract {
pub tokens: i64,
pub plan: Budget,
pub plan: Plan,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
@ -51,7 +77,8 @@ impl Transaction {
/// Create and sign a new Transaction. Used for unit-testing.
pub fn new(from_keypair: &KeyPair, to: PublicKey, tokens: i64, last_id: Hash) -> Self {
let plan = Budget::Pay(Payment { tokens, to });
let budget = Budget::Pay(Payment { tokens, to });
let plan = Plan::Budget(budget);
let instruction = Instruction::NewContract(Contract { plan, tokens });
Self::new_from_instruction(from_keypair, instruction, last_id)
}
@ -77,10 +104,11 @@ impl Transaction {
last_id: Hash,
) -> Self {
let from = from_keypair.pubkey();
let plan = Budget::Race(
let budget = Budget::Race(
(Condition::Timestamp(dt), Payment { tokens, to }),
(Condition::Signature(from), Payment { tokens, to: from }),
);
let plan = Plan::Budget(budget);
let instruction = Instruction::NewContract(Contract { plan, tokens });
let mut tx = Transaction {
instruction,
@ -163,10 +191,11 @@ mod tests {
#[test]
fn test_serialize_claim() {
let plan = Budget::Pay(Payment {
let budget = Budget::Pay(Payment {
tokens: 0,
to: Default::default(),
});
let plan = Plan::Budget(budget);
let instruction = Instruction::NewContract(Contract { plan, tokens: 0 });
let claim0 = Transaction {
instruction,
@ -187,7 +216,7 @@ mod tests {
let mut tx = Transaction::new(&keypair, pubkey, 42, zero);
if let Instruction::NewContract(contract) = &mut tx.instruction {
contract.tokens = 1_000_000; // <-- attack, part 1!
if let Budget::Pay(ref mut payment) = contract.plan {
if let Plan::Budget(Budget::Pay(ref mut payment)) = contract.plan {
payment.tokens = contract.tokens; // <-- attack, part 2!
}
}
@ -204,7 +233,7 @@ mod tests {
let zero = Hash::default();
let mut tx = Transaction::new(&keypair0, pubkey1, 42, zero);
if let Instruction::NewContract(contract) = &mut tx.instruction {
if let Budget::Pay(ref mut payment) = contract.plan {
if let Plan::Budget(Budget::Pay(ref mut payment)) = contract.plan {
payment.to = thief_keypair.pubkey(); // <-- attack!
}
}
@ -228,7 +257,7 @@ mod tests {
let zero = Hash::default();
let mut tx = Transaction::new(&keypair0, keypair1.pubkey(), 1, zero);
if let Instruction::NewContract(contract) = &mut tx.instruction {
if let Budget::Pay(ref mut payment) = contract.plan {
if let Plan::Budget(Budget::Pay(ref mut payment)) = contract.plan {
payment.tokens = 2; // <-- attack!
}
}
@ -236,7 +265,7 @@ mod tests {
// Also, ensure all branchs of the plan spend all tokens
if let Instruction::NewContract(contract) = &mut tx.instruction {
if let Budget::Pay(ref mut payment) = contract.plan {
if let Plan::Budget(Budget::Pay(ref mut payment)) = contract.plan {
payment.tokens = 0; // <-- whoops!
}
}