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

budget as separate contract and system call contract #1189

Merged
merged 4 commits into from
Sep 17, 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
434 changes: 133 additions & 301 deletions src/bank.rs

Large diffs are not rendered by default.

469 changes: 469 additions & 0 deletions src/budget_contract.rs

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/crdt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use bincode::{deserialize, serialize};
use choose_gossip_peer_strategy::{ChooseGossipPeerStrategy, ChooseWeightedPeerStrategy};
use counter::Counter;
use hash::Hash;
use instruction::Vote;
use ledger::LedgerWindow;
use log::Level;
use netutil::{bind_in_range, bind_to, multi_bind_in_range};
Expand All @@ -33,7 +34,6 @@ use std::thread::{sleep, Builder, JoinHandle};
use std::time::{Duration, Instant};
use streamer::{BlobReceiver, BlobSender};
use timing::{duration_as_ms, timestamp};
use transaction::Vote;
use window::{SharedWindow, WindowIndex};

pub const FULLNODE_PORT_RANGE: (u16, u16) = (8000, 10_000);
Expand Down Expand Up @@ -1327,6 +1327,7 @@ mod tests {
};
use entry::Entry;
use hash::{hash, Hash};
use instruction::Vote;
use ledger::{LedgerWindow, LedgerWriter};
use logger;
use packet::BlobRecycler;
Expand All @@ -1339,7 +1340,6 @@ mod tests {
use std::sync::{Arc, RwLock};
use std::thread::sleep;
use std::time::Duration;
use transaction::Vote;
use window::default_window;

#[test]
Expand Down
24 changes: 21 additions & 3 deletions src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,20 @@ mod tests {

// First, verify entries
let keypair = Keypair::new();
let tx0 = Transaction::new_timestamp(&keypair, keypair.pubkey(), Utc::now(), zero);
let tx1 = Transaction::new_signature(&keypair, keypair.pubkey(), Default::default(), zero);
let tx0 = Transaction::budget_new_timestamp(
&keypair,
keypair.pubkey(),
keypair.pubkey(),
Utc::now(),
zero,
);
let tx1 = Transaction::budget_new_signature(
&keypair,
keypair.pubkey(),
keypair.pubkey(),
Default::default(),
zero,
);
let mut e0 = Entry::new(&zero, 0, vec![tx0.clone(), tx1.clone()], false);
assert!(e0.verify(&zero));

Expand All @@ -272,7 +284,13 @@ mod tests {
assert_eq!(tick.id, zero);

let keypair = Keypair::new();
let tx0 = Transaction::new_timestamp(&keypair, keypair.pubkey(), Utc::now(), zero);
let tx0 = Transaction::budget_new_timestamp(
&keypair,
keypair.pubkey(),
keypair.pubkey(),
Utc::now(),
zero,
);
let entry0 = next_entry(&zero, 1, vec![tx0.clone()]);
assert_eq!(entry0.num_hashes, 1);
assert_eq!(entry0.id, next_hash(&zero, 1, &vec![tx0]));
Expand Down
68 changes: 68 additions & 0 deletions src/instruction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use budget::Budget;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instruction.rs is really just an implementation detail of budget, so (maybe later) this file could merge into budget.rs right?

use chrono::prelude::{DateTime, Utc};
use payment_plan::{Payment, PaymentPlan, Witness};
use signature::Pubkey;
use signature::Signature;

/// The type of payment plan. Each item must implement the PaymentPlan trait.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum Plan {
/// The builtin contract language Budget.
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, from: &Pubkey) {
match self {
Plan::Budget(budget) => budget.apply_witness(witness, from),
}
}
}

/// A smart contract.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Contract {
/// The number of tokens allocated to the `Plan` and any transaction fees.
pub tokens: i64,
pub plan: Plan,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Vote {
/// We send some gossip specific membership information through the vote to shortcut
/// liveness voting
/// The version of the CRDT struct that the last_id of this network voted with
pub version: u64,
/// The version of the CRDT struct that has the same network configuration as this one
pub contact_info_version: u64,
// TODO: add signature of the state here as well
}

/// An instruction to progress the smart contract.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum Instruction {
/// Declare and instanstansiate `Contract`.
NewContract(Contract),

/// Tell a payment plan acknowledge the given `DateTime` has past.
ApplyTimestamp(DateTime<Utc>),

/// Tell the payment plan that the `NewContract` with `Signature` has been
/// signed by the containing transaction's `Pubkey`.
ApplySignature(Signature),

/// Vote for a PoH that is equal to the lastid of this transaction
NewVote(Vote),
}
23 changes: 16 additions & 7 deletions src/ledger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use bincode::{self, deserialize, deserialize_from, serialize_into, serialized_size};
use entry::Entry;
use hash::Hash;
use instruction::Vote;
use log::Level::Trace;
use packet::{self, SharedBlob, BLOB_DATA_SIZE};
use rayon::prelude::*;
Expand All @@ -15,7 +16,7 @@ use std::io::prelude::*;
use std::io::{self, BufReader, BufWriter, Seek, SeekFrom};
use std::mem::size_of;
use std::path::Path;
use transaction::{Transaction, Vote};
use transaction::Transaction;
use window::WINDOW_SIZE;

//
Expand Down Expand Up @@ -548,11 +549,12 @@ mod tests {
use chrono::prelude::*;
use entry::{next_entry, Entry};
use hash::hash;
use instruction::Vote;
use packet::{BlobRecycler, BLOB_DATA_SIZE, PACKET_DATA_SIZE};
use signature::{Keypair, KeypairUtil};
use std;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use transaction::{Transaction, Vote};
use transaction::Transaction;

fn tmp_ledger_path(name: &str) -> String {
use std::env;
Expand Down Expand Up @@ -590,9 +592,10 @@ mod tests {
Entry::new_mut(
&mut id,
&mut num_hashes,
vec![Transaction::new_timestamp(
vec![Transaction::budget_new_timestamp(
&keypair,
keypair.pubkey(),
keypair.pubkey(),
Utc::now(),
one,
)],
Expand All @@ -605,7 +608,7 @@ mod tests {
let zero = Hash::default();
let one = hash(&zero.as_ref());
let keypair = Keypair::new();
let tx0 = Transaction::new_vote(
let tx0 = Transaction::budget_new_vote(
&keypair,
Vote {
version: 0,
Expand All @@ -614,7 +617,13 @@ mod tests {
one,
1,
);
let tx1 = Transaction::new_timestamp(&keypair, keypair.pubkey(), Utc::now(), one);
let tx1 = Transaction::budget_new_timestamp(
&keypair,
keypair.pubkey(),
keypair.pubkey(),
Utc::now(),
one,
);
//
// TODO: this magic number and the mix of transaction types
// is designed to fill up a Blob more or less exactly,
Expand Down Expand Up @@ -659,7 +668,7 @@ mod tests {
let id = Hash::default();
let next_id = hash(&id.as_ref());
let keypair = Keypair::new();
let tx_small = Transaction::new_vote(
let tx_small = Transaction::budget_new_vote(
&keypair,
Vote {
version: 0,
Expand All @@ -668,7 +677,7 @@ mod tests {
next_id,
2,
);
let tx_large = Transaction::new(&keypair, keypair.pubkey(), 1, next_id);
let tx_large = Transaction::budget_new(&keypair, keypair.pubkey(), 1, next_id);

let tx_small_size = serialized_size(&tx_small).unwrap() as usize;
let tx_large_size = serialized_size(&tx_large).unwrap() as usize;
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ pub mod broadcast_stage;
pub mod budget;
pub mod choose_gossip_peer_strategy;
pub mod client;
pub mod instruction;
#[macro_use]
pub mod crdt;
pub mod budget_contract;
pub mod drone;
pub mod entry;
pub mod entry_writer;
Expand Down Expand Up @@ -50,6 +52,7 @@ pub mod signature;
pub mod sigverify;
pub mod sigverify_stage;
pub mod streamer;
pub mod system_contract;
pub mod thin_client;
pub mod timing;
pub mod tpu;
Expand Down
6 changes: 3 additions & 3 deletions src/mint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ impl Mint {

pub fn create_transactions(&self) -> Vec<Transaction> {
let keypair = self.keypair();
let tx = Transaction::new(&keypair, self.pubkey(), self.tokens, self.seed());
let tx = Transaction::budget_new(&keypair, self.pubkey(), self.tokens, self.seed());
vec![tx]
}

Expand All @@ -67,14 +67,14 @@ impl Mint {
mod tests {
use super::*;
use budget::Budget;
use instruction::{Instruction, Plan};
use ledger::Block;
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 Some(Instruction::NewContract(contract)) = tx.instruction() {
if let Plan::Budget(Budget::Pay(payment)) = contract.plan {
assert_eq!(*tx.from(), payment.to);
}
Expand Down
2 changes: 1 addition & 1 deletion src/packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub type BlobRecycler = Recycler<Blob>;
pub const NUM_PACKETS: usize = 1024 * 8;
pub const BLOB_SIZE: usize = (64 * 1024 - 128); // wikipedia says there should be 20b for ipv4 headers
pub const BLOB_DATA_SIZE: usize = BLOB_SIZE - (BLOB_HEADER_SIZE * 2);
pub const PACKET_DATA_SIZE: usize = 256;
pub const PACKET_DATA_SIZE: usize = 512;
pub const NUM_BLOBS: usize = (NUM_PACKETS * PACKET_DATA_SIZE) / BLOB_SIZE;

#[derive(Clone, Default, Debug, PartialEq)]
Expand Down
2 changes: 1 addition & 1 deletion src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ mod tests {
let bank = Bank::new(&alice);

let last_id = bank.last_id();
let tx = Transaction::new(&alice.keypair(), bob_pubkey, 20, last_id);
let tx = Transaction::system_move(&alice.keypair(), bob_pubkey, 20, last_id, 0);
bank.process_transaction(&tx).expect("process transaction");

let request_processor = JsonRpcRequestProcessor::new(Arc::new(bank));
Expand Down
80 changes: 80 additions & 0 deletions src/system_contract.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//! system smart contract

use bank::Account;
use bincode::deserialize;
use signature::Pubkey;
use transaction::Transaction;

#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum SystemContract {
/// Create a new account
/// * Transaction::keys[0] - source
/// * Transaction::keys[1] - new account key
/// * tokens - number of tokens to transfer to the new account
/// * space - memory to allocate if greater then zero
/// * contract - the contract id of the new account
CreateAccount {
tokens: i64,
space: u64,
contract_id: Option<Pubkey>,
},
/// Assign account to a contract
/// * Transaction::keys[0] - account to assign
Assign { contract_id: Pubkey },
/// Move tokens
/// * Transaction::keys[0] - source
/// * Transaction::keys[1] - destination
Move { tokens: i64 },
}

pub const SYSTEM_CONTRACT_ID: [u8; 32] = [0u8; 32];

impl SystemContract {
pub fn check_id(contract_id: &Pubkey) -> bool {
contract_id.as_ref() == SYSTEM_CONTRACT_ID
}

pub fn id() -> Pubkey {
Pubkey::new(&SYSTEM_CONTRACT_ID)
}
pub fn get_balance(account: &Account) -> i64 {
account.tokens
}
pub fn process_transaction(tx: &Transaction, accounts: &mut [Account]) {
let syscall: SystemContract = deserialize(&tx.userdata).unwrap();
match syscall {
SystemContract::CreateAccount {
tokens,
space,
contract_id,
} => {
if !Self::check_id(&accounts[1].contract_id) {
return;
}
if !Self::check_id(&accounts[0].contract_id) {
return;
}
if space > 0 && !accounts[1].userdata.is_empty() {
return;
}
accounts[0].tokens -= tokens;
accounts[1].tokens += tokens;
if let Some(id) = contract_id {
accounts[1].contract_id = id;
}
accounts[1].userdata = vec![0; space as usize];
}
SystemContract::Assign { contract_id } => {
if !Self::check_id(&accounts[0].contract_id) {
return;
}
accounts[0].contract_id = contract_id;
}
SystemContract::Move { tokens } => {
//bank should be verifying correctness
accounts[0].tokens -= tokens;
accounts[1].tokens += tokens;
}
}
}
}
Loading