Просмотр исходного кода

script/research/streamlet_rust: keypairs and signatures added

Modifications:
- fixed folder Cargo.toml dependencies and workspace structure.
- added PartialEq macro to Blockchain and Vote structures.
- added nodes keypair functionality using crypto::keypair.
- added signatures impl using crypto::schnorr.
aggstam 4 лет назад
Родитель
Сommit
9b26549e46

+ 7 - 0
script/research/streamlet_rust/Cargo.toml

@@ -3,5 +3,12 @@ name = "streamlet_rust"
 version = "0.1.0"
 edition = "2021"
 
+[dependencies.darkfi]
+path = "../../../"
+features = ["crypto"]
+
 [dependencies]
 chrono = "0.4"
+rand = "0.8.5"
+
+[workspace]

+ 56 - 45
script/research/streamlet_rust/src/lib.rs

@@ -17,12 +17,14 @@ mod tests {
         let genesis_time = Utc::now().timestamp();
 
         // We create some nodes to participate in the Protocol.
-        let mut node0 =
-            Node::new(0, genesis_time, String::from("node_password0"), genesis_block.clone());
-        let mut node1 =
-            Node::new(1, genesis_time, String::from("node_password1"), genesis_block.clone());
-        let mut node2 =
-            Node::new(2, genesis_time, String::from("node_password2"), genesis_block.clone());
+        let mut node0 = Node::new(0, genesis_time, genesis_block.clone());
+        let mut node1 = Node::new(1, genesis_time, genesis_block.clone());
+        let mut node2 = Node::new(2, genesis_time, genesis_block.clone());
+
+        // We store nodes public keys for voting.
+        let node0_public_key = node0.public_key;
+        let node1_public_key = node1.public_key;
+        let node2_public_key = node2.public_key;
 
         // We simulate some epochs to test consistency.
         node0.receive_transaction(String::from("tx0"));
@@ -33,7 +35,7 @@ mod tests {
         node2.broadcast_transaction(vec![&mut node0, &mut node1], String::from("tx2"));
 
         // Each node checks if they are the epoch leader. Leader will propose the block.
-        let proposed_block = if node0.check_if_epoch_leader(3) {
+        let (leader_public_key, block_proposal) = if node0.check_if_epoch_leader(3) {
             node0.propose_block()
         } else if node1.check_if_epoch_leader(3) {
             node1.propose_block()
@@ -42,20 +44,23 @@ mod tests {
         };
 
         // Leader broadcasts the proposed_block to rest nodes and they vote on it.
-        let node0_vote = node0.receive_proposed_block(&proposed_block).unwrap();
-        let node1_vote = node1.receive_proposed_block(&proposed_block).unwrap();
-        let node2_vote = node2.receive_proposed_block(&proposed_block).unwrap();
+        let node0_vote =
+            node0.receive_proposed_block(&leader_public_key, &block_proposal, 3).unwrap();
+        let node1_vote =
+            node1.receive_proposed_block(&leader_public_key, &block_proposal, 3).unwrap();
+        let node2_vote =
+            node2.receive_proposed_block(&leader_public_key, &block_proposal, 3).unwrap();
 
         // Each node broadcasts its vote to rest nodes.
-        node0.receive_vote(&node0_vote, 3);
-        node0.receive_vote(&node1_vote, 3);
-        node0.receive_vote(&node2_vote, 3);
-        node1.receive_vote(&node0_vote, 3);
-        node1.receive_vote(&node1_vote, 3);
-        node1.receive_vote(&node2_vote, 3);
-        node2.receive_vote(&node0_vote, 3);
-        node2.receive_vote(&node1_vote, 3);
-        node2.receive_vote(&node2_vote, 3);
+        node0.receive_vote(&node0_public_key, &node0_vote, 3);
+        node0.receive_vote(&node1_public_key, &node1_vote, 3);
+        node0.receive_vote(&node2_public_key, &node2_vote, 3);
+        node1.receive_vote(&node0_public_key, &node0_vote, 3);
+        node1.receive_vote(&node1_public_key, &node1_vote, 3);
+        node1.receive_vote(&node2_public_key, &node2_vote, 3);
+        node2.receive_vote(&node0_public_key, &node0_vote, 3);
+        node2.receive_vote(&node1_public_key, &node1_vote, 3);
+        node2.receive_vote(&node2_public_key, &node2_vote, 3);
 
         // We verify that all nodes have the same blockchain on round end.
         verify_outputs(&node0, &node1, &node2);
@@ -71,7 +76,7 @@ mod tests {
         node2.broadcast_transaction(vec![&mut node0, &mut node1], String::from("tx5"));
 
         // Each node checks if they are the epoch leader. Leader will propose the block.
-        let proposed_block = if node0.check_if_epoch_leader(3) {
+        let (leader_public_key, block_proposal) = if node0.check_if_epoch_leader(3) {
             node0.propose_block()
         } else if node1.check_if_epoch_leader(3) {
             node1.propose_block()
@@ -80,20 +85,23 @@ mod tests {
         };
 
         // Leader broadcasts the proposed_block to rest nodes and they vote on it.
-        let node0_vote = node0.receive_proposed_block(&proposed_block).unwrap();
-        let node1_vote = node1.receive_proposed_block(&proposed_block).unwrap();
-        let node2_vote = node2.receive_proposed_block(&proposed_block).unwrap();
+        let node0_vote =
+            node0.receive_proposed_block(&leader_public_key, &block_proposal, 3).unwrap();
+        let node1_vote =
+            node1.receive_proposed_block(&leader_public_key, &block_proposal, 3).unwrap();
+        let node2_vote =
+            node2.receive_proposed_block(&leader_public_key, &block_proposal, 3).unwrap();
 
         // Each node broadcasts its vote to rest nodes.
-        node0.receive_vote(&node0_vote, 3);
-        node0.receive_vote(&node1_vote, 3);
-        node0.receive_vote(&node2_vote, 3);
-        node1.receive_vote(&node0_vote, 3);
-        node1.receive_vote(&node1_vote, 3);
-        node1.receive_vote(&node2_vote, 3);
-        node2.receive_vote(&node0_vote, 3);
-        node2.receive_vote(&node1_vote, 3);
-        node2.receive_vote(&node2_vote, 3);
+        node0.receive_vote(&node0_public_key, &node0_vote, 3);
+        node0.receive_vote(&node1_public_key, &node1_vote, 3);
+        node0.receive_vote(&node2_public_key, &node2_vote, 3);
+        node1.receive_vote(&node0_public_key, &node0_vote, 3);
+        node1.receive_vote(&node1_public_key, &node1_vote, 3);
+        node1.receive_vote(&node2_public_key, &node2_vote, 3);
+        node2.receive_vote(&node0_public_key, &node0_vote, 3);
+        node2.receive_vote(&node1_public_key, &node1_vote, 3);
+        node2.receive_vote(&node2_public_key, &node2_vote, 3);
 
         // We verify that all nodes have the same blockchain on round end.
         verify_outputs(&node0, &node1, &node2);
@@ -109,7 +117,7 @@ mod tests {
         node2.broadcast_transaction(vec![&mut node0, &mut node1], String::from("tx8"));
 
         // Each node checks if they are the epoch leader. Leader will propose the block.
-        let proposed_block = if node0.check_if_epoch_leader(3) {
+        let (leader_public_key, block_proposal) = if node0.check_if_epoch_leader(3) {
             node0.propose_block()
         } else if node1.check_if_epoch_leader(3) {
             node1.propose_block()
@@ -118,20 +126,23 @@ mod tests {
         };
 
         // Leader broadcasts the proposed_block to rest nodes and they vote on it.
-        let node0_vote = node0.receive_proposed_block(&proposed_block).unwrap();
-        let node1_vote = node1.receive_proposed_block(&proposed_block).unwrap();
-        let node2_vote = node2.receive_proposed_block(&proposed_block).unwrap();
+        let node0_vote =
+            node0.receive_proposed_block(&leader_public_key, &block_proposal, 3).unwrap();
+        let node1_vote =
+            node1.receive_proposed_block(&leader_public_key, &block_proposal, 3).unwrap();
+        let node2_vote =
+            node2.receive_proposed_block(&leader_public_key, &block_proposal, 3).unwrap();
 
         // Each node broadcasts its vote to rest nodes.
-        node0.receive_vote(&node0_vote, 3);
-        node0.receive_vote(&node1_vote, 3);
-        node0.receive_vote(&node2_vote, 3);
-        node1.receive_vote(&node0_vote, 3);
-        node1.receive_vote(&node1_vote, 3);
-        node1.receive_vote(&node2_vote, 3);
-        node2.receive_vote(&node0_vote, 3);
-        node2.receive_vote(&node1_vote, 3);
-        node2.receive_vote(&node2_vote, 3);
+        node0.receive_vote(&node0_public_key, &node0_vote, 3);
+        node0.receive_vote(&node1_public_key, &node1_vote, 3);
+        node0.receive_vote(&node2_public_key, &node2_vote, 3);
+        node1.receive_vote(&node0_public_key, &node0_vote, 3);
+        node1.receive_vote(&node1_public_key, &node1_vote, 3);
+        node1.receive_vote(&node2_public_key, &node2_vote, 3);
+        node2.receive_vote(&node0_public_key, &node0_vote, 3);
+        node2.receive_vote(&node1_public_key, &node1_vote, 3);
+        node2.receive_vote(&node2_public_key, &node2_vote, 3);
 
         // We verify that all nodes have the same blockchain on round end.
         verify_outputs(&node0, &node1, &node2);

+ 4 - 0
script/research/streamlet_rust/src/structures/block.rs

@@ -24,6 +24,10 @@ impl Block {
     pub fn new(h: String, e: i64, txs: Vec<String>) -> Block {
         Block { h, e, txs, votes: Vec::new(), notarized: false, finalized: false }
     }
+
+    pub fn signature_encode(&self) -> String {
+        self.h.clone() + &self.e.to_string() + &self.txs.clone().join("")
+    }
 }
 
 impl PartialEq for Block {

+ 1 - 7
script/research/streamlet_rust/src/structures/blockchain.rs

@@ -6,7 +6,7 @@ use std::{
 use super::block::Block;
 
 /// This struct represents a sequence of blocks starting with the genesis block.
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct Blockchain {
     pub blocks: Vec<Block>,
 }
@@ -52,9 +52,3 @@ impl Blockchain {
         true
     }
 }
-
-impl PartialEq for Blockchain {
-    fn eq(&self, other: &Self) -> bool {
-        self.blocks == other.blocks
-    }
-}

+ 41 - 20
script/research/streamlet_rust/src/structures/node.rs

@@ -5,6 +5,11 @@ use std::{
 };
 
 use super::{block::Block, blockchain::Blockchain, vote::Vote};
+use darkfi::crypto::{
+    keypair::{PublicKey, SecretKey},
+    schnorr::{SchnorrPublic, SchnorrSecret},
+};
+use rand::rngs::OsRng;
 
 /// This struct represents a protocol node.
 /// Each node is numbered and has a secret-public keys pair, to sign messages.
@@ -14,23 +19,22 @@ use super::{block::Block, blockchain::Blockchain, vote::Vote};
 pub struct Node {
     pub id: u64,
     pub genesis_time: i64,
-    pub password: String,
-    pub private_key: String,
-    pub public_key: String,
+    pub secret_key: SecretKey,
+    pub public_key: PublicKey,
     pub canonical_blockchain: Blockchain,
     pub node_blockchains: Vec<Blockchain>,
     pub unconfirmed_transactions: Vec<String>,
 }
 
 impl Node {
-    pub fn new(id: u64, genesis_time: i64, password: String, init_block: Block) -> Node {
-        // TODO: add keypair generation, clock sync
+    pub fn new(id: u64, genesis_time: i64, init_block: Block) -> Node {
+        // TODO: clock sync
+        let secret = SecretKey::random(&mut OsRng);
         Node {
             id,
             genesis_time,
-            password,
-            private_key: String::from("private_key"),
-            public_key: String::from("public_key"),
+            secret_key: secret,
+            public_key: PublicKey::from_secret(secret),
             canonical_blockchain: Blockchain::new(init_block),
             node_blockchains: Vec::new(),
             unconfirmed_transactions: Vec::new(),
@@ -78,23 +82,34 @@ impl Node {
         self.id == leader
     }
 
-    /// Node generates a block for the current, containing all uncorfirmed transactions.
+    /// Node generates a block proposal(mapped as Vote) for the current epoch,
+    /// containing all uncorfirmed transactions.
     /// Block extends the longest notarized blockchain the node holds.
-    pub fn propose_block(&self) -> Block {
+    pub fn propose_block(&self) -> (PublicKey, Vote) {
         let epoch = self.get_current_epoch();
         let longest_notarized_chain = self.find_longest_notarized_chain();
         let mut hasher = DefaultHasher::new();
         longest_notarized_chain.blocks.last().unwrap().hash(&mut hasher);
         let proposed_block =
             Block::new(hasher.finish().to_string(), epoch, self.unconfirmed_transactions.clone());
-        // TODO: add block signing.
-        proposed_block
+        let signed_block = self.secret_key.sign(proposed_block.signature_encode().as_bytes());
+        (self.public_key, Vote::new(signed_block, proposed_block, self.id))
     }
 
-    /// Node receives the proposed block, verifies its sender(epoch leader), and proceeds with voting on it.
-    pub fn receive_proposed_block(&mut self, proposed_block: &Block) -> Option<Vote> {
-        // TODO: verify leader keys.
-        self.vote_block(proposed_block)
+    /// Node receives the proposed block(mapped as Vote), verifies its sender(epoch leader),
+    /// and proceeds with voting on it.
+    pub fn receive_proposed_block(
+        &mut self,
+        leader_public_key: &PublicKey,
+        proposed_block_vote: &Vote,
+        nodes_count: u64,
+    ) -> Option<Vote> {
+        assert!(self.get_epoch_leader(nodes_count) == proposed_block_vote.id);
+        assert!(leader_public_key.verify(
+            proposed_block_vote.block.signature_encode().as_bytes(),
+            &proposed_block_vote.vote
+        ));
+        self.vote_block(&proposed_block_vote.block)
     }
 
     /// Given a block, node finds which blockchain it extends.
@@ -113,8 +128,9 @@ impl Node {
         };
 
         if self.extends_notarized_blockchain(blockchain) {
-            // TODO: add block signing.
-            return Some(Vote::new(String::from("signed_block"), block.clone(), self.id))
+            let block_copy = block.clone();
+            let signed_block = self.secret_key.sign(block_copy.signature_encode().as_bytes());
+            return Some(Vote::new(signed_block, block_copy, self.id))
         }
         None
     }
@@ -172,8 +188,13 @@ impl Node {
     /// nodes unconfirmed transactions list.
     /// Finally, we check if the notarization of the block can finalize parent blocks
     ///	in its blockchain.
-    pub fn receive_vote(&mut self, vote: &Vote, nodes_count: usize) -> Option<Vote> {
-        // TODO: verify vote signature.
+    pub fn receive_vote(
+        &mut self,
+        node_public_key: &PublicKey,
+        vote: &Vote,
+        nodes_count: usize,
+    ) -> Option<Vote> {
+        assert!(node_public_key.verify(vote.block.signature_encode().as_bytes(), &vote.vote));
         let vote_block = self.find_block(&vote.block);
         if vote_block == None {
             return self.vote_block(&vote.block)

+ 4 - 9
script/research/streamlet_rust/src/structures/vote.rs

@@ -1,10 +1,11 @@
 use super::block::Block;
+use darkfi::crypto::schnorr::Signature;
 
 /// This struct represents a tuple of the form (vote, B, id).
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct Vote {
     /// signed block
-    pub vote: String,
+    pub vote: Signature,
     /// block to vote on
     pub block: Block,
     /// node id
@@ -12,13 +13,7 @@ pub struct Vote {
 }
 
 impl Vote {
-    pub fn new(vote: String, block: Block, id: u64) -> Vote {
+    pub fn new(vote: Signature, block: Block, id: u64) -> Vote {
         Vote { vote, block, id }
     }
 }
-
-impl PartialEq for Vote {
-    fn eq(&self, other: &Self) -> bool {
-        self.vote == other.vote && self.block == other.block && self.id == other.id
-    }
-}