Kaynağa Gözat

connect stakeholder with p2p network

mohab metwally 4 yıl önce
ebeveyn
işleme
0d1d4e6c18

+ 16 - 7
bin/crypsinous/src/main.rs

@@ -1,8 +1,11 @@
 use darkfi::{
     stakeholder::Stakeholder,
-    blockchain::{EpochConsensus}
+    blockchain::{EpochConsensus,},
+    net::{Settings,},
 };
 
+use std::thread;
+
 fn main()
 {
     let slots=3;
@@ -12,13 +15,19 @@ fn main()
     let n = 3;
     /// initialize n stakeholders
     let stakeholders = vec!(n);
+    let settings = net::Settings::new();
+    //TODO populate settings with peers urls
+    let k : u32 = 13; //proof's number of rows
+    let handles = vec!(0);
     for i in n {
-        let stakeholder = Stakeholder::new();
+        let stakeholder = Stakeholder::new(epoch_consensus, settings, Some(k));
         stakeholders.push(stakeholder);
+        let handle = thread.spawn(|| {
+            stakeholders.background();
+        });
+        handles.push(handle);
+    }
+    for handle in handles {
+        handle.join().unwrap();
     }
-    /// when the clock signal a new slot.
-    /// check for leadership.
-    /// if lead publish construct block metadata.
-    /// push the new block before the end of the slot (clock should siganl the beging, and 1/k of the way to the end).
-    ///TODO stakeholder should signal new epoch, new slot in the background
 }

+ 37 - 31
src/blockchain/epoch.rs

@@ -1,6 +1,8 @@
 use halo2_proofs::{arithmetic::Field, dev::MockProver, circuit::Value};
 use incrementalmerkletree::{bridgetree::BridgeTree, Tree};
-use halo2_gadgets::poseidon::primitives as poseidon;
+use halo2_gadgets::{
+    poseidon::{primitives as poseidon},
+};
 
 use pasta_curves::{
     arithmetic::CurveAffine,
@@ -14,8 +16,10 @@ use crate::{
     crypto::{
         constants::MERKLE_DEPTH_ORCHARD,
         leadcoin::LeadCoin,
+        lead_proof,
+        proof::{Proof, ProvingKey, VerifyingKey},
         merkle_node::MerkleNode,
-        util::{mod_r_p, pedersen_commitment_scalar},
+        util::{mod_r_p, pedersen_commitment_scalar, pedersen_commitment_base, pedersen_commitment_u64},
         types::DrkValueBlind,
     },
 };
@@ -37,21 +41,21 @@ pub struct EpochConsensus {
 }
 
 impl EpochConsensus{
-    fn new(len: u64, reward: u64) {
+    fn new(len: u64, reward: u64) -> Self{
         Self {len, reward}
     }
 
     /// TODO how is the reward derived?
-    fn get_reward(&self) {
+    fn get_reward(&self)  -> u64{
         self.reward
     }
 
-    fn get_sl(&self) {
+    fn get_sl(&self)  -> u64{
         self.len
     }
 }
 
-#[derive(Copy,Debug,Default,Clone)]
+#[derive(Debug,Default,Clone)]
 pub struct Epoch {
     // TODO this need to emulate epoch
     // should have ep, slot, current block, etc.
@@ -60,16 +64,17 @@ pub struct Epoch {
     //epoch item
     pub item: Option<EpochItem>,
     pub eta: pallas::Base, // CRS for the leader selection.
-    pub coins: Vec<LeadCoin>,
+    pub coins: Vec<LeadCoin>, // competing coins
 }
 
 impl Epoch {
 
-    pub fn new(consensus: EpochConsensus, true_random:pallas::Base)
+    pub fn new(consensus: EpochConsensus, true_random:pallas::Base) -> Self
     {
-        Self {len: consensus.len,
-              item: EpochItem {consensus.reward},
+        Self {len: Some(consensus.len as usize),
+              item: Some(EpochItem {value: consensus.reward}),
               eta: true_random,
+              coins:vec!(),
         }
     }
     fn create_coins_election_seeds(&self, sl: pallas::Base) -> (pallas::Base, pallas::Base) {
@@ -106,13 +111,13 @@ impl Epoch {
         let mut prev_sk_base : pallas::Base = pallas::Base::one();
         for _i in 0..self.len.unwrap() {
             let sk_bytes = if _i ==0 {
-                let base = pedersen_commitment_scalar(pallas::Scalar::one(), pallas::Scalar::random(&mut rng));
+                let base = pedersen_commitment_u64(1, pallas::Scalar::random(&mut rng));
                 let coord = base.to_affine().coordinates().unwrap();
                 let sk_base =  coord.x() * coord.y();
                 prev_sk_base = sk_base;
                 sk_base.to_repr()
             } else {
-                let base = pedersen_commitment_scalar(pallas::Scalar::one(), mod_r_p(prev_sk_base));
+                let base = pedersen_commitment_u64(1, mod_r_p(prev_sk_base));
                 let coord = base.to_affine().coordinates().unwrap();
                 let sk_base =  coord.x() * coord.y();
                 prev_sk_base = sk_base;
@@ -134,7 +139,7 @@ impl Epoch {
         (root_sks, path_sks)
     }
     //note! the strategy here is single competing coin per slot.
-    pub fn create_coins(&self) -> Vec<LeadCoin> {
+    pub fn create_coins(& mut self) -> Vec<LeadCoin> {
         let mut rng = thread_rng();
         let mut seeds: Vec<u64> = vec![];
         for _i in 0..self.len.unwrap() {
@@ -162,10 +167,10 @@ impl Epoch {
             //
             let c_root_sk: MerkleNode = root_sks[i];
 
-            let c_pk = pedersen_commitment_scalar(mod_r_p(c_tau), mod_r_p(c_root_sk.inner()));
+            let c_pk = pedersen_commitment_base(c_tau, mod_r_p(c_root_sk.inner()));
 
             let c_seed = pallas::Base::from(seeds[i]);
-            let c_sn = pedersen_commitment_scalar(mod_r_p(c_seed), mod_r_p(c_root_sk.inner()));
+            let c_sn = pedersen_commitment_base(c_seed, mod_r_p(c_root_sk.inner()));
             let c_pk_pt = c_pk.to_affine().coordinates().unwrap();
             let c_pk_pt_x: pallas::Base = *c_pk_pt.x();
             let c_pk_pt_y: pallas::Base = *c_pk_pt.y();
@@ -184,7 +189,7 @@ impl Epoch {
             //TODO (FIX) THIS PANICS, ONLY PANICS ON LARGE VALUES!
             //let c_cm: pallas::Point = pedersen_commitment_scalar(lead_coin_msg_hash, c_cm1_blind);
             //note c_v is set to zero, should work
-            let c_cm: pallas::Point = pedersen_commitment_scalar(mod_r_p(c_v), c_cm1_blind);
+            let c_cm: pallas::Point = pedersen_commitment_base(c_v, c_cm1_blind);
 
             let c_cm_coordinates = c_cm.to_affine().coordinates().unwrap();
             let c_cm_base: pallas::Base = c_cm_coordinates.x() * c_cm_coordinates.y();
@@ -194,7 +199,7 @@ impl Epoch {
             let c_root_cm = tree_cm.root(0).unwrap();
             let c_cm_path = tree_cm.authentication_path(leaf_position.unwrap(), &c_root_cm).unwrap();
             // lead coin commitment
-            let c_seed2 = pedersen_commitment_scalar(mod_r_p(c_seed), mod_r_p(c_root_sk.inner()));
+            let c_seed2 = pedersen_commitment_base(c_seed, mod_r_p(c_root_sk.inner()));
             let c_seed2_pt = c_seed2.to_affine().coordinates().unwrap();
 
             let lead_coin_msg = [
@@ -205,7 +210,7 @@ impl Epoch {
                 pallas::Base::one(),
             ];
             let lead_coin_msg_hash : pallas::Base = poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<1>, 3, 2>::init().hash(lead_coin_msg);
-            let c_cm2 = pedersen_commitment_scalar(mod_r_p(lead_coin_msg_hash), c_cm2_blind);
+            let c_cm2 = pedersen_commitment_base(lead_coin_msg_hash, c_cm2_blind);
 
             let c_root_sk = root_sks[i];
 
@@ -243,34 +248,35 @@ impl Epoch {
             };
             coins.push(coin);
         }
-        self.coins = coins;
+        self.coins = coins.clone();
         coins
     }
 
     /// retrive leadership lottary coins of static stake,
     /// retrived for for commitment in the genesis data
     pub fn get_coins(&self) -> Vec<LeadCoin> {
-        return self.coins
+        return self.coins.clone()
     }
 
     /// see if the participant stakeholder of this epoch is
     /// winning the lottery, in case of success return True
     pub fn is_leader(&self, sl: u64) -> bool {
-        let coin = self.coins[i];
+        let coin = self.coins[sl as usize];
         let y_exp = [
-            coin.root_sk,
-            coin.nonce,
+            coin.root_sk.unwrap(),
+            coin.nonce.unwrap(),
         ];
-        let y_exp_hash : pallas::Base = PoseidonHash::<_, _, poseidon::P128Pow5T3, poseidon::ContractLength<2>,3,2,>::init(y_exp);
-        let y = pedersen_commitment_scalar(coin.y_mu, y_exp_hash);
-        let ord = 1024; //TODO (res)
-        let target = ord*coin.value;
-        y < target
+        let y_exp_hash : pallas::Base = poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>,3,2>::init().hash(y_exp);
+        // pick x coordiante of y for comparison
+        let y_x : pallas::Base = *pedersen_commitment_base(coin.y_mu.unwrap(), mod_r_p(y_exp_hash)).to_affine().coordinates().unwrap().x();
+        let ord = pallas::Base::from(1024); //TODO fine tune this scalar.
+        let target = ord*coin.value.unwrap();
+        y_x < target
     }
 
-    pub fn get_proof(&self, sl: u64) -> Proof {
-        let coin = self.coins[sl];
-        lead_proof::create_lead_proof(pk, coin)
+    pub fn get_proof(&self, sl: u64, pk: ProvingKey) -> Proof {
+        let coin = self.coins[sl as usize];
+        lead_proof::create_lead_proof(pk, coin).unwrap()
     }
 }
 

+ 1 - 1
src/blockchain/mod.rs

@@ -15,7 +15,7 @@ use crate::{
 };
 
 pub mod epoch;
-pub use epoch::{Epoch, EpochItem};
+pub use epoch::{Epoch, EpochItem,EpochConsensus};
 
 pub mod blockstore;
 pub use blockstore::{BlockOrderStore, BlockStore};

+ 27 - 6
src/consensus/block.rs

@@ -34,6 +34,12 @@ pub struct Block {
     pub metadata: Metadata,
 }
 
+impl net::Message for Block {
+    fn name() -> &'static str {
+        "block"
+    }
+}
+
 impl Block {
     pub fn new(
         st: blake3::Hash,
@@ -96,6 +102,27 @@ pub struct BlockInfo {
     pub sm: StreamletMetadata,
 }
 
+impl Default for BlockInfo {
+    fn default() -> Self {
+        Self {
+            v: 0,
+            st: blake3::hash(b""),
+            e: 0,
+            sl: 0,
+            txs: vec![],
+            metadata: Metadata::default(),
+            sm: StreamletMetadata::default(),
+        }
+    }
+}
+
+impl net::Message for BlockInfo {
+    fn name() -> &'static str {
+        "blockinfo"
+    }
+}
+
+
 impl BlockInfo {
     pub fn new(
         st: blake3::Hash,
@@ -123,12 +150,6 @@ impl From<BlockInfo> for Block {
     }
 }
 
-impl net::Message for BlockInfo {
-    fn name() -> &'static str {
-        "blockinfo"
-    }
-}
-
 impl_vec!(BlockInfo);
 
 /// Auxiliary structure used for blockchain syncing

+ 33 - 5
src/consensus/metadata.rs

@@ -28,6 +28,15 @@ pub struct Metadata {
     pub om: OuroborosMetadata,
 }
 
+impl Default for Metadata {
+    fn default() -> Self {
+        Self {
+            timestamp: Timestamp::current_time(),
+            om: OuroborosMetadata::default(),
+        }
+    }
+}
+
 impl Metadata {
     pub fn new(timestamp: Timestamp, eta: [u8;32], lead_proof: TransactionLeadProof) -> Self {
         Self { timestamp, om: OuroborosMetadata::new(eta, lead_proof) }
@@ -35,12 +44,21 @@ impl Metadata {
 }
 
 
+/// wrapper over the Proof, for possiblity any metadata necessary in the future.
 #[derive(Debug, Clone, PartialEq,  SerialEncodable, SerialDecodable)]
 pub struct TransactionLeadProof {
     /// leadership proof
     pub lead_proof: Proof,
 }
 
+impl Default for TransactionLeadProof {
+    fn default() -> Self {
+        Self {
+            lead_proof : Proof::default(),
+        }
+    }
+}
+
 impl TransactionLeadProof {
     pub fn new(pk : ProvingKey, coin: LeadCoin) -> Self
     {
@@ -54,14 +72,15 @@ impl TransactionLeadProof {
     }
 }
 
-impl Default for TransactionLeadProof {
-    fn default() -> Self
-    {
-        Self {lead_proof: Proof::new(vec!())}
+impl From<Proof> for TransactionLeadProof {
+    fn from(proof: Proof) -> Self {
+        Self { lead_proof: proof}
     }
 }
 
 
+
+
 /// This struct represents [`Block`](super::Block) information used by the Ouroboros
 /// Praos consensus protocol.
 #[derive(Debug, Clone, PartialEq, SerialEncodable, SerialDecodable)]
@@ -72,6 +91,15 @@ pub struct OuroborosMetadata {
     pub lead_proof : TransactionLeadProof,
 }
 
+impl Default for OuroborosMetadata {
+    fn default() -> Self {
+        Self {
+            eta: [0;32],
+            lead_proof: TransactionLeadProof::default(),
+        }
+    }
+}
+
 impl OuroborosMetadata {
     pub fn new(eta: [u8;32], lead_proof: TransactionLeadProof) -> Self {
         Self { eta, lead_proof }
@@ -80,7 +108,7 @@ impl OuroborosMetadata {
 
 /// This struct represents [`Block`](super::Block) information used by the Streamlet
 /// consensus protocol.
-#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
+#[derive(Debug, Clone, Default, SerialEncodable, SerialDecodable)]
 pub struct StreamletMetadata {
     /// Slot votes
     pub votes: Vec<Vote>,

+ 1 - 1
src/crypto/proof.rs

@@ -44,7 +44,7 @@ impl ProvingKey {
     }
 }
 
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Default, Debug, PartialEq)]
 pub struct Proof(Vec<u8>);
 
 impl AsRef<[u8]> for Proof {

+ 10 - 0
src/crypto/util.rs

@@ -18,6 +18,7 @@ pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> pallas::Scalar {
     pallas::Scalar::from_bytes_wide(ret.as_array())
 }
 
+
 #[allow(non_snake_case)]
 pub fn pedersen_commitment_scalar(value: pallas::Scalar, blind: DrkValueBlind) -> DrkValueCommit {
     let hasher = DrkValueCommit::hash_to_curve(VALUE_COMMITMENT_PERSONALIZATION);
@@ -31,6 +32,15 @@ pub fn pedersen_commitment_u64(value: u64, blind: DrkValueBlind) -> DrkValueComm
     pedersen_commitment_scalar(mod_r_p(DrkValue::from(value)), blind)
 }
 
+#[allow(non_snake_case)]
+pub fn pedersen_commitment_base(value: pallas::Base, blind: DrkValueBlind) -> DrkValueCommit {
+    let hasher = DrkValueCommit::hash_to_curve(VALUE_COMMITMENT_PERSONALIZATION);
+    let V = hasher(&VALUE_COMMITMENT_V_BYTES);
+    let R = hasher(&VALUE_COMMITMENT_R_BYTES);
+
+    V * mod_r_p(value) + R * blind
+}
+
 /// Converts from pallas::Base to pallas::Scalar (aka $x \pmod{r_\mathbb{P}}$).
 ///
 /// This requires no modular reduction because Pallas' base field is smaller than its

+ 6 - 0
src/error.rs

@@ -314,6 +314,11 @@ pub enum Error {
 
     #[error(transparent)]
     ClientFailed(#[from] ClientFailed),
+    //=============
+    // clock
+    //
+    #[error("clock out of sync with peers: {0}")]
+    ClockOutOfSync(String),
 }
 
 /// Transaction verification errors
@@ -351,6 +356,7 @@ pub enum VerifyFailed {
 
     #[error("Internal error: {0}")]
     InternalError(String),
+
 }
 
 /// Client module errors

+ 2 - 1
src/stakeholder/mod.rs

@@ -1,2 +1,3 @@
 pub mod stakeholder;
-pub use stakeholder::{Stakeholder};
+
+pub use stakeholder::Stakeholder;

+ 267 - 27
src/stakeholder/stakeholder.rs

@@ -1,14 +1,28 @@
-use crate::{
-    consensus::{BlockInfo},
-    util::time::Timestamp,
-    blockchain::{Blockchain,Epoch},
-    Result,
-};
+use async_executor::Executor;
+use async_trait::async_trait;
+use async_std::sync::Arc;
+use log::debug;
 
+use crate::zk::circuit::LeadContract;
 
-use darkfi::crypto::proof::VerifyingKey;
-use darkfi::crypto::proof::ProvingKey;
+use crate::{
+    consensus::{Block, BlockInfo,Metadata,StreamletMetadata,TransactionLeadProof},
+    util::{
+        time::Timestamp,
+        clock::{Clock,Ticks},
+    },
+    system::{Subscriber, SubscriberPtr, Subscription},
+    crypto::{
+        proof::{Proof, ProvingKey, VerifyingKey,  },
+        leadcoin::{LeadCoin},
+    },
+    blockchain::{Blockchain,Epoch,EpochConsensus},
+    net::{P2p,Settings, SettingsPtr, Channel, ChannelPtr, Hosts, HostsPtr,MessageSubscription},
+    tx::{Transaction},
+    Result,Error,
+};
 
+use url::Url;
 
 use pasta_curves::{
     pallas,
@@ -16,34 +30,163 @@ use pasta_curves::{
 
 use group::ff::PrimeField;
 
+#[derive(Debug)]
+pub struct SlotWorkspace
+{
+    pub st : blake3::Hash,
+    pub e: u64,
+    pub sl: u64,
+    pub txs: Vec<Transaction>,
+    pub metadata: Metadata,
+    pub is_leader: bool,
+    pub proof: Proof,
+    pub block: BlockInfo,
+}
+
+impl Default for SlotWorkspace {
+    fn default() -> Self {
+        Self {st: blake3::hash(b""),
+              e: 0,
+              sl: 0,
+              txs: vec![],
+              is_leader: false,
+              metadata: Metadata::default(),
+              proof: Proof::default(),
+              block: BlockInfo::default(),
+        }
+    }
+}
+
+impl SlotWorkspace {
+
+    pub fn new_block(&self) -> (BlockInfo, blake3::Hash) {
+        let sm = StreamletMetadata::new(vec!());
+        let block = BlockInfo::new(self.st, self.e, self.sl, self.txs.clone(), self.metadata.clone(), sm);
+        let hash = block.blockhash();
+        (block, hash)
+    }
+
+    pub fn add_tx(& mut self, tx: Transaction) {
+        self.txs.push(tx);
+    }
+
+    pub fn set_metadata(& mut self, md : Metadata) {
+        self.metadata = md;
+    }
+
+    pub fn set_sl(&mut self, sl: u64) {
+        self.sl = sl;
+    }
+
+    pub fn set_st(&mut self, st: blake3::Hash) {
+        self.st = st;
+    }
+
+
+    pub fn set_e(&mut self, e: u64) {
+        self.e = e;
+    }
+
+    pub fn set_proof(&mut self, proof: Proof) {
+        self.proof = proof;
+    }
+
+    pub fn set_leader(&mut self, alead : bool) {
+        self.is_leader = alead;
+    }
+}
+
 pub struct Stakeholder
 {
     pub blockchain: Blockchain, // stakeholder view of the blockchain
+    pub net : Arc<P2p>,
     pub clock : Clock,
     pub coins : Vec<LeadCoin>, // owned stakes
-    pub epoch : &Epoch, // current epoch
+    pub epoch : Epoch, // current epoch
     pub epoch_consensus : EpochConsensus, // configuration for the epoch
     pub pk : ProvingKey,
     pub vk : VerifyingKey,
+    pub playing: bool,
+    pub workspace : SlotWorkspace,
+    pub subscription: Subscription<Result<ChannelPtr>>,
+    pub chanptr : ChannelPtr,
+    pub msgsub : MessageSubscription::<BlockInfo>,
 }
 
 impl Stakeholder
 {
-    /// initialize new stakeholder with sled in /tmp
-    pub fn new(consensus: EpochConsensus) -> Result<Self>
+    pub async fn new(consensus: EpochConsensus, settings: Settings, k: Option<u32>) -> Result<Self>
     {
-        //TODO initialize the blockchain
         let path = "/tmp";
         let db = sled::open(path).unwrap();
         let ts = Timestamp::current_time();
         let genesis_hash = blake3::hash(b"");
+        //TODO lisen and add transactions
         let bc = Blockchain::new(&db, ts, genesis_hash).unwrap();
         //TODO replace with const
-        let eta = pallas::base::one();
-        //let epoch = Epoch::new(consensus, eta);
-        let lead_pk = ProvingKey::build(k, &LeadContract::default());
-        let lead_vk = VerifyingKey::build(k, &LeadContract::default());
-        Ok(Self{blockchain: bc, epoch_consensus: consensus, pk:lead_pk, vk:lead_vk})
+        let eta = pallas::Base::one();
+        let epoch = Epoch::new(consensus, eta);
+        let lead_pk = ProvingKey::build(k.unwrap(), &LeadContract::default());
+        let lead_vk = VerifyingKey::build(k.unwrap(), &LeadContract::default());
+        let p2p = P2p::new(settings.clone()).await;
+        //TODO
+        let workspace = SlotWorkspace::default();
+        let subscription : Subscription<Result<ChannelPtr>> = p2p.subscribe_channel().await;
+        let chanptr : ChannelPtr =  subscription.receive().await.unwrap();
+        //
+        let message_subsytem = chanptr.get_message_subsystem();
+        message_subsytem.add_dispatch::<BlockInfo>().await;
+        //TODO start channel if isn't started yet
+        //let info = chanptr.get_info();
+        //println!("channel info: {}", info);
+        let msg_sub : MessageSubscription::<BlockInfo> =
+            chanptr.subscribe_msg::<BlockInfo>().await.expect("missing blockinfo");
+        //
+        let clock = Clock::new(Some(3), Some(22), Some(22), settings.peers);
+        Ok(Self{blockchain: bc,
+                net: p2p,
+                clock: clock,
+                coins: vec![], //constructed with empty coins for sake of simulation only
+                // but should be populated from wallet db.
+                epoch: epoch,
+                epoch_consensus: consensus,
+                pk:lead_pk,
+                vk:lead_vk,
+                playing: true,
+                workspace: workspace,
+                subscription: subscription,
+                chanptr: chanptr,
+                msgsub: msg_sub,
+        })
+    }
+
+    /// get list stakeholder peers on the p2p network for synchronization
+    pub fn get_peers(&self) -> Vec<Url> {
+        let settings : SettingsPtr = self.net.settings();
+        settings.peers.clone()
+    }
+
+    /*
+    fn  new_block(&self) {
+        //TODO initialize blocks in the epoch, and add coin commitment in genesis
+        let block_info = BlockInfo::new(st, e, sl, txs, metadata, sm);
+        self.block = block_info;
+    }
+    */
+
+    fn init_network(&self) -> Result<()>{
+        //TODO initialize exectutor
+        //let exec = Arc<Executor<'_>>;
+        let exec = Arc::new(Executor::new());
+        exec.run(self.net.clone().start(exec.clone()));
+        //self.net(exec);
+
+        Ok(())
+    }
+
+    pub fn get_net(&self) -> Arc<P2p> {
+        //TODO use P2p ptr not to overwrite wrappers
+        self.net.clone()
     }
 
     /// add new blockinfo to the blockchain
@@ -53,6 +196,11 @@ impl Stakeholder
         self.blockchain.add(&blocks);
     }
 
+    pub fn add_tx(&mut self, tx: Transaction)
+    {
+        self.workspace.add_tx(tx);
+    }
+
     /// extract leader selection lottery randomness \eta
     /// it's the hash of the previous lead proof
     /// converted to pallas base
@@ -67,31 +215,123 @@ impl Stakeholder
         pallas::Base::from_repr(bytes).unwrap()
     }
 
+    pub fn valid_block(&self, blk : BlockInfo)  -> bool {
+        //TODO implement
+        true
+    }
+
+    /// listen to the network,
+    /// for new transactions.
+    pub fn sync_tx (&self) {
+        //TODO
+    }
+
+    /// listen to the network channels,
+    /// receive new messages, or blocks,
+    /// validate the block proof, and the transactions,
+    /// if so add the proof to metadata if stakeholder isn't the lead.
+    pub async fn sync_block(&self) {
+        let res = self.msgsub.receive().await.unwrap();
+        let blk : BlockInfo = (*res).to_owned();
+        //TODO validate the block proof, and transactions.
+        if self.valid_block(blk.clone())  {
+            //TODO if valid only.
+            self.blockchain.add(&[blk.clone()]);
+        } else {
+            debug!("received block is invalid!");
+        }
+    }
+
+    pub async fn background(&mut self) {
+        while self.playing {
+            // clock ticks slot begins
+            // initialize the epoch if it's the time
+            // check for leadership
+            match self.clock.ticks().await {
+                Ticks::GENESIS{e, sl} => {
+                    //TODO (res) any initialization happening here?
+                    self.new_epoch();
+                    self.new_slot(e, sl);
+                }
+                Ticks::NEWEPOCH{e, sl} => {
+                    self.new_epoch();
+                    self.new_slot(e, sl);
+                }
+                Ticks::NEWSLOT{e, sl} => self.new_slot(e, sl),
+                Ticks::TOCKS => {
+                    // slot is about to end.
+                    // sync, and validate.
+                    // no more transactions to be received/send to the end of slot.
+                    if self.workspace.is_leader {
+                        //craete block
+                        let (block_info, block_hash) = self.workspace.new_block();
+                        //add the block to the blockchain
+                        self.add_block(block_info.clone());
+                        let block : Block = Block::from(block_info.clone());
+                        // publish the block.
+                        self.net.broadcast(block);
+                    } else {
+                        //
+                        self.sync_block();
+                    }
+                },
+                Ticks::IDLE => {
+                    continue
+                }
+                Ticks::OUTOFSYNC => {
+                    // clock, and blockchain are out of sync
+                    self.clock.sync().await;
+                    self.sync_block();
+                }
+            }
+        }
+    }
+
     /// on the onset of the epoch, layout the new the competing coins
     /// assuming static stake during the epoch, enforced by the commitment to competing coins
     /// in the epoch's gen2esis data.
-    pub fn new_epoch(&self)
+    fn new_epoch(&mut self)
     {
         let eta = self.get_eta();
-        let epoch = Epoch::new(self.consensus, self.get_eta());
-        let coins : Vec<LeadCoin> = epoch.create_coins();
-        self.epoch = epoch;
-        //TODO initialize blocks in the epoch, and add coin commitment in genesis
+        let mut epoch = Epoch::new(self.epoch_consensus, self.get_eta());
+        epoch.create_coins(); // set epoch interal fields working space with competing coins
+        self.epoch = epoch.clone();
     }
 
+
     /// at the begining of the slot
     /// stakeholder need to play the lottery for the slot.
     /// FIXME if the stakeholder is not winning, staker can try different coins before,
     /// commiting it's coins, to maximize success, thus,
     /// the lottery proof need to be conditioned on the slot itself, and previous proof.
     /// this will encourage each potential leader to play with honesty.
-    pub fn new_slot(&self) -> Result<bool, Proof>
+    fn new_slot(&mut self, e: u64, sl: u64)
     {
-        let sl : u64 = self.clock.slot();
+        let EMPTY_PTR = blake3::hash(b"");
+        let st : blake3::Hash = if e>0 || (e==0&&sl>0) {
+            self.workspace.block.blockhash()
+        } else {
+            EMPTY_PTR
+        };
         let is_leader : bool = self.epoch.is_leader(sl);
         // if is leader create proof
-        let proof = self.epoch.get_proof(sl, self.pk);
-        //TODO initialize blocks in the epoch, and add proof
-        Ok(is_leader, proof)
+        let proof = if is_leader {
+            self.epoch.get_proof(sl, self.pk.clone())
+        } else {
+            Proof::new(vec![])
+        };
+        // set workspace
+        self.workspace.set_sl(sl);
+        self.workspace.set_e(e);
+        self.workspace.set_st(st);
+        self.workspace.set_leader(is_leader);
+        self.workspace.set_proof(proof.clone());
+        //
+        if is_leader {
+            let metadata = Metadata::new(Timestamp::current_time(),
+                                         self.get_eta().to_repr(),
+                                         TransactionLeadProof::from(proof.clone()));
+            self.workspace.set_metadata(metadata);
+        }
     }
 }

+ 60 - 62
src/util/clock.rs

@@ -1,118 +1,117 @@
 use url::Url;
-
+use log::debug;
+use async_trait::async_trait;
 use crate::{
-    util::time,
+    util::{time,Timestamp},
+    error,
+    Result,
+    error::Error
 };
 
-enum Ticks {
-    GENESIS, //genesis epoch
-    NEWSLOT{e: u8, sl: u8}, // new slot
-    NEWEPOCH{e: u8, sl: u8}, // new epoch
+pub enum Ticks {
+    GENESIS{e: u64, sl: u64}, //genesis epoch
+    NEWSLOT{e: u64, sl: u64}, // new slot
+    NEWEPOCH{e: u64, sl: u64}, // new epoch
     TOCKS, //tocks, or slot is ending
     IDLE, // idle clock state
     OUTOFSYNC, //clock, and blockchain are out of sync
 }
-const BB_SL : i64 = -1; //big bang slot time (need to be negative value)
-const BB_E : i64 = -1; //big bang epoch time.
+
+const BB_SL : u64 = u64::MAX; //big bang slot time (need to be negative value)
+const BB_E : u64 = 0; //big bang epoch time.
+const GENESIS_TIME : i64 = 0;
 
 #[derive(Debug)]
 pub struct Clock {
-    pub sl : i64, // relative slot index (zero-based) [0-len[
-    pub e : i64, //epoch index (zero-based) [0-\inf[
-    pub tick_len: u8, // tick length in time
-    pub sl_len: u8, // slot length in ticks
-    pub e_len: u8, // epoch length in slots
+    pub sl : u64, // relative slot index (zero-based) [0-len[
+    pub e : u64, //epoch index (zero-based) [0-\inf[
+    pub tick_len: u64, // tick length in time
+    pub sl_len: u64, // slot length in ticks
+    pub e_len: u64, // epoch length in slots
     pub peers: Vec<Url>,
-
 }
 
 impl Clock {
-    pub fn new(e_len: Option<u8>, sl_len: Option<u8>, tick_len: Option<u8>, peers: Vec<Url>) {
+    pub fn new(e_len: Option<u64>, sl_len: Option<u64>, tick_len: Option<u64>, peers: Vec<Url>) -> Self{
         Self { sl: BB_SL, //necessary for genesis slot
                e: BB_E,
                tick_len: tick_len.unwrap_or(22), // 22 seconds
                sl_len: sl_len.unwrap_or(22),// ~8 minutes
                e_len: e_len.unwrap_or(3), // 24.2 minutes
-               len: len,
                peers: peers,
         }
     }
 
-    fn time(&self) -> Result<Timestamp>{
-        match time::check_clock(self.peers).await {
-            Ok =>  {
-                Some(time::ntp_request())
+    async fn time(&self) -> Result<Timestamp> {
+        match time::check_clock(self.peers.clone()).await {
+            Ok(t) => {
+                Ok(time::ntp_request().await?)
+            },
+            Err(e) => {
+                Err(Error::ClockOutOfSync(e.to_string()
+))
             }
         }
     }
 
     /// time since genesis
-    fn time_to_genesis(&self) -> Timestamp {
-        let genesis_time : u64 = 0;
-        let abs_time = self.time().unwrap() {
-            Err(e) => {
-                !debug("time retrival fails, error: {}", e);
-            }
-        }.unwrap();
-        abs_time - genesis_time
+    async fn time_to_genesis(&self) -> Timestamp {
+        let genesis_time : i64 = GENESIS_TIME;
+        let abs_time = self.time().await.unwrap();
+        Timestamp(abs_time.0 - genesis_time)
     }
 
-    fn tick_time(&self) -> (i64, i64) {
-        let time = self.time_to_genesis();
-        let time_i = time.0;
-        let tick_abs = (time_i / self.tick_len).abs();
-        let tick_rel = time_i % self.tick_len;
+    async fn tick_time(&self) -> (u64, u64) {
+        let time = self.time_to_genesis().await;
+        let time_i = time.0 as u64;
+        let tick_abs: u64 = time_i / self.tick_len;
+        let tick_rel: u64 = time_i % self.tick_len;
         (tick_abs, tick_rel)
     }
-    /// absolute slot ticks
-    fn tick_abs(&self) -> i64 {
-        self.tick_time().0
-    }
 
-    /// return true if the clock is at the begining of the slot
-    fn ticking() -> bool{
-        let (abs, rel) =  self.tick_time();
-        rel < self.tick_len/3
+    /// return true if the clock is at the begining (before 2/3 of the slot).
+    async fn ticking(&self) -> bool {
+        let (abs, rel) =  self.tick_time().await;
+        rel < (self.tick_len) /3
     }
 
-
-    pub fn sync(&self) Result<()>{
-        let e = self.epoch_abs();
-        let sl = self.slot_relative();
+    pub async fn sync(& mut self) -> Result<()> {
+        let e = self.epoch_abs().await;
+        let sl = self.slot_relative().await;
         self.sl = sl;
-        self.e = el;
+        self.e = e;
         Ok(())
     }
 
     /// absolute zero based slot index
-    fn slot_abs(&self) -> i64 {
-        let sl_abs = self.tick_abs() / self.sl_len;
-        sl_abs.abs()
+    async fn slot_abs(&self) -> u64 {
+        let sl_abs = self.tick_time().await.0 / self.sl_len;
+        sl_abs
     }
 
     /// relative zero based slot index
-    fn slot_relative(&self) -> i64 {
-        let e_abs = self.slot_abs() % self.e_len;
+    async fn  slot_relative(&self) -> u64 {
+        let e_abs = self.slot_abs().await % self.e_len;
         e_abs
     }
 
     /// absolute zero based epoch index.
-    fn epoch_abs(&self) -> i64 {
-        let res = self.slot_abs() / self.e_len;
-        res.abs()
+    async fn epoch_abs(&self) -> u64 {
+        let res = self.slot_abs().await / self.e_len;
+        res
     }
 
     /// clock ticks return the ticks phase with corresponding phase parameters
-    pub fn ticks() -> Ticks {
+    pub async fn ticks(&mut self) -> Ticks {
         let prev_e = self.e;
         let prev_sl = self.sl;
-        let e = self.epoch_abs();
-        let sl = self.slot_relative();
-        if self.ticking() {
+        let e = self.epoch_abs().await;
+        let sl = self.slot_relative().await;
+        if self.ticking().await {
             if e==prev_e&&e==BB_E && sl==prev_sl && sl==BB_SL {
-                self.sl=sl;
-                self.e=e;
-                Ticks::GENESIS
+                self.sl=sl; // 0
+                self.e=e; // 0
+                Ticks::GENESIS{e:e, sl:sl}
             } else if e==prev_e&&sl==prev_sl+1 {
                 self.sl=sl;
                 Ticks::NEWSLOT{e:e, sl:sl}
@@ -132,5 +131,4 @@ impl Clock {
             Ticks::TOCKS
         }
     }
-
 }

+ 3 - 1
src/util/mod.rs

@@ -10,6 +10,7 @@ pub mod parse;
 pub mod path;
 pub mod serial;
 pub mod time;
+pub mod clock;
 
 #[cfg(feature = "async-runtime")]
 pub use async_util::sleep;
@@ -17,4 +18,5 @@ pub use async_util::sleep;
 pub use net_name::NetworkName;
 pub use parse::{decode_base10, encode_base10};
 pub use path::{expand_path, join_config_path, load_keypair_to_str};
-pub use time::{check_clock, unix_timestamp, NanoTimestamp, Timestamp};
+pub use time::{check_clock, unix_timestamp, NanoTimestamp, Timestamp, ntp_request};
+pub use clock::{Clock,Ticks};

+ 2 - 1
src/util/time.rs

@@ -85,6 +85,7 @@ impl std::fmt::Display for NanoTimestamp {
 
 // Clock sync parameters
 const RETRIES: u8 = 10;
+///TODO loop through set of ntps, get their average response concurrently.
 const NTP_ADDRESS: &str = "pool.ntp.org:123";
 const EPOCH: i64 = 2208988800; //1900
 
@@ -112,7 +113,7 @@ async fn peer_request(peers: &Vec<Url>) -> Result<Option<Timestamp>> {
 }
 
 // Raw ntp request execution
-async fn ntp_request() -> Result<Timestamp> {
+pub async fn ntp_request() -> Result<Timestamp> {
     // Create socket
     let sock = UdpSocket::bind("0.0.0.0:0")?;
     sock.set_read_timeout(Some(Duration::from_secs(5)))?;