Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.
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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,4 @@ pnet_datalink = "0.21.0"
tokio = "0.1"
tokio-codec = "0.1"
tokio-io = "0.1"
itertools = "0.7.8"
74 changes: 69 additions & 5 deletions src/bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ extern crate libc;
use chrono::prelude::*;
use entry::Entry;
use hash::Hash;
use itertools::Itertools;
use ledger::Block;
use mint::Mint;
use payment_plan::{Payment, PaymentPlan, Witness};
use signature::{KeyPair, PublicKey, Signature};
Expand All @@ -28,6 +30,8 @@ use transaction::{Instruction, Plan, Transaction};
/// not be processed by the network.
pub const MAX_ENTRY_IDS: usize = 1024 * 16;

pub const VERIFY_BLOCK_SIZE: usize = 16;

/// Reasons a transaction might be rejected.
#[derive(Debug, PartialEq, Eq)]
pub enum BankError {
Expand All @@ -51,6 +55,9 @@ pub enum BankError {
/// The transaction is invalid and has requested a debit or credit of negative
/// tokens.
NegativeTokens,

/// Proof of History verification failed.
LedgerVerificationFailed,
}

pub type Result<T> = result::Result<T, BankError>;
Expand Down Expand Up @@ -89,18 +96,24 @@ pub struct Bank {
transaction_count: AtomicUsize,
}

impl Bank {
/// Create an Bank using a deposit.
pub fn new_from_deposit(deposit: &Payment) -> Self {
let bank = Bank {
impl Default for Bank {
fn default() -> Self {
Bank {
balances: RwLock::new(HashMap::new()),
pending: RwLock::new(HashMap::new()),
last_ids: RwLock::new(VecDeque::new()),
last_ids_sigs: RwLock::new(HashMap::new()),
time_sources: RwLock::new(HashSet::new()),
last_time: RwLock::new(Utc.timestamp(0, 0)),
transaction_count: AtomicUsize::new(0),
};
}
}
}

impl Bank {
/// Create an Bank using a deposit.
pub fn new_from_deposit(deposit: &Payment) -> Self {
let bank = Self::default();
bank.apply_payment(deposit, &mut bank.balances.write().unwrap());
bank
}
Expand Down Expand Up @@ -322,6 +335,57 @@ impl Bank {
Ok(entry_count)
}

/// Append entry blocks to the ledger, verifying them along the way.
pub fn process_blocks<I>(&self, entries: I) -> Result<u64>
where
I: IntoIterator<Item = Entry>,
{
// Ledger verification needs to be parallelized, but we can't pull the whole
// thing into memory. We therefore chunk it.
let mut entry_count = 0;
for block in &entries.into_iter().chunks(VERIFY_BLOCK_SIZE) {
let block: Vec<_> = block.collect();
if !block.verify(&self.last_id()) {
return Err(BankError::LedgerVerificationFailed);
}
entry_count += self.process_entries(block)?;
}
Ok(entry_count)
}

/// Process a full ledger.
pub fn process_ledger<I>(&self, entries: I) -> Result<u64>
where
I: IntoIterator<Item = Entry>,
{
let mut entries = entries.into_iter();

// The first item in the ledger is required to be an entry with zero num_hashes,
// which implies its id can be used as the ledger's seed.
let entry0 = entries.next().expect("invalid ledger: empty");

// The second item in the ledger is a special transaction where the to and from
// fields are the same. That entry should be treated as a deposit, not a
// transfer to oneself.
let entry1 = entries
.next()
.expect("invalid ledger: need at least 2 entries");
let tx = &entry1.transactions[0];
let deposit = if let Instruction::NewContract(contract) = &tx.instruction {
contract.plan.final_payment()
} else {
None
}.expect("invalid ledger, needs to start with a contract");

self.apply_payment(&deposit, &mut self.balances.write().unwrap());
self.register_entry_id(&entry0.id);
self.register_entry_id(&entry1.id);

let mut entry_count = 2;
entry_count += self.process_blocks(entries)?;
Ok(entry_count)
}

/// Process a Witness Signature. Any payment plans waiting on this signature
/// will progress one step.
fn apply_signature(&self, from: PublicKey, tx_sig: Signature) -> Result<()> {
Expand Down
32 changes: 5 additions & 27 deletions src/bin/fullnode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ use getopts::Options;
use solana::bank::Bank;
use solana::crdt::ReplicatedData;
use solana::entry::Entry;
use solana::payment_plan::PaymentPlan;
use solana::server::Server;
use solana::transaction::Instruction;
use std::env;
use std::fs::File;
use std::io::{stdin, stdout, BufRead, Write};
Expand Down Expand Up @@ -69,7 +67,7 @@ fn main() {

eprintln!("Initializing...");
let stdin = stdin();
let mut entries = stdin.lock().lines().map(|line| {
let entries = stdin.lock().lines().map(|line| {
let entry: Entry = serde_json::from_str(&line.unwrap()).unwrap_or_else(|e| {
eprintln!("failed to parse json: {}", e);
exit(1);
Expand All @@ -78,34 +76,14 @@ fn main() {
});
eprintln!("done parsing...");

// The first item in the ledger is required to be an entry with zero num_hashes,
// which implies its id can be used as the ledger's seed.
let entry0 = entries.next().expect("invalid ledger: empty");

// The second item in the ledger is a special transaction where the to and from
// fields are the same. That entry should be treated as a deposit, not a
// transfer to oneself.
let entry1 = entries
.next()
.expect("invalid ledger: need at least 2 entries");
let tx = &entry1.transactions[0];
let deposit = if let Instruction::NewContract(contract) = &tx.instruction {
contract.plan.final_payment()
} else {
None
}.expect("invalid ledger, needs to start with a contract");

eprintln!("creating bank...");

let bank = Bank::new_from_deposit(&deposit);
bank.register_entry_id(&entry0.id);
bank.register_entry_id(&entry1.id);
let bank = Bank::default();

// entry_height is the network-wide agreed height of the ledger.
// initialize it from the input ledger
eprintln!("processing entries...");
let entry_height = bank.process_entries(entries).expect("process_entries");
eprintln!("processed {} entries...", entry_height);
eprintln!("processing ledger...");
let entry_height = bank.process_ledger(entries).expect("process_ledger");
eprintln!("processed {} ledger...", entry_height);

eprintln!("creating networking stack...");

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ extern crate bincode;
extern crate byteorder;
extern crate chrono;
extern crate generic_array;
extern crate itertools;
extern crate libc;
#[macro_use]
extern crate log;
Expand Down