Skip to content

Commit

Permalink
Merge pull request #344 from input-output-hk/loki
Browse files Browse the repository at this point in the history
Planification of the leader selection per epoch
  • Loading branch information
vincenthz committed May 14, 2019
2 parents da1cb1d + 464e304 commit 6cb5cb3
Show file tree
Hide file tree
Showing 29 changed files with 950 additions and 377 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Expand Up @@ -33,7 +33,7 @@ cbor_event = "2.1.2"
cryptoxide = "0.1"
futures = "0.1"
http = "0.1.16"
tokio = "0.1"
tokio = "^0.1.16"
structopt = "^0.2"
generic-array = "^0.9"
bytes = "0.4"
Expand All @@ -48,6 +48,7 @@ chain-storage = { path = "cardano-deps/chain-storage" }
chain-storage-sqlite = { path = "cardano-deps/chain-storage-sqlite" }
chain-addr = { path = "cardano-deps/chain-addr" }
chain-crypto = { path = "cardano-deps/chain-crypto" }
chain-time = { path = "cardano-deps/chain-time" }
network-core = { path = "cardano-deps/network-core" }
network-grpc = { path = "cardano-deps/network-grpc" }
cardano = { path = "cardano-deps/cardano", features = [ "generic-serialization" ] }
Expand Down
2 changes: 1 addition & 1 deletion cardano-deps
1 change: 1 addition & 0 deletions src/bin/jcli_app/address.rs
Expand Up @@ -82,6 +82,7 @@ fn address_info(address: &AddressReadable) {
match kind {
Kind::Single(single) => println!("public key: {}", print_pub_key(single)),
Kind::Account(account) => println!("account: {}", print_pub_key(account)),
Kind::Multisig(_) => unimplemented!(),
Kind::Group(pubk, groupk) => {
println!("public key: {}", print_pub_key(pubk));
println!("group key: {}", print_pub_key(groupk));
Expand Down
5 changes: 3 additions & 2 deletions src/bin/jcli_app/transaction/add_account.rs
@@ -1,6 +1,6 @@
use chain_addr::{Address, Kind};
use chain_impl_mockchain::{
transaction::{Input, InputEnum},
transaction::{AccountIdentifier, Input, InputEnum},
value::Value,
};
use structopt::StructOpt;
Expand Down Expand Up @@ -39,9 +39,10 @@ impl AddAccount {
.map_err(|error| AddAccountError::ReadTransaction { error })?;

let account_identifier = match self.account.kind() {
Kind::Account(key) => key.clone().into(),
Kind::Account(key) => AccountIdentifier::from_single_account(key.clone().into()),
Kind::Single(_) => return Err(AddAccountError::InvalidAddressSingle),
Kind::Group(_, _) => return Err(AddAccountError::InvalidAddressGroup),
Kind::Multisig(_) => unimplemented!(),
};

transaction.add_input(Input::from_enum(InputEnum::AccountInput(
Expand Down
6 changes: 5 additions & 1 deletion src/bin/jcli_app/transaction/info.rs
Expand Up @@ -19,6 +19,7 @@ custom_error! {pub InfoError
FormatError { source: FmtError } = "Invalid format",
ReadTransaction { source: StagingError } = "cannot read the staging transaction",
ValueError { source: ValueError } = "Invalid values",
ExpectedSingleAccount = "Expected a single account (multisig not supported yet)",
}

#[derive(StructOpt)]
Expand Down Expand Up @@ -150,7 +151,10 @@ impl Info {
strfmt(&self.format_utxo_input, &vars)?
}
InputEnum::AccountInput(account, value) => {
let account: chain_crypto::PublicKey<_> = account.into();
let account: chain_crypto::PublicKey<_> = account
.to_single_account()
.ok_or(InfoError::ExpectedSingleAccount)?
.into();
vars.insert("account".to_owned(), account.to_string());
vars.insert("value".to_owned(), value.0.to_string());
strfmt(&self.format_account_input, &vars)?
Expand Down
54 changes: 54 additions & 0 deletions src/blockchain/branch.rs
@@ -0,0 +1,54 @@
use crate::blockcfg::{ChainLength, HeaderHash};
use chain_impl_mockchain::multiverse::GCRoot;
use std::sync::Arc;

/// `Branch` or `Fork` are the different _propositions_ of what is
/// the current state of the ledger
///
/// In some modes, like in BFT, it is very unlikely (near impossible)
/// to experiences competitive branches because the leadership is
/// deterministic and *absolute*: only one node is authorized to create
/// a new block at a time.
///
/// In other modes, like in genesis praos, the leadership is deterministic
/// (in the sense we can reproduce the result in the same circumstances)
/// but it is not necessarily *absolute*: it is possible that multiple nodes
/// are elected to propose the next block.
///
/// This Branch structure is useful to maintain states of different branches
/// as well as the consensus branch: the **tip**.
#[derive(Clone)]
pub struct Branch {
/// Make sure we hold the branch's details for as long as we need to
/// in the multiverse.
reference: Arc<GCRoot>,

/// keep the chain length details of the branch
///
/// This is a useful parameter to make choices regarding
/// competitive branch (for the consensus, the choice of the **tip**).
chain_length: ChainLength,
}

impl Branch {
/// create a new branch from the given GCRoot
#[inline]
pub fn new(reference: GCRoot, chain_length: ChainLength) -> Self {
Branch {
reference: Arc::new(reference),
chain_length,
}
}

/// get the branch latest block hash
#[inline]
pub fn hash(&self) -> HeaderHash {
**self.reference
}

/// get the branch latest block hash
#[inline]
pub fn chain_length(&self) -> &ChainLength {
&self.chain_length
}
}
108 changes: 95 additions & 13 deletions src/blockchain/chain.rs
@@ -1,16 +1,19 @@
use std::collections::BTreeMap;
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
use tokio::sync::mpsc;

use chain_core::property::{Block as _, HasHeader as _, HasMessages as _, Header as _};
use chain_impl_mockchain::{
leadership::{self, Verification},
ledger, multiverse,
};
use chain_storage::{error as storage, store::BlockInfo};
use chain_time::{SlotDuration, TimeFrame, Timeline};

use crate::{
blockcfg::{Block, Epoch, Header, HeaderHash, Ledger, Multiverse},
leadership::{Leadership, Leaderships},
blockchain::{Branch, Tip, TipGetError, TipReplaceError},
leadership::{EpochParameters, Leadership, Leaderships},
start_up::NodeStorage,
utils::borrow::Borrow,
};
Expand All @@ -23,7 +26,12 @@ pub struct Blockchain {

pub leaderships: Leaderships,

pub tip: multiverse::GCRoot,
/// the Tip of the blockchain. This is update as the consensus goes
pub tip: Tip,

pub time_frame: TimeFrame,

pub epoch_event: mpsc::Sender<EpochParameters>,

/// Incoming blocks whose parent does not exist yet. Sorted by
/// parent hash to allow quick look up of the children of a
Expand Down Expand Up @@ -75,12 +83,26 @@ pub const LOCAL_BLOCKCHAIN_TIP_TAG: &'static str = "tip";
custom_error! {pub LoadError
Storage{source: storage::Error} = "Error in the blockchain storage: {source}",
Ledger{source: ledger::Error} = "Invalid blockchain state: {source}",
Block0 { source: crate::blockcfg::Block0Error } = "Initial setting of the blockchain are invalid",
}

impl Blockchain {
pub fn load(block_0: Block, mut storage: NodeStorage) -> Result<Self, LoadError> {
pub fn load(
block_0: Block,
mut storage: NodeStorage,
epoch_event: mpsc::Sender<EpochParameters>,
) -> Result<Self, LoadError> {
use blockcfg::Block0DataSource as _;
let mut multiverse = multiverse::Multiverse::new();

let start_time = block_0.start_time()?;
let slot_duration = block_0.slot_duration()?;

let time_frame = TimeFrame::new(
Timeline::new(start_time),
SlotDuration::from_secs(slot_duration.as_secs() as u32),
);

let (tip, leaderships) =
if let Some(tip_hash) = storage.get_tag(LOCAL_BLOCKCHAIN_TIP_TAG)? {
info!("restoring state at tip {}", tip_hash);
Expand Down Expand Up @@ -108,7 +130,7 @@ impl Blockchain {
block.date(),
block.chain_length(),
)?;
tip = Some(multiverse.add(info.block_hash.clone(), state.clone()));
let gc_root = multiverse.add(info.block_hash.clone(), state.clone());
if block_header.date().epoch > epoch {
epoch = block_header.date().epoch;
let leadership = Leadership::new(block_header.date().epoch, &state);
Expand All @@ -119,6 +141,7 @@ impl Blockchain {
leadership,
);
}
tip = Some(Tip::new(Branch::new(gc_root, block_header.chain_length())));
}

(tip.unwrap(), leaderships)
Expand All @@ -128,6 +151,8 @@ impl Blockchain {
let initial_leadership = Leadership::new(block_0.date().epoch, &state);
let tip = multiverse.add(block_0.id(), state);
let leaderships = Leaderships::new(&block_0.header, initial_leadership);
let tip = Tip::new(Branch::new(tip, block_0.header.chain_length()));

(tip, leaderships)
};

Expand All @@ -139,30 +164,58 @@ impl Blockchain {
leaderships,
tip,
unconnected_blocks: BTreeMap::default(),
epoch_event,
time_frame,
})
}

pub fn initial(&mut self) -> Result<(), storage::Error> {
let (_block, block_info) = self.get_block_tip()?;
let state = self.get_ledger(&block_info.block_hash).unwrap().clone();
let slot = self
.time_frame
.slot_at(&std::time::SystemTime::now())
.unwrap();
let leadership = Leadership::new(0, &state);
let date = leadership.era().from_slot_to_era(slot).unwrap();

self.epoch_event
.try_send(EpochParameters {
epoch: date.epoch.0,

ledger_static_parameters: state.get_static_parameters().clone(),
ledger_parameters: state.get_ledger_parameters(),

time_frame: self.time_frame.clone(),
ledger_reference: state,
})
.unwrap_or_else(|_| ());
Ok(())
}

pub fn get_ledger(&self, hash: &HeaderHash) -> Option<&Ledger> {
self.multiverse.get(hash)
}

/// return the current tip hash and date
pub fn get_tip(&self) -> HeaderHash {
self.tip.clone()
pub fn get_tip(&self) -> Result<HeaderHash, TipGetError> {
self.tip.hash()
}

pub fn get_block_tip(&self) -> Result<(Block, BlockInfo<HeaderHash>), storage::Error> {
self.get_block(&self.tip)
self.get_block(&self.get_tip().unwrap())
}

pub fn put_block(&mut self, block: &Block) -> Result<(), storage::Error> {
self.storage.write().unwrap().put_block(block)
}

pub fn put_tip(&mut self, block: &Block) -> Result<(), storage::Error> {
pub fn put_tip(&mut self, branch: Branch, block: &Block) -> Result<(), HandleBlockError> {
let mut storage = self.storage.write().unwrap();
storage.put_block(block)?;
storage.put_tag(LOCAL_BLOCKCHAIN_TIP_TAG, &block.id())
storage.put_tag(LOCAL_BLOCKCHAIN_TIP_TAG, &block.id())?;
self.tip.replace_with(branch)?;
Ok(())
}

pub fn get_block(
Expand All @@ -188,7 +241,7 @@ impl Blockchain {
/// call returns None:
///
/// 1. there is no existing leadership for the given epoch;
/// 2. there is no existing ledget state available for the
/// 2. there is no existing ledger state available for the
/// given block
pub fn get_leadership_or_build<'a>(
&'a self,
Expand All @@ -215,6 +268,7 @@ impl Blockchain {
custom_error! {pub HandleBlockError
Storage{source: storage::Error} = "Error in the blockchain storage",
Ledger{source: ledger::Error} = "Invalid blockchain state",
InternalTip { source: TipReplaceError } = "Cannot update the blockchain's TIP",
}

pub enum HandledBlock {
Expand Down Expand Up @@ -254,6 +308,28 @@ pub enum BlockHeaderTriage {
ProcessBlockToState,
}

pub fn handle_end_of_epoch_event(blockchain: &Blockchain) -> Result<(), HandleBlockError> {
let (tip, tip_info) = blockchain.get_block_tip()?;
let state = blockchain.get_ledger(&tip_info.block_hash).unwrap();

// TODO: get the ledger state from 2 epochs ago

blockchain
.epoch_event
.clone() // clone it to get mutability
.try_send(EpochParameters {
epoch: tip.header().date().epoch + 1,

ledger_static_parameters: state.get_static_parameters().clone(),
ledger_parameters: state.get_ledger_parameters(),

time_frame: blockchain.time_frame.clone(),
ledger_reference: state.clone(),
})
.unwrap_or_else(|_| ());
Ok(())
}

pub fn handle_block(
blockchain: &mut Blockchain,
block: Block,
Expand Down Expand Up @@ -317,11 +393,17 @@ fn process_block(
// corresponding states, but to prevent a DoS, we may
// want to store only sufficiently long chains.

blockchain.put_tip(&block)?;
let new_chain_length = block.chain_length();
let tip = blockchain.multiverse.add(block.id(), state);

let branch = Branch::new(
blockchain.multiverse.add(block.id(), state),
new_chain_length,
);

if new_chain_length > tip_chain_length {
blockchain.tip = tip;
blockchain.put_tip(branch, &block)?;
} else {
blockchain.put_block(&block)?;
}

Ok(HandledBlock::Acquired {
Expand Down
4 changes: 4 additions & 0 deletions src/blockchain/mod.rs
@@ -1,7 +1,11 @@
mod branch;
mod chain;
mod process;
mod tip;

pub use self::branch::Branch;
pub use self::chain::{
handle_block, Blockchain, BlockchainR, HandleBlockError, HandledBlock, LoadError,
};
pub use self::process::handle_input;
pub use self::tip::{Tip, TipGetError, TipReplaceError};
8 changes: 6 additions & 2 deletions src/blockchain/process.rs
Expand Up @@ -27,6 +27,10 @@ pub fn handle_input(
let logger = info.logger().clone();

match bquery {
BlockMsg::LeadershipExpectEndOfEpoch => {
let blockchain = blockchain.lock_read();
chain::handle_end_of_epoch_event(&blockchain).unwrap()
}
BlockMsg::LeadershipBlock(block) => {
let mut blockchain = blockchain.lock_write();
match chain::handle_block(&mut blockchain, block, true).unwrap() {
Expand All @@ -52,7 +56,7 @@ pub fn handle_input(
slog_info!(logger,
"block added successfully to Node's blockchain";
"id" => header.id().to_string(),
"date" => format!("{}.{}", header.date().epoch, header.date().slot_id)
"date" => header.date().to_string()
);
slog_debug!(logger, "Header: {:?}", header);
network_propagate
Expand Down Expand Up @@ -81,7 +85,7 @@ pub fn handle_input(
BlockHeaderTriage::ProcessBlockToState => {
slog_info!(logger, "Block announcement is interesting, fetch block");
// TODO: signal back to the network that the block is interesting
// (get block/rquiest block)
// (get block/request block)
unimplemented!()
}
}
Expand Down

0 comments on commit 6cb5cb3

Please sign in to comment.